diff --git a/client/Wayland/wlfreerdp.c b/client/Wayland/wlfreerdp.c index f6826f593..b5f371f85 100644 --- a/client/Wayland/wlfreerdp.c +++ b/client/Wayland/wlfreerdp.c @@ -32,6 +32,7 @@ #include #include #include +#include #include @@ -702,15 +703,14 @@ int main(int argc, char* argv[]) status = freerdp_client_settings_parse_command_line(settings, argc, argv, FALSE); if (status) { - BOOL list; - rc = freerdp_client_settings_command_line_status_print(settings, status, argc, argv); - list = settings->ListMonitors; - - if (list) + if (settings->ListMonitors) wlf_list_monitors(wlc); + if (settings->ListSmartcards) + freerdp_smartcard_list(settings); + goto fail; } diff --git a/client/Windows/cli/wfreerdp.c b/client/Windows/cli/wfreerdp.c index 6fc7102b0..10fd2f378 100644 --- a/client/Windows/cli/wfreerdp.c +++ b/client/Windows/cli/wfreerdp.c @@ -34,6 +34,7 @@ #include #include #include +#include #include "../resource/resource.h" @@ -103,10 +104,13 @@ INT WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine goto out; status = freerdp_client_settings_parse_command_line(settings, argc, argv, FALSE); - if (status) { ret = freerdp_client_settings_command_line_status_print(settings, status, argc, argv); + + if (settings->ListSmartcards) + freerdp_smartcard_list(settings); + goto out; } diff --git a/client/X11/cli/xfreerdp.c b/client/X11/cli/xfreerdp.c index 82bc66354..54bea838d 100644 --- a/client/X11/cli/xfreerdp.c +++ b/client/X11/cli/xfreerdp.c @@ -29,6 +29,7 @@ #include #include #include +#include #include "../xf_client.h" #include "../xfreerdp.h" @@ -59,15 +60,14 @@ int main(int argc, char* argv[]) status = freerdp_client_settings_parse_command_line(context->settings, argc, argv, FALSE); if (status) { - BOOL list; - rc = freerdp_client_settings_command_line_status_print(settings, status, argc, argv); - list = settings->ListMonitors; - - if (list) + if (settings->ListMonitors) xf_list_monitors(xfc); + if (settings->ListSmartcards) + freerdp_smartcard_list(settings); + goto out; } diff --git a/client/common/client.c b/client/common/client.c index c2662ec20..73059ae42 100644 --- a/client/common/client.c +++ b/client/common/client.c @@ -253,7 +253,6 @@ static BOOL freerdp_client_settings_post_process(rdpSettings* settings) if (settings->SmartcardLogon) { - settings->NlaSecurity = FALSE; /* for now */ settings->TlsSecurity = TRUE; settings->RedirectSmartCards = TRUE; settings->DeviceRedirection = TRUE; @@ -427,6 +426,7 @@ static BOOL client_cli_authenticate_raw(freerdp* instance, rdp_auth_reason reaso { static const size_t password_size = 512; const char* auth[] = { "Username: ", "Domain: ", "Password: " }; + const char* authPin[] = { "Username: ", "Domain: ", "Pin: " }; const char* gw[] = { "GatewayUsername: ", "GatewayDomain: ", "GatewayPassword: " }; const char** prompt; @@ -437,6 +437,9 @@ static BOOL client_cli_authenticate_raw(freerdp* instance, rdp_auth_reason reaso case AUTH_RDP: prompt = auth; break; + case AUTH_SMARTCARD_PIN: + prompt = authPin; + break; case GW_AUTH_HTTP: case GW_AUTH_RDG: case GW_AUTH_RPC: @@ -516,21 +519,14 @@ BOOL client_cli_authenticate_ex(freerdp* instance, char** username, char** passw WINPR_ASSERT(password); WINPR_ASSERT(domain); - if (instance->settings->SmartcardLogon) - { - WLog_INFO(TAG, "Authentication via smartcard"); - return TRUE; - } - switch (reason) { case AUTH_NLA: break; + case AUTH_TLS: - if ((*username) && (*password)) - return TRUE; - break; case AUTH_RDP: + case AUTH_SMARTCARD_PIN: /* in this case password is pin code */ if ((*username) && (*password)) return TRUE; break; diff --git a/client/common/cmdline.c b/client/common/cmdline.c index 38cc31dfd..5d540f896 100644 --- a/client/common/cmdline.c +++ b/client/common/cmdline.c @@ -24,12 +24,13 @@ #endif #include -#include #include +#include #include #include #include +#include #include #include @@ -479,6 +480,22 @@ BOOL freerdp_client_print_command_line_help_ex(int argc, char** argv, printf("\n"); printf("Drive Redirection: /drive:home,/home/user\n"); printf("Smartcard Redirection: /smartcard:\n"); + printf("Smartcard logon with rdp only: /smartcard-logon [/sec:rdp]\n"); + printf("Smartcard logon with Kerberos authentication: /smartcard-logon /sec:nla\n"); + printf("Those options are only accepted with /smartcard-logon:\n"); + printf(" PIN code: /pin:\n"); + printf(" PKCS11 module to load: /pkcs11-module:\n"); + printf(" PKINIT anchors: /pkinit-anchors:\n"); + printf(" Kerberos Ticket start time: /start-time:\n"); + printf(" Kerberos Ticket lifetime: /lifetime:\n"); + printf(" Kerberos Ticket renewable lifetime: /renewable-lifetime:\n"); + /* See also http://web.mit.edu/kerberos/krb5-latest/doc/basic/date_format.html */ + printf(" The delay and lifetime have the following syntax: [s|m|h|d] (for seconds, " + " minutes, hours and days)\n"); + printf(" CSP Name: /csp:\n"); + printf(" Card Name: /card:\n"); + printf("Serial Port Redirection: /serial:,,[SerCx2|SerCx|Serial],[permissive]\n"); printf("Serial Port Redirection: /serial:COM1,/dev/ttyS0\n"); printf("Parallel Port Redirection: /parallel:,\n"); @@ -1354,6 +1371,12 @@ int freerdp_client_settings_command_line_status_print_ex(rdpSettings* settings, settings->ListMonitors = TRUE; } + arg = CommandLineFindArgumentA(largs, "smartcard-list"); + if (arg->Flags & COMMAND_LINE_VALUE_PRESENT) + { + settings->ListSmartcards = TRUE; + } + arg = CommandLineFindArgumentA(largs, "kbd-scancode-list"); if (arg->Flags & COMMAND_LINE_VALUE_PRESENT) @@ -1565,7 +1588,9 @@ int freerdp_client_settings_parse_command_line_arguments(rdpSettings* settings, if (!(arg->Flags & COMMAND_LINE_ARGUMENT_PRESENT)) continue; - CommandLineSwitchStart(arg) CommandLineSwitchCase(arg, "v") + CommandLineSwitchStart(arg) + + CommandLineSwitchCase(arg, "v") { char* p; @@ -1848,6 +1873,10 @@ int freerdp_client_settings_parse_command_line_arguments(rdpSettings* settings, { settings->ListMonitors = enable; } + CommandLineSwitchCase(arg, "smartcard-list") + { + settings->ListSmartcards = enable; + } CommandLineSwitchCase(arg, "t") { if (!freerdp_settings_set_string(settings, FreeRDP_WindowTitle, arg->Value)) @@ -3239,6 +3268,59 @@ int freerdp_client_settings_parse_command_line_arguments(rdpSettings* settings, return COMMAND_LINE_ERROR_UNEXPECTED_VALUE; } } + CommandLineSwitchCase(arg, "pin") + { + if (!copy_value(arg->Value, &settings->Pin)) + return COMMAND_LINE_ERROR_MEMORY; + } + CommandLineSwitchCase(arg, "pkcs11-module") + { + if (!copy_value(arg->Value, &settings->Pkcs11Module)) + return COMMAND_LINE_ERROR_MEMORY; + } + CommandLineSwitchCase(arg, "pkinit-anchors") + { + if (!copy_value(arg->Value, &settings->PkinitAnchors)) + return COMMAND_LINE_ERROR_MEMORY; + } + CommandLineSwitchCase(arg, "kerberos-start-time") + { + /* Let kinit parse time strings according to krb5_string_to_deltat syntax. */ + if (!copy_value(arg->Value, &settings->KerberosStartTime)) + return COMMAND_LINE_ERROR_MEMORY; + } + CommandLineSwitchCase(arg, "kerberos-lifetime") + { + /* Let kinit parse time strings according to krb5_string_to_deltat syntax. */ + if (!copy_value(arg->Value, &settings->KerberosLifeTime)) + return COMMAND_LINE_ERROR_MEMORY; + } + CommandLineSwitchCase(arg, "kerberos-renewable-lifetime") + { + /* Let kinit parse time strings according to krb5_string_to_deltat syntax. */ + if (!copy_value(arg->Value, &settings->KerberosRenewableLifeTime)) + return COMMAND_LINE_ERROR_MEMORY; + } + CommandLineSwitchCase(arg, "csp") + { + if (!copy_value(arg->Value, &settings->CspName)) + return COMMAND_LINE_ERROR_MEMORY; + } + CommandLineSwitchCase(arg, "card") + { + if (!copy_value(arg->Value, &settings->CardName)) + return COMMAND_LINE_ERROR_MEMORY; + } + CommandLineSwitchCase(arg, "reader") + { + if (!copy_value(arg->Value, &settings->ReaderName)) + return COMMAND_LINE_ERROR_MEMORY; + } + CommandLineSwitchCase(arg, "container") + { + if (!copy_value(arg->Value, &settings->ContainerName)) + return COMMAND_LINE_ERROR_MEMORY; + } CommandLineSwitchCase(arg, "action-script") { if (!freerdp_settings_set_string(settings, FreeRDP_ActionScript, arg->Value)) @@ -3263,18 +3345,21 @@ int freerdp_client_settings_parse_command_line_arguments(rdpSettings* settings, } ptr; settings->SmartcardLogon = TRUE; + ptr.p = CommandLineParseCommaSeparatedValuesEx("smartcard-logon", arg->Value, &count); if (ptr.pc) { size_t x; - settings->SmartcardEmulation = TRUE; + settings->SmartcardEmulation = (count > 1); for (x = 1; x < count; x++) { const char* cur = ptr.pc[x]; if (strncmp("cert:", cur, 5) == 0) { const char* f = &cur[5]; - if (!read_pem_file(settings, FreeRDP_SmartcardCertificate, f)) + settings->SmartcardCertificateFile = strdup(f); + if (!settings->SmartcardCertificateFile || + !read_pem_file(settings, FreeRDP_SmartcardCertificate, f)) { free(ptr.p); return COMMAND_LINE_ERROR_UNEXPECTED_VALUE; @@ -3283,7 +3368,9 @@ int freerdp_client_settings_parse_command_line_arguments(rdpSettings* settings, else if (strncmp("key:", cur, 4) == 0) { const char* f = &cur[4]; - if (!read_pem_file(settings, FreeRDP_SmartcardPrivateKey, f)) + settings->SmartcardPrivateKeyFile = strdup(f); + if (!settings->SmartcardPrivateKeyFile || + !read_pem_file(settings, FreeRDP_SmartcardPrivateKey, f)) { free(ptr.p); return COMMAND_LINE_ERROR_UNEXPECTED_VALUE; @@ -3307,7 +3394,6 @@ int freerdp_client_settings_parse_command_line_arguments(rdpSettings* settings, } free(ptr.p); } - CommandLineSwitchCase(arg, "tune") { size_t x, count; @@ -3480,7 +3566,6 @@ int freerdp_client_settings_parse_command_line_arguments(rdpSettings* settings, } arg = CommandLineFindArgumentA(largs, "port"); - if (arg->Flags & COMMAND_LINE_ARGUMENT_PRESENT) { LONGLONG val; @@ -3492,14 +3577,18 @@ int freerdp_client_settings_parse_command_line_arguments(rdpSettings* settings, } arg = CommandLineFindArgumentA(largs, "p"); + if (arg->Flags & COMMAND_LINE_ARGUMENT_PRESENT) + { + FillMemory(arg->Value, strlen(arg->Value), '*'); + } + arg = CommandLineFindArgumentA(largs, "pin"); if (arg->Flags & COMMAND_LINE_ARGUMENT_PRESENT) { FillMemory(arg->Value, strlen(arg->Value), '*'); } arg = CommandLineFindArgumentA(largs, "gp"); - if (arg->Flags & COMMAND_LINE_ARGUMENT_PRESENT) { FillMemory(arg->Value, strlen(arg->Value), '*'); diff --git a/client/common/cmdline.h b/client/common/cmdline.h index 0c1705f38..44621efe1 100644 --- a/client/common/cmdline.h +++ b/client/common/cmdline.h @@ -224,6 +224,12 @@ static const COMMAND_LINE_ARGUMENT_A global_cmd_args[] = { { "kbd-unicode", COMMAND_LINE_VALUE_FLAG, "", NULL, NULL, -1, NULL, "Send unicode symbols, e.g. use the local keyboard map. ATTENTION: Does not work with every " "RDP server!" }, + { "kerberos-start-time", COMMAND_LINE_VALUE_OPTIONAL, "", NULL, NULL, -1, NULL, + "Kerberos Ticket start time" }, + { "kerberos-lifetime", COMMAND_LINE_VALUE_OPTIONAL, "", NULL, NULL, -1, NULL, + "Kerberos Ticket lifetime" }, + { "kerberos-renewable-lifetime", COMMAND_LINE_VALUE_OPTIONAL, "", NULL, + NULL, -1, NULL, "Kerberos Ticket renewable lifetime" }, { "load-balance-info", COMMAND_LINE_VALUE_REQUIRED, "", NULL, NULL, -1, NULL, "Load balance info" }, { "log-filters", COMMAND_LINE_VALUE_REQUIRED, ":[,:[,...]]", NULL, NULL, @@ -239,6 +245,8 @@ static const COMMAND_LINE_ARGUMENT_A global_cmd_args[] = { { "microphone", COMMAND_LINE_VALUE_OPTIONAL, "[sys:,][dev:,][format:,][rate:,][channel:]", NULL, NULL, -1, "mic", "Audio input (microphone)" }, + { "smartcard-list", COMMAND_LINE_VALUE_FLAG | COMMAND_LINE_PRINT, NULL, NULL, NULL, -1, NULL, + "List smartcard informations" }, { "monitor-list", COMMAND_LINE_VALUE_FLAG | COMMAND_LINE_PRINT, NULL, NULL, NULL, -1, NULL, "List detected monitors" }, { "monitors", COMMAND_LINE_VALUE_REQUIRED, "[,[,...]]", NULL, NULL, -1, NULL, @@ -278,8 +286,19 @@ static const COMMAND_LINE_ARGUMENT_A global_cmd_args[] = { "Use smart card authentication with password as smart card PIN" }, { "pcb", COMMAND_LINE_VALUE_REQUIRED, "", NULL, NULL, -1, NULL, "Preconnection Blob" }, { "pcid", COMMAND_LINE_VALUE_REQUIRED, "", NULL, NULL, -1, NULL, "Preconnection Id" }, + { "card", COMMAND_LINE_VALUE_OPTIONAL, "", NULL, NULL, -1, NULL, "Card name" }, + { "csp", COMMAND_LINE_VALUE_OPTIONAL, "", NULL, NULL, -1, NULL, "CSP Name" }, + { "reader", COMMAND_LINE_VALUE_OPTIONAL, "", NULL, NULL, -1, NULL, + "Card reader name" }, + { "container", COMMAND_LINE_VALUE_OPTIONAL, "", NULL, NULL, -1, NULL, + "Container name" }, + { "pin", COMMAND_LINE_VALUE_REQUIRED, "", NULL, NULL, -1, NULL, "PIN code" }, { "pheight", COMMAND_LINE_VALUE_REQUIRED, "", NULL, NULL, -1, NULL, "Physical height of display (in millimeters)" }, + { "pkcs11-module", COMMAND_LINE_VALUE_OPTIONAL, "", NULL, NULL, -1, NULL, + "Module PKCS11" }, + { "pkinit-anchors", COMMAND_LINE_VALUE_OPTIONAL, "", NULL, NULL, -1, NULL, + "PKINIT anchors" }, { "play-rfx", COMMAND_LINE_VALUE_REQUIRED, "", NULL, NULL, -1, NULL, "Replay rfx pcap file" }, { "port", COMMAND_LINE_VALUE_REQUIRED, "", NULL, NULL, -1, NULL, "Server port" }, @@ -339,9 +358,7 @@ static const COMMAND_LINE_ARGUMENT_A global_cmd_args[] = { { "smartcard", COMMAND_LINE_VALUE_OPTIONAL, "[,...]", NULL, NULL, -1, NULL, "Redirect the smartcard devices containing any of the in their names." }, { "smartcard-logon", COMMAND_LINE_VALUE_OPTIONAL, "[cert:,key:,pin:]", NULL, - NULL, -1, NULL, - "Activates Smartcard (optional certificate) Logon authentication. (EXPERIMENTAL: NLA not " - "supported)" }, + NULL, -1, NULL, "Activates Smartcard (optional certificate) Logon authentication." }, { "sound", COMMAND_LINE_VALUE_OPTIONAL, "[sys:,][dev:,][format:,][rate:,][channel:,][latency:<" "latency>,][quality:]", diff --git a/include/freerdp/crypto/crypto.h b/include/freerdp/crypto/crypto.h index d2894c750..08dd0b5f9 100644 --- a/include/freerdp/crypto/crypto.h +++ b/include/freerdp/crypto/crypto.h @@ -54,6 +54,7 @@ extern "C" typedef struct crypto_cert_struct* CryptoCert; FREERDP_API CryptoCert crypto_cert_read(const BYTE* data, UINT32 length); + FREERDP_API CryptoCert crypto_cert_pem_read(const char* data); FREERDP_API BYTE* crypto_cert_hash(X509* xcert, const char* hash, UINT32* length); FREERDP_API char* crypto_cert_fingerprint_by_hash(X509* xcert, const char* hash); FREERDP_API char* crypto_cert_fingerprint_by_hash_ex(X509* xcert, const char* hash, diff --git a/include/freerdp/freerdp.h b/include/freerdp/freerdp.h index 0deb3dea8..5271cd5f2 100644 --- a/include/freerdp/freerdp.h +++ b/include/freerdp/freerdp.h @@ -104,7 +104,8 @@ extern "C" AUTH_RDP, GW_AUTH_HTTP, GW_AUTH_RDG, - GW_AUTH_RPC + GW_AUTH_RPC, + AUTH_SMARTCARD_PIN } rdp_auth_reason; typedef BOOL (*pContextNew)(freerdp* instance, rdpContext* context); diff --git a/include/freerdp/settings.h b/include/freerdp/settings.h index 4c9c64137..bd181bafa 100644 --- a/include/freerdp/settings.h +++ b/include/freerdp/settings.h @@ -663,8 +663,22 @@ typedef struct #define FreeRDP_SmartcardPrivateKey (1286) #define FreeRDP_SmartcardPin (1287) #define FreeRDP_SmartcardEmulation (1288) +#define FreeRDP_SmartcardCertificateFile (1289) +#define FreeRDP_SmartcardPrivateKeyFile (1290) +#define FreeRDP_Pkcs11Module (1291) +#define FreeRDP_Pin (1292) +#define FreeRDP_KeySpec (1293) +#define FreeRDP_CardName (1294) +#define FreeRDP_ReaderName (1295) +#define FreeRDP_ContainerName (1296) +#define FreeRDP_CspName (1297) +#define FreeRDP_PkinitAnchors (1298) +#define FreeRDP_ListSmartcards (1299) #define FreeRDP_KerberosKdc (1344) #define FreeRDP_KerberosRealm (1345) +#define FreeRDP_KerberosStartTime (1346) +#define FreeRDP_KerberosLifeTime (1347) +#define FreeRDP_KerberosRenewableLifeTime (1348) #define FreeRDP_IgnoreCertificate (1408) #define FreeRDP_CertificateName (1409) #define FreeRDP_CertificateFile (1410) @@ -1143,19 +1157,33 @@ struct rdp_settings ALIGN64 UINT32 Password51Length; /* 1281 */ ALIGN64 BOOL SmartcardLogon; /* 1282 */ ALIGN64 BOOL PromptForCredentials; /* 1283 */ + UINT64 padding1284[1285 - 1284]; /* 1284 */ /* Settings used for smartcard emulation */ - UINT64 padding1284[1285 - 1284]; /* 1284 */ - ALIGN64 char* SmartcardCertificate; /* 1285 */ - ALIGN64 char* SmartcardPrivateKey; /* 1286 */ - ALIGN64 char* SmartcardPin; /* 1287 */ - ALIGN64 BOOL SmartcardEmulation; /* 1288 */ - UINT64 padding1344[1344 - 1289]; /* 1289 */ + ALIGN64 char* SmartcardCertificate; /* 1285 */ + ALIGN64 char* SmartcardPrivateKey; /* 1286 */ + ALIGN64 char* SmartcardPin; /* 1287 */ + ALIGN64 BOOL SmartcardEmulation; /* 1288 */ + ALIGN64 char* SmartcardCertificateFile; /* 1289 */ + ALIGN64 char* SmartcardPrivateKeyFile; /* 1290 */ + ALIGN64 char* Pkcs11Module; /* 1291 */ + ALIGN64 char* Pin; /* 1292 */ + ALIGN64 UINT32 KeySpec; /* 1293 */ + ALIGN64 char* CardName; /* 1294 */ + ALIGN64 char* ReaderName; /* 1295 */ + ALIGN64 char* ContainerName; /* 1296 */ + ALIGN64 char* CspName; /* 1297 */ + ALIGN64 char* PkinitAnchors; /* 1298 */ + ALIGN64 BOOL ListSmartcards; /* 1299 */ + UINT64 padding1344[1344 - 1300]; /* 1300 */ /* Kerberos Authentication */ - ALIGN64 char* KerberosKdc; /* 1344 */ - ALIGN64 char* KerberosRealm; /* 1345 */ - UINT64 padding1408[1408 - 1346]; /* 1346 */ + ALIGN64 char* KerberosKdc; /* 1344 */ + ALIGN64 char* KerberosRealm; /* 1345 */ + ALIGN64 char* KerberosStartTime; /* 1346 */ + ALIGN64 char* KerberosLifeTime; /* 1347 */ + ALIGN64 char* KerberosRenewableLifeTime; /* 1348 */ + UINT64 padding1408[1408 - 1349]; /* 1349 */ /* Server Certificate */ ALIGN64 BOOL IgnoreCertificate; /* 1408 */ diff --git a/include/freerdp/utils/smartcard_cli.h b/include/freerdp/utils/smartcard_cli.h new file mode 100644 index 000000000..ee13a7703 --- /dev/null +++ b/include/freerdp/utils/smartcard_cli.h @@ -0,0 +1,37 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * Smartcard client functions + * + * Copyright 2021 David Fort + * + * 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. + */ + +#ifndef UTILS_SMARTCARD_CLI_H__ +#define UTILS_SMARTCARD_CLI_H__ + +#include +#include + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + + FREERDP_API BOOL freerdp_smartcard_list(rdpSettings* settings); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* UTILS_SMARTCARD_CLI_H__ */ diff --git a/libfreerdp/common/settings_getters.c b/libfreerdp/common/settings_getters.c index 13888691c..2a793e323 100644 --- a/libfreerdp/common/settings_getters.c +++ b/libfreerdp/common/settings_getters.c @@ -291,6 +291,9 @@ BOOL freerdp_settings_get_bool(const rdpSettings* settings, size_t id) case FreeRDP_ListMonitors: return settings->ListMonitors; + case FreeRDP_ListSmartcards: + return settings->ListSmartcards; + case FreeRDP_LocalConnection: return settings->LocalConnection; @@ -920,6 +923,10 @@ BOOL freerdp_settings_set_bool(rdpSettings* settings, size_t id, BOOL val) settings->ListMonitors = cnv.c; break; + case FreeRDP_ListSmartcards: + settings->ListSmartcards = cnv.c; + break; + case FreeRDP_LocalConnection: settings->LocalConnection = cnv.c; break; @@ -1502,6 +1509,9 @@ UINT32 freerdp_settings_get_uint32(const rdpSettings* settings, size_t id) case FreeRDP_JpegQuality: return settings->JpegQuality; + case FreeRDP_KeySpec: + return settings->KeySpec; + case FreeRDP_KeyboardCodePage: return settings->KeyboardCodePage; @@ -1910,6 +1920,10 @@ BOOL freerdp_settings_set_uint32(rdpSettings* settings, size_t id, UINT32 val) settings->JpegQuality = cnv.c; break; + case FreeRDP_KeySpec: + settings->KeySpec = cnv.c; + break; + case FreeRDP_KeyboardCodePage: settings->KeyboardCodePage = cnv.c; break; @@ -2340,6 +2354,9 @@ const char* freerdp_settings_get_string(const rdpSettings* settings, size_t id) case FreeRDP_AuthenticationServiceClass: return settings->AuthenticationServiceClass; + case FreeRDP_CardName: + return settings->CardName; + case FreeRDP_CertificateAcceptedFingerprints: return settings->CertificateAcceptedFingerprints; @@ -2373,6 +2390,12 @@ const char* freerdp_settings_get_string(const rdpSettings* settings, size_t id) case FreeRDP_ConnectionFile: return settings->ConnectionFile; + case FreeRDP_ContainerName: + return settings->ContainerName; + + case FreeRDP_CspName: + return settings->CspName; + case FreeRDP_CurrentPath: return settings->CurrentPath; @@ -2415,9 +2438,18 @@ const char* freerdp_settings_get_string(const rdpSettings* settings, size_t id) case FreeRDP_KerberosKdc: return settings->KerberosKdc; + case FreeRDP_KerberosLifeTime: + return settings->KerberosLifeTime; + case FreeRDP_KerberosRealm: return settings->KerberosRealm; + case FreeRDP_KerberosRenewableLifeTime: + return settings->KerberosRenewableLifeTime; + + case FreeRDP_KerberosStartTime: + return settings->KerberosStartTime; + case FreeRDP_KeyboardRemappingList: return settings->KeyboardRemappingList; @@ -2430,6 +2462,15 @@ const char* freerdp_settings_get_string(const rdpSettings* settings, size_t id) case FreeRDP_PasswordHash: return settings->PasswordHash; + case FreeRDP_Pin: + return settings->Pin; + + case FreeRDP_Pkcs11Module: + return settings->Pkcs11Module; + + case FreeRDP_PkinitAnchors: + return settings->PkinitAnchors; + case FreeRDP_PlayRemoteFxFile: return settings->PlayRemoteFxFile; @@ -2460,6 +2501,9 @@ const char* freerdp_settings_get_string(const rdpSettings* settings, size_t id) case FreeRDP_RdpKeyFile: return settings->RdpKeyFile; + case FreeRDP_ReaderName: + return settings->ReaderName; + case FreeRDP_RedirectionAcceptedCert: return settings->RedirectionAcceptedCert; @@ -2517,12 +2561,18 @@ const char* freerdp_settings_get_string(const rdpSettings* settings, size_t id) case FreeRDP_SmartcardCertificate: return settings->SmartcardCertificate; + case FreeRDP_SmartcardCertificateFile: + return settings->SmartcardCertificateFile; + case FreeRDP_SmartcardPin: return settings->SmartcardPin; case FreeRDP_SmartcardPrivateKey: return settings->SmartcardPrivateKey; + case FreeRDP_SmartcardPrivateKeyFile: + return settings->SmartcardPrivateKeyFile; + case FreeRDP_TargetNetAddress: return settings->TargetNetAddress; @@ -2568,6 +2618,9 @@ char* freerdp_settings_get_string_writable(rdpSettings* settings, size_t id) case FreeRDP_AuthenticationServiceClass: return settings->AuthenticationServiceClass; + case FreeRDP_CardName: + return settings->CardName; + case FreeRDP_CertificateAcceptedFingerprints: return settings->CertificateAcceptedFingerprints; @@ -2601,6 +2654,12 @@ char* freerdp_settings_get_string_writable(rdpSettings* settings, size_t id) case FreeRDP_ConnectionFile: return settings->ConnectionFile; + case FreeRDP_ContainerName: + return settings->ContainerName; + + case FreeRDP_CspName: + return settings->CspName; + case FreeRDP_CurrentPath: return settings->CurrentPath; @@ -2643,9 +2702,18 @@ char* freerdp_settings_get_string_writable(rdpSettings* settings, size_t id) case FreeRDP_KerberosKdc: return settings->KerberosKdc; + case FreeRDP_KerberosLifeTime: + return settings->KerberosLifeTime; + case FreeRDP_KerberosRealm: return settings->KerberosRealm; + case FreeRDP_KerberosRenewableLifeTime: + return settings->KerberosRenewableLifeTime; + + case FreeRDP_KerberosStartTime: + return settings->KerberosStartTime; + case FreeRDP_KeyboardRemappingList: return settings->KeyboardRemappingList; @@ -2658,6 +2726,15 @@ char* freerdp_settings_get_string_writable(rdpSettings* settings, size_t id) case FreeRDP_PasswordHash: return settings->PasswordHash; + case FreeRDP_Pin: + return settings->Pin; + + case FreeRDP_Pkcs11Module: + return settings->Pkcs11Module; + + case FreeRDP_PkinitAnchors: + return settings->PkinitAnchors; + case FreeRDP_PlayRemoteFxFile: return settings->PlayRemoteFxFile; @@ -2688,6 +2765,9 @@ char* freerdp_settings_get_string_writable(rdpSettings* settings, size_t id) case FreeRDP_RdpKeyFile: return settings->RdpKeyFile; + case FreeRDP_ReaderName: + return settings->ReaderName; + case FreeRDP_RedirectionAcceptedCert: return settings->RedirectionAcceptedCert; @@ -2745,9 +2825,18 @@ char* freerdp_settings_get_string_writable(rdpSettings* settings, size_t id) case FreeRDP_SmartcardCertificate: return settings->SmartcardCertificate; + case FreeRDP_SmartcardCertificateFile: + return settings->SmartcardCertificateFile; + + case FreeRDP_SmartcardPin: + return settings->SmartcardPin; + case FreeRDP_SmartcardPrivateKey: return settings->SmartcardPrivateKey; + case FreeRDP_SmartcardPrivateKeyFile: + return settings->SmartcardPrivateKeyFile; + case FreeRDP_TargetNetAddress: return settings->TargetNetAddress; @@ -2803,6 +2892,9 @@ BOOL freerdp_settings_set_string_(rdpSettings* settings, size_t id, const char* case FreeRDP_AuthenticationServiceClass: return update_string(&settings->AuthenticationServiceClass, cnv.cc, len, cleanup); + case FreeRDP_CardName: + return update_string(&settings->CardName, cnv.cc, len, cleanup); + case FreeRDP_CertificateAcceptedFingerprints: return update_string(&settings->CertificateAcceptedFingerprints, cnv.cc, len, cleanup); @@ -2836,6 +2928,12 @@ BOOL freerdp_settings_set_string_(rdpSettings* settings, size_t id, const char* case FreeRDP_ConnectionFile: return update_string(&settings->ConnectionFile, cnv.cc, len, cleanup); + case FreeRDP_ContainerName: + return update_string(&settings->ContainerName, cnv.cc, len, cleanup); + + case FreeRDP_CspName: + return update_string(&settings->CspName, cnv.cc, len, cleanup); + case FreeRDP_CurrentPath: return update_string(&settings->CurrentPath, cnv.cc, len, cleanup); @@ -2878,9 +2976,18 @@ BOOL freerdp_settings_set_string_(rdpSettings* settings, size_t id, const char* case FreeRDP_KerberosKdc: return update_string(&settings->KerberosKdc, cnv.cc, len, cleanup); + case FreeRDP_KerberosLifeTime: + return update_string(&settings->KerberosLifeTime, cnv.cc, len, cleanup); + case FreeRDP_KerberosRealm: return update_string(&settings->KerberosRealm, cnv.cc, len, cleanup); + case FreeRDP_KerberosRenewableLifeTime: + return update_string(&settings->KerberosRenewableLifeTime, cnv.cc, len, cleanup); + + case FreeRDP_KerberosStartTime: + return update_string(&settings->KerberosStartTime, cnv.cc, len, cleanup); + case FreeRDP_KeyboardRemappingList: return update_string(&settings->KeyboardRemappingList, cnv.cc, len, cleanup); @@ -2893,6 +3000,15 @@ BOOL freerdp_settings_set_string_(rdpSettings* settings, size_t id, const char* case FreeRDP_PasswordHash: return update_string(&settings->PasswordHash, cnv.cc, len, cleanup); + case FreeRDP_Pin: + return update_string(&settings->Pin, cnv.cc, len, cleanup); + + case FreeRDP_Pkcs11Module: + return update_string(&settings->Pkcs11Module, cnv.cc, len, cleanup); + + case FreeRDP_PkinitAnchors: + return update_string(&settings->PkinitAnchors, cnv.cc, len, cleanup); + case FreeRDP_PlayRemoteFxFile: return update_string(&settings->PlayRemoteFxFile, cnv.cc, len, cleanup); @@ -2923,6 +3039,9 @@ BOOL freerdp_settings_set_string_(rdpSettings* settings, size_t id, const char* case FreeRDP_RdpKeyFile: return update_string(&settings->RdpKeyFile, cnv.cc, len, cleanup); + case FreeRDP_ReaderName: + return update_string(&settings->ReaderName, cnv.cc, len, cleanup); + case FreeRDP_RedirectionAcceptedCert: return update_string(&settings->RedirectionAcceptedCert, cnv.cc, len, cleanup); @@ -2980,12 +3099,18 @@ BOOL freerdp_settings_set_string_(rdpSettings* settings, size_t id, const char* case FreeRDP_SmartcardCertificate: return update_string(&settings->SmartcardCertificate, cnv.cc, len, cleanup); + case FreeRDP_SmartcardCertificateFile: + return update_string(&settings->SmartcardCertificateFile, cnv.cc, len, cleanup); + case FreeRDP_SmartcardPin: return update_string(&settings->SmartcardPin, cnv.cc, len, cleanup); case FreeRDP_SmartcardPrivateKey: return update_string(&settings->SmartcardPrivateKey, cnv.cc, len, cleanup); + case FreeRDP_SmartcardPrivateKeyFile: + return update_string(&settings->SmartcardPrivateKeyFile, cnv.cc, len, cleanup); + case FreeRDP_TargetNetAddress: return update_string(&settings->TargetNetAddress, cnv.cc, len, cleanup); diff --git a/libfreerdp/common/settings_str.c b/libfreerdp/common/settings_str.c index 91f7910ec..e3e5fe271 100644 --- a/libfreerdp/common/settings_str.c +++ b/libfreerdp/common/settings_str.c @@ -100,6 +100,7 @@ static const struct settings_str_entry settings_map[] = { { FreeRDP_IgnoreCertificate, 0, "FreeRDP_IgnoreCertificate" }, { FreeRDP_JpegCodec, 0, "FreeRDP_JpegCodec" }, { FreeRDP_ListMonitors, 0, "FreeRDP_ListMonitors" }, + { FreeRDP_ListSmartcards, 0, "FreeRDP_ListSmartcards" }, { FreeRDP_LocalConnection, 0, "FreeRDP_LocalConnection" }, { FreeRDP_LogonErrors, 0, "FreeRDP_LogonErrors" }, { FreeRDP_LogonNotify, 0, "FreeRDP_LogonNotify" }, @@ -235,6 +236,7 @@ static const struct settings_str_entry settings_map[] = { { FreeRDP_GlyphSupportLevel, 3, "FreeRDP_GlyphSupportLevel" }, { FreeRDP_JpegCodecId, 3, "FreeRDP_JpegCodecId" }, { FreeRDP_JpegQuality, 3, "FreeRDP_JpegQuality" }, + { FreeRDP_KeySpec, 3, "FreeRDP_KeySpec" }, { FreeRDP_KeyboardCodePage, 3, "FreeRDP_KeyboardCodePage" }, { FreeRDP_KeyboardFunctionKey, 3, "FreeRDP_KeyboardFunctionKey" }, { FreeRDP_KeyboardHook, 3, "FreeRDP_KeyboardHook" }, @@ -313,6 +315,7 @@ static const struct settings_str_entry settings_map[] = { { FreeRDP_AlternateShell, 7, "FreeRDP_AlternateShell" }, { FreeRDP_AssistanceFile, 7, "FreeRDP_AssistanceFile" }, { FreeRDP_AuthenticationServiceClass, 7, "FreeRDP_AuthenticationServiceClass" }, + { FreeRDP_CardName, 7, "FreeRDP_CardName" }, { FreeRDP_CertificateAcceptedFingerprints, 7, "FreeRDP_CertificateAcceptedFingerprints" }, { FreeRDP_CertificateContent, 7, "FreeRDP_CertificateContent" }, { FreeRDP_CertificateFile, 7, "FreeRDP_CertificateFile" }, @@ -324,6 +327,8 @@ static const struct settings_str_entry settings_map[] = { { FreeRDP_ComputerName, 7, "FreeRDP_ComputerName" }, { FreeRDP_ConfigPath, 7, "FreeRDP_ConfigPath" }, { FreeRDP_ConnectionFile, 7, "FreeRDP_ConnectionFile" }, + { FreeRDP_ContainerName, 7, "FreeRDP_ContainerName" }, + { FreeRDP_CspName, 7, "FreeRDP_CspName" }, { FreeRDP_CurrentPath, 7, "FreeRDP_CurrentPath" }, { FreeRDP_Domain, 7, "FreeRDP_Domain" }, { FreeRDP_DrivesToRedirect, 7, "FreeRDP_DrivesToRedirect" }, @@ -338,11 +343,17 @@ static const struct settings_str_entry settings_map[] = { { FreeRDP_HomePath, 7, "FreeRDP_HomePath" }, { FreeRDP_ImeFileName, 7, "FreeRDP_ImeFileName" }, { FreeRDP_KerberosKdc, 7, "FreeRDP_KerberosKdc" }, + { FreeRDP_KerberosLifeTime, 7, "FreeRDP_KerberosLifeTime" }, { FreeRDP_KerberosRealm, 7, "FreeRDP_KerberosRealm" }, + { FreeRDP_KerberosRenewableLifeTime, 7, "FreeRDP_KerberosRenewableLifeTime" }, + { FreeRDP_KerberosStartTime, 7, "FreeRDP_KerberosStartTime" }, { FreeRDP_KeyboardRemappingList, 7, "FreeRDP_KeyboardRemappingList" }, { FreeRDP_NtlmSamFile, 7, "FreeRDP_NtlmSamFile" }, { FreeRDP_Password, 7, "FreeRDP_Password" }, { FreeRDP_PasswordHash, 7, "FreeRDP_PasswordHash" }, + { FreeRDP_Pin, 7, "FreeRDP_Pin" }, + { FreeRDP_Pkcs11Module, 7, "FreeRDP_Pkcs11Module" }, + { FreeRDP_PkinitAnchors, 7, "FreeRDP_PkinitAnchors" }, { FreeRDP_PlayRemoteFxFile, 7, "FreeRDP_PlayRemoteFxFile" }, { FreeRDP_PreconnectionBlob, 7, "FreeRDP_PreconnectionBlob" }, { FreeRDP_PrivateKeyContent, 7, "FreeRDP_PrivateKeyContent" }, @@ -353,6 +364,7 @@ static const struct settings_str_entry settings_map[] = { { FreeRDP_RDP2TCPArgs, 7, "FreeRDP_RDP2TCPArgs" }, { FreeRDP_RdpKeyContent, 7, "FreeRDP_RdpKeyContent" }, { FreeRDP_RdpKeyFile, 7, "FreeRDP_RdpKeyFile" }, + { FreeRDP_ReaderName, 7, "FreeRDP_ReaderName" }, { FreeRDP_RedirectionAcceptedCert, 7, "FreeRDP_RedirectionAcceptedCert" }, { FreeRDP_RedirectionDomain, 7, "FreeRDP_RedirectionDomain" }, { FreeRDP_RedirectionTargetFQDN, 7, "FreeRDP_RedirectionTargetFQDN" }, @@ -372,8 +384,10 @@ static const struct settings_str_entry settings_map[] = { { FreeRDP_ServerHostname, 7, "FreeRDP_ServerHostname" }, { FreeRDP_ShellWorkingDirectory, 7, "FreeRDP_ShellWorkingDirectory" }, { FreeRDP_SmartcardCertificate, 7, "FreeRDP_SmartcardCertificate" }, + { FreeRDP_SmartcardCertificateFile, 7, "FreeRDP_SmartcardCertificateFile" }, { FreeRDP_SmartcardPin, 7, "FreeRDP_SmartcardPin" }, { FreeRDP_SmartcardPrivateKey, 7, "FreeRDP_SmartcardPrivateKey" }, + { FreeRDP_SmartcardPrivateKeyFile, 7, "FreeRDP_SmartcardPrivateKeyFile" }, { FreeRDP_TargetNetAddress, 7, "FreeRDP_TargetNetAddress" }, { FreeRDP_TransportDumpFile, 7, "FreeRDP_TransportDumpFile" }, { FreeRDP_Username, 7, "FreeRDP_Username" }, diff --git a/libfreerdp/core/CMakeLists.txt b/libfreerdp/core/CMakeLists.txt index 66e77a104..52e88eecb 100644 --- a/libfreerdp/core/CMakeLists.txt +++ b/libfreerdp/core/CMakeLists.txt @@ -63,6 +63,10 @@ set(${MODULE_PREFIX}_SRCS mcs.h nla.c nla.h + smartcardlogon.c + smartcardlogon.h + tscredentials.c + tscredentials.h nego.c nego.h info.c diff --git a/libfreerdp/core/nla.c b/libfreerdp/core/nla.c index de9adba4d..adc1d664f 100644 --- a/libfreerdp/core/nla.c +++ b/libfreerdp/core/nla.c @@ -42,9 +42,13 @@ #include #include #include +#include +#include #include "nla.h" #include "utils.h" +#include "tscredentials.h" +#include "smartcardlogon.h" #define TAG FREERDP_TAG("core.nla") @@ -134,7 +138,11 @@ struct rdp_nla SecBuffer tsCredentials; LPTSTR ServicePrincipalName; + void* identityPtr; SEC_WINNT_AUTH_IDENTITY* identity; + SEC_WINNT_AUTH_IDENTITY_EXW identityEx; + SEC_WINNT_AUTH_IDENTITY_WINPRA identityWinPr; + SEC_WINPR_KERBEROS_SETTINGS kerberosSettings; PSecurityFunctionTable table; SecPkgContext_Sizes ContextSizes; }; @@ -386,6 +394,7 @@ static SECURITY_STATUS nla_decrypt(rdpNla* nla, SecBuffer* buffer, size_t header Buffers[0].BufferType = SECBUFFER_TOKEN; /* Signature */ Buffers[0].cbBuffer = (ULONG)headerLength; Buffers[0].pvBuffer = &((BYTE*)buffer->pvBuffer)[0]; + Buffers[1].BufferType = SECBUFFER_DATA; /* Encrypted TLS Public Key */ Buffers[1].cbBuffer = (ULONG)(buffer->cbBuffer - headerLength); Buffers[1].pvBuffer = &((BYTE*)buffer->pvBuffer)[headerLength]; @@ -430,6 +439,7 @@ static SECURITY_STATUS nla_encrypt(rdpNla* nla, SecBuffer* buffer, size_t header Buffers[0].BufferType = SECBUFFER_TOKEN; /* Signature */ Buffers[0].cbBuffer = (ULONG)headerLength; Buffers[0].pvBuffer = &((BYTE*)buffer->pvBuffer)[0]; + Buffers[1].BufferType = SECBUFFER_DATA; Buffers[1].cbBuffer = (ULONG)(buffer->cbBuffer - headerLength); Buffers[1].pvBuffer = &((BYTE*)buffer->pvBuffer)[headerLength]; @@ -526,29 +536,218 @@ void nla_identity_free(SEC_WINNT_AUTH_IDENTITY* identity) free(identity); } -/** - * Initialize NTLM/Kerberos SSP authentication module (client). - * @param credssp - */ - -static int nla_client_init(rdpNla* nla) +static BOOL nla_adjust_settings_from_smartcard(rdpNla* nla) { - char* spn; - size_t length; - rdpTls* tls = NULL; - BOOL PromptPassword = FALSE; - freerdp* instance = nla->instance; +#define MAX_SC_CERTS 64 + SmartcardCert certs[MAX_SC_CERTS]; + DWORD count, i = 0; rdpSettings* settings = nla->settings; + SEC_WINPR_KERBEROS_SETTINGS* kerbSettings = &nla->kerberosSettings; + BOOL ret = FALSE; + + if (!settings->CspName) + { + settings->CspName = strdup(MS_SCARD_PROV_A); + if (!settings->CspName) + { + WLog_ERR(TAG, "unable to set CSP name"); + return FALSE; + } + } + + if (settings->PkinitAnchors) + { + kerbSettings->pkinitX509Anchors = _strdup(settings->PkinitAnchors); + if (!kerbSettings->pkinitX509Anchors) + { + WLog_ERR(TAG, "error setting X509 anchors"); + return FALSE; + } + } + + if (!smartcard_enumerateCerts(settings, certs, MAX_SC_CERTS, &count)) + { + WLog_ERR(TAG, "unable to list smartcard certificates"); + return FALSE; + } + + if (!count) + { + WLog_ERR(TAG, "no smartcard certificates found"); + return FALSE; + } + + if (count != 1) + goto setup_pin; + + /* + * just one result let's try to fill missing parameters + */ + if (!settings->Username && certs[0].userHint) + { + settings->Username = strdup(certs[0].userHint); + if (!settings->Username) + { + WLog_ERR(TAG, "unable to copy certificate username"); + goto out; + } + } + + if (!settings->Domain && certs[0].domainHint) + { + settings->Domain = strdup(certs[0].domainHint); + if (!settings->Domain) + { + WLog_ERR(TAG, "unable to copy certificate domain"); + goto out; + } + } + + if (!settings->ReaderName && certs[0].reader) + { + if (ConvertFromUnicode(CP_UTF8, 0, certs[0].reader, -1, &settings->ReaderName, 0, NULL, + NULL) < 0) + { + WLog_ERR(TAG, "unable to copy reader name"); + goto out; + } + } + + if (!settings->ContainerName && certs[0].containerName) + { + settings->ContainerName = strdup(certs[0].containerName); + if (!settings->ContainerName) + { + WLog_ERR(TAG, "unable to copy container name"); + goto out; + } + } + + memcpy(nla->kerberosSettings.certSha1, certs[0].sha1Hash, + sizeof(nla->kerberosSettings.certSha1)); + +#ifndef _WIN32 + if (certs[0].pkinitArgs) + { + kerbSettings->pkinitX509Identity = strdup(certs[0].pkinitArgs); + if (!kerbSettings->pkinitX509Identity) + { + WLog_ERR(TAG, "unable to copy pkinitArgs"); + goto out; + } + } +#endif /* _WIN32 */ + +setup_pin: + if (!settings->Pin) + { + if (settings->SmartcardEmulation) + { + const char* dupSrc = settings->SmartcardPin; + if (!dupSrc) + dupSrc = ""; + + settings->Pin = strdup(dupSrc); + if (!settings->Pin) + { + WLog_ERR(TAG, "error setting pin"); + return FALSE; + } + } + else + { + freerdp* instance = nla->instance; + if (!instance->AuthenticateEx) + { + WLog_ERR(TAG, "no pin configured and no instance->AuthenticateEx callback"); + return TRUE; + } + + if (!instance->AuthenticateEx(instance, &settings->Username, &settings->Pin, + &settings->Domain, AUTH_SMARTCARD_PIN)) + { + WLog_ERR(TAG, "no pin code entered"); + return TRUE; + } + } + } + + ret = TRUE; +out: + for (i = 0; i < count; i++) + smartcardCert_Free(&certs[i]); + + return ret; +} + +static BOOL nla_client_setup_identity(rdpNla* nla) +{ WINPR_SAM* sam; WINPR_SAM_ENTRY* entry; - nla_set_state(nla, NLA_STATE_INITIAL); + BOOL PromptPassword = FALSE; + rdpSettings* settings = nla->settings; + freerdp* instance = nla->instance; - if (settings->RestrictedAdminModeRequired) - settings->DisableCredentialsDelegation = TRUE; + if (settings->SmartcardLogon) + { +#ifdef _WIN32 + { + SEC_WINNT_AUTH_IDENTITY_EXW* identityEx; + CERT_CREDENTIAL_INFO certInfo = { sizeof(CERT_CREDENTIAL_INFO), { 0 } }; + LPWSTR marshalledCredentials; - if (utils_str_is_empty(settings->Username) || - (utils_str_is_empty(settings->Password) && - utils_str_is_empty((const char*)settings->RedirectionPassword))) + identityEx = &nla->identityEx; + memcpy(certInfo.rgbHashOfCert, nla->kerberosSettings.certSha1, + sizeof(certInfo.rgbHashOfCert)); + + if (!CredMarshalCredentialW(CertCredential, &certInfo, &marshalledCredentials)) + { + WLog_ERR(TAG, "error marshalling cert credentials"); + return FALSE; + } + + identityEx->Version = SEC_WINNT_AUTH_IDENTITY_VERSION; + identityEx->Length = sizeof(*identityEx); + identityEx->User = (PUSHORT)marshalledCredentials; + identityEx->UserLength = _wcslen(marshalledCredentials); + if (ConvertToUnicode(CP_UTF8, 0, settings->Pin, -1, &identityEx->Password, 0) <= 0) + return FALSE; + identityEx->PasswordLength = strlen(settings->Pin); + identityEx->Domain = NULL; + identityEx->DomainLength = 0; + identityEx->Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE; + identityEx->PackageList = NULL; + identityEx->PackageListLength = 0; + + nla->identityPtr = identityEx; + } +#else + { + SEC_WINNT_AUTH_IDENTITY_EXA* identityEx = &nla->identityWinPr.identityEx; + + identityEx->Version = SEC_WINNT_AUTH_IDENTITY_VERSION; + identityEx->Length = sizeof(nla->identityWinPr); + identityEx->User = (BYTE*)settings->Username; + identityEx->UserLength = strlen(settings->Username); + identityEx->Domain = (BYTE*)settings->Domain; + identityEx->DomainLength = strlen(settings->Domain); + identityEx->Password = (BYTE*)strdup(settings->Pin); + identityEx->PasswordLength = strlen(settings->Pin); + identityEx->Flags = SEC_WINNT_AUTH_IDENTITY_ANSI; + identityEx->PackageList = NULL; + identityEx->PackageListLength = 0; + + nla->identityPtr = &nla->identityWinPr; + } /* smartcard logon */ +#endif /* _WIN32 */ + + return TRUE; + } + + /* */ + if ((utils_str_is_empty(settings->Username) || + (utils_str_is_empty(settings->Password) && + utils_str_is_empty((const char*)settings->RedirectionPassword)))) { PromptPassword = TRUE; } @@ -556,11 +755,9 @@ static int nla_client_init(rdpNla* nla) if (PromptPassword && !utils_str_is_empty(settings->Username)) { sam = SamOpen(NULL, TRUE); - if (sam) { entry = SamLookupUserA(sam, settings->Username, strlen(settings->Username), NULL, 0); - if (entry) { /** @@ -576,7 +773,6 @@ static int nla_client_init(rdpNla* nla) } #ifndef _WIN32 - if (PromptPassword) { if (settings->RestrictedAdminModeRequired) @@ -585,7 +781,6 @@ static int nla_client_init(rdpNla* nla) PromptPassword = FALSE; } } - #endif if (PromptPassword) @@ -611,6 +806,8 @@ static int nla_client_init(rdpNla* nla) } else { + BOOL usePassword = TRUE; + if (settings->RedirectionPassword && (settings->RedirectionPasswordLength > 0)) { if (sspi_SetAuthIdentityWithUnicodePassword( @@ -619,40 +816,59 @@ static int nla_client_init(rdpNla* nla) settings->RedirectionPasswordLength / sizeof(WCHAR) - 1) < 0) return -1; } - else + + if (settings->RestrictedAdminModeRequired) { - BOOL usePassword = TRUE; - - if (settings->RestrictedAdminModeRequired) - { - if (settings->PasswordHash) - { - if (strlen(settings->PasswordHash) == 32) - { - if (sspi_SetAuthIdentity(nla->identity, settings->Username, - settings->Domain, settings->PasswordHash) < 0) - return -1; - - /** - * Increase password hash length by LB_PASSWORD_MAX_LENGTH to obtain a - * length exceeding the maximum (LB_PASSWORD_MAX_LENGTH) and use it this for - * hash identification in WinPR. - */ - nla->identity->PasswordLength += LB_PASSWORD_MAX_LENGTH; - usePassword = FALSE; - } - } - } - - if (usePassword) + if (settings->PasswordHash && strlen(settings->PasswordHash) == 32) { if (sspi_SetAuthIdentity(nla->identity, settings->Username, settings->Domain, - settings->Password) < 0) + settings->PasswordHash) < 0) return -1; + + /** + * Increase password hash length by LB_PASSWORD_MAX_LENGTH to obtain a + * length exceeding the maximum (LB_PASSWORD_MAX_LENGTH) and use it this for + * hash identification in WinPR. + */ + nla->identity->PasswordLength += LB_PASSWORD_MAX_LENGTH; + usePassword = FALSE; } } + + if (usePassword) + { + if (sspi_SetAuthIdentity(nla->identity, settings->Username, settings->Domain, + settings->Password) < 0) + return -1; + } } + return TRUE; +} + +/** + * Initialize NTLM/Kerberos SSP authentication module (client). + * @param credssp + */ + +static int nla_client_init(rdpNla* nla) +{ + char* spn; + size_t length; + rdpTls* tls = NULL; + rdpSettings* settings = nla->settings; + + nla_set_state(nla, NLA_STATE_INITIAL); + + if (settings->RestrictedAdminModeRequired) + settings->DisableCredentialsDelegation = TRUE; + + if (settings->SmartcardLogon && !nla_adjust_settings_from_smartcard(nla)) + return -1; + + if (!nla_client_setup_identity(nla)) + return -1; + tls = transport_get_tls(nla->transport); if (!tls) @@ -689,7 +905,7 @@ static int nla_client_init(rdpNla* nla) WLog_DBG(TAG, "%s %" PRIu32 " : packageName=%ls ; cbMaxToken=%d", __FUNCTION__, __LINE__, nla->packageName, nla->cbMaxToken); nla->status = nla->table->AcquireCredentialsHandle(NULL, NLA_PKG_NAME, SECPKG_CRED_OUTBOUND, - NULL, nla->identity, NULL, NULL, + NULL, nla->identityPtr, NULL, NULL, &nla->credentials, &nla->expiration); if (nla->status != SEC_E_OK) @@ -892,7 +1108,6 @@ static int nla_client_recv_pub_key_auth(rdpNla* nla) /* Send encrypted credentials */ nla->status = nla_encrypt_ts_credentials(nla); - if (nla->status != SEC_E_OK) return -1; @@ -1576,30 +1791,6 @@ fail: return status; } -static size_t nla_sizeof_ts_password_creds(rdpNla* nla) -{ - size_t length = 0; - - if (nla->identity) - { - length += ber_sizeof_sequence_octet_string(nla->identity->DomainLength * 2); - length += ber_sizeof_sequence_octet_string(nla->identity->UserLength * 2); - length += ber_sizeof_sequence_octet_string(nla->identity->PasswordLength * 2); - } - - return length; -} - -static size_t nla_sizeof_ts_credentials(rdpNla* nla) -{ - size_t size = 0; - size += ber_sizeof_integer(1); - size += ber_sizeof_contextual_tag(ber_sizeof_integer(1)); - size += - ber_sizeof_sequence_octet_string(ber_sizeof_sequence(nla_sizeof_ts_password_creds(nla))); - return size; -} - BOOL nla_read_ts_password_creds(rdpNla* nla, wStream* s) { size_t length; @@ -1691,29 +1882,6 @@ BOOL nla_read_ts_password_creds(rdpNla* nla, wStream* s) return TRUE; } -static size_t nla_write_ts_password_creds(rdpNla* nla, wStream* s) -{ - size_t size = 0; - size_t innerSize = nla_sizeof_ts_password_creds(nla); - /* TSPasswordCreds (SEQUENCE) */ - size += ber_write_sequence_tag(s, innerSize); - - if (nla->identity) - { - /* [0] domainName (OCTET STRING) */ - size += ber_write_sequence_octet_string(s, 0, (BYTE*)nla->identity->Domain, - nla->identity->DomainLength * 2); - /* [1] userName (OCTET STRING) */ - size += ber_write_sequence_octet_string(s, 1, (BYTE*)nla->identity->User, - nla->identity->UserLength * 2); - /* [2] password (OCTET STRING) */ - size += ber_write_sequence_octet_string(s, 2, (BYTE*)nla->identity->Password, - nla->identity->PasswordLength * 2); - } - - return size; -} - static BOOL nla_read_ts_credentials(rdpNla* nla, SecBuffer* data, size_t offset) { wStream* s; @@ -1748,23 +1916,6 @@ fail: return ret; } -static size_t nla_write_ts_credentials(rdpNla* nla, wStream* s) -{ - size_t size = 0; - size_t passwordSize; - size_t innerSize = nla_sizeof_ts_credentials(nla); - /* TSCredentials (SEQUENCE) */ - size += ber_write_sequence_tag(s, innerSize); - /* [0] credType (INTEGER) */ - size += ber_write_contextual_tag(s, 0, ber_sizeof_integer(1), TRUE); - size += ber_write_integer(s, 1); - /* [1] credentials (OCTET STRING) */ - passwordSize = ber_sizeof_sequence(nla_sizeof_ts_password_creds(nla)); - size += ber_write_contextual_tag(s, 1, ber_sizeof_octet_string(passwordSize), TRUE); - size += ber_write_octet_string_tag(s, passwordSize); - size += nla_write_ts_password_creds(nla, s); - return size; -} /** * Encode TSCredentials structure. @@ -1773,57 +1924,88 @@ static size_t nla_write_ts_credentials(rdpNla* nla, wStream* s) static BOOL nla_encode_ts_credentials(rdpNla* nla) { + wStream* credsContentStream; + wStream staticRetStream; wStream* s; size_t length; - UINT32 DomainLength = 0; - UINT32 UserLength = 0; - UINT32 PasswordLength = 0; + BOOL ret = FALSE; - if (nla->identity) + TSCredentials_t cr = { 0 }; + rdpSettings* settings = nla->settings; + + if (settings->SmartcardLogon) { - /* TSPasswordCreds */ - DomainLength = nla->identity->DomainLength; - UserLength = nla->identity->UserLength; - PasswordLength = nla->identity->PasswordLength; + TSSmartCardCreds_t smartcardCreds = { 0 }; + TSCspDataDetail_t cspData = { 0 }; + + if (settings->SmartcardEmulation) + smartcardCreds.pin = settings->SmartcardPin; + if (!smartcardCreds.pin) + smartcardCreds.pin = settings->Pin ? settings->Pin : ""; + /*smartcardCreds.userHint = settings->UserHint; + smartcardCreds.domainHint = settings->DomainHint;*/ + smartcardCreds.cspData = &cspData; + + cspData.keySpec = settings->KeySpec; + cspData.cspName = settings->CspName; + cspData.readerName = settings->ReaderName; + cspData.cardName = settings->CardName; + cspData.containerName = settings->ContainerName; + + length = ber_sizeof_nla_TSSmartCardCreds(&smartcardCreds); + credsContentStream = Stream_New(NULL, length); + if (!credsContentStream) + return FALSE; + + if (ber_write_nla_TSSmartCardCreds(credsContentStream, &smartcardCreds) == 0) + return FALSE; + + cr.credType = 2; + } + else + { + TSPasswordCreds_t passCreds = { 0 }; + + if (!nla->settings->DisableCredentialsDelegation && nla->identity) + { + passCreds.userNameLen = nla->identity->UserLength * 2; + passCreds.userName = (BYTE*)nla->identity->User; + + passCreds.domainNameLen = nla->identity->DomainLength * 2; + passCreds.domainName = (BYTE*)nla->identity->Domain; + + passCreds.passwordLen = nla->identity->PasswordLength * 2; + passCreds.password = (BYTE*)nla->identity->Password; + } + + length = ber_sizeof_nla_TSPasswordCreds(&passCreds); + credsContentStream = Stream_New(NULL, length); + if (!credsContentStream) + return FALSE; + + ber_write_nla_TSPasswordCreds(credsContentStream, &passCreds); + + cr.credType = 1; } - if (nla->settings->DisableCredentialsDelegation && nla->identity) - { - /* TSPasswordCreds */ - nla->identity->DomainLength = 0; - nla->identity->UserLength = 0; - nla->identity->PasswordLength = 0; - } - - length = ber_sizeof_sequence(nla_sizeof_ts_credentials(nla)); + cr.credentialsLen = length; + cr.credentials = Stream_Buffer(credsContentStream); + length = ber_sizeof_nla_TSCredentials(&cr); if (!nla_sec_buffer_alloc(&nla->tsCredentials, length)) { WLog_ERR(TAG, "sspi_SecBufferAlloc failed!"); - return FALSE; + goto out; } - s = Stream_New((BYTE*)nla->tsCredentials.pvBuffer, length); + s = Stream_StaticInit(&staticRetStream, (BYTE*)nla->tsCredentials.pvBuffer, length); + ber_write_nla_TSCredentials(s, &cr); + ret = TRUE; - if (!s) - { - sspi_SecBufferFree(&nla->tsCredentials); - WLog_ERR(TAG, "Stream_New failed!"); - return FALSE; - } - - nla_write_ts_credentials(nla, s); - - if (nla->settings->DisableCredentialsDelegation && nla->identity) - { - /* TSPasswordCreds */ - nla->identity->DomainLength = DomainLength; - nla->identity->UserLength = UserLength; - nla->identity->PasswordLength = PasswordLength; - } - - Stream_Free(s, FALSE); - return TRUE; + printf("creds=%s\n", winpr_BinToHexString((BYTE*)nla->tsCredentials.pvBuffer, length, TRUE)); +out: + Stream_Free(credsContentStream, TRUE); + return ret; } static SECURITY_STATUS nla_encrypt_ts_credentials(rdpNla* nla) @@ -2289,6 +2471,7 @@ rdpNla* nla_new(freerdp* instance, rdpTransport* transport, rdpSettings* setting return NULL; } + nla->identityPtr = nla->identity; nla->instance = instance; nla->settings = settings; nla->server = settings->ServerMode; @@ -2296,6 +2479,7 @@ rdpNla* nla_new(freerdp* instance, rdpTransport* transport, rdpSettings* setting nla->sendSeqNum = 0; nla->recvSeqNum = 0; nla->version = 6; + nla->identityWinPr.kerberosSettings = &nla->kerberosSettings; SecInvalidateHandle(&nla->context); if (settings->NtlmSamFile) diff --git a/libfreerdp/core/settings.c b/libfreerdp/core/settings.c index cab706d8b..d5bb0233a 100644 --- a/libfreerdp/core/settings.c +++ b/libfreerdp/core/settings.c @@ -391,7 +391,8 @@ rdpSettings* freerdp_settings_new(DWORD flags) !freerdp_settings_set_uint32(settings, FreeRDP_ChannelCount, 0) || !freerdp_settings_set_uint32(settings, FreeRDP_ChannelDefArraySize, 32) || !freerdp_settings_set_bool(settings, FreeRDP_CertificateUseKnownHosts, TRUE) || - !freerdp_settings_set_bool(settings, FreeRDP_CertificateCallbackPreferPEM, FALSE)) + !freerdp_settings_set_bool(settings, FreeRDP_CertificateCallbackPreferPEM, FALSE) || + !freerdp_settings_set_uint32(settings, FreeRDP_KeySpec, AT_KEYEXCHANGE)) goto out_fail; settings->ChannelDefArray = (CHANNEL_DEF*)calloc( diff --git a/libfreerdp/core/smartcardlogon.c b/libfreerdp/core/smartcardlogon.c new file mode 100644 index 000000000..7447cb729 --- /dev/null +++ b/libfreerdp/core/smartcardlogon.c @@ -0,0 +1,404 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * Logging in with smartcards + * + * Copyright 2022 David Fort + * + * 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 +#include +#include + +#include + +#include "smartcardlogon.h" + +#define TAG FREERDP_TAG("smartcardlogon") + +static BOOL getAtr(LPWSTR readerName, BYTE* atr, DWORD* atrLen) +{ + WCHAR atrName[256]; + DWORD cbLength; + DWORD dwShareMode = SCARD_SHARE_SHARED; + DWORD dwPreferredProtocols = SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1; + SCARDHANDLE hCardHandle; + DWORD dwActiveProtocol = 0; + BOOL ret = FALSE; + LONG status = 0; + SCARDCONTEXT scContext; + + status = SCardEstablishContext(SCARD_SCOPE_USER, NULL, NULL, &scContext); + if (status != ERROR_SUCCESS || !scContext) + return FALSE; + + status = SCardConnectW(scContext, readerName, dwShareMode, dwPreferredProtocols, &hCardHandle, + &dwActiveProtocol); + if (status != ERROR_SUCCESS) + goto out_connect; + + *atrLen = 256; + status = SCardGetAttrib(hCardHandle, SCARD_ATTR_ATR_STRING, atr, atrLen); + if (status != ERROR_SUCCESS) + goto out_get_attrib; + + cbLength = 256; + status = SCardListCardsW(scContext, atr, NULL, 0, atrName, &cbLength); + if (status != ERROR_SUCCESS) + goto out_listCards; + + /* WLog_DBG(TAG, "ATR name: %ld -> %S\n", cbLength, atrName); */ + ret = TRUE; +out_listCards: +out_get_attrib: + SCardDisconnect(scContext, SCARD_LEAVE_CARD); +out_connect: + SCardReleaseContext(scContext); + return ret; +} + +void smartcardCert_Free(SmartcardCert* scCert) +{ + free(scCert->reader); + crypto_cert_free(scCert->certificate); + free(scCert->pkinitArgs); + free(scCert->containerName); + free(scCert->upn); + free(scCert->userHint); + free(scCert->domainHint); + free(scCert->subject); + free(scCert->issuer); + + ZeroMemory(scCert, sizeof(*scCert)); +} + +static BOOL treat_sc_cert(SmartcardCert* scCert) +{ + scCert->upn = crypto_cert_get_upn(scCert->certificate->px509); + if (scCert->upn) + { + size_t userLen; + const char* atPos = strchr(scCert->upn, '@'); + + if (!atPos) + { + WLog_ERR(TAG, "invalid UPN, for key %s (no @)", scCert->containerName); + return FALSE; + } + + userLen = (atPos - scCert->upn); + scCert->userHint = malloc(userLen + 1); + scCert->domainHint = strdup(atPos + 1); + + if (!scCert->userHint || !scCert->domainHint) + { + WLog_ERR(TAG, "error allocating userHint or domainHint, for key %s", + scCert->containerName); + return FALSE; + } + + memcpy(scCert->userHint, scCert->upn, userLen); + scCert->userHint[userLen] = 0; + } + + scCert->subject = crypto_cert_subject(scCert->certificate->px509); + scCert->issuer = crypto_cert_issuer(scCert->certificate->px509); + return TRUE; +} + +#ifndef _WIN32 +static BOOL build_pkinit_args(rdpSettings* settings, SmartcardCert* scCert) +{ + /* pkinit args only under windows + * PKCS11:module_name=opensc-pkcs11.so + */ + size_t sz = strlen("PKCS11:module_name=:slotid=XXXXX"); + const char* pkModule = settings->Pkcs11Module ? settings->Pkcs11Module : "opensc-pkcs11.so"; + + sz += strlen(pkModule) + 1; + + scCert->pkinitArgs = malloc(sz); + if (!scCert->pkinitArgs) + return FALSE; + + snprintf(scCert->pkinitArgs, sz, "PKCS11:module_name=%s:slotid=%" PRIu16, pkModule, + (UINT16)scCert->slotId); + + return TRUE; +} +#endif /* _WIN32 */ + +static BOOL smartcard_hw_enumerateCerts(rdpSettings* settings, LPCWSTR csp, const char* reader, + const char* userFilter, SmartcardCert* scCert, DWORD count, + DWORD* retCount) +{ + BOOL ret = FALSE; + LPWSTR scope = NULL; + PVOID enumState = NULL; + NCRYPT_PROV_HANDLE provider; + NCryptKeyName* keyName = NULL; + SECURITY_STATUS status; + + if (reader) + { + int res; + size_t readerSz = strlen(reader); + char* scopeStr = malloc(4 + readerSz + 1 + 1); + if (!scopeStr) + goto out; + + snprintf(scopeStr, readerSz + 5, "\\\\.\\%s\\", reader); + res = ConvertToUnicode(CP_UTF8, 0, scopeStr, -1, &scope, 0); + free(scopeStr); + + if (res <= 0) + goto out; + } + + status = NCryptOpenStorageProvider(&provider, csp, 0); + if (status != ERROR_SUCCESS) + { + WLog_ERR(TAG, "unable to open provider"); + goto out; + } + + *retCount = 0; + while ((*retCount < count) && (status = NCryptEnumKeys(provider, scope, &keyName, &enumState, + NCRYPT_SILENT_FLAG)) == ERROR_SUCCESS) + { + NCRYPT_KEY_HANDLE phKey; + PBYTE certBytes = NULL; + DWORD cbOutput; + + if (ConvertFromUnicode(CP_UTF8, 0, keyName->pszName, -1, &scCert->containerName, 0, NULL, + NULL) <= 0) + continue; + + status = NCryptOpenKey(provider, &phKey, keyName->pszName, keyName->dwLegacyKeySpec, + keyName->dwFlags); + if (status != ERROR_SUCCESS) + { + smartcardCert_Free(scCert); + continue; + } + +#ifndef _WIN32 + status = NCryptGetProperty(phKey, NCRYPT_WINPR_SLOTID, (PBYTE)&scCert->slotId, 4, &cbOutput, + NCRYPT_SILENT_FLAG); + if (status != ERROR_SUCCESS) + { + WLog_ERR(TAG, "unable to retrieve slotId for key %s", scCert->containerName); + smartcardCert_Free(scCert); + goto endofloop; + } +#endif /* _WIN32 */ + + /* ====== retrieve key's reader ====== */ + status = NCryptGetProperty(phKey, NCRYPT_READER_PROPERTY, NULL, 0, &cbOutput, + NCRYPT_SILENT_FLAG); + if (status != ERROR_SUCCESS) + { + WLog_ERR(TAG, "unable to retrieve reader's name length for key %s", + scCert->containerName); + smartcardCert_Free(scCert); + goto endofloop; + } + + scCert->reader = calloc(1, cbOutput + 2); + if (!scCert->reader) + { + WLog_ERR(TAG, "unable to allocate reader's name for key %s", scCert->containerName); + smartcardCert_Free(scCert); + goto endofloop; + } + + status = NCryptGetProperty(phKey, NCRYPT_READER_PROPERTY, (PBYTE)scCert->reader, + cbOutput + 2, &cbOutput, NCRYPT_SILENT_FLAG); + if (status != ERROR_SUCCESS) + { + WLog_ERR(TAG, "unable to retrieve reader's name for key %s", scCert->containerName); + smartcardCert_Free(scCert); + goto endofloop; + } + + if (!getAtr(scCert->reader, scCert->atr, &scCert->atrLength)) + { + WLog_ERR(TAG, "unable to retrieve card ATR for key %s", scCert->containerName); + smartcardCert_Free(scCert); + goto endofloop; + } + + /* ========= retrieve the certificate ===============*/ + status = NCryptGetProperty(phKey, NCRYPT_CERTIFICATE_PROPERTY, NULL, 0, &cbOutput, + NCRYPT_SILENT_FLAG); + if (status != ERROR_SUCCESS) + { + /* can happen that key don't have certificates */ + smartcardCert_Free(scCert); + goto endofloop; + } + + certBytes = calloc(1, cbOutput); + if (!certBytes) + { + WLog_ERR(TAG, "unable to allocate %d certBytes for key %s", cbOutput, + scCert->containerName); + smartcardCert_Free(scCert); + goto endofloop; + } + + status = NCryptGetProperty(phKey, NCRYPT_CERTIFICATE_PROPERTY, certBytes, cbOutput, + &cbOutput, NCRYPT_SILENT_FLAG); + if (status != ERROR_SUCCESS) + { + WLog_ERR(TAG, "unable to retrieve certificate for key %s", scCert->containerName); + free(certBytes); + smartcardCert_Free(scCert); + goto endofloop; + } + + if (!winpr_Digest(WINPR_MD_SHA1, certBytes, cbOutput, scCert->sha1Hash, + sizeof(scCert->sha1Hash))) + { + WLog_ERR(TAG, "unable to compute certificate sha1 for key %s", scCert->containerName); + free(certBytes); + smartcardCert_Free(scCert); + goto endofloop; + } + + scCert->certificate = crypto_cert_read(certBytes, cbOutput); + free(certBytes); + + if (!scCert->certificate) + { + WLog_ERR(TAG, "unable to parse X509 certificate for key %s", scCert->containerName); + smartcardCert_Free(scCert); + goto endofloop; + } + + if (!treat_sc_cert(scCert)) + { + smartcardCert_Free(scCert); + goto endofloop; + } + + if (userFilter && scCert->userHint && strcmp(scCert->userHint, userFilter) != 0) + { + smartcardCert_Free(scCert); + goto endofloop; + } + +#ifndef _WIN32 + if (!build_pkinit_args(settings, scCert)) + { + WLog_ERR(TAG, "error build pkinit args"); + smartcardCert_Free(scCert); + goto endofloop; + } +#endif + + ++*retCount; + scCert++; + + endofloop: + NCryptFreeObject((NCRYPT_HANDLE)phKey); + } + + ret = TRUE; + + NCryptFreeObject((NCRYPT_HANDLE)provider); +out: + free(scope); + return ret; +} + +BOOL smartcard_sw_enumerateCerts(rdpSettings* settings, SmartcardCert* scCert, DWORD count, + DWORD* retCount) +{ + size_t sz; + + if (count < 1) + return FALSE; + + if (!settings->SmartcardCertificateFile || !settings->SmartcardPrivateKeyFile) + { + WLog_ERR(TAG, "missing smartcard emulation cert or key"); + return FALSE; + } + + /* compute PKINIT args FILE:, */ + sz = strlen("FILE:") + strlen(settings->SmartcardCertificateFile) + 1 + + strlen(settings->SmartcardPrivateKeyFile) + 1; + scCert->pkinitArgs = malloc(sz); + snprintf(scCert->pkinitArgs, sz, "FILE:%s,%s", settings->SmartcardCertificateFile, + settings->SmartcardPrivateKeyFile); + + scCert->certificate = crypto_cert_pem_read(settings->SmartcardCertificate); + if (!scCert->certificate) + { + WLog_ERR(TAG, "unable to read smartcard certificate"); + goto out_error; + } + + if (!treat_sc_cert(scCert)) + { + WLog_ERR(TAG, "unable to treat smartcard certificate"); + goto out_error; + } + + if (ConvertToUnicode(CP_UTF8, 0, "FreeRDP Emulator", -1, &scCert->reader, 0) < 0) + goto out_error; + + scCert->containerName = strdup("Private Key 00"); + if (!scCert->containerName) + goto out_error; + + *retCount = 1; + return TRUE; + +out_error: + smartcardCert_Free(scCert); + return FALSE; +} + +BOOL smartcard_enumerateCerts(rdpSettings* settings, SmartcardCert* scCert, DWORD count, + DWORD* retCount) +{ + BOOL ret; + LPWSTR csp; + const char* asciiCsp = settings->CspName ? settings->CspName : MS_SCARD_PROV_A; + + if (settings->SmartcardEmulation) + return smartcard_sw_enumerateCerts(settings, scCert, count, retCount); + + if (ConvertToUnicode(CP_UTF8, 0, asciiCsp, -1, &csp, 0) <= 0) + { + WLog_ERR(TAG, "error while converting CSP to WCHAR"); + return FALSE; + } + + ret = smartcard_hw_enumerateCerts(settings, csp, settings->ReaderName, settings->Username, + scCert, count, retCount); + free(csp); + return ret; +} diff --git a/libfreerdp/core/smartcardlogon.h b/libfreerdp/core/smartcardlogon.h new file mode 100644 index 000000000..8c0618b97 --- /dev/null +++ b/libfreerdp/core/smartcardlogon.h @@ -0,0 +1,47 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * Logging in with smartcards + * + * Copyright 2022 David Fort + * + * 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. + */ +#ifndef LIBFREERDP_CORE_SMARTCARDLOGON_H +#define LIBFREERDP_CORE_SMARTCARDLOGON_H + +#include +#include + +typedef struct +{ + LPWSTR reader; + CryptoCert certificate; + char* pkinitArgs; + UINT32 slotId; + char* containerName; + char* upn; + char* userHint; + char* domainHint; + char* subject; + char* issuer; + BYTE atr[256]; + DWORD atrLength; + BYTE sha1Hash[20]; +} SmartcardCert; + +void smartcardCert_Free(SmartcardCert* scCert); + +BOOL smartcard_enumerateCerts(rdpSettings* settings, SmartcardCert* scCert, DWORD count, + DWORD* retCount); + +#endif /* LIBFREERDP_CORE_SMARTCARDLOGON_H */ diff --git a/libfreerdp/core/test/settings_property_lists.h b/libfreerdp/core/test/settings_property_lists.h index 692c2cebb..38533dcca 100644 --- a/libfreerdp/core/test/settings_property_lists.h +++ b/libfreerdp/core/test/settings_property_lists.h @@ -89,6 +89,7 @@ static const size_t bool_list_indices[] = { FreeRDP_IgnoreCertificate, FreeRDP_JpegCodec, FreeRDP_ListMonitors, + FreeRDP_ListSmartcards, FreeRDP_LocalConnection, FreeRDP_LogonErrors, FreeRDP_LogonNotify, @@ -232,6 +233,7 @@ static const size_t uint32_list_indices[] = { FreeRDP_GlyphSupportLevel, FreeRDP_JpegCodecId, FreeRDP_JpegQuality, + FreeRDP_KeySpec, FreeRDP_KeyboardCodePage, FreeRDP_KeyboardFunctionKey, FreeRDP_KeyboardHook, @@ -322,6 +324,7 @@ static const size_t string_list_indices[] = { FreeRDP_AlternateShell, FreeRDP_AssistanceFile, FreeRDP_AuthenticationServiceClass, + FreeRDP_CardName, FreeRDP_CertificateAcceptedFingerprints, FreeRDP_CertificateContent, FreeRDP_CertificateFile, @@ -333,6 +336,8 @@ static const size_t string_list_indices[] = { FreeRDP_ComputerName, FreeRDP_ConfigPath, FreeRDP_ConnectionFile, + FreeRDP_ContainerName, + FreeRDP_CspName, FreeRDP_CurrentPath, FreeRDP_Domain, FreeRDP_DrivesToRedirect, @@ -347,11 +352,17 @@ static const size_t string_list_indices[] = { FreeRDP_HomePath, FreeRDP_ImeFileName, FreeRDP_KerberosKdc, + FreeRDP_KerberosLifeTime, FreeRDP_KerberosRealm, + FreeRDP_KerberosRenewableLifeTime, + FreeRDP_KerberosStartTime, FreeRDP_KeyboardRemappingList, FreeRDP_NtlmSamFile, FreeRDP_Password, FreeRDP_PasswordHash, + FreeRDP_Pin, + FreeRDP_Pkcs11Module, + FreeRDP_PkinitAnchors, FreeRDP_PlayRemoteFxFile, FreeRDP_PreconnectionBlob, FreeRDP_PrivateKeyContent, @@ -362,6 +373,7 @@ static const size_t string_list_indices[] = { FreeRDP_RDP2TCPArgs, FreeRDP_RdpKeyContent, FreeRDP_RdpKeyFile, + FreeRDP_ReaderName, FreeRDP_RedirectionAcceptedCert, FreeRDP_RedirectionDomain, FreeRDP_RedirectionTargetFQDN, @@ -381,8 +393,10 @@ static const size_t string_list_indices[] = { FreeRDP_ServerHostname, FreeRDP_ShellWorkingDirectory, FreeRDP_SmartcardCertificate, + FreeRDP_SmartcardCertificateFile, FreeRDP_SmartcardPin, FreeRDP_SmartcardPrivateKey, + FreeRDP_SmartcardPrivateKeyFile, FreeRDP_TargetNetAddress, FreeRDP_TransportDumpFile, FreeRDP_Username, diff --git a/libfreerdp/crypto/crypto.c b/libfreerdp/crypto/crypto.c index 1fd9e47bc..327031fb9 100644 --- a/libfreerdp/crypto/crypto.c +++ b/libfreerdp/crypto/crypto.c @@ -41,6 +41,23 @@ CryptoCert crypto_cert_read(const BYTE* data, UINT32 length) return cert; } +CryptoCert crypto_cert_pem_read(const char* data) +{ + CryptoCert cert = malloc(sizeof(*cert)); + + if (!cert) + return NULL; + + cert->px509 = crypto_cert_from_pem(data, strlen(data), FALSE); + if (!cert->px509) + { + free(cert); + return NULL; + } + + return cert; +} + void crypto_cert_free(CryptoCert cert) { if (cert == NULL) diff --git a/libfreerdp/utils/CMakeLists.txt b/libfreerdp/utils/CMakeLists.txt index 2d02ac1fa..31d2216a8 100644 --- a/libfreerdp/utils/CMakeLists.txt +++ b/libfreerdp/utils/CMakeLists.txt @@ -28,6 +28,7 @@ set(${MODULE_PREFIX}_SRCS smartcard_operations.c smartcard_pack.c smartcard_call.c + smartcard_cli.c stopwatch.c) freerdp_module_add(${${MODULE_PREFIX}_SRCS}) diff --git a/libfreerdp/utils/smartcard_cli.c b/libfreerdp/utils/smartcard_cli.c new file mode 100644 index 000000000..98479b79c --- /dev/null +++ b/libfreerdp/utils/smartcard_cli.c @@ -0,0 +1,50 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * Smartcard client functions + * + * Copyright 2021 David Fort + * + * 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 "../core/smartcardlogon.h" + +BOOL freerdp_smartcard_list(rdpSettings* settings) +{ + SmartcardCert certs[64] = { 0 }; + DWORD i, count; + + if (!smartcard_enumerateCerts(settings, certs, 64, &count)) + return FALSE; + + for (i = 0; i < count; i++) + { + char readerName[256] = { 0 }; + + printf("%d: %s\n", i, certs[i].subject); + + if (WideCharToMultiByte(CP_UTF8, 0, certs[i].reader, -1, readerName, sizeof(readerName), + NULL, NULL) > 0) + printf("\t* reader: %s\n", readerName); +#ifndef _WIN32 + printf("\t* slotId: %" PRIu32 "\n", certs[i].slotId); + printf("\t* pkinitArgs: %s\n", certs[i].pkinitArgs); +#endif + printf("\t* containerName: %s\n", certs[i].containerName); + if (certs[i].upn) + printf("\t* UPN: %s\n", certs[i].upn); + + smartcardCert_Free(&certs[i]); + } + return TRUE; +} diff --git a/winpr/include/winpr/cred.h b/winpr/include/winpr/cred.h new file mode 100644 index 000000000..3bfd06db9 --- /dev/null +++ b/winpr/include/winpr/cred.h @@ -0,0 +1,57 @@ +/** + * WinPR: Windows Portable Runtime + * Windows credentials + * + * Copyright 2022 David Fort + * + * 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. + */ +#ifndef WINPR_CRED_H_ +#define WINPR_CRED_H_ + +#ifdef _WIN32 +#include +#else + +#define CERT_HASH_LENGTH 20 + +typedef enum _CRED_MARSHAL_TYPE +{ + CertCredential, + UsernameTargetCredential, + BinaryBlobCredential, + UsernameForPackedCredentials, + BinaryBlobForSystem +} CRED_MARSHAL_TYPE, + *PCRED_MARSHAL_TYPE; + +typedef struct _CERT_CREDENTIAL_INFO +{ + ULONG cbSize; + UCHAR rgbHashOfCert[CERT_HASH_LENGTH]; +} CERT_CREDENTIAL_INFO, *PCERT_CREDENTIAL_INFO; + +BOOL CredMarshalCredentialA(CRED_MARSHAL_TYPE CredType, PVOID Credential, + LPSTR* MarshaledCredential); +BOOL CredMarshalCredentialW(CRED_MARSHAL_TYPE CredType, PVOID Credential, + LPWSTR* MarshaledCredential); + +#ifdef UNICODE +#define CredMarshalCredential CredMarshalCredentialW +#else +#define CredMarshalCredential CredMarshalCredentialA +#endif + +#endif /* _WIN32 */ + +#endif /* WINPR_CRED_H_ */ diff --git a/winpr/include/winpr/ncrypt.h b/winpr/include/winpr/ncrypt.h index 1b243756c..d63df58e1 100644 --- a/winpr/include/winpr/ncrypt.h +++ b/winpr/include/winpr/ncrypt.h @@ -157,23 +157,27 @@ extern "C" { #endif - SECURITY_STATUS NCryptEnumStorageProviders(DWORD* wProviderCount, - NCryptProviderName** ppProviderList, DWORD dwFlags); + WINPR_API SECURITY_STATUS NCryptEnumStorageProviders(DWORD* wProviderCount, + NCryptProviderName** ppProviderList, + DWORD dwFlags); - SECURITY_STATUS NCryptOpenStorageProvider(NCRYPT_PROV_HANDLE* phProvider, - LPCWSTR pszProviderName, DWORD dwFlags); + WINPR_API SECURITY_STATUS NCryptOpenStorageProvider(NCRYPT_PROV_HANDLE* phProvider, + LPCWSTR pszProviderName, DWORD dwFlags); - SECURITY_STATUS NCryptEnumKeys(NCRYPT_PROV_HANDLE hProvider, LPCWSTR pszScope, - NCryptKeyName** ppKeyName, PVOID* ppEnumState, DWORD dwFlags); + WINPR_API SECURITY_STATUS NCryptEnumKeys(NCRYPT_PROV_HANDLE hProvider, LPCWSTR pszScope, + NCryptKeyName** ppKeyName, PVOID* ppEnumState, + DWORD dwFlags); - SECURITY_STATUS NCryptOpenKey(NCRYPT_PROV_HANDLE hProvider, NCRYPT_KEY_HANDLE* phKey, - LPCWSTR pszKeyName, DWORD dwLegacyKeySpec, DWORD dwFlags); + WINPR_API SECURITY_STATUS NCryptOpenKey(NCRYPT_PROV_HANDLE hProvider, NCRYPT_KEY_HANDLE* phKey, + LPCWSTR pszKeyName, DWORD dwLegacyKeySpec, + DWORD dwFlags); - SECURITY_STATUS NCryptGetProperty(NCRYPT_HANDLE hObject, LPCWSTR pszProperty, PBYTE pbOutput, - DWORD cbOutput, DWORD* pcbResult, DWORD dwFlags); + WINPR_API SECURITY_STATUS NCryptGetProperty(NCRYPT_HANDLE hObject, LPCWSTR pszProperty, + PBYTE pbOutput, DWORD cbOutput, DWORD* pcbResult, + DWORD dwFlags); - SECURITY_STATUS NCryptFreeObject(NCRYPT_HANDLE hObject); - SECURITY_STATUS NCryptFreeBuffer(PVOID pvInput); + WINPR_API SECURITY_STATUS NCryptFreeObject(NCRYPT_HANDLE hObject); + WINPR_API SECURITY_STATUS NCryptFreeBuffer(PVOID pvInput); #ifdef __cplusplus } diff --git a/winpr/include/winpr/sspi.h b/winpr/include/winpr/sspi.h index bd13fbbf9..22964f117 100644 --- a/winpr/include/winpr/sspi.h +++ b/winpr/include/winpr/sspi.h @@ -34,13 +34,13 @@ #include #include -#endif +#endif /* _WIN32 */ #if !defined(_WIN32) || defined(_UWP) #ifndef SEC_ENTRY #define SEC_ENTRY -#endif +#endif /* SEC_ENTRY */ typedef CHAR SEC_CHAR; typedef WCHAR SEC_WCHAR; @@ -62,7 +62,7 @@ typedef SECURITY_INTEGER* PTimeStamp; #ifndef __SECSTATUS_DEFINED__ typedef LONG SECURITY_STATUS; #define __SECSTATUS_DEFINED__ -#endif +#endif /* __SECSTATUS_DEFINED__ */ #if defined(__clang__) #pragma clang diagnostic pop @@ -96,9 +96,9 @@ typedef SecPkgInfoW* PSecPkgInfoW; #else #define SecPkgInfo SecPkgInfoA #define PSecPkgInfo PSecPkgInfoA -#endif +#endif /* UNICODE */ -#endif +#endif /* !defined(_WIN32) || defined(_UWP) */ #define NTLM_SSP_NAME _T("NTLM") #define KERBEROS_SSP_NAME _T("Kerberos") @@ -217,7 +217,7 @@ typedef SecPkgInfoW* PSecPkgInfoW; #define SEC_I_SIGNATURE_NEEDED (SECURITY_STATUS)0x0009035CL #define SEC_I_NO_RENEGOTIATION (SECURITY_STATUS)0x00090360L -#endif +#endif /* _WINERROR_ */ /* ============== some definitions missing in mingw ========================*/ #ifndef SEC_E_INVALID_PARAMETER @@ -482,7 +482,7 @@ typedef SecPkgCredentials_NamesW* PSecPkgCredentials_NamesW; #define PSecPkgCredentials_Names PSecPkgCredentials_NamesA #endif -#endif +#endif /* !defined(_WIN32) || defined(_UWP) */ #if !defined(_WIN32) || defined(_UWP) #if !defined(__MINGW32__) @@ -601,6 +601,17 @@ typedef struct #define SEC_WINNT_AUTH_IDENTITY_ANSI 0x1 #define SEC_WINNT_AUTH_IDENTITY_UNICODE 0x2 +typedef struct +{ + char* armorCache; + char* pkinitX509Anchors; + char* pkinitX509Identity; + BOOL withPac; + INT32 startTime; + INT32 lifeTime; + BYTE certSha1[20]; +} SEC_WINPR_KERBEROS_SETTINGS; + #if !defined(_WIN32) || defined(_UWP) #if defined(__clang__) @@ -649,6 +660,42 @@ typedef struct #endif /* _AUTH_IDENTITY_DEFINED */ +#ifndef SEC_WINNT_AUTH_IDENTITY_VERSION +#define SEC_WINNT_AUTH_IDENTITY_VERSION 0x200 + +typedef struct _SEC_WINNT_AUTH_IDENTITY_EXW +{ + UINT32 Version; + UINT32 Length; + UINT16* User; + UINT32 UserLength; + UINT16* Domain; + UINT32 DomainLength; + UINT16* Password; + UINT32 PasswordLength; + UINT32 Flags; + BYTE* PackageList; + UINT32 PackageListLength; +} SEC_WINNT_AUTH_IDENTITY_EXW, *PSEC_WINNT_AUTH_IDENTITY_EXW; + +typedef struct _SEC_WINNT_AUTH_IDENTITY_EXA +{ + UINT32 Version; + UINT32 Length; + BYTE* User; + UINT32 UserLength; + BYTE* Domain; + UINT32 DomainLength; + BYTE* Password; + UINT32 PasswordLength; + UINT32 Flags; + BYTE* PackageList; + UINT32 PackageListLength; +} SEC_WINNT_AUTH_IDENTITY_EXA, *PSEC_WINNT_AUTH_IDENTITY_EXA; + +#endif /* SEC_WINNT_AUTH_IDENTITY_VERSION */ + + #if defined(__clang__) #pragma clang diagnostic pop #endif @@ -672,7 +719,19 @@ typedef CtxtHandle* PCtxtHandle; ((((PSecHandle)(x))->dwLower != ((ULONG_PTR)((INT_PTR)-1))) && \ (((PSecHandle)(x))->dwUpper != ((ULONG_PTR)((INT_PTR)-1)))) -#endif +#endif /* !defined(_WIN32) || defined(_UWP) */ + +typedef struct _SEC_WINNT_AUTH_IDENTITY_WINPRA +{ + SEC_WINNT_AUTH_IDENTITY_EXA identityEx; + SEC_WINPR_KERBEROS_SETTINGS* kerberosSettings; +} SEC_WINNT_AUTH_IDENTITY_WINPRA, *PSEC_WINNT_AUTH_IDENTITY_WINPRA; + +typedef struct _SEC_WINNT_AUTH_IDENTITY_WINPRW +{ + SEC_WINNT_AUTH_IDENTITY_EXW identityEx; + SEC_WINPR_KERBEROS_SETTINGS* kerberosSettings; +} SEC_WINNT_AUTH_IDENTITY_WINPRW, *PSEC_WINNT_AUTH_IDENTITY_WINPRW; #define SECBUFFER_VERSION 0 diff --git a/winpr/libwinpr/ncrypt/CMakeLists.txt b/winpr/libwinpr/ncrypt/CMakeLists.txt index e8135cf46..9279cda0d 100644 --- a/winpr/libwinpr/ncrypt/CMakeLists.txt +++ b/winpr/libwinpr/ncrypt/CMakeLists.txt @@ -43,6 +43,10 @@ winpr_module_add( ncrypt_pkcs11.c ) +if (WIN32) + winpr_library_add_public(ncrypt) +endif() + if(BUILD_TESTING) add_subdirectory(test) endif() diff --git a/winpr/libwinpr/ncrypt/ncrypt.c b/winpr/libwinpr/ncrypt/ncrypt.c index 8fc1ad5c4..9b32a0334 100644 --- a/winpr/libwinpr/ncrypt/ncrypt.c +++ b/winpr/libwinpr/ncrypt/ncrypt.c @@ -84,11 +84,13 @@ SECURITY_STATUS winpr_NCryptDefault_dtor(NCRYPT_HANDLE handle) SECURITY_STATUS NCryptEnumStorageProviders(DWORD* wProviderCount, NCryptProviderName** ppProviderList, DWORD dwFlags) { - static const WCHAR emptyComment[] = { 0 }; NCryptProviderName* ret; size_t stringAllocSize = 0; +#ifdef WITH_PKCS11 LPWSTR strPtr; + static const WCHAR emptyComment[] = { 0 }; size_t copyAmount; +#endif *wProviderCount = 0; *ppProviderList = NULL; @@ -106,9 +108,10 @@ SECURITY_STATUS NCryptEnumStorageProviders(DWORD* wProviderCount, if (!ret) return NTE_NO_MEMORY; - strPtr = (LPWSTR)(ret + *wProviderCount); #ifdef WITH_PKCS11 + strPtr = (LPWSTR)(ret + *wProviderCount); + ret->pszName = strPtr; copyAmount = (_wcslen(MS_SCARD_PROV) + 1) * 2; memcpy(strPtr, MS_SCARD_PROV, copyAmount); diff --git a/winpr/libwinpr/sspi/Kerberos/kerberos.c b/winpr/libwinpr/sspi/Kerberos/kerberos.c index f07805487..f308226cb 100644 --- a/winpr/libwinpr/sspi/Kerberos/kerberos.c +++ b/winpr/libwinpr/sspi/Kerberos/kerberos.c @@ -26,6 +26,8 @@ #include #include #include +#include +#include #include #include @@ -231,7 +233,26 @@ static int kerberos_SetContextServicePrincipalNameA(KRB_CONTEXT* context, } #ifdef WITH_GSSAPI -static krb5_error_code acquire_cred(krb5_context ctx, krb5_principal client, const char* password) + +static krb5_error_code krb5_prompter(krb5_context context, void* data, const char* name, + const char* banner, int num_prompts, krb5_prompt prompts[]) +{ + int i; + krb5_prompt_type* ptypes = krb5_get_prompt_types(context); + + for (i = 0; i < num_prompts; i++) + { + if (ptypes && ptypes[i] == KRB5_PROMPT_TYPE_PREAUTH) + { + prompts[i].reply->data = strdup((const char*)data); + prompts[i].reply->length = strlen((const char*)data); + } + } + return 0; +} + +static krb5_error_code acquire_cred(krb5_context ctx, SEC_WINPR_KERBEROS_SETTINGS* optionsBlock, + krb5_principal client, const char* password) { krb5_error_code ret; krb5_creds creds; @@ -264,6 +285,21 @@ static krb5_error_code acquire_cred(krb5_context ctx, krb5_principal client, con /* Set default options */ krb5_get_init_creds_opt_set_forwardable(options, 0); krb5_get_init_creds_opt_set_proxiable(options, 0); + + if (optionsBlock && optionsBlock->withPac) + krb5_get_init_creds_opt_set_pac_request(ctx, options, TRUE); + + if (optionsBlock && optionsBlock->armorCache) + krb5_get_init_creds_opt_set_fast_ccache_name(ctx, options, optionsBlock->armorCache); + + if (optionsBlock && optionsBlock->pkinitX509Identity) + krb5_get_init_creds_opt_set_pa(ctx, options, "X509_user_identity", + optionsBlock->pkinitX509Identity); + + if (optionsBlock && optionsBlock->pkinitX509Anchors) + krb5_get_init_creds_opt_set_pa(ctx, options, "X509_anchors", + optionsBlock->pkinitX509Anchors); + #ifdef WITH_GSSAPI_MIT /* for MIT we specify ccache output using an option */ @@ -275,7 +311,8 @@ static krb5_error_code acquire_cred(krb5_context ctx, krb5_principal client, con #endif - if ((ret = krb5_init_creds_init(ctx, client, NULL, NULL, starttime, options, &init_ctx))) + if ((ret = krb5_init_creds_init(ctx, client, krb5_prompter, (void*)password, starttime, options, + &init_ctx))) { WLog_ERR(TAG, "error krb5_init_creds_init failed"); goto cleanup; @@ -332,7 +369,8 @@ cleanup: return ret; } -static int init_creds(LPCWSTR username, size_t username_len, LPCWSTR password, size_t password_len) +static int init_creds(SEC_WINPR_KERBEROS_SETTINGS* options, LPCWSTR username, size_t username_len, + LPCWSTR domain, size_t domain_len, LPCWSTR password, size_t password_len) { krb5_error_code ret = 0; krb5_context ctx = NULL; @@ -347,6 +385,8 @@ static int init_creds(LPCWSTR username, size_t username_len, LPCWSTR password, s size_t lrealm_len = 0; size_t lusername_len = 0; int status = 0; + BOOL isDefaultRealm = FALSE; + status = ConvertFromUnicode(CP_UTF8, 0, username, username_len, &lusername, 0, NULL, NULL); if (status <= 0) @@ -356,7 +396,6 @@ static int init_creds(LPCWSTR username, size_t username_len, LPCWSTR password, s } status = ConvertFromUnicode(CP_UTF8, 0, password, password_len, &lpassword, 0, NULL, NULL); - if (status <= 0) { WLog_ERR(TAG, "Failed to convert password"); @@ -372,12 +411,29 @@ static int init_creds(LPCWSTR username, size_t username_len, LPCWSTR password, s goto cleanup; } - ret = krb5_get_default_realm(ctx, &lrealm); - - if (ret) + if (domain && domain_len) { - WLog_WARN(TAG, "could not get Kerberos default realm"); - goto cleanup; + char* tmp; + if (ConvertFromUnicode(CP_UTF8, 0, domain, domain_len, &lrealm, 0, NULL, NULL) <= 0) + { + WLog_ERR(TAG, "error: converting domain"); + } + + /* convert to upper case */ + for (tmp = lrealm; *tmp; tmp++) + *tmp = toupper(*tmp); + + isDefaultRealm = FALSE; + } + else + { + ret = krb5_get_default_realm(ctx, &lrealm); + if (ret) + { + WLog_WARN(TAG, "could not get Kerberos default realm"); + goto cleanup; + } + isDefaultRealm = TRUE; } lrealm_len = strlen(lrealm); @@ -411,7 +467,7 @@ static int init_creds(LPCWSTR username, size_t username_len, LPCWSTR password, s goto cleanup; } - ret = acquire_cred(ctx, principal, lpassword); + ret = acquire_cred(ctx, options, principal, lpassword); if (ret) { @@ -427,7 +483,12 @@ cleanup: free(krb_name); if (lrealm) - krb5_free_default_realm(ctx, lrealm); + { + if (isDefaultRealm) + krb5_free_default_realm(ctx, lrealm); + else + free(lrealm); + } if (principal) krb5_free_principal(ctx, principal); @@ -497,10 +558,11 @@ static SECURITY_STATUS SEC_ENTRY kerberos_InitializeSecurityContextA( * If we use smartcard-logon, the credentials have already * been acquired by pkinit process. If not, returned error previously. */ - if (init_creds(context->credentials->identity.User, - context->credentials->identity.UserLength, - context->credentials->identity.Password, - context->credentials->identity.PasswordLength)) + SSPI_CREDENTIALS* credentials = context->credentials; + if (init_creds(credentials->kerbSettings, credentials->identity.User, + credentials->identity.UserLength, credentials->identity.Domain, + credentials->identity.DomainLength, credentials->identity.Password, + credentials->identity.PasswordLength)) return SEC_E_NO_CREDENTIALS; WLog_INFO(TAG, "Authenticated to Kerberos v5 via login/password"); diff --git a/winpr/libwinpr/sspi/Negotiate/negotiate.c b/winpr/libwinpr/sspi/Negotiate/negotiate.c index 81bdafb68..7e5c88a85 100644 --- a/winpr/libwinpr/sspi/Negotiate/negotiate.c +++ b/winpr/libwinpr/sspi/Negotiate/negotiate.c @@ -486,7 +486,6 @@ static SECURITY_STATUS SEC_ENTRY negotiate_AcquireCredentialsHandleW( PTimeStamp ptsExpiry) { SSPI_CREDENTIALS* credentials; - SEC_WINNT_AUTH_IDENTITY* identity; if ((fCredentialUse != SECPKG_CRED_OUTBOUND) && (fCredentialUse != SECPKG_CRED_INBOUND) && (fCredentialUse != SECPKG_CRED_BOTH)) @@ -495,17 +494,50 @@ static SECURITY_STATUS SEC_ENTRY negotiate_AcquireCredentialsHandleW( } credentials = sspi_CredentialsNew(); - if (!credentials) return SEC_E_INTERNAL_ERROR; credentials->fCredentialUse = fCredentialUse; credentials->pGetKeyFn = pGetKeyFn; credentials->pvGetKeyArgument = pvGetKeyArgument; - identity = (SEC_WINNT_AUTH_IDENTITY*)pAuthData; - if (identity) - sspi_CopyAuthIdentity(&(credentials->identity), identity); + if (pAuthData) + { + SEC_WINNT_AUTH_IDENTITY_EXW* identityEx = (SEC_WINNT_AUTH_IDENTITY_EXW*)pAuthData; + BOOL treated = FALSE; + + SEC_WINNT_AUTH_IDENTITY srcIdentity; + if (identityEx->Version == SEC_WINNT_AUTH_IDENTITY_VERSION) + { + if (identityEx->Length >= sizeof(*identityEx)) + { + srcIdentity.User = (UINT16*)identityEx->User; + srcIdentity.UserLength = identityEx->UserLength; + srcIdentity.Domain = (UINT16*)identityEx->Domain; + srcIdentity.DomainLength = identityEx->DomainLength; + srcIdentity.Password = (UINT16*)identityEx->Password; + srcIdentity.PasswordLength = identityEx->PasswordLength; + srcIdentity.Flags = identityEx->Flags; + + if (!sspi_CopyAuthIdentity(&credentials->identity, &srcIdentity)) + return SEC_E_INSUFFICIENT_MEMORY; + + if (identityEx->Length == sizeof(SEC_WINNT_AUTH_IDENTITY_WINPRA)) + { + SEC_WINNT_AUTH_IDENTITY_WINPRW* identityWinpr = + (SEC_WINNT_AUTH_IDENTITY_WINPRW*)pAuthData; + credentials->kerbSettings = identityWinpr->kerberosSettings; + } + treated = TRUE; + } + } + + if (!treated) + { + SEC_WINNT_AUTH_IDENTITY* identity = (SEC_WINNT_AUTH_IDENTITY*)pAuthData; + sspi_CopyAuthIdentity(&(credentials->identity), identity); + } + } sspi_SecureHandleSetLowerPointer(phCredential, (void*)credentials); sspi_SecureHandleSetUpperPointer(phCredential, (void*)NEGO_SSP_NAME); @@ -518,7 +550,6 @@ static SECURITY_STATUS SEC_ENTRY negotiate_AcquireCredentialsHandleA( PTimeStamp ptsExpiry) { SSPI_CREDENTIALS* credentials; - SEC_WINNT_AUTH_IDENTITY* identity; if ((fCredentialUse != SECPKG_CRED_OUTBOUND) && (fCredentialUse != SECPKG_CRED_INBOUND) && (fCredentialUse != SECPKG_CRED_BOTH)) @@ -534,10 +565,44 @@ static SECURITY_STATUS SEC_ENTRY negotiate_AcquireCredentialsHandleA( credentials->fCredentialUse = fCredentialUse; credentials->pGetKeyFn = pGetKeyFn; credentials->pvGetKeyArgument = pvGetKeyArgument; - identity = (SEC_WINNT_AUTH_IDENTITY*)pAuthData; - if (identity) - sspi_CopyAuthIdentity(&(credentials->identity), identity); + if (pAuthData) + { + SEC_WINNT_AUTH_IDENTITY_EXA* identityEx = (SEC_WINNT_AUTH_IDENTITY_EXA*)pAuthData; + BOOL treated = FALSE; + + SEC_WINNT_AUTH_IDENTITY srcIdentity; + if (identityEx->Version == SEC_WINNT_AUTH_IDENTITY_VERSION) + { + if (identityEx->Length >= sizeof(*identityEx)) + { + srcIdentity.User = (UINT16*)identityEx->User; + srcIdentity.UserLength = identityEx->UserLength; + srcIdentity.Domain = (UINT16*)identityEx->Domain; + srcIdentity.DomainLength = identityEx->DomainLength; + srcIdentity.Password = (UINT16*)identityEx->Password; + srcIdentity.PasswordLength = identityEx->PasswordLength; + srcIdentity.Flags = identityEx->Flags; + + if (!sspi_CopyAuthIdentity(&credentials->identity, &srcIdentity)) + return SEC_E_INSUFFICIENT_MEMORY; + + if (identityEx->Length == sizeof(SEC_WINNT_AUTH_IDENTITY_WINPRA)) + { + SEC_WINNT_AUTH_IDENTITY_WINPRA* identityWinpr = + (SEC_WINNT_AUTH_IDENTITY_WINPRA*)pAuthData; + credentials->kerbSettings = identityWinpr->kerberosSettings; + } + treated = TRUE; + } + } + + if (!treated) + { + SEC_WINNT_AUTH_IDENTITY* identity = (SEC_WINNT_AUTH_IDENTITY*)pAuthData; + sspi_CopyAuthIdentity(&(credentials->identity), identity); + } + } sspi_SecureHandleSetLowerPointer(phCredential, (void*)credentials); sspi_SecureHandleSetUpperPointer(phCredential, (void*)NEGO_SSP_NAME); diff --git a/winpr/libwinpr/sspi/sspi.h b/winpr/libwinpr/sspi/sspi.h index da0860f3f..2184c0ecf 100644 --- a/winpr/libwinpr/sspi/sspi.h +++ b/winpr/libwinpr/sspi/sspi.h @@ -28,14 +28,16 @@ #define SSPI_CREDENTIALS_HASH_LENGTH_OFFSET 512 -typedef struct +struct _SSPI_CREDENTIALS { DWORD flags; ULONG fCredentialUse; SEC_GET_KEY_FN pGetKeyFn; void* pvGetKeyArgument; SEC_WINNT_AUTH_IDENTITY identity; -} SSPI_CREDENTIALS; + SEC_WINPR_KERBEROS_SETTINGS* kerbSettings; +}; +typedef struct _SSPI_CREDENTIALS SSPI_CREDENTIALS; SSPI_CREDENTIALS* sspi_CredentialsNew(void); void sspi_CredentialsFree(SSPI_CREDENTIALS* credentials);