From d1e8fdc90a905576462815c3a5aa5f1263b8baea Mon Sep 17 00:00:00 2001 From: Henrik Andersson Date: Mon, 25 Mar 2013 13:01:38 +0000 Subject: [PATCH] Initial support for CredSSP smartcard authentication. - Add implementation of TSSmartCardCreds and TSCSPDataDetail for CredSSP protocol. - Add handling of long opts for getopt() - Added 4 new long opts for providing information to CredSSP which is required for smartcard credentials. - Updated manual with information about the new arguments. If smartcard authentication is request by commandline "-i" option, and no CredSSP smartcard options is provided, rdesktop will negotiate to use SSL and warn. git-svn-id: svn://svn.code.sf.net/p/rdesktop/code/rdesktop/trunk@1705 423420c4-83ab-492f-b58f-81f9feb106b5 --- cssp.c | 201 ++++++++++++++++++++++++++++++++++++++++++++++++- doc/rdesktop.1 | 27 ++++++- iso.c | 10 ++- rdesktop.c | 57 +++++++++++++- 4 files changed, 284 insertions(+), 11 deletions(-) diff --git a/cssp.c b/cssp.c index 2f540c5..829742a 100644 --- a/cssp.c +++ b/cssp.c @@ -1,7 +1,7 @@ /* -*- c-basic-offset: 8 -*- rdesktop: A Remote Desktop Protocol client. CredSSP layer and kerberos support. - Copyright 2012 Henrik Andersson for Cendio AB + Copyright 2012-2013 Henrik Andersson for Cendio AB This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -20,6 +20,13 @@ #include #include "rdesktop.h" +extern RD_BOOL g_use_password_as_pin; + +extern char *g_sc_csp_name; +extern char *g_sc_reader_name; +extern char *g_sc_card_name; +extern char *g_sc_container_name; + static gss_OID_desc _gss_spnego_krb5_mechanism_oid_desc = { 9, (void *) "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02" }; @@ -285,6 +292,178 @@ cssp_encode_tspasswordcreds(char *username, char *password, char *domain) return out; } +/* KeySpecs from wincrypt.h */ +#define AT_KEYEXCHANGE 1 +#define AT_SIGNATURE 2 + +static STREAM +cssp_encode_tscspdatadetail(unsigned char keyspec, char *card, char *reader, char *container, char *csp) +{ + int i; + STREAM out; + STREAM h1, h2; + struct stream tmp = { 0 }; + struct stream message = { 0 }; + + // allocate local streams + tmp.size = 4096; + tmp.data = xmalloc(tmp.size); + s_reset(&tmp); + + message.size = 4096; + message.data = xmalloc(message.size); + s_reset(&message); + + // keySpec [0] + s_reset(&tmp); + out_uint8(&tmp, keyspec); + s_mark_end(&tmp); + h2 = ber_wrap_hdr_data(BER_TAG_INTEGER, &tmp); + h1 = ber_wrap_hdr_data(BER_TAG_CTXT_SPECIFIC | BER_TAG_CONSTRUCTED | 0, h2); + out_uint8p(&message, h1->data, s_length(h1)); + s_free(h2); + s_free(h1); + + // cardName [1] + if (card) + { + s_reset(&tmp); + for (i = 0; i < strlen(card); i++) + out_uint16_le(&tmp, card[i]); + s_mark_end(&tmp); + h2 = ber_wrap_hdr_data(BER_TAG_OCTET_STRING, &tmp); + h1 = ber_wrap_hdr_data(BER_TAG_CTXT_SPECIFIC | BER_TAG_CONSTRUCTED | 1, h2); + out_uint8p(&message, h1->data, s_length(h1)); + s_free(h2); + s_free(h1); + } + + // readerName [2] + if (reader) + { + s_reset(&tmp); + for (i = 0; i < strlen(reader); i++) + out_uint16_le(&tmp, reader[i]); + s_mark_end(&tmp); + h2 = ber_wrap_hdr_data(BER_TAG_OCTET_STRING, &tmp); + h1 = ber_wrap_hdr_data(BER_TAG_CTXT_SPECIFIC | BER_TAG_CONSTRUCTED | 2, h2); + out_uint8p(&message, h1->data, s_length(h1)); + s_free(h2); + s_free(h1); + } + + // containerName [3] + if (container) + { + s_reset(&tmp); + for (i = 0; i < strlen(container); i++) + out_uint16_le(&tmp, container[i]); + s_mark_end(&tmp); + h2 = ber_wrap_hdr_data(BER_TAG_OCTET_STRING, &tmp); + h1 = ber_wrap_hdr_data(BER_TAG_CTXT_SPECIFIC | BER_TAG_CONSTRUCTED | 3, h2); + out_uint8p(&message, h1->data, s_length(h1)); + s_free(h2); + s_free(h1); + } + + // cspName [4] + if (csp) { + s_reset(&tmp); + for (i = 0; i < strlen(csp); i++) + out_uint16_le(&tmp, csp[i]); + s_mark_end(&tmp); + h2 = ber_wrap_hdr_data(BER_TAG_OCTET_STRING, &tmp); + h1 = ber_wrap_hdr_data(BER_TAG_CTXT_SPECIFIC | BER_TAG_CONSTRUCTED | 4, h2); + out_uint8p(&message, h1->data, s_length(h1)); + s_free(h2); + s_free(h1); + } + + s_mark_end(&message); + + // build message + out = ber_wrap_hdr_data(BER_TAG_SEQUENCE | BER_TAG_CONSTRUCTED, &message); + + // cleanup + free(tmp.data); + free(message.data); + return out; +} + +static STREAM +cssp_encode_tssmartcardcreds(char *username, char *password,char *domain) +{ + int i; + STREAM out, h1, h2; + struct stream tmp = { 0 }; + struct stream message = { 0 }; + + // allocate local streams + tmp.size = 4096; + tmp.data = xmalloc(tmp.size); + s_reset(&tmp); + + message.size = 4096; + message.data = xmalloc(message.size); + s_reset(&message); + + // pin [0] + s_reset(&tmp); + for (i = 0; i < strlen(password); i++) + out_uint16_le(&tmp, password[i]); + s_mark_end(&tmp); + h2 = ber_wrap_hdr_data(BER_TAG_OCTET_STRING, &tmp); + h1 = ber_wrap_hdr_data(BER_TAG_CTXT_SPECIFIC | BER_TAG_CONSTRUCTED | 0, h2); + out_uint8p(&message, h1->data, s_length(h1)); + s_free(h2); + s_free(h1); + + // cspData[1] + h2 = cssp_encode_tscspdatadetail(AT_KEYEXCHANGE, g_sc_card_name, g_sc_reader_name, g_sc_container_name, g_sc_csp_name); + h1 = ber_wrap_hdr_data(BER_TAG_CTXT_SPECIFIC | BER_TAG_CONSTRUCTED | 1, h2); + out_uint8p(&message, h1->data, s_length(h1)); + s_free(h2); + s_free(h1); + + // userHint [2] + if (username && strlen(username)) + { + s_reset(&tmp); + for (i = 0; i < strlen(username); i++) + out_uint16_le(&tmp, username[i]); + s_mark_end(&tmp); + h2 = ber_wrap_hdr_data(BER_TAG_OCTET_STRING, &tmp); + h1 = ber_wrap_hdr_data(BER_TAG_CTXT_SPECIFIC | BER_TAG_CONSTRUCTED | 2, h2); + out_uint8p(&message, h1->data, s_length(h1)); + s_free(h2); + s_free(h1); + } + + // domainHint [3] + if (domain && strlen(domain)) + { + s_reset(&tmp); + for (i = 0; i < strlen(domain); i++) + out_uint16_le(&tmp, domain[i]); + s_mark_end(&tmp); + h2 = ber_wrap_hdr_data(BER_TAG_OCTET_STRING, &tmp); + h1 = ber_wrap_hdr_data(BER_TAG_CTXT_SPECIFIC | BER_TAG_CONSTRUCTED | 3, h2); + out_uint8p(&message, h1->data, s_length(h1)); + s_free(h2); + s_free(h1); + } + + s_mark_end(&message); + + // build message + out = ber_wrap_hdr_data(BER_TAG_SEQUENCE | BER_TAG_CONSTRUCTED, &message); + + // cleanup + free(tmp.data); + free(message.data); + return out; +} + STREAM cssp_encode_tscredentials(char *username, char *password, char *domain) { @@ -304,7 +483,15 @@ cssp_encode_tscredentials(char *username, char *password, char *domain) // credType [0] s_reset(&tmp); - out_uint8(&tmp, 1); // TSPasswordCreds + if (g_use_password_as_pin == False) + { + out_uint8(&tmp, 1); // TSPasswordCreds + } + else + { + out_uint8(&tmp, 2); // TSSmartCardCreds + } + s_mark_end(&tmp); h2 = ber_wrap_hdr_data(BER_TAG_INTEGER, &tmp); h1 = ber_wrap_hdr_data(BER_TAG_CTXT_SPECIFIC | BER_TAG_CONSTRUCTED | 0, h2); @@ -313,7 +500,15 @@ cssp_encode_tscredentials(char *username, char *password, char *domain) s_free(h1); // credentials [1] - h3 = cssp_encode_tspasswordcreds(username, password, domain); + if (g_use_password_as_pin == False) + { + h3 = cssp_encode_tspasswordcreds(username, password, domain); + } + else + { + h3 = cssp_encode_tssmartcardcreds(username, password, domain); + } + h2 = ber_wrap_hdr_data(BER_TAG_OCTET_STRING, h3); h1 = ber_wrap_hdr_data(BER_TAG_CTXT_SPECIFIC | BER_TAG_CONSTRUCTED | 1, h2); out_uint8p(&message, h1->data, s_length(h1)); diff --git a/doc/rdesktop.1 b/doc/rdesktop.1 index f43fe82..b3a5768 100644 --- a/doc/rdesktop.1 +++ b/doc/rdesktop.1 @@ -74,9 +74,9 @@ screen size. This requires that rdesktop has been compiled with RandR support. .TP .BR "-i" -Use password as smartcard pin, if a valid user certificate is matched in smart card -reader the password passed with \f-p\fR argument is used as pin for the smart card. This -feature also requires that smart card redirection is used using \f-r scard\fR argument. +Use password as smartcard pin. If a valid user certificate is matched in smart card +reader the password passed with \f-p\fR argument is used as pin for the smart card. +This feature also requires that smart card redirection is used using \f-r scard\fR argument. .TP .BR "-f" Enable fullscreen mode. This overrides the window manager and causes the @@ -241,6 +241,27 @@ Use RDP version 4. .BR "-5" Use RDP version 5 (default). .PP + +.SH "CredSSP Smartcard options" +.TP +.BR "--sc-csp-name " +Specify the CSP (Crypto Service Provider) to use on the windows side for the smartcard +authentication. CSP is the driver for your smartcard and it seems like this is required +to be specified for CredSSP authentication. For swedish NetID the following CSP name is +used; "Net iD - CSP". +.TP +.BR "--sc-container-name " +Specify the container name, usally this is the username for default container and it seems +like this is required to be specified for CredSSP authentication. +.TP +.BR "--sc-reader-name " +Specify the reader name to be used to prevent the pin code being sent to wrong card if there +are several readers. +.TP +.BR "--sc-card-name " +Specify the card name for example; "Telia EID IP5a". +.PP + .SH "EXIT VALUES" .PP .IP "\fB0\fP" diff --git a/iso.c b/iso.c index 9a7c09c..6be30a5 100644 --- a/iso.c +++ b/iso.c @@ -27,6 +27,12 @@ extern RD_BOOL g_use_password_as_pin; static RD_BOOL g_negotiate_rdp_protocol = True; +extern char *g_sc_csp_name; +extern char *g_sc_reader_name; +extern char *g_sc_card_name; +extern char *g_sc_container_name; + + /* Send a self-contained ISO PDU */ static void iso_send_msg(uint8 code) @@ -207,8 +213,10 @@ iso_connect(char *server, char *username, char *domain, char *password, #ifdef WITH_CREDSSP if (!g_use_password_as_pin) neg_proto |= PROTOCOL_HYBRID; + else if (g_sc_csp_name || g_sc_reader_name || g_sc_card_name || g_sc_container_name) + neg_proto |= PROTOCOL_HYBRID; else - warning("CredSSP will be disabled if smartcard SSO is used.\n"); + warning("Disables CredSSP due to missing smartcard information for SSO.\n"); #endif retry: diff --git a/rdesktop.c b/rdesktop.c index 33d1c87..bca2c83 100644 --- a/rdesktop.c +++ b/rdesktop.c @@ -21,6 +21,7 @@ #include /* va_list va_start va_end */ #include /* read close getuid getgid getpid getppid gethostname */ +#include #include /* open */ #include /* getpwuid */ #include /* tcgetattr tcsetattr */ @@ -131,6 +132,27 @@ RD_BOOL g_rdpsnd = False; char g_codepage[16] = ""; #endif + + + +char *g_sc_csp_name = NULL; /* Smartcard CSP name */ +char *g_sc_reader_name = NULL; +char *g_sc_card_name = NULL; +char *g_sc_container_name = NULL; +#define OPT_BASE 256 +#define OPT_SC_CSP_NAME (OPT_BASE+1) +#define OPT_SC_READER_NAME (OPT_BASE+2) +#define OPT_SC_CARD_NAME (OPT_BASE+3) +#define OPT_SC_CONTAINER_NAME (OPT_BASE+4) + +struct option longopts[] = { + {"sc-csp-name", 1, 0, OPT_SC_CSP_NAME}, + {"sc-reader-name", 1, 0, OPT_SC_READER_NAME}, + {"sc-card-name", 1, 0, OPT_SC_CARD_NAME}, + {"sc-container-name", 1, 0, OPT_SC_CONTAINER_NAME}, + {NULL} +}; + extern RDPDR_DEVICE g_rdpdr_device[]; extern uint32 g_num_devices; extern char *g_rdpdr_clientname; @@ -165,7 +187,7 @@ usage(char *program) fprintf(stderr, " -k: keyboard layout on server (en-us, de, sv, etc.)\n"); fprintf(stderr, " -g: desktop geometry (WxH)\n"); #ifdef WITH_SCARD - fprintf(stderr, " -i: password is smartcard pin\n"); + fprintf(stderr, " -i: enables smartcard authentication, password is used as pin\n"); #endif fprintf(stderr, " -f: full-screen mode\n"); fprintf(stderr, " -b: force bitmap updates\n"); @@ -236,6 +258,17 @@ usage(char *program) fprintf(stderr, " -0: attach to console\n"); fprintf(stderr, " -4: use RDP version 4\n"); fprintf(stderr, " -5: use RDP version 5 (default)\n"); +#ifdef WITH_SCARD + fprintf(stderr, "\nCredSSP Smartcard hinting\n"); + fprintf(stderr, " --sc-csp-name Specifies the Crypto Service Provider name which\n"); + fprintf(stderr, " is used to authenticate the user by smartcard\n"); + fprintf(stderr, " --sc-container-name Specifies the container name, this is usally the username\n"); + fprintf(stderr, " --sc-reader-name Smartcard reader name to use\n"); + fprintf(stderr, " --sc-card-name Specifies the card name of the smartcard to use\n"); +#endif + + fprintf(stderr, "\n"); + } static int @@ -484,6 +517,7 @@ main(int argc, char *argv[]) #ifdef WITH_RDPSND char *rdpsnd_optarg = NULL; #endif + int longidx; #ifdef HAVE_LOCALE_H /* Set locale according to environment */ @@ -515,9 +549,8 @@ main(int argc, char *argv[]) #else #define VNCOPT #endif - - while ((c = getopt(argc, argv, - VNCOPT "A:u:L:d:s:c:p:n:k:g:fbBeEitmzCDKS:T:NX:a:x:Pr:045h?")) != -1) + while ((c = getopt_long(argc, argv, + VNCOPT "A:u:L:d:s:c:p:n:k:g:fbBeEitmzCDKS:T:NX:a:x:Pr:045h?", longopts, &longidx)) != -1) { switch (c) { @@ -862,7 +895,23 @@ main(int argc, char *argv[]) case '5': g_rdp_version = RDP_V5; break; +#if WITH_SCARD + case OPT_SC_CSP_NAME: + g_sc_csp_name = strdup(optarg); + break; + case OPT_SC_READER_NAME: + g_sc_reader_name = strdup(optarg); + break; + + case OPT_SC_CARD_NAME: + g_sc_card_name = strdup(optarg); + break; + + case OPT_SC_CONTAINER_NAME: + g_sc_container_name = strdup(optarg); + break; +#endif case 'h': case '?': default: