1 /*
2  * Copyright (C) 2019 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.locksettings.recoverablekeystore.storage;
18 
19 import android.content.Context;
20 import android.os.ServiceSpecificException;
21 import android.os.UserHandle;
22 import android.os.UserManager;
23 import android.util.Log;
24 
25 import com.android.internal.annotations.VisibleForTesting;
26 import com.android.server.locksettings.recoverablekeystore.WrappedKey;
27 
28 import java.util.ArrayList;
29 import java.util.List;
30 import java.util.Map;
31 
32 /**
33  * Cleans up data when user is removed.
34  */
35 public class CleanupManager {
36     private static final String TAG = "CleanupManager";
37 
38     private final UserManager mUserManager;
39     private final RecoverableKeyStoreDb mDatabase;
40     private final RecoverySnapshotStorage mSnapshotStorage;
41     private final ApplicationKeyStorage mApplicationKeyStorage;
42 
43     // Serial number can not be changed at runtime.
44     private Map<Integer, Long> mSerialNumbers; // Always in sync with the database.
45 
46     /**
47      * Creates a new instance of the class.
48      * IMPORTANT: {@code verifyKnownUsers} must be called before the first data access.
49      */
getInstance( Context context, RecoverySnapshotStorage snapshotStorage, RecoverableKeyStoreDb recoverableKeyStoreDb, ApplicationKeyStorage applicationKeyStorage)50     public static CleanupManager getInstance(
51             Context context,
52             RecoverySnapshotStorage snapshotStorage,
53             RecoverableKeyStoreDb recoverableKeyStoreDb,
54             ApplicationKeyStorage applicationKeyStorage) {
55         return new CleanupManager(
56                 snapshotStorage,
57                 recoverableKeyStoreDb,
58                 UserManager.get(context),
59                 applicationKeyStorage);
60     }
61 
62     @VisibleForTesting
CleanupManager( RecoverySnapshotStorage snapshotStorage, RecoverableKeyStoreDb recoverableKeyStoreDb, UserManager userManager, ApplicationKeyStorage applicationKeyStorage)63     CleanupManager(
64             RecoverySnapshotStorage snapshotStorage,
65             RecoverableKeyStoreDb recoverableKeyStoreDb,
66             UserManager userManager,
67             ApplicationKeyStorage applicationKeyStorage) {
68         mSnapshotStorage = snapshotStorage;
69         mDatabase = recoverableKeyStoreDb;
70         mUserManager = userManager;
71         mApplicationKeyStorage = applicationKeyStorage;
72     }
73 
74     /**
75      * Registers recovery agent in the system, if necessary.
76      */
registerRecoveryAgent(int userId, int uid)77     public synchronized void registerRecoveryAgent(int userId, int uid) {
78         if (mSerialNumbers == null) {
79             // Table was uninitialized.
80             verifyKnownUsers();
81         }
82         // uid is ignored since recovery agent is a system app.
83         Long storedSerialNumber =  mSerialNumbers.get(userId);
84         if (storedSerialNumber == null) {
85             storedSerialNumber = -1L;
86         }
87         if (storedSerialNumber != -1) {
88             // User was already registered.
89             return;
90         }
91         // User was added after {@code verifyAllUsers} call.
92         long currentSerialNumber = mUserManager.getSerialNumberForUser(UserHandle.of(userId));
93         if (currentSerialNumber != -1) {
94             storeUserSerialNumber(userId, currentSerialNumber);
95         }
96     }
97 
98     /**
99      * Removes data if serial number for a user was changed.
100      */
verifyKnownUsers()101     public synchronized void verifyKnownUsers() {
102         mSerialNumbers =  mDatabase.getUserSerialNumbers();
103         List<Integer> deletedUserIds = new ArrayList<Integer>(){};
104         for (Map.Entry<Integer, Long> entry : mSerialNumbers.entrySet()) {
105             Integer userId = entry.getKey();
106             Long storedSerialNumber = entry.getValue();
107             if (storedSerialNumber == null) {
108                 storedSerialNumber = -1L;
109             }
110             long currentSerialNumber = mUserManager.getSerialNumberForUser(UserHandle.of(userId));
111             if (currentSerialNumber == -1) {
112                 // User was removed.
113                 deletedUserIds.add(userId);
114                 removeDataForUser(userId);
115             } else if (storedSerialNumber == -1) {
116                 // User is detected for the first time
117                 storeUserSerialNumber(userId, currentSerialNumber);
118             } else if (storedSerialNumber != currentSerialNumber) {
119                 // User has unexpected serial number - delete data related to old serial number.
120                 deletedUserIds.add(userId);
121                 removeDataForUser(userId);
122                 // Register new user.
123                 storeUserSerialNumber(userId, currentSerialNumber);
124             }
125         }
126 
127         for (Integer deletedUser : deletedUserIds) {
128             mSerialNumbers.remove(deletedUser);
129         }
130     }
131 
storeUserSerialNumber(int userId, long userSerialNumber)132     private void storeUserSerialNumber(int userId, long userSerialNumber) {
133         Log.d(TAG, "Storing serial number for user " + userId + ".");
134         mSerialNumbers.put(userId, userSerialNumber);
135         mDatabase.setUserSerialNumber(userId, userSerialNumber);
136     }
137 
138     /**
139      * Removes all data for given user, including
140      *
141      * <ul>
142      *     <li> Recovery snapshots for all agents belonging to the {@code userId}.
143      *     <li> Entries with data related to {@code userId} from the database.
144      * </ul>
145      */
removeDataForUser(int userId)146     private void removeDataForUser(int userId) {
147         Log.d(TAG, "Removing data for user " + userId + ".");
148         List<Integer> recoveryAgents = mDatabase.getRecoveryAgents(userId);
149         for (Integer uid : recoveryAgents) {
150             mSnapshotStorage.remove(uid);
151             removeAllKeysForRecoveryAgent(userId, uid);
152         }
153 
154         mDatabase.removeUserFromAllTables(userId);
155     }
156 
157     /**
158      * Removes keys from Android KeyStore for the recovery agent;
159      * Doesn't remove encrypted key material from the database.
160      */
removeAllKeysForRecoveryAgent(int userId, int uid)161     private void removeAllKeysForRecoveryAgent(int userId, int uid) {
162         int generationId = mDatabase.getPlatformKeyGenerationId(userId);
163         Map<String, WrappedKey> allKeys = mDatabase.getAllKeys(userId, uid, generationId);
164         for (String alias : allKeys.keySet()) {
165             try {
166                 // Delete KeyStore copy.
167                 mApplicationKeyStorage.deleteEntry(userId, uid, alias);
168             } catch (ServiceSpecificException e) {
169                 // Ignore errors during key removal.
170                 Log.e(TAG, "Error while removing recoverable key " + alias + " : " + e);
171             }
172         }
173     }
174 }
175