]> git.wh0rd.org - patches.git/blob - openssh-5.0p1-blacklist.patch
scummvm random work
[patches.git] / openssh-5.0p1-blacklist.patch
1 --- openssh-5.0p1/Makefile.in
2 +++ openssh-5.0p1/Makefile.in
3 @@ -62,7 +62,7 @@
4
5 TARGETS=ssh$(EXEEXT) sshd$(EXEEXT) ssh-add$(EXEEXT) ssh-keygen$(EXEEXT) ssh-keyscan${EXEEXT} ssh-keysign${EXEEXT} ssh-agent$(EXEEXT) scp$(EXEEXT) ssh-rand-helper${EXEEXT} sftp-server$(EXEEXT) sftp$(EXEEXT)
6
7 -LIBSSH_OBJS=acss.o authfd.o authfile.o bufaux.o bufbn.o buffer.o \
8 +LIBSSH_OBJS=acss.o authfd.o authfile.o blacklist.o bufaux.o bufbn.o buffer.o \
9 canohost.o channels.o cipher.o cipher-acss.o cipher-aes.o \
10 cipher-bf1.o cipher-ctr.o cipher-3des1.o cleanup.o \
11 compat.o compress.o crc32.o deattack.o fatal.o hostfile.o \
12 --- openssh-5.0p1/auth-rh-rsa.c
13 +++ openssh-5.0p1/auth-rh-rsa.c
14 @@ -34,6 +34,7 @@
15 #include "ssh-gss.h"
16 #endif
17 #include "monitor_wrap.h"
18 +#include "blacklist.h"
19
20 /* import */
21 extern ServerOptions options;
22 @@ -48,6 +49,9 @@
23 if (!auth_rhosts(pw, cuser))
24 return 0;
25
26 + if (blacklisted_key(client_host_key, 0))
27 + return 0;
28 +
29 host_status = check_key_in_hostfiles(pw, client_host_key,
30 chost, _PATH_SSH_SYSTEM_HOSTFILE,
31 options.ignore_user_known_hosts ? NULL : _PATH_SSH_USER_HOSTFILE);
32 --- openssh-5.0p1/auth-rsa.c
33 +++ openssh-5.0p1/auth-rsa.c
34 @@ -47,6 +47,7 @@
35 #include "monitor_wrap.h"
36 #include "ssh.h"
37 #include "misc.h"
38 +#include "blacklist.h"
39
40 /* import */
41 extern ServerOptions options;
42 @@ -265,6 +272,9 @@
43 "actual %d vs. announced %d.",
44 file, linenum, BN_num_bits(key->rsa->n), bits);
45
46 + if (blacklisted_key(key, 0))
47 + continue;
48 +
49 /* We have found the desired key. */
50 /*
51 * If our options do not allow this key to be used,
52 --- openssh-5.0p1/auth2-hostbased.c
53 +++ openssh-5.0p1/auth2-hostbased.c
54 @@ -47,6 +47,7 @@
55 #endif
56 #include "monitor_wrap.h"
57 #include "pathnames.h"
58 +#include "blacklist.h"
59
60 /* import */
61 extern ServerOptions options;
62 @@ -145,6 +146,9 @@
63 HostStatus host_status;
64 int len;
65
66 + if (blacklisted_key(key, 0))
67 + return 0;
68 +
69 resolvedname = get_canonical_hostname(options.use_dns);
70 ipaddr = get_remote_ipaddr();
71
72 --- openssh-5.0p1/auth2-pubkey.c
73 +++ openssh-5.0p1/auth2-pubkey.c
74 @@ -52,6 +52,7 @@
75 #endif
76 #include "monitor_wrap.h"
77 #include "misc.h"
78 +#include "blacklist.h"
79
80 /* import */
81 extern ServerOptions options;
82 @@ -272,6 +273,9 @@
83 int success;
84 char *file;
85
86 + if (blacklisted_key(key, 0))
87 + return 0;
88 +
89 file = authorized_keys_file(pw);
90 success = user_key_allowed2(pw, key, file);
91 xfree(file);
92 new file mode 100644
93 --- /dev/null
94 +++ openssh-5.0p1/blacklist.c
95 @@ -0,0 +1,267 @@
96 +/*
97 + * Support for RSA/DSA key blacklisting based on partial fingerprints,
98 + * developed under Openwall Project for Owl - http://www.openwall.com/Owl/
99 + *
100 + * Copyright (c) 2008 Dmitry V. Levin <ldv at cvs.openwall.com>
101 + *
102 + * Permission to use, copy, modify, and distribute this software for any
103 + * purpose with or without fee is hereby granted, provided that the above
104 + * copyright notice and this permission notice appear in all copies.
105 + *
106 + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
107 + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
108 + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
109 + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
110 + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
111 + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
112 + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
113 + *
114 + * The blacklist encoding was designed by Solar Designer and Dmitry V. Levin.
115 + * No intellectual property rights to the encoding scheme are claimed.
116 + *
117 + * This effort was supported by CivicActions - http://www.civicactions.com
118 + *
119 + * The file size to encode 294,903 of 48-bit fingerprints is just 1.3 MB,
120 + * which corresponds to less than 4.5 bytes per fingerprint.
121 + */
122 +
123 +#include "includes.h"
124 +#include <string.h>
125 +#include <unistd.h>
126 +#include <errno.h>
127 +#include <fcntl.h>
128 +
129 +#include "atomicio.h"
130 +#include "blacklist.h"
131 +#include "canohost.h"
132 +#include "log.h"
133 +#include "pathnames.h"
134 +#include "servconf.h"
135 +#include "xmalloc.h"
136 +
137 +extern ServerOptions options;
138 +
139 +typedef struct
140 +{
141 + /* format version identifier */
142 + char version[8];
143 + /* index size, in bits */
144 + uint8_t index_size;
145 + /* offset size, in bits */
146 + uint8_t offset_size;
147 + /* record size, in bits */
148 + uint8_t record_bits;
149 + /* number of records */
150 + uint8_t records[3];
151 + /* offset shift */
152 + uint8_t shift[2];
153 +
154 +} __attribute__((packed)) blacklist_header;
155 +
156 +static unsigned
157 +c2u(uint8_t c)
158 +{
159 + return (c >= 'a') ? (c - 'a' + 10) : (c - '0');
160 +}
161 +
162 +static blacklist_error_t
163 +validate_blacklist(const char *fname, int fd, unsigned *bytes,
164 + unsigned *records, unsigned *shift)
165 +{
166 + unsigned expected;
167 + struct stat st;
168 + blacklist_header header;
169 +
170 + if (fstat(fd, &st)) {
171 + error("fstat for blacklist file %s failed: %m", fname);
172 + return BLACKLIST_ERROR_ACCESS;
173 + }
174 +
175 + if (atomicio(read, fd, &header, sizeof(header)) != sizeof(header)) {
176 + error("read blacklist file %s header failed: %m", fname);
177 + return BLACKLIST_ERROR_ACCESS;
178 + }
179 +
180 + if (memcmp(header.version, "SSH-FP", 6)) {
181 + error("blacklist file %s has unrecognized format", fname);
182 + return BLACKLIST_ERROR_FORMAT;
183 + }
184 +
185 + if (header.index_size != 16 || header.offset_size != 16 ||
186 + memcmp(header.version, "SSH-FP00", 8)) {
187 + error("blacklist file %s has unsupported format", fname);
188 + return BLACKLIST_ERROR_VERSION;
189 + }
190 +
191 + *bytes = (header.record_bits >> 3) - 2;
192 + *records =
193 + (((header.records[0] << 8) +
194 + header.records[1]) << 8) + header.records[2];
195 + *shift = (header.shift[0] << 8) + header.shift[1];
196 +
197 + expected = sizeof(header) + 0x20000 + (*records) * (*bytes);
198 + if (st.st_size != expected) {
199 + error("blacklist file %s size mismatch: "
200 + "expected size %u, found size %lu",
201 + fname, expected, (unsigned long) st.st_size);
202 + return BLACKLIST_ERROR_ACCESS;
203 + }
204 +
205 + return BLACKLIST_ERROR_NONE;
206 +}
207 +
208 +static int
209 +expected_offset(uint16_t index, uint16_t shift, unsigned records)
210 +{
211 + return ((index * (long long) records) >> 16) - shift;
212 +}
213 +
214 +static int
215 +xlseek(const char *fname, int fd, unsigned seek)
216 +{
217 + if (lseek(fd, seek, SEEK_SET) != seek) {
218 + error("lseek for blacklist file %s failed: %m", fname);
219 + return BLACKLIST_ERROR_ACCESS;
220 + }
221 + return BLACKLIST_ERROR_NONE;
222 +}
223 +
224 +static blacklist_error_t
225 +check(const char *fname, int fd, const char *s)
226 +{
227 + unsigned bytes, records, shift;
228 + unsigned num, i, j;
229 + int off_start, off_end;
230 + blacklist_error_t rc;
231 + uint16_t index;
232 + /* max number of bytes stored in record_bits, minus two bytes used for index */
233 + uint8_t buf[(0xff >> 3) - 2];
234 +
235 + if ((rc = validate_blacklist(fname, fd, &bytes, &records, &shift)))
236 + return rc;
237 +
238 + index = (((((c2u(s[0]) << 4) | c2u(s[1])) << 4) |
239 + c2u(s[2])) << 4) | c2u(s[3]);
240 + if (xlseek(fname, fd, sizeof(blacklist_header) + index * 2))
241 + return BLACKLIST_ERROR_ACCESS;
242 +
243 + if (atomicio(read, fd, buf, 4) != 4) {
244 + error("read blacklist file %s offsets failed: %m", fname);
245 + return BLACKLIST_ERROR_ACCESS;
246 + }
247 +
248 + off_start = (buf[0] << 8) + buf[1] +
249 + expected_offset(index, shift, records);
250 + if (off_start < 0 || (unsigned) off_start > records) {
251 + error("blacklist file %s off_start overflow [%d] for index %#x",
252 + fname, off_start, index);
253 + return BLACKLIST_ERROR_ACCESS;
254 + }
255 + if (index < 0xffff) {
256 + off_end = (buf[2] << 8) + buf[3] +
257 + expected_offset(index + 1, shift, records);
258 + if (off_end < off_start || (unsigned) off_end > records) {
259 + error("blacklist file %s off_end overflow [%d] for index %#x",
260 + fname, off_end, index);
261 + return BLACKLIST_ERROR_ACCESS;
262 + }
263 + } else
264 + off_end = records;
265 +
266 + if (xlseek(fname, fd,
267 + sizeof(blacklist_header) + 0x20000 + off_start * bytes))
268 + return BLACKLIST_ERROR_ACCESS;
269 +
270 + num = off_end - off_start;
271 + for (i = 0; i < num; ++i) {
272 + if (atomicio(read, fd, buf, bytes) != bytes) {
273 + error("read blacklist file %s fingerprints failed: %m",
274 + fname);
275 + return BLACKLIST_ERROR_ACCESS;
276 + }
277 +
278 + for (j = 0; j < bytes; ++j)
279 + if (((c2u(s[4 + j * 2]) << 4) | c2u(s[5 + j * 2])) !=
280 + buf[j])
281 + break;
282 + if (j >= bytes) {
283 + debug("blacklisted fingerprint: %s offset=%u, number=%u",
284 + s, off_start, i);
285 + return BLACKLIST_ERROR_ALL;
286 + }
287 + }
288 +
289 + debug("non-blacklisted fingerprint: %s offset=%u, number=%u",
290 + s, off_start, num);
291 + return BLACKLIST_ERROR_NONE;
292 +}
293 +
294 +static blacklist_error_t
295 +blacklisted_fingerprint(const char *hex)
296 +{
297 + int fd = -1;
298 + blacklist_error_t rc = BLACKLIST_ERROR_ACCESS;
299 + const char *fname = _PATH_BLACKLIST;
300 + char *s, *p;
301 +
302 + debug("Checking fingerprint %s using blacklist file %s", hex, fname);
303 +
304 + s = xstrdup(hex);
305 + for (p = s; *hex; ++hex)
306 + if (*hex != ':')
307 + *p++ = *hex;
308 + *p = '\0';
309 +
310 + if (strlen(s) != 32 || strlen(s) != strspn(s, "0123456789abcdef")) {
311 + error("%s: invalid fingerprint", s);
312 + goto out;
313 + }
314 +
315 + if ((fd = open(fname, O_RDONLY)) < 0) {
316 + if (ENOENT == errno) {
317 + rc = BLACKLIST_ERROR_MISSING;
318 + verbose("open blacklist file %s failed: %m", fname);
319 + } else
320 + logit("open blacklist file %s failed: %m", fname);
321 + goto out;
322 + }
323 +
324 + rc = check(fname, fd, s);
325 +
326 +out:
327 + close(fd);
328 + xfree(s);
329 + return rc;
330 +}
331 +
332 +int
333 +blacklisted_key(Key *key, int hostkey)
334 +{
335 + int rc;
336 + const char *text;
337 + char *fp = key_fingerprint(key, SSH_FP_MD5, SSH_FP_HEX);
338 +
339 + switch ((rc = blacklisted_fingerprint(fp))) {
340 + case BLACKLIST_ERROR_NONE:
341 + break;
342 + case BLACKLIST_ERROR_ALL:
343 + text = (options.ignore_blacklist_errors == rc) ?
344 + "Permitted" : "Rejected";
345 + if (hostkey)
346 + logit("%s blacklisted host key %s", text, fp);
347 + else
348 + logit("%s blacklisted public key %s from %.100s",
349 + text, fp, get_remote_ipaddr());
350 + break;
351 + default:
352 + if (hostkey)
353 + logit("Unable to check blacklist for host key %s",
354 + fp);
355 + else
356 + logit("Unable to check blacklist for public key %s from %.100s",
357 + fp, get_remote_ipaddr());
358 + }
359 +
360 + xfree(fp);
361 + return (rc > options.ignore_blacklist_errors);
362 +}
363 new file mode 100644
364 --- /dev/null
365 +++ openssh-5.0p1/blacklist.h
366 @@ -0,0 +1,37 @@
367 +/*
368 + * Support for RSA/DSA key blacklisting based on partial fingerprints,
369 + * developed under Openwall Project for Owl - http://www.openwall.com/Owl/
370 + *
371 + * Copyright (c) 2008 Dmitry V. Levin <ldv at cvs.openwall.com>
372 + *
373 + * Permission to use, copy, modify, and distribute this software for any
374 + * purpose with or without fee is hereby granted, provided that the above
375 + * copyright notice and this permission notice appear in all copies.
376 + *
377 + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
378 + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
379 + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
380 + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
381 + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
382 + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
383 + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
384 + */
385 +
386 +#ifndef BLACKLIST_H_
387 +#define BLACKLIST_H_
388 +
389 +#include "key.h"
390 +
391 +int blacklisted_key(Key *, int);
392 +
393 +typedef enum
394 +{
395 + BLACKLIST_ERROR_NONE = 0,
396 + BLACKLIST_ERROR_MISSING,
397 + BLACKLIST_ERROR_VERSION,
398 + BLACKLIST_ERROR_FORMAT,
399 + BLACKLIST_ERROR_ACCESS,
400 + BLACKLIST_ERROR_ALL
401 +} blacklist_error_t;
402 +
403 +#endif /* BLACKLIST_H_ */
404 --- openssh-5.0p1/pathnames.h
405 +++ openssh-5.0p1/pathnames.h
406 @@ -43,6 +43,8 @@
407 /* Backwards compatibility */
408 #define _PATH_DH_PRIMES SSHDIR "/primes"
409
410 +#define _PATH_BLACKLIST SSHDIR "/blacklist"
411 +
412 #ifndef _PATH_SSH_PROGRAM
413 #define _PATH_SSH_PROGRAM "/usr/bin/ssh"
414 #endif
415 --- openssh-5.0p1/servconf.c
416 +++ openssh-5.0p1/servconf.c
417 @@ -39,6 +39,7 @@
418 #include "match.h"
419 #include "channels.h"
420 #include "groupaccess.h"
421 +#include "blacklist.h"
422
423 static void add_listen_addr(ServerOptions *, char *, u_short);
424 static void add_one_listen_addr(ServerOptions *, char *, u_short);
425 @@ -94,6 +95,7 @@
426 options->password_authentication = -1;
427 options->kbd_interactive_authentication = -1;
428 options->challenge_response_authentication = -1;
429 + options->ignore_blacklist_errors = -1;
430 options->permit_empty_passwd = -1;
431 options->permit_user_env = -1;
432 options->use_login = -1;
433 @@ -213,6 +217,8 @@
434 options->kbd_interactive_authentication = 0;
435 if (options->challenge_response_authentication == -1)
436 options->challenge_response_authentication = 1;
437 + if (options->ignore_blacklist_errors == -1)
438 + options->ignore_blacklist_errors = BLACKLIST_ERROR_ALL; //VERSION;
439 if (options->permit_empty_passwd == -1)
440 options->permit_empty_passwd = 0;
441 if (options->permit_user_env == -1)
442 @@ -282,7 +299,7 @@
443 sListenAddress, sAddressFamily,
444 sPrintMotd, sPrintLastLog, sIgnoreRhosts,
445 sX11Forwarding, sX11DisplayOffset, sX11UseLocalhost,
446 - sStrictModes, sEmptyPasswd, sTCPKeepAlive,
447 + sStrictModes, sIgnoreBlacklistErrors, sEmptyPasswd, sTCPKeepAlive,
448 sPermitUserEnvironment, sUseLogin, sAllowTcpForwarding, sCompression,
449 sAllowUsers, sDenyUsers, sAllowGroups, sDenyGroups,
450 sIgnoreUserKnownHosts, sCiphers, sMacs, sProtocol, sPidFile,
451 @@ -372,6 +390,7 @@
452 { "x11uselocalhost", sX11UseLocalhost, SSHCFG_ALL },
453 { "xauthlocation", sXAuthLocation, SSHCFG_GLOBAL },
454 { "strictmodes", sStrictModes, SSHCFG_GLOBAL },
455 + { "ignoreblacklisterrors", sIgnoreBlacklistErrors, SSHCFG_GLOBAL },
456 { "permitemptypasswords", sEmptyPasswd, SSHCFG_GLOBAL },
457 { "permituserenvironment", sPermitUserEnvironment, SSHCFG_GLOBAL },
458 { "uselogin", sUseLogin, SSHCFG_GLOBAL },
459 @@ -923,6 +944,32 @@
460 intptr = &options->tcp_keep_alive;
461 goto parse_flag;
462
463 + case sIgnoreBlacklistErrors:
464 + intptr = &options->ignore_blacklist_errors;
465 + arg = strdelim(&cp);
466 + if (!arg || *arg == '\0')
467 + fatal("%s line %d: missing none/missing/version/format/access/all argument.",
468 + filename, linenum);
469 + value = 0; /* silence compiler */
470 + if (strcmp(arg, "none") == 0)
471 + value = BLACKLIST_ERROR_NONE;
472 + else if (strcmp(arg, "missing") == 0)
473 + value = BLACKLIST_ERROR_MISSING;
474 + else if (strcmp(arg, "version") == 0)
475 + value = BLACKLIST_ERROR_VERSION;
476 + else if (strcmp(arg, "format") == 0)
477 + value = BLACKLIST_ERROR_FORMAT;
478 + else if (strcmp(arg, "access") == 0)
479 + value = BLACKLIST_ERROR_ACCESS;
480 + else if (strcmp(arg, "all") == 0)
481 + value = BLACKLIST_ERROR_ALL;
482 + else
483 + fatal("%s line %d: Bad none/missing/version/format/access/all argument: %s",
484 + filename, linenum, arg);
485 + if (*activep && *intptr == -1)
486 + *intptr = value;
487 + break;
488 +
489 case sEmptyPasswd:
490 intptr = &options->permit_empty_passwd;
491 goto parse_flag;
492 --- openssh-5.0p1/servconf.h
493 +++ openssh-5.0p1/servconf.h
494 @@ -95,6 +95,7 @@
495 * authentication. */
496 int kbd_interactive_authentication; /* If true, permit */
497 int challenge_response_authentication;
498 + int ignore_blacklist_errors; /* none/missing/version/format/access/all */
499 int permit_empty_passwd; /* If false, do not permit empty
500 * passwords. */
501 int permit_user_env; /* If true, read ~/.ssh/environment */
502 --- openssh-5.0p1/sshd.c
503 +++ openssh-5.0p1/sshd.c
504 @@ -118,6 +118,7 @@
505 #include "monitor_wrap.h"
506 #include "monitor_fdpass.h"
507 #include "version.h"
508 +#include "blacklist.h"
509
510 #ifdef LIBWRAP
511 #include <tcpd.h>
512 @@ -1484,6 +1494,11 @@
513 sensitive_data.host_keys[i] = NULL;
514 continue;
515 }
516 + if (blacklisted_key(key, 1)) {
517 + sensitive_data.host_keys[i] = NULL;
518 + key_free(key);
519 + continue;
520 + }
521 switch (key->type) {
522 case KEY_RSA1:
523 sensitive_data.ssh1_host_key = key;
524 --- openssh-5.0p1/sshd_config.5
525 +++ openssh-5.0p1/sshd_config.5
526 @@ -611,6 +611,39 @@
527 Specifies whether password authentication is allowed.
528 The default is
529 .Dq yes .
530 +.It Cm IgnoreBlacklistErrors
531 +Specifies whether
532 +.Xr sshd 8
533 +should allow keys recorded in its blacklist of known-compromised keys.
534 +If
535 +.Dq all ,
536 +then attempts to authenticate with compromised keys will be logged
537 +but accepted.
538 +If
539 +.Dq access ,
540 +then attempts to authenticate with compromised keys will be rejected,
541 +but blacklist file access errors will be ignored.
542 +If
543 +.Dq format ,
544 +then attempts to authenticate with compromised keys will be rejected, but
545 +blacklist file access errors due to missing blacklist file or blacklist
546 +file unrecognized format will be ignored.
547 +If
548 +.Dq version ,
549 +then attempts to authenticate with compromised keys will be rejected, but
550 +blacklist file access errors due to missing blacklist file or blacklist
551 +file format version mismatch will be ignored.
552 +If
553 +.Dq missing ,
554 +then attempts to authenticate with compromised keys will be rejected,
555 +but blacklist file access errors due to missing blacklist file will
556 +be ignored.
557 +If
558 +.Dq none ,
559 +then attempts to authenticate with compromised keys, or in case of
560 +any blacklist file access error, will be rejected.
561 +The default is
562 +.Dq version .
563 .It Cm PermitEmptyPasswords
564 When password authentication is allowed, it specifies whether the
565 server allows login to accounts with empty password strings.