OpenSSH Certkey (PKI)

classic Classic list List threaded Threaded
28 messages Options
12
Reply | Threaded
Open this post in threaded view
|

OpenSSH Certkey (PKI)

Daniel Hartmeier
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@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
+};

Reply | Threaded
Open this post in threaded view
|

Re: OpenSSH Certkey (PKI)

Andre Oppermann-2
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

Reply | Threaded
Open this post in threaded view
|

Re: OpenSSH Certkey (PKI)

beck-7
        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"

>
>  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.  
       
        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.

        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.

        (Now I'll go away and be cranky elsewhere, having a bad day)

        -Bob

Reply | Threaded
Open this post in threaded view
|

Re: OpenSSH Certkey (PKI)

Brian Keefer
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"

Reply | Threaded
Open this post in threaded view
|

Re: OpenSSH Certkey (PKI)

Andre Oppermann-2
In reply to this post by beck-7
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

Reply | Threaded
Open this post in threaded view
|

Re: OpenSSH Certkey (PKI)

Andre Oppermann-2
In reply to this post by Brian Keefer
Brian Keefer wrote:

>
> 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.

Mind you we (OpenSSH) provide tools, not policy.  The tools however
should easily accommodate the largest subset of sound policies.  There
are enough valid cases and policies where a simple blacklist is sufficient.

As per reply to Bob we will provide an automatic system for live CERT
validation too.

--
Andre

Reply | Threaded
Open this post in threaded view
|

Re: OpenSSH Certkey (PKI)

Wolfgang S. Rupprecht-51
In reply to this post by Daniel Hartmeier
Daniel Hartmeier <[hidden email]> 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/

Reply | Threaded
Open this post in threaded view
|

Re: OpenSSH Certkey (PKI)

Marc Balmer-2
In reply to this post by Daniel Hartmeier
Quoting Daniel Hartmeier <[hidden email]>:

> 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.

I like this very much.  We have to administrate quite a number of  
OpenBSD machines (>100) so this comes in very handy.

I have seen becks@ concerns and seeing that Andre already allocated  
ressources to extend it makes me confident that this actually is in  
good hands.

That said, I am in favour of this new functionality.  After all it's  
optional, nobody is forced to use it.  It would be nice if this could  
get committet (after some more testing and with a huge number of oks ;)

- Marc Balmer

----------------------------------------------------------------
This message was sent using IMP, the Internet Messaging Program.

Reply | Threaded
Open this post in threaded view
|

Re: OpenSSH Certkey (PKI)

Daniel Lang-2
In reply to this post by Wolfgang S. Rupprecht-51
Hi Wolfgang,

Wolfgang S. Rupprecht wrote on Wed, Nov 15, 2006 at 04:53:55PM -0800:
[..]
> > +the responsibility of verifying host keys, and users do no longer need to
> > +maintain known_hosts files of their own.
              ^^^^^^^^^^^
[..]
> 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.
[..]

Are you, by any chance, mixing up "known_hosts" and "authorized_keys"?

Cheers,
 Daniel

Reply | Threaded
Open this post in threaded view
|

Re: OpenSSH Certkey (PKI)

Wolfgang S. Rupprecht-51
Daniel Lang <[hidden email]> 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/

Reply | Threaded
Open this post in threaded view
|

Re: OpenSSH Certkey (PKI)

Andre Oppermann-2
Wolfgang S. Rupprecht wrote:

> Daniel Lang <[hidden email]> 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.)

Host certificate verification is separate from user authentication/authorization
through certificates.  You you can use one without using and enabling the other.

--
Andre

Reply | Threaded
Open this post in threaded view
|

Re: OpenSSH Certkey (PKI) adding CAL (online verification)

Daniel Hartmeier
In reply to this post by beck-7
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


--- /dev/null Thu Nov 16 18:55:22 2006
+++ README.cal Thu Nov 16 18:54:41 2006
@@ -0,0 +1,155 @@
+OpenSSH CAL
+
+
+INTRODUCTION
+
+Certificate Authorization List (CAL) allows OpenSSH servers to verify the
+validity of user keys online against one or more CAL servers.
+
+Especially in larger installations with many users, once-trusted users may
+become untrusted, or user keys may become compromised. Removing such user keys
+from many machines can become a tedious task that has to be repeated
+regularly.
+
+Online verification has the advantage that the list of valid user keys (and
+therefore, by absence, the list of invalid user keys) can be maintained in one
+single place (or at least in only a few, if multiple CAL servers are used).
+
+The downside is that OpenSSH servers need to be able to contact at least one
+of the CAL servers for every user login.
+
+
+CONFIGURATION
+
+The feature is enabled through the following two options in the server
+configuration:
+
+  CertkeyAuthentication yes
+  CALHost "10.1.2.3"
+  CALHost "10.1.2.4"
+
+A CAL server maintains a list of all valid host's and user's public keys,
+in form of files, one per key, stored under /etc/ssh/cal/users/ and
+/etc/ssh/cal/hosts/ with file names equal to the key's fingerprint.
+
+Revoking a user key is done by simply removing its file on the CAL server.
+
+Conversely, every new user key must be added to the CAL server.
+
+Similarly, host keys must be created and can be revoked by file
+creation/deletion. The CAL server does not have to be restarted or signaled
+after such changes.
+
+
+PROTOCOL
+
+When a user tries to login to an OpenSSH server using a user public key, the
+OpenSSH server sends a query over UDP to the CAL server on port 22. The query
+contains the OpenSSH server's host key fingerprint and the user key
+fingerprint. It is signed by the OpenSSH server's host key.
+
+The CAL server finds the file containing the host public key based on its
+fingerprint. It reads the host public key and verifies the query's signature.
+If either of those two steps fails, the query is ignored. This ensures that
+only holders of trusted host keys can gain any information from the CAL server
+at all.
+
+Then the CAL server finds the file containing the user public key based on its
+fingerprint. If the file is found, the user key is considered valid, otherwise
+it is considered invalid.
+
+The CAL server sends a reply back to the OpenSSH server, indicating whether
+the user key is valid or invalid. The reply contains the user key fingerprint,
+the CAL public key, the CAL certificate, and a signature made by the CAL
+private key.
+
+The OpenSSH server does not need a pre-arranged copy of the CAL public key to
+verify the signature in the reply. Instead, it relies on the pre-arranged copy
+of the CA public key to verify the certificate in the reply. If the
+certificate is valid and has identity "OpenSSH CA CAL", the OpenSSH server
+trusts the certified CAL public key contained in the reply, and uses it to
+verify the signature of the reply.
+
+The OpenSSH server can be configured to use more than one CAL server, by
+specifying additional CALHost configuration options. The OpenSSH server will
+try the first CAL server in the list first, then cycle through the list in a
+round-robin fashion, until it either gets a valid answer or times out. If no
+valid answer can be obtained, the system fails closed, that is the user is not
+allowed to login.
+
+
+SECURITY IMPLICATIONS
+
+The CAL hosts and their /etc/ssh/cal directories must be protected. While a
+compromised CAL cannot grant access to OpenSSH servers for users that don't
+hold any valid keys (either certified or found in authorized_keys), a
+compromise of a CAL host can lead to revoked user keys getting accepted or
+valid user keys getting refused by OpenSSH servers.
+
+Because the OpenSSH servers configured to use CAL servers will fail closed
+when they can't reach (any) of the CAL servers, a denial of service attack
+against the CAL servers can render Certkey user authentication on the OpenSSH
+servers non-functional.
+
+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).
+
+
+IMPLEMENTATION
+
+Queries
+
+Queries consist of a single UDP packet containing one string of ASCII text.
+The string contains values separated by semi-colons. Values must not contain
+semi-colons, but may be empty. All queries have the form
+
+  hostfp;userfp;rand1;sig
+
+'hostfp' is the host key's SSH_FP_MD5 SSH_FP_HEX fingerprint, as printed by
+ssh-keygen -l.
+
+'userfp' is the user key's fingerprint, in the same format.
+
+'rand1' is a random number, acting as a salt.
+
+'sig' is the signature (in hex) over the first three values, made using the
+host private key.
+
+Example:
+
+  f2:c7:5c:a9:48:d8:8c:82:24:d5:2a:d6:75:48:ab:3d;f2:c7:5c:a9:48:d8:8c:82:\
+  24:d5:2a:d6:75:48:ab:3d;384576238456;dbd33e932d80b5612...5a0b4759bee451
+
+Replies
+
+Replies have the same form as queries (one string, separated by semi-colons),
+but contain other fields:
+
+  decision;userfp;rand1;rand2;calkey,calcert;sig
+
+'decision' is the CAL server's decision about the user key. "VALID" if the
+user key should be accepted, "INVALID" if it should be refused.
+
+'userfp' is the user key's fingerprint, same as in the query.
+
+'rand1' is returned verbatim as supplied in the query.
+
+'rand2' has the same format and purpose as 'rand1', but is chosen by the CAL
+server.
+
+'calkey' is the CAL server's public key, in hex.
+
+'calcert' is a certificate for the CAL server's public key, signed by the CA
+for the special identity "OpenSSH CA CAL", in hex.
+
+'sig' is the signature (in hex) over the first five values, made using the CAL
+server's private key.
+
+Example:
+
+  VALID;f2:c7:5c:a9:48:d8:8c:82:24:d5:2a:d6:75:48:ab:3d;384576238456;122345932;\
+  5a0b4759bee451...932d80b5612;33e932d80b561...4759bee;e932d80b...759bee4
+
+$OpenBSD$
--- /dev/null Thu Nov 16 18:55:45 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@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$
--- /dev/null Thu Nov 16 18:56:03 2006
+++ sshcald.1 Thu Nov 16 17:38:00 2006
@@ -0,0 +1,70 @@
+.\" $OpenBSD$
+.\"
+.\" Copyright (c) 2006 Daniel Hartmeier.  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.
+.\" 3. The name of the author may not be used to endorse or promote products
+.\"    derived from this software without specific prior written permission.
+.\"
+.\" 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.
+.\"
+.Dd November 15, 2006
+.Dt SSHCALD 1
+.Os
+.Sh NAME
+.Nm sshcald
+.Nd Certificate Authorization List (CAL) daemon
+.Sh SYNOPSIS
+.Nm sshcald
+.Bk -words
+.Op Fl D
+.Sh DESCRIPTION
+.Nm
+listens on UDP port 22 and answers queries made by OpenSSH servers during
+Certkey user authentication.
+.Pp
+The user trying to login to the server supplies his public key as well as
+a certificate made by the certificate authority (CA).
+The server then queries
+.Nm
+to check whether the user's public key has been revoked or is still valid.
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl D
+When this option is specified,
+.Nm
+will not detach and does not become a daemon.
+.El
+.Sh FILES
+.Bl -tag -width "/etc/ssh/cal/users/*" -compact
+.It Pa /etc/ssh/cal/cal
+CAL private key.
+.It Pa /etc/ssh/cal/cal.pub
+CAL public key.
+.It Pa /etc/ssh/cal/users/
+Location of user public keys.
+The file names must equal the key fingerprints, as printed by
+.Xr ssh-keygen -l .
+.It Pa /etc/ssh/cal/hosts/
+Location of host public keys.
+.El
+.Sh SEE ALSO
+.Xr sshd_config 5 ,
+.Xr sshd 8
--- /dev/null Thu Nov 16 18:56:06 2006
+++ sshcald.c Thu Nov 16 17:49:26 2006
@@ -0,0 +1,296 @@
+/* $OpenBSD$ */
+
+/*
+ * Copyright (c) 2006 Daniel Hartmeier.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *    - Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *    - 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 COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "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
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS 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 <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <openssl/objects.h>
+
+#include "key.h"
+#include "authfile.h"
+#include "rsa.h"
+
+#define FILE_CAL_KEY "/etc/ssh/cal/cal"
+#define PATH_HOST_KEYS "/etc/ssh/cal/hosts"
+#define PATH_USER_KEYS "/etc/ssh/cal/users"
+#define INTBLOB_LEN 20
+
+static Key *calkey = NULL;
+static u_char calkeystr[8192] = "";
+static u_char calcertstr[8192] = "";
+
+static u_int
+hex(const u_char *bin, u_int binlen, u_char *buf, u_int bufsiz)
+{
+ u_int len = 0, i;
+
+ buf[0] = 0;
+ for (i = 0; i < binlen && len + 3 < bufsiz; ++i) {
+ snprintf(buf + len, bufsiz - len, "%2.2x", bin[i]);
+ len += 2;
+ }
+ buf[len] = 0;
+ return (len);
+}
+
+static u_int
+de_hex(const u_char *hex, u_char *buf, u_int siz)
+{
+ u_int len = 0, i;
+ u_char c;
+
+ for (i = 0; hex[i] && len <= siz; ++i) {
+ if (hex[i] >= '0' && hex[i] <= '9')
+ c = hex[i] - '0';
+ else if (hex[i] >= 'a' && hex[i] <= 'f')
+ c = hex[i] - 'a' + 10;
+ else
+ break;
+ if ((i % 2) == 0)
+ buf[len] = c << 4;
+ else
+ buf[len++] |= c;
+ }
+ return (len);
+}
+
+static void
+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;
+}
+
+static Key *
+get_key(const u_char *path, const u_char *fp)
+{
+ Key *key = NULL;
+ u_char fn[MAXPATHLEN], *fpl;
+
+ if (strlen(fp) != 47 || strchr(fp, '/')) {
+ fprintf(stderr, "get_key: invalid fingerprint '%s'\n", fp);
+ return (NULL);
+ }
+ snprintf(fn, sizeof(fn), "%s/%s", path, fp);
+ key = key_load_public(fn, NULL);
+ if (key == NULL) {
+ fprintf(stderr, "get_key: key_load_public() failed for %s\n", fn);
+ return (NULL);
+ }
+ fpl = key_fingerprint(key, SSH_FP_MD5, SSH_FP_HEX);
+ if (strcmp(fpl, fp)) {
+ fprintf(stderr, "get_key: fingerprint mismatch in %s\n", fn);
+ xfree(fpl);
+ key_free(key);
+ return (NULL);
+ }
+ xfree(fpl);
+ return (key);
+}
+
+static void
+reply(int fd, const struct sockaddr_in *sa, const u_char *answer,
+    const u_char *fpu, const u_char *ran)
+{
+ u_char pkt[8192], buf[8192], *sig;
+ u_int siglen;
+ ssize_t len;
+
+ snprintf(pkt, sizeof(pkt), "%s;%s;%s;%lu;%s;%s", answer,
+    fpu, ran, (unsigned long)arc4random(), calkeystr, calcertstr);
+ siglen = sizeof(sig);
+ if (key_sign(calkey, &sig, &siglen, pkt, strlen(pkt)) < 0) {
+ fprintf(stderr, "reply: sign() failed\n");
+ return;
+ }
+ hex(sig, siglen, buf, sizeof(buf));
+ xfree(sig);
+ snprintf(pkt + strlen(pkt), sizeof(pkt) - strlen(pkt), ";%s", buf);
+ len = sendto(fd, pkt, strlen(pkt), 0, (const struct sockaddr *)sa, sizeof(*sa));
+ if (len < 0) {
+ fprintf(stderr, "sendto: %s\n", strerror(errno));
+ return;
+ }
+ if (len != strlen(pkt)) {
+ fprintf(stderr, "sendto: wrote %d != %d bytes\n", (int)len, strlen(pkt));
+ return;
+ }
+}
+
+static void
+message(int fd, const struct sockaddr_in *sa, const u_char *buf)
+{
+ u_char fph[512], fpu[512], ran[512], sig[1024];
+ u_char sigbuf[1024], datbuf[8192];
+ u_int siglen;
+ Key *host, *user;
+
+ printf("%s:%u %s\n", inet_ntoa(sa->sin_addr), (unsigned)ntohs(sa->sin_port), buf);
+ token(&buf, fph, sizeof(fph));
+ token(&buf, fpu, sizeof(fpu));
+ token(&buf, ran, sizeof(ran));
+ token(&buf, sig, sizeof(sig));
+ printf("  fph '%s'\n", fph);
+ printf("  fpu '%s'\n", fpu);
+ printf("  ran '%s'\n", ran);
+ printf("  sig '%s'\n", sig);
+
+ if ((host = get_key(PATH_HOST_KEYS, fph)) == NULL) {
+ fprintf(stderr, "message: couldn't load host key %s\n", fph);
+ return;
+ }
+
+ snprintf(datbuf, sizeof(datbuf), "%s;%s;%s", fph, fpu, ran);
+ siglen = de_hex(sig, sigbuf, sizeof(sigbuf));
+ if (key_verify(host, sigbuf, siglen, datbuf, strlen(datbuf)) < 0) {
+ fprintf(stderr, "message: key_verify() failed, incorrect signature\n");
+ key_free(host);
+ return;
+ }
+
+ /* signature correct, accept message */
+ if ((user = get_key(PATH_USER_KEYS, fpu)) == NULL) {
+ printf("message: couldn't load user key %s\n", fpu);
+ reply(fd, sa, "INVALID", fpu, ran);
+ } else {
+ key_free(user);
+ printf("message: user key %s is valid\n", fpu);
+ reply(fd, sa, "VALID", fpu, ran);
+ }
+
+ key_free(host);
+}
+
+static void
+usage(void)
+{
+ extern char *__progname;
+
+ fprintf(stderr, "%s [-D]\n", __progname);
+ exit(1);
+}
+
+int main(int argc, char *argv[])
+{
+ int detach = 1;
+ int ch;
+ int fd;
+ u_char buf[65536];
+ struct sockaddr_in sa;
+ socklen_t salen;
+ u_char *blob;
+ u_int len;
+ int n;
+
+ while ((ch = getopt(argc, argv, "D")) != -1) {
+ switch (ch) {
+ case 'D':
+ detach = 0;
+ break;
+ default:
+ usage();
+ }
+ }
+ if (optind < argc)
+ usage();
+
+ ERR_load_crypto_strings();
+ OpenSSL_add_all_digests();
+
+ calkey = key_load_private(FILE_CAL_KEY, "", NULL);
+ if (calkey == NULL) {
+ fprintf(stderr, "couldn't load CAL key from %s\n", FILE_CAL_KEY);
+ return (1);
+ }
+ key_to_blob(calkey, &blob, &len);
+ hex(blob, len, calkeystr, sizeof(calkeystr));
+ xfree(blob);
+ if (calkey->cert == NULL) {
+ fprintf(stderr, "CAL key %s has no certificate\n", FILE_CAL_KEY);
+ return (1);
+ }
+ hex(calkey->cert, strlen(calkey->cert), calcertstr, sizeof(calcertstr));
+
+ fd = socket(PF_INET, SOCK_DGRAM, 0);
+ if (fd < 0) {
+ fprintf(stderr, "socket: %s\n", strerror(errno));
+ return (1);
+ }
+ memset(&sa, 0, sizeof(sa));
+ sa.sin_family = AF_INET;
+ sa.sin_addr.s_addr = inet_addr("0.0.0.0");
+ sa.sin_port = htons(22);
+ if (bind(fd, (struct sockaddr *)&sa, sizeof(sa))) {
+ fprintf(stderr, "bind: %s\n", strerror(errno));
+ close(fd);
+ return (1);
+ }
+
+ if (detach && daemon(0, 0)) {
+ fprintf(stderr, "daemon: %s\n", strerror(errno));
+ return (1);
+ }
+
+ while (1) {
+ ssize_t len;
+
+ memset(&sa, 0, sizeof(sa));
+ salen = sizeof(sa);
+ len = recvfrom(fd, buf, sizeof(buf) - 1, 0, (struct sockaddr *)&sa, &salen);
+ if (len <= 0) {
+ if (len < 0 && errno != EINTR)
+ fprintf(stderr, "recvfrom: %s\n", strerror(errno));
+ continue;
+ }
+ if (salen != sizeof(sa)) {
+ fprintf(stderr, "recvfrom: salen %u != sizeof(sa) %u\n",
+    (unsigned)salen, (unsigned)sizeof(sa));
+ continue;
+ }
+ if (buf[len - 1] == '\n')
+ buf[len - 1] = 0;
+ else
+ buf[len] = 0;
+ message(fd, &sa, buf);
+ }
+
+ close(fd);
+ return (0);
+}
--- /dev/null Thu Nov 16 18:56:18 2006
+++ sshcald/Makefile Thu Nov 16 17:17:54 2006
@@ -0,0 +1,32 @@
+# $OpenBSD$
+
+.PATH: ${.CURDIR}/..
+
+PROG= sshcald
+BINOWN= root
+
+#BINMODE?=4555
+
+BINDIR= /usr/sbin
+MAN=
+MAN= sshcald.1
+#LINKS= ${BINDIR}/ssh ${BINDIR}/slogin
+#MLINKS= ssh.1 slogin.1
+
+SRCS= sshcald.c
+
+.include <bsd.own.mk> # for AFS
+
+.if (${KERBEROS5:L} == "yes")
+CFLAGS+= -DKRB5 -I${DESTDIR}/usr/include/kerberosV -DGSSAPI
+.endif # KERBEROS5
+
+.include <bsd.prog.mk>
+
+.if (${KERBEROS5:L} == "yes")
+DPADD+=  ${LIBGSSAPI} ${LIBKRB5}
+LDADD+=  -lgssapi -lkrb5
+.endif # KERBEROS5
+
+DPADD+= ${LIBCRYPTO} ${LIBZ} ${LIBDES}
+LDADD+= -lcrypto -lz -ldes
--- /dev/null Thu Nov 16 18:56:26 2006
+++ auth2-certkey.c Thu Nov 16 17:18:51 2006
@@ -0,0 +1,319 @@
+/* $OpenBSD$ */
+/*
+ * Copyright (c) 2000 Markus Friedl.  All rights reserved.
+ * Copyright (c) 2006 Daniel Hartmeier.  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 <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <errno.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 "authfile.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;
+}
+
+static int
+user_cert_key_cal_approved(Key *key, Key *ca_key)
+{
+ const char *caladdr = "127.0.0.1";
+ int approved = 0;
+ Key *host_key;
+ int fd, i, j, ret;
+ struct sockaddr_in sa;
+ socklen_t salen;
+ u_char pkt[65536] = "TEST";
+ u_char fph[128], fpu[128], rand1[128];
+ u_char *fp, *sig;
+ u_int siglen;
+ ssize_t len;
+ time_t timeout;
+
+ if ((host_key = get_hostkey_by_type(KEY_RSA)) == NULL) {
+ debug2("user_cert_key_cal_approved: no host key available");
+ return 0;
+ }
+ if ((fp = key_fingerprint(host_key, SSH_FP_MD5, SSH_FP_HEX)) == NULL)
+ return 0;
+ strlcpy(fph, fp, sizeof(fph));
+ xfree(fp);
+ if ((fp = key_fingerprint(key, SSH_FP_MD5, SSH_FP_HEX)) == NULL)
+ return 0;
+ strlcpy(fpu, fp, sizeof(fpu));
+ xfree(fp);
+ snprintf(rand1, sizeof(rand1), "%lu", (unsigned long)arc4random());
+ snprintf(pkt, sizeof(pkt), "%s;%s;%s", fph, fpu, rand1);
+ if (key_sign(host_key, &sig, &siglen, pkt, strlen(pkt)) < 0) {
+ debug2("user_cert_key_cal_approved: key_sign() failed");
+ return 0;
+ }
+ strlcat(pkt, ";", sizeof(pkt));
+ for (i = 0; i < siglen; ++i)
+ snprintf(pkt + strlen(pkt), sizeof(pkt) - strlen(pkt), "%2.2x", sig[i]);
+ fd = socket(PF_INET, SOCK_DGRAM, 0);
+ if (fd < 0) {
+ debug2("user_cert_key_cal_approved: socket: %s", strerror(errno));
+ return 0;
+ }
+
+ timeout = time(NULL) + 3;
+ for (i = 0; time(NULL) < timeout; i = (i + 1) % options.num_cal_hosts) {
+
+ debug3("user_cert_key_cal_approved: trying host %d '%s'",
+    i, options.cal_hosts[i]);
+
+ memset(&sa, 0, sizeof(sa));
+ sa.sin_family = AF_INET;
+ sa.sin_addr.s_addr = inet_addr(options.cal_hosts[i]);
+ sa.sin_port = htons(22);
+ len = sendto(fd, pkt, strlen(pkt), 0, (const struct sockaddr *)&sa, sizeof(sa));
+ if (len < 0) {
+ debug2("user_cert_key_cal_approved: sendto: %s", strerror(errno));
+ continue;
+ }
+
+ /* wait at most 1s and read at most 4 messages from this host */
+ for (j = 0; j < 4; ++j) {
+ fd_set fds;
+ struct timeval tv;
+ int r;
+ ssize_t len;
+
+ FD_ZERO(&fds);
+ FD_SET(fd, &fds);
+ tv.tv_sec = 0;
+ tv.tv_usec = 250000;
+ r = select(fd + 1, &fds, NULL, NULL, &tv);
+ if (r < 0) {
+ if (errno == EINTR)
+ continue;
+ debug2("user_cert_key_cal_approved: select: %s",
+    strerror(errno));
+ break;
+ }
+ if (r == 0 || !FD_ISSET(fd, &fds))
+ continue;
+ salen = sizeof(sa);
+ len = recvfrom(fd, pkt, sizeof(pkt) - 1, 0, (struct sockaddr *)&sa, &salen);
+ if (len < 0) {
+ if (errno == EINTR)
+ continue;
+ debug2("user_cert_key_cal_approved: recvfrom: %s\n",
+    strerror(errno));
+ break;
+ }
+ if (len == 0 || salen != sizeof(sa))
+ continue;
+ pkt[len] = 0;
+ if (strcmp(inet_ntoa(sa.sin_addr), options.cal_hosts[i]) ||
+    ntohs(sa.sin_port) != 22) {
+ debug2("user_cert_key_cal_approved: "
+    "answer from unexpected source %s:%u\n",
+    inet_ntoa(sa.sin_addr), (unsigned)ntohs(sa.sin_port));
+ continue;
+ }
+ if (!(ret = cal_answer_verify(ca_key, pkt, fpu, rand1))) {
+ debug2("user_cert_key_cal_approved: cal_answer_verify() failed\n");
+ continue;
+ }
+ /* we have a trusted answer, but it may not be positive */
+ if (ret > 0)
+ approved = 1;
+ goto done;
+ }
+ }
+done:
+ close(fd);
+ return approved;
+}
+
+/* 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 (options.num_cal_hosts > 0)
+ allowed = allowed && user_cert_key_cal_approved(key, ca_key);
+ if (ca_key != NULL)
+ key_free(ca_key);
+ return allowed;
+}
+
+
+Authmethod method_certkey = {
+ "certkey",
+ userauth_certkey,
+ &options.certkey_authentication
+};
Index: Makefile
===================================================================
RCS file: /cvs/src/usr.bin/ssh/Makefile,v
retrieving revision 1.12
diff -u -r1.12 Makefile
--- Makefile 1 Dec 2003 15:47:20 -0000 1.12
+++ Makefile 16 Nov 2006 17:56:40 -0000
@@ -3,7 +3,7 @@
 .include <bsd.own.mk>
 
 SUBDIR= lib ssh sshd ssh-add ssh-keygen ssh-agent scp sftp-server \
- ssh-keysign ssh-keyscan sftp scard
+ ssh-keysign ssh-keyscan sftp scard sshcald
 
 distribution:
  ${INSTALL} -C -o root -g wheel -m 0644 ${.CURDIR}/ssh_config \
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 16 Nov 2006 17:56:40 -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 16 Nov 2006 17:56:40 -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 16 Nov 2006 17:56:40 -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 16 Nov 2006 17:56:41 -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 16 Nov 2006 17:56:41 -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 16 Nov 2006 17:56:41 -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 16 Nov 2006 17:56:41 -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 16 Nov 2006 17:56:41 -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,167 @@
  }
 
  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;
+}
+
+static u_int
+de_hex(const u_char *hex, u_char *buf, u_int siz)
+{
+ u_int len = 0, i;
+ u_char c;
+
+ for (i = 0; hex[i] && len <= siz; ++i) {
+ if (hex[i] >= '0' && hex[i] <= '9')
+ c = hex[i] - '0';
+ else if (hex[i] >= 'a' && hex[i] <= 'f')
+ c = hex[i] - 'a' + 10;
+ else
+ break;
+ if ((i % 2) == 0)
+ buf[len] = c << 4;
+ else
+ buf[len++] |= c;
+ }
+ return (len);
+}
+
+/* check whether CAL anwser packet is valid and signature correct */
+int
+cal_answer_verify(const Key *ca_key, const u_char *pkt, const u_char *fpu,
+    const u_char *rand1)
+{
+ u_char cal_reply[64], cal_fpu[128], cal_rand1[128], cal_rand2[128];
+ u_char cal_pubkey_hex[8192], cal_cert_hex[8192], cal_sig_hex[8192];
+ u_char cal_pubkey[8192], cal_cert[8192], cal_sig[8192];
+ u_char dat[8192];
+ u_int cal_pubkey_len, cal_cert_len, cal_sig_len;
+ Key *cal_key;
+
+ cert_token(&pkt, cal_reply, sizeof(cal_reply));
+ cert_token(&pkt, cal_fpu, sizeof(cal_fpu));
+ cert_token(&pkt, cal_rand1, sizeof(cal_rand1));
+ cert_token(&pkt, cal_rand2, sizeof(cal_rand2));
+ cert_token(&pkt, cal_pubkey_hex, sizeof(cal_pubkey_hex));
+ cert_token(&pkt, cal_cert_hex, sizeof(cal_cert_hex));
+ cert_token(&pkt, cal_sig_hex, sizeof(cal_sig_hex));
+ cal_pubkey_len = de_hex(cal_pubkey_hex, cal_pubkey, sizeof(cal_pubkey));
+ cal_cert_len = de_hex(cal_cert_hex, cal_cert, sizeof(cal_cert) - 1);
+ cal_cert[cal_cert_len] = 0;
+ cal_sig_len = de_hex(cal_sig_hex, cal_sig, sizeof(cal_sig));
+
+ cal_key = key_from_blob(cal_pubkey, cal_pubkey_len);
+ if (cal_key == NULL) {
+ debug2("cal_answer_verify: key_from_blob() failed");
+ return 0;
+ }
+
+ if (!cert_verify(cal_cert, ca_key, cal_key, "OpenSSH CA CAL")) {
+ debug2("cal_answer_verify: certificate not valid");
+ key_free(cal_key);
+ return 0;
+ }
+
+ snprintf(dat, sizeof(dat), "%s;%s;%s;%s;%s;%s", cal_reply, cal_fpu,
+    cal_rand1, cal_rand2, cal_pubkey_hex, cal_cert_hex);
+ if (key_verify(cal_key, cal_sig, cal_sig_len, dat, strlen(dat)) < 0) {
+ debug2("cal_answer_verify: signature is not valid");
+ key_free(cal_key);
+ return 0;
+ }
+ key_free(cal_key);
+
+ if (strcmp(cal_fpu, fpu)) {
+ debug2("cal_answer_verify: CAL fpu mismatch");
+ return 0;
+ }
+ if (strcmp(cal_rand1, rand1)) {
+ debug2("cal_answer_verify: CAL rand1 mismatch");
+ return 0;
+ }
+ if (strcmp(cal_reply, "VALID"))
+ return -1;
+ return 1;
+}
+
+/* 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 = de_hex(ca_sig, sigbuf, sizeof(sigbuf));
+
+ 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 16 Nov 2006 17:56:41 -0000
@@ -53,6 +53,7 @@
  int flags;
  RSA *rsa;
  DSA *dsa;
+ u_char *cert;
 };
 
 Key *key_new(int);
@@ -83,5 +84,9 @@
 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 *);
+int cal_answer_verify(const Key *, const u_char *, const u_char *,
+    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 16 Nov 2006 17:56:42 -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 16 Nov 2006 17:56:42 -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 16 Nov 2006 17:56:42 -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 16 Nov 2006 17:56:42 -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 16 Nov 2006 17:56:42 -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 16 Nov 2006 17:56:43 -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 16 Nov 2006 17:56:43 -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 16 Nov 2006 17:56:43 -0000
@@ -56,6 +56,8 @@
  options->listen_addrs = NULL;
  options->address_family = -1;
  options->num_host_key_files = 0;
+ options->num_cal_hosts = 0;
+ options->ca_key_file = NULL;
  options->pid_file = NULL;
  options->server_key_bits = -1;
  options->login_grace_time = -1;
@@ -77,6 +79,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 +137,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 +185,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)
@@ -260,8 +267,9 @@
  sPermitUserEnvironment, sUseLogin, sAllowTcpForwarding, sCompression,
  sAllowUsers, sDenyUsers, sAllowGroups, sDenyGroups,
  sIgnoreUserKnownHosts, sCiphers, sMacs, sProtocol, sPidFile,
- sGatewayPorts, sPubkeyAuthentication, sXAuthLocation, sSubsystem,
- sMaxStartups, sMaxAuthTries,
+ sCAKeyFile, sCALHost,
+ sGatewayPorts, sCertkeyAuthentication, sPubkeyAuthentication, sXAuthLocation,
+ sSubsystem, sMaxStartups, sMaxAuthTries,
  sBanner, sUseDNS, sHostbasedAuthentication,
  sHostbasedUsesNameFromPacketOnly, sClientAliveInterval,
  sClientAliveCountMax, sAuthorizedKeysFile, sAuthorizedKeysFile2,
@@ -282,6 +290,8 @@
  u_int flags;
 } keywords[] = {
  { "port", sPort, SSHCFG_GLOBAL },
+ { "cakeyfile", sCAKeyFile, SSHCFG_GLOBAL },
+ { "calhost", sCALHost, SSHCFG_GLOBAL },
  { "hostkey", sHostKeyFile, SSHCFG_GLOBAL },
  { "hostdsakey", sHostKeyFile, SSHCFG_GLOBAL }, /* alias */
  { "pidfile", sPidFile, SSHCFG_GLOBAL },
