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
This commit is contained in:
Henrik Andersson 2013-03-25 13:01:38 +00:00
parent d0bf92a9c3
commit d1e8fdc90a
4 changed files with 284 additions and 11 deletions

197
cssp.c
View File

@ -1,7 +1,7 @@
/* -*- c-basic-offset: 8 -*- /* -*- c-basic-offset: 8 -*-
rdesktop: A Remote Desktop Protocol client. rdesktop: A Remote Desktop Protocol client.
CredSSP layer and kerberos support. CredSSP layer and kerberos support.
Copyright 2012 Henrik Andersson <hean01@cendio.se> for Cendio AB Copyright 2012-2013 Henrik Andersson <hean01@cendio.se> for Cendio AB
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -20,6 +20,13 @@
#include <gssapi/gssapi.h> #include <gssapi/gssapi.h>
#include "rdesktop.h" #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 = static gss_OID_desc _gss_spnego_krb5_mechanism_oid_desc =
{ 9, (void *) "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02" }; { 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; 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 STREAM
cssp_encode_tscredentials(char *username, char *password, char *domain) cssp_encode_tscredentials(char *username, char *password, char *domain)
{ {
@ -304,7 +483,15 @@ cssp_encode_tscredentials(char *username, char *password, char *domain)
// credType [0] // credType [0]
s_reset(&tmp); s_reset(&tmp);
if (g_use_password_as_pin == False)
{
out_uint8(&tmp, 1); // TSPasswordCreds out_uint8(&tmp, 1); // TSPasswordCreds
}
else
{
out_uint8(&tmp, 2); // TSSmartCardCreds
}
s_mark_end(&tmp); s_mark_end(&tmp);
h2 = ber_wrap_hdr_data(BER_TAG_INTEGER, &tmp); h2 = ber_wrap_hdr_data(BER_TAG_INTEGER, &tmp);
h1 = ber_wrap_hdr_data(BER_TAG_CTXT_SPECIFIC | BER_TAG_CONSTRUCTED | 0, h2); 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); s_free(h1);
// credentials [1] // credentials [1]
if (g_use_password_as_pin == False)
{
h3 = cssp_encode_tspasswordcreds(username, password, domain); 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); h2 = ber_wrap_hdr_data(BER_TAG_OCTET_STRING, h3);
h1 = ber_wrap_hdr_data(BER_TAG_CTXT_SPECIFIC | BER_TAG_CONSTRUCTED | 1, h2); h1 = ber_wrap_hdr_data(BER_TAG_CTXT_SPECIFIC | BER_TAG_CONSTRUCTED | 1, h2);
out_uint8p(&message, h1->data, s_length(h1)); out_uint8p(&message, h1->data, s_length(h1));

View File

@ -74,9 +74,9 @@ screen size. This requires that rdesktop has been compiled with RandR
support. support.
.TP .TP
.BR "-i" .BR "-i"
Use password as smartcard pin, if a valid user certificate is matched in smart card 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 reader the password passed with \f-p\fR argument is used as pin for the smart card.
feature also requires that smart card redirection is used using \f-r scard\fR argument. This feature also requires that smart card redirection is used using \f-r scard\fR argument.
.TP .TP
.BR "-f" .BR "-f"
Enable fullscreen mode. This overrides the window manager and causes the Enable fullscreen mode. This overrides the window manager and causes the
@ -241,6 +241,27 @@ Use RDP version 4.
.BR "-5" .BR "-5"
Use RDP version 5 (default). Use RDP version 5 (default).
.PP .PP
.SH "CredSSP Smartcard options"
.TP
.BR "--sc-csp-name <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 <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 <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 <name>"
Specify the card name for example; "Telia EID IP5a".
.PP
.SH "EXIT VALUES" .SH "EXIT VALUES"
.PP .PP
.IP "\fB0\fP" .IP "\fB0\fP"

10
iso.c
View File

@ -27,6 +27,12 @@ extern RD_BOOL g_use_password_as_pin;
static RD_BOOL g_negotiate_rdp_protocol = True; 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 */ /* Send a self-contained ISO PDU */
static void static void
iso_send_msg(uint8 code) iso_send_msg(uint8 code)
@ -207,8 +213,10 @@ iso_connect(char *server, char *username, char *domain, char *password,
#ifdef WITH_CREDSSP #ifdef WITH_CREDSSP
if (!g_use_password_as_pin) if (!g_use_password_as_pin)
neg_proto |= PROTOCOL_HYBRID; 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 else
warning("CredSSP will be disabled if smartcard SSO is used.\n"); warning("Disables CredSSP due to missing smartcard information for SSO.\n");
#endif #endif
retry: retry:

View File

@ -21,6 +21,7 @@
#include <stdarg.h> /* va_list va_start va_end */ #include <stdarg.h> /* va_list va_start va_end */
#include <unistd.h> /* read close getuid getgid getpid getppid gethostname */ #include <unistd.h> /* read close getuid getgid getpid getppid gethostname */
#include <getopt.h>
#include <fcntl.h> /* open */ #include <fcntl.h> /* open */
#include <pwd.h> /* getpwuid */ #include <pwd.h> /* getpwuid */
#include <termios.h> /* tcgetattr tcsetattr */ #include <termios.h> /* tcgetattr tcsetattr */
@ -131,6 +132,27 @@ RD_BOOL g_rdpsnd = False;
char g_codepage[16] = ""; char g_codepage[16] = "";
#endif #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 RDPDR_DEVICE g_rdpdr_device[];
extern uint32 g_num_devices; extern uint32 g_num_devices;
extern char *g_rdpdr_clientname; 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, " -k: keyboard layout on server (en-us, de, sv, etc.)\n");
fprintf(stderr, " -g: desktop geometry (WxH)\n"); fprintf(stderr, " -g: desktop geometry (WxH)\n");
#ifdef WITH_SCARD #ifdef WITH_SCARD
fprintf(stderr, " -i: password is smartcard pin\n"); fprintf(stderr, " -i: enables smartcard authentication, password is used as pin\n");
#endif #endif
fprintf(stderr, " -f: full-screen mode\n"); fprintf(stderr, " -f: full-screen mode\n");
fprintf(stderr, " -b: force bitmap updates\n"); fprintf(stderr, " -b: force bitmap updates\n");
@ -236,6 +258,17 @@ usage(char *program)
fprintf(stderr, " -0: attach to console\n"); fprintf(stderr, " -0: attach to console\n");
fprintf(stderr, " -4: use RDP version 4\n"); fprintf(stderr, " -4: use RDP version 4\n");
fprintf(stderr, " -5: use RDP version 5 (default)\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 static int
@ -484,6 +517,7 @@ main(int argc, char *argv[])
#ifdef WITH_RDPSND #ifdef WITH_RDPSND
char *rdpsnd_optarg = NULL; char *rdpsnd_optarg = NULL;
#endif #endif
int longidx;
#ifdef HAVE_LOCALE_H #ifdef HAVE_LOCALE_H
/* Set locale according to environment */ /* Set locale according to environment */
@ -515,9 +549,8 @@ main(int argc, char *argv[])
#else #else
#define VNCOPT #define VNCOPT
#endif #endif
while ((c = getopt_long(argc, argv,
while ((c = getopt(argc, argv, VNCOPT "A:u:L:d:s:c:p:n:k:g:fbBeEitmzCDKS:T:NX:a:x:Pr:045h?", longopts, &longidx)) != -1)
VNCOPT "A:u:L:d:s:c:p:n:k:g:fbBeEitmzCDKS:T:NX:a:x:Pr:045h?")) != -1)
{ {
switch (c) switch (c)
{ {
@ -862,7 +895,23 @@ main(int argc, char *argv[])
case '5': case '5':
g_rdp_version = RDP_V5; g_rdp_version = RDP_V5;
break; 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 'h':
case '?': case '?':
default: default: