• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
KeySetManager(Map<String, PackageSetting> packages)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      */
packageIsSignedBy(String packageName, KeySet ks)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      */
addDefinedKeySetToPackage(String packageName, Set<PublicKey> keys, String alias)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      */
addSigningKeySetToPackage(String packageName, Set<PublicKey> signingKeys)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      */
getIdByKeySet(KeySet ks)160     public long getIdByKeySet(KeySet ks) {
161         synchronized (mLockObject) {
162             return getIdByKeySetLocked(ks);
163         }
164     }
165 
getIdByKeySetLocked(KeySet ks)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      */
getKeySetById(long id)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      */
getKeySetByAliasAndPackageName(String packageName, String alias)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      */
getSigningKeySetsByPackageName(String packageName)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      */
addKeySetLocked(Set<PublicKey> keys)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      */
addPublicKeyLocked(PublicKey key)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      */
getIdFromKeyIdsLocked(Set<Long> publicKeyIds)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      */
getIdForPublicKeyLocked(PublicKey k)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      */
getFreeKeySetIDLocked()319     private long getFreeKeySetIDLocked() {
320         lastIssuedKeySetId += 1;
321         return lastIssuedKeySetId;
322     }
323 
324     /**
325      * Same as above, but for public keys.
326      */
getFreePublicKeyIdLocked()327     private long getFreePublicKeyIdLocked() {
328         lastIssuedKeyId += 1;
329         return lastIssuedKeyId;
330     }
331 
removeAppKeySetData(String packageName)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 
getKnownKeySetsByPackageNameLocked(String packageName)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 
encodePublicKey(PublicKey k)400     public String encodePublicKey(PublicKey k) throws IOException {
401         return new String(Base64.encode(k.getEncoded(), 0));
402     }
403 
dump(PrintWriter pw, String packageName, PackageManagerService.DumpState dumpState)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 
writeKeySetManagerLPr(XmlSerializer serializer)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 
writePublicKeysLPr(XmlSerializer serializer)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 
writeKeySetsLPr(XmlSerializer serializer)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 
readKeySetsLPw(XmlPullParser parser)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 
readKeysLPw(XmlPullParser parser)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 
readKeySetListLPw(XmlPullParser parser)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 
readIdentifierLPw(XmlPullParser parser)571     long readIdentifierLPw(XmlPullParser parser)
572             throws XmlPullParserException {
573         return Long.parseLong(parser.getAttributeValue(null, "identifier"));
574     }
575 
readPublicKeyLPw(XmlPullParser parser)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