@@ -296,6 +306,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 +749,28 @@
  }
  break;
 
+ case sCAKeyFile:
+ charptr = &options->ca_key_file;
+ goto parse_filename;
+
+ case sCALHost:
+ intptr = &options->num_cal_hosts;
+ if (*intptr >= MAX_CAL_HOSTS)
+ fatal("%s line %d: too many CAL hosts specified (max %d).",
+    filename, linenum, MAX_CAL_HOSTS);
+ charptr = &options->cal_hosts[*intptr];
+ arg = strdelim(&cp);
+ if (!arg || *arg == '\0')
+ fatal("%s line %d: missing value.",
+    filename, linenum);
+ if (*activep && *charptr == NULL) {
+ *charptr = xstrdup(arg);
+ /* increase optional counter */
+ if (intptr != NULL)
+ *intptr = *intptr + 1;
+ }
+ break;
+
  case sPidFile:
  charptr = &options->pid_file;
  goto parse_filename;
@@ -803,6 +836,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 16 Nov 2006 17:56:44 -0000
@@ -26,6 +26,7 @@
 #define MAX_HOSTKEYS 256 /* Max # hostkeys. */
 #define MAX_ACCEPT_ENV 256 /* Max # of env vars. */
 #define MAX_MATCH_GROUPS 256 /* Max # of groups for Match. */
