add cert->signature_type field and keep it in sync with certificate
authordjm <djm@openbsd.org>
Wed, 12 Sep 2018 01:31:30 +0000 (01:31 +0000)
committerdjm <djm@openbsd.org>
Wed, 12 Sep 2018 01:31:30 +0000 (01:31 +0000)
signature wrt loading and certification operations; ok markus@

usr.bin/ssh/sshkey.c
usr.bin/ssh/sshkey.h

index a1d4b1e..9b88f11 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: sshkey.c,v 1.66 2018/07/03 13:20:25 djm Exp $ */
+/* $OpenBSD: sshkey.c,v 1.67 2018/09/12 01:31:30 djm Exp $ */
 /*
  * Copyright (c) 2000, 2001 Markus Friedl.  All rights reserved.
  * Copyright (c) 2008 Alexander von Gernler.  All rights reserved.
@@ -74,6 +74,7 @@ int   sshkey_private_serialize_opt(const struct sshkey *key,
     struct sshbuf *buf, enum sshkey_serialize_rep);
 static int sshkey_from_blob_internal(struct sshbuf *buf,
     struct sshkey **keyp, int allow_cert);
+static int get_sigtype(const u_char *sig, size_t siglen, char **sigtypep);
 
 /* Supported key types */
 struct keytype {
@@ -425,6 +426,7 @@ cert_free(struct sshkey_cert *cert)
                free(cert->principals[i]);
        free(cert->principals);
        sshkey_free(cert->signature_key);
+       free(cert->signature_type);
        freezero(cert, sizeof(*cert));
 }
 
@@ -444,6 +446,7 @@ cert_new(void)
        cert->key_id = NULL;
        cert->principals = NULL;
        cert->signature_key = NULL;
+       cert->signature_type = NULL;
        return cert;
 }
 
@@ -1653,54 +1656,68 @@ sshkey_cert_copy(const struct sshkey *from_key, struct sshkey *to_key)
        u_int i;
        const struct sshkey_cert *from;
        struct sshkey_cert *to;
-       int ret = SSH_ERR_INTERNAL_ERROR;
-
-       if (to_key->cert != NULL) {
-               cert_free(to_key->cert);
-               to_key->cert = NULL;
-       }
+       int r = SSH_ERR_INTERNAL_ERROR;
 
-       if ((from = from_key->cert) == NULL)
+       if (to_key == NULL || (from = from_key->cert) == NULL)
                return SSH_ERR_INVALID_ARGUMENT;
 
-       if ((to = to_key->cert = cert_new()) == NULL)
+       if ((to = cert_new()) == NULL)
                return SSH_ERR_ALLOC_FAIL;
 
-       if ((ret = sshbuf_putb(to->certblob, from->certblob)) != 0 ||
-           (ret = sshbuf_putb(to->critical, from->critical)) != 0 ||
-           (ret = sshbuf_putb(to->extensions, from->extensions)) != 0)
-               return ret;
+       if ((r = sshbuf_putb(to->certblob, from->certblob)) != 0 ||
+           (r = sshbuf_putb(to->critical, from->critical)) != 0 ||
+           (r = sshbuf_putb(to->extensions, from->extensions)) != 0)
+               goto out;
 
        to->serial = from->serial;
        to->type = from->type;
        if (from->key_id == NULL)
                to->key_id = NULL;
-       else if ((to->key_id = strdup(from->key_id)) == NULL)
-               return SSH_ERR_ALLOC_FAIL;
+       else if ((to->key_id = strdup(from->key_id)) == NULL) {
+               r = SSH_ERR_ALLOC_FAIL;
+               goto out;
+       }
        to->valid_after = from->valid_after;
        to->valid_before = from->valid_before;
        if (from->signature_key == NULL)
                to->signature_key = NULL;
-       else if ((ret = sshkey_from_private(from->signature_key,
+       else if ((r = sshkey_from_private(from->signature_key,
            &to->signature_key)) != 0)
-               return ret;
-
-       if (from->nprincipals > SSHKEY_CERT_MAX_PRINCIPALS)
-               return SSH_ERR_INVALID_ARGUMENT;
+               goto out;
+       if (from->signature_type != NULL &&
+           (to->signature_type = strdup(from->signature_type)) == NULL) {
+               r = SSH_ERR_ALLOC_FAIL;
+               goto out;
+       }
+       if (from->nprincipals > SSHKEY_CERT_MAX_PRINCIPALS) {
+               r = SSH_ERR_INVALID_ARGUMENT;
+               goto out;
+       }
        if (from->nprincipals > 0) {
                if ((to->principals = calloc(from->nprincipals,
-                   sizeof(*to->principals))) == NULL)
-                       return SSH_ERR_ALLOC_FAIL;
+                   sizeof(*to->principals))) == NULL) {
+                       r = SSH_ERR_ALLOC_FAIL;
+                       goto out;
+               }
                for (i = 0; i < from->nprincipals; i++) {
                        to->principals[i] = strdup(from->principals[i]);
                        if (to->principals[i] == NULL) {
                                to->nprincipals = i;
-                               return SSH_ERR_ALLOC_FAIL;
+                               r = SSH_ERR_ALLOC_FAIL;
+                               goto out;
                        }
                }
        }
        to->nprincipals = from->nprincipals;
