summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordjm@openbsd.org <djm@openbsd.org>2020-01-23 02:43:48 +0000
committerDamien Miller <djm@mindrot.org>2020-01-23 13:45:24 +1100
commit56cffcc09f8a2e661d2ba02e61364ae6f998b2b1 (patch)
tree7056f21f29a73cce790ed19c6118983f1ceb6c7d
parent65cf8730de6876a56595eef296e07a86c52534a6 (diff)
upstream: add a new signature operations "find-principal" to look
up the principal associated with a signature from an allowed-signers file. Work by Sebastian Kinne; ok dtucker@ OpenBSD-Commit-ID: 6f782cc7e18e38fcfafa62af53246a1dcfe74e5d
-rw-r--r--ssh-keygen.119
-rw-r--r--ssh-keygen.c84
-rw-r--r--sshsig.c117
3 files changed, 209 insertions, 11 deletions
diff --git a/ssh-keygen.1 b/ssh-keygen.1
index c0a22606b..33e3f5375 100644
--- a/ssh-keygen.1
+++ b/ssh-keygen.1
@@ -1,4 +1,4 @@
1.\" $OpenBSD: ssh-keygen.1,v 1.193 2020/01/18 21:16:43 naddy Exp $ 1.\" $OpenBSD: ssh-keygen.1,v 1.194 2020/01/23 02:43:48 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
@@ -35,7 +35,7 @@
35.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 35.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
36.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 36.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
37.\" 37.\"
38.Dd $Mdocdate: January 18 2020 $ 38.Dd $Mdocdate: January 23 2020 $
39.Dt SSH-KEYGEN 1 39.Dt SSH-KEYGEN 1
40.Os 40.Os
41.Sh NAME 41.Sh NAME
@@ -138,6 +138,10 @@
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
142.Fl s Ar signature_file
143.Fl f Ar allowed_signers_file
144.Nm ssh-keygen
141.Fl Y Cm check-novalidate 145.Fl Y Cm check-novalidate
142.Fl n Ar namespace 146.Fl n Ar namespace
143.Fl s Ar signature_file 147.Fl s Ar signature_file
@@ -614,6 +618,17 @@ The maximum is 3.
614Specifies a path to a library that will be used when creating 618Specifies a path to a library that will be used when creating
615FIDO authenticator-hosted keys, overriding the default of using 619FIDO authenticator-hosted keys, overriding the default of using
616the internal USB HID support. 620the internal USB HID support.
621.It Fl Y Cm find-principal
622Find the principal associated with the public key of a signature,
623provided using the
624.Fl s
625flag in an authorized signers file provided using the
626.Fl f
627flag.
628The format of the allowed signers file is documented in the
629.Sx ALLOWED SIGNERS
630section below. If a matching principal is found, it is returned
631on standard output.
617.It Fl Y Cm check-novalidate 632.It Fl Y Cm check-novalidate
618Checks that a signature generated using 633Checks that a signature generated using
619.Nm 634.Nm
diff --git a/ssh-keygen.c b/ssh-keygen.c
index 04492979b..eebd89a27 100644
--- a/ssh-keygen.c
+++ b/ssh-keygen.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: ssh-keygen.c,v 1.385 2020/01/22 04:51:51 claudio Exp $ */ 1/* $OpenBSD: ssh-keygen.c,v 1.386 2020/01/23 02:43:48 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
@@ -2599,7 +2599,7 @@ sign_one(struct sshkey *signkey, const char *filename, int fd,
2599} 2599}
2600 2600
2601static int 2601static int
2602sign(const char *keypath, const char *sig_namespace, int argc, char **argv) 2602sig_sign(const char *keypath, const char *sig_namespace, int argc, char **argv)
2603{ 2603{
2604 int i, fd = -1, r, ret = -1; 2604 int i, fd = -1, r, ret = -1;
2605 int agent_fd = -1; 2605 int agent_fd = -1;
@@ -2670,8 +2670,8 @@ done:
2670} 2670}
2671 2671
2672static int 2672static int
2673verify(const char *signature, const char *sig_namespace, const char *principal, 2673sig_verify(const char *signature, const char *sig_namespace,
2674 const char *allowed_keys, const char *revoked_keys) 2674 const char *principal, const char *allowed_keys, const char *revoked_keys)
2675{ 2675{
2676 int r, ret = -1, sigfd = -1; 2676 int r, ret = -1, sigfd = -1;
2677 struct sshbuf *sigbuf = NULL, *abuf = NULL; 2677 struct sshbuf *sigbuf = NULL, *abuf = NULL;
@@ -2694,7 +2694,7 @@ verify(const char *signature, const char *sig_namespace, const char *principal,
2694 } 2694 }
2695 if ((r = sshsig_dearmor(abuf, &sigbuf)) != 0) { 2695 if ((r = sshsig_dearmor(abuf, &sigbuf)) != 0) {
2696 error("%s: sshsig_armor: %s", __func__, ssh_err(r)); 2696 error("%s: sshsig_armor: %s", __func__, ssh_err(r));
2697 return r; 2697 goto done;
2698 } 2698 }
2699 if ((r = sshsig_verify_fd(sigbuf, STDIN_FILENO, sig_namespace, 2699 if ((r = sshsig_verify_fd(sigbuf, STDIN_FILENO, sig_namespace,
2700 &sign_key, &sig_details)) != 0) 2700 &sign_key, &sig_details)) != 0)
@@ -2757,6 +2757,57 @@ done:
2757 return ret; 2757 return ret;
2758} 2758}
2759 2759
2760static int
2761sig_find_principal(const char *signature, const char *allowed_keys) {
2762 int r, ret = -1, sigfd = -1;
2763 struct sshbuf *sigbuf = NULL, *abuf = NULL;
2764 struct sshkey *sign_key = NULL;
2765 char *principal = NULL;
2766
2767 if ((abuf = sshbuf_new()) == NULL)
2768 fatal("%s: sshbuf_new() failed", __func__);
2769
2770 if ((sigfd = open(signature, O_RDONLY)) < 0) {
2771 error("Couldn't open signature file %s", signature);
2772 goto done;
2773 }
2774
2775 if ((r = sshkey_load_file(sigfd, abuf)) != 0) {
2776 error("Couldn't read signature file: %s", ssh_err(r));
2777 goto done;
2778 }
2779 if ((r = sshsig_dearmor(abuf, &sigbuf)) != 0) {
2780 error("%s: sshsig_armor: %s", __func__, ssh_err(r));
2781 goto done;
2782 }
2783 if ((r = sshsig_get_pubkey(sigbuf, &sign_key)) != 0) {
2784 error("%s: sshsig_get_pubkey: %s",
2785 __func__, ssh_err(r));
2786 goto done;
2787 }
2788
2789 if ((r = sshsig_find_principal(allowed_keys, sign_key,
2790 &principal)) != 0) {
2791 error("%s: sshsig_get_principal: %s",
2792 __func__, ssh_err(r));
2793 goto done;
2794 }
2795 ret = 0;
2796done:
2797 if (ret == 0 ) {
2798 printf("Found matching principal: %s\n", principal);
2799 } else {
2800 printf("Could not find matching principal.\n");
2801 }
2802 if (sigfd != -1)
2803 close(sigfd);
2804 sshbuf_free(sigbuf);
2805 sshbuf_free(abuf);
2806 sshkey_free(sign_key);
2807 free(principal);
2808 return ret;
2809}
2810
2760static void 2811static void
2761do_moduli_gen(const char *out_file, char **opts, size_t nopts) 2812do_moduli_gen(const char *out_file, char **opts, size_t nopts)
2762{ 2813{
@@ -3042,6 +3093,7 @@ usage(void)
3042 " ssh-keygen -k -f krl_file [-u] [-s ca_public] [-z version_number]\n" 3093 " ssh-keygen -k -f krl_file [-u] [-s ca_public] [-z version_number]\n"
3043 " file ...\n" 3094 " file ...\n"
3044 " ssh-keygen -Q -f krl_file file ...\n" 3095 " ssh-keygen -Q -f krl_file file ...\n"
3096 " ssh-keygen -Y find-principal -s signature_file -f allowed_signers_file\n"
3045 " ssh-keygen -Y check-novalidate -n namespace -s signature_file\n" 3097 " ssh-keygen -Y check-novalidate -n namespace -s signature_file\n"
3046 " ssh-keygen -Y sign -f key_file -n namespace file ...\n" 3098 " ssh-keygen -Y sign -f key_file -n namespace file ...\n"
3047 " ssh-keygen -Y verify -f allowed_signers_file -I signer_identity\n" 3099 " ssh-keygen -Y verify -f allowed_signers_file -I signer_identity\n"
@@ -3305,6 +3357,19 @@ main(int argc, char **argv)
3305 argc -= optind; 3357 argc -= optind;
3306 3358
3307 if (sign_op != NULL) { 3359 if (sign_op != NULL) {
3360 if (strncmp(sign_op, "find-principal", 14) == 0) {
3361 if (ca_key_path == NULL) {
3362 error("Too few arguments for find-principal:"
3363 "missing signature file");
3364 exit(1);
3365 }
3366 if (!have_identity) {
3367 error("Too few arguments for find-principal:"
3368 "missing allowed keys file");
3369 exit(1);
3370 }
3371 return sig_find_principal(ca_key_path, identity_file);
3372 }
3308 if (cert_principals == NULL || *cert_principals == '\0') { 3373 if (cert_principals == NULL || *cert_principals == '\0') {
3309 error("Too few arguments for sign/verify: " 3374 error("Too few arguments for sign/verify: "
3310 "missing namespace"); 3375 "missing namespace");
@@ -3316,15 +3381,16 @@ main(int argc, char **argv)
3316 "missing key"); 3381 "missing key");
3317 exit(1); 3382 exit(1);
3318 } 3383 }
3319 return sign(identity_file, cert_principals, argc, argv); 3384 return sig_sign(identity_file, cert_principals,
3385 argc, argv);
3320 } else if (strncmp(sign_op, "check-novalidate", 16) == 0) { 3386 } else if (strncmp(sign_op, "check-novalidate", 16) == 0) {
3321 if (ca_key_path == NULL) { 3387 if (ca_key_path == NULL) {
3322 error("Too few arguments for check-novalidate: " 3388 error("Too few arguments for check-novalidate: "
3323 "missing signature file"); 3389 "missing signature file");
3324 exit(1); 3390 exit(1);
3325 } 3391 }
3326 return verify(ca_key_path, cert_principals, 3392 return sig_verify(ca_key_path, cert_principals,
3327 NULL, NULL, NULL); 3393 NULL, NULL, NULL);
3328 } else if (strncmp(sign_op, "verify", 6) == 0) { 3394 } else if (strncmp(sign_op, "verify", 6) == 0) {
3329 if (ca_key_path == NULL) { 3395 if (ca_key_path == NULL) {
3330 error("Too few arguments for verify: " 3396 error("Too few arguments for verify: "
@@ -3341,7 +3407,7 @@ main(int argc, char **argv)
3341 "missing principal ID"); 3407 "missing principal ID");
3342 exit(1); 3408 exit(1);
3343 } 3409 }
3344 return verify(ca_key_path, cert_principals, 3410 return sig_verify(ca_key_path, cert_principals,
3345 cert_key_id, identity_file, rr_hostname); 3411 cert_key_id, identity_file, rr_hostname);
3346 } 3412 }
3347 usage(); 3413 usage();
diff --git a/sshsig.c b/sshsig.c
index 6d72f92f5..e9f4baa76 100644
--- a/sshsig.c
+++ b/sshsig.c
@@ -866,3 +866,120 @@ sshsig_check_allowed_keys(const char *path, const struct sshkey *sign_key,
866 free(line); 866 free(line);
867 return r == 0 ? SSH_ERR_KEY_NOT_FOUND : r; 867 return r == 0 ? SSH_ERR_KEY_NOT_FOUND : r;
868} 868}
869
870static int
871get_matching_principal_from_line(const char *path, u_long linenum, char *line,
872 const struct sshkey *sign_key, char **principalsp)
873{
874 struct sshkey *found_key = NULL;
875 char *principals = NULL;
876 int r, found = 0;
877 const char *reason = NULL;
878 struct sshsigopt *sigopts = NULL;
879
880 if (principalsp != NULL)
881 *principalsp = NULL;
882
883 /* Parse the line */
884 if ((r = parse_principals_key_and_options(path, linenum, line,
885 NULL, &principals, &found_key, &sigopts)) != 0) {
886 /* error already logged */
887 goto done;
888 }
889
890 if (!sigopts->ca && sshkey_equal(found_key, sign_key)) {
891 /* Exact match of key */
892 debug("%s:%lu: matched key", path, linenum);
893 /* success */
894 found = 1;
895 } else if (sigopts->ca && sshkey_is_cert(sign_key) &&
896 sshkey_equal_public(sign_key->cert->signature_key, found_key)) {
897 /* Match of certificate's CA key */
898 if ((r = sshkey_cert_check_authority(sign_key, 0, 1,
899 principals, &reason)) != 0) {
900 error("%s:%lu: certificate not authorized: %s",
901 path, linenum, reason);
902 goto done;
903 }
904 debug("%s:%lu: matched certificate CA key", path, linenum);
905 /* success */
906 found = 1;
907 } else {
908 /* Key didn't match */
909 goto done;
910 }
911 done:
912 if (found) {
913 *principalsp = principals;
914 principals = NULL; /* transferred */
915 }
916 free(principals);
917 sshkey_free(found_key);
918 sshsigopt_free(sigopts);
919 return found ? 0 : SSH_ERR_KEY_NOT_FOUND;
920}
921
922int
923sshsig_find_principal(const char *path, const struct sshkey *sign_key,
924 char **principal)
925{
926 FILE *f = NULL;
927 char *line = NULL;
928 size_t linesize = 0;
929 u_long linenum = 0;
930 int r, oerrno;
931
932 if ((f = fopen(path, "r")) == NULL) {
933 oerrno = errno;
934 error("Unable to open allowed keys file \"%s\": %s",
935 path, strerror(errno));
936 errno = oerrno;
937 return SSH_ERR_SYSTEM_ERROR;
938 }
939
940 while (getline(&line, &linesize, f) != -1) {
941 linenum++;
942 r = get_matching_principal_from_line(path, linenum, line,
943 sign_key, principal);
944 free(line);
945 line = NULL;
946 if (r == SSH_ERR_KEY_NOT_FOUND)
947 continue;
948 else if (r == 0) {
949 /* success */
950 fclose(f);
951 return 0;
952 } else
953 break;
954 }
955 free(line);
956 /* Either we hit an error parsing or we simply didn't find the key */
957 if (ferror(f) != 0) {
958 oerrno = errno;
959 fclose(f);
960 error("Unable to read allowed keys file \"%s\": %s",
961 path, strerror(errno));
962 errno = oerrno;
963 return SSH_ERR_SYSTEM_ERROR;
964 }
965 fclose(f);
966 return r == 0 ? SSH_ERR_KEY_NOT_FOUND : r;
967}
968
969int
970sshsig_get_pubkey(struct sshbuf *signature, struct sshkey **pubkey)
971{
972 struct sshkey *pk = NULL;
973 int r = SSH_ERR_SIGNATURE_INVALID;
974
975 if (pubkey != NULL)
976 *pubkey = NULL;
977 if ((r = sshsig_parse_preamble(signature)) != 0)
978 return r;
979 if ((r = sshkey_froms(signature, &pk)) != 0)
980 return r;
981
982 *pubkey = pk;
983 pk = NULL;
984 return 0;
985}