+#define MAX_CAL_HOSTS 16 /* Max # of CAL hosts. */
 
 /* permit_root_login */
 #define PERMIT_NOT_SET -1
@@ -43,6 +44,9 @@
  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. */
+ int     num_cal_hosts; /* Number of CAL host IP addresses */
+ char   *cal_hosts[MAX_CAL_HOSTS]; /* CAL host IP addresses */
  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 +79,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 16 Nov 2006 17:56:44 -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", questio
Reply | Threaded
Open this post in threaded view
|

Re: OpenSSH Certkey (PKI)

Lamont Granquist
In reply to this post by Wolfgang S. Rupprecht-51
On Thu, 16 Nov 2006, Wolfgang S. Rupprecht wrote:
>     +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.

User-maintained authorized_keys files tend to be SOX auditing violations
(anyone with access to the account can grant anyone else access with any
notification or audit trail).  It also lends itself to abuses where
software/generic accounts tend to accumulate the public keys of all the
developers desktop accounts.  The kerberos .k5login file is similarly
problematic.  I would love to see a CA-based approach which would solve
both the authentication and authorization pieces in a way that could be
wrapped with proper auditing on the granting of privs, particularly if it
was simple enough that it was widely adopted instead of authorized_keys
even at very small sites.

Reply | Threaded
Open this post in threaded view
|