-       return 0;
+
+       /* success */
+       cert_free(to_key->cert);
+       to_key->cert = to;
+       to = NULL;
+       r = 0;
+ out:
+       cert_free(to);
+       return r;
 }
 
 int
@@ -1910,6 +1927,8 @@ cert_parse(struct sshbuf *b, struct sshkey *key, struct sshbuf *certbuf)
        if ((ret = sshkey_verify(key->cert->signature_key, sig, slen,
            sshbuf_ptr(key->cert->certblob), signed_len, NULL, 0)) != 0)
                goto out;
+       if ((ret = get_sigtype(sig, slen, &key->cert->signature_type)) != 0)
+               goto out;
 
        /* Success */
        ret = 0;
@@ -2479,7 +2498,8 @@ sshkey_certify_custom(struct sshkey *k, struct sshkey *ca, const char *alg,
        u_char *ca_blob = NULL, *sig_blob = NULL, nonce[32];
        size_t i, ca_len, sig_len;
        int ret = SSH_ERR_INTERNAL_ERROR;
-       struct sshbuf *cert;
+       struct sshbuf *cert = NULL;
+       char *sigtype = NULL;
 
        if (k == NULL || k->cert == NULL ||
            k->cert->certblob == NULL || ca == NULL)
@@ -2489,6 +2509,16 @@ sshkey_certify_custom(struct sshkey *k, struct sshkey *ca, const char *alg,
        if (!sshkey_type_is_valid_ca(ca->type))
                return SSH_ERR_KEY_CERT_INVALID_SIGN_KEY;
 
+       /*
+        * If no alg specified as argument but a signature_type was set,
+        * then prefer that. If both were specified, then they must match.
+        */
+       if (alg == NULL)
+               alg = k->cert->signature_type;
+       else if (k->cert->signature_type != NULL &&
+           strcmp(alg, k->cert->signature_type) != 0)
+               return SSH_ERR_INVALID_ARGUMENT;
+
        if ((ret = sshkey_to_blob(ca, &ca_blob, &ca_len)) != 0)
                return SSH_ERR_KEY_CERT_INVALID_SIGN_KEY;
 
@@ -2575,7 +2605,17 @@ sshkey_certify_custom(struct sshkey *k, struct sshkey *ca, const char *alg,
        if ((ret = signer(ca, &sig_blob, &sig_len, sshbuf_ptr(cert),
            sshbuf_len(cert), alg, 0, signer_ctx)) != 0)
                goto out;
-
+       /* Check and update signature_type against what was actually used */
+       if ((ret = get_sigtype(sig_blob, sig_len, &sigtype)) != 0)
+               goto out;
+       if (alg != NULL && strcmp(alg, sigtype) != 0) {
+               ret = SSH_ERR_SIGN_ALG_UNSUPPORTED;
+               goto out;
+       }
+       if (k->cert->signature_type == NULL) {
+               k->cert->signature_type = sigtype;
+               sigtype = NULL;
+       }
        /* Append signature and we are done */
        if ((ret = sshbuf_put_string(cert, sig_blob, sig_len)) != 0)
                goto out;
@@ -2585,6 +2625,7 @@ sshkey_certify_custom(struct sshkey *k, struct sshkey *ca, const char *alg,
                sshbuf_reset(cert);
        free(sig_blob);
        free(ca_blob);
+       free(sigtype);
        sshbuf_free(principals);
        return ret;
 }
index 75b36dc..1acf7f7 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: sshkey.h,v 1.26 2018/07/03 13:20:25 djm Exp $ */
+/* $OpenBSD: sshkey.h,v 1.27 2018/09/12 01:31:30 djm Exp $ */
 
 /*
  * Copyright (c) 2000, 2001 Markus Friedl.  All rights reserved.
@@ -96,6 +96,7 @@ struct sshkey_cert {
        struct sshbuf   *critical;
        struct sshbuf   *extensions;
        struct sshkey   *signature_key;
+       char            *signature_type;
 };
 
 /* XXX opaquify? */