[Midnightbsd-cvs] src: crypto/openssh: add the vulnkey util

laffer1 at midnightbsd.org laffer1 at midnightbsd.org
Fri May 16 16:19:20 EDT 2008


Log Message:
-----------
add the vulnkey util

Added Files:
-----------
    src/crypto/openssh:
        ssh-vulnkey.1 (r1.1)
        ssh-vulnkey.c (r1.1)

-------------- next part --------------
--- /dev/null
+++ crypto/openssh/ssh-vulnkey.1
@@ -0,0 +1,187 @@
+.\" 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
+.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
+.Nm
+will exit zero if any of the given keys were in the compromised list,
+otherwise non-zero.
+.Pp
+Unless the
+.Cm PermitBlacklistedKeys
+option is used,
+.Xr sshd 8
+will reject attempts to authenticate with keys in the compromised list.
+.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.
+.El
+.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 /etc/ssh/blacklist. Ns Ar TYPE Ns Pa - Ns Ar LENGTH
+If present, lists the blacklisted keys of type
+.Ar TYPE
+.Pf ( Dq RSA1 ,
+.Dq RSA ,
+or
+.Dq DSA )
+and bit length
+.Ar LENGTH .
+The format of this file is described above.
+.El
+.Sh SEE ALSO
+.Xr ssh-keygen 1 ,
+.Xr sshd 8
+.Sh AUTHORS
+.An -nosplit
+.An Colin Watson Aq cjwatson at ubuntu.com
+.Pp
+Florian Weimer suggested the option to check keys of all users, and the idea
+of processing
+.Xr ssh-keyscan 1
+output.
--- /dev/null
+++ crypto/openssh/ssh-vulnkey.c
@@ -0,0 +1,325 @@
+/*
+ * 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 <sys/types.h>
+#include <sys/stat.h>
+
+#include <string.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <openssl/evp.h>
+
+#include "xmalloc.h"
+#include "ssh.h"
+#include "log.h"
+#include "key.h"
+#include "authfile.h"
+#include "pathnames.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 quiet = 0;
+
+static void
+usage(void)
+{
+	fprintf(stderr, "usage: %s [-aq] [file ...]\n", __progname);
+	fprintf(stderr, "Options:\n");
+	fprintf(stderr, "  -a          Check keys of all users.\n");
+	fprintf(stderr, "  -q          Quiet mode.\n");
+	exit(1);
+}
+
+void
+describe_key(const char *msg, const Key *key, const char *comment)
+{
+	char *fp;
+
+	fp = key_fingerprint(key, SSH_FP_MD5, SSH_FP_HEX);
+	if (!quiet)
+		printf("%s: %u %s %s\n", msg, key_size(key), fp, comment);
+	xfree(fp);
+}
+
+int
+do_key(const Key *key, const char *comment)
+{
+	char *blacklist_file;
+	struct stat st;
+	int ret = 1;
+
+	blacklist_file = blacklist_filename(key);
+	if (stat(blacklist_file, &st) < 0)
+		describe_key("Unknown (no blacklist information)",
+		    key, comment);
+	else if (blacklisted_key(key)) {
+		describe_key("COMPROMISED", key, comment);
+		ret = 0;
+	} else
+		describe_key("Not blacklisted", key, comment);
+	xfree(blacklist_file);
+
+	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) {
+		f = fopen(filename, "r");
+		if (!f) {
+			char pubfile[MAXPATHLEN];
+			if (strlcpy(pubfile, filename, sizeof pubfile) <
+			    sizeof(pubfile) &&
+			    strlcat(pubfile, ".pub", sizeof pubfile) <
+			    sizeof(pubfile))
+				f = fopen(pubfile, "r");
+		}
+		if (!f) {
+			if (!quiet_open)
+				perror(filename);
+			return -1;
+		}
+	} else
+		f = stdin;
+	while (read_keyfile_line(f, filename, line, sizeof(line),
+		    &linenum) != -1) {
+		int i;
+		char *space;
+		int type;
+
+		/* 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 (atoi(cp) == 0 && 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(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(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(key, comment))
+				ret = 0;
+			found = 1;
+		}
+		if (comment)
+			xfree(comment);
+	}
+
+	return ret;
+}
+
+int
+do_host(void)
+{
+	int i;
+	struct stat st;
+	int ret = 1;
+
+	for (i = 0; default_host_files[i]; i++) {
+		if (stat(default_host_files[i], &st) < 0)
+			continue;
+		if (!do_filename(default_host_files[i], 1))
+			ret = 0;
+	}
+
+	return ret;
+}
+
+int
+do_user(const char *dir)
+{
+	int i;
+	char buf[MAXPATHLEN];
+	struct stat st;
+	int ret = 1;
+
+	for (i = 0; default_files[i]; i++) {
+		snprintf(buf, sizeof(buf), "%s/%s", dir, default_files[i]);
+		if (stat(buf, &st) < 0)
+			continue;
+		if (!do_filename(buf, 0))
+			ret = 0;
+	}
+
+	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, "ahq")) != -1) {
+		switch (opt) {
+		case 'a':
+			all_users = 1;
+			break;
+		case 'q':
+			quiet = 1;
+			break;
+		case 'h':
+		default:
+			usage();
+		}
+	}
+
+	if (all_users) {
+		struct passwd *pw;
+
+		if (!do_host())
+			ret = 0;
+
+		while ((pw = getpwent()) != NULL) {
+			if (pw->pw_dir) {
+				if (!do_user(pw->pw_dir))
+					ret = 0;
+			}
+		}
+	} else if (optind == argc) {
+		struct passwd *pw;
+
+		if (!do_host())
+			ret = 0;
+
+		if ((pw = getpwuid(getuid())) == NULL)
+			fprintf(stderr, "No user found with uid %u\n",
+			    (u_int)getuid());
+		else {
+			if (!do_user(pw->pw_dir))
+				ret = 0;
+		}
+	} else {
+		while (optind < argc)
+			if (!do_filename(argv[optind++], 0))
+				ret = 0;
+	}
+
+	return ret;
+}


More information about the Midnightbsd-cvs mailing list