Merge branch 'CVE-2014-7975' of git://git.kernel.org/pub/scm/linux/kernel/git/luto...
[firefly-linux-kernel-4.4.55.git] / crypto / asymmetric_keys / pkcs7_verify.c
index c62cf8006e1f4e8d160e7cc6423b31d318ed58cc..cd455450b069e3c58d1a202484e3ccba2d5d7da1 100644 (file)
@@ -131,8 +131,7 @@ static int pkcs7_find_key(struct pkcs7_message *pkcs7,
        struct x509_certificate *x509;
        unsigned certix = 1;
 
-       kenter("%u,%u,%u",
-              sinfo->index, sinfo->raw_serial_size, sinfo->raw_issuer_size);
+       kenter("%u", sinfo->index);
 
        for (x509 = pkcs7->certs; x509; x509 = x509->next, certix++) {
                /* I'm _assuming_ that the generator of the PKCS#7 message will
@@ -140,21 +139,11 @@ static int pkcs7_find_key(struct pkcs7_message *pkcs7,
                 * PKCS#7 message - but I can't be 100% sure of that.  It's
                 * possible this will need element-by-element comparison.
                 */
-               if (x509->raw_serial_size != sinfo->raw_serial_size ||
-                   memcmp(x509->raw_serial, sinfo->raw_serial,
-                          sinfo->raw_serial_size) != 0)
+               if (!asymmetric_key_id_same(x509->id, sinfo->signing_cert_id))
                        continue;
                pr_devel("Sig %u: Found cert serial match X.509[%u]\n",
                         sinfo->index, certix);
 
-               if (x509->raw_issuer_size != sinfo->raw_issuer_size ||
-                   memcmp(x509->raw_issuer, sinfo->raw_issuer,
-                          sinfo->raw_issuer_size) != 0) {
-                       pr_warn("Sig %u: X.509 subject and PKCS#7 issuer don't match\n",
-                               sinfo->index);
-                       continue;
-               }
-
                if (x509->pub->pkey_algo != sinfo->sig.pkey_algo) {
                        pr_warn("Sig %u: X.509 algo and PKCS#7 sig algo don't match\n",
                                sinfo->index);
@@ -164,9 +153,14 @@ static int pkcs7_find_key(struct pkcs7_message *pkcs7,
                sinfo->signer = x509;
                return 0;
        }
-       pr_warn("Sig %u: Issuing X.509 cert not found (#%*ph)\n",
-               sinfo->index, sinfo->raw_serial_size, sinfo->raw_serial);
-       return -ENOKEY;
+
+       /* The relevant X.509 cert isn't found here, but it might be found in
+        * the trust keyring.
+        */
+       pr_debug("Sig %u: Issuing X.509 cert not found (#%*phN)\n",
+                sinfo->index,
+                sinfo->signing_cert_id->len, sinfo->signing_cert_id->data);
+       return 0;
 }
 
 /*
@@ -184,15 +178,18 @@ static int pkcs7_verify_sig_chain(struct pkcs7_message *pkcs7,
                p->seen = false;
 
        for (;;) {
-               pr_debug("verify %s: %s\n", x509->subject, x509->fingerprint);
+               pr_debug("verify %s: %*phN\n",
+                        x509->subject,
+                        x509->raw_serial_size, x509->raw_serial);
                x509->seen = true;
                ret = x509_get_sig_params(x509);
                if (ret < 0)
-                       return ret;
+                       goto maybe_missing_crypto_in_x509;
 
                pr_debug("- issuer %s\n", x509->issuer);
                if (x509->authority)
-                       pr_debug("- authkeyid %s\n", x509->authority);
+                       pr_debug("- authkeyid %*phN\n",
+                                x509->authority->len, x509->authority->data);
 
                if (!x509->authority ||
                    strcmp(x509->subject, x509->issuer) == 0) {
@@ -209,7 +206,7 @@ static int pkcs7_verify_sig_chain(struct pkcs7_message *pkcs7,
 
                        ret = x509_check_signature(x509->pub, x509);
                        if (ret < 0)
-                               return ret;
+                               goto maybe_missing_crypto_in_x509;
                        x509->signer = x509;
                        pr_debug("- self-signed\n");
                        return 0;
@@ -218,13 +215,14 @@ 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 %s\n", x509->authority);
+               pr_debug("- want %*phN\n",
+                        x509->authority->len, x509->authority->data);
                for (p = pkcs7->certs; p; p = p->next) {
-                       pr_debug("- cmp [%u] %s\n", p->index, p->fingerprint);
-                       if (p->raw_subject_size == x509->raw_issuer_size &&
-                           strcmp(p->fingerprint, x509->authority) == 0 &&
-                           memcmp(p->raw_subject, x509->raw_issuer,
-                                  x509->raw_issuer_size) == 0)
+                       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;
                }
 
@@ -233,7 +231,7 @@ static int pkcs7_verify_sig_chain(struct pkcs7_message *pkcs7,
                return 0;
 
        found_issuer:
-               pr_debug("- issuer %s\n", p->subject);
+               pr_debug("- subject %s\n", p->subject);
                if (p->seen) {
                        pr_warn("Sig %u: X.509 chain contains loop\n",
                                sinfo->index);
@@ -250,6 +248,17 @@ static int pkcs7_verify_sig_chain(struct pkcs7_message *pkcs7,
                x509 = p;
                might_sleep();
        }
+
+maybe_missing_crypto_in_x509:
+       /* Just prune the certificate chain at this point if we lack some
+        * crypto module to go further.  Note, however, we don't want to set
+        * sinfo->missing_crypto as the signed info block may still be
+        * validatable against an X.509 cert lower in the chain that we have a
+        * trusted copy of.
+        */
+       if (ret == -ENOPKG)
+               return 0;
+       return ret;
 }
 
 /*
@@ -269,11 +278,14 @@ static int pkcs7_verify_one(struct pkcs7_message *pkcs7,
        if (ret < 0)
                return ret;
 
-       /* Find the key for the signature */
+       /* Find the key for the signature if there is one */
        ret = pkcs7_find_key(pkcs7, sinfo);
        if (ret < 0)
                return ret;
 
+       if (!sinfo->signer)
+               return 0;
+
        pr_devel("Using X.509[%u] for sig %u\n",
                 sinfo->signer->index, sinfo->index);
 
@@ -291,11 +303,33 @@ static int pkcs7_verify_one(struct pkcs7_message *pkcs7,
 /**
  * pkcs7_verify - Verify a PKCS#7 message
  * @pkcs7: The PKCS#7 message to be verified
+ *
+ * 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
+ * of the X.509 certificates it carries that matches another X.509 cert in the
+ * message can be verified.
+ *
+ * This does not look to match the contents of the PKCS#7 message against any
+ * external public keys.
+ *
+ * Returns, in order of descending priority:
+ *
+ *  (*) -EKEYREJECTED if a signature failed to match for which we found an
+ *     appropriate X.509 certificate, or:
+ *
+ *  (*) -EBADMSG if some part of the message was invalid, or:
+ *
+ *  (*) -ENOPKG if none of the signature chains are verifiable because suitable
+ *     crypto modules couldn't be found, or:
+ *
+ *  (*) 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)
 {
        struct pkcs7_signed_info *sinfo;
        struct x509_certificate *x509;
+       int enopkg = -ENOPKG;
        int ret, n;
 
        kenter("");
@@ -304,18 +338,24 @@ int pkcs7_verify(struct pkcs7_message *pkcs7)
                ret = x509_get_sig_params(x509);
                if (ret < 0)
                        return ret;
-               pr_debug("X.509[%u] %s\n", n, x509->authority);
+               pr_debug("X.509[%u] %*phN\n",
+                        n, x509->authority->len, x509->authority->data);
        }
 
        for (sinfo = pkcs7->signed_infos; sinfo; sinfo = sinfo->next) {
                ret = pkcs7_verify_one(pkcs7, sinfo);
                if (ret < 0) {
+                       if (ret == -ENOPKG) {
+                               sinfo->unsupported_crypto = true;
+                               continue;
+                       }
                        kleave(" = %d", ret);
                        return ret;
                }
+               enopkg = 0;
        }
 
-       kleave(" = 0");
-       return 0;
+       kleave(" = %d", enopkg);
+       return enopkg;
 }
 EXPORT_SYMBOL_GPL(pkcs7_verify);