summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog11
-rw-r--r--Makefile.in4
-rw-r--r--PROTOCOL.krl164
-rw-r--r--auth.c15
-rw-r--r--key.c40
-rw-r--r--key.h6
-rw-r--r--krl.c1227
-rw-r--r--krl.h63
-rw-r--r--ssh-keygen.1118
-rw-r--r--ssh-keygen.c257
-rw-r--r--sshd_config.513
11 files changed, 1884 insertions, 34 deletions
diff --git a/ChangeLog b/ChangeLog
index 686fe8966..65403d6e7 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,14 @@
120130118
2 - (djm) OpenBSD CVS Sync
3 - djm@cvs.openbsd.org 2013/01/17 23:00:01
4 [auth.c key.c key.h ssh-keygen.1 ssh-keygen.c sshd_config.5]
5 [krl.c krl.h PROTOCOL.krl]
6 add support for Key Revocation Lists (KRLs). These are a compact way to
7 represent lists of revoked keys and certificates, taking as little as
8 a single bit of incremental cost to revoke a certificate by serial number.
9 KRLs are loaded via the existing RevokedKeys sshd_config option.
10 feedback and ok markus@
11
120130117 1220130117
2 - (djm) [regress/cipher-speed.sh regress/integrity.sh regress/try-ciphers.sh] 13 - (djm) [regress/cipher-speed.sh regress/integrity.sh regress/try-ciphers.sh]
3 check for GCM support before testing GCM ciphers. 14 check for GCM support before testing GCM ciphers.
diff --git a/Makefile.in b/Makefile.in
index 8765b7efb..74eeab574 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -1,4 +1,4 @@
1# $Id: Makefile.in,v 1.329 2012/12/17 04:59:43 dtucker Exp $ 1# $Id: Makefile.in,v 1.330 2013/01/18 00:44:04 djm Exp $
2 2
3# uncomment if you run a non bourne compatable shell. Ie. csh 3# uncomment if you run a non bourne compatable shell. Ie. csh
4#SHELL = @SH@ 4#SHELL = @SH@
@@ -71,7 +71,7 @@ LIBSSH_OBJS=acss.o authfd.o authfile.o bufaux.o bufbn.o buffer.o \
71 monitor_fdpass.o rijndael.o ssh-dss.o ssh-ecdsa.o ssh-rsa.o dh.o \ 71 monitor_fdpass.o rijndael.o ssh-dss.o ssh-ecdsa.o ssh-rsa.o dh.o \
72 kexdh.o kexgex.o kexdhc.o kexgexc.o bufec.o kexecdh.o kexecdhc.o \ 72 kexdh.o kexgex.o kexdhc.o kexgexc.o bufec.o kexecdh.o kexecdhc.o \
73 msg.o progressmeter.o dns.o entropy.o gss-genr.o umac.o umac128.o \ 73 msg.o progressmeter.o dns.o entropy.o gss-genr.o umac.o umac128.o \
74 jpake.o schnorr.o ssh-pkcs11.o 74 jpake.o schnorr.o ssh-pkcs11.o krl.o
75 75
76SSHOBJS= ssh.o readconf.o clientloop.o sshtty.o \ 76SSHOBJS= ssh.o readconf.o clientloop.o sshtty.o \
77 sshconnect.o sshconnect1.o sshconnect2.o mux.o \ 77 sshconnect.o sshconnect1.o sshconnect2.o mux.o \
diff --git a/PROTOCOL.krl b/PROTOCOL.krl
new file mode 100644
index 000000000..e8caa4527
--- /dev/null
+++ b/PROTOCOL.krl
@@ -0,0 +1,164 @@
1This describes the key/certificate revocation list format for OpenSSH.
2
31. Overall format
4
5The KRL consists of a header and zero or more sections. The header is:
6
7#define KRL_MAGIC 0x5353484b524c0a00ULL /* "SSHKRL\n\0" */
8#define KRL_FORMAT_VERSION 1
9
10 uint64 KRL_MAGIC
11 uint32 KRL_FORMAT_VERSION
12 uint64 krl_version
13 uint64 generated_date
14 uint64 flags
15 string reserved
16 string comment
17
18Where "krl_version" is a version number that increases each time the KRL
19is modified, "generated_date" is the time in seconds since 1970-01-01
2000:00:00 UTC that the KRL was generated, "comment" is an optional comment
21and "reserved" an extension field whose contents are currently ignored.
22No "flags" are currently defined.
23
24Following the header are zero or more sections, each consisting of:
25
26 byte section_type
27 string section_data
28
29Where "section_type" indicates the type of the "section_data". An exception
30to this is the KRL_SECTION_SIGNATURE section, that has a slightly different
31format (see below).
32
33The available section types are:
34
35#define KRL_SECTION_CERTIFICATES 1
36#define KRL_SECTION_EXPLICIT_KEY 2
37#define KRL_SECTION_FINGERPRINT_SHA1 3
38#define KRL_SECTION_SIGNATURE 4
39
403. Certificate serial section
41
42These sections use type KRL_SECTION_CERTIFICATES to revoke certificates by
43serial number or key ID. The consist of the CA key that issued the
44certificates to be revoked and a reserved field whose contents is currently
45ignored.
46
47 string ca_key
48 string reserved
49
50Followed by one or more sections:
51
52 byte cert_section_type
53 string cert_section_data
54
55The certificate section types are:
56
57#define KRL_SECTION_CERT_SERIAL_LIST 0x20
58#define KRL_SECTION_CERT_SERIAL_RANGE 0x21
59#define KRL_SECTION_CERT_SERIAL_BITMAP 0x22
60#define KRL_SECTION_CERT_KEY_ID 0x23
61
622.1 Certificate serial list section
63
64This section is identified as KRL_SECTION_CERT_SERIAL_LIST. It revokes
65certificates by listing their serial numbers. The cert_section_data in this
66case contains:
67
68 uint64 revoked_cert_serial
69 uint64 ...
70
71This section may appear multiple times.
72
732.2. Certificate serial range section
74
75These sections use type KRL_SECTION_CERT_SERIAL_RANGE and hold
76a range of serial numbers of certificates:
77
78 uint64 serial_min
79 uint64 serial_max
80
81All certificates in the range serial_min <= serial <= serial_max are
82revoked.
83
84This section may appear multiple times.
85
862.3. Certificate serial bitmap section
87
88Bitmap sections use type KRL_SECTION_CERT_SERIAL_BITMAP and revoke keys
89by listing their serial number in a bitmap.
90
91 uint64 serial_offset
92 mpint revoked_keys_bitmap
93
94A bit set at index N in the bitmap corresponds to revocation of a keys with
95serial number (serial_offset + N).
96
97This section may appear multiple times.
98
992.4. Revoked key ID sections
100
101KRL_SECTION_CERT_KEY_ID sections revoke particular certificate "key
102ID" strings. This may be useful in revoking all certificates
103associated with a particular identity, e.g. a host or a user.
104
105 string key_id[0]
106 ...
107
108This section must contain at least one "key_id". This section may appear
109multiple times.
110
1113. Explicit key sections
112
113These sections, identified as KRL_SECTION_EXPLICIT_KEY, revoke keys
114(not certificates). They are less space efficient than serial numbers,
115but are able to revoke plain keys.
116
117 string public_key_blob[0]
118 ....
119
120This section must contain at least one "public_key_blob". The blob
121must be a raw key (i.e. not a certificate).
122
123This section may appear multiple times.
124
1254. SHA1 fingerprint sections
126
127These sections, identified as KRL_SECTION_FINGERPRINT_SHA1, revoke
128plain keys (i.e. not certificates) by listing their SHA1 hashes:
129
130 string public_key_hash[0]
131 ....
132
133This section must contain at least one "public_key_hash". The hash blob
134is obtained by taking the SHA1 hash of the public key blob. Hashes in
135this section must appear in numeric order, treating each hash as a big-
136endian integer.
137
138This section may appear multiple times.
139
1405. KRL signature sections
141
142The KRL_SECTION_SIGNATURE section serves a different purpose to the
143preceeding ones: to provide cryptographic authentication of a KRL that
144is retrieved over a channel that does not provide integrity protection.
145Its format is slightly different to the previously-described sections:
146in order to simplify the signature generation, it includes as a "body"
147two string components instead of one.
148
149 byte KRL_SECTION_SIGNATURE
150 string signature_key
151 string signature
152
153The signature is calculated over the entire KRL from the KRL_MAGIC
154to this subsection's "signature_key", including both and using the
155signature generation rules appropriate for the type of "signature_key".
156
157This section must appear last in the KRL. If multiple signature sections
158appear, they must appear consecutively at the end of the KRL file.
159
160Implementations that retrieve KRLs over untrusted channels must verify
161signatures. Signature sections are optional for KRLs distributed by
162trusted means.
163
164$OpenBSD: PROTOCOL.krl,v 1.2 2013/01/18 00:24:58 djm Exp $
diff --git a/auth.c b/auth.c
index f5e2d3d2e..d978f0271 100644
--- a/auth.c
+++ b/auth.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: auth.c,v 1.99 2012/12/14 05:26:43 dtucker Exp $ */ 1/* $OpenBSD: auth.c,v 1.100 2013/01/17 23:00:01 djm Exp $ */
2/* 2/*
3 * Copyright (c) 2000 Markus Friedl. All rights reserved. 3 * Copyright (c) 2000 Markus Friedl. All rights reserved.
4 * 4 *
@@ -71,6 +71,7 @@
71#endif 71#endif
72#include "authfile.h" 72#include "authfile.h"
73#include "monitor_wrap.h" 73#include "monitor_wrap.h"
74#include "krl.h"
74 75
75/* import */ 76/* import */
76extern ServerOptions options; 77extern ServerOptions options;
@@ -640,7 +641,16 @@ auth_key_is_revoked(Key *key)
640 641
641 if (options.revoked_keys_file == NULL) 642 if (options.revoked_keys_file == NULL)
642 return 0; 643 return 0;
643 644 switch (ssh_krl_file_contains_key(options.revoked_keys_file, key)) {
645 case 0:
646 return 0; /* Not revoked */
647 case -2:
648 break; /* Not a KRL */
649 default:
650 goto revoked;
651 }
652 debug3("%s: treating %s as a key list", __func__,
653 options.revoked_keys_file);
644 switch (key_in_file(key, options.revoked_keys_file, 0)) { 654 switch (key_in_file(key, options.revoked_keys_file, 0)) {
645 case 0: 655 case 0:
646 /* key not revoked */ 656 /* key not revoked */
@@ -651,6 +661,7 @@ auth_key_is_revoked(Key *key)
651 "authentication"); 661 "authentication");
652 return 1; 662 return 1;
653 case 1: 663 case 1:
664 revoked:
654 /* Key revoked */ 665 /* Key revoked */
655 key_fp = key_fingerprint(key, SSH_FP_MD5, SSH_FP_HEX); 666 key_fp = key_fingerprint(key, SSH_FP_MD5, SSH_FP_HEX);
656 error("WARNING: authentication attempt with a revoked " 667 error("WARNING: authentication attempt with a revoked "
diff --git a/key.c b/key.c
index 7e9099703..4cc5c5d35 100644
--- a/key.c
+++ b/key.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: key.c,v 1.99 2012/05/23 03:28:28 djm Exp $ */ 1/* $OpenBSD: key.c,v 1.100 2013/01/17 23:00:01 djm Exp $ */
2/* 2/*
3 * read_bignum(): 3 * read_bignum():
4 * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland 4 * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -55,6 +55,8 @@
55#include "misc.h" 55#include "misc.h"
56#include "ssh2.h" 56#include "ssh2.h"
57 57
58static int to_blob(const Key *, u_char **, u_int *, int);
59
58static struct KeyCert * 60static struct KeyCert *
59cert_new(void) 61cert_new(void)
60{ 62{
@@ -324,14 +326,15 @@ key_equal(const Key *a, const Key *b)
324} 326}
325 327
326u_char* 328u_char*
327key_fingerprint_raw(Key *k, enum fp_type dgst_type, u_int *dgst_raw_length) 329key_fingerprint_raw(const Key *k, enum fp_type dgst_type,
330 u_int *dgst_raw_length)
328{ 331{
329 const EVP_MD *md = NULL; 332 const EVP_MD *md = NULL;
330 EVP_MD_CTX ctx; 333 EVP_MD_CTX ctx;
331 u_char *blob = NULL; 334 u_char *blob = NULL;
332 u_char *retval = NULL; 335 u_char *retval = NULL;
333 u_int len = 0; 336 u_int len = 0;
334 int nlen, elen, otype; 337 int nlen, elen;
335 338
336 *dgst_raw_length = 0; 339 *dgst_raw_length = 0;
337 340
@@ -371,10 +374,7 @@ key_fingerprint_raw(Key *k, enum fp_type dgst_type, u_int *dgst_raw_length)
371 case KEY_ECDSA_CERT: 374 case KEY_ECDSA_CERT:
372 case KEY_RSA_CERT: 375 case KEY_RSA_CERT:
373 /* We want a fingerprint of the _key_ not of the cert */ 376 /* We want a fingerprint of the _key_ not of the cert */
374 otype = k->type; 377 to_blob(k, &blob, &len, 1);
375 k->type = key_type_plain(k->type);
376 key_to_blob(k, &blob, &len);
377 k->type = otype;
378 break; 378 break;
379 case KEY_UNSPEC: 379 case KEY_UNSPEC:
380 return retval; 380 return retval;
@@ -1587,18 +1587,19 @@ key_from_blob(const u_char *blob, u_int blen)
1587 return key; 1587 return key;
1588} 1588}
1589 1589
1590int 1590static int
1591key_to_blob(const Key *key, u_char **blobp, u_int *lenp) 1591to_blob(const Key *key, u_char **blobp, u_int *lenp, int force_plain)
1592{ 1592{
1593 Buffer b; 1593 Buffer b;
1594 int len; 1594 int len, type;
1595 1595
1596 if (key == NULL) { 1596 if (key == NULL) {
1597 error("key_to_blob: key == NULL"); 1597 error("key_to_blob: key == NULL");
1598 return 0; 1598 return 0;
1599 } 1599 }
1600 buffer_init(&b); 1600 buffer_init(&b);
1601 switch (key->type) { 1601 type = force_plain ? key_type_plain(key->type) : key->type;
1602 switch (type) {
1602 case KEY_DSA_CERT_V00: 1603 case KEY_DSA_CERT_V00:
1603 case KEY_RSA_CERT_V00: 1604 case KEY_RSA_CERT_V00:
1604 case KEY_DSA_CERT: 1605 case KEY_DSA_CERT:
@@ -1609,7 +1610,8 @@ key_to_blob(const Key *key, u_char **blobp, u_int *lenp)
1609 buffer_len(&key->cert->certblob)); 1610 buffer_len(&key->cert->certblob));
1610 break; 1611 break;
1611 case KEY_DSA: 1612 case KEY_DSA:
1612 buffer_put_cstring(&b, key_ssh_name(key)); 1613 buffer_put_cstring(&b,
1614 key_ssh_name_from_type_nid(type, key->ecdsa_nid));
1613 buffer_put_bignum2(&b, key->dsa->p); 1615 buffer_put_bignum2(&b, key->dsa->p);
1614 buffer_put_bignum2(&b, key->dsa->q); 1616 buffer_put_bignum2(&b, key->dsa->q);
1615 buffer_put_bignum2(&b, key->dsa->g); 1617 buffer_put_bignum2(&b, key->dsa->g);
@@ -1617,14 +1619,16 @@ key_to_blob(const Key *key, u_char **blobp, u_int *lenp)
1617 break; 1619 break;
1618#ifdef OPENSSL_HAS_ECC 1620#ifdef OPENSSL_HAS_ECC
1619 case KEY_ECDSA: 1621 case KEY_ECDSA:
1620 buffer_put_cstring(&b, key_ssh_name(key)); 1622 buffer_put_cstring(&b,
1623 key_ssh_name_from_type_nid(type, key->ecdsa_nid));
1621 buffer_put_cstring(&b, key_curve_nid_to_name(key->ecdsa_nid)); 1624 buffer_put_cstring(&b, key_curve_nid_to_name(key->ecdsa_nid));
1622 buffer_put_ecpoint(&b, EC_KEY_get0_group(key->ecdsa), 1625 buffer_put_ecpoint(&b, EC_KEY_get0_group(key->ecdsa),
1623 EC_KEY_get0_public_key(key->ecdsa)); 1626 EC_KEY_get0_public_key(key->ecdsa));
1624 break; 1627 break;
1625#endif 1628#endif
1626 case KEY_RSA: 1629 case KEY_RSA:
1627 buffer_put_cstring(&b, key_ssh_name(key)); 1630 buffer_put_cstring(&b,
1631 key_ssh_name_from_type_nid(type, key->ecdsa_nid));
1628 buffer_put_bignum2(&b, key->rsa->e); 1632 buffer_put_bignum2(&b, key->rsa->e);
1629 buffer_put_bignum2(&b, key->rsa->n); 1633 buffer_put_bignum2(&b, key->rsa->n);
1630 break; 1634 break;
@@ -1646,6 +1650,12 @@ key_to_blob(const Key *key, u_char **blobp, u_int *lenp)
1646} 1650}
1647 1651
1648int 1652int
1653key_to_blob(const Key *key, u_char **blobp, u_int *lenp)
1654{
1655 return to_blob(key, blobp, lenp, 0);
1656}
1657
1658int
1649key_sign( 1659key_sign(
1650 const Key *key, 1660 const Key *key,
1651 u_char **sigp, u_int *lenp, 1661 u_char **sigp, u_int *lenp,
@@ -2024,7 +2034,7 @@ key_cert_check_authority(const Key *k, int want_host, int require_principal,
2024} 2034}
2025 2035
2026int 2036int
2027key_cert_is_legacy(Key *k) 2037key_cert_is_legacy(const Key *k)
2028{ 2038{
2029 switch (k->type) { 2039 switch (k->type) {
2030 case KEY_DSA_CERT_V00: 2040 case KEY_DSA_CERT_V00:
diff --git a/key.h b/key.h
index 39e5577f6..ebdf45677 100644
--- a/key.h
+++ b/key.h
@@ -1,4 +1,4 @@
1/* $OpenBSD: key.h,v 1.34 2012/05/23 03:28:28 djm Exp $ */ 1/* $OpenBSD: key.h,v 1.35 2013/01/17 23:00:01 djm Exp $ */
2 2
3/* 3/*
4 * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved. 4 * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved.
@@ -96,7 +96,7 @@ Key *key_demote(const Key *);
96int key_equal_public(const Key *, const Key *); 96int key_equal_public(const Key *, const Key *);
97int key_equal(const Key *, const Key *); 97int key_equal(const Key *, const Key *);
98char *key_fingerprint(Key *, enum fp_type, enum fp_rep); 98char *key_fingerprint(Key *, enum fp_type, enum fp_rep);
99u_char *key_fingerprint_raw(Key *, enum fp_type, u_int *); 99u_char *key_fingerprint_raw(const Key *, enum fp_type, u_int *);
100const char *key_type(const Key *); 100const char *key_type(const Key *);
101const char *key_cert_type(const Key *); 101const char *key_cert_type(const Key *);
102int key_write(const Key *, FILE *); 102int key_write(const Key *, FILE *);
@@ -114,7 +114,7 @@ int key_certify(Key *, Key *);
114void key_cert_copy(const Key *, struct Key *); 114void key_cert_copy(const Key *, struct Key *);
115int key_cert_check_authority(const Key *, int, int, const char *, 115int key_cert_check_authority(const Key *, int, int, const char *,
116 const char **); 116 const char **);
117int key_cert_is_legacy(Key *); 117int key_cert_is_legacy(const Key *);
118 118
119int key_ecdsa_nid_from_name(const char *); 119int key_ecdsa_nid_from_name(const char *);
120int key_curve_name_to_nid(const char *); 120int key_curve_name_to_nid(const char *);
diff --git a/krl.c b/krl.c
new file mode 100644
index 000000000..485057029
--- /dev/null
+++ b/krl.c
@@ -0,0 +1,1227 @@
1/*
2 * Copyright (c) 2012 Damien Miller <djm@mindrot.org>
3 *
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
17/* $OpenBSD: krl.c,v 1.2 2013/01/18 00:24:58 djm Exp $ */
18
19#include "includes.h"
20
21#include <sys/types.h>
22#include <sys/param.h>
23#include <sys/tree.h>
24#include <sys/queue.h>
25
26#include <errno.h>
27#include <fcntl.h>
28#include <limits.h>
29#include <string.h>
30#include <time.h>
31#include <unistd.h>
32
33#include "buffer.h"
34#include "key.h"
35#include "authfile.h"
36#include "err.h"
37#include "misc.h"
38#include "log.h"
39#include "xmalloc.h"
40
41#include "krl.h"
42
43/* #define DEBUG_KRL */
44#ifdef DEBUG_KRL
45# define KRL_DBG(x) debug3 x
46#else
47# define KRL_DBG(x)
48#endif
49
50/*
51 * Trees of revoked serial numbers, key IDs and keys. This allows
52 * quick searching, querying and producing lists in canonical order.
53 */
54
55/* Tree of serial numbers. XXX make smarter: really need a real sparse bitmap */
56struct revoked_serial {
57 u_int64_t lo, hi;
58 RB_ENTRY(revoked_serial) tree_entry;
59};
60static int serial_cmp(struct revoked_serial *a, struct revoked_serial *b);
61RB_HEAD(revoked_serial_tree, revoked_serial);
62RB_GENERATE_STATIC(revoked_serial_tree, revoked_serial, tree_entry, serial_cmp);
63
64/* Tree of key IDs */
65struct revoked_key_id {
66 char *key_id;
67 RB_ENTRY(revoked_key_id) tree_entry;
68};
69static int key_id_cmp(struct revoked_key_id *a, struct revoked_key_id *b);
70RB_HEAD(revoked_key_id_tree, revoked_key_id);
71RB_GENERATE_STATIC(revoked_key_id_tree, revoked_key_id, tree_entry, key_id_cmp);
72
73/* Tree of blobs (used for keys and fingerprints) */
74struct revoked_blob {
75 u_char *blob;
76 u_int len;
77 RB_ENTRY(revoked_blob) tree_entry;
78};
79static int blob_cmp(struct revoked_blob *a, struct revoked_blob *b);
80RB_HEAD(revoked_blob_tree, revoked_blob);
81RB_GENERATE_STATIC(revoked_blob_tree, revoked_blob, tree_entry, blob_cmp);
82
83/* Tracks revoked certs for a single CA */
84struct revoked_certs {
85 Key *ca_key;
86 struct revoked_serial_tree revoked_serials;
87 struct revoked_key_id_tree revoked_key_ids;
88 TAILQ_ENTRY(revoked_certs) entry;
89};
90TAILQ_HEAD(revoked_certs_list, revoked_certs);
91
92struct ssh_krl {
93 u_int64_t krl_version;
94 u_int64_t generated_date;
95 u_int64_t flags;
96 char *comment;
97 struct revoked_blob_tree revoked_keys;
98 struct revoked_blob_tree revoked_sha1s;
99 struct revoked_certs_list revoked_certs;
100};
101
102/* Return equal if a and b overlap */
103static int
104serial_cmp(struct revoked_serial *a, struct revoked_serial *b)
105{
106 if (a->hi >= b->lo && a->lo <= b->hi)
107 return 0;
108 return a->lo < b->lo ? -1 : 1;
109}
110
111static int
112key_id_cmp(struct revoked_key_id *a, struct revoked_key_id *b)
113{
114 return strcmp(a->key_id, b->key_id);
115}
116
117static int
118blob_cmp(struct revoked_blob *a, struct revoked_blob *b)
119{
120 int r;
121
122 if (a->len != b->len) {
123 if ((r = memcmp(a->blob, b->blob, MIN(a->len, b->len))) != 0)
124 return r;
125 return a->len > b->len ? 1 : -1;
126 } else
127 return memcmp(a->blob, b->blob, a->len);
128}
129
130struct ssh_krl *
131ssh_krl_init(void)
132{
133 struct ssh_krl *krl;
134
135 if ((krl = calloc(1, sizeof(*krl))) == NULL)
136 return NULL;
137 RB_INIT(&krl->revoked_keys);
138 RB_INIT(&krl->revoked_sha1s);
139 TAILQ_INIT(&krl->revoked_certs);
140 return krl;
141}
142
143static void
144revoked_certs_free(struct revoked_certs *rc)
145{
146 struct revoked_serial *rs, *trs;
147 struct revoked_key_id *rki, *trki;
148
149 RB_FOREACH_SAFE(rs, revoked_serial_tree, &rc->revoked_serials, trs) {
150 RB_REMOVE(revoked_serial_tree, &rc->revoked_serials, rs);
151 free(rs);
152 }
153 RB_FOREACH_SAFE(rki, revoked_key_id_tree, &rc->revoked_key_ids, trki) {
154 RB_REMOVE(revoked_key_id_tree, &rc->revoked_key_ids, rki);
155 free(rki->key_id);
156 free(rki);
157 }
158 if (rc->ca_key != NULL)
159 key_free(rc->ca_key);
160}
161
162void
163ssh_krl_free(struct ssh_krl *krl)
164{
165 struct revoked_blob *rb, *trb;
166 struct revoked_certs *rc, *trc;
167
168 if (krl == NULL)
169 return;
170
171 free(krl->comment);
172 RB_FOREACH_SAFE(rb, revoked_blob_tree, &krl->revoked_keys, trb) {
173 RB_REMOVE(revoked_blob_tree, &krl->revoked_keys, rb);
174 free(rb->blob);
175 free(rb);
176 }
177 RB_FOREACH_SAFE(rb, revoked_blob_tree, &krl->revoked_sha1s, trb) {
178 RB_REMOVE(revoked_blob_tree, &krl->revoked_sha1s, rb);
179 free(rb->blob);
180 free(rb);
181 }
182 TAILQ_FOREACH_SAFE(rc, &krl->revoked_certs, entry, trc) {
183 TAILQ_REMOVE(&krl->revoked_certs, rc, entry);
184 revoked_certs_free(rc);
185 }
186}
187
188void
189ssh_krl_set_version(struct ssh_krl *krl, u_int64_t version)
190{
191 krl->krl_version = version;
192}
193
194void
195ssh_krl_set_comment(struct ssh_krl *krl, const char *comment)
196{
197 free(krl->comment);
198 if ((krl->comment = strdup(comment)) == NULL)
199 fatal("%s: strdup", __func__);
200}
201
202/*
203 * Find the revoked_certs struct for a CA key. If allow_create is set then
204 * create a new one in the tree if one did not exist already.
205 */
206static int
207revoked_certs_for_ca_key(struct ssh_krl *krl, const Key *ca_key,
208 struct revoked_certs **rcp, int allow_create)
209{
210 struct revoked_certs *rc;
211
212 *rcp = NULL;
213 TAILQ_FOREACH(rc, &krl->revoked_certs, entry) {
214 if (key_equal(rc->ca_key, ca_key)) {
215 *rcp = rc;
216 return 0;
217 }
218 }
219 if (!allow_create)
220 return 0;
221 /* If this CA doesn't exist in the list then add it now */
222 if ((rc = calloc(1, sizeof(*rc))) == NULL)
223 return -1;
224 if ((rc->ca_key = key_from_private(ca_key)) == NULL) {
225 free(rc);
226 return -1;
227 }
228 RB_INIT(&rc->revoked_serials);
229 RB_INIT(&rc->revoked_key_ids);
230 TAILQ_INSERT_TAIL(&krl->revoked_certs, rc, entry);
231 debug3("%s: new CA %s", __func__, key_type(ca_key));
232 *rcp = rc;
233 return 0;
234}
235
236static int
237insert_serial_range(struct revoked_serial_tree *rt, u_int64_t lo, u_int64_t hi)
238{
239 struct revoked_serial rs, *ers, *crs, *irs;
240
241 KRL_DBG(("%s: insert %llu:%llu", __func__, lo, hi));
242 bzero(&rs, sizeof(rs));
243 rs.lo = lo;
244 rs.hi = hi;
245 ers = RB_NFIND(revoked_serial_tree, rt, &rs);
246 if (ers == NULL || serial_cmp(ers, &rs) != 0) {
247 /* No entry matches. Just insert */
248 if ((irs = malloc(sizeof(rs))) == NULL)
249 return -1;
250 memcpy(irs, &rs, sizeof(*irs));
251 ers = RB_INSERT(revoked_serial_tree, rt, irs);
252 if (ers != NULL) {
253 KRL_DBG(("%s: bad: ers != NULL", __func__));
254 /* Shouldn't happen */
255 free(ers);
256 return -1;
257 }
258 ers = irs;
259 } else {
260 KRL_DBG(("%s: overlap found %llu:%llu", __func__,
261 ers->lo, ers->hi));
262 /*
263 * The inserted entry overlaps an existing one. Grow the
264 * existing entry.
265 */
266 if (ers->lo > lo)
267 ers->lo = lo;
268 if (ers->hi < hi)
269 ers->hi = hi;
270 }
271 /*
272 * The inserted or revised range might overlap or abut adjacent ones;
273 * coalesce as necessary.
274 */
275
276 /* Check predecessors */
277 while ((crs = RB_PREV(revoked_serial_tree, rt, ers)) != NULL) {
278 KRL_DBG(("%s: pred %llu:%llu", __func__, crs->lo, crs->hi));
279 if (ers->lo != 0 && crs->hi < ers->lo - 1)
280 break;
281 /* This entry overlaps. */
282 if (crs->lo < ers->lo) {
283 ers->lo = crs->lo;
284 KRL_DBG(("%s: pred extend %llu:%llu", __func__,
285 ers->lo, ers->hi));
286 }
287 RB_REMOVE(revoked_serial_tree, rt, crs);
288 free(crs);
289 }
290 /* Check successors */
291 while ((crs = RB_NEXT(revoked_serial_tree, rt, ers)) != NULL) {
292 KRL_DBG(("%s: succ %llu:%llu", __func__, crs->lo, crs->hi));
293 if (ers->hi != (u_int64_t)-1 && crs->lo > ers->hi + 1)
294 break;
295 /* This entry overlaps. */
296 if (crs->hi > ers->hi) {
297 ers->hi = crs->hi;
298 KRL_DBG(("%s: succ extend %llu:%llu", __func__,
299 ers->lo, ers->hi));
300 }
301 RB_REMOVE(revoked_serial_tree, rt, crs);
302 free(crs);
303 }
304 KRL_DBG(("%s: done, final %llu:%llu", __func__, ers->lo, ers->hi));
305 return 0;
306}
307
308int
309ssh_krl_revoke_cert_by_serial(struct ssh_krl *krl, const Key *ca_key,
310 u_int64_t serial)
311{
312 return ssh_krl_revoke_cert_by_serial_range(krl, ca_key, serial, serial);
313}
314
315int
316ssh_krl_revoke_cert_by_serial_range(struct ssh_krl *krl, const Key *ca_key,
317 u_int64_t lo, u_int64_t hi)
318{
319 struct revoked_certs *rc;
320
321 if (lo > hi || lo == 0)
322 return -1;
323 if (revoked_certs_for_ca_key(krl, ca_key, &rc, 1) != 0)
324 return -1;
325 return insert_serial_range(&rc->revoked_serials, lo, hi);
326}
327
328int
329ssh_krl_revoke_cert_by_key_id(struct ssh_krl *krl, const Key *ca_key,
330 const char *key_id)
331{
332 struct revoked_key_id *rki, *erki;
333 struct revoked_certs *rc;
334
335 if (revoked_certs_for_ca_key(krl, ca_key, &rc, 1) != 0)
336 return -1;
337
338 debug3("%s: revoke %s", __func__, key_id);
339 if ((rki = calloc(1, sizeof(*rki))) == NULL ||
340 (rki->key_id = strdup(key_id)) == NULL) {
341 free(rki);
342 fatal("%s: strdup", __func__);
343 }
344 erki = RB_INSERT(revoked_key_id_tree, &rc->revoked_key_ids, rki);
345 if (erki != NULL) {
346 free(rki->key_id);
347 free(rki);
348 }
349 return 0;
350}
351
352/* Convert "key" to a public key blob without any certificate information */
353static int
354plain_key_blob(const Key *key, u_char **blob, u_int *blen)
355{
356 Key *kcopy;
357 int r;
358
359 if ((kcopy = key_from_private(key)) == NULL)
360 return -1;
361 if (key_is_cert(kcopy)) {
362 if (key_drop_cert(kcopy) != 0) {
363 error("%s: key_drop_cert", __func__);
364 key_free(kcopy);
365 return -1;
366 }
367 }
368 r = key_to_blob(kcopy, blob, blen);
369 free(kcopy);
370 return r == 0 ? -1 : 0;
371}
372
373/* Revoke a key blob. Ownership of blob is transferred to the tree */
374static int
375revoke_blob(struct revoked_blob_tree *rbt, u_char *blob, u_int len)
376{
377 struct revoked_blob *rb, *erb;
378
379 if ((rb = calloc(1, sizeof(*rb))) == NULL)
380 return -1;
381 rb->blob = blob;
382 rb->len = len;
383 erb = RB_INSERT(revoked_blob_tree, rbt, rb);
384 if (erb != NULL) {
385 free(rb->blob);
386 free(rb);
387 }
388 return 0;
389}
390
391int
392ssh_krl_revoke_key_explicit(struct ssh_krl *krl, const Key *key)
393{
394 u_char *blob;
395 u_int len;
396
397 debug3("%s: revoke type %s", __func__, key_type(key));
398 if (plain_key_blob(key, &blob, &len) != 0)
399 return -1;
400 return revoke_blob(&krl->revoked_keys, blob, len);
401}
402
403int
404ssh_krl_revoke_key_sha1(struct ssh_krl *krl, const Key *key)
405{
406 u_char *blob;
407 u_int len;
408
409 debug3("%s: revoke type %s by sha1", __func__, key_type(key));
410 if ((blob = key_fingerprint_raw(key, SSH_FP_SHA1, &len)) == NULL)
411 return -1;
412 return revoke_blob(&krl->revoked_sha1s, blob, len);
413}
414
415int
416ssh_krl_revoke_key(struct ssh_krl *krl, const Key *key)
417{
418 if (!key_is_cert(key))
419 return ssh_krl_revoke_key_sha1(krl, key);
420
421 if (key_cert_is_legacy(key) || key->cert->serial == 0) {
422 return ssh_krl_revoke_cert_by_key_id(krl,
423 key->cert->signature_key,
424 key->cert->key_id);
425 } else {
426 return ssh_krl_revoke_cert_by_serial(krl,
427 key->cert->signature_key,
428 key->cert->serial);
429 }
430}
431
432/*
433 * Select a copact next section type to emit in a KRL based on the
434 * current section type, the run length of contiguous revoked serial
435 * numbers and the gaps from the last and to the next revoked serial.
436 * Applies a mostly-accurate bit cost model to select the section type
437 * that will minimise the size of the resultant KRL.
438 */
439static int
440choose_next_state(int current_state, u_int64_t contig, int final,
441 u_int64_t last_gap, u_int64_t next_gap, int *force_new_section)
442{
443 int new_state;
444 u_int64_t cost, cost_list, cost_range, cost_bitmap, cost_bitmap_restart;
445
446 /*
447 * Avoid unsigned overflows.
448 * The limits are high enough to avoid confusing the calculations.
449 */
450 contig = MIN(contig, 1ULL<<31);
451 last_gap = MIN(last_gap, 1ULL<<31);
452 next_gap = MIN(next_gap, 1ULL<<31);
453
454 /*
455 * Calculate the cost to switch from the current state to candidates.
456 * NB. range sections only ever contain a single range, so their
457 * switching cost is independent of the current_state.
458 */
459 cost_list = cost_bitmap = cost_bitmap_restart = 0;
460 cost_range = 8;
461 switch (current_state) {
462 case KRL_SECTION_CERT_SERIAL_LIST:
463 cost_bitmap_restart = cost_bitmap = 8 + 64;
464 break;
465 case KRL_SECTION_CERT_SERIAL_BITMAP:
466 cost_list = 8;
467 cost_bitmap_restart = 8 + 64;
468 break;
469 case KRL_SECTION_CERT_SERIAL_RANGE:
470 case 0:
471 cost_bitmap_restart = cost_bitmap = 8 + 64;
472 cost_list = 8;
473 }
474
475 /* Estimate base cost in bits of each section type */
476 cost_list += 64 * contig + (final ? 0 : 8+64);
477 cost_range += (2 * 64) + (final ? 0 : 8+64);
478 cost_bitmap += last_gap + contig + (final ? 0 : MIN(next_gap, 8+64));
479 cost_bitmap_restart += contig + (final ? 0 : MIN(next_gap, 8+64));
480
481 /* Convert to byte costs for actual comparison */
482 cost_list = (cost_list + 7) / 8;
483 cost_bitmap = (cost_bitmap + 7) / 8;
484 cost_bitmap_restart = (cost_bitmap_restart + 7) / 8;
485 cost_range = (cost_range + 7) / 8;
486
487 /* Now pick the best choice */
488 *force_new_section = 0;
489 new_state = KRL_SECTION_CERT_SERIAL_BITMAP;
490 cost = cost_bitmap;
491 if (cost_range < cost) {
492 new_state = KRL_SECTION_CERT_SERIAL_RANGE;
493 cost = cost_range;
494 }
495 if (cost_list < cost) {
496 new_state = KRL_SECTION_CERT_SERIAL_LIST;
497 cost = cost_list;
498 }
499 if (cost_bitmap_restart < cost) {
500 new_state = KRL_SECTION_CERT_SERIAL_BITMAP;
501 *force_new_section = 1;
502 cost = cost_bitmap_restart;
503 }
504 debug3("%s: contig %llu last_gap %llu next_gap %llu final %d, costs:"
505 "list %llu range %llu bitmap %llu new bitmap %llu, "
506 "selected 0x%02x%s", __func__, contig, last_gap, next_gap, final,
507 cost_list, cost_range, cost_bitmap, cost_bitmap_restart, new_state,
508 *force_new_section ? " restart" : "");
509 return new_state;
510}
511
512/* Generate a KRL_SECTION_CERTIFICATES KRL section */
513static int
514revoked_certs_generate(struct revoked_certs *rc, Buffer *buf)
515{
516 int final, force_new_sect, r = -1;
517 u_int64_t i, contig, gap, last = 0, bitmap_start = 0;
518 struct revoked_serial *rs, *nrs;
519 struct revoked_key_id *rki;
520 int next_state, state = 0;
521 Buffer sect;
522 u_char *kblob = NULL;
523 u_int klen;
524 BIGNUM *bitmap = NULL;
525
526 /* Prepare CA scope key blob if we have one supplied */
527 if (key_to_blob(rc->ca_key, &kblob, &klen) == 0)
528 return -1;
529
530 buffer_init(&sect);
531
532 /* Store the header */
533 buffer_put_string(buf, kblob, klen);
534 buffer_put_string(buf, NULL, 0); /* Reserved */
535
536 free(kblob);
537
538 /* Store the revoked serials. */
539 for (rs = RB_MIN(revoked_serial_tree, &rc->revoked_serials);
540 rs != NULL;
541 rs = RB_NEXT(revoked_serial_tree, &rc->revoked_serials, rs)) {
542 debug3("%s: serial %llu:%llu state 0x%02x", __func__,
543 rs->lo, rs->hi, state);
544
545 /* Check contiguous length and gap to next section (if any) */
546 nrs = RB_NEXT(revoked_serial_tree, &rc->revoked_serials, rs);
547 final = nrs == NULL;
548 gap = nrs == NULL ? 0 : nrs->lo - rs->hi;
549 contig = 1 + (rs->hi - rs->lo);
550
551 /* Choose next state based on these */
552 next_state = choose_next_state(state, contig, final,
553 state == 0 ? 0 : rs->lo - last, gap, &force_new_sect);
554
555 /*
556 * If the current section is a range section or has a different
557 * type to the next section, then finish it off now.
558 */
559 if (state != 0 && (force_new_sect || next_state != state ||
560 state == KRL_SECTION_CERT_SERIAL_RANGE)) {
561 debug3("%s: finish state 0x%02x", __func__, state);
562 switch (state) {
563 case KRL_SECTION_CERT_SERIAL_LIST:
564 case KRL_SECTION_CERT_SERIAL_RANGE:
565 break;
566 case KRL_SECTION_CERT_SERIAL_BITMAP:
567 buffer_put_bignum2(&sect, bitmap);
568 BN_free(bitmap);
569 bitmap = NULL;
570 break;
571 }
572 buffer_put_char(buf, state);
573 buffer_put_string(buf,
574 buffer_ptr(&sect), buffer_len(&sect));
575 }
576
577 /* If we are starting a new section then prepare it now */
578 if (next_state != state || force_new_sect) {
579 debug3("%s: start state 0x%02x", __func__, next_state);
580 state = next_state;
581 buffer_clear(&sect);
582 switch (state) {
583 case KRL_SECTION_CERT_SERIAL_LIST:
584 case KRL_SECTION_CERT_SERIAL_RANGE:
585 break;
586 case KRL_SECTION_CERT_SERIAL_BITMAP:
587 if ((bitmap = BN_new()) == NULL)
588 goto out;
589 bitmap_start = rs->lo;
590 buffer_put_int64(&sect, bitmap_start);
591 break;
592 }
593 }
594
595 /* Perform section-specific processing */
596 switch (state) {
597 case KRL_SECTION_CERT_SERIAL_LIST:
598 for (i = rs->lo; i < contig; i++)
599 buffer_put_int64(&sect, rs->lo + i);
600 break;
601 case KRL_SECTION_CERT_SERIAL_RANGE:
602 buffer_put_int64(&sect, rs->lo);
603 buffer_put_int64(&sect, rs->hi);
604 break;
605 case KRL_SECTION_CERT_SERIAL_BITMAP:
606 if (rs->lo - bitmap_start > INT_MAX) {
607 error("%s: insane bitmap gap", __func__);
608 goto out;
609 }
610 for (i = 0; i < contig; i++) {
611 if (BN_set_bit(bitmap,
612 rs->lo + i - bitmap_start) != 1)
613 goto out;
614 }
615 break;
616 }
617 last = rs->hi;
618 }
619 /* Flush the remaining section, if any */
620 if (state != 0) {
621 debug3("%s: serial final flush for state 0x%02x",
622 __func__, state);
623 switch (state) {
624 case KRL_SECTION_CERT_SERIAL_LIST:
625 case KRL_SECTION_CERT_SERIAL_RANGE:
626 break;
627 case KRL_SECTION_CERT_SERIAL_BITMAP:
628 buffer_put_bignum2(&sect, bitmap);
629 BN_free(bitmap);
630 bitmap = NULL;
631 break;
632 }
633 buffer_put_char(buf, state);
634 buffer_put_string(buf,
635 buffer_ptr(&sect), buffer_len(&sect));
636 }
637 debug3("%s: serial done ", __func__);
638
639 /* Now output a section for any revocations by key ID */
640 buffer_clear(&sect);
641 RB_FOREACH(rki, revoked_key_id_tree, &rc->revoked_key_ids) {
642 debug3("%s: key ID %s", __func__, rki->key_id);
643 buffer_put_cstring(&sect, rki->key_id);
644 }
645 if (buffer_len(&sect) != 0) {
646 buffer_put_char(buf, KRL_SECTION_CERT_KEY_ID);
647 buffer_put_string(buf, buffer_ptr(&sect),
648 buffer_len(&sect));
649 }
650 r = 0;
651 out:
652 if (bitmap != NULL)
653 BN_free(bitmap);
654 buffer_free(&sect);
655 return r;
656}
657
658int
659ssh_krl_to_blob(struct ssh_krl *krl, Buffer *buf, const Key **sign_keys,
660 u_int nsign_keys)
661{
662 int r = -1;
663 struct revoked_certs *rc;
664 struct revoked_blob *rb;
665 Buffer sect;
666 u_char *kblob = NULL, *sblob = NULL;
667 u_int klen, slen, i;
668
669 if (krl->generated_date == 0)
670 krl->generated_date = time(NULL);
671
672 buffer_init(&sect);
673
674 /* Store the header */
675 buffer_append(buf, KRL_MAGIC, sizeof(KRL_MAGIC) - 1);
676 buffer_put_int(buf, KRL_FORMAT_VERSION);
677 buffer_put_int64(buf, krl->krl_version);
678 buffer_put_int64(buf, krl->generated_date);
679 buffer_put_int64(buf, krl->flags);
680 buffer_put_string(buf, NULL, 0);
681 buffer_put_cstring(buf, krl->comment ? krl->comment : "");
682
683 /* Store sections for revoked certificates */
684 TAILQ_FOREACH(rc, &krl->revoked_certs, entry) {
685 if (revoked_certs_generate(rc, &sect) != 0)
686 goto out;
687 buffer_put_char(buf, KRL_SECTION_CERTIFICATES);
688 buffer_put_string(buf, buffer_ptr(&sect),
689 buffer_len(&sect));
690 }
691
692 /* Finally, output sections for revocations by public key/hash */
693 buffer_clear(&sect);
694 RB_FOREACH(rb, revoked_blob_tree, &krl->revoked_keys) {
695 debug3("%s: key len %u ", __func__, rb->len);
696 buffer_put_string(&sect, rb->blob, rb->len);
697 }
698 if (buffer_len(&sect) != 0) {
699 buffer_put_char(buf, KRL_SECTION_EXPLICIT_KEY);
700 buffer_put_string(buf, buffer_ptr(&sect),
701 buffer_len(&sect));
702 }
703 buffer_clear(&sect);
704 RB_FOREACH(rb, revoked_blob_tree, &krl->revoked_sha1s) {
705 debug3("%s: hash len %u ", __func__, rb->len);
706 buffer_put_string(&sect, rb->blob, rb->len);
707 }
708 if (buffer_len(&sect) != 0) {
709 buffer_put_char(buf, KRL_SECTION_FINGERPRINT_SHA1);
710 buffer_put_string(buf, buffer_ptr(&sect),
711 buffer_len(&sect));
712 }
713
714 for (i = 0; i < nsign_keys; i++) {
715 if (key_to_blob(sign_keys[i], &kblob, &klen) == 0)
716 goto out;
717
718 debug3("%s: signature key len %u", __func__, klen);
719 buffer_put_char(buf, KRL_SECTION_SIGNATURE);
720 buffer_put_string(buf, kblob, klen);
721
722 if (key_sign(sign_keys[i], &sblob, &slen,
723 buffer_ptr(buf), buffer_len(buf)) == -1)
724 goto out;
725 debug3("%s: signature sig len %u", __func__, slen);
726 buffer_put_string(buf, sblob, slen);
727 }
728
729 r = 0;
730 out:
731 free(kblob);
732 free(sblob);
733 buffer_free(&sect);
734 return r;
735}
736
737static void
738format_timestamp(u_int64_t timestamp, char *ts, size_t nts)
739{
740 time_t t;
741 struct tm *tm;
742
743 t = timestamp;
744 tm = localtime(&t);
745 *ts = '\0';
746 strftime(ts, nts, "%Y%m%dT%H%M%S", tm);
747}
748
749static int
750parse_revoked_certs(Buffer *buf, struct ssh_krl *krl)
751{
752 int ret = -1, nbits;
753 u_char type, *blob;
754 u_int blen;
755 Buffer subsect;
756 u_int64_t serial, serial_lo, serial_hi;
757 BIGNUM *bitmap = NULL;
758 char *key_id = NULL;
759 Key *ca_key = NULL;
760
761 buffer_init(&subsect);
762
763 if ((blob = buffer_get_string_ptr_ret(buf, &blen)) == NULL ||
764 buffer_get_string_ptr_ret(buf, NULL) == NULL) { /* reserved */
765 error("%s: buffer error", __func__);
766 goto out;
767 }
768 if ((ca_key = key_from_blob(blob, blen)) == NULL)
769 goto out;
770
771 while (buffer_len(buf) > 0) {
772 if (buffer_get_char_ret(&type, buf) != 0 ||
773 (blob = buffer_get_string_ptr_ret(buf, &blen)) == NULL) {
774 error("%s: buffer error", __func__);
775 goto out;
776 }
777 buffer_clear(&subsect);
778 buffer_append(&subsect, blob, blen);
779 debug3("%s: subsection type 0x%02x", __func__, type);
780 /* buffer_dump(&subsect); */
781
782 switch (type) {
783 case KRL_SECTION_CERT_SERIAL_LIST:
784 while (buffer_len(&subsect) > 0) {
785 if (buffer_get_int64_ret(&serial,
786 &subsect) != 0) {
787 error("%s: buffer error", __func__);
788 goto out;
789 }
790 if (ssh_krl_revoke_cert_by_serial(krl, ca_key,
791 serial) != 0) {
792 error("%s: update failed", __func__);
793 goto out;
794 }
795 }
796 break;
797 case KRL_SECTION_CERT_SERIAL_RANGE:
798 if (buffer_get_int64_ret(&serial_lo, &subsect) != 0 ||
799 buffer_get_int64_ret(&serial_hi, &subsect) != 0) {
800 error("%s: buffer error", __func__);
801 goto out;
802 }
803 if (ssh_krl_revoke_cert_by_serial_range(krl, ca_key,
804 serial_lo, serial_hi) != 0) {
805 error("%s: update failed", __func__);
806 goto out;
807 }
808 break;
809 case KRL_SECTION_CERT_SERIAL_BITMAP:
810 if ((bitmap = BN_new()) == NULL) {
811 error("%s: BN_new", __func__);
812 goto out;
813 }
814 if (buffer_get_int64_ret(&serial_lo, &subsect) != 0 ||
815 buffer_get_bignum2_ret(&subsect, bitmap) != 0) {
816 error("%s: buffer error", __func__);
817 goto out;
818 }
819 if ((nbits = BN_num_bits(bitmap)) < 0) {
820 error("%s: bitmap bits < 0", __func__);
821 goto out;
822 }
823 for (serial = 0; serial < (u_int)nbits; serial++) {
824 if (serial > 0 && serial_lo + serial == 0) {
825 error("%s: bitmap wraps u64", __func__);
826 goto out;
827 }
828 if (!BN_is_bit_set(bitmap, serial))
829 continue;
830 if (ssh_krl_revoke_cert_by_serial(krl, ca_key,
831 serial_lo + serial) != 0) {
832 error("%s: update failed", __func__);
833 goto out;
834 }
835 }
836 BN_free(bitmap);
837 bitmap = NULL;
838 break;
839 case KRL_SECTION_CERT_KEY_ID:
840 while (buffer_len(&subsect) > 0) {
841 if ((key_id = buffer_get_cstring_ret(&subsect,
842 NULL)) == NULL) {
843 error("%s: buffer error", __func__);
844 goto out;
845 }
846 if (ssh_krl_revoke_cert_by_key_id(krl, ca_key,
847 key_id) != 0) {
848 error("%s: update failed", __func__);
849 goto out;
850 }
851 free(key_id);
852 key_id = NULL;
853 }
854 break;
855 default:
856 error("Unsupported KRL certificate section %u", type);
857 goto out;
858 }
859 if (buffer_len(&subsect) > 0) {
860 error("KRL certificate section contains unparsed data");
861 goto out;
862 }
863 }
864
865 ret = 0;
866 out:
867 if (ca_key != NULL)
868 key_free(ca_key);
869 if (bitmap != NULL)
870 BN_free(bitmap);
871 free(key_id);
872 buffer_free(&subsect);
873 return ret;
874}
875
876
877/* Attempt to parse a KRL, checking its signature (if any) with sign_ca_keys. */
878int
879ssh_krl_from_blob(Buffer *buf, struct ssh_krl **krlp,
880 const Key **sign_ca_keys, u_int nsign_ca_keys)
881{
882 Buffer copy, sect;
883 struct ssh_krl *krl;
884 char timestamp[64];
885 int ret = -1, r, sig_seen;
886 Key *key = NULL, **ca_used = NULL;
887 u_char type, *blob;
888 u_int i, j, sig_off, sects_off, blen, format_version, nca_used = 0;
889
890 *krlp = NULL;
891 if (buffer_len(buf) < sizeof(KRL_MAGIC) - 1 ||
892 memcmp(buffer_ptr(buf), KRL_MAGIC, sizeof(KRL_MAGIC) - 1) != 0) {
893 debug3("%s: not a KRL", __func__);
894 /*
895 * Return success but a NULL *krlp here to signal that the
896 * file might be a simple list of keys.
897 */
898 return 0;
899 }
900
901 /* Take a copy of the KRL buffer so we can verify its signature later */
902 buffer_init(&copy);
903 buffer_append(&copy, buffer_ptr(buf), buffer_len(buf));
904
905 buffer_init(&sect);
906 buffer_consume(&copy, sizeof(KRL_MAGIC) - 1);
907
908 if ((krl = ssh_krl_init()) == NULL) {
909 error("%s: alloc failed", __func__);
910 goto out;
911 }
912
913 if (buffer_get_int_ret(&format_version, &copy) != 0) {
914 error("%s: KRL truncated", __func__);
915 goto out;
916 }
917 if (format_version != KRL_FORMAT_VERSION) {
918 error("%s: KRL unsupported format version %u",
919 __func__, format_version);
920 goto out;
921 }
922 if (buffer_get_int64_ret(&krl->krl_version, &copy) != 0 ||
923 buffer_get_int64_ret(&krl->generated_date, &copy) != 0 ||
924 buffer_get_int64_ret(&krl->flags, &copy) != 0 ||
925 buffer_get_string_ptr_ret(&copy, NULL) == NULL || /* reserved */
926 (krl->comment = buffer_get_cstring_ret(&copy, NULL)) == NULL) {
927 error("%s: buffer error", __func__);
928 goto out;
929 }
930
931 format_timestamp(krl->generated_date, timestamp, sizeof(timestamp));
932 debug("KRL version %llu generated at %s%s%s", krl->krl_version,
933 timestamp, *krl->comment ? ": " : "", krl->comment);
934
935 /*
936 * 1st pass: verify signatures, if any. This is done to avoid
937 * detailed parsing of data whose provenance is unverified.
938 */
939 sig_seen = 0;
940 sects_off = buffer_len(buf) - buffer_len(&copy);
941 while (buffer_len(&copy) > 0) {
942 if (buffer_get_char_ret(&type, &copy) != 0 ||
943 (blob = buffer_get_string_ptr_ret(&copy, &blen)) == NULL) {
944 error("%s: buffer error", __func__);
945 goto out;
946 }
947 debug3("%s: first pass, section 0x%02x", __func__, type);
948 if (type != KRL_SECTION_SIGNATURE) {
949 if (sig_seen) {
950 error("KRL contains non-signature section "
951 "after signature");
952 goto out;
953 }
954 /* Not interested for now. */
955 continue;
956 }
957 sig_seen = 1;
958 /* First string component is the signing key */
959 if ((key = key_from_blob(blob, blen)) == NULL) {
960 error("%s: invalid signature key", __func__);
961 goto out;
962 }
963 sig_off = buffer_len(buf) - buffer_len(&copy);
964 /* Second string component is the signature itself */
965 if ((blob = buffer_get_string_ptr_ret(&copy, &blen)) == NULL) {
966 error("%s: buffer error", __func__);
967 goto out;
968 }
969 /* Check signature over entire KRL up to this point */
970 if (key_verify(key, blob, blen,
971 buffer_ptr(buf), buffer_len(buf) - sig_off) == -1) {
972 error("bad signaure on KRL");
973 goto out;
974 }
975 /* Check if this key has already signed this KRL */
976 for (i = 0; i < nca_used; i++) {
977 if (key_equal(ca_used[i], key)) {
978 error("KRL signed more than once with "
979 "the same key");
980 goto out;
981 }
982 }
983 /* Record keys used to sign the KRL */
984 xrealloc(ca_used, nca_used + 1, sizeof(*ca_used));
985 ca_used[nca_used++] = key;
986 key = NULL;
987 break;
988 }
989
990 /*
991 * 2nd pass: parse and load the KRL, skipping the header to the point
992 * where the section start.
993 */
994 buffer_append(&copy, (u_char*)buffer_ptr(buf) + sects_off,
995 buffer_len(buf) - sects_off);
996 while (buffer_len(&copy) > 0) {
997 if (buffer_get_char_ret(&type, &copy) != 0 ||
998 (blob = buffer_get_string_ptr_ret(&copy, &blen)) == NULL) {
999 error("%s: buffer error", __func__);
1000 goto out;
1001 }
1002 debug3("%s: second pass, section 0x%02x", __func__, type);
1003 buffer_clear(&sect);
1004 buffer_append(&sect, blob, blen);
1005
1006 switch (type) {
1007 case KRL_SECTION_CERTIFICATES:
1008 if ((r = parse_revoked_certs(&sect, krl)) != 0)
1009 goto out;
1010 break;
1011 case KRL_SECTION_EXPLICIT_KEY:
1012 case KRL_SECTION_FINGERPRINT_SHA1:
1013 while (buffer_len(&sect) > 0) {
1014 if ((blob = buffer_get_string_ret(&sect,
1015 &blen)) == NULL) {
1016 error("%s: buffer error", __func__);
1017 goto out;
1018 }
1019 if (type == KRL_SECTION_FINGERPRINT_SHA1 &&
1020 blen != 20) {
1021 error("%s: bad SHA1 length", __func__);
1022 goto out;
1023 }
1024 if (revoke_blob(
1025 type == KRL_SECTION_EXPLICIT_KEY ?
1026 &krl->revoked_keys : &krl->revoked_sha1s,
1027 blob, blen) != 0)
1028 goto out; /* revoke_blob frees blob */
1029 }
1030 break;
1031 case KRL_SECTION_SIGNATURE:
1032 /* Handled above, but still need to stay in synch */
1033 buffer_clear(&sect);
1034 if ((blob = buffer_get_string_ptr_ret(&sect,
1035 &blen)) == NULL) {
1036 error("%s: buffer error", __func__);
1037 goto out;
1038 }
1039 break;
1040 default:
1041 error("Unsupported KRL section %u", type);
1042 goto out;
1043 }
1044 if (buffer_len(&sect) > 0) {
1045 error("KRL section contains unparsed data");
1046 goto out;
1047 }
1048 }
1049
1050 /* Check that the key(s) used to sign the KRL weren't revoked */
1051 sig_seen = 0;
1052 for (i = 0; i < nca_used; i++) {
1053 if (ssh_krl_check_key(krl, ca_used[i]) == 0)
1054 sig_seen = 1;
1055 else {
1056 key_free(ca_used[i]);
1057 ca_used[i] = NULL;
1058 }
1059 }
1060 if (nca_used && !sig_seen) {
1061 error("All keys used to sign KRL were revoked");
1062 goto out;
1063 }
1064
1065 /* If we have CA keys, then verify that one was used to sign the KRL */
1066 if (sig_seen && nsign_ca_keys != 0) {
1067 sig_seen = 0;
1068 for (i = 0; !sig_seen && i < nsign_ca_keys; i++) {
1069 for (j = 0; j < nca_used; j++) {
1070 if (ca_used[j] == NULL)
1071 continue;
1072 if (key_equal(ca_used[j], sign_ca_keys[i])) {
1073 sig_seen = 1;
1074 break;
1075 }
1076 }
1077 }
1078 if (!sig_seen) {
1079 error("KRL not signed with any trusted key");
1080 goto out;
1081 }
1082 }
1083
1084 *krlp = krl;
1085 ret = 0;
1086 out:
1087 if (ret != 0)
1088 ssh_krl_free(krl);
1089 for (i = 0; i < nca_used; i++) {
1090 if (ca_used[i] != NULL)
1091 key_free(ca_used[i]);
1092 }
1093 free(ca_used);
1094 if (key != NULL)
1095 key_free(key);
1096 buffer_free(&copy);
1097 buffer_free(&sect);
1098 return ret;
1099}
1100
1101/* Checks whether a given key/cert is revoked. Does not check its CA */
1102static int
1103is_key_revoked(struct ssh_krl *krl, const Key *key)
1104{
1105 struct revoked_blob rb, *erb;
1106 struct revoked_serial rs, *ers;
1107 struct revoked_key_id rki, *erki;
1108 struct revoked_certs *rc;
1109
1110 /* Check explicitly revoked hashes first */
1111 bzero(&rb, sizeof(rb));
1112 if ((rb.blob = key_fingerprint_raw(key, SSH_FP_SHA1, &rb.len)) == NULL)
1113 return -1;
1114 erb = RB_FIND(revoked_blob_tree, &krl->revoked_sha1s, &rb);
1115 free(rb.blob);
1116 if (erb != NULL) {
1117 debug("%s: revoked by key SHA1", __func__);
1118 return -1;
1119 }
1120
1121 /* Next, explicit keys */
1122 bzero(&rb, sizeof(rb));
1123 if (plain_key_blob(key, &rb.blob, &rb.len) != 0)
1124 return -1;
1125 erb = RB_FIND(revoked_blob_tree, &krl->revoked_keys, &rb);
1126 free(rb.blob);
1127 if (erb != NULL) {
1128 debug("%s: revoked by explicit key", __func__);
1129 return -1;
1130 }
1131
1132 if (!key_is_cert(key))
1133 return 0;
1134
1135 /* Check cert revocation */
1136 if (revoked_certs_for_ca_key(krl, key->cert->signature_key,
1137 &rc, 0) != 0)
1138 return -1;
1139 if (rc == NULL)
1140 return 0; /* No entry for this CA */
1141
1142 /* Check revocation by cert key ID */
1143 bzero(&rki, sizeof(rki));
1144 rki.key_id = key->cert->key_id;
1145 erki = RB_FIND(revoked_key_id_tree, &rc->revoked_key_ids, &rki);
1146 if (erki != NULL) {
1147 debug("%s: revoked by key ID", __func__);
1148 return -1;
1149 }
1150
1151 /* Legacy cert formats lack serial numbers */
1152 if (key_cert_is_legacy(key))
1153 return 0;
1154
1155 bzero(&rs, sizeof(rs));
1156 rs.lo = rs.hi = key->cert->serial;
1157 ers = RB_FIND(revoked_serial_tree, &rc->revoked_serials, &rs);
1158 if (ers != NULL) {
1159 KRL_DBG(("%s: %llu matched %llu:%llu", __func__,
1160 key->cert->serial, ers->lo, ers->hi));
1161 debug("%s: revoked by serial", __func__);
1162 return -1;
1163 }
1164 KRL_DBG(("%s: %llu no match", __func__, key->cert->serial));
1165
1166 return 0;
1167}
1168
1169int
1170ssh_krl_check_key(struct ssh_krl *krl, const Key *key)
1171{
1172 int r;
1173
1174 debug2("%s: checking key", __func__);
1175 if ((r = is_key_revoked(krl, key)) != 0)
1176 return r;
1177 if (key_is_cert(key)) {
1178 debug2("%s: checking CA key", __func__);
1179 if ((r = is_key_revoked(krl, key->cert->signature_key)) != 0)
1180 return r;
1181 }
1182 debug3("%s: key okay", __func__);
1183 return 0;
1184}
1185
1186/* Returns 0 on success, -1 on error or key revoked, -2 if path is not a KRL */
1187int
1188ssh_krl_file_contains_key(const char *path, const Key *key)
1189{
1190 Buffer krlbuf;
1191 struct ssh_krl *krl;
1192 int revoked, fd;
1193
1194 if (path == NULL)
1195 return 0;
1196
1197 if ((fd = open(path, O_RDONLY)) == -1) {
1198 error("open %s: %s", path, strerror(errno));
1199 error("Revoked keys file not accessible - refusing public key "
1200 "authentication");
1201 return -1;
1202 }
1203 buffer_init(&krlbuf);
1204 if (!key_load_file(fd, path, &krlbuf)) {
1205 close(fd);
1206 buffer_free(&krlbuf);
1207 error("Revoked keys file not readable - refusing public key "
1208 "authentication");
1209 return -1;
1210 }
1211 close(fd);
1212 if (ssh_krl_from_blob(&krlbuf, &krl, NULL, 0) != 0) {
1213 buffer_free(&krlbuf);
1214 error("Invalid KRL, refusing public key "
1215 "authentication");
1216 return -1;
1217 }
1218 buffer_free(&krlbuf);
1219 if (krl == NULL) {
1220 debug3("%s: %s is not a KRL file", __func__, path);
1221 return -2;
1222 }
1223 debug2("%s: checking KRL %s", __func__, path);
1224 revoked = ssh_krl_check_key(krl, key) != 0;
1225 ssh_krl_free(krl);
1226 return revoked ? -1 : 0;
1227}
diff --git a/krl.h b/krl.h
new file mode 100644
index 000000000..2c43f5bb2
--- /dev/null
+++ b/krl.h
@@ -0,0 +1,63 @@
1/*
2 * Copyright (c) 2012 Damien Miller <djm@mindrot.org>
3 *
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
17/* $OpenBSD: krl.h,v 1.2 2013/01/18 00:24:58 djm Exp $ */
18
19#ifndef _KRL_H
20#define _KRL_H
21
22/* Functions to manage key revocation lists */
23
24#define KRL_MAGIC "SSHKRL\n\0"
25#define KRL_FORMAT_VERSION 1
26
27/* KRL section types */
28#define KRL_SECTION_CERTIFICATES 1
29#define KRL_SECTION_EXPLICIT_KEY 2
30#define KRL_SECTION_FINGERPRINT_SHA1 3
31#define KRL_SECTION_SIGNATURE 4
32
33/* KRL_SECTION_CERTIFICATES subsection types */
34#define KRL_SECTION_CERT_SERIAL_LIST 0x20
35#define KRL_SECTION_CERT_SERIAL_RANGE 0x21
36#define KRL_SECTION_CERT_SERIAL_BITMAP 0x22
37#define KRL_SECTION_CERT_KEY_ID 0x23
38
39struct ssh_krl;
40
41struct ssh_krl *ssh_krl_init(void);
42void ssh_krl_free(struct ssh_krl *krl);
43void ssh_krl_set_version(struct ssh_krl *krl, u_int64_t version);
44void ssh_krl_set_sign_key(struct ssh_krl *krl, const Key *sign_key);
45void ssh_krl_set_comment(struct ssh_krl *krl, const char *comment);
46int ssh_krl_revoke_cert_by_serial(struct ssh_krl *krl, const Key *ca_key,
47 u_int64_t serial);
48int ssh_krl_revoke_cert_by_serial_range(struct ssh_krl *krl, const Key *ca_key,
49 u_int64_t lo, u_int64_t hi);
50int ssh_krl_revoke_cert_by_key_id(struct ssh_krl *krl, const Key *ca_key,
51 const char *key_id);
52int ssh_krl_revoke_key_explicit(struct ssh_krl *krl, const Key *key);
53int ssh_krl_revoke_key_sha1(struct ssh_krl *krl, const Key *key);
54int ssh_krl_revoke_key(struct ssh_krl *krl, const Key *key);
55int ssh_krl_to_blob(struct ssh_krl *krl, Buffer *buf, const Key **sign_keys,
56 u_int nsign_keys);
57int ssh_krl_from_blob(Buffer *buf, struct ssh_krl **krlp,
58 const Key **sign_ca_keys, u_int nsign_ca_keys);
59int ssh_krl_check_key(struct ssh_krl *krl, const Key *key);
60int ssh_krl_file_contains_key(const char *path, const Key *key);
61
62#endif /* _KRL_H */
63
diff --git a/ssh-keygen.1 b/ssh-keygen.1
index 1d5564640..52f4b6ea6 100644
--- a/ssh-keygen.1
+++ b/ssh-keygen.1
@@ -1,4 +1,4 @@
1.\" $OpenBSD: ssh-keygen.1,v 1.110 2012/08/15 18:25:50 jmc Exp $ 1.\" $OpenBSD: ssh-keygen.1,v 1.111 2013/01/17 23:00:01 djm Exp $
2.\" 2.\"
3.\" Author: Tatu Ylonen <ylo@cs.hut.fi> 3.\" Author: Tatu Ylonen <ylo@cs.hut.fi>
4.\" Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland 4.\" Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -35,7 +35,7 @@
35.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 35.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
36.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 36.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
37.\" 37.\"
38.Dd $Mdocdate: August 15 2012 $ 38.Dd $Mdocdate: January 17 2013 $
39.Dt SSH-KEYGEN 1 39.Dt SSH-KEYGEN 1
40.Os 40.Os
41.Sh NAME 41.Sh NAME
@@ -122,6 +122,17 @@
122.Op Fl f Ar input_keyfile 122.Op Fl f Ar input_keyfile
123.Nm ssh-keygen 123.Nm ssh-keygen
124.Fl A 124.Fl A
125.Nm ssh-keygen
126.Fl k
127.Fl f Ar krl_file
128.Op Fl u
129.Op Fl s ca_public
130.Op Fl z version_number
131.Ar
132.Nm ssh-keygen
133.Fl Q
134.Fl f Ar krl_file
135.Ar
125.Ek 136.Ek
126.Sh DESCRIPTION 137.Sh DESCRIPTION
127.Nm 138.Nm
@@ -144,6 +155,13 @@ See the
144.Sx MODULI GENERATION 155.Sx MODULI GENERATION
145section for details. 156section for details.
146.Pp 157.Pp
158Finally,
159.Nm
160can be used to generate and update Key Revocation Lists, and to test whether
161given keys have been revoked by one. See the
162.Sx KEY REVOCATION LISTS
163section for details.
164.Pp
147Normally each user wishing to use SSH 165Normally each user wishing to use SSH
148with public key authentication runs this once to create the authentication 166with public key authentication runs this once to create the authentication
149key in 167key in
@@ -321,6 +339,17 @@ This option allows importing keys from other software, including several
321commercial SSH implementations. 339commercial SSH implementations.
322The default import format is 340The default import format is
323.Dq RFC4716 . 341.Dq RFC4716 .
342.It Fl k
343Generate a KRL file.
344In this mode,
345.Nm
346will generate a KRL file at the location specified via the
347.Fl f
348flag that revokes every key or certificate presented on the command-line.
349Keys/certificates to be revoked may be specified by public key file or
350using the format described in the
351.Sx KEY REVOCATION LISTS
352section.
324.It Fl L 353.It Fl L
325Prints the contents of a certificate. 354Prints the contents of a certificate.
326.It Fl l 355.It Fl l
@@ -448,6 +477,14 @@ Certify (sign) a public key using the specified CA key.
448Please see the 477Please see the
449.Sx CERTIFICATES 478.Sx CERTIFICATES
450section for details. 479section for details.
480.Pp
481When generating a KRL,
482.Fl s
483specifies a path to a CA public key file used to revoke certificated directly
484by key ID or serial number.
485See the
486.Sx KEY REVOCATION LISTS
487section for details.
451.It Fl T Ar output_file 488.It Fl T Ar output_file
452Test DH group exchange candidate primes (generated using the 489Test DH group exchange candidate primes (generated using the
453.Fl G 490.Fl G
@@ -485,6 +522,12 @@ For example:
485(valid from 12:30 PM, January 1st, 2010 to 12:30 PM, January 1st, 2011), 522(valid from 12:30 PM, January 1st, 2010 to 12:30 PM, January 1st, 2011),
486.Dq -1d:20110101 523.Dq -1d:20110101
487(valid from yesterday to midnight, January 1st, 2011). 524(valid from yesterday to midnight, January 1st, 2011).
525.It Fl u
526Update a KRL.
527When specified with
528.Fl k ,
529keys listed via the command-line are added to the existing KRL rather than
530a new KRL being created.
488.It Fl v 531.It Fl v
489Verbose mode. 532Verbose mode.
490Causes 533Causes
@@ -504,6 +547,10 @@ OpenSSH format file and print an OpenSSH public key to stdout.
504Specifies a serial number to be embedded in the certificate to distinguish 547Specifies a serial number to be embedded in the certificate to distinguish
505this certificate from others from the same CA. 548this certificate from others from the same CA.
506The default serial number is zero. 549The default serial number is zero.
550.Pp
551When generating a KRL, the
552.Fl z
553flag is used to specify a KRL version number.
507.El 554.El
508.Sh MODULI GENERATION 555.Sh MODULI GENERATION
509.Nm 556.Nm
@@ -638,6 +685,73 @@ public key must be trusted by
638or 685or
639.Xr ssh 1 . 686.Xr ssh 1 .
640Please refer to those manual pages for details. 687Please refer to those manual pages for details.
688.Sh KEY REVOCATION LISTS
689.Nm
690is able to manage OpenSSH format Key Revocation Lists (KRLs).
691These binary files specify keys or certificates to be revoked using a
692compact format; taking as little a one bit per certificate if they are being
693revoked by serial number.
694.Pp
695KRLs may be generated using the
696.Fl k
697flag.
698This option reads one or more files from the command-line and generates a new
699KRL.
700The files may either contain a KRL specification (see below) or public keys,
701listed one per line.
702Plain public keys are revoked by listing their hash or contents in the KRL and
703certificates revoked by serial number or key ID (if the serial is zero or
704not available).
705.Pp
706Revoking keys using a KRL specification offers explicit control over the
707types of record used to revoke keys and may be used to directly revoke
708certificates by serial number or key ID without having the complete original
709certificate on hand.
710A KRL specification consists of lines containing one of the following directives
711followed by a colon and some directive-specific information.
712.Bl -tag -width Ds
713.It Cm serial : Ar serial_number Op -serial_number
714Revokes a certificate with the specified serial number.
715Serial numbers are 64 bit values, not including zero and may be expressed
716in decimal, hex or octal.
717If two serial numbers are specified separated by a hyphen, then the range
718of serial numbers including and between each is revoked.
719The CA key must have been specified on the
720.Nm
721command-line using the
722.Fl s
723option.
724.It Cm id : Ar key_id
725Revokes a certificate with the specified key ID string.
726The CA key must have been specified on the
727.Nm
728command-line using the
729.Fl s
730option.
731.It Cm key : Ar public_key
732Revokes the specified key.
733In a certificate is listed, then it is revoked as a plain public key.
734.It Cm sha1 : Ar public_key
735Revokes the specified key by its SHA1 hash.
736.El
737.Pp
738KRLs may be updated using the
739.Fl u
740flag in addition to
741.Fl k .
742When this option is specified, keys listed via the command-line are merged into
743the KRL, adding to those already there.
744.Pp
745It is also possible, given a KRL, to test whether it revokes a particular key
746(or keys).
747The
748.Fl Q
749flag will query an existing KRL, testing each key specified on the commandline.
750If any key listed on the command-line has been revoked (or an error encountered)
751then
752.Nm
753will exit with a non-zero exit status.
754A zero exit status will only be returned if no key was revoked.
641.Sh FILES 755.Sh FILES
642.Bl -tag -width Ds -compact 756.Bl -tag -width Ds -compact
643.It Pa ~/.ssh/identity 757.It Pa ~/.ssh/identity
diff --git a/ssh-keygen.c b/ssh-keygen.c
index a19a2b085..861b04e2d 100644
--- a/ssh-keygen.c
+++ b/ssh-keygen.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: ssh-keygen.c,v 1.222 2013/01/09 05:40:17 djm Exp $ */ 1/* $OpenBSD: ssh-keygen.c,v 1.223 2013/01/17 23:00:01 djm Exp $ */
2/* 2/*
3 * Author: Tatu Ylonen <ylo@cs.hut.fi> 3 * Author: Tatu Ylonen <ylo@cs.hut.fi>
4 * Copyright (c) 1994 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland 4 * Copyright (c) 1994 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -48,8 +48,11 @@
48#include "match.h" 48#include "match.h"
49#include "hostfile.h" 49#include "hostfile.h"
50#include "dns.h" 50#include "dns.h"
51#include "ssh.h"
51#include "ssh2.h" 52#include "ssh2.h"
52#include "ssh-pkcs11.h" 53#include "ssh-pkcs11.h"
54#include "atomicio.h"
55#include "krl.h"
53 56
54/* Number of bits in the RSA/DSA key. This value can be set on the command line. */ 57/* Number of bits in the RSA/DSA key. This value can be set on the command line. */
55#define DEFAULT_BITS 2048 58#define DEFAULT_BITS 2048
@@ -1897,6 +1900,226 @@ do_show_cert(struct passwd *pw)
1897} 1900}
1898 1901
1899static void 1902static void
1903load_krl(const char *path, struct ssh_krl **krlp)
1904{
1905 Buffer krlbuf;
1906 int fd;
1907
1908 buffer_init(&krlbuf);
1909 if ((fd = open(path, O_RDONLY)) == -1)
1910 fatal("open %s: %s", path, strerror(errno));
1911 if (!key_load_file(fd, path, &krlbuf))
1912 fatal("Unable to load KRL");
1913 close(fd);
1914 /* XXX check sigs */
1915 if (ssh_krl_from_blob(&krlbuf, krlp, NULL, 0) != 0 ||
1916 *krlp == NULL)
1917 fatal("Invalid KRL file");
1918 buffer_free(&krlbuf);
1919}
1920
1921static void
1922update_krl_from_file(struct passwd *pw, const char *file, const Key *ca,
1923 struct ssh_krl *krl)
1924{
1925 Key *key = NULL;
1926 u_long lnum = 0;
1927 char *path, *cp, *ep, line[SSH_MAX_PUBKEY_BYTES];
1928 unsigned long long serial, serial2;
1929 int i, was_explicit_key, was_sha1, r;
1930 FILE *krl_spec;
1931
1932 path = tilde_expand_filename(file, pw->pw_uid);
1933 if (strcmp(path, "-") == 0) {
1934 krl_spec = stdin;
1935 free(path);
1936 path = xstrdup("(standard input)");
1937 } else if ((krl_spec = fopen(path, "r")) == NULL)
1938 fatal("fopen %s: %s", path, strerror(errno));
1939
1940 if (!quiet)
1941 printf("Revoking from %s\n", path);
1942 while (read_keyfile_line(krl_spec, path, line, sizeof(line),
1943 &lnum) == 0) {
1944 was_explicit_key = was_sha1 = 0;
1945 cp = line + strspn(line, " \t");
1946 /* Trim trailing space, comments and strip \n */
1947 for (i = 0, r = -1; cp[i] != '\0'; i++) {
1948 if (cp[i] == '#' || cp[i] == '\n') {
1949 cp[i] = '\0';
1950 break;
1951 }
1952 if (cp[i] == ' ' || cp[i] == '\t') {
1953 /* Remember the start of a span of whitespace */
1954 if (r == -1)
1955 r = i;
1956 } else
1957 r = -1;
1958 }
1959 if (r != -1)
1960 cp[r] = '\0';
1961 if (*cp == '\0')
1962 continue;
1963 if (strncasecmp(cp, "serial:", 7) == 0) {
1964 if (ca == NULL) {
1965 fatal("revoking certificated by serial number "
1966 "requires specification of a CA key");
1967 }
1968 cp += 7;
1969 cp = cp + strspn(cp, " \t");
1970 errno = 0;
1971 serial = strtoull(cp, &ep, 0);
1972 if (*cp == '\0' || (*ep != '\0' && *ep != '-'))
1973 fatal("%s:%lu: invalid serial \"%s\"",
1974 path, lnum, cp);
1975 if (errno == ERANGE && serial == ULLONG_MAX)
1976 fatal("%s:%lu: serial out of range",
1977 path, lnum);
1978 serial2 = serial;
1979 if (*ep == '-') {
1980 cp = ep + 1;
1981 errno = 0;
1982 serial2 = strtoull(cp, &ep, 0);
1983 if (*cp == '\0' || *ep != '\0')
1984 fatal("%s:%lu: invalid serial \"%s\"",
1985 path, lnum, cp);
1986 if (errno == ERANGE && serial2 == ULLONG_MAX)
1987 fatal("%s:%lu: serial out of range",
1988 path, lnum);
1989 if (serial2 <= serial)
1990 fatal("%s:%lu: invalid serial range "
1991 "%llu:%llu", path, lnum,
1992 (unsigned long long)serial,
1993 (unsigned long long)serial2);
1994 }
1995 if (ssh_krl_revoke_cert_by_serial_range(krl,
1996 ca, serial, serial2) != 0) {
1997 fatal("%s: revoke serial failed",
1998 __func__);
1999 }
2000 } else if (strncasecmp(cp, "id:", 3) == 0) {
2001 if (ca == NULL) {
2002 fatal("revoking certificated by key ID "
2003 "requires specification of a CA key");
2004 }
2005 cp += 3;
2006 cp = cp + strspn(cp, " \t");
2007 if (ssh_krl_revoke_cert_by_key_id(krl, ca, cp) != 0)
2008 fatal("%s: revoke key ID failed", __func__);
2009 } else {
2010 if (strncasecmp(cp, "key:", 4) == 0) {
2011 cp += 4;
2012 cp = cp + strspn(cp, " \t");
2013 was_explicit_key = 1;
2014 } else if (strncasecmp(cp, "sha1:", 5) == 0) {
2015 cp += 5;
2016 cp = cp + strspn(cp, " \t");
2017 was_sha1 = 1;
2018 } else {
2019 /*
2020 * Just try to process the line as a key.
2021 * Parsing will fail if it isn't.
2022 */
2023 }
2024 if ((key = key_new(KEY_UNSPEC)) == NULL)
2025 fatal("key_new");
2026 if (key_read(key, &cp) != 1)
2027 fatal("%s:%lu: invalid key", path, lnum);
2028 if (was_explicit_key)
2029 r = ssh_krl_revoke_key_explicit(krl, key);
2030 else if (was_sha1)
2031 r = ssh_krl_revoke_key_sha1(krl, key);
2032 else
2033 r = ssh_krl_revoke_key(krl, key);
2034 if (r != 0)
2035 fatal("%s: revoke key failed", __func__);
2036 key_free(key);
2037 }
2038 }
2039 if (strcmp(path, "-") != 0)
2040 fclose(krl_spec);
2041}
2042
2043static void
2044do_gen_krl(struct passwd *pw, int updating, int argc, char **argv)
2045{
2046 struct ssh_krl *krl;
2047 struct stat sb;
2048 Key *ca = NULL;
2049 int fd, i;
2050 char *tmp;
2051 Buffer kbuf;
2052
2053 if (*identity_file == '\0')
2054 fatal("KRL generation requires an output file");
2055 if (stat(identity_file, &sb) == -1) {
2056 if (errno != ENOENT)
2057 fatal("Cannot access KRL \"%s\": %s",
2058 identity_file, strerror(errno));
2059 if (updating)
2060 fatal("KRL \"%s\" does not exist", identity_file);
2061 }
2062 if (ca_key_path != NULL) {
2063 tmp = tilde_expand_filename(ca_key_path, pw->pw_uid);
2064 if ((ca = key_load_public(tmp, NULL)) == NULL)
2065 fatal("Cannot load CA public key %s", tmp);
2066 xfree(tmp);
2067 }
2068
2069 if (updating)
2070 load_krl(identity_file, &krl);
2071 else if ((krl = ssh_krl_init()) == NULL)
2072 fatal("couldn't create KRL");
2073
2074 if (cert_serial != 0)
2075 ssh_krl_set_version(krl, cert_serial);
2076 if (identity_comment != NULL)
2077 ssh_krl_set_comment(krl, identity_comment);
2078
2079 for (i = 0; i < argc; i++)
2080 update_krl_from_file(pw, argv[i], ca, krl);
2081
2082 buffer_init(&kbuf);
2083 if (ssh_krl_to_blob(krl, &kbuf, NULL, 0) != 0)
2084 fatal("Couldn't generate KRL");
2085 if ((fd = open(identity_file, O_WRONLY|O_CREAT|O_TRUNC, 0644)) == -1)
2086 fatal("open %s: %s", identity_file, strerror(errno));
2087 if (atomicio(vwrite, fd, buffer_ptr(&kbuf), buffer_len(&kbuf)) !=
2088 buffer_len(&kbuf))
2089 fatal("write %s: %s", identity_file, strerror(errno));
2090 close(fd);
2091 buffer_free(&kbuf);
2092 ssh_krl_free(krl);
2093}
2094
2095static void
2096do_check_krl(struct passwd *pw, int argc, char **argv)
2097{
2098 int i, r, ret = 0;
2099 char *comment;
2100 struct ssh_krl *krl;
2101 Key *k;
2102
2103 if (*identity_file == '\0')
2104 fatal("KRL checking requires an input file");
2105 load_krl(identity_file, &krl);
2106 for (i = 0; i < argc; i++) {
2107 if ((k = key_load_public(argv[i], &comment)) == NULL)
2108 fatal("Cannot load public key %s", argv[i]);
2109 r = ssh_krl_check_key(krl, k);
2110 printf("%s%s%s%s: %s\n", argv[i],
2111 *comment ? " (" : "", comment, *comment ? ")" : "",
2112 r == 0 ? "ok" : "REVOKED");
2113 if (r != 0)
2114 ret = 1;
2115 key_free(k);
2116 free(comment);
2117 }
2118 ssh_krl_free(krl);
2119 exit(ret);
2120}
2121
2122static void
1900usage(void) 2123usage(void)
1901{ 2124{
1902 fprintf(stderr, "usage: %s [options]\n", __progname); 2125 fprintf(stderr, "usage: %s [options]\n", __progname);
@@ -1922,6 +2145,7 @@ usage(void)
1922 fprintf(stderr, " -J number Screen this number of moduli lines.\n"); 2145 fprintf(stderr, " -J number Screen this number of moduli lines.\n");
1923 fprintf(stderr, " -j number Start screening moduli at specified line.\n"); 2146 fprintf(stderr, " -j number Start screening moduli at specified line.\n");
1924 fprintf(stderr, " -K checkpt Write checkpoints to this file.\n"); 2147 fprintf(stderr, " -K checkpt Write checkpoints to this file.\n");
2148 fprintf(stderr, " -k Generate a KRL file.\n");
1925 fprintf(stderr, " -L Print the contents of a certificate.\n"); 2149 fprintf(stderr, " -L Print the contents of a certificate.\n");
1926 fprintf(stderr, " -l Show fingerprint of key file.\n"); 2150 fprintf(stderr, " -l Show fingerprint of key file.\n");
1927 fprintf(stderr, " -M memory Amount of memory (MB) to use for generating DH-GEX moduli.\n"); 2151 fprintf(stderr, " -M memory Amount of memory (MB) to use for generating DH-GEX moduli.\n");
@@ -1931,6 +2155,7 @@ usage(void)
1931 fprintf(stderr, " -O option Specify a certificate option.\n"); 2155 fprintf(stderr, " -O option Specify a certificate option.\n");
1932 fprintf(stderr, " -P phrase Provide old passphrase.\n"); 2156 fprintf(stderr, " -P phrase Provide old passphrase.\n");
1933 fprintf(stderr, " -p Change passphrase of private key file.\n"); 2157 fprintf(stderr, " -p Change passphrase of private key file.\n");
2158 fprintf(stderr, " -Q Test whether key(s) are revoked in KRL.\n");
1934 fprintf(stderr, " -q Quiet.\n"); 2159 fprintf(stderr, " -q Quiet.\n");
1935 fprintf(stderr, " -R hostname Remove host from known_hosts file.\n"); 2160 fprintf(stderr, " -R hostname Remove host from known_hosts file.\n");
1936 fprintf(stderr, " -r hostname Print DNS resource record.\n"); 2161 fprintf(stderr, " -r hostname Print DNS resource record.\n");
@@ -1939,6 +2164,7 @@ usage(void)
1939 fprintf(stderr, " -T file Screen candidates for DH-GEX moduli.\n"); 2164 fprintf(stderr, " -T file Screen candidates for DH-GEX moduli.\n");
1940 fprintf(stderr, " -t type Specify type of key to create.\n"); 2165 fprintf(stderr, " -t type Specify type of key to create.\n");
1941 fprintf(stderr, " -V from:to Specify certificate validity interval.\n"); 2166 fprintf(stderr, " -V from:to Specify certificate validity interval.\n");
2167 fprintf(stderr, " -u Update KRL rather than creating a new one.\n");
1942 fprintf(stderr, " -v Verbose.\n"); 2168 fprintf(stderr, " -v Verbose.\n");
1943 fprintf(stderr, " -W gen Generator to use for generating DH-GEX moduli.\n"); 2169 fprintf(stderr, " -W gen Generator to use for generating DH-GEX moduli.\n");
1944 fprintf(stderr, " -y Read private key file and print public key.\n"); 2170 fprintf(stderr, " -y Read private key file and print public key.\n");
@@ -1955,14 +2181,14 @@ main(int argc, char **argv)
1955{ 2181{
1956 char dotsshdir[MAXPATHLEN], comment[1024], *passphrase1, *passphrase2; 2182 char dotsshdir[MAXPATHLEN], comment[1024], *passphrase1, *passphrase2;
1957 char *checkpoint = NULL; 2183 char *checkpoint = NULL;
1958 char out_file[MAXPATHLEN], *rr_hostname = NULL, *ep; 2184 char out_file[MAXPATHLEN], *ep, *rr_hostname = NULL;
1959 Key *private, *public; 2185 Key *private, *public;
1960 struct passwd *pw; 2186 struct passwd *pw;
1961 struct stat st; 2187 struct stat st;
1962 int opt, type, fd; 2188 int opt, type, fd;
1963 u_int32_t memory = 0, generator_wanted = 0, trials = 100; 2189 u_int32_t memory = 0, generator_wanted = 0, trials = 100;
1964 int do_gen_candidates = 0, do_screen_candidates = 0; 2190 int do_gen_candidates = 0, do_screen_candidates = 0;
1965 int gen_all_hostkeys = 0; 2191 int gen_all_hostkeys = 0, gen_krl = 0, update_krl = 0, check_krl = 0;
1966 unsigned long start_lineno = 0, lines_to_process = 0; 2192 unsigned long start_lineno = 0, lines_to_process = 0;
1967 BIGNUM *start = NULL; 2193 BIGNUM *start = NULL;
1968 FILE *f; 2194 FILE *f;
@@ -1992,8 +2218,8 @@ main(int argc, char **argv)
1992 exit(1); 2218 exit(1);
1993 } 2219 }
1994 2220
1995 while ((opt = getopt(argc, argv, "AegiqpclBHLhvxXyF:b:f:t:D:I:J:j:K:P:" 2221 while ((opt = getopt(argc, argv, "ABHLQXceghiklpquvxy"
1996 "m:N:n:O:C:r:g:R:T:G:M:S:s:a:V:W:z:")) != -1) { 2222 "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) {
1997 switch (opt) { 2223 switch (opt) {
1998 case 'A': 2224 case 'A':
1999 gen_all_hostkeys = 1; 2225 gen_all_hostkeys = 1;
@@ -2072,6 +2298,9 @@ main(int argc, char **argv)
2072 case 'N': 2298 case 'N':
2073 identity_new_passphrase = optarg; 2299 identity_new_passphrase = optarg;
2074 break; 2300 break;
2301 case 'Q':
2302 check_krl = 1;
2303 break;
2075 case 'O': 2304 case 'O':
2076 add_cert_option(optarg); 2305 add_cert_option(optarg);
2077 break; 2306 break;
@@ -2090,6 +2319,9 @@ main(int argc, char **argv)
2090 cert_key_type = SSH2_CERT_TYPE_HOST; 2319 cert_key_type = SSH2_CERT_TYPE_HOST;
2091 certflags_flags = 0; 2320 certflags_flags = 0;
2092 break; 2321 break;
2322 case 'k':
2323 gen_krl = 1;
2324 break;
2093 case 'i': 2325 case 'i':
2094 case 'X': 2326 case 'X':
2095 /* import key */ 2327 /* import key */
@@ -2107,6 +2339,9 @@ main(int argc, char **argv)
2107 case 'D': 2339 case 'D':
2108 pkcs11provider = optarg; 2340 pkcs11provider = optarg;
2109 break; 2341 break;
2342 case 'u':
2343 update_krl = 1;
2344 break;
2110 case 'v': 2345 case 'v':
2111 if (log_level == SYSLOG_LEVEL_INFO) 2346 if (log_level == SYSLOG_LEVEL_INFO)
2112 log_level = SYSLOG_LEVEL_DEBUG1; 2347 log_level = SYSLOG_LEVEL_DEBUG1;
@@ -2182,11 +2417,11 @@ main(int argc, char **argv)
2182 argc -= optind; 2417 argc -= optind;
2183 2418
2184 if (ca_key_path != NULL) { 2419 if (ca_key_path != NULL) {
2185 if (argc < 1) { 2420 if (argc < 1 && !gen_krl) {
2186 printf("Too few arguments.\n"); 2421 printf("Too few arguments.\n");
2187 usage(); 2422 usage();
2188 } 2423 }
2189 } else if (argc > 0) { 2424 } else if (argc > 0 && !gen_krl && !check_krl) {
2190 printf("Too many arguments.\n"); 2425 printf("Too many arguments.\n");
2191 usage(); 2426 usage();
2192 } 2427 }
@@ -2198,6 +2433,14 @@ main(int argc, char **argv)
2198 printf("Cannot use -l with -H or -R.\n"); 2433 printf("Cannot use -l with -H or -R.\n");
2199 usage(); 2434 usage();
2200 } 2435 }
2436 if (gen_krl) {
2437 do_gen_krl(pw, update_krl, argc, argv);
2438 return (0);
2439 }
2440 if (check_krl) {
2441 do_check_krl(pw, argc, argv);
2442 return (0);
2443 }
2201 if (ca_key_path != NULL) { 2444 if (ca_key_path != NULL) {
2202 if (cert_key_id == NULL) 2445 if (cert_key_id == NULL)
2203 fatal("Must specify key id (-I) when certifying"); 2446 fatal("Must specify key id (-I) when certifying");
diff --git a/sshd_config.5 b/sshd_config.5
index e7bb0b55f..c8b814da6 100644
--- a/sshd_config.5
+++ b/sshd_config.5
@@ -33,8 +33,8 @@
33.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 33.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
34.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35.\" 35.\"
36.\" $OpenBSD: sshd_config.5,v 1.153 2013/01/08 18:49:04 markus Exp $ 36.\" $OpenBSD: sshd_config.5,v 1.154 2013/01/17 23:00:01 djm Exp $
37.Dd $Mdocdate: January 8 2013 $ 37.Dd $Mdocdate: January 17 2013 $
38.Dt SSHD_CONFIG 5 38.Dt SSHD_CONFIG 5
39.Os 39.Os
40.Sh NAME 40.Sh NAME
@@ -994,10 +994,17 @@ The default is
994.Dq yes . 994.Dq yes .
995Note that this option applies to protocol version 2 only. 995Note that this option applies to protocol version 2 only.
996.It Cm RevokedKeys 996.It Cm RevokedKeys
997Specifies a list of revoked public keys. 997Specifies revoked public keys.
998Keys listed in this file will be refused for public key authentication. 998Keys listed in this file will be refused for public key authentication.
999Note that if this file is not readable, then public key authentication will 999Note that if this file is not readable, then public key authentication will
1000be refused for all users. 1000be refused for all users.
1001Keys may be specified as a text file, listing one public key per line, or as
1002an OpenSSH Key Revocation List (KRL) as generated by
1003.Xr ssh-keygen 1
1004For more information on KRLs, see the
1005.Sx KEY REVOCATION LISTS
1006section in
1007.Xr ssh-keygen 1 .
1001.It Cm RhostsRSAAuthentication 1008.It Cm RhostsRSAAuthentication
1002Specifies whether rhosts or /etc/hosts.equiv authentication together 1009Specifies whether rhosts or /etc/hosts.equiv authentication together
1003with successful RSA host authentication is allowed. 1010with successful RSA host authentication is allowed.