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>
35 #include <openssl/evp.h>
42 #include "pathnames.h"
45 extern char *__progname;
47 /* Default files to check */
48 static char *default_host_files[] = {
49 _PATH_HOST_RSA_KEY_FILE,
50 _PATH_HOST_DSA_KEY_FILE,
54 static char *default_files[] = {
55 _PATH_SSH_CLIENT_ID_RSA,
56 _PATH_SSH_CLIENT_ID_DSA,
57 _PATH_SSH_CLIENT_IDENTITY,
58 _PATH_SSH_USER_PERMITTED_KEYS,
59 _PATH_SSH_USER_PERMITTED_KEYS2,
68 fprintf(stderr, "usage: %s [-aq] [file ...]\n", __progname);
69 fprintf(stderr, "Options:\n");
70 fprintf(stderr, " -a Check keys of all users.\n");
71 fprintf(stderr, " -q Quiet mode.\n");
76 describe_key(const char *msg, Key *key, const char *comment)
80 fp = key_fingerprint(key, SSH_FP_MD5, SSH_FP_HEX);
82 printf("%s: %u %s %s\n", msg, key_size(key), fp, comment);
87 do_key(Key *key, const char *comment)
93 blacklist_file = blacklist_filename(key);
94 if (stat(blacklist_file, &st) < 0)
95 describe_key("Unknown (no blacklist information)",
97 else if (blacklisted_key(key)) {
98 describe_key("COMPROMISED", key, comment);
101 describe_key("Not blacklisted", key, comment);
102 xfree(blacklist_file);
108 do_filename(const char *filename, int quiet_open)
111 char line[SSH_MAX_PUBKEY_BYTES];
115 char *comment = NULL;
116 int found = 0, ret = 1;
118 /* Copy much of key_load_public's logic here so that we can read
119 * several keys from a single file (e.g. authorized_keys).
122 if (strcmp(filename, "-") != 0) {
123 f = fopen(filename, "r");
125 char pubfile[MAXPATHLEN];
126 if (strlcpy(pubfile, filename, sizeof pubfile) <
128 strlcat(pubfile, ".pub", sizeof pubfile) <
130 f = fopen(pubfile, "r");
139 while (read_keyfile_line(f, filename, line, sizeof(line),
145 /* Chop trailing newline. */
146 i = strlen(line) - 1;
150 /* Skip leading whitespace, empty and comment lines. */
151 for (cp = line; *cp == ' ' || *cp == '\t'; cp++)
153 if (!*cp || *cp == '\n' || *cp == '#')
156 /* Cope with ssh-keyscan output and options in
157 * authorized_keys files.
159 space = strchr(cp, ' ');
163 type = key_type_from_name(cp);
165 /* Leading number (RSA1) or valid type (RSA/DSA) indicates
166 * that we have no host name or options to skip.
168 if (atoi(cp) == 0 && type == KEY_UNSPEC) {
171 for (; *cp && (quoted || (*cp != ' ' && *cp != '\t')); cp++) {
172 if (*cp == '\\' && cp[1] == '"')
173 cp++; /* Skip both */
177 /* Skip remaining whitespace. */
178 for (; *cp == ' ' || *cp == '\t'; cp++)
184 /* Read and process the key itself. */
185 key = key_new(KEY_RSA1);
186 if (key_read(key, &cp) == 1) {
187 while (*cp == ' ' || *cp == '\t')
189 if (!do_key(key, *cp ? cp : filename))
194 key = key_new(KEY_UNSPEC);
195 if (key_read(key, &cp) == 1) {
196 while (*cp == ' ' || *cp == '\t')
198 if (!do_key(key, *cp ? cp : filename))
208 if (!found && filename) {
209 key = key_load_public(filename, &comment);
211 if (!do_key(key, comment))
229 for (i = 0; default_host_files[i]; i++) {
230 if (stat(default_host_files[i], &st) < 0)
232 if (!do_filename(default_host_files[i], 1))
240 do_user(const char *dir)
243 char buf[MAXPATHLEN];
247 for (i = 0; default_files[i]; i++) {
248 snprintf(buf, sizeof(buf), "%s/%s", dir, default_files[i]);
249 if (stat(buf, &st) < 0)
251 if (!do_filename(buf, 0))
259 main(int argc, char **argv)
261 int opt, all_users = 0;
265 /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
268 __progname = ssh_get_progname(argv[0]);
270 SSLeay_add_all_algorithms();
271 log_init(argv[0], SYSLOG_LEVEL_INFO, SYSLOG_FACILITY_USER, 1);
273 /* We don't need the RNG ourselves, but symbol references here allow
274 * ld to link us properly.
279 while ((opt = getopt(argc, argv, "ahq")) != -1) {
299 while ((pw = getpwent()) != NULL) {
301 if (!do_user(pw->pw_dir))
305 } else if (optind == argc) {
311 if ((pw = getpwuid(getuid())) == NULL)
312 fprintf(stderr, "No user found with uid %u\n",
315 if (!do_user(pw->pw_dir))
319 while (optind < argc)
320 if (!do_filename(argv[optind++], 0))