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