]> git.ipfire.org Git - thirdparty/openssh-portable.git/commitdiff
upstream: add a new signature operations "find-principal" to look
authordjm@openbsd.org <djm@openbsd.org>
Thu, 23 Jan 2020 02:43:48 +0000 (02:43 +0000)
committerDamien Miller <djm@mindrot.org>
Thu, 23 Jan 2020 02:45:24 +0000 (13:45 +1100)
up the principal associated with a signature from an allowed-signers file.
Work by Sebastian Kinne; ok dtucker@

OpenBSD-Commit-ID: 6f782cc7e18e38fcfafa62af53246a1dcfe74e5d

ssh-keygen.1
ssh-keygen.c
sshsig.c

index c0a22606b86500e9bea16a279b96889625b72e76..33e3f5375d6d6a4fc5672a0006653545a0300b2c 100644 (file)
@@ -1,4 +1,4 @@
-.\"    $OpenBSD: ssh-keygen.1,v 1.193 2020/01/18 21:16:43 naddy Exp $
+.\"    $OpenBSD: ssh-keygen.1,v 1.194 2020/01/23 02:43:48 djm Exp $
 .\"
 .\" Author: Tatu Ylonen <ylo@cs.hut.fi>
 .\" Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -35,7 +35,7 @@
 .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 .\"
-.Dd $Mdocdate: January 18 2020 $
+.Dd $Mdocdate: January 23 2020 $
 .Dt SSH-KEYGEN 1
 .Os
 .Sh NAME
 .Fl f Ar krl_file
 .Ar
 .Nm ssh-keygen
+.Fl Y Cm find-principal
+.Fl s Ar signature_file
+.Fl f Ar allowed_signers_file
+.Nm ssh-keygen
 .Fl Y Cm check-novalidate
 .Fl n Ar namespace
 .Fl s Ar signature_file
@@ -614,6 +618,17 @@ The maximum is 3.
 Specifies a path to a library that will be used when creating
 FIDO authenticator-hosted keys, overriding the default of using
 the internal USB HID support.
+.It Fl Y Cm find-principal
+Find the principal associated with the public key of a signature,
+provided using the
+.Fl s
+flag in an authorized signers file provided using the
+.Fl f
+flag.
+The format of the allowed signers file is documented in the
+.Sx ALLOWED SIGNERS
+section below. If a matching principal is found, it is returned
+on standard output.
 .It Fl Y Cm check-novalidate
 Checks that a signature generated using
 .Nm
