summaryrefslogtreecommitdiff
path: root/debian/patches/ssh-vulnkey.patch
diff options
context:
space:
mode:
Diffstat (limited to 'debian/patches/ssh-vulnkey.patch')
-rw-r--r--debian/patches/ssh-vulnkey.patch1385
1 files changed, 1385 insertions, 0 deletions
diff --git a/debian/patches/ssh-vulnkey.patch b/debian/patches/ssh-vulnkey.patch
new file mode 100644
index 000000000..78c9833cd
--- /dev/null
+++ b/debian/patches/ssh-vulnkey.patch
@@ -0,0 +1,1385 @@
1Description: Reject vulnerable keys to mitigate Debian OpenSSL flaw
2 In 2008, Debian (and derived distributions such as Ubuntu) shipped an
3 OpenSSL package with a flawed random number generator, causing OpenSSH to
4 generate only a very limited set of keys which were subject to private half
5 precomputation. To mitigate this, this patch checks key authentications
6 against a blacklist of known-vulnerable keys, and adds a new ssh-vulnkey
7 program which can be used to explicitly check keys against that blacklist.
8 See CVE-2008-0166.
9Author: Colin Watson <cjwatson@ubuntu.com>
10Bug: https://bugzilla.mindrot.org/show_bug.cgi?id=1469
11Last-Update: 2010-02-27
12
13Index: b/Makefile.in
14===================================================================
15--- a/Makefile.in
16+++ b/Makefile.in
17@@ -27,6 +27,7 @@
18 SSH_KEYSIGN=$(libexecdir)/ssh-keysign
19 SSH_PKCS11_HELPER=$(libexecdir)/ssh-pkcs11-helper
20 RAND_HELPER=$(libexecdir)/ssh-rand-helper
21+SSH_DATADIR=$(datadir)/ssh
22 PRIVSEP_PATH=@PRIVSEP_PATH@
23 SSH_PRIVSEP_USER=@SSH_PRIVSEP_USER@
24 STRIP_OPT=@STRIP_OPT@
25@@ -39,7 +40,8 @@
26 -D_PATH_SSH_PKCS11_HELPER=\"$(SSH_PKCS11_HELPER)\" \
27 -D_PATH_SSH_PIDDIR=\"$(piddir)\" \
28 -D_PATH_PRIVSEP_CHROOT_DIR=\"$(PRIVSEP_PATH)\" \
29- -DSSH_RAND_HELPER=\"$(RAND_HELPER)\"
30+ -DSSH_RAND_HELPER=\"$(RAND_HELPER)\" \
31+ -D_PATH_SSH_DATADIR=\"$(SSH_DATADIR)\"
32
33 CC=@CC@
34 LD=@LD@
35@@ -63,7 +65,7 @@
36 INSTALL_SSH_PRNG_CMDS=@INSTALL_SSH_PRNG_CMDS@
37 INSTALL_SSH_RAND_HELPER=@INSTALL_SSH_RAND_HELPER@
38
39-TARGETS=ssh$(EXEEXT) sshd$(EXEEXT) ssh-add$(EXEEXT) ssh-keygen$(EXEEXT) ssh-keyscan${EXEEXT} ssh-keysign${EXEEXT} ssh-pkcs11-helper$(EXEEXT) ssh-agent$(EXEEXT) scp$(EXEEXT) ssh-rand-helper${EXEEXT} sftp-server$(EXEEXT) sftp$(EXEEXT)
40+TARGETS=ssh$(EXEEXT) sshd$(EXEEXT) ssh-add$(EXEEXT) ssh-keygen$(EXEEXT) ssh-keyscan${EXEEXT} ssh-keysign${EXEEXT} ssh-pkcs11-helper$(EXEEXT) ssh-agent$(EXEEXT) scp$(EXEEXT) ssh-rand-helper${EXEEXT} sftp-server$(EXEEXT) sftp$(EXEEXT) ssh-vulnkey$(EXEEXT)
41
42 LIBSSH_OBJS=acss.o authfd.o authfile.o bufaux.o bufbn.o buffer.o \
43 canohost.o channels.o cipher.o cipher-acss.o cipher-aes.o \
44@@ -96,8 +98,8 @@
45 sftp-server.o sftp-common.o \
46 roaming_common.o roaming_serv.o
47
48-MANPAGES = moduli.5.out scp.1.out ssh-add.1.out ssh-agent.1.out ssh-keygen.1.out ssh-keyscan.1.out ssh.1.out sshd.8.out sftp-server.8.out sftp.1.out ssh-rand-helper.8.out ssh-keysign.8.out ssh-pkcs11-helper.8.out sshd_config.5.out ssh_config.5.out
49-MANPAGES_IN = moduli.5 scp.1 ssh-add.1 ssh-agent.1 ssh-keygen.1 ssh-keyscan.1 ssh.1 sshd.8 sftp-server.8 sftp.1 ssh-rand-helper.8 ssh-keysign.8 ssh-pkcs11-helper.8 sshd_config.5 ssh_config.5
50+MANPAGES = moduli.5.out scp.1.out ssh-add.1.out ssh-agent.1.out ssh-keygen.1.out ssh-keyscan.1.out ssh.1.out sshd.8.out sftp-server.8.out sftp.1.out ssh-rand-helper.8.out ssh-keysign.8.out ssh-pkcs11-helper.8.out ssh-vulnkey.1.out sshd_config.5.out ssh_config.5.out
51+MANPAGES_IN = moduli.5 scp.1 ssh-add.1 ssh-agent.1 ssh-keygen.1 ssh-keyscan.1 ssh.1 sshd.8 sftp-server.8 sftp.1 ssh-rand-helper.8 ssh-keysign.8 ssh-pkcs11-helper.8 ssh-vulnkey.1 sshd_config.5 ssh_config.5
52 MANTYPE = @MANTYPE@
53
54 CONFIGFILES=sshd_config.out ssh_config.out moduli.out
55@@ -178,6 +180,9 @@
56 ssh-rand-helper${EXEEXT}: $(LIBCOMPAT) libssh.a ssh-rand-helper.o
57 $(LD) -o $@ ssh-rand-helper.o $(LDFLAGS) -lssh -lopenbsd-compat $(LIBS)
58
59+ssh-vulnkey$(EXEEXT): $(LIBCOMPAT) libssh.a ssh-vulnkey.o
60+ $(LD) -o $@ ssh-vulnkey.o $(LDFLAGS) -lssh -lopenbsd-compat $(LIBS)
61+
62 # test driver for the loginrec code - not built by default
63 logintest: logintest.o $(LIBCOMPAT) libssh.a loginrec.o
64 $(LD) -o $@ logintest.o $(LDFLAGS) loginrec.o -lopenbsd-compat -lssh $(LIBS)
65@@ -272,6 +277,7 @@
66 $(INSTALL) -m 0755 $(STRIP_OPT) ssh-pkcs11-helper$(EXEEXT) $(DESTDIR)$(SSH_PKCS11_HELPER)$(EXEEXT)
67 $(INSTALL) -m 0755 $(STRIP_OPT) sftp$(EXEEXT) $(DESTDIR)$(bindir)/sftp$(EXEEXT)
68 $(INSTALL) -m 0755 $(STRIP_OPT) sftp-server$(EXEEXT) $(DESTDIR)$(SFTP_SERVER)$(EXEEXT)
69+ $(INSTALL) -m 0755 $(STRIP_OPT) ssh-vulnkey$(EXEEXT) $(DESTDIR)$(bindir)/ssh-vulnkey$(EXEEXT)
70 $(INSTALL) -m 644 ssh.1.out $(DESTDIR)$(mandir)/$(mansubdir)1/ssh.1
71 $(INSTALL) -m 644 scp.1.out $(DESTDIR)$(mandir)/$(mansubdir)1/scp.1
72 $(INSTALL) -m 644 ssh-add.1.out $(DESTDIR)$(mandir)/$(mansubdir)1/ssh-add.1
73@@ -289,6 +295,7 @@
74 $(INSTALL) -m 644 sftp-server.8.out $(DESTDIR)$(mandir)/$(mansubdir)8/sftp-server.8
75 $(INSTALL) -m 644 ssh-keysign.8.out $(DESTDIR)$(mandir)/$(mansubdir)8/ssh-keysign.8
76 $(INSTALL) -m 644 ssh-pkcs11-helper.8.out $(DESTDIR)$(mandir)/$(mansubdir)8/ssh-pkcs11-helper.8
77+ $(INSTALL) -m 644 ssh-vulnkey.1.out $(DESTDIR)$(mandir)/$(mansubdir)1/ssh-vulnkey.1
78 -rm -f $(DESTDIR)$(bindir)/slogin
79 ln -s ./ssh$(EXEEXT) $(DESTDIR)$(bindir)/slogin
80 -rm -f $(DESTDIR)$(mandir)/$(mansubdir)1/slogin.1
81@@ -378,6 +385,7 @@
82 -rm -f $(DESTDIR)$(bindir)/ssh-agent$(EXEEXT)
83 -rm -f $(DESTDIR)$(bindir)/ssh-keygen$(EXEEXT)
84 -rm -f $(DESTDIR)$(bindir)/ssh-keyscan$(EXEEXT)
85+ -rm -f $(DESTDIR)$(bindir)/ssh-vulnkey$(EXEEXT)
86 -rm -f $(DESTDIR)$(bindir)/sftp$(EXEEXT)
87 -rm -f $(DESTDIR)$(sbindir)/sshd$(EXEEXT)
88 -rm -r $(DESTDIR)$(SFTP_SERVER)$(EXEEXT)
89@@ -391,6 +399,7 @@
90 -rm -f $(DESTDIR)$(mandir)/$(mansubdir)1/ssh-keygen.1
91 -rm -f $(DESTDIR)$(mandir)/$(mansubdir)1/sftp.1
92 -rm -f $(DESTDIR)$(mandir)/$(mansubdir)1/ssh-keyscan.1
93+ -rm -f $(DESTDIR)$(mandir)/$(mansubdir)1/ssh-vulnkey.1
94 -rm -f $(DESTDIR)$(mandir)/$(mansubdir)8/sshd.8
95 -rm -f $(DESTDIR)$(mandir)/$(mansubdir)8/ssh-rand-helper.8
96 -rm -f $(DESTDIR)$(mandir)/$(mansubdir)8/sftp-server.8
97Index: b/auth-rh-rsa.c
98===================================================================
99--- a/auth-rh-rsa.c
100+++ b/auth-rh-rsa.c
101@@ -44,7 +44,7 @@
102 {
103 HostStatus host_status;
104
105- if (auth_key_is_revoked(client_host_key))
106+ if (auth_key_is_revoked(client_host_key, 0))
107 return 0;
108
109 /* Check if we would accept it using rhosts authentication. */
110Index: b/auth-rsa.c
111===================================================================
112--- a/auth-rsa.c
113+++ b/auth-rsa.c
114@@ -247,7 +247,7 @@
115 file, linenum, BN_num_bits(key->rsa->n), bits);
116
117 /* Never accept a revoked key */
118- if (auth_key_is_revoked(key))
119+ if (auth_key_is_revoked(key, 0))
120 break;
121
122 /* We have found the desired key. */
123Index: b/auth.c
124===================================================================
125--- a/auth.c
126+++ b/auth.c
127@@ -59,6 +59,7 @@
128 #include "servconf.h"
129 #include "key.h"
130 #include "hostfile.h"
131+#include "authfile.h"
132 #include "auth.h"
133 #include "auth-options.h"
134 #include "canohost.h"
135@@ -621,10 +622,34 @@
136
137 /* Returns 1 if key is revoked by revoked_keys_file, 0 otherwise */
138 int
139-auth_key_is_revoked(Key *key)
140+auth_key_is_revoked(Key *key, int hostkey)
141 {
142 char *key_fp;
143
144+ if (blacklisted_key(key, &key_fp) == 1) {
145+ if (options.permit_blacklisted_keys) {
146+ if (hostkey)
147+ error("Host key %s blacklisted (see "
148+ "ssh-vulnkey(1)); continuing anyway",
149+ key_fp);
150+ else
151+ logit("Public key %s from %s blacklisted (see "
152+ "ssh-vulnkey(1)); continuing anyway",
153+ key_fp, get_remote_ipaddr());
154+ xfree(key_fp);
155+ } else {
156+ if (hostkey)
157+ error("Host key %s blacklisted (see "
158+ "ssh-vulnkey(1))", key_fp);
159+ else
160+ logit("Public key %s from %s blacklisted (see "
161+ "ssh-vulnkey(1))",
162+ key_fp, get_remote_ipaddr());
163+ xfree(key_fp);
164+ return 1;
165+ }
166+ }
167+
168 if (options.revoked_keys_file == NULL)
169 return 0;
170
171Index: b/auth.h
172===================================================================
173--- a/auth.h
174+++ b/auth.h
175@@ -175,7 +175,7 @@
176
177 FILE *auth_openkeyfile(const char *, struct passwd *, int);
178 FILE *auth_openprincipals(const char *, struct passwd *, int);
179-int auth_key_is_revoked(Key *);
180+int auth_key_is_revoked(Key *, int);
181
182 HostStatus
183 check_key_in_hostfiles(struct passwd *, Key *, const char *,
184Index: b/auth2-hostbased.c
185===================================================================
186--- a/auth2-hostbased.c
187+++ b/auth2-hostbased.c
188@@ -146,7 +146,7 @@
189 int len;
190 char *fp;
191
192- if (auth_key_is_revoked(key))
193+ if (auth_key_is_revoked(key, 0))
194 return 0;
195
196 resolvedname = get_canonical_hostname(options.use_dns);
197Index: b/auth2-pubkey.c
198===================================================================
199--- a/auth2-pubkey.c
200+++ b/auth2-pubkey.c
201@@ -439,9 +439,10 @@
202 int success;
203 char *file;
204
205- if (auth_key_is_revoked(key))
206+ if (auth_key_is_revoked(key, 0))
207 return 0;
208- if (key_is_cert(key) && auth_key_is_revoked(key->cert->signature_key))
209+ if (key_is_cert(key) &&
210+ auth_key_is_revoked(key->cert->signature_key, 0))
211 return 0;
212
213 success = user_cert_trusted_ca(pw, key);
214Index: b/authfile.c
215===================================================================
216--- a/authfile.c
217+++ b/authfile.c
218@@ -68,6 +68,7 @@
219 #include "rsa.h"
220 #include "misc.h"
221 #include "atomicio.h"
222+#include "pathnames.h"
223
224 /* Version identification string for SSH v1 identity files. */
225 static const char authfile_id_string[] =
226@@ -906,3 +907,140 @@
227 return ret;
228 }
229
230+/* Scan a blacklist of known-vulnerable keys in blacklist_file. */
231+static int
232+blacklisted_key_in_file(Key *key, const char *blacklist_file, char **fp)
233+{
234+ int fd = -1;
235+ char *dgst_hex = NULL;
236+ char *dgst_packed = NULL, *p;
237+ int i;
238+ size_t line_len;
239+ struct stat st;
240+ char buf[256];
241+ off_t start, lower, upper;
242+ int ret = 0;
243+
244+ debug("Checking blacklist file %s", blacklist_file);
245+ fd = open(blacklist_file, O_RDONLY);
246+ if (fd < 0) {
247+ ret = -1;
248+ goto out;
249+ }
250+
251+ dgst_hex = key_fingerprint(key, SSH_FP_MD5, SSH_FP_HEX);
252+ /* Remove all colons */
253+ dgst_packed = xcalloc(1, strlen(dgst_hex) + 1);
254+ for (i = 0, p = dgst_packed; dgst_hex[i]; i++)
255+ if (dgst_hex[i] != ':')
256+ *p++ = dgst_hex[i];
257+ /* Only compare least-significant 80 bits (to keep the blacklist
258+ * size down)
259+ */
260+ line_len = strlen(dgst_packed + 12);
261+ if (line_len > 32)
262+ goto out;
263+
264+ /* Skip leading comments */
265+ start = 0;
266+ for (;;) {
267+ ssize_t r;
268+ char *newline;
269+
270+ r = atomicio(read, fd, buf, sizeof(buf));
271+ if (r <= 0)
272+ goto out;
273+ if (buf[0] != '#')
274+ break;
275+
276+ newline = memchr(buf, '\n', sizeof(buf));
277+ if (!newline)
278+ goto out;
279+ start += newline + 1 - buf;
280+ if (lseek(fd, start, SEEK_SET) < 0)
281+ goto out;
282+ }
283+
284+ /* Initialise binary search record numbers */
285+ if (fstat(fd, &st) < 0)
286+ goto out;
287+ lower = 0;
288+ upper = (st.st_size - start) / (line_len + 1);
289+
290+ while (lower != upper) {
291+ off_t cur;
292+ int cmp;
293+
294+ cur = lower + (upper - lower) / 2;
295+
296+ /* Read this line and compare to digest; this is
297+ * overflow-safe since cur < max(off_t) / (line_len + 1) */
298+ if (lseek(fd, start + cur * (line_len + 1), SEEK_SET) < 0)
299+ break;
300+ if (atomicio(read, fd, buf, line_len) != line_len)
301+ break;
302+ cmp = memcmp(buf, dgst_packed + 12, line_len);
303+ if (cmp < 0) {
304+ if (cur == lower)
305+ break;
306+ lower = cur;
307+ } else if (cmp > 0) {
308+ if (cur == upper)
309+ break;
310+ upper = cur;
311+ } else {
312+ debug("Found %s in blacklist", dgst_hex);
313+ ret = 1;
314+ break;
315+ }
316+ }
317+
318+out:
319+ if (dgst_packed)
320+ xfree(dgst_packed);
321+ if (ret != 1 && dgst_hex) {
322+ xfree(dgst_hex);
323+ dgst_hex = NULL;
324+ }
325+ if (fp)
326+ *fp = dgst_hex;
327+ if (fd >= 0)
328+ close(fd);
329+ return ret;
330+}
331+
332+/*
333+ * Scan blacklists of known-vulnerable keys. If a vulnerable key is found,
334+ * its fingerprint is returned in *fp, unless fp is NULL.
335+ */
336+int
337+blacklisted_key(Key *key, char **fp)
338+{
339+ Key *public;
340+ char *blacklist_file;
341+ int ret, ret2;
342+
343+ public = key_demote(key);
344+ if (public->type == KEY_RSA1)
345+ public->type = KEY_RSA;
346+
347+ xasprintf(&blacklist_file, "%s.%s-%u",
348+ _PATH_BLACKLIST, key_type(public), key_size(public));
349+ ret = blacklisted_key_in_file(public, blacklist_file, fp);
350+ xfree(blacklist_file);
351+ if (ret > 0) {
352+ key_free(public);
353+ return ret;
354+ }
355+
356+ xasprintf(&blacklist_file, "%s.%s-%u",
357+ _PATH_BLACKLIST_CONFIG, key_type(public), key_size(public));
358+ ret2 = blacklisted_key_in_file(public, blacklist_file, fp);
359+ xfree(blacklist_file);
360+ if (ret2 > ret)
361+ ret = ret2;
362+
363+ key_free(public);
364+ return ret;
365+}
366+
367Index: b/authfile.h
368===================================================================
369--- a/authfile.h
370+++ b/authfile.h
371@@ -26,4 +26,6 @@
372 int key_perm_ok(int, const char *);
373 int key_in_file(Key *, const char *, int);
374
375+int blacklisted_key(Key *key, char **fp);
376+
377 #endif
378Index: b/pathnames.h
379===================================================================
380--- a/pathnames.h
381+++ b/pathnames.h
382@@ -18,6 +18,10 @@
383 #define SSHDIR ETCDIR "/ssh"
384 #endif
385
386+#ifndef _PATH_SSH_DATADIR
387+#define _PATH_SSH_DATADIR "/usr/share/ssh"
388+#endif
389+
390 #ifndef _PATH_SSH_PIDDIR
391 #define _PATH_SSH_PIDDIR "/var/run"
392 #endif
393@@ -44,6 +48,9 @@
394 /* Backwards compatibility */
395 #define _PATH_DH_PRIMES SSHDIR "/primes"
396
397+#define _PATH_BLACKLIST _PATH_SSH_DATADIR "/blacklist"
398+#define _PATH_BLACKLIST_CONFIG SSHDIR "/blacklist"
399+
400 #ifndef _PATH_SSH_PROGRAM
401 #define _PATH_SSH_PROGRAM "/usr/bin/ssh"
402 #endif
403Index: b/readconf.c
404===================================================================
405--- a/readconf.c
406+++ b/readconf.c
407@@ -125,6 +125,7 @@
408 oGlobalKnownHostsFile2, oUserKnownHostsFile2, oPubkeyAuthentication,
409 oKbdInteractiveAuthentication, oKbdInteractiveDevices, oHostKeyAlias,
410 oDynamicForward, oPreferredAuthentications, oHostbasedAuthentication,
411+ oUseBlacklistedKeys,
412 oHostKeyAlgorithms, oBindAddress, oPKCS11Provider,
413 oClearAllForwardings, oNoHostAuthenticationForLocalhost,
414 oEnableSSHKeysign, oRekeyLimit, oVerifyHostKeyDNS, oConnectTimeout,
415@@ -158,6 +159,7 @@
416 { "passwordauthentication", oPasswordAuthentication },
417 { "kbdinteractiveauthentication", oKbdInteractiveAuthentication },
418 { "kbdinteractivedevices", oKbdInteractiveDevices },
419+ { "useblacklistedkeys", oUseBlacklistedKeys },
420 { "rsaauthentication", oRSAAuthentication },
421 { "pubkeyauthentication", oPubkeyAuthentication },
422 { "dsaauthentication", oPubkeyAuthentication }, /* alias */
423@@ -486,6 +488,10 @@
424 intptr = &options->challenge_response_authentication;
425 goto parse_flag;
426
427+ case oUseBlacklistedKeys:
428+ intptr = &options->use_blacklisted_keys;
429+ goto parse_flag;
430+
431 case oGssAuthentication:
432 intptr = &options->gss_authentication;
433 goto parse_flag;
434@@ -1134,6 +1140,7 @@
435 options->kbd_interactive_devices = NULL;
436 options->rhosts_rsa_authentication = -1;
437 options->hostbased_authentication = -1;
438+ options->use_blacklisted_keys = -1;
439 options->batch_mode = -1;
440 options->check_host_ip = -1;
441 options->strict_host_key_checking = -1;
442@@ -1245,6 +1252,8 @@
443 options->rhosts_rsa_authentication = 0;
444 if (options->hostbased_authentication == -1)
445 options->hostbased_authentication = 0;
446+ if (options->use_blacklisted_keys == -1)
447+ options->use_blacklisted_keys = 0;
448 if (options->batch_mode == -1)
449 options->batch_mode = 0;
450 if (options->check_host_ip == -1)
451Index: b/readconf.h
452===================================================================
453--- a/readconf.h
454+++ b/readconf.h
455@@ -57,6 +57,7 @@
456 int kbd_interactive_authentication; /* Try keyboard-interactive auth. */
457 char *kbd_interactive_devices; /* Keyboard-interactive auth devices. */
458 int zero_knowledge_password_authentication; /* Try jpake */
459+ int use_blacklisted_keys; /* If true, send */
460 int batch_mode; /* Batch mode: do not ask for passwords. */
461 int check_host_ip; /* Also keep track of keys for IP address */
462 int strict_host_key_checking; /* Strict host key checking. */
463Index: b/servconf.c
464===================================================================
465--- a/servconf.c
466+++ b/servconf.c
467@@ -104,6 +104,7 @@
468 options->password_authentication = -1;
469 options->kbd_interactive_authentication = -1;
470 options->challenge_response_authentication = -1;
471+ options->permit_blacklisted_keys = -1;
472 options->permit_empty_passwd = -1;
473 options->permit_user_env = -1;
474 options->use_login = -1;
475@@ -243,6 +244,8 @@
476 options->kbd_interactive_authentication = 0;
477 if (options->challenge_response_authentication == -1)
478 options->challenge_response_authentication = 1;
479+ if (options->permit_blacklisted_keys == -1)
480+ options->permit_blacklisted_keys = 0;
481 if (options->permit_empty_passwd == -1)
482 options->permit_empty_passwd = 0;
483 if (options->permit_user_env == -1)
484@@ -322,7 +325,7 @@
485 sListenAddress, sAddressFamily,
486 sPrintMotd, sPrintLastLog, sIgnoreRhosts,
487 sX11Forwarding, sX11DisplayOffset, sX11UseLocalhost,
488- sStrictModes, sEmptyPasswd, sTCPKeepAlive,
489+ sStrictModes, sPermitBlacklistedKeys, sEmptyPasswd, sTCPKeepAlive,
490 sPermitUserEnvironment, sUseLogin, sAllowTcpForwarding, sCompression,
491 sAllowUsers, sDenyUsers, sAllowGroups, sDenyGroups,
492 sIgnoreUserKnownHosts, sCiphers, sMacs, sProtocol, sPidFile,
493@@ -432,6 +435,7 @@
494 { "x11uselocalhost", sX11UseLocalhost, SSHCFG_ALL },
495 { "xauthlocation", sXAuthLocation, SSHCFG_GLOBAL },
496 { "strictmodes", sStrictModes, SSHCFG_GLOBAL },
497+ { "permitblacklistedkeys", sPermitBlacklistedKeys, SSHCFG_GLOBAL },
498 { "permitemptypasswords", sEmptyPasswd, SSHCFG_ALL },
499 { "permituserenvironment", sPermitUserEnvironment, SSHCFG_GLOBAL },
500 { "uselogin", sUseLogin, SSHCFG_GLOBAL },
501@@ -1029,6 +1033,10 @@
502 intptr = &options->tcp_keep_alive;
503 goto parse_flag;
504
505+ case sPermitBlacklistedKeys:
506+ intptr = &options->permit_blacklisted_keys;
507+ goto parse_flag;
508+
509 case sEmptyPasswd:
510 intptr = &options->permit_empty_passwd;
511 goto parse_flag;
512@@ -1757,6 +1765,7 @@
513 dump_cfg_fmtint(sX11UseLocalhost, o->x11_use_localhost);
514 dump_cfg_fmtint(sStrictModes, o->strict_modes);
515 dump_cfg_fmtint(sTCPKeepAlive, o->tcp_keep_alive);
516+ dump_cfg_fmtint(sPermitBlacklistedKeys, o->permit_blacklisted_keys);
517 dump_cfg_fmtint(sEmptyPasswd, o->permit_empty_passwd);
518 dump_cfg_fmtint(sPermitUserEnvironment, o->permit_user_env);
519 dump_cfg_fmtint(sUseLogin, o->use_login);
520Index: b/servconf.h
521===================================================================
522--- a/servconf.h
523+++ b/servconf.h
524@@ -107,6 +107,7 @@
525 int challenge_response_authentication;
526 int zero_knowledge_password_authentication;
527 /* If true, permit jpake auth */
528+ int permit_blacklisted_keys; /* If true, permit */
529 int permit_empty_passwd; /* If false, do not permit empty
530 * passwords. */
531 int permit_user_env; /* If true, read ~/.ssh/environment */
532Index: b/ssh-add.1
533===================================================================
534--- a/ssh-add.1
535+++ b/ssh-add.1
536@@ -81,6 +81,10 @@
537 .Nm
538 to work.
539 .Pp
540+Any keys recorded in the blacklist of known-compromised keys (see
541+.Xr ssh-vulnkey 1 )
542+will be refused.
543+.Pp
544 The options are as follows:
545 .Bl -tag -width Ds
546 .It Fl c
547@@ -183,6 +187,7 @@
548 .Xr ssh 1 ,
549 .Xr ssh-agent 1 ,
550 .Xr ssh-keygen 1 ,
551+.Xr ssh-vulnkey 1 ,
552 .Xr sshd 8
553 .Sh AUTHORS
554 OpenSSH is a derivative of the original and free
555Index: b/ssh-add.c
556===================================================================
557--- a/ssh-add.c
558+++ b/ssh-add.c
559@@ -142,7 +142,7 @@
560 add_file(AuthenticationConnection *ac, const char *filename)
561 {
562 Key *private, *cert;
563- char *comment = NULL;
564+ char *comment = NULL, *fp;
565 char msg[1024], *certpath;
566 int fd, perms_ok, ret = -1;
567
568@@ -187,6 +187,14 @@
569 "Bad passphrase, try again for %.200s: ", comment);
570 }
571 }
572+ if (blacklisted_key(private, &fp) == 1) {
573+ fprintf(stderr, "Public key %s blacklisted (see "
574+ "ssh-vulnkey(1)); refusing to add it\n", fp);
575+ xfree(fp);
576+ key_free(private);
577+ xfree(comment);
578+ return -1;
579+ }
580
581 if (ssh_add_identity_constrained(ac, private, comment, lifetime,
582 confirm)) {
583Index: b/ssh-keygen.1
584===================================================================
585--- a/ssh-keygen.1
586+++ b/ssh-keygen.1
587@@ -659,6 +659,7 @@
588 .Xr ssh 1 ,
589 .Xr ssh-add 1 ,
590 .Xr ssh-agent 1 ,
591+.Xr ssh-vulnkey 1 ,
592 .Xr moduli 5 ,
593 .Xr sshd 8
594 .Rs
595Index: b/ssh-vulnkey.1
596===================================================================
597--- /dev/null
598+++ b/ssh-vulnkey.1
599@@ -0,0 +1,242 @@
600+.\" Copyright (c) 2008 Canonical Ltd. All rights reserved.
601+.\"
602+.\" Redistribution and use in source and binary forms, with or without
603+.\" modification, are permitted provided that the following conditions
604+.\" are met:
605+.\" 1. Redistributions of source code must retain the above copyright
606+.\" notice, this list of conditions and the following disclaimer.
607+.\" 2. Redistributions in binary form must reproduce the above copyright
608+.\" notice, this list of conditions and the following disclaimer in the
609+.\" documentation and/or other materials provided with the distribution.
610+.\"
611+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
612+.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
613+.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
614+.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
615+.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
616+.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
617+.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
618+.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
619+.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
620+.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
621+.\"
622+.Dd $Mdocdate: May 12 2008 $
623+.Dt SSH-VULNKEY 1
624+.Os
625+.Sh NAME
626+.Nm ssh-vulnkey
627+.Nd check blacklist of compromised keys
628+.Sh SYNOPSIS
629+.Nm
630+.Op Fl q | Fl v
631+.Ar file ...
632+.Nm
633+.Fl a
634+.Sh DESCRIPTION
635+.Nm
636+checks a key against a blacklist of compromised keys.
637+.Pp
638+A substantial number of keys are known to have been generated using a broken
639+version of OpenSSL distributed by Debian which failed to seed its random
640+number generator correctly.
641+Keys generated using these OpenSSL versions should be assumed to be
642+compromised.
643+This tool may be useful in checking for such keys.
644+.Pp
645+Keys that are compromised cannot be repaired; replacements must be generated
646+using
647+.Xr ssh-keygen 1 .
648+Make sure to update
649+.Pa authorized_keys
650+files on all systems where compromised keys were permitted to authenticate.
651+.Pp
652+The argument list will be interpreted as a list of paths to public key files
653+or
654+.Pa authorized_keys
655+files.
656+If no suitable file is found at a given path,
657+.Nm
658+will append
659+.Pa .pub
660+and retry, in case it was given a private key file.
661+If no files are given as arguments,
662+.Nm
663+will check
664+.Pa ~/.ssh/id_rsa ,
665+.Pa ~/.ssh/id_dsa ,
666+.Pa ~/.ssh/identity ,
667+.Pa ~/.ssh/authorized_keys
668+and
669+.Pa ~/.ssh/authorized_keys2 ,
670+as well as the system's host keys if readable.
671+.Pp
672+If
673+.Dq -
674+is given as an argument,
675+.Nm
676+will read from standard input.
677+This can be used to process output from
678+.Xr ssh-keyscan 1 ,
679+for example:
680+.Pp
681+.Dl $ ssh-keyscan -t rsa remote.example.org | ssh-vulnkey -
682+.Pp
683+Unless the
684+.Cm PermitBlacklistedKeys
685+option is used,
686+.Xr sshd 8
687+will reject attempts to authenticate with keys in the compromised list.
688+.Pp
689+The output from
690+.Nm
691+looks like this:
692+.Pp
693+.Bd -literal -offset indent
694+/etc/ssh/ssh_host_key:1: COMPROMISED: RSA1 2048 xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx root@host
695+/home/user/.ssh/id_dsa:1: Not blacklisted: DSA 1024 xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx /home/user/.ssh/id_dsa.pub
696+/home/user/.ssh/authorized_keys:3: Unknown (blacklist file not installed): RSA 1024 xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx user@host
697+.Ed
698+.Pp
699+Each line is of the following format (any lines beginning with
700+.Dq #
701+should be ignored by scripts):
702+.Pp
703+.Dl Ar filename : Ns Ar line : Ar status : Ar type Ar size Ar fingerprint Ar comment
704+.Pp
705+It is important to distinguish between the possible values of
706+.Ar status :
707+.Pp
708+.Bl -tag -width Ds
709+.It COMPROMISED
710+These keys are listed in a blacklist file, normally because their
711+corresponding private keys are well-known.
712+Replacements must be generated using
713+.Xr ssh-keygen 1 .
714+.It Not blacklisted
715+A blacklist file exists for this key type and size, but this key is not
716+listed in it.
717+Unless there is some particular reason to believe otherwise, this key
718+may be used safely.
719+(Note that DSA keys used with the broken version of OpenSSL distributed
720+by Debian may be compromised in the event that anyone captured a network
721+trace, even if they were generated with a secure version of OpenSSL.)
722+.It Unknown (blacklist file not installed)
723+No blacklist file exists for this key type and size.
724+You should find a suitable published blacklist and install it before
725+deciding whether this key is safe to use.
726+.El
727+.Pp
728+The options are as follows:
729+.Bl -tag -width Ds
730+.It Fl a
731+Check keys of all users on the system.
732+You will typically need to run
733+.Nm
734+as root to use this option.
735+For each user,
736+.Nm
737+will check
738+.Pa ~/.ssh/id_rsa ,
739+.Pa ~/.ssh/id_dsa ,
740+.Pa ~/.ssh/identity ,
741+.Pa ~/.ssh/authorized_keys
742+and
743+.Pa ~/.ssh/authorized_keys2 .
744+It will also check the system's host keys.
745+.It Fl q
746+Quiet mode.
747+Normally,
748+.Nm
749+outputs the fingerprint of each key scanned, with a description of its
750+status.
751+This option suppresses that output.
752+.It Fl v
753+Verbose mode.
754+Normally,
755+.Nm
756+does not output anything for keys that are not listed in their corresponding
757+blacklist file (although it still produces output for keys for which there
758+is no blacklist file, since their status is unknown).
759+This option causes
760+.Nm
761+to produce output for all keys.
762+.El
763+.Sh EXIT STATUS
764+.Nm
765+will exit zero if any of the given keys were in the compromised list,
766+otherwise non-zero.
767+.Sh BLACKLIST FILE FORMAT
768+The blacklist file may start with comments, on lines starting with
769+.Dq # .
770+After these initial comments, it must follow a strict format:
771+.Pp
772+.Bl -bullet -offset indent -compact
773+.It
774+All the lines must be exactly the same length (20 characters followed by a
775+newline) and must be in sorted order.
776+.It
777+Each line must consist of the lower-case hexadecimal MD5 key fingerprint,
778+without colons, and with the first 12 characters removed (that is, the least
779+significant 80 bits of the fingerprint).
780+.El
781+.Pp
782+The key fingerprint may be generated using
783+.Xr ssh-keygen 1 :
784+.Pp
785+.Dl $ ssh-keygen -l -f /path/to/key
786+.Pp
787+This strict format is necessary to allow the blacklist file to be checked
788+quickly, using a binary-search algorithm.
789+.Sh FILES
790+.Bl -tag -width Ds
791+.It Pa ~/.ssh/id_rsa
792+If present, contains the protocol version 2 RSA authentication identity of
793+the user.
794+.It Pa ~/.ssh/id_dsa
795+If present, contains the protocol version 2 DSA authentication identity of
796+the user.
797+.It Pa ~/.ssh/identity
798+If present, contains the protocol version 1 RSA authentication identity of
799+the user.
800+.It Pa ~/.ssh/authorized_keys
801+If present, lists the public keys (RSA/DSA) that can be used for logging in
802+as this user.
803+.It Pa ~/.ssh/authorized_keys2
804+Obsolete name for
805+.Pa ~/.ssh/authorized_keys .
806+This file may still be present on some old systems, but should not be
807+created if it is missing.
808+.It Pa /etc/ssh/ssh_host_rsa_key
809+If present, contains the protocol version 2 RSA identity of the system.
810+.It Pa /etc/ssh/ssh_host_dsa_key
811+If present, contains the protocol version 2 DSA identity of the system.
812+.It Pa /etc/ssh/ssh_host_key
813+If present, contains the protocol version 1 RSA identity of the system.
814+.It Pa /usr/share/ssh/blacklist. Ns Ar TYPE Ns Pa - Ns Ar LENGTH
815+If present, lists the blacklisted keys of type
816+.Ar TYPE
817+.Pf ( Dq RSA
818+or
819+.Dq DSA )
820+and bit length
821+.Ar LENGTH .
822+The format of this file is described above.
823+RSA1 keys are converted to RSA before being checked in the blacklist.
824+Note that the fingerprints of RSA1 keys are computed differently, so you
825+will not be able to find them in the blacklist by hand.
826+.It Pa /etc/ssh/blacklist. Ns Ar TYPE Ns Pa - Ns Ar LENGTH
827+Same as
828+.Pa /usr/share/ssh/blacklist. Ns Ar TYPE Ns Pa - Ns Ar LENGTH ,
829+but may be edited by the system administrator to add new blacklist entries.
830+.El
831+.Sh SEE ALSO
832+.Xr ssh-keygen 1 ,
833+.Xr sshd 8
834+.Sh AUTHORS
835+.An -nosplit
836+.An Colin Watson Aq cjwatson@ubuntu.com
837+.Pp
838+Florian Weimer suggested the option to check keys of all users, and the idea
839+of processing
840+.Xr ssh-keyscan 1
841+output.
842Index: b/ssh-vulnkey.c
843===================================================================
844--- /dev/null
845+++ b/ssh-vulnkey.c
846@@ -0,0 +1,388 @@
847+/*
848+ * Copyright (c) 2008 Canonical Ltd. All rights reserved.
849+ *
850+ * Redistribution and use in source and binary forms, with or without
851+ * modification, are permitted provided that the following conditions
852+ * are met:
853+ * 1. Redistributions of source code must retain the above copyright
854+ * notice, this list of conditions and the following disclaimer.
855+ * 2. Redistributions in binary form must reproduce the above copyright
856+ * notice, this list of conditions and the following disclaimer in the
857+ * documentation and/or other materials provided with the distribution.
858+ *
859+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
860+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
861+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
862+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
863+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
864+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
865+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
866+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
867+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
868+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
869+ */
870+
871+#include "includes.h"
872+
873+#include <sys/types.h>
874+#include <sys/stat.h>
875+
876+#include <errno.h>
877+#include <string.h>
878+#include <stdio.h>
879+#include <fcntl.h>
880+#include <unistd.h>
881+
882+#include <openssl/evp.h>
883+
884+#include "xmalloc.h"
885+#include "ssh.h"
886+#include "log.h"
887+#include "key.h"
888+#include "authfile.h"
889+#include "pathnames.h"
890+#include "uidswap.h"
891+#include "misc.h"
892+
893+extern char *__progname;
894+
895+/* Default files to check */
896+static char *default_host_files[] = {
897+ _PATH_HOST_RSA_KEY_FILE,
898+ _PATH_HOST_DSA_KEY_FILE,
899+ _PATH_HOST_KEY_FILE,
900+ NULL
901+};
902+static char *default_files[] = {
903+ _PATH_SSH_CLIENT_ID_RSA,
904+ _PATH_SSH_CLIENT_ID_DSA,
905+ _PATH_SSH_CLIENT_IDENTITY,
906+ _PATH_SSH_USER_PERMITTED_KEYS,
907+ _PATH_SSH_USER_PERMITTED_KEYS2,
908+ NULL
909+};
910+
911+static int verbosity = 0;
912+
913+static int some_keys = 0;
914+static int some_unknown = 0;
915+static int some_compromised = 0;
916+
917+static void
918+usage(void)
919+{
920+ fprintf(stderr, "usage: %s [-aqv] [file ...]\n", __progname);
921+ fprintf(stderr, "Options:\n");
922+ fprintf(stderr, " -a Check keys of all users.\n");
923+ fprintf(stderr, " -q Quiet mode.\n");
924+ fprintf(stderr, " -v Verbose mode.\n");
925+ exit(1);
926+}
927+
928+static void
929+describe_key(const char *filename, u_long linenum, const char *msg,
930+ Key *key, const char *comment, int min_verbosity)
931+{
932+ char *fp;
933+
934+ fp = key_fingerprint(key, SSH_FP_MD5, SSH_FP_HEX);
935+ if (verbosity >= min_verbosity) {
936+ if (strchr(filename, ':'))
937+ printf("\"%s\"", filename);
938+ else
939+ printf("%s", filename);
940+ printf(":%lu: %s: %s %u %s %s\n", linenum, msg,
941+ key_type(key), key_size(key), fp, comment);
942+ }
943+ xfree(fp);
944+}
945+
946+static int
947+do_key(const char *filename, u_long linenum,
948+ Key *key, const char *comment)
949+{
950+ Key *public;
951+ int blacklist_status;
952+ int ret = 1;
953+
954+ some_keys = 1;
955+
956+ public = key_demote(key);
957+ if (public->type == KEY_RSA1)
958+ public->type = KEY_RSA;
959+
960+ blacklist_status = blacklisted_key(public, NULL);
961+ if (blacklist_status == -1) {
962+ describe_key(filename, linenum,
963+ "Unknown (blacklist file not installed)", key, comment, 0);
964+ some_unknown = 1;
965+ } else if (blacklist_status == 1) {
966+ describe_key(filename, linenum,
967+ "COMPROMISED", key, comment, 0);
968+ some_compromised = 1;
969+ ret = 0;
970+ } else
971+ describe_key(filename, linenum,
972+ "Not blacklisted", key, comment, 1);
973+
974+ key_free(public);
975+
976+ return ret;
977+}
978+
979+static int
980+do_filename(const char *filename, int quiet_open)
981+{
982+ FILE *f;
983+ char line[SSH_MAX_PUBKEY_BYTES];
984+ char *cp;
985+ u_long linenum = 0;
986+ Key *key;
987+ char *comment = NULL;
988+ int found = 0, ret = 1;
989+
990+ /* Copy much of key_load_public's logic here so that we can read
991+ * several keys from a single file (e.g. authorized_keys).
992+ */
993+
994+ if (strcmp(filename, "-") != 0) {
995+ int save_errno;
996+ f = fopen(filename, "r");
997+ save_errno = errno;
998+ if (!f) {
999+ char pubfile[MAXPATHLEN];
1000+ if (strlcpy(pubfile, filename, sizeof pubfile) <
1001+ sizeof(pubfile) &&
1002+ strlcat(pubfile, ".pub", sizeof pubfile) <
1003+ sizeof(pubfile))
1004+ f = fopen(pubfile, "r");
1005+ }
1006+ errno = save_errno; /* earlier errno is more useful */
1007+ if (!f) {
1008+ if (!quiet_open)
1009+ perror(filename);
1010+ return -1;
1011+ }
1012+ if (verbosity > 0)
1013+ printf("# %s\n", filename);
1014+ } else
1015+ f = stdin;
1016+ while (read_keyfile_line(f, filename, line, sizeof(line),
1017+ &linenum) != -1) {
1018+ int i;
1019+ char *space;
1020+ int type;
1021+ char *end;
1022+
1023+ /* Chop trailing newline. */
1024+ i = strlen(line) - 1;
1025+ if (line[i] == '\n')
1026+ line[i] = '\0';
1027+
1028+ /* Skip leading whitespace, empty and comment lines. */
1029+ for (cp = line; *cp == ' ' || *cp == '\t'; cp++)
1030+ ;
1031+ if (!*cp || *cp == '\n' || *cp == '#')
1032+ continue;
1033+
1034+ /* Cope with ssh-keyscan output and options in
1035+ * authorized_keys files.
1036+ */
1037+ space = strchr(cp, ' ');
1038+ if (!space)
1039+ continue;
1040+ *space = '\0';
1041+ type = key_type_from_name(cp);
1042+ *space = ' ';
1043+ /* Leading number (RSA1) or valid type (RSA/DSA) indicates
1044+ * that we have no host name or options to skip.
1045+ */
1046+ if ((strtol(cp, &end, 10) == 0 || *end != ' ') &&
1047+ type == KEY_UNSPEC) {
1048+ int quoted = 0;
1049+
1050+ for (; *cp && (quoted || (*cp != ' ' && *cp != '\t')); cp++) {
1051+ if (*cp == '\\' && cp[1] == '"')
1052+ cp++; /* Skip both */
1053+ else if (*cp == '"')
1054+ quoted = !quoted;
1055+ }
1056+ /* Skip remaining whitespace. */
1057+ for (; *cp == ' ' || *cp == '\t'; cp++)
1058+ ;
1059+ if (!*cp)
1060+ continue;
1061+ }
1062+
1063+ /* Read and process the key itself. */
1064+ key = key_new(KEY_RSA1);
1065+ if (key_read(key, &cp) == 1) {
1066+ while (*cp == ' ' || *cp == '\t')
1067+ cp++;
1068+ if (!do_key(filename, linenum,
1069+ key, *cp ? cp : filename))
1070+ ret = 0;
1071+ found = 1;
1072+ } else {
1073+ key_free(key);
1074+ key = key_new(KEY_UNSPEC);
1075+ if (key_read(key, &cp) == 1) {
1076+ while (*cp == ' ' || *cp == '\t')
1077+ cp++;
1078+ if (!do_key(filename, linenum,
1079+ key, *cp ? cp : filename))
1080+ ret = 0;
1081+ found = 1;
1082+ }
1083+ }
1084+ key_free(key);
1085+ }
1086+ if (f != stdin)
1087+ fclose(f);
1088+
1089+ if (!found && filename) {
1090+ key = key_load_public(filename, &comment);
1091+ if (key) {
1092+ if (!do_key(filename, 1, key, comment))
1093+ ret = 0;
1094+ found = 1;
1095+ }
1096+ if (comment)
1097+ xfree(comment);
1098+ }
1099+
1100+ return ret;
1101+}
1102+
1103+static int
1104+do_host(int quiet_open)
1105+{
1106+ int i;
1107+ struct stat st;
1108+ int ret = 1;
1109+
1110+ for (i = 0; default_host_files[i]; i++) {
1111+ if (stat(default_host_files[i], &st) < 0 && errno == ENOENT)
1112+ continue;
1113+ if (!do_filename(default_host_files[i], quiet_open))
1114+ ret = 0;
1115+ }
1116+
1117+ return ret;
1118+}
1119+
1120+static int
1121+do_user(const char *dir)
1122+{
1123+ int i;
1124+ char *file;
1125+ struct stat st;
1126+ int ret = 1;
1127+
1128+ for (i = 0; default_files[i]; i++) {
1129+ xasprintf(&file, "%s/%s", dir, default_files[i]);
1130+ if (stat(file, &st) < 0 && errno == ENOENT) {
1131+ xfree(file);
1132+ continue;
1133+ }
1134+ if (!do_filename(file, 0))
1135+ ret = 0;
1136+ xfree(file);
1137+ }
1138+
1139+ return ret;
1140+}
1141+
1142+int
1143+main(int argc, char **argv)
1144+{
1145+ int opt, all_users = 0;
1146+ int ret = 1;
1147+ extern int optind;
1148+
1149+ /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
1150+ sanitise_stdfd();
1151+
1152+ __progname = ssh_get_progname(argv[0]);
1153+
1154+ SSLeay_add_all_algorithms();
1155+ log_init(argv[0], SYSLOG_LEVEL_INFO, SYSLOG_FACILITY_USER, 1);
1156+
1157+ /* We don't need the RNG ourselves, but symbol references here allow
1158+ * ld to link us properly.
1159+ */
1160+ init_rng();
1161+ seed_rng();
1162+
1163+ while ((opt = getopt(argc, argv, "ahqv")) != -1) {
1164+ switch (opt) {
1165+ case 'a':
1166+ all_users = 1;
1167+ break;
1168+ case 'q':
1169+ verbosity--;
1170+ break;
1171+ case 'v':
1172+ verbosity++;
1173+ break;
1174+ case 'h':
1175+ default:
1176+ usage();
1177+ }
1178+ }
1179+
1180+ if (all_users) {
1181+ struct passwd *pw;
1182+
1183+ if (!do_host(0))
1184+ ret = 0;
1185+
1186+ while ((pw = getpwent()) != NULL) {
1187+ if (pw->pw_dir) {
1188+ temporarily_use_uid(pw);
1189+ if (!do_user(pw->pw_dir))
1190+ ret = 0;
1191+ restore_uid();
1192+ }
1193+ }
1194+ } else if (optind == argc) {
1195+ struct passwd *pw;
1196+
1197+ if (!do_host(1))
1198+ ret = 0;
1199+
1200+ if ((pw = getpwuid(geteuid())) == NULL)
1201+ fprintf(stderr, "No user found with uid %u\n",
1202+ (u_int)geteuid());
1203+ else {
1204+ if (!do_user(pw->pw_dir))
1205+ ret = 0;
1206+ }
1207+ } else {
1208+ while (optind < argc)
1209+ if (!do_filename(argv[optind++], 0))
1210+ ret = 0;
1211+ }
1212+
1213+ if (verbosity >= 0) {
1214+ if (some_unknown) {
1215+ printf("#\n");
1216+ printf("# The status of some keys on your system is unknown.\n");
1217+ printf("# You may need to install additional blacklist files.\n");
1218+ }
1219+ if (some_compromised) {
1220+ printf("#\n");
1221+ printf("# Some keys on your system have been compromised!\n");
1222+ printf("# You must replace them using ssh-keygen(1).\n");
1223+ }
1224+ if (some_unknown || some_compromised) {
1225+ printf("#\n");
1226+ printf("# See the ssh-vulnkey(1) manual page for further advice.\n");
1227+ } else if (some_keys && verbosity > 0) {
1228+ printf("#\n");
1229+ printf("# No blacklisted keys!\n");
1230+ }
1231+ }
1232+
1233+ return ret;
1234+}
1235Index: b/ssh.1
1236===================================================================
1237--- a/ssh.1
1238+++ b/ssh.1
1239@@ -1402,6 +1402,7 @@
1240 .Xr ssh-agent 1 ,
1241 .Xr ssh-keygen 1 ,
1242 .Xr ssh-keyscan 1 ,
1243+.Xr ssh-vulnkey 1 ,
1244 .Xr tun 4 ,
1245 .Xr hosts.equiv 5 ,
1246 .Xr ssh_config 5 ,
1247Index: b/ssh.c
1248===================================================================
1249--- a/ssh.c
1250+++ b/ssh.c
1251@@ -1448,7 +1448,7 @@
1252 static void
1253 load_public_identity_files(void)
1254 {
1255- char *filename, *cp, thishost[NI_MAXHOST];
1256+ char *filename, *cp, thishost[NI_MAXHOST], *fp;
1257 char *pwdir = NULL, *pwname = NULL;
1258 int i = 0;
1259 Key *public;
1260@@ -1505,6 +1505,22 @@
1261 public = key_load_public(filename, NULL);
1262 debug("identity file %s type %d", filename,
1263 public ? public->type : -1);
1264+ if (public && blacklisted_key(public, &fp) == 1) {
1265+ if (options.use_blacklisted_keys)
1266+ logit("Public key %s blacklisted (see "
1267+ "ssh-vulnkey(1)); continuing anyway", fp);
1268+ else
1269+ logit("Public key %s blacklisted (see "
1270+ "ssh-vulnkey(1)); refusing to send it",
1271+ fp);
1272+ xfree(fp);
1273+ if (!options.use_blacklisted_keys) {
1274+ key_free(public);
1275+ xfree(filename);
1276+ filename = NULL;
1277+ public = NULL;
1278+ }
1279+ }
1280 xfree(options.identity_files[i]);
1281 identity_files[n_ids] = filename;
1282 identity_keys[n_ids] = public;
1283Index: b/ssh_config.5
1284===================================================================
1285--- a/ssh_config.5
1286+++ b/ssh_config.5
1287@@ -1146,6 +1146,23 @@
1288 .Dq any .
1289 The default is
1290 .Dq any:any .
1291+.It Cm UseBlacklistedKeys
1292+Specifies whether
1293+.Xr ssh 1
1294+should use keys recorded in its blacklist of known-compromised keys (see
1295+.Xr ssh-vulnkey 1 )
1296+for authentication.
1297+If
1298+.Dq yes ,
1299+then attempts to use compromised keys for authentication will be logged but
1300+accepted.
1301+It is strongly recommended that this be used only to install new authorized
1302+keys on the remote system, and even then only with the utmost care.
1303+If
1304+.Dq no ,
1305+then attempts to use compromised keys for authentication will be prevented.
1306+The default is
1307+.Dq no .
1308 .It Cm UsePrivilegedPort
1309 Specifies whether to use a privileged port for outgoing connections.
1310 The argument must be
1311Index: b/sshconnect2.c
1312===================================================================
1313--- a/sshconnect2.c
1314+++ b/sshconnect2.c
1315@@ -1488,6 +1488,8 @@
1316
1317 /* list of keys stored in the filesystem */
1318 for (i = 0; i < options.num_identity_files; i++) {
1319+ if (options.identity_files[i] == NULL)
1320+ continue;
1321 key = options.identity_keys[i];
1322 if (key && key->type == KEY_RSA1)
1323 continue;
1324@@ -1581,7 +1583,7 @@
1325 debug("Offering %s public key: %s", key_type(id->key),
1326 id->filename);
1327 sent = send_pubkey_test(authctxt, id);
1328- } else if (id->key == NULL) {
1329+ } else if (id->key == NULL && id->filename) {
1330 debug("Trying private key: %s", id->filename);
1331 id->key = load_identity_file(id->filename);
1332 if (id->key != NULL) {
1333Index: b/sshd.8
1334===================================================================
1335--- a/sshd.8
1336+++ b/sshd.8
1337@@ -945,6 +945,7 @@
1338 .Xr ssh-agent 1 ,
1339 .Xr ssh-keygen 1 ,
1340 .Xr ssh-keyscan 1 ,
1341+.Xr ssh-vulnkey 1 ,
1342 .Xr chroot 2 ,
1343 .Xr hosts_access 5 ,
1344 .Xr login.conf 5 ,
1345Index: b/sshd.c
1346===================================================================
1347--- a/sshd.c
1348+++ b/sshd.c
1349@@ -1576,6 +1576,11 @@
1350 sensitive_data.host_keys[i] = NULL;
1351 continue;
1352 }
1353+ if (auth_key_is_revoked(key, 1)) {
1354+ key_free(key);
1355+ sensitive_data.host_keys[i] = NULL;
1356+ continue;
1357+ }
1358 switch (key->type) {
1359 case KEY_RSA1:
1360 sensitive_data.ssh1_host_key = key;
1361Index: b/sshd_config.5
1362===================================================================
1363--- a/sshd_config.5
1364+++ b/sshd_config.5
1365@@ -792,6 +792,20 @@
1366 Specifies whether password authentication is allowed.
1367 The default is
1368 .Dq yes .
1369+.It Cm PermitBlacklistedKeys
1370+Specifies whether
1371+.Xr sshd 8
1372+should allow keys recorded in its blacklist of known-compromised keys (see
1373+.Xr ssh-vulnkey 1 ) .
1374+If
1375+.Dq yes ,
1376+then attempts to authenticate with compromised keys will be logged but
1377+accepted.
1378+If
1379+.Dq no ,
1380+then attempts to authenticate with compromised keys will be rejected.
1381+The default is
1382+.Dq no .
1383 .It Cm PermitEmptyPasswords
1384 When password authentication is allowed, it specifies whether the
1385 server allows login to accounts with empty password strings.