summaryrefslogtreecommitdiff
path: root/ssh-vulnkey.c
diff options
context:
space:
mode:
Diffstat (limited to 'ssh-vulnkey.c')
-rw-r--r--ssh-vulnkey.c386
1 files changed, 386 insertions, 0 deletions
diff --git a/ssh-vulnkey.c b/ssh-vulnkey.c
new file mode 100644
index 000000000..ca1a5be74
--- /dev/null
+++ b/ssh-vulnkey.c
@@ -0,0 +1,386 @@
1/*
2 * Copyright (c) 2008 Canonical Ltd. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
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.
12 *
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.
23 */
24
25#include "includes.h"
26
27#include <sys/types.h>
28#include <sys/stat.h>
29
30#include <errno.h>
31#include <string.h>
32#include <stdio.h>
33#include <fcntl.h>
34#include <unistd.h>
35
36#include <openssl/evp.h>
37
38#include "xmalloc.h"
39#include "ssh.h"
40#include "log.h"
41#include "key.h"
42#include "authfile.h"
43#include "pathnames.h"
44#include "uidswap.h"
45#include "misc.h"
46
47extern char *__progname;
48
49/* Default files to check */
50static char *default_host_files[] = {
51 _PATH_HOST_RSA_KEY_FILE,
52 _PATH_HOST_DSA_KEY_FILE,
53 _PATH_HOST_KEY_FILE,
54 NULL
55};
56static 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,
62 NULL
63};
64
65static int verbosity = 0;
66
67static int some_keys = 0;
68static int some_unknown = 0;
69static int some_compromised = 0;
70
71static void
72usage(void)
73{
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");
79 exit(1);
80}
81
82static void
83describe_key(const char *filename, u_long linenum, const char *msg,
84 Key *key, const char *comment, int min_verbosity)
85{
86 char *fp;
87
88 fp = key_fingerprint(key, SSH_FP_MD5, SSH_FP_HEX);
89 if (verbosity >= min_verbosity) {
90 if (strchr(filename, ':'))
91 printf("\"%s\"", filename);
92 else
93 printf("%s", filename);
94 printf(":%lu: %s: %s %u %s %s\n", linenum, msg,
95 key_type(key), key_size(key), fp, comment);
96 }
97 free(fp);
98}
99
100static int
101do_key(const char *filename, u_long linenum,
102 Key *key, const char *comment)
103{
104 Key *public;
105 int blacklist_status;
106 int ret = 1;
107
108 some_keys = 1;
109
110 public = key_demote(key);
111 if (public->type == KEY_RSA1)
112 public->type = KEY_RSA;
113
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);
118 some_unknown = 1;
119 } else if (blacklist_status == 1) {
120 describe_key(filename, linenum,
121 "COMPROMISED", key, comment, 0);
122 some_compromised = 1;
123 ret = 0;
124 } else
125 describe_key(filename, linenum,
126 "Not blacklisted", key, comment, 1);
127
128 key_free(public);
129
130 return ret;
131}
132
133static int
134do_filename(const char *filename, int quiet_open)
135{
136 FILE *f;
137 char line[SSH_MAX_PUBKEY_BYTES];
138 char *cp;
139 u_long linenum = 0;
140 Key *key;
141 char *comment = NULL;
142 int found = 0, ret = 1;
143
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).
146 */
147
148 if (strcmp(filename, "-") != 0) {
149 int save_errno;
150 f = fopen(filename, "r");
151 save_errno = errno;
152 if (!f) {
153 char pubfile[MAXPATHLEN];
154 if (strlcpy(pubfile, filename, sizeof pubfile) <
155 sizeof(pubfile) &&
156 strlcat(pubfile, ".pub", sizeof pubfile) <
157 sizeof(pubfile))
158 f = fopen(pubfile, "r");
159 }
160 errno = save_errno; /* earlier errno is more useful */
161 if (!f) {
162 if (!quiet_open)
163 perror(filename);
164 return -1;
165 }
166 if (verbosity > 0)
167 printf("# %s\n", filename);
168 } else
169 f = stdin;
170 while (read_keyfile_line(f, filename, line, sizeof(line),
171 &linenum) != -1) {
172 int i;
173 char *space;
174 int type;
175 char *end;
176
177 /* Chop trailing newline. */
178 i = strlen(line) - 1;
179 if (line[i] == '\n')
180 line[i] = '\0';
181
182 /* Skip leading whitespace, empty and comment lines. */
183 for (cp = line; *cp == ' ' || *cp == '\t'; cp++)
184 ;
185 if (!*cp || *cp == '\n' || *cp == '#')
186 continue;
187
188 /* Cope with ssh-keyscan output and options in
189 * authorized_keys files.
190 */
191 space = strchr(cp, ' ');
192 if (!space)
193 continue;
194 *space = '\0';
195 type = key_type_from_name(cp);
196 *space = ' ';
197 /* Leading number (RSA1) or valid type (RSA/DSA) indicates
198 * that we have no host name or options to skip.
199 */
200 if ((strtol(cp, &end, 10) == 0 || *end != ' ') &&
201 type == KEY_UNSPEC) {
202 int quoted = 0;
203
204 for (; *cp && (quoted || (*cp != ' ' && *cp != '\t')); cp++) {
205 if (*cp == '\\' && cp[1] == '"')
206 cp++; /* Skip both */
207 else if (*cp == '"')
208 quoted = !quoted;
209 }
210 /* Skip remaining whitespace. */
211 for (; *cp == ' ' || *cp == '\t'; cp++)
212 ;
213 if (!*cp)
214 continue;
215 }
216
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')
221 cp++;
222 if (!do_key(filename, linenum,
223 key, *cp ? cp : filename))
224 ret = 0;
225 found = 1;
226 } else {
227 key_free(key);
228 key = key_new(KEY_UNSPEC);
229 if (key_read(key, &cp) == 1) {
230 while (*cp == ' ' || *cp == '\t')
231 cp++;
232 if (!do_key(filename, linenum,
233 key, *cp ? cp : filename))
234 ret = 0;
235 found = 1;
236 }
237 }
238 key_free(key);
239 }
240 if (f != stdin)
241 fclose(f);
242
243 if (!found && filename) {
244 key = key_load_public(filename, &comment);
245 if (key) {
246 if (!do_key(filename, 1, key, comment))
247 ret = 0;
248 found = 1;
249 }
250 free(comment);
251 }
252
253 return ret;
254}
255
256static int
257do_host(int quiet_open)
258{
259 int i;
260 struct stat st;
261 int ret = 1;
262
263 for (i = 0; default_host_files[i]; i++) {
264 if (stat(default_host_files[i], &st) < 0 && errno == ENOENT)
265 continue;
266 if (!do_filename(default_host_files[i], quiet_open))
267 ret = 0;
268 }
269
270 return ret;
271}
272
273static int
274do_user(const char *dir)
275{
276 int i;
277 char *file;
278 struct stat st;
279 int ret = 1;
280
281 for (i = 0; default_files[i]; i++) {
282 xasprintf(&file, "%s/%s", dir, default_files[i]);
283 if (stat(file, &st) < 0 && errno == ENOENT) {
284 free(file);
285 continue;
286 }
287 if (!do_filename(file, 0))
288 ret = 0;
289 free(file);
290 }
291
292 return ret;
293}
294
295int
296main(int argc, char **argv)
297{
298 int opt, all_users = 0;
299 int ret = 1;
300 extern int optind;
301
302 /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
303 sanitise_stdfd();
304
305 __progname = ssh_get_progname(argv[0]);
306
307 SSLeay_add_all_algorithms();
308 log_init(argv[0], SYSLOG_LEVEL_INFO, SYSLOG_FACILITY_USER, 1);
309
310 /* We don't need the RNG ourselves, but symbol references here allow
311 * ld to link us properly.
312 */
313 seed_rng();
314
315 while ((opt = getopt(argc, argv, "ahqv")) != -1) {
316 switch (opt) {
317 case 'a':
318 all_users = 1;
319 break;
320 case 'q':
321 verbosity--;
322 break;
323 case 'v':
324 verbosity++;
325 break;
326 case 'h':
327 default:
328 usage();
329 }
330 }
331
332 if (all_users) {
333 struct passwd *pw;
334
335 if (!do_host(0))
336 ret = 0;
337
338 while ((pw = getpwent()) != NULL) {
339 if (pw->pw_dir) {
340 temporarily_use_uid(pw);
341 if (!do_user(pw->pw_dir))
342 ret = 0;
343 restore_uid();
344 }
345 }
346 } else if (optind == argc) {
347 struct passwd *pw;
348
349 if (!do_host(1))
350 ret = 0;
351
352 if ((pw = getpwuid(geteuid())) == NULL)
353 fprintf(stderr, "No user found with uid %u\n",
354 (u_int)geteuid());
355 else {
356 if (!do_user(pw->pw_dir))
357 ret = 0;
358 }
359 } else {
360 while (optind < argc)
361 if (!do_filename(argv[optind++], 0))
362 ret = 0;
363 }
364
365 if (verbosity >= 0) {
366 if (some_unknown) {
367 printf("#\n");
368 printf("# The status of some keys on your system is unknown.\n");
369 printf("# You may need to install additional blacklist files.\n");
370 }
371 if (some_compromised) {
372 printf("#\n");
373 printf("# Some keys on your system have been compromised!\n");
374 printf("# You must replace them using ssh-keygen(1).\n");
375 }
376 if (some_unknown || some_compromised) {
377 printf("#\n");
378 printf("# See the ssh-vulnkey(1) manual page for further advice.\n");
379 } else if (some_keys && verbosity > 0) {
380 printf("#\n");
381 printf("# No blacklisted keys!\n");
382 }
383 }
384
385 return ret;
386}