From 669aee994348468af8b4b2ebd29b602cf2860b22 Mon Sep 17 00:00:00 2001 From: "djm@openbsd.org" Date: Fri, 30 Jan 2015 01:10:33 +0000 Subject: [PATCH] upstream commit permit KRLs that revoke certificates by serial number or key ID without scoping to a particular CA; ok markus@ --- PROTOCOL.krl | 9 ++++- krl.c | 102 ++++++++++++++++++++++++++++++++------------------- ssh-keygen.c | 26 +++++++------ 3 files changed, 86 insertions(+), 51 deletions(-) diff --git a/PROTOCOL.krl b/PROTOCOL.krl index e8caa4527..b9695107b 100644 --- a/PROTOCOL.krl +++ b/PROTOCOL.krl @@ -37,7 +37,7 @@ The available section types are: #define KRL_SECTION_FINGERPRINT_SHA1 3 #define KRL_SECTION_SIGNATURE 4 -3. Certificate serial section +2. Certificate section These sections use type KRL_SECTION_CERTIFICATES to revoke certificates by serial number or key ID. The consist of the CA key that issued the @@ -47,6 +47,11 @@ ignored. string ca_key string reserved +Where "ca_key" is the standard SSH wire serialisation of the CA's +public key. Alternately, "ca_key" may be an empty string to indicate +the certificate section applies to all CAs (this is most useful when +revoking key IDs). + Followed by one or more sections: byte cert_section_type @@ -161,4 +166,4 @@ Implementations that retrieve KRLs over untrusted channels must verify signatures. Signature sections are optional for KRLs distributed by trusted means. -$OpenBSD: PROTOCOL.krl,v 1.2 2013/01/18 00:24:58 djm Exp $ +$OpenBSD: PROTOCOL.krl,v 1.3 2015/01/30 01:10:33 djm Exp $ diff --git a/krl.c b/krl.c index 3fe29c8b1..4bbaa2080 100644 --- a/krl.c +++ b/krl.c @@ -14,7 +14,7 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -/* $OpenBSD: krl.c,v 1.30 2015/01/26 02:59:11 djm Exp $ */ +/* $OpenBSD: krl.c,v 1.31 2015/01/30 01:10:33 djm Exp $ */ #include "includes.h" @@ -156,8 +156,7 @@ revoked_certs_free(struct revoked_certs *rc) free(rki->key_id); free(rki); } - if (rc->ca_key != NULL) - sshkey_free(rc->ca_key); + sshkey_free(rc->ca_key); } void @@ -214,7 +213,8 @@ revoked_certs_for_ca_key(struct ssh_krl *krl, const struct sshkey *ca_key, *rcp = NULL; TAILQ_FOREACH(rc, &krl->revoked_certs, entry) { - if (sshkey_equal(rc->ca_key, ca_key)) { + if ((ca_key == NULL && rc->ca_key == NULL) || + sshkey_equal(rc->ca_key, ca_key)) { *rcp = rc; return 0; } @@ -224,14 +224,17 @@ revoked_certs_for_ca_key(struct ssh_krl *krl, const struct sshkey *ca_key, /* If this CA doesn't exist in the list then add it now */ if ((rc = calloc(1, sizeof(*rc))) == NULL) return SSH_ERR_ALLOC_FAIL; - if ((r = sshkey_from_private(ca_key, &rc->ca_key)) != 0) { + if (ca_key == NULL) + rc->ca_key = NULL; + else if ((r = sshkey_from_private(ca_key, &rc->ca_key)) != 0) { free(rc); return r; } RB_INIT(&rc->revoked_serials); RB_INIT(&rc->revoked_key_ids); TAILQ_INSERT_TAIL(&krl->revoked_certs, rc, entry); - KRL_DBG(("%s: new CA %s", __func__, sshkey_type(ca_key))); + KRL_DBG(("%s: new CA %s", __func__, + ca_key == NULL ? "*" : sshkey_type(ca_key))); *rcp = rc; return 0; } @@ -554,9 +557,15 @@ revoked_certs_generate(struct revoked_certs *rc, struct sshbuf *buf) if ((sect = sshbuf_new()) == NULL) return SSH_ERR_ALLOC_FAIL; - /* Store the header: CA scope key, reserved */ - if ((r = sshkey_puts(rc->ca_key, buf)) != 0 || - (r = sshbuf_put_string(buf, NULL, 0)) != 0) + /* Store the header: optional CA scope key, reserved */ + if (rc->ca_key == NULL) { + if ((r = sshbuf_put_string(buf, NULL, 0)) != 0) + goto out; + } else { + if ((r = sshkey_puts(rc->ca_key, buf)) != 0) + goto out; + } + if ((r = sshbuf_put_string(buf, NULL, 0)) != 0) goto out; /* Store the revoked serials. */ @@ -813,7 +822,7 @@ parse_revoked_certs(struct sshbuf *buf, struct ssh_krl *krl) if ((r = sshbuf_get_string_direct(buf, &blob, &blen)) != 0 || (r = sshbuf_skip_string(buf)) != 0) goto out; - if ((r = sshkey_from_blob(blob, blen, &ca_key)) != 0) + if (blen != 0 && (r = sshkey_from_blob(blob, blen, &ca_key)) != 0) goto out; while (sshbuf_len(buf) > 0) { @@ -1154,13 +1163,45 @@ ssh_krl_from_blob(struct sshbuf *buf, struct ssh_krl **krlp, return r; } +/* Checks certificate serial number and key ID revocation */ +static int +is_cert_revoked(const struct sshkey *key, struct revoked_certs *rc) +{ + struct revoked_serial rs, *ers; + struct revoked_key_id rki, *erki; + + /* Check revocation by cert key ID */ + memset(&rki, 0, sizeof(rki)); + rki.key_id = key->cert->key_id; + erki = RB_FIND(revoked_key_id_tree, &rc->revoked_key_ids, &rki); + if (erki != NULL) { + KRL_DBG(("%s: revoked by key ID", __func__)); + return SSH_ERR_KEY_REVOKED; + } + + /* + * Legacy cert formats lack serial numbers. Zero serials numbers + * are ignored (it's the default when the CA doesn't specify one). + */ + if (sshkey_cert_is_legacy(key) || key->cert->serial == 0) + return 0; + + memset(&rs, 0, sizeof(rs)); + rs.lo = rs.hi = key->cert->serial; + ers = RB_FIND(revoked_serial_tree, &rc->revoked_serials, &rs); + if (ers != NULL) { + KRL_DBG(("%s: revoked serial %llu matched %llu:%llu", __func__, + key->cert->serial, ers->lo, ers->hi)); + return SSH_ERR_KEY_REVOKED; + } + return 0; +} + /* Checks whether a given key/cert is revoked. Does not check its CA */ static int is_key_revoked(struct ssh_krl *krl, const struct sshkey *key) { struct revoked_blob rb, *erb; - struct revoked_serial rs, *ers; - struct revoked_key_id rki, *erki; struct revoked_certs *rc; int r; @@ -1190,37 +1231,22 @@ is_key_revoked(struct ssh_krl *krl, const struct sshkey *key) if (!sshkey_is_cert(key)) return 0; - /* Check cert revocation */ + /* Check cert revocation for the specified CA */ if ((r = revoked_certs_for_ca_key(krl, key->cert->signature_key, &rc, 0)) != 0) return r; - if (rc == NULL) - return 0; /* No entry for this CA */ - - /* Check revocation by cert key ID */ - memset(&rki, 0, sizeof(rki)); - rki.key_id = key->cert->key_id; - erki = RB_FIND(revoked_key_id_tree, &rc->revoked_key_ids, &rki); - if (erki != NULL) { - KRL_DBG(("%s: revoked by key ID", __func__)); - return SSH_ERR_KEY_REVOKED; + if (rc != NULL) { + if ((r = is_cert_revoked(key, rc)) != 0) + return r; + } + /* Check cert revocation for the wildcard CA */ + if ((r = revoked_certs_for_ca_key(krl, NULL, &rc, 0)) != 0) + return r; + if (rc != NULL) { + if ((r = is_cert_revoked(key, rc)) != 0) + return r; } - /* - * Legacy cert formats lack serial numbers. Zero serials numbers - * are ignored (it's the default when the CA doesn't specify one). - */ - if (sshkey_cert_is_legacy(key) || key->cert->serial == 0) - return 0; - - memset(&rs, 0, sizeof(rs)); - rs.lo = rs.hi = key->cert->serial; - ers = RB_FIND(revoked_serial_tree, &rc->revoked_serials, &rs); - if (ers != NULL) { - KRL_DBG(("%s: revoked serial %llu matched %llu:%llu", __func__, - key->cert->serial, ers->lo, ers->hi)); - return SSH_ERR_KEY_REVOKED; - } KRL_DBG(("%s: %llu no match", __func__, key->cert->serial)); return 0; } diff --git a/ssh-keygen.c b/ssh-keygen.c index b435498cb..2c6a56839 100644 --- a/ssh-keygen.c +++ b/ssh-keygen.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ssh-keygen.c,v 1.260 2015/01/30 00:59:19 djm Exp $ */ +/* $OpenBSD: ssh-keygen.c,v 1.261 2015/01/30 01:10:33 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1994 Tatu Ylonen , Espoo, Finland @@ -1973,7 +1973,7 @@ load_krl(const char *path, struct ssh_krl **krlp) } static void -update_krl_from_file(struct passwd *pw, const char *file, +update_krl_from_file(struct passwd *pw, const char *file, int wild_ca, const struct sshkey *ca, struct ssh_krl *krl) { struct sshkey *key = NULL; @@ -2015,7 +2015,7 @@ update_krl_from_file(struct passwd *pw, const char *file, if (*cp == '\0') continue; if (strncasecmp(cp, "serial:", 7) == 0) { - if (ca == NULL) { + if (ca == NULL && !wild_ca) { fatal("revoking certificates by serial number " "requires specification of a CA key"); } @@ -2052,7 +2052,7 @@ update_krl_from_file(struct passwd *pw, const char *file, __func__); } } else if (strncasecmp(cp, "id:", 3) == 0) { - if (ca == NULL) { + if (ca == NULL && !wild_ca) { fatal("revoking certificates by key ID " "requires specification of a CA key"); } @@ -2103,7 +2103,7 @@ do_gen_krl(struct passwd *pw, int updating, int argc, char **argv) struct ssh_krl *krl; struct stat sb; struct sshkey *ca = NULL; - int fd, i, r; + int fd, i, r, wild_ca = 0; char *tmp; struct sshbuf *kbuf; @@ -2117,11 +2117,15 @@ do_gen_krl(struct passwd *pw, int updating, int argc, char **argv) fatal("KRL \"%s\" does not exist", identity_file); } if (ca_key_path != NULL) { - tmp = tilde_expand_filename(ca_key_path, pw->pw_uid); - if ((r = sshkey_load_public(tmp, &ca, NULL)) != 0) - fatal("Cannot load CA public key %s: %s", - tmp, ssh_err(r)); - free(tmp); + if (strcasecmp(ca_key_path, "none") == 0) + wild_ca = 1; + else { + tmp = tilde_expand_filename(ca_key_path, pw->pw_uid); + if ((r = sshkey_load_public(tmp, &ca, NULL)) != 0) + fatal("Cannot load CA public key %s: %s", + tmp, ssh_err(r)); + free(tmp); + } } if (updating) @@ -2135,7 +2139,7 @@ do_gen_krl(struct passwd *pw, int updating, int argc, char **argv) ssh_krl_set_comment(krl, identity_comment); for (i = 0; i < argc; i++) - update_krl_from_file(pw, argv[i], ca, krl); + update_krl_from_file(pw, argv[i], wild_ca, ca, krl); if ((kbuf = sshbuf_new()) == NULL) fatal("sshbuf_new failed");