mirror of
https://github.com/morgan9e/FreeRDP
synced 2026-04-15 00:44:19 +09:00
Merge pull request #10692 from akallabeth/warn-fixes-kerberos
Warn fixes kerberos
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user