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