diff options
Diffstat (limited to 'authfile.c')
-rw-r--r-- | authfile.c | 159 |
1 files changed, 97 insertions, 62 deletions
diff --git a/authfile.c b/authfile.c index 2ed250232..e5fe0b801 100644 --- a/authfile.c +++ b/authfile.c | |||
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: authfile.c,v 1.87 2010/11/29 18:57:04 markus Exp $ */ | 1 | /* $OpenBSD: authfile.c,v 1.92 2011/06/14 22:49:18 markus Exp $ */ |
2 | /* | 2 | /* |
3 | * Author: Tatu Ylonen <ylo@cs.hut.fi> | 3 | * Author: Tatu Ylonen <ylo@cs.hut.fi> |
4 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | 4 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland |
@@ -70,6 +70,8 @@ | |||
70 | #include "atomicio.h" | 70 | #include "atomicio.h" |
71 | #include "pathnames.h" | 71 | #include "pathnames.h" |
72 | 72 | ||
73 | #define MAX_KEY_FILE_SIZE (1024 * 1024) | ||
74 | |||
73 | /* Version identification string for SSH v1 identity files. */ | 75 | /* Version identification string for SSH v1 identity files. */ |
74 | static const char authfile_id_string[] = | 76 | static const char authfile_id_string[] = |
75 | "SSH PRIVATE KEY FILE FORMAT 1.1\n"; | 77 | "SSH PRIVATE KEY FILE FORMAT 1.1\n"; |
@@ -278,6 +280,7 @@ static Key * | |||
278 | key_parse_public_rsa1(Buffer *blob, char **commentp) | 280 | key_parse_public_rsa1(Buffer *blob, char **commentp) |
279 | { | 281 | { |
280 | Key *pub; | 282 | Key *pub; |
283 | Buffer copy; | ||
281 | 284 | ||
282 | /* Check that it is at least big enough to contain the ID string. */ | 285 | /* Check that it is at least big enough to contain the ID string. */ |
283 | if (buffer_len(blob) < sizeof(authfile_id_string)) { | 286 | if (buffer_len(blob) < sizeof(authfile_id_string)) { |
@@ -294,31 +297,33 @@ key_parse_public_rsa1(Buffer *blob, char **commentp) | |||
294 | debug3("Incorrect RSA1 identifier"); | 297 | debug3("Incorrect RSA1 identifier"); |
295 | return NULL; | 298 | return NULL; |
296 | } | 299 | } |
297 | buffer_consume(blob, sizeof(authfile_id_string)); | 300 | buffer_init(©); |
301 | buffer_append(©, buffer_ptr(blob), buffer_len(blob)); | ||
302 | buffer_consume(©, sizeof(authfile_id_string)); | ||
298 | 303 | ||
299 | /* Skip cipher type and reserved data. */ | 304 | /* Skip cipher type and reserved data. */ |
300 | (void) buffer_get_char(blob); /* cipher type */ | 305 | (void) buffer_get_char(©); /* cipher type */ |
301 | (void) buffer_get_int(blob); /* reserved */ | 306 | (void) buffer_get_int(©); /* reserved */ |
302 | 307 | ||
303 | /* Read the public key from the buffer. */ | 308 | /* Read the public key from the buffer. */ |
304 | (void) buffer_get_int(blob); | 309 | (void) buffer_get_int(©); |
305 | pub = key_new(KEY_RSA1); | 310 | pub = key_new(KEY_RSA1); |
306 | buffer_get_bignum(blob, pub->rsa->n); | 311 | buffer_get_bignum(©, pub->rsa->n); |
307 | buffer_get_bignum(blob, pub->rsa->e); | 312 | buffer_get_bignum(©, pub->rsa->e); |
308 | if (commentp) | 313 | if (commentp) |
309 | *commentp = buffer_get_string(blob, NULL); | 314 | *commentp = buffer_get_string(©, NULL); |
310 | /* The encrypted private part is not parsed by this function. */ | 315 | /* The encrypted private part is not parsed by this function. */ |
311 | buffer_clear(blob); | 316 | buffer_free(©); |
312 | 317 | ||
313 | return pub; | 318 | return pub; |
314 | } | 319 | } |
315 | 320 | ||
316 | /* Load the contents of a key file into a buffer */ | 321 | /* Load a key from a fd into a buffer */ |
317 | static int | 322 | int |
318 | key_load_file(int fd, const char *filename, Buffer *blob) | 323 | key_load_file(int fd, const char *filename, Buffer *blob) |
319 | { | 324 | { |
320 | size_t len, readcount; | 325 | u_char buf[1024]; |
321 | u_char *cp; | 326 | size_t len; |
322 | struct stat st; | 327 | struct stat st; |
323 | 328 | ||
324 | if (fstat(fd, &st) < 0) { | 329 | if (fstat(fd, &st) < 0) { |
@@ -326,33 +331,45 @@ key_load_file(int fd, const char *filename, Buffer *blob) | |||
326 | filename == NULL ? "" : filename, | 331 | filename == NULL ? "" : filename, |
327 | filename == NULL ? "" : " ", | 332 | filename == NULL ? "" : " ", |
328 | strerror(errno)); | 333 | strerror(errno)); |
329 | close(fd); | ||
330 | return 0; | 334 | return 0; |
331 | } | 335 | } |
332 | if (st.st_size > 1*1024*1024) { | 336 | if ((st.st_mode & (S_IFSOCK|S_IFCHR|S_IFIFO)) == 0 && |
337 | st.st_size > MAX_KEY_FILE_SIZE) { | ||
338 | toobig: | ||
333 | error("%s: key file %.200s%stoo large", __func__, | 339 | error("%s: key file %.200s%stoo large", __func__, |
334 | filename == NULL ? "" : filename, | 340 | filename == NULL ? "" : filename, |
335 | filename == NULL ? "" : " "); | 341 | filename == NULL ? "" : " "); |
336 | close(fd); | ||
337 | return 0; | 342 | return 0; |
338 | } | 343 | } |
339 | len = (size_t)st.st_size; /* truncated */ | ||
340 | if (0 == len && S_ISFIFO(st.st_mode)) | ||
341 | len = 8192; /* we will try reading up to 8KiB from a FIFO */ | ||
342 | |||
343 | buffer_init(blob); | 344 | buffer_init(blob); |
344 | cp = buffer_append_space(blob, len); | 345 | for (;;) { |
345 | 346 | if ((len = atomicio(read, fd, buf, sizeof(buf))) == 0) { | |
346 | readcount = atomicio(read, fd, cp, len); | 347 | if (errno == EPIPE) |
347 | if (readcount != len && !(readcount > 0 && S_ISFIFO(st.st_mode))) { | 348 | break; |
348 | debug("%s: read from key file %.200s%sfailed: %.100s", __func__, | 349 | debug("%s: read from key file %.200s%sfailed: %.100s", |
349 | filename == NULL ? "" : filename, | 350 | __func__, filename == NULL ? "" : filename, |
350 | filename == NULL ? "" : " ", | 351 | filename == NULL ? "" : " ", strerror(errno)); |
351 | strerror(errno)); | 352 | buffer_clear(blob); |
353 | bzero(buf, sizeof(buf)); | ||
354 | return 0; | ||
355 | } | ||
356 | buffer_append(blob, buf, len); | ||
357 | if (buffer_len(blob) > MAX_KEY_FILE_SIZE) { | ||
358 | buffer_clear(blob); | ||
359 | bzero(buf, sizeof(buf)); | ||
360 | goto toobig; | ||
361 | } | ||
362 | } | ||
363 | bzero(buf, sizeof(buf)); | ||
364 | if ((st.st_mode & (S_IFSOCK|S_IFCHR|S_IFIFO)) == 0 && | ||
365 | st.st_size != buffer_len(blob)) { | ||
366 | debug("%s: key file %.200s%schanged size while reading", | ||
367 | __func__, filename == NULL ? "" : filename, | ||
368 | filename == NULL ? "" : " "); | ||
352 | buffer_clear(blob); | 369 | buffer_clear(blob); |
353 | close(fd); | ||
354 | return 0; | 370 | return 0; |
355 | } | 371 | } |
372 | |||
356 | return 1; | 373 | return 1; |
357 | } | 374 | } |
358 | 375 | ||
@@ -407,6 +424,7 @@ key_parse_private_rsa1(Buffer *blob, const char *passphrase, char **commentp) | |||
407 | CipherContext ciphercontext; | 424 | CipherContext ciphercontext; |
408 | Cipher *cipher; | 425 | Cipher *cipher; |
409 | Key *prv = NULL; | 426 | Key *prv = NULL; |
427 | Buffer copy; | ||
410 | 428 | ||
411 | /* Check that it is at least big enough to contain the ID string. */ | 429 | /* Check that it is at least big enough to contain the ID string. */ |
412 | if (buffer_len(blob) < sizeof(authfile_id_string)) { | 430 | if (buffer_len(blob) < sizeof(authfile_id_string)) { |
@@ -423,41 +441,44 @@ key_parse_private_rsa1(Buffer *blob, const char *passphrase, char **commentp) | |||
423 | debug3("Incorrect RSA1 identifier"); | 441 | debug3("Incorrect RSA1 identifier"); |
424 | return NULL; | 442 | return NULL; |
425 | } | 443 | } |
426 | buffer_consume(blob, sizeof(authfile_id_string)); | 444 | buffer_init(©); |
445 | buffer_append(©, buffer_ptr(blob), buffer_len(blob)); | ||
446 | buffer_consume(©, sizeof(authfile_id_string)); | ||
427 | 447 | ||
428 | /* Read cipher type. */ | 448 | /* Read cipher type. */ |
429 | cipher_type = buffer_get_char(blob); | 449 | cipher_type = buffer_get_char(©); |
430 | (void) buffer_get_int(blob); /* Reserved data. */ | 450 | (void) buffer_get_int(©); /* Reserved data. */ |
431 | 451 | ||
432 | /* Read the public key from the buffer. */ | 452 | /* Read the public key from the buffer. */ |
433 | (void) buffer_get_int(blob); | 453 | (void) buffer_get_int(©); |
434 | prv = key_new_private(KEY_RSA1); | 454 | prv = key_new_private(KEY_RSA1); |
435 | 455 | ||
436 | buffer_get_bignum(blob, prv->rsa->n); | 456 | buffer_get_bignum(©, prv->rsa->n); |
437 | buffer_get_bignum(blob, prv->rsa->e); | 457 | buffer_get_bignum(©, prv->rsa->e); |
438 | if (commentp) | 458 | if (commentp) |
439 | *commentp = buffer_get_string(blob, NULL); | 459 | *commentp = buffer_get_string(©, NULL); |
440 | else | 460 | else |
441 | (void)buffer_get_string_ptr(blob, NULL); | 461 | (void)buffer_get_string_ptr(©, NULL); |
442 | 462 | ||
443 | /* Check that it is a supported cipher. */ | 463 | /* Check that it is a supported cipher. */ |
444 | cipher = cipher_by_number(cipher_type); | 464 | cipher = cipher_by_number(cipher_type); |
445 | if (cipher == NULL) { | 465 | if (cipher == NULL) { |
446 | debug("Unsupported RSA1 cipher %d", cipher_type); | 466 | debug("Unsupported RSA1 cipher %d", cipher_type); |
467 | buffer_free(©); | ||
447 | goto fail; | 468 | goto fail; |
448 | } | 469 | } |
449 | /* Initialize space for decrypted data. */ | 470 | /* Initialize space for decrypted data. */ |
450 | buffer_init(&decrypted); | 471 | buffer_init(&decrypted); |
451 | cp = buffer_append_space(&decrypted, buffer_len(blob)); | 472 | cp = buffer_append_space(&decrypted, buffer_len(©)); |
452 | 473 | ||
453 | /* Rest of the buffer is encrypted. Decrypt it using the passphrase. */ | 474 | /* Rest of the buffer is encrypted. Decrypt it using the passphrase. */ |
454 | cipher_set_key_string(&ciphercontext, cipher, passphrase, | 475 | cipher_set_key_string(&ciphercontext, cipher, passphrase, |
455 | CIPHER_DECRYPT); | 476 | CIPHER_DECRYPT); |
456 | cipher_crypt(&ciphercontext, cp, | 477 | cipher_crypt(&ciphercontext, cp, |
457 | buffer_ptr(blob), buffer_len(blob)); | 478 | buffer_ptr(©), buffer_len(©)); |
458 | cipher_cleanup(&ciphercontext); | 479 | cipher_cleanup(&ciphercontext); |
459 | memset(&ciphercontext, 0, sizeof(ciphercontext)); | 480 | memset(&ciphercontext, 0, sizeof(ciphercontext)); |
460 | buffer_clear(blob); | 481 | buffer_free(©); |
461 | 482 | ||
462 | check1 = buffer_get_char(&decrypted); | 483 | check1 = buffer_get_char(&decrypted); |
463 | check2 = buffer_get_char(&decrypted); | 484 | check2 = buffer_get_char(&decrypted); |
@@ -610,7 +631,7 @@ key_perm_ok(int fd, const char *filename) | |||
610 | error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); | 631 | error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); |
611 | error("Permissions 0%3.3o for '%s' are too open.", | 632 | error("Permissions 0%3.3o for '%s' are too open.", |
612 | (u_int)st.st_mode & 0777, filename); | 633 | (u_int)st.st_mode & 0777, filename); |
613 | error("It is recommended that your private key files are NOT accessible by others."); | 634 | error("It is required that your private key files are NOT accessible by others."); |
614 | error("This private key will be ignored."); | 635 | error("This private key will be ignored."); |
615 | return 0; | 636 | return 0; |
616 | } | 637 | } |
@@ -630,6 +651,7 @@ key_parse_private_type(Buffer *blob, int type, const char *passphrase, | |||
630 | case KEY_UNSPEC: | 651 | case KEY_UNSPEC: |
631 | return key_parse_private_pem(blob, type, passphrase, commentp); | 652 | return key_parse_private_pem(blob, type, passphrase, commentp); |
632 | default: | 653 | default: |
654 | error("%s: cannot parse key type %d", __func__, type); | ||
633 | break; | 655 | break; |
634 | } | 656 | } |
635 | return NULL; | 657 | return NULL; |
@@ -674,11 +696,34 @@ key_load_private_type(int type, const char *filename, const char *passphrase, | |||
674 | } | 696 | } |
675 | 697 | ||
676 | Key * | 698 | Key * |
699 | key_parse_private(Buffer *buffer, const char *filename, | ||
700 | const char *passphrase, char **commentp) | ||
701 | { | ||
702 | Key *pub, *prv; | ||
703 | |||
704 | /* it's a SSH v1 key if the public key part is readable */ | ||
705 | pub = key_parse_public_rsa1(buffer, commentp); | ||
706 | if (pub == NULL) { | ||
707 | prv = key_parse_private_type(buffer, KEY_UNSPEC, | ||
708 | passphrase, NULL); | ||
709 | /* use the filename as a comment for PEM */ | ||
710 | if (commentp && prv) | ||
711 | *commentp = xstrdup(filename); | ||
712 | } else { | ||
713 | key_free(pub); | ||
714 | /* key_parse_public_rsa1() has already loaded the comment */ | ||
715 | prv = key_parse_private_type(buffer, KEY_RSA1, passphrase, | ||
716 | NULL); | ||
717 | } | ||
718 | return prv; | ||
719 | } | ||
720 | |||
721 | Key * | ||
677 | key_load_private(const char *filename, const char *passphrase, | 722 | key_load_private(const char *filename, const char *passphrase, |
678 | char **commentp) | 723 | char **commentp) |
679 | { | 724 | { |
680 | Key *pub, *prv; | 725 | Key *prv; |
681 | Buffer buffer, pubcopy; | 726 | Buffer buffer; |
682 | int fd; | 727 | int fd; |
683 | 728 | ||
684 | fd = open(filename, O_RDONLY); | 729 | fd = open(filename, O_RDONLY); |
@@ -701,23 +746,7 @@ key_load_private(const char *filename, const char *passphrase, | |||
701 | } | 746 | } |
702 | close(fd); | 747 | close(fd); |
703 | 748 | ||
704 | buffer_init(&pubcopy); | 749 | prv = key_parse_private(&buffer, filename, passphrase, commentp); |
705 | buffer_append(&pubcopy, buffer_ptr(&buffer), buffer_len(&buffer)); | ||
706 | /* it's a SSH v1 key if the public key part is readable */ | ||
707 | pub = key_parse_public_rsa1(&pubcopy, commentp); | ||
708 | buffer_free(&pubcopy); | ||
709 | if (pub == NULL) { | ||
710 | prv = key_parse_private_type(&buffer, KEY_UNSPEC, | ||
711 | passphrase, NULL); | ||
712 | /* use the filename as a comment for PEM */ | ||
713 | if (commentp && prv) | ||
714 | *commentp = xstrdup(filename); | ||
715 | } else { | ||
716 | key_free(pub); | ||
717 | /* key_parse_public_rsa1() has already loaded the comment */ | ||
718 | prv = key_parse_private_type(&buffer, KEY_RSA1, passphrase, | ||
719 | NULL); | ||
720 | } | ||
721 | buffer_free(&buffer); | 750 | buffer_free(&buffer); |
722 | return prv; | 751 | return prv; |
723 | } | 752 | } |
@@ -741,13 +770,19 @@ key_try_load_public(Key *k, const char *filename, char **commentp) | |||
741 | case '\0': | 770 | case '\0': |
742 | continue; | 771 | continue; |
743 | } | 772 | } |
773 | /* Abort loading if this looks like a private key */ | ||
774 | if (strncmp(cp, "-----BEGIN", 10) == 0) | ||
775 | break; | ||
744 | /* Skip leading whitespace. */ | 776 | /* Skip leading whitespace. */ |
745 | for (; *cp && (*cp == ' ' || *cp == '\t'); cp++) | 777 | for (; *cp && (*cp == ' ' || *cp == '\t'); cp++) |
746 | ; | 778 | ; |
747 | if (*cp) { | 779 | if (*cp) { |
748 | if (key_read(k, &cp) == 1) { | 780 | if (key_read(k, &cp) == 1) { |
749 | if (commentp) | 781 | cp[strcspn(cp, "\r\n")] = '\0'; |
750 | *commentp=xstrdup(filename); | 782 | if (commentp) { |
783 | *commentp = xstrdup(*cp ? | ||
784 | cp : filename); | ||
785 | } | ||
751 | fclose(f); | 786 | fclose(f); |
752 | return 1; | 787 | return 1; |
753 | } | 788 | } |