summaryrefslogtreecommitdiff
path: root/ssh-vulnkey.c
diff options
context:
space:
mode:
authorColin Watson <cjwatson@debian.org>2008-05-12 23:33:01 +0000
committerColin Watson <cjwatson@debian.org>2008-05-12 23:33:01 +0000
commit47608c17e64138f8d16aa2bdc49a0eb00e1c3549 (patch)
tree92572d90b9aa8f45c0d9e6dbb185065667fdcea0 /ssh-vulnkey.c
parent19ccea525446d5a3c2a176d813c505be81b91cbf (diff)
* Mitigate OpenSSL security vulnerability:
- Add key blacklisting support. Keys listed in /etc/ssh/blacklist.TYPE-LENGTH will be rejected for authentication by sshd, unless "PermitBlacklistedKeys yes" is set in /etc/ssh/sshd_config. - Add a new program, ssh-vulnkey, which can be used to check keys against these blacklists. - Depend on openssh-blacklist. - Force dependencies on libssl0.9.8 / libcrypto0.9.8-udeb to at least 0.9.8g-9. - Automatically regenerate known-compromised host keys, with a critical-priority debconf note. (I regret that there was no time to gather translations.)
Diffstat (limited to 'ssh-vulnkey.c')
-rw-r--r--ssh-vulnkey.c311
1 files changed, 311 insertions, 0 deletions
diff --git a/ssh-vulnkey.c b/ssh-vulnkey.c
new file mode 100644
index 000000000..ba87cbd28
--- /dev/null
+++ b/ssh-vulnkey.c
@@ -0,0 +1,311 @@
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
45extern char *__progname;
46
47/* Default files to check */
48static char *default_host_files[] = {
49 _PATH_HOST_RSA_KEY_FILE,
50 _PATH_HOST_DSA_KEY_FILE,
51 _PATH_HOST_KEY_FILE,
52 NULL
53};
54static 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
63static int quiet = 0;
64
65static void
66usage(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
75void
76describe_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
86int
87do_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
107int
108do_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
208int
209do_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
225int
226do_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
244int
245main(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}