summaryrefslogtreecommitdiff
path: root/ssh-vulnkey.c
diff options
context:
space:
mode:
Diffstat (limited to 'ssh-vulnkey.c')
-rw-r--r--ssh-vulnkey.c388
1 files changed, 388 insertions, 0 deletions
diff --git a/ssh-vulnkey.c b/ssh-vulnkey.c
new file mode 100644
index 000000000..4a5780488
--- /dev/null
+++ b/ssh-vulnkey.c
@@ -0,0 +1,388 @@
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
82void
83describe_key(const char *filename, u_long linenum, const char *msg,
84 const 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 xfree(fp);
98}
99
100int
101do_key(const char *filename, u_long linenum,
102 const 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
133int
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 if (comment)
251 xfree(comment);
252 }
253
254 return ret;
255}
256
257int
258do_host(int quiet_open)
259{
260 int i;
261 struct stat st;
262 int ret = 1;
263
264 for (i = 0; default_host_files[i]; i++) {
265 if (stat(default_host_files[i], &st) < 0 && errno == ENOENT)
266 continue;
267 if (!do_filename(default_host_files[i], quiet_open))
268 ret = 0;
269 }
270
271 return ret;
272}
273
274int
275do_user(const char *dir)
276{
277 int i;
278 char *file;
279 struct stat st;
280 int ret = 1;
281
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) {
285 xfree(file);
286 continue;
287 }
288 if (!do_filename(file, 0))
289 ret = 0;
290 xfree(file);
291 }
292
293 return ret;
294}
295
296int
297main(int argc, char **argv)
298{
299 int opt, all_users = 0;
300 int ret = 1;
301 extern int optind;
302
303 /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
304 sanitise_stdfd();
305
306 __progname = ssh_get_progname(argv[0]);
307
308 SSLeay_add_all_algorithms();
309 log_init(argv[0], SYSLOG_LEVEL_INFO, SYSLOG_FACILITY_USER, 1);
310
311 /* We don't need the RNG ourselves, but symbol references here allow
312 * ld to link us properly.
313 */
314 init_rng();
315 seed_rng();
316
317 while ((opt = getopt(argc, argv, "ahqv")) != -1) {
318 switch (opt) {
319 case 'a':
320 all_users = 1;
321 break;
322 case 'q':
323 verbosity--;
324 break;
325 case 'v':
326 verbosity++;
327 break;
328 case 'h':
329 default:
330 usage();
331 }
332 }
333
334 if (all_users) {
335 struct passwd *pw;
336
337 if (!do_host(0))
338 ret = 0;
339
340 while ((pw = getpwent()) != NULL) {
341 if (pw->pw_dir) {
342 temporarily_use_uid(pw);
343 if (!do_user(pw->pw_dir))
344 ret = 0;
345 restore_uid();
346 }
347 }
348 } else if (optind == argc) {
349 struct passwd *pw;
350
351 if (!do_host(1))
352 ret = 0;
353
354 if ((pw = getpwuid(geteuid())) == NULL)
355 fprintf(stderr, "No user found with uid %u\n",
356 (u_int)geteuid());
357 else {
358 if (!do_user(pw->pw_dir))
359 ret = 0;
360 }
361 } else {
362 while (optind < argc)
363 if (!do_filename(argv[optind++], 0))
364 ret = 0;
365 }
366
367 if (verbosity >= 0) {
368 if (some_unknown) {
369 printf("#\n");
370 printf("# The status of some keys on your system is unknown.\n");
371 printf("# You may need to install additional blacklist files.\n");
372 }
373 if (some_compromised) {
374 printf("#\n");
375 printf("# Some keys on your system have been compromised!\n");
376 printf("# You must replace them using ssh-keygen(1).\n");
377 }
378 if (some_unknown || some_compromised) {
379 printf("#\n");
380 printf("# See the ssh-vulnkey(1) manual page for further advice.\n");
381 } else if (some_keys && verbosity > 0) {
382 printf("#\n");
383 printf("# No blacklisted keys!\n");
384 }
385 }
386
387 return ret;
388}