diff options
author | Colin Watson <cjwatson@debian.org> | 2014-10-07 13:33:15 +0100 |
---|---|---|
committer | Colin Watson <cjwatson@debian.org> | 2014-10-07 14:27:30 +0100 |
commit | f0b009aea83e9ff3a50be30f51012099a5143c16 (patch) | |
tree | 3825e6f7e3b7ea4481d06ed89aba9a7a95150df5 /authfile.c | |
parent | 47f0bad4330b16ec3bad870fcf9839c196e42c12 (diff) | |
parent | 762c062828f5a8f6ed189ed6e44ad38fd92f8b36 (diff) |
Merge 6.7p1.
* New upstream release (http://www.openssh.com/txt/release-6.7):
- sshd(8): The default set of ciphers and MACs has been altered to
remove unsafe algorithms. In particular, CBC ciphers and arcfour* are
disabled by default. The full set of algorithms remains available if
configured explicitly via the Ciphers and MACs sshd_config options.
- ssh(1), sshd(8): Add support for Unix domain socket forwarding. A
remote TCP port may be forwarded to a local Unix domain socket and
vice versa or both ends may be a Unix domain socket (closes: #236718).
- ssh(1), ssh-keygen(1): Add support for SSHFP DNS records for ED25519
key types.
- sftp(1): Allow resumption of interrupted uploads.
- ssh(1): When rekeying, skip file/DNS lookups of the hostkey if it is
the same as the one sent during initial key exchange.
- sshd(8): Allow explicit ::1 and 127.0.0.1 forwarding bind addresses
when GatewayPorts=no; allows client to choose address family.
- sshd(8): Add a sshd_config PermitUserRC option to control whether
~/.ssh/rc is executed, mirroring the no-user-rc authorized_keys
option.
- ssh(1): Add a %C escape sequence for LocalCommand and ControlPath that
expands to a unique identifer based on a hash of the tuple of (local
host, remote user, hostname, port). Helps avoid exceeding miserly
pathname limits for Unix domain sockets in multiplexing control paths.
- sshd(8): Make the "Too many authentication failures" message include
the user, source address, port and protocol in a format similar to the
authentication success / failure messages.
- Use CLOCK_BOOTTIME in preference to CLOCK_MONOTONIC when it is
available. It considers time spent suspended, thereby ensuring
timeouts (e.g. for expiring agent keys) fire correctly (closes:
#734553).
- Use prctl() to prevent sftp-server from accessing
/proc/self/{mem,maps}.
* Restore TCP wrappers support, removed upstream in 6.7. It is true that
dropping this reduces preauth attack surface in sshd. On the other
hand, this support seems to be quite widely used, and abruptly dropping
it (from the perspective of users who don't read openssh-unix-dev) could
easily cause more serious problems in practice. It's not entirely clear
what the right long-term answer for Debian is, but it at least probably
doesn't involve dropping this feature shortly before a freeze.
* Replace patch to disable OpenSSL version check with an updated version
of Kurt Roeckx's patch from #732940 to just avoid checking the status
field.
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 | |||