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