Just An Application

August 22, 2014

And Another One: Part Twenty One — The Package Manager Revisited

File

    $(ANDROID_SRC)/frameworks/base/core/java/android/webkit/PluginManager.java

Source

    ...

    private static boolean containsPluginPermissionAndSignatures(PackageInfo pkgInfo) {
    
        // check if the plugin has the required permissions
        String permissions[] = pkgInfo.requestedPermissions;
        if (permissions == null) {
            return false;
        }
        boolean permissionOk = false;
        for (String permit : permissions) {
            if (PLUGIN_PERMISSION.equals(permit)) {
                permissionOk = true;
                break;
            }
        }
        if (!permissionOk) {
            return false;
        }
    
        // check to ensure the plugin is properly signed
        Signature signatures[] = pkgInfo.signatures;
        if (signatures == null) {
            return false;
        }
        if (SystemProperties.getBoolean("ro.secure", false)) {
            boolean signatureMatch = false;
            for (Signature signature : signatures) {
                for (int i = 0; i < SIGNATURES.length; i++) {
                    if (SIGNATURES[i].equals(signature)) {
                        signatureMatch = true;
                        break;
                    }
                }
            }
            if (!signatureMatch) {
                return false;
            }
        }
        
        return true;
    }
    
    ...

This is where we came in.

As an aside, the Signature handling code in this method bears an uncanny resemblance to the PackageManagerService checkSignaturesLP method code as it was in 2008 which may be a clue as to where it originated.

Anyway, given what we now know, how do we fix it ?

In what follows I will use the term signer certificate to mean the certificate which specifies the public key corresponding to the private key with which the plugin was signed.

Perhaps the easiest thing to do is start by seeing if we can find the hard-wired certificate at all before worrying whether someone is trying to pull a fast one.

        ...
    
            int index       = 0;
            int nSignatures = signatures.length;
    
            while (index < nSignatures)
            {
                if (signatures[index].equals(SIGNATURE_1))
                {
                    break;
                }
                ++index;
            }

If we don’t find it we return false.


            if (index == nSignatures)
            {
                // not found
                return false;
            }

If we find it and there is only one Signature then the hard-wired certificate is the signer certificate so we return true.


            else
            if (nSignatures == 1)
            {
                return true;
            }

If we find the hard-wired certificate at index 0 we know it is the first certificate of the first certificate chain so, irrespective of the number of Signatures, we know it is a signer certificate.

Whether we ought to know this is debateable, but given that we already know that Signatures are encoded certificates and are taking advantage of the fact, its a bit late in the day to worry about it.


            else
            if (index == 0)
            {
                return true;
            }

If none of the above are true then there is nothing for it. We are going to have to establish that the hard-wired certificate is genuinely part of a certificate chain.

We know that the hard-wired certificate is self-signed so it has to appear standalone, i.e., in a certificate chain of length one, or as the last certificate in a chain of length two or greater.

We can determine whether it is at the end of a certificate chain by checking to see who issued the preceding certificate.

Assuming that we have a method makeCertificate for making magically making X509Certificates, then


            X509Certificate ourCert    = makeCertificate(signatures[index--]);
            X509Certificate previous   = makeCertificate(signatures[index--]);
    

If the issuer of the previous certificate wasn’t the subject of the hard-wired certificate then the hard-wired certificate is a signer certificate so we can return true.


            if (!previous.getIssuerDN().equals(ourCert.getSubjectDN()))
            {
                return true;
            }

If the hard-wired certificate is at the end of a certificate chain then we should be able to use the public key from it to verify the previous one.


            try
            {
                previous.verify(ourCert.getPublicKey());
            }
            catch (Exception e)
            {
                return false;
            }

If the verification succeds we keep going until we reach the front of the certificate chain.


            X509Certificate current = previous;

            while (index > 0)
            {
                previous = makeCertificate(signatures[index--]);
                if (!previous.getIssuerDN().equals(current.getSubjectDN()))
                {
                    // we've successfully reached the front of the chain
                    break;
                }
                try
                {
                    previous.verify(current.getPublicKey());
                }
                catch (Exception e)
                {
                    return false;
                }
                current = previous;
            }

If we make it to the first certificate in the chain in one piece we have a valid certificate chain so we return true;

And there you have it.

Not only does it fix the problem but as a bonus it manages to tightly couple the PluginManager to a whole bunch of classes that do not even know it exists.

Note that it would be a lot simpler if there were some known invariants.

For example, if the hard-wired certificate must be the signer certificate and there cannot be multiple signers then the whole thing collapses down to

    return (signatures[length] == 0) && signatures[0].equals(SIGNATURE_1);

which is a lot less fragile.

Now all we need to do is find all the other code that is implementing ad-hoc security policies on the basis of knowing that Signatures are encoded certificates and fix that as well.


Copyright (c) 2014 By Simon Lewis. All Rights Reserved.

Unauthorized use and/or duplication of this material without express and written permission from this blog’s author and owner Simon Lewis is strictly prohibited.

Excerpts and links may be used, provided that full and clear credit is given to Simon Lewis and justanapplication.wordpress.com with appropriate and specific direction to the original content.

Advertisements

Leave a Comment »

No comments yet.

RSS feed for comments on this post. TrackBack URI

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Create a free website or blog at WordPress.com.

%d bloggers like this: