Merge branch 'locking-arch-for-linus' of git://git.kernel.org/pub/scm/linux/kernel...
[cascardo/linux.git] / crypto / asymmetric_keys / pkcs7_trust.c
index e666eb0..1d29376 100644 (file)
@@ -23,9 +23,9 @@
 /**
  * Check the trust on one PKCS#7 SignedInfo block.
  */
-int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7,
-                            struct pkcs7_signed_info *sinfo,
-                            struct key *trust_keyring)
+static int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7,
+                                   struct pkcs7_signed_info *sinfo,
+                                   struct key *trust_keyring)
 {
        struct public_key_signature *sig = &sinfo->sig;
        struct x509_certificate *x509, *last = NULL, *p;
@@ -35,6 +35,11 @@ int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7,
 
        kenter(",%u,", sinfo->index);
 
+       if (sinfo->unsupported_crypto) {
+               kleave(" = -ENOPKG [cached]");
+               return -ENOPKG;
+       }
+
        for (x509 = sinfo->signer; x509; x509 = x509->signer) {
                if (x509->seen) {
                        if (x509->verified) {
@@ -49,15 +54,18 @@ int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7,
                /* Look to see if this certificate is present in the trusted
                 * keys.
                 */
-               key = x509_request_asymmetric_key(trust_keyring, x509->subject,
-                                                 x509->fingerprint);
-               if (!IS_ERR(key))
+               key = x509_request_asymmetric_key(trust_keyring, x509->id,
+                                                 false);
+               if (!IS_ERR(key)) {
                        /* One of the X.509 certificates in the PKCS#7 message
                         * is apparently the same as one we already trust.
                         * Verify that the trusted variant can also validate
                         * the signature on the descendant.
                         */
+                       pr_devel("sinfo %u: Cert %u as key %x\n",
+                                sinfo->index, x509->index, key_serial(key));
                        goto matched;
+               }
                if (key == ERR_PTR(-ENOMEM))
                        return -ENOMEM;
 
@@ -77,16 +85,36 @@ int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7,
        /* No match - see if the root certificate has a signer amongst the
         * trusted keys.
         */
-       if (!last || !last->issuer || !last->authority) {
-               kleave(" = -ENOKEY [no backref]");
-               return -ENOKEY;
+       if (last && last->authority) {
+               key = x509_request_asymmetric_key(trust_keyring, last->authority,
+                                                 false);
+               if (!IS_ERR(key)) {
+                       x509 = last;
+                       pr_devel("sinfo %u: Root cert %u signer is key %x\n",
+                                sinfo->index, x509->index, key_serial(key));
+                       goto matched;
+               }
+               if (PTR_ERR(key) != -ENOKEY)
+                       return PTR_ERR(key);
+       }
+
+       /* As a last resort, see if we have a trusted public key that matches
+        * the signed info directly.
+        */
+       key = x509_request_asymmetric_key(trust_keyring,
+                                         sinfo->signing_cert_id,
+                                         false);
+       if (!IS_ERR(key)) {
+               pr_devel("sinfo %u: Direct signer is key %x\n",
+                        sinfo->index, key_serial(key));
+               x509 = NULL;
+               goto matched;
        }
+       if (PTR_ERR(key) != -ENOKEY)
+               return PTR_ERR(key);
 
-       key = x509_request_asymmetric_key(trust_keyring, last->issuer,
-                                         last->authority);
-       if (IS_ERR(key))
-               return PTR_ERR(key) == -ENOMEM ? -ENOMEM : -ENOKEY;
-       x509 = last;
+       kleave(" = -ENOKEY [no backref]");
+       return -ENOKEY;
 
 matched:
        ret = verify_signature(key, sig);
@@ -100,10 +128,12 @@ matched:
        }
 
 verified:
-       x509->verified = true;
-       for (p = sinfo->signer; p != x509; p = p->signer) {
-               p->verified = true;
-               p->trusted = trusted;
+       if (x509) {
+               x509->verified = true;
+               for (p = sinfo->signer; p != x509; p = p->signer) {
+                       p->verified = true;
+                       p->trusted = trusted;
+               }
        }
        sinfo->trusted = trusted;
        kleave(" = 0");
@@ -141,24 +171,28 @@ int pkcs7_validate_trust(struct pkcs7_message *pkcs7,
 {
        struct pkcs7_signed_info *sinfo;
        struct x509_certificate *p;
-       int cached_ret = 0, ret;
+       int cached_ret = -ENOKEY;
+       int ret;
 
        for (p = pkcs7->certs; p; p = p->next)
                p->seen = false;
 
        for (sinfo = pkcs7->signed_infos; sinfo; sinfo = sinfo->next) {
                ret = pkcs7_validate_trust_one(pkcs7, sinfo, trust_keyring);
-               if (ret < 0) {
-                       if (ret == -ENOPKG) {
+               switch (ret) {
+               case -ENOKEY:
+                       continue;
+               case -ENOPKG:
+                       if (cached_ret == -ENOKEY)
                                cached_ret = -ENOPKG;
-                       } else if (ret == -ENOKEY) {
-                               if (cached_ret == 0)
-                                       cached_ret = -ENOKEY;
-                       } else {
-                               return ret;
-                       }
+                       continue;
+               case 0:
+                       *_trusted |= sinfo->trusted;
+                       cached_ret = 0;
+                       continue;
+               default:
+                       return ret;
                }
-               *_trusted |= sinfo->trusted;
        }
 
        return cached_ret;