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