freerdp: add configurable NTLM SAM file option for server-side NLA

This commit is contained in:
Marc-André Moreau 2016-07-21 18:58:24 -04:00
parent 1ffbd774e9
commit 801dc0f826
12 changed files with 96 additions and 31 deletions

View File

@ -602,6 +602,7 @@ typedef struct _RDPDR_PARALLEL RDPDR_PARALLEL;
#define FreeRDP_AuthenticationLevel 1100 #define FreeRDP_AuthenticationLevel 1100
#define FreeRDP_AllowedTlsCiphers 1101 #define FreeRDP_AllowedTlsCiphers 1101
#define FreeRDP_VmConnectMode 1102 #define FreeRDP_VmConnectMode 1102
#define FreeRDP_NtlmSamFile 1103
#define FreeRDP_MstscCookieMode 1152 #define FreeRDP_MstscCookieMode 1152
#define FreeRDP_CookieMaxLength 1153 #define FreeRDP_CookieMaxLength 1153
#define FreeRDP_PreconnectionId 1154 #define FreeRDP_PreconnectionId 1154
@ -1005,7 +1006,8 @@ struct rdp_settings
ALIGN64 BOOL AuthenticationLevel; /* 1100 */ ALIGN64 BOOL AuthenticationLevel; /* 1100 */
ALIGN64 char* AllowedTlsCiphers; /* 1101 */ ALIGN64 char* AllowedTlsCiphers; /* 1101 */
ALIGN64 BOOL VmConnectMode; /* 1102 */ ALIGN64 BOOL VmConnectMode; /* 1102 */
UINT64 padding1152[1152 - 1103]; /* 1103 */ ALIGN64 char* NtlmSamFile; /* 1103 */
UINT64 padding1152[1152 - 1104]; /* 1104 */
/* Connection Cookie */ /* Connection Cookie */
ALIGN64 BOOL MstscCookieMode; /* 1152 */ ALIGN64 BOOL MstscCookieMode; /* 1152 */

View File

@ -2398,6 +2398,12 @@ char* freerdp_get_param_string(rdpSettings* settings, int id)
case FreeRDP_AuthenticationServiceClass: case FreeRDP_AuthenticationServiceClass:
return settings->AuthenticationServiceClass; return settings->AuthenticationServiceClass;
case FreeRDP_AllowedTlsCiphers:
return settings->AllowedTlsCiphers;
case FreeRDP_NtlmSamFile:
return settings->NtlmSamFile;
case FreeRDP_PreconnectionBlob: case FreeRDP_PreconnectionBlob:
return settings->PreconnectionBlob; return settings->PreconnectionBlob;
@ -2574,6 +2580,14 @@ int freerdp_set_param_string(rdpSettings* settings, int id, const char* param)
tmp = &settings->AuthenticationServiceClass; tmp = &settings->AuthenticationServiceClass;
break; break;
case FreeRDP_AllowedTlsCiphers:
tmp = &settings->AllowedTlsCiphers;
break;
case FreeRDP_NtlmSamFile:
tmp = &settings->NtlmSamFile;
break;
case FreeRDP_PreconnectionBlob: case FreeRDP_PreconnectionBlob:
tmp = &settings->PreconnectionBlob; tmp = &settings->PreconnectionBlob;
break; break;

View File

