2 * Copyright (c) 2008 Canonical Ltd. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
14 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
15 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
16 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
17 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
18 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
19 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
20 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
22 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 #include <sys/types.h>
36 #include <openssl/evp.h>
43 #include "pathnames.h"
47 extern char *__progname;
49 /* Default files to check */
50 static char *default_host_files[] = {
51 _PATH_HOST_RSA_KEY_FILE,
52 _PATH_HOST_DSA_KEY_FILE,
56 static char *default_files[] = {
57 _PATH_SSH_CLIENT_ID_RSA,
58 _PATH_SSH_CLIENT_ID_DSA,
59 _PATH_SSH_CLIENT_IDENTITY,
60 _PATH_SSH_USER_PERMITTED_KEYS,
61 _PATH_SSH_USER_PERMITTED_KEYS2,
65 static int verbosity = 0;
67 static int some_keys = 0;
68 static int some_unknown = 0;
69 static int some_compromised = 0;
74 fprintf(stderr, "usage: %s [-aqv] [file ...]\n", __progname);
75 fprintf(stderr, "Options:\n");
76 fprintf(stderr, " -a Check keys of all users.\n");
77 fprintf(stderr, " -q Quiet mode.\n");
78 fprintf(stderr, " -v Verbose mode.\n");
83 describe_key(const char *filename, u_long linenum, const char *msg,
84 Key *key, const char *comment, int min_verbosity)
88 fp = key_fingerprint(key, SSH_FP_MD5, SSH_FP_HEX);
89 if (verbosity >= min_verbosity) {
90 if (strchr(filename, ':'))
91 printf("\"%s\"", filename);
93 printf("%s", filename);
94 printf(":%lu: %s: %s %u %s %s\n", linenum, msg,
95 key_type(key), key_size(key), fp, comment);
101 do_key(const char *filename, u_long linenum,
102 Key *key, const char *comment)
105 int blacklist_status;
110 public = key_demote(key);
111 if (public->type == KEY_RSA1)
112 public->type = KEY_RSA;
114 blacklist_status = blacklisted_key(public, NULL);
115 if (blacklist_status == -1) {
116 describe_key(filename, linenum,
117 "Unknown (blacklist file not installed)", key, comment, 0);
119 } else if (blacklist_status == 1) {
120 describe_key(filename, linenum,
121 "COMPROMISED", key, comment, 0);
122 some_compromised = 1;
125 describe_key(filename, linenum,
126 "Not blacklisted", key, comment, 1);
134 do_filename(const char *filename, int quiet_open)
137 char line[SSH_MAX_PUBKEY_BYTES];
141 char *comment = NULL;
142 int found = 0, ret = 1;
144 /* Copy much of key_load_public's logic here so that we can read
145 * several keys from a single file (e.g. authorized_keys).
148 if (strcmp(filename, "-") != 0) {
150 f = fopen(filename, "r");
153 char pubfile[MAXPATHLEN];
154 if (strlcpy(pubfile, filename, sizeof pubfile) <
156 strlcat(pubfile, ".pub", sizeof pubfile) <
158 f = fopen(pubfile, "r");
160 errno = save_errno; /* earlier errno is more useful */
167 printf("# %s\n", filename);
170 while (read_keyfile_line(f, filename, line, sizeof(line),
177 /* Chop trailing newline. */
178 i = strlen(line) - 1;
182 /* Skip leading whitespace, empty and comment lines. */
183 for (cp = line; *cp == ' ' || *cp == '\t'; cp++)
185 if (!*cp || *cp == '\n' || *cp == '#')
188 /* Cope with ssh-keyscan output and options in
189 * authorized_keys files.
191 space = strchr(cp, ' ');
195 type = key_type_from_name(cp);
197 /* Leading number (RSA1) or valid type (RSA/DSA) indicates
198 * that we have no host name or options to skip.
200 if ((strtol(cp, &end, 10) == 0 || *end != ' ') &&
201 type == KEY_UNSPEC) {
204 for (; *cp && (quoted || (*cp != ' ' && *cp != '\t')); cp++) {
205 if (*cp == '\\' && cp[1] == '"')
206 cp++; /* Skip both */
210 /* Skip remaining whitespace. */
211 for (; *cp == ' ' || *cp == '\t'; cp++)
217 /* Read and process the key itself. */
218 key = key_new(KEY_RSA1);
219 if (key_read(key, &cp) == 1) {
220 while (*cp == ' ' || *cp == '\t')
222 if (!do_key(filename, linenum,
223 key, *cp ? cp : filename))
228 key = key_new(KEY_UNSPEC);
229 if (key_read(key, &cp) == 1) {
230 while (*cp == ' ' || *cp == '\t')
232 if (!do_key(filename, linenum,
233 key, *cp ? cp : filename))
243 if (!found && filename) {
244 key = key_load_public(filename, &comment);
246 if (!do_key(filename, 1, key, comment))
258 do_host(int quiet_open)
264 for (i = 0; default_host_files[i]; i++) {
265 if (stat(default_host_files[i], &st) < 0 && errno == ENOENT)
267 if (!do_filename(default_host_files[i], quiet_open))
275 do_user(const char *dir)
282 for (i = 0; default_files[i]; i++) {
283 xasprintf(&file, "%s/%s", dir, default_files[i]);
284 if (stat(file, &st) < 0 && errno == ENOENT) {
288 if (!do_filename(file, 0))
297 main(int argc, char **argv)
299 int opt, all_users = 0;
303 /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
306 __progname = ssh_get_progname(argv[0]);
308 SSLeay_add_all_algorithms();
309 log_init(argv[0], SYSLOG_LEVEL_INFO, SYSLOG_FACILITY_USER, 1);
311 /* We don't need the RNG ourselves, but symbol references here allow
312 * ld to link us properly.
316 while ((opt = getopt(argc, argv, "ahqv")) != -1) {
339 while ((pw = getpwent()) != NULL) {
341 temporarily_use_uid(pw);
342 if (!do_user(pw->pw_dir))
347 } else if (optind == argc) {
353 if ((pw = getpwuid(geteuid())) == NULL)
354 fprintf(stderr, "No user found with uid %u\n",
357 if (!do_user(pw->pw_dir))
361 while (optind < argc)
362 if (!do_filename(argv[optind++], 0))
366 if (verbosity >= 0) {
369 printf("# The status of some keys on your system is unknown.\n");
370 printf("# You may need to install additional blacklist files.\n");
372 if (some_compromised) {
374 printf("# Some keys on your system have been compromised!\n");
375 printf("# You must replace them using ssh-keygen(1).\n");
377 if (some_unknown || some_compromised) {
379 printf("# See the ssh-vulnkey(1) manual page for further advice.\n");
380 } else if (some_keys && verbosity > 0) {
382 printf("# No blacklisted keys!\n");