index 04492979bc0d78aad39233ddcf1c96ca7d7c8102..eebd89a276dfcd59377533fb7b7bf843cc45ce05 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: ssh-keygen.c,v 1.385 2020/01/22 04:51:51 claudio Exp $ */
+/* $OpenBSD: ssh-keygen.c,v 1.386 2020/01/23 02:43:48 djm Exp $ */
 /*
  * Author: Tatu Ylonen <ylo@cs.hut.fi>
  * 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,
 }
 
 static int
-sign(const char *keypath, const char *sig_namespace, int argc, char **argv)
+sig_sign(const char *keypath, const char *sig_namespace, int argc, char **argv)
 {
        int i, fd = -1, r, ret = -1;
        int agent_fd = -1;
@@ -2670,8 +2670,8 @@ done:
 }
 
 static int
-verify(const char *signature, const char *sig_namespace, const char *principal,
-    const char *allowed_keys, const char *revoked_keys)
+sig_verify(const char *signature, const char *sig_namespace,
+    const char *principal, const char *allowed_keys, const char *revoked_keys)
 {
        int r, ret = -1, sigfd = -1;
        struct sshbuf *sigbuf = NULL, *abuf = NULL;
@@ -2694,7 +2694,7 @@ verify(const char *signature, const char *sig_namespace, const char *principal,
        }
        if ((r = sshsig_dearmor(abuf, &sigbuf)) != 0) {
                error("%s: sshsig_armor: %s", __func__, ssh_err(r));
-               return r;
+               goto done;
        }
        if ((r = sshsig_verify_fd(sigbuf, STDIN_FILENO, sig_namespace,
            &sign_key, &sig_details)) != 0)
@@ -2757,6 +2757,57 @@ done:
        return ret;
 }
 
+static int
+sig_find_principal(const char *signature, const char *allowed_keys) {
+       int r, ret = -1, sigfd = -1;
+       struct sshbuf *sigbuf = NULL, *abuf = NULL;
+       struct sshkey *sign_key = NULL;
+       char *principal = NULL;
+
+       if ((abuf = sshbuf_new()) == NULL)
+               fatal("%s: sshbuf_new() failed", __func__);
+
+       if ((sigfd = open(signature, O_RDONLY)) < 0) {
+               error("Couldn't open signature file %s", signature);
+               goto done;
+       }
+
+       if ((r = sshkey_load_file(sigfd, abuf)) != 0) {
+               error("Couldn't read signature file: %s", ssh_err(r));
+               goto done;
+       }
+       if ((r = sshsig_dearmor(abuf, &sigbuf)) != 0) {
+               error("%s: sshsig_armor: %s", __func__, ssh_err(r));
+               goto done;
+       }
+       if ((r = sshsig_get_pubkey(sigbuf, &sign_key)) != 0) {
+               error("%s: sshsig_get_pubkey: %s",
+                     __func__, ssh_err(r));
+               goto done;
+       }
+
+       if ((r = sshsig_find_principal(allowed_keys, sign_key,
+                                     &principal)) != 0) {
+               error("%s: sshsig_get_principal: %s",
+                     __func__, ssh_err(r));
+               goto done;
+       }
+       ret = 0;
+done:
+       if (ret == 0 ) {
+               printf("Found matching principal: %s\n", principal);
+       } else {
+               printf("Could not find matching principal.\n");
+       }
+       if (sigfd != -1)
+               close(sigfd);
+       sshbuf_free(sigbuf);
+       sshbuf_free(abuf);
+       sshkey_free(sign_key);
+       free(principal);
+       return ret;
+}
+
 static void
 do_moduli_gen(const char *out_file, char **opts, size_t nopts)
 {
@@ -3042,6 +3093,7 @@ usage(void)
            "       ssh-keygen -k -f krl_file [-u] [-s ca_public] [-z version_number]\n"
            "                  file ...\n"
            "       ssh-keygen -Q -f krl_file file ...\n"
+           "       ssh-keygen -Y find-principal -s signature_file -f allowed_signers_file\n"
            "       ssh-keygen -Y check-novalidate -n namespace -s signature_file\n"
            "       ssh-keygen -Y sign -f key_file -n namespace file ...\n"
            "       ssh-keygen -Y verify -f allowed_signers_file -I signer_identity\n"
@@ -3305,6 +3357,19 @@ main(int argc, char **argv)
        argc -= optind;
 
        if (sign_op != NULL) {
+               if (strncmp(sign_op, "find-principal", 14) == 0) {
+                       if (ca_key_path == NULL) {
+                               error("Too few arguments for find-principal:"
+                                     "missing signature file");
+                               exit(1);
+                       }
+                       if (!have_identity) {
+                               error("Too few arguments for find-principal:"
+                                     "missing allowed keys file");
+                               exit(1);
+                       }
+                       return sig_find_principal(ca_key_path, identity_file);
+               }
                if (cert_principals == NULL || *cert_principals == '\0') {
                        error("Too few arguments for sign/verify: "
                            "missing namespace");
@@ -3316,15 +3381,16 @@ main(int argc, char **argv)
                                    "missing key");
                                exit(1);
                        }
-                       return sign(identity_file, cert_principals, argc, argv);
+                       return sig_sign(identity_file, cert_principals,
+                           argc, argv);
                } else if (strncmp(sign_op, "check-novalidate", 16) == 0) {
                        if (ca_key_path == NULL) {
                                error("Too few arguments for check-novalidate: "
                                      "missing signature file");
                                exit(1);
                        }
-                       return verify(ca_key_path, cert_principals,
-                                     NULL, NULL, NULL);
+                       return sig_verify(ca_key_path, cert_principals,
+                           NULL, NULL, NULL);
                } else if (strncmp(sign_op, "verify", 6) == 0) {
                        if (ca_key_path == NULL) {
                                error("Too few arguments for verify: "
@@ -3341,7 +3407,7 @@ main(int argc, char **argv)
                                    "missing principal ID");
                                exit(1);
                        }
-                       return verify(ca_key_path, cert_principals,
+                       return sig_verify(ca_key_path, cert_principals,
                            cert_key_id, identity_file, rr_hostname);
                }
                usage();
index 6d72f92f5f1f07747cf73cf44c24bc6006516e08..e9f4baa762decc12b071c1b0a8346236b8ea0b0b 100644 (file)
--- a/sshsig.c
+++ b/sshsig.c
@@ -866,3 +866,120 @@ sshsig_check_allowed_keys(const char *path, const struct sshkey *sign_key,
        free(line);
        return r == 0 ? SSH_ERR_KEY_NOT_FOUND : r;
 }
+
+static int
+get_matching_principal_from_line(const char *path, u_long linenum, char *line,
+    const struct sshkey *sign_key, char **principalsp)
+{
+       struct sshkey *found_key = NULL;
+       char *principals = NULL;
+       int r, found = 0;
+       const char *reason = NULL;
+       struct sshsigopt *sigopts = NULL;
+
+       if (principalsp != NULL)
+               *principalsp = NULL;
+
+       /* Parse the line */
+       if ((r = parse_principals_key_and_options(path, linenum, line,
+           NULL, &principals, &found_key, &sigopts)) != 0) {
+               /* error already logged */
+               goto done;
+       }
+
+       if (!sigopts->ca && sshkey_equal(found_key, sign_key)) {
+               /* Exact match of key */
+               debug("%s:%lu: matched key", path, linenum);
+               /* success */
+               found = 1;
+       } else if (sigopts->ca && sshkey_is_cert(sign_key) &&
+           sshkey_equal_public(sign_key->cert->signature_key, found_key)) {
+               /* Match of certificate's CA key */
+               if ((r = sshkey_cert_check_authority(sign_key, 0, 1,
+                   principals, &reason)) != 0) {
+                       error("%s:%lu: certificate not authorized: %s",
+                           path, linenum, reason);
+                       goto done;
+               }
+               debug("%s:%lu: matched certificate CA key", path, linenum);
+               /* success */
+               found = 1;
+       } else {
+               /* Key didn't match */
+               goto done;
+       }
+ done:
+       if (found) {
+               *principalsp = principals;
+               principals = NULL; /* transferred */
+       }
+       free(principals);
+       sshkey_free(found_key);
+       sshsigopt_free(sigopts);
+       return found ? 0 : SSH_ERR_KEY_NOT_FOUND;
+}
+
+int
+sshsig_find_principal(const char *path, const struct sshkey *sign_key,
+    char **principal)
+{
+       FILE *f = NULL;
+       char *line = NULL;
+       size_t linesize = 0;
+       u_long linenum = 0;
+       int r, oerrno;
+
+       if ((f = fopen(path, "r")) == NULL) {
+               oerrno = errno;
+               error("Unable to open allowed keys file \"%s\": %s",
+                   path, strerror(errno));
+               errno = oerrno;
+               return SSH_ERR_SYSTEM_ERROR;
+       }
+
+       while (getline(&line, &linesize, f) != -1) {
+               linenum++;
+               r = get_matching_principal_from_line(path, linenum, line,
+                   sign_key, principal);
+               free(line);
+               line = NULL;
+               if (r == SSH_ERR_KEY_NOT_FOUND)
+                       continue;
+               else if (r == 0) {
+                       /* success */
+                       fclose(f);
+                       return 0;
+               } else
+                       break;
+       }
+       free(line);
+       /* Either we hit an error parsing or we simply didn't find the key */
+       if (ferror(f) != 0) {
+               oerrno = errno;
+               fclose(f);
+               error("Unable to read allowed keys file \"%s\": %s",
+                   path, strerror(errno));
+               errno = oerrno;
+               return SSH_ERR_SYSTEM_ERROR;
+       }
+       fclose(f);
+       return r == 0 ? SSH_ERR_KEY_NOT_FOUND : r;
+}
+
+int
+sshsig_get_pubkey(struct sshbuf *signature, struct sshkey **pubkey)
+{
+       struct sshkey *pk = NULL;
+       int r = SSH_ERR_SIGNATURE_INVALID;
+
+       if (pubkey != NULL)
+               *pubkey = NULL;
+       if ((r = sshsig_parse_preamble(signature)) != 0)
+               return r;
+       if ((r = sshkey_froms(signature, &pk)) != 0)
+               return r;
+
+       *pubkey = pk;
+       pk = NULL;
+       return 0;
+}