summaryrefslogtreecommitdiff
path: root/sshsig.c
diff options
context:
space:
mode:
Diffstat (limited to 'sshsig.c')
-rw-r--r--sshsig.c326
1 files changed, 281 insertions, 45 deletions
diff --git a/sshsig.c b/sshsig.c
index b19cd077d..e63a36e1e 100644
--- a/sshsig.c
+++ b/sshsig.c
@@ -151,8 +151,9 @@ done:
151 151
152static int 152static int
153sshsig_wrap_sign(struct sshkey *key, const char *hashalg, 153sshsig_wrap_sign(struct sshkey *key, const char *hashalg,
154 const struct sshbuf *h_message, const char *sig_namespace, 154 const char *sk_provider, const struct sshbuf *h_message,
155 struct sshbuf **out, sshsig_signer *signer, void *signer_ctx) 155 const char *sig_namespace, struct sshbuf **out,
156 sshsig_signer *signer, void *signer_ctx)
156{ 157{
157 int r; 158 int r;
158 size_t slen = 0; 159 size_t slen = 0;
@@ -184,14 +185,14 @@ sshsig_wrap_sign(struct sshkey *key, const char *hashalg,
184 if (signer != NULL) { 185 if (signer != NULL) {
185 if ((r = signer(key, &sig, &slen, 186 if ((r = signer(key, &sig, &slen,
186 sshbuf_ptr(tosign), sshbuf_len(tosign), 187 sshbuf_ptr(tosign), sshbuf_len(tosign),
187 sign_alg, 0, signer_ctx)) != 0) { 188 sign_alg, sk_provider, 0, signer_ctx)) != 0) {
188 error("Couldn't sign message: %s", ssh_err(r)); 189 error("Couldn't sign message: %s", ssh_err(r));
189 goto done; 190 goto done;
190 } 191 }
191 } else { 192 } else {
192 if ((r = sshkey_sign(key, &sig, &slen, 193 if ((r = sshkey_sign(key, &sig, &slen,
193 sshbuf_ptr(tosign), sshbuf_len(tosign), 194 sshbuf_ptr(tosign), sshbuf_len(tosign),
194 sign_alg, 0)) != 0) { 195 sign_alg, sk_provider, 0)) != 0) {
195 error("Couldn't sign message: %s", ssh_err(r)); 196 error("Couldn't sign message: %s", ssh_err(r));
196 goto done; 197 goto done;
197 } 198 }
@@ -285,7 +286,7 @@ sshsig_peek_hashalg(struct sshbuf *signature, char **hashalgp)
285static int 286static int
286sshsig_wrap_verify(struct sshbuf *signature, const char *hashalg, 287sshsig_wrap_verify(struct sshbuf *signature, const char *hashalg,
287 const struct sshbuf *h_message, const char *expect_namespace, 288 const struct sshbuf *h_message, const char *expect_namespace,
288 struct sshkey **sign_keyp) 289 struct sshkey **sign_keyp, struct sshkey_sig_details **sig_details)
289{ 290{
290 int r = SSH_ERR_INTERNAL_ERROR; 291 int r = SSH_ERR_INTERNAL_ERROR;
291 struct sshbuf *buf = NULL, *toverify = NULL; 292 struct sshbuf *buf = NULL, *toverify = NULL;
@@ -295,6 +296,8 @@ sshsig_wrap_verify(struct sshbuf *signature, const char *hashalg,
295 size_t siglen; 296 size_t siglen;
296 297
297 debug("%s: verify message length %zu", __func__, sshbuf_len(h_message)); 298 debug("%s: verify message length %zu", __func__, sshbuf_len(h_message));
299 if (sig_details != NULL)
300 *sig_details = NULL;
298 if (sign_keyp != NULL) 301 if (sign_keyp != NULL)
299 *sign_keyp = NULL; 302 *sign_keyp = NULL;
300 303
@@ -360,7 +363,7 @@ sshsig_wrap_verify(struct sshbuf *signature, const char *hashalg,
360 } 363 }
361 } 364 }
362 if ((r = sshkey_verify(key, sig, siglen, sshbuf_ptr(toverify), 365 if ((r = sshkey_verify(key, sig, siglen, sshbuf_ptr(toverify),
363 sshbuf_len(toverify), NULL, 0)) != 0) { 366 sshbuf_len(toverify), NULL, 0, sig_details)) != 0) {
364 error("Signature verification failed: %s", ssh_err(r)); 367 error("Signature verification failed: %s", ssh_err(r));
365 goto done; 368 goto done;
366 } 369 }
@@ -425,7 +428,7 @@ hash_buffer(const struct sshbuf *m, const char *hashalg, struct sshbuf **bp)
425} 428}
426 429
427int 430int
428sshsig_signb(struct sshkey *key, const char *hashalg, 431sshsig_signb(struct sshkey *key, const char *hashalg, const char *sk_provider,
429 const struct sshbuf *message, const char *sig_namespace, 432 const struct sshbuf *message, const char *sig_namespace,
430 struct sshbuf **out, sshsig_signer *signer, void *signer_ctx) 433 struct sshbuf **out, sshsig_signer *signer, void *signer_ctx)
431{ 434{
@@ -440,8 +443,8 @@ sshsig_signb(struct sshkey *key, const char *hashalg,
440 error("%s: hash_buffer failed: %s", __func__, ssh_err(r)); 443 error("%s: hash_buffer failed: %s", __func__, ssh_err(r));
441 goto out; 444 goto out;
442 } 445 }
443 if ((r = sshsig_wrap_sign(key, hashalg, b, sig_namespace, out, 446 if ((r = sshsig_wrap_sign(key, hashalg, sk_provider, b,
444 signer, signer_ctx)) != 0) 447 sig_namespace, out, signer, signer_ctx)) != 0)
445 goto out; 448 goto out;
446 /* success */ 449 /* success */
447 r = 0; 450 r = 0;
@@ -452,15 +455,17 @@ sshsig_signb(struct sshkey *key, const char *hashalg,
452 455
453int 456int
454sshsig_verifyb(struct sshbuf *signature, const struct sshbuf *message, 457sshsig_verifyb(struct sshbuf *signature, const struct sshbuf *message,
455 const char *expect_namespace, struct sshkey **sign_keyp) 458 const char *expect_namespace, struct sshkey **sign_keyp,
459 struct sshkey_sig_details **sig_details)
456{ 460{
457 struct sshbuf *b = NULL; 461 struct sshbuf *b = NULL;
458 int r = SSH_ERR_INTERNAL_ERROR; 462 int r = SSH_ERR_INTERNAL_ERROR;
459 char *hashalg = NULL; 463 char *hashalg = NULL;
460 464
465 if (sig_details != NULL)
466 *sig_details = NULL;
461 if (sign_keyp != NULL) 467 if (sign_keyp != NULL)
462 *sign_keyp = NULL; 468 *sign_keyp = NULL;
463
464 if ((r = sshsig_peek_hashalg(signature, &hashalg)) != 0) 469 if ((r = sshsig_peek_hashalg(signature, &hashalg)) != 0)
465 return r; 470 return r;
466 debug("%s: signature made with hash \"%s\"", __func__, hashalg); 471 debug("%s: signature made with hash \"%s\"", __func__, hashalg);
@@ -469,7 +474,7 @@ sshsig_verifyb(struct sshbuf *signature, const struct sshbuf *message,
469 goto out; 474 goto out;
470 } 475 }
471 if ((r = sshsig_wrap_verify(signature, hashalg, b, expect_namespace, 476 if ((r = sshsig_wrap_verify(signature, hashalg, b, expect_namespace,
472 sign_keyp)) != 0) 477 sign_keyp, sig_details)) != 0)
473 goto out; 478 goto out;
474 /* success */ 479 /* success */
475 r = 0; 480 r = 0;
@@ -551,7 +556,7 @@ hash_file(int fd, const char *hashalg, struct sshbuf **bp)
551} 556}
552 557
553int 558int
554sshsig_sign_fd(struct sshkey *key, const char *hashalg, 559sshsig_sign_fd(struct sshkey *key, const char *hashalg, const char *sk_provider,
555 int fd, const char *sig_namespace, struct sshbuf **out, 560 int fd, const char *sig_namespace, struct sshbuf **out,
556 sshsig_signer *signer, void *signer_ctx) 561 sshsig_signer *signer, void *signer_ctx)
557{ 562{
@@ -566,8 +571,8 @@ sshsig_sign_fd(struct sshkey *key, const char *hashalg,
566 error("%s: hash_file failed: %s", __func__, ssh_err(r)); 571 error("%s: hash_file failed: %s", __func__, ssh_err(r));
567 return r; 572 return r;
568 } 573 }
569 if ((r = sshsig_wrap_sign(key, hashalg, b, sig_namespace, out, 574 if ((r = sshsig_wrap_sign(key, hashalg, sk_provider, b,
570 signer, signer_ctx)) != 0) 575 sig_namespace, out, signer, signer_ctx)) != 0)
571 goto out; 576 goto out;
572 /* success */ 577 /* success */
573 r = 0; 578 r = 0;
@@ -578,15 +583,17 @@ sshsig_sign_fd(struct sshkey *key, const char *hashalg,
578 583
579int 584int
580sshsig_verify_fd(struct sshbuf *signature, int fd, 585sshsig_verify_fd(struct sshbuf *signature, int fd,
581 const char *expect_namespace, struct sshkey **sign_keyp) 586 const char *expect_namespace, struct sshkey **sign_keyp,
587 struct sshkey_sig_details **sig_details)
582{ 588{
583 struct sshbuf *b = NULL; 589 struct sshbuf *b = NULL;
584 int r = SSH_ERR_INTERNAL_ERROR; 590 int r = SSH_ERR_INTERNAL_ERROR;
585 char *hashalg = NULL; 591 char *hashalg = NULL;
586 592
593 if (sig_details != NULL)
594 *sig_details = NULL;
587 if (sign_keyp != NULL) 595 if (sign_keyp != NULL)
588 *sign_keyp = NULL; 596 *sign_keyp = NULL;
589
590 if ((r = sshsig_peek_hashalg(signature, &hashalg)) != 0) 597 if ((r = sshsig_peek_hashalg(signature, &hashalg)) != 0)
591 return r; 598 return r;
592 debug("%s: signature made with hash \"%s\"", __func__, hashalg); 599 debug("%s: signature made with hash \"%s\"", __func__, hashalg);
@@ -595,7 +602,7 @@ sshsig_verify_fd(struct sshbuf *signature, int fd,
595 goto out; 602 goto out;
596 } 603 }
597 if ((r = sshsig_wrap_verify(signature, hashalg, b, expect_namespace, 604 if ((r = sshsig_wrap_verify(signature, hashalg, b, expect_namespace,
598 sign_keyp)) != 0) 605 sign_keyp, sig_details)) != 0)
599 goto out; 606 goto out;
600 /* success */ 607 /* success */
601 r = 0; 608 r = 0;
@@ -672,56 +679,116 @@ sshsigopt_free(struct sshsigopt *opts)
672} 679}
673 680
674static int 681static int
675check_allowed_keys_line(const char *path, u_long linenum, char *line, 682parse_principals_key_and_options(const char *path, u_long linenum, char *line,
676 const struct sshkey *sign_key, const char *principal, 683 const char *required_principal, char **principalsp, struct sshkey **keyp,
677 const char *sig_namespace) 684 struct sshsigopt **sigoptsp)
678{ 685{
679 struct sshkey *found_key = NULL; 686 char *opts = NULL, *tmp, *cp, *principals = NULL;
680 char *cp, *opts = NULL, *identities = NULL;
681 int r, found = 0;
682 const char *reason = NULL; 687 const char *reason = NULL;
683 struct sshsigopt *sigopts = NULL; 688 struct sshsigopt *sigopts = NULL;
689 struct sshkey *key = NULL;
690 int r = SSH_ERR_INTERNAL_ERROR;
684 691
685 if ((found_key = sshkey_new(KEY_UNSPEC)) == NULL) { 692 if (principalsp != NULL)
686 error("%s: sshkey_new failed", __func__); 693 *principalsp = NULL;
687 return SSH_ERR_ALLOC_FAIL; 694 if (sigoptsp != NULL)
688 } 695 *sigoptsp = NULL;
696 if (keyp != NULL)
697 *keyp = NULL;
689 698
690 /* format: identity[,identity...] [option[,option...]] key */
691 cp = line; 699 cp = line;
692 cp = cp + strspn(cp, " \t"); /* skip leading whitespace */ 700 cp = cp + strspn(cp, " \t"); /* skip leading whitespace */
693 if (*cp == '#' || *cp == '\0') 701 if (*cp == '#' || *cp == '\0')
694 goto done; 702 return SSH_ERR_KEY_NOT_FOUND; /* blank or all-comment line */
695 if ((identities = strdelimw(&cp)) == NULL) { 703
704 /* format: identity[,identity...] [option[,option...]] key */
705 if ((tmp = strdelimw(&cp)) == NULL) {
696 error("%s:%lu: invalid line", path, linenum); 706 error("%s:%lu: invalid line", path, linenum);
697 goto done; 707 r = SSH_ERR_INVALID_FORMAT;
708 goto out;
698 } 709 }
699 if (match_pattern_list(principal, identities, 0) != 1) { 710 if ((principals = strdup(tmp)) == NULL) {
700 /* principal didn't match */ 711 error("%s: strdup failed", __func__);
701 goto done; 712 r = SSH_ERR_ALLOC_FAIL;
713 goto out;
714 }
715 /*
716 * Bail out early if we're looking for a particular principal and this
717 * line does not list it.
718 */
719 if (required_principal != NULL) {
720 if (match_pattern_list(required_principal,
721 principals, 0) != 1) {
722 /* principal didn't match */
723 r = SSH_ERR_KEY_NOT_FOUND;
724 goto out;
725 }
726 debug("%s: %s:%lu: matched principal \"%s\"",
727 __func__, path, linenum, required_principal);
702 } 728 }
703 debug("%s: %s:%lu: matched principal \"%s\"",
704 __func__, path, linenum, principal);
705 729
706 if (sshkey_read(found_key, &cp) != 0) { 730 if ((key = sshkey_new(KEY_UNSPEC)) == NULL) {
731 error("%s: sshkey_new failed", __func__);
732 r = SSH_ERR_ALLOC_FAIL;
733 goto out;
734 }
735 if (sshkey_read(key, &cp) != 0) {
707 /* no key? Check for options */ 736 /* no key? Check for options */
708 opts = cp; 737 opts = cp;
709 if (sshkey_advance_past_options(&cp) != 0) { 738 if (sshkey_advance_past_options(&cp) != 0) {
710 error("%s:%lu: invalid options", 739 error("%s:%lu: invalid options", path, linenum);
711 path, linenum); 740 r = SSH_ERR_INVALID_FORMAT;
712 goto done; 741 goto out;
713 } 742 }
714 *cp++ = '\0'; 743 *cp++ = '\0';
715 skip_space(&cp); 744 skip_space(&cp);
716 if (sshkey_read(found_key, &cp) != 0) { 745 if (sshkey_read(key, &cp) != 0) {
717 error("%s:%lu: invalid key", path, 746 error("%s:%lu: invalid key", path, linenum);
718 linenum); 747 r = SSH_ERR_INVALID_FORMAT;
719 goto done; 748 goto out;
720 } 749 }
721 } 750 }
722 debug3("%s:%lu: options %s", path, linenum, opts == NULL ? "" : opts); 751 debug3("%s:%lu: options %s", path, linenum, opts == NULL ? "" : opts);
723 if ((sigopts = sshsigopt_parse(opts, path, linenum, &reason)) == NULL) { 752 if ((sigopts = sshsigopt_parse(opts, path, linenum, &reason)) == NULL) {
724 error("%s:%lu: bad options: %s", path, linenum, reason); 753 error("%s:%lu: bad options: %s", path, linenum, reason);
754 r = SSH_ERR_INVALID_FORMAT;
755 goto out;
756 }
757 /* success */
758 if (principalsp != NULL) {
759 *principalsp = principals;
760 principals = NULL; /* transferred */
761 }
762 if (sigoptsp != NULL) {
763 *sigoptsp = sigopts;
764 sigopts = NULL; /* transferred */
765 }
766 if (keyp != NULL) {
767 *keyp = key;
768 key = NULL; /* transferred */
769 }
770 r = 0;
771 out:
772 free(principals);
773 sshsigopt_free(sigopts);
774 sshkey_free(key);
775 return r;
776}
777
778static int
779check_allowed_keys_line(const char *path, u_long linenum, char *line,
780 const struct sshkey *sign_key, const char *principal,
781 const char *sig_namespace)
782{
783 struct sshkey *found_key = NULL;
784 int r, found = 0;
785 const char *reason = NULL;
786 struct sshsigopt *sigopts = NULL;
787
788 /* Parse the line */
789 if ((r = parse_principals_key_and_options(path, linenum, line,
790 principal, NULL, &found_key, &sigopts)) != 0) {
791 /* error already logged */
725 goto done; 792 goto done;
726 } 793 }
727 794
@@ -799,3 +866,172 @@ sshsig_check_allowed_keys(const char *path, const struct sshkey *sign_key,
799 free(line); 866 free(line);
800 return r == 0 ? SSH_ERR_KEY_NOT_FOUND : r; 867 return r == 0 ? SSH_ERR_KEY_NOT_FOUND : r;
801} 868}
869
870static int
871cert_filter_principals(const char *path, u_long linenum,
872 char **principalsp, const struct sshkey *cert)
873{
874 char *cp, *oprincipals, *principals;
875 const char *reason;
876 struct sshbuf *nprincipals;
877 int r = SSH_ERR_INTERNAL_ERROR, success = 0;
878
879 oprincipals = principals = *principalsp;
880 *principalsp = NULL;
881
882 if ((nprincipals = sshbuf_new()) == NULL)
883 return SSH_ERR_ALLOC_FAIL;
884
885 while ((cp = strsep(&principals, ",")) != NULL && *cp != '\0') {
886 if (strcspn(cp, "!?*") != strlen(cp)) {
887 debug("%s:%lu: principal \"%s\" not authorized: "
888 "contains wildcards", path, linenum, cp);
889 continue;
890 }
891 /* Check against principals list in certificate */
892 if ((r = sshkey_cert_check_authority(cert, 0, 1,
893 cp, &reason)) != 0) {
894 debug("%s:%lu: principal \"%s\" not authorized: %s",
895 path, linenum, cp, reason);
896 continue;
897 }
898 if ((r = sshbuf_putf(nprincipals, "%s%s",
899 sshbuf_len(nprincipals) != 0 ? "," : "", cp)) != 0) {
900 error("%s: buffer error", __func__);
901 goto out;
902 }
903 }
904 if (sshbuf_len(nprincipals) == 0) {
905 error("%s:%lu: no valid principals found", path, linenum);
906 r = SSH_ERR_KEY_CERT_INVALID;
907 goto out;
908 }
909 if ((principals = sshbuf_dup_string(nprincipals)) == NULL) {
910 error("%s: buffer error", __func__);
911 goto out;
912 }
913 /* success */
914 success = 1;
915 *principalsp = principals;
916 out:
917 sshbuf_free(nprincipals);
918 free(oprincipals);
919 return success ? 0 : r;
920}
921
922static int
923get_matching_principals_from_line(const char *path, u_long linenum, char *line,
924 const struct sshkey *sign_key, char **principalsp)
925{
926 struct sshkey *found_key = NULL;
927 char *principals = NULL;
928 int r, found = 0;
929 struct sshsigopt *sigopts = NULL;
930
931 if (principalsp != NULL)
932 *principalsp = NULL;
933
934 /* Parse the line */
935 if ((r = parse_principals_key_and_options(path, linenum, line,
936 NULL, &principals, &found_key, &sigopts)) != 0) {
937 /* error already logged */
938 goto done;
939 }
940
941 if (!sigopts->ca && sshkey_equal(found_key, sign_key)) {
942 /* Exact match of key */
943 debug("%s:%lu: matched key", path, linenum);
944 /* success */
945 found = 1;
946 } else if (sigopts->ca && sshkey_is_cert(sign_key) &&
947 sshkey_equal_public(sign_key->cert->signature_key, found_key)) {
948 /* Remove principals listed in file but not allowed by cert */
949 if ((r = cert_filter_principals(path, linenum,
950 &principals, sign_key)) != 0) {
951 /* error already displayed */
952 debug("%s:%lu: cert_filter_principals: %s",
953 path, linenum, ssh_err(r));
954 goto done;
955 }
956 debug("%s:%lu: matched certificate CA key", path, linenum);
957 /* success */
958 found = 1;
959 } else {
960 /* Key didn't match */
961 goto done;
962 }
963 done:
964 if (found) {
965 *principalsp = principals;
966 principals = NULL; /* transferred */
967 }
968 free(principals);
969 sshkey_free(found_key);
970 sshsigopt_free(sigopts);
971 return found ? 0 : SSH_ERR_KEY_NOT_FOUND;
972}
973
974int
975sshsig_find_principals(const char *path, const struct sshkey *sign_key,
976 char **principals)
977{
978 FILE *f = NULL;
979 char *line = NULL;
980 size_t linesize = 0;
981 u_long linenum = 0;
982 int r, oerrno;
983
984 if ((f = fopen(path, "r")) == NULL) {
985 oerrno = errno;
986 error("Unable to open allowed keys file \"%s\": %s",
987 path, strerror(errno));
988 errno = oerrno;
989 return SSH_ERR_SYSTEM_ERROR;
990 }
991
992 while (getline(&line, &linesize, f) != -1) {
993 linenum++;
994 r = get_matching_principals_from_line(path, linenum, line,
995 sign_key, principals);
996 free(line);
997 line = NULL;
998 if (r == SSH_ERR_KEY_NOT_FOUND)
999 continue;
1000 else if (r == 0) {
1001 /* success */
1002 fclose(f);
1003 return 0;
1004 } else
1005 break;
1006 }
1007 free(line);
1008 /* Either we hit an error parsing or we simply didn't find the key */
1009 if (ferror(f) != 0) {
1010 oerrno = errno;
1011 fclose(f);
1012 error("Unable to read allowed keys file \"%s\": %s",
1013 path, strerror(errno));
1014 errno = oerrno;
1015 return SSH_ERR_SYSTEM_ERROR;
1016 }
1017 fclose(f);
1018 return r == 0 ? SSH_ERR_KEY_NOT_FOUND : r;
1019}
1020
1021int
1022sshsig_get_pubkey(struct sshbuf *signature, struct sshkey **pubkey)
1023{
1024 struct sshkey *pk = NULL;
1025 int r = SSH_ERR_SIGNATURE_INVALID;
1026
1027 if (pubkey != NULL)
1028 *pubkey = NULL;
1029 if ((r = sshsig_parse_preamble(signature)) != 0)
1030 return r;
1031 if ((r = sshkey_froms(signature, &pk)) != 0)
1032 return r;
1033
1034 *pubkey = pk;
1035 pk = NULL;
1036 return 0;
1037}