This patch against OpenBSD -current adds a simple form of PKI to OpenSSH. We'll be using it at work. See README.certkey (the first chunk of the patch) for details. Everything below is BSD licensed, sponsored by Allamanda Networks AG. Daniel --- /dev/null Wed Nov 15 15:14:20 2006 +++ README.certkey Wed Nov 15 15:13:45 2006 @@ -0,0 +1,176 @@ +OpenSSH Certkey + +INTRODUCTION + +Certkey allows OpenSSH to transmit certificates from server to client for host +authentication and from client to server for user authentication. Certificates +are basically signatures made by a certificate authority (CA) private key. + +A host certificate is a guarantee made by the CA that a host public key is +valid. When a host public key carries a valid certificate, the client can +use the host public key without asking the user to confirm the fingerprint +manually and through out-of-band communication the first time. The CA takes +the responsibility of verifying host keys, and users do no longer need to +maintain known_hosts files of their own. + +A user certificate is an authorization made by the CA that the holder of a +specific private key may login to the server as a specific user, without the +need of an authorized_keys file being present. The CA gains the power to grant +individual users access to the server, and users do no longer need to maintain +authorized_keys files of their own. + +Functionally, the CA assumes responsibility and control over users' known_hosts +and authorized_keys files. + +Certkey does not involve online verfication, the CA is not contacted by either +client or server. Instead, the CA generates certificates which are (once) +distributed to hosts and users. Any subsequent logins take place without the +involvment of the CA, based solely on the certificates provided between client +and server. + +For example, a company sets up a new host where many existing users need to +login. Traditionally, every one of those users will have to verify the new +host's key the first time they login. Also, each user will have to authorize +their public key on the new host. With Certkey enabled in this case (and +assuming the users have already been certified), this procedure is reduced to +the CA generating a certificate for the new host and installing the +certificate on the new host. + + +SECURITY IMPLICATIONS + +The CA, specifically the holder of the CA private key (and its password, if it +is password encrypted), holds broad control over hosts and user accounts set +up in this way. Should the CA private key become compromised, all user +accounts become compromised. + +There is no way to revoke a certificate once it has been published, the +certificate is valid until it reaches the expiry date set by the CA. + + +CONFIGURATION + +The feature is enabled through the following two options in the client and +server configurations: + + CertkeyAuthentication yes + CAKeyFile /etc/ssh/ca.pub + + +USAGE + +(1) Generating a CA key pair + + # ssh-keygen + Generating public/private rsa key pair. + Enter file in which to save the key (/root/.ssh/id_rsa): /root/.ssh/ca + Enter passphrase (empty for no passphrase): + Enter same passphrase again: + Your identification has been saved in /root/.ssh/ca. + Your public key has been saved in /root/.ssh/ca.pub. + The key fingerprint is: + f2:c7:5c:a9:48:d8:8c:82:24:d5:2a:d6:75:48:ab:3d root at host + +(2) Generating a host certificate + + # ssh-keygen -s + Enter file in which the CA key is (/root/.ssh/id_rsa): /root/.ssh/ca + CA key fingerprint f2:c7:5c:a9:48:d8:8c:82:24:d5:2a:d6:75:48:ab:3d + Enter file in which the user/host key is: /etc/ssh/ssh_host_rsa_key.pub + host/user key fingerprint 68:8c:25:e3:b1:17:8a:7f:0c:19:fa:0d:f7:12:6f:8a + CA name : benzedrine.cx + identity : lenovo.benzedrine.cx + options : + valid from : 0 + valid until: 20061231 + Certificate has been saved in /etc/ssh/ssh_host_rsa_key.cert. + + # cp /root/.ssh/ca.pub /etc/ssh/ca.pub + +(3) Generating a user certificate + + # ssh-keygen -s + Enter file in which the CA key is (/root/.ssh/id_rsa): /root/.ssh/ca + CA key fingerprint f2:c7:5c:a9:48:d8:8c:82:24:d5:2a:d6:75:48:ab:3d + Enter file in which the user/host key is: /home/dhartmei/.ssh/id_dsa.pub + host/user key fingerprint 86:c8:52:3e:b1:17:8a:7f:0c:19:fa:0d:f7:12:f6:a8 + CA name : benzedrine.cx + identity : dhartmei + options : + valid from : 20061101 + valid until: 20071231 + Certificate has been saved in /home/dhartmei/.ssh/id_dsa.cert. + + +IMPLEMENTATION + +Host and user certificates are introduced into the transport layer and +authentication protocol by addition of a new method respectively. + +Transport Layer Protocol + +An additional key exchange method "diffie-hellman-group-exchange-cert" has +been added. This method is completely identical to the existing method +"diffie-hellman-group-exchange-sha1", except for one additional string +(the host certificate), placed at the end of the message, after the signature. + +Authentication Protocol + +An additional authentication method "certkey" has been added. This method is +completely identical to the existing method "publickey", except for one +additional string (the user certificate), placed at the end of the message, +after the signature. + +Certificate format + +Both host and user certificates share the same format. They consist of a single +string, containing values separated by semi-colons, in the following order + + fingerprint;caname;identity;options;validfrom;validto;algorithm;signature + +Values must not contain semi-colons or NUL bytes, but may be empty. + +'fingerprint' is the SSH_FP_MD5 SSH_FP_HEX fingerprint of the RSA key signing +the certificate (the CA key), e.g. the output of ssh-keygen -l for +/etc/ssh/ca.pub. + +'caname' is the name of the CA. This can be used to associate certificates with +CAs. The format is not defined, though using domain names is suggested. + +'identity' is the identity being certified by the CA with this certificate. +For user certificates, this is the user name the certifcate grants login to. +For host certificates, the format is not defined, though using the host's +fully-qualified domain name is suggested. + +'options' may contain additional options, in form of key=value pairs separated +by pipes '|', like 'foo=bar|src=10/8,*.networx.ch|dst=192.168/16'. keys and +values must not contain semi-colons, pipes, '=' or NUL bytes. The meaning of +options is not currently defined, though keys 'src' and 'dst' are reserved for +later implementation of restrictions based on client/server addresses. + +'validfrom' and 'validto' are timestamps (UNIX Epoch time) defining the time +frame the certificate is valid in. When, upon certificate verification, the +current time is outside this period, the certificate is not valid. If zero is +used as value for 'validto', the certificate is valid indefinitely after +'validfrom'. + +'algorithm' defines the hash algorithm used for the signature. Currently, the +only legal value is 'ripemd160'. + +'signature' is the signature itself in hex without colons. The data being +signed consists of + + fingerprint;caname;identity;options;validfrom;validto + +where 'fingerprint' is the SSH_FP_MD5 SSH_FP_HEX fingerprint of the user or +host key (not the CA key). + +Example: + + f2:c7:5c:a9:48:d8:8c:82:24:d5:2a:d6:75:48:ab:3d;networx.ch;dhartmei;; + 1136070000;1451602800;ripemd160;dbd33e932d80b5612...5a0b4759bee451 + +Note that the certificate does not contain any newline characters, it's +wrapped onto two lines here to improve readability. + +$OpenBSD$ Index: auth.h ==================================================================RCS file: /cvs/src/usr.bin/ssh/auth.h,v retrieving revision 1.58 diff -u -r1.58 auth.h --- auth.h 18 Aug 2006 09:15:20 -0000 1.58 +++ auth.h 15 Nov 2006 14:14:32 -0000 @@ -115,6 +115,7 @@ int auth_rhosts_rsa_key_allowed(struct passwd *, char *, char *, Key *); int hostbased_key_allowed(struct passwd *, const char *, char *, Key *); int user_key_allowed(struct passwd *, Key *); +int user_cert_key_allowed(struct passwd *, Key *); #ifdef KRB5 int auth_krb5(Authctxt *authctxt, krb5_data *auth, char **client, krb5_data *); Index: auth2.c ==================================================================RCS file: /cvs/src/usr.bin/ssh/auth2.c,v retrieving revision 1.113 diff -u -r1.113 auth2.c --- auth2.c 3 Aug 2006 03:34:41 -0000 1.113 +++ auth2.c 15 Nov 2006 14:14:32 -0000 @@ -55,6 +55,7 @@ /* methods */ extern Authmethod method_none; +extern Authmethod method_certkey; extern Authmethod method_pubkey; extern Authmethod method_passwd; extern Authmethod method_kbdint; @@ -65,6 +66,7 @@ Authmethod *authmethods[] = { &method_none, + &method_certkey, &method_pubkey, #ifdef GSSAPI &method_gssapi, Index: authfile.c ==================================================================RCS file: /cvs/src/usr.bin/ssh/authfile.c,v retrieving revision 1.76 diff -u -r1.76 authfile.c --- authfile.c 3 Aug 2006 03:34:41 -0000 1.76 +++ authfile.c 15 Nov 2006 14:14:33 -0000 @@ -302,6 +302,43 @@ return pub; } +static void +key_try_load_cert(Key *key, const char *filename) +{ + char fn[MAXPATHLEN]; + int fd; + ssize_t r; + + if (key->cert) { + xfree(key->cert); + key->cert = NULL; + } + if (strlen(filename) > 4 && strlen(filename) < sizeof(fn) && + !strcmp(filename + strlen(filename) - 4, ".pub")) + strlcpy(fn, filename, strlen(filename) - 3); + else + strlcpy(fn, filename, sizeof(fn)); + strlcat(fn, ".cert", sizeof(fn)); + + fd = open(fn, O_RDONLY); + if (fd >= 0) { + key->cert = xmalloc(8192); + if (key->cert) { + r = read(fd, key->cert, 8192); + if (r > 0) { + if (key->cert[r - 1] == '\n') + key->cert[r - 1] = 0; + else + key->cert[r] = 0; + } else { + xfree(key->cert); + key->cert = NULL; + } + } + close(fd); + } +} + /* load public key from private-key file, works only for SSH v1 */ Key * key_load_public_type(int type, const char *filename, char **commentp) @@ -315,6 +352,8 @@ return NULL; pub = key_load_public_rsa1(fd, filename, commentp); close(fd); + if (pub != NULL) + key_try_load_cert(pub, filename); return pub; } return NULL; @@ -604,6 +643,8 @@ /* closes fd */ prv = key_load_private_rsa1(fd, filename, passphrase, NULL); } + if (prv != NULL) + key_try_load_cert(prv, filename); return prv; } @@ -634,6 +675,7 @@ if (commentp) *commentp=xstrdup(filename); fclose(f); + key_try_load_cert(k, filename); return 1; } } Index: kex.c ==================================================================RCS file: /cvs/src/usr.bin/ssh/kex.c,v retrieving revision 1.76 diff -u -r1.76 kex.c --- kex.c 3 Aug 2006 03:34:42 -0000 1.76 +++ kex.c 15 Nov 2006 14:14:33 -0000 @@ -312,6 +312,9 @@ } else if (strcmp(k->name, KEX_DHGEX_SHA256) == 0) { k->kex_type = KEX_DH_GEX_SHA256; k->evp_md = evp_ssh_sha256(); + } else if (strcmp(k->name, KEX_DHGEX_CERT) == 0) { + k->kex_type = KEX_DH_GEX_CERT; + k->evp_md = EVP_sha1(); } else fatal("bad kex alg %s", k->name); } Index: kex.h ==================================================================RCS file: /cvs/src/usr.bin/ssh/kex.h,v retrieving revision 1.44 diff -u -r1.44 kex.h --- kex.h 3 Aug 2006 03:34:42 -0000 1.44 +++ kex.h 15 Nov 2006 14:14:33 -0000 @@ -32,6 +32,7 @@ #define KEX_DH14 "diffie-hellman-group14-sha1" #define KEX_DHGEX_SHA1 "diffie-hellman-group-exchange-sha1" #define KEX_DHGEX_SHA256 "diffie-hellman-group-exchange-sha256" +#define KEX_DHGEX_CERT "diffie-hellman-group-exchange-cert" #define COMP_NONE 0 #define COMP_ZLIB 1 @@ -62,6 +63,7 @@ KEX_DH_GRP14_SHA1, KEX_DH_GEX_SHA1, KEX_DH_GEX_SHA256, + KEX_DH_GEX_CERT, KEX_MAX }; Index: kexgexc.c ==================================================================RCS file: /cvs/src/usr.bin/ssh/kexgexc.c,v retrieving revision 1.11 diff -u -r1.11 kexgexc.c --- kexgexc.c 6 Nov 2006 21:25:28 -0000 1.11 +++ kexgexc.c 15 Nov 2006 14:14:33 -0000 @@ -124,8 +124,6 @@ fatal("type mismatch for decoded server_host_key_blob"); if (kex->verify_host_key == NULL) fatal("cannot verify server_host_key"); - if (kex->verify_host_key(server_host_key) == -1) - fatal("server_host_key verification failed"); /* DH parameter f, server public DH key */ if ((dh_server_pub = BN_new()) == NULL) @@ -141,7 +139,20 @@ /* signed H */ signature = packet_get_string(&slen); + if (kex->kex_type == KEX_DH_GEX_CERT) { + u_char *cert; + u_int len; + + cert = packet_get_string(&len); + if (cert != NULL) { + server_host_key->cert = xstrdup(cert); + xfree(cert); + } + } packet_check_eom(); + + if (kex->verify_host_key(server_host_key) == -1) + fatal("server_host_key verification failed"); if (!dh_pub_is_valid(dh, dh_server_pub)) packet_disconnect("bad server public DH value"); Index: kexgexs.c ==================================================================RCS file: /cvs/src/usr.bin/ssh/kexgexs.c,v retrieving revision 1.10 diff -u -r1.10 kexgexs.c --- kexgexs.c 6 Nov 2006 21:25:28 -0000 1.10 +++ kexgexs.c 15 Nov 2006 14:14:33 -0000 @@ -183,6 +183,13 @@ packet_put_string(server_host_key_blob, sbloblen); packet_put_bignum2(dh->pub_key); /* f */ packet_put_string(signature, slen); + if (kex->kex_type == KEX_DH_GEX_CERT) { + if (server_host_key->cert != NULL) + packet_put_string(server_host_key->cert, + strlen(server_host_key->cert)); + else + packet_put_string("", 0); + } packet_send(); xfree(signature); Index: key.c ==================================================================RCS file: /cvs/src/usr.bin/ssh/key.c,v retrieving revision 1.68 diff -u -r1.68 key.c --- key.c 6 Nov 2006 21:25:28 -0000 1.68 +++ key.c 15 Nov 2006 14:14:33 -0000 @@ -57,6 +57,7 @@ k->type = type; k->dsa = NULL; k->rsa = NULL; + k->cert = NULL; switch (k->type) { case KEY_RSA1: case KEY_RSA: @@ -145,6 +146,9 @@ fatal("key_free: bad key type %d", k->type); break; } + if (k->cert != NULL) + xfree(k->cert); + k->cert = NULL; xfree(k); } @@ -833,6 +837,7 @@ pk->flags = k->flags; pk->dsa = NULL; pk->rsa = NULL; + pk->cert = k->cert ? xstrdup(k->cert) : NULL; switch (k->type) { case KEY_RSA1: @@ -862,4 +867,100 @@ } return (pk); +} + +static void +cert_token(const u_char **c, u_char *buf, int len) +{ + int i = 0; + + while (**c && **c != ';' && i + 1 < len) + buf[i++] = *(*c)++; + if (**c == ';') + (*c)++; + buf[i] = 0; +} + +/* check whether certificate is valid and signature correct */ +int +cert_verify(const u_char *cert, const Key *ca_key, const Key *key, + const u_char *identity) +{ + u_char ca_fp[128], ca_name[128], ca_id[128], ca_opts[512]; + u_char ca_vf[16], ca_vt[16], ca_alg[64], ca_sig[1024]; + u_char sigbuf[1024], datbuf[2048], c, *fp; + unsigned long vf, vt, now = time(NULL); + u_int siglen, i; + + if (cert == NULL || ca_key == NULL || ca_key->type != KEY_RSA || + ca_key->rsa == NULL || key == NULL) { + debug2("cert_verify: invalid arguments"); + return 0; + } + + cert_token(&cert, ca_fp, sizeof(ca_fp)); + cert_token(&cert, ca_name, sizeof(ca_name)); + cert_token(&cert, ca_id, sizeof(ca_id)); + cert_token(&cert, ca_opts, sizeof(ca_opts)); + cert_token(&cert, ca_vf, sizeof(ca_vf)); + vf = strtoul(ca_vf, NULL, 10); + cert_token(&cert, ca_vt, sizeof(ca_vt)); + vt = strtoul(ca_vt, NULL, 10); + cert_token(&cert, ca_alg, sizeof(ca_alg)); + cert_token(&cert, ca_sig, sizeof(ca_sig)); + + if (strcmp(ca_alg, "ripemd160")) { + debug2("cert_verify: unsupported alg '%s'\n", ca_alg); + return 0; + } + + siglen = 0; + for (i = 0; ca_sig[i]; ++i) { + if (ca_sig[i] >= '0' && ca_sig[i] <= '9') + c = ca_sig[i] - '0'; + else if (ca_sig[i] >= 'a' && ca_sig[i] <= 'f') + c = ca_sig[i] - 'a' + 10; + else + break; + if ((i % 2) == 0) + sigbuf[siglen] = c << 4; + else + sigbuf[siglen++] |= c; + } + + fp = key_fingerprint(ca_key, SSH_FP_MD5, SSH_FP_HEX); + if (strcmp(fp, ca_fp)) { + debug2("cert_verify: CA key fingerprint mismatch ('%s' != '%s')", + fp, ca_fp); + xfree(fp); + return 0; + } + xfree(fp); + + fp = key_fingerprint(key, SSH_FP_MD5, SSH_FP_HEX); + snprintf(datbuf, sizeof(datbuf), "%s;%s;%s;%s;%lu;%lu", + fp, ca_name, ca_id, ca_opts, vf, vt); + xfree(fp); + + if (RSA_verify(NID_ripemd160, datbuf, strlen(datbuf), sigbuf, siglen, + ca_key->rsa) != 1) { + debug2("cert_verify: signature not valid ('%s')", ca_sig); + return 0; + } + if (vf && vf > now) { + debug2("cert_verify: certificate is not yet valid (%lu > %lu)", + vf, now); + return 0; + } + if (vt && vt < now) { + debug2("cert_verify: certificate has expired (%lu < %lu)", + vt, now); + return 0; + } + if (identity != NULL && strcmp(identity, ca_id)) { + debug2("cert_verify: identity mismatches ('%s' != '%s')", + identity, ca_id); + return 0; + } + return 1; } Index: key.h ==================================================================RCS file: /cvs/src/usr.bin/ssh/key.h,v retrieving revision 1.26 diff -u -r1.26 key.h --- key.h 3 Aug 2006 03:34:42 -0000 1.26 +++ key.h 15 Nov 2006 14:14:33 -0000 @@ -53,6 +53,7 @@ int flags; RSA *rsa; DSA *dsa; + u_char *cert; }; Key *key_new(int); @@ -83,5 +84,7 @@ int ssh_dss_verify(const Key *, const u_char *, u_int, const u_char *, u_int); int ssh_rsa_sign(const Key *, u_char **, u_int *, const u_char *, u_int); int ssh_rsa_verify(const Key *, const u_char *, u_int, const u_char *, u_int); + +int cert_verify(const u_char *cert, const Key *, const Key *, const u_char *); #endif Index: monitor.c ==================================================================RCS file: /cvs/src/usr.bin/ssh/monitor.c,v retrieving revision 1.89 diff -u -r1.89 monitor.c --- monitor.c 7 Nov 2006 10:31:31 -0000 1.89 +++ monitor.c 15 Nov 2006 14:14:35 -0000 @@ -797,6 +797,17 @@ if (key != NULL && authctxt->valid) { switch (type) { + case MM_CERTKEY: { + u_char *cert; + u_int clen; + + cert = buffer_get_string(m, &clen); + key->cert = xstrdup(cert); + allowed = options.certkey_authentication && + user_cert_key_allowed(authctxt->pw, key); + auth_method = "certkey"; + break; + } case MM_USERKEY: allowed = options.pubkey_authentication && user_key_allowed(authctxt->pw, key); @@ -859,7 +870,7 @@ } static int -monitor_valid_userblob(u_char *data, u_int datalen) +monitor_valid_userblob(u_char *data, u_int datalen, u_char *name) { Buffer b; char *p; @@ -900,7 +911,7 @@ fail++; } else { p = buffer_get_string(&b, NULL); - if (strcmp("publickey", p) != 0) + if (strcmp(name, p) != 0) fail++; xfree(p); if (!buffer_get_char(&b)) @@ -992,8 +1003,11 @@ fatal("%s: bad public key blob", __func__); switch (key_blobtype) { + case MM_CERTKEY: + valid_data = monitor_valid_userblob(data, datalen, "certkey"); + break; case MM_USERKEY: - valid_data = monitor_valid_userblob(data, datalen); + valid_data = monitor_valid_userblob(data, datalen, "publickey"); break; case MM_HOSTKEY: valid_data = monitor_valid_hostbasedblob(data, datalen, @@ -1015,7 +1029,12 @@ xfree(signature); xfree(data); - auth_method = key_blobtype == MM_USERKEY ? "publickey" : "hostbased"; + if (key_blobtype == MM_CERTKEY) + auth_method = "certkey"; + else if (key_blobtype == MM_USERKEY) + auth_method = "publickey"; + else + auth_method = "hostbased"; monitor_reset_key_state(); Index: monitor_wrap.c ==================================================================RCS file: /cvs/src/usr.bin/ssh/monitor_wrap.c,v retrieving revision 1.54 diff -u -r1.54 monitor_wrap.c --- monitor_wrap.c 12 Aug 2006 20:46:46 -0000 1.54 +++ monitor_wrap.c 15 Nov 2006 14:14:35 -0000 @@ -295,6 +295,12 @@ } int +mm_user_cert_key_allowed(struct passwd *pw, Key *key) +{ + return (mm_key_allowed(MM_CERTKEY, NULL, NULL, key)); +} + +int mm_user_key_allowed(struct passwd *pw, Key *key) { return (mm_key_allowed(MM_USERKEY, NULL, NULL, key)); @@ -351,6 +357,8 @@ buffer_put_cstring(&m, user ? user : ""); buffer_put_cstring(&m, host ? host : ""); buffer_put_string(&m, blob, len); + if (type == MM_CERTKEY && key && key->cert) + buffer_put_string(&m, key->cert, strlen(key->cert)); xfree(blob); mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_KEYALLOWED, &m); Index: monitor_wrap.h ==================================================================RCS file: /cvs/src/usr.bin/ssh/monitor_wrap.h,v retrieving revision 1.20 diff -u -r1.20 monitor_wrap.h --- monitor_wrap.h 3 Aug 2006 03:34:42 -0000 1.20 +++ monitor_wrap.h 15 Nov 2006 14:14:35 -0000 @@ -31,7 +31,7 @@ extern int use_privsep; #define PRIVSEP(x) (use_privsep ? mm_##x : x) -enum mm_keytype {MM_NOKEY, MM_HOSTKEY, MM_USERKEY, MM_RSAHOSTKEY, MM_RSAUSERKEY}; +enum mm_keytype {MM_NOKEY, MM_HOSTKEY, MM_CERTKEY, MM_USERKEY, MM_RSAHOSTKEY, MM_RSAUSERKEY}; struct monitor; struct mm_master; @@ -46,6 +46,7 @@ int mm_auth_password(struct Authctxt *, char *); int mm_key_allowed(enum mm_keytype, char *, char *, Key *); int mm_user_key_allowed(struct passwd *, Key *); +int mm_user_cert_key_allowed(struct passwd *, Key *); int mm_hostbased_key_allowed(struct passwd *, char *, char *, Key *); int mm_auth_rhosts_rsa_key_allowed(struct passwd *, char *, char *, Key *); int mm_key_verify(Key *, u_char *, u_int, u_char *, u_int); Index: myproposal.h ==================================================================RCS file: /cvs/src/usr.bin/ssh/myproposal.h,v retrieving revision 1.21 diff -u -r1.21 myproposal.h --- myproposal.h 25 Mar 2006 22:22:43 -0000 1.21 +++ myproposal.h 15 Nov 2006 14:14:35 -0000 @@ -24,6 +24,7 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #define KEX_DEFAULT_KEX \ + "diffie-hellman-group-exchange-cert," \ "diffie-hellman-group-exchange-sha256," \ "diffie-hellman-group-exchange-sha1," \ "diffie-hellman-group14-sha1," \ Index: pathnames.h ==================================================================RCS file: /cvs/src/usr.bin/ssh/pathnames.h,v retrieving revision 1.16 diff -u -r1.16 pathnames.h --- pathnames.h 25 Mar 2006 22:22:43 -0000 1.16 +++ pathnames.h 15 Nov 2006 14:14:35 -0000 @@ -36,6 +36,7 @@ #define _PATH_DH_MODULI ETCDIR "/moduli" /* Backwards compatibility */ #define _PATH_DH_PRIMES ETCDIR "/primes" +#define _PATH_CA_KEY_FILE SSHDIR "/ca.pub" #define _PATH_SSH_PROGRAM "/usr/bin/ssh" Index: readconf.c ==================================================================RCS file: /cvs/src/usr.bin/ssh/readconf.c,v retrieving revision 1.159 diff -u -r1.159 readconf.c --- readconf.c 3 Aug 2006 03:34:42 -0000 1.159 +++ readconf.c 15 Nov 2006 14:14:36 -0000 @@ -117,7 +117,8 @@ oBatchMode, oCheckHostIP, oStrictHostKeyChecking, oCompression, oCompressionLevel, oTCPKeepAlive, oNumberOfPasswordPrompts, oUsePrivilegedPort, oLogLevel, oCiphers, oProtocol, oMacs, - oGlobalKnownHostsFile2, oUserKnownHostsFile2, oPubkeyAuthentication, + oGlobalKnownHostsFile2, oUserKnownHostsFile2, oCertkeyAuthentication, + oCAKeyFile, oPubkeyAuthentication, oKbdInteractiveAuthentication, oKbdInteractiveDevices, oHostKeyAlias, oDynamicForward, oPreferredAuthentications, oHostbasedAuthentication, oHostKeyAlgorithms, oBindAddress, oSmartcardDevice, @@ -148,6 +149,8 @@ { "kbdinteractiveauthentication", oKbdInteractiveAuthentication }, { "kbdinteractivedevices", oKbdInteractiveDevices }, { "rsaauthentication", oRSAAuthentication }, + { "certkeyauthentication", oCertkeyAuthentication }, + { "cakeyfile", oCAKeyFile }, { "pubkeyauthentication", oPubkeyAuthentication }, { "dsaauthentication", oPubkeyAuthentication }, /* alias */ { "rhostsrsaauthentication", oRhostsRSAAuthentication }, @@ -412,6 +415,10 @@ charptr = &options->kbd_interactive_devices; goto parse_string; + case oCertkeyAuthentication: + intptr = &options->certkey_authentication; + goto parse_flag; + case oPubkeyAuthentication: intptr = &options->pubkey_authentication; goto parse_flag; @@ -560,6 +567,10 @@ *charptr = xstrdup(arg); break; + case oCAKeyFile: + charptr = &options->ca_key_file; + goto parse_string; + case oGlobalKnownHostsFile: charptr = &options->system_hostfile; goto parse_string; @@ -1002,6 +1013,8 @@ options->gateway_ports = -1; options->use_privileged_port = -1; options->rsa_authentication = -1; + options->certkey_authentication = -1; + options->ca_key_file = NULL; options->pubkey_authentication = -1; options->challenge_response_authentication = -1; options->gss_authentication = -1; @@ -1088,6 +1101,10 @@ options->use_privileged_port = 0; if (options->rsa_authentication == -1) options->rsa_authentication = 1; + if (options->certkey_authentication == -1) + options->certkey_authentication = 0; + if (options->ca_key_file == NULL) + options->ca_key_file = _PATH_CA_KEY_FILE; if (options->pubkey_authentication == -1) options->pubkey_authentication = 1; if (options->challenge_response_authentication == -1) Index: readconf.h ==================================================================RCS file: /cvs/src/usr.bin/ssh/readconf.h,v retrieving revision 1.71 diff -u -r1.71 readconf.h --- readconf.h 3 Aug 2006 03:34:42 -0000 1.71 +++ readconf.h 15 Nov 2006 14:14:36 -0000 @@ -39,6 +39,8 @@ int rhosts_rsa_authentication; /* Try rhosts with RSA * authentication. */ int rsa_authentication; /* Try RSA authentication. */ + int certkey_authentication; /* Try ssh2 certkey authentication. */ + char *ca_key_file; /* File containing CA key. */ int pubkey_authentication; /* Try ssh2 pubkey authentication. */ int hostbased_authentication; /* ssh2's rhosts_rsa */ int challenge_response_authentication; Index: servconf.c ==================================================================RCS file: /cvs/src/usr.bin/ssh/servconf.c,v retrieving revision 1.165 diff -u -r1.165 servconf.c --- servconf.c 14 Aug 2006 12:40:25 -0000 1.165 +++ servconf.c 15 Nov 2006 14:14:37 -0000 @@ -56,6 +56,7 @@ options->listen_addrs = NULL; options->address_family = -1; options->num_host_key_files = 0; + options->ca_key_file = NULL; options->pid_file = NULL; options->server_key_bits = -1; options->login_grace_time = -1; @@ -77,6 +78,7 @@ options->hostbased_authentication = -1; options->hostbased_uses_name_from_packet_only = -1; options->rsa_authentication = -1; + options->certkey_authentication = -1; options->pubkey_authentication = -1; options->kerberos_authentication = -1; options->kerberos_or_local_passwd = -1; @@ -134,6 +136,8 @@ _PATH_HOST_DSA_KEY_FILE; } } + if (options->ca_key_file == NULL) + options->ca_key_file = _PATH_CA_KEY_FILE; if (options->num_ports == 0) options->ports[options->num_ports++] = SSH_DEFAULT_PORT; if (options->listen_addrs == NULL) @@ -180,6 +184,8 @@ options->hostbased_uses_name_from_packet_only = 0; if (options->rsa_authentication == -1) options->rsa_authentication = 1; + if (options->certkey_authentication == -1) + options->certkey_authentication = 0; if (options->pubkey_authentication == -1) options->pubkey_authentication = 1; if (options->kerberos_authentication == -1) @@ -259,9 +265,9 @@ sStrictModes, sEmptyPasswd, sTCPKeepAlive, sPermitUserEnvironment, sUseLogin, sAllowTcpForwarding, sCompression, sAllowUsers, sDenyUsers, sAllowGroups, sDenyGroups, - sIgnoreUserKnownHosts, sCiphers, sMacs, sProtocol, sPidFile, - sGatewayPorts, sPubkeyAuthentication, sXAuthLocation, sSubsystem, - sMaxStartups, sMaxAuthTries, + sIgnoreUserKnownHosts, sCiphers, sMacs, sProtocol, sPidFile, sCAKeyFile, + sGatewayPorts, sCertkeyAuthentication, sPubkeyAuthentication, sXAuthLocation, + sSubsystem, sMaxStartups, sMaxAuthTries, sBanner, sUseDNS, sHostbasedAuthentication, sHostbasedUsesNameFromPacketOnly, sClientAliveInterval, sClientAliveCountMax, sAuthorizedKeysFile, sAuthorizedKeysFile2, @@ -282,6 +288,7 @@ u_int flags; } keywords[] = { { "port", sPort, SSHCFG_GLOBAL }, + { "cakeyfile", sCAKeyFile, SSHCFG_GLOBAL }, { "hostkey", sHostKeyFile, SSHCFG_GLOBAL }, { "hostdsakey", sHostKeyFile, SSHCFG_GLOBAL }, /* alias */ { "pidfile", sPidFile, SSHCFG_GLOBAL }, @@ -296,6 +303,7 @@ { "hostbasedauthentication", sHostbasedAuthentication, SSHCFG_GLOBAL }, { "hostbasedusesnamefrompacketonly", sHostbasedUsesNameFromPacketOnly, SSHCFG_GLOBAL }, { "rsaauthentication", sRSAAuthentication, SSHCFG_GLOBAL }, + { "certkeyauthentication", sCertkeyAuthentication, SSHCFG_GLOBAL }, { "pubkeyauthentication", sPubkeyAuthentication, SSHCFG_GLOBAL }, { "dsaauthentication", sPubkeyAuthentication, SSHCFG_GLOBAL }, /* alias */ #ifdef KRB5 @@ -738,6 +746,10 @@ } break; + case sCAKeyFile: + charptr = &options->ca_key_file; + goto parse_filename; + case sPidFile: charptr = &options->pid_file; goto parse_filename; @@ -803,6 +815,10 @@ case sRSAAuthentication: intptr = &options->rsa_authentication; + goto parse_flag; + + case sCertkeyAuthentication: + intptr = &options->certkey_authentication; goto parse_flag; case sPubkeyAuthentication: Index: servconf.h ==================================================================RCS file: /cvs/src/usr.bin/ssh/servconf.h,v retrieving revision 1.79 diff -u -r1.79 servconf.h --- servconf.h 14 Aug 2006 12:40:25 -0000 1.79 +++ servconf.h 15 Nov 2006 14:14:37 -0000 @@ -43,6 +43,7 @@ char *listen_addr; /* Address on which the server listens. */ struct addrinfo *listen_addrs; /* Addresses on which the server listens. */ int address_family; /* Address family used by the server. */ + char *ca_key_file; /* File containing CA key. */ char *host_key_files[MAX_HOSTKEYS]; /* Files containing host keys. */ int num_host_key_files; /* Number of files for host keys. */ char *pid_file; /* Where to put our pid */ @@ -75,6 +76,7 @@ int hostbased_uses_name_from_packet_only; /* experimental */ int rsa_authentication; /* If true, permit RSA authentication. */ int pubkey_authentication; /* If true, permit ssh2 pubkey authentication. */ + int certkey_authentication; /* If true, permit ssh2 certkey authentication. */ int kerberos_authentication; /* If true, permit Kerberos * authentication. */ int kerberos_or_local_passwd; /* If true, permit kerberos Index: ssh-keygen.c ==================================================================RCS file: /cvs/src/usr.bin/ssh/ssh-keygen.c,v retrieving revision 1.156 diff -u -r1.156 ssh-keygen.c --- ssh-keygen.c 14 Nov 2006 19:41:04 -0000 1.156 +++ ssh-keygen.c 15 Nov 2006 14:14:37 -0000 @@ -94,6 +94,8 @@ int print_public = 0; int print_generic = 0; +int sign_host_key = 0; + char *key_type_name = NULL; /* argv0 */ @@ -494,6 +496,142 @@ #endif /* SMARTCARD */ static void +ask_string(const char *question, char *buf, int len) +{ + printf("%s", question); + if (fgets(buf, len, stdin) == NULL) + exit(1); + buf[len - 1] = 0; + len = strlen(buf); + if (len > 0 && buf[len - 1] == '\n') + buf[len - 1] = 0; +} + +static unsigned long +ask_date(const char *question) +{ + char buf[64]; + int len; + unsigned year, mon = 1, mday = 1, hour = 0, min = 0, sec = 0; + struct tm tm; + + printf("%s", question); + if (fgets(buf, sizeof(buf), stdin) == NULL) + exit(1); + buf[sizeof(buf) - 1] = 0; + len = strlen(buf); + if (len > 0 && buf[len - 1] == '\n') + buf[len - 1] = 0; + if (sscanf(buf, "%4u%2u%2u%2u%2u%2u", + &year, &mon, &mday, &hour, &min, &sec) < 1) { + error("invalid date"); + exit(1); + } + if (!year) + return 0; + memset(&tm, 0, sizeof(tm)); + tm.tm_year = year - 1900; + tm.tm_mon = mon - 1; + tm.tm_mday = mday; + tm.tm_hour = hour; + tm.tm_min = min; + tm.tm_sec = sec; + return timegm(&tm); +} + +static void +do_sign_host_key(struct passwd *pw) +{ + struct stat st; + u_char ca_name[128], ca_id[128], ca_opts[512]; + u_char dat[8192], sig[8192], key_fn[1024], cert_fn[1024]; + unsigned long valid_from, valid_to; + u_int slen; + Key *ca_key, *host_key; + char *ca_fp, *host_fp; + FILE *f; + int i; + + if (!have_identity) + ask_filename(pw, "Enter file in which the CA key is"); + if (stat(identity_file, &st) < 0) { + perror(identity_file); + exit(1); + } + ca_key = load_identity(identity_file); + if (ca_key == NULL) { + error("load failed"); + exit(1); + } + if (ca_key->type != KEY_RSA || ca_key->rsa == NULL) { + error("key invalid"); + exit(1); + } + ca_fp = key_fingerprint(ca_key, SSH_FP_MD5, SSH_FP_HEX); + printf("CA key fingerprint %s\n", ca_fp); + + ask_string("Enter file in which the user/host key is: ", key_fn, sizeof(key_fn)); + if (stat(key_fn, &st) < 0) { + perror(key_fn); + exit(1); + } + host_key = key_load_public(key_fn, NULL); + if (host_key == NULL) { + error("load failed"); + exit(1); + } + strlcpy(cert_fn, key_fn, sizeof(cert_fn)); + if (strlen(cert_fn) > 4 && !strcmp(cert_fn + strlen(cert_fn) - 4, ".pub")) + cert_fn[strlen(cert_fn) - 4] = 0; + strlcat(cert_fn, ".cert", sizeof(cert_fn)); + host_fp = key_fingerprint(host_key, SSH_FP_MD5, SSH_FP_HEX); + printf("host/user key fingerprint %s\n", host_fp); + + ask_string("CA name : ", ca_name, sizeof(ca_name)); + if (!ca_name[0] || strchr(ca_name, ';')) { + error("invalid CA name"); + exit(1); + } + ask_string("identity : ", ca_id, sizeof(ca_id)); + if (!ca_id[0] || strchr(ca_id, ';')) { + error("invalid identity"); + exit(1); + } + ask_string("options : ", ca_opts, sizeof(ca_opts)); + if (strchr(ca_opts, ';')) { + error("invalid options"); + exit(1); + } + valid_from = ask_date("valid from : "); + valid_to = ask_date("valid until: "); + + snprintf(dat, sizeof(dat), "%s;%s;%s;%s;%lu;%lu", + host_fp, ca_name, ca_id, ca_opts, valid_from, valid_to); + if (RSA_sign(NID_ripemd160, dat, strlen(dat), sig, &slen, ca_key->rsa) != 1 || !slen) { + fprintf(stderr, "RSA_sign() failed\n"); + exit(1); + } + if (RSA_verify(NID_ripemd160, dat, strlen(dat), sig, slen, ca_key->rsa) != 1) { + fprintf(stderr, "RSA_verify() failed\n"); + exit(1); + } + + snprintf(dat, sizeof(dat), "%s;%s;%s;%s;%lu;%lu;ripemd160;", + ca_fp, ca_name, ca_id, ca_opts, valid_from, valid_to); + for (i = 0; i < slen; ++i) + snprintf(dat + strlen(dat), sizeof(dat) - strlen(dat), "%.2x", sig[i]); + f = fopen(cert_fn, "w"); + if (f == NULL) { + fprintf(stderr, "fopen: %s: %s\n", cert_fn, strerror(errno)); + exit(1); + } + fprintf(f, "%s", dat); + fclose(f); + printf("Certificate has been saved in %s.\n", cert_fn); + exit(0); +} + +static void do_fingerprint(struct passwd *pw) { FILE *f; @@ -1026,6 +1164,7 @@ fprintf(stderr, " -R hostname Remove host from known_hosts file.\n"); fprintf(stderr, " -r hostname Print DNS resource record.\n"); fprintf(stderr, " -S start Start point (hex) for generating DH-GEX moduli.\n"); + fprintf(stderr, " -s Generate certificate for user/host key using CA key.\n"); fprintf(stderr, " -T file Screen candidates for DH-GEX moduli.\n"); fprintf(stderr, " -t type Specify type of key to create.\n"); #ifdef SMARTCARD @@ -1079,7 +1218,7 @@ } while ((opt = getopt(argc, argv, - "degiqpclBHvxXyF:b:f:t:U:D:P:N:C:r:g:R:T:G:M:S:a:W:")) != -1) { + "degiqpsclBHvxXyF: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 +1295,9 @@ case 'U': reader_id = optarg; break; + case 's': + sign_host_key = 1; + break; case 'v': if (log_level == SYSLOG_LEVEL_INFO) log_level = SYSLOG_LEVEL_DEBUG1; @@ -1221,6 +1363,8 @@ printf("Can only have one of -p and -c.\n"); usage(); } + if (sign_host_key) + do_sign_host_key(pw); if (delete_host || hash_hosts || find_host) do_known_hosts(pw, rr_hostname); if (print_fingerprint || print_bubblebabble) Index: ssh_config.5 ==================================================================RCS file: /cvs/src/usr.bin/ssh/ssh_config.5,v retrieving revision 1.97 diff -u -r1.97 ssh_config.5 --- ssh_config.5 27 Jul 2006 08:00:50 -0000 1.97 +++ ssh_config.5 15 Nov 2006 14:14:38 -0000 @@ -145,6 +145,15 @@ .Cm UsePrivilegedPort is set to .Dq yes . +.It Cm CAKeyFile +Specifies a file containing a public CA key. +The default is +.Pa /etc/ssh/ca.pub . +.It Cm CertkeyAuthentication +Specifies whether certified key authentication is allowed. +The default is +.Dq no . +Note that this option applies to protocol version 2 only. .It Cm ChallengeResponseAuthentication Specifies whether to use challenge-response authentication. The argument to this keyword must be Index: sshconnect.c ==================================================================RCS file: /cvs/src/usr.bin/ssh/sshconnect.c,v retrieving revision 1.200 diff -u -r1.200 sshconnect.c --- sshconnect.c 10 Oct 2006 10:12:45 -0000 1.200 +++ sshconnect.c 15 Nov 2006 14:14:39 -0000 @@ -21,6 +21,7 @@ #include <netinet/in.h> +#include <openssl/objects.h> #include <ctype.h> #include <errno.h> #include <netdb.h> @@ -48,6 +49,7 @@ #include "misc.h" #include "dns.h" #include "version.h" +#include "authfile.h" char *client_version_string = NULL; char *server_version_string = NULL; @@ -884,6 +886,19 @@ { struct stat st; int flags = 0; + + if (options.certkey_authentication && host_key->cert != NULL) { + Key *ca_key; + int verified; + + ca_key = key_load_public(options.ca_key_file, NULL); + if (ca_key != NULL) { + verified = cert_verify(host_key->cert, ca_key, host_key, NULL); + key_free(ca_key); + if (verified) + return 0; + } + } if (options.verify_host_key_dns && verify_host_key_dns(host, hostaddr, host_key, &flags) == 0) { Index: sshconnect2.c ==================================================================RCS file: /cvs/src/usr.bin/ssh/sshconnect2.c,v retrieving revision 1.162 diff -u -r1.162 sshconnect2.c --- sshconnect2.c 30 Aug 2006 00:06:51 -0000 1.162 +++ sshconnect2.c 15 Nov 2006 14:14:40 -0000 @@ -133,6 +133,7 @@ kex->kex[KEX_DH_GRP14_SHA1] = kexdh_client; kex->kex[KEX_DH_GEX_SHA1] = kexgex_client; kex->kex[KEX_DH_GEX_SHA256] = kexgex_client; + kex->kex[KEX_DH_GEX_CERT] = kexgex_client; kex->client_version_string=client_version_string; kex->server_version_string=server_version_string; kex->verify_host_key=&verify_host_key_callback; @@ -168,6 +169,7 @@ Key *key; /* public/private key */ char *filename; /* comment for agent-only keys */ int tried; + int triedcert; int isprivate; /* key points to the private key */ }; TAILQ_HEAD(idlist, identity); @@ -206,6 +208,7 @@ void input_userauth_passwd_changereq(int, u_int32_t, void *); int userauth_none(Authctxt *); +int userauth_certkey(Authctxt *); int userauth_pubkey(Authctxt *); int userauth_passwd(Authctxt *); int userauth_kbdint(Authctxt *); @@ -224,6 +227,7 @@ void userauth(Authctxt *, char *); static int sign_and_send_pubkey(Authctxt *, Identity *); +static int sign_and_send_certkey(Authctxt *, Identity *); static void pubkey_prepare(Authctxt *); static void pubkey_cleanup(Authctxt *); static Key *load_identity_file(char *); @@ -243,6 +247,10 @@ userauth_hostbased, &options.hostbased_authentication, NULL}, + {"certkey", + userauth_certkey, + &options.certkey_authentication, + NULL}, {"publickey", userauth_pubkey, &options.pubkey_authentication, @@ -472,7 +480,11 @@ */ TAILQ_FOREACH_REVERSE(id, &authctxt->keys, idlist, next) { if (key_equal(key, id->key)) { - sent = sign_and_send_pubkey(authctxt, id); + if (!strcmp(authctxt->method->name, "certkey")) { + if (id->key->cert != NULL) + sent = sign_and_send_certkey(authctxt, id); + } else + sent = sign_and_send_pubkey(authctxt, id); break; } } @@ -851,6 +863,93 @@ } static int +sign_and_send_certkey(Authctxt *authctxt, Identity *id) +{ + Buffer b; + u_char *blob, *signature; + u_int bloblen, slen; + u_int skip = 0; + int ret = -1; + int have_sig = 1; + + debug3("sign_and_send_certkey"); + + if (key_to_blob(id->key, &blob, &bloblen) == 0) { + /* we cannot handle this key */ + debug3("sign_and_send_certkey: cannot handle key"); + return 0; + } + /* data to be signed */ + buffer_init(&b); + if (datafellows & SSH_OLD_SESSIONID) { + buffer_append(&b, session_id2, session_id2_len); + skip = session_id2_len; + } else { + buffer_put_string(&b, session_id2, session_id2_len); + skip = buffer_len(&b); + } + buffer_put_char(&b, SSH2_MSG_USERAUTH_REQUEST); + buffer_put_cstring(&b, authctxt->server_user); + buffer_put_cstring(&b, + datafellows & SSH_BUG_PKSERVICE ? + "ssh-userauth" : + authctxt->service); + if (datafellows & SSH_BUG_PKAUTH) { + buffer_put_char(&b, have_sig); + } else { + buffer_put_cstring(&b, authctxt->method->name); + buffer_put_char(&b, have_sig); + buffer_put_cstring(&b, key_ssh_name(id->key)); + } + buffer_put_string(&b, blob, bloblen); + + /* generate signature */ + ret = identity_sign(id, &signature, &slen, + buffer_ptr(&b), buffer_len(&b)); + if (ret == -1) { + xfree(blob); + buffer_free(&b); + return 0; + } +#ifdef DEBUG_PK + buffer_dump(&b); +#endif + if (datafellows & SSH_BUG_PKSERVICE) { + buffer_clear(&b); + buffer_append(&b, session_id2, session_id2_len); + skip = session_id2_len; + buffer_put_char(&b, SSH2_MSG_USERAUTH_REQUEST); + buffer_put_cstring(&b, authctxt->server_user); + buffer_put_cstring(&b, authctxt->service); + buffer_put_cstring(&b, authctxt->method->name); + buffer_put_char(&b, have_sig); + if (!(datafellows & SSH_BUG_PKAUTH)) + buffer_put_cstring(&b, key_ssh_name(id->key)); + buffer_put_string(&b, blob, bloblen); + } + xfree(blob); + + /* append signature */ + buffer_put_string(&b, signature, slen); + xfree(signature); + + buffer_put_string(&b, id->key->cert, strlen(id->key->cert)); + + /* skip session id and packet type */ + if (buffer_len(&b) < skip + 1) + fatal("userauth_pubkey: internal error"); + buffer_consume(&b, skip + 1); + + /* put remaining data from buffer into packet */ + packet_start(SSH2_MSG_USERAUTH_REQUEST); + packet_put_raw(buffer_ptr(&b), buffer_len(&b)); + buffer_free(&b); + packet_send(); + + return 1; +} + +static int sign_and_send_pubkey(Authctxt *authctxt, Identity *id) { Buffer b; @@ -936,6 +1035,31 @@ } static int +send_certkey_test(Authctxt *authctxt, Identity *id) +{ + u_char *blob; + u_int bloblen, have_sig = 0; + + if (key_to_blob(id->key, &blob, &bloblen) == 0) + return 0; + /* register callback for USERAUTH_PK_OK message */ + dispatch_set(SSH2_MSG_USERAUTH_PK_OK, &input_userauth_pk_ok); + + packet_start(SSH2_MSG_USERAUTH_REQUEST); + packet_put_cstring(authctxt->server_user); + packet_put_cstring(authctxt->service); + packet_put_cstring(authctxt->method->name); + packet_put_char(have_sig); + if (!(datafellows & SSH_BUG_PKAUTH)) + packet_put_cstring(key_ssh_name(id->key)); + packet_put_string(blob, bloblen); + xfree(blob); + packet_put_string(id->key->cert, strlen(id->key->cert)); + packet_send(); + return 1; +} + +static int send_pubkey_test(Authctxt *authctxt, Identity *id) { u_char *blob; @@ -1095,6 +1219,42 @@ xfree(id->filename); xfree(id); } +} + +int +userauth_certkey(Authctxt *authctxt) +{ + Identity *id; + int sent = 0; + + while ((id = TAILQ_FIRST(&authctxt->keys))) { + if (id->triedcert++) + return (0); + /* move key to the end of the queue */ + TAILQ_REMOVE(&authctxt->keys, id, next); + TAILQ_INSERT_TAIL(&authctxt->keys, id, next); + /* + * send a test message if we have the public key. for + * encrypted keys we cannot do this and have to load the + * private key instead + */ + if (id->key && id->key->cert && id->key->type != KEY_RSA1) { + debug("Offering public key: %s", id->filename); + sent = send_certkey_test(authctxt, id); + } else if (id->key == NULL) { + debug("Trying private key: %s", id->filename); + id->key = load_identity_file(id->filename); + if (id->key != NULL && id->key->cert != NULL) { + id->isprivate = 1; + sent = sign_and_send_certkey(authctxt, id); + key_free(id->key); + id->key = NULL; + } + } + if (sent) + return (sent); + } + return (0); } int Index: sshd.c ==================================================================RCS file: /cvs/src/usr.bin/ssh/sshd.c,v retrieving revision 1.348 diff -u -r1.348 sshd.c --- sshd.c 6 Nov 2006 21:25:28 -0000 1.348 +++ sshd.c 15 Nov 2006 14:14:40 -0000 @@ -1999,6 +1999,7 @@ kex->kex[KEX_DH_GRP14_SHA1] = kexdh_server; kex->kex[KEX_DH_GEX_SHA1] = kexgex_server; kex->kex[KEX_DH_GEX_SHA256] = kexgex_server; + kex->kex[KEX_DH_GEX_CERT] = kexgex_server; kex->server = 1; kex->client_version_string=client_version_string; kex->server_version_string=server_version_string; Index: sshd_config.5 ==================================================================RCS file: /cvs/src/usr.bin/ssh/sshd_config.5,v retrieving revision 1.70 diff -u -r1.70 sshd_config.5 --- sshd_config.5 21 Aug 2006 08:14:01 -0000 1.70 +++ sshd_config.5 15 Nov 2006 14:14:41 -0000 @@ -167,6 +167,15 @@ authentication is allowed. This option is only available for protocol version 2. By default, no banner is displayed. +.It Cm CAKeyFile +Specifies a file containing a public CA key. +The default is +.Pa /etc/ssh/ca.pub . +.It Cm CertkeyAuthentication +Specifies whether certified key authentication is allowed. +The default is +.Dq no . +Note that this option applies to protocol version 2 only. .It Cm ChallengeResponseAuthentication Specifies whether challenge-response authentication is allowed. All authentication styles from Index: sshd/Makefile ==================================================================RCS file: /cvs/src/usr.bin/ssh/sshd/Makefile,v retrieving revision 1.64 diff -u -r1.64 Makefile --- sshd/Makefile 23 Aug 2004 14:26:39 -0000 1.64 +++ sshd/Makefile 15 Nov 2006 14:14:41 -0000 @@ -14,7 +14,7 @@ auth.c auth1.c auth2.c auth-options.c session.c \ auth-chall.c auth2-chall.c groupaccess.c \ auth-skey.c auth-bsdauth.c auth2-hostbased.c auth2-kbdint.c \ - auth2-none.c auth2-passwd.c auth2-pubkey.c \ + auth2-none.c auth2-passwd.c auth2-pubkey.c auth2-certkey.c \ monitor_mm.c monitor.c monitor_wrap.c \ kexdhs.c kexgexs.c --- /dev/null Wed Nov 15 15:14:51 2006 +++ auth2-certkey.c Wed Nov 15 11:07:56 2006 @@ -0,0 +1,196 @@ +/* $OpenBSD$ */ +/* + * Copyright (c) 2000 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. + */ + +#include <sys/types.h> +#include <sys/stat.h> + +#include <pwd.h> +#include <stdio.h> +#include <stdarg.h> + +#include "xmalloc.h" +#include "ssh.h" +#include "ssh2.h" +#include "packet.h" +#include "buffer.h" +#include "log.h" +#include "servconf.h" +#include "compat.h" +#include "key.h" +#include "hostfile.h" +#include "auth.h" +#include "pathnames.h" +#include "uidswap.h" +#include "auth-options.h" +#include "canohost.h" +#ifdef GSSAPI +#include "ssh-gss.h" +#endif +#include "monitor_wrap.h" +#include "misc.h" + +/* import */ +extern ServerOptions options; +extern u_char *session_id2; +extern u_int session_id2_len; + +static int +userauth_certkey(Authctxt *authctxt) +{ + Buffer b; + Key *key = NULL; + char *pkalg; + u_char *pkblob, *sig, *cert; + u_int alen, blen, slen, clen; + int have_sig, pktype; + int authenticated = 0; + + if (!authctxt->valid) { + debug2("userauth_certkey: disabled because of invalid user"); + return 0; + } + have_sig = packet_get_char(); + if (datafellows & SSH_BUG_PKAUTH) { + debug2("userauth_certkey: SSH_BUG_PKAUTH"); + /* no explicit pkalg given */ + pkblob = packet_get_string(&blen); + buffer_init(&b); + buffer_append(&b, pkblob, blen); + /* so we have to extract the pkalg from the pkblob */ + pkalg = buffer_get_string(&b, &alen); + buffer_free(&b); + } else { + pkalg = packet_get_string(&alen); + pkblob = packet_get_string(&blen); + } + pktype = key_type_from_name(pkalg); + if (pktype == KEY_UNSPEC) { + /* this is perfectly legal */ + logit("userauth_certkey: unsupported public key algorithm: %s", + pkalg); + goto done; + } + key = key_from_blob(pkblob, blen); + if (key == NULL) { + error("userauth_certkey: cannot decode key: %s", pkalg); + goto done; + } + if (key->type != pktype) { + error("userauth_certkey: type mismatch for decoded key " + "(received %d, expected %d)", key->type, pktype); + goto done; + } + if (have_sig) { + sig = packet_get_string(&slen); + cert = packet_get_string(&clen); + if (!cert || clen <= 0) { + error("userauth_certkey: no cert"); + goto done; + } + key->cert = xstrdup(cert); + packet_check_eom(); + buffer_init(&b); + if (datafellows & SSH_OLD_SESSIONID) { + buffer_append(&b, session_id2, session_id2_len); + } else { + buffer_put_string(&b, session_id2, session_id2_len); + } + /* reconstruct packet */ + buffer_put_char(&b, SSH2_MSG_USERAUTH_REQUEST); + buffer_put_cstring(&b, authctxt->user); + buffer_put_cstring(&b, + datafellows & SSH_BUG_PKSERVICE ? + "ssh-userauth" : + authctxt->service); + if (datafellows & SSH_BUG_PKAUTH) { + buffer_put_char(&b, have_sig); + } else { + buffer_put_cstring(&b, "certkey"); + buffer_put_char(&b, have_sig); + buffer_put_cstring(&b, pkalg); + } + buffer_put_string(&b, pkblob, blen); +#ifdef DEBUG_PK + buffer_dump(&b); +#endif + /* test for correct signature */ + authenticated = 0; + if (PRIVSEP(user_cert_key_allowed(authctxt->pw, key)) && + PRIVSEP(key_verify(key, sig, slen, buffer_ptr(&b), + buffer_len(&b))) == 1) + authenticated = 1; + buffer_free(&b); + xfree(sig); + } else { + debug("test whether pkalg/pkblob are acceptable"); + cert = packet_get_string(&clen); + if (!cert || clen <= 0) { + error("userauth_certkey: no cert"); + goto done; + } + key->cert = xstrdup(cert); + packet_check_eom(); + + if (PRIVSEP(user_cert_key_allowed(authctxt->pw, key))) { + packet_start(SSH2_MSG_USERAUTH_PK_OK); + packet_put_string(pkalg, alen); + packet_put_string(pkblob, blen); + packet_send(); + packet_write_wait(); + authctxt->postponed = 1; + } + } + if (authenticated != 1) + auth_clear_options(); +done: + debug2("userauth_certkey: authenticated %d pkalg %s", authenticated, pkalg); + if (key != NULL) + key_free(key); + xfree(pkalg); + xfree(pkblob); + return authenticated; +} + +/* check whether given key is signed by certificate */ +int +user_cert_key_allowed(struct passwd *pw, Key *key) +{ + int allowed = 0; + Key *ca_key; + + temporarily_use_uid(pw); + ca_key = key_load_public(options.ca_key_file, NULL); + restore_uid(); + allowed = cert_verify(key->cert, ca_key, key, pw->pw_name); + if (ca_key != NULL) + key_free(ca_key); + return allowed; +} + +Authmethod method_certkey = { + "certkey", + userauth_certkey, + &options.certkey_authentication +};
Daniel Hartmeier wrote:> This patch against OpenBSD -current adds a simple form of PKI to > OpenSSH. We'll be using it at work. See README.certkey (the first chunk > of the patch) for details. > > Everything below is BSD licensed, sponsored by Allamanda Networks AG.As the one who assigned this work to Daniel Hartmeier I can add the rationale for this simple and easy PKI functionality in OpenSSH. Rationale for PKI in OpenSSH: Managing a large m:n relationship of users and hosts in OpenSSH is tedious and requires the use of scripting and rsync distribution methods for known_hosts and authorized_keys with all associated failure modes. Users tend not to verify server pubkey fingerprints out of band upon the first connection attempt which weakens the strong theoretic security of SSHs public key system. Known_hosts and authorized_keys do not contain any validity or expiration information. Reality shows that a lot of cruft and old information tends to remain. This tends to accidentally leave once granted privileges to users or hosts which should no longer posses them. It requires strict discipline to properly clean up and stay up to date on n hosts. What does OpenSSH PKI do: It does three things: a) It adds a certificate to the public key of a ssh server so the ssh client can verify the authenticity of a server pubkey without having to rely on other unspecified out of band methods (on first connection) or the known_hosts file (on subsequent connections). b) It adds a certificate to the public key of a ssh user so the ssh server can verify the authenticity of a users pubkey without having to have a pre-populated authorized_keys file in each users home directory on all machines this user has access to. c) It adds a number of optional fields in the certificate which can specify additional constrains on the hosts and users. The constrains include lists of IP addresses or networks which limit where the clients or server can connect to/from. This way hosts with a number of interfaces can be handled as one entity. Moving/copying of server pub/privkeys to a different IP address is prevented. Users may be restricted to be able to login only from defined list of IP addresses/networks specified in the certificate. What are the advantages: Only the CA's public key has to be distributed once to all hosts. -> Can be rolled into the host setup procedure/automation. The CA has to sign each host or user public key once. None of the user or host keys have to be distributed (copied) to all other hosts. -> Instant acceptance by all hosts with the same CA pubkey. The addition of any users or hosts is controlled by the CA. -> Single point of entry and control. The CA specifies the expiry date of any user and host certificate thus a policy can regularly timeout any of them. -> Any old cruft and temporary privileges expire by itself w/o having to individually check all hosts for them. Who is it for: Centrally managed organizations or collections of hosts which trust a single master role (the CA certification authority, a.k.a 'root'). Many real world deployments do resemble the single trust model of a CA. What are the risks: All trust and authentication/authorization is vested in the operator of the CA and the strength and secrecy of the CA private key. If the CA operator or the CA private key are compromised all doors are open. The CA operator is equal to 'root' which is trusted anyway. He can do everything root can do (that is reading/modifying each users private keys and authorized keys files). Less severe incidents include lost or compromised user keys. OpenSSH PKI gives two methods to deal with this. First it allows to specify an expiry date for all certificates. If set sufficiently short all certs lose their usefulness quickly. All users have to be re-certified in intervals and the re-certification can be tied to any number of administrative criteria. For immediate reactions any number of public key fingerprints can be blacklisted on the ssh server. This is a simple file based certificate revocation. Online revocation checks could optionally be implemented as well. The operator then has to specify whether to fail open or close depending on priorities and risk assessments. Why not implement/use X.509 or OpenPGP PKI methods: The goal is to use only basic and non-complex cryptographic methods which are widely available and do *not* require the installation of additional software libraries (eg. OpenPGP SDK or OpenSSL for OpenBSD) and a simple and modular certificate binary format (no ASN.1 or such). It should work out of the box on all currently supported OpenSSH platforms without any external dependencies. This OpenSSH PKI system is very simple and easy to use. All programs and functions necessary to use it to its full extent are included with the base OpenSSH distribution. -- Andre
On Nov 15, 2006, at 9:47 AM, Bob Beck wrote:> In other words, I have to maintain a pre-populated "un-authorized" > keys file because in any real deployment you are GOING to have these. > and quite frequently with any sizable deployment. So I still have > to maintain a file. > > "authorized keys" -> anything that is not allowed is denied. > "un-authorized keys" -> anything that is not denied is allowed. > > NOT being prepared to maintain a file when doing this > is pretty much akin to "Don't worry, I'll pull out before I cum". > Everything's > great until there a problem and then it's a fuckshow. ><snip>> Don't get me wrong, I think this is possibly useful, but I don't > think it should go in incomplete like this. In my view it is complete > where when turning it on you specify a set of (possibly other) ssh > server(s) the server itself will connect to and use as a CRL when > presented with a key. - i.e. we should make it decently doable and > document how to use a CRL in this case. ><snip>> > -Bob >That sounds very much like OCSP. The objections to CRL distribution style revocation are pretty valid, IMO. Brian Keefer www.Tumbleweed.com "The Experts in Secure Internet Communication"
Bob Beck wrote:> Sigh. My objections to this are my objections to PKI implementations > in general. In my experience PKI methods are implemented and deployed > the same way people deploy half done raid drivers - we'll make the > disk work, but we won't worry about how to monitor and fix it when it > gets fucked up. "that'll be implemented later/left as an exercise for > the deployer to hack up something disgusting"Well, we post these patches to solicit valuable input and to refine it based on all the smart brains choosing to care. In our primary application we exactly want offline mode (the ability to login even if half or most of the network is down). We actually are the network we want to bring up again. Here we had to make a tradeoff and traded the ability to revoke a certificate against not being able to recover the network. Yeah, this is an easy one to make. ;-) Nonetheless we want to feed back a useful solution to the community and I've assigned some more developer time (Daniel Hartmeier) to address your concerns.>> b) It adds a certificate to the public key of a ssh user so the ssh >> server can verify the authenticity of a users pubkey without having >> to have a pre-populated authorized_keys file in each users home >> directory on all machines this user has access to. >> keys and authorized keys files). > > I maintain it does NOT do this - and here is why : > >> Less severe incidents include lost or compromised user keys. OpenSSH >> PKI gives two methods to deal with this. First it allows to specify >> an expiry date for all certificates. If set sufficiently short all >> certs lose their usefulness quickly. All users have to be re-certified >> in intervals and the re-certification can be tied to any number of >> administrative criteria. For immediate reactions any number of public >> key fingerprints can be blacklisted on the ssh server. This is a simple >> file based certificate revocation. Online revocation checks could >> optionally be implemented as well. The operator then has to specify >> whether to fail open or close depending on priorities and risk >> assessments. > > In other words, I have to maintain a pre-populated "un-authorized" > keys file because in any real deployment you are GOING to have these. > and quite frequently with any sizable deployment. So I still have > to maintain a file. > > "authorized keys" -> anything that is not allowed is denied. > "un-authorized keys" -> anything that is not denied is allowed. > > NOT being prepared to maintain a file when doing this > is pretty much akin to "Don't worry, I'll pull out before I cum". Everything's > great until there a problem and then it's a fuckshow.Well, step 1 is to have something that is better than reality shows today while requiring the same effort or dealing with the same laziness. There are way too many people out there who handle the security OpenSSH could provide in a very ineffective and dangerous way. If we can lift them up to the next practical security level while maintaining the same ease of use (or inappropriate practices) and laziness I call it a first success. Step 2 is to make the theoretical perfect security a reality by maintaining (almost) the same ease of use and laziness on part of the user. For the administrator it must mean only a very slight increase in inconvenience and effort with the clear payoff of much increased security. Step 1 we IMHO have mastered. Lets work on Step 2. See below.> I.E. this is the same "it's really cool and we almost have it right" > argument I've seen from everything pushing PKI and x509 goo as > authentication. An expiry date for all the user certs? gimme a break. > even setting it to a month (which would be a pita to have users > constantly re-certed) means someone gets a month to fuck around. Even > suggesting that certificate expiry times should be used to mitigate > this is irresponsible. You have to be able to nail something you know > is compromised quickly. The only thing expiry times to is A) make > money for commercial CA's, B) make stuff not have to stay forever in > your CRL if you have one.The latter is our main motivator. ;-)> Don't get me wrong, I think this is possibly useful, but I don't > think it should go in incomplete like this. In my view it is complete > where when turning it on you specify a set of (possibly other) ssh > server(s) the server itself will connect to and use as a CRL when > presented with a key. - i.e. we should make it decently doable and > document how to use a CRL in this case. I feel quite strongly that > without tying the last piece together, we're handing people an > incomplete thing with enough rope to hang themselves - "Here's a nice > thing for a centralized deployment but oops if a luser loses a key you > have to run around and do a panty raid to all your servers, because we > left that as an exercise just like every other fucktard that > implements this stuff does instead of giving you the tools to do it > right" > > So, My two cents, make it complete first. Making an archetecture > for ssh that makes it easy to add trust centrally WITHOUT MAKING IT EASY > TO REMOVE IT is irresponsible.Ok, I've devised a way to easily configure and run a secure CAL (Certificate Authorization List) that can just as easily be distributed to multiple redundant CAL Servers. We'll implement it tomorrow (after some internal discussion with claudio&dhartmei) and post it here for review as an updated OpenSSH PKI patch. -- Andre
Daniel Hartmeier <daniel at benzedrine.cx> writes:> This patch against OpenBSD -current adds a simple form of PKI to > OpenSSH. We'll be using it at work.Sounds like something that was needed for a while.> +A host certificate is a guarantee made by the CA that a host public key is > +valid. When a host public key carries a valid certificate, the client can > +use the host public key without asking the user to confirm the fingerprint > +manually and through out-of-band communication the first time. The CA takes > +the responsibility of verifying host keys, and users do no longer need to > +maintain known_hosts files of their own.This confuses the whole authentication vs. authorization concepts. authentication - "May I please see your drivers license?" authorization - "That's a valid license but I don't see your name on the list to go in." I would hate to have my ssh allow anyone in just because we used the same CA. I still see the authorized_keys file as having a very important role even if the first layer defense is to check if the certificate is signed by a CA I trust.> +The CA, specifically the holder of the CA private key (and its password, if it > +is password encrypted), holds broad control over hosts and user accounts set > +up in this way. Should the CA private key become compromised, all user > +accounts become compromised. > + > +There is no way to revoke a certificate once it has been published, the > +certificate is valid until it reaches the expiry date set by the CA.This fix is in the bag once authorized_keys gets consulted even for certificates. -wolfgang -- Wolfgang S. Rupprecht http://www.wsrcc.com/wolfgang/
Daniel Lang <dl at leo.org> writes:> Are you, by any chance, mixing up "known_hosts" and "authorized_keys"?Oops. I quoted the wrong section. I had meant to quote the section about the user_certificates. This is what I meant to cite: +A user certificate is an authorization made by the CA that the +holder of a specific private key may login to the server as a +specific user, without the need of an authorized_keys file being +present. The CA gains the power to grant individual users access +to the server, and users do no longer need to maintain +authorized_keys files of their own. I don't see a problem with the host certificates methodology. (In fact I'd love to see the known_hosts files fade away as more hosts transition to using host certificates.) Thanks, -wolfgang -- Wolfgang S. Rupprecht http://www.wsrcc.com/wolfgang/
> +SECURITY IMPLICATIONS > + > +The CA, specifically the holder of the CA private key (and its password, if it > +is password encrypted), holds broad control over hosts and user accounts set > +up in this way. Should the CA private key become compromised, all user > +accounts become compromised. > + > +There is no way to revoke a certificate once it has been published, the > +certificate is valid until it reaches the expiry date set by the CA. > +After spending a good part of a night locking down a network when an admin "left" this leaves me feeling cold. I think the addition of CAL gives you at least a prayer of addressing this in a timely manner. In the event that you need to reauthorize from the top: 1. Shutdown your CAL servers. 2. Generate and distribute new CA cert. 3. Generate and distribute new host certs. 4. Startup CAL servers. 5. Generate and distribute new user certs. Did I miss anything? The vulnerability window is now time from compromise to time of shutdown of CAL servers. Note that there is one other time where the same procedure is required but without the time pressure - at CA cert expiry time. I think the procedure should at least be included in the documentation if not supported in some way by software... -N
Andre Oppermann
2006-Nov-16 20:58 UTC
OpenSSH Certkey (PKI) adding CAL (online verification)
chefren wrote:> On 11/16/06 19:01, Daniel Hartmeier wrote: > > On Wed, Nov 15, 2006 at 10:47:47AM -0700, Bob Beck wrote: > > > >>So, My two cents, make it complete first. Making an archetecture > >>for ssh that makes it easy to add trust centrally WITHOUT MAKING IT > >>EASY TO REMOVE IT is irresponsible. > > > > Thank you for the rant ;) > > > > Here's the result. Adding a simple daemon that the OpenSSH servers > > can query (over UDP port 22) to check user keys. See the first patch > > chunk for details. > > > > Is this what you had in mind? > > > > Daniel > > Gentlemen, > > I fully agree with the concerns of Bob Beck and I'm happy with the > attention of Daniel Hartmeier. And while everything is better than SSL... > > The security and thus revocation should always be on, by default.As soon as you configure the CAL server it is enabled. If you don't, well... UNIX and especially the *BSD's are about tools, not policy. We provide good tools to implement a wide range of sound policies. It's up to each admin to weight the tradeoffs and implement what is actually the most appropriate approach for their situation.> So it's a certificate system with off-line use of certificates with > inherent bad revocation since you cannot revoke a certificate without > being on-line with the authorizing server.If you configure online certificate verification it will fail closed. So if there is no response, the users will be denied access.> Or it should be an on-line (might of course be local) system where the > authorizing server (and hopefully a well designed backup...) is at least > always asked if access is OK at the beginning of a session (hopefully > possible to limit with time or amount of traffic or packets or or... > (but don't rebuild SSL!)).Have ever tried to read the patch Daniel posted with the message you are replying to? Apparently not so, it would have answered all that.> Please drop the classic "off-line" PKI scheme and present us an elegant > and robust on-line system.I don't think you are in the position to make any demands here... and most certainly not ridiculous ones. If you really want this, then do it yourself and post the patch. Sounds fair, doesn't it? -- Andre
Daniel Hartmeier
2006-Nov-16 21:03 UTC
OpenSSH Certkey (PKI) adding CAL (online verification)
On Thu, Nov 16, 2006 at 07:01:41PM +0100, Daniel Hartmeier wrote:> +When Certkey user authentication fails either because no CAL server can be > +reached or because one CAL server delivers a valid reply marking the user key > +as invalid, the user key can still be used with other authentication methods > +(publickey) to gain access (if found in authorized_keys).Maybe it should be possible to enable CAL even for the traditional publickey authentication. That would enforce an online check even if Certkey isn't used. You could then revoke user keys and they wouldn't work even if they're present in the traditional authorized_keys files. Of course, if you do that and the CALs go down, the only way to login is using passwords. You don't expect CALs to disable these, too, I hope ;) Daniel
Greetings, Overall I'd like to see OpenSSH support PKI in addition to the existing methods. I'm more keen on it being used for host authentication than for user certificates, personally. I did want to comment on this though: * Daniel Hartmeier (daniel at benzedrine.cx) wrote:> +Certkey does not involve online verfication, the CA is not contacted by either > +client or server. Instead, the CA generates certificates which are (once) > +distributed to hosts and users. Any subsequent logins take place without the > +involvment of the CA, based solely on the certificates provided between client > +and server.Would you consider adding support for OCSP? I saw alot of discussion regarding CRLs (and some of their rather well known downsides) but only once saw mention of OCSP, and that with no response. While CRLs are useful in some circumstances I believe OCSP is generally a better approach. Ideally, both would be supported. If I had to pick one I'd rather see OCSP than CRL support though. Thanks, Stephen -------------- next part -------------- A non-text attachment was scrubbed... Name: not available Type: application/pgp-signature Size: 189 bytes Desc: Digital signature Url : http://lists.mindrot.org/pipermail/openssh-unix-dev/attachments/20061116/ee3257d3/attachment.bin
Bob Beck wrote:> > I would think it would be nice if "CAL" had a way of > saying "these are the ones to be revoked" so no shutdown, just > propagate the bad one - but I'm talking to daniel offline about it..That's easy. echo "ab:cd:ef..." > /etc/ssh/blacklist Or use a prediodic rsync to do that. Every pubkey fingerprint listed in it is denied access. -- Andre
Daniel Lang <dl at leo.org> writes:> In fact, it would mean, that you could abandon the authorized_keys > file, but you would still need an "authorized_users" file, that > would need to contain the DN (or a similar identifier) of the user > that matches the certificate. So not a lot is saved, but things > may become less transparent....The advantage of splitting the authorization / authentication is it opens up the possibility of a single certificate being used to identify a user over quite a large range of non-cooperating organizations. That way a potential user can approach the system admin with their company-wide (or Internet-wide) certificate and the system admin can enter that certificate into the a user's list (or into the user's authorized_keys file etc). I'd much rather they use the whole certificate as the test instead of just the DN it contains. That way, the only aspect of the PKI they need to trust is that the key is strong enough to resist breaking. They don't really need to trust that the DN is their true name or that there won't be a DN name-clash a few months down the road. They just need to trust that the PKI works. -wolfgang -- Wolfgang S. Rupprecht http://www.wsrcc.com/wolfgang/
Seemingly Similar Threads
- [PATCH] Getting AFS tokens from a GSSAPI-delegated TGT
- Help request: merging OpenBSD Kerberos change into Portable.
- functions : server_input_channel_req userauth_pubkey
- BSD Auth: set child environment variables requested by login script [PATCH]
- privsep+kerb5+ssh1