@ -164,7 +164,7 @@ int nla_client_init(rdpNla* nla)
if (PromptPassword && settings->Username && strlen(settings->Username)) if (PromptPassword && settings->Username && strlen(settings->Username))
{ {
sam = SamOpen(TRUE); sam = SamOpen(NULL, TRUE);
if (sam) if (sam)
{ {
@ -715,9 +715,9 @@ int nla_server_authenticate(rdpNla* nla)
return -1; return -1;
nla->status = nla->table->AcceptSecurityContext(&nla->credentials, nla->status = nla->table->AcceptSecurityContext(&nla->credentials,
nla-> haveContext? &nla->context: NULL, nla->haveContext? &nla->context: NULL,
&nla->inputBufferDesc, nla->fContextReq, SECURITY_NATIVE_DREP, &nla->context, &nla->inputBufferDesc, nla->fContextReq, SECURITY_NATIVE_DREP, &nla->context,
&nla->outputBufferDesc, &nla->pfContextAttr, &nla->expiration); &nla->outputBufferDesc, &nla->pfContextAttr, &nla->expiration);
WLog_VRB(TAG, "AcceptSecurityContext status %s [%08X]", WLog_VRB(TAG, "AcceptSecurityContext status %s [%08X]",
GetSecurityStatusString(nla->status), nla->status); 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->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) if (nla->table->CompleteAuthToken)
{ {
SECURITY_STATUS status; SECURITY_STATUS status;
status = nla->table->CompleteAuthToken(&nla->context, &nla->outputBufferDesc); status = nla->table->CompleteAuthToken(&nla->context, &nla->outputBufferDesc);
if (status != SEC_E_OK) if (status != SEC_E_OK)
{ {
WLog_WARN(TAG, "CompleteAuthToken status %s [%08X]", WLog_WARN(TAG, "CompleteAuthToken status %s [%08X]",
@ -737,6 +744,7 @@ int nla_server_authenticate(rdpNla* nla)
return -1; return -1;
} }
} }
if (nla->status == SEC_I_COMPLETE_NEEDED) if (nla->status == SEC_I_COMPLETE_NEEDED)
nla->status = SEC_E_OK; nla->status = SEC_E_OK;
else if (nla->status == SEC_I_COMPLETE_AND_CONTINUE) 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_new(freerdp* instance, rdpTransport* transport, rdpSettings* settings)
{ {
rdpNla* nla = (rdpNla*) calloc(1, sizeof(rdpNla)); rdpNla* nla = (rdpNla*) calloc(1, sizeof(rdpNla));
if (!nla) if (!nla)
return NULL; return NULL;
nla->identity = calloc(1, sizeof(SEC_WINNT_AUTH_IDENTITY)); nla->identity = calloc(1, sizeof(SEC_WINNT_AUTH_IDENTITY));
if (!nla->identity) if (!nla->identity)
{ {
free (nla); free(nla);
return NULL; return NULL;
} }
@ -1738,6 +1746,9 @@ rdpNla* nla_new(freerdp* instance, rdpTransport* transport, rdpSettings* setting
nla->recvSeqNum = 0; nla->recvSeqNum = 0;
nla->version = 3; nla->version = 3;
if (settings->NtlmSamFile)
nla->SamFile = _strdup(settings->NtlmSamFile);
ZeroMemory(&nla->negoToken, sizeof(SecBuffer)); ZeroMemory(&nla->negoToken, sizeof(SecBuffer));
ZeroMemory(&nla->pubKeyAuth, sizeof(SecBuffer)); ZeroMemory(&nla->pubKeyAuth, sizeof(SecBuffer));
ZeroMemory(&nla->authInfo, 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->PublicKey);
sspi_SecBufferFree(&nla->tsCredentials); sspi_SecBufferFree(&nla->tsCredentials);

View File

@ -56,6 +56,7 @@ struct rdp_nla
freerdp* instance; freerdp* instance;
CtxtHandle context; CtxtHandle context;
LPTSTR SspiModule; LPTSTR SspiModule;
char* SamFile;
rdpSettings* settings; rdpSettings* settings;
rdpTransport* transport; rdpTransport* transport;
UINT32 cbMaxToken; UINT32 cbMaxToken;

View File

@ -598,6 +598,7 @@ rdpSettings* freerdp_settings_clone(rdpSettings* settings)
CHECKED_STRDUP(RemoteAssistanceRCTicket); /* 1028 */ CHECKED_STRDUP(RemoteAssistanceRCTicket); /* 1028 */
CHECKED_STRDUP(AuthenticationServiceClass); /* 1098 */ CHECKED_STRDUP(AuthenticationServiceClass); /* 1098 */
CHECKED_STRDUP(AllowedTlsCiphers); /* 1101 */ CHECKED_STRDUP(AllowedTlsCiphers); /* 1101 */
CHECKED_STRDUP(NtlmSamFile); /* 1103 */
CHECKED_STRDUP(PreconnectionBlob); /* 1155 */ CHECKED_STRDUP(PreconnectionBlob); /* 1155 */
CHECKED_STRDUP(KerberosKdc); /* 1344 */ CHECKED_STRDUP(KerberosKdc); /* 1344 */
CHECKED_STRDUP(KerberosRealm); /* 1345 */ CHECKED_STRDUP(KerberosRealm); /* 1345 */
@ -920,6 +921,7 @@ void freerdp_settings_free(rdpSettings* settings)
free(settings->ClientAddress); free(settings->ClientAddress);
free(settings->ClientDir); free(settings->ClientDir);
free(settings->AllowedTlsCiphers); free(settings->AllowedTlsCiphers);
free(settings->NtlmSamFile);
free(settings->CertificateFile); free(settings->CertificateFile);
free(settings->PrivateKeyFile); free(settings->PrivateKeyFile);
free(settings->ConnectionFile); free(settings->ConnectionFile);

View File

@ -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-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-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" }, { "sec-ext", COMMAND_LINE_VALUE_BOOL, NULL, BoolValueFalse, NULL, -1, NULL, "nla extended protocol security" },
{ "sam-file", COMMAND_LINE_VALUE_REQUIRED, "<file>", 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" }, { "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" }, { "help", COMMAND_LINE_VALUE_FLAG | COMMAND_LINE_PRINT_HELP, NULL, NULL, NULL, -1, "?", "Print help" },
{ NULL, 0, NULL, NULL, NULL, -1, NULL, NULL } { 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; settings->ExtSecurity = arg->Value ? TRUE : FALSE;
} }
CommandLineSwitchCase(arg, "sam-file")
{
freerdp_set_param_string(settings, FreeRDP_NtlmSamFile, arg->Value);
}
CommandLineSwitchDefault(arg) CommandLineSwitchDefault(arg)
{ {

View File

@ -28,7 +28,7 @@ struct winpr_sam
FILE* fp; FILE* fp;
char* line; char* line;
char* buffer; char* buffer;
BOOL read_only; BOOL readOnly;
}; };
typedef struct winpr_sam WINPR_SAM; 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 SamResetEntry(WINPR_SAM_ENTRY* entry);
WINPR_API void SamFreeEntry(WINPR_SAM* sam, 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); WINPR_API void SamClose(WINPR_SAM* sam);
#ifdef __cplusplus #ifdef __cplusplus

View File

@ -1005,13 +1005,14 @@ extern "C" {
#define SECPKG_ATTR_AUTH_IDENTITY 1001 #define SECPKG_ATTR_AUTH_IDENTITY 1001
#define SECPKG_ATTR_AUTH_PASSWORD 1002 #define SECPKG_ATTR_AUTH_PASSWORD 1002
#define SECPKG_ATTR_AUTH_NTLM_HASH 1003 #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_MESSAGE 1100
#define SECPKG_ATTR_AUTH_NTLM_TIMESTAMP 1101 #define SECPKG_ATTR_AUTH_NTLM_TIMESTAMP 1101
#define SECPKG_ATTR_AUTH_NTLM_CLIENT_CHALLENGE 1102 #define SECPKG_ATTR_AUTH_NTLM_CLIENT_CHALLENGE 1102
#define SECPKG_ATTR_AUTH_NTLM_SERVER_CHALLENGE 1103 #define SECPKG_ATTR_AUTH_NTLM_SERVER_CHALLENGE 1103
#define SECPKG_ATTR_AUTH_NTLM_NTPROOF_VALUE 1104 #define SECPKG_ATTR_AUTH_NTLM_NTPROOF_VALUE 1104
#define SECPKG_ATTR_AUTH_NTLM_RANDKEY 1105 #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 #define SECPKG_ATTR_AUTH_NTLM_MIC_VALUE 1107

View File

@ -805,6 +805,15 @@ SECURITY_STATUS SEC_ENTRY ntlm_SetContextAttributesW(PCtxtHandle phContext, ULON
return SEC_E_OK; 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) else if (ulAttribute == SECPKG_ATTR_AUTH_NTLM_MESSAGE)
{ {
SecPkgContext_AuthNtlmMessage* AuthNtlmMessage = (SecPkgContext_AuthNtlmMessage*) pBuffer; SecPkgContext_AuthNtlmMessage* AuthNtlmMessage = (SecPkgContext_AuthNtlmMessage*) pBuffer;

View File

@ -220,6 +220,7 @@ struct _NTLM_CONTEXT
NTLM_STATE state; NTLM_STATE state;
int SendSeqNum; int SendSeqNum;
int RecvSeqNum; int RecvSeqNum;
char* SamFile;
BYTE NtlmHash[16]; BYTE NtlmHash[16];
BYTE NtlmV2Hash[16]; BYTE NtlmV2Hash[16];
BYTE MachineID[32]; BYTE MachineID[32];

View File

@ -196,7 +196,7 @@ int ntlm_fetch_ntlm_v2_hash(NTLM_CONTEXT* context, BYTE* hash)
WINPR_SAM_ENTRY* entry; WINPR_SAM_ENTRY* entry;
SSPI_CREDENTIALS* credentials = context->credentials; SSPI_CREDENTIALS* credentials = context->credentials;
sam = SamOpen(TRUE); sam = SamOpen(context->SamFile, TRUE);
if (!sam) if (!sam)
return -1; return -1;

View File

@ -30,6 +30,7 @@
#include <winpr/print.h> #include <winpr/print.h>
#include "../log.h" #include "../log.h"
#ifdef HAVE_UNISTD_H #ifdef HAVE_UNISTD_H
#include <unistd.h> #include <unistd.h>
#endif #endif
@ -41,73 +42,83 @@
#endif #endif
#define TAG WINPR_TAG("utils") #define TAG WINPR_TAG("utils")
WINPR_SAM* SamOpen(BOOL read_only) WINPR_SAM* SamOpen(const char* filename, BOOL readOnly)
{ {
FILE* fp = NULL; FILE* fp = NULL;
WINPR_SAM* sam = 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 else
{ {
fp = fopen(WINPR_SAM_FILE, "r+"); fp = fopen(filename, "r+");
if (!fp) if (!fp)
fp = fopen(WINPR_SAM_FILE, "w+"); fp = fopen(filename, "w+");
} }
if (fp) if (fp)
{ {
sam = (WINPR_SAM*) malloc(sizeof(WINPR_SAM)); sam = (WINPR_SAM*) malloc(sizeof(WINPR_SAM));
if (!sam) if (!sam)
{ {
fclose(fp); fclose(fp);
return NULL; return NULL;
} }
sam->read_only = read_only;
sam->readOnly = readOnly;
sam->fp = fp; sam->fp = fp;
} }
else else
{
WLog_DBG(TAG, "Could not open SAM file!"); WLog_DBG(TAG, "Could not open SAM file!");
}
return sam; return sam;
} }
static BOOL SamLookupStart(WINPR_SAM* sam) static BOOL SamLookupStart(WINPR_SAM* sam)
{ {
size_t read_size; size_t readSize;
long int file_size; long int fileSize;
fseek(sam->fp, 0, SEEK_END); fseek(sam->fp, 0, SEEK_END);
file_size = ftell(sam->fp); fileSize = ftell(sam->fp);
fseek(sam->fp, 0, SEEK_SET); fseek(sam->fp, 0, SEEK_SET);
if (file_size < 1) if (fileSize < 1)
return FALSE; return FALSE;
sam->buffer = (char*) malloc(file_size + 2); sam->buffer = (char*) malloc(fileSize + 2);
if (!sam->buffer) if (!sam->buffer)
return FALSE; 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)) if (!ferror(sam->fp))
read_size = file_size; readSize = fileSize;
} }
if (read_size < 1) if (readSize < 1)
{ {
free(sam->buffer); free(sam->buffer);
sam->buffer = NULL; sam->buffer = NULL;
return FALSE; return FALSE;
} }
sam->buffer[file_size] = '\n'; sam->buffer[fileSize] = '\n';
sam->buffer[file_size + 1] = '\0'; sam->buffer[fileSize + 1] = '\0';
sam->line = strtok(sam->buffer, "\n"); sam->line = strtok(sam->buffer, "\n");
return TRUE; return TRUE;
} }
@ -224,17 +235,19 @@ void SamResetEntry(WINPR_SAM_ENTRY* entry)
free(entry->Domain); free(entry->Domain);
entry->Domain = NULL; entry->Domain = NULL;
} }
ZeroMemory(entry->LmHash, sizeof(entry->LmHash)); ZeroMemory(entry->LmHash, sizeof(entry->LmHash));
ZeroMemory(entry->NtHash, sizeof(entry->NtHash)); ZeroMemory(entry->NtHash, sizeof(entry->NtHash));
} }
WINPR_SAM_ENTRY* SamLookupUserA(WINPR_SAM* sam, LPSTR User, UINT32 UserLength, LPSTR Domain, UINT32 DomainLength) WINPR_SAM_ENTRY* SamLookupUserA(WINPR_SAM* sam, LPSTR User, UINT32 UserLength, LPSTR Domain, UINT32 DomainLength)
{ {
int length; int length;
BOOL found = FALSE; BOOL found = FALSE;
WINPR_SAM_ENTRY* entry; WINPR_SAM_ENTRY* entry;
entry = (WINPR_SAM_ENTRY*) calloc(1, sizeof(WINPR_SAM_ENTRY)); entry = (WINPR_SAM_ENTRY*) calloc(1, sizeof(WINPR_SAM_ENTRY));
if (!entry) if (!entry)
return NULL; return NULL;