diff --git a/winpr/libwinpr/sspi/Kerberos/kerberos.c b/winpr/libwinpr/sspi/Kerberos/kerberos.c index 3b489dc35..bf7d22e54 100644 --- a/winpr/libwinpr/sspi/Kerberos/kerberos.c +++ b/winpr/libwinpr/sspi/Kerberos/kerberos.c @@ -659,116 +659,189 @@ cleanup: return ret; } +static BOOL append(char* dst, size_t dstSize, const char* src) +{ + const size_t dlen = strnlen(dst, dstSize); + const size_t slen = strlen(src); + if (dlen + slen >= dstSize) + return FALSE; + if (!strncat(dst, src, slen)) + return FALSE; + return TRUE; +} + +static BOOL kerberos_rd_tgt_req_tag2(WinPrAsn1Decoder* dec, char* buf, size_t len) +{ + BOOL rc = FALSE; + WinPrAsn1Decoder seq = { 0 }; + + /* server-name [2] PrincipalName (SEQUENCE) */ + if (!WinPrAsn1DecReadSequence(dec, &seq)) + goto end; + + /* name-type [0] INTEGER */ + BOOL error = FALSE; + WinPrAsn1_INTEGER val = 0; + if (!WinPrAsn1DecReadContextualInteger(&seq, 0, &error, &val)) + goto end; + + /* name-string [1] SEQUENCE OF GeneralString */ + if (!WinPrAsn1DecReadContextualSequence(&seq, 1, &error, dec)) + goto end; + + WinPrAsn1_tag tag = 0; + BOOL first = TRUE; + while (WinPrAsn1DecPeekTag(dec, &tag)) + { + char* lstr = NULL; + if (!WinPrAsn1DecReadGeneralString(dec, &lstr)) + goto end; + + if (!first) + { + if (!append(buf, len, "/")) + goto end; + } + first = FALSE; + + if (!append(buf, len, lstr)) + goto end; + free(lstr); + } + + rc = TRUE; +end: + return rc; +} + +static BOOL kerberos_rd_tgt_req_tag3(WinPrAsn1Decoder* dec, char* buf, size_t len) +{ + /* realm [3] Realm */ + BOOL rc = FALSE; + WinPrAsn1_STRING str = NULL; + if (!WinPrAsn1DecReadGeneralString(dec, &str)) + goto end; + + if (!append(buf, len, "@")) + goto end; + if (!append(buf, len, str)) + goto end; + + rc = TRUE; +end: + free(str); + return rc; +} + +static BOOL kerberos_rd_tgt_req(WinPrAsn1Decoder* dec, char** target) +{ + BOOL rc = FALSE; + + if (!target) + return FALSE; + *target = NULL; + + wStream s = WinPrAsn1DecGetStream(dec); + const size_t len = Stream_Length(&s); + if (len == 0) + return TRUE; + + WinPrAsn1Decoder dec2 = { 0 }; + WinPrAsn1_tagId tag = 0; + if (WinPrAsn1DecReadContextualTag(dec, &tag, &dec2) == 0) + return FALSE; + + char* buf = calloc(len + 1, sizeof(char)); + if (!buf) + return FALSE; + + /* We expect ASN1 context tag values 2 or 3. + * + * In case we got value 2 an (optional) context tag value 3 might follow. + */ + BOOL checkForTag3 = TRUE; + if (tag == 2) + { + rc = kerberos_rd_tgt_req_tag2(&dec2, buf, len); + if (rc) + { + const size_t res = WinPrAsn1DecReadContextualTag(dec, &tag, dec); + if (res == 0) + checkForTag3 = FALSE; + } + } + + if (checkForTag3) + { + if (tag == 3) + rc = kerberos_rd_tgt_req_tag3(&dec2, buf, len); + else + rc = FALSE; + } + +end: + if (rc) + *target = buf; + else + free(buf); + return rc; +} + +static BOOL kerberos_rd_tgt_rep(WinPrAsn1Decoder* dec, krb5_data* ticket) +{ + if (!ticket) + return FALSE; + + /* ticket [2] Ticket */ + WinPrAsn1Decoder asnTicket = { 0 }; + WinPrAsn1_tagId tag = 0; + if (WinPrAsn1DecReadContextualTag(dec, &tag, &asnTicket) == 0) + return FALSE; + + if (tag != 2) + return FALSE; + + wStream s = WinPrAsn1DecGetStream(&asnTicket); + ticket->data = Stream_BufferAs(&s, char); + ticket->length = Stream_Length(&s); + return TRUE; +} + static BOOL kerberos_rd_tgt_token(const sspi_gss_data* token, char** target, krb5_data* ticket) { - WinPrAsn1Decoder dec; - WinPrAsn1Decoder dec2; BOOL error = 0; - WinPrAsn1_tagId tag = 0; WinPrAsn1_INTEGER val = 0; - size_t len = 0; - wStream s; - char* buf = NULL; - char* str = NULL; WINPR_ASSERT(token); - WinPrAsn1Decoder_InitMem(&dec, WINPR_ASN1_DER, (BYTE*)token->data, token->length); + if (target) + *target = NULL; + + WinPrAsn1Decoder der = { 0 }; + WinPrAsn1Decoder_InitMem(&der, WINPR_ASN1_DER, (BYTE*)token->data, token->length); /* KERB-TGT-REQUEST (SEQUENCE) */ - if (!WinPrAsn1DecReadSequence(&dec, &dec2)) + WinPrAsn1Decoder seq = { 0 }; + if (!WinPrAsn1DecReadSequence(&der, &seq)) return FALSE; - dec = dec2; /* pvno [0] INTEGER */ - if (!WinPrAsn1DecReadContextualInteger(&dec, 0, &error, &val) || val != 5) + if (!WinPrAsn1DecReadContextualInteger(&seq, 0, &error, &val) || val != 5) return FALSE; /* msg-type [1] INTEGER */ - if (!WinPrAsn1DecReadContextualInteger(&dec, 1, &error, &val)) + if (!WinPrAsn1DecReadContextualInteger(&seq, 1, &error, &val)) return FALSE; - if (val == KRB_TGT_REQ) + switch (val) { - if (!target) - return FALSE; - *target = NULL; - - s = WinPrAsn1DecGetStream(&dec); - len = Stream_Length(&s); - if (len == 0) - return TRUE; - - buf = malloc(len); - if (!buf) - return FALSE; - - *buf = 0; - *target = buf; - - if (!WinPrAsn1DecReadContextualTag(&dec, &tag, &dec2)) - goto fail; - - if (tag == 2) - { - WinPrAsn1Decoder seq; - /* server-name [2] PrincipalName (SEQUENCE) */ - if (!WinPrAsn1DecReadSequence(&dec2, &seq)) - goto fail; - - /* name-type [0] INTEGER */ - if (!WinPrAsn1DecReadContextualInteger(&seq, 0, &error, &val)) - goto fail; - - /* name-string [1] SEQUENCE OF GeneralString */ - if (!WinPrAsn1DecReadContextualSequence(&seq, 1, &error, &dec2)) - goto fail; - - while (WinPrAsn1DecPeekTag(&dec2, &tag)) - { - if (!WinPrAsn1DecReadGeneralString(&dec2, &str)) - goto fail; - - if (buf != *target) - *buf++ = '/'; - buf = stpcpy(buf, str); - free(str); - } - - if (!WinPrAsn1DecReadContextualTag(&dec, &tag, &dec2)) - return TRUE; - } - - /* realm [3] Realm */ - if (tag != 3 || !WinPrAsn1DecReadGeneralString(&dec2, &str)) - goto fail; - - *buf++ = '@'; - strcpy(buf, str); - free(str); - return TRUE; + case KRB_TGT_REQ: + return kerberos_rd_tgt_req(&seq, target); + case KRB_TGT_REP: + return kerberos_rd_tgt_rep(&seq, ticket); + default: + break; } - else if (val == KRB_TGT_REP) - { - if (!ticket) - return FALSE; - - /* ticket [2] Ticket */ - if (!WinPrAsn1DecReadContextualTag(&dec, &tag, &dec2) || tag != 2) - return FALSE; - - s = WinPrAsn1DecGetStream(&dec2); - ticket->data = Stream_BufferAs(&s, char); - ticket->length = Stream_Length(&s); - return TRUE; - } - else - return FALSE; - -fail: - free(buf); - if (target) - *target = NULL; return FALSE; } diff --git a/winpr/libwinpr/sspi/NTLM/ntlm_compute.c b/winpr/libwinpr/sspi/NTLM/ntlm_compute.c index 12777abb7..0e420354b 100644 --- a/winpr/libwinpr/sspi/NTLM/ntlm_compute.c +++ b/winpr/libwinpr/sspi/NTLM/ntlm_compute.c @@ -500,7 +500,7 @@ static BOOL ntlm_compute_ntlm_v2_hash(NTLM_CONTEXT* context, BYTE* hash) return TRUE; } -BOOL ntlm_compute_lm_v2_response(NTLM_CONTEXT* context) +SECURITY_STATUS ntlm_compute_lm_v2_response(NTLM_CONTEXT* context) { BYTE* response = NULL; BYTE value[WINPR_MD5_DIGEST_LENGTH] = { 0 }; @@ -510,23 +510,23 @@ BOOL ntlm_compute_lm_v2_response(NTLM_CONTEXT* context) if (context->LmCompatibilityLevel < 2) { if (!sspi_SecBufferAlloc(&context->LmChallengeResponse, 24)) - return FALSE; + return SEC_E_INSUFFICIENT_MEMORY; ZeroMemory(context->LmChallengeResponse.pvBuffer, 24); - return TRUE; + return SEC_E_OK; } /* Compute the NTLMv2 hash */ if (!ntlm_compute_ntlm_v2_hash(context, context->NtlmV2Hash)) - return FALSE; + return SEC_E_NO_CREDENTIALS; /* Concatenate the server and client challenges */ CopyMemory(value, context->ServerChallenge, 8); CopyMemory(&value[8], context->ClientChallenge, 8); if (!sspi_SecBufferAlloc(&context->LmChallengeResponse, 24)) - return FALSE; + return SEC_E_INSUFFICIENT_MEMORY; response = (BYTE*)context->LmChallengeResponse.pvBuffer; /* Compute the HMAC-MD5 hash of the resulting value using the NTLMv2 hash as the key */ @@ -535,7 +535,7 @@ BOOL ntlm_compute_lm_v2_response(NTLM_CONTEXT* context) /* Concatenate the resulting HMAC-MD5 hash and the client challenge, giving us the LMv2 response * (24 bytes) */ CopyMemory(&response[16], context->ClientChallenge, 8); - return TRUE; + return SEC_E_OK; } /** @@ -548,25 +548,24 @@ BOOL ntlm_compute_lm_v2_response(NTLM_CONTEXT* context) * @return \b TRUE for success, \b FALSE for failure */ -BOOL ntlm_compute_ntlm_v2_response(NTLM_CONTEXT* context) +SECURITY_STATUS ntlm_compute_ntlm_v2_response(NTLM_CONTEXT* context) { - BYTE* blob = NULL; SecBuffer ntlm_v2_temp = { 0 }; SecBuffer ntlm_v2_temp_chal = { 0 }; - PSecBuffer TargetInfo = NULL; WINPR_ASSERT(context); - TargetInfo = &context->ChallengeTargetInfo; - BOOL ret = FALSE; + PSecBuffer TargetInfo = &context->ChallengeTargetInfo; + SECURITY_STATUS ret = SEC_E_INSUFFICIENT_MEMORY; if (!sspi_SecBufferAlloc(&ntlm_v2_temp, TargetInfo->cbBuffer + 28)) goto exit; ZeroMemory(ntlm_v2_temp.pvBuffer, ntlm_v2_temp.cbBuffer); - blob = (BYTE*)ntlm_v2_temp.pvBuffer; + BYTE* blob = (BYTE*)ntlm_v2_temp.pvBuffer; /* Compute the NTLMv2 hash */ + ret = SEC_E_NO_CREDENTIALS; if (!ntlm_compute_ntlm_v2_hash(context, (BYTE*)context->NtlmV2Hash)) goto exit; @@ -585,7 +584,7 @@ BOOL ntlm_compute_ntlm_v2_response(NTLM_CONTEXT* context) #endif /* Concatenate server challenge with temp */ - + ret = SEC_E_INSUFFICIENT_MEMORY; if (!sspi_SecBufferAlloc(&ntlm_v2_temp_chal, ntlm_v2_temp.cbBuffer + 8)) goto exit; @@ -608,7 +607,7 @@ BOOL ntlm_compute_ntlm_v2_response(NTLM_CONTEXT* context) winpr_HMAC(WINPR_MD_MD5, (BYTE*)context->NtlmV2Hash, WINPR_MD5_DIGEST_LENGTH, context->NtProofString, WINPR_MD5_DIGEST_LENGTH, context->SessionBaseKey, WINPR_MD5_DIGEST_LENGTH); - ret = TRUE; + ret = SEC_E_OK; exit: sspi_SecBufferFree(&ntlm_v2_temp); sspi_SecBufferFree(&ntlm_v2_temp_chal); diff --git a/winpr/libwinpr/sspi/NTLM/ntlm_compute.h b/winpr/libwinpr/sspi/NTLM/ntlm_compute.h index 006a449ed..74448329e 100644 --- a/winpr/libwinpr/sspi/NTLM/ntlm_compute.h +++ b/winpr/libwinpr/sspi/NTLM/ntlm_compute.h @@ -41,8 +41,8 @@ void ntlm_output_channel_bindings(NTLM_CONTEXT* context); void ntlm_current_time(BYTE* timestamp); void ntlm_generate_timestamp(NTLM_CONTEXT* context); -BOOL ntlm_compute_lm_v2_response(NTLM_CONTEXT* context); -BOOL ntlm_compute_ntlm_v2_response(NTLM_CONTEXT* context); +SECURITY_STATUS ntlm_compute_lm_v2_response(NTLM_CONTEXT* context); +SECURITY_STATUS ntlm_compute_ntlm_v2_response(NTLM_CONTEXT* context); void ntlm_rc4k(BYTE* key, size_t length, BYTE* plaintext, BYTE* ciphertext); void ntlm_generate_client_challenge(NTLM_CONTEXT* context); diff --git a/winpr/libwinpr/sspi/NTLM/ntlm_message.c b/winpr/libwinpr/sspi/NTLM/ntlm_message.c index 4a757fd0f..27540bd48 100644 --- a/winpr/libwinpr/sspi/NTLM/ntlm_message.c +++ b/winpr/libwinpr/sspi/NTLM/ntlm_message.c @@ -766,17 +766,27 @@ SECURITY_STATUS ntlm_read_ChallengeMessage(NTLM_CONTEXT* context, PSecBuffer buf ntlm_generate_timestamp(context); /* Timestamp */ - if (!ntlm_compute_lm_v2_response(context)) /* LmChallengeResponse */ + const SECURITY_STATUS rc = ntlm_compute_lm_v2_response(context); /* LmChallengeResponse */ + if (rc != SEC_E_OK) + { + status = rc; goto fail; + } - if (!ntlm_compute_ntlm_v2_response(context)) /* NtChallengeResponse */ + const SECURITY_STATUS rc2 = ntlm_compute_ntlm_v2_response(context); /* NtChallengeResponse */ + if (rc2 != SEC_E_OK) + { + status = rc2; goto fail; + } ntlm_generate_key_exchange_key(context); /* KeyExchangeKey */ ntlm_generate_random_session_key(context); /* RandomSessionKey */ ntlm_generate_exported_session_key(context); /* ExportedSessionKey */ ntlm_encrypt_random_session_key(context); /* EncryptedRandomSessionKey */ + /* Generate signing keys */ + status = SEC_E_ENCRYPT_FAILURE; if (!ntlm_generate_client_signing_key(context)) goto fail; if (!ntlm_generate_server_signing_key(context)) @@ -1092,12 +1102,14 @@ SECURITY_STATUS ntlm_read_AuthenticateMessage(NTLM_CONTEXT* context, PSecBuffer if (context->NegotiateFlags & NTLMSSP_NEGOTIATE_LM_KEY) { - if (!ntlm_compute_lm_v2_response(context)) /* LmChallengeResponse */ - return SEC_E_INTERNAL_ERROR; + const SECURITY_STATUS rc = ntlm_compute_lm_v2_response(context); /* LmChallengeResponse */ + if (rc != SEC_E_OK) + return rc; } - if (!ntlm_compute_ntlm_v2_response(context)) /* NtChallengeResponse */ - return SEC_E_INTERNAL_ERROR; + const SECURITY_STATUS rc = ntlm_compute_ntlm_v2_response(context); /* NtChallengeResponse */ + if (rc != SEC_E_OK) + return rc; /* KeyExchangeKey */ ntlm_generate_key_exchange_key(context);