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