diff options
author | djm@openbsd.org <djm@openbsd.org> | 2020-01-23 23:31:52 +0000 |
---|---|---|
committer | Damien Miller <djm@mindrot.org> | 2020-01-25 11:27:29 +1100 |
commit | 72a8bea2d748c8bd7f076a8b39a52082c79ae95f (patch) | |
tree | 14bea4a63d81af371d75708384811f5829a38267 | |
parent | 0585b5697201f5d8b32e6f1b0fee7e188268d30d (diff) |
upstream: ssh-keygen -Y find-principals fixes based on feedback
from Markus:
use "principals" instead of principal, as allowed_signers lines may list
multiple.
When the signing key is a certificate, emit only principals that match
the certificate principal list.
NB. the command -Y name changes: "find-principal" => "find-principals"
ok markus@
OpenBSD-Commit-ID: ab575946ff9a55624cd4e811bfd338bf3b1d0faf
-rw-r--r-- | ssh-keygen.1 | 11 | ||||
-rw-r--r-- | ssh-keygen.c | 27 | ||||
-rw-r--r-- | sshsig.c | 74 | ||||
-rw-r--r-- | sshsig.h | 5 |
4 files changed, 84 insertions, 33 deletions
diff --git a/ssh-keygen.1 b/ssh-keygen.1 index 5d33902f7..b4a873920 100644 --- a/ssh-keygen.1 +++ b/ssh-keygen.1 | |||
@@ -1,4 +1,4 @@ | |||
1 | .\" $OpenBSD: ssh-keygen.1,v 1.195 2020/01/23 07:16:38 jmc Exp $ | 1 | .\" $OpenBSD: ssh-keygen.1,v 1.196 2020/01/23 23:31:52 djm 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 |
@@ -138,7 +138,7 @@ | |||
138 | .Fl f Ar krl_file | 138 | .Fl f Ar krl_file |
139 | .Ar | 139 | .Ar |
140 | .Nm ssh-keygen | 140 | .Nm ssh-keygen |
141 | .Fl Y Cm find-principal | 141 | .Fl Y Cm find-principals |
142 | .Fl s Ar signature_file | 142 | .Fl s Ar signature_file |
143 | .Fl f Ar allowed_signers_file | 143 | .Fl f Ar allowed_signers_file |
144 | .Nm ssh-keygen | 144 | .Nm ssh-keygen |
@@ -618,8 +618,8 @@ The maximum is 3. | |||
618 | Specifies a path to a library that will be used when creating | 618 | Specifies a path to a library that will be used when creating |
619 | FIDO authenticator-hosted keys, overriding the default of using | 619 | FIDO authenticator-hosted keys, overriding the default of using |
620 | the internal USB HID support. | 620 | the internal USB HID support. |
621 | .It Fl Y Cm find-principal | 621 | .It Fl Y Cm find-principals |
622 | Find the principal associated with the public key of a signature, | 622 | Find the principal(s) associated with the public key of a signature, |
623 | provided using the | 623 | provided using the |
624 | .Fl s | 624 | .Fl s |
625 | flag in an authorized signers file provided using the | 625 | flag in an authorized signers file provided using the |
@@ -628,7 +628,8 @@ flag. | |||
628 | The format of the allowed signers file is documented in the | 628 | The format of the allowed signers file is documented in the |
629 | .Sx ALLOWED SIGNERS | 629 | .Sx ALLOWED SIGNERS |
630 | section below. | 630 | section below. |
631 | If a matching principal is found, it is returned on standard output. | 631 | If one or more matching principals are found, they are returned on |
632 | standard output. | ||
632 | .It Fl Y Cm check-novalidate | 633 | .It Fl Y Cm check-novalidate |
633 | Checks that a signature generated using | 634 | Checks that a signature generated using |
634 | .Nm | 635 | .Nm |
diff --git a/ssh-keygen.c b/ssh-keygen.c index ce94a5ab0..363da70db 100644 --- a/ssh-keygen.c +++ b/ssh-keygen.c | |||
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: ssh-keygen.c,v 1.387 2020/01/23 07:54:04 djm Exp $ */ | 1 | /* $OpenBSD: ssh-keygen.c,v 1.388 2020/01/23 23:31:52 djm Exp $ */ |
2 | /* | 2 | /* |
3 | * Author: Tatu Ylonen <ylo@cs.hut.fi> | 3 | * Author: Tatu Ylonen <ylo@cs.hut.fi> |
4 | * Copyright (c) 1994 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | 4 | * Copyright (c) 1994 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland |
@@ -2758,11 +2758,11 @@ done: | |||
2758 | } | 2758 | } |
2759 | 2759 | ||
2760 | static int | 2760 | static int |
2761 | sig_find_principal(const char *signature, const char *allowed_keys) { | 2761 | sig_find_principals(const char *signature, const char *allowed_keys) { |
2762 | int r, ret = -1, sigfd = -1; | 2762 | int r, ret = -1, sigfd = -1; |
2763 | struct sshbuf *sigbuf = NULL, *abuf = NULL; | 2763 | struct sshbuf *sigbuf = NULL, *abuf = NULL; |
2764 | struct sshkey *sign_key = NULL; | 2764 | struct sshkey *sign_key = NULL; |
2765 | char *principal = NULL; | 2765 | char *principals = NULL; |
2766 | 2766 | ||
2767 | if ((abuf = sshbuf_new()) == NULL) | 2767 | if ((abuf = sshbuf_new()) == NULL) |
2768 | fatal("%s: sshbuf_new() failed", __func__); | 2768 | fatal("%s: sshbuf_new() failed", __func__); |
@@ -2782,12 +2782,11 @@ sig_find_principal(const char *signature, const char *allowed_keys) { | |||
2782 | } | 2782 | } |
2783 | if ((r = sshsig_get_pubkey(sigbuf, &sign_key)) != 0) { | 2783 | if ((r = sshsig_get_pubkey(sigbuf, &sign_key)) != 0) { |
2784 | error("%s: sshsig_get_pubkey: %s", | 2784 | error("%s: sshsig_get_pubkey: %s", |
2785 | __func__, ssh_err(r)); | 2785 | __func__, ssh_err(r)); |
2786 | goto done; | 2786 | goto done; |
2787 | } | 2787 | } |
2788 | 2788 | if ((r = sshsig_find_principals(allowed_keys, sign_key, | |
2789 | if ((r = sshsig_find_principal(allowed_keys, sign_key, | 2789 | &principals)) != 0) { |
2790 | &principal)) != 0) { | ||
2791 | error("%s: sshsig_get_principal: %s", | 2790 | error("%s: sshsig_get_principal: %s", |
2792 | __func__, ssh_err(r)); | 2791 | __func__, ssh_err(r)); |
2793 | goto done; | 2792 | goto done; |
@@ -2795,7 +2794,7 @@ sig_find_principal(const char *signature, const char *allowed_keys) { | |||
2795 | ret = 0; | 2794 | ret = 0; |
2796 | done: | 2795 | done: |
2797 | if (ret == 0 ) { | 2796 | if (ret == 0 ) { |
2798 | printf("Found matching principal: %s\n", principal); | 2797 | printf("Found matching principal: %s\n", principals); |
2799 | } else { | 2798 | } else { |
2800 | printf("Could not find matching principal.\n"); | 2799 | printf("Could not find matching principal.\n"); |
2801 | } | 2800 | } |
@@ -2804,7 +2803,7 @@ done: | |||
2804 | sshbuf_free(sigbuf); | 2803 | sshbuf_free(sigbuf); |
2805 | sshbuf_free(abuf); | 2804 | sshbuf_free(abuf); |
2806 | sshkey_free(sign_key); | 2805 | sshkey_free(sign_key); |
2807 | free(principal); | 2806 | free(principals); |
2808 | return ret; | 2807 | return ret; |
2809 | } | 2808 | } |
2810 | 2809 | ||
@@ -3093,7 +3092,7 @@ usage(void) | |||
3093 | " ssh-keygen -k -f krl_file [-u] [-s ca_public] [-z version_number]\n" | 3092 | " ssh-keygen -k -f krl_file [-u] [-s ca_public] [-z version_number]\n" |
3094 | " file ...\n" | 3093 | " file ...\n" |
3095 | " ssh-keygen -Q -f krl_file file ...\n" | 3094 | " ssh-keygen -Q -f krl_file file ...\n" |
3096 | " ssh-keygen -Y find-principal -s signature_file -f allowed_signers_file\n" | 3095 | " ssh-keygen -Y find-principals -s signature_file -f allowed_signers_file\n" |
3097 | " ssh-keygen -Y check-novalidate -n namespace -s signature_file\n" | 3096 | " ssh-keygen -Y check-novalidate -n namespace -s signature_file\n" |
3098 | " ssh-keygen -Y sign -f key_file -n namespace file ...\n" | 3097 | " ssh-keygen -Y sign -f key_file -n namespace file ...\n" |
3099 | " ssh-keygen -Y verify -f allowed_signers_file -I signer_identity\n" | 3098 | " ssh-keygen -Y verify -f allowed_signers_file -I signer_identity\n" |
@@ -3357,18 +3356,18 @@ main(int argc, char **argv) | |||
3357 | argc -= optind; | 3356 | argc -= optind; |
3358 | 3357 | ||
3359 | if (sign_op != NULL) { | 3358 | if (sign_op != NULL) { |
3360 | if (strncmp(sign_op, "find-principal", 14) == 0) { | 3359 | if (strncmp(sign_op, "find-principals", 15) == 0) { |
3361 | if (ca_key_path == NULL) { | 3360 | if (ca_key_path == NULL) { |
3362 | error("Too few arguments for find-principal:" | 3361 | error("Too few arguments for find-principals:" |
3363 | "missing signature file"); | 3362 | "missing signature file"); |
3364 | exit(1); | 3363 | exit(1); |
3365 | } | 3364 | } |
3366 | if (!have_identity) { | 3365 | if (!have_identity) { |
3367 | error("Too few arguments for find-principal:" | 3366 | error("Too few arguments for find-principals:" |
3368 | "missing allowed keys file"); | 3367 | "missing allowed keys file"); |
3369 | exit(1); | 3368 | exit(1); |
3370 | } | 3369 | } |
3371 | return sig_find_principal(ca_key_path, identity_file); | 3370 | return sig_find_principals(ca_key_path, identity_file); |
3372 | } | 3371 | } |
3373 | if (cert_principals == NULL || *cert_principals == '\0') { | 3372 | if (cert_principals == NULL || *cert_principals == '\0') { |
3374 | error("Too few arguments for sign/verify: " | 3373 | error("Too few arguments for sign/verify: " |
@@ -868,13 +868,64 @@ sshsig_check_allowed_keys(const char *path, const struct sshkey *sign_key, | |||
868 | } | 868 | } |
869 | 869 | ||
870 | static int | 870 | static int |
871 | get_matching_principal_from_line(const char *path, u_long linenum, char *line, | 871 | cert_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 | |||
922 | static int | ||
923 | get_matching_principals_from_line(const char *path, u_long linenum, char *line, | ||
872 | const struct sshkey *sign_key, char **principalsp) | 924 | const struct sshkey *sign_key, char **principalsp) |
873 | { | 925 | { |
874 | struct sshkey *found_key = NULL; | 926 | struct sshkey *found_key = NULL; |
875 | char *principals = NULL; | 927 | char *principals = NULL; |
876 | int r, found = 0; | 928 | int r, found = 0; |
877 | const char *reason = NULL; | ||
878 | struct sshsigopt *sigopts = NULL; | 929 | struct sshsigopt *sigopts = NULL; |
879 | 930 | ||
880 | if (principalsp != NULL) | 931 | if (principalsp != NULL) |
@@ -894,11 +945,12 @@ get_matching_principal_from_line(const char *path, u_long linenum, char *line, | |||
894 | found = 1; | 945 | found = 1; |
895 | } else if (sigopts->ca && sshkey_is_cert(sign_key) && | 946 | } else if (sigopts->ca && sshkey_is_cert(sign_key) && |
896 | sshkey_equal_public(sign_key->cert->signature_key, found_key)) { | 947 | sshkey_equal_public(sign_key->cert->signature_key, found_key)) { |
897 | /* Match of certificate's CA key */ | 948 | /* Remove principals listed in file but not allowed by cert */ |
898 | if ((r = sshkey_cert_check_authority(sign_key, 0, 1, | 949 | if ((r = cert_filter_principals(path, linenum, |
899 | principals, &reason)) != 0) { | 950 | &principals, sign_key)) != 0) { |
900 | error("%s:%lu: certificate not authorized: %s", | 951 | /* error already displayed */ |
901 | path, linenum, reason); | 952 | debug("%s:%lu: cert_filter_principals: %s", |
953 | path, linenum, ssh_err(r)); | ||
902 | goto done; | 954 | goto done; |
903 | } | 955 | } |
904 | debug("%s:%lu: matched certificate CA key", path, linenum); | 956 | debug("%s:%lu: matched certificate CA key", path, linenum); |
@@ -920,8 +972,8 @@ get_matching_principal_from_line(const char *path, u_long linenum, char *line, | |||
920 | } | 972 | } |
921 | 973 | ||
922 | int | 974 | int |
923 | sshsig_find_principal(const char *path, const struct sshkey *sign_key, | 975 | sshsig_find_principals(const char *path, const struct sshkey *sign_key, |
924 | char **principal) | 976 | char **principals) |
925 | { | 977 | { |
926 | FILE *f = NULL; | 978 | FILE *f = NULL; |
927 | char *line = NULL; | 979 | char *line = NULL; |
@@ -939,8 +991,8 @@ sshsig_find_principal(const char *path, const struct sshkey *sign_key, | |||
939 | 991 | ||
940 | while (getline(&line, &linesize, f) != -1) { | 992 | while (getline(&line, &linesize, f) != -1) { |
941 | linenum++; | 993 | linenum++; |
942 | r = get_matching_principal_from_line(path, linenum, line, | 994 | r = get_matching_principals_from_line(path, linenum, line, |
943 | sign_key, principal); | 995 | sign_key, principals); |
944 | free(line); | 996 | free(line); |
945 | line = NULL; | 997 | line = NULL; |
946 | if (r == SSH_ERR_KEY_NOT_FOUND) | 998 | if (r == SSH_ERR_KEY_NOT_FOUND) |
@@ -93,13 +93,12 @@ struct sshsigopt *sshsigopt_parse(const char *opts, | |||
93 | void sshsigopt_free(struct sshsigopt *opts); | 93 | void sshsigopt_free(struct sshsigopt *opts); |
94 | 94 | ||
95 | /* Get public key from signature */ | 95 | /* Get public key from signature */ |
96 | int | 96 | int sshsig_get_pubkey(struct sshbuf *signature, struct sshkey **pubkey); |
97 | sshsig_get_pubkey(struct sshbuf *signature, struct sshkey **pubkey); | ||
98 | 97 | ||
99 | /* Find principal in allowed_keys file, given a sshkey. Returns | 98 | /* Find principal in allowed_keys file, given a sshkey. Returns |
100 | * 0 on success. | 99 | * 0 on success. |
101 | */ | 100 | */ |
102 | int sshsig_find_principal(const char *path, const struct sshkey *sign_key, | 101 | int sshsig_find_principals(const char *path, const struct sshkey *sign_key, |
103 | char **principal); | 102 | char **principal); |
104 | 103 | ||
105 | #endif /* SSHSIG_H */ | 104 | #endif /* SSHSIG_H */ |