win32u: Move NtUserTranslateMessage implementation from user32.
[wine.git] / dlls / msv1_0 / main.c
blob77dcfa3b67d687395650027bd2ce8b7e0d3404b3
1 /*
2 * Copyright 2005, 2006 Kai Blin
3 * Copyright 2021 Hans Leidekker for CodeWeavers
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20 #include <stdarg.h>
21 #include <stdlib.h>
22 #include <wchar.h>
23 #include "ntstatus.h"
24 #define WIN32_NO_STATUS
25 #include "windef.h"
26 #include "winbase.h"
27 #include "winternl.h"
28 #include "sspi.h"
29 #include "ntsecapi.h"
30 #include "ntsecpkg.h"
31 #include "rpc.h"
32 #include "wincred.h"
33 #include "lmwksta.h"
34 #include "lmapibuf.h"
35 #include "lmerr.h"
37 #include "wine/debug.h"
38 #include "unixlib.h"
40 WINE_DEFAULT_DEBUG_CHANNEL(ntlm);
42 static ULONG ntlm_package_id;
43 static LSA_DISPATCH_TABLE lsa_dispatch;
45 static unixlib_handle_t ntlm_handle;
47 static NTSTATUS ntlm_check_version(void)
49 return __wine_unix_call( ntlm_handle, unix_check_version, NULL );
52 static void ntlm_cleanup( struct ntlm_ctx *ctx )
54 __wine_unix_call( ntlm_handle, unix_cleanup, ctx );
57 static NTSTATUS ntlm_chat( struct ntlm_ctx *ctx, char *buf, unsigned int buflen, unsigned int *retlen )
59 struct chat_params params = { ctx, buf, buflen, retlen };
61 return __wine_unix_call( ntlm_handle, unix_chat, &params );
64 static NTSTATUS ntlm_fork( struct ntlm_ctx *ctx, char **argv )
66 struct fork_params params = { ctx, argv };
68 return __wine_unix_call( ntlm_handle, unix_fork, &params );
71 #define NTLM_CAPS \
72 ( SECPKG_FLAG_INTEGRITY \
73 | SECPKG_FLAG_PRIVACY \
74 | SECPKG_FLAG_TOKEN_ONLY \
75 | SECPKG_FLAG_CONNECTION \
76 | SECPKG_FLAG_MULTI_REQUIRED \
77 | SECPKG_FLAG_IMPERSONATION \
78 | SECPKG_FLAG_ACCEPT_WIN32_NAME \
79 | SECPKG_FLAG_NEGOTIABLE \
80 | SECPKG_FLAG_LOGON \
81 | SECPKG_FLAG_RESTRICTED_TOKENS )
83 #define NTLM_MAX_BUF 1904
85 static const SecPkgInfoW ntlm_package_info =
87 NTLM_CAPS,
89 RPC_C_AUTHN_WINNT,
90 NTLM_MAX_BUF,
91 (SEC_WCHAR *)L"NTLM",
92 (SEC_WCHAR *)L"NTLM Security Package"
95 static inline const char *debugstr_as( const STRING *str )
97 if (!str) return "<null>";
98 return debugstr_an( str->Buffer, str->Length );
101 static inline const char *debugstr_us( const UNICODE_STRING *str )
103 if (!str) return "<null>";
104 return debugstr_wn( str->Buffer, str->Length / sizeof(WCHAR) );
107 static NTSTATUS NTAPI ntlm_LsaApInitializePackage( ULONG package_id, LSA_DISPATCH_TABLE *dispatch,
108 LSA_STRING *database, LSA_STRING *confidentiality,
109 LSA_STRING **package_name )
111 LSA_STRING *str;
112 char *ptr;
114 TRACE( "%#lx, %p, %s, %s, %p\n", package_id, dispatch, debugstr_as(database), debugstr_as(confidentiality),
115 package_name );
117 if (ntlm_check_version())
119 ERR( "no NTLM support, expect problems\n" );
120 return STATUS_UNSUCCESSFUL;
123 if (!(str = dispatch->AllocateLsaHeap( sizeof(*str) + sizeof("NTLM" )))) return STATUS_NO_MEMORY;
124 ptr = (char *)(str + 1);
125 memcpy( ptr, "NTLM", sizeof("NTLM") );
126 RtlInitString( str, ptr );
128 ntlm_package_id = package_id;
129 lsa_dispatch = *dispatch;
131 *package_name = str;
132 return STATUS_SUCCESS;
135 static NTSTATUS NTAPI ntlm_SpInitialize( ULONG_PTR package_id, SECPKG_PARAMETERS *params,
136 LSA_SECPKG_FUNCTION_TABLE *lsa_function_table )
138 TRACE( "%#Ix, %p, %p\n", package_id, params, lsa_function_table );
140 if (ntlm_check_version())
142 ERR( "no NTLM support, expect problems\n" );
143 return STATUS_UNSUCCESSFUL;
145 return STATUS_SUCCESS;
148 static NTSTATUS NTAPI ntlm_SpGetInfo( SecPkgInfoW *info )
150 TRACE( "%p\n", info );
151 *info = ntlm_package_info;
152 return STATUS_SUCCESS;
155 static char *get_username_arg( const WCHAR *user, int user_len )
157 static const char arg[] = "--username=";
158 int len = sizeof(arg);
159 char *ret;
161 len += WideCharToMultiByte( CP_UNIXCP, WC_NO_BEST_FIT_CHARS, user, user_len, NULL, 0, NULL, NULL );
162 if (!(ret = malloc( len ))) return NULL;
163 memcpy( ret, arg, sizeof(arg) - 1 );
164 WideCharToMultiByte( CP_UNIXCP, WC_NO_BEST_FIT_CHARS, user, user_len, ret + sizeof(arg) - 1,
165 len - sizeof(arg) + 1, NULL, NULL );
166 ret[len - 1] = 0;
167 return ret;
170 static char *get_domain_arg( const WCHAR *domain, int domain_len )
172 static const char arg[] = "--domain=";
173 int len = sizeof(arg);
174 char *ret;
176 len += WideCharToMultiByte( CP_UNIXCP, WC_NO_BEST_FIT_CHARS, domain, domain_len, NULL, 0, NULL, NULL );
177 if (!(ret = malloc( len ))) return NULL;
178 memcpy( ret, arg, sizeof(arg) - 1 );
179 WideCharToMultiByte( CP_UNIXCP, WC_NO_BEST_FIT_CHARS, domain, domain_len, ret + sizeof(arg) - 1,
180 len - sizeof(arg) + 1, NULL, NULL );
181 ret[len - 1] = 0;
182 return ret;
185 #define WINE_NO_CACHED_CREDENTIALS 0x10000000
186 static NTSTATUS NTAPI ntlm_SpAcquireCredentialsHandle( UNICODE_STRING *principal, ULONG cred_use, LUID *logon_id,
187 void *auth_data, void *get_key_fn, void *get_key_arg,
188 LSA_SEC_HANDLE *handle, TimeStamp *expiry )
190 SECURITY_STATUS status = SEC_E_INSUFFICIENT_MEMORY;
191 struct ntlm_cred *cred = NULL;
192 WCHAR *domain = NULL, *user = NULL, *password = NULL;
193 SEC_WINNT_AUTH_IDENTITY_W *id = NULL;
195 TRACE( "%s, %#lx, %p, %p, %p, %p, %p, %p\n", debugstr_us(principal), cred_use, logon_id, auth_data,
196 get_key_fn, get_key_arg, cred, expiry );
198 switch (cred_use & ~SECPKG_CRED_RESERVED)
200 case SECPKG_CRED_INBOUND:
201 if (!(cred = malloc( sizeof(*cred) ))) return SEC_E_INSUFFICIENT_MEMORY;
202 cred->mode = MODE_SERVER;
203 cred->username_arg = NULL;
204 cred->domain_arg = NULL;
205 cred->password = NULL;
206 cred->password_len = 0;
207 cred->no_cached_credentials = 0;
209 *handle = (LSA_SEC_HANDLE)cred;
210 status = SEC_E_OK;
211 break;
213 case SECPKG_CRED_OUTBOUND:
214 if (!(cred = malloc( sizeof(*cred) ))) return SEC_E_INSUFFICIENT_MEMORY;
216 cred->mode = MODE_CLIENT;
217 cred->username_arg = NULL;
218 cred->domain_arg = NULL;
219 cred->password = NULL;
220 cred->password_len = 0;
221 cred->no_cached_credentials = (cred_use & WINE_NO_CACHED_CREDENTIALS);
223 if ((id = auth_data))
225 int domain_len = 0, user_len = 0, password_len = 0;
226 if (id->Flags & SEC_WINNT_AUTH_IDENTITY_ANSI)
228 if (id->DomainLength)
230 domain_len = MultiByteToWideChar( CP_ACP, 0, (char *)id->Domain, id->DomainLength, NULL, 0 );
231 if (!(domain = malloc( sizeof(WCHAR) * domain_len ))) goto done;
232 MultiByteToWideChar( CP_ACP, 0, (char *)id->Domain, id->DomainLength, domain, domain_len );
234 if (id->UserLength)
236 user_len = MultiByteToWideChar( CP_ACP, 0, (char *)id->User, id->UserLength, NULL, 0 );
237 if (!(user = malloc( sizeof(WCHAR) * user_len ))) goto done;
238 MultiByteToWideChar( CP_ACP, 0, (char *)id->User, id->UserLength, user, user_len );
240 if (id->PasswordLength)
242 password_len = MultiByteToWideChar( CP_ACP, 0,(char *)id->Password, id->PasswordLength, NULL, 0 );
243 if (!(password = malloc( sizeof(WCHAR) * password_len ))) goto done;
244 MultiByteToWideChar( CP_ACP, 0, (char *)id->Password, id->PasswordLength, password, password_len );
247 else
249 domain = id->Domain;
250 domain_len = id->DomainLength;
251 user = id->User;
252 user_len = id->UserLength;
253 password = id->Password;
254 password_len = id->PasswordLength;
257 TRACE( "username is %s\n", debugstr_wn(user, user_len) );
258 TRACE( "domain name is %s\n", debugstr_wn(domain, domain_len) );
260 cred->username_arg = get_username_arg( user, user_len );
261 cred->domain_arg = get_domain_arg( domain, domain_len );
262 if (password_len)
264 cred->password_len = WideCharToMultiByte( CP_UNIXCP, WC_NO_BEST_FIT_CHARS, password, password_len,
265 NULL, 0, NULL, NULL );
266 if (!(cred->password = malloc( cred->password_len ))) goto done;
267 WideCharToMultiByte( CP_UNIXCP, WC_NO_BEST_FIT_CHARS, password, password_len, cred->password,
268 cred->password_len, NULL, NULL );
272 *handle = (LSA_SEC_HANDLE)cred;
273 status = SEC_E_OK;
274 break;
276 case SECPKG_CRED_BOTH:
277 FIXME( "SECPKG_CRED_BOTH not supported\n" );
278 status = SEC_E_UNSUPPORTED_FUNCTION;
279 break;
281 default:
282 status = SEC_E_UNKNOWN_CREDENTIALS;
283 break;
286 done:
287 if (id && (id->Flags & SEC_WINNT_AUTH_IDENTITY_ANSI))
289 free( domain );
290 free( user );
291 free( password );
293 if (status != SEC_E_OK) free( cred );
294 return status;
297 static NTSTATUS NTAPI ntlm_SpFreeCredentialsHandle( LSA_SEC_HANDLE handle )
299 struct ntlm_cred *cred = (struct ntlm_cred *)handle;
301 TRACE( "%#Ix\n", handle );
303 if (!cred) return SEC_E_OK;
305 cred->mode = MODE_INVALID;
306 if (cred->password) memset( cred->password, 0, cred->password_len );
307 free( cred->password );
308 free( cred->username_arg );
309 free( cred->domain_arg );
310 free( cred );
311 return SEC_E_OK;
314 static BOOL get_cached_credential( const UNICODE_STRING *target, CREDENTIALW **cred )
316 const WCHAR *ptr, *host;
317 WCHAR *hostonly;
318 size_t len;
319 BOOL ret;
321 if (!target) return FALSE;
323 len = target->Length / sizeof(WCHAR);
324 if ((host = wmemchr( target->Buffer, '/', len )))
326 host++;
327 len -= host - target->Buffer;
328 if (!(ptr = wmemchr( host, ':', len ))) ptr = wmemchr( host, '/', len );
329 if (!ptr) ptr = host + len;
331 else
333 host = target->Buffer;
334 ptr = host + len;
337 if (!(hostonly = malloc( (ptr - host + 1) * sizeof(WCHAR) ))) return FALSE;
338 memcpy( hostonly, host, (ptr - host) * sizeof(WCHAR) );
339 hostonly[ptr - host] = 0;
341 ret = CredReadW( hostonly, CRED_TYPE_DOMAIN_PASSWORD, 0, cred );
342 free( hostonly );
343 return ret;
346 static UINT encode_base64( const char *bin, unsigned int len, char *base64 )
348 static const char base64enc[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
349 UINT n = 0, x;
351 while (len > 0)
353 /* first 6 bits, all from bin[0] */
354 base64[n++] = base64enc[(bin[0] & 0xfc) >> 2];
355 x = (bin[0] & 3) << 4;
357 /* next 6 bits, 2 from bin[0] and 4 from bin[1] */
358 if (len == 1)
360 base64[n++] = base64enc[x];
361 base64[n++] = '=';
362 base64[n++] = '=';
363 break;
365 base64[n++] = base64enc[x | ((bin[1] & 0xf0) >> 4)];
366 x = (bin[1] & 0x0f) << 2;
368 /* next 6 bits 4 from bin[1] and 2 from bin[2] */
369 if (len == 2)
371 base64[n++] = base64enc[x];
372 base64[n++] = '=';
373 break;
375 base64[n++] = base64enc[x | ((bin[2] & 0xc0) >> 6)];
377 /* last 6 bits, all from bin[2] */
378 base64[n++] = base64enc[bin[2] & 0x3f];
379 bin += 3;
380 len -= 3;
382 base64[n] = 0;
383 return n;
386 static inline char decode_char( char c )
388 if (c >= 'A' && c <= 'Z') return c - 'A';
389 if (c >= 'a' && c <= 'z') return c - 'a' + 26;
390 if (c >= '0' && c <= '9') return c - '0' + 52;
391 if (c == '+') return 62;
392 if (c == '/') return 63;
393 return 64;
396 static unsigned int decode_base64( const char *base64, unsigned int len, char *buf )
398 unsigned int i = 0;
399 char c0, c1, c2, c3;
400 const char *p = base64;
402 while (len > 4)
404 if ((c0 = decode_char( p[0] )) > 63) return 0;
405 if ((c1 = decode_char( p[1] )) > 63) return 0;
406 if ((c2 = decode_char( p[2] )) > 63) return 0;
407 if ((c3 = decode_char( p[3] )) > 63) return 0;
408 if (buf)
410 buf[i + 0] = (c0 << 2) | (c1 >> 4);
411 buf[i + 1] = (c1 << 4) | (c2 >> 2);
412 buf[i + 2] = (c2 << 6) | c3;
414 len -= 4;
415 i += 3;
416 p += 4;
418 if (p[2] == '=')
420 if ((c0 = decode_char( p[0] )) > 63) return 0;
421 if ((c1 = decode_char( p[1] )) > 63) return 0;
422 if (buf) buf[i] = (c0 << 2) | (c1 >> 4);
423 i++;
425 else if (p[3] == '=')
427 if ((c0 = decode_char( p[0] )) > 63) return 0;
428 if ((c1 = decode_char( p[1] )) > 63) return 0;
429 if ((c2 = decode_char( p[2] )) > 63) return 0;
430 if (buf)
432 buf[i + 0] = (c0 << 2) | (c1 >> 4);
433 buf[i + 1] = (c1 << 4) | (c2 >> 2);
435 i += 2;
437 else
439 if ((c0 = decode_char( p[0] )) > 63) return 0;
440 if ((c1 = decode_char( p[1] )) > 63) return 0;
441 if ((c2 = decode_char( p[2] )) > 63) return 0;
442 if ((c3 = decode_char( p[3] )) > 63) return 0;
443 if (buf)
445 buf[i + 0] = (c0 << 2) | (c1 >> 4);
446 buf[i + 1] = (c1 << 4) | (c2 >> 2);
447 buf[i + 2] = (c2 << 6) | c3;
449 i += 3;
451 return i;
454 struct md4_ctx
456 unsigned int buf[4];
457 unsigned int i[2];
458 unsigned char in[64];
459 unsigned char digest[16];
462 void WINAPI MD4Init( struct md4_ctx * );
463 void WINAPI MD4Update( struct md4_ctx *, const char *, unsigned int );
464 void WINAPI MD4Final( struct md4_ctx * );
466 static void create_ntlm1_session_key( const char *secret, unsigned int len, char *session_key )
468 struct md4_ctx ctx;
469 char hash[16];
471 MD4Init( &ctx );
472 MD4Update( &ctx, secret, len );
473 MD4Final( &ctx );
474 memcpy( hash, ctx.digest, 16 );
476 MD4Init( &ctx );
477 MD4Update( &ctx, hash, 16 );
478 MD4Final( &ctx );
479 memcpy( session_key, ctx.digest, 16 );
482 struct md5_ctx
484 unsigned int i[2];
485 unsigned int buf[4];
486 unsigned char in[64];
487 unsigned char digest[16];
490 void WINAPI MD5Init( struct md5_ctx * );
491 void WINAPI MD5Update( struct md5_ctx *, const char *, unsigned int );
492 void WINAPI MD5Final( struct md5_ctx * );
494 static void calc_ntlm2_subkey( const char *session_key, const char *magic, char *subkey )
496 struct md5_ctx ctx;
498 MD5Init( &ctx );
499 MD5Update( &ctx, session_key, 16 );
500 MD5Update( &ctx, magic, strlen(magic) + 1 );
501 MD5Final( &ctx );
502 memcpy( subkey, ctx.digest, 16 );
505 static const char client_to_server_sign_constant[] = "session key to client-to-server signing key magic constant";
506 static const char client_to_server_seal_constant[] = "session key to client-to-server sealing key magic constant";
507 static const char server_to_client_sign_constant[] = "session key to server-to-client signing key magic constant";
508 static const char server_to_client_seal_constant[] = "session key to server-to-client sealing key magic constant";
510 static void create_ntlm2_subkeys( struct ntlm_ctx *ctx )
512 if (ctx->mode == MODE_CLIENT)
514 calc_ntlm2_subkey( ctx->session_key, client_to_server_sign_constant, ctx->crypt.ntlm2.send_sign_key );
515 calc_ntlm2_subkey( ctx->session_key, client_to_server_seal_constant, ctx->crypt.ntlm2.send_seal_key );
516 calc_ntlm2_subkey( ctx->session_key, server_to_client_sign_constant, ctx->crypt.ntlm2.recv_sign_key );
517 calc_ntlm2_subkey( ctx->session_key, server_to_client_seal_constant, ctx->crypt.ntlm2.recv_seal_key );
519 else
521 calc_ntlm2_subkey( ctx->session_key, server_to_client_sign_constant, ctx->crypt.ntlm2.send_sign_key );
522 calc_ntlm2_subkey( ctx->session_key, server_to_client_seal_constant, ctx->crypt.ntlm2.send_seal_key );
523 calc_ntlm2_subkey( ctx->session_key, client_to_server_sign_constant, ctx->crypt.ntlm2.recv_sign_key );
524 calc_ntlm2_subkey( ctx->session_key, client_to_server_seal_constant, ctx->crypt.ntlm2.recv_seal_key );
529 * The arc4 code is based on dlls/advapi32/crypt_arc4.c by Mike McCormack,
530 * which in turn is based on public domain code by Wei Dai
532 static void arc4_init( struct arc4_info *info, const char *key, unsigned int len )
534 unsigned int key_idx = 0, state_idx = 0, i, a;
536 info->x = info->y = 0;
537 for (i = 0; i < 256; i++) info->state[i] = i;
539 for (i = 0; i < 256; i++)
541 a = info->state[i];
542 state_idx += key[key_idx] + a;
543 state_idx &= 0xff;
544 info->state[i] = info->state[state_idx];
545 info->state[state_idx] = a;
546 if (++key_idx >= len) key_idx = 0;
550 static void arc4_process( struct arc4_info *info, char *buf, unsigned int len )
552 char *state = info->state;
553 unsigned int x = info->x, y = info->y, a, b;
555 while (len--)
557 x = (x + 1) & 0xff;
558 a = state[x];
559 y = (y + a) & 0xff;
560 b = state[y];
561 state[x] = b;
562 state[y] = a;
563 *buf++ ^= state[(a + b) & 0xff];
566 info->x = x;
567 info->y = y;
570 static int get_buffer_index( SecBufferDesc *desc, ULONG type )
572 int idx;
573 for (idx = 0; idx < desc->cBuffers; idx++)
575 if (desc->pBuffers[idx].BufferType == type) return idx;
577 return -1;
580 static NTSTATUS NTAPI ntlm_SpInitLsaModeContext( LSA_SEC_HANDLE cred_handle, LSA_SEC_HANDLE ctx_handle,
581 UNICODE_STRING *target, ULONG ctx_req, ULONG data_rep,
582 SecBufferDesc *input, LSA_SEC_HANDLE *new_ctx_handle,
583 SecBufferDesc *output, ULONG *ctx_attr, TimeStamp *expiry,
584 BOOLEAN *mapped_ctx, SecBuffer *ctx_data )
586 NTSTATUS status = SEC_E_INSUFFICIENT_MEMORY;
587 struct ntlm_ctx *ctx = NULL;
588 char *buf, *bin, *want_flags = NULL, *username = NULL, *domain = NULL, *password = NULL;
589 unsigned int len, bin_len;
590 int idx;
592 TRACE( "%#Ix, %#Ix, %s, %#lx, %lu, %p, %p, %p, %p, %p, %p, %p\n", cred_handle, ctx_handle, debugstr_us(target),
593 ctx_req, data_rep, input, new_ctx_handle, output, ctx_attr, expiry, mapped_ctx, ctx_data );
595 /* when communicating with the client there can be the following reply packets:
596 * YR <base64 blob> should be sent to the server
597 * PW should be sent back to helper with base64 encoded password
598 * AF <base64 blob> client is done, blob should be sent to server with KK prefixed
599 * GF <string list> a string list of negotiated flags
600 * GK <base64 blob> base64 encoded session key
601 * BH <char reason> something broke
603 * The squid cache size is 2010 chars and that's what ntlm_auth uses */
605 if (!(buf = malloc( NTLM_MAX_BUF ))) return SEC_E_INSUFFICIENT_MEMORY;
606 if (!(bin = malloc( NTLM_MAX_BUF ))) goto done;
608 if (!ctx_handle && !input)
610 char *argv[5];
611 int password_len = 0;
612 struct ntlm_cred *cred = (struct ntlm_cred *)cred_handle;
614 if (!cred || cred->mode != MODE_CLIENT)
616 status = SEC_E_INVALID_HANDLE;
617 goto done;
620 argv[0] = (char *)"ntlm_auth";
621 argv[1] = (char *)"--helper-protocol=ntlmssp-client-1";
622 if (!cred->username_arg && !cred->domain_arg)
624 WKSTA_USER_INFO_1 *ui = NULL;
625 NET_API_STATUS ret;
626 CREDENTIALW *cached;
628 if (get_cached_credential( target, &cached ))
630 WCHAR *p;
631 if ((p = wcschr( cached->UserName, '\\' )))
633 if (!(domain = get_domain_arg( cached->UserName, p - cached->UserName ))) goto done;
634 p++;
636 else
638 if (!(domain = get_domain_arg( NULL, 0 ))) goto done;
639 p = cached->UserName;
641 if (!(username = get_username_arg( p, -1 ))) goto done;
643 if (cached->CredentialBlobSize)
645 password_len = WideCharToMultiByte( CP_UNIXCP, WC_NO_BEST_FIT_CHARS, (WCHAR *)cached->CredentialBlob,
646 cached->CredentialBlobSize / sizeof(WCHAR), NULL, 0, NULL, NULL );
647 if (!(password = malloc( password_len ))) goto done;
648 WideCharToMultiByte( CP_UNIXCP, WC_NO_BEST_FIT_CHARS, (WCHAR *)cached->CredentialBlob,
649 cached->CredentialBlobSize / sizeof(WCHAR), password, password_len, NULL, NULL );
651 CredFree( cached );
653 argv[2] = username;
654 argv[3] = domain;
655 argv[4] = NULL;
657 else
659 ret = NetWkstaUserGetInfo( NULL, 1, (BYTE **)&ui );
660 if (ret != NERR_Success || !ui || cred->no_cached_credentials)
662 status = SEC_E_NO_CREDENTIALS;
663 goto done;
665 if (!(username = get_username_arg( ui->wkui1_username, -1 ))) goto done;
666 NetApiBufferFree( ui );
667 TRACE("using cached credentials\n");
669 argv[2] = username;
670 argv[3] = (char *)"--use-cached-creds";
671 argv[4] = NULL;
674 else
676 argv[2] = cred->username_arg;
677 argv[3] = cred->domain_arg;
678 argv[4] = NULL;
681 if (!(ctx = calloc( 1, sizeof(*ctx) ))) goto done;
683 if ((status = ntlm_fork( ctx, argv )) != SEC_E_OK) goto done;
684 status = SEC_E_INSUFFICIENT_MEMORY;
686 ctx->mode = MODE_CLIENT;
687 memset( ctx->session_key, 0, sizeof(ctx->session_key) );
689 /* generate the dummy session key = MD4(MD4(password))*/
690 if (password || cred->password)
692 WCHAR *passwordW;
693 len = MultiByteToWideChar( CP_ACP, 0, password ? password : cred->password,
694 password ? password_len : cred->password_len, NULL, 0 );
695 if (!(passwordW = malloc( len * sizeof(WCHAR) ))) goto done;
696 MultiByteToWideChar( CP_ACP, 0, password ? password : cred->password,
697 password ? password_len : cred->password_len, passwordW, len );
699 create_ntlm1_session_key( (const char *)passwordW, len * sizeof(WCHAR), ctx->session_key );
700 free( passwordW );
703 /* allocate space for a maximum string of
704 * "SF NTLMSSP_FEATURE_SIGN NTLMSSP_FEATURE_SEAL NTLMSSP_FEATURE_SESSION_KEY"
706 if (!(want_flags = malloc( 73 ))) goto done;
707 strcpy( want_flags, "SF" );
708 if (ctx_req & ISC_REQ_CONFIDENTIALITY) strcat( want_flags, " NTLMSSP_FEATURE_SEAL" );
709 if ((ctx_req & ISC_REQ_INTEGRITY) || (ctx_req & ISC_REQ_REPLAY_DETECT) ||
710 (ctx_req & ISC_REQ_SEQUENCE_DETECT)) strcat( want_flags, " NTLMSSP_FEATURE_SIGN" );
712 if (ctx_req & ISC_REQ_CONNECTION) ctx->attrs |= ISC_RET_CONNECTION;
713 if (ctx_req & ISC_REQ_EXTENDED_ERROR) ctx->attrs |= ISC_RET_EXTENDED_ERROR;
714 if (ctx_req & ISC_REQ_MUTUAL_AUTH) ctx->attrs |= ISC_RET_MUTUAL_AUTH;
715 if (ctx_req & ISC_REQ_USE_DCE_STYLE) ctx->attrs |= ISC_RET_USED_DCE_STYLE;
716 if (ctx_req & ISC_REQ_DELEGATE) ctx->attrs |= ISC_RET_DELEGATE;
717 if (ctx_req & ISC_REQ_STREAM) FIXME( "ISC_REQ_STREAM\n" );
719 /* use cached credentials if no password was given, fall back to an empty password on failure */
720 if (!password && !cred->password)
722 strcpy( buf, "OK" );
723 if ((status = ntlm_chat( ctx, buf, NTLM_MAX_BUF, &len )) != SEC_E_OK) goto done;
725 /* if the helper replied with "PW" using cached credentials failed */
726 if (!strncmp( buf, "PW", 2 ))
728 TRACE( "using cached credentials failed\n" );
729 strcpy( buf, "PW AA==" );
731 else strcpy( buf, "OK" ); /* just do a noop on the next run */
733 else
735 strcpy( buf, "PW " );
736 encode_base64( password ? password : cred->password, password ? password_len : cred->password_len, buf + 3 );
739 TRACE( "sending to ntlm_auth: %s\n", debugstr_a(buf) );
740 if ((status = ntlm_chat( ctx, buf, NTLM_MAX_BUF, &len )) != SEC_E_OK) goto done;
741 TRACE( "ntlm_auth returned %s\n", debugstr_a(buf) );
743 if (strlen( want_flags ) > 2)
745 TRACE( "want flags are %s\n", debugstr_a(want_flags) );
746 strcpy( buf, want_flags );
747 if ((status = ntlm_chat( ctx, buf, NTLM_MAX_BUF, &len )) != SEC_E_OK) goto done;
748 if (!strncmp( buf, "BH", 2 )) ERR( "ntlm_auth doesn't understand new command set\n" );
751 strcpy( buf, "YR" );
752 if ((status = ntlm_chat( ctx, buf, NTLM_MAX_BUF, &len )) != SEC_E_OK) goto done;
753 TRACE( "ntlm_auth returned %s\n", buf );
754 if (strncmp( buf, "YR ", 3 ))
756 status = SEC_E_INTERNAL_ERROR;
757 goto done;
759 bin_len = decode_base64( buf + 3, len - 3, bin );
761 *new_ctx_handle = (LSA_SEC_HANDLE)ctx;
762 status = SEC_I_CONTINUE_NEEDED;
764 else /* !ctx_handle && !input */
766 if (!input || ((idx = get_buffer_index( input, SECBUFFER_TOKEN )) == -1))
768 status = SEC_E_INVALID_TOKEN;
769 goto done;
772 ctx = (struct ntlm_ctx *)ctx_handle;
773 if (!ctx || ctx->mode != MODE_CLIENT)
775 status = SEC_E_INVALID_HANDLE;
776 goto done;
779 if (!input->pBuffers[idx].pvBuffer || input->pBuffers[idx].cbBuffer > NTLM_MAX_BUF)
781 status = SEC_E_INVALID_TOKEN;
782 goto done;
784 bin_len = input->pBuffers[idx].cbBuffer;
785 memcpy( bin, input->pBuffers[idx].pvBuffer, bin_len );
787 strcpy( buf, "TT " );
788 encode_base64( bin, bin_len, buf + 3 );
789 TRACE( "server sent: %s\n", debugstr_a(buf) );
791 if ((status = ntlm_chat( ctx, buf, NTLM_MAX_BUF, &len ))) goto done;
792 TRACE( "ntlm_auth returned: %s\n", debugstr_a(buf) );
794 if (strncmp( buf, "KK ", 3 ) && strncmp( buf, "AF ", 3 ))
796 status = SEC_E_INVALID_TOKEN;
797 goto done;
799 bin_len = decode_base64( buf + 3, len - 3, bin );
801 *new_ctx_handle = (LSA_SEC_HANDLE)ctx;
802 status = SEC_E_OK;
805 if (!output || ((idx = get_buffer_index( output, SECBUFFER_TOKEN )) == -1))
807 if (!ctx_handle && !input) *new_ctx_handle = 0;
808 status = SEC_E_BUFFER_TOO_SMALL;
809 goto done;
812 if (ctx_req & ISC_REQ_ALLOCATE_MEMORY)
814 /* freed with secur32.FreeContextBuffer */
815 if (!(output->pBuffers[idx].pvBuffer = RtlAllocateHeap( GetProcessHeap(), 0, bin_len )))
817 status = SEC_E_INSUFFICIENT_MEMORY;
818 goto done;
820 output->pBuffers[idx].cbBuffer = bin_len;
822 else if (output->pBuffers[idx].cbBuffer < bin_len)
824 if (!ctx_handle && !input) *new_ctx_handle = 0;
825 status = SEC_E_BUFFER_TOO_SMALL;
826 goto done;
829 if (!output->pBuffers[idx].pvBuffer)
831 if (!ctx_handle && !input) *new_ctx_handle = 0;
832 status = SEC_E_INTERNAL_ERROR;
833 goto done;
836 output->pBuffers[idx].cbBuffer = bin_len;
837 memcpy( output->pBuffers[idx].pvBuffer, bin, bin_len );
838 if (status == SEC_E_OK)
840 strcpy( buf, "GF" );
841 if ((status = ntlm_chat( ctx, buf, NTLM_MAX_BUF, &len )) != SEC_E_OK) goto done;
842 if (len < 3) ctx->flags = 0;
843 else sscanf( buf + 3, "%x", &ctx->flags );
845 strcpy( buf, "GK" );
846 if ((status = ntlm_chat( ctx, buf, NTLM_MAX_BUF, &len )) != SEC_E_OK) goto done;
848 if (!strncmp( buf, "BH", 2 )) TRACE( "no key negotiated\n" );
849 else if (!strncmp( buf, "GK ", 3 ))
851 bin_len = decode_base64( buf + 3, len - 3, bin );
852 TRACE( "session key is %s\n", debugstr_a(buf + 3) );
853 memcpy( ctx->session_key, bin, bin_len );
856 arc4_init( &ctx->crypt.ntlm.arc4info, ctx->session_key, 16 );
857 ctx->crypt.ntlm.seq_no = 0;
858 create_ntlm2_subkeys( ctx );
859 arc4_init( &ctx->crypt.ntlm2.send_arc4info, ctx->crypt.ntlm2.send_seal_key, 16 );
860 arc4_init( &ctx->crypt.ntlm2.recv_arc4info, ctx->crypt.ntlm2.recv_seal_key, 16 );
861 ctx->crypt.ntlm2.send_seq_no = 0;
862 ctx->crypt.ntlm2.recv_seq_no = 0;
865 done:
866 if (status != SEC_E_OK && status != SEC_I_CONTINUE_NEEDED)
868 ntlm_cleanup( ctx );
869 free( ctx );
871 free( username );
872 free( domain );
873 free( password );
874 free( buf );
875 free( bin );
876 free( want_flags );
878 TRACE( "returning %#lx\n", status );
879 return status;
882 static NTSTATUS NTAPI ntlm_SpAcceptLsaModeContext( LSA_SEC_HANDLE cred_handle, LSA_SEC_HANDLE ctx_handle,
883 SecBufferDesc *input, ULONG ctx_req, ULONG data_rep,
884 LSA_SEC_HANDLE *new_ctx_handle, SecBufferDesc *output,
885 ULONG *ctx_attr, TimeStamp *expiry, BOOLEAN *mapped_ctx,
886 SecBuffer *ctx_data )
888 NTSTATUS status = SEC_E_INSUFFICIENT_MEMORY;
889 struct ntlm_ctx *ctx = NULL;
890 char *buf, *bin, *want_flags = NULL;
891 unsigned int len, bin_len;
893 TRACE( "%#Ix, %#Ix, %#lx, %lu, %p, %p, %p, %p, %p, %p, %p\n", cred_handle, ctx_handle, ctx_req, data_rep, input,
894 new_ctx_handle, output, ctx_attr, expiry, mapped_ctx, ctx_data );
895 if (ctx_req) FIXME( "ignoring flags %#lx\n", ctx_req );
897 if (!(buf = malloc( NTLM_MAX_BUF ))) return SEC_E_INSUFFICIENT_MEMORY;
898 if (!(bin = malloc( NTLM_MAX_BUF ))) goto done;
900 if (!ctx_handle)
902 struct ntlm_cred *cred = (struct ntlm_cred *)cred_handle;
903 char *argv[3];
905 if (!cred || cred->mode != MODE_SERVER)
907 status = SEC_E_INVALID_HANDLE;
908 goto done;
911 if (!input || input->cBuffers < 1)
913 status = SEC_E_INCOMPLETE_MESSAGE;
914 goto done;
917 if (input->pBuffers[0].cbBuffer > NTLM_MAX_BUF)
919 status = SEC_E_INVALID_TOKEN;
920 goto done;
922 else bin_len = input->pBuffers[0].cbBuffer;
924 if (!(ctx = calloc( 1, sizeof(*ctx) ))) goto done;
926 argv[0] = (char *)"ntlm_auth";
927 argv[1] = (char *)"--helper-protocol=squid-2.5-ntlmssp";
928 argv[2] = NULL;
929 if ((status = ntlm_fork( ctx, argv )) != SEC_E_OK) goto done;
930 ctx->mode = MODE_SERVER;
932 if (!(want_flags = malloc( 73 )))
934 status = SEC_E_INSUFFICIENT_MEMORY;
935 goto done;
937 strcpy( want_flags, "SF" );
938 if (ctx_req & ASC_REQ_CONFIDENTIALITY) strcat( want_flags, " NTLMSSP_FEATURE_SEAL" );
939 if (ctx_req & ASC_REQ_CONNECTION)
941 strcat( want_flags, " NTLMSSP_FEATURE_SESSION_KEY" );
942 ctx->attrs |= ASC_RET_CONNECTION;
944 if (ctx_req & ASC_REQ_INTEGRITY) strcat( want_flags, " NTLMSSP_FEATURE_SIGN" );
945 if (ctx_req & ASC_REQ_ALLOCATE_MEMORY) FIXME( "ASC_REQ_ALLOCATE_MEMORY\n" );
946 if (ctx_req & ASC_REQ_EXTENDED_ERROR) FIXME( "ASC_REQ_EXTENDED_ERROR\n" );
947 if (ctx_req & ASC_REQ_MUTUAL_AUTH) FIXME( "ASC_REQ_MUTUAL_AUTH\n" );
948 if (ctx_req & ASC_REQ_REPLAY_DETECT) FIXME( "ASC_REQ_REPLAY_DETECT\n" );
949 if (ctx_req & ASC_REQ_SEQUENCE_DETECT) FIXME( "ASC_REQ_SEQUENCE_DETECT\n" );
950 if (ctx_req & ASC_REQ_STREAM) FIXME( "ASC_REQ_STREAM\n" );
952 if (strlen( want_flags ) > 3)
954 TRACE( "want flags are %s\n", debugstr_a(want_flags) );
955 strcpy( buf, want_flags );
956 if ((status = ntlm_chat( ctx, buf, NTLM_MAX_BUF, &len )) != SEC_E_OK) goto done;
957 if (!strncmp( buf, "BH", 2 )) ERR( "ntlm_auth doesn't understand new command set\n" );
960 memcpy( bin, input->pBuffers[0].pvBuffer, bin_len );
961 strcpy( buf, "YR " );
962 encode_base64( bin, bin_len, buf + 3 );
964 if ((status = ntlm_chat( ctx, buf, NTLM_MAX_BUF, &len )) != SEC_E_OK) goto done;
965 TRACE( "ntlm_auth returned %s\n", buf );
966 if (strncmp( buf, "TT ", 3))
968 status = SEC_E_INTERNAL_ERROR;
969 goto done;
971 bin_len = decode_base64( buf + 3, len - 3, bin );
973 if (!output || output->cBuffers < 1)
975 status = SEC_E_INSUFFICIENT_MEMORY;
976 goto done;
978 output->pBuffers[0].cbBuffer = bin_len;
979 output->pBuffers[0].BufferType = SECBUFFER_DATA;
980 memcpy( output->pBuffers[0].pvBuffer, bin, bin_len );
982 *new_ctx_handle = (LSA_SEC_HANDLE)ctx;
983 status = SEC_I_CONTINUE_NEEDED;
985 else
987 if (!input || input->cBuffers < 1)
989 status = SEC_E_INCOMPLETE_MESSAGE;
990 goto done;
993 ctx = (struct ntlm_ctx *)ctx_handle;
994 if (!ctx || ctx->mode != MODE_SERVER)
996 status = SEC_E_INVALID_HANDLE;
997 goto done;
1000 if (input->pBuffers[0].cbBuffer > NTLM_MAX_BUF)
1002 status = SEC_E_INVALID_TOKEN;
1003 goto done;
1005 else bin_len = input->pBuffers[0].cbBuffer;
1006 memcpy( bin, input->pBuffers[0].pvBuffer, bin_len );
1008 strcpy( buf, "KK " );
1009 encode_base64( bin, bin_len, buf + 3 );
1011 TRACE( "client sent %s\n", debugstr_a(buf) );
1012 if ((status = ntlm_chat( ctx, buf, NTLM_MAX_BUF, &len )) != SEC_E_OK) goto done;
1013 TRACE( "ntlm_auth returned %s\n", debugstr_a(buf) );
1015 /* At this point, we get a NA if the user didn't authenticate, but a BH if ntlm_auth could not
1016 * connect to winbindd. Apart from running Wine as root, there is no way to fix this for now,
1017 * so just handle this as a failed login. */
1018 if (strncmp( buf, "AF ", 3 ))
1020 if (!strncmp( buf, "NA ", 3 ))
1022 status = SEC_E_LOGON_DENIED;
1023 goto done;
1025 else
1027 const char err_v3[] = "BH NT_STATUS_ACCESS_DENIED";
1028 const char err_v4[] = "BH NT_STATUS_UNSUCCESSFUL";
1030 if ((len >= strlen(err_v3) && !strncmp( buf, err_v3, strlen(err_v3) )) ||
1031 (len >= strlen(err_v4) && !strncmp( buf, err_v4, strlen(err_v4) )))
1033 TRACE( "connection to winbindd failed\n" );
1034 status = SEC_E_LOGON_DENIED;
1036 else status = SEC_E_INTERNAL_ERROR;
1037 goto done;
1040 output->pBuffers[0].cbBuffer = 0;
1042 strcpy( buf, "GF" );
1043 if ((status = ntlm_chat( ctx, buf, NTLM_MAX_BUF, &len )) != SEC_E_OK) goto done;
1044 if (len < 3) ctx->flags = 0;
1045 else sscanf( buf + 3, "%x", &ctx->flags );
1047 strcpy( buf, "GK" );
1048 if ((status = ntlm_chat( ctx, buf, NTLM_MAX_BUF, &len )) != SEC_E_OK) goto done;
1050 if (!strncmp( buf, "BH", 2 )) TRACE( "no key negotiated\n" );
1051 else if (!strncmp( buf, "GK ", 3 ))
1053 bin_len = decode_base64( buf + 3, len - 3, bin );
1054 TRACE( "session key is %s\n", debugstr_a(buf + 3) );
1055 memcpy( ctx->session_key, bin, bin_len );
1058 if (len < 3) memset( ctx->session_key, 0 , 16 );
1059 else
1061 if (!strncmp( buf, "BH ", 3 ))
1063 TRACE( "helper sent %s\n", debugstr_a(buf + 3) );
1064 /*FIXME: generate dummy session key = MD4(MD4(password))*/
1065 memset( ctx->session_key, 0 , 16 );
1067 else if (!strncmp( buf, "GK ", 3 ))
1069 bin_len = decode_base64( buf + 3, len - 3, bin );
1070 TRACE( "session key is %s\n", debugstr_a(buf + 3) );
1071 memcpy( ctx->session_key, bin, 16 );
1074 arc4_init( &ctx->crypt.ntlm.arc4info, ctx->session_key, 16 );
1075 ctx->crypt.ntlm.seq_no = 0;
1077 *new_ctx_handle = (LSA_SEC_HANDLE)ctx;
1078 status = SEC_E_OK;
1081 done:
1082 if (status != SEC_E_OK && status != SEC_I_CONTINUE_NEEDED)
1084 ntlm_cleanup( ctx );
1085 free( ctx );
1087 free( buf );
1088 free( bin );
1089 free( want_flags );
1091 TRACE( "returning %#lx\n", status );
1092 return status;
1095 static NTSTATUS NTAPI ntlm_SpDeleteContext( LSA_SEC_HANDLE handle )
1097 struct ntlm_ctx *ctx = (struct ntlm_ctx *)handle;
1099 TRACE( "%#Ix\n", handle );
1101 if (!ctx) return SEC_E_INVALID_HANDLE;
1102 ntlm_cleanup( ctx );
1103 free( ctx );
1104 return SEC_E_OK;
1107 static SecPkgInfoW *build_package_info( const SecPkgInfoW *info )
1109 SecPkgInfoW *ret;
1110 DWORD size_name = (wcslen(info->Name) + 1) * sizeof(WCHAR);
1111 DWORD size_comment = (wcslen(info->Comment) + 1) * sizeof(WCHAR);
1113 if (!(ret = RtlAllocateHeap( GetProcessHeap(), 0, sizeof(*ret) + size_name + size_comment ))) return NULL;
1114 ret->fCapabilities = info->fCapabilities;
1115 ret->wVersion = info->wVersion;
1116 ret->wRPCID = info->wRPCID;
1117 ret->cbMaxToken = info->cbMaxToken;
1118 ret->Name = (SEC_WCHAR *)(ret + 1);
1119 memcpy( ret->Name, info->Name, size_name );
1120 ret->Comment = (SEC_WCHAR *)((char *)ret->Name + size_name);
1121 memcpy( ret->Comment, info->Comment, size_comment );
1122 return ret;
1125 static NTSTATUS NTAPI ntlm_SpQueryContextAttributes( LSA_SEC_HANDLE handle, ULONG attr, void *buf )
1127 TRACE( "%#Ix, %lu, %p\n", handle, attr, buf );
1129 if (!handle) return SEC_E_INVALID_HANDLE;
1131 switch (attr)
1133 #define X(x) case (x) : FIXME(#x" stub\n"); break
1134 X(SECPKG_ATTR_ACCESS_TOKEN);
1135 X(SECPKG_ATTR_AUTHORITY);
1136 X(SECPKG_ATTR_DCE_INFO);
1137 X(SECPKG_ATTR_KEY_INFO);
1138 X(SECPKG_ATTR_LIFESPAN);
1139 X(SECPKG_ATTR_NAMES);
1140 X(SECPKG_ATTR_NATIVE_NAMES);
1141 X(SECPKG_ATTR_PACKAGE_INFO);
1142 X(SECPKG_ATTR_PASSWORD_EXPIRY);
1143 X(SECPKG_ATTR_SESSION_KEY);
1144 X(SECPKG_ATTR_STREAM_SIZES);
1145 X(SECPKG_ATTR_TARGET_INFORMATION);
1146 case SECPKG_ATTR_FLAGS:
1148 SecPkgContext_Flags *flags = (SecPkgContext_Flags *)buf;
1149 struct ntlm_ctx *ctx = (struct ntlm_ctx *)handle;
1151 flags->Flags = 0;
1152 if (ctx->flags & FLAG_NEGOTIATE_SIGN) flags->Flags |= ISC_RET_INTEGRITY;
1153 if (ctx->flags & FLAG_NEGOTIATE_SEAL) flags->Flags |= ISC_RET_CONFIDENTIALITY;
1154 return SEC_E_OK;
1156 case SECPKG_ATTR_SIZES:
1158 SecPkgContext_Sizes *sizes = (SecPkgContext_Sizes *)buf;
1159 sizes->cbMaxToken = NTLM_MAX_BUF;
1160 sizes->cbMaxSignature = 16;
1161 sizes->cbBlockSize = 0;
1162 sizes->cbSecurityTrailer = 16;
1163 return SEC_E_OK;
1165 case SECPKG_ATTR_NEGOTIATION_INFO:
1167 SecPkgContext_NegotiationInfoW *info = (SecPkgContext_NegotiationInfoW *)buf;
1168 if (!(info->PackageInfo = build_package_info( &ntlm_package_info ))) return SEC_E_INSUFFICIENT_MEMORY;
1169 info->NegotiationState = SECPKG_NEGOTIATION_COMPLETE;
1170 return SEC_E_OK;
1172 #undef X
1173 default:
1174 FIXME( "unknown attribute %lu\n", attr );
1175 break;
1178 return SEC_E_UNSUPPORTED_FUNCTION;
1181 static SECPKG_FUNCTION_TABLE ntlm_table =
1183 ntlm_LsaApInitializePackage,
1184 NULL, /* LsaLogonUser */
1185 NULL, /* CallPackage */
1186 NULL, /* LogonTerminated */
1187 NULL, /* CallPackageUntrusted */
1188 NULL, /* CallPackagePassthrough */
1189 NULL, /* LogonUserEx */
1190 NULL, /* LogonUserEx2 */
1191 ntlm_SpInitialize,
1192 NULL, /* SpShutdown */
1193 ntlm_SpGetInfo,
1194 NULL, /* AcceptCredentials */
1195 ntlm_SpAcquireCredentialsHandle,
1196 NULL, /* SpQueryCredentialsAttributes */
1197 ntlm_SpFreeCredentialsHandle,
1198 NULL, /* SaveCredentials */
1199 NULL, /* GetCredentials */
1200 NULL, /* DeleteCredentials */
1201 ntlm_SpInitLsaModeContext,
1202 ntlm_SpAcceptLsaModeContext,
1203 ntlm_SpDeleteContext,
1204 NULL, /* ApplyControlToken */
1205 NULL, /* GetUserInfo */
1206 NULL, /* GetExtendedInformation */
1207 ntlm_SpQueryContextAttributes,
1208 NULL, /* SpAddCredentials */
1209 NULL, /* SetExtendedInformation */
1210 NULL, /* SetContextAttributes */
1211 NULL, /* SetCredentialsAttributes */
1212 NULL, /* ChangeAccountPassword */
1213 NULL, /* QueryMetaData */
1214 NULL, /* ExchangeMetaData */
1215 NULL, /* GetCredUIContext */
1216 NULL, /* UpdateCredentials */
1217 NULL, /* ValidateTargetInfo */
1218 NULL, /* PostLogonUser */
1221 NTSTATUS NTAPI SpLsaModeInitialize( ULONG lsa_version, ULONG *package_version, SECPKG_FUNCTION_TABLE **table,
1222 ULONG *table_count )
1224 TRACE( "%#lx, %p, %p, %p\n", lsa_version, package_version, table, table_count );
1226 *package_version = SECPKG_INTERFACE_VERSION;
1227 *table = &ntlm_table;
1228 *table_count = 1;
1229 return STATUS_SUCCESS;
1232 static NTSTATUS NTAPI ntlm_SpInstanceInit( ULONG version, SECPKG_DLL_FUNCTIONS *dll_functions, void **user_functions )
1234 TRACE( "%#lx, %p, %p\n", version, dll_functions, user_functions );
1235 return STATUS_SUCCESS;
1238 struct hmac_md5_ctx
1240 struct md5_ctx ctx;
1241 char outer_padding[64];
1244 static void hmac_md5_init( struct hmac_md5_ctx *ctx, const char *key, unsigned int key_len )
1246 char inner_padding[64], tmp_key[16];
1247 unsigned int i;
1249 if (key_len > 64)
1251 struct md5_ctx tmp_ctx;
1253 MD5Init( &tmp_ctx );
1254 MD5Update( &tmp_ctx, key, key_len );
1255 MD5Final( &tmp_ctx );
1256 memcpy( tmp_key, tmp_ctx.digest, 16 );
1258 key = tmp_key;
1259 key_len = 16;
1262 memset( inner_padding, 0, 64 );
1263 memset( ctx->outer_padding, 0, 64 );
1264 memcpy( inner_padding, key, key_len );
1265 memcpy( ctx->outer_padding, key, key_len );
1267 for (i = 0; i < 64; i++)
1269 inner_padding[i] ^= 0x36;
1270 ctx->outer_padding[i] ^= 0x5c;
1273 MD5Init( &ctx->ctx );
1274 MD5Update( &ctx->ctx, inner_padding, 64 );
1277 static void hmac_md5_update( struct hmac_md5_ctx *ctx, const char *buf, unsigned int len )
1279 MD5Update( &ctx->ctx, buf, len );
1282 static void hmac_md5_final( struct hmac_md5_ctx *ctx, char *digest )
1284 struct md5_ctx outer_ctx;
1285 char inner_digest[16];
1287 MD5Final( &ctx->ctx );
1288 memcpy( inner_digest, ctx->ctx.digest, 16 );
1290 MD5Init( &outer_ctx );
1291 MD5Update( &outer_ctx, ctx->outer_padding, 64 );
1292 MD5Update( &outer_ctx, inner_digest, 16 );
1293 MD5Final( &outer_ctx );
1295 memcpy( digest, outer_ctx.digest, 16 );
1298 static SECURITY_STATUS create_signature( struct ntlm_ctx *ctx, unsigned int flags, SecBufferDesc *msg, int idx,
1299 enum sign_direction dir, BOOL encrypt )
1301 unsigned int i, sign_version = 1;
1302 char *sig = msg->pBuffers[idx].pvBuffer;
1304 if (flags & FLAG_NEGOTIATE_NTLM2 && flags & FLAG_NEGOTIATE_SIGN)
1306 char digest[16], seq_no[4];
1307 struct hmac_md5_ctx hmac_md5;
1309 if (dir == SIGN_SEND)
1311 seq_no[0] = (ctx->crypt.ntlm2.send_seq_no >> 0) & 0xff;
1312 seq_no[1] = (ctx->crypt.ntlm2.send_seq_no >> 8) & 0xff;
1313 seq_no[2] = (ctx->crypt.ntlm2.send_seq_no >> 16) & 0xff;
1314 seq_no[3] = (ctx->crypt.ntlm2.send_seq_no >> 24) & 0xff;
1315 ctx->crypt.ntlm2.send_seq_no++;
1317 hmac_md5_init( &hmac_md5, ctx->crypt.ntlm2.send_sign_key, 16 );
1319 else
1321 seq_no[0] = (ctx->crypt.ntlm2.recv_seq_no >> 0) & 0xff;
1322 seq_no[1] = (ctx->crypt.ntlm2.recv_seq_no >> 8) & 0xff;
1323 seq_no[2] = (ctx->crypt.ntlm2.recv_seq_no >> 16) & 0xff;
1324 seq_no[3] = (ctx->crypt.ntlm2.recv_seq_no >> 24) & 0xff;
1325 ctx->crypt.ntlm2.recv_seq_no++;
1327 hmac_md5_init( &hmac_md5, ctx->crypt.ntlm2.recv_sign_key, 16 );
1330 hmac_md5_update( &hmac_md5, seq_no, 4 );
1331 for (i = 0; i < msg->cBuffers; ++i)
1333 if (msg->pBuffers[i].BufferType & SECBUFFER_DATA)
1334 hmac_md5_update( &hmac_md5, msg->pBuffers[i].pvBuffer, msg->pBuffers[i].cbBuffer );
1336 hmac_md5_final( &hmac_md5, digest );
1338 if (encrypt && flags & FLAG_NEGOTIATE_KEY_EXCHANGE)
1340 if (dir == SIGN_SEND)
1341 arc4_process( &ctx->crypt.ntlm2.send_arc4info, digest, 8 );
1342 else
1343 arc4_process( &ctx->crypt.ntlm2.recv_arc4info, digest, 8 );
1346 sig[0] = (sign_version >> 0) & 0xff;
1347 sig[1] = (sign_version >> 8) & 0xff;
1348 sig[2] = (sign_version >> 16) & 0xff;
1349 sig[3] = (sign_version >> 24) & 0xff;
1350 memcpy( sig + 4, digest, 8 );
1351 memcpy( sig + 12, seq_no, 4 );
1353 msg->pBuffers[idx].cbBuffer = 16;
1354 return SEC_E_OK;
1357 if (flags & FLAG_NEGOTIATE_SIGN)
1359 unsigned int crc = 0;
1361 for (i = 0; i < msg->cBuffers; ++i)
1363 if (msg->pBuffers[i].BufferType & SECBUFFER_DATA)
1364 crc = RtlComputeCrc32( crc, msg->pBuffers[i].pvBuffer, msg->pBuffers[i].cbBuffer );
1367 sig[0] = (sign_version >> 0) & 0xff;
1368 sig[1] = (sign_version >> 8) & 0xff;
1369 sig[2] = (sign_version >> 16) & 0xff;
1370 sig[3] = (sign_version >> 24) & 0xff;
1371 memset( sig + 4, 0, 4 );
1372 sig[8] = (crc >> 0) & 0xff;
1373 sig[9] = (crc >> 8) & 0xff;
1374 sig[10] = (crc >> 16) & 0xff;
1375 sig[11] = (crc >> 24) & 0xff;
1376 sig[12] = (ctx->crypt.ntlm.seq_no >> 0) & 0xff;
1377 sig[13] = (ctx->crypt.ntlm.seq_no >> 8) & 0xff;
1378 sig[14] = (ctx->crypt.ntlm.seq_no >> 16) & 0xff;
1379 sig[15] = (ctx->crypt.ntlm.seq_no >> 24) & 0xff;
1380 ctx->crypt.ntlm.seq_no++;
1382 if (encrypt) arc4_process( &ctx->crypt.ntlm.arc4info, sig + 4, 12 );
1383 return SEC_E_OK;
1386 if (flags & FLAG_NEGOTIATE_ALWAYS_SIGN || !flags)
1388 /* create dummy signature */
1389 memset( msg->pBuffers[idx].pvBuffer, 0, 16 );
1390 memset( msg->pBuffers[idx].pvBuffer, 1, 1 );
1391 msg->pBuffers[idx].cbBuffer = 16;
1392 return SEC_E_OK;
1395 return SEC_E_UNSUPPORTED_FUNCTION;
1398 static NTSTATUS NTAPI ntlm_SpMakeSignature( LSA_SEC_HANDLE handle, ULONG qop, SecBufferDesc *msg, ULONG msg_seq_no )
1400 struct ntlm_ctx *ctx = (struct ntlm_ctx *)handle;
1401 int idx;
1403 TRACE( "%#Ix, %#lx, %p, %lu\n", handle, qop, msg, msg_seq_no );
1404 if (qop) FIXME( "ignoring quality of protection %#lx\n", qop );
1405 if (msg_seq_no) FIXME( "ignoring message sequence number %lu\n", msg_seq_no );
1407 if (!handle) return SEC_E_INVALID_HANDLE;
1408 if (!msg || !msg->pBuffers || msg->cBuffers < 2 || (idx = get_buffer_index( msg, SECBUFFER_TOKEN )) == -1)
1409 return SEC_E_INVALID_TOKEN;
1410 if (msg->pBuffers[idx].cbBuffer < 16) return SEC_E_BUFFER_TOO_SMALL;
1412 return create_signature( ctx, ctx->flags, msg, idx, SIGN_SEND, TRUE );
1415 static NTSTATUS verify_signature( struct ntlm_ctx *ctx, unsigned int flags, SecBufferDesc *msg, int idx )
1417 NTSTATUS status;
1418 unsigned int i;
1419 SecBufferDesc desc;
1420 SecBuffer *buf;
1421 char sig[16];
1423 if (!(buf = malloc( msg->cBuffers * sizeof(*buf) ))) return SEC_E_INSUFFICIENT_MEMORY;
1424 desc.ulVersion = SECBUFFER_VERSION;
1425 desc.cBuffers = msg->cBuffers;
1426 desc.pBuffers = buf;
1428 for (i = 0; i < msg->cBuffers; i++)
1430 if (msg->pBuffers[i].BufferType == SECBUFFER_TOKEN)
1432 buf[i].BufferType = SECBUFFER_TOKEN;
1433 buf[i].cbBuffer = 16;
1434 buf[i].pvBuffer = sig;
1436 else
1438 buf[i].BufferType = msg->pBuffers[i].BufferType;
1439 buf[i].cbBuffer = msg->pBuffers[i].cbBuffer;
1440 buf[i].pvBuffer = msg->pBuffers[i].pvBuffer;
1444 if ((status = create_signature( ctx, flags, &desc, idx, SIGN_RECV, TRUE )) == SEC_E_OK)
1446 if (memcmp( (char *)buf[idx].pvBuffer + 8, (char *)msg->pBuffers[idx].pvBuffer + 8, 8 ))
1447 status = SEC_E_MESSAGE_ALTERED;
1450 free( buf );
1451 return status;
1454 static NTSTATUS NTAPI ntlm_SpVerifySignature( LSA_SEC_HANDLE handle, SecBufferDesc *msg, ULONG msg_seq_no, ULONG *qop )
1456 struct ntlm_ctx *ctx = (struct ntlm_ctx *)handle;
1457 int idx;
1459 TRACE( "%#Ix, %p, %lu, %p\n", handle, msg, msg_seq_no, qop );
1460 if (msg_seq_no) FIXME( "ignoring message sequence number %lu\n", msg_seq_no );
1462 if (!handle) return SEC_E_INVALID_HANDLE;
1463 if (!msg || !msg->pBuffers || msg->cBuffers < 2 || (idx = get_buffer_index( msg, SECBUFFER_TOKEN )) == -1)
1464 return SEC_E_INVALID_TOKEN;
1465 if (msg->pBuffers[idx].cbBuffer < 16) return SEC_E_BUFFER_TOO_SMALL;
1467 return verify_signature( ctx, ctx->flags, msg, idx );
1470 static NTSTATUS NTAPI ntlm_SpSealMessage( LSA_SEC_HANDLE handle, ULONG qop, SecBufferDesc *msg, ULONG msg_seq_no )
1472 int token_idx, data_idx;
1473 struct ntlm_ctx *ctx;
1475 TRACE( "%#Ix, %#lx, %p %lu\n", handle, qop, msg, msg_seq_no );
1476 if (qop) FIXME( "ignoring quality of protection %#lx\n", qop );
1477 if (msg_seq_no) FIXME( "ignoring message sequence number %lu\n", msg_seq_no );
1479 if (!handle) return SEC_E_INVALID_HANDLE;
1481 if (!msg || !msg->pBuffers || msg->cBuffers < 2 ||
1482 (token_idx = get_buffer_index( msg, SECBUFFER_TOKEN )) == -1 ||
1483 (data_idx = get_buffer_index( msg, SECBUFFER_DATA )) == -1) return SEC_E_INVALID_TOKEN;
1485 if (msg->pBuffers[token_idx].cbBuffer < 16) return SEC_E_BUFFER_TOO_SMALL;
1487 ctx = (struct ntlm_ctx *)handle;
1488 if (ctx->flags & FLAG_NEGOTIATE_NTLM2 && ctx->flags & FLAG_NEGOTIATE_SEAL)
1490 create_signature( ctx, ctx->flags, msg, token_idx, SIGN_SEND, FALSE );
1492 arc4_process( &ctx->crypt.ntlm2.send_arc4info, msg->pBuffers[data_idx].pvBuffer,
1493 msg->pBuffers[data_idx].cbBuffer );
1494 if (ctx->flags & FLAG_NEGOTIATE_KEY_EXCHANGE)
1495 arc4_process( &ctx->crypt.ntlm2.send_arc4info, (char *)msg->pBuffers[token_idx].pvBuffer + 4, 8 );
1497 else
1499 char *sig = msg->pBuffers[token_idx].pvBuffer;
1501 create_signature( ctx, ctx->flags | FLAG_NEGOTIATE_SIGN, msg, token_idx, SIGN_SEND, FALSE );
1503 arc4_process( &ctx->crypt.ntlm.arc4info, msg->pBuffers[data_idx].pvBuffer, msg->pBuffers[data_idx].cbBuffer );
1504 arc4_process( &ctx->crypt.ntlm.arc4info, sig + 4, 12 );
1506 if (ctx->flags & FLAG_NEGOTIATE_ALWAYS_SIGN || !ctx->flags) memset( sig + 4, 0, 4 );
1509 return SEC_E_OK;
1512 static NTSTATUS NTAPI ntlm_SpUnsealMessage( LSA_SEC_HANDLE handle, SecBufferDesc *msg, ULONG msg_seq_no, ULONG *qop )
1514 int token_idx, data_idx;
1515 struct ntlm_ctx *ctx;
1517 TRACE( "%#Ix, %p, %lu, %p\n", handle, msg, msg_seq_no, qop );
1518 if (msg_seq_no) FIXME( "ignoring message sequence number %lu\n", msg_seq_no );
1520 if (!handle) return SEC_E_INVALID_HANDLE;
1522 if (!msg || !msg->pBuffers || msg->cBuffers < 2 ||
1523 (token_idx = get_buffer_index( msg, SECBUFFER_TOKEN )) == -1 ||
1524 (data_idx = get_buffer_index( msg, SECBUFFER_DATA )) == -1) return SEC_E_INVALID_TOKEN;
1526 if (msg->pBuffers[token_idx].cbBuffer < 16) return SEC_E_BUFFER_TOO_SMALL;
1528 ctx = (struct ntlm_ctx *)handle;
1529 if (ctx->flags & FLAG_NEGOTIATE_NTLM2 && ctx->flags & FLAG_NEGOTIATE_SEAL)
1530 arc4_process( &ctx->crypt.ntlm2.recv_arc4info, msg->pBuffers[data_idx].pvBuffer,
1531 msg->pBuffers[data_idx].cbBuffer );
1532 else
1533 arc4_process( &ctx->crypt.ntlm.arc4info, msg->pBuffers[data_idx].pvBuffer,
1534 msg->pBuffers[data_idx].cbBuffer);
1536 /* make sure we use a session key for the signature check, SealMessage always does that,
1537 even in the dummy case */
1538 return verify_signature( ctx, ctx->flags | FLAG_NEGOTIATE_SIGN, msg, token_idx );
1541 static SECPKG_USER_FUNCTION_TABLE ntlm_user_table =
1543 ntlm_SpInstanceInit,
1544 NULL, /* SpInitUserModeContext */
1545 ntlm_SpMakeSignature,
1546 ntlm_SpVerifySignature,
1547 ntlm_SpSealMessage,
1548 ntlm_SpUnsealMessage,
1549 NULL, /* SpGetContextToken */
1550 NULL, /* SpQueryContextAttributes */
1551 NULL, /* SpCompleteAuthToken */
1552 NULL, /* SpDeleteContext */
1553 NULL, /* SpFormatCredentialsFn */
1554 NULL, /* SpMarshallSupplementalCreds */
1555 NULL, /* SpExportSecurityContext */
1556 NULL /* SpImportSecurityContext */
1559 NTSTATUS NTAPI SpUserModeInitialize( ULONG lsa_version, ULONG *package_version, SECPKG_USER_FUNCTION_TABLE **table,
1560 ULONG *table_count )
1562 TRACE( "%#lx, %p, %p, %p\n", lsa_version, package_version, table, table_count );
1564 *package_version = SECPKG_INTERFACE_VERSION;
1565 *table = &ntlm_user_table;
1566 *table_count = 1;
1567 return STATUS_SUCCESS;
1570 BOOL WINAPI DllMain( HINSTANCE hinst, DWORD reason, void *reserved )
1572 switch (reason)
1574 case DLL_PROCESS_ATTACH:
1575 if (NtQueryVirtualMemory( GetCurrentProcess(), hinst, MemoryWineUnixFuncs,
1576 &ntlm_handle, sizeof(ntlm_handle), NULL ))
1577 return FALSE;
1578 DisableThreadLibraryCalls( hinst );
1579 break;
1581 return TRUE;