new: support for protocol version 6

Based on 8df96364f2

Backported by:
	Mike Gabriel <mike.gabriel@das-netzwerkteam.de>
	Bernhard Miklautz <bernhard.miklautz@thincast.com>
	Martin Fleisz <martin.fleisz@thincast.com>
This commit is contained in:
Bernhard Miklautz 2019-05-09 12:03:06 +02:00
parent 6fa64006a1
commit a99ec8cb65
9 changed files with 699 additions and 70 deletions

View File

@ -74,6 +74,7 @@ struct crypto_cert_struct
X509 * px509;
};
#define CRYPTO_SHA256_DIGEST_LENGTH SHA256_DIGEST_LENGTH
#define CRYPTO_SHA1_DIGEST_LENGTH SHA_DIGEST_LENGTH
typedef struct crypto_sha1_struct* CryptoSha1;

View File

@ -136,7 +136,7 @@ set_complex_link_libraries(VARIABLE ${MODULE_PREFIX}_LIBS
set_complex_link_libraries(VARIABLE ${MODULE_PREFIX}_LIBS
MONOLITHIC ${MONOLITHIC_BUILD}
MODULE winpr
MODULES winpr-registry winpr-utils winpr-interlocked winpr-dsparse winpr-sspi winpr-rpc winpr-crt)
MODULES winpr-registry winpr-utils winpr-interlocked winpr-dsparse winpr-sspi winpr-rpc winpr-crt winpr-error)
if(MONOLITHIC_BUILD)
set(FREERDP_LIBS ${FREERDP_LIBS} ${${MODULE_PREFIX}_LIBS} PARENT_SCOPE)

View File

