Home | History | Annotate | Download | only in pm
      1 /*
      2  * Copyright (C) 2013 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.server.pm;
     18 
     19 import android.content.pm.KeySet;
     20 import android.content.pm.PackageParser;
     21 import android.os.Binder;
     22 import android.util.Base64;
     23 import android.util.Log;
     24 import android.util.LongSparseArray;
     25 
     26 import java.io.IOException;
     27 import java.io.PrintWriter;
     28 import java.security.PublicKey;
     29 import java.util.HashMap;
     30 import java.util.HashSet;
     31 import java.util.Map;
     32 import java.util.Set;
     33 
     34 import org.xmlpull.v1.XmlPullParser;
     35 import org.xmlpull.v1.XmlPullParserException;
     36 import org.xmlpull.v1.XmlSerializer;
     37 
     38 /*
     39  * Manages system-wide KeySet state.
     40  */
     41 public class KeySetManager {
     42 
     43     static final String TAG = "KeySetManager";
     44 
     45     /** Sentinel value returned when a {@code KeySet} is not found. */
     46     public static final long KEYSET_NOT_FOUND = -1;
     47 
     48     /** Sentinel value returned when public key is not found. */
     49     private static final long PUBLIC_KEY_NOT_FOUND = -1;
     50 
     51     private final Object mLockObject = new Object();
     52 
     53     private final LongSparseArray<KeySet> mKeySets;
     54 
     55     private final LongSparseArray<PublicKey> mPublicKeys;
     56 
     57     private final LongSparseArray<Set<Long>> mKeySetMapping;
     58 
     59     private final Map<String, PackageSetting> mPackages;
     60 
     61     private static long lastIssuedKeySetId = 0;
     62 
     63     private static long lastIssuedKeyId = 0;
     64 
     65     public KeySetManager(Map<String, PackageSetting> packages) {
     66         mKeySets = new LongSparseArray<KeySet>();
     67         mPublicKeys = new LongSparseArray<PublicKey>();
     68         mKeySetMapping = new LongSparseArray<Set<Long>>();
     69         mPackages = packages;
     70     }
     71 
     72     /**
     73      * Determine if a package is signed by the given KeySet.
     74      *
     75      * Returns false if the package was not signed by all the
     76      * keys in the KeySet.
     77      *
     78      * Returns true if the package was signed by at least the
     79      * keys in the given KeySet.
     80      *
     81      * Note that this can return true for multiple KeySets.
     82      */
     83     public boolean packageIsSignedBy(String packageName, KeySet ks) {
     84         synchronized (mLockObject) {
     85             PackageSetting pkg = mPackages.get(packageName);
     86             if (pkg == null) {
     87                 throw new NullPointerException("Invalid package name");
     88             }
     89             if (pkg.keySetData == null) {
     90                 throw new NullPointerException("Package has no KeySet data");
     91             }
     92             long id = getIdByKeySetLocked(ks);
     93             return pkg.keySetData.packageIsSignedBy(id);
     94         }
     95     }
     96 
     97     /**
     98      * This informs the system that the given package has defined a KeySet
     99      * in its manifest that a) contains the given keys and b) is named
    100      * alias by that package.
    101      */
    102     public void addDefinedKeySetToPackage(String packageName,
    103             Set<PublicKey> keys, String alias) {
    104         if ((packageName == null) || (keys == null) || (alias == null)) {
    105             //Log.d(TAG, "Got null argument for a defined keyset, ignoring!");
    106             return;
    107         }
    108         synchronized (mLockObject) {
    109             KeySet ks = addKeySetLocked(keys);
    110             PackageSetting pkg = mPackages.get(packageName);
    111             if (pkg == null) {
    112                 throw new NullPointerException("Unknown package");
    113             }
    114             long id = getIdByKeySetLocked(ks);
    115             pkg.keySetData.addDefinedKeySet(id, alias);
    116         }
    117     }
    118 
    119     /**
    120      * Similar to the above, this informs the system that the given package
    121      * was signed by the provided KeySet.
    122      */
    123     public void addSigningKeySetToPackage(String packageName,
    124             Set<PublicKey> signingKeys) {
    125         if ((packageName == null) || (signingKeys == null)) {
    126             //Log.d(TAG, "Got null argument for a signing keyset, ignoring!");
    127             return;
    128         }
    129         synchronized (mLockObject) {
    130             // add the signing KeySet
    131             KeySet ks = addKeySetLocked(signingKeys);
    132             long id = getIdByKeySetLocked(ks);
    133             Set<Long> publicKeyIds = mKeySetMapping.get(id);
    134             if (publicKeyIds == null) {
    135                 throw new NullPointerException("Got invalid KeySet id");
    136             }
    137 
    138             // attach it to the package
    139             PackageSetting pkg = mPackages.get(packageName);
    140             if (pkg == null) {
    141                 throw new NullPointerException("No such package!");
    142             }
    143             pkg.keySetData.addSigningKeySet(id);
    144 
    145             // for each KeySet the package defines which is a subset of
    146             // the one above, add the KeySet id to the package's signing KeySets
    147             for (Long keySetID : pkg.keySetData.getDefinedKeySets()) {
    148                 Set<Long> definedKeys = mKeySetMapping.get(keySetID);
    149                 if (publicKeyIds.contains(definedKeys)) {
    150                     pkg.keySetData.addSigningKeySet(keySetID);
    151                 }
    152             }
    153         }
    154     }
    155 
    156     /**
    157      * Fetches the stable identifier associated with the given KeySet. Returns
    158      * {@link #KEYSET_NOT_FOUND} if the KeySet... wasn't found.
    159      */
    160     public long getIdByKeySet(KeySet ks) {
    161         synchronized (mLockObject) {
    162             return getIdByKeySetLocked(ks);
    163         }
    164     }
    165 
    166     private long getIdByKeySetLocked(KeySet ks) {
    167         for (int keySetIndex = 0; keySetIndex < mKeySets.size(); keySetIndex++) {
    168             KeySet value = mKeySets.valueAt(keySetIndex);
    169             if (ks.equals(value)) {
    170                 return mKeySets.keyAt(keySetIndex);
    171             }
    172         }
    173         return KEYSET_NOT_FOUND;
    174     }
    175 
    176     /**
    177      * Fetches the KeySet corresponding to the given stable identifier.
    178      *
    179      * Returns {@link #KEYSET_NOT_FOUND} if the identifier doesn't
    180      * identify a {@link KeySet}.
    181      */
    182     public KeySet getKeySetById(long id) {
    183         synchronized (mLockObject) {
    184             return mKeySets.get(id);
    185         }
    186     }
    187 
    188     /**
    189      * Fetches the KeySet that a given package refers to by the provided alias.
    190      *
    191      * If the package isn't known to us, throws an IllegalArgumentException.
    192      * Returns null if the alias isn't known to us.
    193      */
    194     public KeySet getKeySetByAliasAndPackageName(String packageName, String alias) {
    195         synchronized (mLockObject) {
    196             PackageSetting p = mPackages.get(packageName);
    197             if (p == null) {
    198                 throw new NullPointerException("Unknown package");
    199             }
    200             if (p.keySetData == null) {
    201                 throw new IllegalArgumentException("Package has no keySet data");
    202             }
    203             long keySetId = p.keySetData.getAliases().get(alias);
    204             return mKeySets.get(keySetId);
    205         }
    206     }
    207 
    208     /**
    209      * Fetches all the known {@link KeySet KeySets} that signed the given
    210      * package. Returns {@code null} if package is unknown.
    211      */
    212     public Set<KeySet> getSigningKeySetsByPackageName(String packageName) {
    213         synchronized (mLockObject) {
    214             Set<KeySet> signingKeySets = new HashSet<KeySet>();
    215             PackageSetting p = mPackages.get(packageName);
    216             if (p == null) {
    217                 throw new NullPointerException("Unknown package");
    218             }
    219             if (p.keySetData == null) {
    220                 throw new IllegalArgumentException("Package has no keySet data");
    221             }
    222             for (long l : p.keySetData.getSigningKeySets()) {
    223                 signingKeySets.add(mKeySets.get(l));
    224             }
    225             return signingKeySets;
    226         }
    227     }
    228 
    229     /**
    230      * Creates a new KeySet corresponding to the given keys.
    231      *
    232      * If the {@link PublicKey PublicKeys} aren't known to the system, this
    233      * adds them. Otherwise, they're deduped.
    234      *
    235      * If the KeySet isn't known to the system, this adds that and creates the
    236      * mapping to the PublicKeys. If it is known, then it's deduped.
    237      *
    238      * Throws if the provided set is {@code null}.
    239      */
    240     private KeySet addKeySetLocked(Set<PublicKey> keys) {
    241         if (keys == null) {
    242             throw new NullPointerException("Provided keys cannot be null");
    243         }
    244         // add each of the keys in the provided set
    245         Set<Long> addedKeyIds = new HashSet<Long>(keys.size());
    246         for (PublicKey k : keys) {
    247             long id = addPublicKeyLocked(k);
    248             addedKeyIds.add(id);
    249         }
    250 
    251         // check to see if the resulting keyset is new
    252         long existingKeySetId = getIdFromKeyIdsLocked(addedKeyIds);
    253         if (existingKeySetId != KEYSET_NOT_FOUND) {
    254             return mKeySets.get(existingKeySetId);
    255         }
    256 
    257         // create the KeySet object
    258         KeySet ks = new KeySet(new Binder());
    259         // get the first unoccupied slot in mKeySets
    260         long id = getFreeKeySetIDLocked();
    261         // add the KeySet object to it
    262         mKeySets.put(id, ks);
    263         // add the stable key ids to the mapping
    264         mKeySetMapping.put(id, addedKeyIds);
    265         // go home
    266         return ks;
    267     }
    268 
    269     /**
    270      * Adds the given PublicKey to the system, deduping as it goes.
    271      */
    272     private long addPublicKeyLocked(PublicKey key) {
    273         // check if the public key is new
    274         long existingKeyId = getIdForPublicKeyLocked(key);
    275         if (existingKeyId != PUBLIC_KEY_NOT_FOUND) {
    276             return existingKeyId;
    277         }
    278         // if it's new find the first unoccupied slot in the public keys
    279         long id = getFreePublicKeyIdLocked();
    280         // add the public key to it
    281         mPublicKeys.put(id, key);
    282         // return the stable identifier
    283         return id;
    284     }
    285 
    286     /**
    287      * Finds the stable identifier for a KeySet based on a set of PublicKey stable IDs.
    288      *
    289      * Returns KEYSET_NOT_FOUND if there isn't one.
    290      */
    291     private long getIdFromKeyIdsLocked(Set<Long> publicKeyIds) {
    292         for (int keyMapIndex = 0; keyMapIndex < mKeySetMapping.size(); keyMapIndex++) {
    293             Set<Long> value = mKeySetMapping.valueAt(keyMapIndex);
    294             if (value.equals(publicKeyIds)) {
    295                 return mKeySetMapping.keyAt(keyMapIndex);
    296             }
    297         }
    298         return KEYSET_NOT_FOUND;
    299     }
    300 
    301     /**
    302      * Finds the stable identifier for a PublicKey or PUBLIC_KEY_NOT_FOUND.
    303      */
    304     private long getIdForPublicKeyLocked(PublicKey k) {
    305         String encodedPublicKey = new String(k.getEncoded());
    306         for (int publicKeyIndex = 0; publicKeyIndex < mPublicKeys.size(); publicKeyIndex++) {
    307             PublicKey value = mPublicKeys.valueAt(publicKeyIndex);
    308             String encodedExistingKey = new String(value.getEncoded());
    309             if (encodedPublicKey.equals(encodedExistingKey)) {
    310                 return mPublicKeys.keyAt(publicKeyIndex);
    311             }
    312         }
    313         return PUBLIC_KEY_NOT_FOUND;
    314     }
    315 
    316     /**
    317      * Gets an unused stable identifier for a KeySet.
    318      */
    319     private long getFreeKeySetIDLocked() {
    320         lastIssuedKeySetId += 1;
    321         return lastIssuedKeySetId;
    322     }
    323 
    324     /**
    325      * Same as above, but for public keys.
    326      */
    327     private long getFreePublicKeyIdLocked() {
    328         lastIssuedKeyId += 1;
    329         return lastIssuedKeyId;
    330     }
    331 
    332     public void removeAppKeySetData(String packageName) {
    333         synchronized (mLockObject) {
    334             // Get the package's known keys and KeySets
    335             Set<Long> deletableKeySets = getKnownKeySetsByPackageNameLocked(packageName);
    336             Set<Long> deletableKeys = new HashSet<Long>();
    337             Set<Long> knownKeys = null;
    338             for (Long ks : deletableKeySets) {
    339                 knownKeys = mKeySetMapping.get(ks);
    340                 if (knownKeys != null) {
    341                     deletableKeys.addAll(knownKeys);
    342                 }
    343             }
    344 
    345             // Now remove the keys and KeySets known to any other package
    346             for (String pkgName : mPackages.keySet()) {
    347                 if (pkgName.equals(packageName)) {
    348                     continue;
    349                 }
    350                 Set<Long> knownKeySets = getKnownKeySetsByPackageNameLocked(pkgName);
    351                 deletableKeySets.removeAll(knownKeySets);
    352                 knownKeys = new HashSet<Long>();
    353                 for (Long ks : knownKeySets) {
    354                     knownKeys = mKeySetMapping.get(ks);
    355                     if (knownKeys != null) {
    356                         deletableKeys.removeAll(knownKeys);
    357                     }
    358                 }
    359             }
    360 
    361             // The remaining keys and KeySets are not known to any other
    362             // application and so can be safely deleted.
    363             for (Long ks : deletableKeySets) {
    364                 mKeySets.delete(ks);
    365                 mKeySetMapping.delete(ks);
    366             }
    367             for (Long keyId : deletableKeys) {
    368                 mPublicKeys.delete(keyId);
    369             }
    370 
    371             // Now remove them from the KeySets known to each package
    372             for (String pkgName : mPackages.keySet()) {
    373                 PackageSetting p = mPackages.get(pkgName);
    374                 for (Long ks : deletableKeySets) {
    375                     p.keySetData.removeSigningKeySet(ks);
    376                     p.keySetData.removeDefinedKeySet(ks);
    377                 }
    378             }
    379         }
    380     }
    381 
    382     private Set<Long> getKnownKeySetsByPackageNameLocked(String packageName) {
    383         PackageSetting p = mPackages.get(packageName);
    384         if (p == null) {
    385             throw new NullPointerException("Unknown package");
    386         }
    387         if (p.keySetData == null) {
    388             throw new IllegalArgumentException("Package has no keySet data");
    389         }
    390         Set<Long> knownKeySets = new HashSet<Long>();
    391         for (long ks : p.keySetData.getSigningKeySets()) {
    392             knownKeySets.add(ks);
    393         }
    394         for (long ks : p.keySetData.getDefinedKeySets()) {
    395             knownKeySets.add(ks);
    396         }
    397         return knownKeySets;
    398     }
    399 
    400     public String encodePublicKey(PublicKey k) throws IOException {
    401         return new String(Base64.encode(k.getEncoded(), 0));
    402     }
    403 
    404     public void dump(PrintWriter pw, String packageName,
    405             PackageManagerService.DumpState dumpState) {
    406         synchronized (mLockObject) {
    407             boolean printedHeader = false;
    408             for (Map.Entry<String, PackageSetting> e : mPackages.entrySet()) {
    409                 String keySetPackage = e.getKey();
    410                 if (packageName != null && !packageName.equals(keySetPackage)) {
    411                     continue;
    412                 }
    413                 if (!printedHeader) {
    414                     if (dumpState.onTitlePrinted())
    415                         pw.println();
    416                     pw.println("Key Set Manager:");
    417                     printedHeader = true;
    418                 }
    419                 PackageSetting pkg = e.getValue();
    420                 pw.print("  ["); pw.print(keySetPackage); pw.println("]");
    421                 if (pkg.keySetData != null) {
    422                     boolean printedLabel = false;
    423                     for (Map.Entry<String, Long> entry : pkg.keySetData.getAliases().entrySet()) {
    424                         if (!printedLabel) {
    425                             pw.print("      KeySets Aliases: ");
    426                             printedLabel = true;
    427                         } else {
    428                             pw.print(", ");
    429                         }
    430                         pw.print(entry.getKey());
    431                         pw.print('=');
    432                         pw.print(Long.toString(entry.getValue()));
    433                     }
    434                     if (printedLabel) {
    435                         pw.println("");
    436                     }
    437                     printedLabel = false;
    438                     for (long keySetId : pkg.keySetData.getDefinedKeySets()) {
    439                         if (!printedLabel) {
    440                             pw.print("      Defined KeySets: ");
    441                             printedLabel = true;
    442                         } else {
    443                             pw.print(", ");
    444                         }
    445                         pw.print(Long.toString(keySetId));
    446                     }
    447                     if (printedLabel) {
    448                         pw.println("");
    449                     }
    450                     printedLabel = false;
    451                     for (long keySetId : pkg.keySetData.getSigningKeySets()) {
    452                         if (!printedLabel) {
    453                             pw.print("      Signing KeySets: ");
    454                             printedLabel = true;
    455                         } else {
    456                             pw.print(", ");
    457                         }
    458                         pw.print(Long.toString(keySetId));
    459                     }
    460                     if (printedLabel) {
    461                         pw.println("");
    462                     }
    463                 }
    464             }
    465         }
    466     }
    467 
    468     void writeKeySetManagerLPr(XmlSerializer serializer) throws IOException {
    469         serializer.startTag(null, "keyset-settings");
    470         writePublicKeysLPr(serializer);
    471         writeKeySetsLPr(serializer);
    472         serializer.startTag(null, "lastIssuedKeyId");
    473         serializer.attribute(null, "value", Long.toString(lastIssuedKeyId));
    474         serializer.endTag(null, "lastIssuedKeyId");
    475         serializer.startTag(null, "lastIssuedKeySetId");
    476         serializer.attribute(null, "value", Long.toString(lastIssuedKeySetId));
    477         serializer.endTag(null, "lastIssuedKeySetId");
    478         serializer.endTag(null, "keyset-settings");
    479     }
    480 
    481     void writePublicKeysLPr(XmlSerializer serializer) throws IOException {
    482         serializer.startTag(null, "keys");
    483         for (int pKeyIndex = 0; pKeyIndex < mPublicKeys.size(); pKeyIndex++) {
    484             long id = mPublicKeys.keyAt(pKeyIndex);
    485             PublicKey key = mPublicKeys.valueAt(pKeyIndex);
    486             String encodedKey = encodePublicKey(key);
    487             serializer.startTag(null, "public-key");
    488             serializer.attribute(null, "identifier", Long.toString(id));
    489             serializer.attribute(null, "value", encodedKey);
    490             serializer.endTag(null, "public-key");
    491         }
    492         serializer.endTag(null, "keys");
    493     }
    494 
    495     void writeKeySetsLPr(XmlSerializer serializer) throws IOException {
    496         serializer.startTag(null, "keysets");
    497         for (int keySetIndex = 0; keySetIndex < mKeySetMapping.size(); keySetIndex++) {
    498             long id = mKeySetMapping.keyAt(keySetIndex);
    499             Set<Long> keys = mKeySetMapping.valueAt(keySetIndex);
    500             serializer.startTag(null, "keyset");
    501             serializer.attribute(null, "identifier", Long.toString(id));
    502             for (long keyId : keys) {
    503                 serializer.startTag(null, "key-id");
    504                 serializer.attribute(null, "identifier", Long.toString(keyId));
    505                 serializer.endTag(null, "key-id");
    506             }
    507             serializer.endTag(null, "keyset");
    508         }
    509         serializer.endTag(null, "keysets");
    510     }
    511 
    512     void readKeySetsLPw(XmlPullParser parser)
    513             throws XmlPullParserException, IOException {
    514         int type;
    515         long currentKeySetId = 0;
    516         while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
    517             if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
    518                 continue;
    519             }
    520             final String tagName = parser.getName();
    521             if (tagName.equals("keys")) {
    522                 readKeysLPw(parser);
    523             } else if (tagName.equals("keysets")) {
    524                 readKeySetListLPw(parser);
    525             }
    526         }
    527     }
    528 
    529     void readKeysLPw(XmlPullParser parser)
    530             throws XmlPullParserException, IOException {
    531         int outerDepth = parser.getDepth();
    532         int type;
    533         while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
    534                 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
    535             if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
    536                 continue;
    537             }
    538             final String tagName = parser.getName();
    539             if (tagName.equals("public-key")) {
    540                 readPublicKeyLPw(parser);
    541             } else if (tagName.equals("lastIssuedKeyId")) {
    542                 lastIssuedKeyId = Long.parseLong(parser.getAttributeValue(null, "value"));
    543             } else if (tagName.equals("lastIssuedKeySetId")) {
    544                 lastIssuedKeySetId = Long.parseLong(parser.getAttributeValue(null, "value"));
    545             }
    546         }
    547     }
    548 
    549     void readKeySetListLPw(XmlPullParser parser)
    550             throws XmlPullParserException, IOException {
    551         int outerDepth = parser.getDepth();
    552         int type;
    553         long currentKeySetId = 0;
    554         while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
    555                 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
    556             if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
    557                 continue;
    558             }
    559             final String tagName = parser.getName();
    560             if (tagName.equals("keyset")) {
    561                 currentKeySetId = readIdentifierLPw(parser);
    562                 mKeySets.put(currentKeySetId, new KeySet(new Binder()));
    563                 mKeySetMapping.put(currentKeySetId, new HashSet<Long>());
    564             } else if (tagName.equals("key-id")) {
    565                 long id = readIdentifierLPw(parser);
    566                 mKeySetMapping.get(currentKeySetId).add(id);
    567             }
    568         }
    569     }
    570 
    571     long readIdentifierLPw(XmlPullParser parser)
    572             throws XmlPullParserException {
    573         return Long.parseLong(parser.getAttributeValue(null, "identifier"));
    574     }
    575 
    576     void readPublicKeyLPw(XmlPullParser parser)
    577             throws XmlPullParserException {
    578         String encodedID = parser.getAttributeValue(null, "identifier");
    579         long identifier = Long.parseLong(encodedID);
    580         String encodedPublicKey = parser.getAttributeValue(null, "value");
    581         PublicKey pub = PackageParser.parsePublicKey(encodedPublicKey);
    582         if (pub != null) {
    583             mPublicKeys.put(identifier, pub);
    584         }
    585     }
    586 }
    587