From 801dc0f826863854be88d9a94ebfbb21faad239a Mon Sep 17 00:00:00 2001 From: =?utf8?q?Marc-Andr=C3=A9=20Moreau?= Date: Thu, 21 Jul 2016 18:58:24 -0400 Subject: [PATCH] freerdp: add configurable NTLM SAM file option for server-side NLA --- include/freerdp/settings.h | 4 ++- libfreerdp/common/settings.c | 14 ++++++++++ libfreerdp/core/nla.c | 29 +++++++++++++++---- libfreerdp/core/nla.h | 1 + libfreerdp/core/settings.c | 2 ++ server/shadow/shadow_server.c | 5 ++++ winpr/include/winpr/sam.h | 4 +-- winpr/include/winpr/sspi.h | 7 +++-- winpr/libwinpr/sspi/NTLM/ntlm.c | 9 ++++++ winpr/libwinpr/sspi/NTLM/ntlm.h | 1 + winpr/libwinpr/sspi/NTLM/ntlm_compute.c | 2 +- winpr/libwinpr/utils/sam.c | 49 +++++++++++++++++++++------------ 12 files changed, 96 insertions(+), 31 deletions(-) diff --git a/include/freerdp/settings.h b/include/freerdp/settings.h index 8fb335a..47d88e8 100644 --- a/include/freerdp/settings.h +++ b/include/freerdp/settings.h @@ -602,6 +602,7 @@ typedef struct _RDPDR_PARALLEL RDPDR_PARALLEL; #define FreeRDP_AuthenticationLevel 1100 #define FreeRDP_AllowedTlsCiphers 1101 #define FreeRDP_VmConnectMode 1102 +#define FreeRDP_NtlmSamFile 1103 #define FreeRDP_MstscCookieMode 1152 #define FreeRDP_CookieMaxLength 1153 #define FreeRDP_PreconnectionId 1154 @@ -1005,7 +1006,8 @@ struct rdp_settings ALIGN64 BOOL AuthenticationLevel; /* 1100 */ ALIGN64 char* AllowedTlsCiphers; /* 1101 */ ALIGN64 BOOL VmConnectMode; /* 1102 */ - UINT64 padding1152[1152 - 1103]; /* 1103 */ + ALIGN64 char* NtlmSamFile; /* 1103 */ + UINT64 padding1152[1152 - 1104]; /* 1104 */ /* Connection Cookie */ ALIGN64 BOOL MstscCookieMode; /* 1152 */ diff --git a/libfreerdp/common/settings.c b/libfreerdp/common/settings.c index b8a4a20..9b24162 100644 --- a/libfreerdp/common/settings.c +++ b/libfreerdp/common/settings.c @@ -2398,6 +2398,12 @@ char* freerdp_get_param_string(rdpSettings* settings, int id) case FreeRDP_AuthenticationServiceClass: return settings->AuthenticationServiceClass; + case FreeRDP_AllowedTlsCiphers: + return settings->AllowedTlsCiphers; + + case FreeRDP_NtlmSamFile: + return settings->NtlmSamFile; + case FreeRDP_PreconnectionBlob: return settings->PreconnectionBlob; @@ -2574,6 +2580,14 @@ int freerdp_set_param_string(rdpSettings* settings, int id, const char* param) tmp = &settings->AuthenticationServiceClass; break; + case FreeRDP_AllowedTlsCiphers: + tmp = &settings->AllowedTlsCiphers; + break; + + case FreeRDP_NtlmSamFile: + tmp = &settings->NtlmSamFile; + break; + case FreeRDP_PreconnectionBlob: tmp = &settings->PreconnectionBlob; break; diff --git a/libfreerdp/core/nla.c b/libfreerdp/core/nla.c index c586cdb..606c809 100644 --- a/libfreerdp/core/nla.c +++ b/libfreerdp/core/nla.c @@ -164,7 +164,7 @@ int nla_client_init(rdpNla* nla) if (PromptPassword && settings->Username && strlen(settings->Username)) { - sam = SamOpen(TRUE); + sam = SamOpen(NULL, TRUE); if (sam) { @@ -715,9 +715,9 @@ int nla_server_authenticate(rdpNla* nla) return -1; nla->status = nla->table->AcceptSecurityContext(&nla->credentials, - nla-> haveContext? &nla->context: NULL, - &nla->inputBufferDesc, nla->fContextReq, SECURITY_NATIVE_DREP, &nla->context, - &nla->outputBufferDesc, &nla->pfContextAttr, &nla->expiration); + nla->haveContext? &nla->context: NULL, + &nla->inputBufferDesc, nla->fContextReq, SECURITY_NATIVE_DREP, &nla->context, + &nla->outputBufferDesc, &nla->pfContextAttr, &nla->expiration); WLog_VRB(TAG, "AcceptSecurityContext status %s [%08X]", GetSecurityStatusString(nla->status), nla->status); @@ -726,10 +726,17 @@ int nla_server_authenticate(rdpNla* nla) if ((nla->status == SEC_I_COMPLETE_AND_CONTINUE) || (nla->status == SEC_I_COMPLETE_NEEDED)) { + if (nla->SamFile) + { + nla->table->SetContextAttributes(&nla->context, + SECPKG_ATTR_AUTH_NTLM_SAM_FILE, nla->SamFile, strlen(nla->SamFile) + 1); + } + if (nla->table->CompleteAuthToken) { SECURITY_STATUS status; status = nla->table->CompleteAuthToken(&nla->context, &nla->outputBufferDesc); + if (status != SEC_E_OK) { WLog_WARN(TAG, "CompleteAuthToken status %s [%08X]", @@ -737,6 +744,7 @@ int nla_server_authenticate(rdpNla* nla) return -1; } } + if (nla->status == SEC_I_COMPLETE_NEEDED) nla->status = SEC_E_OK; else if (nla->status == SEC_I_COMPLETE_AND_CONTINUE) @@ -1717,16 +1725,16 @@ LPTSTR nla_make_spn(const char* ServiceClass, const char* hostname) rdpNla* nla_new(freerdp* instance, rdpTransport* transport, rdpSettings* settings) { - rdpNla* nla = (rdpNla*) calloc(1, sizeof(rdpNla)); if (!nla) return NULL; nla->identity = calloc(1, sizeof(SEC_WINNT_AUTH_IDENTITY)); + if (!nla->identity) { - free (nla); + free(nla); return NULL; } @@ -1738,6 +1746,9 @@ rdpNla* nla_new(freerdp* instance, rdpTransport* transport, rdpSettings* setting nla->recvSeqNum = 0; nla->version = 3; + if (settings->NtlmSamFile) + nla->SamFile = _strdup(settings->NtlmSamFile); + ZeroMemory(&nla->negoToken, sizeof(SecBuffer)); ZeroMemory(&nla->pubKeyAuth, sizeof(SecBuffer)); ZeroMemory(&nla->authInfo, sizeof(SecBuffer)); @@ -1804,6 +1815,12 @@ void nla_free(rdpNla* nla) } } + if (nla->SamFile) + { + free(nla->SamFile); + nla->SamFile = NULL; + } + sspi_SecBufferFree(&nla->PublicKey); sspi_SecBufferFree(&nla->tsCredentials); diff --git a/libfreerdp/core/nla.h b/libfreerdp/core/nla.h index f6f173e..49bef8f 100644 --- a/libfreerdp/core/nla.h +++ b/libfreerdp/core/nla.h @@ -56,6 +56,7 @@ struct rdp_nla freerdp* instance; CtxtHandle context; LPTSTR SspiModule; + char* SamFile; rdpSettings* settings; rdpTransport* transport; UINT32 cbMaxToken; diff --git a/libfreerdp/core/settings.c b/libfreerdp/core/settings.c index 5fee7f2..a353f08 100644 --- a/libfreerdp/core/settings.c +++ b/libfreerdp/core/settings.c @@ -598,6 +598,7 @@ rdpSettings* freerdp_settings_clone(rdpSettings* settings) CHECKED_STRDUP(RemoteAssistanceRCTicket); /* 1028 */ CHECKED_STRDUP(AuthenticationServiceClass); /* 1098 */ CHECKED_STRDUP(AllowedTlsCiphers); /* 1101 */ + CHECKED_STRDUP(NtlmSamFile); /* 1103 */ CHECKED_STRDUP(PreconnectionBlob); /* 1155 */ CHECKED_STRDUP(KerberosKdc); /* 1344 */ CHECKED_STRDUP(KerberosRealm); /* 1345 */ @@ -920,6 +921,7 @@ void freerdp_settings_free(rdpSettings* settings) free(settings->ClientAddress); free(settings->ClientDir); free(settings->AllowedTlsCiphers); + free(settings->NtlmSamFile); free(settings->CertificateFile); free(settings->PrivateKeyFile); free(settings->ConnectionFile); diff --git a/server/shadow/shadow_server.c b/server/shadow/shadow_server.c index 7f6a6e2..d3dd1a0 100644 --- a/server/shadow/shadow_server.c +++ b/server/shadow/shadow_server.c @@ -55,6 +55,7 @@ static COMMAND_LINE_ARGUMENT_A shadow_args[] = { "sec-tls", COMMAND_LINE_VALUE_BOOL, NULL, BoolValueTrue, NULL, -1, NULL, "tls protocol security" }, { "sec-nla", COMMAND_LINE_VALUE_BOOL, NULL, BoolValueTrue, NULL, -1, NULL, "nla protocol security" }, { "sec-ext", COMMAND_LINE_VALUE_BOOL, NULL, BoolValueFalse, NULL, -1, NULL, "nla extended protocol security" }, + { "sam-file", COMMAND_LINE_VALUE_REQUIRED, "", NULL, NULL, -1, NULL, "NTLM SAM file for NLA authentication" }, { "version", COMMAND_LINE_VALUE_FLAG | COMMAND_LINE_PRINT_VERSION, NULL, NULL, NULL, -1, NULL, "Print version" }, { "help", COMMAND_LINE_VALUE_FLAG | COMMAND_LINE_PRINT_HELP, NULL, NULL, NULL, -1, "?", "Print help" }, { NULL, 0, NULL, NULL, NULL, -1, NULL, NULL } @@ -311,6 +312,10 @@ int shadow_server_parse_command_line(rdpShadowServer* server, int argc, char** a { settings->ExtSecurity = arg->Value ? TRUE : FALSE; } + CommandLineSwitchCase(arg, "sam-file") + { + freerdp_set_param_string(settings, FreeRDP_NtlmSamFile, arg->Value); + } CommandLineSwitchDefault(arg) { diff --git a/winpr/include/winpr/sam.h b/winpr/include/winpr/sam.h index d1c6f1f..3740daf 100644 --- a/winpr/include/winpr/sam.h +++ b/winpr/include/winpr/sam.h @@ -28,7 +28,7 @@ struct winpr_sam FILE* fp; char* line; char* buffer; - BOOL read_only; + BOOL readOnly; }; typedef struct winpr_sam WINPR_SAM; @@ -53,7 +53,7 @@ WINPR_API WINPR_SAM_ENTRY* SamLookupUserW(WINPR_SAM* sam, LPWSTR User, UINT32 Us WINPR_API void SamResetEntry(WINPR_SAM_ENTRY* entry); WINPR_API void SamFreeEntry(WINPR_SAM* sam, WINPR_SAM_ENTRY* entry); -WINPR_API WINPR_SAM* SamOpen(BOOL read_only); +WINPR_API WINPR_SAM* SamOpen(const char* filename, BOOL readOnly); WINPR_API void SamClose(WINPR_SAM* sam); #ifdef __cplusplus diff --git a/winpr/include/winpr/sspi.h b/winpr/include/winpr/sspi.h index f069fb2..790a6e7 100644 --- a/winpr/include/winpr/sspi.h +++ b/winpr/include/winpr/sspi.h @@ -1005,13 +1005,14 @@ extern "C" { #define SECPKG_ATTR_AUTH_IDENTITY 1001 #define SECPKG_ATTR_AUTH_PASSWORD 1002 #define SECPKG_ATTR_AUTH_NTLM_HASH 1003 +#define SECPKG_ATTR_AUTH_NTLM_SAM_FILE 1004 #define SECPKG_ATTR_AUTH_NTLM_MESSAGE 1100 #define SECPKG_ATTR_AUTH_NTLM_TIMESTAMP 1101 -#define SECPKG_ATTR_AUTH_NTLM_CLIENT_CHALLENGE 1102 -#define SECPKG_ATTR_AUTH_NTLM_SERVER_CHALLENGE 1103 +#define SECPKG_ATTR_AUTH_NTLM_CLIENT_CHALLENGE 1102 +#define SECPKG_ATTR_AUTH_NTLM_SERVER_CHALLENGE 1103 #define SECPKG_ATTR_AUTH_NTLM_NTPROOF_VALUE 1104 #define SECPKG_ATTR_AUTH_NTLM_RANDKEY 1105 -#define SECPKG_ATTR_AUTH_NTLM_MIC 1106 +#define SECPKG_ATTR_AUTH_NTLM_MIC 1106 #define SECPKG_ATTR_AUTH_NTLM_MIC_VALUE 1107 diff --git a/winpr/libwinpr/sspi/NTLM/ntlm.c b/winpr/libwinpr/sspi/NTLM/ntlm.c index 1d56de1..32ff60a 100644 --- a/winpr/libwinpr/sspi/NTLM/ntlm.c +++ b/winpr/libwinpr/sspi/NTLM/ntlm.c @@ -805,6 +805,15 @@ SECURITY_STATUS SEC_ENTRY ntlm_SetContextAttributesW(PCtxtHandle phContext, ULON return SEC_E_OK; } + else if (ulAttribute == SECPKG_ATTR_AUTH_NTLM_SAM_FILE) + { + const char* filename = (char*) pBuffer; + + free(context->SamFile); + context->SamFile = filename ? _strdup(filename) : NULL; + + return SEC_E_OK; + } else if (ulAttribute == SECPKG_ATTR_AUTH_NTLM_MESSAGE) { SecPkgContext_AuthNtlmMessage* AuthNtlmMessage = (SecPkgContext_AuthNtlmMessage*) pBuffer; diff --git a/winpr/libwinpr/sspi/NTLM/ntlm.h b/winpr/libwinpr/sspi/NTLM/ntlm.h index 8253f3d..5a6256c 100644 --- a/winpr/libwinpr/sspi/NTLM/ntlm.h +++ b/winpr/libwinpr/sspi/NTLM/ntlm.h @@ -220,6 +220,7 @@ struct _NTLM_CONTEXT NTLM_STATE state; int SendSeqNum; int RecvSeqNum; + char* SamFile; BYTE NtlmHash[16]; BYTE NtlmV2Hash[16]; BYTE MachineID[32]; diff --git a/winpr/libwinpr/sspi/NTLM/ntlm_compute.c b/winpr/libwinpr/sspi/NTLM/ntlm_compute.c index 8c18466..bde267b 100644 --- a/winpr/libwinpr/sspi/NTLM/ntlm_compute.c +++ b/winpr/libwinpr/sspi/NTLM/ntlm_compute.c @@ -196,7 +196,7 @@ int ntlm_fetch_ntlm_v2_hash(NTLM_CONTEXT* context, BYTE* hash) WINPR_SAM_ENTRY* entry; SSPI_CREDENTIALS* credentials = context->credentials; - sam = SamOpen(TRUE); + sam = SamOpen(context->SamFile, TRUE); if (!sam) return -1; diff --git a/winpr/libwinpr/utils/sam.c b/winpr/libwinpr/utils/sam.c index d097c0a..2b68fbf 100644 --- a/winpr/libwinpr/utils/sam.c +++ b/winpr/libwinpr/utils/sam.c @@ -30,6 +30,7 @@ #include #include "../log.h" + #ifdef HAVE_UNISTD_H #include #endif @@ -41,73 +42,83 @@ #endif #define TAG WINPR_TAG("utils") -WINPR_SAM* SamOpen(BOOL read_only) +WINPR_SAM* SamOpen(const char* filename, BOOL readOnly) { FILE* fp = NULL; WINPR_SAM* sam = NULL; - if (read_only) + if (!filename) + filename = WINPR_SAM_FILE; + + if (readOnly) { - fp = fopen(WINPR_SAM_FILE, "r"); + fp = fopen(filename, "r"); } else { - fp = fopen(WINPR_SAM_FILE, "r+"); + fp = fopen(filename, "r+"); if (!fp) - fp = fopen(WINPR_SAM_FILE, "w+"); + fp = fopen(filename, "w+"); } if (fp) { sam = (WINPR_SAM*) malloc(sizeof(WINPR_SAM)); + if (!sam) { fclose(fp); return NULL; } - sam->read_only = read_only; + + sam->readOnly = readOnly; sam->fp = fp; } else + { WLog_DBG(TAG, "Could not open SAM file!"); + } return sam; } static BOOL SamLookupStart(WINPR_SAM* sam) { - size_t read_size; - long int file_size; + size_t readSize; + long int fileSize; + fseek(sam->fp, 0, SEEK_END); - file_size = ftell(sam->fp); + fileSize = ftell(sam->fp); fseek(sam->fp, 0, SEEK_SET); - if (file_size < 1) + if (fileSize < 1) return FALSE; - sam->buffer = (char*) malloc(file_size + 2); + sam->buffer = (char*) malloc(fileSize + 2); + if (!sam->buffer) return FALSE; - read_size = fread(sam->buffer, file_size, 1, sam->fp); + readSize = fread(sam->buffer, fileSize, 1, sam->fp); - if (!read_size) + if (!readSize) { if (!ferror(sam->fp)) - read_size = file_size; + readSize = fileSize; } - if (read_size < 1) + if (readSize < 1) { free(sam->buffer); sam->buffer = NULL; return FALSE; } - sam->buffer[file_size] = '\n'; - sam->buffer[file_size + 1] = '\0'; + sam->buffer[fileSize] = '\n'; + sam->buffer[fileSize + 1] = '\0'; sam->line = strtok(sam->buffer, "\n"); + return TRUE; } @@ -224,17 +235,19 @@ void SamResetEntry(WINPR_SAM_ENTRY* entry) free(entry->Domain); entry->Domain = NULL; } + ZeroMemory(entry->LmHash, sizeof(entry->LmHash)); ZeroMemory(entry->NtHash, sizeof(entry->NtHash)); } - WINPR_SAM_ENTRY* SamLookupUserA(WINPR_SAM* sam, LPSTR User, UINT32 UserLength, LPSTR Domain, UINT32 DomainLength) { int length; BOOL found = FALSE; WINPR_SAM_ENTRY* entry; + entry = (WINPR_SAM_ENTRY*) calloc(1, sizeof(WINPR_SAM_ENTRY)); + if (!entry) return NULL; -- 2.7.4