@ -3,6 +3,7 @@
* Network Level Authentication (NLA)
*
* Copyright 2010-2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
* Copyright 2016 Martin Fleisz <martin.fleisz@thincast.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -22,6 +23,7 @@
#endif
#include <time.h>
#include <inttypes.h>
#ifndef _WIN32
#include <unistd.h>
@ -35,15 +37,29 @@
#include <winpr/tchar.h>
#include <winpr/library.h>
#include <winpr/registry.h>
#include <winpr/nt.h>
#include <winpr/error.h>
#include <openssl/sha.h>
#include <openssl/hmac.h>
#include <openssl/rand.h>
#include <openssl/engine.h>
#include "nla.h"
#ifdef UNICODE
#define _tcsncmp wcsncmp
#else
#define _tcsncmp strncmp
#endif /* UNICODE */
/**
* TSRequest ::= SEQUENCE {
* version [0] INTEGER,
* negoTokens [1] NegoData OPTIONAL,
* authInfo [2] OCTET STRING OPTIONAL,
* pubKeyAuth [3] OCTET STRING OPTIONAL
* pubKeyAuth [3] OCTET STRING OPTIONAL,
* errorCode [4] INTEGER OPTIONAL
* }
*
* NegoData ::= SEQUENCE OF NegoDataItem
@ -80,10 +96,6 @@
*
*/
#ifdef WITH_DEBUG_NLA
#define WITH_DEBUG_CREDSSP
#endif
#ifdef WITH_NATIVE_SSPI
#define NLA_PKG_NAME NTLMSP_NAME
#else
@ -97,13 +109,36 @@ int credssp_recv(rdpCredssp* credssp);
void credssp_buffer_print(rdpCredssp* credssp);
void credssp_buffer_free(rdpCredssp* credssp);
SECURITY_STATUS credssp_encrypt_public_key_echo(rdpCredssp* credssp);
SECURITY_STATUS credssp_encrypt_public_key_hash(rdpCredssp* credssp);
SECURITY_STATUS credssp_decrypt_public_key_echo(rdpCredssp* credssp);
SECURITY_STATUS credssp_decrypt_public_key_hash(rdpCredssp* credssp);
SECURITY_STATUS credssp_encrypt_ts_credentials(rdpCredssp* credssp);
SECURITY_STATUS credssp_decrypt_ts_credentials(rdpCredssp* credssp);
#define ber_sizeof_sequence_octet_string(length) ber_sizeof_contextual_tag(ber_sizeof_octet_string(length)) + ber_sizeof_octet_string(length)
#define ber_write_sequence_octet_string(stream, context, value, length) ber_write_contextual_tag(stream, context, ber_sizeof_octet_string(length), TRUE) + ber_write_octet_string(stream, value, length)
/* CredSSP Client-To-Server Binding Hash\0 */
static const BYTE ClientServerHashMagic[] =
{
0x43, 0x72, 0x65, 0x64, 0x53, 0x53, 0x50, 0x20,
0x43, 0x6C, 0x69, 0x65, 0x6E, 0x74, 0x2D, 0x54,
0x6F, 0x2D, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72,
0x20, 0x42, 0x69, 0x6E, 0x64, 0x69, 0x6E, 0x67,
0x20, 0x48, 0x61, 0x73, 0x68, 0x00
};
/* CredSSP Server-To-Client Binding Hash\0 */
static const BYTE ServerClientHashMagic[] =
{
0x43, 0x72, 0x65, 0x64, 0x53, 0x53, 0x50, 0x20,
0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2D, 0x54,
0x6F, 0x2D, 0x43, 0x6C, 0x69, 0x65, 0x6E, 0x74,
0x20, 0x42, 0x69, 0x6E, 0x64, 0x69, 0x6E, 0x67,
0x20, 0x48, 0x61, 0x73, 0x68, 0x00
};
static const UINT32 NonceLength = 32;
/**
* Initialize NTLMSSP authentication module (client).
* @param credssp
@ -132,7 +167,7 @@ int credssp_ntlm_client_init(rdpCredssp* credssp)
sspi_SetAuthIdentity(&(credssp->identity), settings->Username, settings->Domain, settings->Password);
#ifdef WITH_DEBUG_NLA
#ifdef WITH_DEBUG_CREDSSP
_tprintf(_T("User: %s Domain: %s Password: %s\n"),
(char*) credssp->identity.User, (char*) credssp->identity.Domain, (char*) credssp->identity.Password);
#endif
@ -189,7 +224,6 @@ int credssp_client_authenticate(rdpCredssp* credssp)
SecBufferDesc output_buffer_desc;
BOOL have_context;
BOOL have_input_buffer;
BOOL have_pub_key_auth;
sspi_GlobalInit();
@ -224,6 +258,7 @@ int credssp_client_authenticate(rdpCredssp* credssp)
}
cbMaxToken = pPackageInfo->cbMaxToken;
credssp->packageName = pPackageInfo->Name;
status = credssp->table->AcquireCredentialsHandle(NULL, NLA_PKG_NAME,
SECPKG_CRED_OUTBOUND, NULL, &credssp->identity, NULL, NULL, &credentials, &expiration);
@ -236,7 +271,6 @@ int credssp_client_authenticate(rdpCredssp* credssp)
have_context = FALSE;
have_input_buffer = FALSE;
have_pub_key_auth = FALSE;
ZeroMemory(&input_buffer, sizeof(SecBuffer));
ZeroMemory(&output_buffer, sizeof(SecBuffer));
ZeroMemory(&credssp->ContextSizes, sizeof(SecPkgContext_Sizes));
@ -272,12 +306,19 @@ int credssp_client_authenticate(rdpCredssp* credssp)
input_buffer.pvBuffer = NULL;
}
if ((status == SEC_I_COMPLETE_AND_CONTINUE) || (status == SEC_I_COMPLETE_NEEDED) || (status == SEC_E_OK))
if ((status == SEC_I_COMPLETE_AND_CONTINUE) || (status == SEC_I_COMPLETE_NEEDED))
{
if (credssp->table->CompleteAuthToken != NULL)
credssp->table->CompleteAuthToken(&credssp->context, &output_buffer_desc);
have_pub_key_auth = TRUE;
if (status == SEC_I_COMPLETE_NEEDED)
status = SEC_E_OK;
else if (status == SEC_I_COMPLETE_AND_CONTINUE)
status = SEC_I_CONTINUE_NEEDED;
}
if (status == SEC_E_OK)
{
if (credssp->table->QueryContextAttributes(&credssp->context, SECPKG_ATTR_SIZES, &credssp->ContextSizes) != SEC_E_OK)
{
@ -285,12 +326,15 @@ int credssp_client_authenticate(rdpCredssp* credssp)
return 0;
}
credssp_encrypt_public_key_echo(credssp);
if (credssp->peerVersion < 5)
status = credssp_encrypt_public_key_echo(credssp);
else
{
status = credssp_encrypt_public_key_hash(credssp);
}
if (status == SEC_I_COMPLETE_NEEDED)
status = SEC_E_OK;
else if (status == SEC_I_COMPLETE_AND_CONTINUE)
status = SEC_I_CONTINUE_NEEDED;
if (status != SEC_E_OK)
return -1;
}
/* send authentication token to server */
@ -340,7 +384,11 @@ int credssp_client_authenticate(rdpCredssp* credssp)
/* Verify Server Public Key Echo */
status = credssp_decrypt_public_key_echo(credssp);
if (credssp->peerVersion < 5)
status = credssp_decrypt_public_key_echo(credssp);
else
status = credssp_decrypt_public_key_hash(credssp);
credssp_buffer_free(credssp);
if (status != SEC_E_OK)
@ -350,7 +398,6 @@ int credssp_client_authenticate(rdpCredssp* credssp)
}
/* Send encrypted credentials */
status = credssp_encrypt_ts_credentials(credssp);
if (status != SEC_E_OK)
@ -391,7 +438,6 @@ int credssp_server_authenticate(rdpCredssp* credssp)
SecBufferDesc output_buffer_desc;
BOOL have_context;
BOOL have_input_buffer;
BOOL have_pub_key_auth;
sspi_GlobalInit();
@ -440,6 +486,7 @@ int credssp_server_authenticate(rdpCredssp* credssp)
}
cbMaxToken = pPackageInfo->cbMaxToken;
credssp->packageName = pPackageInfo->Name;
status = credssp->table->AcquireCredentialsHandle(NULL, NLA_PKG_NAME,
SECPKG_CRED_INBOUND, NULL, NULL, NULL, NULL, &credentials, &expiration);
@ -452,7 +499,6 @@ int credssp_server_authenticate(rdpCredssp* credssp)
have_context = FALSE;
have_input_buffer = FALSE;
have_pub_key_auth = FALSE;
ZeroMemory(&input_buffer, sizeof(SecBuffer));
ZeroMemory(&output_buffer, sizeof(SecBuffer));
ZeroMemory(&input_buffer_desc, sizeof(SecBufferDesc));
@ -537,7 +583,6 @@ int credssp_server_authenticate(rdpCredssp* credssp)
if (status == SEC_E_OK)
{
have_pub_key_auth = TRUE;
if (credssp->table->QueryContextAttributes(&credssp->context, SECPKG_ATTR_SIZES, &credssp->ContextSizes) != SEC_E_OK)
{
@ -545,7 +590,12 @@ int credssp_server_authenticate(rdpCredssp* credssp)
return 0;
}
if (credssp_decrypt_public_key_echo(credssp) != SEC_E_OK)
if (credssp->version < 5)
status = credssp_decrypt_public_key_echo(credssp);
else
status = credssp_decrypt_public_key_hash(credssp);
if (status != SEC_E_OK)
{
fprintf(stderr, "Error: could not verify client's public key echo\n");
return -1;
@ -555,13 +605,39 @@ int credssp_server_authenticate(rdpCredssp* credssp)
credssp->negoToken.pvBuffer = NULL;
credssp->negoToken.cbBuffer = 0;
credssp_encrypt_public_key_echo(credssp);
if (credssp->version < 5)
status = credssp_encrypt_public_key_echo(credssp);
else
status = credssp_encrypt_public_key_hash(credssp);
if (status != SEC_E_OK)
return -1;
}
if ((status != SEC_E_OK) && (status != SEC_I_CONTINUE_NEEDED))
{
/* Special handling of these specific error codes as HRESULT_FROM_WIN32
unfortunately does not map directly to the corresponding NTSTATUS values
*/
switch (GetLastError())
{
case ERROR_PASSWORD_MUST_CHANGE:
credssp->errorCode = STATUS_PASSWORD_MUST_CHANGE;
break;
case ERROR_PASSWORD_EXPIRED:
credssp->errorCode = STATUS_PASSWORD_EXPIRED;
break;
case ERROR_ACCOUNT_DISABLED:
credssp->errorCode = STATUS_ACCOUNT_DISABLED;
break;
default:
credssp->errorCode = HRESULT_FROM_WIN32(GetLastError());
break;
}
fprintf(stderr, "AcceptSecurityContext status: 0x%08X\n", status);
return -1;
credssp_send(credssp);
return -1; /* Access Denied */
}
/* send authentication token */
@ -602,10 +678,6 @@ int credssp_server_authenticate(rdpCredssp* credssp)
if (status != SEC_E_OK)
{
fprintf(stderr, "ImpersonateSecurityContext status: 0x%08X\n", status);
return 0;
}
else
{
status = credssp->table->RevertSecurityContext(&credssp->context);
if (status != SEC_E_OK)
@ -714,6 +786,66 @@ SECURITY_STATUS credssp_encrypt_public_key_echo(rdpCredssp* credssp)
return status;
}
SECURITY_STATUS credssp_encrypt_public_key_hash(rdpCredssp* credssp)
{
SecBuffer Buffers[2] = { { 0 } };
SecBufferDesc Message;
SECURITY_STATUS status = SEC_E_INTERNAL_ERROR;
EVP_MD_CTX* sha256 = NULL;
const EVP_MD* md = NULL;
const ULONG auth_data_length = (credssp->ContextSizes.cbSecurityTrailer + CRYPTO_SHA256_DIGEST_LENGTH);
const BYTE* hashMagic = credssp->server ? ServerClientHashMagic : ClientServerHashMagic;
const size_t hashSize = credssp->server ? sizeof(ServerClientHashMagic) : sizeof(ClientServerHashMagic);
sha256 = EVP_MD_CTX_create();
if (!sha256)
return status;
md = EVP_get_digestbyname("sha256");
if (!md)
goto out;
sspi_SecBufferAlloc(&credssp->pubKeyAuth, auth_data_length);
if (!credssp->pubKeyAuth.pvBuffer)
goto out;
if (!EVP_DigestInit_ex(sha256, md, NULL))
goto out;
/* generate SHA256 of following data: ClientServerHashMagic, Nonce, SubjectPublicKey */
if (!EVP_DigestUpdate(sha256, hashMagic, hashSize) ||
!EVP_DigestUpdate(sha256, credssp->ClientNonce.pvBuffer, credssp->ClientNonce.cbBuffer) ||
!EVP_DigestUpdate(sha256, credssp->PublicKey.pvBuffer, credssp->PublicKey.cbBuffer))
goto out;
Message.cBuffers = 2;
Buffers[0].BufferType = SECBUFFER_TOKEN; /* Signature */
Buffers[0].cbBuffer = credssp->ContextSizes.cbSecurityTrailer;
Buffers[0].pvBuffer = credssp->pubKeyAuth.pvBuffer;
Buffers[1].BufferType = SECBUFFER_DATA; /* SHA256 hash */
Buffers[1].cbBuffer = CRYPTO_SHA256_DIGEST_LENGTH;
Buffers[1].pvBuffer = ((BYTE *) credssp->pubKeyAuth.pvBuffer) + credssp->ContextSizes.cbSecurityTrailer;
if (!EVP_DigestFinal(sha256, Buffers[1].pvBuffer, NULL))
goto out;
/* encrypt message */
Message.pBuffers = (PSecBuffer) &Buffers;
Message.ulVersion = SECBUFFER_VERSION;
status = credssp->table->EncryptMessage(&credssp->context, 0, &Message, credssp->send_seq_num++);
if (status != SEC_E_OK)
{
fprintf(stderr, "EncryptMessage status %s [0x%08"PRIX32"]\n",
GetSecurityStatusString(status), status);
}
if (Message.cBuffers == 2 && Buffers[0].cbBuffer < credssp->ContextSizes.cbSecurityTrailer)
{
/* IMPORTANT: EncryptMessage may not use all the signature space, so we need to shrink the excess between the buffers */
MoveMemory(((BYTE*)Buffers[0].pvBuffer) + Buffers[0].cbBuffer, Buffers[1].pvBuffer,
Buffers[1].cbBuffer);
credssp->pubKeyAuth.cbBuffer = Buffers[0].cbBuffer + Buffers[1].cbBuffer;
}
out:
EVP_MD_CTX_destroy(sha256);
return status;
}
SECURITY_STATUS credssp_decrypt_public_key_echo(rdpCredssp* credssp)
{
int length;
@ -728,7 +860,7 @@ SECURITY_STATUS credssp_decrypt_public_key_echo(rdpCredssp* credssp)
if (credssp->PublicKey.cbBuffer + credssp->ContextSizes.cbMaxSignature != credssp->pubKeyAuth.cbBuffer)
{
fprintf(stderr, "unexpected pubKeyAuth buffer size:%d\n", (int) credssp->pubKeyAuth.cbBuffer);
fprintf(stderr, "unexpected pubKeyAuth buffer size :%lu\n", credssp->pubKeyAuth.cbBuffer);
return SEC_E_INVALID_TOKEN;
}
@ -786,6 +918,95 @@ SECURITY_STATUS credssp_decrypt_public_key_echo(rdpCredssp* credssp)
return SEC_E_OK;
}
SECURITY_STATUS credssp_decrypt_public_key_hash(rdpCredssp* credssp)
{
unsigned long length;
BYTE* buffer = NULL;
ULONG pfQOP = 0;
int signature_length;
SecBuffer Buffers[2] = { { 0 } };
SecBufferDesc Message;
EVP_MD_CTX* sha256 = NULL;
const EVP_MD* md = NULL;
BYTE serverClientHash[CRYPTO_SHA256_DIGEST_LENGTH];
SECURITY_STATUS status = SEC_E_INVALID_TOKEN;
const BYTE* hashMagic = credssp->server ? ClientServerHashMagic : ServerClientHashMagic;
const size_t hashSize = credssp->server ? sizeof(ServerClientHashMagic) : sizeof(ClientServerHashMagic);
signature_length = credssp->pubKeyAuth.cbBuffer - CRYPTO_SHA256_DIGEST_LENGTH;
if ((signature_length < 0) || (signature_length > (int)credssp->ContextSizes.cbSecurityTrailer))
{
fprintf(stderr, "unexpected pubKeyAuth buffer size: %lu\n", credssp->pubKeyAuth.cbBuffer);
goto fail;
}
if ((credssp->ContextSizes.cbSecurityTrailer + CRYPTO_SHA256_DIGEST_LENGTH) != credssp->pubKeyAuth.cbBuffer)
{
fprintf(stderr, "unexpected pubKeyAuth buffer size: %lu\n", credssp->pubKeyAuth.cbBuffer);
goto fail;
}
length = credssp->pubKeyAuth.cbBuffer;
buffer = (BYTE*)malloc(length);
if (!buffer)
{
status = SEC_E_INSUFFICIENT_MEMORY;
goto fail;
}
sha256 = EVP_MD_CTX_create();
if (!sha256)
{
status = SEC_E_INSUFFICIENT_MEMORY;
goto fail;
}
md = EVP_get_digestbyname("sha256");
if (!md)
{
status = SEC_E_INTERNAL_ERROR;
goto fail;
}
CopyMemory(buffer, credssp->pubKeyAuth.pvBuffer, length);
Buffers[0].BufferType = SECBUFFER_TOKEN; /* Signature */
Buffers[0].cbBuffer = signature_length;
Buffers[0].pvBuffer = buffer;
Buffers[1].BufferType = SECBUFFER_DATA; /* Encrypted Hash */
Buffers[1].cbBuffer = CRYPTO_SHA256_DIGEST_LENGTH;
Buffers[1].pvBuffer = buffer + signature_length;
Message.cBuffers = 2;
Message.ulVersion = SECBUFFER_VERSION;
Message.pBuffers = (PSecBuffer)&Buffers;
status = credssp->table->DecryptMessage(&credssp->context, &Message, credssp->recv_seq_num++, &pfQOP);
if (status != SEC_E_OK)
{
fprintf(stderr, "DecryptMessage failure %s [%08"PRIX32"]\n",
GetSecurityStatusString(status), status);
goto fail;
}
/* generate SHA256 of following data: ServerClientHashMagic, Nonce, SubjectPublicKey */
if (!EVP_DigestInit_ex(sha256, md, NULL) ||
!EVP_DigestUpdate(sha256, hashMagic, hashSize) ||
!EVP_DigestUpdate(sha256, credssp->ClientNonce.pvBuffer, credssp->ClientNonce.cbBuffer) ||
!EVP_DigestUpdate(sha256, credssp->PublicKey.pvBuffer, credssp->PublicKey.cbBuffer) ||
!EVP_DigestFinal(sha256, serverClientHash, NULL)
)
{
status = SEC_E_INTERNAL_ERROR;
goto fail;
}
/* verify hash */
if (memcmp(serverClientHash, Buffers[1].pvBuffer, CRYPTO_SHA256_DIGEST_LENGTH) != 0)
{
fprintf(stderr, "Could not verify server's hash\n");
status = SEC_E_MESSAGE_ALTERED; /* DO NOT SEND CREDENTIALS! */
goto fail;
}
fail:
free(buffer);
EVP_MD_CTX_destroy(sha256);
return status;
}
int credssp_sizeof_ts_password_creds(rdpCredssp* credssp)
{
int length = 0;
@ -941,19 +1162,18 @@ SECURITY_STATUS credssp_encrypt_ts_credentials(rdpCredssp* credssp)
credssp_encode_ts_credentials(credssp);
sspi_SecBufferAlloc(&credssp->authInfo, credssp->ContextSizes.cbSecurityTrailer + credssp->ts_credentials.cbBuffer);
Buffers[0].BufferType = SECBUFFER_TOKEN; /* Signature */
Buffers[1].BufferType = SECBUFFER_DATA; /* TSCredentials */
sspi_SecBufferAlloc(&credssp->authInfo, credssp->ContextSizes.cbMaxSignature + credssp->ts_credentials.cbBuffer);
Buffers[0].cbBuffer = credssp->ContextSizes.cbMaxSignature;
Buffers[0].cbBuffer = credssp->ContextSizes.cbSecurityTrailer;
Buffers[0].pvBuffer = credssp->authInfo.pvBuffer;
ZeroMemory(Buffers[0].pvBuffer, Buffers[0].cbBuffer);
Buffers[1].BufferType = SECBUFFER_DATA; /* TSCredentials */
Buffers[1].cbBuffer = credssp->ts_credentials.cbBuffer;
Buffers[1].pvBuffer = &((BYTE*) credssp->authInfo.pvBuffer)[Buffers[0].cbBuffer];
CopyMemory(Buffers[1].pvBuffer, credssp->ts_credentials.pvBuffer, Buffers[1].cbBuffer);
Message.cBuffers = 2;
Message.ulVersion = SECBUFFER_VERSION;
Message.pBuffers = (PSecBuffer) &Buffers;
@ -1040,6 +1260,13 @@ int credssp_sizeof_auth_info(int length)
return length;
}
static size_t credssp_sizeof_client_nonce(size_t length)
{
length = ber_sizeof_octet_string(length);
length += ber_sizeof_contextual_tag(length);
return length;
}
int credssp_sizeof_ts_request(int length)
{
length += ber_sizeof_integer(2);
@ -1057,15 +1284,23 @@ void credssp_send(rdpCredssp* credssp)
wStream* s;
int length;
int ts_request_length;
int nego_tokens_length;
int pub_key_auth_length;
int auth_info_length;
int nego_tokens_length = 0;
int pub_key_auth_length = 0;
int auth_info_length = 0;
int error_code_context_length = 0;
int error_code_length = 0;
int client_nonce_length = 0;
nego_tokens_length = (credssp->negoToken.cbBuffer > 0) ? credssp_sizeof_nego_tokens(credssp->negoToken.cbBuffer) : 0;
pub_key_auth_length = (credssp->pubKeyAuth.cbBuffer > 0) ? credssp_sizeof_pub_key_auth(credssp->pubKeyAuth.cbBuffer) : 0;
auth_info_length = (credssp->authInfo.cbBuffer > 0) ? credssp_sizeof_auth_info(credssp->authInfo.cbBuffer) : 0;
client_nonce_length = (credssp->ClientNonce.cbBuffer > 0) ? credssp_sizeof_client_nonce(credssp->ClientNonce.cbBuffer) : 0;
length = nego_tokens_length + pub_key_auth_length + auth_info_length;
if (credssp->peerVersion >= 3 && credssp->peerVersion != 5 && credssp->errorCode != 0)
{
error_code_length = ber_sizeof_integer(credssp->errorCode);
error_code_context_length = ber_sizeof_contextual_tag(error_code_length);
}
length = nego_tokens_length + pub_key_auth_length + auth_info_length + error_code_context_length + error_code_length + client_nonce_length;
ts_request_length = credssp_sizeof_ts_request(length);
@ -1076,17 +1311,21 @@ void credssp_send(rdpCredssp* credssp)
/* [0] version */
ber_write_contextual_tag(s, 0, 3, TRUE);
ber_write_integer(s, 2); /* INTEGER */
ber_write_integer(s, credssp->version); /* INTEGER */
/* [1] negoTokens (NegoData) */
if (nego_tokens_length > 0)
{
length = nego_tokens_length;
length -= ber_write_contextual_tag(s, 1, ber_sizeof_sequence(ber_sizeof_sequence(ber_sizeof_sequence_octet_string(credssp->negoToken.cbBuffer))), TRUE); /* NegoData */
length -= ber_write_sequence_tag(s, ber_sizeof_sequence(ber_sizeof_sequence_octet_string(credssp->negoToken.cbBuffer))); /* SEQUENCE OF NegoDataItem */
length -= ber_write_sequence_tag(s, ber_sizeof_sequence_octet_string(credssp->negoToken.cbBuffer)); /* NegoDataItem */
length -= ber_write_sequence_octet_string(s, 0, (BYTE*) credssp->negoToken.pvBuffer, credssp->negoToken.cbBuffer); /* OCTET STRING */
length = ber_write_contextual_tag(s, 1, ber_sizeof_sequence(ber_sizeof_sequence(ber_sizeof_sequence_octet_string(credssp->negoToken.cbBuffer))), TRUE); /* NegoData */
length += ber_write_sequence_tag(s, ber_sizeof_sequence(ber_sizeof_sequence_octet_string(credssp->negoToken.cbBuffer))); /* SEQUENCE OF NegoDataItem */
length += ber_write_sequence_tag(s, ber_sizeof_sequence_octet_string(credssp->negoToken.cbBuffer)); /* NegoDataItem */
length += ber_write_sequence_octet_string(s, 0, (BYTE*) credssp->negoToken.pvBuffer, credssp->negoToken.cbBuffer); /* OCTET STRING */
if (length != nego_tokens_length)
{
Stream_Free(s, TRUE);
return;
}
// assert length == 0
}
@ -1094,19 +1333,40 @@ void credssp_send(rdpCredssp* credssp)
/* [2] authInfo (OCTET STRING) */
if (auth_info_length > 0)
{
length = auth_info_length;
length -= ber_write_sequence_octet_string(s, 2, credssp->authInfo.pvBuffer, credssp->authInfo.cbBuffer);
// assert length == 0
if (ber_write_sequence_octet_string(s, 2, credssp->authInfo.pvBuffer, credssp->authInfo.cbBuffer) != auth_info_length)
{
Stream_Free(s, TRUE);
return;
}
}
/* [3] pubKeyAuth (OCTET STRING) */
if (pub_key_auth_length > 0)
{
length = pub_key_auth_length;
length -= ber_write_sequence_octet_string(s, 3, credssp->pubKeyAuth.pvBuffer, credssp->pubKeyAuth.cbBuffer);
if (ber_write_sequence_octet_string(s, 3, credssp->pubKeyAuth.pvBuffer, credssp->pubKeyAuth.cbBuffer) != pub_key_auth_length)
{
Stream_Free(s, TRUE);
return;
}
// assert length == 0
}
/* [4] errorCode (INTEGER) */
if (error_code_length > 0)
{
ber_write_contextual_tag(s, 4, error_code_length, TRUE);
ber_write_integer(s, credssp->errorCode);
}
/* [5] clientNonce (OCTET STRING) */
if (client_nonce_length > 0)
{
if (ber_write_sequence_octet_string(s, 5, credssp->ClientNonce.pvBuffer,
credssp->ClientNonce.cbBuffer) != client_nonce_length)
{
Stream_Free(s, TRUE);
return;
}
}
Stream_SealLength(s);
@ -1145,7 +1405,26 @@ int credssp_recv(rdpCredssp* credssp)
if(!ber_read_sequence_tag(s, &length) ||
!ber_read_contextual_tag(s, 0, &length, TRUE) ||
!ber_read_integer(s, &version))
{
Stream_Free(s, TRUE);
return -1;
}
if (credssp->peerVersion == 0)
{
#ifdef WITH_DEBUG_CREDSSP
fprintf(stderr, "CredSSP protocol support %"PRIu32", peer supports %"PRIu32"\n",
credssp->version, version);
#endif
credssp->peerVersion = version;
}
/* if the peer suddenly changed its version - kick it */
if (credssp->peerVersion != version)
{
fprintf(stderr, "CredSSP peer changed protocol version from %"PRIu32" to %"PRIu32"\n",
credssp->peerVersion, version);
return -1;
}
/* [1] negoTokens (NegoData) */
if (ber_read_contextual_tag(s, 1, &length, TRUE) != FALSE)
@ -1155,7 +1434,10 @@ int credssp_recv(rdpCredssp* credssp)
!ber_read_contextual_tag(s, 0, &length, TRUE) || /* [0] negoToken */
!ber_read_octet_string_tag(s, &length) || /* OCTET STRING */
Stream_GetRemainingLength(s) < length)
{
Stream_Free(s, TRUE);
return -1;
}
sspi_SecBufferAlloc(&credssp->negoToken, length);
Stream_Read(s, credssp->negoToken.pvBuffer, length);
credssp->negoToken.cbBuffer = length;
@ -1166,7 +1448,10 @@ int credssp_recv(rdpCredssp* credssp)
{
if(!ber_read_octet_string_tag(s, &length) || /* OCTET STRING */
Stream_GetRemainingLength(s) < length)
{
Stream_Free(s, TRUE);
return -1;
}
sspi_SecBufferAlloc(&credssp->authInfo, length);
Stream_Read(s, credssp->authInfo.pvBuffer, length);
credssp->authInfo.cbBuffer = length;
@ -1177,12 +1462,46 @@ int credssp_recv(rdpCredssp* credssp)
{
if(!ber_read_octet_string_tag(s, &length) || /* OCTET STRING */
Stream_GetRemainingLength(s) < length)
{
Stream_Free(s, TRUE);
return -1;
}
sspi_SecBufferAlloc(&credssp->pubKeyAuth, length);
Stream_Read(s, credssp->pubKeyAuth.pvBuffer, length);
credssp->pubKeyAuth.cbBuffer = length;
}
/* [4] errorCode (INTEGER) */
if (credssp->peerVersion >= 3)
{
if (ber_read_contextual_tag(s, 4, &length, TRUE) != FALSE)
{
if (!ber_read_integer(s, &credssp->errorCode))
{
Stream_Free(s, TRUE);
return -1;
}
}
}
if (credssp->peerVersion >= 5)
{
if (ber_read_contextual_tag(s, 5, &length, TRUE) != FALSE)
{
if (!ber_read_octet_string_tag(s, &length) || /* OCTET STRING */
Stream_GetRemainingLength(s) < length)
{
Stream_Free(s, TRUE);
return -1;
}
sspi_SecBufferAlloc(&credssp->ClientNonce, length);
Stream_Read(s, credssp->ClientNonce.pvBuffer, length);
credssp->ClientNonce.cbBuffer = length;
}
}
Stream_Free(s, TRUE);
return 0;
@ -1242,11 +1561,19 @@ rdpCredssp* credssp_new(freerdp* instance, rdpTransport* transport, rdpSettings*
credssp->transport = transport;
credssp->send_seq_num = 0;
credssp->recv_seq_num = 0;
credssp->version = 6;
ZeroMemory(&credssp->ClientNonce, sizeof(SecBuffer));
ZeroMemory(&credssp->negoToken, sizeof(SecBuffer));
ZeroMemory(&credssp->pubKeyAuth, sizeof(SecBuffer));
ZeroMemory(&credssp->authInfo, sizeof(SecBuffer));
SecInvalidateHandle(&credssp->context);
sspi_SecBufferAlloc(&credssp->ClientNonce, NonceLength);
/* generate random 32-byte nonce */
RAND_bytes(credssp->ClientNonce.pvBuffer, NonceLength);
if (credssp->server)
{
status = RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T("Software\\FreeRDP\\Server"),
@ -1288,6 +1615,7 @@ void credssp_free(rdpCredssp* credssp)
if (credssp->table)
credssp->table->DeleteSecurityContext(&credssp->context);
sspi_SecBufferFree(&credssp->ClientNonce);
sspi_SecBufferFree(&credssp->PublicKey);
sspi_SecBufferFree(&credssp->ts_credentials);

View File

@ -45,9 +45,12 @@ struct rdp_credssp
LPTSTR SspiModule;
rdpSettings* settings;
rdpTransport* transport;
UINT32 version;
UINT32 errorCode;
SecBuffer negoToken;
SecBuffer pubKeyAuth;
SecBuffer authInfo;
SecBuffer ClientNonce;
SecBuffer PublicKey;
SecBuffer ts_credentials;
CryptoRc4 rc4_seal_state;
@ -55,6 +58,12 @@ struct rdp_credssp
SEC_WINNT_AUTH_IDENTITY identity;
PSecurityFunctionTable table;
SecPkgContext_Sizes ContextSizes;
#if defined(UNICODE)
SEC_WCHAR* packageName;
#else
SEC_CHAR* packageName;
#endif
UINT32 peerVersion;
};
int credssp_authenticate(rdpCredssp* credssp);

View File

@ -448,6 +448,14 @@ int ber_write_integer(wStream* s, UINT32 value)
Stream_Write_UINT32_BE(s, value);
return 6;
}
else
{
/* treat as signed integer i.e. NT/HRESULT error codes */
ber_write_universal_tag(s, BER_TAG_INTEGER, FALSE);
ber_write_length(s, 4);
Stream_Write_UINT32_BE(s, value);
return 6;
}
return 0;
}
@ -470,6 +478,11 @@ int ber_sizeof_integer(UINT32 value)
{
return 6;
}
else
{
/* treat as signed integer i.e. NT/HRESULT error codes */
return 6;
}
return 0;
}

