From 76dedc59bf62e899e343b04bc3228ff91caa5e80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Moreau?= Date: Sun, 18 Mar 2012 22:02:23 -0400 Subject: [PATCH] libfreerdp-sspi: fix server-side NTLM session key generation --- include/freerdp/sspi/credssp.h | 1 + libfreerdp-sspi/NTLM/ntlm.c | 9 +- libfreerdp-sspi/NTLM/ntlm_av_pairs.c | 2 + libfreerdp-sspi/NTLM/ntlm_compute.c | 11 +++ libfreerdp-sspi/NTLM/ntlm_compute.h | 1 + libfreerdp-sspi/NTLM/ntlm_message.c | 108 +++++++++++++++++++++--- libfreerdp-sspi/credssp.c | 119 ++++----------------------- 7 files changed, 138 insertions(+), 113 deletions(-) diff --git a/include/freerdp/sspi/credssp.h b/include/freerdp/sspi/credssp.h index cf478a234..ffc470ed9 100644 --- a/include/freerdp/sspi/credssp.h +++ b/include/freerdp/sspi/credssp.h @@ -39,6 +39,7 @@ struct rdp_credssp rdpTls* tls; boolean server; int send_seq_num; + int recv_seq_num; UNICONV* uniconv; freerdp* instance; CtxtHandle context; diff --git a/libfreerdp-sspi/NTLM/ntlm.c b/libfreerdp-sspi/NTLM/ntlm.c index 02b05a08a..a7e363cfe 100644 --- a/libfreerdp-sspi/NTLM/ntlm.c +++ b/libfreerdp-sspi/NTLM/ntlm.c @@ -62,11 +62,13 @@ void ntlm_SetContextIdentity(NTLM_CONTEXT* context, SEC_WINNT_AUTH_IDENTITY* ide { context->identity.User = (uint16*) xmalloc(identity->UserLength); memcpy(context->identity.User, identity->User, identity->UserLength); + context->identity.UserLength = identity->UserLength; if (identity->DomainLength > 0) { context->identity.Domain = (uint16*) xmalloc(identity->DomainLength); memcpy(context->identity.Domain, identity->Domain, identity->DomainLength); + context->identity.DomainLength = identity->DomainLength; } else { @@ -76,6 +78,7 @@ void ntlm_SetContextIdentity(NTLM_CONTEXT* context, SEC_WINNT_AUTH_IDENTITY* ide context->identity.Password = (uint16*) xmalloc(identity->PasswordLength); memcpy(context->identity.Password, identity->Password, identity->PasswordLength); + context->identity.PasswordLength = identity->PasswordLength; } } @@ -145,8 +148,8 @@ SECURITY_STATUS ntlm_AcquireCredentialsHandle(char* pszPrincipal, char* pszPacka if (fCredentialUse == SECPKG_CRED_OUTBOUND) { credentials = sspi_CredentialsNew(); - identity = (SEC_WINNT_AUTH_IDENTITY*) pAuthData; + identity = (SEC_WINNT_AUTH_IDENTITY*) pAuthData; memcpy(&(credentials->identity), identity, sizeof(SEC_WINNT_AUTH_IDENTITY)); sspi_SecureHandleSetLowerPointer(phCredential, (void*) credentials); @@ -158,6 +161,9 @@ SECURITY_STATUS ntlm_AcquireCredentialsHandle(char* pszPrincipal, char* pszPacka { credentials = sspi_CredentialsNew(); + identity = (SEC_WINNT_AUTH_IDENTITY*) pAuthData; + memcpy(&(credentials->identity), identity, sizeof(SEC_WINNT_AUTH_IDENTITY)); + sspi_SecureHandleSetLowerPointer(phCredential, (void*) credentials); sspi_SecureHandleSetUpperPointer(phCredential, (void*) NTLM_PACKAGE_NAME); @@ -221,6 +227,7 @@ SECURITY_STATUS ntlm_AcceptSecurityContext(CredHandle* phCredential, CtxtHandle* context = ntlm_ContextNew(); credentials = (CREDENTIALS*) sspi_SecureHandleGetLowerPointer(phCredential); + ntlm_SetContextIdentity(context, &credentials->identity); ntlm_SetContextTargetName(context, "FreeRDP"); diff --git a/libfreerdp-sspi/NTLM/ntlm_av_pairs.c b/libfreerdp-sspi/NTLM/ntlm_av_pairs.c index f37990387..961594ff1 100644 --- a/libfreerdp-sspi/NTLM/ntlm_av_pairs.c +++ b/libfreerdp-sspi/NTLM/ntlm_av_pairs.c @@ -23,6 +23,8 @@ #include #include +#include "ntlm_compute.h" + #include "ntlm_av_pairs.h" const char* const AV_PAIRS_STRINGS[] = diff --git a/libfreerdp-sspi/NTLM/ntlm_compute.c b/libfreerdp-sspi/NTLM/ntlm_compute.c index 0a9210a1a..f30089e40 100644 --- a/libfreerdp-sspi/NTLM/ntlm_compute.c +++ b/libfreerdp-sspi/NTLM/ntlm_compute.c @@ -402,6 +402,17 @@ void ntlm_encrypt_random_session_key(NTLM_CONTEXT* context) ntlm_rc4k(context->KeyExchangeKey, 16, context->RandomSessionKey, context->EncryptedRandomSessionKey); } +/** + * Decrypt RandomSessionKey (RC4-encrypted RandomSessionKey, using KeyExchangeKey as the key). + * @param NTLM context + */ + +void ntlm_decrypt_random_session_key(NTLM_CONTEXT* context) +{ + /* In NTLMv2, EncryptedRandomSessionKey is the ExportedSessionKey RC4-encrypted with the KeyExchangeKey */ + ntlm_rc4k(context->KeyExchangeKey, 16, context->EncryptedRandomSessionKey, context->RandomSessionKey); +} + /** * Generate signing key.\n * @msdn{cc236711} diff --git a/libfreerdp-sspi/NTLM/ntlm_compute.h b/libfreerdp-sspi/NTLM/ntlm_compute.h index c7422ade5..dd67e9560 100644 --- a/libfreerdp-sspi/NTLM/ntlm_compute.h +++ b/libfreerdp-sspi/NTLM/ntlm_compute.h @@ -43,6 +43,7 @@ void ntlm_generate_key_exchange_key(NTLM_CONTEXT* context); void ntlm_generate_random_session_key(NTLM_CONTEXT* context); void ntlm_generate_exported_session_key(NTLM_CONTEXT* context); void ntlm_encrypt_random_session_key(NTLM_CONTEXT* context); +void ntlm_decrypt_random_session_key(NTLM_CONTEXT* context); void ntlm_generate_client_signing_key(NTLM_CONTEXT* context); void ntlm_generate_server_signing_key(NTLM_CONTEXT* context); diff --git a/libfreerdp-sspi/NTLM/ntlm_message.c b/libfreerdp-sspi/NTLM/ntlm_message.c index c81e34da1..f01f3404d 100644 --- a/libfreerdp-sspi/NTLM/ntlm_message.c +++ b/libfreerdp-sspi/NTLM/ntlm_message.c @@ -437,6 +437,12 @@ SECURITY_STATUS ntlm_read_ChallengeMessage(NTLM_CONTEXT* context, SecBuffer* buf /* KeyExchangeKey */ ntlm_generate_key_exchange_key(context); + /* RandomSessionKey */ + ntlm_generate_random_session_key(context); + + /* ExportedSessionKey */ + ntlm_generate_exported_session_key(context); + /* EncryptedRandomSessionKey */ ntlm_encrypt_random_session_key(context); @@ -476,7 +482,7 @@ SECURITY_STATUS ntlm_read_ChallengeMessage(NTLM_CONTEXT* context, SecBuffer* buf freerdp_hexdump(context->RandomSessionKey, 16); printf("\n"); - printf("ClientSignKey\n"); + printf("ClientSigningKey\n"); freerdp_hexdump(context->ClientSigningKey, 16); printf("\n"); @@ -484,6 +490,14 @@ SECURITY_STATUS ntlm_read_ChallengeMessage(NTLM_CONTEXT* context, SecBuffer* buf freerdp_hexdump(context->ClientSealingKey, 16); printf("\n"); + printf("ServerSigningKey\n"); + freerdp_hexdump(context->ServerSigningKey, 16); + printf("\n"); + + printf("ServerSealingKey\n"); + freerdp_hexdump(context->ServerSealingKey, 16); + printf("\n"); + printf("Timestamp\n"); freerdp_hexdump(context->Timestamp, 8); printf("\n"); @@ -612,15 +626,6 @@ SECURITY_STATUS ntlm_write_ChallengeMessage(NTLM_CONTEXT* context, SecBuffer* bu printf("\n"); #endif - /* Initialize RC4 seal state */ - ntlm_init_rc4_seal_states(context); - -#ifdef WITH_DEBUG_NTLM - printf("ServerChallenge\n"); - freerdp_hexdump(context->ServerChallenge, 8); - printf("\n"); -#endif - context->state = NTLM_STATE_AUTHENTICATE; stream_detach(s); @@ -783,7 +788,13 @@ SECURITY_STATUS ntlm_read_AuthenticateMessage(NTLM_CONTEXT* context, SecBuffer* /* NtChallengeResponse */ if (NtChallengeResponseLen > 0) { + uint8* ClientChallengeBuffer; + NtChallengeResponseBuffer = s->data + NtChallengeResponseBufferOffset; + + ClientChallengeBuffer = NtChallengeResponseBuffer + 32; + memcpy(context->ClientChallenge, ClientChallengeBuffer, 8); + #ifdef WITH_DEBUG_NTLM printf("NtChallengeResponse (length = %d, offset = %d)\n", NtChallengeResponseLen, NtChallengeResponseBufferOffset); freerdp_hexdump(NtChallengeResponseBuffer, NtChallengeResponseLen); @@ -795,6 +806,8 @@ SECURITY_STATUS ntlm_read_AuthenticateMessage(NTLM_CONTEXT* context, SecBuffer* if (EncryptedRandomSessionKeyLen > 0) { EncryptedRandomSessionKeyBuffer = s->data + EncryptedRandomSessionKeyBufferOffset; + memcpy(context->EncryptedRandomSessionKey, EncryptedRandomSessionKeyBuffer, 16); + #ifdef WITH_DEBUG_NTLM printf("EncryptedRandomSessionKey (length = %d, offset = %d)\n", EncryptedRandomSessionKeyLen, EncryptedRandomSessionKeyBufferOffset); freerdp_hexdump(EncryptedRandomSessionKeyBuffer, EncryptedRandomSessionKeyLen); @@ -802,6 +815,81 @@ SECURITY_STATUS ntlm_read_AuthenticateMessage(NTLM_CONTEXT* context, SecBuffer* #endif } + /* LmChallengeResponse */ + ntlm_compute_lm_v2_response(context); + + if (context->ntlm_v2) + memset(context->LmChallengeResponse.pvBuffer, 0, context->LmChallengeResponse.cbBuffer); + + /* NtChallengeResponse */ + ntlm_compute_ntlm_v2_response(context); + + /* KeyExchangeKey */ + ntlm_generate_key_exchange_key(context); + + /* EncryptedRandomSessionKey */ + ntlm_decrypt_random_session_key(context); + + /* ExportedSessionKey */ + ntlm_generate_exported_session_key(context); + + /* Generate signing keys */ + ntlm_generate_client_signing_key(context); + ntlm_generate_server_signing_key(context); + + /* Generate sealing keys */ + ntlm_generate_client_sealing_key(context); + ntlm_generate_server_sealing_key(context); + + /* Initialize RC4 seal state */ + ntlm_init_rc4_seal_states(context); + +#ifdef WITH_DEBUG_NTLM + printf("ClientChallenge\n"); + freerdp_hexdump(context->ClientChallenge, 8); + printf("\n"); + + printf("ServerChallenge\n"); + freerdp_hexdump(context->ServerChallenge, 8); + printf("\n"); + + printf("SessionBaseKey\n"); + freerdp_hexdump(context->SessionBaseKey, 16); + printf("\n"); + + printf("KeyExchangeKey\n"); + freerdp_hexdump(context->KeyExchangeKey, 16); + printf("\n"); + + printf("ExportedSessionKey\n"); + freerdp_hexdump(context->ExportedSessionKey, 16); + printf("\n"); + + printf("RandomSessionKey\n"); + freerdp_hexdump(context->RandomSessionKey, 16); + printf("\n"); + + printf("ClientSigningKey\n"); + freerdp_hexdump(context->ClientSigningKey, 16); + printf("\n"); + + printf("ClientSealingKey\n"); + freerdp_hexdump(context->ClientSealingKey, 16); + printf("\n"); + + printf("ServerSigningKey\n"); + freerdp_hexdump(context->ServerSigningKey, 16); + printf("\n"); + + printf("ServerSealingKey\n"); + freerdp_hexdump(context->ServerSealingKey, 16); + printf("\n"); + + printf("Timestamp\n"); + freerdp_hexdump(context->Timestamp, 8); + printf("\n"); +#endif + context->state = NTLM_STATE_FINAL; stream_detach(s); diff --git a/libfreerdp-sspi/credssp.c b/libfreerdp-sspi/credssp.c index 178f14606..f0fd61fa9 100644 --- a/libfreerdp-sspi/credssp.c +++ b/libfreerdp-sspi/credssp.c @@ -77,49 +77,27 @@ #define WITH_DEBUG_CREDSSP #endif -void credssp_SetContextIdentity(rdpCredssp* context, SEC_WINNT_AUTH_IDENTITY* identity) +void credssp_SetContextIdentity(rdpCredssp* context, char* user, char* domain, char* password) { size_t size; context->identity.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE; - if (identity->Flags == SEC_WINNT_AUTH_IDENTITY_ANSI) + context->identity.User = (uint16*) freerdp_uniconv_out(context->uniconv, user, &size); + context->identity.UserLength = (uint32) size; + + if (domain) { - context->identity.User = (uint16*) freerdp_uniconv_out(context->uniconv, (char*) identity->User, &size); - context->identity.UserLength = (uint32) size; - - if (identity->DomainLength > 0) - { - context->identity.Domain = (uint16*) freerdp_uniconv_out(context->uniconv, (char*) identity->Domain, &size); - context->identity.DomainLength = (uint32) size; - } - else - { - context->identity.Domain = (uint16*) NULL; - context->identity.DomainLength = 0; - } - - context->identity.Password = (uint16*) freerdp_uniconv_out(context->uniconv, (char*) identity->Password, &size); - context->identity.PasswordLength = (uint32) size; + context->identity.Domain = (uint16*) freerdp_uniconv_out(context->uniconv, domain, &size); + context->identity.DomainLength = (uint32) size; } else { - context->identity.User = (uint16*) xmalloc(identity->UserLength); - memcpy(context->identity.User, identity->User, identity->UserLength); - - if (identity->DomainLength > 0) - { - context->identity.Domain = (uint16*) xmalloc(identity->DomainLength); - memcpy(context->identity.Domain, identity->Domain, identity->DomainLength); - } - else - { - context->identity.Domain = (uint16*) NULL; - context->identity.DomainLength = 0; - } - - context->identity.Password = (uint16*) xmalloc(identity->PasswordLength); - memcpy(context->identity.Password, identity->User, identity->PasswordLength); + context->identity.Domain = (uint16*) NULL; + context->identity.DomainLength = 0; } + + context->identity.Password = (uint16*) freerdp_uniconv_out(context->uniconv, (char*) password, &size); + context->identity.PasswordLength = (uint32) size; } /** @@ -130,7 +108,6 @@ void credssp_SetContextIdentity(rdpCredssp* context, SEC_WINNT_AUTH_IDENTITY* id int credssp_ntlm_client_init(rdpCredssp* credssp) { freerdp* instance; - SEC_WINNT_AUTH_IDENTITY identity; rdpSettings* settings = credssp->settings; instance = (freerdp*) settings->instance; @@ -145,34 +122,11 @@ int credssp_ntlm_client_init(rdpCredssp* credssp) } } - identity.User = (uint16*) xstrdup(settings->username); - identity.UserLength = strlen(settings->username); - - if (settings->domain) - { - identity.Domain = (uint16*) xstrdup(settings->domain); - identity.DomainLength = strlen(settings->domain); - } - else - { - identity.Domain = (uint16*) NULL; - identity.DomainLength = 0; - } - - identity.Password = (uint16*) xstrdup(settings->password); - identity.PasswordLength = strlen(settings->password); - - identity.Flags = SEC_WINNT_AUTH_IDENTITY_ANSI; - - credssp_SetContextIdentity(credssp, &identity); + credssp_SetContextIdentity(credssp, settings->username, settings->domain, settings->password); sspi_SecBufferAlloc(&credssp->PublicKey, credssp->tls->public_key.length); memcpy(credssp->PublicKey.pvBuffer, credssp->tls->public_key.data, credssp->tls->public_key.length); - xfree(identity.User); - xfree(identity.Domain); - xfree(identity.Password); - return 1; } @@ -186,31 +140,15 @@ char* test_Password = "password"; int credssp_ntlm_server_init(rdpCredssp* credssp) { - size_t size; freerdp* instance; - SEC_WINNT_AUTH_IDENTITY identity; rdpSettings* settings = credssp->settings; instance = (freerdp*) settings->instance; - identity.User = (uint16*) freerdp_uniconv_out(credssp->uniconv, test_User, &size); - identity.UserLength = (uint32) size; - - identity.Domain = (uint16*) NULL; - identity.DomainLength = 0; - - identity.Password = (uint16*) freerdp_uniconv_out(credssp->uniconv, test_Password, &size); - identity.PasswordLength = (uint32) size; - - identity.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE; - - credssp_SetContextIdentity(credssp, &identity); + credssp_SetContextIdentity(credssp, test_User, NULL, test_Password); sspi_SecBufferAlloc(&credssp->PublicKey, credssp->tls->public_key.length); memcpy(credssp->PublicKey.pvBuffer, credssp->tls->public_key.data, credssp->tls->public_key.length); - xfree(identity.User); - xfree(identity.Password); - return 1; } @@ -225,7 +163,6 @@ int credssp_client_authenticate(rdpCredssp* credssp) CredHandle credentials; SEC_TIMESTAMP expiration; SecPkgInfo* pPackageInfo; - SEC_WINNT_AUTH_IDENTITY identity; SecBuffer* p_buffer; SecBuffer input_buffer; SecBuffer output_buffer; @@ -234,7 +171,6 @@ int credssp_client_authenticate(rdpCredssp* credssp) boolean have_context; boolean have_input_buffer; boolean have_pub_key_auth; - rdpSettings* settings = credssp->settings; sspi_GlobalInit(); @@ -253,27 +189,8 @@ int credssp_client_authenticate(rdpCredssp* credssp) cbMaxToken = pPackageInfo->cbMaxToken; - identity.User = (uint16*) xstrdup(settings->username); - identity.UserLength = strlen(settings->username); - - if (settings->domain) - { - identity.Domain = (uint16*) xstrdup(settings->domain); - identity.DomainLength = strlen(settings->domain); - } - else - { - identity.Domain = (uint16*) NULL; - identity.DomainLength = 0; - } - - identity.Password = (uint16*) xstrdup(settings->password); - identity.PasswordLength = strlen(settings->password); - - identity.Flags = SEC_WINNT_AUTH_IDENTITY_ANSI; - status = credssp->table->AcquireCredentialsHandle(NULL, NTLM_PACKAGE_NAME, - SECPKG_CRED_OUTBOUND, NULL, &identity, NULL, NULL, &credentials, &expiration); + SECPKG_CRED_OUTBOUND, NULL, &credssp->identity, NULL, NULL, &credentials, &expiration); if (status != SEC_E_OK) { @@ -432,9 +349,6 @@ int credssp_client_authenticate(rdpCredssp* credssp) FreeCredentialsHandle(&credentials); FreeContextBuffer(pPackageInfo); - xfree(identity.User); - xfree(identity.Domain); - xfree(identity.Password); return 1; } @@ -481,7 +395,7 @@ int credssp_server_authenticate(rdpCredssp* credssp) cbMaxToken = pPackageInfo->cbMaxToken; status = credssp->table->AcquireCredentialsHandle(NULL, NTLM_PACKAGE_NAME, - SECPKG_CRED_INBOUND, NULL, NULL, NULL, NULL, &credentials, &expiration); + SECPKG_CRED_INBOUND, NULL, &credssp->identity, NULL, NULL, &credentials, &expiration); if (status != SEC_E_OK) { @@ -1081,6 +995,7 @@ rdpCredssp* credssp_new(freerdp* instance, rdpTls* tls, rdpSettings* settings) credssp->server = settings->server_mode; credssp->tls = tls; credssp->send_seq_num = 0; + credssp->recv_seq_num = 0; credssp->uniconv = freerdp_uniconv_new(); memset(&credssp->negoToken, 0, sizeof(SecBuffer)); memset(&credssp->pubKeyAuth, 0, sizeof(SecBuffer));