diff --git a/reactos/dll/win32/secur32/CMakeLists.txt b/reactos/dll/win32/secur32/CMakeLists.txt index ec6cd661e49..f1b696d584d 100644 --- a/reactos/dll/win32/secur32/CMakeLists.txt +++ b/reactos/dll/win32/secur32/CMakeLists.txt @@ -10,6 +10,13 @@ list(APPEND SOURCE stubs.c thunks.c wrapper.c + wine/base64_codec.c + wine/dispatcher.c + wine/hmac_md5.c + wine/kerberos.c + wine/negotiate.c + wine/ntlm.c + wine/util.c precomp.h) add_library(secur32 SHARED @@ -20,6 +27,6 @@ add_library(secur32 SHARED set_module_type(secur32 win32dll UNICODE) target_link_libraries(secur32 lsalib wine) -add_importlibs(secur32 advapi32 msvcrt kernel32 ntdll) +add_importlibs(secur32 advapi32 netapi32 msvcrt kernel32 ntdll) add_pch(secur32 precomp.h SOURCE) add_cd_file(TARGET secur32 DESTINATION reactos/system32 FOR all) diff --git a/reactos/dll/win32/secur32/dllmain.c b/reactos/dll/win32/secur32/dllmain.c index b1f71ecc441..cb0fcf2a29a 100644 --- a/reactos/dll/win32/secur32/dllmain.c +++ b/reactos/dll/win32/secur32/dllmain.c @@ -18,6 +18,11 @@ HANDLE Secur32Heap; /* FUNCTIONS *****************************************************************/ +#ifdef __REACTOS__ +// See sspi.c +extern void SECUR32_freeProviders(void); +#endif + BOOL WINAPI DllMain(HINSTANCE hInstance, @@ -36,6 +41,9 @@ DllMain(HINSTANCE hInstance, break; case DLL_PROCESS_DETACH: +#ifdef __REACTOS__ + SECUR32_freeProviders(); +#endif LsapCloseLsaPort(); if (!RtlDestroyHeap(Secur32Heap)) { diff --git a/reactos/dll/win32/secur32/secur32_priv.h b/reactos/dll/win32/secur32/secur32_priv.h index 6c675ae43c8..e7de636b1ea 100644 --- a/reactos/dll/win32/secur32/secur32_priv.h +++ b/reactos/dll/win32/secur32/secur32_priv.h @@ -40,6 +40,24 @@ typedef struct _SecurePackage SecureProvider *provider; } SecurePackage; +/* Allocates space for and initializes a new provider. If fnTableA or fnTableW + * is non-NULL, assumes the provider is built-in, and if moduleName is non-NULL, + * means must load the LSA/user mode functions tables from external SSP/AP module. + * Otherwise moduleName must not be NULL. + * Returns a pointer to the stored provider entry, for use adding packages. + */ +SecureProvider *SECUR32_addProvider(const SecurityFunctionTableA *fnTableA, + const SecurityFunctionTableW *fnTableW, PCWSTR moduleName) DECLSPEC_HIDDEN; + +/* Allocates space for and adds toAdd packages with the given provider. + * provider must not be NULL, and either infoA or infoW may be NULL, but not + * both. + */ +void SECUR32_addPackages(SecureProvider *provider, ULONG toAdd, + const SecPkgInfoA *infoA, const SecPkgInfoW *infoW) DECLSPEC_HIDDEN; + +#include "wine/wine_supp.h" + /* Tries to find the package named packageName. If it finds it, implicitly * loads the package if it isn't already loaded. */ diff --git a/reactos/dll/win32/secur32/sspi.c b/reactos/dll/win32/secur32/sspi.c index 6fe7806f01f..d97e91ba441 100644 --- a/reactos/dll/win32/secur32/sspi.c +++ b/reactos/dll/win32/secur32/sspi.c @@ -42,6 +42,7 @@ typedef struct _SecureProviderTable } SecureProviderTable; static void SECUR32_initializeProviders(void); +/* static */ void SECUR32_freeProviders(void); static CRITICAL_SECTION cs; static CRITICAL_SECTION_DEBUG cs_debug = @@ -359,7 +360,7 @@ static void _copyPackageInfo(PSecPkgInfoW info, const SecPkgInfoA *inInfoA, } } -static +// static SecureProvider *SECUR32_addProvider(const SecurityFunctionTableA *fnTableA, const SecurityFunctionTableW *fnTableW, PCWSTR moduleName) { @@ -406,7 +407,7 @@ SecureProvider *SECUR32_addProvider(const SecurityFunctionTableA *fnTableA, return ret; } -static +// static void SECUR32_addPackages(SecureProvider *provider, ULONG toAdd, const SecPkgInfoA *infoA, const SecPkgInfoW *infoW) { @@ -519,7 +520,6 @@ static const WCHAR securityProvidersW[] = { 'S','e','c','u','r','i','t','y','P','r','o','v','i','d','e','r','s',0 }; - /* FIXME: we're missing SECUR32_freeProviders, so all of this gets leaked */ static void SECUR32_initializeProviders(void) { HKEY key; @@ -559,6 +559,19 @@ static void SECUR32_initializeProviders(void) } RegCloseKey(key); } + + /* Now load the built-in providers (in Wine, this is done before the registry loading) */ +#ifdef __REACTOS__ +/// FIXME: Interim Wine code until we get Samuel's rewrite! + /* First load built-in providers */ + SECUR32_initNTLMSP(); + SECUR32_initKerberosSP(); + /* Load the Negotiate provider last so apps stumble over the working NTLM + * provider first. Attempting to fix bug #16905 while keeping the + * application reported on wine-users on 2006-09-12 working. */ + SECUR32_initNegotiateSP(); +#endif + } SecurePackage *SECUR32_findPackageW(PCWSTR packageName) @@ -632,6 +645,50 @@ SecurePackage *SECUR32_findPackageA(PCSTR packageName) return ret; } +/* static */ void SECUR32_freeProviders(void) +{ + TRACE("\n"); + EnterCriticalSection(&cs); + +#ifndef __REACTOS__ + SECUR32_deinitSchannelSP(); +#endif + + if (packageTable) + { + SecurePackage *package, *package_next; + LIST_FOR_EACH_ENTRY_SAFE(package, package_next, &packageTable->table, + SecurePackage, entry) + { + HeapFree(GetProcessHeap(), 0, package->infoW.Name); + HeapFree(GetProcessHeap(), 0, package->infoW.Comment); + HeapFree(GetProcessHeap(), 0, package); + } + + HeapFree(GetProcessHeap(), 0, packageTable); + packageTable = NULL; + } + + if (providerTable) + { + SecureProvider *provider, *provider_next; + LIST_FOR_EACH_ENTRY_SAFE(provider, provider_next, &providerTable->table, + SecureProvider, entry) + { + HeapFree(GetProcessHeap(), 0, provider->moduleName); + if (provider->lib) + FreeLibrary(provider->lib); + HeapFree(GetProcessHeap(), 0, provider); + } + + HeapFree(GetProcessHeap(), 0, providerTable); + providerTable = NULL; + } + + LeaveCriticalSection(&cs); + DeleteCriticalSection(&cs); +} + /*********************************************************************** * FreeContextBuffer (SECUR32.@) * diff --git a/reactos/dll/win32/secur32/wine/base64_codec.c b/reactos/dll/win32/secur32/wine/base64_codec.c new file mode 100644 index 00000000000..cd97ea41389 --- /dev/null +++ b/reactos/dll/win32/secur32/wine/base64_codec.c @@ -0,0 +1,190 @@ +/* + * base64 encoder/decoder + * + * Copyright 2005 by Kai Blin + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "precomp.h" + +#include +WINE_DEFAULT_DEBUG_CHANNEL(ntlm); + +static const char b64[] = +"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +SECURITY_STATUS encodeBase64(PBYTE in_buf, int in_len, char* out_buf, + int max_len, int *out_len) +{ + int div, i; + PBYTE d = in_buf; + int bytes = (in_len*8 + 5)/6, pad_bytes = (bytes % 4) ? 4 - (bytes % 4) : 0; + + TRACE("bytes is %d, pad bytes is %d\n", bytes, pad_bytes); + *out_len = bytes + pad_bytes; + + if(bytes + pad_bytes + 1 > max_len) + return SEC_E_BUFFER_TOO_SMALL; + + /* Three bytes of input give 4 chars of output */ + div = in_len / 3; + + i = 0; + while(div > 0) + { + /* first char is the first 6 bits of the first byte*/ + out_buf[i + 0] = b64[ ( d[0] >> 2) & 0x3f ]; + /* second char is the last 2 bits of the first byte and the first 4 + * bits of the second byte */ + out_buf[i + 1] = b64[ ((d[0] << 4) & 0x30) | (d[1] >> 4 & 0x0f)]; + /* third char is the last 4 bits of the second byte and the first 2 + * bits of the third byte */ + out_buf[i + 2] = b64[ ((d[1] << 2) & 0x3c) | (d[2] >> 6 & 0x03)]; + /* fourth char is the remaining 6 bits of the third byte */ + out_buf[i + 3] = b64[ d[2] & 0x3f]; + i += 4; + d += 3; + div--; + } + + switch(pad_bytes) + { + case 1: + /* first char is the first 6 bits of the first byte*/ + out_buf[i + 0] = b64[ ( d[0] >> 2) & 0x3f ]; + /* second char is the last 2 bits of the first byte and the first 4 + * bits of the second byte */ + out_buf[i + 1] = b64[ ((d[0] << 4) & 0x30) | (d[1] >> 4 & 0x0f)]; + /* third char is the last 4 bits of the second byte padded with + * two zeroes */ + out_buf[i + 2] = b64[ ((d[1] << 2) & 0x3c) ]; + /* fourth char is a = to indicate one byte of padding */ + out_buf[i + 3] = '='; + out_buf[i + 4] = 0; + break; + case 2: + /* first char is the first 6 bits of the first byte*/ + out_buf[i + 0] = b64[ ( d[0] >> 2) & 0x3f ]; + /* second char is the last 2 bits of the first byte padded with + * four zeroes*/ + out_buf[i + 1] = b64[ ((d[0] << 4) & 0x30)]; + /* third char is = to indicate padding */ + out_buf[i + 2] = '='; + /* fourth char is = to indicate padding */ + out_buf[i + 3] = '='; + out_buf[i + 4] = 0; + break; + default: + out_buf[i] = 0; + } + + return SEC_E_OK; +} + +static inline BYTE decode(char c) +{ + if( c >= 'A' && c <= 'Z') + return c - 'A'; + if( c >= 'a' && c <= 'z') + return c - 'a' + 26; + if( c >= '0' && c <= '9') + return c - '0' + 52; + if( c == '+') + return 62; + if( c == '/') + return 63; + else + return 64; +} + +SECURITY_STATUS decodeBase64(char *in_buf, int in_len, PBYTE out_buf, + int max_len, int *out_len) +{ + int len = in_len, i; + char *d = in_buf; + int ip0, ip1, ip2, ip3; + + TRACE("in_len: %d\n", in_len); + + if((in_len % 4) != 0) + return SEC_E_INVALID_TOKEN; + + if(in_len > max_len) + return SEC_E_BUFFER_TOO_SMALL; + + i = 0; + while(len > 4) + { + if((ip0 = decode(d[0])) > 63) + return SEC_E_INVALID_TOKEN; + if((ip1 = decode(d[1])) > 63) + return SEC_E_INVALID_TOKEN; + if((ip2 = decode(d[2])) > 63) + return SEC_E_INVALID_TOKEN; + if((ip3 = decode(d[3])) > 63) + return SEC_E_INVALID_TOKEN; + + out_buf[i + 0] = (ip0 << 2) | (ip1 >> 4); + out_buf[i + 1] = (ip1 << 4) | (ip2 >> 2); + out_buf[i + 2] = (ip2 << 6) | ip3; + len -= 4; + i += 3; + d += 4; + } + + if(d[2] == '=') + { + if((ip0 = decode(d[0])) > 63) + return SEC_E_INVALID_TOKEN; + if((ip1 = decode(d[1])) > 63) + return SEC_E_INVALID_TOKEN; + + out_buf[i] = (ip0 << 2) | (ip1 >> 4); + i++; + } + else if(d[3] == '=') + { + if((ip0 = decode(d[0])) > 63) + return SEC_E_INVALID_TOKEN; + if((ip1 = decode(d[1])) > 63) + return SEC_E_INVALID_TOKEN; + if((ip2 = decode(d[2])) > 63) + return SEC_E_INVALID_TOKEN; + + out_buf[i + 0] = (ip0 << 2) | (ip1 >> 4); + out_buf[i + 1] = (ip1 << 4) | (ip2 >> 2); + i += 2; + } + else + { + if((ip0 = decode(d[0])) > 63) + return SEC_E_INVALID_TOKEN; + if((ip1 = decode(d[1])) > 63) + return SEC_E_INVALID_TOKEN; + if((ip2 = decode(d[2])) > 63) + return SEC_E_INVALID_TOKEN; + if((ip3 = decode(d[3])) > 63) + return SEC_E_INVALID_TOKEN; + + + out_buf[i + 0] = (ip0 << 2) | (ip1 >> 4); + out_buf[i + 1] = (ip1 << 4) | (ip2 >> 2); + out_buf[i + 2] = (ip2 << 6) | ip3; + i += 3; + } + *out_len = i; + return SEC_E_OK; +} diff --git a/reactos/dll/win32/secur32/wine/base64_codec.h b/reactos/dll/win32/secur32/wine/base64_codec.h new file mode 100644 index 00000000000..df5c1ff8273 --- /dev/null +++ b/reactos/dll/win32/secur32/wine/base64_codec.h @@ -0,0 +1,12 @@ + +#ifndef __WINE_BASE64_CODEC_H__ +#define __WINE_BASE64_CODEC_H__ + +/* Functions from base64_codec.c used elsewhere */ +SECURITY_STATUS encodeBase64(PBYTE in_buf, int in_len, char* out_buf, + int max_len, int *out_len) DECLSPEC_HIDDEN; + +SECURITY_STATUS decodeBase64(char *in_buf, int in_len, BYTE *out_buf, + int max_len, int *out_len) DECLSPEC_HIDDEN; + +#endif /* __WINE_BASE64_CODEC_H__ */ diff --git a/reactos/dll/win32/secur32/wine/dispatcher.c b/reactos/dll/win32/secur32/wine/dispatcher.c new file mode 100644 index 00000000000..e0089a61b27 --- /dev/null +++ b/reactos/dll/win32/secur32/wine/dispatcher.c @@ -0,0 +1,441 @@ +/* + * Copyright 2005, 2006 Kai Blin + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + * + * A dispatcher to run ntlm_auth for wine's sspi module. + */ + +#include "precomp.h" + +#include +#include +#include +#include +#include + +#ifdef __REACTOS__ +#define close _close +#define read _read +#define write _write +#endif + +#include +WINE_DEFAULT_DEBUG_CHANNEL(ntlm); + +#define INITIAL_BUFFER_SIZE 200 + +char* flatten_cmdline(const char *prog, char* const argv[]) +{ + int i; + SIZE_T argstr_size = 0; + char *argstr, *p; + + /* Compute space needed for the new string, and allocate it */ + argstr_size += strlen(prog) + 3; // 3 == 2 quotes between 'prog', and 1 space + for(i = 0; argv[i] != NULL; ++i) + { + argstr_size += strlen(argv[i]) + 1; // 1 for space + } + argstr = HeapAlloc(GetProcessHeap(), 0, (argstr_size + 1) * sizeof(CHAR)); + if (argstr == NULL) + { + ERR("ERROR: Not enough memory\n"); + return NULL; + } + + /* Copy the contents and NULL-terminate the string */ + p = argstr; + strcpy(p, "\""); // Open quote + strcat(p, prog); + strcat(p, "\" "); // Close quote + space + p += strlen(p); + for(i = 0; argv[i] != NULL; ++i) + { + strcpy(p, argv[i]); + p += strlen(argv[i]); + *p++ = ' '; + } + *p = '\0'; + + return argstr; +} + +SECURITY_STATUS fork_helper(PNegoHelper *new_helper, const char *prog, + char* const argv[]) +{ + int pipe_in[2]; + int pipe_out[2]; +#ifdef __REACTOS__ + HANDLE hPipe; + PROCESS_INFORMATION pi; + STARTUPINFOA si; + char* cmdline; +#endif + int i; + PNegoHelper helper; + + TRACE("%s ", debugstr_a(prog)); + for(i = 0; argv[i] != NULL; ++i) + { + TRACE("%s ", debugstr_a(argv[i])); + } + TRACE("\n"); + +#ifndef __REACTOS__ + +#ifdef HAVE_PIPE2 + if (pipe2( pipe_in, O_CLOEXEC ) < 0 ) +#endif + { + if( pipe(pipe_in) < 0 ) return SEC_E_INTERNAL_ERROR; + fcntl( pipe_in[0], F_SETFD, FD_CLOEXEC ); + fcntl( pipe_in[1], F_SETFD, FD_CLOEXEC ); + } +#ifdef HAVE_PIPE2 + if (pipe2( pipe_out, O_CLOEXEC ) < 0 ) +#endif + { + if( pipe(pipe_out) < 0 ) + { + close(pipe_in[0]); + close(pipe_in[1]); + return SEC_E_INTERNAL_ERROR; + } + fcntl( pipe_out[0], F_SETFD, FD_CLOEXEC ); + fcntl( pipe_out[1], F_SETFD, FD_CLOEXEC ); + } + +#else + + if (_pipe(pipe_in, 0, _O_BINARY /* _O_TEXT */ | _O_NOINHERIT) < 0) + { + return SEC_E_INTERNAL_ERROR; + } + + if (_pipe(pipe_out, 0, _O_BINARY /* _O_TEXT */ | _O_NOINHERIT) < 0) + { + close(pipe_in[0]); + close(pipe_in[1]); + return SEC_E_INTERNAL_ERROR; + } + +#endif + + if (!(helper = HeapAlloc(GetProcessHeap(),0, sizeof(NegoHelper)))) + { + close(pipe_in[0]); + close(pipe_in[1]); + close(pipe_out[0]); + close(pipe_out[1]); + return SEC_E_INSUFFICIENT_MEMORY; + } + +#ifndef __REACTOS__ + helper->helper_pid = fork(); +#else + + cmdline = flatten_cmdline(prog, argv); + if (!cmdline) + { + close(pipe_in[0]); + close(pipe_in[1]); + close(pipe_out[0]); + close(pipe_out[1]); + HeapFree( GetProcessHeap(), 0, helper ); + return SEC_E_INSUFFICIENT_MEMORY; + } + + ZeroMemory(&pi, sizeof(pi)); + ZeroMemory(&si, sizeof(si)); + si.cb = sizeof(si); + + si.dwFlags |= STARTF_USESTDHANDLES; + + /* The reading side of the pipe is STDIN for this process */ + hPipe = (HANDLE)_get_osfhandle(pipe_out[0]); + SetHandleInformation(hPipe, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT); + si.hStdInput = hPipe; + + /* The writing side of the pipe is STDOUT for this process */ + hPipe = (HANDLE)_get_osfhandle(pipe_in[1]); + SetHandleInformation(hPipe, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT); + si.hStdOutput = hPipe; + si.hStdError = hPipe; + + if (!CreateProcessA(NULL, cmdline, NULL, NULL, TRUE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi)) + { + /* We fail just afterwards */ + helper->helper_pid = (HANDLE)-1; + } + else + { + helper->helper_pid = pi.hProcess; + CloseHandle(pi.hThread); + CloseHandle(pi.hProcess); + } + + HeapFree( GetProcessHeap(), 0, cmdline ); + +#endif + +#ifndef __REACTOS__ + if(helper->helper_pid == -1) +#else + if(helper->helper_pid == (HANDLE)-1) +#endif + { + close(pipe_in[0]); + close(pipe_in[1]); + close(pipe_out[0]); + close(pipe_out[1]); + HeapFree( GetProcessHeap(), 0, helper ); + return SEC_E_INTERNAL_ERROR; + } + +#ifndef __REACTOS__ + if(helper->helper_pid == 0) + { + /* We're in the child now */ + dup2(pipe_out[0], 0); + close(pipe_out[0]); + close(pipe_out[1]); + + dup2(pipe_in[1], 1); + close(pipe_in[0]); + close(pipe_in[1]); + + execvp(prog, argv); + + /* Whoops, we shouldn't get here. Big badaboom.*/ + write(STDOUT_FILENO, "BH\n", 3); + _exit(1); + } + else +#endif + { + *new_helper = helper; + helper->major = helper->minor = helper->micro = -1; + helper->com_buf = NULL; + helper->com_buf_size = 0; + helper->com_buf_offset = 0; + helper->session_key = NULL; + helper->neg_flags = 0; + helper->crypt.ntlm.a4i = NULL; + helper->crypt.ntlm2.send_a4i = NULL; + helper->crypt.ntlm2.recv_a4i = NULL; + helper->crypt.ntlm2.send_sign_key = NULL; + helper->crypt.ntlm2.send_seal_key = NULL; + helper->crypt.ntlm2.recv_sign_key = NULL; + helper->crypt.ntlm2.recv_seal_key = NULL; + helper->pipe_in = pipe_in[0]; // Keep in(read) + close(pipe_in[1]); // Close in(write) + helper->pipe_out = pipe_out[1]; // Keep out(write) + close(pipe_out[0]); // Close out(read) + } + + return SEC_E_OK; +} + +static SECURITY_STATUS read_line(PNegoHelper helper, int *offset_len) +{ + char *newline; + int read_size; + + if(helper->com_buf == NULL) + { + TRACE("Creating a new buffer for the helper\n"); + if((helper->com_buf = HeapAlloc(GetProcessHeap(), 0, INITIAL_BUFFER_SIZE)) == NULL) + return SEC_E_INSUFFICIENT_MEMORY; + + /* Created a new buffer, size is INITIAL_BUFFER_SIZE, offset is 0 */ + helper->com_buf_size = INITIAL_BUFFER_SIZE; + helper->com_buf_offset = 0; + } + + do + { + TRACE("offset = %d, size = %d\n", helper->com_buf_offset, helper->com_buf_size); + if(helper->com_buf_offset + INITIAL_BUFFER_SIZE > helper->com_buf_size) + { + /* increment buffer size in INITIAL_BUFFER_SIZE steps */ + char *buf = HeapReAlloc(GetProcessHeap(), 0, helper->com_buf, + helper->com_buf_size + INITIAL_BUFFER_SIZE); + TRACE("Resizing buffer!\n"); + if (!buf) return SEC_E_INSUFFICIENT_MEMORY; + helper->com_buf_size += INITIAL_BUFFER_SIZE; + helper->com_buf = buf; + } + if((read_size = read(helper->pipe_in, helper->com_buf + helper->com_buf_offset, + helper->com_buf_size - helper->com_buf_offset)) <= 0) + { + return SEC_E_INTERNAL_ERROR; + } + + TRACE("read_size = %d, read: %s\n", read_size, + debugstr_a(helper->com_buf + helper->com_buf_offset)); + helper->com_buf_offset += read_size; + newline = memchr(helper->com_buf, '\n', helper->com_buf_offset); + }while(newline == NULL); + + /* Now, if there's a newline character, and we read more than that newline, + * we have to store the offset so we can preserve the additional data.*/ + if( newline != helper->com_buf + helper->com_buf_offset) + { + TRACE("offset_len is calculated from %p - %p\n", + (helper->com_buf + helper->com_buf_offset), newline+1); + /* the length of the offset is the number of chars after the newline */ + *offset_len = (helper->com_buf + helper->com_buf_offset) - (newline + 1); + } + else + { + *offset_len = 0; + } + + *newline = '\0'; + + return SEC_E_OK; +} + +static SECURITY_STATUS preserve_unused(PNegoHelper helper, int offset_len) +{ + TRACE("offset_len = %d\n", offset_len); + + if(offset_len > 0) + { + memmove(helper->com_buf, helper->com_buf + helper->com_buf_offset, + offset_len); + helper->com_buf_offset = offset_len; + } + else + { + helper->com_buf_offset = 0; + } + + TRACE("helper->com_buf_offset was set to: %d\n", helper->com_buf_offset); + return SEC_E_OK; +} + +SECURITY_STATUS run_helper(PNegoHelper helper, char *buffer, + unsigned int max_buflen, int *buflen) +{ + int offset_len; + SECURITY_STATUS sec_status = SEC_E_OK; + + TRACE("In helper: sending %s\n", debugstr_a(buffer)); + + /* buffer + '\n' */ + write(helper->pipe_out, buffer, lstrlenA(buffer)); + write(helper->pipe_out, "\n", 1); + + if((sec_status = read_line(helper, &offset_len)) != SEC_E_OK) + { + return sec_status; + } + + TRACE("In helper: received %s\n", debugstr_a(helper->com_buf)); + *buflen = lstrlenA(helper->com_buf); + + if( *buflen > max_buflen) + { + ERR("Buffer size too small(%d given, %d required) dropping data!\n", + max_buflen, *buflen); + return SEC_E_BUFFER_TOO_SMALL; + } + + if( *buflen < 2 ) + { + return SEC_E_ILLEGAL_MESSAGE; + } + + /* We only get ERR if the input size is too big. On a GENSEC error, + * ntlm_auth will return BH */ + if(strncmp(helper->com_buf, "ERR", 3) == 0) + { + return SEC_E_INVALID_TOKEN; + } + + memcpy(buffer, helper->com_buf, *buflen+1); + + sec_status = preserve_unused(helper, offset_len); + + return sec_status; +} + +void cleanup_helper(PNegoHelper helper) +{ + + TRACE("Killing helper %p\n", helper); + if(helper == NULL) + return; + + HeapFree(GetProcessHeap(), 0, helper->com_buf); + HeapFree(GetProcessHeap(), 0, helper->session_key); + + /* closing stdin will terminate ntlm_auth */ + close(helper->pipe_out); + close(helper->pipe_in); + +#ifndef __REACTOS__ + +#ifdef HAVE_FORK + if (helper->helper_pid > 0) /* reap child */ + { + pid_t wret; + do { + wret = waitpid(helper->helper_pid, NULL, 0); + } while (wret < 0 && errno == EINTR); + } +#endif + +#endif + + HeapFree(GetProcessHeap(), 0, helper); +} + +void check_version(PNegoHelper helper) +{ + char temp[80]; + char *newline; + int major = 0, minor = 0, micro = 0, ret; + + TRACE("Checking version of helper\n"); + if(helper != NULL) + { + int len = read(helper->pipe_in, temp, sizeof(temp)-1); + if (len > 8) + { + if((newline = memchr(temp, '\n', len)) != NULL) + *newline = '\0'; + else + temp[len] = 0; + + TRACE("Exact version is %s\n", debugstr_a(temp)); + ret = sscanf(temp, "Version %d.%d.%d", &major, &minor, µ); + if(ret != 3) + { + ERR("Failed to get the helper version.\n"); + helper->major = helper->minor = helper->micro = -1; + } + else + { + TRACE("Version recognized: %d.%d.%d\n", major, minor, micro); + helper->major = major; + helper->minor = minor; + helper->micro = micro; + } + } + } +} diff --git a/reactos/dll/win32/secur32/wine/dispatcher.h b/reactos/dll/win32/secur32/wine/dispatcher.h new file mode 100644 index 00000000000..42f59def532 --- /dev/null +++ b/reactos/dll/win32/secur32/wine/dispatcher.h @@ -0,0 +1,16 @@ + +#ifndef __WINE_DISPATCHER_H__ +#define __WINE_DISPATCHER_H__ + +/* Functions from dispatcher.c used elsewhere in the code */ +SECURITY_STATUS fork_helper(PNegoHelper *new_helper, const char *prog, + char* const argv[]) DECLSPEC_HIDDEN; + +SECURITY_STATUS run_helper(PNegoHelper helper, char *buffer, + unsigned int max_buflen, int *buflen) DECLSPEC_HIDDEN; + +void cleanup_helper(PNegoHelper helper) DECLSPEC_HIDDEN; + +void check_version(PNegoHelper helper) DECLSPEC_HIDDEN; + +#endif /* __WINE_DISPATCHER_H__ */ diff --git a/reactos/dll/win32/secur32/wine/hmac_md5.c b/reactos/dll/win32/secur32/wine/hmac_md5.c new file mode 100644 index 00000000000..3479178132d --- /dev/null +++ b/reactos/dll/win32/secur32/wine/hmac_md5.c @@ -0,0 +1,77 @@ +/* + * Copyright 2006 Kai Blin + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + * + * This file implements RFC 2104 (HMAC) for the MD5 provider. + * It is needed for NTLM2 signing and sealing. + */ + +#include "hmac_md5.h" + +void HMACMD5Init(HMAC_MD5_CTX *ctx, const unsigned char *key, unsigned int key_len) +{ + int i; + unsigned char inner_padding[64]; + unsigned char temp_key[16]; + + if(key_len > 64) + { + MD5_CTX temp_ctx; + + MD5Init(&temp_ctx); + MD5Update(&temp_ctx, key, key_len); + MD5Final(&temp_ctx); + memcpy(temp_key, temp_ctx.digest, 16); + + key = temp_key; + key_len = 16; + } + + memset(inner_padding, 0, 64); + memset(ctx->outer_padding, 0, 64); + memcpy(inner_padding, key, key_len); + memcpy(ctx->outer_padding, key, key_len); + + for(i = 0; i < 64; ++i) + { + inner_padding[i] ^= 0x36; + ctx->outer_padding[i] ^= 0x5c; + } + + MD5Init(&(ctx->ctx)); + MD5Update(&(ctx->ctx), inner_padding, 64); +} + +void HMACMD5Update(HMAC_MD5_CTX *ctx, const unsigned char *data, unsigned int data_len) +{ + MD5Update(&(ctx->ctx), data, data_len); +} + +void HMACMD5Final(HMAC_MD5_CTX *ctx, unsigned char *digest) +{ + MD5_CTX outer_ctx; + unsigned char inner_digest[16]; + + MD5Final(&(ctx->ctx)); + memcpy(inner_digest, ctx->ctx.digest, 16); + + MD5Init(&outer_ctx); + MD5Update(&outer_ctx, ctx->outer_padding, 64); + MD5Update(&outer_ctx, inner_digest, 16); + MD5Final(&outer_ctx); + + memcpy(digest, outer_ctx.digest, 16); +} diff --git a/reactos/dll/win32/secur32/wine/hmac_md5.h b/reactos/dll/win32/secur32/wine/hmac_md5.h new file mode 100644 index 00000000000..2d5f3bfa184 --- /dev/null +++ b/reactos/dll/win32/secur32/wine/hmac_md5.h @@ -0,0 +1,48 @@ +/* + * Copyright 2006 Kai Blin + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + * + * This file holds the declarations needed for HMAC-MD5. + */ + +#ifndef _HMAC_MD5_H_ +#define _HMAC_MD5_H_ + +#include +#include + +typedef struct +{ + unsigned int i[2]; + unsigned int buf[4]; + unsigned char in[64]; + unsigned char digest[16]; +} MD5_CTX; + +typedef struct +{ + MD5_CTX ctx; + unsigned char outer_padding[64]; +} HMAC_MD5_CTX; + +void WINAPI MD5Init( MD5_CTX *ctx ); +void WINAPI MD5Update( MD5_CTX *ctx, const unsigned char *buf, unsigned int len ); +void WINAPI MD5Final( MD5_CTX *ctx ); + +void HMACMD5Init(HMAC_MD5_CTX *ctx, const unsigned char *key, unsigned int key_len) DECLSPEC_HIDDEN; +void HMACMD5Update(HMAC_MD5_CTX *ctx, const unsigned char *data, unsigned int data_len) DECLSPEC_HIDDEN; +void HMACMD5Final(HMAC_MD5_CTX *ctx, unsigned char *digest) DECLSPEC_HIDDEN; +#endif /*_HMAC_MD5_H_*/ diff --git a/reactos/dll/win32/secur32/wine/kerberos.c b/reactos/dll/win32/secur32/wine/kerberos.c new file mode 100644 index 00000000000..7d7d102f5a7 --- /dev/null +++ b/reactos/dll/win32/secur32/wine/kerberos.c @@ -0,0 +1,315 @@ +/* + * Copyright 2005, 2006 Kai Blin + * Copyright 2016 Jacek Caban for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "precomp.h" + +#include + +#include +WINE_DEFAULT_DEBUG_CHANNEL(secur32); + +#define KERBEROS_MAX_BUF 12000 + +/*********************************************************************** + * QueryCredentialsAttributesA + */ +static SECURITY_STATUS SEC_ENTRY kerberos_QueryCredentialsAttributesA(CredHandle *phCredential, ULONG ulAttribute, void *pBuffer) +{ + FIXME("(%p %d %p)\n", phCredential, ulAttribute, pBuffer); + return SEC_E_UNSUPPORTED_FUNCTION; +} + +/*********************************************************************** + * QueryCredentialsAttributesW + */ +static SECURITY_STATUS SEC_ENTRY kerberos_QueryCredentialsAttributesW(CredHandle *phCredential, ULONG ulAttribute, void *pBuffer) +{ + FIXME("(%p, %d, %p)\n", phCredential, ulAttribute, pBuffer); + return SEC_E_UNSUPPORTED_FUNCTION; +} + +/*********************************************************************** + * AcquireCredentialsHandleW + */ +static SECURITY_STATUS SEC_ENTRY kerberos_AcquireCredentialsHandleW(SEC_WCHAR *pszPrincipal, SEC_WCHAR *pszPackage, ULONG fCredentialUse, + LUID *pLogonID, void *pAuthData, SEC_GET_KEY_FN pGetKeyFn, void *pGetKeyArgument, CredHandle *phCredential, TimeStamp *ptsExpiry) +{ + FIXME("(%s %s 0x%08x %p %p %p %p %p %p)\n", debugstr_w(pszPrincipal), debugstr_w(pszPackage), fCredentialUse, + pLogonID, pAuthData, pGetKeyFn, pGetKeyArgument, phCredential, ptsExpiry); + return SEC_E_NO_CREDENTIALS; +} + +/*********************************************************************** + * AcquireCredentialsHandleA + */ +static SECURITY_STATUS SEC_ENTRY kerberos_AcquireCredentialsHandleA(SEC_CHAR *pszPrincipal, SEC_CHAR *pszPackage, ULONG fCredentialUse, + LUID *pLogonID, void *pAuthData, SEC_GET_KEY_FN pGetKeyFn, void *pGetKeyArgument, CredHandle *phCredential, TimeStamp *ptsExpiry) +{ + FIXME("(%s %s 0x%08x %p %p %p %p %p %p)\n", debugstr_a(pszPrincipal), debugstr_a(pszPackage), fCredentialUse, + pLogonID, pAuthData, pGetKeyFn, pGetKeyArgument, phCredential, ptsExpiry); + return SEC_E_UNSUPPORTED_FUNCTION; +} + +/*********************************************************************** + * InitializeSecurityContextW + */ +static SECURITY_STATUS SEC_ENTRY kerberos_InitializeSecurityContextW(CredHandle *phCredential, CtxtHandle *phContext, SEC_WCHAR *pszTargetName, + ULONG fContextReq, ULONG Reserved1, ULONG TargetDataRep, SecBufferDesc *pInput, ULONG Reserved2, CtxtHandle *phNewContext, + SecBufferDesc *pOutput, ULONG *pfContextAttr, TimeStamp *ptsExpiry) +{ + FIXME("(%p %p %s 0x%08x %d %d %p %d %p %p %p %p)\n", phCredential, phContext, debugstr_w(pszTargetName), + fContextReq, Reserved1, TargetDataRep, pInput, Reserved1, phNewContext, pOutput, pfContextAttr, ptsExpiry); + return SEC_E_UNSUPPORTED_FUNCTION; +} + +/*********************************************************************** + * InitializeSecurityContextA + */ +static SECURITY_STATUS SEC_ENTRY kerberos_InitializeSecurityContextA(CredHandle *phCredential, CtxtHandle *phContext, SEC_CHAR *pszTargetName, + ULONG fContextReq, ULONG Reserved1, ULONG TargetDataRep, SecBufferDesc *pInput, ULONG Reserved2, CtxtHandle *phNewContext, + SecBufferDesc *pOutput, ULONG *pfContextAttr, TimeStamp *ptsExpiry) +{ + FIXME("%p %p %s %d %d %d %p %d %p %p %p %p\n", phCredential, phContext, debugstr_a(pszTargetName), fContextReq, + Reserved1, TargetDataRep, pInput, Reserved1, phNewContext, pOutput, pfContextAttr, ptsExpiry); + return SEC_E_UNSUPPORTED_FUNCTION; +} + +/*********************************************************************** + * AcceptSecurityContext + */ +static SECURITY_STATUS SEC_ENTRY kerberos_AcceptSecurityContext(CredHandle *phCredential, CtxtHandle *phContext, SecBufferDesc *pInput, + ULONG fContextReq, ULONG TargetDataRep, CtxtHandle *phNewContext, SecBufferDesc *pOutput, ULONG *pfContextAttr, TimeStamp *ptsExpiry) +{ + FIXME("(%p %p %p %d %d %p %p %p %p)\n", phCredential, phContext, pInput, fContextReq, TargetDataRep, phNewContext, pOutput, + pfContextAttr, ptsExpiry); + return SEC_E_UNSUPPORTED_FUNCTION; +} + +/*********************************************************************** + * CompleteAuthToken + */ +static SECURITY_STATUS SEC_ENTRY kerberos_CompleteAuthToken(CtxtHandle *phContext, SecBufferDesc *pToken) +{ + FIXME("(%p %p)\n", phContext, pToken); + return SEC_E_UNSUPPORTED_FUNCTION; +} + +/*********************************************************************** + * DeleteSecurityContext + */ +static SECURITY_STATUS SEC_ENTRY kerberos_DeleteSecurityContext(CtxtHandle *phContext) +{ + FIXME("(%p)\n", phContext); + return SEC_E_UNSUPPORTED_FUNCTION; +} + +/*********************************************************************** + * QueryContextAttributesW + */ +static SECURITY_STATUS SEC_ENTRY kerberos_QueryContextAttributesW(CtxtHandle *phContext, ULONG ulAttribute, void *pBuffer) +{ + FIXME("(%p %d %p)\n", phContext, ulAttribute, pBuffer); + return SEC_E_UNSUPPORTED_FUNCTION; +} + +/*********************************************************************** + * QueryContextAttributesA + */ +static SECURITY_STATUS SEC_ENTRY kerberos_QueryContextAttributesA(CtxtHandle *phContext, ULONG ulAttribute, void *pBuffer) +{ + FIXME("(%p %d %p)\n", phContext, ulAttribute, pBuffer); + return SEC_E_UNSUPPORTED_FUNCTION; +} + +/*********************************************************************** + * ImpersonateSecurityContext + */ +static SECURITY_STATUS SEC_ENTRY kerberos_ImpersonateSecurityContext(CtxtHandle *phContext) +{ + FIXME("(%p)\n", phContext); + return SEC_E_UNSUPPORTED_FUNCTION; +} + +/*********************************************************************** + * RevertSecurityContext + */ +static SECURITY_STATUS SEC_ENTRY kerberos_RevertSecurityContext(CtxtHandle *phContext) +{ + FIXME("(%p)\n", phContext); + return SEC_E_UNSUPPORTED_FUNCTION; +} + +/*********************************************************************** + * MakeSignature + */ +static SECURITY_STATUS SEC_ENTRY kerberos_MakeSignature(CtxtHandle *phContext, ULONG fQOP, SecBufferDesc *pMessage, ULONG MessageSeqNo) +{ + FIXME("(%p %d %p %d)\n", phContext, fQOP, pMessage, MessageSeqNo); + return SEC_E_UNSUPPORTED_FUNCTION; +} + +/*********************************************************************** + * VerifySignature + */ +static SECURITY_STATUS SEC_ENTRY kerberos_VerifySignature(CtxtHandle *phContext, SecBufferDesc *pMessage, ULONG MessageSeqNo, PULONG pfQOP) +{ + FIXME("(%p %p %d %p)\n", phContext, pMessage, MessageSeqNo, pfQOP); + return SEC_E_UNSUPPORTED_FUNCTION; +} + +/*********************************************************************** + * FreeCredentialsHandle + */ +static SECURITY_STATUS SEC_ENTRY kerberos_FreeCredentialsHandle(PCredHandle phCredential) +{ + FIXME("(%p)\n", phCredential); + return SEC_E_UNSUPPORTED_FUNCTION; +} + +/*********************************************************************** + * EncryptMessage + */ +static SECURITY_STATUS SEC_ENTRY kerberos_EncryptMessage(CtxtHandle *phContext, ULONG fQOP, SecBufferDesc *pMessage, ULONG MessageSeqNo) +{ + FIXME("(%p %d %p %d)\n", phContext, fQOP, pMessage, MessageSeqNo); + return SEC_E_UNSUPPORTED_FUNCTION; +} + +/*********************************************************************** + * DecryptMessage + */ +static SECURITY_STATUS SEC_ENTRY kerberos_DecryptMessage(CtxtHandle *phContext, SecBufferDesc *pMessage, ULONG MessageSeqNo, PULONG pfQOP) +{ + FIXME("(%p %p %d %p)\n", phContext, pMessage, MessageSeqNo, pfQOP); + return SEC_E_UNSUPPORTED_FUNCTION; +} + +static const SecurityFunctionTableA kerberosTableA = { + 1, + NULL, /* EnumerateSecurityPackagesA */ + kerberos_QueryCredentialsAttributesA, /* QueryCredentialsAttributesA */ + kerberos_AcquireCredentialsHandleA, /* AcquireCredentialsHandleA */ + kerberos_FreeCredentialsHandle, /* FreeCredentialsHandle */ + NULL, /* Reserved2 */ + kerberos_InitializeSecurityContextA, /* InitializeSecurityContextA */ + kerberos_AcceptSecurityContext, /* AcceptSecurityContext */ + kerberos_CompleteAuthToken, /* CompleteAuthToken */ + kerberos_DeleteSecurityContext, /* DeleteSecurityContext */ + NULL, /* ApplyControlToken */ + kerberos_QueryContextAttributesA, /* QueryContextAttributesA */ + kerberos_ImpersonateSecurityContext, /* ImpersonateSecurityContext */ + kerberos_RevertSecurityContext, /* RevertSecurityContext */ + kerberos_MakeSignature, /* MakeSignature */ + kerberos_VerifySignature, /* VerifySignature */ + FreeContextBuffer, /* FreeContextBuffer */ + NULL, /* QuerySecurityPackageInfoA */ + NULL, /* Reserved3 */ + NULL, /* Reserved4 */ + NULL, /* ExportSecurityContext */ + NULL, /* ImportSecurityContextA */ + NULL, /* AddCredentialsA */ + NULL, /* Reserved8 */ + NULL, /* QuerySecurityContextToken */ + kerberos_EncryptMessage, /* EncryptMessage */ + kerberos_DecryptMessage, /* DecryptMessage */ + NULL, /* SetContextAttributesA */ +}; + +static const SecurityFunctionTableW kerberosTableW = { + 1, + NULL, /* EnumerateSecurityPackagesW */ + kerberos_QueryCredentialsAttributesW, /* QueryCredentialsAttributesW */ + kerberos_AcquireCredentialsHandleW, /* AcquireCredentialsHandleW */ + kerberos_FreeCredentialsHandle, /* FreeCredentialsHandle */ + NULL, /* Reserved2 */ + kerberos_InitializeSecurityContextW, /* InitializeSecurityContextW */ + kerberos_AcceptSecurityContext, /* AcceptSecurityContext */ + kerberos_CompleteAuthToken, /* CompleteAuthToken */ + kerberos_DeleteSecurityContext, /* DeleteSecurityContext */ + NULL, /* ApplyControlToken */ + kerberos_QueryContextAttributesW, /* QueryContextAttributesW */ + kerberos_ImpersonateSecurityContext, /* ImpersonateSecurityContext */ + kerberos_RevertSecurityContext, /* RevertSecurityContext */ + kerberos_MakeSignature, /* MakeSignature */ + kerberos_VerifySignature, /* VerifySignature */ + FreeContextBuffer, /* FreeContextBuffer */ + NULL, /* QuerySecurityPackageInfoW */ + NULL, /* Reserved3 */ + NULL, /* Reserved4 */ + NULL, /* ExportSecurityContext */ + NULL, /* ImportSecurityContextW */ + NULL, /* AddCredentialsW */ + NULL, /* Reserved8 */ + NULL, /* QuerySecurityContextToken */ + kerberos_EncryptMessage, /* EncryptMessage */ + kerberos_DecryptMessage, /* DecryptMessage */ + NULL, /* SetContextAttributesW */ +}; + +#define KERBEROS_COMMENT \ + {'M','i','c','r','o','s','o','f','t',' ','K','e','r','b','e','r','o','s',' ','V','1','.','0',0} +static CHAR kerberos_comment_A[] = KERBEROS_COMMENT; +static WCHAR kerberos_comment_W[] = KERBEROS_COMMENT; + +#define KERBEROS_NAME {'K','e','r','b','e','r','o','s',0} +static char kerberos_name_A[] = KERBEROS_NAME; +static WCHAR kerberos_name_W[] = KERBEROS_NAME; + +#define CAPS \ + ( SECPKG_FLAG_INTEGRITY \ + | SECPKG_FLAG_PRIVACY \ + | SECPKG_FLAG_TOKEN_ONLY \ + | SECPKG_FLAG_DATAGRAM \ + | SECPKG_FLAG_CONNECTION \ + | SECPKG_FLAG_MULTI_REQUIRED \ + | SECPKG_FLAG_EXTENDED_ERROR \ + | SECPKG_FLAG_IMPERSONATION \ + | SECPKG_FLAG_ACCEPT_WIN32_NAME \ + | SECPKG_FLAG_NEGOTIABLE \ + | SECPKG_FLAG_GSS_COMPATIBLE \ + | SECPKG_FLAG_LOGON \ + | SECPKG_FLAG_MUTUAL_AUTH \ + | SECPKG_FLAG_DELEGATION \ + | SECPKG_FLAG_READONLY_WITH_CHECKSUM \ + | SECPKG_FLAG_RESTRICTED_TOKENS \ + | SECPKG_FLAG_APPCONTAINER_CHECKS) + +static const SecPkgInfoW infoW = { + CAPS, + 1, + RPC_C_AUTHN_GSS_KERBEROS, + KERBEROS_MAX_BUF, + kerberos_name_W, + kerberos_comment_W +}; + +static const SecPkgInfoA infoA = { + CAPS, + 1, + RPC_C_AUTHN_GSS_KERBEROS, + KERBEROS_MAX_BUF, + kerberos_name_A, + kerberos_comment_A +}; + +void SECUR32_initKerberosSP(void) +{ + SecureProvider *provider = SECUR32_addProvider(&kerberosTableA, &kerberosTableW, NULL); + SECUR32_addPackages(provider, 1, &infoA, &infoW); +} diff --git a/reactos/dll/win32/secur32/wine/kerberos.h b/reactos/dll/win32/secur32/wine/kerberos.h new file mode 100644 index 00000000000..9c6a37ff61a --- /dev/null +++ b/reactos/dll/win32/secur32/wine/kerberos.h @@ -0,0 +1,7 @@ + +#ifndef __WINE_KERBEROS_H__ +#define __WINE_KERBEROS_H__ + +void SECUR32_initKerberosSP(void) DECLSPEC_HIDDEN; + +#endif /* __WINE_KERBEROS_H__ */ diff --git a/reactos/dll/win32/secur32/wine/negotiate.c b/reactos/dll/win32/secur32/wine/negotiate.c new file mode 100644 index 00000000000..a5c99955208 --- /dev/null +++ b/reactos/dll/win32/secur32/wine/negotiate.c @@ -0,0 +1,525 @@ +/* + * Copyright 2005 Kai Blin + * Copyright 2012 Hans Leidekker for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + * + * This file implements a Negotiate provider that simply forwards to + * the NTLM provider. + */ + +#include "precomp.h" + +#include +WINE_DEFAULT_DEBUG_CHANNEL(secur32); + +/*********************************************************************** + * QueryCredentialsAttributesA + */ +static SECURITY_STATUS SEC_ENTRY nego_QueryCredentialsAttributesA( + PCredHandle phCredential, ULONG ulAttribute, PVOID pBuffer) +{ + FIXME("%p, %u, %p\n", phCredential, ulAttribute, pBuffer); + return SEC_E_UNSUPPORTED_FUNCTION; +} + +/*********************************************************************** + * QueryCredentialsAttributesW + */ +static SECURITY_STATUS SEC_ENTRY nego_QueryCredentialsAttributesW( + PCredHandle phCredential, ULONG ulAttribute, PVOID pBuffer) +{ + FIXME("%p, %u, %p\n", phCredential, ulAttribute, pBuffer); + return SEC_E_UNSUPPORTED_FUNCTION; +} + +/*********************************************************************** + * AcquireCredentialsHandleW + */ +static SECURITY_STATUS SEC_ENTRY nego_AcquireCredentialsHandleW( + SEC_WCHAR *pszPrincipal, SEC_WCHAR *pszPackage, ULONG fCredentialUse, + PLUID pLogonID, PVOID pAuthData, SEC_GET_KEY_FN pGetKeyFn, + PVOID pGetKeyArgument, PCredHandle phCredential, PTimeStamp ptsExpiry ) +{ + static SEC_WCHAR ntlmW[] = {'N','T','L','M',0}; + SECURITY_STATUS ret; + + TRACE("%s, %s, 0x%08x, %p, %p, %p, %p, %p, %p\n", + debugstr_w(pszPrincipal), debugstr_w(pszPackage), fCredentialUse, + pLogonID, pAuthData, pGetKeyFn, pGetKeyArgument, phCredential, ptsExpiry); + + FIXME("forwarding to NTLM\n"); + ret = ntlm_AcquireCredentialsHandleW( pszPrincipal, ntlmW, fCredentialUse, + pLogonID, pAuthData, pGetKeyFn, pGetKeyArgument, + phCredential, ptsExpiry ); + if (ret == SEC_E_OK) + { + NtlmCredentials *cred = (NtlmCredentials *)phCredential->dwLower; + cred->no_cached_credentials = (pAuthData == NULL); + } + return ret; +} + +/*********************************************************************** + * AcquireCredentialsHandleA + */ +static SECURITY_STATUS SEC_ENTRY nego_AcquireCredentialsHandleA( + SEC_CHAR *pszPrincipal, SEC_CHAR *pszPackage, ULONG fCredentialUse, + PLUID pLogonID, PVOID pAuthData, SEC_GET_KEY_FN pGetKeyFn, + PVOID pGetKeyArgument, PCredHandle phCredential, PTimeStamp ptsExpiry ) +{ + SECURITY_STATUS ret = SEC_E_INSUFFICIENT_MEMORY; + SEC_WCHAR *user = NULL, *domain = NULL, *passwd = NULL, *package = NULL; + SEC_WINNT_AUTH_IDENTITY_W *identityW = NULL; + + TRACE("%s, %s, 0x%08x, %p, %p, %p, %p, %p, %p\n", + debugstr_a(pszPrincipal), debugstr_a(pszPackage), fCredentialUse, + pLogonID, pAuthData, pGetKeyFn, pGetKeyArgument, phCredential, ptsExpiry); + + if (pszPackage) + { + int package_len = MultiByteToWideChar( CP_ACP, 0, pszPackage, -1, NULL, 0 ); + package = HeapAlloc( GetProcessHeap(), 0, package_len * sizeof(SEC_WCHAR) ); + if (!package) return SEC_E_INSUFFICIENT_MEMORY; + MultiByteToWideChar( CP_ACP, 0, pszPackage, -1, package, package_len ); + } + if (pAuthData) + { + SEC_WINNT_AUTH_IDENTITY_A *identity = pAuthData; + int user_len, domain_len, passwd_len; + + if (identity->Flags == SEC_WINNT_AUTH_IDENTITY_ANSI) + { + identityW = HeapAlloc( GetProcessHeap(), 0, sizeof(*identityW) ); + if (!identityW) goto done; + + if (!identity->UserLength) user_len = 0; + else + { + user_len = MultiByteToWideChar( CP_ACP, 0, (LPCSTR)identity->User, + identity->UserLength, NULL, 0 ); + user = HeapAlloc( GetProcessHeap(), 0, user_len * sizeof(SEC_WCHAR) ); + if (!user) goto done; + MultiByteToWideChar( CP_ACP, 0, (LPCSTR)identity->User, identity->UserLength, + user, user_len ); + } + if (!identity->DomainLength) domain_len = 0; + else + { + domain_len = MultiByteToWideChar( CP_ACP, 0, (LPCSTR)identity->Domain, + identity->DomainLength, NULL, 0 ); + domain = HeapAlloc( GetProcessHeap(), 0, domain_len * sizeof(SEC_WCHAR) ); + if (!domain) goto done; + MultiByteToWideChar( CP_ACP, 0, (LPCSTR)identity->Domain, identity->DomainLength, + domain, domain_len ); + } + if (!identity->PasswordLength) passwd_len = 0; + else + { + passwd_len = MultiByteToWideChar( CP_ACP, 0, (LPCSTR)identity->Password, + identity->PasswordLength, NULL, 0 ); + passwd = HeapAlloc( GetProcessHeap(), 0, passwd_len * sizeof(SEC_WCHAR) ); + if (!passwd) goto done; + MultiByteToWideChar( CP_ACP, 0, (LPCSTR)identity->Password, identity->PasswordLength, + passwd, passwd_len ); + } + identityW->Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE; + identityW->User = user; + identityW->UserLength = user_len; + identityW->Domain = domain; + identityW->DomainLength = domain_len; + identityW->Password = passwd; + identityW->PasswordLength = passwd_len; + } + else identityW = (SEC_WINNT_AUTH_IDENTITY_W *)identity; + } + ret = nego_AcquireCredentialsHandleW( NULL, package, fCredentialUse, pLogonID, identityW, + pGetKeyFn, pGetKeyArgument, phCredential, ptsExpiry ); +done: + HeapFree( GetProcessHeap(), 0, package ); + HeapFree( GetProcessHeap(), 0, user ); + HeapFree( GetProcessHeap(), 0, domain ); + HeapFree( GetProcessHeap(), 0, passwd ); + HeapFree( GetProcessHeap(), 0, identityW ); + return ret; +} + +/*********************************************************************** + * InitializeSecurityContextW + */ +static SECURITY_STATUS SEC_ENTRY nego_InitializeSecurityContextW( + PCredHandle phCredential, PCtxtHandle phContext, SEC_WCHAR *pszTargetName, + ULONG fContextReq, ULONG Reserved1, ULONG TargetDataRep, + PSecBufferDesc pInput, ULONG Reserved2, PCtxtHandle phNewContext, + PSecBufferDesc pOutput, ULONG *pfContextAttr, PTimeStamp ptsExpiry ) +{ + TRACE("%p, %p, %s, 0x%08x, %u, %u, %p, %u, %p, %p, %p, %p\n", + phCredential, phContext, debugstr_w(pszTargetName), fContextReq, + Reserved1, TargetDataRep, pInput, Reserved1, phNewContext, pOutput, + pfContextAttr, ptsExpiry); + + return ntlm_InitializeSecurityContextW( phCredential, phContext, pszTargetName, + fContextReq, Reserved1, TargetDataRep, + pInput, Reserved2, phNewContext, + pOutput, pfContextAttr, ptsExpiry ); +} + +/*********************************************************************** + * InitializeSecurityContextA + */ +static SECURITY_STATUS SEC_ENTRY nego_InitializeSecurityContextA( + PCredHandle phCredential, PCtxtHandle phContext, SEC_CHAR *pszTargetName, + ULONG fContextReq, ULONG Reserved1, ULONG TargetDataRep, + PSecBufferDesc pInput, ULONG Reserved2, PCtxtHandle phNewContext, + PSecBufferDesc pOutput, ULONG *pfContextAttr, PTimeStamp ptsExpiry ) +{ + SECURITY_STATUS ret; + SEC_WCHAR *target = NULL; + + TRACE("%p, %p, %s, 0x%08x, %u, %u, %p, %u, %p, %p, %p, %p\n", + phCredential, phContext, debugstr_a(pszTargetName), fContextReq, + Reserved1, TargetDataRep, pInput, Reserved1, phNewContext, pOutput, + pfContextAttr, ptsExpiry); + + if (pszTargetName) + { + int target_len = MultiByteToWideChar( CP_ACP, 0, pszTargetName, -1, NULL, 0 ); + target = HeapAlloc(GetProcessHeap(), 0, target_len * sizeof(SEC_WCHAR) ); + if (!target) return SEC_E_INSUFFICIENT_MEMORY; + MultiByteToWideChar( CP_ACP, 0, pszTargetName, -1, target, target_len ); + } + ret = nego_InitializeSecurityContextW( phCredential, phContext, target, fContextReq, + Reserved1, TargetDataRep, pInput, Reserved2, + phNewContext, pOutput, pfContextAttr, ptsExpiry ); + HeapFree( GetProcessHeap(), 0, target ); + return ret; +} + +/*********************************************************************** + * AcceptSecurityContext + */ +static SECURITY_STATUS SEC_ENTRY nego_AcceptSecurityContext( + PCredHandle phCredential, PCtxtHandle phContext, PSecBufferDesc pInput, + ULONG fContextReq, ULONG TargetDataRep, PCtxtHandle phNewContext, + PSecBufferDesc pOutput, ULONG *pfContextAttr, PTimeStamp ptsExpiry) +{ + TRACE("%p, %p, %p, 0x%08x, %u, %p, %p, %p, %p\n", phCredential, phContext, + pInput, fContextReq, TargetDataRep, phNewContext, pOutput, pfContextAttr, + ptsExpiry); + + return ntlm_AcceptSecurityContext( phCredential, phContext, pInput, + fContextReq, TargetDataRep, phNewContext, + pOutput, pfContextAttr, ptsExpiry ); +} + +/*********************************************************************** + * CompleteAuthToken + */ +static SECURITY_STATUS SEC_ENTRY nego_CompleteAuthToken(PCtxtHandle phContext, + PSecBufferDesc pToken) +{ + SECURITY_STATUS ret; + + TRACE("%p %p\n", phContext, pToken); + if (phContext) + { + ret = SEC_E_UNSUPPORTED_FUNCTION; + } + else + { + ret = SEC_E_INVALID_HANDLE; + } + return ret; +} + +/*********************************************************************** + * DeleteSecurityContext + */ +static SECURITY_STATUS SEC_ENTRY nego_DeleteSecurityContext(PCtxtHandle phContext) +{ + TRACE("%p\n", phContext); + + return ntlm_DeleteSecurityContext( phContext ); +} + +/*********************************************************************** + * ApplyControlToken + */ +static SECURITY_STATUS SEC_ENTRY nego_ApplyControlToken(PCtxtHandle phContext, + PSecBufferDesc pInput) +{ + SECURITY_STATUS ret; + + TRACE("%p %p\n", phContext, pInput); + if (phContext) + { + ret = SEC_E_UNSUPPORTED_FUNCTION; + } + else + { + ret = SEC_E_INVALID_HANDLE; + } + return ret; +} + +/*********************************************************************** + * QueryContextAttributesW + */ +static SECURITY_STATUS SEC_ENTRY nego_QueryContextAttributesW( + PCtxtHandle phContext, ULONG ulAttribute, void *pBuffer) +{ + TRACE("%p, %u, %p\n", phContext, ulAttribute, pBuffer); + + switch (ulAttribute) + { + case SECPKG_ATTR_SIZES: + { + SecPkgContext_Sizes *sizes = (SecPkgContext_Sizes *)pBuffer; + sizes->cbMaxToken = 2888; + sizes->cbMaxSignature = 16; + sizes->cbSecurityTrailer = 16; + sizes->cbBlockSize = 0; + return SEC_E_OK; + } + case SECPKG_ATTR_NEGOTIATION_INFO: + { + SecPkgContext_NegotiationInfoW *info = (SecPkgContext_NegotiationInfoW *)pBuffer; + info->PackageInfo = ntlm_package_infoW; + info->NegotiationState = SECPKG_NEGOTIATION_COMPLETE; + return SEC_E_OK; + } + default: + return ntlm_QueryContextAttributesW( phContext, ulAttribute, pBuffer ); + } +} + +/*********************************************************************** + * QueryContextAttributesA + */ +static SECURITY_STATUS SEC_ENTRY nego_QueryContextAttributesA(PCtxtHandle phContext, + ULONG ulAttribute, void *pBuffer) +{ + TRACE("%p, %u, %p\n", phContext, ulAttribute, pBuffer); + + switch (ulAttribute) + { + case SECPKG_ATTR_SIZES: + { + SecPkgContext_Sizes *sizes = (SecPkgContext_Sizes *)pBuffer; + sizes->cbMaxToken = 2888; + sizes->cbMaxSignature = 16; + sizes->cbSecurityTrailer = 16; + sizes->cbBlockSize = 0; + return SEC_E_OK; + } + case SECPKG_ATTR_NEGOTIATION_INFO: + { + SecPkgContext_NegotiationInfoA *info = (SecPkgContext_NegotiationInfoA *)pBuffer; + info->PackageInfo = ntlm_package_infoA; + info->NegotiationState = SECPKG_NEGOTIATION_COMPLETE; + return SEC_E_OK; + } + default: + return ntlm_QueryContextAttributesA( phContext, ulAttribute, pBuffer ); + } +} + +/*********************************************************************** + * ImpersonateSecurityContext + */ +static SECURITY_STATUS SEC_ENTRY nego_ImpersonateSecurityContext(PCtxtHandle phContext) +{ + SECURITY_STATUS ret; + + TRACE("%p\n", phContext); + if (phContext) + { + ret = SEC_E_UNSUPPORTED_FUNCTION; + } + else + { + ret = SEC_E_INVALID_HANDLE; + } + return ret; +} + +/*********************************************************************** + * RevertSecurityContext + */ +static SECURITY_STATUS SEC_ENTRY nego_RevertSecurityContext(PCtxtHandle phContext) +{ + SECURITY_STATUS ret; + + TRACE("%p\n", phContext); + if (phContext) + { + ret = SEC_E_UNSUPPORTED_FUNCTION; + } + else + { + ret = SEC_E_INVALID_HANDLE; + } + return ret; +} + +/*********************************************************************** + * MakeSignature + */ +static SECURITY_STATUS SEC_ENTRY nego_MakeSignature(PCtxtHandle phContext, + ULONG fQOP, PSecBufferDesc pMessage, ULONG MessageSeqNo) +{ + TRACE("%p, 0x%08x, %p, %u\n", phContext, fQOP, pMessage, MessageSeqNo); + + return ntlm_MakeSignature( phContext, fQOP, pMessage, MessageSeqNo ); +} + +/*********************************************************************** + * VerifySignature + */ +static SECURITY_STATUS SEC_ENTRY nego_VerifySignature(PCtxtHandle phContext, + PSecBufferDesc pMessage, ULONG MessageSeqNo, PULONG pfQOP) +{ + TRACE("%p, %p, %u, %p\n", phContext, pMessage, MessageSeqNo, pfQOP); + + return ntlm_VerifySignature( phContext, pMessage, MessageSeqNo, pfQOP ); +} + +/*********************************************************************** + * FreeCredentialsHandle + */ +static SECURITY_STATUS SEC_ENTRY nego_FreeCredentialsHandle(PCredHandle phCredential) +{ + TRACE("%p\n", phCredential); + + return ntlm_FreeCredentialsHandle( phCredential ); +} + +/*********************************************************************** + * EncryptMessage + */ +static SECURITY_STATUS SEC_ENTRY nego_EncryptMessage(PCtxtHandle phContext, + ULONG fQOP, PSecBufferDesc pMessage, ULONG MessageSeqNo) +{ + TRACE("%p, 0x%08x, %p, %u\n", phContext, fQOP, pMessage, MessageSeqNo); + + return ntlm_EncryptMessage( phContext, fQOP, pMessage, MessageSeqNo ); +} + +/*********************************************************************** + * DecryptMessage + */ +static SECURITY_STATUS SEC_ENTRY nego_DecryptMessage(PCtxtHandle phContext, + PSecBufferDesc pMessage, ULONG MessageSeqNo, PULONG pfQOP) +{ + TRACE("%p, %p, %u, %p\n", phContext, pMessage, MessageSeqNo, pfQOP); + + return ntlm_DecryptMessage( phContext, pMessage, MessageSeqNo, pfQOP ); +} + +static const SecurityFunctionTableA negoTableA = { + 1, + NULL, /* EnumerateSecurityPackagesA */ + nego_QueryCredentialsAttributesA, /* QueryCredentialsAttributesA */ + nego_AcquireCredentialsHandleA, /* AcquireCredentialsHandleA */ + nego_FreeCredentialsHandle, /* FreeCredentialsHandle */ + NULL, /* Reserved2 */ + nego_InitializeSecurityContextA, /* InitializeSecurityContextA */ + nego_AcceptSecurityContext, /* AcceptSecurityContext */ + nego_CompleteAuthToken, /* CompleteAuthToken */ + nego_DeleteSecurityContext, /* DeleteSecurityContext */ + nego_ApplyControlToken, /* ApplyControlToken */ + nego_QueryContextAttributesA, /* QueryContextAttributesA */ + nego_ImpersonateSecurityContext, /* ImpersonateSecurityContext */ + nego_RevertSecurityContext, /* RevertSecurityContext */ + nego_MakeSignature, /* MakeSignature */ + nego_VerifySignature, /* VerifySignature */ + FreeContextBuffer, /* FreeContextBuffer */ + NULL, /* QuerySecurityPackageInfoA */ + NULL, /* Reserved3 */ + NULL, /* Reserved4 */ + NULL, /* ExportSecurityContext */ + NULL, /* ImportSecurityContextA */ + NULL, /* AddCredentialsA */ + NULL, /* Reserved8 */ + NULL, /* QuerySecurityContextToken */ + nego_EncryptMessage, /* EncryptMessage */ + nego_DecryptMessage, /* DecryptMessage */ + NULL, /* SetContextAttributesA */ +}; + +static const SecurityFunctionTableW negoTableW = { + 1, + NULL, /* EnumerateSecurityPackagesW */ + nego_QueryCredentialsAttributesW, /* QueryCredentialsAttributesW */ + nego_AcquireCredentialsHandleW, /* AcquireCredentialsHandleW */ + nego_FreeCredentialsHandle, /* FreeCredentialsHandle */ + NULL, /* Reserved2 */ + nego_InitializeSecurityContextW, /* InitializeSecurityContextW */ + nego_AcceptSecurityContext, /* AcceptSecurityContext */ + nego_CompleteAuthToken, /* CompleteAuthToken */ + nego_DeleteSecurityContext, /* DeleteSecurityContext */ + nego_ApplyControlToken, /* ApplyControlToken */ + nego_QueryContextAttributesW, /* QueryContextAttributesW */ + nego_ImpersonateSecurityContext, /* ImpersonateSecurityContext */ + nego_RevertSecurityContext, /* RevertSecurityContext */ + nego_MakeSignature, /* MakeSignature */ + nego_VerifySignature, /* VerifySignature */ + FreeContextBuffer, /* FreeContextBuffer */ + NULL, /* QuerySecurityPackageInfoW */ + NULL, /* Reserved3 */ + NULL, /* Reserved4 */ + NULL, /* ExportSecurityContext */ + NULL, /* ImportSecurityContextW */ + NULL, /* AddCredentialsW */ + NULL, /* Reserved8 */ + NULL, /* QuerySecurityContextToken */ + nego_EncryptMessage, /* EncryptMessage */ + nego_DecryptMessage, /* DecryptMessage */ + NULL, /* SetContextAttributesW */ +}; + +#define NEGO_MAX_TOKEN 12000 + +static WCHAR nego_name_W[] = {'N','e','g','o','t','i','a','t','e',0}; +static char nego_name_A[] = "Negotiate"; + +static WCHAR negotiate_comment_W[] = + {'M','i','c','r','o','s','o','f','t',' ','P','a','c','k','a','g','e',' ', + 'N','e','g','o','t','i','a','t','o','r',0}; +static CHAR negotiate_comment_A[] = "Microsoft Package Negotiator"; + +#define CAPS ( \ + SECPKG_FLAG_INTEGRITY | \ + SECPKG_FLAG_PRIVACY | \ + SECPKG_FLAG_CONNECTION | \ + SECPKG_FLAG_MULTI_REQUIRED | \ + SECPKG_FLAG_EXTENDED_ERROR | \ + SECPKG_FLAG_IMPERSONATION | \ + SECPKG_FLAG_ACCEPT_WIN32_NAME | \ + SECPKG_FLAG_NEGOTIABLE | \ + SECPKG_FLAG_GSS_COMPATIBLE | \ + SECPKG_FLAG_LOGON | \ + SECPKG_FLAG_RESTRICTED_TOKENS ) + +void SECUR32_initNegotiateSP(void) +{ + SecureProvider *provider = SECUR32_addProvider(&negoTableA, &negoTableW, NULL); + + const SecPkgInfoW infoW = {CAPS, 1, RPC_C_AUTHN_GSS_NEGOTIATE, NEGO_MAX_TOKEN, + nego_name_W, negotiate_comment_W}; + const SecPkgInfoA infoA = {CAPS, 1, RPC_C_AUTHN_GSS_NEGOTIATE, NEGO_MAX_TOKEN, + nego_name_A, negotiate_comment_A}; + SECUR32_addPackages(provider, 1L, &infoA, &infoW); +} diff --git a/reactos/dll/win32/secur32/wine/negotiate.h b/reactos/dll/win32/secur32/wine/negotiate.h new file mode 100644 index 00000000000..1cb4711051c --- /dev/null +++ b/reactos/dll/win32/secur32/wine/negotiate.h @@ -0,0 +1,7 @@ + +#ifndef __WINE_NEGOTIATE_H__ +#define __WINE_NEGOTIATE_H__ + +void SECUR32_initNegotiateSP(void) DECLSPEC_HIDDEN; + +#endif /* __WINE_NEGOTIATE_H__ */ diff --git a/reactos/dll/win32/secur32/wine/ntlm.c b/reactos/dll/win32/secur32/wine/ntlm.c new file mode 100644 index 00000000000..1d9cb44ab03 --- /dev/null +++ b/reactos/dll/win32/secur32/wine/ntlm.c @@ -0,0 +1,2064 @@ +/* + * Copyright 2005, 2006 Kai Blin + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + * + * This file implements the NTLM security provider. + */ + +#include "precomp.h" + +#include +#include +#include +#include + +#include "hmac_md5.h" + +#include +#include +WINE_DEFAULT_DEBUG_CHANNEL(ntlm); +WINE_DECLARE_DEBUG_CHANNEL(winediag); + +#define NTLM_MAX_BUF 1904 +#define MIN_NTLM_AUTH_MAJOR_VERSION 3 +#define MIN_NTLM_AUTH_MINOR_VERSION 0 +#ifndef __REACTOS__ +#define MIN_NTLM_AUTH_MICRO_VERSION 25 +#else +#define MIN_NTLM_AUTH_MICRO_VERSION 23 +#endif + +static CHAR ntlm_auth[] = "ntlm_auth"; + +/*********************************************************************** + * QueryCredentialsAttributesA + */ +static SECURITY_STATUS SEC_ENTRY ntlm_QueryCredentialsAttributesA( + PCredHandle phCredential, ULONG ulAttribute, PVOID pBuffer) +{ + SECURITY_STATUS ret; + + TRACE("(%p, %d, %p)\n", phCredential, ulAttribute, pBuffer); + + if(ulAttribute == SECPKG_ATTR_NAMES) + { + FIXME("SECPKG_CRED_ATTR_NAMES: stub\n"); + ret = SEC_E_UNSUPPORTED_FUNCTION; + } + else + ret = SEC_E_UNSUPPORTED_FUNCTION; + + return ret; +} + +/*********************************************************************** + * QueryCredentialsAttributesW + */ +static SECURITY_STATUS SEC_ENTRY ntlm_QueryCredentialsAttributesW( + PCredHandle phCredential, ULONG ulAttribute, PVOID pBuffer) +{ + SECURITY_STATUS ret; + + TRACE("(%p, %d, %p)\n", phCredential, ulAttribute, pBuffer); + + if(ulAttribute == SECPKG_ATTR_NAMES) + { + FIXME("SECPKG_CRED_ATTR_NAMES: stub\n"); + ret = SEC_E_UNSUPPORTED_FUNCTION; + } + else + ret = SEC_E_UNSUPPORTED_FUNCTION; + + return ret; +} + +static char *ntlm_GetUsernameArg(LPCWSTR userW, INT userW_length) +{ + static const char username_arg[] = "--username="; + char *user; + int unixcp_size; + + unixcp_size = WideCharToMultiByte(CP_UNIXCP, WC_NO_BEST_FIT_CHARS, + userW, userW_length, NULL, 0, NULL, NULL) + sizeof(username_arg); + user = HeapAlloc(GetProcessHeap(), 0, unixcp_size); + if (!user) return NULL; + memcpy(user, username_arg, sizeof(username_arg) - 1); + WideCharToMultiByte(CP_UNIXCP, WC_NO_BEST_FIT_CHARS, userW, userW_length, + user + sizeof(username_arg) - 1, + unixcp_size - sizeof(username_arg) + 1, NULL, NULL); + user[unixcp_size - 1] = '\0'; + return user; +} + +static char *ntlm_GetDomainArg(LPCWSTR domainW, INT domainW_length) +{ + static const char domain_arg[] = "--domain="; + char *domain; + int unixcp_size; + + unixcp_size = WideCharToMultiByte(CP_UNIXCP, WC_NO_BEST_FIT_CHARS, + domainW, domainW_length, NULL, 0, NULL, NULL) + sizeof(domain_arg); + domain = HeapAlloc(GetProcessHeap(), 0, unixcp_size); + if (!domain) return NULL; + memcpy(domain, domain_arg, sizeof(domain_arg) - 1); + WideCharToMultiByte(CP_UNIXCP, WC_NO_BEST_FIT_CHARS, domainW, + domainW_length, domain + sizeof(domain_arg) - 1, + unixcp_size - sizeof(domain) + 1, NULL, NULL); + domain[unixcp_size - 1] = '\0'; + return domain; +} + +/*********************************************************************** + * AcquireCredentialsHandleW + */ +SECURITY_STATUS SEC_ENTRY ntlm_AcquireCredentialsHandleW( + SEC_WCHAR *pszPrincipal, SEC_WCHAR *pszPackage, ULONG fCredentialUse, + PLUID pLogonID, PVOID pAuthData, SEC_GET_KEY_FN pGetKeyFn, + PVOID pGetKeyArgument, PCredHandle phCredential, PTimeStamp ptsExpiry) +{ + SECURITY_STATUS ret; + PNtlmCredentials ntlm_cred; + LPWSTR domain = NULL, user = NULL, password = NULL; + PSEC_WINNT_AUTH_IDENTITY_W auth_data = NULL; + + TRACE("(%s, %s, 0x%08x, %p, %p, %p, %p, %p, %p)\n", + debugstr_w(pszPrincipal), debugstr_w(pszPackage), fCredentialUse, + pLogonID, pAuthData, pGetKeyFn, pGetKeyArgument, phCredential, ptsExpiry); + + switch(fCredentialUse) + { + case SECPKG_CRED_INBOUND: + ntlm_cred = HeapAlloc(GetProcessHeap(), 0, sizeof(*ntlm_cred)); + if (!ntlm_cred) + ret = SEC_E_INSUFFICIENT_MEMORY; + else + { + ntlm_cred->mode = NTLM_SERVER; + ntlm_cred->username_arg = NULL; + ntlm_cred->domain_arg = NULL; + ntlm_cred->password = NULL; + ntlm_cred->pwlen = 0; + ntlm_cred->no_cached_credentials = 0; + + phCredential->dwUpper = fCredentialUse; + phCredential->dwLower = (ULONG_PTR)ntlm_cred; + ret = SEC_E_OK; + } + break; + case SECPKG_CRED_OUTBOUND: + { + auth_data = pAuthData; + ntlm_cred = HeapAlloc(GetProcessHeap(), 0, sizeof(*ntlm_cred)); + if (!ntlm_cred) + { + ret = SEC_E_INSUFFICIENT_MEMORY; + break; + } + ntlm_cred->mode = NTLM_CLIENT; + ntlm_cred->username_arg = NULL; + ntlm_cred->domain_arg = NULL; + ntlm_cred->password = NULL; + ntlm_cred->pwlen = 0; + ntlm_cred->no_cached_credentials = 0; + + if(pAuthData != NULL) + { + int domain_len = 0, user_len = 0, password_len = 0; + + if (auth_data->Flags & SEC_WINNT_AUTH_IDENTITY_ANSI) + { + if (auth_data->DomainLength) + { + domain_len = MultiByteToWideChar(CP_ACP, 0, (char *)auth_data->Domain, + auth_data->DomainLength, NULL, 0); + domain = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR) * domain_len); + if(!domain) + { + ret = SEC_E_INSUFFICIENT_MEMORY; + break; + } + MultiByteToWideChar(CP_ACP, 0, (char *)auth_data->Domain, auth_data->DomainLength, + domain, domain_len); + } + + if (auth_data->UserLength) + { + user_len = MultiByteToWideChar(CP_ACP, 0, (char *)auth_data->User, + auth_data->UserLength, NULL, 0); + user = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR) * user_len); + if(!user) + { + ret = SEC_E_INSUFFICIENT_MEMORY; + break; + } + MultiByteToWideChar(CP_ACP, 0, (char *)auth_data->User, auth_data->UserLength, + user, user_len); + } + + if (auth_data->PasswordLength) + { + password_len = MultiByteToWideChar(CP_ACP, 0,(char *)auth_data->Password, + auth_data->PasswordLength, NULL, 0); + password = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR) * password_len); + if(!password) + { + ret = SEC_E_INSUFFICIENT_MEMORY; + break; + } + MultiByteToWideChar(CP_ACP, 0, (char *)auth_data->Password, auth_data->PasswordLength, + password, password_len); + } + } + else + { + domain = auth_data->Domain; + domain_len = auth_data->DomainLength; + + user = auth_data->User; + user_len = auth_data->UserLength; + + password = auth_data->Password; + password_len = auth_data->PasswordLength; + } + + TRACE("Username is %s\n", debugstr_wn(user, user_len)); + TRACE("Domain name is %s\n", debugstr_wn(domain, domain_len)); + + ntlm_cred->username_arg = ntlm_GetUsernameArg(user, user_len); + ntlm_cred->domain_arg = ntlm_GetDomainArg(domain, domain_len); + + if(password_len != 0) + { + ntlm_cred->pwlen = WideCharToMultiByte(CP_UNIXCP, WC_NO_BEST_FIT_CHARS, password, + password_len, NULL, 0, NULL, NULL); + + ntlm_cred->password = HeapAlloc(GetProcessHeap(), 0, + ntlm_cred->pwlen); + if(!ntlm_cred->password) + { + ret = SEC_E_INSUFFICIENT_MEMORY; + break; + } + + WideCharToMultiByte(CP_UNIXCP, WC_NO_BEST_FIT_CHARS, password, password_len, + ntlm_cred->password, ntlm_cred->pwlen, NULL, NULL); + } + } + + phCredential->dwUpper = fCredentialUse; + phCredential->dwLower = (ULONG_PTR)ntlm_cred; + TRACE("ACH phCredential->dwUpper: 0x%08lx, dwLower: 0x%08lx\n", + phCredential->dwUpper, phCredential->dwLower); + ret = SEC_E_OK; + break; + } + case SECPKG_CRED_BOTH: + FIXME("AcquireCredentialsHandle: SECPKG_CRED_BOTH stub\n"); + ret = SEC_E_UNSUPPORTED_FUNCTION; + phCredential = NULL; + break; + default: + phCredential = NULL; + ret = SEC_E_UNKNOWN_CREDENTIALS; + } + + if (auth_data && auth_data->Flags & SEC_WINNT_AUTH_IDENTITY_ANSI) + { + HeapFree(GetProcessHeap(), 0, domain); + HeapFree(GetProcessHeap(), 0, user); + HeapFree(GetProcessHeap(), 0, password); + } + return ret; +} + +/*********************************************************************** + * AcquireCredentialsHandleA + */ +static SECURITY_STATUS SEC_ENTRY ntlm_AcquireCredentialsHandleA( + SEC_CHAR *pszPrincipal, SEC_CHAR *pszPackage, ULONG fCredentialUse, + PLUID pLogonID, PVOID pAuthData, SEC_GET_KEY_FN pGetKeyFn, + PVOID pGetKeyArgument, PCredHandle phCredential, PTimeStamp ptsExpiry) +{ + SECURITY_STATUS ret; + int user_sizeW, domain_sizeW, passwd_sizeW; + + SEC_WCHAR *user = NULL, *domain = NULL, *passwd = NULL, *package = NULL; + + PSEC_WINNT_AUTH_IDENTITY_W pAuthDataW = NULL; + PSEC_WINNT_AUTH_IDENTITY_A identity = NULL; + + TRACE("(%s, %s, 0x%08x, %p, %p, %p, %p, %p, %p)\n", + debugstr_a(pszPrincipal), debugstr_a(pszPackage), fCredentialUse, + pLogonID, pAuthData, pGetKeyFn, pGetKeyArgument, phCredential, ptsExpiry); + + if(pszPackage != NULL) + { + int package_sizeW = MultiByteToWideChar(CP_ACP, 0, pszPackage, -1, + NULL, 0); + + package = HeapAlloc(GetProcessHeap(), 0, package_sizeW * + sizeof(SEC_WCHAR)); + MultiByteToWideChar(CP_ACP, 0, pszPackage, -1, package, package_sizeW); + } + + + if(pAuthData != NULL) + { + identity = pAuthData; + + if(identity->Flags == SEC_WINNT_AUTH_IDENTITY_ANSI) + { + pAuthDataW = HeapAlloc(GetProcessHeap(), 0, + sizeof(SEC_WINNT_AUTH_IDENTITY_W)); + + if(identity->UserLength != 0) + { + user_sizeW = MultiByteToWideChar(CP_ACP, 0, + (LPCSTR)identity->User, identity->UserLength, NULL, 0); + user = HeapAlloc(GetProcessHeap(), 0, user_sizeW * + sizeof(SEC_WCHAR)); + MultiByteToWideChar(CP_ACP, 0, (LPCSTR)identity->User, + identity->UserLength, user, user_sizeW); + } + else + { + user_sizeW = 0; + } + + if(identity->DomainLength != 0) + { + domain_sizeW = MultiByteToWideChar(CP_ACP, 0, + (LPCSTR)identity->Domain, identity->DomainLength, NULL, 0); + domain = HeapAlloc(GetProcessHeap(), 0, domain_sizeW + * sizeof(SEC_WCHAR)); + MultiByteToWideChar(CP_ACP, 0, (LPCSTR)identity->Domain, + identity->DomainLength, domain, domain_sizeW); + } + else + { + domain_sizeW = 0; + } + + if(identity->PasswordLength != 0) + { + passwd_sizeW = MultiByteToWideChar(CP_ACP, 0, + (LPCSTR)identity->Password, identity->PasswordLength, + NULL, 0); + passwd = HeapAlloc(GetProcessHeap(), 0, passwd_sizeW + * sizeof(SEC_WCHAR)); + MultiByteToWideChar(CP_ACP, 0, (LPCSTR)identity->Password, + identity->PasswordLength, passwd, passwd_sizeW); + } + else + { + passwd_sizeW = 0; + } + + pAuthDataW->Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE; + pAuthDataW->User = user; + pAuthDataW->UserLength = user_sizeW; + pAuthDataW->Domain = domain; + pAuthDataW->DomainLength = domain_sizeW; + pAuthDataW->Password = passwd; + pAuthDataW->PasswordLength = passwd_sizeW; + } + else + { + pAuthDataW = (PSEC_WINNT_AUTH_IDENTITY_W)identity; + } + } + + ret = ntlm_AcquireCredentialsHandleW(NULL, package, fCredentialUse, + pLogonID, pAuthDataW, pGetKeyFn, pGetKeyArgument, phCredential, + ptsExpiry); + + HeapFree(GetProcessHeap(), 0, package); + HeapFree(GetProcessHeap(), 0, user); + HeapFree(GetProcessHeap(), 0, domain); + HeapFree(GetProcessHeap(), 0, passwd); + if(pAuthDataW != (PSEC_WINNT_AUTH_IDENTITY_W)identity) + HeapFree(GetProcessHeap(), 0, pAuthDataW); + + return ret; +} + +/************************************************************************* + * ntlm_GetTokenBufferIndex + * Calculates the index of the secbuffer with BufferType == SECBUFFER_TOKEN + * Returns index if found or -1 if not found. + */ +static int ntlm_GetTokenBufferIndex(PSecBufferDesc pMessage) +{ + UINT i; + + TRACE("%p\n", pMessage); + + for( i = 0; i < pMessage->cBuffers; ++i ) + { + if(pMessage->pBuffers[i].BufferType == SECBUFFER_TOKEN) + return i; + } + + return -1; +} + +/************************************************************************* + * ntlm_GetDataBufferIndex + * Calculates the index of the first secbuffer with BufferType == SECBUFFER_DATA + * Returns index if found or -1 if not found. + */ +static int ntlm_GetDataBufferIndex(PSecBufferDesc pMessage) +{ + UINT i; + + TRACE("%p\n", pMessage); + + for( i = 0; i < pMessage->cBuffers; ++i ) + { + if(pMessage->pBuffers[i].BufferType == SECBUFFER_DATA) + return i; + } + + return -1; +} + +static BOOL ntlm_GetCachedCredential(const SEC_WCHAR *pszTargetName, PCREDENTIALW *cred) +{ + LPCWSTR p; + LPCWSTR pszHost; + LPWSTR pszHostOnly; + BOOL ret; + + if (!pszTargetName) + return FALSE; + + /* try to get the start of the hostname from service principal name (SPN) */ + pszHost = strchrW(pszTargetName, '/'); + if (pszHost) + { + /* skip slash character */ + pszHost++; + + /* find end of host by detecting start of instance port or start of referrer */ + p = strchrW(pszHost, ':'); + if (!p) + p = strchrW(pszHost, '/'); + if (!p) + p = pszHost + strlenW(pszHost); + } + else /* otherwise not an SPN, just a host */ + { + pszHost = pszTargetName; + p = pszHost + strlenW(pszHost); + } + + pszHostOnly = HeapAlloc(GetProcessHeap(), 0, (p - pszHost + 1) * sizeof(WCHAR)); + if (!pszHostOnly) + return FALSE; + + memcpy(pszHostOnly, pszHost, (p - pszHost) * sizeof(WCHAR)); + pszHostOnly[p - pszHost] = '\0'; + + ret = CredReadW(pszHostOnly, CRED_TYPE_DOMAIN_PASSWORD, 0, cred); + + HeapFree(GetProcessHeap(), 0, pszHostOnly); + return ret; +} + +/*********************************************************************** + * InitializeSecurityContextW + */ +SECURITY_STATUS SEC_ENTRY ntlm_InitializeSecurityContextW( + PCredHandle phCredential, PCtxtHandle phContext, SEC_WCHAR *pszTargetName, + ULONG fContextReq, ULONG Reserved1, ULONG TargetDataRep, + PSecBufferDesc pInput, ULONG Reserved2, PCtxtHandle phNewContext, + PSecBufferDesc pOutput, ULONG *pfContextAttr, PTimeStamp ptsExpiry) +{ + SECURITY_STATUS ret; + PNtlmCredentials ntlm_cred; + PNegoHelper helper = NULL; + ULONG ctxt_attr = 0; + char* buffer, *want_flags = NULL; + PBYTE bin; + int buffer_len, bin_len, max_len = NTLM_MAX_BUF; + int token_idx; + SEC_CHAR *username = NULL; + SEC_CHAR *domain = NULL; + SEC_CHAR *password = NULL; + + TRACE("%p %p %s 0x%08x %d %d %p %d %p %p %p %p\n", phCredential, phContext, + debugstr_w(pszTargetName), fContextReq, Reserved1, TargetDataRep, pInput, + Reserved1, phNewContext, pOutput, pfContextAttr, ptsExpiry); + + /**************************************** + * When communicating with the client, there can be the + * following reply packets: + * YR should be sent to the server + * PW should be sent back to helper with + * base64 encoded password + * AF client is done, blob should be + * sent to server with KK prefixed + * GF A string list of negotiated flags + * GK base64 encoded session key + * BH something broke + */ + /* The squid cache size is 2010 chars, and that's what ntlm_auth uses */ + + if(TargetDataRep == SECURITY_NETWORK_DREP){ + TRACE("Setting SECURITY_NETWORK_DREP\n"); + } + + buffer = HeapAlloc(GetProcessHeap(), 0, sizeof(char) * NTLM_MAX_BUF); + bin = HeapAlloc(GetProcessHeap(), 0, sizeof(BYTE) * NTLM_MAX_BUF); + + if((phContext == NULL) && (pInput == NULL)) + { + static char helper_protocol[] = "--helper-protocol=ntlmssp-client-1"; + static CHAR credentials_argv[] = "--use-cached-creds"; + SEC_CHAR *client_argv[5]; + int pwlen = 0; + + TRACE("First time in ISC()\n"); + + if(!phCredential) + { + ret = SEC_E_INVALID_HANDLE; + goto isc_end; + } + + /* As the server side of sspi never calls this, make sure that + * the handler is a client handler. + */ + ntlm_cred = (PNtlmCredentials)phCredential->dwLower; + if(ntlm_cred->mode != NTLM_CLIENT) + { + TRACE("Cred mode = %d\n", ntlm_cred->mode); + ret = SEC_E_INVALID_HANDLE; + goto isc_end; + } + + client_argv[0] = ntlm_auth; + client_argv[1] = helper_protocol; + if (!ntlm_cred->username_arg && !ntlm_cred->domain_arg) + { + LPWKSTA_USER_INFO_1 ui = NULL; + NET_API_STATUS status; + PCREDENTIALW cred; + + if (ntlm_GetCachedCredential(pszTargetName, &cred)) + { + LPWSTR p; + p = strchrW(cred->UserName, '\\'); + if (p) + { + domain = ntlm_GetDomainArg(cred->UserName, p - cred->UserName); + p++; + } + else + { + domain = ntlm_GetDomainArg(NULL, 0); + p = cred->UserName; + } + + username = ntlm_GetUsernameArg(p, -1); + + if(cred->CredentialBlobSize != 0) + { + pwlen = WideCharToMultiByte(CP_UNIXCP, + WC_NO_BEST_FIT_CHARS, (LPWSTR)cred->CredentialBlob, + cred->CredentialBlobSize / sizeof(WCHAR), NULL, 0, + NULL, NULL); + + password = HeapAlloc(GetProcessHeap(), 0, pwlen); + + WideCharToMultiByte(CP_UNIXCP, WC_NO_BEST_FIT_CHARS, + (LPWSTR)cred->CredentialBlob, + cred->CredentialBlobSize / sizeof(WCHAR), + password, pwlen, NULL, NULL); + } + + CredFree(cred); + + client_argv[2] = username; + client_argv[3] = domain; + client_argv[4] = NULL; + } + else + { + status = NetWkstaUserGetInfo(NULL, 1, (LPBYTE *)&ui); + if (status != NERR_Success || ui == NULL || ntlm_cred->no_cached_credentials) + { + ret = SEC_E_NO_CREDENTIALS; + goto isc_end; + } + username = ntlm_GetUsernameArg(ui->wkui1_username, -1); + NetApiBufferFree(ui); + + TRACE("using cached credentials\n"); + + client_argv[2] = username; + client_argv[3] = credentials_argv; + client_argv[4] = NULL; + } + } + else + { + client_argv[2] = ntlm_cred->username_arg; + client_argv[3] = ntlm_cred->domain_arg; + client_argv[4] = NULL; + } + + if((ret = fork_helper(&helper, ntlm_auth, client_argv)) != SEC_E_OK) + goto isc_end; + + helper->mode = NTLM_CLIENT; + helper->session_key = HeapAlloc(GetProcessHeap(), 0, 16); + if (!helper->session_key) + { + cleanup_helper(helper); + ret = SEC_E_INSUFFICIENT_MEMORY; + goto isc_end; + } + + /* Generate the dummy session key = MD4(MD4(password))*/ + if(password || ntlm_cred->password) + { + SEC_WCHAR *unicode_password; + int passwd_lenW; + + TRACE("Converting password to unicode.\n"); + passwd_lenW = MultiByteToWideChar(CP_ACP, 0, + password ? password : ntlm_cred->password, + password ? pwlen : ntlm_cred->pwlen, + NULL, 0); + unicode_password = HeapAlloc(GetProcessHeap(), 0, + passwd_lenW * sizeof(SEC_WCHAR)); + MultiByteToWideChar(CP_ACP, 0, password ? password : ntlm_cred->password, + password ? pwlen : ntlm_cred->pwlen, unicode_password, passwd_lenW); + + SECUR32_CreateNTLM1SessionKey((PBYTE)unicode_password, + passwd_lenW * sizeof(SEC_WCHAR), helper->session_key); + + HeapFree(GetProcessHeap(), 0, unicode_password); + } + else + memset(helper->session_key, 0, 16); + + /* Allocate space for a maximal string of + * "SF NTLMSSP_FEATURE_SIGN NTLMSSP_FEATURE_SEAL + * NTLMSSP_FEATURE_SESSION_KEY" + */ + want_flags = HeapAlloc(GetProcessHeap(), 0, 73); + if(want_flags == NULL) + { + cleanup_helper(helper); + ret = SEC_E_INSUFFICIENT_MEMORY; + goto isc_end; + } + lstrcpyA(want_flags, "SF"); + if(fContextReq & ISC_REQ_CONFIDENTIALITY) + { + if(strstr(want_flags, "NTLMSSP_FEATURE_SEAL") == NULL) + lstrcatA(want_flags, " NTLMSSP_FEATURE_SEAL"); + } + if(fContextReq & ISC_REQ_CONNECTION) + ctxt_attr |= ISC_RET_CONNECTION; + if(fContextReq & ISC_REQ_EXTENDED_ERROR) + ctxt_attr |= ISC_RET_EXTENDED_ERROR; + if(fContextReq & ISC_REQ_INTEGRITY) + { + if(strstr(want_flags, "NTLMSSP_FEATURE_SIGN") == NULL) + lstrcatA(want_flags, " NTLMSSP_FEATURE_SIGN"); + } + if(fContextReq & ISC_REQ_MUTUAL_AUTH) + ctxt_attr |= ISC_RET_MUTUAL_AUTH; + if(fContextReq & ISC_REQ_REPLAY_DETECT) + { + if(strstr(want_flags, "NTLMSSP_FEATURE_SIGN") == NULL) + lstrcatA(want_flags, " NTLMSSP_FEATURE_SIGN"); + } + if(fContextReq & ISC_REQ_SEQUENCE_DETECT) + { + if(strstr(want_flags, "NTLMSSP_FEATURE_SIGN") == NULL) + lstrcatA(want_flags, " NTLMSSP_FEATURE_SIGN"); + } + if(fContextReq & ISC_REQ_STREAM) + FIXME("ISC_REQ_STREAM\n"); + if(fContextReq & ISC_REQ_USE_DCE_STYLE) + ctxt_attr |= ISC_RET_USED_DCE_STYLE; + if(fContextReq & ISC_REQ_DELEGATE) + ctxt_attr |= ISC_RET_DELEGATE; + + /* If no password is given, try to use cached credentials. Fall back to an empty + * password if this failed. */ + if(!password && !ntlm_cred->password) + { + lstrcpynA(buffer, "OK", max_len-1); + if((ret = run_helper(helper, buffer, max_len, &buffer_len)) != SEC_E_OK) + { + cleanup_helper(helper); + goto isc_end; + } + /* If the helper replied with "PW", using cached credentials failed */ + if(!strncmp(buffer, "PW", 2)) + { + TRACE("Using cached credentials failed.\n"); + lstrcpynA(buffer, "PW AA==", max_len-1); + } + else /* Just do a noop on the next run */ + lstrcpynA(buffer, "OK", max_len-1); + } + else + { + lstrcpynA(buffer, "PW ", max_len-1); + if((ret = encodeBase64(password ? (unsigned char *)password : (unsigned char *)ntlm_cred->password, + password ? pwlen : ntlm_cred->pwlen, buffer+3, + max_len-3, &buffer_len)) != SEC_E_OK) + { + cleanup_helper(helper); + goto isc_end; + } + + } + + TRACE("Sending to helper: %s\n", debugstr_a(buffer)); + if((ret = run_helper(helper, buffer, max_len, &buffer_len)) != SEC_E_OK) + { + cleanup_helper(helper); + goto isc_end; + } + + TRACE("Helper returned %s\n", debugstr_a(buffer)); + + if(lstrlenA(want_flags) > 2) + { + TRACE("Want flags are %s\n", debugstr_a(want_flags)); + lstrcpynA(buffer, want_flags, max_len-1); + if((ret = run_helper(helper, buffer, max_len, &buffer_len)) + != SEC_E_OK) + { + cleanup_helper(helper); + goto isc_end; + } + if(!strncmp(buffer, "BH", 2)) + ERR("Helper doesn't understand new command set. Expect more things to fail.\n"); + } + + lstrcpynA(buffer, "YR", max_len-1); + + if((ret = run_helper(helper, buffer, max_len, &buffer_len)) != SEC_E_OK) + { + cleanup_helper(helper); + goto isc_end; + } + + TRACE("%s\n", buffer); + + if(strncmp(buffer, "YR ", 3) != 0) + { + /* Something borked */ + TRACE("Helper returned %c%c\n", buffer[0], buffer[1]); + ret = SEC_E_INTERNAL_ERROR; + cleanup_helper(helper); + goto isc_end; + } + if((ret = decodeBase64(buffer+3, buffer_len-3, bin, + max_len-1, &bin_len)) != SEC_E_OK) + { + cleanup_helper(helper); + goto isc_end; + } + + /* put the decoded client blob into the out buffer */ + + phNewContext->dwUpper = ctxt_attr; + phNewContext->dwLower = (ULONG_PTR)helper; + + ret = SEC_I_CONTINUE_NEEDED; + } + else + { + int input_token_idx; + + /* handle second call here */ + /* encode server data to base64 */ + if (!pInput || ((input_token_idx = ntlm_GetTokenBufferIndex(pInput)) == -1)) + { + ret = SEC_E_INVALID_TOKEN; + goto isc_end; + } + + if(!phContext) + { + ret = SEC_E_INVALID_HANDLE; + goto isc_end; + } + + /* As the server side of sspi never calls this, make sure that + * the handler is a client handler. + */ + helper = (PNegoHelper)phContext->dwLower; + if(helper->mode != NTLM_CLIENT) + { + TRACE("Helper mode = %d\n", helper->mode); + ret = SEC_E_INVALID_HANDLE; + goto isc_end; + } + + if (!pInput->pBuffers[input_token_idx].pvBuffer) + { + ret = SEC_E_INTERNAL_ERROR; + goto isc_end; + } + + if(pInput->pBuffers[input_token_idx].cbBuffer > max_len) + { + TRACE("pInput->pBuffers[%d].cbBuffer is: %d\n", + input_token_idx, + pInput->pBuffers[input_token_idx].cbBuffer); + ret = SEC_E_INVALID_TOKEN; + goto isc_end; + } + else + bin_len = pInput->pBuffers[input_token_idx].cbBuffer; + + memcpy(bin, pInput->pBuffers[input_token_idx].pvBuffer, bin_len); + + lstrcpynA(buffer, "TT ", max_len-1); + + if((ret = encodeBase64(bin, bin_len, buffer+3, + max_len-3, &buffer_len)) != SEC_E_OK) + goto isc_end; + + TRACE("Server sent: %s\n", debugstr_a(buffer)); + + /* send TT base64 blob to ntlm_auth */ + if((ret = run_helper(helper, buffer, max_len, &buffer_len)) != SEC_E_OK) + goto isc_end; + + TRACE("Helper replied: %s\n", debugstr_a(buffer)); + + if( (strncmp(buffer, "KK ", 3) != 0) && + (strncmp(buffer, "AF ", 3) !=0)) + { + TRACE("Helper returned %c%c\n", buffer[0], buffer[1]); + ret = SEC_E_INVALID_TOKEN; + goto isc_end; + } + + /* decode the blob and send it to server */ + if((ret = decodeBase64(buffer+3, buffer_len-3, bin, max_len, + &bin_len)) != SEC_E_OK) + { + goto isc_end; + } + + phNewContext->dwUpper = ctxt_attr; + phNewContext->dwLower = (ULONG_PTR)helper; + + ret = SEC_E_OK; + } + + /* put the decoded client blob into the out buffer */ + + if (!pOutput || ((token_idx = ntlm_GetTokenBufferIndex(pOutput)) == -1)) + { + TRACE("no SECBUFFER_TOKEN buffer could be found\n"); + ret = SEC_E_BUFFER_TOO_SMALL; + if ((phContext == NULL) && (pInput == NULL)) + { + cleanup_helper(helper); + phNewContext->dwUpper = 0; + phNewContext->dwLower = 0; + } + goto isc_end; + } + + if (fContextReq & ISC_REQ_ALLOCATE_MEMORY) + { + pOutput->pBuffers[token_idx].pvBuffer = HeapAlloc(GetProcessHeap(), 0, bin_len); + pOutput->pBuffers[token_idx].cbBuffer = bin_len; + } + else if (pOutput->pBuffers[token_idx].cbBuffer < bin_len) + { + TRACE("out buffer is NULL or has not enough space\n"); + ret = SEC_E_BUFFER_TOO_SMALL; + if ((phContext == NULL) && (pInput == NULL)) + { + cleanup_helper(helper); + phNewContext->dwUpper = 0; + phNewContext->dwLower = 0; + } + goto isc_end; + } + + if (!pOutput->pBuffers[token_idx].pvBuffer) + { + TRACE("out buffer is NULL\n"); + ret = SEC_E_INTERNAL_ERROR; + if ((phContext == NULL) && (pInput == NULL)) + { + cleanup_helper(helper); + phNewContext->dwUpper = 0; + phNewContext->dwLower = 0; + } + goto isc_end; + } + + pOutput->pBuffers[token_idx].cbBuffer = bin_len; + memcpy(pOutput->pBuffers[token_idx].pvBuffer, bin, bin_len); + + if(ret == SEC_E_OK) + { + TRACE("Getting negotiated flags\n"); + lstrcpynA(buffer, "GF", max_len - 1); + if((ret = run_helper(helper, buffer, max_len, &buffer_len)) != SEC_E_OK) + goto isc_end; + + if(buffer_len < 3) + { + TRACE("No flags negotiated.\n"); + helper->neg_flags = 0l; + } + else + { + TRACE("Negotiated %s\n", debugstr_a(buffer)); + sscanf(buffer + 3, "%lx", &(helper->neg_flags)); + TRACE("Stored 0x%08x as flags\n", helper->neg_flags); + } + + TRACE("Getting session key\n"); + lstrcpynA(buffer, "GK", max_len - 1); + if((ret = run_helper(helper, buffer, max_len, &buffer_len)) != SEC_E_OK) + goto isc_end; + + if(strncmp(buffer, "BH", 2) == 0) + TRACE("No key negotiated.\n"); + else if(strncmp(buffer, "GK ", 3) == 0) + { + if((ret = decodeBase64(buffer+3, buffer_len-3, bin, max_len, + &bin_len)) != SEC_E_OK) + { + TRACE("Failed to decode session key\n"); + } + TRACE("Session key is %s\n", debugstr_a(buffer+3)); + HeapFree(GetProcessHeap(), 0, helper->session_key); + helper->session_key = HeapAlloc(GetProcessHeap(), 0, bin_len); + if(!helper->session_key) + { + ret = SEC_E_INSUFFICIENT_MEMORY; + goto isc_end; + } + memcpy(helper->session_key, bin, bin_len); + } + + helper->crypt.ntlm.a4i = SECUR32_arc4Alloc(); + SECUR32_arc4Init(helper->crypt.ntlm.a4i, helper->session_key, 16); + helper->crypt.ntlm.seq_num = 0l; + SECUR32_CreateNTLM2SubKeys(helper); + helper->crypt.ntlm2.send_a4i = SECUR32_arc4Alloc(); + helper->crypt.ntlm2.recv_a4i = SECUR32_arc4Alloc(); + SECUR32_arc4Init(helper->crypt.ntlm2.send_a4i, + helper->crypt.ntlm2.send_seal_key, 16); + SECUR32_arc4Init(helper->crypt.ntlm2.recv_a4i, + helper->crypt.ntlm2.recv_seal_key, 16); + helper->crypt.ntlm2.send_seq_no = 0l; + helper->crypt.ntlm2.recv_seq_no = 0l; + } + +isc_end: + HeapFree(GetProcessHeap(), 0, username); + HeapFree(GetProcessHeap(), 0, domain); + HeapFree(GetProcessHeap(), 0, password); + HeapFree(GetProcessHeap(), 0, want_flags); + HeapFree(GetProcessHeap(), 0, buffer); + HeapFree(GetProcessHeap(), 0, bin); + return ret; +} + +/*********************************************************************** + * InitializeSecurityContextA + */ +static SECURITY_STATUS SEC_ENTRY ntlm_InitializeSecurityContextA( + PCredHandle phCredential, PCtxtHandle phContext, SEC_CHAR *pszTargetName, + ULONG fContextReq, ULONG Reserved1, ULONG TargetDataRep, + PSecBufferDesc pInput,ULONG Reserved2, PCtxtHandle phNewContext, + PSecBufferDesc pOutput, ULONG *pfContextAttr, PTimeStamp ptsExpiry) +{ + SECURITY_STATUS ret; + SEC_WCHAR *target = NULL; + + TRACE("%p %p %s %d %d %d %p %d %p %p %p %p\n", phCredential, phContext, + debugstr_a(pszTargetName), fContextReq, Reserved1, TargetDataRep, pInput, + Reserved1, phNewContext, pOutput, pfContextAttr, ptsExpiry); + + if(pszTargetName != NULL) + { + int target_size = MultiByteToWideChar(CP_ACP, 0, pszTargetName, + strlen(pszTargetName)+1, NULL, 0); + target = HeapAlloc(GetProcessHeap(), 0, target_size * + sizeof(SEC_WCHAR)); + MultiByteToWideChar(CP_ACP, 0, pszTargetName, strlen(pszTargetName)+1, + target, target_size); + } + + ret = ntlm_InitializeSecurityContextW(phCredential, phContext, target, + fContextReq, Reserved1, TargetDataRep, pInput, Reserved2, + phNewContext, pOutput, pfContextAttr, ptsExpiry); + + HeapFree(GetProcessHeap(), 0, target); + return ret; +} + +/*********************************************************************** + * AcceptSecurityContext + */ +SECURITY_STATUS SEC_ENTRY ntlm_AcceptSecurityContext( + PCredHandle phCredential, PCtxtHandle phContext, PSecBufferDesc pInput, + ULONG fContextReq, ULONG TargetDataRep, PCtxtHandle phNewContext, + PSecBufferDesc pOutput, ULONG *pfContextAttr, PTimeStamp ptsExpiry) +{ + SECURITY_STATUS ret; + char *buffer, *want_flags = NULL; + PBYTE bin; + int buffer_len, bin_len, max_len = NTLM_MAX_BUF; + ULONG ctxt_attr = 0; + PNegoHelper helper; + PNtlmCredentials ntlm_cred; + + TRACE("%p %p %p %d %d %p %p %p %p\n", phCredential, phContext, pInput, + fContextReq, TargetDataRep, phNewContext, pOutput, pfContextAttr, + ptsExpiry); + + buffer = HeapAlloc(GetProcessHeap(), 0, sizeof(char) * NTLM_MAX_BUF); + bin = HeapAlloc(GetProcessHeap(),0, sizeof(BYTE) * NTLM_MAX_BUF); + + if(TargetDataRep == SECURITY_NETWORK_DREP){ + TRACE("Using SECURITY_NETWORK_DREP\n"); + } + + if(phContext == NULL) + { + static CHAR server_helper_protocol[] = "--helper-protocol=squid-2.5-ntlmssp"; + SEC_CHAR *server_argv[] = { ntlm_auth, + server_helper_protocol, + NULL }; + + if (!phCredential) + { + ret = SEC_E_INVALID_HANDLE; + goto asc_end; + } + + ntlm_cred = (PNtlmCredentials)phCredential->dwLower; + + if(ntlm_cred->mode != NTLM_SERVER) + { + ret = SEC_E_INVALID_HANDLE; + goto asc_end; + } + + /* This is the first call to AcceptSecurityHandle */ + if(pInput == NULL) + { + ret = SEC_E_INCOMPLETE_MESSAGE; + goto asc_end; + } + + if(pInput->cBuffers < 1) + { + ret = SEC_E_INCOMPLETE_MESSAGE; + goto asc_end; + } + + if(pInput->pBuffers[0].cbBuffer > max_len) + { + ret = SEC_E_INVALID_TOKEN; + goto asc_end; + } + else + bin_len = pInput->pBuffers[0].cbBuffer; + + if( (ret = fork_helper(&helper, ntlm_auth, server_argv)) != + SEC_E_OK) + { + ret = SEC_E_INTERNAL_ERROR; + goto asc_end; + } + helper->mode = NTLM_SERVER; + + /* Handle all the flags */ + want_flags = HeapAlloc(GetProcessHeap(), 0, 73); + if(want_flags == NULL) + { + TRACE("Failed to allocate memory for the want_flags!\n"); + ret = SEC_E_INSUFFICIENT_MEMORY; + cleanup_helper(helper); + goto asc_end; + } + lstrcpyA(want_flags, "SF"); + if(fContextReq & ASC_REQ_ALLOCATE_MEMORY) + { + FIXME("ASC_REQ_ALLOCATE_MEMORY stub\n"); + } + if(fContextReq & ASC_REQ_CONFIDENTIALITY) + { + lstrcatA(want_flags, " NTLMSSP_FEATURE_SEAL"); + } + if(fContextReq & ASC_REQ_CONNECTION) + { + /* This is default, so we'll enable it */ + lstrcatA(want_flags, " NTLMSSP_FEATURE_SESSION_KEY"); + ctxt_attr |= ASC_RET_CONNECTION; + } + if(fContextReq & ASC_REQ_EXTENDED_ERROR) + { + FIXME("ASC_REQ_EXTENDED_ERROR stub\n"); + } + if(fContextReq & ASC_REQ_INTEGRITY) + { + lstrcatA(want_flags, " NTLMSSP_FEATURE_SIGN"); + } + if(fContextReq & ASC_REQ_MUTUAL_AUTH) + { + FIXME("ASC_REQ_MUTUAL_AUTH stub\n"); + } + if(fContextReq & ASC_REQ_REPLAY_DETECT) + { + FIXME("ASC_REQ_REPLAY_DETECT stub\n"); + } + if(fContextReq & ISC_REQ_SEQUENCE_DETECT) + { + FIXME("ASC_REQ_SEQUENCE_DETECT stub\n"); + } + if(fContextReq & ISC_REQ_STREAM) + { + FIXME("ASC_REQ_STREAM stub\n"); + } + /* Done with the flags */ + + if(lstrlenA(want_flags) > 3) + { + TRACE("Server set want_flags: %s\n", debugstr_a(want_flags)); + lstrcpynA(buffer, want_flags, max_len - 1); + if((ret = run_helper(helper, buffer, max_len, &buffer_len)) != + SEC_E_OK) + { + cleanup_helper(helper); + goto asc_end; + } + if(!strncmp(buffer, "BH", 2)) + TRACE("Helper doesn't understand new command set\n"); + } + + /* This is the YR request from the client, encode to base64 */ + + memcpy(bin, pInput->pBuffers[0].pvBuffer, bin_len); + + lstrcpynA(buffer, "YR ", max_len-1); + + if((ret = encodeBase64(bin, bin_len, buffer+3, max_len-3, + &buffer_len)) != SEC_E_OK) + { + cleanup_helper(helper); + goto asc_end; + } + + TRACE("Client sent: %s\n", debugstr_a(buffer)); + + if((ret = run_helper(helper, buffer, max_len, &buffer_len)) != + SEC_E_OK) + { + cleanup_helper(helper); + goto asc_end; + } + + TRACE("Reply from ntlm_auth: %s\n", debugstr_a(buffer)); + /* The expected answer is TT */ + + if(strncmp(buffer, "TT ", 3) != 0) + { + ret = SEC_E_INTERNAL_ERROR; + cleanup_helper(helper); + goto asc_end; + } + + if((ret = decodeBase64(buffer+3, buffer_len-3, bin, max_len, + &bin_len)) != SEC_E_OK) + { + cleanup_helper(helper); + goto asc_end; + } + + /* send this to the client */ + if(pOutput == NULL) + { + ret = SEC_E_INSUFFICIENT_MEMORY; + cleanup_helper(helper); + goto asc_end; + } + + if(pOutput->cBuffers < 1) + { + ret = SEC_E_INSUFFICIENT_MEMORY; + cleanup_helper(helper); + goto asc_end; + } + + pOutput->pBuffers[0].cbBuffer = bin_len; + pOutput->pBuffers[0].BufferType = SECBUFFER_DATA; + memcpy(pOutput->pBuffers[0].pvBuffer, bin, bin_len); + ret = SEC_I_CONTINUE_NEEDED; + + } + else + { + /* we expect a KK request from client */ + if(pInput == NULL) + { + ret = SEC_E_INCOMPLETE_MESSAGE; + goto asc_end; + } + + if(pInput->cBuffers < 1) + { + ret = SEC_E_INCOMPLETE_MESSAGE; + goto asc_end; + } + + helper = (PNegoHelper)phContext->dwLower; + + if(helper->mode != NTLM_SERVER) + { + ret = SEC_E_INVALID_HANDLE; + goto asc_end; + } + + if(pInput->pBuffers[0].cbBuffer > max_len) + { + ret = SEC_E_INVALID_TOKEN; + goto asc_end; + } + else + bin_len = pInput->pBuffers[0].cbBuffer; + + memcpy(bin, pInput->pBuffers[0].pvBuffer, bin_len); + + lstrcpynA(buffer, "KK ", max_len-1); + + if((ret = encodeBase64(bin, bin_len, buffer+3, max_len-3, + &buffer_len)) != SEC_E_OK) + { + goto asc_end; + } + + TRACE("Client sent: %s\n", debugstr_a(buffer)); + + if((ret = run_helper(helper, buffer, max_len, &buffer_len)) != + SEC_E_OK) + { + goto asc_end; + } + + TRACE("Reply from ntlm_auth: %s\n", debugstr_a(buffer)); + + /* At this point, we get a NA if the user didn't authenticate, but a BH + * if ntlm_auth could not connect to winbindd. Apart from running Wine + * as root, there is no way to fix this for now, so just handle this as + * a failed login. */ + if(strncmp(buffer, "AF ", 3) != 0) + { + if(strncmp(buffer, "NA ", 3) == 0) + { + ret = SEC_E_LOGON_DENIED; + goto asc_end; + } + else + { + size_t ntlm_pipe_err_v3_len = strlen("BH NT_STATUS_ACCESS_DENIED"); + size_t ntlm_pipe_err_v4_len = strlen("BH NT_STATUS_UNSUCCESSFUL"); + + if( (buffer_len >= ntlm_pipe_err_v3_len && + strncmp(buffer, "BH NT_STATUS_ACCESS_DENIED", ntlm_pipe_err_v3_len) == 0) || + (buffer_len >= ntlm_pipe_err_v4_len && + strncmp(buffer, "BH NT_STATUS_UNSUCCESSFUL", ntlm_pipe_err_v4_len) == 0) ) + { + TRACE("Connection to winbindd failed\n"); + ret = SEC_E_LOGON_DENIED; + } + else + ret = SEC_E_INTERNAL_ERROR; + + goto asc_end; + } + } + pOutput->pBuffers[0].cbBuffer = 0; + + TRACE("Getting negotiated flags\n"); + lstrcpynA(buffer, "GF", max_len - 1); + if((ret = run_helper(helper, buffer, max_len, &buffer_len)) != SEC_E_OK) + goto asc_end; + + if(buffer_len < 3) + { + TRACE("No flags negotiated, or helper does not support GF command\n"); + } + else + { + TRACE("Negotiated %s\n", debugstr_a(buffer)); + sscanf(buffer + 3, "%lx", &(helper->neg_flags)); + TRACE("Stored 0x%08x as flags\n", helper->neg_flags); + } + + TRACE("Getting session key\n"); + lstrcpynA(buffer, "GK", max_len - 1); + if((ret = run_helper(helper, buffer, max_len, &buffer_len)) != SEC_E_OK) + goto asc_end; + + if(buffer_len < 3) + TRACE("Helper does not support GK command\n"); + else + { + if(strncmp(buffer, "BH ", 3) == 0) + { + TRACE("Helper sent %s\n", debugstr_a(buffer+3)); + HeapFree(GetProcessHeap(), 0, helper->session_key); + helper->session_key = HeapAlloc(GetProcessHeap(), 0, 16); + if (!helper->session_key) + { + ret = SEC_E_INSUFFICIENT_MEMORY; + goto asc_end; + } + /*FIXME: Generate the dummy session key = MD4(MD4(password))*/ + memset(helper->session_key, 0 , 16); + } + else if(strncmp(buffer, "GK ", 3) == 0) + { + if((ret = decodeBase64(buffer+3, buffer_len-3, bin, max_len, + &bin_len)) != SEC_E_OK) + { + TRACE("Failed to decode session key\n"); + } + TRACE("Session key is %s\n", debugstr_a(buffer+3)); + HeapFree(GetProcessHeap(), 0, helper->session_key); + helper->session_key = HeapAlloc(GetProcessHeap(), 0, 16); + if(!helper->session_key) + { + ret = SEC_E_INSUFFICIENT_MEMORY; + goto asc_end; + } + memcpy(helper->session_key, bin, 16); + } + } + helper->crypt.ntlm.a4i = SECUR32_arc4Alloc(); + SECUR32_arc4Init(helper->crypt.ntlm.a4i, helper->session_key, 16); + helper->crypt.ntlm.seq_num = 0l; + } + + phNewContext->dwUpper = ctxt_attr; + phNewContext->dwLower = (ULONG_PTR)helper; + +asc_end: + HeapFree(GetProcessHeap(), 0, want_flags); + HeapFree(GetProcessHeap(), 0, buffer); + HeapFree(GetProcessHeap(), 0, bin); + return ret; +} + +/*********************************************************************** + * CompleteAuthToken + */ +static SECURITY_STATUS SEC_ENTRY ntlm_CompleteAuthToken(PCtxtHandle phContext, + PSecBufferDesc pToken) +{ + /* We never need to call CompleteAuthToken anyway */ + TRACE("%p %p\n", phContext, pToken); + if (!phContext) + return SEC_E_INVALID_HANDLE; + + return SEC_E_OK; +} + +/*********************************************************************** + * DeleteSecurityContext + */ +SECURITY_STATUS SEC_ENTRY ntlm_DeleteSecurityContext(PCtxtHandle phContext) +{ + PNegoHelper helper; + + TRACE("%p\n", phContext); + if (!phContext) + return SEC_E_INVALID_HANDLE; + + helper = (PNegoHelper)phContext->dwLower; + + phContext->dwUpper = 0; + phContext->dwLower = 0; + + SECUR32_arc4Cleanup(helper->crypt.ntlm.a4i); + SECUR32_arc4Cleanup(helper->crypt.ntlm2.send_a4i); + SECUR32_arc4Cleanup(helper->crypt.ntlm2.recv_a4i); + HeapFree(GetProcessHeap(), 0, helper->crypt.ntlm2.send_sign_key); + HeapFree(GetProcessHeap(), 0, helper->crypt.ntlm2.send_seal_key); + HeapFree(GetProcessHeap(), 0, helper->crypt.ntlm2.recv_sign_key); + HeapFree(GetProcessHeap(), 0, helper->crypt.ntlm2.recv_seal_key); + + cleanup_helper(helper); + + return SEC_E_OK; +} + +/*********************************************************************** + * QueryContextAttributesW + */ +SECURITY_STATUS SEC_ENTRY ntlm_QueryContextAttributesW(PCtxtHandle phContext, + ULONG ulAttribute, void *pBuffer) +{ + TRACE("%p %d %p\n", phContext, ulAttribute, pBuffer); + if (!phContext) + return SEC_E_INVALID_HANDLE; + + switch(ulAttribute) + { +#define _x(x) case (x) : FIXME(#x" stub\n"); break + _x(SECPKG_ATTR_ACCESS_TOKEN); + _x(SECPKG_ATTR_AUTHORITY); + _x(SECPKG_ATTR_DCE_INFO); + case SECPKG_ATTR_FLAGS: + { + PSecPkgContext_Flags spcf = (PSecPkgContext_Flags)pBuffer; + PNegoHelper helper = (PNegoHelper)phContext->dwLower; + + spcf->Flags = 0; + if(helper->neg_flags & NTLMSSP_NEGOTIATE_SIGN) + spcf->Flags |= ISC_RET_INTEGRITY; + if(helper->neg_flags & NTLMSSP_NEGOTIATE_SEAL) + spcf->Flags |= ISC_RET_CONFIDENTIALITY; + return SEC_E_OK; + } + _x(SECPKG_ATTR_KEY_INFO); + _x(SECPKG_ATTR_LIFESPAN); + _x(SECPKG_ATTR_NAMES); + _x(SECPKG_ATTR_NATIVE_NAMES); + _x(SECPKG_ATTR_NEGOTIATION_INFO); + _x(SECPKG_ATTR_PACKAGE_INFO); + _x(SECPKG_ATTR_PASSWORD_EXPIRY); + _x(SECPKG_ATTR_SESSION_KEY); + case SECPKG_ATTR_SIZES: + { + PSecPkgContext_Sizes spcs = (PSecPkgContext_Sizes)pBuffer; + spcs->cbMaxToken = NTLM_MAX_BUF; + spcs->cbMaxSignature = 16; + spcs->cbBlockSize = 0; + spcs->cbSecurityTrailer = 16; + return SEC_E_OK; + } + _x(SECPKG_ATTR_STREAM_SIZES); + _x(SECPKG_ATTR_TARGET_INFORMATION); +#undef _x + default: + TRACE("Unknown value %d passed for ulAttribute\n", ulAttribute); + } + + return SEC_E_UNSUPPORTED_FUNCTION; +} + +/*********************************************************************** + * QueryContextAttributesA + */ +SECURITY_STATUS SEC_ENTRY ntlm_QueryContextAttributesA(PCtxtHandle phContext, + ULONG ulAttribute, void *pBuffer) +{ + return ntlm_QueryContextAttributesW(phContext, ulAttribute, pBuffer); +} + +/*********************************************************************** + * ImpersonateSecurityContext + */ +static SECURITY_STATUS SEC_ENTRY ntlm_ImpersonateSecurityContext(PCtxtHandle phContext) +{ + SECURITY_STATUS ret; + + TRACE("%p\n", phContext); + if (phContext) + { + ret = SEC_E_UNSUPPORTED_FUNCTION; + } + else + { + ret = SEC_E_INVALID_HANDLE; + } + return ret; +} + +/*********************************************************************** + * RevertSecurityContext + */ +static SECURITY_STATUS SEC_ENTRY ntlm_RevertSecurityContext(PCtxtHandle phContext) +{ + SECURITY_STATUS ret; + + TRACE("%p\n", phContext); + if (phContext) + { + ret = SEC_E_UNSUPPORTED_FUNCTION; + } + else + { + ret = SEC_E_INVALID_HANDLE; + } + return ret; +} + +/*********************************************************************** + * ntlm_CreateSignature + * As both MakeSignature and VerifySignature need this, but different keys + * are needed for NTLM2, the logic goes into a helper function. + * To ensure maximal reusability, we can specify the direction as NTLM_SEND for + * signing/encrypting and NTLM_RECV for verifying/decrypting. When encrypting, + * the signature is encrypted after the message was encrypted, so + * CreateSignature shouldn't do it. In this case, encrypt_sig can be set to + * false. + */ +static SECURITY_STATUS ntlm_CreateSignature(PNegoHelper helper, PSecBufferDesc pMessage, + int token_idx, SignDirection direction, BOOL encrypt_sig) +{ + ULONG sign_version = 1; + UINT i; + PBYTE sig; + TRACE("%p, %p, %d, %d, %d\n", helper, pMessage, token_idx, direction, + encrypt_sig); + + sig = pMessage->pBuffers[token_idx].pvBuffer; + + if(helper->neg_flags & NTLMSSP_NEGOTIATE_NTLM2 && + helper->neg_flags & NTLMSSP_NEGOTIATE_SIGN) + { + BYTE digest[16]; + BYTE seq_no[4]; + HMAC_MD5_CTX hmac_md5_ctx; + + TRACE("Signing NTLM2 style\n"); + + if(direction == NTLM_SEND) + { + seq_no[0] = (helper->crypt.ntlm2.send_seq_no >> 0) & 0xff; + seq_no[1] = (helper->crypt.ntlm2.send_seq_no >> 8) & 0xff; + seq_no[2] = (helper->crypt.ntlm2.send_seq_no >> 16) & 0xff; + seq_no[3] = (helper->crypt.ntlm2.send_seq_no >> 24) & 0xff; + + ++(helper->crypt.ntlm2.send_seq_no); + + HMACMD5Init(&hmac_md5_ctx, helper->crypt.ntlm2.send_sign_key, 16); + } + else + { + seq_no[0] = (helper->crypt.ntlm2.recv_seq_no >> 0) & 0xff; + seq_no[1] = (helper->crypt.ntlm2.recv_seq_no >> 8) & 0xff; + seq_no[2] = (helper->crypt.ntlm2.recv_seq_no >> 16) & 0xff; + seq_no[3] = (helper->crypt.ntlm2.recv_seq_no >> 24) & 0xff; + + ++(helper->crypt.ntlm2.recv_seq_no); + + HMACMD5Init(&hmac_md5_ctx, helper->crypt.ntlm2.recv_sign_key, 16); + } + + HMACMD5Update(&hmac_md5_ctx, seq_no, 4); + for( i = 0; i < pMessage->cBuffers; ++i ) + { + if(pMessage->pBuffers[i].BufferType & SECBUFFER_DATA) + HMACMD5Update(&hmac_md5_ctx, pMessage->pBuffers[i].pvBuffer, + pMessage->pBuffers[i].cbBuffer); + } + + HMACMD5Final(&hmac_md5_ctx, digest); + + if(encrypt_sig && helper->neg_flags & NTLMSSP_NEGOTIATE_KEY_EXCHANGE) + { + if(direction == NTLM_SEND) + SECUR32_arc4Process(helper->crypt.ntlm2.send_a4i, digest, 8); + else + SECUR32_arc4Process(helper->crypt.ntlm2.recv_a4i, digest, 8); + } + + /* The NTLM2 signature is the sign version */ + sig[ 0] = (sign_version >> 0) & 0xff; + sig[ 1] = (sign_version >> 8) & 0xff; + sig[ 2] = (sign_version >> 16) & 0xff; + sig[ 3] = (sign_version >> 24) & 0xff; + /* The first 8 bytes of the digest */ + memcpy(sig+4, digest, 8); + /* And the sequence number */ + memcpy(sig+12, seq_no, 4); + + pMessage->pBuffers[token_idx].cbBuffer = 16; + + return SEC_E_OK; + } + if(helper->neg_flags & NTLMSSP_NEGOTIATE_SIGN) + { + ULONG crc = 0U; + TRACE("Signing NTLM1 style\n"); + + for(i=0; i < pMessage->cBuffers; ++i) + { + if(pMessage->pBuffers[i].BufferType & SECBUFFER_DATA) + { + crc = ComputeCrc32(pMessage->pBuffers[i].pvBuffer, + pMessage->pBuffers[i].cbBuffer, crc); + } + } + + sig[ 0] = (sign_version >> 0) & 0xff; + sig[ 1] = (sign_version >> 8) & 0xff; + sig[ 2] = (sign_version >> 16) & 0xff; + sig[ 3] = (sign_version >> 24) & 0xff; + memset(sig+4, 0, 4); + sig[ 8] = (crc >> 0) & 0xff; + sig[ 9] = (crc >> 8) & 0xff; + sig[10] = (crc >> 16) & 0xff; + sig[11] = (crc >> 24) & 0xff; + sig[12] = (helper->crypt.ntlm.seq_num >> 0) & 0xff; + sig[13] = (helper->crypt.ntlm.seq_num >> 8) & 0xff; + sig[14] = (helper->crypt.ntlm.seq_num >> 16) & 0xff; + sig[15] = (helper->crypt.ntlm.seq_num >> 24) & 0xff; + + ++(helper->crypt.ntlm.seq_num); + + if(encrypt_sig) + SECUR32_arc4Process(helper->crypt.ntlm.a4i, sig+4, 12); + return SEC_E_OK; + } + + if(helper->neg_flags & NTLMSSP_NEGOTIATE_ALWAYS_SIGN || helper->neg_flags == 0) + { + TRACE("Creating a dummy signature.\n"); + /* A dummy signature is 0x01 followed by 15 bytes of 0x00 */ + memset(pMessage->pBuffers[token_idx].pvBuffer, 0, 16); + memset(pMessage->pBuffers[token_idx].pvBuffer, 0x01, 1); + pMessage->pBuffers[token_idx].cbBuffer = 16; + return SEC_E_OK; + } + + return SEC_E_UNSUPPORTED_FUNCTION; +} + +/*********************************************************************** + * MakeSignature + */ +SECURITY_STATUS SEC_ENTRY ntlm_MakeSignature(PCtxtHandle phContext, + ULONG fQOP, PSecBufferDesc pMessage, ULONG MessageSeqNo) +{ + PNegoHelper helper; + int token_idx; + + TRACE("%p %d %p %d\n", phContext, fQOP, pMessage, MessageSeqNo); + if (!phContext) + return SEC_E_INVALID_HANDLE; + + if(fQOP) + FIXME("Ignoring fQOP 0x%08x\n", fQOP); + + if(MessageSeqNo) + FIXME("Ignoring MessageSeqNo\n"); + + if(!pMessage || !pMessage->pBuffers || pMessage->cBuffers < 2) + return SEC_E_INVALID_TOKEN; + + /* If we didn't find a SECBUFFER_TOKEN type buffer */ + if((token_idx = ntlm_GetTokenBufferIndex(pMessage)) == -1) + return SEC_E_INVALID_TOKEN; + + if(pMessage->pBuffers[token_idx].cbBuffer < 16) + return SEC_E_BUFFER_TOO_SMALL; + + helper = (PNegoHelper)phContext->dwLower; + TRACE("Negotiated flags are: 0x%08x\n", helper->neg_flags); + + return ntlm_CreateSignature(helper, pMessage, token_idx, NTLM_SEND, TRUE); +} + +/*********************************************************************** + * VerifySignature + */ +SECURITY_STATUS SEC_ENTRY ntlm_VerifySignature(PCtxtHandle phContext, + PSecBufferDesc pMessage, ULONG MessageSeqNo, PULONG pfQOP) +{ + PNegoHelper helper; + UINT i; + int token_idx; + SECURITY_STATUS ret; + SecBufferDesc local_desc; + PSecBuffer local_buff; + BYTE local_sig[16]; + + TRACE("%p %p %d %p\n", phContext, pMessage, MessageSeqNo, pfQOP); + if(!phContext) + return SEC_E_INVALID_HANDLE; + + if(!pMessage || !pMessage->pBuffers || pMessage->cBuffers < 2) + return SEC_E_INVALID_TOKEN; + + if((token_idx = ntlm_GetTokenBufferIndex(pMessage)) == -1) + return SEC_E_INVALID_TOKEN; + + if(pMessage->pBuffers[token_idx].cbBuffer < 16) + return SEC_E_BUFFER_TOO_SMALL; + + if(MessageSeqNo) + FIXME("Ignoring MessageSeqNo\n"); + + helper = (PNegoHelper)phContext->dwLower; + TRACE("Negotiated flags: 0x%08x\n", helper->neg_flags); + + local_buff = HeapAlloc(GetProcessHeap(), 0, pMessage->cBuffers * sizeof(SecBuffer)); + + local_desc.ulVersion = SECBUFFER_VERSION; + local_desc.cBuffers = pMessage->cBuffers; + local_desc.pBuffers = local_buff; + + for(i=0; i < pMessage->cBuffers; ++i) + { + if(pMessage->pBuffers[i].BufferType == SECBUFFER_TOKEN) + { + local_buff[i].BufferType = SECBUFFER_TOKEN; + local_buff[i].cbBuffer = 16; + local_buff[i].pvBuffer = local_sig; + } + else + { + local_buff[i].BufferType = pMessage->pBuffers[i].BufferType; + local_buff[i].cbBuffer = pMessage->pBuffers[i].cbBuffer; + local_buff[i].pvBuffer = pMessage->pBuffers[i].pvBuffer; + } + } + + if((ret = ntlm_CreateSignature(helper, &local_desc, token_idx, NTLM_RECV, TRUE)) != SEC_E_OK) + return ret; + + if(memcmp(((PBYTE)local_buff[token_idx].pvBuffer) + 8, + ((PBYTE)pMessage->pBuffers[token_idx].pvBuffer) + 8, 8)) + ret = SEC_E_MESSAGE_ALTERED; + else + ret = SEC_E_OK; + + HeapFree(GetProcessHeap(), 0, local_buff); + + return ret; + +} + +/*********************************************************************** + * FreeCredentialsHandle + */ +SECURITY_STATUS SEC_ENTRY ntlm_FreeCredentialsHandle(PCredHandle phCredential) +{ + SECURITY_STATUS ret; + + if(phCredential){ + PNtlmCredentials ntlm_cred = (PNtlmCredentials) phCredential->dwLower; + phCredential->dwUpper = 0; + phCredential->dwLower = 0; + if (ntlm_cred->password) + memset(ntlm_cred->password, 0, ntlm_cred->pwlen); + HeapFree(GetProcessHeap(), 0, ntlm_cred->password); + HeapFree(GetProcessHeap(), 0, ntlm_cred->username_arg); + HeapFree(GetProcessHeap(), 0, ntlm_cred->domain_arg); + HeapFree(GetProcessHeap(), 0, ntlm_cred); + ret = SEC_E_OK; + } + else + ret = SEC_E_OK; + + return ret; +} + +/*********************************************************************** + * EncryptMessage + */ +SECURITY_STATUS SEC_ENTRY ntlm_EncryptMessage(PCtxtHandle phContext, + ULONG fQOP, PSecBufferDesc pMessage, ULONG MessageSeqNo) +{ + PNegoHelper helper; + int token_idx, data_idx; + + TRACE("(%p %d %p %d)\n", phContext, fQOP, pMessage, MessageSeqNo); + + if(!phContext) + return SEC_E_INVALID_HANDLE; + + if(fQOP) + FIXME("Ignoring fQOP\n"); + + if(MessageSeqNo) + FIXME("Ignoring MessageSeqNo\n"); + + if(!pMessage || !pMessage->pBuffers || pMessage->cBuffers < 2) + return SEC_E_INVALID_TOKEN; + + if((token_idx = ntlm_GetTokenBufferIndex(pMessage)) == -1) + return SEC_E_INVALID_TOKEN; + + if((data_idx = ntlm_GetDataBufferIndex(pMessage)) ==-1 ) + return SEC_E_INVALID_TOKEN; + + if(pMessage->pBuffers[token_idx].cbBuffer < 16) + return SEC_E_BUFFER_TOO_SMALL; + + helper = (PNegoHelper) phContext->dwLower; + + if(helper->neg_flags & NTLMSSP_NEGOTIATE_NTLM2 && + helper->neg_flags & NTLMSSP_NEGOTIATE_SEAL) + { + ntlm_CreateSignature(helper, pMessage, token_idx, NTLM_SEND, FALSE); + SECUR32_arc4Process(helper->crypt.ntlm2.send_a4i, + pMessage->pBuffers[data_idx].pvBuffer, + pMessage->pBuffers[data_idx].cbBuffer); + + if(helper->neg_flags & NTLMSSP_NEGOTIATE_KEY_EXCHANGE) + SECUR32_arc4Process(helper->crypt.ntlm2.send_a4i, + ((BYTE *)pMessage->pBuffers[token_idx].pvBuffer)+4, 8); + } + else + { + PBYTE sig; + ULONG save_flags; + + /* EncryptMessage always produces real signatures, so make sure + * NTLMSSP_NEGOTIATE_SIGN is set*/ + save_flags = helper->neg_flags; + helper->neg_flags |= NTLMSSP_NEGOTIATE_SIGN; + ntlm_CreateSignature(helper, pMessage, token_idx, NTLM_SEND, FALSE); + helper->neg_flags = save_flags; + + sig = pMessage->pBuffers[token_idx].pvBuffer; + + SECUR32_arc4Process(helper->crypt.ntlm.a4i, + pMessage->pBuffers[data_idx].pvBuffer, + pMessage->pBuffers[data_idx].cbBuffer); + SECUR32_arc4Process(helper->crypt.ntlm.a4i, sig+4, 12); + + if(helper->neg_flags & NTLMSSP_NEGOTIATE_ALWAYS_SIGN || helper->neg_flags == 0) + memset(sig+4, 0, 4); + } + return SEC_E_OK; +} + +/*********************************************************************** + * DecryptMessage + */ +SECURITY_STATUS SEC_ENTRY ntlm_DecryptMessage(PCtxtHandle phContext, + PSecBufferDesc pMessage, ULONG MessageSeqNo, PULONG pfQOP) +{ + SECURITY_STATUS ret; + ULONG ntlmssp_flags_save; + PNegoHelper helper; + int token_idx, data_idx; + TRACE("(%p %p %d %p)\n", phContext, pMessage, MessageSeqNo, pfQOP); + + if(!phContext) + return SEC_E_INVALID_HANDLE; + + if(MessageSeqNo) + FIXME("Ignoring MessageSeqNo\n"); + + if(!pMessage || !pMessage->pBuffers || pMessage->cBuffers < 2) + return SEC_E_INVALID_TOKEN; + + if((token_idx = ntlm_GetTokenBufferIndex(pMessage)) == -1) + return SEC_E_INVALID_TOKEN; + + if((data_idx = ntlm_GetDataBufferIndex(pMessage)) ==-1) + return SEC_E_INVALID_TOKEN; + + if(pMessage->pBuffers[token_idx].cbBuffer < 16) + return SEC_E_BUFFER_TOO_SMALL; + + helper = (PNegoHelper) phContext->dwLower; + + if(helper->neg_flags & NTLMSSP_NEGOTIATE_NTLM2 && helper->neg_flags & NTLMSSP_NEGOTIATE_SEAL) + { + SECUR32_arc4Process(helper->crypt.ntlm2.recv_a4i, + pMessage->pBuffers[data_idx].pvBuffer, + pMessage->pBuffers[data_idx].cbBuffer); + } + else + { + SECUR32_arc4Process(helper->crypt.ntlm.a4i, + pMessage->pBuffers[data_idx].pvBuffer, + pMessage->pBuffers[data_idx].cbBuffer); + } + + /* Make sure we use a session key for the signature check, EncryptMessage + * always does that, even in the dummy case */ + ntlmssp_flags_save = helper->neg_flags; + + helper->neg_flags |= NTLMSSP_NEGOTIATE_SIGN; + ret = ntlm_VerifySignature(phContext, pMessage, MessageSeqNo, pfQOP); + + helper->neg_flags = ntlmssp_flags_save; + + return ret; +} + +static const SecurityFunctionTableA ntlmTableA = { + 1, + NULL, /* EnumerateSecurityPackagesA */ + ntlm_QueryCredentialsAttributesA, /* QueryCredentialsAttributesA */ + ntlm_AcquireCredentialsHandleA, /* AcquireCredentialsHandleA */ + ntlm_FreeCredentialsHandle, /* FreeCredentialsHandle */ + NULL, /* Reserved2 */ + ntlm_InitializeSecurityContextA, /* InitializeSecurityContextA */ + ntlm_AcceptSecurityContext, /* AcceptSecurityContext */ + ntlm_CompleteAuthToken, /* CompleteAuthToken */ + ntlm_DeleteSecurityContext, /* DeleteSecurityContext */ + NULL, /* ApplyControlToken */ + ntlm_QueryContextAttributesA, /* QueryContextAttributesA */ + ntlm_ImpersonateSecurityContext, /* ImpersonateSecurityContext */ + ntlm_RevertSecurityContext, /* RevertSecurityContext */ + ntlm_MakeSignature, /* MakeSignature */ + ntlm_VerifySignature, /* VerifySignature */ + FreeContextBuffer, /* FreeContextBuffer */ + NULL, /* QuerySecurityPackageInfoA */ + NULL, /* Reserved3 */ + NULL, /* Reserved4 */ + NULL, /* ExportSecurityContext */ + NULL, /* ImportSecurityContextA */ + NULL, /* AddCredentialsA */ + NULL, /* Reserved8 */ + NULL, /* QuerySecurityContextToken */ + ntlm_EncryptMessage, /* EncryptMessage */ + ntlm_DecryptMessage, /* DecryptMessage */ + NULL, /* SetContextAttributesA */ +}; + +static const SecurityFunctionTableW ntlmTableW = { + 1, + NULL, /* EnumerateSecurityPackagesW */ + ntlm_QueryCredentialsAttributesW, /* QueryCredentialsAttributesW */ + ntlm_AcquireCredentialsHandleW, /* AcquireCredentialsHandleW */ + ntlm_FreeCredentialsHandle, /* FreeCredentialsHandle */ + NULL, /* Reserved2 */ + ntlm_InitializeSecurityContextW, /* InitializeSecurityContextW */ + ntlm_AcceptSecurityContext, /* AcceptSecurityContext */ + ntlm_CompleteAuthToken, /* CompleteAuthToken */ + ntlm_DeleteSecurityContext, /* DeleteSecurityContext */ + NULL, /* ApplyControlToken */ + ntlm_QueryContextAttributesW, /* QueryContextAttributesW */ + ntlm_ImpersonateSecurityContext, /* ImpersonateSecurityContext */ + ntlm_RevertSecurityContext, /* RevertSecurityContext */ + ntlm_MakeSignature, /* MakeSignature */ + ntlm_VerifySignature, /* VerifySignature */ + FreeContextBuffer, /* FreeContextBuffer */ + NULL, /* QuerySecurityPackageInfoW */ + NULL, /* Reserved3 */ + NULL, /* Reserved4 */ + NULL, /* ExportSecurityContext */ + NULL, /* ImportSecurityContextW */ + NULL, /* AddCredentialsW */ + NULL, /* Reserved8 */ + NULL, /* QuerySecurityContextToken */ + ntlm_EncryptMessage, /* EncryptMessage */ + ntlm_DecryptMessage, /* DecryptMessage */ + NULL, /* SetContextAttributesW */ +}; + +#define NTLM_COMMENT \ + { 'N', 'T', 'L', 'M', ' ', \ + 'S', 'e', 'c', 'u', 'r', 'i', 't', 'y', ' ', \ + 'P', 'a', 'c', 'k', 'a', 'g', 'e', 0} + +static CHAR ntlm_comment_A[] = NTLM_COMMENT; +static WCHAR ntlm_comment_W[] = NTLM_COMMENT; + +#define NTLM_NAME {'N', 'T', 'L', 'M', 0} + +static char ntlm_name_A[] = NTLM_NAME; +static WCHAR ntlm_name_W[] = NTLM_NAME; + +/* According to Windows, NTLM has the following capabilities. */ +#define CAPS ( \ + SECPKG_FLAG_INTEGRITY | \ + SECPKG_FLAG_PRIVACY | \ + SECPKG_FLAG_TOKEN_ONLY | \ + SECPKG_FLAG_CONNECTION | \ + SECPKG_FLAG_MULTI_REQUIRED | \ + SECPKG_FLAG_IMPERSONATION | \ + SECPKG_FLAG_ACCEPT_WIN32_NAME | \ + SECPKG_FLAG_NEGOTIABLE | \ + SECPKG_FLAG_LOGON | \ + SECPKG_FLAG_RESTRICTED_TOKENS ) + +static const SecPkgInfoW infoW = { + CAPS, + 1, + RPC_C_AUTHN_WINNT, + NTLM_MAX_BUF, + ntlm_name_W, + ntlm_comment_W +}; + +static const SecPkgInfoA infoA = { + CAPS, + 1, + RPC_C_AUTHN_WINNT, + NTLM_MAX_BUF, + ntlm_name_A, + ntlm_comment_A +}; + +SecPkgInfoA *ntlm_package_infoA = (SecPkgInfoA *)&infoA; +SecPkgInfoW *ntlm_package_infoW = (SecPkgInfoW *)&infoW; + +void SECUR32_initNTLMSP(void) +{ + PNegoHelper helper; + static CHAR version[] = "--version"; + + SEC_CHAR *args[] = { + ntlm_auth, + version, + NULL }; + + if(fork_helper(&helper, ntlm_auth, args) != SEC_E_OK) + helper = NULL; + else + check_version(helper); + + if( helper && + ((helper->major > MIN_NTLM_AUTH_MAJOR_VERSION) || + (helper->major == MIN_NTLM_AUTH_MAJOR_VERSION && + helper->minor > MIN_NTLM_AUTH_MINOR_VERSION) || + (helper->major == MIN_NTLM_AUTH_MAJOR_VERSION && + helper->minor == MIN_NTLM_AUTH_MINOR_VERSION && + helper->micro >= MIN_NTLM_AUTH_MICRO_VERSION)) ) + { + SecureProvider *provider = SECUR32_addProvider(&ntlmTableA, &ntlmTableW, NULL); + SECUR32_addPackages(provider, 1L, ntlm_package_infoA, ntlm_package_infoW); + } + else + { + ERR_(winediag)("%s was not found or is outdated. " + "Make sure that ntlm_auth >= %d.%d.%d is in your path. " + "Usually, you can find it in the winbind package of your distribution.\n", + ntlm_auth, + MIN_NTLM_AUTH_MAJOR_VERSION, + MIN_NTLM_AUTH_MINOR_VERSION, + MIN_NTLM_AUTH_MICRO_VERSION); + + } + cleanup_helper(helper); +} diff --git a/reactos/dll/win32/secur32/wine/ntlm.h b/reactos/dll/win32/secur32/wine/ntlm.h new file mode 100644 index 00000000000..0006ad573aa --- /dev/null +++ b/reactos/dll/win32/secur32/wine/ntlm.h @@ -0,0 +1,116 @@ + +#ifndef __WINE_NTLM_H__ +#define __WINE_NTLM_H__ + +#include +#define CP_UNIXCP CP_ACP + +void SECUR32_initNTLMSP(void) DECLSPEC_HIDDEN; + +typedef enum _helper_mode { + NTLM_SERVER, + NTLM_CLIENT, + NUM_HELPER_MODES +} HelperMode; + +typedef struct tag_arc4_info { + unsigned char x, y; + unsigned char state[256]; +} arc4_info; + +typedef struct _NegoHelper { +#ifndef __REACTOS__ + pid_t helper_pid; +#else + HANDLE helper_pid; +#endif + HelperMode mode; + int pipe_in; + int pipe_out; + int major; + int minor; + int micro; + char *com_buf; + int com_buf_size; + int com_buf_offset; + BYTE *session_key; + ULONG neg_flags; + struct { + struct { + ULONG seq_num; + arc4_info *a4i; + } ntlm; + struct { + BYTE *send_sign_key; + BYTE *send_seal_key; + BYTE *recv_sign_key; + BYTE *recv_seal_key; + ULONG send_seq_no; + ULONG recv_seq_no; + arc4_info *send_a4i; + arc4_info *recv_a4i; + } ntlm2; + } crypt; +} NegoHelper, *PNegoHelper; + +typedef struct _NtlmCredentials +{ + HelperMode mode; + + /* these are all in the Unix codepage */ + char *username_arg; + char *domain_arg; + char *password; /* not nul-terminated */ + int pwlen; + int no_cached_credentials; /* don't try to use cached Samba credentials */ +} NtlmCredentials, *PNtlmCredentials; + +typedef enum _sign_direction { + NTLM_SEND, + NTLM_RECV +} SignDirection; + +SECURITY_STATUS SECUR32_CreateNTLM1SessionKey(PBYTE password, int len, PBYTE session_key) DECLSPEC_HIDDEN; +SECURITY_STATUS SECUR32_CreateNTLM2SubKeys(PNegoHelper helper) DECLSPEC_HIDDEN; + +/* NTLMSSP flags indicating the negotiated features */ +#define NTLMSSP_NEGOTIATE_UNICODE 0x00000001 +#define NTLMSSP_NEGOTIATE_OEM 0x00000002 +#define NTLMSSP_REQUEST_TARGET 0x00000004 +#define NTLMSSP_NEGOTIATE_SIGN 0x00000010 +#define NTLMSSP_NEGOTIATE_SEAL 0x00000020 +#define NTLMSSP_NEGOTIATE_DATAGRAM_STYLE 0x00000040 +#define NTLMSSP_NEGOTIATE_LM_SESSION_KEY 0x00000080 +#define NTLMSSP_NEGOTIATE_NTLM 0x00000200 +#define NTLMSSP_NEGOTIATE_DOMAIN_SUPPLIED 0x00001000 +#define NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED 0x00002000 +#define NTLMSSP_NEGOTIATE_LOCAL_CALL 0x00004000 +#define NTLMSSP_NEGOTIATE_ALWAYS_SIGN 0x00008000 +#define NTLMSSP_NEGOTIATE_TARGET_TYPE_DOMAIN 0x00010000 +#define NTLMSSP_NEGOTIATE_TARGET_TYPE_SERVER 0x00020000 +#define NTLMSSP_NEGOTIATE_NTLM2 0x00080000 +#define NTLMSSP_NEGOTIATE_TARGET_INFO 0x00800000 +#define NTLMSSP_NEGOTIATE_128 0x20000000 +#define NTLMSSP_NEGOTIATE_KEY_EXCHANGE 0x40000000 +#define NTLMSSP_NEGOTIATE_56 0x80000000 + +SECURITY_STATUS SEC_ENTRY ntlm_AcquireCredentialsHandleW(SEC_WCHAR *, SEC_WCHAR *, + ULONG, PLUID, PVOID, SEC_GET_KEY_FN, PVOID, PCredHandle, PTimeStamp) DECLSPEC_HIDDEN; +SECURITY_STATUS SEC_ENTRY ntlm_InitializeSecurityContextW(PCredHandle, PCtxtHandle, + SEC_WCHAR *, ULONG fContextReq, ULONG, ULONG, PSecBufferDesc, ULONG, PCtxtHandle, + PSecBufferDesc, ULONG *, PTimeStamp) DECLSPEC_HIDDEN; +SECURITY_STATUS SEC_ENTRY ntlm_AcceptSecurityContext(PCredHandle, PCtxtHandle, PSecBufferDesc, + ULONG, ULONG, PCtxtHandle, PSecBufferDesc, ULONG *, PTimeStamp) DECLSPEC_HIDDEN; +SECURITY_STATUS SEC_ENTRY ntlm_QueryContextAttributesA(PCtxtHandle, ULONG, void *) DECLSPEC_HIDDEN; +SECURITY_STATUS SEC_ENTRY ntlm_QueryContextAttributesW(PCtxtHandle, ULONG, void *) DECLSPEC_HIDDEN; +SECURITY_STATUS SEC_ENTRY ntlm_EncryptMessage(PCtxtHandle, ULONG, PSecBufferDesc, ULONG) DECLSPEC_HIDDEN; +SECURITY_STATUS SEC_ENTRY ntlm_DecryptMessage(PCtxtHandle, PSecBufferDesc, ULONG, PULONG) DECLSPEC_HIDDEN; +SECURITY_STATUS SEC_ENTRY ntlm_FreeCredentialsHandle(PCredHandle) DECLSPEC_HIDDEN; +SECURITY_STATUS SEC_ENTRY ntlm_DeleteSecurityContext(PCtxtHandle) DECLSPEC_HIDDEN; +SECURITY_STATUS SEC_ENTRY ntlm_MakeSignature(PCtxtHandle, ULONG, PSecBufferDesc, ULONG) DECLSPEC_HIDDEN; +SECURITY_STATUS SEC_ENTRY ntlm_VerifySignature(PCtxtHandle, PSecBufferDesc, ULONG, PULONG) DECLSPEC_HIDDEN; + +SecPkgInfoW *ntlm_package_infoW DECLSPEC_HIDDEN; +SecPkgInfoA *ntlm_package_infoA DECLSPEC_HIDDEN; + +#endif /* __WINE_NTLM_H__ */ diff --git a/reactos/dll/win32/secur32/wine/util.c b/reactos/dll/win32/secur32/wine/util.c new file mode 100644 index 00000000000..2f9fe015823 --- /dev/null +++ b/reactos/dll/win32/secur32/wine/util.c @@ -0,0 +1,249 @@ +/* + * Copyright 2006 Kai Blin + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + * + * This file contains various helper functions needed for NTLM and maybe others + */ +#include "precomp.h" + +#include +WINE_DEFAULT_DEBUG_CHANNEL(ntlm); + +/* The CRC32 code is copyright (C) 1986 Gary S. Brown and was placed in the + * public domain. + * CRC polynomial 0xedb88320 + */ +static const ULONG CRC_table[256] = +{ + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, + 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, + 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, + 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, + 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, + 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, + 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, + 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, + 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, + 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, + 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, + 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, + 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, + 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, + 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, + 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, + 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, + 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, + 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, + 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, + 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, + 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, + 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, + 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, + 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, + 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, + 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, + 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, + 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, + 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, + 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, + 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, + 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, + 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, + 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, + 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, + 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, + 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, + 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, + 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, + 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, + 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, + 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d +}; + +static const char client_to_server_sign_constant[] = "session key to client-to-server signing key magic constant"; +static const char client_to_server_seal_constant[] = "session key to client-to-server sealing key magic constant"; +static const char server_to_client_sign_constant[] = "session key to server-to-client signing key magic constant"; +static const char server_to_client_seal_constant[] = "session key to server-to-client sealing key magic constant"; + +typedef struct +{ + unsigned int buf[4]; + unsigned int i[2]; + unsigned char in[64]; + unsigned char digest[16]; +} MD4_CTX; + +#ifndef __REACTOS__ // See hmac_md5.h +/* And now the same with a different memory layout. */ +typedef struct +{ + unsigned int i[2]; + unsigned int buf[4]; + unsigned char in[64]; + unsigned char digest[16]; +} MD5_CTX; +#endif + +VOID WINAPI MD4Init( MD4_CTX *ctx ); +VOID WINAPI MD4Update( MD4_CTX *ctx, const unsigned char *buf, unsigned int len ); +VOID WINAPI MD4Final( MD4_CTX *ctx ); +#ifndef __REACTOS__ // See hmac_md5.h +VOID WINAPI MD5Init( MD5_CTX *ctx ); +VOID WINAPI MD5Update( MD5_CTX *ctx, const unsigned char *buf, unsigned int len ); +VOID WINAPI MD5Final( MD5_CTX *ctx ); +#endif + +ULONG ComputeCrc32(const BYTE *pData, INT iLen, ULONG initial_crc) +{ + ULONG crc = ~initial_crc; + + while (iLen > 0) + { + crc = CRC_table[(crc ^ *pData) & 0xff] ^ (crc >> 8); + pData++; + iLen--; + } + return ~crc; +} + +SECURITY_STATUS SECUR32_CreateNTLM1SessionKey(PBYTE password, int len, PBYTE session_key) +{ + MD4_CTX ctx; + BYTE ntlm_hash[16]; + + TRACE("(%p, %p)\n", password, session_key); + + MD4Init(&ctx); + MD4Update(&ctx, password, len); + MD4Final(&ctx); + + memcpy(ntlm_hash, ctx.digest, 0x10); + + MD4Init(&ctx); + MD4Update(&ctx, ntlm_hash, 0x10u); + MD4Final(&ctx); + + memcpy(session_key, ctx.digest, 0x10); + + return SEC_E_OK; +} + +static void SECUR32_CalcNTLM2Subkey(const BYTE *session_key, const char *magic, PBYTE subkey) +{ + MD5_CTX ctx; + + MD5Init(&ctx); + MD5Update(&ctx, session_key, 16); + MD5Update(&ctx, (const unsigned char*)magic, lstrlenA(magic)+1); + MD5Final(&ctx); + memcpy(subkey, ctx.digest, 16); +} + +/* This assumes we do have a valid NTLM2 user session key */ +SECURITY_STATUS SECUR32_CreateNTLM2SubKeys(PNegoHelper helper) +{ + helper->crypt.ntlm2.send_sign_key = HeapAlloc(GetProcessHeap(), 0, 16); + helper->crypt.ntlm2.send_seal_key = HeapAlloc(GetProcessHeap(), 0, 16); + helper->crypt.ntlm2.recv_sign_key = HeapAlloc(GetProcessHeap(), 0, 16); + helper->crypt.ntlm2.recv_seal_key = HeapAlloc(GetProcessHeap(), 0, 16); + + if(helper->mode == NTLM_CLIENT) + { + SECUR32_CalcNTLM2Subkey(helper->session_key, client_to_server_sign_constant, + helper->crypt.ntlm2.send_sign_key); + SECUR32_CalcNTLM2Subkey(helper->session_key, client_to_server_seal_constant, + helper->crypt.ntlm2.send_seal_key); + SECUR32_CalcNTLM2Subkey(helper->session_key, server_to_client_sign_constant, + helper->crypt.ntlm2.recv_sign_key); + SECUR32_CalcNTLM2Subkey(helper->session_key, server_to_client_seal_constant, + helper->crypt.ntlm2.recv_seal_key); + } + else + { + SECUR32_CalcNTLM2Subkey(helper->session_key, server_to_client_sign_constant, + helper->crypt.ntlm2.send_sign_key); + SECUR32_CalcNTLM2Subkey(helper->session_key, server_to_client_seal_constant, + helper->crypt.ntlm2.send_seal_key); + SECUR32_CalcNTLM2Subkey(helper->session_key, client_to_server_sign_constant, + helper->crypt.ntlm2.recv_sign_key); + SECUR32_CalcNTLM2Subkey(helper->session_key, client_to_server_seal_constant, + helper->crypt.ntlm2.recv_seal_key); + } + + return SEC_E_OK; +} + +arc4_info *SECUR32_arc4Alloc(void) +{ + arc4_info *a4i = HeapAlloc(GetProcessHeap(), 0, sizeof(arc4_info)); + return a4i; +} + +/* + * The arc4 code is based on dlls/advapi32/crypt_arc4.c by Mike McCormack, + * which in turn is based on public domain code by Wei Dai + */ +void SECUR32_arc4Init(arc4_info *a4i, const BYTE *key, unsigned int keyLen) +{ + unsigned int keyIndex = 0, stateIndex = 0; + unsigned int i, a; + + TRACE("(%p, %p, %d)\n", a4i, key, keyLen); + + a4i->x = a4i->y = 0; + + for (i=0; i<256; i++) + a4i->state[i] = i; + + for (i=0; i<256; i++) + { + a = a4i->state[i]; + stateIndex += key[keyIndex] + a; + stateIndex &= 0xff; + a4i->state[i] = a4i->state[stateIndex]; + a4i->state[stateIndex] = a; + if (++keyIndex >= keyLen) + keyIndex = 0; + } + +} + +void SECUR32_arc4Process(arc4_info *a4i, BYTE *inoutString, unsigned int length) +{ + BYTE *const s=a4i->state; + unsigned int x = a4i->x; + unsigned int y = a4i->y; + unsigned int a, b; + + while(length--) + { + x = (x+1) & 0xff; + a = s[x]; + y = (y+a) & 0xff; + b = s[y]; + s[x] = b; + s[y] = a; + *inoutString++ ^= s[(a+b) & 0xff]; + } + + a4i->x = x; + a4i->y = y; +} + +void SECUR32_arc4Cleanup(arc4_info *a4i) +{ + HeapFree(GetProcessHeap(), 0, a4i); +} diff --git a/reactos/dll/win32/secur32/wine/util.h b/reactos/dll/win32/secur32/wine/util.h new file mode 100644 index 00000000000..f4b35f12580 --- /dev/null +++ b/reactos/dll/win32/secur32/wine/util.h @@ -0,0 +1,14 @@ + +#ifndef __WINE_UTIL_H__ +#define __WINE_UTIL_H__ + +/* Functions from util.c */ +ULONG ComputeCrc32(const BYTE *pData, INT iLen, ULONG initial_crc) DECLSPEC_HIDDEN; +SECURITY_STATUS SECUR32_CreateNTLM1SessionKey(PBYTE password, int len, PBYTE session_key) DECLSPEC_HIDDEN; +SECURITY_STATUS SECUR32_CreateNTLM2SubKeys(PNegoHelper helper) DECLSPEC_HIDDEN; +arc4_info *SECUR32_arc4Alloc(void) DECLSPEC_HIDDEN; +void SECUR32_arc4Init(arc4_info *a4i, const BYTE *key, unsigned int keyLen) DECLSPEC_HIDDEN; +void SECUR32_arc4Process(arc4_info *a4i, BYTE *inoutString, unsigned int length) DECLSPEC_HIDDEN; +void SECUR32_arc4Cleanup(arc4_info *a4i) DECLSPEC_HIDDEN; + +#endif /* __WINE_UTIL_H__ */ diff --git a/reactos/dll/win32/secur32/wine/wine_supp.h b/reactos/dll/win32/secur32/wine/wine_supp.h new file mode 100644 index 00000000000..5528290a028 --- /dev/null +++ b/reactos/dll/win32/secur32/wine/wine_supp.h @@ -0,0 +1,9 @@ + +#include "base64_codec.h" +#include "hmac_md5.h" +#include "kerberos.h" +#include "negotiate.h" +// WARN: The ordering of the following two headers must be kept! +#include "ntlm.h" +#include "dispatcher.h" +#include "util.h"