From 8dcc7c5ef45cf5032dca7a308ffe17d3935e62d5 Mon Sep 17 00:00:00 2001 From: Colin Watson Date: Sat, 27 Feb 2010 14:05:10 +0000 Subject: Convert to source format 3.0 (quilt). --- debian/patches/ssh-vulnkey.patch | 1376 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 1376 insertions(+) create mode 100644 debian/patches/ssh-vulnkey.patch (limited to 'debian/patches/ssh-vulnkey.patch') diff --git a/debian/patches/ssh-vulnkey.patch b/debian/patches/ssh-vulnkey.patch new file mode 100644 index 000000000..3e4e96493 --- /dev/null +++ b/debian/patches/ssh-vulnkey.patch @@ -0,0 +1,1376 @@ +Index: b/Makefile.in +=================================================================== +--- a/Makefile.in ++++ b/Makefile.in +@@ -26,6 +26,7 @@ + SFTP_SERVER=$(libexecdir)/sftp-server + SSH_KEYSIGN=$(libexecdir)/ssh-keysign + RAND_HELPER=$(libexecdir)/ssh-rand-helper ++SSH_DATADIR=$(datadir)/ssh + PRIVSEP_PATH=@PRIVSEP_PATH@ + SSH_PRIVSEP_USER=@SSH_PRIVSEP_USER@ + STRIP_OPT=@STRIP_OPT@ +@@ -37,7 +38,8 @@ + -D_PATH_SSH_KEY_SIGN=\"$(SSH_KEYSIGN)\" \ + -D_PATH_SSH_PIDDIR=\"$(piddir)\" \ + -D_PATH_PRIVSEP_CHROOT_DIR=\"$(PRIVSEP_PATH)\" \ +- -DSSH_RAND_HELPER=\"$(RAND_HELPER)\" ++ -DSSH_RAND_HELPER=\"$(RAND_HELPER)\" \ ++ -D_PATH_SSH_DATADIR=\"$(SSH_DATADIR)\" + + CC=@CC@ + LD=@LD@ +@@ -60,7 +62,7 @@ + INSTALL_SSH_PRNG_CMDS=@INSTALL_SSH_PRNG_CMDS@ + INSTALL_SSH_RAND_HELPER=@INSTALL_SSH_RAND_HELPER@ + +-TARGETS=ssh$(EXEEXT) sshd$(EXEEXT) ssh-add$(EXEEXT) ssh-keygen$(EXEEXT) ssh-keyscan${EXEEXT} ssh-keysign${EXEEXT} ssh-agent$(EXEEXT) scp$(EXEEXT) ssh-rand-helper${EXEEXT} sftp-server$(EXEEXT) sftp$(EXEEXT) ++TARGETS=ssh$(EXEEXT) sshd$(EXEEXT) ssh-add$(EXEEXT) ssh-keygen$(EXEEXT) ssh-keyscan${EXEEXT} ssh-keysign${EXEEXT} ssh-agent$(EXEEXT) scp$(EXEEXT) ssh-rand-helper${EXEEXT} sftp-server$(EXEEXT) sftp$(EXEEXT) ssh-vulnkey$(EXEEXT) + + LIBSSH_OBJS=acss.o authfd.o authfile.o bufaux.o bufbn.o buffer.o \ + canohost.o channels.o cipher.o cipher-acss.o cipher-aes.o \ +@@ -91,8 +93,8 @@ + audit.o audit-bsm.o platform.o sftp-server.o sftp-common.o \ + roaming_common.o + +-MANPAGES = moduli.5.out scp.1.out ssh-add.1.out ssh-agent.1.out ssh-keygen.1.out ssh-keyscan.1.out ssh.1.out sshd.8.out sftp-server.8.out sftp.1.out ssh-rand-helper.8.out ssh-keysign.8.out sshd_config.5.out ssh_config.5.out +-MANPAGES_IN = moduli.5 scp.1 ssh-add.1 ssh-agent.1 ssh-keygen.1 ssh-keyscan.1 ssh.1 sshd.8 sftp-server.8 sftp.1 ssh-rand-helper.8 ssh-keysign.8 sshd_config.5 ssh_config.5 ++MANPAGES = moduli.5.out scp.1.out ssh-add.1.out ssh-agent.1.out ssh-keygen.1.out ssh-keyscan.1.out ssh.1.out sshd.8.out sftp-server.8.out sftp.1.out ssh-rand-helper.8.out ssh-keysign.8.out ssh-vulnkey.1.out sshd_config.5.out ssh_config.5.out ++MANPAGES_IN = moduli.5 scp.1 ssh-add.1 ssh-agent.1 ssh-keygen.1 ssh-keyscan.1 ssh.1 sshd.8 sftp-server.8 sftp.1 ssh-rand-helper.8 ssh-keysign.8 ssh-vulnkey.1 sshd_config.5 ssh_config.5 + MANTYPE = @MANTYPE@ + + CONFIGFILES=sshd_config.out ssh_config.out moduli.out +@@ -169,6 +171,9 @@ + ssh-rand-helper${EXEEXT}: $(LIBCOMPAT) libssh.a ssh-rand-helper.o + $(LD) -o $@ ssh-rand-helper.o $(LDFLAGS) -lssh -lopenbsd-compat $(LIBS) + ++ssh-vulnkey$(EXEEXT): $(LIBCOMPAT) libssh.a ssh-vulnkey.o ++ $(LD) -o $@ ssh-vulnkey.o $(LDFLAGS) -lssh -lopenbsd-compat $(LIBS) ++ + # test driver for the loginrec code - not built by default + logintest: logintest.o $(LIBCOMPAT) libssh.a loginrec.o + $(LD) -o $@ logintest.o $(LDFLAGS) loginrec.o -lopenbsd-compat -lssh $(LIBS) +@@ -268,6 +273,7 @@ + $(INSTALL) -m 4711 $(STRIP_OPT) ssh-keysign $(DESTDIR)$(SSH_KEYSIGN) + $(INSTALL) -m 0755 $(STRIP_OPT) sftp $(DESTDIR)$(bindir)/sftp + $(INSTALL) -m 0755 $(STRIP_OPT) sftp-server $(DESTDIR)$(SFTP_SERVER) ++ $(INSTALL) -m 0755 $(STRIP_OPT) ssh-vulnkey $(DESTDIR)$(bindir)/ssh-vulnkey + $(INSTALL) -m 644 ssh.1.out $(DESTDIR)$(mandir)/$(mansubdir)1/ssh.1 + $(INSTALL) -m 644 scp.1.out $(DESTDIR)$(mandir)/$(mansubdir)1/scp.1 + $(INSTALL) -m 644 ssh-add.1.out $(DESTDIR)$(mandir)/$(mansubdir)1/ssh-add.1 +@@ -284,6 +290,7 @@ + $(INSTALL) -m 644 sftp.1.out $(DESTDIR)$(mandir)/$(mansubdir)1/sftp.1 + $(INSTALL) -m 644 sftp-server.8.out $(DESTDIR)$(mandir)/$(mansubdir)8/sftp-server.8 + $(INSTALL) -m 644 ssh-keysign.8.out $(DESTDIR)$(mandir)/$(mansubdir)8/ssh-keysign.8 ++ $(INSTALL) -m 644 ssh-vulnkey.1.out $(DESTDIR)$(mandir)/$(mansubdir)1/ssh-vulnkey.1 + -rm -f $(DESTDIR)$(bindir)/slogin + ln -s ./ssh$(EXEEXT) $(DESTDIR)$(bindir)/slogin + -rm -f $(DESTDIR)$(mandir)/$(mansubdir)1/slogin.1 +@@ -365,6 +372,7 @@ + -rm -f $(DESTDIR)$(bindir)/ssh-agent$(EXEEXT) + -rm -f $(DESTDIR)$(bindir)/ssh-keygen$(EXEEXT) + -rm -f $(DESTDIR)$(bindir)/ssh-keyscan$(EXEEXT) ++ -rm -f $(DESTDIR)$(bindir)/ssh-vulnkey$(EXEEXT) + -rm -f $(DESTDIR)$(bindir)/sftp$(EXEEXT) + -rm -f $(DESTDIR)$(sbindir)/sshd$(EXEEXT) + -rm -r $(DESTDIR)$(SFTP_SERVER)$(EXEEXT) +@@ -377,6 +385,7 @@ + -rm -f $(DESTDIR)$(mandir)/$(mansubdir)1/ssh-keygen.1 + -rm -f $(DESTDIR)$(mandir)/$(mansubdir)1/sftp.1 + -rm -f $(DESTDIR)$(mandir)/$(mansubdir)1/ssh-keyscan.1 ++ -rm -f $(DESTDIR)$(mandir)/$(mansubdir)1/ssh-vulnkey.1 + -rm -f $(DESTDIR)$(mandir)/$(mansubdir)8/sshd.8 + -rm -f $(DESTDIR)$(mandir)/$(mansubdir)8/ssh-rand-helper.8 + -rm -f $(DESTDIR)$(mandir)/$(mansubdir)8/sftp-server.8 +Index: b/auth-rh-rsa.c +=================================================================== +--- a/auth-rh-rsa.c ++++ b/auth-rh-rsa.c +@@ -44,6 +44,9 @@ + { + HostStatus host_status; + ++ if (reject_blacklisted_key(client_host_key, 0) == 1) ++ return 0; ++ + /* Check if we would accept it using rhosts authentication. */ + if (!auth_rhosts(pw, cuser)) + return 0; +Index: b/auth-rsa.c +=================================================================== +--- a/auth-rsa.c ++++ b/auth-rsa.c +@@ -246,6 +246,9 @@ + "actual %d vs. announced %d.", + file, linenum, BN_num_bits(key->rsa->n), bits); + ++ if (reject_blacklisted_key(key, 0) == 1) ++ continue; ++ + /* We have found the desired key. */ + /* + * If our options do not allow this key to be used, +Index: b/auth.c +=================================================================== +--- a/auth.c ++++ b/auth.c +@@ -59,6 +59,7 @@ + #include "servconf.h" + #include "key.h" + #include "hostfile.h" ++#include "authfile.h" + #include "auth.h" + #include "auth-options.h" + #include "canohost.h" +@@ -398,6 +399,38 @@ + return host_status; + } + ++int ++reject_blacklisted_key(Key *key, int hostkey) ++{ ++ char *fp; ++ ++ if (blacklisted_key(key, &fp) != 1) ++ return 0; ++ ++ if (options.permit_blacklisted_keys) { ++ if (hostkey) ++ error("Host key %s blacklisted (see " ++ "ssh-vulnkey(1)); continuing anyway", fp); ++ else ++ logit("Public key %s from %s blacklisted (see " ++ "ssh-vulnkey(1)); continuing anyway", ++ fp, get_remote_ipaddr()); ++ xfree(fp); ++ } else { ++ if (hostkey) ++ error("Host key %s blacklisted (see " ++ "ssh-vulnkey(1))", fp); ++ else ++ logit("Public key %s from %s blacklisted (see " ++ "ssh-vulnkey(1))", ++ fp, get_remote_ipaddr()); ++ xfree(fp); ++ return 1; ++ } ++ ++ return 0; ++} ++ + + /* + * Check a given file for security. This is defined as all components +Index: b/auth.h +=================================================================== +--- a/auth.h ++++ b/auth.h +@@ -178,6 +178,8 @@ + check_key_in_hostfiles(struct passwd *, Key *, const char *, + const char *, const char *); + ++int reject_blacklisted_key(Key *, int); ++ + /* hostkey handling */ + Key *get_hostkey_by_index(int); + Key *get_hostkey_by_type(int); +Index: b/auth2-hostbased.c +=================================================================== +--- a/auth2-hostbased.c ++++ b/auth2-hostbased.c +@@ -145,6 +145,9 @@ + HostStatus host_status; + int len; + ++ if (reject_blacklisted_key(key, 0) == 1) ++ return 0; ++ + resolvedname = get_canonical_hostname(options.use_dns); + ipaddr = get_remote_ipaddr(); + +Index: b/auth2-pubkey.c +=================================================================== +--- a/auth2-pubkey.c ++++ b/auth2-pubkey.c +@@ -254,6 +254,9 @@ + int success; + char *file; + ++ if (reject_blacklisted_key(key, 0) == 1) ++ return 0; ++ + file = authorized_keys_file(pw); + success = user_key_allowed2(pw, key, file); + xfree(file); +Index: b/authfile.c +=================================================================== +--- a/authfile.c ++++ b/authfile.c +@@ -65,6 +65,7 @@ + #include "rsa.h" + #include "misc.h" + #include "atomicio.h" ++#include "pathnames.h" + + /* Version identification string for SSH v1 identity files. */ + static const char authfile_id_string[] = +@@ -677,3 +678,140 @@ + key_free(pub); + return NULL; + } ++ ++/* Scan a blacklist of known-vulnerable keys in blacklist_file. */ ++static int ++blacklisted_key_in_file(const Key *key, const char *blacklist_file, char **fp) ++{ ++ int fd = -1; ++ char *dgst_hex = NULL; ++ char *dgst_packed = NULL, *p; ++ int i; ++ size_t line_len; ++ struct stat st; ++ char buf[256]; ++ off_t start, lower, upper; ++ int ret = 0; ++ ++ debug("Checking blacklist file %s", blacklist_file); ++ fd = open(blacklist_file, O_RDONLY); ++ if (fd < 0) { ++ ret = -1; ++ goto out; ++ } ++ ++ dgst_hex = key_fingerprint(key, SSH_FP_MD5, SSH_FP_HEX); ++ /* Remove all colons */ ++ dgst_packed = xcalloc(1, strlen(dgst_hex) + 1); ++ for (i = 0, p = dgst_packed; dgst_hex[i]; i++) ++ if (dgst_hex[i] != ':') ++ *p++ = dgst_hex[i]; ++ /* Only compare least-significant 80 bits (to keep the blacklist ++ * size down) ++ */ ++ line_len = strlen(dgst_packed + 12); ++ if (line_len > 32) ++ goto out; ++ ++ /* Skip leading comments */ ++ start = 0; ++ for (;;) { ++ ssize_t r; ++ char *newline; ++ ++ r = atomicio(read, fd, buf, sizeof(buf)); ++ if (r <= 0) ++ goto out; ++ if (buf[0] != '#') ++ break; ++ ++ newline = memchr(buf, '\n', sizeof(buf)); ++ if (!newline) ++ goto out; ++ start += newline + 1 - buf; ++ if (lseek(fd, start, SEEK_SET) < 0) ++ goto out; ++ } ++ ++ /* Initialise binary search record numbers */ ++ if (fstat(fd, &st) < 0) ++ goto out; ++ lower = 0; ++ upper = (st.st_size - start) / (line_len + 1); ++ ++ while (lower != upper) { ++ off_t cur; ++ int cmp; ++ ++ cur = lower + (upper - lower) / 2; ++ ++ /* Read this line and compare to digest; this is ++ * overflow-safe since cur < max(off_t) / (line_len + 1) */ ++ if (lseek(fd, start + cur * (line_len + 1), SEEK_SET) < 0) ++ break; ++ if (atomicio(read, fd, buf, line_len) != line_len) ++ break; ++ cmp = memcmp(buf, dgst_packed + 12, line_len); ++ if (cmp < 0) { ++ if (cur == lower) ++ break; ++ lower = cur; ++ } else if (cmp > 0) { ++ if (cur == upper) ++ break; ++ upper = cur; ++ } else { ++ debug("Found %s in blacklist", dgst_hex); ++ ret = 1; ++ break; ++ } ++ } ++ ++out: ++ if (dgst_packed) ++ xfree(dgst_packed); ++ if (ret != 1 && dgst_hex) { ++ xfree(dgst_hex); ++ dgst_hex = NULL; ++ } ++ if (fp) ++ *fp = dgst_hex; ++ if (fd >= 0) ++ close(fd); ++ return ret; ++} ++ ++/* ++ * Scan blacklists of known-vulnerable keys. If a vulnerable key is found, ++ * its fingerprint is returned in *fp, unless fp is NULL. ++ */ ++int ++blacklisted_key(const Key *key, char **fp) ++{ ++ Key *public; ++ char *blacklist_file; ++ int ret, ret2; ++ ++ public = key_demote(key); ++ if (public->type == KEY_RSA1) ++ public->type = KEY_RSA; ++ ++ xasprintf(&blacklist_file, "%s.%s-%u", ++ _PATH_BLACKLIST, key_type(public), key_size(public)); ++ ret = blacklisted_key_in_file(public, blacklist_file, fp); ++ xfree(blacklist_file); ++ if (ret > 0) { ++ key_free(public); ++ return ret; ++ } ++ ++ xasprintf(&blacklist_file, "%s.%s-%u", ++ _PATH_BLACKLIST_CONFIG, key_type(public), key_size(public)); ++ ret2 = blacklisted_key_in_file(public, blacklist_file, fp); ++ xfree(blacklist_file); ++ if (ret2 > ret) ++ ret = ret2; ++ ++ key_free(public); ++ return ret; ++} +Index: b/authfile.h +=================================================================== +--- a/authfile.h ++++ b/authfile.h +@@ -23,4 +23,6 @@ + Key *key_load_private_pem(int, int, const char *, char **); + int key_perm_ok(int, const char *); + ++int blacklisted_key(const Key *key, char **fp); ++ + #endif +Index: b/pathnames.h +=================================================================== +--- a/pathnames.h ++++ b/pathnames.h +@@ -18,6 +18,10 @@ + #define SSHDIR ETCDIR "/ssh" + #endif + ++#ifndef _PATH_SSH_DATADIR ++#define _PATH_SSH_DATADIR "/usr/share/ssh" ++#endif ++ + #ifndef _PATH_SSH_PIDDIR + #define _PATH_SSH_PIDDIR "/var/run" + #endif +@@ -43,6 +47,9 @@ + /* Backwards compatibility */ + #define _PATH_DH_PRIMES SSHDIR "/primes" + ++#define _PATH_BLACKLIST _PATH_SSH_DATADIR "/blacklist" ++#define _PATH_BLACKLIST_CONFIG SSHDIR "/blacklist" ++ + #ifndef _PATH_SSH_PROGRAM + #define _PATH_SSH_PROGRAM "/usr/bin/ssh" + #endif +Index: b/readconf.c +=================================================================== +--- a/readconf.c ++++ b/readconf.c +@@ -123,6 +123,7 @@ + oGlobalKnownHostsFile2, oUserKnownHostsFile2, oPubkeyAuthentication, + oKbdInteractiveAuthentication, oKbdInteractiveDevices, oHostKeyAlias, + oDynamicForward, oPreferredAuthentications, oHostbasedAuthentication, ++ oUseBlacklistedKeys, + oHostKeyAlgorithms, oBindAddress, oSmartcardDevice, + oClearAllForwardings, oNoHostAuthenticationForLocalhost, + oEnableSSHKeysign, oRekeyLimit, oVerifyHostKeyDNS, oConnectTimeout, +@@ -152,6 +153,7 @@ + { "passwordauthentication", oPasswordAuthentication }, + { "kbdinteractiveauthentication", oKbdInteractiveAuthentication }, + { "kbdinteractivedevices", oKbdInteractiveDevices }, ++ { "useblacklistedkeys", oUseBlacklistedKeys }, + { "rsaauthentication", oRSAAuthentication }, + { "pubkeyauthentication", oPubkeyAuthentication }, + { "dsaauthentication", oPubkeyAuthentication }, /* alias */ +@@ -459,6 +461,10 @@ + intptr = &options->challenge_response_authentication; + goto parse_flag; + ++ case oUseBlacklistedKeys: ++ intptr = &options->use_blacklisted_keys; ++ goto parse_flag; ++ + case oGssAuthentication: + intptr = &options->gss_authentication; + goto parse_flag; +@@ -1048,6 +1054,7 @@ + options->kbd_interactive_devices = NULL; + options->rhosts_rsa_authentication = -1; + options->hostbased_authentication = -1; ++ options->use_blacklisted_keys = -1; + options->batch_mode = -1; + options->check_host_ip = -1; + options->strict_host_key_checking = -1; +@@ -1150,6 +1157,8 @@ + options->rhosts_rsa_authentication = 0; + if (options->hostbased_authentication == -1) + options->hostbased_authentication = 0; ++ if (options->use_blacklisted_keys == -1) ++ options->use_blacklisted_keys = 0; + if (options->batch_mode == -1) + options->batch_mode = 0; + if (options->check_host_ip == -1) +Index: b/readconf.h +=================================================================== +--- a/readconf.h ++++ b/readconf.h +@@ -54,6 +54,7 @@ + int kbd_interactive_authentication; /* Try keyboard-interactive auth. */ + char *kbd_interactive_devices; /* Keyboard-interactive auth devices. */ + int zero_knowledge_password_authentication; /* Try jpake */ ++ int use_blacklisted_keys; /* If true, send */ + int batch_mode; /* Batch mode: do not ask for passwords. */ + int check_host_ip; /* Also keep track of keys for IP address */ + int strict_host_key_checking; /* Strict host key checking. */ +Index: b/servconf.c +=================================================================== +--- a/servconf.c ++++ b/servconf.c +@@ -99,6 +99,7 @@ + options->password_authentication = -1; + options->kbd_interactive_authentication = -1; + options->challenge_response_authentication = -1; ++ options->permit_blacklisted_keys = -1; + options->permit_empty_passwd = -1; + options->permit_user_env = -1; + options->use_login = -1; +@@ -227,6 +228,8 @@ + options->kbd_interactive_authentication = 0; + if (options->challenge_response_authentication == -1) + options->challenge_response_authentication = 1; ++ if (options->permit_blacklisted_keys == -1) ++ options->permit_blacklisted_keys = 0; + if (options->permit_empty_passwd == -1) + options->permit_empty_passwd = 0; + if (options->permit_user_env == -1) +@@ -302,7 +305,7 @@ + sListenAddress, sAddressFamily, + sPrintMotd, sPrintLastLog, sIgnoreRhosts, + sX11Forwarding, sX11DisplayOffset, sX11UseLocalhost, +- sStrictModes, sEmptyPasswd, sTCPKeepAlive, ++ sStrictModes, sPermitBlacklistedKeys, sEmptyPasswd, sTCPKeepAlive, + sPermitUserEnvironment, sUseLogin, sAllowTcpForwarding, sCompression, + sAllowUsers, sDenyUsers, sAllowGroups, sDenyGroups, + sIgnoreUserKnownHosts, sCiphers, sMacs, sProtocol, sPidFile, +@@ -410,6 +413,7 @@ + { "x11uselocalhost", sX11UseLocalhost, SSHCFG_ALL }, + { "xauthlocation", sXAuthLocation, SSHCFG_GLOBAL }, + { "strictmodes", sStrictModes, SSHCFG_GLOBAL }, ++ { "permitblacklistedkeys", sPermitBlacklistedKeys, SSHCFG_GLOBAL }, + { "permitemptypasswords", sEmptyPasswd, SSHCFG_ALL }, + { "permituserenvironment", sPermitUserEnvironment, SSHCFG_GLOBAL }, + { "uselogin", sUseLogin, SSHCFG_GLOBAL }, +@@ -976,6 +980,10 @@ + intptr = &options->tcp_keep_alive; + goto parse_flag; + ++ case sPermitBlacklistedKeys: ++ intptr = &options->permit_blacklisted_keys; ++ goto parse_flag; ++ + case sEmptyPasswd: + intptr = &options->permit_empty_passwd; + goto parse_flag; +@@ -1643,6 +1651,7 @@ + dump_cfg_fmtint(sX11UseLocalhost, o->x11_use_localhost); + dump_cfg_fmtint(sStrictModes, o->strict_modes); + dump_cfg_fmtint(sTCPKeepAlive, o->tcp_keep_alive); ++ dump_cfg_fmtint(sPermitBlacklistedKeys, o->permit_blacklisted_keys); + dump_cfg_fmtint(sEmptyPasswd, o->permit_empty_passwd); + dump_cfg_fmtint(sPermitUserEnvironment, o->permit_user_env); + dump_cfg_fmtint(sUseLogin, o->use_login); +Index: b/servconf.h +=================================================================== +--- a/servconf.h ++++ b/servconf.h +@@ -101,6 +101,7 @@ + int challenge_response_authentication; + int zero_knowledge_password_authentication; + /* If true, permit jpake auth */ ++ int permit_blacklisted_keys; /* If true, permit */ + int permit_empty_passwd; /* If false, do not permit empty + * passwords. */ + int permit_user_env; /* If true, read ~/.ssh/environment */ +Index: b/ssh-add.1 +=================================================================== +--- a/ssh-add.1 ++++ b/ssh-add.1 +@@ -75,6 +75,10 @@ + .Nm + to work. + .Pp ++Any keys recorded in the blacklist of known-compromised keys (see ++.Xr ssh-vulnkey 1 ) ++will be refused. ++.Pp + The options are as follows: + .Bl -tag -width Ds + .It Fl c +@@ -174,6 +178,7 @@ + .Xr ssh 1 , + .Xr ssh-agent 1 , + .Xr ssh-keygen 1 , ++.Xr ssh-vulnkey 1 , + .Xr sshd 8 + .Sh AUTHORS + OpenSSH is a derivative of the original and free +Index: b/ssh-add.c +=================================================================== +--- a/ssh-add.c ++++ b/ssh-add.c +@@ -139,7 +139,7 @@ + add_file(AuthenticationConnection *ac, const char *filename) + { + Key *private; +- char *comment = NULL; ++ char *comment = NULL, *fp; + char msg[1024]; + int fd, perms_ok, ret = -1; + +@@ -184,6 +184,14 @@ + "Bad passphrase, try again for %.200s: ", comment); + } + } ++ if (blacklisted_key(private, &fp) == 1) { ++ fprintf(stderr, "Public key %s blacklisted (see " ++ "ssh-vulnkey(1)); refusing to add it\n", fp); ++ xfree(fp); ++ key_free(private); ++ xfree(comment); ++ return -1; ++ } + + if (ssh_add_identity_constrained(ac, private, comment, lifetime, + confirm)) { +Index: b/ssh-keygen.1 +=================================================================== +--- a/ssh-keygen.1 ++++ b/ssh-keygen.1 +@@ -451,6 +451,7 @@ + .Xr ssh 1 , + .Xr ssh-add 1 , + .Xr ssh-agent 1 , ++.Xr ssh-vulnkey 1 , + .Xr moduli 5 , + .Xr sshd 8 + .Rs +Index: b/ssh-vulnkey.1 +=================================================================== +--- /dev/null ++++ b/ssh-vulnkey.1 +@@ -0,0 +1,242 @@ ++.\" Copyright (c) 2008 Canonical Ltd. All rights reserved. ++.\" ++.\" Redistribution and use in source and binary forms, with or without ++.\" modification, are permitted provided that the following conditions ++.\" are met: ++.\" 1. Redistributions of source code must retain the above copyright ++.\" notice, this list of conditions and the following disclaimer. ++.\" 2. Redistributions in binary form must reproduce the above copyright ++.\" notice, this list of conditions and the following disclaimer in the ++.\" documentation and/or other materials provided with the distribution. ++.\" ++.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR ++.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES ++.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. ++.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, ++.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT ++.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ++.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ++.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ++.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF ++.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++.\" ++.Dd $Mdocdate: May 12 2008 $ ++.Dt SSH-VULNKEY 1 ++.Os ++.Sh NAME ++.Nm ssh-vulnkey ++.Nd check blacklist of compromised keys ++.Sh SYNOPSIS ++.Nm ++.Op Fl q | Fl v ++.Ar file ... ++.Nm ++.Fl a ++.Sh DESCRIPTION ++.Nm ++checks a key against a blacklist of compromised keys. ++.Pp ++A substantial number of keys are known to have been generated using a broken ++version of OpenSSL distributed by Debian which failed to seed its random ++number generator correctly. ++Keys generated using these OpenSSL versions should be assumed to be ++compromised. ++This tool may be useful in checking for such keys. ++.Pp ++Keys that are compromised cannot be repaired; replacements must be generated ++using ++.Xr ssh-keygen 1 . ++Make sure to update ++.Pa authorized_keys ++files on all systems where compromised keys were permitted to authenticate. ++.Pp ++The argument list will be interpreted as a list of paths to public key files ++or ++.Pa authorized_keys ++files. ++If no suitable file is found at a given path, ++.Nm ++will append ++.Pa .pub ++and retry, in case it was given a private key file. ++If no files are given as arguments, ++.Nm ++will check ++.Pa ~/.ssh/id_rsa , ++.Pa ~/.ssh/id_dsa , ++.Pa ~/.ssh/identity , ++.Pa ~/.ssh/authorized_keys ++and ++.Pa ~/.ssh/authorized_keys2 , ++as well as the system's host keys if readable. ++.Pp ++If ++.Dq - ++is given as an argument, ++.Nm ++will read from standard input. ++This can be used to process output from ++.Xr ssh-keyscan 1 , ++for example: ++.Pp ++.Dl $ ssh-keyscan -t rsa remote.example.org | ssh-vulnkey - ++.Pp ++Unless the ++.Cm PermitBlacklistedKeys ++option is used, ++.Xr sshd 8 ++will reject attempts to authenticate with keys in the compromised list. ++.Pp ++The output from ++.Nm ++looks like this: ++.Pp ++.Bd -literal -offset indent ++/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 ++/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 ++/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 ++.Ed ++.Pp ++Each line is of the following format (any lines beginning with ++.Dq # ++should be ignored by scripts): ++.Pp ++.Dl Ar filename : Ns Ar line : Ar status : Ar type Ar size Ar fingerprint Ar comment ++.Pp ++It is important to distinguish between the possible values of ++.Ar status : ++.Pp ++.Bl -tag -width Ds ++.It COMPROMISED ++These keys are listed in a blacklist file, normally because their ++corresponding private keys are well-known. ++Replacements must be generated using ++.Xr ssh-keygen 1 . ++.It Not blacklisted ++A blacklist file exists for this key type and size, but this key is not ++listed in it. ++Unless there is some particular reason to believe otherwise, this key ++may be used safely. ++(Note that DSA keys used with the broken version of OpenSSL distributed ++by Debian may be compromised in the event that anyone captured a network ++trace, even if they were generated with a secure version of OpenSSL.) ++.It Unknown (blacklist file not installed) ++No blacklist file exists for this key type and size. ++You should find a suitable published blacklist and install it before ++deciding whether this key is safe to use. ++.El ++.Pp ++The options are as follows: ++.Bl -tag -width Ds ++.It Fl a ++Check keys of all users on the system. ++You will typically need to run ++.Nm ++as root to use this option. ++For each user, ++.Nm ++will check ++.Pa ~/.ssh/id_rsa , ++.Pa ~/.ssh/id_dsa , ++.Pa ~/.ssh/identity , ++.Pa ~/.ssh/authorized_keys ++and ++.Pa ~/.ssh/authorized_keys2 . ++It will also check the system's host keys. ++.It Fl q ++Quiet mode. ++Normally, ++.Nm ++outputs the fingerprint of each key scanned, with a description of its ++status. ++This option suppresses that output. ++.It Fl v ++Verbose mode. ++Normally, ++.Nm ++does not output anything for keys that are not listed in their corresponding ++blacklist file (although it still produces output for keys for which there ++is no blacklist file, since their status is unknown). ++This option causes ++.Nm ++to produce output for all keys. ++.El ++.Sh EXIT STATUS ++.Nm ++will exit zero if any of the given keys were in the compromised list, ++otherwise non-zero. ++.Sh BLACKLIST FILE FORMAT ++The blacklist file may start with comments, on lines starting with ++.Dq # . ++After these initial comments, it must follow a strict format: ++.Pp ++.Bl -bullet -offset indent -compact ++.It ++All the lines must be exactly the same length (20 characters followed by a ++newline) and must be in sorted order. ++.It ++Each line must consist of the lower-case hexadecimal MD5 key fingerprint, ++without colons, and with the first 12 characters removed (that is, the least ++significant 80 bits of the fingerprint). ++.El ++.Pp ++The key fingerprint may be generated using ++.Xr ssh-keygen 1 : ++.Pp ++.Dl $ ssh-keygen -l -f /path/to/key ++.Pp ++This strict format is necessary to allow the blacklist file to be checked ++quickly, using a binary-search algorithm. ++.Sh FILES ++.Bl -tag -width Ds ++.It Pa ~/.ssh/id_rsa ++If present, contains the protocol version 2 RSA authentication identity of ++the user. ++.It Pa ~/.ssh/id_dsa ++If present, contains the protocol version 2 DSA authentication identity of ++the user. ++.It Pa ~/.ssh/identity ++If present, contains the protocol version 1 RSA authentication identity of ++the user. ++.It Pa ~/.ssh/authorized_keys ++If present, lists the public keys (RSA/DSA) that can be used for logging in ++as this user. ++.It Pa ~/.ssh/authorized_keys2 ++Obsolete name for ++.Pa ~/.ssh/authorized_keys . ++This file may still be present on some old systems, but should not be ++created if it is missing. ++.It Pa /etc/ssh/ssh_host_rsa_key ++If present, contains the protocol version 2 RSA identity of the system. ++.It Pa /etc/ssh/ssh_host_dsa_key ++If present, contains the protocol version 2 DSA identity of the system. ++.It Pa /etc/ssh/ssh_host_key ++If present, contains the protocol version 1 RSA identity of the system. ++.It Pa /usr/share/ssh/blacklist. Ns Ar TYPE Ns Pa - Ns Ar LENGTH ++If present, lists the blacklisted keys of type ++.Ar TYPE ++.Pf ( Dq RSA ++or ++.Dq DSA ) ++and bit length ++.Ar LENGTH . ++The format of this file is described above. ++RSA1 keys are converted to RSA before being checked in the blacklist. ++Note that the fingerprints of RSA1 keys are computed differently, so you ++will not be able to find them in the blacklist by hand. ++.It Pa /etc/ssh/blacklist. Ns Ar TYPE Ns Pa - Ns Ar LENGTH ++Same as ++.Pa /usr/share/ssh/blacklist. Ns Ar TYPE Ns Pa - Ns Ar LENGTH , ++but may be edited by the system administrator to add new blacklist entries. ++.El ++.Sh SEE ALSO ++.Xr ssh-keygen 1 , ++.Xr sshd 8 ++.Sh AUTHORS ++.An -nosplit ++.An Colin Watson Aq cjwatson@ubuntu.com ++.Pp ++Florian Weimer suggested the option to check keys of all users, and the idea ++of processing ++.Xr ssh-keyscan 1 ++output. +Index: b/ssh-vulnkey.c +=================================================================== +--- /dev/null ++++ b/ssh-vulnkey.c +@@ -0,0 +1,388 @@ ++/* ++ * Copyright (c) 2008 Canonical Ltd. All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * 1. Redistributions of source code must retain the above copyright ++ * notice, this list of conditions and the following disclaimer. ++ * 2. Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES ++ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. ++ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, ++ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT ++ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ++ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ++ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ++ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF ++ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ */ ++ ++#include "includes.h" ++ ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#include "xmalloc.h" ++#include "ssh.h" ++#include "log.h" ++#include "key.h" ++#include "authfile.h" ++#include "pathnames.h" ++#include "uidswap.h" ++#include "misc.h" ++ ++extern char *__progname; ++ ++/* Default files to check */ ++static char *default_host_files[] = { ++ _PATH_HOST_RSA_KEY_FILE, ++ _PATH_HOST_DSA_KEY_FILE, ++ _PATH_HOST_KEY_FILE, ++ NULL ++}; ++static char *default_files[] = { ++ _PATH_SSH_CLIENT_ID_RSA, ++ _PATH_SSH_CLIENT_ID_DSA, ++ _PATH_SSH_CLIENT_IDENTITY, ++ _PATH_SSH_USER_PERMITTED_KEYS, ++ _PATH_SSH_USER_PERMITTED_KEYS2, ++ NULL ++}; ++ ++static int verbosity = 0; ++ ++static int some_keys = 0; ++static int some_unknown = 0; ++static int some_compromised = 0; ++ ++static void ++usage(void) ++{ ++ fprintf(stderr, "usage: %s [-aqv] [file ...]\n", __progname); ++ fprintf(stderr, "Options:\n"); ++ fprintf(stderr, " -a Check keys of all users.\n"); ++ fprintf(stderr, " -q Quiet mode.\n"); ++ fprintf(stderr, " -v Verbose mode.\n"); ++ exit(1); ++} ++ ++void ++describe_key(const char *filename, u_long linenum, const char *msg, ++ const Key *key, const char *comment, int min_verbosity) ++{ ++ char *fp; ++ ++ fp = key_fingerprint(key, SSH_FP_MD5, SSH_FP_HEX); ++ if (verbosity >= min_verbosity) { ++ if (strchr(filename, ':')) ++ printf("\"%s\"", filename); ++ else ++ printf("%s", filename); ++ printf(":%lu: %s: %s %u %s %s\n", linenum, msg, ++ key_type(key), key_size(key), fp, comment); ++ } ++ xfree(fp); ++} ++ ++int ++do_key(const char *filename, u_long linenum, ++ const Key *key, const char *comment) ++{ ++ Key *public; ++ int blacklist_status; ++ int ret = 1; ++ ++ some_keys = 1; ++ ++ public = key_demote(key); ++ if (public->type == KEY_RSA1) ++ public->type = KEY_RSA; ++ ++ blacklist_status = blacklisted_key(public, NULL); ++ if (blacklist_status == -1) { ++ describe_key(filename, linenum, ++ "Unknown (blacklist file not installed)", key, comment, 0); ++ some_unknown = 1; ++ } else if (blacklist_status == 1) { ++ describe_key(filename, linenum, ++ "COMPROMISED", key, comment, 0); ++ some_compromised = 1; ++ ret = 0; ++ } else ++ describe_key(filename, linenum, ++ "Not blacklisted", key, comment, 1); ++ ++ key_free(public); ++ ++ return ret; ++} ++ ++int ++do_filename(const char *filename, int quiet_open) ++{ ++ FILE *f; ++ char line[SSH_MAX_PUBKEY_BYTES]; ++ char *cp; ++ u_long linenum = 0; ++ Key *key; ++ char *comment = NULL; ++ int found = 0, ret = 1; ++ ++ /* Copy much of key_load_public's logic here so that we can read ++ * several keys from a single file (e.g. authorized_keys). ++ */ ++ ++ if (strcmp(filename, "-") != 0) { ++ int save_errno; ++ f = fopen(filename, "r"); ++ save_errno = errno; ++ if (!f) { ++ char pubfile[MAXPATHLEN]; ++ if (strlcpy(pubfile, filename, sizeof pubfile) < ++ sizeof(pubfile) && ++ strlcat(pubfile, ".pub", sizeof pubfile) < ++ sizeof(pubfile)) ++ f = fopen(pubfile, "r"); ++ } ++ errno = save_errno; /* earlier errno is more useful */ ++ if (!f) { ++ if (!quiet_open) ++ perror(filename); ++ return -1; ++ } ++ if (verbosity > 0) ++ printf("# %s\n", filename); ++ } else ++ f = stdin; ++ while (read_keyfile_line(f, filename, line, sizeof(line), ++ &linenum) != -1) { ++ int i; ++ char *space; ++ int type; ++ char *end; ++ ++ /* Chop trailing newline. */ ++ i = strlen(line) - 1; ++ if (line[i] == '\n') ++ line[i] = '\0'; ++ ++ /* Skip leading whitespace, empty and comment lines. */ ++ for (cp = line; *cp == ' ' || *cp == '\t'; cp++) ++ ; ++ if (!*cp || *cp == '\n' || *cp == '#') ++ continue; ++ ++ /* Cope with ssh-keyscan output and options in ++ * authorized_keys files. ++ */ ++ space = strchr(cp, ' '); ++ if (!space) ++ continue; ++ *space = '\0'; ++ type = key_type_from_name(cp); ++ *space = ' '; ++ /* Leading number (RSA1) or valid type (RSA/DSA) indicates ++ * that we have no host name or options to skip. ++ */ ++ if ((strtol(cp, &end, 10) == 0 || *end != ' ') && ++ type == KEY_UNSPEC) { ++ int quoted = 0; ++ ++ for (; *cp && (quoted || (*cp != ' ' && *cp != '\t')); cp++) { ++ if (*cp == '\\' && cp[1] == '"') ++ cp++; /* Skip both */ ++ else if (*cp == '"') ++ quoted = !quoted; ++ } ++ /* Skip remaining whitespace. */ ++ for (; *cp == ' ' || *cp == '\t'; cp++) ++ ; ++ if (!*cp) ++ continue; ++ } ++ ++ /* Read and process the key itself. */ ++ key = key_new(KEY_RSA1); ++ if (key_read(key, &cp) == 1) { ++ while (*cp == ' ' || *cp == '\t') ++ cp++; ++ if (!do_key(filename, linenum, ++ key, *cp ? cp : filename)) ++ ret = 0; ++ found = 1; ++ } else { ++ key_free(key); ++ key = key_new(KEY_UNSPEC); ++ if (key_read(key, &cp) == 1) { ++ while (*cp == ' ' || *cp == '\t') ++ cp++; ++ if (!do_key(filename, linenum, ++ key, *cp ? cp : filename)) ++ ret = 0; ++ found = 1; ++ } ++ } ++ key_free(key); ++ } ++ if (f != stdin) ++ fclose(f); ++ ++ if (!found && filename) { ++ key = key_load_public(filename, &comment); ++ if (key) { ++ if (!do_key(filename, 1, key, comment)) ++ ret = 0; ++ found = 1; ++ } ++ if (comment) ++ xfree(comment); ++ } ++ ++ return ret; ++} ++ ++int ++do_host(int quiet_open) ++{ ++ int i; ++ struct stat st; ++ int ret = 1; ++ ++ for (i = 0; default_host_files[i]; i++) { ++ if (stat(default_host_files[i], &st) < 0 && errno == ENOENT) ++ continue; ++ if (!do_filename(default_host_files[i], quiet_open)) ++ ret = 0; ++ } ++ ++ return ret; ++} ++ ++int ++do_user(const char *dir) ++{ ++ int i; ++ char *file; ++ struct stat st; ++ int ret = 1; ++ ++ for (i = 0; default_files[i]; i++) { ++ xasprintf(&file, "%s/%s", dir, default_files[i]); ++ if (stat(file, &st) < 0 && errno == ENOENT) { ++ xfree(file); ++ continue; ++ } ++ if (!do_filename(file, 0)) ++ ret = 0; ++ xfree(file); ++ } ++ ++ return ret; ++} ++ ++int ++main(int argc, char **argv) ++{ ++ int opt, all_users = 0; ++ int ret = 1; ++ extern int optind; ++ ++ /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */ ++ sanitise_stdfd(); ++ ++ __progname = ssh_get_progname(argv[0]); ++ ++ SSLeay_add_all_algorithms(); ++ log_init(argv[0], SYSLOG_LEVEL_INFO, SYSLOG_FACILITY_USER, 1); ++ ++ /* We don't need the RNG ourselves, but symbol references here allow ++ * ld to link us properly. ++ */ ++ init_rng(); ++ seed_rng(); ++ ++ while ((opt = getopt(argc, argv, "ahqv")) != -1) { ++ switch (opt) { ++ case 'a': ++ all_users = 1; ++ break; ++ case 'q': ++ verbosity--; ++ break; ++ case 'v': ++ verbosity++; ++ break; ++ case 'h': ++ default: ++ usage(); ++ } ++ } ++ ++ if (all_users) { ++ struct passwd *pw; ++ ++ if (!do_host(0)) ++ ret = 0; ++ ++ while ((pw = getpwent()) != NULL) { ++ if (pw->pw_dir) { ++ temporarily_use_uid(pw); ++ if (!do_user(pw->pw_dir)) ++ ret = 0; ++ restore_uid(); ++ } ++ } ++ } else if (optind == argc) { ++ struct passwd *pw; ++ ++ if (!do_host(1)) ++ ret = 0; ++ ++ if ((pw = getpwuid(geteuid())) == NULL) ++ fprintf(stderr, "No user found with uid %u\n", ++ (u_int)geteuid()); ++ else { ++ if (!do_user(pw->pw_dir)) ++ ret = 0; ++ } ++ } else { ++ while (optind < argc) ++ if (!do_filename(argv[optind++], 0)) ++ ret = 0; ++ } ++ ++ if (verbosity >= 0) { ++ if (some_unknown) { ++ printf("#\n"); ++ printf("# The status of some keys on your system is unknown.\n"); ++ printf("# You may need to install additional blacklist files.\n"); ++ } ++ if (some_compromised) { ++ printf("#\n"); ++ printf("# Some keys on your system have been compromised!\n"); ++ printf("# You must replace them using ssh-keygen(1).\n"); ++ } ++ if (some_unknown || some_compromised) { ++ printf("#\n"); ++ printf("# See the ssh-vulnkey(1) manual page for further advice.\n"); ++ } else if (some_keys && verbosity > 0) { ++ printf("#\n"); ++ printf("# No blacklisted keys!\n"); ++ } ++ } ++ ++ return ret; ++} +Index: b/ssh.1 +=================================================================== +--- a/ssh.1 ++++ b/ssh.1 +@@ -1396,6 +1396,7 @@ + .Xr ssh-agent 1 , + .Xr ssh-keygen 1 , + .Xr ssh-keyscan 1 , ++.Xr ssh-vulnkey 1 , + .Xr tun 4 , + .Xr hosts.equiv 5 , + .Xr ssh_config 5 , +Index: b/ssh.c +=================================================================== +--- a/ssh.c ++++ b/ssh.c +@@ -1229,7 +1229,7 @@ + static void + load_public_identity_files(void) + { +- char *filename, *cp, thishost[NI_MAXHOST]; ++ char *filename, *cp, thishost[NI_MAXHOST], *fp; + char *pwdir = NULL, *pwname = NULL; + int i = 0; + Key *public; +@@ -1276,6 +1276,22 @@ + public = key_load_public(filename, NULL); + debug("identity file %s type %d", filename, + public ? public->type : -1); ++ if (public && blacklisted_key(public, &fp) == 1) { ++ if (options.use_blacklisted_keys) ++ logit("Public key %s blacklisted (see " ++ "ssh-vulnkey(1)); continuing anyway", fp); ++ else ++ logit("Public key %s blacklisted (see " ++ "ssh-vulnkey(1)); refusing to send it", ++ fp); ++ xfree(fp); ++ if (!options.use_blacklisted_keys) { ++ key_free(public); ++ xfree(filename); ++ filename = NULL; ++ public = NULL; ++ } ++ } + xfree(options.identity_files[i]); + options.identity_files[i] = filename; + options.identity_keys[i] = public; +Index: b/ssh_config.5 +=================================================================== +--- a/ssh_config.5 ++++ b/ssh_config.5 +@@ -1041,6 +1041,23 @@ + .Dq any . + The default is + .Dq any:any . ++.It Cm UseBlacklistedKeys ++Specifies whether ++.Xr ssh 1 ++should use keys recorded in its blacklist of known-compromised keys (see ++.Xr ssh-vulnkey 1 ) ++for authentication. ++If ++.Dq yes , ++then attempts to use compromised keys for authentication will be logged but ++accepted. ++It is strongly recommended that this be used only to install new authorized ++keys on the remote system, and even then only with the utmost care. ++If ++.Dq no , ++then attempts to use compromised keys for authentication will be prevented. ++The default is ++.Dq no . + .It Cm UsePrivilegedPort + Specifies whether to use a privileged port for outgoing connections. + The argument must be +Index: b/sshconnect2.c +=================================================================== +--- a/sshconnect2.c ++++ b/sshconnect2.c +@@ -1418,6 +1418,8 @@ + + /* list of keys stored in the filesystem */ + for (i = 0; i < options.num_identity_files; i++) { ++ if (options.identity_files[i] == NULL) ++ continue; + key = options.identity_keys[i]; + if (key && key->type == KEY_RSA1) + continue; +@@ -1508,7 +1510,7 @@ + if (id->key && id->key->type != KEY_RSA1) { + debug("Offering public key: %s", id->filename); + sent = send_pubkey_test(authctxt, id); +- } else if (id->key == NULL) { ++ } else if (id->key == NULL && id->filename) { + debug("Trying private key: %s", id->filename); + id->key = load_identity_file(id->filename); + if (id->key != NULL) { +Index: b/sshd.8 +=================================================================== +--- a/sshd.8 ++++ b/sshd.8 +@@ -871,6 +871,7 @@ + .Xr ssh-agent 1 , + .Xr ssh-keygen 1 , + .Xr ssh-keyscan 1 , ++.Xr ssh-vulnkey 1 , + .Xr chroot 2 , + .Xr hosts_access 5 , + .Xr login.conf 5 , +Index: b/sshd.c +=================================================================== +--- a/sshd.c ++++ b/sshd.c +@@ -1518,6 +1518,11 @@ + sensitive_data.host_keys[i] = NULL; + continue; + } ++ if (reject_blacklisted_key(key, 1) == 1) { ++ key_free(key); ++ sensitive_data.host_keys[i] = NULL; ++ continue; ++ } + switch (key->type) { + case KEY_RSA1: + sensitive_data.ssh1_host_key = key; +Index: b/sshd_config.5 +=================================================================== +--- a/sshd_config.5 ++++ b/sshd_config.5 +@@ -685,6 +685,20 @@ + Specifies whether password authentication is allowed. + The default is + .Dq yes . ++.It Cm PermitBlacklistedKeys ++Specifies whether ++.Xr sshd 8 ++should allow keys recorded in its blacklist of known-compromised keys (see ++.Xr ssh-vulnkey 1 ) . ++If ++.Dq yes , ++then attempts to authenticate with compromised keys will be logged but ++accepted. ++If ++.Dq no , ++then attempts to authenticate with compromised keys will be rejected. ++The default is ++.Dq no . + .It Cm PermitEmptyPasswords + When password authentication is allowed, it specifies whether the + server allows login to accounts with empty password strings. -- cgit v1.2.3