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 57e90fa17f2bcc2a43d86cebe7fc44145fba71d1..cd455450b069e3c58d1a202484e3ccba2d5d7da1 100644 (file)
@@ -154,10 +154,13 @@ static int pkcs7_find_key(struct pkcs7_message *pkcs7,
                return 0;
        }
 
-       pr_warn("Sig %u: Issuing X.509 cert not found (#%*ph)\n",
-               sinfo->index,
-               sinfo->signing_cert_id->len, sinfo->signing_cert_id->data);
-       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;
 }
 
 /*
@@ -181,7 +184,7 @@ static int pkcs7_verify_sig_chain(struct pkcs7_message *pkcs7,
                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)
@@ -203,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;
@@ -245,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;
 }
 
 /*
@@ -264,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);
 
@@ -286,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("");
@@ -306,12 +345,17 @@ int pkcs7_verify(struct pkcs7_message *pkcs7)
        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);