From 97a9e9da8504718660d03253a528161da7cfdb8f Mon Sep 17 00:00:00 2001 From: usa Date: Mon, 3 Dec 2007 02:53:13 +0000 Subject: * ext/Win32API/*: removed or moved to ext/dl/win32. * ext/dl/win32/*: new. [ruby-dev:32387] git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@14093 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- ext/Win32API/.cvsignore | 3 - ext/Win32API/Win32API.c | 215 ---------- ext/Win32API/depend | 1 - ext/Win32API/extconf.rb | 6 - ext/Win32API/getch.rb | 5 - ext/Win32API/lib/win32/registry.rb | 831 ------------------------------------- ext/Win32API/lib/win32/resolv.rb | 366 ---------------- ext/Win32API/lib/win32/sspi.rb | 331 --------------- ext/Win32API/point.rb | 18 - ext/dl/win32/extconf.rb | 3 + ext/dl/win32/lib/Win32API.rb | 28 ++ ext/dl/win32/lib/win32/registry.rb | 831 +++++++++++++++++++++++++++++++++++++ ext/dl/win32/lib/win32/resolv.rb | 366 ++++++++++++++++ ext/dl/win32/lib/win32/sspi.rb | 331 +++++++++++++++ 14 files changed, 1559 insertions(+), 1776 deletions(-) delete mode 100644 ext/Win32API/.cvsignore delete mode 100644 ext/Win32API/Win32API.c delete mode 100644 ext/Win32API/depend delete mode 100644 ext/Win32API/extconf.rb delete mode 100644 ext/Win32API/getch.rb delete mode 100644 ext/Win32API/lib/win32/registry.rb delete mode 100644 ext/Win32API/lib/win32/resolv.rb delete mode 100644 ext/Win32API/lib/win32/sspi.rb delete mode 100644 ext/Win32API/point.rb create mode 100644 ext/dl/win32/extconf.rb create mode 100644 ext/dl/win32/lib/Win32API.rb create mode 100644 ext/dl/win32/lib/win32/registry.rb create mode 100644 ext/dl/win32/lib/win32/resolv.rb create mode 100644 ext/dl/win32/lib/win32/sspi.rb (limited to 'ext') diff --git a/ext/Win32API/.cvsignore b/ext/Win32API/.cvsignore deleted file mode 100644 index 90c83ed9b1..0000000000 --- a/ext/Win32API/.cvsignore +++ /dev/null @@ -1,3 +0,0 @@ -Makefile -*.log -*.def diff --git a/ext/Win32API/Win32API.c b/ext/Win32API/Win32API.c deleted file mode 100644 index 766058d1aa..0000000000 --- a/ext/Win32API/Win32API.c +++ /dev/null @@ -1,215 +0,0 @@ -/* - Win32API - Ruby Win32 API Import Facility -*/ - -#if !defined _MSC_VER && !defined _WIN32 -#define WIN32_LEAN_AND_MEAN -#include -#include -#endif - -#define _T_VOID 0 -#define _T_NUMBER 1 -#define _T_POINTER 2 -#define _T_INTEGER 3 - -#include "ruby.h" - -typedef struct { - HANDLE dll; - HANDLE proc; - VALUE dllname; - VALUE import; - VALUE export; -} Win32API; - -static void -Win32API_FreeLibrary(hdll) - HINSTANCE hdll; -{ - FreeLibrary(hdll); -} - -static VALUE -Win32API_initialize(self, dllname, proc, import, export) - VALUE self; - VALUE dllname; - VALUE proc; - VALUE import; - VALUE export; -{ - HANDLE hproc; - HINSTANCE hdll; - VALUE str; - VALUE a_import; - VALUE *ptr; - char *s; - int i; - int len; - int ex = _T_VOID; - - SafeStringValue(dllname); - SafeStringValue(proc); - hdll = LoadLibrary(RSTRING_PTR(dllname)); - if (!hdll) - rb_raise(rb_eRuntimeError, "LoadLibrary: %s\n", RSTRING_PTR(dllname)); - rb_iv_set(self, "__hdll__", Data_Wrap_Struct(rb_cData, 0, Win32API_FreeLibrary, (void*)hdll)); - hproc = (HANDLE)GetProcAddress(hdll, RSTRING_PTR(proc)); - if (!hproc) { - str = rb_str_new3(proc); - str = rb_str_cat(str, "A", 1); - hproc = (HANDLE)GetProcAddress(hdll, RSTRING_PTR(str)); - if (!hproc) - rb_raise(rb_eRuntimeError, "GetProcAddress: %s or %s\n", - RSTRING_PTR(proc), RSTRING_PTR(str)); - } - rb_iv_set(self, "__dll__", UINT2NUM((unsigned long)hdll)); - rb_iv_set(self, "__dllname__", dllname); - rb_iv_set(self, "__proc__", UINT2NUM((unsigned long)hproc)); - - a_import = rb_ary_new(); - switch (TYPE(import)) { - case T_NIL: - break; - case T_ARRAY: - ptr = RARRAY_PTR(import); - for (i = 0, len = RARRAY_LEN(import); i < len; i++) { - SafeStringValue(ptr[i]); - switch (*(char *)RSTRING_PTR(ptr[i])) { - case 'N': case 'n': case 'L': case 'l': - rb_ary_push(a_import, INT2FIX(_T_NUMBER)); - break; - case 'P': case 'p': - rb_ary_push(a_import, INT2FIX(_T_POINTER)); - break; - case 'I': case 'i': - rb_ary_push(a_import, INT2FIX(_T_INTEGER)); - break; - } - } - break; - default: - SafeStringValue(import); - s = RSTRING_PTR(import); - for (i = 0, len = RSTRING_LEN(import); i < len; i++) { - switch (*s++) { - case 'N': case 'n': case 'L': case 'l': - rb_ary_push(a_import, INT2FIX(_T_NUMBER)); - break; - case 'P': case 'p': - rb_ary_push(a_import, INT2FIX(_T_POINTER)); - break; - case 'I': case 'i': - rb_ary_push(a_import, INT2FIX(_T_INTEGER)); - break; - } - } - break; - } - - if (16 < RARRAY_LEN(a_import)) { - rb_raise(rb_eRuntimeError, "too many parameters: %ld\n", RARRAY_LEN(a_import)); - } - - rb_iv_set(self, "__import__", a_import); - - if (NIL_P(export)) { - ex = _T_VOID; - } else { - SafeStringValue(export); - switch (*RSTRING_PTR(export)) { - case 'V': case 'v': - ex = _T_VOID; - break; - case 'N': case 'n': case 'L': case 'l': - ex = _T_NUMBER; - break; - case 'P': case 'p': - ex = _T_POINTER; - break; - case 'I': case 'i': - ex = _T_INTEGER; - break; - } - } - rb_iv_set(self, "__export__", INT2FIX(ex)); - - return Qnil; -} - -#ifdef _MSC_VER -#pragma optimize("g", off) -#endif -static VALUE -Win32API_Call(argc, argv, obj) - int argc; - VALUE *argv; - VALUE obj; -{ - VALUE args; - unsigned long ret; - int i; - struct { - unsigned long params[16]; - } param; -#define params param.params - - VALUE obj_proc = rb_iv_get(obj, "__proc__"); - VALUE obj_import = rb_iv_get(obj, "__import__"); - VALUE obj_export = rb_iv_get(obj, "__export__"); - FARPROC ApiFunction = (FARPROC)NUM2ULONG(obj_proc); - int items = rb_scan_args(argc, argv, "0*", &args); - int nimport = RARRAY_LEN(obj_import); - - - if (items != nimport) - rb_raise(rb_eRuntimeError, "wrong number of parameters: expected %d, got %d", - nimport, items); - - for (i = 0; i < nimport; i++) { - unsigned long lParam = 0; - switch (FIX2INT(rb_ary_entry(obj_import, i))) { - VALUE str; - case _T_NUMBER: - case _T_INTEGER: - default: - lParam = NUM2ULONG(rb_ary_entry(args, i)); - break; - case _T_POINTER: - str = rb_ary_entry(args, i); - if (NIL_P(str)) { - lParam = 0; - } else if (FIXNUM_P(str)) { - lParam = NUM2ULONG(str); - } else { - StringValue(str); - rb_str_modify(str); - lParam = (unsigned long)StringValuePtr(str); - } - break; - } - params[i] = lParam; - } - - ret = ApiFunction(param); - - switch (FIX2INT(obj_export)) { - case _T_NUMBER: - case _T_INTEGER: - return INT2NUM(ret); - case _T_POINTER: - return rb_str_new2((char *)ret); - case _T_VOID: - default: - return INT2NUM(0); - } -} - -void -Init_Win32API() -{ - VALUE cWin32API = rb_define_class("Win32API", rb_cObject); - rb_define_method(cWin32API, "initialize", Win32API_initialize, 4); - rb_define_method(cWin32API, "call", Win32API_Call, -1); - rb_define_alias(cWin32API, "Call", "call"); -} diff --git a/ext/Win32API/depend b/ext/Win32API/depend deleted file mode 100644 index b224bb66c9..0000000000 --- a/ext/Win32API/depend +++ /dev/null @@ -1 +0,0 @@ -Win32API.o : Win32API.c $(hdrdir)/ruby.h $(topdir)/config.h $(hdrdir)/defines.h diff --git a/ext/Win32API/extconf.rb b/ext/Win32API/extconf.rb deleted file mode 100644 index 865788556f..0000000000 --- a/ext/Win32API/extconf.rb +++ /dev/null @@ -1,6 +0,0 @@ -require 'mkmf' - -dir_config("win32") -if have_header("windows.h") and have_library("kernel32") - create_makefile("Win32API") -end diff --git a/ext/Win32API/getch.rb b/ext/Win32API/getch.rb deleted file mode 100644 index c015bbe9bc..0000000000 --- a/ext/Win32API/getch.rb +++ /dev/null @@ -1,5 +0,0 @@ -require 'Win32API' - -getch = Win32API.new("crtdll", "_getch", [], 'L') - -puts getch.Call.chr diff --git a/ext/Win32API/lib/win32/registry.rb b/ext/Win32API/lib/win32/registry.rb deleted file mode 100644 index 2671551a33..0000000000 --- a/ext/Win32API/lib/win32/registry.rb +++ /dev/null @@ -1,831 +0,0 @@ -=begin -= Win32 Registry I/F -win32/registry is registry accessor library for Win32 platform. -It uses Win32API to call Win32 Registry APIs. - -== example - Win32::Registry::HKEY_CURRENT_USER.open('SOFTWARE\foo') do |reg| - value = reg['foo'] # read a value - value = reg['foo', Win32::Registry::REG_SZ] # read a value with type - type, value = reg.read('foo') # read a value - reg['foo'] = 'bar' # write a value - reg['foo', Win32::Registry::REG_SZ] = 'bar' # write a value with type - reg.write('foo', Win32::Registry::REG_SZ, 'bar') # write a value - - reg.each_value { |name, type, data| ... } # Enumerate values - reg.each_key { |key, wtime| ... } # Enumerate subkeys - - reg.delete_value(name) # Delete a value - reg.delete_key(name) # Delete a subkey - reg.delete_key(name, true) # Delete a subkey recursively - end - -= Reference - -== Win32::Registry class - -=== including modules - -* Enumerable -* Registry::Constants - -=== class methods ---- Registry.open(key, subkey, desired = KEY_READ, opt = REG_OPTION_RESERVED) ---- Registry.open(key, subkey, desired = KEY_READ, opt = REG_OPTION_RESERVED) { |reg| ... } - Open the registry key ((|subkey|)) under ((|key|)). - ((|key|)) is Win32::Registry object of parent key. - You can use predefined key HKEY_* (see (())) - - ((|desired|)) and ((|opt|)) is access mask and key option. - For detail, see (()). - - If block is given, the key is closed automatically. - ---- Registry.create(key, subkey, desired = KEY_ALL_ACCESS, opt = REG_OPTION_RESERVED) ---- Registry.create(key, subkey, desired = KEY_ALL_ACCESS, opt = REG_OPTION_RESERVED) { |reg| ... } - Create or open the registry key ((|subkey|)) under ((|key|)). - You can use predefined key HKEY_* (see (())) - - If subkey is already exists, key is opened and Registry#(()) - method will return false. - - If block is given, the key is closed automatically. - ---- Registry.expand_environ(str) - Replace (({%\w+%})) into the environment value of ((|str|)). - This method is used for REG_EXPAND_SZ. - - For detail, see (()) Win32 API. - ---- Registry.type2name(type) - Convert registry type value to readable string. - ---- Registry.wtime2time(wtime) - Convert 64-bit FILETIME integer into Time object. - ---- Registry.time2wtime(time) - Convert Time object or Integer object into 64-bit FILETIME. - -=== instance methods ---- open(subkey, desired = KEY_READ, opt = REG_OPTION_RESERVED) - Same as (({Win32::(())(self, subkey, desired, opt)})) - ---- create(subkey, desired = KEY_ALL_ACCESS, opt = REG_OPTION_RESERVED) - Same as (({Win32::(())(self, subkey, desired, opt)})) - ---- close - Close key. - - After closed, most method raises error. - ---- read(name, *rtype) - Read a registry value named ((|name|)) and return array of - [ ((|type|)), ((|data|)) ]. - When name is nil, the `default' value is read. - - ((|type|)) is value type. (see (())) - ((|data|)) is value data, its class is: - :REG_SZ, REG_EXPAND_SZ - String - :REG_MULTI_SZ - Array of String - :REG_DWORD, REG_DWORD_BIG_ENDIAN, REG_QWORD - Integer - :REG_BINARY - String (contains binary data) - - When ((|rtype|)) is specified, the value type must be included by - ((|rtype|)) array, or TypeError is raised. - ---- self[name, *rtype] - Read a registry value named ((|name|)) and return its value data. - The class of value is same as (()) method returns. - - If the value type is REG_EXPAND_SZ, returns value data whose environment - variables are replaced. - If the value type is neither REG_SZ, REG_MULTI_SZ, REG_DWORD, - REG_DWORD_BIG_ENDIAN, nor REG_QWORD, TypeError is raised. - - The meaning of ((|rtype|)) is same as (()) method. - ---- read_s(name) ---- read_i(name) ---- read_bin(name) - Read a REG_SZ(read_s), REG_DWORD(read_i), or REG_BINARY(read_bin) - registry value named ((|name|)). - - If the values type does not match, TypeError is raised. - ---- read_s_expand(name) - Read a REG_SZ or REG_EXPAND_SZ registry value named ((|name|)). - - If the value type is REG_EXPAND_SZ, environment variables are replaced. - Unless the value type is REG_SZ or REG_EXPAND_SZ, TypeError is raised. - ---- write(name, type, data) - Write ((|data|)) to a registry value named ((|name|)). - When name is nil, write to the `default' value. - - ((|type|)) is type value. (see (())) - Class of ((|data|)) must be same as which (()) - method returns. - ---- self[name, wtype = nil] = value - Write ((|value|)) to a registry value named ((|name|)). - - If ((|wtype|)) is specified, the value type is it. - Otherwise, the value type is depend on class of ((|value|)): - :Integer - REG_DWORD - :String - REG_SZ - :Array - REG_MULTI_SZ - ---- write_s(name, value) ---- write_i(name, value) ---- write_bin(name, value) - Write ((|value|)) to a registry value named ((|name|)). - - The value type is REG_SZ(write_s), REG_DWORD(write_i), or - REG_BINARY(write_bin). - ---- each { |name, type, value| ... } ---- each_value { |name, type, value| ... } - Enumerate values. - ---- each_key { |subkey, wtime| ... } - Enumerate subkeys. - - ((|subkey|)) is String which contains name of subkey. - ((|wtime|)) is last write time as FILETIME (64-bit integer). - (see (())) - ---- delete(name) ---- delete_value(name) - Delete a registry value named ((|name|)). - We can not delete the `default' value. - ---- delete_key(name, recursive = false) - Delete a subkey named ((|name|)) and all its values. - - If ((|recursive|)) is false, the subkey must not have subkeys. - Otherwise, this method deletes all subkeys and values recursively. - ---- flush - Write all the attributes into the registry file. - ---- created? - Returns if key is created ((*newly*)). - (see (())) - ---- open? - Returns if key is not closed. - ---- hkey - Returns key handle value. - ---- parent - Win32::Registry object of parent key, or nil if predefeined key. - ---- keyname - Same as ((|subkey|)) value of (()) or - (()) method. - ---- disposition - Disposition value (REG_CREATED_NEW_KEY or REG_OPENED_EXISTING_KEY). - ---- name ---- to_s - Full path of key such as (({'HKEY_CURRENT_USER\SOFTWARE\foo\bar'})). - ---- info - Returns key information as Array of: - :num_keys - The number of subkeys. - :max_key_length - Maximum length of name of subkeys. - :num_values - The number of values. - :max_value_name_length - Maximum length of name of values. - :max_value_length - Maximum length of value of values. - :descriptor_length - Length of security descriptor. - :wtime - Last write time as FILETIME(64-bit integer) - - For detail, see (()) Win32 API. - ---- num_keys ---- max_key_length ---- num_values ---- max_value_name_length ---- max_value_length ---- descriptor_length ---- wtime - Returns an item of key information. - -=== constants ---- HKEY_CLASSES_ROOT ---- HKEY_CURRENT_USER ---- HKEY_LOCAL_MACHINE ---- HKEY_PERFORMANCE_DATA ---- HKEY_CURRENT_CONFIG ---- HKEY_DYN_DATA - Win32::Registry object whose key is predefined key. - For detail, see (()). - -== Win32::Registry::Constants module - -For detail, see (()). - ---- HKEY_* - Predefined key ((*handle*)). - These are Integer, not Win32::Registry. - ---- REG_* - Registry value type. - ---- KEY_* - Security access mask. - ---- KEY_OPTIONS_* - Key options. - ---- REG_CREATED_NEW_KEY ---- REG_OPENED_EXISTING_KEY - If the key is created newly or opened existing key. - See also Registry#(()) method. - -=end - -require 'Win32API' - -module Win32 - class Registry - module Constants - HKEY_CLASSES_ROOT = 0x80000000 - HKEY_CURRENT_USER = 0x80000001 - HKEY_LOCAL_MACHINE = 0x80000002 - HKEY_USERS = 0x80000003 - HKEY_PERFORMANCE_DATA = 0x80000004 - HKEY_PERFORMANCE_TEXT = 0x80000050 - HKEY_PERFORMANCE_NLSTEXT = 0x80000060 - HKEY_CURRENT_CONFIG = 0x80000005 - HKEY_DYN_DATA = 0x80000006 - - REG_NONE = 0 - REG_SZ = 1 - REG_EXPAND_SZ = 2 - REG_BINARY = 3 - REG_DWORD = 4 - REG_DWORD_LITTLE_ENDIAN = 4 - REG_DWORD_BIG_ENDIAN = 5 - REG_LINK = 6 - REG_MULTI_SZ = 7 - REG_RESOURCE_LIST = 8 - REG_FULL_RESOURCE_DESCRIPTOR = 9 - REG_RESOURCE_REQUIREMENTS_LIST = 10 - REG_QWORD = 11 - REG_QWORD_LITTLE_ENDIAN = 11 - - STANDARD_RIGHTS_READ = 0x00020000 - STANDARD_RIGHTS_WRITE = 0x00020000 - KEY_QUERY_VALUE = 0x0001 - KEY_SET_VALUE = 0x0002 - KEY_CREATE_SUB_KEY = 0x0004 - KEY_ENUMERATE_SUB_KEYS = 0x0008 - KEY_NOTIFY = 0x0010 - KEY_CREATE_LINK = 0x0020 - KEY_READ = STANDARD_RIGHTS_READ | - KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS | KEY_NOTIFY - KEY_WRITE = STANDARD_RIGHTS_WRITE | - KEY_SET_VALUE | KEY_CREATE_SUB_KEY - KEY_EXECUTE = KEY_READ - KEY_ALL_ACCESS = KEY_READ | KEY_WRITE | KEY_CREATE_LINK - - REG_OPTION_RESERVED = 0x0000 - REG_OPTION_NON_VOLATILE = 0x0000 - REG_OPTION_VOLATILE = 0x0001 - REG_OPTION_CREATE_LINK = 0x0002 - REG_OPTION_BACKUP_RESTORE = 0x0004 - REG_OPTION_OPEN_LINK = 0x0008 - REG_LEGAL_OPTION = REG_OPTION_RESERVED | - REG_OPTION_NON_VOLATILE | REG_OPTION_CREATE_LINK | - REG_OPTION_BACKUP_RESTORE | REG_OPTION_OPEN_LINK - - REG_CREATED_NEW_KEY = 1 - REG_OPENED_EXISTING_KEY = 2 - - REG_WHOLE_HIVE_VOLATILE = 0x0001 - REG_REFRESH_HIVE = 0x0002 - REG_NO_LAZY_FLUSH = 0x0004 - REG_FORCE_RESTORE = 0x0008 - - MAX_KEY_LENGTH = 514 - MAX_VALUE_LENGTH = 32768 - end - include Constants - include Enumerable - - # - # Error - # - class Error < ::StandardError - FormatMessageA = Win32API.new('kernel32.dll', 'FormatMessageA', 'LPLLPLP', 'L') - def initialize(code) - @code = code - msg = "\0" * 1024 - len = FormatMessageA.call(0x1200, 0, code, 0, msg, 1024, 0) - super msg[0, len].tr("\r", '').chomp - end - attr_reader :code - end - - # - # Predefined Keys - # - class PredefinedKey < Registry - def initialize(hkey, keyname) - @hkey = hkey - @parent = nil - @keyname = keyname - @disposition = REG_OPENED_EXISTING_KEY - end - - # Predefined keys cannot be closed - def close - raise Error.new(5) ## ERROR_ACCESS_DENIED - end - - # Fake class for Registry#open, Registry#create - def class - Registry - end - - # Make all - Constants.constants.grep(/^HKEY_/) do |c| - Registry.const_set c, new(Constants.const_get(c), c) - end - end - - # - # Win32 APIs - # - module API - [ - %w/RegOpenKeyExA LPLLP L/, - %w/RegCreateKeyExA LPLLLLPPP L/, - %w/RegEnumValueA LLPPPPPP L/, - %w/RegEnumKeyExA LLPPLLLP L/, - %w/RegQueryValueExA LPLPPP L/, - %w/RegSetValueExA LPLLPL L/, - %w/RegDeleteValue LP L/, - %w/RegDeleteKey LP L/, - %w/RegFlushKey L L/, - %w/RegCloseKey L L/, - %w/RegQueryInfoKey LPPPPPPPPPPP L/, - ].each do |fn| - const_set fn[0].intern, Win32API.new('advapi32.dll', *fn) - end - - module_function - - def check(result) - raise Error, result, caller(2) if result != 0 - end - - def packdw(dw) - [dw].pack('V') - end - - def unpackdw(dw) - dw += [0].pack('V') - dw.unpack('V')[0] - end - - def packqw(qw) - [ qw & 0xFFFFFFFF, qw >> 32 ].pack('VV') - end - - def unpackqw(qw) - qw = qw.unpack('VV') - (qw[1] << 32) | qw[0] - end - - def OpenKey(hkey, name, opt, desired) - result = packdw(0) - check RegOpenKeyExA.call(hkey, name, opt, desired, result) - unpackdw(result) - end - - def CreateKey(hkey, name, opt, desired) - result = packdw(0) - disp = packdw(0) - check RegCreateKeyExA.call(hkey, name, 0, 0, opt, desired, - 0, result, disp) - [ unpackdw(result), unpackdw(disp) ] - end - - def EnumValue(hkey, index) - name = ' ' * Constants::MAX_KEY_LENGTH - size = packdw(Constants::MAX_KEY_LENGTH) - check RegEnumValueA.call(hkey, index, name, size, 0, 0, 0, 0) - name[0, unpackdw(size)] - end - - def EnumKey(hkey, index) - name = ' ' * Constants::MAX_KEY_LENGTH - size = packdw(Constants::MAX_KEY_LENGTH) - wtime = ' ' * 8 - check RegEnumKeyExA.call(hkey, index, name, size, 0, 0, 0, wtime) - [ name[0, unpackdw(size)], unpackqw(wtime) ] - end - - def QueryValue(hkey, name) - type = packdw(0) - size = packdw(0) - check RegQueryValueExA.call(hkey, name, 0, type, 0, size) - data = ' ' * unpackdw(size) - check RegQueryValueExA.call(hkey, name, 0, type, data, size) - [ unpackdw(type), data[0, unpackdw(size)] ] - end - - def SetValue(hkey, name, type, data, size) - check RegSetValueExA.call(hkey, name, 0, type, data, size) - end - - def DeleteValue(hkey, name) - check RegDeleteValue.call(hkey, name) - end - - def DeleteKey(hkey, name) - check RegDeleteKey.call(hkey, name) - end - - def FlushKey(hkey) - check RegFlushKey.call(hkey) - end - - def CloseKey(hkey) - check RegCloseKey.call(hkey) - end - - def QueryInfoKey(hkey) - subkeys = packdw(0) - maxsubkeylen = packdw(0) - values = packdw(0) - maxvaluenamelen = packdw(0) - maxvaluelen = packdw(0) - secdescs = packdw(0) - wtime = ' ' * 8 - check RegQueryInfoKey.call(hkey, 0, 0, 0, subkeys, maxsubkeylen, 0, - values, maxvaluenamelen, maxvaluelen, secdescs, wtime) - [ unpackdw(subkeys), unpackdw(maxsubkeylen), unpackdw(values), - unpackdw(maxvaluenamelen), unpackdw(maxvaluelen), - unpackdw(secdescs), unpackqw(wtime) ] - end - end - - # - # utility functions - # - def self.expand_environ(str) - str.gsub(/%([^%]+)%/) { ENV[$1] || $& } - end - - @@type2name = { } - %w[ - REG_NONE REG_SZ REG_EXPAND_SZ REG_BINARY REG_DWORD - REG_DWORD_BIG_ENDIAN REG_LINK REG_MULTI_SZ - REG_RESOURCE_LIST REG_FULL_RESOURCE_DESCRIPTOR - REG_RESOURCE_REQUIREMENTS_LIST REG_QWORD - ].each do |type| - @@type2name[Constants.const_get(type)] = type - end - - def self.type2name(type) - @@type2name[type] || type.to_s - end - - def self.wtime2time(wtime) - Time.at((wtime - 116444736000000000) / 10000000) - end - - def self.time2wtime(time) - time.to_i * 10000000 + 116444736000000000 - end - - # - # constructors - # - private_class_method :new - - def self.open(hkey, subkey, desired = KEY_READ, opt = REG_OPTION_RESERVED) - subkey = subkey.chomp('\\') - newkey = API.OpenKey(hkey.hkey, subkey, opt, desired) - obj = new(newkey, hkey, subkey, REG_OPENED_EXISTING_KEY) - if block_given? - begin - yield obj - ensure - obj.close - end - else - obj - end - end - - def self.create(hkey, subkey, desired = KEY_ALL_ACCESS, opt = REG_OPTION_RESERVED) - newkey, disp = API.CreateKey(hkey.hkey, subkey, opt, desired) - obj = new(newkey, hkey, subkey, disp) - if block_given? - begin - yield obj - ensure - obj.close - end - else - obj - end - end - - # - # finalizer - # - @@final = proc { |hkey| proc { API.CloseKey(hkey[0]) if hkey[0] } } - - # - # initialize - # - def initialize(hkey, parent, keyname, disposition) - @hkey = hkey - @parent = parent - @keyname = keyname - @disposition = disposition - @hkeyfinal = [ hkey ] - ObjectSpace.define_finalizer self, @@final.call(@hkeyfinal) - end - attr_reader :hkey, :parent, :keyname, :disposition - - # - # attributes - # - def created? - @disposition == REG_CREATED_NEW_KEY - end - - def open? - !@hkey.nil? - end - - def name - parent = self - name = @keyname - while parent = parent.parent - name = parent.keyname + '\\' + name - end - name - end - - def inspect - "\#" - end - - # - # marshalling - # - def _dump(depth) - raise TypeError, "can't dump Win32::Registry" - end - - # - # open/close - # - def open(subkey, desired = KEY_READ, opt = REG_OPTION_RESERVED, &blk) - self.class.open(self, subkey, desired, opt, &blk) - end - - def create(subkey, desired = KEY_ALL_ACCESS, opt = REG_OPTION_RESERVED, &blk) - self.class.create(self, subkey, desired, opt, &blk) - end - - def close - API.CloseKey(@hkey) - @hkey = @parent = @keyname = nil - @hkeyfinal[0] = nil - end - - # - # iterator - # - def each_value - index = 0 - while true - begin - subkey = API.EnumValue(@hkey, index) - rescue Error - break - end - begin - type, data = read(subkey) - rescue Error - next - end - yield subkey, type, data - index += 1 - end - index - end - alias each each_value - - def each_key - index = 0 - while true - begin - subkey, wtime = API.EnumKey(@hkey, index) - rescue Error - break - end - yield subkey, wtime - index += 1 - end - index - end - - def keys - keys_ary = [] - each_key { |key,| keys_ary << key } - keys_ary - end - - # - # reader - # - def read(name, *rtype) - type, data = API.QueryValue(@hkey, name) - unless rtype.empty? or rtype.include?(type) - raise TypeError, "Type mismatch (expect #{rtype.inspect} but #{type} present)" - end - case type - when REG_SZ, REG_EXPAND_SZ - [ type, data.chop ] - when REG_MULTI_SZ - [ type, data.split(/\0/) ] - when REG_BINARY - [ type, data ] - when REG_DWORD - [ type, API.unpackdw(data) ] - when REG_DWORD_BIG_ENDIAN - [ type, data.unpack('N')[0] ] - when REG_QWORD - [ type, API.unpackqw(data) ] - else - raise TypeError, "Type #{type} is not supported." - end - end - - def [](name, *rtype) - type, data = read(name, *rtype) - case type - when REG_SZ, REG_DWORD, REG_QWORD, REG_MULTI_SZ - data - when REG_EXPAND_SZ - Registry.expand_environ(data) - else - raise TypeError, "Type #{type} is not supported." - end - end - - def read_s(name) - read(name, REG_SZ)[1] - end - - def read_s_expand(name) - type, data = read(name, REG_SZ, REG_EXPAND_SZ) - if type == REG_EXPAND_SZ - Registry.expand_environ(data) - else - data - end - end - - def read_i(name) - read(name, REG_DWORD, REG_DWORD_BIG_ENDIAN, REG_QWORD)[1] - end - - def read_bin(name) - read(name, REG_BINARY)[1] - end - - # - # writer - # - def write(name, type, data) - case type - when REG_SZ, REG_EXPAND_SZ - data = data.to_s + "\0" - when REG_MULTI_SZ - data = data.to_a.join("\0") + "\0\0" - when REG_BINARY - data = data.to_s - when REG_DWORD - data = API.packdw(data.to_i) - when REG_DWORD_BIG_ENDIAN - data = [data.to_i].pack('N') - when REG_QWORD - data = API.packqw(data.to_i) - else - raise TypeError, "Unsupported type #{type}" - end - API.SetValue(@hkey, name, type, data, data.length) - end - - def []=(name, rtype, value = nil) - if value - write name, rtype, value - else - case value = rtype - when Integer - write name, REG_DWORD, value - when String - write name, REG_SZ, value - when Array - write name, REG_MULTI_SZ, value - else - raise TypeError, "Unexpected type #{value.class}" - end - end - value - end - - def write_s(name, value) - write name, REG_SZ, value.to_s - end - - def write_i(name, value) - write name, REG_DWORD, value.to_i - end - - def write_bin(name, value) - write name, REG_BINARY, value.to_s - end - - # - # delete - # - def delete_value(name) - API.DeleteValue(@hkey, name) - end - alias delete delete_value - - def delete_key(name, recursive = false) - if recursive - open(name, KEY_ALL_ACCESS) do |reg| - reg.keys.each do |key| - begin - reg.delete_key(key, true) - rescue Error - # - end - end - end - API.DeleteKey(@hkey, name) - else - begin - API.EnumKey @hkey, 0 - rescue Error - return API.DeleteKey(@hkey, name) - end - raise Error.new(5) ## ERROR_ACCESS_DENIED - end - end - - # - # flush - # - def flush - API.FlushKey @hkey - end - - # - # key information - # - def info - API.QueryInfoKey(@hkey) - end - %w[ - num_keys max_key_length - num_values max_value_name_length max_value_length - descriptor_length wtime - ].each_with_index do |s, i| - eval <<-__END__ - def #{s} - info[#{i}] - end - __END__ - end - end -end diff --git a/ext/Win32API/lib/win32/resolv.rb b/ext/Win32API/lib/win32/resolv.rb deleted file mode 100644 index 6534d20760..0000000000 --- a/ext/Win32API/lib/win32/resolv.rb +++ /dev/null @@ -1,366 +0,0 @@ -=begin -= Win32 DNS and DHCP I/F - -=end - -require 'win32/registry' - -module Win32 - module Resolv - API = Registry::API - - def self.get_hosts_path - path = get_hosts_dir - path = File.join(path.gsub(/\\/, File::SEPARATOR), 'hosts') - File.exist?(path) ? path : nil - end - - def self.get_resolv_info - search, nameserver = get_info - if search.empty? - search = nil - else - search.delete("") - search.uniq! - end - if nameserver.empty? - nameserver = nil - else - nameserver.delete("") - nameserver.delete("0.0.0.0") - nameserver.uniq! - end - [ search, nameserver ] - end - -getv = Win32API.new('kernel32.dll', 'GetVersionExA', 'P', 'L') -info = [ 148, 0, 0, 0, 0 ].pack('V5') + "\0" * 128 -getv.call(info) -if info.unpack('V5')[4] == 2 # VER_PLATFORM_WIN32_NT -#==================================================================== -# Windows NT -#==================================================================== - module_eval <<-'__EOS__', __FILE__, __LINE__+1 - TCPIP_NT = 'SYSTEM\CurrentControlSet\Services\Tcpip\Parameters' - - class << self - private - def get_hosts_dir - Registry::HKEY_LOCAL_MACHINE.open(TCPIP_NT) do |reg| - reg.read_s_expand('DataBasePath') - end - end - - def get_info - search = nil - nameserver = [] - Registry::HKEY_LOCAL_MACHINE.open(TCPIP_NT) do |reg| - begin - slist = reg.read_s('SearchList') - search = slist.split(/,\s*/) unless slist.empty? - rescue Registry::Error - end - - if add_search = search.nil? - search = [] - begin - nvdom = reg.read_s('NV Domain') - unless nvdom.empty? - @search = [ nvdom ] - if reg.read_i('UseDomainNameDevolution') != 0 - if /^[\w\d]+\./ =~ nvdom - devo = $' - end - end - end - rescue Registry::Error - end - end - - reg.open('Interfaces') do |reg| - reg.each_key do |iface,| - reg.open(iface) do |regif| - begin - [ 'NameServer', 'DhcpNameServer' ].each do |key| - ns = regif.read_s(key) - unless ns.empty? - nameserver.concat(ns.split(/[,\s]\s*/)) - break - end - end - rescue Registry::Error - end - - if add_search - begin - [ 'Domain', 'DhcpDomain' ].each do |key| - dom = regif.read_s(key) - unless dom.empty? - search.concat(dom.split(/,\s*/)) - break - end - end - rescue Registry::Error - end - end - end - end - end - search << devo if add_search and devo - end - [ search.uniq, nameserver.uniq ] - end - end - __EOS__ -else -#==================================================================== -# Windows 9x -#==================================================================== - module_eval <<-'__EOS__', __FILE__, __LINE__+1 - TCPIP_9X = 'SYSTEM\CurrentControlSet\Services\VxD\MSTCP' - DHCP_9X = 'SYSTEM\CurrentControlSet\Services\VxD\DHCP' - WINDOWS = 'Software\Microsoft\Windows\CurrentVersion' - - class << self - # private - - def get_hosts_dir - Registry::HKEY_LOCAL_MACHINE.open(WINDOWS) do |reg| - reg.read_s_expand('SystemRoot') - end - end - - def get_info - search = [] - nameserver = [] - begin - Registry::HKEY_LOCAL_MACHINE.open(TCPIP_9X) do |reg| - if reg.read_s("EnableDNS") == "1" - domain = reg.read_s("Domain") - ns = reg.read_s("NameServer") - slist = reg.read_s("SearchList") - search << domain unless domain.empty? - search.concat(slist.split(/,\s*/)) - nameserver.concat(ns.split(/[,\s]\s*/)) - end - end - rescue Registry::Error - end - - dhcpinfo = get_dhcpinfo - search.concat(dhcpinfo[0]) - nameserver.concat(dhcpinfo[1]) - [ search, nameserver ] - end - - def get_dhcpinfo - macaddrs = {} - ipaddrs = {} - WsControl.get_iflist.each do |index, macaddr, *ipaddr| - macaddrs[macaddr] = 1 - ipaddr.each { |ipaddr| ipaddrs[ipaddr] = 1 } - end - iflist = [ macaddrs, ipaddrs ] - - search = [] - nameserver = [] - version = -1 - Registry::HKEY_LOCAL_MACHINE.open(DHCP_9X) do |reg| - begin - version = API.unpackdw(reg.read_bin("Version")) - rescue Registry::Error - end - - reg.each_key do |key,| - catch(:not_used) do - reg.open(key) do |regdi| - dom, ns = get_dhcpinfo_key(version, regdi, iflist) - search << dom if dom - nameserver.concat(ns) if ns - end - end - end - end - [ search, nameserver ] - end - - def get_dhcpinfo_95(reg) - dhcp = reg.read_bin("DhcpInfo") - [ - API.unpackdw(dhcp[4..7]), - API.unpackdw(dhcp[8..11]), - 1, - dhcp[45..50], - reg.read_bin("OptionInfo"), - ] - end - - def get_dhcpinfo_98(reg) - [ - API.unpackdw(reg.read_bin("DhcpIPAddress")), - API.unpackdw(reg.read_bin("DhcpSubnetMask")), - API.unpackdw(reg.read_bin("HardwareType")), - reg.read_bin("HardwareAddress"), - reg.read_bin("OptionInfo"), - ] - end - - def get_dhcpinfo_key(version, reg, iflist) - info = case version - when 1 - get_dhcpinfo_95(reg) - when 2 - get_dhcpinfo_98(reg) - else - begin - get_dhcpinfo_98(reg) - rescue Registry::Error - get_dhcpinfo_95(reg) - end - end - ipaddr, netmask, hwtype, macaddr, opt = info - throw :not_used unless - ipaddr and ipaddr != 0 and - netmask and netmask != 0 and - macaddr and macaddr.size == 6 and - hwtype == 1 and - iflist[0][macaddr] and iflist[1][ipaddr] - - size = opt.size - idx = 0 - while idx <= size - opttype = opt[idx] - optsize = opt[idx + 1] - optval = opt[idx + 2, optsize] - case opttype - when 0xFF ## term - break - when 0x0F ## domain - domain = optval.chomp("\0") - when 0x06 ## dns - nameserver = optval.scan(/..../).collect { |addr| - "%d.%d.%d.%d" % addr.unpack('C4') - } - end - idx += optsize + 2 - end - [ domain, nameserver ] - rescue Registry::Error - throw :not_used - end - end - - module WsControl - WsControl = Win32API.new('wsock32.dll', 'WsControl', 'LLPPPP', 'L') - WSAGetLastError = Win32API.new('wsock32.dll', 'WSAGetLastError', 'V', 'L') - - MAX_TDI_ENTITIES = 512 - IPPROTO_TCP = 6 - WSCTL_TCP_QUERY_INFORMATION = 0 - INFO_CLASS_GENERIC = 0x100 - INFO_CLASS_PROTOCOL = 0x200 - INFO_TYPE_PROVIDER = 0x100 - ENTITY_LIST_ID = 0 - GENERIC_ENTITY = 0 - CL_NL_ENTITY = 0x301 - IF_ENTITY = 0x200 - ENTITY_TYPE_ID = 1 - CL_NL_IP = 0x303 - IF_MIB = 0x202 - IF_MIB_STATS_ID = 1 - IP_MIB_ADDRTABLE_ENTRY_ID = 0x102 - - def self.wsctl(tei_entity, tei_instance, - toi_class, toi_type, toi_id, - buffsize) - reqinfo = [ - ## TDIEntityID - tei_entity, tei_instance, - ## TDIObjectID - toi_class, toi_type, toi_id, - ## TCP_REQUEST_INFORMATION_EX - "" - ].pack('VVVVVa16') - reqsize = API.packdw(reqinfo.size) - buff = "\0" * buffsize - buffsize = API.packdw(buffsize) - result = WsControl.call( - IPPROTO_TCP, - WSCTL_TCP_QUERY_INFORMATION, - reqinfo, reqsize, - buff, buffsize) - if result != 0 - raise RuntimeError, "WsControl failed.(#{result})" - end - [ buff, API.unpackdw(buffsize) ] - end - private_class_method :wsctl - - def self.get_iflist - # Get TDI Entity List - entities, size = - wsctl(GENERIC_ENTITY, 0, - INFO_CLASS_GENERIC, - INFO_TYPE_PROVIDER, - ENTITY_LIST_ID, - MAX_TDI_ENTITIES * 8) # sizeof(TDIEntityID) - entities = entities[0, size]. - scan(/.{8}/). - collect { |e| e.unpack('VV') } - - # Get MIB Interface List - iflist = [] - ifcount = 0 - entities.each do |entity, instance| - if( (entity & IF_ENTITY)>0 ) - ifcount += 1 - etype, = wsctl(entity, instance, - INFO_CLASS_GENERIC, - INFO_TYPE_PROVIDER, - ENTITY_TYPE_ID, - 4) - if( (API.unpackdw(etype) & IF_MIB)==IF_MIB ) - ifentry, = wsctl(entity, instance, - INFO_CLASS_PROTOCOL, - INFO_TYPE_PROVIDER, - IF_MIB_STATS_ID, - 21 * 4 + 8 + 130) # sizeof(IFEntry) - iflist << [ - API.unpackdw(ifentry[0,4]), - ifentry[20, 6] - ] - end - end - end - - # Get IP Addresses - entities.each do |entity, instance| - if entity == CL_NL_ENTITY - etype, = wsctl(entity, instance, - INFO_CLASS_GENERIC, - INFO_TYPE_PROVIDER, - ENTITY_TYPE_ID, - 4) - if API.unpackdw(etype) == CL_NL_IP - ipentries, = wsctl(entity, instance, - INFO_CLASS_PROTOCOL, - INFO_TYPE_PROVIDER, - IP_MIB_ADDRTABLE_ENTRY_ID, - 24 * (ifcount+1)) # sizeof(IPAddrEntry) - ipentries.scan(/.{24}/) do |ipentry| - ipaddr, index = ipentry.unpack('VV') - if ifitem = iflist.assoc(index) - ifitem << ipaddr - end - end - end - end - end - iflist - end - end - __EOS__ -end -#==================================================================== - end -end diff --git a/ext/Win32API/lib/win32/sspi.rb b/ext/Win32API/lib/win32/sspi.rb deleted file mode 100644 index 60291d51ea..0000000000 --- a/ext/Win32API/lib/win32/sspi.rb +++ /dev/null @@ -1,331 +0,0 @@ -# -# = win32/sspi.rb -# -# Copyright (c) 2006-2007 Justin Bailey -# -# Written and maintained by Justin Bailey . -# -# This program is free software. You can re-distribute and/or -# modify this program under the same terms of ruby itself --- -# Ruby Distribution License or GNU General Public License. -# - -require 'Win32API' -require 'base64' - -# Implements bindings to Win32 SSPI functions, focused on authentication to a proxy server over HTTP. -module Win32 - module SSPI - # Specifies how credential structure requested will be used. Only SECPKG_CRED_OUTBOUND is used - # here. - SECPKG_CRED_INBOUND = 0x00000001 - SECPKG_CRED_OUTBOUND = 0x00000002 - SECPKG_CRED_BOTH = 0x00000003 - - # Format of token. NETWORK format is used here. - SECURITY_NATIVE_DREP = 0x00000010 - SECURITY_NETWORK_DREP = 0x00000000 - - # InitializeSecurityContext Requirement flags - ISC_REQ_REPLAY_DETECT = 0x00000004 - ISC_REQ_SEQUENCE_DETECT = 0x00000008 - ISC_REQ_CONFIDENTIALITY = 0x00000010 - ISC_REQ_USE_SESSION_KEY = 0x00000020 - ISC_REQ_PROMPT_FOR_CREDS = 0x00000040 - ISC_REQ_CONNECTION = 0x00000800 - - # Win32 API Functions. Uses Win32API to bind methods to constants contained in class. - module API - # Can be called with AcquireCredentialsHandle.call() - AcquireCredentialsHandle = Win32API.new("secur32", "AcquireCredentialsHandle", 'ppLpppppp', 'L') - # Can be called with InitializeSecurityContext.call() - InitializeSecurityContext = Win32API.new("secur32", "InitializeSecurityContext", 'pppLLLpLpppp', 'L') - # Can be called with DeleteSecurityContext.call() - DeleteSecurityContext = Win32API.new("secur32", "DeleteSecurityContext", 'P', 'L') - # Can be called with FreeCredentialsHandle.call() - FreeCredentialsHandle = Win32API.new("secur32", "FreeCredentialsHandle", 'P', 'L') - end - - # SecHandle struct - class SecurityHandle - def upper - @struct.unpack("LL")[1] - end - - def lower - @struct.unpack("LL")[0] - end - - def to_p - @struct ||= "\0" * 8 - end - end - - # Some familiar aliases for the SecHandle structure - CredHandle = CtxtHandle = SecurityHandle - - # TimeStamp struct - class TimeStamp - attr_reader :struct - - def to_p - @struct ||= "\0" * 8 - end - end - - # Creates binary representaiton of a SecBufferDesc structure, - # including the SecBuffer contained inside. - class SecurityBuffer - - SECBUFFER_TOKEN = 2 # Security token - - TOKENBUFSIZE = 12288 - SECBUFFER_VERSION = 0 - - def initialize(buffer = nil) - @buffer = buffer || "\0" * TOKENBUFSIZE - @bufferSize = @buffer.length - @type = SECBUFFER_TOKEN - end - - def bufferSize - unpack - @bufferSize - end - - def bufferType - unpack - @type - end - - def token - unpack - @buffer - end - - def to_p - # Assumption is that when to_p is called we are going to get a packed structure. Therefore, - # set @unpacked back to nil so we know to unpack when accessors are next accessed. - @unpacked = nil - # Assignment of inner structure to variable is very important here. Without it, - # will not be able to unpack changes to the structure. Alternative, nested unpacks, - # does not work (i.e. @struct.unpack("LLP12")[2].unpack("LLP12") results in "no associated pointer") - @sec_buffer ||= [@bufferSize, @type, @buffer].pack("LLP") - @struct ||= [SECBUFFER_VERSION, 1, @sec_buffer].pack("LLP") - end - - private - - # Unpacks the SecurityBufferDesc structure into member variables. We - # only want to do this once per struct, so the struct is deleted - # after unpacking. - def unpack - if ! @unpacked && @sec_buffer && @struct - @bufferSize, @type = @sec_buffer.unpack("LL") - @buffer = @sec_buffer.unpack("LLP#{@bufferSize}")[2] - @struct = nil - @sec_buffer = nil - @unpacked = true - end - end - end - - # SEC_WINNT_AUTH_IDENTITY structure - class Identity - SEC_WINNT_AUTH_IDENTITY_ANSI = 0x1 - - attr_accessor :user, :domain, :password - - def initialize(user = nil, domain = nil, password = nil) - @user = user - @domain = domain - @password = password - @flags = SEC_WINNT_AUTH_IDENTITY_ANSI - end - - def to_p - [@user, @user ? @user.length : 0, - @domain, @domain ? @domain.length : 0, - @password, @password ? @password.length : 0, - @flags].pack("PLPLPLL") - end - end - - # Takes a return result from an SSPI function and interprets the value. - class SSPIResult - # Good results - SEC_E_OK = 0x00000000 - SEC_I_CONTINUE_NEEDED = 0x00090312 - - # These are generally returned by InitializeSecurityContext - SEC_E_INSUFFICIENT_MEMORY = 0x80090300 - SEC_E_INTERNAL_ERROR = 0x80090304 - SEC_E_INVALID_HANDLE = 0x80090301 - SEC_E_INVALID_TOKEN = 0x80090308 - SEC_E_LOGON_DENIED = 0x8009030C - SEC_E_NO_AUTHENTICATING_AUTHORITY = 0x80090311 - SEC_E_NO_CREDENTIALS = 0x8009030E - SEC_E_TARGET_UNKNOWN = 0x80090303 - SEC_E_UNSUPPORTED_FUNCTION = 0x80090302 - SEC_E_WRONG_PRINCIPAL = 0x80090322 - - # These are generally returned by AcquireCredentialsHandle - SEC_E_NOT_OWNER = 0x80090306 - SEC_E_SECPKG_NOT_FOUND = 0x80090305 - SEC_E_UNKNOWN_CREDENTIALS = 0x8009030D - - @@map = {} - constants.each { |v| @@map[self.const_get(v.to_s)] = v } - - attr_reader :value - - def initialize(value) - # convert to unsigned long - value = [value].pack("L").unpack("L").first - raise "#{value.to_s(16)} is not a recognized result" unless @@map.has_key? value - @value = value - end - - def to_s - @@map[@value].to_s - end - - def ok? - @value == SEC_I_CONTINUE_NEEDED || @value == SEC_E_OK - end - - def ==(other) - if other.is_a?(SSPIResult) - @value == other.value - elsif other.is_a?(Fixnum) - @value == @@map[other] - else - false - end - end - end - - # Handles "Negotiate" type authentication. Geared towards authenticating with a proxy server over HTTP - class NegotiateAuth - attr_accessor :credentials, :context, :contextAttributes, :user, :domain - - # Default request flags for SSPI functions - REQUEST_FLAGS = ISC_REQ_CONFIDENTIALITY | ISC_REQ_REPLAY_DETECT | ISC_REQ_CONNECTION - - # NTLM tokens start with this header always. Encoding alone adds "==" and newline, so remove those - B64_TOKEN_PREFIX = Base64.encode64("NTLMSSP").delete("=\n") - - # Given a connection and a request path, performs authentication as the current user and returns - # the response from a GET request. The connnection should be a Net::HTTP object, and it should - # have been constructed using the Net::HTTP.Proxy method, but anything that responds to "get" will work. - # If a user and domain are given, will authenticate as the given user. - # Returns the response received from the get method (usually Net::HTTPResponse) - def NegotiateAuth.proxy_auth_get(http, path, user = nil, domain = nil) - raise "http must respond to :get" unless http.respond_to?(:get) - nego_auth = self.new user, domain - - resp = http.get path, { "Proxy-Authorization" => "Negotiate " + nego_auth.get_initial_token } - if resp["Proxy-Authenticate"] - resp = http.get path, { "Proxy-Authorization" => "Negotiate " + nego_auth.complete_authentication(resp["Proxy-Authenticate"].split(" ").last.strip) } - end - - resp - end - - # Creates a new instance ready for authentication as the given user in the given domain. - # Defaults to current user and domain as defined by ENV["USERDOMAIN"] and ENV["USERNAME"] if - # no arguments are supplied. - def initialize(user = nil, domain = nil) - if user.nil? && domain.nil? && ENV["USERNAME"].nil? && ENV["USERDOMAIN"].nil? - raise "A username or domain must be supplied since they cannot be retrieved from the environment" - end - - @user = user || ENV["USERNAME"] - @domain = domain || ENV["USERDOMAIN"] - end - - # Gets the initial Negotiate token. Returns it as a base64 encoded string suitable for use in HTTP. Can - # be easily decoded, however. - def get_initial_token - raise "This object is no longer usable because its resources have been freed." if @cleaned_up - get_credentials - - outputBuffer = SecurityBuffer.new - @context = CtxtHandle.new - @contextAttributes = "\0" * 4 - - result = SSPIResult.new(API::InitializeSecurityContext.call(@credentials.to_p, nil, nil, - REQUEST_FLAGS,0, SECURITY_NETWORK_DREP, nil, 0, @context.to_p, outputBuffer.to_p, @contextAttributes, TimeStamp.new.to_p)) - - if result.ok? then - return encode_token(outputBuffer.token) - else - raise "Error: #{result.to_s}" - end - end - - # Takes a token and gets the next token in the Negotiate authentication chain. Token can be Base64 encoded or not. - # The token can include the "Negotiate" header and it will be stripped. - # Does not indicate if SEC_I_CONTINUE or SEC_E_OK was returned. - # Token returned is Base64 encoded w/ all new lines removed. - def complete_authentication(token) - raise "This object is no longer usable because its resources have been freed." if @cleaned_up - - # Nil token OK, just set it to empty string - token = "" if token.nil? - - if token.include? "Negotiate" - # If the Negotiate prefix is passed in, assume we are seeing "Negotiate " and get the token. - token = token.split(" ").last - end - - if token.include? B64_TOKEN_PREFIX - # indicates base64 encoded token - token = Base64.decode64(token.strip) - end - - outputBuffer = SecurityBuffer.new - result = SSPIResult.new(API::InitializeSecurityContext.call(@credentials.to_p, @context.to_p, nil, - REQUEST_FLAGS, 0, SECURITY_NETWORK_DREP, SecurityBuffer.new(token).to_p, 0, - @context.to_p, - outputBuffer.to_p, @contextAttributes, TimeStamp.new.to_p)) - - if result.ok? then - return encode_token(outputBuffer.token) - else - raise "Error: #{result.to_s}" - end - ensure - # need to make sure we don't clean up if we've already cleaned up. - clean_up unless @cleaned_up - end - - private - - def clean_up - # free structures allocated - @cleaned_up = true - API::FreeCredentialsHandle.call(@credentials.to_p) - API::DeleteSecurityContext.call(@context.to_p) - @context = nil - @credentials = nil - @contextAttributes = nil - end - - # Gets credentials based on user, domain or both. If both are nil, an error occurs - def get_credentials - @credentials = CredHandle.new - ts = TimeStamp.new - @identity = Identity.new @user, @domain - result = SSPIResult.new(API::AcquireCredentialsHandle.call(nil, "Negotiate", SECPKG_CRED_OUTBOUND, nil, @identity.to_p, - nil, nil, @credentials.to_p, ts.to_p)) - raise "Error acquire credentials: #{result}" unless result.ok? - end - - def encode_token(t) - # encode64 will add newlines every 60 characters so we need to remove those. - Base64.encode64(t).delete("\n") - end - end - end -end \ No newline at end of file diff --git a/ext/Win32API/point.rb b/ext/Win32API/point.rb deleted file mode 100644 index 60e265f3ee..0000000000 --- a/ext/Win32API/point.rb +++ /dev/null @@ -1,18 +0,0 @@ -require 'Win32API' - -getCursorPos = Win32API.new("user32", "GetCursorPos", ['P'], 'V') - -lpPoint = " " * 8 # store two LONGs -getCursorPos.Call(lpPoint) -x, y = lpPoint.unpack("LL") # get the actual values - -print "x: ", x, "\n" -print "y: ", y, "\n" - -ods = Win32API.new("kernel32", "OutputDebugString", ['P'], 'V') -ods.Call("Hello, World\n"); - -GetDesktopWindow = Win32API.new("user32", "GetDesktopWindow", [], 'L') -GetActiveWindow = Win32API.new("user32", "GetActiveWindow", [], 'L') -SendMessage = Win32API.new("user32", "SendMessage", ['L'] * 4, 'L') -SendMessage.Call GetDesktopWindow.Call, 274, 0xf140, 0 diff --git a/ext/dl/win32/extconf.rb b/ext/dl/win32/extconf.rb new file mode 100644 index 0000000000..a72ca49c06 --- /dev/null +++ b/ext/dl/win32/extconf.rb @@ -0,0 +1,3 @@ +if compiled?('dl') and $mswin||$bccwin||$mingw||$cygwin + create_makefile('win32') +end diff --git a/ext/dl/win32/lib/Win32API.rb b/ext/dl/win32/lib/Win32API.rb new file mode 100644 index 0000000000..b642e0c8fe --- /dev/null +++ b/ext/dl/win32/lib/Win32API.rb @@ -0,0 +1,28 @@ +# -*- ruby -*- +# for backward compatibility +warn "Warning:#{caller[0].sub(/:in `.*'\z/, '')}: Win32API is deprecated after Ruby 1.9.1; use dl directly instead" if $VERBOSE + +require 'dl' + +class Win32API + DLL = {} + TYPEMAP = {"0" => DL::TYPE_VOID, "S" => DL::TYPE_VOIDP, "I" => DL::TYPE_LONG} + + def initialize(dllname, func, import, export = "0") + @proto = [import].join.tr("VPpNnLlIi", "0SSI").sub(/^(.)0*$/, '\1') + handle = DLL[dllname] ||= DL.dlopen(dllname) + @func = DL::CFunc.new(handle[func], TYPEMAP[export.tr("VPpNnLlIi", "0SSI")], func) + end + + def call(*args) + import = @proto.split("") + args.each_with_index do |x, i| + args[i], = [x == 0 ? nil : x].pack("p").unpack("l!*") if import[i] == "S" + args[i], = [x].pack("I").unpack("i") if import[i] == "I" + end + ret, = @func.call(args) + return ret || 0 + end + + alias Call call +end diff --git a/ext/dl/win32/lib/win32/registry.rb b/ext/dl/win32/lib/win32/registry.rb new file mode 100644 index 0000000000..2671551a33 --- /dev/null +++ b/ext/dl/win32/lib/win32/registry.rb @@ -0,0 +1,831 @@ +=begin += Win32 Registry I/F +win32/registry is registry accessor library for Win32 platform. +It uses Win32API to call Win32 Registry APIs. + +== example + Win32::Registry::HKEY_CURRENT_USER.open('SOFTWARE\foo') do |reg| + value = reg['foo'] # read a value + value = reg['foo', Win32::Registry::REG_SZ] # read a value with type + type, value = reg.read('foo') # read a value + reg['foo'] = 'bar' # write a value + reg['foo', Win32::Registry::REG_SZ] = 'bar' # write a value with type + reg.write('foo', Win32::Registry::REG_SZ, 'bar') # write a value + + reg.each_value { |name, type, data| ... } # Enumerate values + reg.each_key { |key, wtime| ... } # Enumerate subkeys + + reg.delete_value(name) # Delete a value + reg.delete_key(name) # Delete a subkey + reg.delete_key(name, true) # Delete a subkey recursively + end + += Reference + +== Win32::Registry class + +=== including modules + +* Enumerable +* Registry::Constants + +=== class methods +--- Registry.open(key, subkey, desired = KEY_READ, opt = REG_OPTION_RESERVED) +--- Registry.open(key, subkey, desired = KEY_READ, opt = REG_OPTION_RESERVED) { |reg| ... } + Open the registry key ((|subkey|)) under ((|key|)). + ((|key|)) is Win32::Registry object of parent key. + You can use predefined key HKEY_* (see (())) + + ((|desired|)) and ((|opt|)) is access mask and key option. + For detail, see (()). + + If block is given, the key is closed automatically. + +--- Registry.create(key, subkey, desired = KEY_ALL_ACCESS, opt = REG_OPTION_RESERVED) +--- Registry.create(key, subkey, desired = KEY_ALL_ACCESS, opt = REG_OPTION_RESERVED) { |reg| ... } + Create or open the registry key ((|subkey|)) under ((|key|)). + You can use predefined key HKEY_* (see (())) + + If subkey is already exists, key is opened and Registry#(()) + method will return false. + + If block is given, the key is closed automatically. + +--- Registry.expand_environ(str) + Replace (({%\w+%})) into the environment value of ((|str|)). + This method is used for REG_EXPAND_SZ. + + For detail, see (()) Win32 API. + +--- Registry.type2name(type) + Convert registry type value to readable string. + +--- Registry.wtime2time(wtime) + Convert 64-bit FILETIME integer into Time object. + +--- Registry.time2wtime(time) + Convert Time object or Integer object into 64-bit FILETIME. + +=== instance methods +--- open(subkey, desired = KEY_READ, opt = REG_OPTION_RESERVED) + Same as (({Win32::(())(self, subkey, desired, opt)})) + +--- create(subkey, desired = KEY_ALL_ACCESS, opt = REG_OPTION_RESERVED) + Same as (({Win32::(())(self, subkey, desired, opt)})) + +--- close + Close key. + + After closed, most method raises error. + +--- read(name, *rtype) + Read a registry value named ((|name|)) and return array of + [ ((|type|)), ((|data|)) ]. + When name is nil, the `default' value is read. + + ((|type|)) is value type. (see (())) + ((|data|)) is value data, its class is: + :REG_SZ, REG_EXPAND_SZ + String + :REG_MULTI_SZ + Array of String + :REG_DWORD, REG_DWORD_BIG_ENDIAN, REG_QWORD + Integer + :REG_BINARY + String (contains binary data) + + When ((|rtype|)) is specified, the value type must be included by + ((|rtype|)) array, or TypeError is raised. + +--- self[name, *rtype] + Read a registry value named ((|name|)) and return its value data. + The class of value is same as (()) method returns. + + If the value type is REG_EXPAND_SZ, returns value data whose environment + variables are replaced. + If the value type is neither REG_SZ, REG_MULTI_SZ, REG_DWORD, + REG_DWORD_BIG_ENDIAN, nor REG_QWORD, TypeError is raised. + + The meaning of ((|rtype|)) is same as (()) method. + +--- read_s(name) +--- read_i(name) +--- read_bin(name) + Read a REG_SZ(read_s), REG_DWORD(read_i), or REG_BINARY(read_bin) + registry value named ((|name|)). + + If the values type does not match, TypeError is raised. + +--- read_s_expand(name) + Read a REG_SZ or REG_EXPAND_SZ registry value named ((|name|)). + + If the value type is REG_EXPAND_SZ, environment variables are replaced. + Unless the value type is REG_SZ or REG_EXPAND_SZ, TypeError is raised. + +--- write(name, type, data) + Write ((|data|)) to a registry value named ((|name|)). + When name is nil, write to the `default' value. + + ((|type|)) is type value. (see (())) + Class of ((|data|)) must be same as which (()) + method returns. + +--- self[name, wtype = nil] = value + Write ((|value|)) to a registry value named ((|name|)). + + If ((|wtype|)) is specified, the value type is it. + Otherwise, the value type is depend on class of ((|value|)): + :Integer + REG_DWORD + :String + REG_SZ + :Array + REG_MULTI_SZ + +--- write_s(name, value) +--- write_i(name, value) +--- write_bin(name, value) + Write ((|value|)) to a registry value named ((|name|)). + + The value type is REG_SZ(write_s), REG_DWORD(write_i), or + REG_BINARY(write_bin). + +--- each { |name, type, value| ... } +--- each_value { |name, type, value| ... } + Enumerate values. + +--- each_key { |subkey, wtime| ... } + Enumerate subkeys. + + ((|subkey|)) is String which contains name of subkey. + ((|wtime|)) is last write time as FILETIME (64-bit integer). + (see (())) + +--- delete(name) +--- delete_value(name) + Delete a registry value named ((|name|)). + We can not delete the `default' value. + +--- delete_key(name, recursive = false) + Delete a subkey named ((|name|)) and all its values. + + If ((|recursive|)) is false, the subkey must not have subkeys. + Otherwise, this method deletes all subkeys and values recursively. + +--- flush + Write all the attributes into the registry file. + +--- created? + Returns if key is created ((*newly*)). + (see (())) + +--- open? + Returns if key is not closed. + +--- hkey + Returns key handle value. + +--- parent + Win32::Registry object of parent key, or nil if predefeined key. + +--- keyname + Same as ((|subkey|)) value of (()) or + (()) method. + +--- disposition + Disposition value (REG_CREATED_NEW_KEY or REG_OPENED_EXISTING_KEY). + +--- name +--- to_s + Full path of key such as (({'HKEY_CURRENT_USER\SOFTWARE\foo\bar'})). + +--- info + Returns key information as Array of: + :num_keys + The number of subkeys. + :max_key_length + Maximum length of name of subkeys. + :num_values + The number of values. + :max_value_name_length + Maximum length of name of values. + :max_value_length + Maximum length of value of values. + :descriptor_length + Length of security descriptor. + :wtime + Last write time as FILETIME(64-bit integer) + + For detail, see (()) Win32 API. + +--- num_keys +--- max_key_length +--- num_values +--- max_value_name_length +--- max_value_length +--- descriptor_length +--- wtime + Returns an item of key information. + +=== constants +--- HKEY_CLASSES_ROOT +--- HKEY_CURRENT_USER +--- HKEY_LOCAL_MACHINE +--- HKEY_PERFORMANCE_DATA +--- HKEY_CURRENT_CONFIG +--- HKEY_DYN_DATA + Win32::Registry object whose key is predefined key. + For detail, see (()). + +== Win32::Registry::Constants module + +For detail, see (()). + +--- HKEY_* + Predefined key ((*handle*)). + These are Integer, not Win32::Registry. + +--- REG_* + Registry value type. + +--- KEY_* + Security access mask. + +--- KEY_OPTIONS_* + Key options. + +--- REG_CREATED_NEW_KEY +--- REG_OPENED_EXISTING_KEY + If the key is created newly or opened existing key. + See also Registry#(()) method. + +=end + +require 'Win32API' + +module Win32 + class Registry + module Constants + HKEY_CLASSES_ROOT = 0x80000000 + HKEY_CURRENT_USER = 0x80000001 + HKEY_LOCAL_MACHINE = 0x80000002 + HKEY_USERS = 0x80000003 + HKEY_PERFORMANCE_DATA = 0x80000004 + HKEY_PERFORMANCE_TEXT = 0x80000050 + HKEY_PERFORMANCE_NLSTEXT = 0x80000060 + HKEY_CURRENT_CONFIG = 0x80000005 + HKEY_DYN_DATA = 0x80000006 + + REG_NONE = 0 + REG_SZ = 1 + REG_EXPAND_SZ = 2 + REG_BINARY = 3 + REG_DWORD = 4 + REG_DWORD_LITTLE_ENDIAN = 4 + REG_DWORD_BIG_ENDIAN = 5 + REG_LINK = 6 + REG_MULTI_SZ = 7 + REG_RESOURCE_LIST = 8 + REG_FULL_RESOURCE_DESCRIPTOR = 9 + REG_RESOURCE_REQUIREMENTS_LIST = 10 + REG_QWORD = 11 + REG_QWORD_LITTLE_ENDIAN = 11 + + STANDARD_RIGHTS_READ = 0x00020000 + STANDARD_RIGHTS_WRITE = 0x00020000 + KEY_QUERY_VALUE = 0x0001 + KEY_SET_VALUE = 0x0002 + KEY_CREATE_SUB_KEY = 0x0004 + KEY_ENUMERATE_SUB_KEYS = 0x0008 + KEY_NOTIFY = 0x0010 + KEY_CREATE_LINK = 0x0020 + KEY_READ = STANDARD_RIGHTS_READ | + KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS | KEY_NOTIFY + KEY_WRITE = STANDARD_RIGHTS_WRITE | + KEY_SET_VALUE | KEY_CREATE_SUB_KEY + KEY_EXECUTE = KEY_READ + KEY_ALL_ACCESS = KEY_READ | KEY_WRITE | KEY_CREATE_LINK + + REG_OPTION_RESERVED = 0x0000 + REG_OPTION_NON_VOLATILE = 0x0000 + REG_OPTION_VOLATILE = 0x0001 + REG_OPTION_CREATE_LINK = 0x0002 + REG_OPTION_BACKUP_RESTORE = 0x0004 + REG_OPTION_OPEN_LINK = 0x0008 + REG_LEGAL_OPTION = REG_OPTION_RESERVED | + REG_OPTION_NON_VOLATILE | REG_OPTION_CREATE_LINK | + REG_OPTION_BACKUP_RESTORE | REG_OPTION_OPEN_LINK + + REG_CREATED_NEW_KEY = 1 + REG_OPENED_EXISTING_KEY = 2 + + REG_WHOLE_HIVE_VOLATILE = 0x0001 + REG_REFRESH_HIVE = 0x0002 + REG_NO_LAZY_FLUSH = 0x0004 + REG_FORCE_RESTORE = 0x0008 + + MAX_KEY_LENGTH = 514 + MAX_VALUE_LENGTH = 32768 + end + include Constants + include Enumerable + + # + # Error + # + class Error < ::StandardError + FormatMessageA = Win32API.new('kernel32.dll', 'FormatMessageA', 'LPLLPLP', 'L') + def initialize(code) + @code = code + msg = "\0" * 1024 + len = FormatMessageA.call(0x1200, 0, code, 0, msg, 1024, 0) + super msg[0, len].tr("\r", '').chomp + end + attr_reader :code + end + + # + # Predefined Keys + # + class PredefinedKey < Registry + def initialize(hkey, keyname) + @hkey = hkey + @parent = nil + @keyname = keyname + @disposition = REG_OPENED_EXISTING_KEY + end + + # Predefined keys cannot be closed + def close + raise Error.new(5) ## ERROR_ACCESS_DENIED + end + + # Fake class for Registry#open, Registry#create + def class + Registry + end + + # Make all + Constants.constants.grep(/^HKEY_/) do |c| + Registry.const_set c, new(Constants.const_get(c), c) + end + end + + # + # Win32 APIs + # + module API + [ + %w/RegOpenKeyExA LPLLP L/, + %w/RegCreateKeyExA LPLLLLPPP L/, + %w/RegEnumValueA LLPPPPPP L/, + %w/RegEnumKeyExA LLPPLLLP L/, + %w/RegQueryValueExA LPLPPP L/, + %w/RegSetValueExA LPLLPL L/, + %w/RegDeleteValue LP L/, + %w/RegDeleteKey LP L/, + %w/RegFlushKey L L/, + %w/RegCloseKey L L/, + %w/RegQueryInfoKey LPPPPPPPPPPP L/, + ].each do |fn| + const_set fn[0].intern, Win32API.new('advapi32.dll', *fn) + end + + module_function + + def check(result) + raise Error, result, caller(2) if result != 0 + end + + def packdw(dw) + [dw].pack('V') + end + + def unpackdw(dw) + dw += [0].pack('V') + dw.unpack('V')[0] + end + + def packqw(qw) + [ qw & 0xFFFFFFFF, qw >> 32 ].pack('VV') + end + + def unpackqw(qw) + qw = qw.unpack('VV') + (qw[1] << 32) | qw[0] + end + + def OpenKey(hkey, name, opt, desired) + result = packdw(0) + check RegOpenKeyExA.call(hkey, name, opt, desired, result) + unpackdw(result) + end + + def CreateKey(hkey, name, opt, desired) + result = packdw(0) + disp = packdw(0) + check RegCreateKeyExA.call(hkey, name, 0, 0, opt, desired, + 0, result, disp) + [ unpackdw(result), unpackdw(disp) ] + end + + def EnumValue(hkey, index) + name = ' ' * Constants::MAX_KEY_LENGTH + size = packdw(Constants::MAX_KEY_LENGTH) + check RegEnumValueA.call(hkey, index, name, size, 0, 0, 0, 0) + name[0, unpackdw(size)] + end + + def EnumKey(hkey, index) + name = ' ' * Constants::MAX_KEY_LENGTH + size = packdw(Constants::MAX_KEY_LENGTH) + wtime = ' ' * 8 + check RegEnumKeyExA.call(hkey, index, name, size, 0, 0, 0, wtime) + [ name[0, unpackdw(size)], unpackqw(wtime) ] + end + + def QueryValue(hkey, name) + type = packdw(0) + size = packdw(0) + check RegQueryValueExA.call(hkey, name, 0, type, 0, size) + data = ' ' * unpackdw(size) + check RegQueryValueExA.call(hkey, name, 0, type, data, size) + [ unpackdw(type), data[0, unpackdw(size)] ] + end + + def SetValue(hkey, name, type, data, size) + check RegSetValueExA.call(hkey, name, 0, type, data, size) + end + + def DeleteValue(hkey, name) + check RegDeleteValue.call(hkey, name) + end + + def DeleteKey(hkey, name) + check RegDeleteKey.call(hkey, name) + end + + def FlushKey(hkey) + check RegFlushKey.call(hkey) + end + + def CloseKey(hkey) + check RegCloseKey.call(hkey) + end + + def QueryInfoKey(hkey) + subkeys = packdw(0) + maxsubkeylen = packdw(0) + values = packdw(0) + maxvaluenamelen = packdw(0) + maxvaluelen = packdw(0) + secdescs = packdw(0) + wtime = ' ' * 8 + check RegQueryInfoKey.call(hkey, 0, 0, 0, subkeys, maxsubkeylen, 0, + values, maxvaluenamelen, maxvaluelen, secdescs, wtime) + [ unpackdw(subkeys), unpackdw(maxsubkeylen), unpackdw(values), + unpackdw(maxvaluenamelen), unpackdw(maxvaluelen), + unpackdw(secdescs), unpackqw(wtime) ] + end + end + + # + # utility functions + # + def self.expand_environ(str) + str.gsub(/%([^%]+)%/) { ENV[$1] || $& } + end + + @@type2name = { } + %w[ + REG_NONE REG_SZ REG_EXPAND_SZ REG_BINARY REG_DWORD + REG_DWORD_BIG_ENDIAN REG_LINK REG_MULTI_SZ + REG_RESOURCE_LIST REG_FULL_RESOURCE_DESCRIPTOR + REG_RESOURCE_REQUIREMENTS_LIST REG_QWORD + ].each do |type| + @@type2name[Constants.const_get(type)] = type + end + + def self.type2name(type) + @@type2name[type] || type.to_s + end + + def self.wtime2time(wtime) + Time.at((wtime - 116444736000000000) / 10000000) + end + + def self.time2wtime(time) + time.to_i * 10000000 + 116444736000000000 + end + + # + # constructors + # + private_class_method :new + + def self.open(hkey, subkey, desired = KEY_READ, opt = REG_OPTION_RESERVED) + subkey = subkey.chomp('\\') + newkey = API.OpenKey(hkey.hkey, subkey, opt, desired) + obj = new(newkey, hkey, subkey, REG_OPENED_EXISTING_KEY) + if block_given? + begin + yield obj + ensure + obj.close + end + else + obj + end + end + + def self.create(hkey, subkey, desired = KEY_ALL_ACCESS, opt = REG_OPTION_RESERVED) + newkey, disp = API.CreateKey(hkey.hkey, subkey, opt, desired) + obj = new(newkey, hkey, subkey, disp) + if block_given? + begin + yield obj + ensure + obj.close + end + else + obj + end + end + + # + # finalizer + # + @@final = proc { |hkey| proc { API.CloseKey(hkey[0]) if hkey[0] } } + + # + # initialize + # + def initialize(hkey, parent, keyname, disposition) + @hkey = hkey + @parent = parent + @keyname = keyname + @disposition = disposition + @hkeyfinal = [ hkey ] + ObjectSpace.define_finalizer self, @@final.call(@hkeyfinal) + end + attr_reader :hkey, :parent, :keyname, :disposition + + # + # attributes + # + def created? + @disposition == REG_CREATED_NEW_KEY + end + + def open? + !@hkey.nil? + end + + def name + parent = self + name = @keyname + while parent = parent.parent + name = parent.keyname + '\\' + name + end + name + end + + def inspect + "\#" + end + + # + # marshalling + # + def _dump(depth) + raise TypeError, "can't dump Win32::Registry" + end + + # + # open/close + # + def open(subkey, desired = KEY_READ, opt = REG_OPTION_RESERVED, &blk) + self.class.open(self, subkey, desired, opt, &blk) + end + + def create(subkey, desired = KEY_ALL_ACCESS, opt = REG_OPTION_RESERVED, &blk) + self.class.create(self, subkey, desired, opt, &blk) + end + + def close + API.CloseKey(@hkey) + @hkey = @parent = @keyname = nil + @hkeyfinal[0] = nil + end + + # + # iterator + # + def each_value + index = 0 + while true + begin + subkey = API.EnumValue(@hkey, index) + rescue Error + break + end + begin + type, data = read(subkey) + rescue Error + next + end + yield subkey, type, data + index += 1 + end + index + end + alias each each_value + + def each_key + index = 0 + while true + begin + subkey, wtime = API.EnumKey(@hkey, index) + rescue Error + break + end + yield subkey, wtime + index += 1 + end + index + end + + def keys + keys_ary = [] + each_key { |key,| keys_ary << key } + keys_ary + end + + # + # reader + # + def read(name, *rtype) + type, data = API.QueryValue(@hkey, name) + unless rtype.empty? or rtype.include?(type) + raise TypeError, "Type mismatch (expect #{rtype.inspect} but #{type} present)" + end + case type + when REG_SZ, REG_EXPAND_SZ + [ type, data.chop ] + when REG_MULTI_SZ + [ type, data.split(/\0/) ] + when REG_BINARY + [ type, data ] + when REG_DWORD + [ type, API.unpackdw(data) ] + when REG_DWORD_BIG_ENDIAN + [ type, data.unpack('N')[0] ] + when REG_QWORD + [ type, API.unpackqw(data) ] + else + raise TypeError, "Type #{type} is not supported." + end + end + + def [](name, *rtype) + type, data = read(name, *rtype) + case type + when REG_SZ, REG_DWORD, REG_QWORD, REG_MULTI_SZ + data + when REG_EXPAND_SZ + Registry.expand_environ(data) + else + raise TypeError, "Type #{type} is not supported." + end + end + + def read_s(name) + read(name, REG_SZ)[1] + end + + def read_s_expand(name) + type, data = read(name, REG_SZ, REG_EXPAND_SZ) + if type == REG_EXPAND_SZ + Registry.expand_environ(data) + else + data + end + end + + def read_i(name) + read(name, REG_DWORD, REG_DWORD_BIG_ENDIAN, REG_QWORD)[1] + end + + def read_bin(name) + read(name, REG_BINARY)[1] + end + + # + # writer + # + def write(name, type, data) + case type + when REG_SZ, REG_EXPAND_SZ + data = data.to_s + "\0" + when REG_MULTI_SZ + data = data.to_a.join("\0") + "\0\0" + when REG_BINARY + data = data.to_s + when REG_DWORD + data = API.packdw(data.to_i) + when REG_DWORD_BIG_ENDIAN + data = [data.to_i].pack('N') + when REG_QWORD + data = API.packqw(data.to_i) + else + raise TypeError, "Unsupported type #{type}" + end + API.SetValue(@hkey, name, type, data, data.length) + end + + def []=(name, rtype, value = nil) + if value + write name, rtype, value + else + case value = rtype + when Integer + write name, REG_DWORD, value + when String + write name, REG_SZ, value + when Array + write name, REG_MULTI_SZ, value + else + raise TypeError, "Unexpected type #{value.class}" + end + end + value + end + + def write_s(name, value) + write name, REG_SZ, value.to_s + end + + def write_i(name, value) + write name, REG_DWORD, value.to_i + end + + def write_bin(name, value) + write name, REG_BINARY, value.to_s + end + + # + # delete + # + def delete_value(name) + API.DeleteValue(@hkey, name) + end + alias delete delete_value + + def delete_key(name, recursive = false) + if recursive + open(name, KEY_ALL_ACCESS) do |reg| + reg.keys.each do |key| + begin + reg.delete_key(key, true) + rescue Error + # + end + end + end + API.DeleteKey(@hkey, name) + else + begin + API.EnumKey @hkey, 0 + rescue Error + return API.DeleteKey(@hkey, name) + end + raise Error.new(5) ## ERROR_ACCESS_DENIED + end + end + + # + # flush + # + def flush + API.FlushKey @hkey + end + + # + # key information + # + def info + API.QueryInfoKey(@hkey) + end + %w[ + num_keys max_key_length + num_values max_value_name_length max_value_length + descriptor_length wtime + ].each_with_index do |s, i| + eval <<-__END__ + def #{s} + info[#{i}] + end + __END__ + end + end +end diff --git a/ext/dl/win32/lib/win32/resolv.rb b/ext/dl/win32/lib/win32/resolv.rb new file mode 100644 index 0000000000..6534d20760 --- /dev/null +++ b/ext/dl/win32/lib/win32/resolv.rb @@ -0,0 +1,366 @@ +=begin += Win32 DNS and DHCP I/F + +=end + +require 'win32/registry' + +module Win32 + module Resolv + API = Registry::API + + def self.get_hosts_path + path = get_hosts_dir + path = File.join(path.gsub(/\\/, File::SEPARATOR), 'hosts') + File.exist?(path) ? path : nil + end + + def self.get_resolv_info + search, nameserver = get_info + if search.empty? + search = nil + else + search.delete("") + search.uniq! + end + if nameserver.empty? + nameserver = nil + else + nameserver.delete("") + nameserver.delete("0.0.0.0") + nameserver.uniq! + end + [ search, nameserver ] + end + +getv = Win32API.new('kernel32.dll', 'GetVersionExA', 'P', 'L') +info = [ 148, 0, 0, 0, 0 ].pack('V5') + "\0" * 128 +getv.call(info) +if info.unpack('V5')[4] == 2 # VER_PLATFORM_WIN32_NT +#==================================================================== +# Windows NT +#==================================================================== + module_eval <<-'__EOS__', __FILE__, __LINE__+1 + TCPIP_NT = 'SYSTEM\CurrentControlSet\Services\Tcpip\Parameters' + + class << self + private + def get_hosts_dir + Registry::HKEY_LOCAL_MACHINE.open(TCPIP_NT) do |reg| + reg.read_s_expand('DataBasePath') + end + end + + def get_info + search = nil + nameserver = [] + Registry::HKEY_LOCAL_MACHINE.open(TCPIP_NT) do |reg| + begin + slist = reg.read_s('SearchList') + search = slist.split(/,\s*/) unless slist.empty? + rescue Registry::Error + end + + if add_search = search.nil? + search = [] + begin + nvdom = reg.read_s('NV Domain') + unless nvdom.empty? + @search = [ nvdom ] + if reg.read_i('UseDomainNameDevolution') != 0 + if /^[\w\d]+\./ =~ nvdom + devo = $' + end + end + end + rescue Registry::Error + end + end + + reg.open('Interfaces') do |reg| + reg.each_key do |iface,| + reg.open(iface) do |regif| + begin + [ 'NameServer', 'DhcpNameServer' ].each do |key| + ns = regif.read_s(key) + unless ns.empty? + nameserver.concat(ns.split(/[,\s]\s*/)) + break + end + end + rescue Registry::Error + end + + if add_search + begin + [ 'Domain', 'DhcpDomain' ].each do |key| + dom = regif.read_s(key) + unless dom.empty? + search.concat(dom.split(/,\s*/)) + break + end + end + rescue Registry::Error + end + end + end + end + end + search << devo if add_search and devo + end + [ search.uniq, nameserver.uniq ] + end + end + __EOS__ +else +#==================================================================== +# Windows 9x +#==================================================================== + module_eval <<-'__EOS__', __FILE__, __LINE__+1 + TCPIP_9X = 'SYSTEM\CurrentControlSet\Services\VxD\MSTCP' + DHCP_9X = 'SYSTEM\CurrentControlSet\Services\VxD\DHCP' + WINDOWS = 'Software\Microsoft\Windows\CurrentVersion' + + class << self + # private + + def get_hosts_dir + Registry::HKEY_LOCAL_MACHINE.open(WINDOWS) do |reg| + reg.read_s_expand('SystemRoot') + end + end + + def get_info + search = [] + nameserver = [] + begin + Registry::HKEY_LOCAL_MACHINE.open(TCPIP_9X) do |reg| + if reg.read_s("EnableDNS") == "1" + domain = reg.read_s("Domain") + ns = reg.read_s("NameServer") + slist = reg.read_s("SearchList") + search << domain unless domain.empty? + search.concat(slist.split(/,\s*/)) + nameserver.concat(ns.split(/[,\s]\s*/)) + end + end + rescue Registry::Error + end + + dhcpinfo = get_dhcpinfo + search.concat(dhcpinfo[0]) + nameserver.concat(dhcpinfo[1]) + [ search, nameserver ] + end + + def get_dhcpinfo + macaddrs = {} + ipaddrs = {} + WsControl.get_iflist.each do |index, macaddr, *ipaddr| + macaddrs[macaddr] = 1 + ipaddr.each { |ipaddr| ipaddrs[ipaddr] = 1 } + end + iflist = [ macaddrs, ipaddrs ] + + search = [] + nameserver = [] + version = -1 + Registry::HKEY_LOCAL_MACHINE.open(DHCP_9X) do |reg| + begin + version = API.unpackdw(reg.read_bin("Version")) + rescue Registry::Error + end + + reg.each_key do |key,| + catch(:not_used) do + reg.open(key) do |regdi| + dom, ns = get_dhcpinfo_key(version, regdi, iflist) + search << dom if dom + nameserver.concat(ns) if ns + end + end + end + end + [ search, nameserver ] + end + + def get_dhcpinfo_95(reg) + dhcp = reg.read_bin("DhcpInfo") + [ + API.unpackdw(dhcp[4..7]), + API.unpackdw(dhcp[8..11]), + 1, + dhcp[45..50], + reg.read_bin("OptionInfo"), + ] + end + + def get_dhcpinfo_98(reg) + [ + API.unpackdw(reg.read_bin("DhcpIPAddress")), + API.unpackdw(reg.read_bin("DhcpSubnetMask")), + API.unpackdw(reg.read_bin("HardwareType")), + reg.read_bin("HardwareAddress"), + reg.read_bin("OptionInfo"), + ] + end + + def get_dhcpinfo_key(version, reg, iflist) + info = case version + when 1 + get_dhcpinfo_95(reg) + when 2 + get_dhcpinfo_98(reg) + else + begin + get_dhcpinfo_98(reg) + rescue Registry::Error + get_dhcpinfo_95(reg) + end + end + ipaddr, netmask, hwtype, macaddr, opt = info + throw :not_used unless + ipaddr and ipaddr != 0 and + netmask and netmask != 0 and + macaddr and macaddr.size == 6 and + hwtype == 1 and + iflist[0][macaddr] and iflist[1][ipaddr] + + size = opt.size + idx = 0 + while idx <= size + opttype = opt[idx] + optsize = opt[idx + 1] + optval = opt[idx + 2, optsize] + case opttype + when 0xFF ## term + break + when 0x0F ## domain + domain = optval.chomp("\0") + when 0x06 ## dns + nameserver = optval.scan(/..../).collect { |addr| + "%d.%d.%d.%d" % addr.unpack('C4') + } + end + idx += optsize + 2 + end + [ domain, nameserver ] + rescue Registry::Error + throw :not_used + end + end + + module WsControl + WsControl = Win32API.new('wsock32.dll', 'WsControl', 'LLPPPP', 'L') + WSAGetLastError = Win32API.new('wsock32.dll', 'WSAGetLastError', 'V', 'L') + + MAX_TDI_ENTITIES = 512 + IPPROTO_TCP = 6 + WSCTL_TCP_QUERY_INFORMATION = 0 + INFO_CLASS_GENERIC = 0x100 + INFO_CLASS_PROTOCOL = 0x200 + INFO_TYPE_PROVIDER = 0x100 + ENTITY_LIST_ID = 0 + GENERIC_ENTITY = 0 + CL_NL_ENTITY = 0x301 + IF_ENTITY = 0x200 + ENTITY_TYPE_ID = 1 + CL_NL_IP = 0x303 + IF_MIB = 0x202 + IF_MIB_STATS_ID = 1 + IP_MIB_ADDRTABLE_ENTRY_ID = 0x102 + + def self.wsctl(tei_entity, tei_instance, + toi_class, toi_type, toi_id, + buffsize) + reqinfo = [ + ## TDIEntityID + tei_entity, tei_instance, + ## TDIObjectID + toi_class, toi_type, toi_id, + ## TCP_REQUEST_INFORMATION_EX + "" + ].pack('VVVVVa16') + reqsize = API.packdw(reqinfo.size) + buff = "\0" * buffsize + buffsize = API.packdw(buffsize) + result = WsControl.call( + IPPROTO_TCP, + WSCTL_TCP_QUERY_INFORMATION, + reqinfo, reqsize, + buff, buffsize) + if result != 0 + raise RuntimeError, "WsControl failed.(#{result})" + end + [ buff, API.unpackdw(buffsize) ] + end + private_class_method :wsctl + + def self.get_iflist + # Get TDI Entity List + entities, size = + wsctl(GENERIC_ENTITY, 0, + INFO_CLASS_GENERIC, + INFO_TYPE_PROVIDER, + ENTITY_LIST_ID, + MAX_TDI_ENTITIES * 8) # sizeof(TDIEntityID) + entities = entities[0, size]. + scan(/.{8}/). + collect { |e| e.unpack('VV') } + + # Get MIB Interface List + iflist = [] + ifcount = 0 + entities.each do |entity, instance| + if( (entity & IF_ENTITY)>0 ) + ifcount += 1 + etype, = wsctl(entity, instance, + INFO_CLASS_GENERIC, + INFO_TYPE_PROVIDER, + ENTITY_TYPE_ID, + 4) + if( (API.unpackdw(etype) & IF_MIB)==IF_MIB ) + ifentry, = wsctl(entity, instance, + INFO_CLASS_PROTOCOL, + INFO_TYPE_PROVIDER, + IF_MIB_STATS_ID, + 21 * 4 + 8 + 130) # sizeof(IFEntry) + iflist << [ + API.unpackdw(ifentry[0,4]), + ifentry[20, 6] + ] + end + end + end + + # Get IP Addresses + entities.each do |entity, instance| + if entity == CL_NL_ENTITY + etype, = wsctl(entity, instance, + INFO_CLASS_GENERIC, + INFO_TYPE_PROVIDER, + ENTITY_TYPE_ID, + 4) + if API.unpackdw(etype) == CL_NL_IP + ipentries, = wsctl(entity, instance, + INFO_CLASS_PROTOCOL, + INFO_TYPE_PROVIDER, + IP_MIB_ADDRTABLE_ENTRY_ID, + 24 * (ifcount+1)) # sizeof(IPAddrEntry) + ipentries.scan(/.{24}/) do |ipentry| + ipaddr, index = ipentry.unpack('VV') + if ifitem = iflist.assoc(index) + ifitem << ipaddr + end + end + end + end + end + iflist + end + end + __EOS__ +end +#==================================================================== + end +end diff --git a/ext/dl/win32/lib/win32/sspi.rb b/ext/dl/win32/lib/win32/sspi.rb new file mode 100644 index 0000000000..60291d51ea --- /dev/null +++ b/ext/dl/win32/lib/win32/sspi.rb @@ -0,0 +1,331 @@ +# +# = win32/sspi.rb +# +# Copyright (c) 2006-2007 Justin Bailey +# +# Written and maintained by Justin Bailey . +# +# This program is free software. You can re-distribute and/or +# modify this program under the same terms of ruby itself --- +# Ruby Distribution License or GNU General Public License. +# + +require 'Win32API' +require 'base64' + +# Implements bindings to Win32 SSPI functions, focused on authentication to a proxy server over HTTP. +module Win32 + module SSPI + # Specifies how credential structure requested will be used. Only SECPKG_CRED_OUTBOUND is used + # here. + SECPKG_CRED_INBOUND = 0x00000001 + SECPKG_CRED_OUTBOUND = 0x00000002 + SECPKG_CRED_BOTH = 0x00000003 + + # Format of token. NETWORK format is used here. + SECURITY_NATIVE_DREP = 0x00000010 + SECURITY_NETWORK_DREP = 0x00000000 + + # InitializeSecurityContext Requirement flags + ISC_REQ_REPLAY_DETECT = 0x00000004 + ISC_REQ_SEQUENCE_DETECT = 0x00000008 + ISC_REQ_CONFIDENTIALITY = 0x00000010 + ISC_REQ_USE_SESSION_KEY = 0x00000020 + ISC_REQ_PROMPT_FOR_CREDS = 0x00000040 + ISC_REQ_CONNECTION = 0x00000800 + + # Win32 API Functions. Uses Win32API to bind methods to constants contained in class. + module API + # Can be called with AcquireCredentialsHandle.call() + AcquireCredentialsHandle = Win32API.new("secur32", "AcquireCredentialsHandle", 'ppLpppppp', 'L') + # Can be called with InitializeSecurityContext.call() + InitializeSecurityContext = Win32API.new("secur32", "InitializeSecurityContext", 'pppLLLpLpppp', 'L') + # Can be called with DeleteSecurityContext.call() + DeleteSecurityContext = Win32API.new("secur32", "DeleteSecurityContext", 'P', 'L') + # Can be called with FreeCredentialsHandle.call() + FreeCredentialsHandle = Win32API.new("secur32", "FreeCredentialsHandle", 'P', 'L') + end + + # SecHandle struct + class SecurityHandle + def upper + @struct.unpack("LL")[1] + end + + def lower + @struct.unpack("LL")[0] + end + + def to_p + @struct ||= "\0" * 8 + end + end + + # Some familiar aliases for the SecHandle structure + CredHandle = CtxtHandle = SecurityHandle + + # TimeStamp struct + class TimeStamp + attr_reader :struct + + def to_p + @struct ||= "\0" * 8 + end + end + + # Creates binary representaiton of a SecBufferDesc structure, + # including the SecBuffer contained inside. + class SecurityBuffer + + SECBUFFER_TOKEN = 2 # Security token + + TOKENBUFSIZE = 12288 + SECBUFFER_VERSION = 0 + + def initialize(buffer = nil) + @buffer = buffer || "\0" * TOKENBUFSIZE + @bufferSize = @buffer.length + @type = SECBUFFER_TOKEN + end + + def bufferSize + unpack + @bufferSize + end + + def bufferType + unpack + @type + end + + def token + unpack + @buffer + end + + def to_p + # Assumption is that when to_p is called we are going to get a packed structure. Therefore, + # set @unpacked back to nil so we know to unpack when accessors are next accessed. + @unpacked = nil + # Assignment of inner structure to variable is very important here. Without it, + # will not be able to unpack changes to the structure. Alternative, nested unpacks, + # does not work (i.e. @struct.unpack("LLP12")[2].unpack("LLP12") results in "no associated pointer") + @sec_buffer ||= [@bufferSize, @type, @buffer].pack("LLP") + @struct ||= [SECBUFFER_VERSION, 1, @sec_buffer].pack("LLP") + end + + private + + # Unpacks the SecurityBufferDesc structure into member variables. We + # only want to do this once per struct, so the struct is deleted + # after unpacking. + def unpack + if ! @unpacked && @sec_buffer && @struct + @bufferSize, @type = @sec_buffer.unpack("LL") + @buffer = @sec_buffer.unpack("LLP#{@bufferSize}")[2] + @struct = nil + @sec_buffer = nil + @unpacked = true + end + end + end + + # SEC_WINNT_AUTH_IDENTITY structure + class Identity + SEC_WINNT_AUTH_IDENTITY_ANSI = 0x1 + + attr_accessor :user, :domain, :password + + def initialize(user = nil, domain = nil, password = nil) + @user = user + @domain = domain + @password = password + @flags = SEC_WINNT_AUTH_IDENTITY_ANSI + end + + def to_p + [@user, @user ? @user.length : 0, + @domain, @domain ? @domain.length : 0, + @password, @password ? @password.length : 0, + @flags].pack("PLPLPLL") + end + end + + # Takes a return result from an SSPI function and interprets the value. + class SSPIResult + # Good results + SEC_E_OK = 0x00000000 + SEC_I_CONTINUE_NEEDED = 0x00090312 + + # These are generally returned by InitializeSecurityContext + SEC_E_INSUFFICIENT_MEMORY = 0x80090300 + SEC_E_INTERNAL_ERROR = 0x80090304 + SEC_E_INVALID_HANDLE = 0x80090301 + SEC_E_INVALID_TOKEN = 0x80090308 + SEC_E_LOGON_DENIED = 0x8009030C + SEC_E_NO_AUTHENTICATING_AUTHORITY = 0x80090311 + SEC_E_NO_CREDENTIALS = 0x8009030E + SEC_E_TARGET_UNKNOWN = 0x80090303 + SEC_E_UNSUPPORTED_FUNCTION = 0x80090302 + SEC_E_WRONG_PRINCIPAL = 0x80090322 + + # These are generally returned by AcquireCredentialsHandle + SEC_E_NOT_OWNER = 0x80090306 + SEC_E_SECPKG_NOT_FOUND = 0x80090305 + SEC_E_UNKNOWN_CREDENTIALS = 0x8009030D + + @@map = {} + constants.each { |v| @@map[self.const_get(v.to_s)] = v } + + attr_reader :value + + def initialize(value) + # convert to unsigned long + value = [value].pack("L").unpack("L").first + raise "#{value.to_s(16)} is not a recognized result" unless @@map.has_key? value + @value = value + end + + def to_s + @@map[@value].to_s + end + + def ok? + @value == SEC_I_CONTINUE_NEEDED || @value == SEC_E_OK + end + + def ==(other) + if other.is_a?(SSPIResult) + @value == other.value + elsif other.is_a?(Fixnum) + @value == @@map[other] + else + false + end + end + end + + # Handles "Negotiate" type authentication. Geared towards authenticating with a proxy server over HTTP + class NegotiateAuth + attr_accessor :credentials, :context, :contextAttributes, :user, :domain + + # Default request flags for SSPI functions + REQUEST_FLAGS = ISC_REQ_CONFIDENTIALITY | ISC_REQ_REPLAY_DETECT | ISC_REQ_CONNECTION + + # NTLM tokens start with this header always. Encoding alone adds "==" and newline, so remove those + B64_TOKEN_PREFIX = Base64.encode64("NTLMSSP").delete("=\n") + + # Given a connection and a request path, performs authentication as the current user and returns + # the response from a GET request. The connnection should be a Net::HTTP object, and it should + # have been constructed using the Net::HTTP.Proxy method, but anything that responds to "get" will work. + # If a user and domain are given, will authenticate as the given user. + # Returns the response received from the get method (usually Net::HTTPResponse) + def NegotiateAuth.proxy_auth_get(http, path, user = nil, domain = nil) + raise "http must respond to :get" unless http.respond_to?(:get) + nego_auth = self.new user, domain + + resp = http.get path, { "Proxy-Authorization" => "Negotiate " + nego_auth.get_initial_token } + if resp["Proxy-Authenticate"] + resp = http.get path, { "Proxy-Authorization" => "Negotiate " + nego_auth.complete_authentication(resp["Proxy-Authenticate"].split(" ").last.strip) } + end + + resp + end + + # Creates a new instance ready for authentication as the given user in the given domain. + # Defaults to current user and domain as defined by ENV["USERDOMAIN"] and ENV["USERNAME"] if + # no arguments are supplied. + def initialize(user = nil, domain = nil) + if user.nil? && domain.nil? && ENV["USERNAME"].nil? && ENV["USERDOMAIN"].nil? + raise "A username or domain must be supplied since they cannot be retrieved from the environment" + end + + @user = user || ENV["USERNAME"] + @domain = domain || ENV["USERDOMAIN"] + end + + # Gets the initial Negotiate token. Returns it as a base64 encoded string suitable for use in HTTP. Can + # be easily decoded, however. + def get_initial_token + raise "This object is no longer usable because its resources have been freed." if @cleaned_up + get_credentials + + outputBuffer = SecurityBuffer.new + @context = CtxtHandle.new + @contextAttributes = "\0" * 4 + + result = SSPIResult.new(API::InitializeSecurityContext.call(@credentials.to_p, nil, nil, + REQUEST_FLAGS,0, SECURITY_NETWORK_DREP, nil, 0, @context.to_p, outputBuffer.to_p, @contextAttributes, TimeStamp.new.to_p)) + + if result.ok? then + return encode_token(outputBuffer.token) + else + raise "Error: #{result.to_s}" + end + end + + # Takes a token and gets the next token in the Negotiate authentication chain. Token can be Base64 encoded or not. + # The token can include the "Negotiate" header and it will be stripped. + # Does not indicate if SEC_I_CONTINUE or SEC_E_OK was returned. + # Token returned is Base64 encoded w/ all new lines removed. + def complete_authentication(token) + raise "This object is no longer usable because its resources have been freed." if @cleaned_up + + # Nil token OK, just set it to empty string + token = "" if token.nil? + + if token.include? "Negotiate" + # If the Negotiate prefix is passed in, assume we are seeing "Negotiate " and get the token. + token = token.split(" ").last + end + + if token.include? B64_TOKEN_PREFIX + # indicates base64 encoded token + token = Base64.decode64(token.strip) + end + + outputBuffer = SecurityBuffer.new + result = SSPIResult.new(API::InitializeSecurityContext.call(@credentials.to_p, @context.to_p, nil, + REQUEST_FLAGS, 0, SECURITY_NETWORK_DREP, SecurityBuffer.new(token).to_p, 0, + @context.to_p, + outputBuffer.to_p, @contextAttributes, TimeStamp.new.to_p)) + + if result.ok? then + return encode_token(outputBuffer.token) + else + raise "Error: #{result.to_s}" + end + ensure + # need to make sure we don't clean up if we've already cleaned up. + clean_up unless @cleaned_up + end + + private + + def clean_up + # free structures allocated + @cleaned_up = true + API::FreeCredentialsHandle.call(@credentials.to_p) + API::DeleteSecurityContext.call(@context.to_p) + @context = nil + @credentials = nil + @contextAttributes = nil + end + + # Gets credentials based on user, domain or both. If both are nil, an error occurs + def get_credentials + @credentials = CredHandle.new + ts = TimeStamp.new + @identity = Identity.new @user, @domain + result = SSPIResult.new(API::AcquireCredentialsHandle.call(nil, "Negotiate", SECPKG_CRED_OUTBOUND, nil, @identity.to_p, + nil, nil, @credentials.to_p, ts.to_p)) + raise "Error acquire credentials: #{result}" unless result.ok? + end + + def encode_token(t) + # encode64 will add newlines every 60 characters so we need to remove those. + Base64.encode64(t).delete("\n") + end + end + end +end \ No newline at end of file -- cgit v1.2.3