Re: OpenSSH Certkey (PKI)

patrick keshishian
In reply to this post by Daniel Hartmeier
Greetings,


> +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));


Maybe I'm reading this wrong, but lets assume filename is
set to "opensshiscool.pub".  After the strlcat, fn will
be "opensshiscool..cert", which I'm not sure is the desired
result.


ps., I stopped right there, got my own bugs to write ;-)

Reply | Threaded
Open this post in threaded view
|

Re: OpenSSH Certkey (PKI)

Nick Bender
In reply to this post by Daniel Hartmeier
> +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

Reply | Threaded
Open this post in threaded view
|

Re: OpenSSH Certkey (PKI) adding CAL (online verification)

chefren
In reply to this post by Daniel Hartmeier
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.

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.

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!)).

Please drop the classic "off-line" PKI scheme and present us an
elegant and robust on-line system.

+++chefren

Reply | Threaded
Open this post in threaded view
|

Re: OpenSSH Certkey (PKI)

beck-7
In reply to this post by Nick Bender
        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..


* Nick Bender <[hidden email]> [2006-11-16 13:23]:

> >+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
>

--
#!/usr/bin/perl
if ((not 0 && not 1) !=  (! 0 && ! 1)) {
   print "Larry and Tom must smoke some really primo stuff...\n";
}

Reply | Threaded
Open this post in threaded view
|

Re: OpenSSH Certkey (PKI) adding CAL (online verification)

Claudio Jeker
In reply to this post by chefren
On Thu, Nov 16, 2006 at 09:16:13PM +0100, 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.
>
> 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.
>
> 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!)).
>
> Please drop the classic "off-line" PKI scheme and present us an
> elegant and robust on-line system.
>

If you like to pay us, we may consider it. But for us off-line is a very
important property. As a network operator, working via ssh while large
parts of your network is unreachable, is an important part of your
business.

--
:wq Claudio

Reply | Threaded
Open this post in threaded view
|

Re: OpenSSH Certkey (PKI) adding CAL (online verification)

Andre Oppermann-2
In reply to this post by chefren
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

Reply | Threaded
Open this post in threaded view
|

Re: OpenSSH Certkey (PKI) adding CAL (online verification)

Daniel Hartmeier
In reply to this post by Daniel Hartmeier
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

12