mirror of
git://source.winehq.org/git/wine.git
synced 2024-10-31 19:49:50 +00:00
msv1_0: Implement SpInitLsaModeContext and SpDeleteContext.
Signed-off-by: Hans Leidekker <hans@codeweavers.com> Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
parent
e73932e08d
commit
368d9cb591
4 changed files with 629 additions and 2 deletions
|
@ -1,4 +1,5 @@
|
|||
MODULE = msv1_0.dll
|
||||
IMPORTS = netapi32 advapi32
|
||||
|
||||
EXTRADLLFLAGS = -mno-cygwin
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
|
||||
#include <stdarg.h>
|
||||
#include <stdlib.h>
|
||||
#include <wchar.h>
|
||||
#include "ntstatus.h"
|
||||
#define WIN32_NO_STATUS
|
||||
#include "windef.h"
|
||||
|
@ -28,6 +29,10 @@
|
|||
#include "ntsecapi.h"
|
||||
#include "ntsecpkg.h"
|
||||
#include "rpc.h"
|
||||
#include "wincred.h"
|
||||
#include "lmwksta.h"
|
||||
#include "lmapibuf.h"
|
||||
#include "lmerr.h"
|
||||
|
||||
#include "wine/debug.h"
|
||||
#include "unixlib.h"
|
||||
|
@ -282,6 +287,558 @@ static NTSTATUS NTAPI ntlm_SpFreeCredentialsHandle( LSA_SEC_HANDLE handle )
|
|||
return SEC_E_OK;
|
||||
}
|
||||
|
||||
static BOOL get_cached_credential( const UNICODE_STRING *target, CREDENTIALW **cred )
|
||||
{
|
||||
const WCHAR *ptr, *host;
|
||||
WCHAR *hostonly;
|
||||
size_t len;
|
||||
BOOL ret;
|
||||
|
||||
if (!target) return FALSE;
|
||||
|
||||
len = target->Length / sizeof(WCHAR);
|
||||
if ((host = wmemchr( target->Buffer, '/', len )))
|
||||
{
|
||||
host++;
|
||||
len -= host - target->Buffer;
|
||||
if (!(ptr = wmemchr( host, ':', len ))) ptr = wmemchr( host, '/', len );
|
||||
if (!ptr) ptr = host + len;
|
||||
}
|
||||
else
|
||||
{
|
||||
host = target->Buffer;
|
||||
ptr = host + len;
|
||||
}
|
||||
|
||||
if (!(hostonly = malloc( (ptr - host + 1) * sizeof(WCHAR) ))) return FALSE;
|
||||
memcpy( hostonly, host, (ptr - host) * sizeof(WCHAR) );
|
||||
hostonly[ptr - host] = 0;
|
||||
|
||||
ret = CredReadW( hostonly, CRED_TYPE_DOMAIN_PASSWORD, 0, cred );
|
||||
free( hostonly );
|
||||
return ret;
|
||||
}
|
||||
|
||||
static UINT encode_base64( const char *bin, unsigned int len, char *base64 )
|
||||
{
|
||||
static const char base64enc[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||
UINT n = 0, x;
|
||||
|
||||
while (len > 0)
|
||||
{
|
||||
/* first 6 bits, all from bin[0] */
|
||||
base64[n++] = base64enc[(bin[0] & 0xfc) >> 2];
|
||||
x = (bin[0] & 3) << 4;
|
||||
|
||||
/* next 6 bits, 2 from bin[0] and 4 from bin[1] */
|
||||
if (len == 1)
|
||||
{
|
||||
base64[n++] = base64enc[x];
|
||||
base64[n++] = '=';
|
||||
base64[n++] = '=';
|
||||
break;
|
||||
}
|
||||
base64[n++] = base64enc[x | ((bin[1] & 0xf0) >> 4)];
|
||||
x = (bin[1] & 0x0f) << 2;
|
||||
|
||||
/* next 6 bits 4 from bin[1] and 2 from bin[2] */
|
||||
if (len == 2)
|
||||
{
|
||||
base64[n++] = base64enc[x];
|
||||
base64[n++] = '=';
|
||||
break;
|
||||
}
|
||||
base64[n++] = base64enc[x | ((bin[2] & 0xc0) >> 6)];
|
||||
|
||||
/* last 6 bits, all from bin[2] */
|
||||
base64[n++] = base64enc[bin[2] & 0x3f];
|
||||
bin += 3;
|
||||
len -= 3;
|
||||
}
|
||||
base64[n] = 0;
|
||||
return n;
|
||||
}
|
||||
|
||||
static inline char decode_char( 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;
|
||||
return 64;
|
||||
}
|
||||
|
||||
static unsigned int decode_base64( const char *base64, unsigned int len, char *buf )
|
||||
{
|
||||
unsigned int i = 0;
|
||||
char c0, c1, c2, c3;
|
||||
const char *p = base64;
|
||||
|
||||
while (len > 4)
|
||||
{
|
||||
if ((c0 = decode_char( p[0] )) > 63) return 0;
|
||||
if ((c1 = decode_char( p[1] )) > 63) return 0;
|
||||
if ((c2 = decode_char( p[2] )) > 63) return 0;
|
||||
if ((c3 = decode_char( p[3] )) > 63) return 0;
|
||||
if (buf)
|
||||
{
|
||||
buf[i + 0] = (c0 << 2) | (c1 >> 4);
|
||||
buf[i + 1] = (c1 << 4) | (c2 >> 2);
|
||||
buf[i + 2] = (c2 << 6) | c3;
|
||||
}
|
||||
len -= 4;
|
||||
i += 3;
|
||||
p += 4;
|
||||
}
|
||||
if (p[2] == '=')
|
||||
{
|
||||
if ((c0 = decode_char( p[0] )) > 63) return 0;
|
||||
if ((c1 = decode_char( p[1] )) > 63) return 0;
|
||||
if (buf) buf[i] = (c0 << 2) | (c1 >> 4);
|
||||
i++;
|
||||
}
|
||||
else if (p[3] == '=')
|
||||
{
|
||||
if ((c0 = decode_char( p[0] )) > 63) return 0;
|
||||
if ((c1 = decode_char( p[1] )) > 63) return 0;
|
||||
if ((c2 = decode_char( p[2] )) > 63) return 0;
|
||||
if (buf)
|
||||
{
|
||||
buf[i + 0] = (c0 << 2) | (c1 >> 4);
|
||||
buf[i + 1] = (c1 << 4) | (c2 >> 2);
|
||||
}
|
||||
i += 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((c0 = decode_char( p[0] )) > 63) return 0;
|
||||
if ((c1 = decode_char( p[1] )) > 63) return 0;
|
||||
if ((c2 = decode_char( p[2] )) > 63) return 0;
|
||||
if ((c3 = decode_char( p[3] )) > 63) return 0;
|
||||
if (buf)
|
||||
{
|
||||
buf[i + 0] = (c0 << 2) | (c1 >> 4);
|
||||
buf[i + 1] = (c1 << 4) | (c2 >> 2);
|
||||
buf[i + 2] = (c2 << 6) | c3;
|
||||
}
|
||||
i += 3;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
struct md4_ctx
|
||||
{
|
||||
unsigned int buf[4];
|
||||
unsigned int i[2];
|
||||
unsigned char in[64];
|
||||
unsigned char digest[16];
|
||||
};
|
||||
|
||||
void WINAPI MD4Init( struct md4_ctx * );
|
||||
void WINAPI MD4Update( struct md4_ctx *, const char *, unsigned int );
|
||||
void WINAPI MD4Final( struct md4_ctx * );
|
||||
|
||||
static void create_ntlm1_session_key( const char *secret, unsigned int len, char *session_key )
|
||||
{
|
||||
struct md4_ctx ctx;
|
||||
char hash[16];
|
||||
|
||||
MD4Init( &ctx );
|
||||
MD4Update( &ctx, secret, len );
|
||||
MD4Final( &ctx );
|
||||
memcpy( hash, ctx.digest, 16 );
|
||||
|
||||
MD4Init( &ctx );
|
||||
MD4Update( &ctx, hash, 16 );
|
||||
MD4Final( &ctx );
|
||||
memcpy( session_key, ctx.digest, 16 );
|
||||
}
|
||||
|
||||
struct md5_ctx
|
||||
{
|
||||
unsigned int i[2];
|
||||
unsigned int buf[4];
|
||||
unsigned char in[64];
|
||||
unsigned char digest[16];
|
||||
};
|
||||
|
||||
void WINAPI MD5Init( struct md5_ctx * );
|
||||
void WINAPI MD5Update( struct md5_ctx *, const char *, unsigned int );
|
||||
void WINAPI MD5Final( struct md5_ctx * );
|
||||
|
||||
static void calc_ntlm2_subkey( const char *session_key, const char *magic, char *subkey )
|
||||
{
|
||||
struct md5_ctx ctx;
|
||||
|
||||
MD5Init( &ctx );
|
||||
MD5Update( &ctx, session_key, 16 );
|
||||
MD5Update( &ctx, magic, strlen(magic) + 1 );
|
||||
MD5Final( &ctx );
|
||||
memcpy( subkey, ctx.digest, 16 );
|
||||
}
|
||||
|
||||
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";
|
||||
|
||||
static void create_ntlm2_subkeys( struct ntlm_ctx *ctx )
|
||||
{
|
||||
if (ctx->mode == MODE_CLIENT)
|
||||
{
|
||||
calc_ntlm2_subkey( ctx->session_key, client_to_server_sign_constant, ctx->crypt.ntlm2.send_sign_key );
|
||||
calc_ntlm2_subkey( ctx->session_key, client_to_server_seal_constant, ctx->crypt.ntlm2.send_seal_key );
|
||||
calc_ntlm2_subkey( ctx->session_key, server_to_client_sign_constant, ctx->crypt.ntlm2.recv_sign_key );
|
||||
calc_ntlm2_subkey( ctx->session_key, server_to_client_seal_constant, ctx->crypt.ntlm2.recv_seal_key );
|
||||
}
|
||||
else
|
||||
{
|
||||
calc_ntlm2_subkey( ctx->session_key, server_to_client_sign_constant, ctx->crypt.ntlm2.send_sign_key );
|
||||
calc_ntlm2_subkey( ctx->session_key, server_to_client_seal_constant, ctx->crypt.ntlm2.send_seal_key );
|
||||
calc_ntlm2_subkey( ctx->session_key, client_to_server_sign_constant, ctx->crypt.ntlm2.recv_sign_key );
|
||||
calc_ntlm2_subkey( ctx->session_key, client_to_server_seal_constant, ctx->crypt.ntlm2.recv_seal_key );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 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
|
||||
*/
|
||||
static void arc4_init( struct arc4_info *info, const char *key, unsigned int len )
|
||||
{
|
||||
unsigned int key_idx = 0, state_idx = 0, i, a;
|
||||
|
||||
info->x = info->y = 0;
|
||||
for (i = 0; i < 256; i++) info->state[i] = i;
|
||||
|
||||
for (i = 0; i < 256; i++)
|
||||
{
|
||||
a = info->state[i];
|
||||
state_idx += key[key_idx] + a;
|
||||
state_idx &= 0xff;
|
||||
info->state[i] = info->state[state_idx];
|
||||
info->state[state_idx] = a;
|
||||
if (++key_idx >= len) key_idx = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int get_buffer_index( SecBufferDesc *desc, ULONG type )
|
||||
{
|
||||
int idx;
|
||||
for (idx = 0; idx < desc->cBuffers; idx++)
|
||||
{
|
||||
if (desc->pBuffers[idx].BufferType == type) return idx;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static NTSTATUS NTAPI ntlm_SpInitLsaModeContext( LSA_SEC_HANDLE cred_handle, LSA_SEC_HANDLE ctx_handle,
|
||||
UNICODE_STRING *target, ULONG ctx_req, ULONG data_rep,
|
||||
SecBufferDesc *input, LSA_SEC_HANDLE *new_ctx_handle,
|
||||
SecBufferDesc *output, ULONG *ctx_attr, TimeStamp *expiry,
|
||||
BOOLEAN *mapped_ctx, SecBuffer *ctx_data )
|
||||
{
|
||||
NTSTATUS status = SEC_E_INSUFFICIENT_MEMORY;
|
||||
struct ntlm_ctx *ctx = NULL;
|
||||
char *buf, *bin, *want_flags = NULL, *username = NULL, *domain = NULL, *password = NULL;
|
||||
unsigned int len, bin_len;
|
||||
int idx;
|
||||
|
||||
TRACE( "%lx, %lx, %s, 0x%08x, %u, %p, %p, %p, %p, %p, %p, %p\n", cred_handle, ctx_handle, debugstr_us(target),
|
||||
ctx_req, data_rep, input, new_ctx_handle, output, ctx_attr, expiry, mapped_ctx, ctx_data );
|
||||
|
||||
/* when communicating with the client there can be the following reply packets:
|
||||
* YR <base64 blob> should be sent to the server
|
||||
* PW should be sent back to helper with base64 encoded password
|
||||
* AF <base64 blob> client is done, blob should be sent to server with KK prefixed
|
||||
* GF <string list> a string list of negotiated flags
|
||||
* GK <base64 blob> base64 encoded session key
|
||||
* BH <char reason> something broke
|
||||
*
|
||||
* The squid cache size is 2010 chars and that's what ntlm_auth uses */
|
||||
|
||||
if (!(buf = malloc( NTLM_MAX_BUF ))) return SEC_E_INSUFFICIENT_MEMORY;
|
||||
if (!(bin = malloc( NTLM_MAX_BUF ))) goto done;
|
||||
|
||||
if (!ctx_handle && !input)
|
||||
{
|
||||
char *argv[5];
|
||||
int password_len = 0;
|
||||
struct ntlm_cred *cred = (struct ntlm_cred *)cred_handle;
|
||||
|
||||
if (!cred || cred->mode != MODE_CLIENT)
|
||||
{
|
||||
status = SEC_E_INVALID_HANDLE;
|
||||
goto done;
|
||||
}
|
||||
|
||||
argv[0] = (char *)"ntlm_auth";
|
||||
argv[1] = (char *)"--helper-protocol=ntlmssp-client-1";
|
||||
if (!cred->username_arg && !cred->domain_arg)
|
||||
{
|
||||
WKSTA_USER_INFO_1 *ui = NULL;
|
||||
NET_API_STATUS ret;
|
||||
CREDENTIALW *cached;
|
||||
|
||||
if (get_cached_credential( target, &cached ))
|
||||
{
|
||||
WCHAR *p;
|
||||
if ((p = wcschr( cached->UserName, '\\' )))
|
||||
{
|
||||
if (!(domain = get_domain_arg( cached->UserName, p - cached->UserName ))) goto done;
|
||||
p++;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!(domain = get_domain_arg( NULL, 0 ))) goto done;
|
||||
p = cached->UserName;
|
||||
}
|
||||
if (!(username = get_username_arg( p, -1 ))) goto done;
|
||||
|
||||
if (cached->CredentialBlobSize)
|
||||
{
|
||||
password_len = WideCharToMultiByte( CP_UNIXCP, WC_NO_BEST_FIT_CHARS, (WCHAR *)cached->CredentialBlob,
|
||||
cached->CredentialBlobSize / sizeof(WCHAR), NULL, 0, NULL, NULL );
|
||||
if (!(password = malloc( password_len ))) goto done;
|
||||
WideCharToMultiByte( CP_UNIXCP, WC_NO_BEST_FIT_CHARS, (WCHAR *)cached->CredentialBlob,
|
||||
cached->CredentialBlobSize / sizeof(WCHAR), password, password_len, NULL, NULL );
|
||||
}
|
||||
CredFree( cached );
|
||||
|
||||
argv[2] = username;
|
||||
argv[3] = domain;
|
||||
argv[4] = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
ret = NetWkstaUserGetInfo( NULL, 1, (BYTE **)&ui );
|
||||
if (ret != NERR_Success || !ui || cred->no_cached_credentials)
|
||||
{
|
||||
status = SEC_E_NO_CREDENTIALS;
|
||||
goto done;
|
||||
}
|
||||
if (!(username = get_username_arg( ui->wkui1_username, -1 ))) goto done;
|
||||
NetApiBufferFree( ui );
|
||||
TRACE("using cached credentials\n");
|
||||
|
||||
argv[2] = username;
|
||||
argv[3] = (char *)"--use-cached-creds";
|
||||
argv[4] = NULL;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
argv[2] = cred->username_arg;
|
||||
argv[3] = cred->domain_arg;
|
||||
argv[4] = NULL;
|
||||
}
|
||||
|
||||
if ((status = ntlm_funcs->fork( argv, &ctx )) != SEC_E_OK) goto done;
|
||||
status = SEC_E_INSUFFICIENT_MEMORY;
|
||||
|
||||
ctx->mode = MODE_CLIENT;
|
||||
memset( ctx->session_key, 0, sizeof(ctx->session_key) );
|
||||
|
||||
/* generate the dummy session key = MD4(MD4(password))*/
|
||||
if (password || cred->password)
|
||||
{
|
||||
WCHAR *passwordW;
|
||||
len = MultiByteToWideChar( CP_ACP, 0, password ? password : cred->password,
|
||||
password ? password_len : cred->password_len, NULL, 0 );
|
||||
if (!(passwordW = malloc( len * sizeof(WCHAR) ))) goto done;
|
||||
MultiByteToWideChar( CP_ACP, 0, password ? password : cred->password,
|
||||
password ? password_len : cred->password_len, passwordW, len );
|
||||
|
||||
create_ntlm1_session_key( (const char *)passwordW, len * sizeof(WCHAR), ctx->session_key );
|
||||
free( passwordW );
|
||||
}
|
||||
|
||||
/* allocate space for a maximum string of
|
||||
* "SF NTLMSSP_FEATURE_SIGN NTLMSSP_FEATURE_SEAL NTLMSSP_FEATURE_SESSION_KEY"
|
||||
*/
|
||||
if (!(want_flags = malloc( 73 ))) goto done;
|
||||
strcpy( want_flags, "SF" );
|
||||
if (ctx_req & ISC_REQ_CONFIDENTIALITY) strcat( want_flags, " NTLMSSP_FEATURE_SEAL" );
|
||||
if ((ctx_req & ISC_REQ_INTEGRITY) || (ctx_req & ISC_REQ_REPLAY_DETECT) ||
|
||||
(ctx_req & ISC_REQ_SEQUENCE_DETECT)) strcat( want_flags, " NTLMSSP_FEATURE_SIGN" );
|
||||
|
||||
if (ctx_req & ISC_REQ_CONNECTION) ctx->attrs |= ISC_RET_CONNECTION;
|
||||
if (ctx_req & ISC_REQ_EXTENDED_ERROR) ctx->attrs |= ISC_RET_EXTENDED_ERROR;
|
||||
if (ctx_req & ISC_REQ_MUTUAL_AUTH) ctx->attrs |= ISC_RET_MUTUAL_AUTH;
|
||||
if (ctx_req & ISC_REQ_USE_DCE_STYLE) ctx->attrs |= ISC_RET_USED_DCE_STYLE;
|
||||
if (ctx_req & ISC_REQ_DELEGATE) ctx->attrs |= ISC_RET_DELEGATE;
|
||||
if (ctx_req & ISC_REQ_STREAM) FIXME( "ISC_REQ_STREAM\n" );
|
||||
|
||||
/* use cached credentials if no password was given, fall back to an empty password on failure */
|
||||
if (!password && !cred->password)
|
||||
{
|
||||
strcpy( buf, "OK" );
|
||||
if ((status = ntlm_funcs->chat( ctx, buf, NTLM_MAX_BUF, &len )) != SEC_E_OK) goto done;
|
||||
|
||||
/* if the helper replied with "PW" using cached credentials failed */
|
||||
if (!strncmp( buf, "PW", 2 ))
|
||||
{
|
||||
TRACE( "using cached credentials failed\n" );
|
||||
strcpy( buf, "PW AA==" );
|
||||
}
|
||||
else strcpy( buf, "OK" ); /* just do a noop on the next run */
|
||||
}
|
||||
else
|
||||
{
|
||||
strcpy( buf, "PW " );
|
||||
encode_base64( password ? password : cred->password, password ? password_len : cred->password_len, buf + 3 );
|
||||
}
|
||||
|
||||
TRACE( "sending to ntlm_auth: %s\n", debugstr_a(buf) );
|
||||
if ((status = ntlm_funcs->chat( ctx, buf, NTLM_MAX_BUF, &len )) != SEC_E_OK) goto done;
|
||||
TRACE( "ntlm_auth returned %s\n", debugstr_a(buf) );
|
||||
|
||||
if (strlen( want_flags ) > 2)
|
||||
{
|
||||
TRACE( "want flags are %s\n", debugstr_a(want_flags) );
|
||||
strcpy( buf, want_flags );
|
||||
if ((status = ntlm_funcs->chat( ctx, buf, NTLM_MAX_BUF, &len )) != SEC_E_OK) goto done;
|
||||
if (!strncmp( buf, "BH", 2 )) ERR( "ntlm_auth doesn't understand new command set\n" );
|
||||
}
|
||||
|
||||
strcpy( buf, "YR" );
|
||||
if ((status = ntlm_funcs->chat( ctx, buf, NTLM_MAX_BUF, &len )) != SEC_E_OK) goto done;
|
||||
TRACE( "ntlm_auth returned %s\n", buf );
|
||||
if (strncmp( buf, "YR ", 3 ))
|
||||
{
|
||||
status = SEC_E_INTERNAL_ERROR;
|
||||
goto done;
|
||||
}
|
||||
bin_len = decode_base64( buf + 3, len - 3, bin );
|
||||
|
||||
*new_ctx_handle = (LSA_SEC_HANDLE)ctx;
|
||||
status = SEC_I_CONTINUE_NEEDED;
|
||||
}
|
||||
else /* !ctx_handle && !input */
|
||||
{
|
||||
if (!input || ((idx = get_buffer_index( input, SECBUFFER_TOKEN )) == -1))
|
||||
{
|
||||
status = SEC_E_INVALID_TOKEN;
|
||||
goto done;
|
||||
}
|
||||
|
||||
ctx = (struct ntlm_ctx *)ctx_handle;
|
||||
if (!ctx || ctx->mode != MODE_CLIENT)
|
||||
{
|
||||
status = SEC_E_INVALID_HANDLE;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (!input->pBuffers[idx].pvBuffer || input->pBuffers[idx].cbBuffer > NTLM_MAX_BUF)
|
||||
{
|
||||
status = SEC_E_INVALID_TOKEN;
|
||||
goto done;
|
||||
}
|
||||
bin_len = input->pBuffers[idx].cbBuffer;
|
||||
memcpy( bin, input->pBuffers[idx].pvBuffer, bin_len );
|
||||
|
||||
strcpy( buf, "TT " );
|
||||
encode_base64( bin, bin_len, buf + 3 );
|
||||
TRACE( "server sent: %s\n", debugstr_a(buf) );
|
||||
|
||||
if ((status = ntlm_funcs->chat( ctx, buf, NTLM_MAX_BUF, &len ))) goto done;
|
||||
TRACE( "ntlm_auth returned: %s\n", debugstr_a(buf) );
|
||||
|
||||
if (strncmp( buf, "KK ", 3 ) && strncmp( buf, "AF ", 3 ))
|
||||
{
|
||||
status = SEC_E_INVALID_TOKEN;
|
||||
goto done;
|
||||
}
|
||||
bin_len = decode_base64( buf + 3, len - 3, bin );
|
||||
|
||||
*new_ctx_handle = (LSA_SEC_HANDLE)ctx;
|
||||
status = SEC_E_OK;
|
||||
}
|
||||
|
||||
if (!output || ((idx = get_buffer_index( output, SECBUFFER_TOKEN )) == -1))
|
||||
{
|
||||
if (!ctx_handle && !input) *new_ctx_handle = 0;
|
||||
status = SEC_E_BUFFER_TOO_SMALL;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (ctx_req & ISC_REQ_ALLOCATE_MEMORY)
|
||||
{
|
||||
if (!(output->pBuffers[idx].pvBuffer = malloc( bin_len )))
|
||||
{
|
||||
status = SEC_E_INSUFFICIENT_MEMORY;
|
||||
goto done;
|
||||
}
|
||||
output->pBuffers[idx].cbBuffer = bin_len;
|
||||
}
|
||||
else if (output->pBuffers[idx].cbBuffer < bin_len)
|
||||
{
|
||||
if (!ctx_handle && !input) *new_ctx_handle = 0;
|
||||
status = SEC_E_BUFFER_TOO_SMALL;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (!output->pBuffers[idx].pvBuffer)
|
||||
{
|
||||
if (!ctx_handle && !input) *new_ctx_handle = 0;
|
||||
status = SEC_E_INTERNAL_ERROR;
|
||||
goto done;
|
||||
}
|
||||
|
||||
output->pBuffers[idx].cbBuffer = bin_len;
|
||||
memcpy( output->pBuffers[idx].pvBuffer, bin, bin_len );
|
||||
if (status == SEC_E_OK)
|
||||
{
|
||||
strcpy( buf, "GF" );
|
||||
if ((status = ntlm_funcs->chat( ctx, buf, NTLM_MAX_BUF, &len )) != SEC_E_OK) goto done;
|
||||
if (len < 3) ctx->flags = 0;
|
||||
else sscanf( buf + 3, "%x", &ctx->flags );
|
||||
|
||||
strcpy( buf, "GK" );
|
||||
if ((status = ntlm_funcs->chat( ctx, buf, NTLM_MAX_BUF, &len )) != SEC_E_OK) goto done;
|
||||
|
||||
if (!strncmp( buf, "BH", 2 )) TRACE( "no key negotiated\n" );
|
||||
else if (!strncmp( buf, "GK ", 3 ))
|
||||
{
|
||||
bin_len = decode_base64( buf + 3, len - 3, bin );
|
||||
TRACE( "session key is %s\n", debugstr_a(buf + 3) );
|
||||
memcpy( ctx->session_key, bin, bin_len );
|
||||
}
|
||||
|
||||
arc4_init( &ctx->crypt.ntlm.arc4info, ctx->session_key, 16 );
|
||||
ctx->crypt.ntlm.seq_no = 0;
|
||||
create_ntlm2_subkeys( ctx );
|
||||
arc4_init( &ctx->crypt.ntlm2.send_arc4info, ctx->crypt.ntlm2.send_seal_key, 16 );
|
||||
arc4_init( &ctx->crypt.ntlm2.recv_arc4info, ctx->crypt.ntlm2.recv_seal_key, 16 );
|
||||
ctx->crypt.ntlm2.send_seq_no = 0;
|
||||
ctx->crypt.ntlm2.recv_seq_no = 0;
|
||||
}
|
||||
|
||||
done:
|
||||
if (status != SEC_E_OK && status != SEC_I_CONTINUE_NEEDED) ntlm_funcs->cleanup( ctx );
|
||||
free( username );
|
||||
free( domain );
|
||||
free( password );
|
||||
free( buf );
|
||||
free( bin );
|
||||
free( want_flags );
|
||||
|
||||
TRACE( "returning %08x\n", status );
|
||||
return status;
|
||||
}
|
||||
|
||||
static NTSTATUS NTAPI ntlm_SpDeleteContext( LSA_SEC_HANDLE handle )
|
||||
{
|
||||
struct ntlm_ctx *ctx = (struct ntlm_ctx *)handle;
|
||||
|
||||
TRACE( "%lx\n", handle );
|
||||
|
||||
if (!ctx) return SEC_E_INVALID_HANDLE;
|
||||
ntlm_funcs->cleanup( ctx );
|
||||
return SEC_E_OK;
|
||||
}
|
||||
|
||||
static SECPKG_FUNCTION_TABLE ntlm_table =
|
||||
{
|
||||
ntlm_LsaApInitializePackage,
|
||||
|
@ -302,9 +859,9 @@ static SECPKG_FUNCTION_TABLE ntlm_table =
|
|||
NULL, /* SaveCredentials */
|
||||
NULL, /* GetCredentials */
|
||||
NULL, /* DeleteCredentials */
|
||||
NULL, /* SpInitLsaModeContext */
|
||||
ntlm_SpInitLsaModeContext,
|
||||
NULL, /* SpAcceptLsaModeContext */
|
||||
NULL, /* SpDeleteContext */
|
||||
ntlm_SpDeleteContext,
|
||||
NULL, /* ApplyControlToken */
|
||||
NULL, /* GetUserInfo */
|
||||
NULL, /* GetExtendedInformation */
|
||||
|
|
|
@ -41,6 +41,73 @@
|
|||
WINE_DEFAULT_DEBUG_CHANNEL(ntlm);
|
||||
WINE_DECLARE_DEBUG_CHANNEL(winediag);
|
||||
|
||||
#define INITIAL_BUFFER_SIZE 200
|
||||
|
||||
static SECURITY_STATUS read_line( struct ntlm_ctx *ctx, unsigned int *offset )
|
||||
{
|
||||
char *newline;
|
||||
|
||||
if (!ctx->com_buf)
|
||||
{
|
||||
if (!(ctx->com_buf = RtlAllocateHeap( GetProcessHeap(), 0, INITIAL_BUFFER_SIZE )))
|
||||
return SEC_E_INSUFFICIENT_MEMORY;
|
||||
ctx->com_buf_size = INITIAL_BUFFER_SIZE;
|
||||
ctx->com_buf_offset = 0;
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
ssize_t size;
|
||||
if (ctx->com_buf_offset + INITIAL_BUFFER_SIZE > ctx->com_buf_size)
|
||||
{
|
||||
char *buf = RtlReAllocateHeap( GetProcessHeap(), 0, ctx->com_buf, ctx->com_buf_size + INITIAL_BUFFER_SIZE );
|
||||
if (!buf) return SEC_E_INSUFFICIENT_MEMORY;
|
||||
ctx->com_buf_size += INITIAL_BUFFER_SIZE;
|
||||
ctx->com_buf = buf;
|
||||
}
|
||||
size = read( ctx->pipe_in, ctx->com_buf + ctx->com_buf_offset, ctx->com_buf_size - ctx->com_buf_offset );
|
||||
if (size <= 0) return SEC_E_INTERNAL_ERROR;
|
||||
|
||||
ctx->com_buf_offset += size;
|
||||
newline = memchr( ctx->com_buf, '\n', ctx->com_buf_offset );
|
||||
} while (!newline);
|
||||
|
||||
/* 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 != ctx->com_buf + ctx->com_buf_offset) *offset = (ctx->com_buf + ctx->com_buf_offset) - (newline + 1);
|
||||
else *offset = 0;
|
||||
|
||||
*newline = 0;
|
||||
return SEC_E_OK;
|
||||
}
|
||||
|
||||
static SECURITY_STATUS CDECL ntlm_chat( struct ntlm_ctx *ctx, char *buf, unsigned int buflen, unsigned int *retlen )
|
||||
{
|
||||
SECURITY_STATUS status = SEC_E_OK;
|
||||
unsigned int offset;
|
||||
|
||||
write( ctx->pipe_out, buf, strlen(buf) );
|
||||
write( ctx->pipe_out, "\n", 1 );
|
||||
|
||||
if ((status = read_line( ctx, &offset )) != SEC_E_OK) return status;
|
||||
*retlen = strlen( ctx->com_buf );
|
||||
|
||||
if (*retlen > buflen) return SEC_E_BUFFER_TOO_SMALL;
|
||||
if (*retlen < 2) return SEC_E_ILLEGAL_MESSAGE;
|
||||
if (!strncmp( ctx->com_buf, "ERR", 3 )) return SEC_E_INVALID_TOKEN;
|
||||
|
||||
memcpy( buf, ctx->com_buf, *retlen + 1 );
|
||||
|
||||
if (!offset) ctx->com_buf_offset = 0;
|
||||
else
|
||||
{
|
||||
memmove( ctx->com_buf, ctx->com_buf + ctx->com_buf_offset, offset );
|
||||
ctx->com_buf_offset = offset;
|
||||
}
|
||||
|
||||
return SEC_E_OK;
|
||||
}
|
||||
|
||||
static void CDECL ntlm_cleanup( struct ntlm_ctx *ctx )
|
||||
{
|
||||
if (!ctx || (ctx->mode != MODE_CLIENT && ctx->mode != MODE_SERVER)) return;
|
||||
|
@ -172,6 +239,7 @@ static BOOL check_version( void )
|
|||
|
||||
static const struct ntlm_funcs funcs =
|
||||
{
|
||||
ntlm_chat,
|
||||
ntlm_cleanup,
|
||||
ntlm_fork,
|
||||
};
|
||||
|
|
|
@ -90,6 +90,7 @@ struct ntlm_ctx
|
|||
|
||||
struct ntlm_funcs
|
||||
{
|
||||
SECURITY_STATUS (CDECL *chat)( struct ntlm_ctx *, char *, unsigned int, unsigned int * );
|
||||
void (CDECL *cleanup)( struct ntlm_ctx * );
|
||||
SECURITY_STATUS (CDECL *fork)( char **, struct ntlm_ctx ** );
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue