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