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