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