- djm@cvs.openbsd.org 2013/01/17 23:00:01
[auth.c key.c key.h ssh-keygen.1 ssh-keygen.c sshd_config.5]
[krl.c krl.h PROTOCOL.krl]
add support for Key Revocation Lists (KRLs). These are a compact way to
represent lists of revoked keys and certificates, taking as little as
a single bit of incremental cost to revoke a certificate by serial number.
KRLs are loaded via the existing RevokedKeys sshd_config option.
feedback and ok markus@
diff --git a/ssh-keygen.c b/ssh-keygen.c
index a19a2b0..861b04e 100644
--- a/ssh-keygen.c
+++ b/ssh-keygen.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ssh-keygen.c,v 1.222 2013/01/09 05:40:17 djm Exp $ */
+/* $OpenBSD: ssh-keygen.c,v 1.223 2013/01/17 23:00:01 djm Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1994 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -48,8 +48,11 @@
#include "match.h"
#include "hostfile.h"
#include "dns.h"
+#include "ssh.h"
#include "ssh2.h"
#include "ssh-pkcs11.h"
+#include "atomicio.h"
+#include "krl.h"
/* Number of bits in the RSA/DSA key. This value can be set on the command line. */
#define DEFAULT_BITS 2048
@@ -1897,6 +1900,226 @@
}
static void
+load_krl(const char *path, struct ssh_krl **krlp)
+{
+ Buffer krlbuf;
+ int fd;
+
+ buffer_init(&krlbuf);
+ if ((fd = open(path, O_RDONLY)) == -1)
+ fatal("open %s: %s", path, strerror(errno));
+ if (!key_load_file(fd, path, &krlbuf))
+ fatal("Unable to load KRL");
+ close(fd);
+ /* XXX check sigs */
+ if (ssh_krl_from_blob(&krlbuf, krlp, NULL, 0) != 0 ||
+ *krlp == NULL)
+ fatal("Invalid KRL file");
+ buffer_free(&krlbuf);
+}
+
+static void
+update_krl_from_file(struct passwd *pw, const char *file, const Key *ca,
+ struct ssh_krl *krl)
+{
+ Key *key = NULL;
+ u_long lnum = 0;
+ char *path, *cp, *ep, line[SSH_MAX_PUBKEY_BYTES];
+ unsigned long long serial, serial2;
+ int i, was_explicit_key, was_sha1, r;
+ FILE *krl_spec;
+
+ path = tilde_expand_filename(file, pw->pw_uid);
+ if (strcmp(path, "-") == 0) {
+ krl_spec = stdin;
+ free(path);
+ path = xstrdup("(standard input)");
+ } else if ((krl_spec = fopen(path, "r")) == NULL)
+ fatal("fopen %s: %s", path, strerror(errno));
+
+ if (!quiet)
+ printf("Revoking from %s\n", path);
+ while (read_keyfile_line(krl_spec, path, line, sizeof(line),
+ &lnum) == 0) {
+ was_explicit_key = was_sha1 = 0;
+ cp = line + strspn(line, " \t");
+ /* Trim trailing space, comments and strip \n */
+ for (i = 0, r = -1; cp[i] != '\0'; i++) {
+ if (cp[i] == '#' || cp[i] == '\n') {
+ cp[i] = '\0';
+ break;
+ }
+ if (cp[i] == ' ' || cp[i] == '\t') {
+ /* Remember the start of a span of whitespace */
+ if (r == -1)
+ r = i;
+ } else
+ r = -1;
+ }
+ if (r != -1)
+ cp[r] = '\0';
+ if (*cp == '\0')
+ continue;
+ if (strncasecmp(cp, "serial:", 7) == 0) {
+ if (ca == NULL) {
+ fatal("revoking certificated by serial number "
+ "requires specification of a CA key");
+ }
+ cp += 7;
+ cp = cp + strspn(cp, " \t");
+ errno = 0;
+ serial = strtoull(cp, &ep, 0);
+ if (*cp == '\0' || (*ep != '\0' && *ep != '-'))
+ fatal("%s:%lu: invalid serial \"%s\"",
+ path, lnum, cp);
+ if (errno == ERANGE && serial == ULLONG_MAX)
+ fatal("%s:%lu: serial out of range",
+ path, lnum);
+ serial2 = serial;
+ if (*ep == '-') {
+ cp = ep + 1;
+ errno = 0;
+ serial2 = strtoull(cp, &ep, 0);
+ if (*cp == '\0' || *ep != '\0')
+ fatal("%s:%lu: invalid serial \"%s\"",
+ path, lnum, cp);
+ if (errno == ERANGE && serial2 == ULLONG_MAX)
+ fatal("%s:%lu: serial out of range",
+ path, lnum);
+ if (serial2 <= serial)
+ fatal("%s:%lu: invalid serial range "
+ "%llu:%llu", path, lnum,
+ (unsigned long long)serial,
+ (unsigned long long)serial2);
+ }
+ if (ssh_krl_revoke_cert_by_serial_range(krl,
+ ca, serial, serial2) != 0) {
+ fatal("%s: revoke serial failed",
+ __func__);
+ }
+ } else if (strncasecmp(cp, "id:", 3) == 0) {
+ if (ca == NULL) {
+ fatal("revoking certificated by key ID "
+ "requires specification of a CA key");
+ }
+ cp += 3;
+ cp = cp + strspn(cp, " \t");
+ if (ssh_krl_revoke_cert_by_key_id(krl, ca, cp) != 0)
+ fatal("%s: revoke key ID failed", __func__);
+ } else {
+ if (strncasecmp(cp, "key:", 4) == 0) {
+ cp += 4;
+ cp = cp + strspn(cp, " \t");
+ was_explicit_key = 1;
+ } else if (strncasecmp(cp, "sha1:", 5) == 0) {
+ cp += 5;
+ cp = cp + strspn(cp, " \t");
+ was_sha1 = 1;
+ } else {
+ /*
+ * Just try to process the line as a key.
+ * Parsing will fail if it isn't.
+ */
+ }
+ if ((key = key_new(KEY_UNSPEC)) == NULL)
+ fatal("key_new");
+ if (key_read(key, &cp) != 1)
+ fatal("%s:%lu: invalid key", path, lnum);
+ if (was_explicit_key)
+ r = ssh_krl_revoke_key_explicit(krl, key);
+ else if (was_sha1)
+ r = ssh_krl_revoke_key_sha1(krl, key);
+ else
+ r = ssh_krl_revoke_key(krl, key);
+ if (r != 0)
+ fatal("%s: revoke key failed", __func__);
+ key_free(key);
+ }
+ }
+ if (strcmp(path, "-") != 0)
+ fclose(krl_spec);
+}
+
+static void
+do_gen_krl(struct passwd *pw, int updating, int argc, char **argv)
+{
+ struct ssh_krl *krl;
+ struct stat sb;
+ Key *ca = NULL;
+ int fd, i;
+ char *tmp;
+ Buffer kbuf;
+
+ if (*identity_file == '\0')
+ fatal("KRL generation requires an output file");
+ if (stat(identity_file, &sb) == -1) {
+ if (errno != ENOENT)
+ fatal("Cannot access KRL \"%s\": %s",
+ identity_file, strerror(errno));
+ if (updating)
+ fatal("KRL \"%s\" does not exist", identity_file);
+ }
+ if (ca_key_path != NULL) {
+ tmp = tilde_expand_filename(ca_key_path, pw->pw_uid);
+ if ((ca = key_load_public(tmp, NULL)) == NULL)
+ fatal("Cannot load CA public key %s", tmp);
+ xfree(tmp);
+ }
+
+ if (updating)
+ load_krl(identity_file, &krl);
+ else if ((krl = ssh_krl_init()) == NULL)
+ fatal("couldn't create KRL");
+
+ if (cert_serial != 0)
+ ssh_krl_set_version(krl, cert_serial);
+ if (identity_comment != NULL)
+ ssh_krl_set_comment(krl, identity_comment);
+
+ for (i = 0; i < argc; i++)
+ update_krl_from_file(pw, argv[i], ca, krl);
+
+ buffer_init(&kbuf);
+ if (ssh_krl_to_blob(krl, &kbuf, NULL, 0) != 0)
+ fatal("Couldn't generate KRL");
+ if ((fd = open(identity_file, O_WRONLY|O_CREAT|O_TRUNC, 0644)) == -1)
+ fatal("open %s: %s", identity_file, strerror(errno));
+ if (atomicio(vwrite, fd, buffer_ptr(&kbuf), buffer_len(&kbuf)) !=
+ buffer_len(&kbuf))
+ fatal("write %s: %s", identity_file, strerror(errno));
+ close(fd);
+ buffer_free(&kbuf);
+ ssh_krl_free(krl);
+}
+
+static void
+do_check_krl(struct passwd *pw, int argc, char **argv)
+{
+ int i, r, ret = 0;
+ char *comment;
+ struct ssh_krl *krl;
+ Key *k;
+
+ if (*identity_file == '\0')
+ fatal("KRL checking requires an input file");
+ load_krl(identity_file, &krl);
+ for (i = 0; i < argc; i++) {
+ if ((k = key_load_public(argv[i], &comment)) == NULL)
+ fatal("Cannot load public key %s", argv[i]);
+ r = ssh_krl_check_key(krl, k);
+ printf("%s%s%s%s: %s\n", argv[i],
+ *comment ? " (" : "", comment, *comment ? ")" : "",
+ r == 0 ? "ok" : "REVOKED");
+ if (r != 0)
+ ret = 1;
+ key_free(k);
+ free(comment);
+ }
+ ssh_krl_free(krl);
+ exit(ret);
+}
+
+static void
usage(void)
{
fprintf(stderr, "usage: %s [options]\n", __progname);
@@ -1922,6 +2145,7 @@
fprintf(stderr, " -J number Screen this number of moduli lines.\n");
fprintf(stderr, " -j number Start screening moduli at specified line.\n");
fprintf(stderr, " -K checkpt Write checkpoints to this file.\n");
+ fprintf(stderr, " -k Generate a KRL file.\n");
fprintf(stderr, " -L Print the contents of a certificate.\n");
fprintf(stderr, " -l Show fingerprint of key file.\n");
fprintf(stderr, " -M memory Amount of memory (MB) to use for generating DH-GEX moduli.\n");
@@ -1931,6 +2155,7 @@
fprintf(stderr, " -O option Specify a certificate option.\n");
fprintf(stderr, " -P phrase Provide old passphrase.\n");
fprintf(stderr, " -p Change passphrase of private key file.\n");
+ fprintf(stderr, " -Q Test whether key(s) are revoked in KRL.\n");
fprintf(stderr, " -q Quiet.\n");
fprintf(stderr, " -R hostname Remove host from known_hosts file.\n");
fprintf(stderr, " -r hostname Print DNS resource record.\n");
@@ -1939,6 +2164,7 @@
fprintf(stderr, " -T file Screen candidates for DH-GEX moduli.\n");
fprintf(stderr, " -t type Specify type of key to create.\n");
fprintf(stderr, " -V from:to Specify certificate validity interval.\n");
+ fprintf(stderr, " -u Update KRL rather than creating a new one.\n");
fprintf(stderr, " -v Verbose.\n");
fprintf(stderr, " -W gen Generator to use for generating DH-GEX moduli.\n");
fprintf(stderr, " -y Read private key file and print public key.\n");
@@ -1955,14 +2181,14 @@
{
char dotsshdir[MAXPATHLEN], comment[1024], *passphrase1, *passphrase2;
char *checkpoint = NULL;
- char out_file[MAXPATHLEN], *rr_hostname = NULL, *ep;
+ char out_file[MAXPATHLEN], *ep, *rr_hostname = NULL;
Key *private, *public;
struct passwd *pw;
struct stat st;
int opt, type, fd;
u_int32_t memory = 0, generator_wanted = 0, trials = 100;
int do_gen_candidates = 0, do_screen_candidates = 0;
- int gen_all_hostkeys = 0;
+ int gen_all_hostkeys = 0, gen_krl = 0, update_krl = 0, check_krl = 0;
unsigned long start_lineno = 0, lines_to_process = 0;
BIGNUM *start = NULL;
FILE *f;
@@ -1992,8 +2218,8 @@
exit(1);
}
- while ((opt = getopt(argc, argv, "AegiqpclBHLhvxXyF:b:f:t:D:I:J:j:K:P:"
- "m:N:n:O:C:r:g:R:T:G:M:S:s:a:V:W:z:")) != -1) {
+ while ((opt = getopt(argc, argv, "ABHLQXceghiklpquvxy"
+ "C:D:F:G:I:J:K:M:N:O:P:R:S:T:V:W:a:b:f:g:j:m:n:r:s:t:z:")) != -1) {
switch (opt) {
case 'A':
gen_all_hostkeys = 1;
@@ -2072,6 +2298,9 @@
case 'N':
identity_new_passphrase = optarg;
break;
+ case 'Q':
+ check_krl = 1;
+ break;
case 'O':
add_cert_option(optarg);
break;
@@ -2090,6 +2319,9 @@
cert_key_type = SSH2_CERT_TYPE_HOST;
certflags_flags = 0;
break;
+ case 'k':
+ gen_krl = 1;
+ break;
case 'i':
case 'X':
/* import key */
@@ -2107,6 +2339,9 @@
case 'D':
pkcs11provider = optarg;
break;
+ case 'u':
+ update_krl = 1;
+ break;
case 'v':
if (log_level == SYSLOG_LEVEL_INFO)
log_level = SYSLOG_LEVEL_DEBUG1;
@@ -2182,11 +2417,11 @@
argc -= optind;
if (ca_key_path != NULL) {
- if (argc < 1) {
+ if (argc < 1 && !gen_krl) {
printf("Too few arguments.\n");
usage();
}
- } else if (argc > 0) {
+ } else if (argc > 0 && !gen_krl && !check_krl) {
printf("Too many arguments.\n");
usage();
}
@@ -2198,6 +2433,14 @@
printf("Cannot use -l with -H or -R.\n");
usage();
}
+ if (gen_krl) {
+ do_gen_krl(pw, update_krl, argc, argv);
+ return (0);
+ }
+ if (check_krl) {
+ do_check_krl(pw, argc, argv);
+ return (0);
+ }
if (ca_key_path != NULL) {
if (cert_key_id == NULL)
fatal("Must specify key id (-I) when certifying");