Line 0
Link Here
|
|
|
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 <string.h> |
31 |
#include <stdio.h> |
32 |
#include <fcntl.h> |
33 |
#include <unistd.h> |
34 |
|
35 |
#include <openssl/evp.h> |
36 |
|
37 |
#include "xmalloc.h" |
38 |
#include "ssh.h" |
39 |
#include "log.h" |
40 |
#include "key.h" |
41 |
#include "authfile.h" |
42 |
#include "pathnames.h" |
43 |
#include "misc.h" |
44 |
|
45 |
extern char *__progname; |
46 |
|
47 |
/* Default files to check */ |
48 |
static char *default_host_files[] = { |
49 |
_PATH_HOST_RSA_KEY_FILE, |
50 |
_PATH_HOST_DSA_KEY_FILE, |
51 |
_PATH_HOST_KEY_FILE, |
52 |
NULL |
53 |
}; |
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, |
60 |
NULL |
61 |
}; |
62 |
|
63 |
static int quiet = 0; |
64 |
|
65 |
static void |
66 |
usage(void) |
67 |
{ |
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"); |
72 |
exit(1); |
73 |
} |
74 |
|
75 |
void |
76 |
describe_key(const char *msg, const Key *key, const char *comment) |
77 |
{ |
78 |
char *fp; |
79 |
|
80 |
fp = key_fingerprint(key, SSH_FP_MD5, SSH_FP_HEX); |
81 |
if (!quiet) |
82 |
printf("%s: %u %s %s\n", msg, key_size(key), fp, comment); |
83 |
xfree(fp); |
84 |
} |
85 |
|
86 |
int |
87 |
do_key(const Key *key, const char *comment) |
88 |
{ |
89 |
char *blacklist_file; |
90 |
struct stat st; |
91 |
int ret = 1; |
92 |
|
93 |
blacklist_file = blacklist_filename(key); |
94 |
if (stat(blacklist_file, &st) < 0) |
95 |
describe_key("Unknown (no blacklist information)", |
96 |
key, comment); |
97 |
else if (blacklisted_key(key)) { |
98 |
describe_key("COMPROMISED", key, comment); |
99 |
ret = 0; |
100 |
} else |
101 |
describe_key("Not blacklisted", key, comment); |
102 |
xfree(blacklist_file); |
103 |
|
104 |
return ret; |
105 |
} |
106 |
|
107 |
int |
108 |
do_filename(const char *filename, int quiet_open) |
109 |
{ |
110 |
FILE *f; |
111 |
char line[SSH_MAX_PUBKEY_BYTES]; |
112 |
char *cp; |
113 |
u_long linenum = 0; |
114 |
Key *key; |
115 |
char *comment = NULL; |
116 |
int found = 0, ret = 1; |
117 |
|
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). |
120 |
*/ |
121 |
|
122 |
if (strcmp(filename, "-") != 0) { |
123 |
f = fopen(filename, "r"); |
124 |
if (!f) { |
125 |
char pubfile[MAXPATHLEN]; |
126 |
if (strlcpy(pubfile, filename, sizeof pubfile) < |
127 |
sizeof(pubfile) && |
128 |
strlcat(pubfile, ".pub", sizeof pubfile) < |
129 |
sizeof(pubfile)) |
130 |
f = fopen(pubfile, "r"); |
131 |
} |
132 |
if (!f) { |
133 |
if (!quiet_open) |
134 |
perror(filename); |
135 |
return -1; |
136 |
} |
137 |
} else |
138 |
f = stdin; |
139 |
while (read_keyfile_line(f, filename, line, sizeof(line), |
140 |
&linenum) != -1) { |
141 |
cp = line; |
142 |
switch (*cp) { |
143 |
case '#': |
144 |
case '\n': |
145 |
case '\0': |
146 |
continue; |
147 |
} |
148 |
/* Skip leading whitespace. */ |
149 |
for (; *cp && (*cp == ' ' || *cp == '\t'); cp++) |
150 |
; |
151 |
/* Cope with ssh-keyscan output. */ |
152 |
comment = NULL; |
153 |
if (*cp) { |
154 |
char *space; |
155 |
int type; |
156 |
|
157 |
space = strchr(cp, ' '); |
158 |
if (!space) |
159 |
continue; |
160 |
*space = '\0'; |
161 |
type = key_type_from_name(cp); |
162 |
if (type == KEY_UNSPEC) { |
163 |
comment = xstrdup(cp); |
164 |
cp = space + 1; |
165 |
} |
166 |
*space = ' '; |
167 |
} |
168 |
if (!comment) |
169 |
comment = xstrdup(filename); |
170 |
if (*cp) { |
171 |
key = key_new(KEY_RSA1); |
172 |
if (key_read(key, &cp) == 1) { |
173 |
if (!do_key(key, comment)) |
174 |
ret = 0; |
175 |
key_free(key); |
176 |
found = 1; |
177 |
} else { |
178 |
key_free(key); |
179 |
key = key_new(KEY_UNSPEC); |
180 |
if (key_read(key, &cp) == 1) { |
181 |
if (!do_key(key, comment)) |
182 |
ret = 0; |
183 |
key_free(key); |
184 |
found = 1; |
185 |
} |
186 |
} |
187 |
} |
188 |
xfree(comment); |
189 |
comment = NULL; |
190 |
} |
191 |
if (f != stdin) |
192 |
fclose(f); |
193 |
|
194 |
if (!found && filename) { |
195 |
key = key_load_public(filename, &comment); |
196 |
if (key) { |
197 |
if (!do_key(key, comment)) |
198 |
ret = 0; |
199 |
found = 1; |
200 |
} |
201 |
if (comment) |
202 |
xfree(comment); |
203 |
} |
204 |
|
205 |
return ret; |
206 |
} |
207 |
|
208 |
int |
209 |
do_host(void) |
210 |
{ |
211 |
int i; |
212 |
struct stat st; |
213 |
int ret = 1; |
214 |
|
215 |
for (i = 0; default_host_files[i]; i++) { |
216 |
if (stat(default_host_files[i], &st) < 0) |
217 |
continue; |
218 |
if (!do_filename(default_host_files[i], 1)) |
219 |
ret = 0; |
220 |
} |
221 |
|
222 |
return ret; |
223 |
} |
224 |
|
225 |
int |
226 |
do_user(const char *dir) |
227 |
{ |
228 |
int i; |
229 |
char buf[MAXPATHLEN]; |
230 |
struct stat st; |
231 |
int ret = 1; |
232 |
|
233 |
for (i = 0; default_files[i]; i++) { |
234 |
snprintf(buf, sizeof(buf), "%s/%s", dir, default_files[i]); |
235 |
if (stat(buf, &st) < 0) |
236 |
continue; |
237 |
if (!do_filename(buf, 0)) |
238 |
ret = 0; |
239 |
} |
240 |
|
241 |
return ret; |
242 |
} |
243 |
|
244 |
int |
245 |
main(int argc, char **argv) |
246 |
{ |
247 |
int opt, all_users = 0; |
248 |
int ret = 1; |
249 |
extern int optind; |
250 |
|
251 |
/* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */ |
252 |
sanitise_stdfd(); |
253 |
|
254 |
__progname = ssh_get_progname(argv[0]); |
255 |
|
256 |
SSLeay_add_all_algorithms(); |
257 |
log_init(argv[0], SYSLOG_LEVEL_INFO, SYSLOG_FACILITY_USER, 1); |
258 |
|
259 |
/* We don't need the RNG ourselves, but symbol references here allow |
260 |
* ld to link us properly. |
261 |
*/ |
262 |
init_rng(); |
263 |
seed_rng(); |
264 |
|
265 |
while ((opt = getopt(argc, argv, "ahq")) != -1) { |
266 |
switch (opt) { |
267 |
case 'a': |
268 |
all_users = 1; |
269 |
break; |
270 |
case 'q': |
271 |
quiet = 1; |
272 |
break; |
273 |
case 'h': |
274 |
default: |
275 |
usage(); |
276 |
} |
277 |
} |
278 |
|
279 |
if (all_users) { |
280 |
struct passwd *pw; |
281 |
|
282 |
if (!do_host()) |
283 |
ret = 0; |
284 |
|
285 |
while ((pw = getpwent()) != NULL) { |
286 |
if (pw->pw_dir) { |
287 |
if (!do_user(pw->pw_dir)) |
288 |
ret = 0; |
289 |
} |
290 |
} |
291 |
} else if (optind == argc) { |
292 |
struct passwd *pw; |
293 |
|
294 |
if (!do_host()) |
295 |
ret = 0; |
296 |
|
297 |
if ((pw = getpwuid(getuid())) == NULL) |
298 |
fprintf(stderr, "No user found with uid %u\n", |
299 |
(u_int)getuid()); |
300 |
else { |
301 |
if (!do_user(pw->pw_dir)) |
302 |
ret = 0; |
303 |
} |
304 |
} else { |
305 |
while (optind < argc) |
306 |
if (!do_filename(argv[optind++], 0)) |
307 |
ret = 0; |
308 |
} |
309 |
|
310 |
return ret; |
311 |
} |