/** * FreeRDP: A Remote Desktop Protocol Implementation * NTLM Security Package * * Copyright 2011-2012 Marc-Andre Moreau * * 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 #include #include #include #include #include #include #include #include "ntlm.h" #include "../sspi.h" #include "ntlm_message.h" char* NTLM_PACKAGE_NAME = "NTLM"; void ntlm_SetContextIdentity(NTLM_CONTEXT* context, SEC_AUTH_IDENTITY* identity) { size_t size; context->identity.Flags = SEC_AUTH_IDENTITY_UNICODE; if (identity->Flags == SEC_AUTH_IDENTITY_ANSI) { 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; } 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); } } void ntlm_SetContextWorkstation(NTLM_CONTEXT* context, char* Workstation) { size_t size; context->Workstation = (uint16*) freerdp_uniconv_out(context->uniconv, Workstation, &size); context->WorkstationLength = (uint32) size; } NTLM_CONTEXT* ntlm_ContextNew() { NTLM_CONTEXT* context; context = xnew(NTLM_CONTEXT); if (context != NULL) { context->ntlm_v2 = false; context->NegotiateFlags = 0; context->state = NTLM_STATE_INITIAL; context->uniconv = freerdp_uniconv_new(); context->av_pairs = (AV_PAIRS*) xzalloc(sizeof(AV_PAIRS)); } return context; } void ntlm_ContextFree(NTLM_CONTEXT* context) { if (!context) return; xfree(context); } SECURITY_STATUS ntlm_AcquireCredentialsHandle(char* pszPrincipal, char* pszPackage, uint32 fCredentialUse, void* pvLogonID, void* pAuthData, void* pGetKeyFn, void* pvGetKeyArgument, CRED_HANDLE* phCredential, SEC_TIMESTAMP* ptsExpiry) { CREDENTIALS* credentials; SEC_AUTH_IDENTITY* identity; if (fCredentialUse == SECPKG_CRED_OUTBOUND) { credentials = sspi_CredentialsNew(); identity = (SEC_AUTH_IDENTITY*) pAuthData; memcpy(&(credentials->identity), identity, sizeof(SEC_AUTH_IDENTITY)); sspi_SecureHandleSetLowerPointer(phCredential, (void*) credentials); sspi_SecureHandleSetUpperPointer(phCredential, (void*) NTLM_PACKAGE_NAME); return SEC_E_OK; } return SEC_E_OK; } SECURITY_STATUS ntlm_FreeCredentialsHandle(CRED_HANDLE* phCredential) { CREDENTIALS* credentials; if (!phCredential) return SEC_E_INVALID_HANDLE; credentials = (CREDENTIALS*) sspi_SecureHandleGetLowerPointer(phCredential); if (!credentials) return SEC_E_INVALID_HANDLE; sspi_CredentialsFree(credentials); return SEC_E_OK; } SECURITY_STATUS ntlm_QueryCredentialsAttributes(CRED_HANDLE* phCredential, uint32 ulAttribute, void* pBuffer) { if (ulAttribute == SECPKG_CRED_ATTR_NAMES) { CREDENTIALS* credentials; SEC_PKG_CREDENTIALS_NAMES* credential_names = (SEC_PKG_CREDENTIALS_NAMES*) pBuffer; credentials = (CREDENTIALS*) sspi_SecureHandleGetLowerPointer(phCredential); if (credentials->identity.Flags == SEC_AUTH_IDENTITY_ANSI) credential_names->sUserName = xstrdup((char*) credentials->identity.User); return SEC_E_OK; } return SEC_E_UNSUPPORTED_FUNCTION; } /* http://msdn.microsoft.com/en-us/library/windows/desktop/aa375512/ */ SECURITY_STATUS ntlm_InitializeSecurityContext(CRED_HANDLE* phCredential, CTXT_HANDLE* phContext, char* pszTargetName, uint32 fContextReq, uint32 Reserved1, uint32 TargetDataRep, SEC_BUFFER_DESC* pInput, uint32 Reserved2, CTXT_HANDLE* phNewContext, SEC_BUFFER_DESC* pOutput, uint32* pfContextAttr, SEC_TIMESTAMP* ptsExpiry) { NTLM_CONTEXT* context; SECURITY_STATUS status; CREDENTIALS* credentials; SEC_BUFFER* input_sec_buffer; SEC_BUFFER* output_sec_buffer; context = sspi_SecureHandleGetLowerPointer(phContext); if (!context) { context = ntlm_ContextNew(); credentials = (CREDENTIALS*) sspi_SecureHandleGetLowerPointer(phCredential); ntlm_SetContextIdentity(context, &credentials->identity); ntlm_SetContextWorkstation(context, "WORKSTATION"); sspi_SecureHandleSetLowerPointer(phNewContext, context); sspi_SecureHandleSetUpperPointer(phNewContext, (void*) NTLM_PACKAGE_NAME); } if ((!pInput) || (context->state == NTLM_STATE_AUTHENTICATE)) { if (!pOutput) return SEC_E_INVALID_TOKEN; if (pOutput->cBuffers < 1) return SEC_E_INVALID_TOKEN; output_sec_buffer = &pOutput->pBuffers[0]; if (output_sec_buffer->BufferType != SECBUFFER_TOKEN) return SEC_E_INVALID_TOKEN; if (output_sec_buffer->cbBuffer < 1) return SEC_E_INSUFFICIENT_MEMORY; if (context->state == NTLM_STATE_INITIAL) context->state = NTLM_STATE_NEGOTIATE; if (context->state == NTLM_STATE_NEGOTIATE) return ntlm_write_NegotiateMessage(context, output_sec_buffer); return SEC_E_OUT_OF_SEQUENCE; } else { context = (NTLM_CONTEXT*) sspi_SecureHandleGetLowerPointer(phContext); if (!context) return SEC_E_INVALID_HANDLE; if (!pInput) return SEC_E_INVALID_TOKEN; if (pInput->cBuffers < 1) return SEC_E_INVALID_TOKEN; input_sec_buffer = &pInput->pBuffers[0]; if (input_sec_buffer->BufferType != SECBUFFER_TOKEN) return SEC_E_INVALID_TOKEN; if (input_sec_buffer->cbBuffer < 1) return SEC_E_INVALID_TOKEN; if (context->state == NTLM_STATE_CHALLENGE) { status = ntlm_read_ChallengeMessage(context, input_sec_buffer); if (!pOutput) return SEC_E_INVALID_TOKEN; if (pOutput->cBuffers < 1) return SEC_E_INVALID_TOKEN; output_sec_buffer = &pOutput->pBuffers[0]; if (output_sec_buffer->BufferType != SECBUFFER_TOKEN) return SEC_E_INVALID_TOKEN; if (output_sec_buffer->cbBuffer < 1) return SEC_E_INSUFFICIENT_MEMORY; if (context->state == NTLM_STATE_AUTHENTICATE) return ntlm_write_AuthenticateMessage(context, output_sec_buffer); } return SEC_E_OUT_OF_SEQUENCE; } return SEC_E_OK; } /* http://msdn.microsoft.com/en-us/library/windows/desktop/aa379337/ */ SECURITY_STATUS ntlm_QueryContextAttributes(CTXT_HANDLE* phContext, uint32 ulAttribute, void* pBuffer) { if (!phContext) return SEC_E_INVALID_HANDLE; if (!pBuffer) return SEC_E_INSUFFICIENT_MEMORY; if (ulAttribute == SECPKG_ATTR_SIZES) { SEC_PKG_CONTEXT_SIZES* ContextSizes = (SEC_PKG_CONTEXT_SIZES*) pBuffer; ContextSizes->cbMaxToken = 2010; ContextSizes->cbMaxSignature = 16; ContextSizes->cbBlockSize = 0; ContextSizes->cbSecurityTrailer = 16; return SEC_E_OK; } return SEC_E_UNSUPPORTED_FUNCTION; } SECURITY_STATUS ntlm_EncryptMessage(CTXT_HANDLE* phContext, uint32 fQOP, SEC_BUFFER_DESC* pMessage, uint32 MessageSeqNo) { int index; int length; void* data; HMAC_CTX hmac; uint8 digest[16]; uint8 checksum[8]; uint8* signature; uint32 version = 1; NTLM_CONTEXT* context; SEC_BUFFER* data_buffer = NULL; SEC_BUFFER* signature_buffer = NULL; context = sspi_SecureHandleGetLowerPointer(phContext); for (index = 0; index < pMessage->cBuffers; index++) { if (pMessage->pBuffers[index].BufferType == SECBUFFER_DATA) data_buffer = &pMessage->pBuffers[index]; else if (pMessage->pBuffers[index].BufferType == SECBUFFER_PADDING) signature_buffer = &pMessage->pBuffers[index]; } if (!data_buffer) return SEC_E_INVALID_TOKEN; if (!signature_buffer) return SEC_E_INVALID_TOKEN; /* Copy original data buffer */ length = data_buffer->cbBuffer; data = xmalloc(length); memcpy(data, data_buffer->pvBuffer, length); /* Compute the HMAC-MD5 hash of ConcatenationOf(seq_num,data) using the client signing key */ HMAC_CTX_init(&hmac); HMAC_Init_ex(&hmac, context->ClientSigningKey, 16, EVP_md5(), NULL); HMAC_Update(&hmac, (void*) &(MessageSeqNo), 4); HMAC_Update(&hmac, data, length); HMAC_Final(&hmac, digest, NULL); HMAC_CTX_cleanup(&hmac); /* Encrypt message using with RC4, result overwrites original buffer */ crypto_rc4(context->send_rc4_seal, length, data, data_buffer->pvBuffer); xfree(data); #ifdef WITH_DEBUG_NTLM printf("Data Buffer (length = %d)\n", length); freerdp_hexdump(data, length); printf("\n"); printf("Encrypted Data Buffer (length = %d)\n", data_buffer->cbBuffer); freerdp_hexdump(data_buffer->pvBuffer, data_buffer->cbBuffer); printf("\n"); #endif /* RC4-encrypt first 8 bytes of digest */ crypto_rc4(context->send_rc4_seal, 8, digest, checksum); signature = (uint8*) signature_buffer->pvBuffer; /* Concatenate version, ciphertext and sequence number to build signature */ memcpy(signature, (void*) &version, 4); memcpy(&signature[4], (void*) checksum, 8); memcpy(&signature[12], (void*) &(MessageSeqNo), 4); context->send_seq_num++; #ifdef WITH_DEBUG_NTLM printf("Signature (length = %d)\n", signature_buffer->cbBuffer); freerdp_hexdump(signature_buffer->pvBuffer, signature_buffer->cbBuffer); printf("\n"); #endif return SEC_E_OK; } SECURITY_STATUS ntlm_DecryptMessage(CTXT_HANDLE* phContext, SEC_BUFFER_DESC* pMessage, uint32 MessageSeqNo, uint32* pfQOP) { int index; int length; void* data; HMAC_CTX hmac; uint8 digest[16]; uint8 checksum[8]; uint32 version = 1; NTLM_CONTEXT* context; uint8 expected_signature[16]; SEC_BUFFER* data_buffer = NULL; SEC_BUFFER* signature_buffer = NULL; context = sspi_SecureHandleGetLowerPointer(phContext); for (index = 0; index < pMessage->cBuffers; index++) { if (pMessage->pBuffers[index].BufferType == SECBUFFER_DATA) data_buffer = &pMessage->pBuffers[index]; else if (pMessage->pBuffers[index].BufferType == SECBUFFER_PADDING) signature_buffer = &pMessage->pBuffers[index]; } if (!data_buffer) return SEC_E_INVALID_TOKEN; if (!signature_buffer) return SEC_E_INVALID_TOKEN; /* Copy original data buffer */ length = data_buffer->cbBuffer; data = xmalloc(length); memcpy(data, data_buffer->pvBuffer, length); /* Decrypt message using with RC4 */ crypto_rc4(context->recv_rc4_seal, length, data, data_buffer->pvBuffer); /* Compute the HMAC-MD5 hash of ConcatenationOf(seq_num,data) using the client signing key */ HMAC_CTX_init(&hmac); HMAC_Init_ex(&hmac, context->ServerSigningKey, 16, EVP_md5(), NULL); HMAC_Update(&hmac, (void*) &(MessageSeqNo), 4); HMAC_Update(&hmac, data_buffer->pvBuffer, data_buffer->cbBuffer); HMAC_Final(&hmac, digest, NULL); HMAC_CTX_cleanup(&hmac); xfree(data); /* RC4-encrypt first 8 bytes of digest */ crypto_rc4(context->recv_rc4_seal, 8, digest, checksum); /* Concatenate version, ciphertext and sequence number to build signature */ memcpy(expected_signature, (void*) &version, 4); memcpy(&expected_signature[4], (void*) checksum, 8); memcpy(&expected_signature[12], (void*) &(MessageSeqNo), 4); context->recv_seq_num++; if (memcmp(signature_buffer->pvBuffer, expected_signature, 16) != 0) { /* signature verification failed! */ printf("signature verification failed, something nasty is going on!\n"); return SEC_E_MESSAGE_ALTERED; } return SEC_E_OK; } SECURITY_STATUS ntlm_MakeSignature(CTXT_HANDLE* phContext, uint32 fQOP, SEC_BUFFER_DESC* pMessage, uint32 MessageSeqNo) { return SEC_E_OK; } SECURITY_STATUS ntlm_VerifySignature(CTXT_HANDLE* phContext, SEC_BUFFER_DESC* pMessage, uint32 MessageSeqNo, uint32* pfQOP) { return SEC_E_OK; } const SEC_PKG_INFO NTLM_SEC_PKG_INFO = { 0x00082B37, /* fCapabilities */ 1, /* wVersion */ 0x000A, /* wRPCID */ 0x00000B48, /* cbMaxToken */ "NTLM", /* Name */ "NTLM Security Package" /* Comment */ }; const SECURITY_FUNCTION_TABLE NTLM_SECURITY_FUNCTION_TABLE = { 1, /* dwVersion */ NULL, /* EnumerateSecurityPackages */ NULL, /* Reserved1 */ ntlm_QueryCredentialsAttributes, /* QueryCredentialsAttributes */ ntlm_AcquireCredentialsHandle, /* AcquireCredentialsHandle */ ntlm_FreeCredentialsHandle, /* FreeCredentialsHandle */ NULL, /* Reserved2 */ ntlm_InitializeSecurityContext, /* InitializeSecurityContext */ NULL, /* AcceptSecurityContext */ NULL, /* CompleteAuthToken */ NULL, /* DeleteSecurityContext */ NULL, /* ApplyControlToken */ ntlm_QueryContextAttributes, /* QueryContextAttributes */ NULL, /* ImpersonateSecurityContext */ NULL, /* RevertSecurityContext */ ntlm_MakeSignature, /* MakeSignature */ ntlm_VerifySignature, /* VerifySignature */ NULL, /* FreeContextBuffer */ NULL, /* QuerySecurityPackageInfo */ NULL, /* Reserved3 */ NULL, /* Reserved4 */ NULL, /* ExportSecurityContext */ NULL, /* ImportSecurityContext */ NULL, /* AddCredentials */ NULL, /* Reserved8 */ NULL, /* QuerySecurityContextToken */ ntlm_EncryptMessage, /* EncryptMessage */ ntlm_DecryptMessage, /* DecryptMessage */ NULL, /* SetContextAttributes */ };