]> git.wh0rd.org - patches.git/blame - openssh-5.0p1-blacklist.patch
more random patches. who knows.
[patches.git] / openssh-5.0p1-blacklist.patch
CommitLineData
5e993f12 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);
92new 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+}
363new 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.