1 |
|
|
/* $OpenBSD: authfile.c,v 1.127 2017/07/01 13:50:45 djm Exp $ */ |
2 |
|
|
/* |
3 |
|
|
* Copyright (c) 2000, 2013 Markus Friedl. All rights reserved. |
4 |
|
|
* |
5 |
|
|
* Redistribution and use in source and binary forms, with or without |
6 |
|
|
* modification, are permitted provided that the following conditions |
7 |
|
|
* are met: |
8 |
|
|
* 1. Redistributions of source code must retain the above copyright |
9 |
|
|
* notice, this list of conditions and the following disclaimer. |
10 |
|
|
* 2. Redistributions in binary form must reproduce the above copyright |
11 |
|
|
* notice, this list of conditions and the following disclaimer in the |
12 |
|
|
* documentation and/or other materials provided with the distribution. |
13 |
|
|
* |
14 |
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
15 |
|
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
16 |
|
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
17 |
|
|
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, |
18 |
|
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
19 |
|
|
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
20 |
|
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
21 |
|
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
22 |
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
23 |
|
|
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
24 |
|
|
*/ |
25 |
|
|
|
26 |
|
|
|
27 |
|
|
#include <sys/types.h> |
28 |
|
|
#include <sys/stat.h> |
29 |
|
|
#include <sys/uio.h> |
30 |
|
|
|
31 |
|
|
#include <errno.h> |
32 |
|
|
#include <fcntl.h> |
33 |
|
|
#include <stdio.h> |
34 |
|
|
#include <stdlib.h> |
35 |
|
|
#include <string.h> |
36 |
|
|
#include <unistd.h> |
37 |
|
|
#include <limits.h> |
38 |
|
|
|
39 |
|
|
#include "cipher.h" |
40 |
|
|
#include "ssh.h" |
41 |
|
|
#include "log.h" |
42 |
|
|
#include "authfile.h" |
43 |
|
|
#include "misc.h" |
44 |
|
|
#include "atomicio.h" |
45 |
|
|
#include "sshkey.h" |
46 |
|
|
#include "sshbuf.h" |
47 |
|
|
#include "ssherr.h" |
48 |
|
|
#include "krl.h" |
49 |
|
|
|
50 |
|
|
#define MAX_KEY_FILE_SIZE (1024 * 1024) |
51 |
|
|
|
52 |
|
|
/* Save a key blob to a file */ |
53 |
|
|
static int |
54 |
|
|
sshkey_save_private_blob(struct sshbuf *keybuf, const char *filename) |
55 |
|
|
{ |
56 |
|
|
int fd, oerrno; |
57 |
|
|
|
58 |
✗✓ |
26 |
if ((fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0600)) < 0) |
59 |
|
|
return SSH_ERR_SYSTEM_ERROR; |
60 |
✗✓ |
39 |
if (atomicio(vwrite, fd, (u_char *)sshbuf_ptr(keybuf), |
61 |
|
26 |
sshbuf_len(keybuf)) != sshbuf_len(keybuf)) { |
62 |
|
|
oerrno = errno; |
63 |
|
|
close(fd); |
64 |
|
|
unlink(filename); |
65 |
|
|
errno = oerrno; |
66 |
|
|
return SSH_ERR_SYSTEM_ERROR; |
67 |
|
|
} |
68 |
|
13 |
close(fd); |
69 |
|
13 |
return 0; |
70 |
|
13 |
} |
71 |
|
|
|
72 |
|
|
int |
73 |
|
|
sshkey_save_private(struct sshkey *key, const char *filename, |
74 |
|
|
const char *passphrase, const char *comment, |
75 |
|
|
int force_new_format, const char *new_format_cipher, int new_format_rounds) |
76 |
|
|
{ |
77 |
|
|
struct sshbuf *keyblob = NULL; |
78 |
|
|
int r; |
79 |
|
|
|
80 |
✗✓ |
26 |
if ((keyblob = sshbuf_new()) == NULL) |
81 |
|
|
return SSH_ERR_ALLOC_FAIL; |
82 |
✓✗ |
26 |
if ((r = sshkey_private_to_fileblob(key, keyblob, passphrase, comment, |
83 |
|
13 |
force_new_format, new_format_cipher, new_format_rounds)) != 0) |
84 |
|
|
goto out; |
85 |
|
13 |
if ((r = sshkey_save_private_blob(keyblob, filename)) != 0) |
86 |
|
|
goto out; |
87 |
|
|
r = 0; |
88 |
|
|
out: |
89 |
|
13 |
sshbuf_free(keyblob); |
90 |
|
13 |
return r; |
91 |
|
13 |
} |
92 |
|
|
|
93 |
|
|
/* Load a key from a fd into a buffer */ |
94 |
|
|
int |
95 |
|
|
sshkey_load_file(int fd, struct sshbuf *blob) |
96 |
|
|
{ |
97 |
|
100 |
u_char buf[1024]; |
98 |
|
|
size_t len; |
99 |
|
50 |
struct stat st; |
100 |
|
|
int r; |
101 |
|
|
|
102 |
✗✓ |
50 |
if (fstat(fd, &st) < 0) |
103 |
|
|
return SSH_ERR_SYSTEM_ERROR; |
104 |
✗✓✗✗
|
50 |
if ((st.st_mode & (S_IFSOCK|S_IFCHR|S_IFIFO)) == 0 && |
105 |
|
|
st.st_size > MAX_KEY_FILE_SIZE) |
106 |
|
|
return SSH_ERR_INVALID_FORMAT; |
107 |
|
|
for (;;) { |
108 |
✓✓ |
121 |
if ((len = atomicio(read, fd, buf, sizeof(buf))) == 0) { |
109 |
✗✓ |
50 |
if (errno == EPIPE) |
110 |
|
|
break; |
111 |
|
|
r = SSH_ERR_SYSTEM_ERROR; |
112 |
|
|
goto out; |
113 |
|
|
} |
114 |
✓✗ |
71 |
if ((r = sshbuf_put(blob, buf, len)) != 0) |
115 |
|
|
goto out; |
116 |
✓✗ |
71 |
if (sshbuf_len(blob) > MAX_KEY_FILE_SIZE) { |
117 |
|
|
r = SSH_ERR_INVALID_FORMAT; |
118 |
|
|
goto out; |
119 |
|
|
} |
120 |
|
|
} |
121 |
✗✓✗✗
|
50 |
if ((st.st_mode & (S_IFSOCK|S_IFCHR|S_IFIFO)) == 0 && |
122 |
|
|
st.st_size != (off_t)sshbuf_len(blob)) { |
123 |
|
|
r = SSH_ERR_FILE_CHANGED; |
124 |
|
|
goto out; |
125 |
|
|
} |
126 |
|
50 |
r = 0; |
127 |
|
|
|
128 |
|
|
out: |
129 |
|
50 |
explicit_bzero(buf, sizeof(buf)); |
130 |
✗✓ |
50 |
if (r != 0) |
131 |
|
|
sshbuf_reset(blob); |
132 |
|
50 |
return r; |
133 |
|
50 |
} |
134 |
|
|
|
135 |
|
|
|
136 |
|
|
/* XXX remove error() calls from here? */ |
137 |
|
|
int |
138 |
|
|
sshkey_perm_ok(int fd, const char *filename) |
139 |
|
|
{ |
140 |
|
100 |
struct stat st; |
141 |
|
|
|
142 |
✗✓ |
50 |
if (fstat(fd, &st) < 0) |
143 |
|
|
return SSH_ERR_SYSTEM_ERROR; |
144 |
|
|
/* |
145 |
|
|
* if a key owned by the user is accessed, then we check the |
146 |
|
|
* permissions of the file. if the key owned by a different user, |
147 |
|
|
* then we don't care. |
148 |
|
|
*/ |
149 |
✓✗✗✓
|
100 |
if ((st.st_uid == getuid()) && (st.st_mode & 077) != 0) { |
150 |
|
|
error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); |
151 |
|
|
error("@ WARNING: UNPROTECTED PRIVATE KEY FILE! @"); |
152 |
|
|
error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); |
153 |
|
|
error("Permissions 0%3.3o for '%s' are too open.", |
154 |
|
|
(u_int)st.st_mode & 0777, filename); |
155 |
|
|
error("It is required that your private key files are NOT accessible by others."); |
156 |
|
|
error("This private key will be ignored."); |
157 |
|
|
return SSH_ERR_KEY_BAD_PERMISSIONS; |
158 |
|
|
} |
159 |
|
50 |
return 0; |
160 |
|
50 |
} |
161 |
|
|
|
162 |
|
|
/* XXX kill perm_ok now that we have SSH_ERR_KEY_BAD_PERMISSIONS? */ |
163 |
|
|
int |
164 |
|
|
sshkey_load_private_type(int type, const char *filename, const char *passphrase, |
165 |
|
|
struct sshkey **keyp, char **commentp, int *perm_ok) |
166 |
|
|
{ |
167 |
|
|
int fd, r; |
168 |
|
|
|
169 |
|
|
if (keyp != NULL) |
170 |
|
|
*keyp = NULL; |
171 |
|
|
if (commentp != NULL) |
172 |
|
|
*commentp = NULL; |
173 |
|
|
|
174 |
|
|
if ((fd = open(filename, O_RDONLY)) < 0) { |
175 |
|
|
if (perm_ok != NULL) |
176 |
|
|
*perm_ok = 0; |
177 |
|
|
return SSH_ERR_SYSTEM_ERROR; |
178 |
|
|
} |
179 |
|
|
if (sshkey_perm_ok(fd, filename) != 0) { |
180 |
|
|
if (perm_ok != NULL) |
181 |
|
|
*perm_ok = 0; |
182 |
|
|
r = SSH_ERR_KEY_BAD_PERMISSIONS; |
183 |
|
|
goto out; |
184 |
|
|
} |
185 |
|
|
if (perm_ok != NULL) |
186 |
|
|
*perm_ok = 1; |
187 |
|
|
|
188 |
|
|
r = sshkey_load_private_type_fd(fd, type, passphrase, keyp, commentp); |
189 |
|
|
out: |
190 |
|
|
close(fd); |
191 |
|
|
return r; |
192 |
|
|
} |
193 |
|
|
|
194 |
|
|
int |
195 |
|
|
sshkey_load_private_type_fd(int fd, int type, const char *passphrase, |
196 |
|
|
struct sshkey **keyp, char **commentp) |
197 |
|
|
{ |
198 |
|
|
struct sshbuf *buffer = NULL; |
199 |
|
|
int r; |
200 |
|
|
|
201 |
|
|
if (keyp != NULL) |
202 |
|
|
*keyp = NULL; |
203 |
|
|
if ((buffer = sshbuf_new()) == NULL) { |
204 |
|
|
r = SSH_ERR_ALLOC_FAIL; |
205 |
|
|
goto out; |
206 |
|
|
} |
207 |
|
|
if ((r = sshkey_load_file(fd, buffer)) != 0 || |
208 |
|
|
(r = sshkey_parse_private_fileblob_type(buffer, type, |
209 |
|
|
passphrase, keyp, commentp)) != 0) |
210 |
|
|
goto out; |
211 |
|
|
|
212 |
|
|
/* success */ |
213 |
|
|
r = 0; |
214 |
|
|
out: |
215 |
|
|
sshbuf_free(buffer); |
216 |
|
|
return r; |
217 |
|
|
} |
218 |
|
|
|
219 |
|
|
/* XXX this is almost identical to sshkey_load_private_type() */ |
220 |
|
|
int |
221 |
|
|
sshkey_load_private(const char *filename, const char *passphrase, |
222 |
|
|
struct sshkey **keyp, char **commentp) |
223 |
|
|
{ |
224 |
|
|
struct sshbuf *buffer = NULL; |
225 |
|
|
int r, fd; |
226 |
|
|
|
227 |
✓✗ |
100 |
if (keyp != NULL) |
228 |
|
50 |
*keyp = NULL; |
229 |
✗✓ |
50 |
if (commentp != NULL) |
230 |
|
|
*commentp = NULL; |
231 |
|
|
|
232 |
✗✓ |
50 |
if ((fd = open(filename, O_RDONLY)) < 0) |
233 |
|
|
return SSH_ERR_SYSTEM_ERROR; |
234 |
✗✓ |
50 |
if (sshkey_perm_ok(fd, filename) != 0) { |
235 |
|
|
r = SSH_ERR_KEY_BAD_PERMISSIONS; |
236 |
|
|
goto out; |
237 |
|
|
} |
238 |
|
|
|
239 |
✗✓ |
50 |
if ((buffer = sshbuf_new()) == NULL) { |
240 |
|
|
r = SSH_ERR_ALLOC_FAIL; |
241 |
|
|
goto out; |
242 |
|
|
} |
243 |
✓✗ |
100 |
if ((r = sshkey_load_file(fd, buffer)) != 0 || |
244 |
|
50 |
(r = sshkey_parse_private_fileblob(buffer, passphrase, keyp, |
245 |
|
50 |
commentp)) != 0) |
246 |
|
|
goto out; |
247 |
|
|
r = 0; |
248 |
|
|
out: |
249 |
|
50 |
close(fd); |
250 |
|
50 |
sshbuf_free(buffer); |
251 |
|
50 |
return r; |
252 |
|
50 |
} |
253 |
|
|
|
254 |
|
|
static int |
255 |
|
|
sshkey_try_load_public(struct sshkey *k, const char *filename, char **commentp) |
256 |
|
|
{ |
257 |
|
|
FILE *f; |
258 |
|
336 |
char line[SSH_MAX_PUBKEY_BYTES]; |
259 |
|
168 |
char *cp; |
260 |
|
168 |
u_long linenum = 0; |
261 |
|
|
int r; |
262 |
|
|
|
263 |
✓✓ |
168 |
if (commentp != NULL) |
264 |
|
64 |
*commentp = NULL; |
265 |
✓✓ |
168 |
if ((f = fopen(filename, "r")) == NULL) |
266 |
|
44 |
return SSH_ERR_SYSTEM_ERROR; |
267 |
✓✗ |
248 |
while (read_keyfile_line(f, filename, line, sizeof(line), |
268 |
|
124 |
&linenum) != -1) { |
269 |
|
191 |
cp = line; |
270 |
✗✗✓✓
|
191 |
switch (*cp) { |
271 |
|
|
case '#': |
272 |
|
|
case '\n': |
273 |
|
|
case '\0': |
274 |
|
|
continue; |
275 |
|
|
} |
276 |
|
|
/* Abort loading if this looks like a private key */ |
277 |
✓✓✓✗
|
174 |
if (strncmp(cp, "-----BEGIN", 10) == 0 || |
278 |
|
50 |
strcmp(cp, "SSH PRIVATE KEY FILE") == 0) |
279 |
|
|
break; |
280 |
|
|
/* Skip leading whitespace. */ |
281 |
✓✗✓✗ ✗✓ |
150 |
for (; *cp && (*cp == ' ' || *cp == '\t'); cp++) |
282 |
|
|
; |
283 |
✗✓ |
50 |
if (*cp) { |
284 |
✗✓ |
50 |
if ((r = sshkey_read(k, &cp)) == 0) { |
285 |
|
50 |
cp[strcspn(cp, "\r\n")] = '\0'; |
286 |
✓✓ |
50 |
if (commentp) { |
287 |
|
32 |
*commentp = strdup(*cp ? |
288 |
|
|
cp : filename); |
289 |
✗✓ |
32 |
if (*commentp == NULL) |
290 |
|
|
r = SSH_ERR_ALLOC_FAIL; |
291 |
|
|
} |
292 |
|
50 |
fclose(f); |
293 |
|
50 |
return r; |
294 |
|
|
} |
295 |
|
|
} |
296 |
|
|
} |
297 |
|
74 |
fclose(f); |
298 |
|
74 |
return SSH_ERR_INVALID_FORMAT; |
299 |
|
168 |
} |
300 |
|
|
|
301 |
|
|
/* load public key from any pubkey file */ |
302 |
|
|
int |
303 |
|
|
sshkey_load_public(const char *filename, struct sshkey **keyp, char **commentp) |
304 |
|
|
{ |
305 |
|
|
struct sshkey *pub = NULL; |
306 |
|
172 |
char *file = NULL; |
307 |
|
|
int r; |
308 |
|
|
|
309 |
✓✗ |
86 |
if (keyp != NULL) |
310 |
|
86 |
*keyp = NULL; |
311 |
✓✓ |
86 |
if (commentp != NULL) |
312 |
|
32 |
*commentp = NULL; |
313 |
|
|
|
314 |
✗✓ |
86 |
if ((pub = sshkey_new(KEY_UNSPEC)) == NULL) |
315 |
|
|
return SSH_ERR_ALLOC_FAIL; |
316 |
✓✓ |
86 |
if ((r = sshkey_try_load_public(pub, filename, commentp)) == 0) { |
317 |
✓✗ |
4 |
if (keyp != NULL) { |
318 |
|
4 |
*keyp = pub; |
319 |
|
|
pub = NULL; |
320 |
|
4 |
} |
321 |
|
|
r = 0; |
322 |
|
4 |
goto out; |
323 |
|
|
} |
324 |
|
82 |
sshkey_free(pub); |
325 |
|
|
|
326 |
|
|
/* try .pub suffix */ |
327 |
✗✓ |
82 |
if (asprintf(&file, "%s.pub", filename) == -1) |
328 |
|
|
return SSH_ERR_ALLOC_FAIL; |
329 |
✗✓ |
82 |
if ((pub = sshkey_new(KEY_UNSPEC)) == NULL) { |
330 |
|
|
r = SSH_ERR_ALLOC_FAIL; |
331 |
|
|
goto out; |
332 |
|
|
} |
333 |
✓✓ |
82 |
if ((r = sshkey_try_load_public(pub, file, commentp)) == 0) { |
334 |
✓✗ |
46 |
if (keyp != NULL) { |
335 |
|
46 |
*keyp = pub; |
336 |
|
|
pub = NULL; |
337 |
|
46 |
} |
338 |
|
|
r = 0; |
339 |
|
46 |
} |
340 |
|
|
out: |
341 |
|
86 |
free(file); |
342 |
|
86 |
sshkey_free(pub); |
343 |
|
86 |
return r; |
344 |
|
86 |
} |
345 |
|
|
|
346 |
|
|
/* Load the certificate associated with the named private key */ |
347 |
|
|
int |
348 |
|
|
sshkey_load_cert(const char *filename, struct sshkey **keyp) |
349 |
|
|
{ |
350 |
|
|
struct sshkey *pub = NULL; |
351 |
|
|
char *file = NULL; |
352 |
|
|
int r = SSH_ERR_INTERNAL_ERROR; |
353 |
|
|
|
354 |
|
|
if (keyp != NULL) |
355 |
|
|
*keyp = NULL; |
356 |
|
|
|
357 |
|
|
if (asprintf(&file, "%s-cert.pub", filename) == -1) |
358 |
|
|
return SSH_ERR_ALLOC_FAIL; |
359 |
|
|
|
360 |
|
|
if ((pub = sshkey_new(KEY_UNSPEC)) == NULL) { |
361 |
|
|
goto out; |
362 |
|
|
} |
363 |
|
|
if ((r = sshkey_try_load_public(pub, file, NULL)) != 0) |
364 |
|
|
goto out; |
365 |
|
|
/* success */ |
366 |
|
|
if (keyp != NULL) { |
367 |
|
|
*keyp = pub; |
368 |
|
|
pub = NULL; |
369 |
|
|
} |
370 |
|
|
r = 0; |
371 |
|
|
out: |
372 |
|
|
free(file); |
373 |
|
|
sshkey_free(pub); |
374 |
|
|
return r; |
375 |
|
|
} |
376 |
|
|
|
377 |
|
|
/* Load private key and certificate */ |
378 |
|
|
int |
379 |
|
|
sshkey_load_private_cert(int type, const char *filename, const char *passphrase, |
380 |
|
|
struct sshkey **keyp, int *perm_ok) |
381 |
|
|
{ |
382 |
|
|
struct sshkey *key = NULL, *cert = NULL; |
383 |
|
|
int r; |
384 |
|
|
|
385 |
|
|
if (keyp != NULL) |
386 |
|
|
*keyp = NULL; |
387 |
|
|
|
388 |
|
|
switch (type) { |
389 |
|
|
#ifdef WITH_OPENSSL |
390 |
|
|
case KEY_RSA: |
391 |
|
|
case KEY_DSA: |
392 |
|
|
case KEY_ECDSA: |
393 |
|
|
#endif /* WITH_OPENSSL */ |
394 |
|
|
case KEY_ED25519: |
395 |
|
|
case KEY_UNSPEC: |
396 |
|
|
break; |
397 |
|
|
default: |
398 |
|
|
return SSH_ERR_KEY_TYPE_UNKNOWN; |
399 |
|
|
} |
400 |
|
|
|
401 |
|
|
if ((r = sshkey_load_private_type(type, filename, |
402 |
|
|
passphrase, &key, NULL, perm_ok)) != 0 || |
403 |
|
|
(r = sshkey_load_cert(filename, &cert)) != 0) |
404 |
|
|
goto out; |
405 |
|
|
|
406 |
|
|
/* Make sure the private key matches the certificate */ |
407 |
|
|
if (sshkey_equal_public(key, cert) == 0) { |
408 |
|
|
r = SSH_ERR_KEY_CERT_MISMATCH; |
409 |
|
|
goto out; |
410 |
|
|
} |
411 |
|
|
|
412 |
|
|
if ((r = sshkey_to_certified(key)) != 0 || |
413 |
|
|
(r = sshkey_cert_copy(cert, key)) != 0) |
414 |
|
|
goto out; |
415 |
|
|
r = 0; |
416 |
|
|
if (keyp != NULL) { |
417 |
|
|
*keyp = key; |
418 |
|
|
key = NULL; |
419 |
|
|
} |
420 |
|
|
out: |
421 |
|
|
sshkey_free(key); |
422 |
|
|
sshkey_free(cert); |
423 |
|
|
return r; |
424 |
|
|
} |
425 |
|
|
|
426 |
|
|
/* |
427 |
|
|
* Returns success if the specified "key" is listed in the file "filename", |
428 |
|
|
* SSH_ERR_KEY_NOT_FOUND: if the key is not listed or another error. |
429 |
|
|
* If "strict_type" is set then the key type must match exactly, |
430 |
|
|
* otherwise a comparison that ignores certficiate data is performed. |
431 |
|
|
* If "check_ca" is set and "key" is a certificate, then its CA key is |
432 |
|
|
* also checked and sshkey_in_file() will return success if either is found. |
433 |
|
|
*/ |
434 |
|
|
int |
435 |
|
|
sshkey_in_file(struct sshkey *key, const char *filename, int strict_type, |
436 |
|
|
int check_ca) |
437 |
|
|
{ |
438 |
|
|
FILE *f; |
439 |
|
|
char line[SSH_MAX_PUBKEY_BYTES]; |
440 |
|
|
char *cp; |
441 |
|
|
u_long linenum = 0; |
442 |
|
|
int r = 0; |
443 |
|
|
struct sshkey *pub = NULL; |
444 |
|
|
int (*sshkey_compare)(const struct sshkey *, const struct sshkey *) = |
445 |
|
|
strict_type ? sshkey_equal : sshkey_equal_public; |
446 |
|
|
|
447 |
|
|
if ((f = fopen(filename, "r")) == NULL) |
448 |
|
|
return SSH_ERR_SYSTEM_ERROR; |
449 |
|
|
|
450 |
|
|
while (read_keyfile_line(f, filename, line, sizeof(line), |
451 |
|
|
&linenum) != -1) { |
452 |
|
|
cp = line; |
453 |
|
|
|
454 |
|
|
/* Skip leading whitespace. */ |
455 |
|
|
for (; *cp && (*cp == ' ' || *cp == '\t'); cp++) |
456 |
|
|
; |
457 |
|
|
|
458 |
|
|
/* Skip comments and empty lines */ |
459 |
|
|
switch (*cp) { |
460 |
|
|
case '#': |
461 |
|
|
case '\n': |
462 |
|
|
case '\0': |
463 |
|
|
continue; |
464 |
|
|
} |
465 |
|
|
|
466 |
|
|
if ((pub = sshkey_new(KEY_UNSPEC)) == NULL) { |
467 |
|
|
r = SSH_ERR_ALLOC_FAIL; |
468 |
|
|
goto out; |
469 |
|
|
} |
470 |
|
|
if ((r = sshkey_read(pub, &cp)) != 0) |
471 |
|
|
goto out; |
472 |
|
|
if (sshkey_compare(key, pub) || |
473 |
|
|
(check_ca && sshkey_is_cert(key) && |
474 |
|
|
sshkey_compare(key->cert->signature_key, pub))) { |
475 |
|
|
r = 0; |
476 |
|
|
goto out; |
477 |
|
|
} |
478 |
|
|
sshkey_free(pub); |
479 |
|
|
pub = NULL; |
480 |
|
|
} |
481 |
|
|
r = SSH_ERR_KEY_NOT_FOUND; |
482 |
|
|
out: |
483 |
|
|
sshkey_free(pub); |
484 |
|
|
fclose(f); |
485 |
|
|
return r; |
486 |
|
|
} |
487 |
|
|
|
488 |
|
|
/* |
489 |
|
|
* Checks whether the specified key is revoked, returning 0 if not, |
490 |
|
|
* SSH_ERR_KEY_REVOKED if it is or another error code if something |
491 |
|
|
* unexpected happened. |
492 |
|
|
* This will check both the key and, if it is a certificate, its CA key too. |
493 |
|
|
* "revoked_keys_file" may be a KRL or a one-per-line list of public keys. |
494 |
|
|
*/ |
495 |
|
|
int |
496 |
|
|
sshkey_check_revoked(struct sshkey *key, const char *revoked_keys_file) |
497 |
|
|
{ |
498 |
|
|
int r; |
499 |
|
|
|
500 |
|
|
r = ssh_krl_file_contains_key(revoked_keys_file, key); |
501 |
|
|
/* If this was not a KRL to begin with then continue below */ |
502 |
|
|
if (r != SSH_ERR_KRL_BAD_MAGIC) |
503 |
|
|
return r; |
504 |
|
|
|
505 |
|
|
/* |
506 |
|
|
* If the file is not a KRL or we can't handle KRLs then attempt to |
507 |
|
|
* parse the file as a flat list of keys. |
508 |
|
|
*/ |
509 |
|
|
switch ((r = sshkey_in_file(key, revoked_keys_file, 0, 1))) { |
510 |
|
|
case 0: |
511 |
|
|
/* Key found => revoked */ |
512 |
|
|
return SSH_ERR_KEY_REVOKED; |
513 |
|
|
case SSH_ERR_KEY_NOT_FOUND: |
514 |
|
|
/* Key not found => not revoked */ |
515 |
|
|
return 0; |
516 |
|
|
default: |
517 |
|
|
/* Some other error occurred */ |
518 |
|
|
return r; |
519 |
|
|
} |
520 |
|
|
} |
521 |
|
|
|