FreeRDP/libwinpr-sspi/credssp.c
2012-05-04 19:48:53 -04:00

1124 lines
30 KiB
C

/**
* FreeRDP: A Remote Desktop Protocol Implementation
* Credential Security Support Provider (CredSSP)
*
* Copyright 2010-2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <time.h>
#ifndef _WIN32
#include <unistd.h>
#endif
#include <freerdp/crypto/tls.h>
#include <freerdp/utils/stream.h>
#include <winpr/sspi.h>
#include <winpr/credssp.h>
#include "sspi.h"
/**
* TSRequest ::= SEQUENCE {
* version [0] INTEGER,
* negoTokens [1] NegoData OPTIONAL,
* authInfo [2] OCTET STRING OPTIONAL,
* pubKeyAuth [3] OCTET STRING OPTIONAL
* }
*
* NegoData ::= SEQUENCE OF NegoDataItem
*
* NegoDataItem ::= SEQUENCE {
* negoToken [0] OCTET STRING
* }
*
* TSCredentials ::= SEQUENCE {
* credType [0] INTEGER,
* credentials [1] OCTET STRING
* }
*
* TSPasswordCreds ::= SEQUENCE {
* domainName [0] OCTET STRING,
* userName [1] OCTET STRING,
* password [2] OCTET STRING
* }
*
* TSSmartCardCreds ::= SEQUENCE {
* pin [0] OCTET STRING,
* cspData [1] TSCspDataDetail,
* userHint [2] OCTET STRING OPTIONAL,
* domainHint [3] OCTET STRING OPTIONAL
* }
*
* TSCspDataDetail ::= SEQUENCE {
* keySpec [0] INTEGER,
* cardName [1] OCTET STRING OPTIONAL,
* readerName [2] OCTET STRING OPTIONAL,
* containerName [3] OCTET STRING OPTIONAL,
* cspName [4] OCTET STRING OPTIONAL
* }
*
*/
#ifdef WITH_DEBUG_NLA
#define WITH_DEBUG_CREDSSP
#endif
void credssp_SetContextIdentity(rdpCredssp* context, char* user, char* domain, char* password)
{
size_t size;
context->identity.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
context->identity.User = (uint16*) freerdp_uniconv_out(context->uniconv, user, &size);
context->identity.UserLength = (uint32) size;
if (domain)
{
context->identity.Domain = (uint16*) freerdp_uniconv_out(context->uniconv, 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*) password, &size);
context->identity.PasswordLength = (uint32) size;
}
/**
* Initialize NTLMSSP authentication module (client).
* @param credssp
*/
int credssp_ntlm_client_init(rdpCredssp* credssp)
{
freerdp* instance;
rdpSettings* settings = credssp->settings;
instance = (freerdp*) settings->instance;
if ((settings->password == NULL) || (settings->username == NULL))
{
if (instance->Authenticate)
{
boolean proceed = instance->Authenticate(instance,
&settings->username, &settings->password, &settings->domain);
if (!proceed)
return 0;
}
}
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);
return 1;
}
/**
* Initialize NTLMSSP authentication module (server).
* @param credssp
*/
char* test_User = "username";
char* test_Password = "password";
int credssp_ntlm_server_init(rdpCredssp* credssp)
{
freerdp* instance;
rdpSettings* settings = credssp->settings;
instance = (freerdp*) settings->instance;
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);
return 1;
}
#define NTLM_PACKAGE_NAME _T("NTLM")
int credssp_client_authenticate(rdpCredssp* credssp)
{
uint32 cbMaxToken;
uint32 fContextReq;
uint32 pfContextAttr;
SECURITY_STATUS status;
CredHandle credentials;
TimeStamp expiration;
SecPkgInfo* pPackageInfo;
PSecBuffer p_buffer;
SecBuffer input_buffer;
SecBuffer output_buffer;
SecBufferDesc input_buffer_desc;
SecBufferDesc output_buffer_desc;
boolean have_context;
boolean have_input_buffer;
boolean have_pub_key_auth;
sspi_GlobalInit();
if (credssp_ntlm_client_init(credssp) == 0)
return 0;
credssp->table = InitSecurityInterface();
status = QuerySecurityPackageInfo(NTLM_PACKAGE_NAME, &pPackageInfo);
if (status != SEC_E_OK)
{
printf("QuerySecurityPackageInfo status: 0x%08X\n", status);
return 0;
}
cbMaxToken = pPackageInfo->cbMaxToken;
status = credssp->table->AcquireCredentialsHandle(NULL, NTLM_PACKAGE_NAME,
SECPKG_CRED_OUTBOUND, NULL, &credssp->identity, NULL, NULL, &credentials, &expiration);
if (status != SEC_E_OK)
{
printf("AcquireCredentialsHandle status: 0x%08X\n", status);
return 0;
}
have_context = false;
have_input_buffer = false;
have_pub_key_auth = false;
memset(&input_buffer, 0, sizeof(SecBuffer));
memset(&output_buffer, 0, sizeof(SecBuffer));
memset(&credssp->ContextSizes, 0, sizeof(SecPkgContext_Sizes));
fContextReq = ISC_REQ_REPLAY_DETECT | ISC_REQ_SEQUENCE_DETECT |
ISC_REQ_CONFIDENTIALITY | ISC_REQ_DELEGATE;
while (true)
{
output_buffer_desc.ulVersion = SECBUFFER_VERSION;
output_buffer_desc.cBuffers = 1;
output_buffer_desc.pBuffers = &output_buffer;
output_buffer.BufferType = SECBUFFER_TOKEN;
output_buffer.cbBuffer = cbMaxToken;
output_buffer.pvBuffer = xmalloc(output_buffer.cbBuffer);
status = credssp->table->InitializeSecurityContext(&credentials,
(have_context) ? &credssp->context : NULL,
NULL, fContextReq, 0, SECURITY_NATIVE_DREP,
(have_input_buffer) ? &input_buffer_desc : NULL,
0, &credssp->context, &output_buffer_desc, &pfContextAttr, &expiration);
if (input_buffer.pvBuffer != NULL)
{
xfree(input_buffer.pvBuffer);
input_buffer.pvBuffer = NULL;
}
if ((status == SEC_I_COMPLETE_AND_CONTINUE) || (status == SEC_I_COMPLETE_NEEDED) || (status == SEC_E_OK))
{
if (credssp->table->CompleteAuthToken != NULL)
credssp->table->CompleteAuthToken(&credssp->context, &output_buffer_desc);
have_pub_key_auth = true;
if (credssp->table->QueryContextAttributes(&credssp->context, SECPKG_ATTR_SIZES, &credssp->ContextSizes) != SEC_E_OK)
{
printf("QueryContextAttributes SECPKG_ATTR_SIZES failure\n");
return 0;
}
if (have_pub_key_auth)
{
uint8* p;
SecBuffer Buffers[2];
SecBufferDesc Message;
SECURITY_STATUS encrypt_status;
Buffers[0].BufferType = SECBUFFER_DATA; /* TLS Public Key */
Buffers[1].BufferType = SECBUFFER_TOKEN; /* Signature */
Buffers[0].cbBuffer = credssp->PublicKey.cbBuffer;
Buffers[0].pvBuffer = xmalloc(Buffers[0].cbBuffer);
memcpy(Buffers[0].pvBuffer, credssp->PublicKey.pvBuffer, Buffers[0].cbBuffer);
Buffers[1].cbBuffer = credssp->ContextSizes.cbMaxSignature;
Buffers[1].pvBuffer = xzalloc(Buffers[1].cbBuffer);
Message.cBuffers = 2;
Message.ulVersion = SECBUFFER_VERSION;
Message.pBuffers = (PSecBuffer) &Buffers;
sspi_SecBufferAlloc(&credssp->pubKeyAuth, Buffers[0].cbBuffer + Buffers[1].cbBuffer);
encrypt_status = credssp->table->EncryptMessage(&credssp->context, 0, &Message, 0);
if (encrypt_status != SEC_E_OK)
{
printf("EncryptMessage status: 0x%08X\n", encrypt_status);
return 0;
}
p = (uint8*) credssp->pubKeyAuth.pvBuffer;
memcpy(p, Buffers[1].pvBuffer, Buffers[1].cbBuffer); /* Message Signature */
memcpy(&p[Buffers[1].cbBuffer], Buffers[0].pvBuffer, Buffers[0].cbBuffer); /* Encrypted Public Key */
xfree(Buffers[0].pvBuffer);
xfree(Buffers[1].pvBuffer);
}
if (status == SEC_I_COMPLETE_NEEDED)
status = SEC_E_OK;
else if (status == SEC_I_COMPLETE_AND_CONTINUE)
status = SEC_I_CONTINUE_NEEDED;
}
/* send authentication token to server */
if (output_buffer.cbBuffer > 0)
{
p_buffer = &output_buffer_desc.pBuffers[0];
credssp->negoToken.pvBuffer = p_buffer->pvBuffer;
credssp->negoToken.cbBuffer = p_buffer->cbBuffer;
#ifdef WITH_DEBUG_CREDSSP
printf("Sending Authentication Token\n");
freerdp_hexdump(credssp->negoToken.pvBuffer, credssp->negoToken.cbBuffer);
#endif
credssp_send(credssp);
credssp_buffer_free(credssp);
}
if (status != SEC_I_CONTINUE_NEEDED)
break;
/* receive server response and place in input buffer */
input_buffer_desc.ulVersion = SECBUFFER_VERSION;
input_buffer_desc.cBuffers = 1;
input_buffer_desc.pBuffers = &input_buffer;
input_buffer.BufferType = SECBUFFER_TOKEN;
if (credssp_recv(credssp) < 0)
return -1;
#ifdef WITH_DEBUG_CREDSSP
printf("Receiving Authentication Token\n");
freerdp_hexdump(credssp->negoToken.pvBuffer, credssp->negoToken.cbBuffer);
#endif
p_buffer = &input_buffer_desc.pBuffers[0];
p_buffer->pvBuffer = credssp->negoToken.pvBuffer;
p_buffer->cbBuffer = credssp->negoToken.cbBuffer;
have_input_buffer = true;
have_context = true;
}
/* Encrypted Public Key +1 */
if (credssp_recv(credssp) < 0)
return -1;
/* Verify Server Public Key Echo */
status = credssp_verify_public_key_echo(credssp);
credssp_buffer_free(credssp);
if (status != SEC_E_OK)
return 0;
/* Send encrypted credentials */
status = credssp_encrypt_ts_credentials(credssp);
if (status != SEC_E_OK)
{
printf("credssp_encrypt_ts_credentials status: 0x%08X\n", status);
return 0;
}
credssp_send(credssp);
credssp_buffer_free(credssp);
/* Free resources */
FreeCredentialsHandle(&credentials);
FreeContextBuffer(pPackageInfo);
return 1;
}
/**
* Authenticate with client using CredSSP (server).
* @param credssp
* @return 1 if authentication is successful
*/
int credssp_server_authenticate(rdpCredssp* credssp)
{
uint32 cbMaxToken;
uint32 fContextReq;
uint32 pfContextAttr;
SECURITY_STATUS status;
CredHandle credentials;
TimeStamp expiration;
SecPkgInfo* pPackageInfo;
PSecBuffer p_buffer;
SecBuffer input_buffer;
SecBuffer output_buffer;
SecBufferDesc input_buffer_desc;
SecBufferDesc output_buffer_desc;
boolean have_context;
boolean have_input_buffer;
boolean have_pub_key_auth;
sspi_GlobalInit();
if (credssp_ntlm_server_init(credssp) == 0)
return 0;
credssp->table = InitSecurityInterface();
status = QuerySecurityPackageInfo(NTLM_PACKAGE_NAME, &pPackageInfo);
if (status != SEC_E_OK)
{
printf("QuerySecurityPackageInfo status: 0x%08X\n", status);
return 0;
}
cbMaxToken = pPackageInfo->cbMaxToken;
status = credssp->table->AcquireCredentialsHandle(NULL, NTLM_PACKAGE_NAME,
SECPKG_CRED_INBOUND, NULL, &credssp->identity, NULL, NULL, &credentials, &expiration);
if (status != SEC_E_OK)
{
printf("AcquireCredentialsHandle status: 0x%08X\n", status);
return 0;
}
have_context = false;
have_input_buffer = false;
have_pub_key_auth = false;
memset(&input_buffer, 0, sizeof(SecBuffer));
memset(&output_buffer, 0, sizeof(SecBuffer));
memset(&credssp->ContextSizes, 0, sizeof(SecPkgContext_Sizes));
fContextReq = ISC_REQ_REPLAY_DETECT | ISC_REQ_SEQUENCE_DETECT |
ISC_REQ_CONFIDENTIALITY | ISC_REQ_DELEGATE;
while (true)
{
input_buffer_desc.ulVersion = SECBUFFER_VERSION;
input_buffer_desc.cBuffers = 1;
input_buffer_desc.pBuffers = &input_buffer;
input_buffer.BufferType = SECBUFFER_TOKEN;
/* receive authentication token */
input_buffer_desc.ulVersion = SECBUFFER_VERSION;
input_buffer_desc.cBuffers = 1;
input_buffer_desc.pBuffers = &input_buffer;
input_buffer.BufferType = SECBUFFER_TOKEN;
if (credssp_recv(credssp) < 0)
return -1;
#ifdef WITH_DEBUG_CREDSSP
printf("Receiving Authentication Token\n");
credssp_buffer_print(credssp);
#endif
p_buffer = &input_buffer_desc.pBuffers[0];
p_buffer->pvBuffer = credssp->negoToken.pvBuffer;
p_buffer->cbBuffer = credssp->negoToken.cbBuffer;
output_buffer_desc.ulVersion = SECBUFFER_VERSION;
output_buffer_desc.cBuffers = 1;
output_buffer_desc.pBuffers = &output_buffer;
output_buffer.BufferType = SECBUFFER_TOKEN;
output_buffer.cbBuffer = cbMaxToken;
output_buffer.pvBuffer = xmalloc(output_buffer.cbBuffer);
status = credssp->table->AcceptSecurityContext(&credentials,
have_context? &credssp->context: NULL,
&input_buffer_desc, 0, SECURITY_NATIVE_DREP, &credssp->context,
&output_buffer_desc, &pfContextAttr, &expiration);
if (input_buffer.pvBuffer != NULL)
{
xfree(input_buffer.pvBuffer);
input_buffer.pvBuffer = NULL;
}
p_buffer = &output_buffer_desc.pBuffers[0];
credssp->negoToken.pvBuffer = p_buffer->pvBuffer;
credssp->negoToken.cbBuffer = p_buffer->cbBuffer;
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;
sspi_SecBufferFree(&credssp->negoToken);
credssp->negoToken.pvBuffer = NULL;
credssp->negoToken.cbBuffer = 0;
if (credssp->table->QueryContextAttributes(&credssp->context, SECPKG_ATTR_SIZES, &credssp->ContextSizes) != SEC_E_OK)
{
printf("QueryContextAttributes SECPKG_ATTR_SIZES failure\n");
return 0;
}
if (have_pub_key_auth)
{
uint8* p;
SecBuffer Buffers[2];
SecBufferDesc Message;
Buffers[0].BufferType = SECBUFFER_DATA; /* TLS Public Key */
Buffers[1].BufferType = SECBUFFER_TOKEN; /* Signature */
Buffers[0].cbBuffer = credssp->PublicKey.cbBuffer;
Buffers[0].pvBuffer = xmalloc(Buffers[0].cbBuffer);
memcpy(Buffers[0].pvBuffer, credssp->PublicKey.pvBuffer, Buffers[0].cbBuffer);
Buffers[1].cbBuffer = credssp->ContextSizes.cbMaxSignature;
Buffers[1].pvBuffer = xzalloc(Buffers[1].cbBuffer);
Message.cBuffers = 2;
Message.ulVersion = SECBUFFER_VERSION;
Message.pBuffers = (PSecBuffer) &Buffers;
p = (uint8*) Buffers[0].pvBuffer;
p[0]++; /* Public Key +1 */
sspi_SecBufferAlloc(&credssp->pubKeyAuth, Buffers[0].cbBuffer + Buffers[1].cbBuffer);
credssp->table->EncryptMessage(&credssp->context, 0, &Message, 0);
p = (uint8*) credssp->pubKeyAuth.pvBuffer;
memcpy(p, Buffers[1].pvBuffer, Buffers[1].cbBuffer); /* Message Signature */
memcpy(&p[Buffers[1].cbBuffer], Buffers[0].pvBuffer, Buffers[0].cbBuffer); /* Encrypted Public Key */
}
if (status == SEC_I_COMPLETE_NEEDED)
status = SEC_E_OK;
else if (status == SEC_I_COMPLETE_AND_CONTINUE)
status = SEC_I_CONTINUE_NEEDED;
}
/* send authentication token */
#ifdef WITH_DEBUG_CREDSSP
printf("Sending Authentication Token\n");
credssp_buffer_print(credssp);
#endif
credssp_send(credssp);
credssp_buffer_free(credssp);
if (status != SEC_I_CONTINUE_NEEDED)
break;
have_context = true;
}
/* Receive encrypted credentials */
if (credssp_recv(credssp) < 0)
return -1;
if (status != SEC_E_OK)
{
printf("AcceptSecurityContext status: 0x%08X\n", status);
return 0;
}
status = credssp->table->ImpersonateSecurityContext(&credssp->context);
if (status != SEC_E_OK)
{
printf("ImpersonateSecurityContext status: 0x%08X\n", status);
return 0;
}
else
{
status = credssp->table->RevertSecurityContext(&credssp->context);
if (status != SEC_E_OK)
{
printf("RevertSecurityContext status: 0x%08X\n", status);
return 0;
}
}
FreeContextBuffer(pPackageInfo);
return 1;
}
/**
* Authenticate using CredSSP.
* @param credssp
* @return 1 if authentication is successful
*/
int credssp_authenticate(rdpCredssp* credssp)
{
if (credssp->server)
return credssp_server_authenticate(credssp);
else
return credssp_client_authenticate(credssp);
}
SECURITY_STATUS credssp_verify_public_key_echo(rdpCredssp* credssp)
{
int length;
uint32 pfQOP;
uint8* public_key1;
uint8* public_key2;
uint8* pub_key_auth;
int public_key_length;
SecBuffer Buffers[2];
SecBufferDesc Message;
SECURITY_STATUS status;
length = credssp->pubKeyAuth.cbBuffer;
pub_key_auth = (uint8*) credssp->pubKeyAuth.pvBuffer;
public_key_length = credssp->PublicKey.cbBuffer;
Buffers[0].BufferType = SECBUFFER_TOKEN; /* Signature */
Buffers[1].BufferType = SECBUFFER_DATA; /* Encrypted TLS Public Key */
Buffers[0].cbBuffer = credssp->ContextSizes.cbMaxSignature;
Buffers[0].pvBuffer = xmalloc(Buffers[0].cbBuffer);
memcpy(Buffers[0].pvBuffer, pub_key_auth, Buffers[0].cbBuffer);
Buffers[1].cbBuffer = length - Buffers[0].cbBuffer;
Buffers[1].pvBuffer = xmalloc(Buffers[1].cbBuffer);
memcpy(Buffers[1].pvBuffer, &pub_key_auth[Buffers[0].cbBuffer], Buffers[1].cbBuffer);
Message.cBuffers = 2;
Message.ulVersion = SECBUFFER_VERSION;
Message.pBuffers = (PSecBuffer) &Buffers;
status = credssp->table->DecryptMessage(&credssp->context, &Message, 0, &pfQOP);
if (status != SEC_E_OK)
return status;
public_key1 = (uint8*) credssp->PublicKey.pvBuffer;
public_key2 = (uint8*) Buffers[1].pvBuffer;
public_key2[0]--; /* server echos the public key +1 */
if (memcmp(public_key1, public_key2, public_key_length) != 0)
{
printf("Could not verify server's public key echo\n");
printf("Expected (length = %d):\n", public_key_length);
freerdp_hexdump(public_key1, public_key_length);
printf("Actual (length = %d):\n", public_key_length);
freerdp_hexdump(public_key2, public_key_length);
return SEC_E_MESSAGE_ALTERED; /* DO NOT SEND CREDENTIALS! */
}
public_key2[0]++;
xfree(Buffers[0].pvBuffer);
xfree(Buffers[1].pvBuffer);
return SEC_E_OK;
}
SECURITY_STATUS credssp_encrypt_ts_credentials(rdpCredssp* credssp)
{
uint8* p;
SecBuffer Buffers[2];
SecBufferDesc Message;
SECURITY_STATUS status;
credssp_encode_ts_credentials(credssp);
Buffers[0].BufferType = SECBUFFER_DATA; /* TSCredentials */
Buffers[1].BufferType = SECBUFFER_TOKEN; /* Signature */
Buffers[0].cbBuffer = credssp->ts_credentials.cbBuffer;
Buffers[0].pvBuffer = xmalloc(Buffers[0].cbBuffer);
memcpy(Buffers[0].pvBuffer, credssp->ts_credentials.pvBuffer, Buffers[0].cbBuffer);
Buffers[1].cbBuffer = 16;
Buffers[1].pvBuffer = xzalloc(Buffers[1].cbBuffer);
Message.cBuffers = 2;
Message.ulVersion = SECBUFFER_VERSION;
Message.pBuffers = (PSecBuffer) &Buffers;
sspi_SecBufferAlloc(&credssp->authInfo, Buffers[0].cbBuffer + Buffers[1].cbBuffer);
status = credssp->table->EncryptMessage(&credssp->context, 0, &Message, 1);
if (status != SEC_E_OK)
return status;
p = (uint8*) credssp->authInfo.pvBuffer;
memcpy(p, Buffers[1].pvBuffer, Buffers[1].cbBuffer); /* Message Signature */
memcpy(&p[Buffers[1].cbBuffer], Buffers[0].pvBuffer, Buffers[0].cbBuffer); /* Encrypted TSCredentials */
xfree(Buffers[0].pvBuffer);
xfree(Buffers[1].pvBuffer);
return SEC_E_OK;
}
int credssp_skip_ts_password_creds(rdpCredssp* credssp)
{
int length;
int ts_password_creds_length = 0;
length = ber_skip_octet_string(credssp->identity.DomainLength);
length += ber_skip_contextual_tag(length);
ts_password_creds_length += length;
length = ber_skip_octet_string(credssp->identity.UserLength);
length += ber_skip_contextual_tag(length);
ts_password_creds_length += length;
length = ber_skip_octet_string(credssp->identity.PasswordLength);
length += ber_skip_contextual_tag(length);
ts_password_creds_length += length;
length = ber_skip_sequence(ts_password_creds_length);
return length;
}
void credssp_write_ts_password_creds(rdpCredssp* credssp, STREAM* s)
{
int length;
length = credssp_skip_ts_password_creds(credssp);
/* TSPasswordCreds (SEQUENCE) */
length = ber_get_content_length(length);
ber_write_sequence_tag(s, length);
/* [0] domainName (OCTET STRING) */
ber_write_contextual_tag(s, 0, credssp->identity.DomainLength + 2, true);
ber_write_octet_string(s, (uint8*) credssp->identity.Domain, credssp->identity.DomainLength);
/* [1] userName (OCTET STRING) */
ber_write_contextual_tag(s, 1, credssp->identity.UserLength + 2, true);
ber_write_octet_string(s, (uint8*) credssp->identity.User, credssp->identity.UserLength);
/* [2] password (OCTET STRING) */
ber_write_contextual_tag(s, 2, credssp->identity.PasswordLength + 2, true);
ber_write_octet_string(s, (uint8*) credssp->identity.Password, credssp->identity.PasswordLength);
}
int credssp_skip_ts_credentials(rdpCredssp* credssp)
{
int length;
int ts_password_creds_length;
int ts_credentials_length = 0;
length = ber_skip_integer(0);
length += ber_skip_contextual_tag(length);
ts_credentials_length += length;
ts_password_creds_length = credssp_skip_ts_password_creds(credssp);
length = ber_skip_octet_string(ts_password_creds_length);
length += ber_skip_contextual_tag(length);
ts_credentials_length += length;
length = ber_skip_sequence(ts_credentials_length);
return length;
}
void credssp_write_ts_credentials(rdpCredssp* credssp, STREAM* s)
{
int length;
int ts_password_creds_length;
length = credssp_skip_ts_credentials(credssp);
ts_password_creds_length = credssp_skip_ts_password_creds(credssp);
/* TSCredentials (SEQUENCE) */
length = ber_get_content_length(length);
length -= ber_write_sequence_tag(s, length);
/* [0] credType (INTEGER) */
length -= ber_write_contextual_tag(s, 0, 3, true);
length -= ber_write_integer(s, 1);
/* [1] credentials (OCTET STRING) */
length -= 1;
length -= ber_write_contextual_tag(s, 1, length, true);
length -= ber_write_octet_string_tag(s, ts_password_creds_length);
credssp_write_ts_password_creds(credssp, s);
}
/**
* Encode TSCredentials structure.
* @param credssp
*/
void credssp_encode_ts_credentials(rdpCredssp* credssp)
{
STREAM* s;
int length;
s = stream_new(0);
length = credssp_skip_ts_credentials(credssp);
sspi_SecBufferAlloc(&credssp->ts_credentials, length);
stream_attach(s, credssp->ts_credentials.pvBuffer, length);
credssp_write_ts_credentials(credssp, s);
stream_detach(s);
stream_free(s);
}
int credssp_skip_nego_token(int length)
{
length = ber_skip_octet_string(length);
length += ber_skip_contextual_tag(length);
return length;
}
int credssp_skip_nego_tokens(int length)
{
length = credssp_skip_nego_token(length);
length += ber_skip_sequence_tag(length);
length += ber_skip_sequence_tag(length);
length += ber_skip_contextual_tag(length);
return length;
}
int credssp_skip_pub_key_auth(int length)
{
length = ber_skip_octet_string(length);
length += ber_skip_contextual_tag(length);
return length;
}
int credssp_skip_auth_info(int length)
{
length = ber_skip_octet_string(length);
length += ber_skip_contextual_tag(length);
return length;
}
int credssp_skip_ts_request(int length)
{
length += ber_skip_integer(2);
length += ber_skip_contextual_tag(3);
length += ber_skip_sequence_tag(length);
return length;
}
/**
* Send CredSSP message.
* @param credssp
*/
void credssp_send(rdpCredssp* credssp)
{
STREAM* s;
int length;
int ts_request_length;
int nego_tokens_length;
int pub_key_auth_length;
int auth_info_length;
nego_tokens_length = (credssp->negoToken.cbBuffer > 0) ? credssp_skip_nego_tokens(credssp->negoToken.cbBuffer) : 0;
pub_key_auth_length = (credssp->pubKeyAuth.cbBuffer > 0) ? credssp_skip_pub_key_auth(credssp->pubKeyAuth.cbBuffer) : 0;
auth_info_length = (credssp->authInfo.cbBuffer > 0) ? credssp_skip_auth_info(credssp->authInfo.cbBuffer) : 0;
length = nego_tokens_length + pub_key_auth_length + auth_info_length;
ts_request_length = credssp_skip_ts_request(length);
s = stream_new(ts_request_length);
/* TSRequest */
length = ber_get_content_length(ts_request_length);
ber_write_sequence_tag(s, length); /* SEQUENCE */
ber_write_contextual_tag(s, 0, 3, true); /* [0] version */
ber_write_integer(s, 2); /* INTEGER */
/* [1] negoTokens (NegoData) */
if (nego_tokens_length > 0)
{
length = ber_get_content_length(nego_tokens_length);
length -= ber_write_contextual_tag(s, 1, length, true); /* NegoData */
length -= ber_write_sequence_tag(s, length); /* SEQUENCE OF NegoDataItem */
length -= ber_write_sequence_tag(s, length); /* NegoDataItem */
length -= ber_write_contextual_tag(s, 0, length, true); /* [0] negoToken */
ber_write_octet_string(s, credssp->negoToken.pvBuffer, length); /* OCTET STRING */
}
/* [2] authInfo (OCTET STRING) */
if (auth_info_length > 0)
{
length = ber_get_content_length(auth_info_length);
length -= ber_write_contextual_tag(s, 2, length, true);
ber_write_octet_string(s, credssp->authInfo.pvBuffer, credssp->authInfo.cbBuffer);
}
/* [3] pubKeyAuth (OCTET STRING) */
if (pub_key_auth_length > 0)
{
length = ber_get_content_length(pub_key_auth_length);
length -= ber_write_contextual_tag(s, 3, length, true);
ber_write_octet_string(s, credssp->pubKeyAuth.pvBuffer, length);
}
tls_write(credssp->tls, s->data, stream_get_length(s));
stream_free(s);
}
/**
* Receive CredSSP message.
* @param credssp
* @return
*/
int credssp_recv(rdpCredssp* credssp)
{
STREAM* s;
int length;
int status;
uint32 version;
s = stream_new(2048);
status = tls_read(credssp->tls, s->data, stream_get_left(s));
if (status < 0)
return -1;
/* TSRequest */
ber_read_sequence_tag(s, &length);
ber_read_contextual_tag(s, 0, &length, true);
ber_read_integer(s, &version);
/* [1] negoTokens (NegoData) */
if (ber_read_contextual_tag(s, 1, &length, true) != false)
{
ber_read_sequence_tag(s, &length); /* SEQUENCE OF NegoDataItem */
ber_read_sequence_tag(s, &length); /* NegoDataItem */
ber_read_contextual_tag(s, 0, &length, true); /* [0] negoToken */
ber_read_octet_string(s, &length); /* OCTET STRING */
sspi_SecBufferAlloc(&credssp->negoToken, length);
stream_read(s, credssp->negoToken.pvBuffer, length);
credssp->negoToken.cbBuffer = length;
}
/* [2] authInfo (OCTET STRING) */
if (ber_read_contextual_tag(s, 2, &length, true) != false)
{
ber_read_octet_string(s, &length); /* OCTET STRING */
sspi_SecBufferAlloc(&credssp->authInfo, length);
stream_read(s, credssp->authInfo.pvBuffer, length);
credssp->authInfo.cbBuffer = length;
}
/* [3] pubKeyAuth (OCTET STRING) */
if (ber_read_contextual_tag(s, 3, &length, true) != false)
{
ber_read_octet_string(s, &length); /* OCTET STRING */
sspi_SecBufferAlloc(&credssp->pubKeyAuth, length);
stream_read(s, credssp->pubKeyAuth.pvBuffer, length);
credssp->pubKeyAuth.cbBuffer = length;
}
stream_free(s);
return 0;
}
void credssp_buffer_print(rdpCredssp* credssp)
{
if (credssp->negoToken.cbBuffer > 0)
{
printf("CredSSP.negoToken (length = %d):\n", credssp->negoToken.cbBuffer);
freerdp_hexdump(credssp->negoToken.pvBuffer, credssp->negoToken.cbBuffer);
}
if (credssp->pubKeyAuth.cbBuffer > 0)
{
printf("CredSSP.pubKeyAuth (length = %d):\n", credssp->pubKeyAuth.cbBuffer);
freerdp_hexdump(credssp->pubKeyAuth.pvBuffer, credssp->pubKeyAuth.cbBuffer);
}
if (credssp->authInfo.cbBuffer > 0)
{
printf("CredSSP.authInfo (length = %d):\n", credssp->authInfo.cbBuffer);
freerdp_hexdump(credssp->authInfo.pvBuffer, credssp->authInfo.cbBuffer);
}
}
void credssp_buffer_free(rdpCredssp* credssp)
{
sspi_SecBufferFree(&credssp->negoToken);
sspi_SecBufferFree(&credssp->pubKeyAuth);
sspi_SecBufferFree(&credssp->authInfo);
}
/**
* Create new CredSSP state machine.
* @param transport
* @return new CredSSP state machine.
*/
rdpCredssp* credssp_new(freerdp* instance, rdpTls* tls, rdpSettings* settings)
{
rdpCredssp* credssp;
credssp = (rdpCredssp*) xzalloc(sizeof(rdpCredssp));
if (credssp != NULL)
{
credssp->instance = instance;
credssp->settings = 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));
memset(&credssp->authInfo, 0, sizeof(SecBuffer));
}
return credssp;
}
/**
* Free CredSSP state machine.
* @param credssp
*/
void credssp_free(rdpCredssp* credssp)
{
if (credssp != NULL)
{
if (credssp->table)
credssp->table->DeleteSecurityContext(&credssp->context);
sspi_SecBufferFree(&credssp->PublicKey);
sspi_SecBufferFree(&credssp->ts_credentials);
freerdp_uniconv_free(credssp->uniconv);
xfree(credssp->identity.User);
xfree(credssp->identity.Domain);
xfree(credssp->identity.Password);
xfree(credssp);
}
}
/* SSPI */
const SecurityFunctionTableA CREDSSP_SecurityFunctionTableA =
{
1, /* dwVersion */
NULL, /* EnumerateSecurityPackages */
NULL, /* QueryCredentialsAttributes */
NULL, /* AcquireCredentialsHandle */
NULL, /* FreeCredentialsHandle */
NULL, /* Reserved2 */
NULL, /* InitializeSecurityContext */
NULL, /* AcceptSecurityContext */
NULL, /* CompleteAuthToken */
NULL, /* DeleteSecurityContext */
NULL, /* ApplyControlToken */
NULL, /* QueryContextAttributes */
NULL, /* ImpersonateSecurityContext */
NULL, /* RevertSecurityContext */
NULL, /* MakeSignature */
NULL, /* VerifySignature */
NULL, /* FreeContextBuffer */
NULL, /* QuerySecurityPackageInfo */
NULL, /* Reserved3 */
NULL, /* Reserved4 */
NULL, /* ExportSecurityContext */
NULL, /* ImportSecurityContext */
NULL, /* AddCredentials */
NULL, /* Reserved8 */
NULL, /* QuerySecurityContextToken */
NULL, /* EncryptMessage */
NULL, /* DecryptMessage */
NULL /* SetContextAttributes */
};
const SecurityFunctionTableW CREDSSP_SecurityFunctionTableW =
{
1, /* dwVersion */
NULL, /* EnumerateSecurityPackages */
NULL, /* QueryCredentialsAttributes */
NULL, /* AcquireCredentialsHandle */
NULL, /* FreeCredentialsHandle */
NULL, /* Reserved2 */
NULL, /* InitializeSecurityContext */
NULL, /* AcceptSecurityContext */
NULL, /* CompleteAuthToken */
NULL, /* DeleteSecurityContext */
NULL, /* ApplyControlToken */
NULL, /* QueryContextAttributes */
NULL, /* ImpersonateSecurityContext */
NULL, /* RevertSecurityContext */
NULL, /* MakeSignature */
NULL, /* VerifySignature */
NULL, /* FreeContextBuffer */
NULL, /* QuerySecurityPackageInfo */
NULL, /* Reserved3 */
NULL, /* Reserved4 */
NULL, /* ExportSecurityContext */
NULL, /* ImportSecurityContext */
NULL, /* AddCredentials */
NULL, /* Reserved8 */
NULL, /* QuerySecurityContextToken */
NULL, /* EncryptMessage */
NULL, /* DecryptMessage */
NULL /* SetContextAttributes */
};
const SecPkgInfoA CREDSSP_SecPkgInfoA =
{
0x000110733, /* fCapabilities */
1, /* wVersion */
0xFFFF, /* wRPCID */
0x000090A8, /* cbMaxToken */
"CREDSSP", /* Name */
"Microsoft CredSSP Security Provider" /* Comment */
};
const SecPkgInfoW CREDSSP_SecPkgInfoW =
{
0x000110733, /* fCapabilities */
1, /* wVersion */
0xFFFF, /* wRPCID */
0x000090A8, /* cbMaxToken */
L"CREDSSP", /* Name */
L"Microsoft CredSSP Security Provider" /* Comment */
};