diff options
Diffstat (limited to 'authfile.c')
-rw-r--r-- | authfile.c | 1395 |
1 files changed, 321 insertions, 1074 deletions
diff --git a/authfile.c b/authfile.c index d7eaa9dec..e93d86738 100644 --- a/authfile.c +++ b/authfile.c | |||
@@ -1,18 +1,5 @@ | |||
1 | /* $OpenBSD: authfile.c,v 1.103 2014/02/02 03:44:31 djm Exp $ */ | 1 | /* $OpenBSD: authfile.c,v 1.107 2014/06/24 01:13:21 djm Exp $ */ |
2 | /* | 2 | /* |
3 | * Author: Tatu Ylonen <ylo@cs.hut.fi> | ||
4 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | ||
5 | * All rights reserved | ||
6 | * This file contains functions for reading and writing identity files, and | ||
7 | * for reading the passphrase from the user. | ||
8 | * | ||
9 | * As far as I am concerned, the code I have written for this software | ||
10 | * can be used freely for any purpose. Any derived versions of this | ||
11 | * software must be clearly marked as such, and if the derived work is | ||
12 | * incompatible with the protocol description in the RFC file, it must be | ||
13 | * called by a name other than "ssh" or "Secure Shell". | ||
14 | * | ||
15 | * | ||
16 | * Copyright (c) 2000, 2013 Markus Friedl. All rights reserved. | 3 | * Copyright (c) 2000, 2013 Markus Friedl. All rights reserved. |
17 | * | 4 | * |
18 | * Redistribution and use in source and binary forms, with or without | 5 | * Redistribution and use in source and binary forms, with or without |
@@ -43,30 +30,15 @@ | |||
43 | #include <sys/param.h> | 30 | #include <sys/param.h> |
44 | #include <sys/uio.h> | 31 | #include <sys/uio.h> |
45 | 32 | ||
46 | #include <openssl/err.h> | ||
47 | #include <openssl/evp.h> | ||
48 | #include <openssl/pem.h> | ||
49 | |||
50 | /* compatibility with old or broken OpenSSL versions */ | ||
51 | #include "openbsd-compat/openssl-compat.h" | ||
52 | |||
53 | #include "crypto_api.h" | ||
54 | |||
55 | #include <errno.h> | 33 | #include <errno.h> |
56 | #include <fcntl.h> | 34 | #include <fcntl.h> |
57 | #include <stdarg.h> | ||
58 | #include <stdio.h> | 35 | #include <stdio.h> |
36 | #include <stdarg.h> | ||
59 | #include <stdlib.h> | 37 | #include <stdlib.h> |
60 | #include <string.h> | 38 | #include <string.h> |
61 | #include <unistd.h> | 39 | #include <unistd.h> |
62 | 40 | ||
63 | #ifdef HAVE_UTIL_H | ||
64 | #include <util.h> | ||
65 | #endif | ||
66 | |||
67 | #include "xmalloc.h" | ||
68 | #include "cipher.h" | 41 | #include "cipher.h" |
69 | #include "buffer.h" | ||
70 | #include "key.h" | 42 | #include "key.h" |
71 | #include "ssh.h" | 43 | #include "ssh.h" |
72 | #include "log.h" | 44 | #include "log.h" |
@@ -74,903 +46,159 @@ | |||
74 | #include "rsa.h" | 46 | #include "rsa.h" |
75 | #include "misc.h" | 47 | #include "misc.h" |
76 | #include "atomicio.h" | 48 | #include "atomicio.h" |
77 | #include "uuencode.h" | 49 | #include "sshbuf.h" |
78 | 50 | #include "ssherr.h" | |
79 | /* openssh private key file format */ | ||
80 | #define MARK_BEGIN "-----BEGIN OPENSSH PRIVATE KEY-----\n" | ||
81 | #define MARK_END "-----END OPENSSH PRIVATE KEY-----\n" | ||
82 | #define KDFNAME "bcrypt" | ||
83 | #define AUTH_MAGIC "openssh-key-v1" | ||
84 | #define SALT_LEN 16 | ||
85 | #define DEFAULT_CIPHERNAME "aes256-cbc" | ||
86 | #define DEFAULT_ROUNDS 16 | ||
87 | 51 | ||
88 | #define MAX_KEY_FILE_SIZE (1024 * 1024) | 52 | #define MAX_KEY_FILE_SIZE (1024 * 1024) |
89 | 53 | ||
90 | /* Version identification string for SSH v1 identity files. */ | ||
91 | static const char authfile_id_string[] = | ||
92 | "SSH PRIVATE KEY FILE FORMAT 1.1\n"; | ||
93 | |||
94 | static int | ||
95 | key_private_to_blob2(Key *prv, Buffer *blob, const char *passphrase, | ||
96 | const char *comment, const char *ciphername, int rounds) | ||
97 | { | ||
98 | u_char *key, *cp, salt[SALT_LEN]; | ||
99 | size_t keylen, ivlen, blocksize, authlen; | ||
100 | u_int len, check; | ||
101 | int i, n; | ||
102 | const Cipher *c; | ||
103 | Buffer encoded, b, kdf; | ||
104 | CipherContext ctx; | ||
105 | const char *kdfname = KDFNAME; | ||
106 | |||
107 | if (rounds <= 0) | ||
108 | rounds = DEFAULT_ROUNDS; | ||
109 | if (passphrase == NULL || !strlen(passphrase)) { | ||
110 | ciphername = "none"; | ||
111 | kdfname = "none"; | ||
112 | } else if (ciphername == NULL) | ||
113 | ciphername = DEFAULT_CIPHERNAME; | ||
114 | else if (cipher_number(ciphername) != SSH_CIPHER_SSH2) | ||
115 | fatal("invalid cipher"); | ||
116 | |||
117 | if ((c = cipher_by_name(ciphername)) == NULL) | ||
118 | fatal("unknown cipher name"); | ||
119 | buffer_init(&kdf); | ||
120 | blocksize = cipher_blocksize(c); | ||
121 | keylen = cipher_keylen(c); | ||
122 | ivlen = cipher_ivlen(c); | ||
123 | authlen = cipher_authlen(c); | ||
124 | key = xcalloc(1, keylen + ivlen); | ||
125 | if (strcmp(kdfname, "none") != 0) { | ||
126 | arc4random_buf(salt, SALT_LEN); | ||
127 | if (bcrypt_pbkdf(passphrase, strlen(passphrase), | ||
128 | salt, SALT_LEN, key, keylen + ivlen, rounds) < 0) | ||
129 | fatal("bcrypt_pbkdf failed"); | ||
130 | buffer_put_string(&kdf, salt, SALT_LEN); | ||
131 | buffer_put_int(&kdf, rounds); | ||
132 | } | ||
133 | cipher_init(&ctx, c, key, keylen, key + keylen , ivlen, 1); | ||
134 | explicit_bzero(key, keylen + ivlen); | ||
135 | free(key); | ||
136 | |||
137 | buffer_init(&encoded); | ||
138 | buffer_append(&encoded, AUTH_MAGIC, sizeof(AUTH_MAGIC)); | ||
139 | buffer_put_cstring(&encoded, ciphername); | ||
140 | buffer_put_cstring(&encoded, kdfname); | ||
141 | buffer_put_string(&encoded, buffer_ptr(&kdf), buffer_len(&kdf)); | ||
142 | buffer_put_int(&encoded, 1); /* number of keys */ | ||
143 | key_to_blob(prv, &cp, &len); /* public key */ | ||
144 | buffer_put_string(&encoded, cp, len); | ||
145 | |||
146 | explicit_bzero(cp, len); | ||
147 | free(cp); | ||
148 | |||
149 | buffer_free(&kdf); | ||
150 | |||
151 | /* set up the buffer that will be encrypted */ | ||
152 | buffer_init(&b); | ||
153 | |||
154 | /* Random check bytes */ | ||
155 | check = arc4random(); | ||
156 | buffer_put_int(&b, check); | ||
157 | buffer_put_int(&b, check); | ||
158 | |||
159 | /* append private key and comment*/ | ||
160 | key_private_serialize(prv, &b); | ||
161 | buffer_put_cstring(&b, comment); | ||
162 | |||
163 | /* padding */ | ||
164 | i = 0; | ||
165 | while (buffer_len(&b) % blocksize) | ||
166 | buffer_put_char(&b, ++i & 0xff); | ||
167 | |||
168 | /* length */ | ||
169 | buffer_put_int(&encoded, buffer_len(&b)); | ||
170 | |||
171 | /* encrypt */ | ||
172 | cp = buffer_append_space(&encoded, buffer_len(&b) + authlen); | ||
173 | if (cipher_crypt(&ctx, 0, cp, buffer_ptr(&b), buffer_len(&b), 0, | ||
174 | authlen) != 0) | ||
175 | fatal("%s: cipher_crypt failed", __func__); | ||
176 | buffer_free(&b); | ||
177 | cipher_cleanup(&ctx); | ||
178 | |||
179 | /* uuencode */ | ||
180 | len = 2 * buffer_len(&encoded); | ||
181 | cp = xmalloc(len); | ||
182 | n = uuencode(buffer_ptr(&encoded), buffer_len(&encoded), | ||
183 | (char *)cp, len); | ||
184 | if (n < 0) | ||
185 | fatal("%s: uuencode", __func__); | ||
186 | |||
187 | buffer_clear(blob); | ||
188 | buffer_append(blob, MARK_BEGIN, sizeof(MARK_BEGIN) - 1); | ||
189 | for (i = 0; i < n; i++) { | ||
190 | buffer_put_char(blob, cp[i]); | ||
191 | if (i % 70 == 69) | ||
192 | buffer_put_char(blob, '\n'); | ||
193 | } | ||
194 | if (i % 70 != 69) | ||
195 | buffer_put_char(blob, '\n'); | ||
196 | buffer_append(blob, MARK_END, sizeof(MARK_END) - 1); | ||
197 | free(cp); | ||
198 | |||
199 | return buffer_len(blob); | ||
200 | } | ||
201 | |||
202 | static Key * | ||
203 | key_parse_private2(Buffer *blob, int type, const char *passphrase, | ||
204 | char **commentp) | ||
205 | { | ||
206 | u_char *key = NULL, *cp, *salt = NULL, pad, last; | ||
207 | char *comment = NULL, *ciphername = NULL, *kdfname = NULL, *kdfp; | ||
208 | u_int keylen = 0, ivlen, blocksize, slen, klen, len, rounds, nkeys; | ||
209 | u_int check1, check2, m1len, m2len; | ||
210 | size_t authlen; | ||
211 | const Cipher *c; | ||
212 | Buffer b, encoded, copy, kdf; | ||
213 | CipherContext ctx; | ||
214 | Key *k = NULL; | ||
215 | int dlen, ret, i; | ||
216 | |||
217 | buffer_init(&b); | ||
218 | buffer_init(&kdf); | ||
219 | buffer_init(&encoded); | ||
220 | buffer_init(©); | ||
221 | |||
222 | /* uudecode */ | ||
223 | m1len = sizeof(MARK_BEGIN) - 1; | ||
224 | m2len = sizeof(MARK_END) - 1; | ||
225 | cp = buffer_ptr(blob); | ||
226 | len = buffer_len(blob); | ||
227 | if (len < m1len || memcmp(cp, MARK_BEGIN, m1len)) { | ||
228 | debug("%s: missing begin marker", __func__); | ||
229 | goto out; | ||
230 | } | ||
231 | cp += m1len; | ||
232 | len -= m1len; | ||
233 | while (len) { | ||
234 | if (*cp != '\n' && *cp != '\r') | ||
235 | buffer_put_char(&encoded, *cp); | ||
236 | last = *cp; | ||
237 | len--; | ||
238 | cp++; | ||
239 | if (last == '\n') { | ||
240 | if (len >= m2len && !memcmp(cp, MARK_END, m2len)) { | ||
241 | buffer_put_char(&encoded, '\0'); | ||
242 | break; | ||
243 | } | ||
244 | } | ||
245 | } | ||
246 | if (!len) { | ||
247 | debug("%s: no end marker", __func__); | ||
248 | goto out; | ||
249 | } | ||
250 | len = buffer_len(&encoded); | ||
251 | if ((cp = buffer_append_space(©, len)) == NULL) { | ||
252 | error("%s: buffer_append_space", __func__); | ||
253 | goto out; | ||
254 | } | ||
255 | if ((dlen = uudecode(buffer_ptr(&encoded), cp, len)) < 0) { | ||
256 | error("%s: uudecode failed", __func__); | ||
257 | goto out; | ||
258 | } | ||
259 | if ((u_int)dlen > len) { | ||
260 | error("%s: crazy uudecode length %d > %u", __func__, dlen, len); | ||
261 | goto out; | ||
262 | } | ||
263 | buffer_consume_end(©, len - dlen); | ||
264 | if (buffer_len(©) < sizeof(AUTH_MAGIC) || | ||
265 | memcmp(buffer_ptr(©), AUTH_MAGIC, sizeof(AUTH_MAGIC))) { | ||
266 | error("%s: bad magic", __func__); | ||
267 | goto out; | ||
268 | } | ||
269 | buffer_consume(©, sizeof(AUTH_MAGIC)); | ||
270 | |||
271 | ciphername = buffer_get_cstring_ret(©, NULL); | ||
272 | if (ciphername == NULL || | ||
273 | (c = cipher_by_name(ciphername)) == NULL) { | ||
274 | error("%s: unknown cipher name", __func__); | ||
275 | goto out; | ||
276 | } | ||
277 | if ((passphrase == NULL || !strlen(passphrase)) && | ||
278 | strcmp(ciphername, "none") != 0) { | ||
279 | /* passphrase required */ | ||
280 | goto out; | ||
281 | } | ||
282 | kdfname = buffer_get_cstring_ret(©, NULL); | ||
283 | if (kdfname == NULL || | ||
284 | (!strcmp(kdfname, "none") && !strcmp(kdfname, "bcrypt"))) { | ||
285 | error("%s: unknown kdf name", __func__); | ||
286 | goto out; | ||
287 | } | ||
288 | if (!strcmp(kdfname, "none") && strcmp(ciphername, "none") != 0) { | ||
289 | error("%s: cipher %s requires kdf", __func__, ciphername); | ||
290 | goto out; | ||
291 | } | ||
292 | /* kdf options */ | ||
293 | kdfp = buffer_get_string_ptr_ret(©, &klen); | ||
294 | if (kdfp == NULL) { | ||
295 | error("%s: kdf options not set", __func__); | ||
296 | goto out; | ||
297 | } | ||
298 | if (klen > 0) { | ||
299 | if ((cp = buffer_append_space(&kdf, klen)) == NULL) { | ||
300 | error("%s: kdf alloc failed", __func__); | ||
301 | goto out; | ||
302 | } | ||
303 | memcpy(cp, kdfp, klen); | ||
304 | } | ||
305 | /* number of keys */ | ||
306 | if (buffer_get_int_ret(&nkeys, ©) < 0) { | ||
307 | error("%s: key counter missing", __func__); | ||
308 | goto out; | ||
309 | } | ||
310 | if (nkeys != 1) { | ||
311 | error("%s: only one key supported", __func__); | ||
312 | goto out; | ||
313 | } | ||
314 | /* pubkey */ | ||
315 | if ((cp = buffer_get_string_ret(©, &len)) == NULL) { | ||
316 | error("%s: pubkey not found", __func__); | ||
317 | goto out; | ||
318 | } | ||
319 | free(cp); /* XXX check pubkey against decrypted private key */ | ||
320 | |||
321 | /* size of encrypted key blob */ | ||
322 | len = buffer_get_int(©); | ||
323 | blocksize = cipher_blocksize(c); | ||
324 | authlen = cipher_authlen(c); | ||
325 | if (len < blocksize) { | ||
326 | error("%s: encrypted data too small", __func__); | ||
327 | goto out; | ||
328 | } | ||
329 | if (len % blocksize) { | ||
330 | error("%s: length not multiple of blocksize", __func__); | ||
331 | goto out; | ||
332 | } | ||
333 | |||
334 | /* setup key */ | ||
335 | keylen = cipher_keylen(c); | ||
336 | ivlen = cipher_ivlen(c); | ||
337 | key = xcalloc(1, keylen + ivlen); | ||
338 | if (!strcmp(kdfname, "bcrypt")) { | ||
339 | if ((salt = buffer_get_string_ret(&kdf, &slen)) == NULL) { | ||
340 | error("%s: salt not set", __func__); | ||
341 | goto out; | ||
342 | } | ||
343 | if (buffer_get_int_ret(&rounds, &kdf) < 0) { | ||
344 | error("%s: rounds not set", __func__); | ||
345 | goto out; | ||
346 | } | ||
347 | if (bcrypt_pbkdf(passphrase, strlen(passphrase), salt, slen, | ||
348 | key, keylen + ivlen, rounds) < 0) { | ||
349 | error("%s: bcrypt_pbkdf failed", __func__); | ||
350 | goto out; | ||
351 | } | ||
352 | } | ||
353 | |||
354 | cp = buffer_append_space(&b, len); | ||
355 | cipher_init(&ctx, c, key, keylen, key + keylen, ivlen, 0); | ||
356 | ret = cipher_crypt(&ctx, 0, cp, buffer_ptr(©), len, 0, authlen); | ||
357 | cipher_cleanup(&ctx); | ||
358 | buffer_consume(©, len); | ||
359 | |||
360 | /* fail silently on decryption errors */ | ||
361 | if (ret != 0) { | ||
362 | debug("%s: decrypt failed", __func__); | ||
363 | goto out; | ||
364 | } | ||
365 | |||
366 | if (buffer_len(©) != 0) { | ||
367 | error("%s: key blob has trailing data (len = %u)", __func__, | ||
368 | buffer_len(©)); | ||
369 | goto out; | ||
370 | } | ||
371 | |||
372 | /* check bytes */ | ||
373 | if (buffer_get_int_ret(&check1, &b) < 0 || | ||
374 | buffer_get_int_ret(&check2, &b) < 0) { | ||
375 | error("check bytes missing"); | ||
376 | goto out; | ||
377 | } | ||
378 | if (check1 != check2) { | ||
379 | debug("%s: decrypt failed: 0x%08x != 0x%08x", __func__, | ||
380 | check1, check2); | ||
381 | goto out; | ||
382 | } | ||
383 | |||
384 | k = key_private_deserialize(&b); | ||
385 | |||
386 | /* comment */ | ||
387 | comment = buffer_get_cstring_ret(&b, NULL); | ||
388 | |||
389 | i = 0; | ||
390 | while (buffer_len(&b)) { | ||
391 | if (buffer_get_char_ret(&pad, &b) == -1 || | ||
392 | pad != (++i & 0xff)) { | ||
393 | error("%s: bad padding", __func__); | ||
394 | key_free(k); | ||
395 | k = NULL; | ||
396 | goto out; | ||
397 | } | ||
398 | } | ||
399 | |||
400 | if (k && commentp) { | ||
401 | *commentp = comment; | ||
402 | comment = NULL; | ||
403 | } | ||
404 | |||
405 | /* XXX decode pubkey and check against private */ | ||
406 | out: | ||
407 | free(ciphername); | ||
408 | free(kdfname); | ||
409 | free(salt); | ||
410 | free(comment); | ||
411 | if (key) | ||
412 | explicit_bzero(key, keylen + ivlen); | ||
413 | free(key); | ||
414 | buffer_free(&encoded); | ||
415 | buffer_free(©); | ||
416 | buffer_free(&kdf); | ||
417 | buffer_free(&b); | ||
418 | return k; | ||
419 | } | ||
420 | |||
421 | /* | ||
422 | * Serialises the authentication (private) key to a blob, encrypting it with | ||
423 | * passphrase. The identification of the blob (lowest 64 bits of n) will | ||
424 | * precede the key to provide identification of the key without needing a | ||
425 | * passphrase. | ||
426 | */ | ||
427 | static int | ||
428 | key_private_rsa1_to_blob(Key *key, Buffer *blob, const char *passphrase, | ||
429 | const char *comment) | ||
430 | { | ||
431 | Buffer buffer, encrypted; | ||
432 | u_char buf[100], *cp; | ||
433 | int i, cipher_num; | ||
434 | CipherContext ciphercontext; | ||
435 | const Cipher *cipher; | ||
436 | u_int32_t rnd; | ||
437 | |||
438 | /* | ||
439 | * If the passphrase is empty, use SSH_CIPHER_NONE to ease converting | ||
440 | * to another cipher; otherwise use SSH_AUTHFILE_CIPHER. | ||
441 | */ | ||
442 | cipher_num = (strcmp(passphrase, "") == 0) ? | ||
443 | SSH_CIPHER_NONE : SSH_AUTHFILE_CIPHER; | ||
444 | if ((cipher = cipher_by_number(cipher_num)) == NULL) | ||
445 | fatal("save_private_key_rsa: bad cipher"); | ||
446 | |||
447 | /* This buffer is used to built the secret part of the private key. */ | ||
448 | buffer_init(&buffer); | ||
449 | |||
450 | /* Put checkbytes for checking passphrase validity. */ | ||
451 | rnd = arc4random(); | ||
452 | buf[0] = rnd & 0xff; | ||
453 | buf[1] = (rnd >> 8) & 0xff; | ||
454 | buf[2] = buf[0]; | ||
455 | buf[3] = buf[1]; | ||
456 | buffer_append(&buffer, buf, 4); | ||
457 | |||
458 | /* | ||
459 | * Store the private key (n and e will not be stored because they | ||
460 | * will be stored in plain text, and storing them also in encrypted | ||
461 | * format would just give known plaintext). | ||
462 | */ | ||
463 | buffer_put_bignum(&buffer, key->rsa->d); | ||
464 | buffer_put_bignum(&buffer, key->rsa->iqmp); | ||
465 | buffer_put_bignum(&buffer, key->rsa->q); /* reverse from SSL p */ | ||
466 | buffer_put_bignum(&buffer, key->rsa->p); /* reverse from SSL q */ | ||
467 | |||
468 | /* Pad the part to be encrypted until its size is a multiple of 8. */ | ||
469 | while (buffer_len(&buffer) % 8 != 0) | ||
470 | buffer_put_char(&buffer, 0); | ||
471 | |||
472 | /* This buffer will be used to contain the data in the file. */ | ||
473 | buffer_init(&encrypted); | ||
474 | |||
475 | /* First store keyfile id string. */ | ||
476 | for (i = 0; authfile_id_string[i]; i++) | ||
477 | buffer_put_char(&encrypted, authfile_id_string[i]); | ||
478 | buffer_put_char(&encrypted, 0); | ||
479 | |||
480 | /* Store cipher type. */ | ||
481 | buffer_put_char(&encrypted, cipher_num); | ||
482 | buffer_put_int(&encrypted, 0); /* For future extension */ | ||
483 | |||
484 | /* Store public key. This will be in plain text. */ | ||
485 | buffer_put_int(&encrypted, BN_num_bits(key->rsa->n)); | ||
486 | buffer_put_bignum(&encrypted, key->rsa->n); | ||
487 | buffer_put_bignum(&encrypted, key->rsa->e); | ||
488 | buffer_put_cstring(&encrypted, comment); | ||
489 | |||
490 | /* Allocate space for the private part of the key in the buffer. */ | ||
491 | cp = buffer_append_space(&encrypted, buffer_len(&buffer)); | ||
492 | |||
493 | cipher_set_key_string(&ciphercontext, cipher, passphrase, | ||
494 | CIPHER_ENCRYPT); | ||
495 | if (cipher_crypt(&ciphercontext, 0, cp, | ||
496 | buffer_ptr(&buffer), buffer_len(&buffer), 0, 0) != 0) | ||
497 | fatal("%s: cipher_crypt failed", __func__); | ||
498 | cipher_cleanup(&ciphercontext); | ||
499 | explicit_bzero(&ciphercontext, sizeof(ciphercontext)); | ||
500 | |||
501 | /* Destroy temporary data. */ | ||
502 | explicit_bzero(buf, sizeof(buf)); | ||
503 | buffer_free(&buffer); | ||
504 | |||
505 | buffer_append(blob, buffer_ptr(&encrypted), buffer_len(&encrypted)); | ||
506 | buffer_free(&encrypted); | ||
507 | |||
508 | return 1; | ||
509 | } | ||
510 | |||
511 | /* convert SSH v2 key in OpenSSL PEM format */ | ||
512 | static int | ||
513 | key_private_pem_to_blob(Key *key, Buffer *blob, const char *_passphrase, | ||
514 | const char *comment) | ||
515 | { | ||
516 | int success = 0; | ||
517 | int blen, len = strlen(_passphrase); | ||
518 | u_char *passphrase = (len > 0) ? (u_char *)_passphrase : NULL; | ||
519 | #if (OPENSSL_VERSION_NUMBER < 0x00907000L) | ||
520 | const EVP_CIPHER *cipher = (len > 0) ? EVP_des_ede3_cbc() : NULL; | ||
521 | #else | ||
522 | const EVP_CIPHER *cipher = (len > 0) ? EVP_aes_128_cbc() : NULL; | ||
523 | #endif | ||
524 | const u_char *bptr; | ||
525 | BIO *bio; | ||
526 | |||
527 | if (len > 0 && len <= 4) { | ||
528 | error("passphrase too short: have %d bytes, need > 4", len); | ||
529 | return 0; | ||
530 | } | ||
531 | if ((bio = BIO_new(BIO_s_mem())) == NULL) { | ||
532 | error("%s: BIO_new failed", __func__); | ||
533 | return 0; | ||
534 | } | ||
535 | switch (key->type) { | ||
536 | case KEY_DSA: | ||
537 | success = PEM_write_bio_DSAPrivateKey(bio, key->dsa, | ||
538 | cipher, passphrase, len, NULL, NULL); | ||
539 | break; | ||
540 | #ifdef OPENSSL_HAS_ECC | ||
541 | case KEY_ECDSA: | ||
542 | success = PEM_write_bio_ECPrivateKey(bio, key->ecdsa, | ||
543 | cipher, passphrase, len, NULL, NULL); | ||
544 | break; | ||
545 | #endif | ||
546 | case KEY_RSA: | ||
547 | success = PEM_write_bio_RSAPrivateKey(bio, key->rsa, | ||
548 | cipher, passphrase, len, NULL, NULL); | ||
549 | break; | ||
550 | } | ||
551 | if (success) { | ||
552 | if ((blen = BIO_get_mem_data(bio, &bptr)) <= 0) | ||
553 | success = 0; | ||
554 | else | ||
555 | buffer_append(blob, bptr, blen); | ||
556 | } | ||
557 | BIO_free(bio); | ||
558 | return success; | ||
559 | } | ||
560 | |||
561 | /* Save a key blob to a file */ | 54 | /* Save a key blob to a file */ |
562 | static int | 55 | static int |
563 | key_save_private_blob(Buffer *keybuf, const char *filename) | 56 | sshkey_save_private_blob(struct sshbuf *keybuf, const char *filename) |
564 | { | 57 | { |
565 | int fd; | 58 | int fd, oerrno; |
566 | 59 | ||
567 | if ((fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0600)) < 0) { | 60 | if ((fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0600)) < 0) |
568 | error("open %s failed: %s.", filename, strerror(errno)); | 61 | return SSH_ERR_SYSTEM_ERROR; |
569 | return 0; | 62 | if (atomicio(vwrite, fd, (u_char *)sshbuf_ptr(keybuf), |
570 | } | 63 | sshbuf_len(keybuf)) != sshbuf_len(keybuf)) { |
571 | if (atomicio(vwrite, fd, buffer_ptr(keybuf), | 64 | oerrno = errno; |
572 | buffer_len(keybuf)) != buffer_len(keybuf)) { | ||
573 | error("write to key file %s failed: %s", filename, | ||
574 | strerror(errno)); | ||
575 | close(fd); | 65 | close(fd); |
576 | unlink(filename); | 66 | unlink(filename); |
577 | return 0; | 67 | errno = oerrno; |
68 | return SSH_ERR_SYSTEM_ERROR; | ||
578 | } | 69 | } |
579 | close(fd); | 70 | close(fd); |
580 | return 1; | 71 | return 0; |
581 | } | ||
582 | |||
583 | /* Serialise "key" to buffer "blob" */ | ||
584 | static int | ||
585 | key_private_to_blob(Key *key, Buffer *blob, const char *passphrase, | ||
586 | const char *comment, int force_new_format, const char *new_format_cipher, | ||
587 | int new_format_rounds) | ||
588 | { | ||
589 | switch (key->type) { | ||
590 | case KEY_RSA1: | ||
591 | return key_private_rsa1_to_blob(key, blob, passphrase, comment); | ||
592 | case KEY_DSA: | ||
593 | case KEY_ECDSA: | ||
594 | case KEY_RSA: | ||
595 | if (force_new_format) { | ||
596 | return key_private_to_blob2(key, blob, passphrase, | ||
597 | comment, new_format_cipher, new_format_rounds); | ||
598 | } | ||
599 | return key_private_pem_to_blob(key, blob, passphrase, comment); | ||
600 | case KEY_ED25519: | ||
601 | return key_private_to_blob2(key, blob, passphrase, | ||
602 | comment, new_format_cipher, new_format_rounds); | ||
603 | default: | ||
604 | error("%s: cannot save key type %d", __func__, key->type); | ||
605 | return 0; | ||
606 | } | ||
607 | } | 72 | } |
608 | 73 | ||
609 | int | 74 | int |
610 | key_save_private(Key *key, const char *filename, const char *passphrase, | 75 | sshkey_save_private(struct sshkey *key, const char *filename, |
611 | const char *comment, int force_new_format, const char *new_format_cipher, | 76 | const char *passphrase, const char *comment, |
612 | int new_format_rounds) | 77 | int force_new_format, const char *new_format_cipher, int new_format_rounds) |
613 | { | 78 | { |
614 | Buffer keyblob; | 79 | struct sshbuf *keyblob = NULL; |
615 | int success = 0; | 80 | int r; |
616 | 81 | ||
617 | buffer_init(&keyblob); | 82 | if ((keyblob = sshbuf_new()) == NULL) |
618 | if (!key_private_to_blob(key, &keyblob, passphrase, comment, | 83 | return SSH_ERR_ALLOC_FAIL; |
619 | force_new_format, new_format_cipher, new_format_rounds)) | 84 | if ((r = sshkey_private_to_fileblob(key, keyblob, passphrase, comment, |
85 | force_new_format, new_format_cipher, new_format_rounds)) != 0) | ||
620 | goto out; | 86 | goto out; |
621 | if (!key_save_private_blob(&keyblob, filename)) | 87 | if ((r = sshkey_save_private_blob(keyblob, filename)) != 0) |
622 | goto out; | 88 | goto out; |
623 | success = 1; | 89 | r = 0; |
624 | out: | 90 | out: |
625 | buffer_free(&keyblob); | 91 | sshbuf_free(keyblob); |
626 | return success; | 92 | return r; |
627 | } | ||
628 | |||
629 | /* | ||
630 | * Parse the public, unencrypted portion of a RSA1 key. | ||
631 | */ | ||
632 | static Key * | ||
633 | key_parse_public_rsa1(Buffer *blob, char **commentp) | ||
634 | { | ||
635 | Key *pub; | ||
636 | Buffer copy; | ||
637 | |||
638 | /* Check that it is at least big enough to contain the ID string. */ | ||
639 | if (buffer_len(blob) < sizeof(authfile_id_string)) { | ||
640 | debug3("Truncated RSA1 identifier"); | ||
641 | return NULL; | ||
642 | } | ||
643 | |||
644 | /* | ||
645 | * Make sure it begins with the id string. Consume the id string | ||
646 | * from the buffer. | ||
647 | */ | ||
648 | if (memcmp(buffer_ptr(blob), authfile_id_string, | ||
649 | sizeof(authfile_id_string)) != 0) { | ||
650 | debug3("Incorrect RSA1 identifier"); | ||
651 | return NULL; | ||
652 | } | ||
653 | buffer_init(©); | ||
654 | buffer_append(©, buffer_ptr(blob), buffer_len(blob)); | ||
655 | buffer_consume(©, sizeof(authfile_id_string)); | ||
656 | |||
657 | /* Skip cipher type and reserved data. */ | ||
658 | (void) buffer_get_char(©); /* cipher type */ | ||
659 | (void) buffer_get_int(©); /* reserved */ | ||
660 | |||
661 | /* Read the public key from the buffer. */ | ||
662 | (void) buffer_get_int(©); | ||
663 | pub = key_new(KEY_RSA1); | ||
664 | buffer_get_bignum(©, pub->rsa->n); | ||
665 | buffer_get_bignum(©, pub->rsa->e); | ||
666 | if (commentp) | ||
667 | *commentp = buffer_get_string(©, NULL); | ||
668 | /* The encrypted private part is not parsed by this function. */ | ||
669 | buffer_free(©); | ||
670 | |||
671 | return pub; | ||
672 | } | 93 | } |
673 | 94 | ||
674 | /* Load a key from a fd into a buffer */ | 95 | /* Load a key from a fd into a buffer */ |
675 | int | 96 | int |
676 | key_load_file(int fd, const char *filename, Buffer *blob) | 97 | sshkey_load_file(int fd, const char *filename, struct sshbuf *blob) |
677 | { | 98 | { |
678 | u_char buf[1024]; | 99 | u_char buf[1024]; |
679 | size_t len; | 100 | size_t len; |
680 | struct stat st; | 101 | struct stat st; |
102 | int r; | ||
681 | 103 | ||
682 | if (fstat(fd, &st) < 0) { | 104 | if (fstat(fd, &st) < 0) |
683 | error("%s: fstat of key file %.200s%sfailed: %.100s", __func__, | 105 | return SSH_ERR_SYSTEM_ERROR; |
684 | filename == NULL ? "" : filename, | ||
685 | filename == NULL ? "" : " ", | ||
686 | strerror(errno)); | ||
687 | return 0; | ||
688 | } | ||
689 | if ((st.st_mode & (S_IFSOCK|S_IFCHR|S_IFIFO)) == 0 && | 106 | if ((st.st_mode & (S_IFSOCK|S_IFCHR|S_IFIFO)) == 0 && |
690 | st.st_size > MAX_KEY_FILE_SIZE) { | 107 | st.st_size > MAX_KEY_FILE_SIZE) |
691 | toobig: | 108 | return SSH_ERR_INVALID_FORMAT; |
692 | error("%s: key file %.200s%stoo large", __func__, | ||
693 | filename == NULL ? "" : filename, | ||
694 | filename == NULL ? "" : " "); | ||
695 | return 0; | ||
696 | } | ||
697 | buffer_clear(blob); | ||
698 | for (;;) { | 109 | for (;;) { |
699 | if ((len = atomicio(read, fd, buf, sizeof(buf))) == 0) { | 110 | if ((len = atomicio(read, fd, buf, sizeof(buf))) == 0) { |
700 | if (errno == EPIPE) | 111 | if (errno == EPIPE) |
701 | break; | 112 | break; |
702 | debug("%s: read from key file %.200s%sfailed: %.100s", | 113 | r = SSH_ERR_SYSTEM_ERROR; |
703 | __func__, filename == NULL ? "" : filename, | 114 | goto out; |
704 | filename == NULL ? "" : " ", strerror(errno)); | ||
705 | buffer_clear(blob); | ||
706 | explicit_bzero(buf, sizeof(buf)); | ||
707 | return 0; | ||
708 | } | 115 | } |
709 | buffer_append(blob, buf, len); | 116 | if ((r = sshbuf_put(blob, buf, len)) != 0) |
710 | if (buffer_len(blob) > MAX_KEY_FILE_SIZE) { | 117 | goto out; |
711 | buffer_clear(blob); | 118 | if (sshbuf_len(blob) > MAX_KEY_FILE_SIZE) { |
712 | explicit_bzero(buf, sizeof(buf)); | 119 | r = SSH_ERR_INVALID_FORMAT; |
713 | goto toobig; | 120 | goto out; |
714 | } | 121 | } |
715 | } | 122 | } |
716 | explicit_bzero(buf, sizeof(buf)); | ||
717 | if ((st.st_mode & (S_IFSOCK|S_IFCHR|S_IFIFO)) == 0 && | 123 | if ((st.st_mode & (S_IFSOCK|S_IFCHR|S_IFIFO)) == 0 && |
718 | st.st_size != buffer_len(blob)) { | 124 | st.st_size != (off_t)sshbuf_len(blob)) { |
719 | debug("%s: key file %.200s%schanged size while reading", | 125 | r = SSH_ERR_FILE_CHANGED; |
720 | __func__, filename == NULL ? "" : filename, | 126 | goto out; |
721 | filename == NULL ? "" : " "); | ||
722 | buffer_clear(blob); | ||
723 | return 0; | ||
724 | } | 127 | } |
128 | r = 0; | ||
725 | 129 | ||
726 | return 1; | 130 | out: |
131 | explicit_bzero(buf, sizeof(buf)); | ||
132 | if (r != 0) | ||
133 | sshbuf_reset(blob); | ||
134 | return r; | ||
727 | } | 135 | } |
728 | 136 | ||
137 | #ifdef WITH_SSH1 | ||
729 | /* | 138 | /* |
730 | * Loads the public part of the ssh v1 key file. Returns NULL if an error was | 139 | * Loads the public part of the ssh v1 key file. Returns NULL if an error was |
731 | * encountered (the file does not exist or is not readable), and the key | 140 | * encountered (the file does not exist or is not readable), and the key |
732 | * otherwise. | 141 | * otherwise. |
733 | */ | 142 | */ |
734 | static Key * | 143 | static int |
735 | key_load_public_rsa1(int fd, const char *filename, char **commentp) | 144 | sshkey_load_public_rsa1(int fd, const char *filename, |
145 | struct sshkey **keyp, char **commentp) | ||
736 | { | 146 | { |
737 | Buffer buffer; | 147 | struct sshbuf *b = NULL; |
738 | Key *pub; | 148 | int r; |
739 | |||
740 | buffer_init(&buffer); | ||
741 | if (!key_load_file(fd, filename, &buffer)) { | ||
742 | buffer_free(&buffer); | ||
743 | return NULL; | ||
744 | } | ||
745 | 149 | ||
746 | pub = key_parse_public_rsa1(&buffer, commentp); | 150 | *keyp = NULL; |
747 | if (pub == NULL) | 151 | if (commentp != NULL) |
748 | debug3("Could not load \"%s\" as a RSA1 public key", filename); | 152 | *commentp = NULL; |
749 | buffer_free(&buffer); | ||
750 | return pub; | ||
751 | } | ||
752 | |||
753 | /* load public key from private-key file, works only for SSH v1 */ | ||
754 | Key * | ||
755 | key_load_public_type(int type, const char *filename, char **commentp) | ||
756 | { | ||
757 | Key *pub; | ||
758 | int fd; | ||
759 | 153 | ||
760 | if (type == KEY_RSA1) { | 154 | if ((b = sshbuf_new()) == NULL) |
761 | fd = open(filename, O_RDONLY); | 155 | return SSH_ERR_ALLOC_FAIL; |
762 | if (fd < 0) | 156 | if ((r = sshkey_load_file(fd, filename, b)) != 0) |
763 | return NULL; | 157 | goto out; |
764 | pub = key_load_public_rsa1(fd, filename, commentp); | 158 | if ((r = sshkey_parse_public_rsa1_fileblob(b, keyp, commentp)) != 0) |
765 | close(fd); | 159 | goto out; |
766 | return pub; | 160 | r = 0; |
767 | } | 161 | out: |
768 | return NULL; | 162 | sshbuf_free(b); |
163 | return r; | ||
769 | } | 164 | } |
165 | #endif /* WITH_SSH1 */ | ||
770 | 166 | ||
771 | static Key * | 167 | #ifdef WITH_OPENSSL |
772 | key_parse_private_rsa1(Buffer *blob, const char *passphrase, char **commentp) | 168 | /* XXX Deprecate? */ |
169 | int | ||
170 | sshkey_load_private_pem(int fd, int type, const char *passphrase, | ||
171 | struct sshkey **keyp, char **commentp) | ||
773 | { | 172 | { |
774 | int check1, check2, cipher_type; | 173 | struct sshbuf *buffer = NULL; |
775 | Buffer decrypted; | 174 | int r; |
776 | u_char *cp; | ||
777 | CipherContext ciphercontext; | ||
778 | const Cipher *cipher; | ||
779 | Key *prv = NULL; | ||
780 | Buffer copy; | ||
781 | |||
782 | /* Check that it is at least big enough to contain the ID string. */ | ||
783 | if (buffer_len(blob) < sizeof(authfile_id_string)) { | ||
784 | debug3("Truncated RSA1 identifier"); | ||
785 | return NULL; | ||
786 | } | ||
787 | |||
788 | /* | ||
789 | * Make sure it begins with the id string. Consume the id string | ||
790 | * from the buffer. | ||
791 | */ | ||
792 | if (memcmp(buffer_ptr(blob), authfile_id_string, | ||
793 | sizeof(authfile_id_string)) != 0) { | ||
794 | debug3("Incorrect RSA1 identifier"); | ||
795 | return NULL; | ||
796 | } | ||
797 | buffer_init(©); | ||
798 | buffer_append(©, buffer_ptr(blob), buffer_len(blob)); | ||
799 | buffer_consume(©, sizeof(authfile_id_string)); | ||
800 | |||
801 | /* Read cipher type. */ | ||
802 | cipher_type = buffer_get_char(©); | ||
803 | (void) buffer_get_int(©); /* Reserved data. */ | ||
804 | |||
805 | /* Read the public key from the buffer. */ | ||
806 | (void) buffer_get_int(©); | ||
807 | prv = key_new_private(KEY_RSA1); | ||
808 | |||
809 | buffer_get_bignum(©, prv->rsa->n); | ||
810 | buffer_get_bignum(©, prv->rsa->e); | ||
811 | if (commentp) | ||
812 | *commentp = buffer_get_string(©, NULL); | ||
813 | else | ||
814 | (void)buffer_get_string_ptr(©, NULL); | ||
815 | 175 | ||
816 | /* Check that it is a supported cipher. */ | 176 | *keyp = NULL; |
817 | cipher = cipher_by_number(cipher_type); | ||
818 | if (cipher == NULL) { | ||
819 | debug("Unsupported RSA1 cipher %d", cipher_type); | ||
820 | buffer_free(©); | ||
821 | goto fail; | ||
822 | } | ||
823 | /* Initialize space for decrypted data. */ | ||
824 | buffer_init(&decrypted); | ||
825 | cp = buffer_append_space(&decrypted, buffer_len(©)); | ||
826 | |||
827 | /* Rest of the buffer is encrypted. Decrypt it using the passphrase. */ | ||
828 | cipher_set_key_string(&ciphercontext, cipher, passphrase, | ||
829 | CIPHER_DECRYPT); | ||
830 | if (cipher_crypt(&ciphercontext, 0, cp, | ||
831 | buffer_ptr(©), buffer_len(©), 0, 0) != 0) | ||
832 | fatal("%s: cipher_crypt failed", __func__); | ||
833 | cipher_cleanup(&ciphercontext); | ||
834 | explicit_bzero(&ciphercontext, sizeof(ciphercontext)); | ||
835 | buffer_free(©); | ||
836 | |||
837 | check1 = buffer_get_char(&decrypted); | ||
838 | check2 = buffer_get_char(&decrypted); | ||
839 | if (check1 != buffer_get_char(&decrypted) || | ||
840 | check2 != buffer_get_char(&decrypted)) { | ||
841 | if (strcmp(passphrase, "") != 0) | ||
842 | debug("Bad passphrase supplied for RSA1 key"); | ||
843 | /* Bad passphrase. */ | ||
844 | buffer_free(&decrypted); | ||
845 | goto fail; | ||
846 | } | ||
847 | /* Read the rest of the private key. */ | ||
848 | buffer_get_bignum(&decrypted, prv->rsa->d); | ||
849 | buffer_get_bignum(&decrypted, prv->rsa->iqmp); /* u */ | ||
850 | /* in SSL and SSH v1 p and q are exchanged */ | ||
851 | buffer_get_bignum(&decrypted, prv->rsa->q); /* p */ | ||
852 | buffer_get_bignum(&decrypted, prv->rsa->p); /* q */ | ||
853 | |||
854 | /* calculate p-1 and q-1 */ | ||
855 | rsa_generate_additional_parameters(prv->rsa); | ||
856 | |||
857 | buffer_free(&decrypted); | ||
858 | |||
859 | /* enable blinding */ | ||
860 | if (RSA_blinding_on(prv->rsa, NULL) != 1) { | ||
861 | error("%s: RSA_blinding_on failed", __func__); | ||
862 | goto fail; | ||
863 | } | ||
864 | return prv; | ||
865 | |||
866 | fail: | ||
867 | if (commentp != NULL) | 177 | if (commentp != NULL) |
868 | free(*commentp); | 178 | *commentp = NULL; |
869 | key_free(prv); | ||
870 | return NULL; | ||
871 | } | ||
872 | |||
873 | static Key * | ||
874 | key_parse_private_pem(Buffer *blob, int type, const char *passphrase, | ||
875 | char **commentp) | ||
876 | { | ||
877 | EVP_PKEY *pk = NULL; | ||
878 | Key *prv = NULL; | ||
879 | char *name = "<no key>"; | ||
880 | BIO *bio; | ||
881 | 179 | ||
882 | if ((bio = BIO_new_mem_buf(buffer_ptr(blob), | 180 | if ((buffer = sshbuf_new()) == NULL) |
883 | buffer_len(blob))) == NULL) { | 181 | return SSH_ERR_ALLOC_FAIL; |
884 | error("%s: BIO_new_mem_buf failed", __func__); | 182 | if ((r = sshkey_load_file(fd, NULL, buffer)) != 0) |
885 | return NULL; | 183 | goto out; |
886 | } | 184 | if ((r = sshkey_parse_private_pem_fileblob(buffer, type, passphrase, |
887 | 185 | keyp, commentp)) != 0) | |
888 | pk = PEM_read_bio_PrivateKey(bio, NULL, NULL, (char *)passphrase); | 186 | goto out; |
889 | BIO_free(bio); | 187 | r = 0; |
890 | if (pk == NULL) { | 188 | out: |
891 | debug("%s: PEM_read_PrivateKey failed", __func__); | 189 | sshbuf_free(buffer); |
892 | (void)ERR_get_error(); | 190 | return r; |
893 | } else if (pk->type == EVP_PKEY_RSA && | ||
894 | (type == KEY_UNSPEC||type==KEY_RSA)) { | ||
895 | prv = key_new(KEY_UNSPEC); | ||
896 | prv->rsa = EVP_PKEY_get1_RSA(pk); | ||
897 | prv->type = KEY_RSA; | ||
898 | name = "rsa w/o comment"; | ||
899 | #ifdef DEBUG_PK | ||
900 | RSA_print_fp(stderr, prv->rsa, 8); | ||
901 | #endif | ||
902 | if (RSA_blinding_on(prv->rsa, NULL) != 1) { | ||
903 | error("%s: RSA_blinding_on failed", __func__); | ||
904 | key_free(prv); | ||
905 | prv = NULL; | ||
906 | } | ||
907 | } else if (pk->type == EVP_PKEY_DSA && | ||
908 | (type == KEY_UNSPEC||type==KEY_DSA)) { | ||
909 | prv = key_new(KEY_UNSPEC); | ||
910 | prv->dsa = EVP_PKEY_get1_DSA(pk); | ||
911 | prv->type = KEY_DSA; | ||
912 | name = "dsa w/o comment"; | ||
913 | #ifdef DEBUG_PK | ||
914 | DSA_print_fp(stderr, prv->dsa, 8); | ||
915 | #endif | ||
916 | #ifdef OPENSSL_HAS_ECC | ||
917 | } else if (pk->type == EVP_PKEY_EC && | ||
918 | (type == KEY_UNSPEC||type==KEY_ECDSA)) { | ||
919 | prv = key_new(KEY_UNSPEC); | ||
920 | prv->ecdsa = EVP_PKEY_get1_EC_KEY(pk); | ||
921 | prv->type = KEY_ECDSA; | ||
922 | if ((prv->ecdsa_nid = key_ecdsa_key_to_nid(prv->ecdsa)) == -1 || | ||
923 | key_curve_nid_to_name(prv->ecdsa_nid) == NULL || | ||
924 | key_ec_validate_public(EC_KEY_get0_group(prv->ecdsa), | ||
925 | EC_KEY_get0_public_key(prv->ecdsa)) != 0 || | ||
926 | key_ec_validate_private(prv->ecdsa) != 0) { | ||
927 | error("%s: bad ECDSA key", __func__); | ||
928 | key_free(prv); | ||
929 | prv = NULL; | ||
930 | } | ||
931 | name = "ecdsa w/o comment"; | ||
932 | #ifdef DEBUG_PK | ||
933 | if (prv != NULL && prv->ecdsa != NULL) | ||
934 | key_dump_ec_key(prv->ecdsa); | ||
935 | #endif | ||
936 | #endif /* OPENSSL_HAS_ECC */ | ||
937 | } else { | ||
938 | error("%s: PEM_read_PrivateKey: mismatch or " | ||
939 | "unknown EVP_PKEY save_type %d", __func__, pk->save_type); | ||
940 | } | ||
941 | if (pk != NULL) | ||
942 | EVP_PKEY_free(pk); | ||
943 | if (prv != NULL && commentp) | ||
944 | *commentp = xstrdup(name); | ||
945 | debug("read PEM private key done: type %s", | ||
946 | prv ? key_type(prv) : "<unknown>"); | ||
947 | return prv; | ||
948 | } | ||
949 | |||
950 | Key * | ||
951 | key_load_private_pem(int fd, int type, const char *passphrase, | ||
952 | char **commentp) | ||
953 | { | ||
954 | Buffer buffer; | ||
955 | Key *prv; | ||
956 | |||
957 | buffer_init(&buffer); | ||
958 | if (!key_load_file(fd, NULL, &buffer)) { | ||
959 | buffer_free(&buffer); | ||
960 | return NULL; | ||
961 | } | ||
962 | prv = key_parse_private_pem(&buffer, type, passphrase, commentp); | ||
963 | buffer_free(&buffer); | ||
964 | return prv; | ||
965 | } | 191 | } |
192 | #endif /* WITH_OPENSSL */ | ||
966 | 193 | ||
194 | /* XXX remove error() calls from here? */ | ||
967 | int | 195 | int |
968 | key_perm_ok(int fd, const char *filename) | 196 | sshkey_perm_ok(int fd, const char *filename) |
969 | { | 197 | { |
970 | struct stat st; | 198 | struct stat st; |
971 | 199 | ||
972 | if (fstat(fd, &st) < 0) | 200 | if (fstat(fd, &st) < 0) |
973 | return 0; | 201 | return SSH_ERR_SYSTEM_ERROR; |
974 | /* | 202 | /* |
975 | * if a key owned by the user is accessed, then we check the | 203 | * if a key owned by the user is accessed, then we check the |
976 | * permissions of the file. if the key owned by a different user, | 204 | * permissions of the file. if the key owned by a different user, |
@@ -985,298 +213,311 @@ key_perm_ok(int fd, const char *filename) | |||
985 | error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); | 213 | error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); |
986 | error("Permissions 0%3.3o for '%s' are too open.", | 214 | error("Permissions 0%3.3o for '%s' are too open.", |
987 | (u_int)st.st_mode & 0777, filename); | 215 | (u_int)st.st_mode & 0777, filename); |
988 | error("It is required that your private key files are NOT accessible by others."); | 216 | error("It is recommended that your private key files are NOT accessible by others."); |
989 | error("This private key will be ignored."); | 217 | error("This private key will be ignored."); |
990 | return 0; | 218 | return SSH_ERR_KEY_BAD_PERMISSIONS; |
991 | } | 219 | } |
992 | return 1; | 220 | return 0; |
993 | } | 221 | } |
994 | 222 | ||
995 | static Key * | 223 | /* XXX kill perm_ok now that we have SSH_ERR_KEY_BAD_PERMISSIONS? */ |
996 | key_parse_private_type(Buffer *blob, int type, const char *passphrase, | 224 | int |
997 | char **commentp) | 225 | sshkey_load_private_type(int type, const char *filename, const char *passphrase, |
226 | struct sshkey **keyp, char **commentp, int *perm_ok) | ||
998 | { | 227 | { |
999 | Key *k; | 228 | int fd, r; |
229 | struct sshbuf *buffer = NULL; | ||
1000 | 230 | ||
1001 | switch (type) { | 231 | *keyp = NULL; |
1002 | case KEY_RSA1: | 232 | if (commentp != NULL) |
1003 | return key_parse_private_rsa1(blob, passphrase, commentp); | 233 | *commentp = NULL; |
1004 | case KEY_DSA: | ||
1005 | case KEY_ECDSA: | ||
1006 | case KEY_RSA: | ||
1007 | return key_parse_private_pem(blob, type, passphrase, commentp); | ||
1008 | case KEY_ED25519: | ||
1009 | return key_parse_private2(blob, type, passphrase, commentp); | ||
1010 | case KEY_UNSPEC: | ||
1011 | if ((k = key_parse_private2(blob, type, passphrase, commentp))) | ||
1012 | return k; | ||
1013 | return key_parse_private_pem(blob, type, passphrase, commentp); | ||
1014 | default: | ||
1015 | error("%s: cannot parse key type %d", __func__, type); | ||
1016 | break; | ||
1017 | } | ||
1018 | return NULL; | ||
1019 | } | ||
1020 | |||
1021 | Key * | ||
1022 | key_load_private_type(int type, const char *filename, const char *passphrase, | ||
1023 | char **commentp, int *perm_ok) | ||
1024 | { | ||
1025 | int fd; | ||
1026 | Key *ret; | ||
1027 | Buffer buffer; | ||
1028 | 234 | ||
1029 | fd = open(filename, O_RDONLY); | 235 | if ((fd = open(filename, O_RDONLY)) < 0) { |
1030 | if (fd < 0) { | ||
1031 | debug("could not open key file '%s': %s", filename, | ||
1032 | strerror(errno)); | ||
1033 | if (perm_ok != NULL) | 236 | if (perm_ok != NULL) |
1034 | *perm_ok = 0; | 237 | *perm_ok = 0; |
1035 | return NULL; | 238 | return SSH_ERR_SYSTEM_ERROR; |
1036 | } | 239 | } |
1037 | if (!key_perm_ok(fd, filename)) { | 240 | if (sshkey_perm_ok(fd, filename) != 0) { |
1038 | if (perm_ok != NULL) | 241 | if (perm_ok != NULL) |
1039 | *perm_ok = 0; | 242 | *perm_ok = 0; |
1040 | error("bad permissions: ignore key: %s", filename); | 243 | r = SSH_ERR_KEY_BAD_PERMISSIONS; |
1041 | close(fd); | 244 | goto out; |
1042 | return NULL; | ||
1043 | } | 245 | } |
1044 | if (perm_ok != NULL) | 246 | if (perm_ok != NULL) |
1045 | *perm_ok = 1; | 247 | *perm_ok = 1; |
1046 | 248 | ||
1047 | buffer_init(&buffer); | 249 | if ((buffer = sshbuf_new()) == NULL) { |
1048 | if (!key_load_file(fd, filename, &buffer)) { | 250 | r = SSH_ERR_ALLOC_FAIL; |
1049 | buffer_free(&buffer); | 251 | goto out; |
1050 | close(fd); | ||
1051 | return NULL; | ||
1052 | } | 252 | } |
253 | if ((r = sshkey_load_file(fd, filename, buffer)) != 0) | ||
254 | goto out; | ||
255 | if ((r = sshkey_parse_private_fileblob_type(buffer, type, passphrase, | ||
256 | keyp, commentp)) != 0) | ||
257 | goto out; | ||
258 | r = 0; | ||
259 | out: | ||
1053 | close(fd); | 260 | close(fd); |
1054 | ret = key_parse_private_type(&buffer, type, passphrase, commentp); | 261 | if (buffer != NULL) |
1055 | buffer_free(&buffer); | 262 | sshbuf_free(buffer); |
1056 | return ret; | 263 | return r; |
1057 | } | 264 | } |
1058 | 265 | ||
1059 | Key * | 266 | /* XXX this is almost identical to sshkey_load_private_type() */ |
1060 | key_parse_private(Buffer *buffer, const char *filename, | 267 | int |
1061 | const char *passphrase, char **commentp) | 268 | sshkey_load_private(const char *filename, const char *passphrase, |
269 | struct sshkey **keyp, char **commentp) | ||
1062 | { | 270 | { |
1063 | Key *pub, *prv; | 271 | struct sshbuf *buffer = NULL; |
1064 | 272 | int r, fd; | |
1065 | /* it's a SSH v1 key if the public key part is readable */ | ||
1066 | pub = key_parse_public_rsa1(buffer, commentp); | ||
1067 | if (pub == NULL) { | ||
1068 | prv = key_parse_private_type(buffer, KEY_UNSPEC, | ||
1069 | passphrase, NULL); | ||
1070 | /* use the filename as a comment for PEM */ | ||
1071 | if (commentp && prv) | ||
1072 | *commentp = xstrdup(filename); | ||
1073 | } else { | ||
1074 | key_free(pub); | ||
1075 | /* key_parse_public_rsa1() has already loaded the comment */ | ||
1076 | prv = key_parse_private_type(buffer, KEY_RSA1, passphrase, | ||
1077 | NULL); | ||
1078 | } | ||
1079 | return prv; | ||
1080 | } | ||
1081 | 273 | ||
1082 | Key * | 274 | *keyp = NULL; |
1083 | key_load_private(const char *filename, const char *passphrase, | 275 | if (commentp != NULL) |
1084 | char **commentp) | 276 | *commentp = NULL; |
1085 | { | ||
1086 | Key *prv; | ||
1087 | Buffer buffer; | ||
1088 | int fd; | ||
1089 | 277 | ||
1090 | fd = open(filename, O_RDONLY); | 278 | if ((fd = open(filename, O_RDONLY)) < 0) |
1091 | if (fd < 0) { | 279 | return SSH_ERR_SYSTEM_ERROR; |
1092 | debug("could not open key file '%s': %s", filename, | 280 | if (sshkey_perm_ok(fd, filename) != 0) { |
1093 | strerror(errno)); | 281 | r = SSH_ERR_KEY_BAD_PERMISSIONS; |
1094 | return NULL; | 282 | goto out; |
1095 | } | ||
1096 | if (!key_perm_ok(fd, filename)) { | ||
1097 | error("bad permissions: ignore key: %s", filename); | ||
1098 | close(fd); | ||
1099 | return NULL; | ||
1100 | } | 283 | } |
1101 | 284 | ||
1102 | buffer_init(&buffer); | 285 | if ((buffer = sshbuf_new()) == NULL) { |
1103 | if (!key_load_file(fd, filename, &buffer)) { | 286 | r = SSH_ERR_ALLOC_FAIL; |
1104 | buffer_free(&buffer); | 287 | goto out; |
1105 | close(fd); | ||
1106 | return NULL; | ||
1107 | } | 288 | } |
289 | if ((r = sshkey_load_file(fd, filename, buffer)) != 0 || | ||
290 | (r = sshkey_parse_private_fileblob(buffer, passphrase, filename, | ||
291 | keyp, commentp)) != 0) | ||
292 | goto out; | ||
293 | r = 0; | ||
294 | out: | ||
1108 | close(fd); | 295 | close(fd); |
1109 | 296 | if (buffer != NULL) | |
1110 | prv = key_parse_private(&buffer, filename, passphrase, commentp); | 297 | sshbuf_free(buffer); |
1111 | buffer_free(&buffer); | 298 | return r; |
1112 | return prv; | ||
1113 | } | 299 | } |
1114 | 300 | ||
1115 | static int | 301 | static int |
1116 | key_try_load_public(Key *k, const char *filename, char **commentp) | 302 | sshkey_try_load_public(struct sshkey *k, const char *filename, char **commentp) |
1117 | { | 303 | { |
1118 | FILE *f; | 304 | FILE *f; |
1119 | char line[SSH_MAX_PUBKEY_BYTES]; | 305 | char line[SSH_MAX_PUBKEY_BYTES]; |
1120 | char *cp; | 306 | char *cp; |
1121 | u_long linenum = 0; | 307 | u_long linenum = 0; |
308 | int r; | ||
1122 | 309 | ||
1123 | f = fopen(filename, "r"); | 310 | if (commentp != NULL) |
1124 | if (f != NULL) { | 311 | *commentp = NULL; |
1125 | while (read_keyfile_line(f, filename, line, sizeof(line), | 312 | if ((f = fopen(filename, "r")) == NULL) |
1126 | &linenum) != -1) { | 313 | return SSH_ERR_SYSTEM_ERROR; |
1127 | cp = line; | 314 | while (read_keyfile_line(f, filename, line, sizeof(line), |
1128 | switch (*cp) { | 315 | &linenum) != -1) { |
1129 | case '#': | 316 | cp = line; |
1130 | case '\n': | 317 | switch (*cp) { |
1131 | case '\0': | 318 | case '#': |
1132 | continue; | 319 | case '\n': |
1133 | } | 320 | case '\0': |
1134 | /* Abort loading if this looks like a private key */ | 321 | continue; |
1135 | if (strncmp(cp, "-----BEGIN", 10) == 0) | 322 | } |
1136 | break; | 323 | /* Abort loading if this looks like a private key */ |
1137 | /* Skip leading whitespace. */ | 324 | if (strncmp(cp, "-----BEGIN", 10) == 0 || |
1138 | for (; *cp && (*cp == ' ' || *cp == '\t'); cp++) | 325 | strcmp(cp, "SSH PRIVATE KEY FILE") == 0) |
1139 | ; | 326 | break; |
1140 | if (*cp) { | 327 | /* Skip leading whitespace. */ |
1141 | if (key_read(k, &cp) == 1) { | 328 | for (; *cp && (*cp == ' ' || *cp == '\t'); cp++) |
1142 | cp[strcspn(cp, "\r\n")] = '\0'; | 329 | ; |
1143 | if (commentp) { | 330 | if (*cp) { |
1144 | *commentp = xstrdup(*cp ? | 331 | if ((r = sshkey_read(k, &cp)) == 0) { |
1145 | cp : filename); | 332 | cp[strcspn(cp, "\r\n")] = '\0'; |
1146 | } | 333 | if (commentp) { |
1147 | fclose(f); | 334 | *commentp = strdup(*cp ? |
1148 | return 1; | 335 | cp : filename); |
336 | if (*commentp == NULL) | ||
337 | r = SSH_ERR_ALLOC_FAIL; | ||
1149 | } | 338 | } |
339 | fclose(f); | ||
340 | return r; | ||
1150 | } | 341 | } |
1151 | } | 342 | } |
1152 | fclose(f); | ||
1153 | } | 343 | } |
1154 | return 0; | 344 | fclose(f); |
345 | return SSH_ERR_INVALID_FORMAT; | ||
1155 | } | 346 | } |
1156 | 347 | ||
1157 | /* load public key from ssh v1 private or any pubkey file */ | 348 | /* load public key from ssh v1 private or any pubkey file */ |
1158 | Key * | 349 | int |
1159 | key_load_public(const char *filename, char **commentp) | 350 | sshkey_load_public(const char *filename, struct sshkey **keyp, char **commentp) |
1160 | { | 351 | { |
1161 | Key *pub; | 352 | struct sshkey *pub = NULL; |
1162 | char file[MAXPATHLEN]; | 353 | char file[MAXPATHLEN]; |
354 | int r, fd; | ||
355 | |||
356 | if (keyp != NULL) | ||
357 | *keyp = NULL; | ||
358 | if (commentp != NULL) | ||
359 | *commentp = NULL; | ||
1163 | 360 | ||
361 | if ((fd = open(filename, O_RDONLY)) < 0) | ||
362 | goto skip; | ||
363 | #ifdef WITH_SSH1 | ||
1164 | /* try rsa1 private key */ | 364 | /* try rsa1 private key */ |
1165 | pub = key_load_public_type(KEY_RSA1, filename, commentp); | 365 | r = sshkey_load_public_rsa1(fd, filename, keyp, commentp); |
1166 | if (pub != NULL) | 366 | close(fd); |
1167 | return pub; | 367 | switch (r) { |
368 | case SSH_ERR_INTERNAL_ERROR: | ||
369 | case SSH_ERR_ALLOC_FAIL: | ||
370 | case SSH_ERR_INVALID_ARGUMENT: | ||
371 | case SSH_ERR_SYSTEM_ERROR: | ||
372 | case 0: | ||
373 | return r; | ||
374 | } | ||
375 | #endif /* WITH_SSH1 */ | ||
1168 | 376 | ||
377 | /* try ssh2 public key */ | ||
378 | if ((pub = sshkey_new(KEY_UNSPEC)) == NULL) | ||
379 | return SSH_ERR_ALLOC_FAIL; | ||
380 | if ((r = sshkey_try_load_public(pub, filename, commentp)) == 0) { | ||
381 | if (keyp != NULL) | ||
382 | *keyp = pub; | ||
383 | return 0; | ||
384 | } | ||
385 | sshkey_free(pub); | ||
386 | |||
387 | #ifdef WITH_SSH1 | ||
1169 | /* try rsa1 public key */ | 388 | /* try rsa1 public key */ |
1170 | pub = key_new(KEY_RSA1); | 389 | if ((pub = sshkey_new(KEY_RSA1)) == NULL) |
1171 | if (key_try_load_public(pub, filename, commentp) == 1) | 390 | return SSH_ERR_ALLOC_FAIL; |
1172 | return pub; | 391 | if ((r = sshkey_try_load_public(pub, filename, commentp)) == 0) { |
1173 | key_free(pub); | 392 | if (keyp != NULL) |
393 | *keyp = pub; | ||
394 | return 0; | ||
395 | } | ||
396 | sshkey_free(pub); | ||
397 | #endif /* WITH_SSH1 */ | ||
1174 | 398 | ||
1175 | /* try ssh2 public key */ | 399 | skip: |
1176 | pub = key_new(KEY_UNSPEC); | 400 | /* try .pub suffix */ |
1177 | if (key_try_load_public(pub, filename, commentp) == 1) | 401 | if ((pub = sshkey_new(KEY_UNSPEC)) == NULL) |
1178 | return pub; | 402 | return SSH_ERR_ALLOC_FAIL; |
403 | r = SSH_ERR_ALLOC_FAIL; /* in case strlcpy or strlcat fail */ | ||
1179 | if ((strlcpy(file, filename, sizeof file) < sizeof(file)) && | 404 | if ((strlcpy(file, filename, sizeof file) < sizeof(file)) && |
1180 | (strlcat(file, ".pub", sizeof file) < sizeof(file)) && | 405 | (strlcat(file, ".pub", sizeof file) < sizeof(file)) && |
1181 | (key_try_load_public(pub, file, commentp) == 1)) | 406 | (r = sshkey_try_load_public(pub, file, commentp)) == 0) { |
1182 | return pub; | 407 | if (keyp != NULL) |
1183 | key_free(pub); | 408 | *keyp = pub; |
1184 | return NULL; | 409 | return 0; |
410 | } | ||
411 | sshkey_free(pub); | ||
412 | return r; | ||
1185 | } | 413 | } |
1186 | 414 | ||
1187 | /* Load the certificate associated with the named private key */ | 415 | /* Load the certificate associated with the named private key */ |
1188 | Key * | 416 | int |
1189 | key_load_cert(const char *filename) | 417 | sshkey_load_cert(const char *filename, struct sshkey **keyp) |
1190 | { | 418 | { |
1191 | Key *pub; | 419 | struct sshkey *pub = NULL; |
1192 | char *file; | 420 | char *file = NULL; |
421 | int r = SSH_ERR_INTERNAL_ERROR; | ||
1193 | 422 | ||
1194 | pub = key_new(KEY_UNSPEC); | 423 | *keyp = NULL; |
1195 | xasprintf(&file, "%s-cert.pub", filename); | 424 | |
1196 | if (key_try_load_public(pub, file, NULL) == 1) { | 425 | if (asprintf(&file, "%s-cert.pub", filename) == -1) |
1197 | free(file); | 426 | return SSH_ERR_ALLOC_FAIL; |
1198 | return pub; | 427 | |
428 | if ((pub = sshkey_new(KEY_UNSPEC)) == NULL) { | ||
429 | goto out; | ||
1199 | } | 430 | } |
1200 | free(file); | 431 | if ((r = sshkey_try_load_public(pub, file, NULL)) != 0) |
1201 | key_free(pub); | 432 | goto out; |
1202 | return NULL; | 433 | |
434 | *keyp = pub; | ||
435 | pub = NULL; | ||
436 | r = 0; | ||
437 | |||
438 | out: | ||
439 | if (file != NULL) | ||
440 | free(file); | ||
441 | if (pub != NULL) | ||
442 | sshkey_free(pub); | ||
443 | return r; | ||
1203 | } | 444 | } |
1204 | 445 | ||
1205 | /* Load private key and certificate */ | 446 | /* Load private key and certificate */ |
1206 | Key * | 447 | int |
1207 | key_load_private_cert(int type, const char *filename, const char *passphrase, | 448 | sshkey_load_private_cert(int type, const char *filename, const char *passphrase, |
1208 | int *perm_ok) | 449 | struct sshkey **keyp, int *perm_ok) |
1209 | { | 450 | { |
1210 | Key *key, *pub; | 451 | struct sshkey *key = NULL, *cert = NULL; |
452 | int r; | ||
453 | |||
454 | *keyp = NULL; | ||
1211 | 455 | ||
1212 | switch (type) { | 456 | switch (type) { |
457 | #ifdef WITH_OPENSSL | ||
1213 | case KEY_RSA: | 458 | case KEY_RSA: |
1214 | case KEY_DSA: | 459 | case KEY_DSA: |
1215 | case KEY_ECDSA: | 460 | case KEY_ECDSA: |
1216 | case KEY_ED25519: | 461 | case KEY_ED25519: |
462 | #endif /* WITH_OPENSSL */ | ||
463 | case KEY_UNSPEC: | ||
1217 | break; | 464 | break; |
1218 | default: | 465 | default: |
1219 | error("%s: unsupported key type", __func__); | 466 | return SSH_ERR_KEY_TYPE_UNKNOWN; |
1220 | return NULL; | ||
1221 | } | 467 | } |
1222 | 468 | ||
1223 | if ((key = key_load_private_type(type, filename, | 469 | if ((r = sshkey_load_private_type(type, filename, |
1224 | passphrase, NULL, perm_ok)) == NULL) | 470 | passphrase, &key, NULL, perm_ok)) != 0 || |
1225 | return NULL; | 471 | (r = sshkey_load_cert(filename, &cert)) != 0) |
1226 | 472 | goto out; | |
1227 | if ((pub = key_load_cert(filename)) == NULL) { | ||
1228 | key_free(key); | ||
1229 | return NULL; | ||
1230 | } | ||
1231 | 473 | ||
1232 | /* Make sure the private key matches the certificate */ | 474 | /* Make sure the private key matches the certificate */ |
1233 | if (key_equal_public(key, pub) == 0) { | 475 | if (sshkey_equal_public(key, cert) == 0) { |
1234 | error("%s: certificate does not match private key %s", | 476 | r = SSH_ERR_KEY_CERT_MISMATCH; |
1235 | __func__, filename); | 477 | goto out; |
1236 | } else if (key_to_certified(key, key_cert_is_legacy(pub)) != 0) { | ||
1237 | error("%s: key_to_certified failed", __func__); | ||
1238 | } else { | ||
1239 | key_cert_copy(pub, key); | ||
1240 | key_free(pub); | ||
1241 | return key; | ||
1242 | } | 478 | } |
1243 | 479 | ||
1244 | key_free(key); | 480 | if ((r = sshkey_to_certified(key, sshkey_cert_is_legacy(cert))) != 0 || |
1245 | key_free(pub); | 481 | (r = sshkey_cert_copy(cert, key)) != 0) |
1246 | return NULL; | 482 | goto out; |
483 | r = 0; | ||
484 | *keyp = key; | ||
485 | key = NULL; | ||
486 | out: | ||
487 | if (key != NULL) | ||
488 | sshkey_free(key); | ||
489 | if (cert != NULL) | ||
490 | sshkey_free(cert); | ||
491 | return r; | ||
1247 | } | 492 | } |
1248 | 493 | ||
1249 | /* | 494 | /* |
1250 | * Returns 1 if the specified "key" is listed in the file "filename", | 495 | * Returns success if the specified "key" is listed in the file "filename", |
1251 | * 0 if the key is not listed or -1 on error. | 496 | * SSH_ERR_KEY_NOT_FOUND: if the key is not listed or another error. |
1252 | * If strict_type is set then the key type must match exactly, | 497 | * If strict_type is set then the key type must match exactly, |
1253 | * otherwise a comparison that ignores certficiate data is performed. | 498 | * otherwise a comparison that ignores certficiate data is performed. |
1254 | */ | 499 | */ |
1255 | int | 500 | int |
1256 | key_in_file(Key *key, const char *filename, int strict_type) | 501 | sshkey_in_file(struct sshkey *key, const char *filename, int strict_type) |
1257 | { | 502 | { |
1258 | FILE *f; | 503 | FILE *f; |
1259 | char line[SSH_MAX_PUBKEY_BYTES]; | 504 | char line[SSH_MAX_PUBKEY_BYTES]; |
1260 | char *cp; | 505 | char *cp; |
1261 | u_long linenum = 0; | 506 | u_long linenum = 0; |
1262 | int ret = 0; | 507 | int r = 0; |
1263 | Key *pub; | 508 | struct sshkey *pub = NULL; |
1264 | int (*key_compare)(const Key *, const Key *) = strict_type ? | 509 | int (*sshkey_compare)(const struct sshkey *, const struct sshkey *) = |
1265 | key_equal : key_equal_public; | 510 | strict_type ? sshkey_equal : sshkey_equal_public; |
1266 | 511 | ||
1267 | if ((f = fopen(filename, "r")) == NULL) { | 512 | if ((f = fopen(filename, "r")) == NULL) { |
1268 | if (errno == ENOENT) { | 513 | if (errno == ENOENT) |
1269 | debug("%s: keyfile \"%s\" missing", __func__, filename); | 514 | return SSH_ERR_KEY_NOT_FOUND; |
1270 | return 0; | 515 | else |
1271 | } else { | 516 | return SSH_ERR_SYSTEM_ERROR; |
1272 | error("%s: could not open keyfile \"%s\": %s", __func__, | ||
1273 | filename, strerror(errno)); | ||
1274 | return -1; | ||
1275 | } | ||
1276 | } | 517 | } |
1277 | 518 | ||
1278 | while (read_keyfile_line(f, filename, line, sizeof(line), | 519 | while (read_keyfile_line(f, filename, line, sizeof(line), |
1279 | &linenum) != -1) { | 520 | &linenum) != -1) { |
1280 | cp = line; | 521 | cp = line; |
1281 | 522 | ||
1282 | /* Skip leading whitespace. */ | 523 | /* Skip leading whitespace. */ |
@@ -1291,18 +532,24 @@ key_in_file(Key *key, const char *filename, int strict_type) | |||
1291 | continue; | 532 | continue; |
1292 | } | 533 | } |
1293 | 534 | ||
1294 | pub = key_new(KEY_UNSPEC); | 535 | if ((pub = sshkey_new(KEY_UNSPEC)) == NULL) { |
1295 | if (key_read(pub, &cp) != 1) { | 536 | r = SSH_ERR_ALLOC_FAIL; |
1296 | key_free(pub); | 537 | goto out; |
1297 | continue; | ||
1298 | } | 538 | } |
1299 | if (key_compare(key, pub)) { | 539 | if ((r = sshkey_read(pub, &cp)) != 0) |
1300 | ret = 1; | 540 | goto out; |
1301 | key_free(pub); | 541 | if (sshkey_compare(key, pub)) { |
1302 | break; | 542 | r = 0; |
543 | goto out; | ||
1303 | } | 544 | } |
1304 | key_free(pub); | 545 | sshkey_free(pub); |
546 | pub = NULL; | ||
1305 | } | 547 | } |
548 | r = SSH_ERR_KEY_NOT_FOUND; | ||
549 | out: | ||
550 | if (pub != NULL) | ||
551 | sshkey_free(pub); | ||
1306 | fclose(f); | 552 | fclose(f); |
1307 | return ret; | 553 | return r; |
1308 | } | 554 | } |
555 | |||