These changes are the raw update to linux-4.4.6-rt14. Kernel sources
[kvmfornfv.git] / kernel / crypto / asymmetric_keys / pkcs7_verify.c
index cd45545..325575c 100644 (file)
@@ -49,11 +49,12 @@ static int pkcs7_digest(struct pkcs7_message *pkcs7,
        sinfo->sig.digest_size = digest_size = crypto_shash_digestsize(tfm);
 
        ret = -ENOMEM;
-       digest = kzalloc(digest_size + desc_size, GFP_KERNEL);
+       digest = kzalloc(ALIGN(digest_size, __alignof__(*desc)) + desc_size,
+                        GFP_KERNEL);
        if (!digest)
                goto error_no_desc;
 
-       desc = digest + digest_size;
+       desc = PTR_ALIGN(digest + digest_size, __alignof__(*desc));
        desc->tfm   = tfm;
        desc->flags = CRYPTO_TFM_REQ_MAY_SLEEP;
 
@@ -70,9 +71,15 @@ static int pkcs7_digest(struct pkcs7_message *pkcs7,
         * message digest attribute amongst them which corresponds to the
         * digest we just calculated.
         */
-       if (sinfo->msgdigest) {
+       if (sinfo->authattrs) {
                u8 tag;
 
+               if (!sinfo->msgdigest) {
+                       pr_warn("Sig %u: No messageDigest\n", sinfo->index);
+                       ret = -EKEYREJECTED;
+                       goto error;
+               }
+
                if (sinfo->msgdigest_len != sinfo->sig.digest_size) {
                        pr_debug("Sig %u: Invalid digest size (%u)\n",
                                 sinfo->index, sinfo->msgdigest_len);
@@ -170,6 +177,7 @@ static int pkcs7_verify_sig_chain(struct pkcs7_message *pkcs7,
                                  struct pkcs7_signed_info *sinfo)
 {
        struct x509_certificate *x509 = sinfo->signer, *p;
+       struct asymmetric_key_id *auth;
        int ret;
 
        kenter("");
@@ -187,11 +195,14 @@ static int pkcs7_verify_sig_chain(struct pkcs7_message *pkcs7,
                        goto maybe_missing_crypto_in_x509;
 
                pr_debug("- issuer %s\n", x509->issuer);
-               if (x509->authority)
-                       pr_debug("- authkeyid %*phN\n",
-                                x509->authority->len, x509->authority->data);
-
-               if (!x509->authority ||
+               if (x509->akid_id)
+                       pr_debug("- authkeyid.id %*phN\n",
+                                x509->akid_id->len, x509->akid_id->data);
+               if (x509->akid_skid)
+                       pr_debug("- authkeyid.skid %*phN\n",
+                                x509->akid_skid->len, x509->akid_skid->data);
+
+               if ((!x509->akid_id && !x509->akid_skid) ||
                    strcmp(x509->subject, x509->issuer) == 0) {
                        /* If there's no authority certificate specified, then
                         * the certificate must be self-signed and is the root
@@ -215,21 +226,42 @@ static int pkcs7_verify_sig_chain(struct pkcs7_message *pkcs7,
                /* Look through the X.509 certificates in the PKCS#7 message's
                 * list to see if the next one is there.
                 */
-               pr_debug("- want %*phN\n",
-                        x509->authority->len, x509->authority->data);
-               for (p = pkcs7->certs; p; p = p->next) {
-                       if (!p->skid)
-                               continue;
-                       pr_debug("- cmp [%u] %*phN\n",
-                                p->index, p->skid->len, p->skid->data);
-                       if (asymmetric_key_id_same(p->skid, x509->authority))
-                               goto found_issuer;
+               auth = x509->akid_id;
+               if (auth) {
+                       pr_debug("- want %*phN\n", auth->len, auth->data);
+                       for (p = pkcs7->certs; p; p = p->next) {
+                               pr_debug("- cmp [%u] %*phN\n",
+                                        p->index, p->id->len, p->id->data);
+                               if (asymmetric_key_id_same(p->id, auth))
+                                       goto found_issuer_check_skid;
+                       }
+               } else {
+                       auth = x509->akid_skid;
+                       pr_debug("- want %*phN\n", auth->len, auth->data);
+                       for (p = pkcs7->certs; p; p = p->next) {
+                               if (!p->skid)
+                                       continue;
+                               pr_debug("- cmp [%u] %*phN\n",
+                                        p->index, p->skid->len, p->skid->data);
+                               if (asymmetric_key_id_same(p->skid, auth))
+                                       goto found_issuer;
+                       }
                }
 
                /* We didn't find the root of this chain */
                pr_debug("- top\n");
                return 0;
 
+       found_issuer_check_skid:
+               /* We matched issuer + serialNumber, but if there's an
+                * authKeyId.keyId, that must match the CA subjKeyId also.
+                */
+               if (x509->akid_skid &&
+                   !asymmetric_key_id_same(p->skid, x509->akid_skid)) {
+                       pr_warn("Sig %u: X.509 chain contains auth-skid nonmatch (%u->%u)\n",
+                               sinfo->index, x509->index, p->index);
+                       return -EKEYREJECTED;
+               }
        found_issuer:
                pr_debug("- subject %s\n", p->subject);
                if (p->seen) {
@@ -289,6 +321,18 @@ static int pkcs7_verify_one(struct pkcs7_message *pkcs7,
        pr_devel("Using X.509[%u] for sig %u\n",
                 sinfo->signer->index, sinfo->index);
 
+       /* Check that the PKCS#7 signing time is valid according to the X.509
+        * certificate.  We can't, however, check against the system clock
+        * since that may not have been set yet and may be wrong.
+        */
+       if (test_bit(sinfo_has_signing_time, &sinfo->aa_set)) {
+               if (sinfo->signing_time < sinfo->signer->valid_from ||
+                   sinfo->signing_time > sinfo->signer->valid_to) {
+                       pr_warn("Message signed outside of X.509 validity window\n");
+                       return -EKEYREJECTED;
+               }
+       }
+
        /* Verify the PKCS#7 binary against the key */
        ret = public_key_verify_signature(sinfo->signer->pub, &sinfo->sig);
        if (ret < 0)
@@ -303,6 +347,7 @@ static int pkcs7_verify_one(struct pkcs7_message *pkcs7,
 /**
  * pkcs7_verify - Verify a PKCS#7 message
  * @pkcs7: The PKCS#7 message to be verified
+ * @usage: The use to which the key is being put
  *
  * Verify a PKCS#7 message is internally consistent - that is, the data digest
  * matches the digest in the AuthAttrs and any signature in the message or one
@@ -314,6 +359,9 @@ static int pkcs7_verify_one(struct pkcs7_message *pkcs7,
  *
  * Returns, in order of descending priority:
  *
+ *  (*) -EKEYREJECTED if a key was selected that had a usage restriction at
+ *      odds with the specified usage, or:
+ *
  *  (*) -EKEYREJECTED if a signature failed to match for which we found an
  *     appropriate X.509 certificate, or:
  *
@@ -325,7 +373,8 @@ static int pkcs7_verify_one(struct pkcs7_message *pkcs7,
  *  (*) 0 if all the signature chains that don't incur -ENOPKG can be verified
  *     (note that a signature chain may be of zero length), or:
  */
-int pkcs7_verify(struct pkcs7_message *pkcs7)
+int pkcs7_verify(struct pkcs7_message *pkcs7,
+                enum key_being_used_for usage)
 {
        struct pkcs7_signed_info *sinfo;
        struct x509_certificate *x509;
@@ -334,12 +383,48 @@ int pkcs7_verify(struct pkcs7_message *pkcs7)
 
        kenter("");
 
+       switch (usage) {
+       case VERIFYING_MODULE_SIGNATURE:
+               if (pkcs7->data_type != OID_data) {
+                       pr_warn("Invalid module sig (not pkcs7-data)\n");
+                       return -EKEYREJECTED;
+               }
+               if (pkcs7->have_authattrs) {
+                       pr_warn("Invalid module sig (has authattrs)\n");
+                       return -EKEYREJECTED;
+               }
+               break;
+       case VERIFYING_FIRMWARE_SIGNATURE:
+               if (pkcs7->data_type != OID_data) {
+                       pr_warn("Invalid firmware sig (not pkcs7-data)\n");
+                       return -EKEYREJECTED;
+               }
+               if (!pkcs7->have_authattrs) {
+                       pr_warn("Invalid firmware sig (missing authattrs)\n");
+                       return -EKEYREJECTED;
+               }
+               break;
+       case VERIFYING_KEXEC_PE_SIGNATURE:
+               if (pkcs7->data_type != OID_msIndirectData) {
+                       pr_warn("Invalid kexec sig (not Authenticode)\n");
+                       return -EKEYREJECTED;
+               }
+               /* Authattr presence checked in parser */
+               break;
+       case VERIFYING_UNSPECIFIED_SIGNATURE:
+               if (pkcs7->data_type != OID_data) {
+                       pr_warn("Invalid unspecified sig (not pkcs7-data)\n");
+                       return -EKEYREJECTED;
+               }
+               break;
+       default:
+               return -EINVAL;
+       }
+
        for (n = 0, x509 = pkcs7->certs; x509; x509 = x509->next, n++) {
                ret = x509_get_sig_params(x509);
                if (ret < 0)
                        return ret;
-               pr_debug("X.509[%u] %*phN\n",
-                        n, x509->authority->len, x509->authority->data);
        }
 
        for (sinfo = pkcs7->signed_infos; sinfo; sinfo = sinfo->next) {
@@ -359,3 +444,28 @@ int pkcs7_verify(struct pkcs7_message *pkcs7)
        return enopkg;
 }
 EXPORT_SYMBOL_GPL(pkcs7_verify);
+
+/**
+ * pkcs7_supply_detached_data - Supply the data needed to verify a PKCS#7 message
+ * @pkcs7: The PKCS#7 message
+ * @data: The data to be verified
+ * @datalen: The amount of data
+ *
+ * Supply the detached data needed to verify a PKCS#7 message.  Note that no
+ * attempt to retain/pin the data is made.  That is left to the caller.  The
+ * data will not be modified by pkcs7_verify() and will not be freed when the
+ * PKCS#7 message is freed.
+ *
+ * Returns -EINVAL if data is already supplied in the message, 0 otherwise.
+ */
+int pkcs7_supply_detached_data(struct pkcs7_message *pkcs7,
+                              const void *data, size_t datalen)
+{
+       if (pkcs7->data) {
+               pr_debug("Data already supplied\n");
+               return -EINVAL;
+       }
+       pkcs7->data = data;
+       pkcs7->data_len = datalen;
+       return 0;
+}