diff -up openssh-5.1p1/key.c.nss-keys openssh-5.1p1/key.c --- openssh-5.1p1/key.c.nss-keys 2008-07-11 09:35:09.000000000 +0200 +++ openssh-5.1p1/key.c 2008-11-18 19:11:41.000000000 +0100 @@ -96,6 +96,54 @@ key_new(int type) return k; } +#ifdef HAVE_LIBNSS +Key * +key_new_nss(int type) +{ + Key *k = key_new(type); + + k->nss = xcalloc(1, sizeof(*k->nss)); + k->flags = KEY_FLAG_EXT | KEY_FLAG_NSS; + + return k; +} + +Key * +key_new_nss_copy(int type, const Key *c) +{ + Key *k = key_new_nss(type); + + switch (k->type) { + case KEY_RSA: + if ((BN_copy(k->rsa->n, c->rsa->n) == NULL) || + (BN_copy(k->rsa->e, c->rsa->e) == NULL)) + fatal("key_new_nss_copy: BN_copy failed"); + break; + case KEY_DSA: + if ((BN_copy(k->dsa->p, c->rsa->p) == NULL) || + (BN_copy(k->dsa->q, c->dsa->q) == NULL) || + (BN_copy(k->dsa->g, c->dsa->g) == NULL) || + (BN_copy(k->dsa->pub_key, c->dsa->pub_key) == NULL)) + fatal("key_new_nss_copy: BN_copy failed"); + break; + } + + k->nss->privk = SECKEY_CopyPrivateKey(c->nss->privk); + if (k->nss->privk == NULL) + fatal("key_new_nss_copy: SECKEY_CopyPrivateKey failed"); + + k->nss->pubk = SECKEY_CopyPublicKey(c->nss->pubk); + if (k->nss->pubk == NULL) + fatal("key_new_nss_copy: SECKEY_CopyPublicKey failed"); + + if (c->nss->privk->wincx) + k->nss->privk->wincx = xstrdup(c->nss->privk->wincx); + + return k; +} +#endif + + Key * key_new_private(int type) { @@ -151,6 +199,19 @@ key_free(Key *k) fatal("key_free: bad key type %d", k->type); break; } +#ifdef HAVE_LIBNSS + if (k->flags & KEY_FLAG_NSS) { + if (k->nss->privk != NULL && k->nss->privk->wincx != NULL) { + memset(k->nss->privk->wincx, 0, + strlen(k->nss->privk->wincx)); + xfree(k->nss->privk->wincx); + k->nss->privk->wincx = NULL; + } + SECKEY_DestroyPrivateKey(k->nss->privk); + SECKEY_DestroyPublicKey(k->nss->pubk); + xfree(k->nss); + } +#endif xfree(k); } diff -up openssh-5.1p1/ssh-dss.c.nss-keys openssh-5.1p1/ssh-dss.c --- openssh-5.1p1/ssh-dss.c.nss-keys 2006-11-07 13:14:42.000000000 +0100 +++ openssh-5.1p1/ssh-dss.c 2008-11-18 19:11:41.000000000 +0100 @@ -39,6 +39,10 @@ #include "log.h" #include "key.h" +#ifdef HAVE_LIBNSS +#include +#endif + #define INTBLOB_LEN 20 #define SIGBLOB_LEN (2*INTBLOB_LEN) @@ -57,6 +61,34 @@ ssh_dss_sign(const Key *key, u_char **si error("ssh_dss_sign: no DSA key"); return -1; } +#ifdef HAVE_LIBNSS + if (key->flags & KEY_FLAG_NSS) { + SECItem sigitem; + SECItem *rawsig; + + memset(&sigitem, 0, sizeof(sigitem)); + if (SEC_SignData(&sigitem, (u_char *)data, datalen, key->nss->privk, + SEC_OID_ANSIX9_DSA_SIGNATURE_WITH_SHA1_DIGEST) != SECSuccess) { + error("ssh_dss_sign: sign failed"); + return -1; + } + + if ((rawsig=DSAU_DecodeDerSig(&sigitem)) == NULL) { + error("ssh_dss_sign: der decode failed"); + SECITEM_ZfreeItem(&sigitem, PR_FALSE); + return -1; + } + SECITEM_ZfreeItem(&sigitem, PR_FALSE); + if (rawsig->len != SIGBLOB_LEN) { + error("ssh_dss_sign: unsupported signature length %d", + rawsig->len); + SECITEM_ZfreeItem(rawsig, PR_TRUE); + return -1; + } + memcpy(sigblob, rawsig->data, SIGBLOB_LEN); + SECITEM_ZfreeItem(rawsig, PR_TRUE); + } else { +#endif EVP_DigestInit(&md, evp_md); EVP_DigestUpdate(&md, data, datalen); EVP_DigestFinal(&md, digest, &dlen); @@ -80,7 +112,9 @@ ssh_dss_sign(const Key *key, u_char **si BN_bn2bin(sig->r, sigblob+ SIGBLOB_LEN - INTBLOB_LEN - rlen); BN_bn2bin(sig->s, sigblob+ SIGBLOB_LEN - slen); DSA_SIG_free(sig); - +#ifdef HAVE_LIBNSS + } +#endif if (datafellows & SSH_BUG_SIGBLOB) { if (lenp != NULL) *lenp = SIGBLOB_LEN; diff -up openssh-5.1p1/ssh-agent.c.nss-keys openssh-5.1p1/ssh-agent.c --- openssh-5.1p1/ssh-agent.c.nss-keys 2008-07-04 15:10:49.000000000 +0200 +++ openssh-5.1p1/ssh-agent.c 2008-11-18 19:11:41.000000000 +0100 @@ -80,6 +80,10 @@ #include "scard.h" #endif +#ifdef HAVE_LIBNSS +#include "nsskeys.h" +#endif + #if defined(HAVE_SYS_PRCTL_H) #include /* For prctl() and PR_SET_DUMPABLE */ #endif @@ -714,6 +718,114 @@ send: } #endif /* SMARTCARD */ +#ifdef HAVE_LIBNSS +static void +process_add_nss_key (SocketEntry *e) +{ + char *tokenname = NULL, *keyname = NULL, *password = NULL; + int i, version, success = 0, death = 0, confirm = 0; + Key **keys, *k; + Identity *id; + Idtab *tab; + + tokenname = buffer_get_string(&e->request, NULL); + keyname = buffer_get_string(&e->request, NULL); + password = buffer_get_string(&e->request, NULL); + + while (buffer_len(&e->request)) { + switch (buffer_get_char(&e->request)) { + case SSH_AGENT_CONSTRAIN_LIFETIME: + death = time(NULL) + buffer_get_int(&e->request); + break; + case SSH_AGENT_CONSTRAIN_CONFIRM: + confirm = 1; + break; + default: + break; + } + } + if (lifetime && !death) + death = time(NULL) + lifetime; + + keys = nss_get_keys(tokenname, keyname, password); + /* password is owned by keys[0] now */ + xfree(tokenname); + xfree(keyname); + + if (keys == NULL) { + memset(password, 0, strlen(password)); + xfree(password); + error("nss_get_keys failed"); + goto send; + } + for (i = 0; keys[i] != NULL; i++) { + k = keys[i]; + version = k->type == KEY_RSA1 ? 1 : 2; + tab = idtab_lookup(version); + if (lookup_identity(k, version) == NULL) { + id = xmalloc(sizeof(Identity)); + id->key = k; + id->comment = nss_get_key_label(k); + id->death = death; + id->confirm = confirm; + TAILQ_INSERT_TAIL(&tab->idlist, id, next); + tab->nentries++; + success = 1; + } else { + key_free(k); + } + keys[i] = NULL; + } + xfree(keys); +send: + buffer_put_int(&e->output, 1); + buffer_put_char(&e->output, + success ? SSH_AGENT_SUCCESS : SSH_AGENT_FAILURE); +} + +static void +process_remove_nss_key(SocketEntry *e) +{ + char *tokenname = NULL, *keyname = NULL, *password = NULL; + int i, version, success = 0; + Key **keys, *k = NULL; + Identity *id; + Idtab *tab; + + tokenname = buffer_get_string(&e->request, NULL); + keyname = buffer_get_string(&e->request, NULL); + password = buffer_get_string(&e->request, NULL); + + keys = nss_get_keys(tokenname, keyname, password); + xfree(tokenname); + xfree(keyname); + xfree(password); + + if (keys == NULL || keys[0] == NULL) { + error("nss_get_keys failed"); + goto send; + } + for (i = 0; keys[i] != NULL; i++) { + k = keys[i]; + version = k->type == KEY_RSA1 ? 1 : 2; + if ((id = lookup_identity(k, version)) != NULL) { + tab = idtab_lookup(version); + TAILQ_REMOVE(&tab->idlist, id, next); + tab->nentries--; + free_identity(id); + success = 1; + } + key_free(k); + keys[i] = NULL; + } + xfree(keys); +send: + buffer_put_int(&e->output, 1); + buffer_put_char(&e->output, + success ? SSH_AGENT_SUCCESS : SSH_AGENT_FAILURE); +} +#endif /* HAVE_LIBNSS */ + /* dispatch incoming messages */ static void @@ -806,6 +918,15 @@ process_message(SocketEntry *e) process_remove_smartcard_key(e); break; #endif /* SMARTCARD */ +#ifdef HAVE_LIBNSS + case SSH_AGENTC_ADD_NSS_KEY: + case SSH_AGENTC_ADD_NSS_KEY_CONSTRAINED: + process_add_nss_key(e); + break; + case SSH_AGENTC_REMOVE_NSS_KEY: + process_remove_nss_key(e); + break; +#endif /* SMARTCARD */ default: /* Unknown message. Respond with failure. */ error("Unknown message %d", type); diff -up openssh-5.1p1/authfd.h.nss-keys openssh-5.1p1/authfd.h --- openssh-5.1p1/authfd.h.nss-keys 2006-08-05 04:39:39.000000000 +0200 +++ openssh-5.1p1/authfd.h 2008-11-18 19:11:41.000000000 +0100 @@ -49,6 +49,12 @@ #define SSH2_AGENTC_ADD_ID_CONSTRAINED 25 #define SSH_AGENTC_ADD_SMARTCARD_KEY_CONSTRAINED 26 +/* nss */ +#define SSH_AGENTC_ADD_NSS_KEY 30 +#define SSH_AGENTC_REMOVE_NSS_KEY 31 +#define SSH_AGENTC_ADD_NSS_KEY_CONSTRAINED 32 + + #define SSH_AGENT_CONSTRAIN_LIFETIME 1 #define SSH_AGENT_CONSTRAIN_CONFIRM 2 @@ -83,6 +89,8 @@ int ssh_remove_all_identities(Authentic int ssh_lock_agent(AuthenticationConnection *, int, const char *); int ssh_update_card(AuthenticationConnection *, int, const char *, const char *, u_int, u_int); +int ssh_update_nss_key(AuthenticationConnection *, int, const char *, + const char *, const char *, u_int, u_int); int ssh_decrypt_challenge(AuthenticationConnection *, Key *, BIGNUM *, u_char[16], diff -up openssh-5.1p1/configure.ac.nss-keys openssh-5.1p1/configure.ac --- openssh-5.1p1/configure.ac.nss-keys 2008-11-18 19:11:41.000000000 +0100 +++ openssh-5.1p1/configure.ac 2008-11-18 19:12:38.000000000 +0100 @@ -3436,6 +3436,20 @@ AC_ARG_WITH(kerberos5, ] ) +# Check whether user wants NSS support +LIBNSS_MSG="no" +AC_ARG_WITH(nss, + [ --with-nss Enable NSS support], + [ if test "x$withval" != "xno" ; then + AC_DEFINE(HAVE_LIBNSS,1,[Define if you want NSS support.]) + LIBNSS_MSG="yes" + CPPFLAGS="$CPPFLAGS -I/usr/include/nss3 -I/usr/include/nspr4" + AC_CHECK_HEADERS(pk11pub.h) + LIBS="$LIBS -lnss3" + fi + ]) +AC_SUBST(LIBNSS) + # Looking for programs, paths and files PRIVSEP_PATH=/var/empty @@ -4163,6 +4177,7 @@ echo " TCP Wrappers support echo " MD5 password support: $MD5_MSG" echo " libedit support: $LIBEDIT_MSG" echo " Solaris process contract support: $SPC_MSG" +echo " NSS support: $LIBNSS_MSG" echo " IP address in \$DISPLAY hack: $DISPLAY_HACK_MSG" echo " Translate v4 in v6 hack: $IPV4_IN6_HACK_MSG" echo " BSD Auth support: $BSD_AUTH_MSG" diff -up /dev/null openssh-5.1p1/README.nss --- /dev/null 2008-11-17 17:51:52.160001870 +0100 +++ openssh-5.1p1/README.nss 2008-11-18 19:11:41.000000000 +0100 @@ -0,0 +1,36 @@ +How to use NSS tokens with OpenSSH? + +This version of OpenSSH contains experimental support for authentication using +keys stored in tokens stored in NSS database. This for example includes any +PKCS#11 tokens which are installed in your NSS database. + +As the code is experimental and preliminary only SSH protocol 2 is supported. +The NSS certificate and token databases are looked for in the ~/.ssh +directory or in a directory specified by environment variable NSS_DB_PATH. + +Common operations: + +(1) tell the ssh client to use the NSS keys: + + $ ssh -o 'UseNSS yes' otherhost + + if you want to use a specific token: + + $ ssh -o 'UseNSS yes' -o 'NSS Token My PKCS11 Token' otherhost + +(2) or tell the agent to use the NSS keys: + + $ ssh-add -n + + if you want to use a specific token: + + $ ssh-add -n -T 'My PKCS11 Token' + +(3) extract the public key from token so it can be added to the +server: + + $ ssh-keygen -n + + if you want to use a specific token and/or key: + + $ ssh-keygen -n -D 'My PKCS11 Token' 'My Key ID' diff -up openssh-5.1p1/authfd.c.nss-keys openssh-5.1p1/authfd.c --- openssh-5.1p1/authfd.c.nss-keys 2006-09-01 07:38:36.000000000 +0200 +++ openssh-5.1p1/authfd.c 2008-11-18 19:11:41.000000000 +0100 @@ -626,6 +626,45 @@ ssh_update_card(AuthenticationConnection return decode_reply(type); } +int +ssh_update_nss_key(AuthenticationConnection *auth, int add, + const char *tokenname, const char *keyname, + const char *pass, u_int life, u_int confirm) +{ + Buffer msg; + int type, constrained = (life || confirm); + + if (add) { + type = constrained ? + SSH_AGENTC_ADD_NSS_KEY_CONSTRAINED : + SSH_AGENTC_ADD_NSS_KEY; + } else + type = SSH_AGENTC_REMOVE_NSS_KEY; + + buffer_init(&msg); + buffer_put_char(&msg, type); + buffer_put_cstring(&msg, tokenname); + buffer_put_cstring(&msg, keyname); + buffer_put_cstring(&msg, pass); + + if (constrained) { + if (life != 0) { + buffer_put_char(&msg, SSH_AGENT_CONSTRAIN_LIFETIME); + buffer_put_int(&msg, life); + } + if (confirm != 0) + buffer_put_char(&msg, SSH_AGENT_CONSTRAIN_CONFIRM); + } + + if (ssh_request_reply(auth, &msg, &msg) == 0) { + buffer_free(&msg); + return 0; + } + type = buffer_get_char(&msg); + buffer_free(&msg); + return decode_reply(type); +} + /* * Removes all identities from the agent. This call is not meant to be used * by normal applications. diff -up openssh-5.1p1/readconf.h.nss-keys openssh-5.1p1/readconf.h --- openssh-5.1p1/readconf.h.nss-keys 2008-06-29 16:04:03.000000000 +0200 +++ openssh-5.1p1/readconf.h 2008-11-18 19:11:41.000000000 +0100 @@ -84,6 +84,8 @@ typedef struct { char *preferred_authentications; char *bind_address; /* local socket address for connection to sshd */ char *smartcard_device; /* Smartcard reader device */ + int use_nss; /* Use NSS library for keys */ + char *nss_token; /* Look for NSS keys on token */ int verify_host_key_dns; /* Verify host key using DNS */ int num_identity_files; /* Number of files for RSA/DSA identities. */ diff -up /dev/null openssh-5.1p1/nsskeys.c --- /dev/null 2008-11-17 17:51:52.160001870 +0100 +++ openssh-5.1p1/nsskeys.c 2008-11-18 19:11:41.000000000 +0100 @@ -0,0 +1,327 @@ +/* + * Copyright (c) 2001 Markus Friedl. All rights reserved. + * Copyright (c) 2007 Red Hat, Inc. 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. + */ + +#include "includes.h" +#ifdef HAVE_LIBNSS + +#include + +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include "xmalloc.h" +#include "key.h" +#include "log.h" +#include "misc.h" +#include "nsskeys.h" +#include "pathnames.h" + +static char * +password_cb(PK11SlotInfo *slot, PRBool retry, void *arg) +{ + char *password = arg; + if (retry || password == NULL) + return NULL; + + return PL_strdup(password); +} + +int +nss_init(PK11PasswordFunc pwfn) +{ + char *dbpath; + char buf[MAXPATHLEN]; + + if (NSS_IsInitialized()) + return 0; + + if ((dbpath=getenv("NSS_DB_PATH")) == NULL) { + struct passwd *pw; + if ((pw = getpwuid(getuid())) == NULL || + pw->pw_dir == NULL) { + return -1; + } + snprintf(buf, sizeof(buf), "%s/%s", pw->pw_dir, + _PATH_SSH_USER_DIR); + dbpath = buf; + } + + if (NSS_Init(dbpath) != SECSuccess) + return -1; + + if (pwfn == NULL) { + pwfn = password_cb; + } + + PK11_SetPasswordFunc(pwfn); + + return 0; +} + +static Key * +make_key_from_privkey(SECKEYPrivateKey *privk, char *password) +{ + Key *k; + switch (SECKEY_GetPrivateKeyType(privk)) { + case rsaKey: + k = key_new_nss(KEY_RSA); + break; + case dsaKey: + k = key_new_nss(KEY_DSA); + break; + default: + return NULL; + } + k->nss->pubk = SECKEY_ConvertToPublicKey(privk); + if (k->nss->pubk != NULL) { + k->nss->privk = SECKEY_CopyPrivateKey(privk); + } + if (k->nss->privk != NULL) { + if (password != NULL) { + k->nss->privk->wincx = xstrdup(password); + } + return k; + } + key_free(k); + return NULL; +} + +static Key ** +add_key_to_list(Key *k, Key **keys, size_t *i, size_t *allocated) +{ + if (*allocated < *i + 2) { + *allocated += 16; + keys = xrealloc(keys, *allocated, sizeof(k)); + } + keys[*i] = k; + (*i)++; + keys[*i] = NULL; + return keys; +} + +static int +nss_convert_pubkey(Key *k) +{ + u_char *n; + unsigned int len; + char *p; + + switch (k->type) { + case KEY_RSA: + n = k->nss->pubk->u.rsa.modulus.data; + len = k->nss->pubk->u.rsa.modulus.len; + + if (BN_bin2bn(n, len, k->rsa->n) == NULL) { + fatal("nss_convert_pubkey: BN_bin2bn failed"); + } + + n = k->nss->pubk->u.rsa.publicExponent.data; + len = k->nss->pubk->u.rsa.publicExponent.len; + + if (BN_bin2bn(n, len, k->rsa->e) == NULL) { + fatal("nss_convert_pubkey: BN_bin2bn failed"); + } + break; + case KEY_DSA: + n = k->nss->pubk->u.dsa.params.prime.data; + len = k->nss->pubk->u.dsa.params.prime.len; + + if (BN_bin2bn(n, len, k->dsa->p) == NULL) { + fatal("nss_convert_pubkey: BN_bin2bn failed"); + } + + n = k->nss->pubk->u.dsa.params.subPrime.data; + len = k->nss->pubk->u.dsa.params.subPrime.len; + + if (BN_bin2bn(n, len, k->dsa->q) == NULL) { + fatal("nss_convert_pubkey: BN_bin2bn failed"); + } + + n = k->nss->pubk->u.dsa.params.base.data; + len = k->nss->pubk->u.dsa.params.base.len; + + if (BN_bin2bn(n, len, k->dsa->g) == NULL) { + fatal("nss_convert_pubkey: BN_bin2bn failed"); + } + + n = k->nss->pubk->u.dsa.publicValue.data; + len = k->nss->pubk->u.dsa.publicValue.len; + + if (BN_bin2bn(n, len, k->dsa->pub_key) == NULL) { + fatal("nss_convert_pubkey: BN_bin2bn failed"); + } + break; + } + + p = key_fingerprint(k, SSH_FP_MD5, SSH_FP_HEX); + debug("fingerprint %u %s", key_size(k), p); + xfree(p); + + return 0; +} + +static Key ** +nss_find_privkeys(const char *tokenname, const char *keyname, + char *password) +{ + Key *k = NULL; + Key **keys = NULL; + PK11SlotList *slots; + PK11SlotListElement *sle; + size_t allocated = 0; + size_t i = 0; + + if ((slots=PK11_FindSlotsByNames(NULL, NULL, tokenname, PR_TRUE)) == NULL) { + if (tokenname == NULL) { + debug("No NSS token found"); + } else { + debug("NSS token not found: %s", tokenname); + } + return NULL; + } + + for (sle = slots->head; sle; sle = sle->next) { + SECKEYPrivateKeyList *list; + SECKEYPrivateKeyListNode *node; + char *tmppass = password; + + if (PK11_NeedLogin(sle->slot)) { + if (password == NULL) { + char *prompt; + if (asprintf(&prompt, "Enter passphrase for token %s: ", + PK11_GetTokenName(sle->slot)) < 0) + fatal("password_cb: asprintf failed"); + tmppass = read_passphrase(prompt, RP_ALLOW_STDIN); + } + PK11_Authenticate(sle->slot, PR_TRUE, tmppass); + } + + debug("Looking for: %s:%s", tokenname, keyname); + list = PK11_ListPrivKeysInSlot(sle->slot, (char *)keyname, + tmppass); + if (list == NULL && keyname != NULL) { + char *fooname; + /* NSS bug workaround */ + if (asprintf(&fooname, "%s~", keyname) < 0) { + error("nss_find_privkey: asprintf failed"); + PK11_FreeSlotList(slots); + return NULL; + } + list = PK11_ListPrivKeysInSlot(sle->slot, fooname, + tmppass); + free(fooname); + } + if (list == NULL && keyname != NULL) { + CERTCertificate *cert; + SECKEYPrivateKey *privk; + cert = CERT_FindCertByNickname(CERT_GetDefaultCertDB(), + (char *)keyname); + if (cert == NULL) + goto cleanup; + privk = PK11_FindPrivateKeyFromCert(sle->slot, cert, tmppass); + CERT_DestroyCertificate(cert); + if (privk == NULL) + goto cleanup; + if ((k=make_key_from_privkey(privk, tmppass)) != NULL) { + nss_convert_pubkey(k); + keys = add_key_to_list(k, keys, &i, &allocated); + } + SECKEY_DestroyPrivateKey(privk); + } else { + if (list == NULL) + goto cleanup; + for (node=PRIVKEY_LIST_HEAD(list); !PRIVKEY_LIST_END(node, list); + node=PRIVKEY_LIST_NEXT(node)) + if ((k=make_key_from_privkey(node->key, tmppass)) != NULL) { + nss_convert_pubkey(k); + keys = add_key_to_list(k, keys, &i, &allocated); + } + SECKEY_DestroyPrivateKeyList(list); + } +cleanup: + if (password == NULL && tmppass != NULL) { + memset(tmppass, 0, strlen(tmppass)); + xfree(tmppass); + } + } + PK11_FreeSlotList(slots); + + return keys; +} + +Key ** +nss_get_keys(const char *tokenname, const char *keyname, + char *password) +{ + Key **keys; + + if (nss_init(NULL) == -1) { + error("Failed to initialize NSS library"); + return NULL; + } + + keys = nss_find_privkeys(tokenname, keyname, password); + if (keys == NULL && keyname != NULL) { + error("Cannot find key in nss, token removed"); + return NULL; + } +#if 0 + keys = xcalloc(3, sizeof(Key *)); + + if (k->type == KEY_RSA) { + n = key_new_nss_copy(KEY_RSA1, k); + + keys[0] = n; + keys[1] = k; + keys[2] = NULL; + } else { + keys[0] = k; + keys[1] = NULL; + } +#endif + return keys; +} + +char * +nss_get_key_label(Key *key) +{ + char *label, *nickname; + + nickname = PK11_GetPrivateKeyNickname(key->nss->privk); + label = xstrdup(nickname); + PORT_Free(nickname); + + return label; +} + +#endif /* HAVE_LIBNSS */ diff -up openssh-5.1p1/ssh.c.nss-keys openssh-5.1p1/ssh.c --- openssh-5.1p1/ssh.c.nss-keys 2008-07-04 04:53:50.000000000 +0200 +++ openssh-5.1p1/ssh.c 2008-11-18 19:11:41.000000000 +0100 @@ -104,6 +104,9 @@ #ifdef SMARTCARD #include "scard.h" #endif +#ifdef HAVE_LIBNSS +#include "nsskeys.h" +#endif extern char *__progname; @@ -1235,9 +1238,11 @@ load_public_identity_files(void) int i = 0; Key *public; struct passwd *pw; -#ifdef SMARTCARD +#if defined(SMARTCARD) || defined(HAVE_LIBNSS) Key **keys; +#endif +#ifdef SMARTCARD if (options.smartcard_device != NULL && options.num_identity_files < SSH_MAX_IDENTITY_FILES && (keys = sc_get_keys(options.smartcard_device, NULL)) != NULL) { @@ -1260,6 +1265,27 @@ load_public_identity_files(void) xfree(keys); } #endif /* SMARTCARD */ +#ifdef HAVE_LIBNSS + if (options.use_nss && + options.num_identity_files < SSH_MAX_IDENTITY_FILES && + (keys = nss_get_keys(options.nss_token, NULL, NULL)) != NULL) { + int count; + for (count = 0; keys[count] != NULL; count++) { + memmove(&options.identity_files[1], &options.identity_files[0], + sizeof(char *) * (SSH_MAX_IDENTITY_FILES - 1)); + memmove(&options.identity_keys[1], &options.identity_keys[0], + sizeof(Key *) * (SSH_MAX_IDENTITY_FILES - 1)); + options.num_identity_files++; + options.identity_keys[0] = keys[count]; + options.identity_files[0] = nss_get_key_label(keys[count]); + } + if (options.num_identity_files > SSH_MAX_IDENTITY_FILES) + options.num_identity_files = SSH_MAX_IDENTITY_FILES; + i += count; + xfree(keys); + } +#endif /* HAVE_LIBNSS */ + if ((pw = getpwuid(original_real_uid)) == NULL) fatal("load_public_identity_files: getpwuid failed"); pwname = xstrdup(pw->pw_name); diff -up /dev/null openssh-5.1p1/nsskeys.h --- /dev/null 2008-11-17 17:51:52.160001870 +0100 +++ openssh-5.1p1/nsskeys.h 2008-11-18 19:11:41.000000000 +0100 @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2001 Markus Friedl. All rights reserved. + * Copyright (c) 2007 Red Hat, Inc. 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. + */ + +#ifndef NSSKEYS_H +#define NSSKEYS_H +#ifdef HAVE_LIBNSS +#include +#include + +int nss_init(PK11PasswordFunc); +Key **nss_get_keys(const char *, const char *, char *); +char *nss_get_key_label(Key *); +/*void sc_close(void);*/ +/*int sc_put_key(Key *, const char *);*/ + +#endif +#endif diff -up openssh-5.1p1/Makefile.in.nss-keys openssh-5.1p1/Makefile.in --- openssh-5.1p1/Makefile.in.nss-keys 2008-07-08 16:21:12.000000000 +0200 +++ openssh-5.1p1/Makefile.in 2008-11-18 19:11:41.000000000 +0100 @@ -71,7 +71,7 @@ LIBSSH_OBJS=acss.o authfd.o authfile.o b atomicio.o key.o dispatch.o kex.o mac.o uidswap.o uuencode.o misc.o \ monitor_fdpass.o rijndael.o ssh-dss.o ssh-rsa.o dh.o kexdh.o \ kexgex.o kexdhc.o kexgexc.o scard.o msg.o progressmeter.o dns.o \ - entropy.o scard-opensc.o gss-genr.o umac.o + entropy.o scard-opensc.o gss-genr.o umac.o nsskeys.o SSHOBJS= ssh.o readconf.o clientloop.o sshtty.o \ sshconnect.o sshconnect1.o sshconnect2.o mux.o diff -up openssh-5.1p1/key.h.nss-keys openssh-5.1p1/key.h --- openssh-5.1p1/key.h.nss-keys 2008-06-12 20:40:35.000000000 +0200 +++ openssh-5.1p1/key.h 2008-11-18 19:11:41.000000000 +0100 @@ -29,11 +29,17 @@ #include #include +#ifdef HAVE_LIBNSS +#include +#include +#endif + typedef struct Key Key; enum types { KEY_RSA1, KEY_RSA, KEY_DSA, + KEY_NSS, KEY_UNSPEC }; enum fp_type { @@ -48,16 +54,30 @@ enum fp_rep { /* key is stored in external hardware */ #define KEY_FLAG_EXT 0x0001 +#define KEY_FLAG_NSS 0x0002 + +#ifdef HAVE_LIBNSS +typedef struct NSSKey NSSKey; +struct NSSKey { + SECKEYPrivateKey *privk; + SECKEYPublicKey *pubk; +}; +#endif struct Key { int type; int flags; RSA *rsa; DSA *dsa; +#ifdef HAVE_LIBNSS + NSSKey *nss; +#endif }; Key *key_new(int); Key *key_new_private(int); +Key *key_new_nss(int); +Key *key_new_nss_copy(int, const Key *); void key_free(Key *); Key *key_demote(const Key *); int key_equal(const Key *, const Key *); diff -up openssh-5.1p1/ssh-add.c.nss-keys openssh-5.1p1/ssh-add.c --- openssh-5.1p1/ssh-add.c.nss-keys 2008-02-28 09:13:52.000000000 +0100 +++ openssh-5.1p1/ssh-add.c 2008-11-18 19:11:41.000000000 +0100 @@ -44,6 +44,14 @@ #include #include "openbsd-compat/openssl-compat.h" +#ifdef HAVE_LIBNSS +#include +#include +#include +#include +#include +#endif + #include #include #include @@ -57,6 +65,7 @@ #include "rsa.h" #include "log.h" #include "key.h" +#include "nsskeys.h" #include "buffer.h" #include "authfd.h" #include "authfile.h" @@ -307,6 +316,117 @@ do_file(AuthenticationConnection *ac, in return 0; } +#ifdef HAVE_LIBNSS +static char * +password_cb(PK11SlotInfo *slot, PRBool retry, void *arg) +{ + char **passcache = arg; + char *password, *p2 = NULL; + char *prompt; + + if (retry) + return NULL; + + if (asprintf(&prompt, "Enter passphrase for token %s: ", + PK11_GetTokenName(slot)) < 0) + fatal("password_cb: asprintf failed"); + + password = read_passphrase(prompt, RP_ALLOW_STDIN); + + if (password != NULL && (p2=PL_strdup(password)) == NULL) { + memset(password, 0, strlen(password)); + fatal("password_cb: PL_strdup failed"); + } + + if (passcache != NULL) { + if (*passcache != NULL) { + memset(*passcache, 0, strlen(*passcache)); + xfree(*passcache); + } + *passcache = password; + } else { + memset(password, 0, strlen(password)); + xfree(password); + } + + return p2; +} + +static int +add_slot_keys(AuthenticationConnection *ac, PK11SlotInfo *slot, int add) +{ + SECKEYPrivateKeyList *list; + SECKEYPrivateKeyListNode *node; + char *passcache = NULL; + char *tokenname; + + int count = 0; + + if (PK11_NeedLogin(slot)) + PK11_Authenticate(slot, PR_TRUE, &passcache); + + if ((list=PK11_ListPrivKeysInSlot(slot, NULL, NULL)) == NULL) { + return 0; + } + + tokenname = PK11_GetTokenName(slot); + + for (node=PRIVKEY_LIST_HEAD(list); !PRIVKEY_LIST_END(node, list); + node=PRIVKEY_LIST_NEXT(node)) { + char *keyname; + SECKEYPublicKey *pub; + + keyname = PK11_GetPrivateKeyNickname(node->key); + if (keyname == NULL || *keyname == '\0') { + /* no nickname to refer to */ + CERTCertificate *cert; + char *kn; + cert = PK11_GetCertFromPrivateKey(node->key); + if (cert == NULL) + continue; + kn = strchr(cert->nickname, ':'); + if (kn == NULL) + kn = cert->nickname; + else + kn++; + keyname = PORT_Strdup(kn); + CERT_DestroyCertificate(cert); + if (keyname == NULL) + continue; + } + pub = SECKEY_ConvertToPublicKey(node->key); + if (pub == NULL) { + fprintf(stderr, "No public key for: %s:%s\n", + tokenname, keyname); + continue; /* not possible to obtain public key */ + } + SECKEY_DestroyPublicKey(pub); + + if (ssh_update_nss_key(ac, add, tokenname, keyname, + passcache?passcache:"", lifetime, confirm)) { + fprintf(stderr, "Key %s: %s:%s\n", + add?"added":"removed", tokenname, keyname); + count++; + } else { + fprintf(stderr, "Could not %s key: %s:%s\n", + add?"add":"remove", tokenname, keyname); + } + + PORT_Free(keyname); + count++; + } + + if (passcache != NULL) { + memset(passcache, 0, strlen(passcache)); + xfree(passcache); + } + + SECKEY_DestroyPrivateKeyList(list); + + return count; +} +#endif + static void usage(void) { @@ -334,6 +454,10 @@ main(int argc, char **argv) AuthenticationConnection *ac = NULL; char *sc_reader_id = NULL; int i, ch, deleting = 0, ret = 0; +#ifdef HAVE_LIBNSS + char *token_id = NULL; + int use_nss = 0; +#endif /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */ sanitise_stdfd(); @@ -351,7 +475,7 @@ main(int argc, char **argv) "Could not open a connection to your authentication agent.\n"); exit(2); } - while ((ch = getopt(argc, argv, "lLcdDxXe:s:t:")) != -1) { + while ((ch = getopt(argc, argv, "lLcdDnxXe:s:t:T:")) != -1) { switch (ch) { case 'l': case 'L': @@ -373,6 +497,11 @@ main(int argc, char **argv) if (delete_all(ac) == -1) ret = 1; goto done; +#ifdef HAVE_LIBNSS + case 'n': + use_nss = 1; + break; +#endif case 's': sc_reader_id = optarg; break; @@ -387,6 +516,11 @@ main(int argc, char **argv) goto done; } break; +#ifdef HAVE_LIBNSS + case 'T': + token_id = optarg; + break; +#endif default: usage(); ret = 1; @@ -400,6 +534,40 @@ main(int argc, char **argv) ret = 1; goto done; } +#ifdef HAVE_LIBNSS + if (use_nss) { + PK11SlotList *slots; + PK11SlotListElement *sle; + int count = 0; + if (nss_init(password_cb) == -1) { + fprintf(stderr, "Failed to initialize NSS library\n"); + ret = 1; + goto done; + } + + if ((slots=PK11_GetAllTokens(CKM_INVALID_MECHANISM, PR_FALSE, PR_FALSE, + NULL)) == NULL) { + fprintf(stderr, "No tokens found\n"); + ret = 1; + goto nss_done; + } + + for (sle = slots->head; sle; sle = sle->next) { + int rv; + if ((rv=add_slot_keys(ac, sle->slot, !deleting)) == -1) { + ret = 1; + } + count += rv; + } + if (count == 0) { + ret = 1; + } +nss_done: + NSS_Shutdown(); + clear_pass(); + goto done; + } +#endif if (argc == 0) { char buf[MAXPATHLEN]; struct passwd *pw; diff -up openssh-5.1p1/ssh-rsa.c.nss-keys openssh-5.1p1/ssh-rsa.c --- openssh-5.1p1/ssh-rsa.c.nss-keys 2006-09-01 07:38:37.000000000 +0200 +++ openssh-5.1p1/ssh-rsa.c 2008-11-18 19:11:41.000000000 +0100 @@ -32,6 +32,10 @@ #include "compat.h" #include "ssh.h" +#ifdef HAVE_LIBNSS +#include +#endif + static int openssh_RSA_verify(int, u_char *, u_int, u_char *, u_int, RSA *); /* RSASSA-PKCS1-v1_5 (PKCS #1 v2.0 signature) with SHA1 */ @@ -50,6 +54,38 @@ ssh_rsa_sign(const Key *key, u_char **si error("ssh_rsa_sign: no RSA key"); return -1; } + + slen = RSA_size(key->rsa); + sig = xmalloc(slen); + +#ifdef HAVE_LIBNSS + if (key->flags & KEY_FLAG_NSS) { + SECItem sigitem; + SECOidTag alg; + + memset(&sigitem, 0, sizeof(sigitem)); + alg = (datafellows & SSH_BUG_RSASIGMD5) ? + SEC_OID_PKCS1_MD5_WITH_RSA_ENCRYPTION : + SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION; + + if (SEC_SignData(&sigitem, (u_char *)data, datalen, key->nss->privk, + alg) != SECSuccess) { + error("ssh_rsa_sign: sign failed"); + return -1; + } + if (sigitem.len > slen) { + error("ssh_rsa_sign: slen %u slen2 %u", slen, sigitem.len); + xfree(sig); + SECITEM_ZfreeItem(&sigitem, PR_FALSE); + return -1; + } + if (sigitem.len < slen) { + memset(sig, 0, slen - sigitem.len); + } + memcpy(sig+slen-sigitem.len, sigitem.data, sigitem.len); + SECITEM_ZfreeItem(&sigitem, PR_FALSE); + } else { +#endif nid = (datafellows & SSH_BUG_RSASIGMD5) ? NID_md5 : NID_sha1; if ((evp_md = EVP_get_digestbynid(nid)) == NULL) { error("ssh_rsa_sign: EVP_get_digestbynid %d failed", nid); @@ -59,9 +95,6 @@ ssh_rsa_sign(const Key *key, u_char **si EVP_DigestUpdate(&md, data, datalen); EVP_DigestFinal(&md, digest, &dlen); - slen = RSA_size(key->rsa); - sig = xmalloc(slen); - ok = RSA_sign(nid, digest, dlen, sig, &len, key->rsa); memset(digest, 'd', sizeof(digest)); @@ -83,6 +116,9 @@ ssh_rsa_sign(const Key *key, u_char **si xfree(sig); return -1; } +#ifdef HAVE_LIBNSS + } +#endif /* encode signature */ buffer_init(&b); buffer_put_cstring(&b, "ssh-rsa"); diff -up openssh-5.1p1/ssh-keygen.c.nss-keys openssh-5.1p1/ssh-keygen.c --- openssh-5.1p1/ssh-keygen.c.nss-keys 2008-07-14 03:28:29.000000000 +0200 +++ openssh-5.1p1/ssh-keygen.c 2008-11-18 19:11:41.000000000 +0100 @@ -53,6 +53,11 @@ #include "scard.h" #endif +#ifdef HAVE_LIBNSS +#include +#include "nsskeys.h" +#endif + /* Number of bits in the RSA/DSA key. This value can be set on the command line. */ #define DEFAULT_BITS 2048 #define DEFAULT_BITS_DSA 1024 @@ -501,6 +506,26 @@ do_download(struct passwd *pw, const cha } #endif /* SMARTCARD */ +#ifdef HAVE_LIBNSS +static void +do_nss_download(struct passwd *pw, const char *tokenname, const char *keyname) +{ + Key **keys = NULL; + int i; + + keys = nss_get_keys(tokenname, keyname, NULL); + if (keys == NULL) + fatal("cannot find public key in NSS"); + for (i = 0; keys[i]; i++) { + key_write(keys[i], stdout); + key_free(keys[i]); + fprintf(stdout, "\n"); + } + xfree(keys); + exit(0); +} +#endif /* HAVE_LIBNSS */ + static void do_fingerprint(struct passwd *pw) { @@ -1083,7 +1108,8 @@ main(int argc, char **argv) Key *private, *public; struct passwd *pw; struct stat st; - int opt, type, fd, download = 0; + int opt, type, fd, download = 1; + int use_nss = 0; u_int32_t memory = 0, generator_wanted = 0, trials = 100; int do_gen_candidates = 0, do_screen_candidates = 0; BIGNUM *start = NULL; @@ -1116,7 +1142,7 @@ main(int argc, char **argv) } while ((opt = getopt(argc, argv, - "degiqpclBHvxXyF:b:f:t:U:D:P:N:C:r:g:R:T:G:M:S:a:W:")) != -1) { + "degiqpclnBHvxXyF:b:f:t:U:D:P:N:C:r:g:R:T:G:M:S:a:W:")) != -1) { switch (opt) { case 'b': bits = (u_int32_t)strtonum(optarg, 768, 32768, &errstr); @@ -1156,6 +1182,10 @@ main(int argc, char **argv) case 'g': print_generic = 1; break; + case 'n': + use_nss = 1; + download = 1; + break; case 'P': identity_passphrase = optarg; break; @@ -1187,10 +1217,10 @@ main(int argc, char **argv) case 't': key_type_name = optarg; break; - case 'D': - download = 1; - /*FALLTHROUGH*/ case 'U': + download = 0; + /*FALLTHROUGH*/ + case 'D': reader_id = optarg; break; case 'v': @@ -1299,6 +1329,17 @@ main(int argc, char **argv) exit(0); } } + + if (use_nss) { +#ifdef HAVE_LIBNSS + if (download) + do_nss_download(pw, reader_id, identity_file); + else + fatal("no support for NSS key upload."); +#else + fatal("no support for NSS keys."); +#endif + } if (reader_id != NULL) { #ifdef SMARTCARD if (download) diff -up openssh-5.1p1/readconf.c.nss-keys openssh-5.1p1/readconf.c --- openssh-5.1p1/readconf.c.nss-keys 2008-06-29 16:04:03.000000000 +0200 +++ openssh-5.1p1/readconf.c 2008-11-18 19:11:41.000000000 +0100 @@ -124,6 +124,7 @@ typedef enum { oKbdInteractiveAuthentication, oKbdInteractiveDevices, oHostKeyAlias, oDynamicForward, oPreferredAuthentications, oHostbasedAuthentication, oHostKeyAlgorithms, oBindAddress, oSmartcardDevice, + oUseNSS, oNSSToken, oClearAllForwardings, oNoHostAuthenticationForLocalhost, oEnableSSHKeysign, oRekeyLimit, oVerifyHostKeyDNS, oConnectTimeout, oAddressFamily, oGssAuthentication, oGssDelegateCreds, @@ -210,6 +211,13 @@ static struct { #else { "smartcarddevice", oUnsupported }, #endif +#ifdef HAVE_LIBNSS + { "usenss", oUseNSS }, + { "nsstoken", oNSSToken }, +#else + { "usenss", oUnsupported }, + { "nsstoken", oNSSToken }, +#endif { "clearallforwardings", oClearAllForwardings }, { "enablesshkeysign", oEnableSSHKeysign }, { "verifyhostkeydns", oVerifyHostKeyDNS }, @@ -603,6 +611,14 @@ parse_string: charptr = &options->smartcard_device; goto parse_string; + case oUseNSS: + intptr = &options->use_nss; + goto parse_flag; + + case oNSSToken: + charptr = &options->nss_token; + goto parse_command; + case oProxyCommand: charptr = &options->proxy_command; parse_command: @@ -1055,6 +1071,8 @@ initialize_options(Options * options) options->preferred_authentications = NULL; options->bind_address = NULL; options->smartcard_device = NULL; + options->use_nss = -1; + options->nss_token = NULL; options->enable_ssh_keysign = - 1; options->no_host_authentication_for_localhost = - 1; options->identities_only = - 1; @@ -1184,6 +1202,8 @@ fill_default_options(Options * options) options->no_host_authentication_for_localhost = 0; if (options->identities_only == -1) options->identities_only = 0; + if (options->use_nss == -1) + options->use_nss = 0; if (options->enable_ssh_keysign == -1) options->enable_ssh_keysign = 0; if (options->rekey_limit == -1)