]>
Commit | Line | Data |
---|---|---|
a66b6504 JK |
1 | This patch is against OpenSSH 4.7p1, although should apply to other versions |
2 | without too much difficulty. It makes the following changes: | |
3 | ||
4 | * Add key blacklisting support. Keys listed in | |
5 | /etc/ssh/blacklist.TYPE-LENGTH will be rejected for authentication by | |
6 | sshd, unless "PermitBlacklistedKeys yes" is set in /etc/ssh/sshd_config. | |
7 | ||
8 | * Add a new program, ssh-vulnkey, which can be used to check keys against | |
9 | these blacklists. | |
10 | ||
11 | This patch is up to date with respect to Debian openssh 1:4.7p1-10. | |
12 | ||
13 | --- openssh-4.7p1.orig/sshd_config.5 | |
14 | +++ openssh-4.7p1/sshd_config.5 | |
15 | @@ -615,6 +615,20 @@ | |
16 | Specifies whether password authentication is allowed. | |
17 | The default is | |
18 | .Dq yes . | |
19 | +.It Cm PermitBlacklistedKeys | |
20 | +Specifies whether | |
21 | +.Xr sshd 8 | |
22 | +should allow keys recorded in its blacklist of known-compromised keys (see | |
23 | +.Xr ssh-vulnkey 1 ) . | |
24 | +If | |
25 | +.Dq yes , | |
26 | +then attempts to authenticate with compromised keys will be logged but | |
27 | +accepted. | |
28 | +If | |
29 | +.Dq no , | |
30 | +then attempts to authenticate with compromised keys will be rejected. | |
31 | +The default is | |
32 | +.Dq no . | |
33 | .It Cm PermitEmptyPasswords | |
34 | When password authentication is allowed, it specifies whether the | |
35 | server allows login to accounts with empty password strings. | |
36 | --- openssh-4.7p1.orig/sshd.c | |
37 | +++ openssh-4.7p1/sshd.c | |
38 | @@ -1466,6 +1466,21 @@ | |
39 | ||
40 | for (i = 0; i < options.num_host_key_files; i++) { | |
41 | key = key_load_private(options.host_key_files[i], "", NULL); | |
42 | + if (key && blacklisted_key(key)) { | |
43 | + char *fp; | |
44 | + fp = key_fingerprint(key, SSH_FP_MD5, SSH_FP_HEX); | |
45 | + if (options.permit_blacklisted_keys) | |
46 | + error("Host key %s blacklisted (see " | |
47 | + "ssh-vulnkey(1)); continuing anyway", fp); | |
48 | + else | |
49 | + error("Host key %s blacklisted (see " | |
50 | + "ssh-vulnkey(1))", fp); | |
51 | + xfree(fp); | |
52 | + if (!options.permit_blacklisted_keys) { | |
53 | + sensitive_data.host_keys[i] = NULL; | |
54 | + continue; | |
55 | + } | |
56 | + } | |
57 | sensitive_data.host_keys[i] = key; | |
58 | if (key == NULL) { | |
59 | error("Could not load host key: %s", | |
60 | --- openssh-4.7p1.orig/servconf.c | |
61 | +++ openssh-4.7p1/servconf.c | |
62 | @@ -96,6 +96,7 @@ | |
63 | options->password_authentication = -1; | |
64 | options->kbd_interactive_authentication = -1; | |
65 | options->challenge_response_authentication = -1; | |
66 | + options->permit_blacklisted_keys = -1; | |
67 | options->permit_empty_passwd = -1; | |
68 | options->permit_user_env = -1; | |
69 | options->use_login = -1; | |
70 | @@ -218,6 +219,8 @@ | |
71 | options->kbd_interactive_authentication = 0; | |
72 | if (options->challenge_response_authentication == -1) | |
73 | options->challenge_response_authentication = 1; | |
74 | + if (options->permit_blacklisted_keys == -1) | |
75 | + options->permit_blacklisted_keys = 0; | |
76 | if (options->permit_empty_passwd == -1) | |
77 | options->permit_empty_passwd = 0; | |
78 | if (options->permit_user_env == -1) | |
79 | @@ -287,7 +290,7 @@ | |
80 | sListenAddress, sAddressFamily, | |
81 | sPrintMotd, sPrintLastLog, sIgnoreRhosts, | |
82 | sX11Forwarding, sX11DisplayOffset, sX11UseLocalhost, | |
83 | - sStrictModes, sEmptyPasswd, sTCPKeepAlive, | |
84 | + sStrictModes, sPermitBlacklistedKeys, sEmptyPasswd, sTCPKeepAlive, | |
85 | sPermitUserEnvironment, sUseLogin, sAllowTcpForwarding, sCompression, | |
86 | sAllowUsers, sDenyUsers, sAllowGroups, sDenyGroups, | |
87 | sIgnoreUserKnownHosts, sCiphers, sMacs, sProtocol, sPidFile, | |
88 | @@ -387,6 +390,7 @@ | |
89 | { "x11uselocalhost", sX11UseLocalhost, SSHCFG_ALL }, | |
90 | { "xauthlocation", sXAuthLocation, SSHCFG_GLOBAL }, | |
91 | { "strictmodes", sStrictModes, SSHCFG_GLOBAL }, | |
92 | + { "permitblacklistedkeys", sPermitBlacklistedKeys, SSHCFG_GLOBAL }, | |
a3aa7ff4 | 93 | { "permitemptypasswords", sEmptyPasswd, SSHCFG_GLOBAL }, |
a66b6504 JK |
94 | { "permituserenvironment", sPermitUserEnvironment, SSHCFG_GLOBAL }, |
95 | { "uselogin", sUseLogin, SSHCFG_GLOBAL }, | |
96 | @@ -943,6 +947,10 @@ | |
97 | intptr = &options->tcp_keep_alive; | |
98 | goto parse_flag; | |
99 | ||
100 | + case sPermitBlacklistedKeys: | |
101 | + intptr = &options->permit_blacklisted_keys; | |
102 | + goto parse_flag; | |
103 | + | |
104 | case sEmptyPasswd: | |
105 | intptr = &options->permit_empty_passwd; | |
106 | goto parse_flag; | |
107 | --- openssh-4.7p1.orig/servconf.h | |
108 | +++ openssh-4.7p1/servconf.h | |
109 | @@ -94,6 +94,7 @@ | |
110 | * authentication. */ | |
111 | int kbd_interactive_authentication; /* If true, permit */ | |
112 | int challenge_response_authentication; | |
113 | + int permit_blacklisted_keys; /* If true, permit */ | |
114 | int permit_empty_passwd; /* If false, do not permit empty | |
a3aa7ff4 ER |
115 | * passwords. */ |
116 | int permit_user_env; /* If true, read ~/.ssh/environment */ | |
a66b6504 JK |
117 | --- openssh-4.7p1.orig/Makefile.in |
118 | +++ openssh-4.7p1/Makefile.in | |
119 | @@ -60,7 +60,7 @@ | |
120 | INSTALL_SSH_PRNG_CMDS=@INSTALL_SSH_PRNG_CMDS@ | |
121 | INSTALL_SSH_RAND_HELPER=@INSTALL_SSH_RAND_HELPER@ | |
122 | ||
123 | -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) | |
124 | +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) ssh-vulnkey$(EXEEXT) | |
125 | ||
126 | LIBSSH_OBJS=acss.o authfd.o authfile.o bufaux.o bufbn.o buffer.o \ | |
127 | canohost.o channels.o cipher.o cipher-acss.o cipher-aes.o \ | |
128 | @@ -88,8 +88,8 @@ | |
129 | loginrec.o auth-pam.o auth-shadow.o auth-sia.o md5crypt.o \ | |
a3aa7ff4 | 130 | audit.o audit-bsm.o platform.o |
a66b6504 | 131 | |
a3aa7ff4 ER |
132 | -MANPAGES = scp.1.out ssh-add.1.out ssh-agent.1.out ssh-keygen.1.out ssh-keyscan.1.out ssh.1.out sshd.8.out sftp-server.8.out sftp.1.out ssh-rand-helper.8.out ssh-keysign.8.out sshd_config.5.out ssh_config.5.out |
133 | -MANPAGES_IN = scp.1 ssh-add.1 ssh-agent.1 ssh-keygen.1 ssh-keyscan.1 ssh.1 sshd.8 sftp-server.8 sftp.1 ssh-rand-helper.8 ssh-keysign.8 sshd_config.5 ssh_config.5 | |
134 | +MANPAGES = scp.1.out ssh-add.1.out ssh-agent.1.out ssh-keygen.1.out ssh-keyscan.1.out ssh.1.out sshd.8.out sftp-server.8.out sftp.1.out ssh-rand-helper.8.out ssh-keysign.8.out ssh-vulnkey.1.out sshd_config.5.out ssh_config.5.out | |
135 | +MANPAGES_IN = scp.1 ssh-add.1 ssh-agent.1 ssh-keygen.1 ssh-keyscan.1 ssh.1 sshd.8 sftp-server.8 sftp.1 ssh-rand-helper.8 ssh-keysign.8 ssh-vulnkey.1 sshd_config.5 ssh_config.5 | |
a66b6504 JK |
136 | MANTYPE = @MANTYPE@ |
137 | ||
138 | CONFIGFILES=sshd_config.out ssh_config.out moduli.out | |
139 | @@ -165,6 +165,9 @@ | |
140 | ssh-rand-helper${EXEEXT}: $(LIBCOMPAT) libssh.a ssh-rand-helper.o | |
141 | $(LD) -o $@ ssh-rand-helper.o $(LDFLAGS) -lssh -lopenbsd-compat $(LIBS) | |
142 | ||
143 | +ssh-vulnkey$(EXEEXT): $(LIBCOMPAT) libssh.a ssh-vulnkey.o | |
144 | + $(LD) -o $@ ssh-vulnkey.o $(LDFLAGS) -lssh -lopenbsd-compat $(LIBS) | |
145 | + | |
146 | # test driver for the loginrec code - not built by default | |
147 | logintest: logintest.o $(LIBCOMPAT) libssh.a loginrec.o | |
148 | $(LD) -o $@ logintest.o $(LDFLAGS) loginrec.o -lopenbsd-compat -lssh $(LIBS) | |
149 | @@ -264,6 +267,7 @@ | |
150 | $(INSTALL) -m 4711 $(STRIP_OPT) ssh-keysign $(DESTDIR)$(SSH_KEYSIGN) | |
151 | $(INSTALL) -m 0755 $(STRIP_OPT) sftp $(DESTDIR)$(bindir)/sftp | |
152 | $(INSTALL) -m 0755 $(STRIP_OPT) sftp-server $(DESTDIR)$(SFTP_SERVER) | |
153 | + $(INSTALL) -m 0755 $(STRIP_OPT) ssh-vulnkey $(DESTDIR)$(bindir)/ssh-vulnkey | |
154 | $(INSTALL) -m 644 ssh.1.out $(DESTDIR)$(mandir)/$(mansubdir)1/ssh.1 | |
155 | $(INSTALL) -m 644 scp.1.out $(DESTDIR)$(mandir)/$(mansubdir)1/scp.1 | |
156 | $(INSTALL) -m 644 ssh-add.1.out $(DESTDIR)$(mandir)/$(mansubdir)1/ssh-add.1 | |
157 | @@ -280,6 +284,7 @@ | |
158 | $(INSTALL) -m 644 sftp.1.out $(DESTDIR)$(mandir)/$(mansubdir)1/sftp.1 | |
159 | $(INSTALL) -m 644 sftp-server.8.out $(DESTDIR)$(mandir)/$(mansubdir)8/sftp-server.8 | |
160 | $(INSTALL) -m 644 ssh-keysign.8.out $(DESTDIR)$(mandir)/$(mansubdir)8/ssh-keysign.8 | |
161 | + $(INSTALL) -m 644 ssh-vulnkey.1.out $(DESTDIR)$(mandir)/$(mansubdir)1/ssh-vulnkey.1 | |
162 | -rm -f $(DESTDIR)$(bindir)/slogin | |
163 | ln -s ssh$(EXEEXT) $(DESTDIR)$(bindir)/slogin | |
164 | -rm -f $(DESTDIR)$(mandir)/$(mansubdir)1/slogin.1 | |
165 | @@ -361,6 +366,7 @@ | |
166 | -rm -f $(DESTDIR)$(bindir)/ssh-agent$(EXEEXT) | |
167 | -rm -f $(DESTDIR)$(bindir)/ssh-keygen$(EXEEXT) | |
168 | -rm -f $(DESTDIR)$(bindir)/ssh-keyscan$(EXEEXT) | |
169 | + -rm -f $(DESTDIR)$(bindir)/ssh-vulnkey$(EXEEXT) | |
170 | -rm -f $(DESTDIR)$(bindir)/sftp$(EXEEXT) | |
171 | -rm -f $(DESTDIR)$(sbindir)/sshd$(EXEEXT) | |
172 | -rm -r $(DESTDIR)$(SFTP_SERVER)$(EXEEXT) | |
173 | @@ -373,6 +379,7 @@ | |
174 | -rm -f $(DESTDIR)$(mandir)/$(mansubdir)1/ssh-keygen.1 | |
175 | -rm -f $(DESTDIR)$(mandir)/$(mansubdir)1/sftp.1 | |
176 | -rm -f $(DESTDIR)$(mandir)/$(mansubdir)1/ssh-keyscan.1 | |
177 | + -rm -f $(DESTDIR)$(mandir)/$(mansubdir)1/ssh-vulnkey.1 | |
178 | -rm -f $(DESTDIR)$(mandir)/$(mansubdir)8/sshd.8 | |
179 | -rm -f $(DESTDIR)$(mandir)/$(mansubdir)8/ssh-rand-helper.8 | |
180 | -rm -f $(DESTDIR)$(mandir)/$(mansubdir)8/sftp-server.8 | |
181 | --- openssh-4.7p1.orig/auth-rh-rsa.c | |
182 | +++ openssh-4.7p1/auth-rh-rsa.c | |
183 | @@ -20,6 +20,7 @@ | |
184 | #include <pwd.h> | |
185 | #include <stdarg.h> | |
186 | ||
187 | +#include "xmalloc.h" | |
188 | #include "packet.h" | |
189 | #include "uidswap.h" | |
190 | #include "log.h" | |
191 | @@ -27,6 +28,7 @@ | |
192 | #include "servconf.h" | |
193 | #include "key.h" | |
194 | #include "hostfile.h" | |
195 | +#include "authfile.h" | |
196 | #include "pathnames.h" | |
197 | #include "auth.h" | |
198 | #include "canohost.h" | |
199 | @@ -42,8 +44,22 @@ | |
200 | auth_rhosts_rsa_key_allowed(struct passwd *pw, char *cuser, char *chost, | |
201 | Key *client_host_key) | |
202 | { | |
203 | + char *fp; | |
204 | HostStatus host_status; | |
205 | ||
206 | + if (blacklisted_key(client_host_key)) { | |
207 | + fp = key_fingerprint(client_host_key, SSH_FP_MD5, SSH_FP_HEX); | |
208 | + if (options.permit_blacklisted_keys) | |
209 | + logit("Public key %s blacklisted (see " | |
210 | + "ssh-vulnkey(1)); continuing anyway", fp); | |
211 | + else | |
212 | + logit("Public key %s blacklisted (see " | |
213 | + "ssh-vulnkey(1))", fp); | |
214 | + xfree(fp); | |
215 | + if (!options.permit_blacklisted_keys) | |
216 | + return 0; | |
217 | + } | |
218 | + | |
219 | /* Check if we would accept it using rhosts authentication. */ | |
220 | if (!auth_rhosts(pw, cuser)) | |
221 | return 0; | |
222 | --- openssh-4.7p1.orig/authfile.h | |
223 | +++ openssh-4.7p1/authfile.h | |
224 | @@ -23,4 +23,7 @@ | |
225 | Key *key_load_private_pem(int, int, const char *, char **); | |
226 | int key_perm_ok(int, const char *); | |
227 | ||
228 | +char *blacklist_filename(const Key *key); | |
229 | +int blacklisted_key(const Key *key); | |
230 | + | |
231 | #endif | |
232 | --- openssh-4.7p1.orig/ssh-vulnkey.1 | |
233 | +++ openssh-4.7p1/ssh-vulnkey.1 | |
234 | @@ -0,0 +1,187 @@ | |
235 | +.\" Copyright (c) 2008 Canonical Ltd. All rights reserved. | |
236 | +.\" | |
237 | +.\" Redistribution and use in source and binary forms, with or without | |
238 | +.\" modification, are permitted provided that the following conditions | |
239 | +.\" are met: | |
240 | +.\" 1. Redistributions of source code must retain the above copyright | |
241 | +.\" notice, this list of conditions and the following disclaimer. | |
242 | +.\" 2. Redistributions in binary form must reproduce the above copyright | |
243 | +.\" notice, this list of conditions and the following disclaimer in the | |
244 | +.\" documentation and/or other materials provided with the distribution. | |
245 | +.\" | |
246 | +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR | |
247 | +.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | |
248 | +.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. | |
249 | +.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | |
250 | +.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | |
251 | +.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
252 | +.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
253 | +.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
254 | +.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | |
255 | +.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
256 | +.\" | |
257 | +.Dd $Mdocdate: May 12 2008 $ | |
258 | +.Dt SSH-VULNKEY 1 | |
259 | +.Os | |
260 | +.Sh NAME | |
261 | +.Nm ssh-vulnkey | |
262 | +.Nd check blacklist of compromised keys | |
263 | +.Sh SYNOPSIS | |
264 | +.Nm | |
265 | +.Op Fl q | |
266 | +.Ar file ... | |
267 | +.Nm | |
268 | +.Fl a | |
269 | +.Sh DESCRIPTION | |
270 | +.Nm | |
271 | +checks a key against a blacklist of compromised keys. | |
272 | +.Pp | |
273 | +A substantial number of keys are known to have been generated using a broken | |
274 | +version of OpenSSL distributed by Debian which failed to seed its random | |
275 | +number generator correctly. | |
276 | +Keys generated using these OpenSSL versions should be assumed to be | |
277 | +compromised. | |
278 | +This tool may be useful in checking for such keys. | |
279 | +.Pp | |
280 | +Keys that are compromised cannot be repaired; replacements must be generated | |
281 | +using | |
282 | +.Xr ssh-keygen 1 . | |
283 | +Make sure to update | |
284 | +.Pa authorized_keys | |
285 | +files on all systems where compromised keys were permitted to authenticate. | |
286 | +.Pp | |
287 | +The argument list will be interpreted as a list of paths to public key files | |
288 | +or | |
289 | +.Pa authorized_keys | |
290 | +files. | |
291 | +If no suitable file is found at a given path, | |
292 | +.Nm | |
293 | +will append | |
294 | +.Pa .pub | |
295 | +and retry, in case it was given a private key file. | |
296 | +If no files are given as arguments, | |
297 | +.Nm | |
298 | +will check | |
299 | +.Pa ~/.ssh/id_rsa , | |
300 | +.Pa ~/.ssh/id_dsa , | |
301 | +.Pa ~/.ssh/identity , | |
302 | +.Pa ~/.ssh/authorized_keys | |
303 | +and | |
304 | +.Pa ~/.ssh/authorized_keys2 , | |
305 | +as well as the system's host keys if readable. | |
306 | +.Pp | |
307 | +If | |
308 | +.Dq - | |
309 | +is given as an argument, | |
310 | +.Nm | |
311 | +will read from standard input. | |
312 | +This can be used to process output from | |
313 | +.Xr ssh-keyscan 1 , | |
314 | +for example: | |
315 | +.Pp | |
316 | +.Dl $ ssh-keyscan -t rsa remote.example.org | ssh-vulnkey - | |
317 | +.Pp | |
318 | +.Nm | |
319 | +will exit zero if any of the given keys were in the compromised list, | |
320 | +otherwise non-zero. | |
321 | +.Pp | |
322 | +Unless the | |
323 | +.Cm PermitBlacklistedKeys | |
324 | +option is used, | |
325 | +.Xr sshd 8 | |
326 | +will reject attempts to authenticate with keys in the compromised list. | |
327 | +.Pp | |
328 | +The options are as follows: | |
329 | +.Bl -tag -width Ds | |
330 | +.It Fl a | |
331 | +Check keys of all users on the system. | |
332 | +You will typically need to run | |
333 | +.Nm | |
334 | +as root to use this option. | |
335 | +For each user, | |
336 | +.Nm | |
337 | +will check | |
338 | +.Pa ~/.ssh/id_rsa , | |
339 | +.Pa ~/.ssh/id_dsa , | |
340 | +.Pa ~/.ssh/identity , | |
341 | +.Pa ~/.ssh/authorized_keys | |
342 | +and | |
343 | +.Pa ~/.ssh/authorized_keys2 . | |
344 | +It will also check the system's host keys. | |
345 | +.It Fl q | |
346 | +Quiet mode. | |
347 | +Normally, | |
348 | +.Nm | |
349 | +outputs the fingerprint of each key scanned, with a description of its | |
350 | +status. | |
351 | +This option suppresses that output. | |
352 | +.El | |
353 | +.Sh BLACKLIST FILE FORMAT | |
354 | +The blacklist file may start with comments, on lines starting with | |
355 | +.Dq # . | |
356 | +After these initial comments, it must follow a strict format: | |
357 | +.Pp | |
358 | +.Bl -bullet -offset indent -compact | |
359 | +.It | |
360 | +All the lines must be exactly the same length (20 characters followed by a | |
361 | +newline) and must be in sorted order. | |
362 | +.It | |
363 | +Each line must consist of the lower-case hexadecimal MD5 key fingerprint, | |
364 | +without colons, and with the first 12 characters removed (that is, the least | |
365 | +significant 80 bits of the fingerprint). | |
366 | +.El | |
367 | +.Pp | |
368 | +The key fingerprint may be generated using | |
369 | +.Xr ssh-keygen 1 : | |
370 | +.Pp | |
371 | +.Dl $ ssh-keygen -l -f /path/to/key | |
372 | +.Pp | |
373 | +This strict format is necessary to allow the blacklist file to be checked | |
374 | +quickly, using a binary-search algorithm. | |
375 | +.Sh FILES | |
376 | +.Bl -tag -width Ds | |
377 | +.It Pa ~/.ssh/id_rsa | |
378 | +If present, contains the protocol version 2 RSA authentication identity of | |
379 | +the user. | |
380 | +.It Pa ~/.ssh/id_dsa | |
381 | +If present, contains the protocol version 2 DSA authentication identity of | |
382 | +the user. | |
383 | +.It Pa ~/.ssh/identity | |
384 | +If present, contains the protocol version 1 RSA authentication identity of | |
385 | +the user. | |
386 | +.It Pa ~/.ssh/authorized_keys | |
387 | +If present, lists the public keys (RSA/DSA) that can be used for logging in | |
388 | +as this user. | |
389 | +.It Pa ~/.ssh/authorized_keys2 | |
390 | +Obsolete name for | |
391 | +.Pa ~/.ssh/authorized_keys . | |
392 | +This file may still be present on some old systems, but should not be | |
393 | +created if it is missing. | |
394 | +.It Pa /etc/ssh/ssh_host_rsa_key | |
395 | +If present, contains the protocol version 2 RSA identity of the system. | |
396 | +.It Pa /etc/ssh/ssh_host_dsa_key | |
397 | +If present, contains the protocol version 2 DSA identity of the system. | |
398 | +.It Pa /etc/ssh/ssh_host_key | |
399 | +If present, contains the protocol version 1 RSA identity of the system. | |
400 | +.It Pa /etc/ssh/blacklist. Ns Ar TYPE Ns Pa - Ns Ar LENGTH | |
401 | +If present, lists the blacklisted keys of type | |
402 | +.Ar TYPE | |
403 | +.Pf ( Dq RSA1 , | |
404 | +.Dq RSA , | |
405 | +or | |
406 | +.Dq DSA ) | |
407 | +and bit length | |
408 | +.Ar LENGTH . | |
409 | +The format of this file is described above. | |
410 | +.El | |
411 | +.Sh SEE ALSO | |
412 | +.Xr ssh-keygen 1 , | |
413 | +.Xr sshd 8 | |
414 | +.Sh AUTHORS | |
415 | +.An -nosplit | |
416 | +.An Colin Watson Aq cjwatson@ubuntu.com | |
417 | +.Pp | |
418 | +Florian Weimer suggested the option to check keys of all users, and the idea | |
419 | +of processing | |
420 | +.Xr ssh-keyscan 1 | |
421 | +output. | |
422 | --- openssh-4.7p1.orig/auth2-hostbased.c | |
423 | +++ openssh-4.7p1/auth2-hostbased.c | |
424 | @@ -40,6 +40,7 @@ | |
425 | #include "compat.h" | |
426 | #include "key.h" | |
427 | #include "hostfile.h" | |
428 | +#include "authfile.h" | |
429 | #include "auth.h" | |
430 | #include "canohost.h" | |
431 | #ifdef GSSAPI | |
432 | @@ -141,10 +142,24 @@ | |
433 | hostbased_key_allowed(struct passwd *pw, const char *cuser, char *chost, | |
434 | Key *key) | |
435 | { | |
436 | + char *fp; | |
437 | const char *resolvedname, *ipaddr, *lookup; | |
438 | HostStatus host_status; | |
439 | int len; | |
440 | ||
441 | + if (blacklisted_key(key)) { | |
442 | + fp = key_fingerprint(key, SSH_FP_MD5, SSH_FP_HEX); | |
443 | + if (options.permit_blacklisted_keys) | |
444 | + logit("Public key %s blacklisted (see " | |
445 | + "ssh-vulnkey(1)); continuing anyway", fp); | |
446 | + else | |
447 | + logit("Public key %s blacklisted (see " | |
448 | + "ssh-vulnkey(1))", fp); | |
449 | + xfree(fp); | |
450 | + if (!options.permit_blacklisted_keys) | |
451 | + return 0; | |
452 | + } | |
453 | + | |
454 | resolvedname = get_canonical_hostname(options.use_dns); | |
455 | ipaddr = get_remote_ipaddr(); | |
456 | ||
457 | --- openssh-4.7p1.orig/authfile.c | |
458 | +++ openssh-4.7p1/authfile.c | |
459 | @@ -65,6 +65,7 @@ | |
460 | #include "rsa.h" | |
461 | #include "misc.h" | |
462 | #include "atomicio.h" | |
463 | +#include "pathnames.h" | |
464 | ||
465 | /* Version identification string for SSH v1 identity files. */ | |
466 | static const char authfile_id_string[] = | |
467 | @@ -677,3 +678,113 @@ | |
468 | key_free(pub); | |
469 | return NULL; | |
470 | } | |
471 | + | |
472 | +char * | |
473 | +blacklist_filename(const Key *key) | |
474 | +{ | |
475 | + char *name; | |
476 | + | |
477 | + xasprintf(&name, "%s.%s-%u", | |
478 | + _PATH_BLACKLIST, key_type(key), key_size(key)); | |
479 | + return name; | |
480 | +} | |
481 | + | |
482 | +/* Scan a blacklist of known-vulnerable keys. */ | |
483 | +int | |
484 | +blacklisted_key(const Key *key) | |
485 | +{ | |
486 | + char *blacklist_file; | |
487 | + int fd = -1; | |
488 | + char *dgst_hex = NULL; | |
489 | + char *dgst_packed = NULL, *p; | |
490 | + int i; | |
491 | + size_t line_len; | |
492 | + struct stat st; | |
493 | + char buf[256]; | |
494 | + off_t start, lower, upper; | |
495 | + int ret = 0; | |
496 | + | |
497 | + blacklist_file = blacklist_filename(key); | |
498 | + debug("Checking blacklist file %s", blacklist_file); | |
499 | + fd = open(blacklist_file, O_RDONLY); | |
500 | + if (fd < 0) | |
501 | + goto out; | |
502 | + | |
503 | + dgst_hex = key_fingerprint(key, SSH_FP_MD5, SSH_FP_HEX); | |
504 | + /* Remove all colons */ | |
505 | + dgst_packed = xcalloc(1, strlen(dgst_hex) + 1); | |
506 | + for (i = 0, p = dgst_packed; dgst_hex[i]; i++) | |
507 | + if (dgst_hex[i] != ':') | |
508 | + *p++ = dgst_hex[i]; | |
509 | + /* Only compare least-significant 80 bits (to keep the blacklist | |
510 | + * size down) | |
511 | + */ | |
512 | + line_len = strlen(dgst_packed + 12); | |
513 | + if (line_len > 32) | |
514 | + goto out; | |
515 | + | |
516 | + /* Skip leading comments */ | |
517 | + start = 0; | |
518 | + for (;;) { | |
519 | + ssize_t r; | |
520 | + char *newline; | |
521 | + | |
522 | + r = atomicio(read, fd, buf, 256); | |
523 | + if (r <= 0) | |
524 | + goto out; | |
525 | + if (buf[0] != '#') | |
526 | + break; | |
527 | + | |
528 | + newline = memchr(buf, '\n', 256); | |
529 | + if (!newline) | |
530 | + goto out; | |
531 | + start += newline + 1 - buf; | |
532 | + if (lseek(fd, start, SEEK_SET) < 0) | |
533 | + goto out; | |
534 | + } | |
535 | + | |
536 | + /* Initialise binary search record numbers */ | |
537 | + if (fstat(fd, &st) < 0) | |
538 | + goto out; | |
539 | + lower = 0; | |
540 | + upper = (st.st_size - start) / (line_len + 1); | |
541 | + | |
542 | + while (lower != upper) { | |
543 | + off_t cur; | |
544 | + char buf[32]; | |
545 | + int cmp; | |
546 | + | |
547 | + cur = lower + (upper - lower) / 2; | |
548 | + | |
549 | + /* Read this line and compare to digest; this is | |
550 | + * overflow-safe since cur < max(off_t) / (line_len + 1) */ | |
551 | + if (lseek(fd, start + cur * (line_len + 1), SEEK_SET) < 0) | |
552 | + break; | |
553 | + if (atomicio(read, fd, buf, line_len) != line_len) | |
554 | + break; | |
555 | + cmp = memcmp(buf, dgst_packed + 12, line_len); | |
556 | + if (cmp < 0) { | |
557 | + if (cur == lower) | |
558 | + break; | |
559 | + lower = cur; | |
560 | + } else if (cmp > 0) { | |
561 | + if (cur == upper) | |
562 | + break; | |
563 | + upper = cur; | |
564 | + } else { | |
565 | + debug("Found %s in blacklist", dgst_hex); | |
566 | + ret = 1; | |
567 | + break; | |
568 | + } | |
569 | + } | |
570 | + | |
571 | +out: | |
572 | + if (dgst_packed) | |
573 | + xfree(dgst_packed); | |
574 | + if (dgst_hex) | |
575 | + xfree(dgst_hex); | |
576 | + if (fd >= 0) | |
577 | + close(fd); | |
578 | + xfree(blacklist_file); | |
579 | + return ret; | |
580 | +} | |
581 | --- openssh-4.7p1.orig/ssh-vulnkey.c | |
582 | +++ openssh-4.7p1/ssh-vulnkey.c | |
583 | @@ -0,0 +1,325 @@ | |
584 | +/* | |
585 | + * Copyright (c) 2008 Canonical Ltd. All rights reserved. | |
586 | + * | |
587 | + * Redistribution and use in source and binary forms, with or without | |
588 | + * modification, are permitted provided that the following conditions | |
589 | + * are met: | |
590 | + * 1. Redistributions of source code must retain the above copyright | |
591 | + * notice, this list of conditions and the following disclaimer. | |
592 | + * 2. Redistributions in binary form must reproduce the above copyright | |
593 | + * notice, this list of conditions and the following disclaimer in the | |
594 | + * documentation and/or other materials provided with the distribution. | |
595 | + * | |
596 | + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR | |
597 | + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | |
598 | + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. | |
599 | + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | |
600 | + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | |
601 | + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
602 | + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
603 | + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
604 | + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | |
605 | + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
606 | + */ | |
607 | + | |
608 | +#include "includes.h" | |
609 | + | |
610 | +#include <sys/types.h> | |
611 | +#include <sys/stat.h> | |
612 | + | |
613 | +#include <string.h> | |
614 | +#include <stdio.h> | |
615 | +#include <fcntl.h> | |
616 | +#include <unistd.h> | |
617 | + | |
618 | +#include <openssl/evp.h> | |
619 | + | |
620 | +#include "xmalloc.h" | |
621 | +#include "ssh.h" | |
622 | +#include "log.h" | |
623 | +#include "key.h" | |
624 | +#include "authfile.h" | |
625 | +#include "pathnames.h" | |
626 | +#include "misc.h" | |
627 | + | |
628 | +extern char *__progname; | |
629 | + | |
630 | +/* Default files to check */ | |
631 | +static char *default_host_files[] = { | |
632 | + _PATH_HOST_RSA_KEY_FILE, | |
633 | + _PATH_HOST_DSA_KEY_FILE, | |
634 | + _PATH_HOST_KEY_FILE, | |
635 | + NULL | |
636 | +}; | |
637 | +static char *default_files[] = { | |
638 | + _PATH_SSH_CLIENT_ID_RSA, | |
639 | + _PATH_SSH_CLIENT_ID_DSA, | |
640 | + _PATH_SSH_CLIENT_IDENTITY, | |
641 | + _PATH_SSH_USER_PERMITTED_KEYS, | |
642 | + _PATH_SSH_USER_PERMITTED_KEYS2, | |
643 | + NULL | |
644 | +}; | |
645 | + | |
646 | +static int quiet = 0; | |
647 | + | |
648 | +static void | |
649 | +usage(void) | |
650 | +{ | |
651 | + fprintf(stderr, "usage: %s [-aq] [file ...]\n", __progname); | |
652 | + fprintf(stderr, "Options:\n"); | |
653 | + fprintf(stderr, " -a Check keys of all users.\n"); | |
654 | + fprintf(stderr, " -q Quiet mode.\n"); | |
655 | + exit(1); | |
656 | +} | |
657 | + | |
658 | +void | |
659 | +describe_key(const char *msg, const Key *key, const char *comment) | |
660 | +{ | |
661 | + char *fp; | |
662 | + | |
663 | + fp = key_fingerprint(key, SSH_FP_MD5, SSH_FP_HEX); | |
664 | + if (!quiet) | |
665 | + printf("%s: %u %s %s\n", msg, key_size(key), fp, comment); | |
666 | + xfree(fp); | |
667 | +} | |
668 | + | |
669 | +int | |
670 | +do_key(const Key *key, const char *comment) | |
671 | +{ | |
672 | + char *blacklist_file; | |
673 | + struct stat st; | |
674 | + int ret = 1; | |
675 | + | |
676 | + blacklist_file = blacklist_filename(key); | |
677 | + if (stat(blacklist_file, &st) < 0) | |
678 | + describe_key("Unknown (no blacklist information)", | |
679 | + key, comment); | |
680 | + else if (blacklisted_key(key)) { | |
681 | + describe_key("COMPROMISED", key, comment); | |
682 | + ret = 0; | |
683 | + } else | |
684 | + describe_key("Not blacklisted", key, comment); | |
685 | + xfree(blacklist_file); | |
686 | + | |
687 | + return ret; | |
688 | +} | |
689 | + | |
690 | +int | |
691 | +do_filename(const char *filename, int quiet_open) | |
692 | +{ | |
693 | + FILE *f; | |
694 | + char line[SSH_MAX_PUBKEY_BYTES]; | |
695 | + char *cp; | |
696 | + u_long linenum = 0; | |
697 | + Key *key; | |
698 | + char *comment = NULL; | |
699 | + int found = 0, ret = 1; | |
700 | + | |
701 | + /* Copy much of key_load_public's logic here so that we can read | |
702 | + * several keys from a single file (e.g. authorized_keys). | |
703 | + */ | |
704 | + | |
705 | + if (strcmp(filename, "-") != 0) { | |
706 | + f = fopen(filename, "r"); | |
707 | + if (!f) { | |
708 | + char pubfile[MAXPATHLEN]; | |
709 | + if (strlcpy(pubfile, filename, sizeof pubfile) < | |
710 | + sizeof(pubfile) && | |
711 | + strlcat(pubfile, ".pub", sizeof pubfile) < | |
712 | + sizeof(pubfile)) | |
713 | + f = fopen(pubfile, "r"); | |
714 | + } | |
715 | + if (!f) { | |
716 | + if (!quiet_open) | |
717 | + perror(filename); | |
718 | + return -1; | |
719 | + } | |
720 | + } else | |
721 | + f = stdin; | |
722 | + while (read_keyfile_line(f, filename, line, sizeof(line), | |
723 | + &linenum) != -1) { | |
724 | + int i; | |
725 | + char *space; | |
726 | + int type; | |
727 | + | |
728 | + /* Chop trailing newline. */ | |
729 | + i = strlen(line) - 1; | |
730 | + if (line[i] == '\n') | |
731 | + line[i] = '\0'; | |
732 | + | |
733 | + /* Skip leading whitespace, empty and comment lines. */ | |
734 | + for (cp = line; *cp == ' ' || *cp == '\t'; cp++) | |
735 | + ; | |
736 | + if (!*cp || *cp == '\n' || *cp == '#') | |
737 | + continue; | |
738 | + | |
739 | + /* Cope with ssh-keyscan output and options in | |
740 | + * authorized_keys files. | |
741 | + */ | |
742 | + space = strchr(cp, ' '); | |
743 | + if (!space) | |
744 | + continue; | |
745 | + *space = '\0'; | |
746 | + type = key_type_from_name(cp); | |
747 | + *space = ' '; | |
748 | + /* Leading number (RSA1) or valid type (RSA/DSA) indicates | |
749 | + * that we have no host name or options to skip. | |
750 | + */ | |
751 | + if (atoi(cp) == 0 && type == KEY_UNSPEC) { | |
752 | + int quoted = 0; | |
753 | + | |
754 | + for (; *cp && (quoted || (*cp != ' ' && *cp != '\t')); cp++) { | |
755 | + if (*cp == '\\' && cp[1] == '"') | |
756 | + cp++; /* Skip both */ | |
757 | + else if (*cp == '"') | |
758 | + quoted = !quoted; | |
759 | + } | |
760 | + /* Skip remaining whitespace. */ | |
761 | + for (; *cp == ' ' || *cp == '\t'; cp++) | |
762 | + ; | |
763 | + if (!*cp) | |
764 | + continue; | |
765 | + } | |
766 | + | |
767 | + /* Read and process the key itself. */ | |
768 | + key = key_new(KEY_RSA1); | |
769 | + if (key_read(key, &cp) == 1) { | |
770 | + while (*cp == ' ' || *cp == '\t') | |
771 | + cp++; | |
772 | + if (!do_key(key, *cp ? cp : filename)) | |
773 | + ret = 0; | |
774 | + found = 1; | |
775 | + } else { | |
776 | + key_free(key); | |
777 | + key = key_new(KEY_UNSPEC); | |
778 | + if (key_read(key, &cp) == 1) { | |
779 | + while (*cp == ' ' || *cp == '\t') | |
780 | + cp++; | |
781 | + if (!do_key(key, *cp ? cp : filename)) | |
782 | + ret = 0; | |
783 | + found = 1; | |
784 | + } | |
785 | + } | |
786 | + key_free(key); | |
787 | + } | |
788 | + if (f != stdin) | |
789 | + fclose(f); | |
790 | + | |
791 | + if (!found && filename) { | |
792 | + key = key_load_public(filename, &comment); | |
793 | + if (key) { | |
794 | + if (!do_key(key, comment)) | |
795 | + ret = 0; | |
796 | + found = 1; | |
797 | + } | |
798 | + if (comment) | |
799 | + xfree(comment); | |
800 | + } | |
801 | + | |
802 | + return ret; | |
803 | +} | |
804 | + | |
805 | +int | |
806 | +do_host(void) | |
807 | +{ | |
808 | + int i; | |
809 | + struct stat st; | |
810 | + int ret = 1; | |
811 | + | |
812 | + for (i = 0; default_host_files[i]; i++) { | |
813 | + if (stat(default_host_files[i], &st) < 0) | |
814 | + continue; | |
815 | + if (!do_filename(default_host_files[i], 1)) | |
816 | + ret = 0; | |
817 | + } | |
818 | + | |
819 | + return ret; | |
820 | +} | |
821 | + | |
822 | +int | |
823 | +do_user(const char *dir) | |
824 | +{ | |
825 | + int i; | |
826 | + char buf[MAXPATHLEN]; | |
827 | + struct stat st; | |
828 | + int ret = 1; | |
829 | + | |
830 | + for (i = 0; default_files[i]; i++) { | |
831 | + snprintf(buf, sizeof(buf), "%s/%s", dir, default_files[i]); | |
832 | + if (stat(buf, &st) < 0) | |
833 | + continue; | |
834 | + if (!do_filename(buf, 0)) | |
835 | + ret = 0; | |
836 | + } | |
837 | + | |
838 | + return ret; | |
839 | +} | |
840 | + | |
841 | +int | |
842 | +main(int argc, char **argv) | |
843 | +{ | |
844 | + int opt, all_users = 0; | |
845 | + int ret = 1; | |
846 | + extern int optind; | |
847 | + | |
848 | + /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */ | |
849 | + sanitise_stdfd(); | |
850 | + | |
851 | + __progname = ssh_get_progname(argv[0]); | |
852 | + | |
853 | + SSLeay_add_all_algorithms(); | |
854 | + log_init(argv[0], SYSLOG_LEVEL_INFO, SYSLOG_FACILITY_USER, 1); | |
855 | + | |
856 | + /* We don't need the RNG ourselves, but symbol references here allow | |
857 | + * ld to link us properly. | |
858 | + */ | |
859 | + init_rng(); | |
860 | + seed_rng(); | |
861 | + | |
862 | + while ((opt = getopt(argc, argv, "ahq")) != -1) { | |
863 | + switch (opt) { | |
864 | + case 'a': | |
865 | + all_users = 1; | |
866 | + break; | |
867 | + case 'q': | |
868 | + quiet = 1; | |
869 | + break; | |
870 | + case 'h': | |
871 | + default: | |
872 | + usage(); | |
873 | + } | |
874 | + } | |
875 | + | |
876 | + if (all_users) { | |
877 | + struct passwd *pw; | |
878 | + | |
879 | + if (!do_host()) | |
880 | + ret = 0; | |
881 | + | |
882 | + while ((pw = getpwent()) != NULL) { | |
883 | + if (pw->pw_dir) { | |
884 | + if (!do_user(pw->pw_dir)) | |
885 | + ret = 0; | |
886 | + } | |
887 | + } | |
888 | + } else if (optind == argc) { | |
889 | + struct passwd *pw; | |
890 | + | |
891 | + if (!do_host()) | |
892 | + ret = 0; | |
893 | + | |
894 | + if ((pw = getpwuid(getuid())) == NULL) | |
895 | + fprintf(stderr, "No user found with uid %u\n", | |
896 | + (u_int)getuid()); | |
897 | + else { | |
898 | + if (!do_user(pw->pw_dir)) | |
899 | + ret = 0; | |
900 | + } | |
901 | + } else { | |
902 | + while (optind < argc) | |
903 | + if (!do_filename(argv[optind++], 0)) | |
904 | + ret = 0; | |
905 | + } | |
906 | + | |
907 | + return ret; | |
908 | +} | |
909 | --- openssh-4.7p1.orig/auth-rsa.c | |
910 | +++ openssh-4.7p1/auth-rsa.c | |
911 | @@ -40,6 +40,7 @@ | |
912 | #include "servconf.h" | |
913 | #include "key.h" | |
914 | #include "hostfile.h" | |
915 | +#include "authfile.h" | |
916 | #include "auth.h" | |
917 | #ifdef GSSAPI | |
918 | #include "ssh-gss.h" | |
919 | @@ -221,6 +222,7 @@ | |
920 | char *cp; | |
921 | char *key_options; | |
922 | int keybits; | |
923 | + char *fp; | |
924 | ||
925 | /* Skip leading whitespace, empty and comment lines. */ | |
926 | for (cp = line; *cp == ' ' || *cp == '\t'; cp++) | |
927 | @@ -265,6 +267,19 @@ | |
928 | "actual %d vs. announced %d.", | |
929 | file, linenum, BN_num_bits(key->rsa->n), bits); | |
930 | ||
931 | + if (blacklisted_key(key)) { | |
932 | + fp = key_fingerprint(key, SSH_FP_MD5, SSH_FP_HEX); | |
933 | + if (options.permit_blacklisted_keys) | |
934 | + logit("Public key %s blacklisted (see " | |
935 | + "ssh-vulnkey(1)); continuing anyway", fp); | |
936 | + else | |
937 | + logit("Public key %s blacklisted (see " | |
938 | + "ssh-vulnkey(1))", fp); | |
939 | + xfree(fp); | |
940 | + if (!options.permit_blacklisted_keys) | |
941 | + continue; | |
942 | + } | |
943 | + | |
944 | /* We have found the desired key. */ | |
945 | /* | |
946 | * If our options do not allow this key to be used, | |
947 | --- openssh-4.7p1.orig/pathnames.h | |
948 | +++ openssh-4.7p1/pathnames.h | |
949 | @@ -43,6 +43,8 @@ | |
950 | /* Backwards compatibility */ | |
951 | #define _PATH_DH_PRIMES SSHDIR "/primes" | |
952 | ||
953 | +#define _PATH_BLACKLIST SSHDIR "/blacklist" | |
954 | + | |
955 | #ifndef _PATH_SSH_PROGRAM | |
956 | #define _PATH_SSH_PROGRAM "/usr/bin/ssh" | |
957 | #endif | |
958 | --- openssh-4.7p1.orig/auth2-pubkey.c | |
959 | +++ openssh-4.7p1/auth2-pubkey.c | |
960 | @@ -42,6 +42,7 @@ | |
961 | #include "compat.h" | |
962 | #include "key.h" | |
963 | #include "hostfile.h" | |
964 | +#include "authfile.h" | |
965 | #include "auth.h" | |
966 | #include "pathnames.h" | |
967 | #include "uidswap.h" | |
968 | @@ -269,9 +270,23 @@ | |
969 | int | |
970 | user_key_allowed(struct passwd *pw, Key *key) | |
971 | { | |
972 | + char *fp; | |
973 | int success; | |
974 | char *file; | |
975 | ||
976 | + if (blacklisted_key(key)) { | |
977 | + fp = key_fingerprint(key, SSH_FP_MD5, SSH_FP_HEX); | |
978 | + if (options.permit_blacklisted_keys) | |
979 | + logit("Public key %s blacklisted (see " | |
980 | + "ssh-vulnkey(1)); continuing anyway", fp); | |
981 | + else | |
982 | + logit("Public key %s blacklisted (see " | |
983 | + "ssh-vulnkey(1))", fp); | |
984 | + xfree(fp); | |
985 | + if (!options.permit_blacklisted_keys) | |
986 | + return 0; | |
987 | + } | |
988 | + | |
989 | file = authorized_keys_file(pw); | |
990 | success = user_key_allowed2(pw, key, file); | |
991 | xfree(file); |