From 3024dcdf459b8abf443e3ccbeb12e54647b62e92 Mon Sep 17 00:00:00 2001 From: Markus Friedl Date: Tue, 26 Jun 2001 05:33:37 +0000 Subject: more smartcard support. --- usr.bin/ssh/lib/Makefile | 7 +- usr.bin/ssh/scard.c | 359 ++++++++++++++++++++++++++++++++++++++++ usr.bin/ssh/scard.h | 30 ++++ usr.bin/ssh/ssh-agent.c | 118 ++++++++++++- usr.bin/ssh/ssh-agent/Makefile | 7 +- usr.bin/ssh/ssh-keygen/Makefile | 7 +- 6 files changed, 523 insertions(+), 5 deletions(-) create mode 100644 usr.bin/ssh/scard.c create mode 100644 usr.bin/ssh/scard.h (limited to 'usr.bin') diff --git a/usr.bin/ssh/lib/Makefile b/usr.bin/ssh/lib/Makefile index 0a0b0343171..b807a7040f5 100644 --- a/usr.bin/ssh/lib/Makefile +++ b/usr.bin/ssh/lib/Makefile @@ -1,4 +1,4 @@ -# $OpenBSD: Makefile,v 1.25 2001/06/24 05:35:34 markus Exp $ +# $OpenBSD: Makefile,v 1.26 2001/06/26 05:33:35 markus Exp $ .PATH: ${.CURDIR}/.. @@ -9,6 +9,7 @@ SRCS= authfd.c authfile.c bufaux.c buffer.c canohost.c \ rsa.c tildexpand.c ttymodes.c uidswap.c xmalloc.c atomicio.c \ key.c dispatch.c kex.c mac.c uuencode.c misc.c \ rijndael.c ssh-dss.c ssh-rsa.c dh.c kexdh.c kexgex.c \ + scard.c NOPROFILE= yes NOPIC= yes @@ -26,4 +27,8 @@ SRCS+= radix.c .endif # AFS .endif # KERBEROS +.if (${SMARTCARD:L} == "yes") +CFLAGS+= -DSMARTCARD +.endif # SMARTCARD + .include diff --git a/usr.bin/ssh/scard.c b/usr.bin/ssh/scard.c new file mode 100644 index 00000000000..6dbabf546c2 --- /dev/null +++ b/usr.bin/ssh/scard.c @@ -0,0 +1,359 @@ +/* + * Copyright (c) 2001 Markus Friedl. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifdef SMARTCARD +#include "includes.h" +RCSID("$OpenBSD: scard.c,v 1.1 2001/06/26 05:33:34 markus Exp $"); + +#include +#include + +#include "key.h" +#include "log.h" +#include "xmalloc.h" +#include "scard.h" + +#define CLA_SSH 0x05 +#define INS_DECRYPT 0x10 +#define INS_GET_KEYLENGTH 0x20 +#define INS_GET_PUBKEY 0x30 +#define INS_GET_RESPONSE 0xc0 + +#define MAX_BUF_SIZE 256 + +static int sc_fd = -1; +static int sc_reader_num = 0; +static int cla = 0x00; /* class */ + +/* interface to libsectok */ + +static int +sc_open(int num) +{ + int n; + u_char atr[256]; + + if (sc_fd >= 0) + return sc_fd; + sc_reader_num = num; + + sc_fd = scopen(sc_reader_num, 0, NULL); + if (sc_fd < 0) { + error("scopen failed %d", sc_fd); + return sc_fd; + } + n = screset(sc_fd, atr, NULL); + if (n <= 0) { + error("screset failed."); + sc_fd = -1; + return sc_fd; + } + debug("open ok %d", sc_fd); + return sc_fd; +} + +static int +sc_reset(void) +{ + scclose(sc_fd); + sc_fd = -1; + return sc_open(sc_reader_num); +} + +static int +selectfile(int fd, int f0, int f1, int verbose) +{ + int n, r1, r2, code; + u_char buf[2], obuf[256]; + + buf[0] = f0; + buf[1] = f1; + n = scrw(sc_fd, cla, 0xa4, 0, 0, 2, buf, sizeof obuf, obuf, &r1, &r2); + if (n < 0) { + error("selectfile: scwrite failed"); + return -2; + } + if (r1 == 0x90 || r1 == 0x61) + code = 0; + else if (r1 == 0x6a && r2 == 0x82) + /* file not found */ + code = -1; + else + code = -2; + if (verbose && n > 0) + dump_reply(obuf, n, 0, 0); + if (verbose || code == -2) { + error("%x.%x: %s", f0, f1, get_r1r2s(r1, r2)); + } + return code; +} + +static int +sc_enable_applet(void) +{ + u_char data[MAX_BUF_SIZE]; + u_char progID[2], contID[2], aid[MAX_BUF_SIZE]; + int i, len, rv, r1, r2, aid_len; + + len = rv = r1 = r2 = 0; + progID[0] = 0x77; + progID[1] = 0x77; + contID[0] = 0x77; + contID[1] = 0x78; + aid_len = 5; + + for (i = 0; i < 16; i++) + aid[i] = 0x77; + + rv = selectfile(sc_fd, contID[0], contID[1], 0); + if (rv < 0) { + error("selectfile failed"); + return -1; + } + for (i = 0; i < aid_len; i++) + data[i] = (u_char) aid[i]; + rv = scwrite(sc_fd, cla, 0xa4, 0x04, 0, aid_len, data, &r1, &r2); + if (r1 != 0x90 && r1 != 0x61) { + /* error */ + error("selecting the cardlet: "); + for (i = 0; i < aid_len; i++) { + error("%02x", (u_char) aid[i]); + } + print_r1r2(r1, r2); + return -1; + } + return 0; +} + +static int +sc_read_pubkey(Key * k) +{ + u_char buf[256]; + char *p; + int len, rv, r1, r2; + + len = rv = r1 = r2 = 0; + + /* get key size */ + rv = scread(sc_fd, CLA_SSH, INS_GET_KEYLENGTH, 0, 0, 2, buf, &r1, &r2); + if (rv < 0) { + error("could not obtain key length."); + return rv; + } + len = (buf[0] << 8) | buf[1]; + error("len %d r1 %d r2 %d", len, r1, r2); + len /= 8; + + /* get n */ + rv = scread(sc_fd, CLA_SSH, INS_GET_PUBKEY, 0, 0, len, buf, &r1, &r2); + if (rv < 0) { + error("could not obtain public key"); + return rv; + } + debug("len %d r1 %d r2 %d", len, r1, r2); + BN_bin2bn(buf, len, k->rsa->n); + + /* currently the java applet just stores 'n' */ + BN_set_word(k->rsa->e, 35); /* XXX */ + + p = key_fingerprint(k, SSH_FP_MD5, SSH_FP_HEX); + debug("fingerprint %d %s", key_size(k), p); + xfree(p); + + return 0; +} + +/* private key operations */ + +static int +sc_private_decrypt(int flen, unsigned char *from, + unsigned char *to, RSA *rsa, int padding) +{ + int rv, num, r1, r2, olen; + u_char *padded = NULL; + + debug("sc_private_decrypt called"); + + olen = num = r1 = r2 = 0; + if (padding != RSA_PKCS1_PADDING) + goto err; + + num = BN_num_bytes(rsa->n); + padded = xmalloc(num); + + rv = scwrite(sc_fd, CLA_SSH, INS_DECRYPT, 0, 0, num, from, &r1, &r2); + if (rv < 0) { + error("scwrite() for decrypt failed."); + goto err; + } + if (r1 != 0x90 && r1 != 0x61) { + error("INS_DECRYPT: r1 %x r2 %x", r1, r2); + goto err; + } + rv = scread(sc_fd, CLA_SSH, INS_GET_RESPONSE, 0, 0, num, padded, &r1, &r2); + if (rv < 0) { + error("scread() for decrypt failed"); + goto err; + } + if (r1 != 0x90 && r1 != 0x61) { + error("INS_GET_RESPONSE: r1 %x r2 %x", r1, r2); + goto err; + } + debug("r1 %x r2 %x", r1, r2); + olen = RSA_padding_check_PKCS1_type_2(to, num, padded + 1, num - 1, num); +err: + if (padded) + xfree(padded); + return olen; +} + + +static int +sc_private_encrypt(int flen, unsigned char *from, + unsigned char *to, RSA *rsa, int padding) +{ + int rv, i, num, r1, r2; + u_char *padded = NULL; + + num = r1 = r2 = 0; + if (padding != RSA_PKCS1_PADDING) + goto err; + + error("sc_private_encrypt called"); + num = BN_num_bytes(rsa->n); + padded = xmalloc(num); + i = RSA_padding_add_PKCS1_type_1(padded, num, from, flen); + if (i <= 0) { + error("RSA_padding_add_PKCS1_type_1 failed"); + goto err; + } + rv = scwrite(sc_fd, CLA_SSH, INS_DECRYPT, 0, 0, num, padded, &r1, &r2); + if (rv < 0) { + error("scwrite() for rsa failed"); + sc_reset(); + goto err; + } + if (r1 != 0x90 && r1 != 0x61) { + error("INS_DECRYPT: r1 %x r2 %x", r1, r2); + goto err; + } + rv = scread(sc_fd, CLA_SSH, INS_GET_RESPONSE, 0, 0, num, to, &r1, &r2); + if (rv < 0) { + error("scread() for rsa failed"); + sc_reset(); + goto err; + } + if (r1 != 0x90 && r1 != 0x61) { + error("INS_GET_RESPONSE: r1 %x r2 %x", r1, r2); + goto err; + } +err: + if (padded) + xfree(padded); + return num; +} + +/* engine for overloading private key operations */ + +static ENGINE *smart_engine = NULL; +static RSA_METHOD smart_rsa = +{ + "smartcard", + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + 0, + NULL, +}; + +ENGINE * +sc_get_engine() +{ + RSA_METHOD *def; + + def = RSA_get_default_openssl_method(); + + /* overload */ + smart_rsa.rsa_priv_enc = sc_private_encrypt; + smart_rsa.rsa_priv_dec = sc_private_decrypt; + + /* just use the OpenSSL version */ + smart_rsa.rsa_pub_enc = def->rsa_pub_enc; + smart_rsa.rsa_pub_dec = def->rsa_pub_dec; + smart_rsa.rsa_mod_exp = def->rsa_mod_exp; + smart_rsa.bn_mod_exp = def->bn_mod_exp; + smart_rsa.init = def->init; + smart_rsa.finish = def->finish; + smart_rsa.flags = def->flags; + smart_rsa.app_data = def->app_data; + smart_rsa.rsa_sign = def->rsa_sign; + smart_rsa.rsa_verify = def->rsa_verify; + + smart_engine = ENGINE_new(); + + ENGINE_set_id(smart_engine, "xxx"); + ENGINE_set_name(smart_engine, "xxx"); + ENGINE_set_RSA(smart_engine, &smart_rsa); + ENGINE_set_DSA(smart_engine, DSA_get_default_openssl_method()); + ENGINE_set_DH(smart_engine, DH_get_default_openssl_method()); + ENGINE_set_RAND(smart_engine, RAND_SSLeay()); + ENGINE_set_BN_mod_exp(smart_engine, BN_mod_exp); + + return smart_engine; +} + +Key * +sc_get_key(int sc_reader_num) +{ + Key *k; + int rv; + + rv = sc_open (sc_reader_num); + if (rv < 0) { + error("sc_open failed"); + return NULL; + } + rv = sc_enable_applet(); + if (rv < 0) { + error("sc_enable_applet failed"); + return NULL; + } + k = key_new(KEY_RSA); + if (k == NULL) { + return NULL; + } + rv = sc_read_pubkey (k); + if (rv < 0) { + error("sc_read_pubkey failed"); + key_free(k); + return NULL; + } + return k; +} +#endif diff --git a/usr.bin/ssh/scard.h b/usr.bin/ssh/scard.h new file mode 100644 index 00000000000..3425967b88a --- /dev/null +++ b/usr.bin/ssh/scard.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2001 Markus Friedl. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* $OpenBSD: scard.h,v 1.1 2001/06/26 05:33:34 markus Exp $ */ + +#include + +Key *sc_get_key(int sc_reader_num); +ENGINE *sc_get_engine(); diff --git a/usr.bin/ssh/ssh-agent.c b/usr.bin/ssh/ssh-agent.c index c529f6d7d58..288eb6914c0 100644 --- a/usr.bin/ssh/ssh-agent.c +++ b/usr.bin/ssh/ssh-agent.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ssh-agent.c,v 1.58 2001/06/26 05:07:43 markus Exp $ */ +/* $OpenBSD: ssh-agent.c,v 1.59 2001/06/26 05:33:34 markus Exp $ */ /* * Author: Tatu Ylonen @@ -36,7 +36,7 @@ */ #include "includes.h" -RCSID("$OpenBSD: ssh-agent.c,v 1.58 2001/06/26 05:07:43 markus Exp $"); +RCSID("$OpenBSD: ssh-agent.c,v 1.59 2001/06/26 05:33:34 markus Exp $"); #include #include @@ -56,6 +56,11 @@ RCSID("$OpenBSD: ssh-agent.c,v 1.58 2001/06/26 05:07:43 markus Exp $"); #include "compat.h" #include "log.h" +#ifdef SMARTCARD +#include +#include "scard.h" +#endif + typedef struct { int fd; enum { @@ -435,6 +440,106 @@ send: success ? SSH_AGENT_SUCCESS : SSH_AGENT_FAILURE); } + +#ifdef SMARTCARD +static void +process_add_smartcard_key (SocketEntry *e) +{ + Idtab *tab; + Key *n = NULL, *k = NULL; + int success = 0; + int sc_reader_num = 0; + + sc_reader_num = buffer_get_int(&e->input); + + k = sc_get_key(sc_reader_num); + if (k == NULL) { + error("sc_get_pubkey failed"); + goto send; + } + success = 1; + + tab = idtab_lookup(1); + if (lookup_private_key(k, NULL, 1) == NULL) { + if (tab->nentries == 0) + tab->identities = xmalloc(sizeof(Identity)); + else + tab->identities = xrealloc(tab->identities, + (tab->nentries + 1) * sizeof(Identity)); + n = key_new(KEY_RSA1); + BN_copy(n->rsa->n, k->rsa->n); + BN_copy(n->rsa->e, k->rsa->e); + RSA_set_method(n->rsa, sc_get_engine()); + tab->identities[tab->nentries].key = n; + tab->identities[tab->nentries].comment = + xstrdup("rsa1 smartcard"); + tab->nentries++; + } + tab = idtab_lookup(2); + if (lookup_private_key(k, NULL, 2) == NULL) { + if (tab->nentries == 0) + tab->identities = xmalloc(sizeof(Identity)); + else + tab->identities = xrealloc(tab->identities, + (tab->nentries + 1) * sizeof(Identity)); + n = key_new(KEY_RSA); + BN_copy(n->rsa->n, k->rsa->n); + BN_copy(n->rsa->e, k->rsa->e); + RSA_set_method(n->rsa, sc_get_engine()); + tab->identities[tab->nentries].key = n; + tab->identities[tab->nentries].comment = + xstrdup("rsa smartcard"); + tab->nentries++; + } + key_free(k); +send: + buffer_put_int(&e->output, 1); + buffer_put_char(&e->output, + success ? SSH_AGENT_SUCCESS : SSH_AGENT_FAILURE); +} + +static void +process_remove_smartcard_key(SocketEntry *e) +{ + Key *k = NULL, *private; + int idx; + int success = 0; + int sc_reader_num = 0; + + sc_reader_num = buffer_get_int(&e->input); + + if ((k = sc_get_key(sc_reader_num)) == NULL) { + error("sc_get_pubkey failed"); + } else { + private = lookup_private_key(k, &idx, 1); + if (private != NULL) { + Idtab *tab = idtab_lookup(1); + key_free(tab->identities[idx].key); + xfree(tab->identities[idx].comment); + if (idx != tab->nentries) + tab->identities[idx] = tab->identities[tab->nentries]; + tab->nentries--; + success = 1; + } + private = lookup_private_key(k, &idx, 2); + if (private != NULL) { + Idtab *tab = idtab_lookup(2); + key_free(tab->identities[idx].key); + xfree(tab->identities[idx].comment); + if (idx != tab->nentries) + tab->identities[idx] = tab->identities[tab->nentries]; + tab->nentries--; + success = 1; + } + key_free(k); + } + + buffer_put_int(&e->output, 1); + buffer_put_char(&e->output, + success ? SSH_AGENT_SUCCESS : SSH_AGENT_FAILURE); +} +#endif + /* dispatch incoming messages */ static void @@ -458,6 +563,7 @@ process_message(SocketEntry *e) buffer_consume(&e->input, 4); type = buffer_get_char(&e->input); + debug("type %d", type); switch (type) { /* ssh1 */ case SSH_AGENTC_RSA_CHALLENGE: @@ -491,6 +597,14 @@ process_message(SocketEntry *e) case SSH2_AGENTC_REMOVE_ALL_IDENTITIES: process_remove_all_identities(e, 2); break; +#ifdef SMARTCARD + case SSH_AGENTC_ADD_SMARTCARD_KEY: + process_add_smartcard_key(e); + break; + case SSH_AGENTC_REMOVE_SMARTCARD_KEY: + process_remove_smartcard_key(e); + break; +#endif default: /* Unknown message. Respond with failure. */ error("Unknown message %d", type); diff --git a/usr.bin/ssh/ssh-agent/Makefile b/usr.bin/ssh/ssh-agent/Makefile index e1a2e2c6cbf..c397bf1ed1d 100644 --- a/usr.bin/ssh/ssh-agent/Makefile +++ b/usr.bin/ssh/ssh-agent/Makefile @@ -1,4 +1,4 @@ -# $OpenBSD: Makefile,v 1.17 2001/03/04 00:51:25 markus Exp $ +# $OpenBSD: Makefile,v 1.18 2001/06/26 05:33:35 markus Exp $ .PATH: ${.CURDIR}/.. @@ -16,3 +16,8 @@ SRCS= ssh-agent.c LDADD+= -lcrypto DPADD+= ${LIBCRYPTO} + +.if (${SMARTCARD:L} == "yes") +CFLAGS+= -DSMARTCARD +LDADD+= -lsectok +.endif # SMARTCARD diff --git a/usr.bin/ssh/ssh-keygen/Makefile b/usr.bin/ssh/ssh-keygen/Makefile index b7c115169c8..2e6c112822e 100644 --- a/usr.bin/ssh/ssh-keygen/Makefile +++ b/usr.bin/ssh/ssh-keygen/Makefile @@ -1,4 +1,4 @@ -# $OpenBSD: Makefile,v 1.17 2001/03/04 00:51:26 markus Exp $ +# $OpenBSD: Makefile,v 1.18 2001/06/26 05:33:36 markus Exp $ .PATH: ${.CURDIR}/.. @@ -16,3 +16,8 @@ SRCS= ssh-keygen.c LDADD+= -lcrypto DPADD+= ${LIBCRYPTO} + +.if (${SMARTCARD:L} == "yes") +CFLAGS+= -DSMARTCARD +LDADD+= -lsectok +.endif # SMARTCARD -- cgit v1.2.3