View File

@ -101,10 +101,10 @@ extern "C" {
*((UINT32*) _d) = _v; } while (0)
#define Data_Write_UINT32(_d, _v) do { \
*(_d) = (_v) & 0xFF; \
*(_d + 1) = ((_v) >> 8) & 0xFF; \
*(_d + 2) = ((_v) >> 16) & 0xFF; \
*(_d + 3) = ((_v) >> 24) & 0xFF; \
*((BYTE *) _d) = (_v) & 0xFF; \
*((BYTE *) _d + 1) = ((_v) >> 8) & 0xFF; \
*((BYTE *) _d + 2) = ((_v) >> 16) & 0xFF; \
*((BYTE *) _d + 3) = ((_v) >> 24) & 0xFF; \
} while (0)
#define Data_Write_UINT32_BE(_d, _v) do { \

View File

@ -103,6 +103,7 @@ typedef SecPkgInfoW* PSecPkgInfoW;
#define NTLMSP_NAME _T("NTLM")
#define NEGOSSP_NAME _T("Negotiate")
#define KERBEROS_SSP_NAME _T("Kerberos")
#endif
@ -1015,6 +1016,7 @@ WINPR_API void sspi_SecBufferFree(PSecBuffer SecBuffer);
WINPR_API void sspi_SetAuthIdentity(SEC_WINNT_AUTH_IDENTITY* identity, char* user, char* domain, char* password);
WINPR_API void sspi_CopyAuthIdentity(SEC_WINNT_AUTH_IDENTITY* identity, SEC_WINNT_AUTH_IDENTITY* srcIdentity);
WINPR_API const char* GetSecurityStatusString(SECURITY_STATUS status);
#ifdef __cplusplus
}

View File

@ -33,6 +33,7 @@
#include <winpr/print.h>
#include <winpr/sysinfo.h>
#include <winpr/registry.h>
#include <winpr/endian.h>
#include "ntlm.h"
#include "../sspi.h"
@ -622,6 +623,7 @@ SECURITY_STATUS SEC_ENTRY ntlm_EncryptMessage(PCtxtHandle phContext, ULONG fQOP,
int index;
int length;
void* data;
UINT32 value;
UINT32 SeqNo;
HMAC_CTX hmac;
BYTE digest[16];
@ -657,7 +659,9 @@ SECURITY_STATUS SEC_ENTRY ntlm_EncryptMessage(PCtxtHandle phContext, ULONG fQOP,
/* Compute the HMAC-MD5 hash of ConcatenationOf(seq_num,data) using the client signing key */
HMAC_CTX_init(&hmac);
HMAC_Init_ex(&hmac, context->SendSigningKey, 16, EVP_md5(), NULL);
HMAC_Update(&hmac, (void*) &(SeqNo), 4);
Data_Write_UINT32(&value, SeqNo);
HMAC_Update(&hmac, (void*) &value, 4);
HMAC_Update(&hmac, data, length);
HMAC_Final(&hmac, digest, NULL);
HMAC_CTX_cleanup(&hmac);
@ -687,11 +691,11 @@ SECURITY_STATUS SEC_ENTRY ntlm_EncryptMessage(PCtxtHandle phContext, ULONG fQOP,
signature = (BYTE*) signature_buffer->pvBuffer;
/* Concatenate version, ciphertext and sequence number to build signature */
CopyMemory(signature, (void*) &version, 4);
CopyMemory(&signature[4], (void*) checksum, 8);
CopyMemory(&signature[12], (void*) &(SeqNo), 4);
context->SendSeqNum++;
Data_Write_UINT32(signature, version);
CopyMemory(&signature[4], (void*) checksum, 8);
Data_Write_UINT32(&signature[12], SeqNo);
context->SendSeqNum++;
#ifdef WITH_DEBUG_NTLM
fprintf(stderr, "Signature (length = %d)\n", (int) signature_buffer->cbBuffer);
winpr_HexDump(signature_buffer->pvBuffer, signature_buffer->cbBuffer);

View File

@ -294,21 +294,30 @@ void sspi_SecureHandleFree(SecHandle* handle)
void sspi_SetAuthIdentity(SEC_WINNT_AUTH_IDENTITY* identity, char* user, char* domain, char* password)
{
int status;
identity->Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
free(identity->User);
identity->User = (UINT16*) NULL;
identity->UserLength = 0;
if (user)
{
identity->UserLength = ConvertToUnicode(CP_UTF8, 0, user, -1, &identity->User, 0) - 1;
}
else
{
identity->User = (UINT16*) NULL;
identity->UserLength = 0;
status = ConvertToUnicode(CP_UTF8, 0, user, -1, (LPWSTR*) & (identity->User), 0);
if (status <= 0)
return;
identity->UserLength = (ULONG)(status - 1);
}
if (domain)
{
identity->DomainLength = ConvertToUnicode(CP_UTF8, 0, domain, -1, &identity->Domain, 0) - 1;
status = ConvertToUnicode(CP_UTF8, 0, domain, -1, (LPWSTR*) & (identity->Domain), 0);
if (status <= 0)
return;
identity->DomainLength = (ULONG)(status - 1);
}
else
{
@ -318,7 +327,10 @@ void sspi_SetAuthIdentity(SEC_WINNT_AUTH_IDENTITY* identity, char* user, char* d
if (password != NULL)
{
identity->PasswordLength = ConvertToUnicode(CP_UTF8, 0, password, -1, &identity->Password, 0) - 1;
status = ConvertToUnicode(CP_UTF8, 0, password, -1, (LPWSTR*) & (identity->Password), 0);
identity->PasswordLength = (ULONG)(status - 1);
}
else
{
@ -1159,4 +1171,264 @@ SecurityFunctionTableW SSPI_SecurityFunctionTableW =
SetContextAttributes, /* SetContextAttributes */
};
const char* GetSecurityStatusString(SECURITY_STATUS status)
{
switch (status)
{
case SEC_E_OK:
return "SEC_E_OK";
case SEC_E_INSUFFICIENT_MEMORY:
return "SEC_E_INSUFFICIENT_MEMORY";
case SEC_E_INVALID_HANDLE:
return "SEC_E_INVALID_HANDLE";
case SEC_E_UNSUPPORTED_FUNCTION:
return "SEC_E_UNSUPPORTED_FUNCTION";
case SEC_E_TARGET_UNKNOWN:
return "SEC_E_TARGET_UNKNOWN";
case SEC_E_INTERNAL_ERROR:
return "SEC_E_INTERNAL_ERROR";
case SEC_E_SECPKG_NOT_FOUND:
return "SEC_E_SECPKG_NOT_FOUND";
case SEC_E_NOT_OWNER:
return "SEC_E_NOT_OWNER";
case SEC_E_CANNOT_INSTALL:
return "SEC_E_CANNOT_INSTALL";
case SEC_E_INVALID_TOKEN:
return "SEC_E_INVALID_TOKEN";
case SEC_E_CANNOT_PACK:
return "SEC_E_CANNOT_PACK";
case SEC_E_QOP_NOT_SUPPORTED:
return "SEC_E_QOP_NOT_SUPPORTED";
case SEC_E_NO_IMPERSONATION:
return "SEC_E_NO_IMPERSONATION";
case SEC_E_LOGON_DENIED:
return "SEC_E_LOGON_DENIED";
case SEC_E_UNKNOWN_CREDENTIALS:
return "SEC_E_UNKNOWN_CREDENTIALS";
case SEC_E_NO_CREDENTIALS:
return "SEC_E_NO_CREDENTIALS";
case SEC_E_MESSAGE_ALTERED:
return "SEC_E_MESSAGE_ALTERED";
case SEC_E_OUT_OF_SEQUENCE:
return "SEC_E_OUT_OF_SEQUENCE";
case SEC_E_NO_AUTHENTICATING_AUTHORITY:
return "SEC_E_NO_AUTHENTICATING_AUTHORITY";
case SEC_E_BAD_PKGID:
return "SEC_E_BAD_PKGID";
case SEC_E_CONTEXT_EXPIRED:
return "SEC_E_CONTEXT_EXPIRED";
case SEC_E_INCOMPLETE_MESSAGE:
return "SEC_E_INCOMPLETE_MESSAGE";
case SEC_E_INCOMPLETE_CREDENTIALS:
return "SEC_E_INCOMPLETE_CREDENTIALS";
case SEC_E_BUFFER_TOO_SMALL:
return "SEC_E_BUFFER_TOO_SMALL";
case SEC_E_WRONG_PRINCIPAL:
return "SEC_E_WRONG_PRINCIPAL";
case SEC_E_TIME_SKEW:
return "SEC_E_TIME_SKEW";
case SEC_E_UNTRUSTED_ROOT:
return "SEC_E_UNTRUSTED_ROOT";
case SEC_E_ILLEGAL_MESSAGE:
return "SEC_E_ILLEGAL_MESSAGE";
case SEC_E_CERT_UNKNOWN:
return "SEC_E_CERT_UNKNOWN";
case SEC_E_CERT_EXPIRED:
return "SEC_E_CERT_EXPIRED";
case SEC_E_ENCRYPT_FAILURE:
return "SEC_E_ENCRYPT_FAILURE";
case SEC_E_DECRYPT_FAILURE:
return "SEC_E_DECRYPT_FAILURE";
case SEC_E_ALGORITHM_MISMATCH:
return "SEC_E_ALGORITHM_MISMATCH";
case SEC_E_SECURITY_QOS_FAILED:
return "SEC_E_SECURITY_QOS_FAILED";
case SEC_E_UNFINISHED_CONTEXT_DELETED:
return "SEC_E_UNFINISHED_CONTEXT_DELETED";
case SEC_E_NO_TGT_REPLY:
return "SEC_E_NO_TGT_REPLY";
case SEC_E_NO_IP_ADDRESSES:
return "SEC_E_NO_IP_ADDRESSES";
case SEC_E_WRONG_CREDENTIAL_HANDLE:
return "SEC_E_WRONG_CREDENTIAL_HANDLE";
case SEC_E_CRYPTO_SYSTEM_INVALID:
return "SEC_E_CRYPTO_SYSTEM_INVALID";
case SEC_E_MAX_REFERRALS_EXCEEDED:
return "SEC_E_MAX_REFERRALS_EXCEEDED";
case SEC_E_MUST_BE_KDC:
return "SEC_E_MUST_BE_KDC";
case SEC_E_STRONG_CRYPTO_NOT_SUPPORTED:
return "SEC_E_STRONG_CRYPTO_NOT_SUPPORTED";
case SEC_E_TOO_MANY_PRINCIPALS:
return "SEC_E_TOO_MANY_PRINCIPALS";
case SEC_E_NO_PA_DATA:
return "SEC_E_NO_PA_DATA";
case SEC_E_PKINIT_NAME_MISMATCH:
return "SEC_E_PKINIT_NAME_MISMATCH";
case SEC_E_SMARTCARD_LOGON_REQUIRED:
return "SEC_E_SMARTCARD_LOGON_REQUIRED";
case SEC_E_SHUTDOWN_IN_PROGRESS:
return "SEC_E_SHUTDOWN_IN_PROGRESS";
case SEC_E_KDC_INVALID_REQUEST:
return "SEC_E_KDC_INVALID_REQUEST";
case SEC_E_KDC_UNABLE_TO_REFER:
return "SEC_E_KDC_UNABLE_TO_REFER";
case SEC_E_KDC_UNKNOWN_ETYPE:
return "SEC_E_KDC_UNKNOWN_ETYPE";
case SEC_E_UNSUPPORTED_PREAUTH:
return "SEC_E_UNSUPPORTED_PREAUTH";
case SEC_E_DELEGATION_REQUIRED:
return "SEC_E_DELEGATION_REQUIRED";
case SEC_E_BAD_BINDINGS:
return "SEC_E_BAD_BINDINGS";
case SEC_E_MULTIPLE_ACCOUNTS:
return "SEC_E_MULTIPLE_ACCOUNTS";
case SEC_E_NO_KERB_KEY:
return "SEC_E_NO_KERB_KEY";
case SEC_E_CERT_WRONG_USAGE:
return "SEC_E_CERT_WRONG_USAGE";
case SEC_E_DOWNGRADE_DETECTED:
return "SEC_E_DOWNGRADE_DETECTED";
case SEC_E_SMARTCARD_CERT_REVOKED:
return "SEC_E_SMARTCARD_CERT_REVOKED";
case SEC_E_ISSUING_CA_UNTRUSTED:
return "SEC_E_ISSUING_CA_UNTRUSTED";
case SEC_E_REVOCATION_OFFLINE_C:
return "SEC_E_REVOCATION_OFFLINE_C";
case SEC_E_PKINIT_CLIENT_FAILURE:
return "SEC_E_PKINIT_CLIENT_FAILURE";
case SEC_E_SMARTCARD_CERT_EXPIRED:
return "SEC_E_SMARTCARD_CERT_EXPIRED";
case SEC_E_NO_S4U_PROT_SUPPORT:
return "SEC_E_NO_S4U_PROT_SUPPORT";
case SEC_E_CROSSREALM_DELEGATION_FAILURE:
return "SEC_E_CROSSREALM_DELEGATION_FAILURE";
case SEC_E_REVOCATION_OFFLINE_KDC:
return "SEC_E_REVOCATION_OFFLINE_KDC";
case SEC_E_ISSUING_CA_UNTRUSTED_KDC:
return "SEC_E_ISSUING_CA_UNTRUSTED_KDC";
case SEC_E_KDC_CERT_EXPIRED:
return "SEC_E_KDC_CERT_EXPIRED";
case SEC_E_KDC_CERT_REVOKED:
return "SEC_E_KDC_CERT_REVOKED";
case SEC_E_INVALID_PARAMETER:
return "SEC_E_INVALID_PARAMETER";
case SEC_E_DELEGATION_POLICY:
return "SEC_E_DELEGATION_POLICY";
case SEC_E_POLICY_NLTM_ONLY:
return "SEC_E_POLICY_NLTM_ONLY";
case SEC_E_NO_CONTEXT:
return "SEC_E_NO_CONTEXT";
case SEC_E_PKU2U_CERT_FAILURE:
return "SEC_E_PKU2U_CERT_FAILURE";
case SEC_E_MUTUAL_AUTH_FAILED:
return "SEC_E_MUTUAL_AUTH_FAILED";
case SEC_I_CONTINUE_NEEDED:
return "SEC_I_CONTINUE_NEEDED";
case SEC_I_COMPLETE_NEEDED:
return "SEC_I_COMPLETE_NEEDED";
case SEC_I_COMPLETE_AND_CONTINUE:
return "SEC_I_COMPLETE_AND_CONTINUE";
case SEC_I_LOCAL_LOGON:
return "SEC_I_LOCAL_LOGON";
case SEC_I_CONTEXT_EXPIRED:
return "SEC_I_CONTEXT_EXPIRED";
case SEC_I_INCOMPLETE_CREDENTIALS:
return "SEC_I_INCOMPLETE_CREDENTIALS";
case SEC_I_RENEGOTIATE:
return "SEC_I_RENEGOTIATE";
case SEC_I_NO_LSA_CONTEXT:
return "SEC_I_NO_LSA_CONTEXT";
case SEC_I_SIGNATURE_NEEDED:
return "SEC_I_SIGNATURE_NEEDED";
case SEC_I_NO_RENEGOTIATION:
return "SEC_I_NO_RENEGOTIATION";
}
return "SEC_E_UNKNOWN";
}
#endif