diff --git a/client/common/cmdline.c b/client/common/cmdline.c index c38d6a9ce..d772c0fe1 100644 --- a/client/common/cmdline.c +++ b/client/common/cmdline.c @@ -52,6 +52,7 @@ COMMAND_LINE_ARGUMENT_A args[] = { "kbd-fn-key", COMMAND_LINE_VALUE_REQUIRED, "", NULL, NULL, -1, NULL, "Keyboard function key count" }, { "admin", COMMAND_LINE_VALUE_FLAG, NULL, NULL, NULL, -1, "console", "Admin (or console) session" }, { "restricted-admin", COMMAND_LINE_VALUE_FLAG, NULL, NULL, NULL, -1, "restrictedAdmin", "Restricted admin mode" }, + { "pth", COMMAND_LINE_VALUE_REQUIRED, "", NULL, NULL, -1, "pass-the-hash", "Pass the hash (restricted admin mode)" }, { "multimon", COMMAND_LINE_VALUE_OPTIONAL, NULL, NULL, NULL, -1, NULL, "Use multiple monitors" }, { "workarea", COMMAND_LINE_VALUE_FLAG, NULL, NULL, NULL, -1, NULL, "Use available work area" }, { "monitors", COMMAND_LINE_VALUE_REQUIRED, "<0,1,2...>", NULL, NULL, -1, NULL, "Select monitors to use" }, @@ -1237,6 +1238,12 @@ int freerdp_client_settings_parse_command_line_arguments(rdpSettings* settings, settings->ConsoleSession = TRUE; settings->RestrictedAdminModeRequired = TRUE; } + CommandLineSwitchCase(arg, "pth") + { + settings->ConsoleSession = TRUE; + settings->RestrictedAdminModeRequired = TRUE; + settings->PasswordHash = _strdup(arg->Value); + } CommandLineSwitchCase(arg, "kbd") { int id; diff --git a/include/freerdp/settings.h b/include/freerdp/settings.h index 384440407..59481d9dd 100644 --- a/include/freerdp/settings.h +++ b/include/freerdp/settings.h @@ -476,6 +476,7 @@ typedef struct _RDPDR_PARALLEL RDPDR_PARALLEL; #define FreeRDP_Username 21 #define FreeRDP_Password 22 #define FreeRDP_Domain 23 +#define FreeRDP_PasswordHash 24 #define FreeRDP_RdpVersion 128 #define FreeRDP_DesktopWidth 129 #define FreeRDP_DesktopHeight 130 @@ -760,7 +761,8 @@ struct rdp_settings ALIGN64 char* Username; /* 21 */ ALIGN64 char* Password; /* 22 */ ALIGN64 char* Domain; /* 23 */ - UINT64 padding0064[64 - 24]; /* 24 */ + ALIGN64 char* PasswordHash; /* 24 */ + UINT64 padding0064[64 - 25]; /* 25 */ UINT64 padding0128[128 - 64]; /* 64 */ /** diff --git a/libfreerdp/common/settings.c b/libfreerdp/common/settings.c index 83419807e..ba0bea378 100644 --- a/libfreerdp/common/settings.c +++ b/libfreerdp/common/settings.c @@ -2096,6 +2096,10 @@ char* freerdp_get_param_string(rdpSettings* settings, int id) return settings->Domain; break; + case FreeRDP_PasswordHash: + return settings->PasswordHash; + break; + case FreeRDP_ClientHostname: return settings->ClientHostname; break; @@ -2264,6 +2268,11 @@ int freerdp_set_param_string(rdpSettings* settings, int id, const char* param) settings->Domain = _strdup(param); break; + case FreeRDP_PasswordHash: + free(settings->PasswordHash); + settings->PasswordHash = _strdup(param); + break; + case FreeRDP_ClientHostname: free(settings->ClientHostname); settings->ClientHostname = _strdup(param); diff --git a/libfreerdp/core/nla.c b/libfreerdp/core/nla.c index 56a3e519d..5e1a2feec 100644 --- a/libfreerdp/core/nla.c +++ b/libfreerdp/core/nla.c @@ -113,20 +113,39 @@ int credssp_ntlm_client_init(rdpCredssp* credssp) { char* spn; int length; + BOOL PromptPassword; rdpTls* tls = NULL; freerdp* instance; rdpSettings* settings; + PromptPassword = FALSE; settings = credssp->settings; instance = (freerdp*) settings->instance; - if ((settings->Password == NULL ) || (settings->Username == NULL) + if ((!settings->Password) || (!settings->Username) || (!strlen(settings->Password)) || (!strlen(settings->Username))) + { + PromptPassword = TRUE; + } + +#ifndef _WIN32 + if (PromptPassword) + { + if (settings->RestrictedAdminModeRequired) + { + if ((settings->PasswordHash) && (strlen(settings->PasswordHash) > 0)) + PromptPassword = FALSE; + } + } +#endif + + if (PromptPassword) { if (instance->Authenticate) { BOOL proceed = instance->Authenticate(instance, &settings->Username, &settings->Password, &settings->Domain); + if (!proceed) return 0; } @@ -134,6 +153,33 @@ int credssp_ntlm_client_init(rdpCredssp* credssp) sspi_SetAuthIdentity(&(credssp->identity), settings->Username, settings->Domain, settings->Password); +#ifndef _WIN32 + { + SEC_WINNT_AUTH_IDENTITY* identity = &(credssp->identity); + + if (settings->RestrictedAdminModeRequired) + { + if (settings->PasswordHash) + { + if (strlen(settings->PasswordHash) == 32) + { + if (identity->Password) + free(identity->Password); + + identity->PasswordLength = ConvertToUnicode(CP_UTF8, 0, + settings->PasswordHash, -1, &identity->Password, 0) - 1; + + /** + * Multiply password hash length by 64 to obtain a length exceeding + * the maximum (256) and use it this for hash identification in WinPR. + */ + identity->PasswordLength = 32 * 64; /* 2048 */ + } + } + } + } +#endif + #ifdef WITH_DEBUG_NLA _tprintf(_T("User: %s Domain: %s Password: %s\n"), (char*) credssp->identity.User, (char*) credssp->identity.Domain, (char*) credssp->identity.Password); diff --git a/libfreerdp/core/settings.c b/libfreerdp/core/settings.c index 9dbaf834a..5870e49b5 100644 --- a/libfreerdp/core/settings.c +++ b/libfreerdp/core/settings.c @@ -445,6 +445,7 @@ rdpSettings* freerdp_settings_clone(rdpSettings* settings) _settings->Username = _strdup(settings->Username); /* 21 */ _settings->Password = _strdup(settings->Password); /* 22 */ _settings->Domain = _strdup(settings->Domain); /* 23 */ + _settings->PasswordHash = _strdup(settings->PasswordHash); /* 24 */ //_settings->ClientHostname = _strdup(settings->ClientHostname); /* 134 */ //_settings->ClientProductId = _strdup(settings->ClientProductId); /* 135 */ _settings->AlternateShell = _strdup(settings->AlternateShell); /* 640 */ @@ -780,6 +781,7 @@ void freerdp_settings_free(rdpSettings* settings) free(settings->Username); free(settings->Password); free(settings->Domain); + free(settings->PasswordHash); free(settings->AlternateShell); free(settings->ShellWorkingDirectory); free(settings->ComputerName); diff --git a/winpr/libwinpr/sspi/NTLM/ntlm_compute.c b/winpr/libwinpr/sspi/NTLM/ntlm_compute.c index 5bd0a81a5..fb77c8c1f 100644 --- a/winpr/libwinpr/sspi/NTLM/ntlm_compute.c +++ b/winpr/libwinpr/sspi/NTLM/ntlm_compute.c @@ -282,9 +282,43 @@ void ntlm_fetch_ntlm_v2_hash(NTLM_CONTEXT* context, char* hash) SamClose(sam); } +void ntlm_convert_password_hash(NTLM_CONTEXT* context, BYTE* hash) +{ + int i, hn, ln; + char* PasswordHash = NULL; + UINT32 PasswordHashLength = 0; + + /* Password contains a password hash of length (PasswordLength / SSPI_CREDENTIALS_HASH_LENGTH_FACTOR) */ + + PasswordHashLength = context->identity.PasswordLength / SSPI_CREDENTIALS_HASH_LENGTH_FACTOR; + ConvertFromUnicode(CP_UTF8, 0, context->identity.Password, PasswordHashLength, &PasswordHash, 0, NULL, NULL); + CharUpperBuffA(PasswordHash, PasswordHashLength); + + for (i = 0; i < 32; i += 2) + { + hn = PasswordHash[i] > '9' ? PasswordHash[i] - 'A' + 10 : PasswordHash[i] - '0'; + ln = PasswordHash[i + 1] > '9' ? PasswordHash[i + 1] - 'A' + 10 : PasswordHash[i + 1] - '0'; + hash[i / 2] = (hn << 4) | ln; + } + + free(PasswordHash); +} + void ntlm_compute_ntlm_v2_hash(NTLM_CONTEXT* context, char* hash) { - if (context->identity.PasswordLength > 0) + if (context->identity.PasswordLength > 256) + { + BYTE PasswordHash[16]; + + /* Special case for WinPR: password hash */ + ntlm_convert_password_hash(context, PasswordHash); + + NTOWFv2FromHashW(PasswordHash, + (LPWSTR) context->identity.User, context->identity.UserLength * 2, + (LPWSTR) context->identity.Domain, context->identity.DomainLength * 2, + (BYTE*) hash); + } + else if (context->identity.PasswordLength > 0) { NTOWFv2W((LPWSTR) context->identity.Password, context->identity.PasswordLength * 2, (LPWSTR) context->identity.User, context->identity.UserLength * 2, diff --git a/winpr/libwinpr/sspi/sspi.c b/winpr/libwinpr/sspi/sspi.c index 1869baca4..4922756ee 100644 --- a/winpr/libwinpr/sspi/sspi.c +++ b/winpr/libwinpr/sspi/sspi.c @@ -192,11 +192,10 @@ CREDENTIALS* sspi_CredentialsNew() CREDENTIALS* credentials; credentials = (CREDENTIALS*) malloc(sizeof(CREDENTIALS)); - ZeroMemory(credentials, sizeof(CREDENTIALS)); - if (credentials != NULL) + if (credentials) { - + ZeroMemory(credentials, sizeof(CREDENTIALS)); } return credentials; @@ -319,7 +318,7 @@ void sspi_SetAuthIdentity(SEC_WINNT_AUTH_IDENTITY* identity, char* user, char* d identity->DomainLength = 0; } - if (password != NULL) + if (password) { identity->PasswordLength = ConvertToUnicode(CP_UTF8, 0, password, -1, &identity->Password, 0) - 1; } @@ -366,12 +365,17 @@ void sspi_CopyAuthIdentity(SEC_WINNT_AUTH_IDENTITY* identity, SEC_WINNT_AUTH_IDE identity->PasswordLength = srcIdentity->PasswordLength; + if (identity->PasswordLength > 256) + identity->PasswordLength /= SSPI_CREDENTIALS_HASH_LENGTH_FACTOR; + if (identity->PasswordLength > 0) { identity->Password = (UINT16*) malloc((identity->PasswordLength + 1) * sizeof(WCHAR)); CopyMemory(identity->Password, srcIdentity->Password, identity->PasswordLength * sizeof(WCHAR)); identity->Password[identity->PasswordLength] = 0; } + + identity->PasswordLength = srcIdentity->PasswordLength; } PSecBuffer sspi_FindSecBuffer(PSecBufferDesc pMessage, ULONG BufferType) diff --git a/winpr/libwinpr/sspi/sspi.h b/winpr/libwinpr/sspi/sspi.h index dab63578b..60f5e0508 100644 --- a/winpr/libwinpr/sspi/sspi.h +++ b/winpr/libwinpr/sspi/sspi.h @@ -24,8 +24,13 @@ #define SCHANNEL_CB_MAX_TOKEN 0x00006000 +#define SSPI_CREDENTIALS_PASSWORD_HASH 0x00000001 + +#define SSPI_CREDENTIALS_HASH_LENGTH_FACTOR 64 + struct _CREDENTIALS { + DWORD flags; SEC_WINNT_AUTH_IDENTITY identity; }; typedef struct _CREDENTIALS CREDENTIALS;