blob: 5436ce020abcf5d8e40a10e3d0685d053a1d331e [file] [log] [blame]
Amith Yamasani52c489c2012-03-28 11:42:42 -07001/*
2 * Copyright (C) 2012 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
Jeff Sharkey7a96c392012-11-15 14:01:46 -080017package com.android.server;
Amith Yamasani52c489c2012-03-28 11:42:42 -070018
Adrian Roos230635e2015-01-07 20:50:29 +010019import android.app.admin.DevicePolicyManager;
Amith Yamasani072543f2015-02-13 11:09:45 -080020import android.app.backup.BackupManager;
Robin Leef0246a82014-08-13 09:50:25 +010021import android.content.BroadcastReceiver;
Amith Yamasani52c489c2012-03-28 11:42:42 -070022import android.content.ContentResolver;
Amith Yamasani52c489c2012-03-28 11:42:42 -070023import android.content.Context;
Robin Leef0246a82014-08-13 09:50:25 +010024import android.content.Intent;
25import android.content.IntentFilter;
Jim Miller158fe192013-04-17 15:23:55 -070026import android.content.pm.PackageManager;
Jim Miller187ec582013-04-15 18:27:54 -070027import android.content.pm.UserInfo;
Adrian Roos261d5ab2014-10-29 14:42:38 +010028import static android.Manifest.permission.ACCESS_KEYGUARD_SECURE_STORAGE;
Jim Miller187ec582013-04-15 18:27:54 -070029import static android.content.Context.USER_SERVICE;
Jim Miller158fe192013-04-17 15:23:55 -070030import static android.Manifest.permission.READ_PROFILE;
Amith Yamasani52c489c2012-03-28 11:42:42 -070031import android.database.sqlite.SQLiteDatabase;
Amith Yamasani52c489c2012-03-28 11:42:42 -070032import android.os.Binder;
Paul Lawrence945490c2014-03-27 16:37:28 +000033import android.os.IBinder;
Robin Leef0246a82014-08-13 09:50:25 +010034import android.os.Process;
Amith Yamasani52c489c2012-03-28 11:42:42 -070035import android.os.RemoteException;
Paul Lawrence945490c2014-03-27 16:37:28 +000036import android.os.storage.IMountService;
37import android.os.ServiceManager;
Amith Yamasanid1645f82012-06-12 11:53:26 -070038import android.os.SystemProperties;
Dianne Hackbornf02b60a2012-08-16 10:48:27 -070039import android.os.UserHandle;
Jim Miller187ec582013-04-15 18:27:54 -070040import android.os.UserManager;
Amith Yamasani52c489c2012-03-28 11:42:42 -070041import android.provider.Settings;
42import android.provider.Settings.Secure;
Jim Miller187ec582013-04-15 18:27:54 -070043import android.provider.Settings.SettingNotFoundException;
Jim Millerde1af082013-09-11 14:58:26 -070044import android.security.KeyStore;
Andres Morales23974272015-05-14 22:42:26 -070045import android.service.gatekeeper.GateKeeperResponse;
Andres Morales8fa56652015-03-31 09:19:50 -070046import android.service.gatekeeper.IGateKeeperService;
Amith Yamasani52c489c2012-03-28 11:42:42 -070047import android.text.TextUtils;
48import android.util.Slog;
49
Amith Yamasani072543f2015-02-13 11:09:45 -080050import com.android.internal.util.ArrayUtils;
Jeff Sharkey7a96c392012-11-15 14:01:46 -080051import com.android.internal.widget.ILockSettings;
52import com.android.internal.widget.LockPatternUtils;
Andres Morales23974272015-05-14 22:42:26 -070053import com.android.internal.widget.VerifyCredentialResponse;
Andres Morales8fa56652015-03-31 09:19:50 -070054import com.android.server.LockSettingsStorage.CredentialHash;
Jeff Sharkey7a96c392012-11-15 14:01:46 -080055
Amith Yamasani52c489c2012-03-28 11:42:42 -070056import java.util.Arrays;
Jim Miller187ec582013-04-15 18:27:54 -070057import java.util.List;
Amith Yamasani52c489c2012-03-28 11:42:42 -070058
59/**
60 * Keeps the lock pattern/password data and related settings for each user.
61 * Used by LockPatternUtils. Needs to be a service because Settings app also needs
62 * to be able to save lockscreen information for secondary users.
63 * @hide
64 */
65public class LockSettingsService extends ILockSettings.Stub {
66
Adrian Roos261d5ab2014-10-29 14:42:38 +010067 private static final String PERMISSION = ACCESS_KEYGUARD_SECURE_STORAGE;
Adrian Roos4f788452014-05-22 20:45:59 +020068
Amith Yamasani52c489c2012-03-28 11:42:42 -070069 private static final String TAG = "LockSettingsService";
70
Amith Yamasani52c489c2012-03-28 11:42:42 -070071 private final Context mContext;
Adrian Roos261d5ab2014-10-29 14:42:38 +010072
73 private final LockSettingsStorage mStorage;
74
Jim Millerde1af082013-09-11 14:58:26 -070075 private LockPatternUtils mLockPatternUtils;
Paul Lawrence945490c2014-03-27 16:37:28 +000076 private boolean mFirstCallToVold;
Andres Morales8fa56652015-03-31 09:19:50 -070077 private IGateKeeperService mGateKeeperService;
Amith Yamasani52c489c2012-03-28 11:42:42 -070078
Andres Morales23974272015-05-14 22:42:26 -070079 private interface CredentialUtil {
80 void setCredential(String credential, String savedCredential, int userId)
81 throws RemoteException;
82 byte[] toHash(String credential, int userId);
83 }
84
Amith Yamasani52c489c2012-03-28 11:42:42 -070085 public LockSettingsService(Context context) {
86 mContext = context;
87 // Open the database
Jim Millerde1af082013-09-11 14:58:26 -070088
89 mLockPatternUtils = new LockPatternUtils(context);
Paul Lawrence945490c2014-03-27 16:37:28 +000090 mFirstCallToVold = true;
Robin Leef0246a82014-08-13 09:50:25 +010091
92 IntentFilter filter = new IntentFilter();
93 filter.addAction(Intent.ACTION_USER_ADDED);
Adrian Roos3dcae682014-10-29 14:43:56 +010094 filter.addAction(Intent.ACTION_USER_STARTING);
Adrian Roosdb0f76e2015-01-07 22:19:38 +010095 filter.addAction(Intent.ACTION_USER_REMOVED);
Robin Leef0246a82014-08-13 09:50:25 +010096 mContext.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, filter, null, null);
Adrian Roos261d5ab2014-10-29 14:42:38 +010097
98 mStorage = new LockSettingsStorage(context, new LockSettingsStorage.Callback() {
99 @Override
100 public void initialize(SQLiteDatabase db) {
101 // Get the lockscreen default from a system property, if available
102 boolean lockScreenDisable = SystemProperties.getBoolean(
103 "ro.lockscreen.disable.default", false);
104 if (lockScreenDisable) {
105 mStorage.writeKeyValue(db, LockPatternUtils.DISABLE_LOCKSCREEN_KEY, "1", 0);
106 }
107 }
108 });
Amith Yamasani52c489c2012-03-28 11:42:42 -0700109 }
110
Robin Leef0246a82014-08-13 09:50:25 +0100111 private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
112 @Override
113 public void onReceive(Context context, Intent intent) {
Robin Lee1096cf82014-09-01 16:52:47 +0100114 if (Intent.ACTION_USER_ADDED.equals(intent.getAction())) {
Chad Brubaker83ce0952015-05-12 13:00:02 -0700115 // Notify keystore that a new user was added.
Robin Leef0246a82014-08-13 09:50:25 +0100116 final int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
Robin Lee49d810c2014-09-23 13:50:22 +0100117 final KeyStore ks = KeyStore.getInstance();
Robin Leef0246a82014-08-13 09:50:25 +0100118 final UserManager um = (UserManager) mContext.getSystemService(USER_SERVICE);
119 final UserInfo parentInfo = um.getProfileParent(userHandle);
Chad Brubaker83ce0952015-05-12 13:00:02 -0700120 final int parentHandle = parentInfo != null ? parentInfo.id : -1;
121 ks.onUserAdded(userHandle, parentHandle);
Adrian Roos3dcae682014-10-29 14:43:56 +0100122 } else if (Intent.ACTION_USER_STARTING.equals(intent.getAction())) {
123 final int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
124 mStorage.prefetchUser(userHandle);
Adrian Roosdb0f76e2015-01-07 22:19:38 +0100125 } else if (Intent.ACTION_USER_REMOVED.equals(intent.getAction())) {
126 final int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
127 if (userHandle > 0) {
128 removeUser(userHandle);
129 }
Robin Leef0246a82014-08-13 09:50:25 +0100130 }
131 }
132 };
133
Amith Yamasani52c489c2012-03-28 11:42:42 -0700134 public void systemReady() {
135 migrateOldData();
Andres Morales301ea442015-04-17 09:15:47 -0700136 try {
137 getGateKeeperService();
138 } catch (RemoteException e) {
139 Slog.e(TAG, "Failure retrieving IGateKeeperService", e);
140 }
Adrian Roos3dcae682014-10-29 14:43:56 +0100141 mStorage.prefetchUser(UserHandle.USER_OWNER);
Amith Yamasani52c489c2012-03-28 11:42:42 -0700142 }
143
144 private void migrateOldData() {
145 try {
Jim Miller187ec582013-04-15 18:27:54 -0700146 // These Settings moved before multi-user was enabled, so we only have to do it for the
147 // root user.
148 if (getString("migrated", null, 0) == null) {
149 final ContentResolver cr = mContext.getContentResolver();
150 for (String validSetting : VALID_SETTINGS) {
151 String value = Settings.Secure.getString(cr, validSetting);
152 if (value != null) {
153 setString(validSetting, value, 0);
154 }
155 }
156 // No need to move the password / pattern files. They're already in the right place.
157 setString("migrated", "true", 0);
158 Slog.i(TAG, "Migrated lock settings to new location");
Amith Yamasani52c489c2012-03-28 11:42:42 -0700159 }
160
Jim Miller187ec582013-04-15 18:27:54 -0700161 // These Settings changed after multi-user was enabled, hence need to be moved per user.
162 if (getString("migrated_user_specific", null, 0) == null) {
163 final UserManager um = (UserManager) mContext.getSystemService(USER_SERVICE);
164 final ContentResolver cr = mContext.getContentResolver();
165 List<UserInfo> users = um.getUsers();
166 for (int user = 0; user < users.size(); user++) {
Jim Miller2d8ecf9d2013-04-22 17:17:03 -0700167 // Migrate owner info
168 final int userId = users.get(user).id;
169 final String OWNER_INFO = Secure.LOCK_SCREEN_OWNER_INFO;
170 String ownerInfo = Settings.Secure.getStringForUser(cr, OWNER_INFO, userId);
171 if (ownerInfo != null) {
172 setString(OWNER_INFO, ownerInfo, userId);
173 Settings.Secure.putStringForUser(cr, ownerInfo, "", userId);
174 }
Jim Miller187ec582013-04-15 18:27:54 -0700175
Jim Miller2d8ecf9d2013-04-22 17:17:03 -0700176 // Migrate owner info enabled. Note there was a bug where older platforms only
177 // stored this value if the checkbox was toggled at least once. The code detects
178 // this case by handling the exception.
179 final String OWNER_INFO_ENABLED = Secure.LOCK_SCREEN_OWNER_INFO_ENABLED;
180 boolean enabled;
181 try {
182 int ivalue = Settings.Secure.getIntForUser(cr, OWNER_INFO_ENABLED, userId);
183 enabled = ivalue != 0;
184 setLong(OWNER_INFO_ENABLED, enabled ? 1 : 0, userId);
185 } catch (SettingNotFoundException e) {
186 // Setting was never stored. Store it if the string is not empty.
187 if (!TextUtils.isEmpty(ownerInfo)) {
188 setLong(OWNER_INFO_ENABLED, 1, userId);
Jim Miller187ec582013-04-15 18:27:54 -0700189 }
190 }
Jim Miller2d8ecf9d2013-04-22 17:17:03 -0700191 Settings.Secure.putIntForUser(cr, OWNER_INFO_ENABLED, 0, userId);
Amith Yamasani52c489c2012-03-28 11:42:42 -0700192 }
Jim Miller187ec582013-04-15 18:27:54 -0700193 // No need to move the password / pattern files. They're already in the right place.
194 setString("migrated_user_specific", "true", 0);
195 Slog.i(TAG, "Migrated per-user lock settings to new location");
Amith Yamasani52c489c2012-03-28 11:42:42 -0700196 }
Adrian Roos230635e2015-01-07 20:50:29 +0100197
198 // Migrates biometric weak such that the fallback mechanism becomes the primary.
199 if (getString("migrated_biometric_weak", null, 0) == null) {
200 final UserManager um = (UserManager) mContext.getSystemService(USER_SERVICE);
201 List<UserInfo> users = um.getUsers();
202 for (int i = 0; i < users.size(); i++) {
203 int userId = users.get(i).id;
204 long type = getLong(LockPatternUtils.PASSWORD_TYPE_KEY,
205 DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED,
206 userId);
207 long alternateType = getLong(LockPatternUtils.PASSWORD_TYPE_ALTERNATE_KEY,
208 DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED,
209 userId);
210 if (type == DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK) {
211 setLong(LockPatternUtils.PASSWORD_TYPE_KEY,
212 alternateType,
213 userId);
214 }
215 setLong(LockPatternUtils.PASSWORD_TYPE_ALTERNATE_KEY,
216 DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED,
217 userId);
218 }
219 setString("migrated_biometric_weak", "true", 0);
220 Slog.i(TAG, "Migrated biometric weak to use the fallback instead");
221 }
Adrian Roos43830582015-04-21 16:04:43 -0700222
223 // Migrates lockscreen.disabled. Prior to M, the flag was ignored when more than one
224 // user was present on the system, so if we're upgrading to M and there is more than one
225 // user we disable the flag to remain consistent.
226 if (getString("migrated_lockscreen_disabled", null, 0) == null) {
227 final UserManager um = (UserManager) mContext.getSystemService(USER_SERVICE);
228
229 final List<UserInfo> users = um.getUsers();
230 final int userCount = users.size();
231 int switchableUsers = 0;
232 for (int i = 0; i < userCount; i++) {
233 if (users.get(i).supportsSwitchTo()) {
234 switchableUsers++;
235 }
236 }
237
238 if (switchableUsers > 1) {
239 for (int i = 0; i < userCount; i++) {
240 int id = users.get(i).id;
241
242 if (getBoolean(LockPatternUtils.DISABLE_LOCKSCREEN_KEY, false, id)) {
243 setBoolean(LockPatternUtils.DISABLE_LOCKSCREEN_KEY, false, id);
244 }
245 }
246 }
247
248 setString("migrated_lockscreen_disabled", "true", 0);
249 Slog.i(TAG, "Migrated lockscreen disabled flag");
250 }
Amith Yamasani52c489c2012-03-28 11:42:42 -0700251 } catch (RemoteException re) {
Jim Miller187ec582013-04-15 18:27:54 -0700252 Slog.e(TAG, "Unable to migrate old data", re);
Amith Yamasani52c489c2012-03-28 11:42:42 -0700253 }
254 }
255
Jim Miller5ecd8112013-01-09 18:50:26 -0800256 private final void checkWritePermission(int userId) {
Jim Miller505329b2013-11-08 13:25:36 -0800257 mContext.enforceCallingOrSelfPermission(PERMISSION, "LockSettingsWrite");
Amith Yamasani52c489c2012-03-28 11:42:42 -0700258 }
259
Jim Miller5ecd8112013-01-09 18:50:26 -0800260 private final void checkPasswordReadPermission(int userId) {
Jim Miller505329b2013-11-08 13:25:36 -0800261 mContext.enforceCallingOrSelfPermission(PERMISSION, "LockSettingsRead");
Amith Yamasani52c489c2012-03-28 11:42:42 -0700262 }
263
Jim Miller158fe192013-04-17 15:23:55 -0700264 private final void checkReadPermission(String requestedKey, int userId) {
Amith Yamasani52c489c2012-03-28 11:42:42 -0700265 final int callingUid = Binder.getCallingUid();
Adrian Roos001b00d2015-02-24 17:08:48 +0100266
Jim Miller158fe192013-04-17 15:23:55 -0700267 for (int i = 0; i < READ_PROFILE_PROTECTED_SETTINGS.length; i++) {
268 String key = READ_PROFILE_PROTECTED_SETTINGS[i];
269 if (key.equals(requestedKey) && mContext.checkCallingOrSelfPermission(READ_PROFILE)
270 != PackageManager.PERMISSION_GRANTED) {
271 throw new SecurityException("uid=" + callingUid
272 + " needs permission " + READ_PROFILE + " to read "
273 + requestedKey + " for user " + userId);
274 }
Amith Yamasani52c489c2012-03-28 11:42:42 -0700275 }
Adrian Roos001b00d2015-02-24 17:08:48 +0100276
277 for (int i = 0; i < READ_PASSWORD_PROTECTED_SETTINGS.length; i++) {
278 String key = READ_PASSWORD_PROTECTED_SETTINGS[i];
279 if (key.equals(requestedKey) && mContext.checkCallingOrSelfPermission(PERMISSION)
280 != PackageManager.PERMISSION_GRANTED) {
281 throw new SecurityException("uid=" + callingUid
282 + " needs permission " + PERMISSION + " to read "
283 + requestedKey + " for user " + userId);
284 }
285 }
Amith Yamasani52c489c2012-03-28 11:42:42 -0700286 }
287
288 @Override
289 public void setBoolean(String key, boolean value, int userId) throws RemoteException {
290 checkWritePermission(userId);
Adrian Roos261d5ab2014-10-29 14:42:38 +0100291 setStringUnchecked(key, userId, value ? "1" : "0");
Amith Yamasani52c489c2012-03-28 11:42:42 -0700292 }
293
294 @Override
295 public void setLong(String key, long value, int userId) throws RemoteException {
296 checkWritePermission(userId);
Adrian Roos261d5ab2014-10-29 14:42:38 +0100297 setStringUnchecked(key, userId, Long.toString(value));
Amith Yamasani52c489c2012-03-28 11:42:42 -0700298 }
299
300 @Override
301 public void setString(String key, String value, int userId) throws RemoteException {
302 checkWritePermission(userId);
Adrian Roos261d5ab2014-10-29 14:42:38 +0100303 setStringUnchecked(key, userId, value);
304 }
Amith Yamasani52c489c2012-03-28 11:42:42 -0700305
Adrian Roos261d5ab2014-10-29 14:42:38 +0100306 private void setStringUnchecked(String key, int userId, String value) {
307 mStorage.writeKeyValue(key, value, userId);
Amith Yamasani072543f2015-02-13 11:09:45 -0800308 if (ArrayUtils.contains(SETTINGS_TO_BACKUP, key)) {
309 BackupManager.dataChanged("com.android.providers.settings");
310 }
Amith Yamasani52c489c2012-03-28 11:42:42 -0700311 }
312
313 @Override
314 public boolean getBoolean(String key, boolean defaultValue, int userId) throws RemoteException {
Jim Miller158fe192013-04-17 15:23:55 -0700315 checkReadPermission(key, userId);
Adrian Roos9dd16eb2015-01-08 16:20:49 +0100316 String value = getStringUnchecked(key, null, userId);
Amith Yamasani52c489c2012-03-28 11:42:42 -0700317 return TextUtils.isEmpty(value) ?
318 defaultValue : (value.equals("1") || value.equals("true"));
319 }
320
321 @Override
322 public long getLong(String key, long defaultValue, int userId) throws RemoteException {
Jim Miller158fe192013-04-17 15:23:55 -0700323 checkReadPermission(key, userId);
Amith Yamasani52c489c2012-03-28 11:42:42 -0700324
Adrian Roos9dd16eb2015-01-08 16:20:49 +0100325 String value = getStringUnchecked(key, null, userId);
Amith Yamasani52c489c2012-03-28 11:42:42 -0700326 return TextUtils.isEmpty(value) ? defaultValue : Long.parseLong(value);
327 }
328
329 @Override
330 public String getString(String key, String defaultValue, int userId) throws RemoteException {
Jim Miller158fe192013-04-17 15:23:55 -0700331 checkReadPermission(key, userId);
Amith Yamasani52c489c2012-03-28 11:42:42 -0700332
Adrian Roos9dd16eb2015-01-08 16:20:49 +0100333 return getStringUnchecked(key, defaultValue, userId);
334 }
335
336 public String getStringUnchecked(String key, String defaultValue, int userId) {
337 if (Settings.Secure.LOCK_PATTERN_ENABLED.equals(key)) {
338 return mLockPatternUtils.isLockPatternEnabled(userId) ? "1" : "0";
339 }
340
Adrian Roos261d5ab2014-10-29 14:42:38 +0100341 return mStorage.readKeyValue(key, defaultValue, userId);
Amith Yamasani52c489c2012-03-28 11:42:42 -0700342 }
343
Adrian Roos4f788452014-05-22 20:45:59 +0200344 @Override
Amith Yamasani52c489c2012-03-28 11:42:42 -0700345 public boolean havePassword(int userId) throws RemoteException {
346 // Do we need a permissions check here?
347
Adrian Roos261d5ab2014-10-29 14:42:38 +0100348 return mStorage.hasPassword(userId);
Amith Yamasani52c489c2012-03-28 11:42:42 -0700349 }
350
351 @Override
352 public boolean havePattern(int userId) throws RemoteException {
353 // Do we need a permissions check here?
354
Adrian Roos261d5ab2014-10-29 14:42:38 +0100355 return mStorage.hasPattern(userId);
Amith Yamasani52c489c2012-03-28 11:42:42 -0700356 }
357
Chad Brubakera91a8502015-05-07 10:02:22 -0700358 private void setKeystorePassword(String password, int userHandle) {
Robin Leef0246a82014-08-13 09:50:25 +0100359 final UserManager um = (UserManager) mContext.getSystemService(USER_SERVICE);
360 final KeyStore ks = KeyStore.getInstance();
361
362 final List<UserInfo> profiles = um.getProfiles(userHandle);
Robin Leef0246a82014-08-13 09:50:25 +0100363 for (UserInfo pi : profiles) {
Chad Brubakera91a8502015-05-07 10:02:22 -0700364 ks.onUserPasswordChanged(pi.id, password);
365 }
366 }
367
368 private void unlockKeystore(String password, int userHandle) {
369 final UserManager um = (UserManager) mContext.getSystemService(USER_SERVICE);
370 final KeyStore ks = KeyStore.getInstance();
371
372 final List<UserInfo> profiles = um.getProfiles(userHandle);
373 for (UserInfo pi : profiles) {
374 ks.unlock(pi.id, password);
Jim Millerde1af082013-09-11 14:58:26 -0700375 }
376 }
377
Amith Yamasani52c489c2012-03-28 11:42:42 -0700378
Andres Morales8fa56652015-03-31 09:19:50 -0700379 private byte[] getCurrentHandle(int userId) {
380 CredentialHash credential;
381 byte[] currentHandle;
Jim Millerde1af082013-09-11 14:58:26 -0700382
Andres Morales8fa56652015-03-31 09:19:50 -0700383 int currentHandleType = mStorage.getStoredCredentialType(userId);
384 switch (currentHandleType) {
385 case CredentialHash.TYPE_PATTERN:
386 credential = mStorage.readPatternHash(userId);
387 currentHandle = credential != null
388 ? credential.hash
389 : null;
390 break;
391 case CredentialHash.TYPE_PASSWORD:
392 credential = mStorage.readPasswordHash(userId);
393 currentHandle = credential != null
394 ? credential.hash
395 : null;
396 break;
397 case CredentialHash.TYPE_NONE:
398 default:
399 currentHandle = null;
400 break;
401 }
402
403 // sanity check
404 if (currentHandleType != CredentialHash.TYPE_NONE && currentHandle == null) {
405 Slog.e(TAG, "Stored handle type [" + currentHandleType + "] but no handle available");
406 }
407
408 return currentHandle;
Amith Yamasani52c489c2012-03-28 11:42:42 -0700409 }
410
Andres Morales8fa56652015-03-31 09:19:50 -0700411
Amith Yamasani52c489c2012-03-28 11:42:42 -0700412 @Override
Andres Morales8fa56652015-03-31 09:19:50 -0700413 public void setLockPattern(String pattern, String savedCredential, int userId)
414 throws RemoteException {
415 byte[] currentHandle = getCurrentHandle(userId);
416
Andres Moralesd9fc85a2015-04-09 19:14:42 -0700417 if (pattern == null) {
Andres Moralescfb61602015-04-16 16:31:15 -0700418 getGateKeeperService().clearSecureUserId(userId);
Andres Moralesd9fc85a2015-04-09 19:14:42 -0700419 mStorage.writePatternHash(null, userId);
Chad Brubakera91a8502015-05-07 10:02:22 -0700420 setKeystorePassword(null, userId);
Andres Moralesd9fc85a2015-04-09 19:14:42 -0700421 return;
422 }
423
Andres Morales8fa56652015-03-31 09:19:50 -0700424 if (currentHandle == null) {
425 if (savedCredential != null) {
426 Slog.w(TAG, "Saved credential provided, but none stored");
427 }
428 savedCredential = null;
429 }
430
431 byte[] enrolledHandle = enrollCredential(currentHandle, savedCredential, pattern, userId);
432 if (enrolledHandle != null) {
433 mStorage.writePatternHash(enrolledHandle, userId);
434 } else {
435 Slog.e(TAG, "Failed to enroll pattern");
436 }
437 }
438
439
440 @Override
441 public void setLockPassword(String password, String savedCredential, int userId)
442 throws RemoteException {
Andres Morales8fa56652015-03-31 09:19:50 -0700443 byte[] currentHandle = getCurrentHandle(userId);
444
Andres Moralesd9fc85a2015-04-09 19:14:42 -0700445 if (password == null) {
Andres Moralescfb61602015-04-16 16:31:15 -0700446 getGateKeeperService().clearSecureUserId(userId);
Andres Moralesd9fc85a2015-04-09 19:14:42 -0700447 mStorage.writePasswordHash(null, userId);
Chad Brubakera91a8502015-05-07 10:02:22 -0700448 setKeystorePassword(null, userId);
Andres Moralesd9fc85a2015-04-09 19:14:42 -0700449 return;
450 }
451
Andres Morales8fa56652015-03-31 09:19:50 -0700452 if (currentHandle == null) {
453 if (savedCredential != null) {
454 Slog.w(TAG, "Saved credential provided, but none stored");
455 }
456 savedCredential = null;
457 }
458
459 byte[] enrolledHandle = enrollCredential(currentHandle, savedCredential, password, userId);
460 if (enrolledHandle != null) {
461 mStorage.writePasswordHash(enrolledHandle, userId);
462 } else {
463 Slog.e(TAG, "Failed to enroll password");
464 }
465 }
466
467 private byte[] enrollCredential(byte[] enrolledHandle,
468 String enrolledCredential, String toEnroll, int userId)
469 throws RemoteException {
Jim Millerde1af082013-09-11 14:58:26 -0700470 checkWritePermission(userId);
Andres Morales8fa56652015-03-31 09:19:50 -0700471 byte[] enrolledCredentialBytes = enrolledCredential == null
472 ? null
473 : enrolledCredential.getBytes();
474 byte[] toEnrollBytes = toEnroll == null
475 ? null
476 : toEnroll.getBytes();
Andres Morales23974272015-05-14 22:42:26 -0700477 GateKeeperResponse response = getGateKeeperService().enroll(userId, enrolledHandle,
478 enrolledCredentialBytes, toEnrollBytes);
Jim Millerde1af082013-09-11 14:58:26 -0700479
Andres Morales23974272015-05-14 22:42:26 -0700480 if (response == null) {
481 return null;
Andres Morales8fa56652015-03-31 09:19:50 -0700482 }
Jim Millerde1af082013-09-11 14:58:26 -0700483
Andres Morales23974272015-05-14 22:42:26 -0700484 byte[] hash = response.getPayload();
485 if (hash != null) {
486 setKeystorePassword(toEnroll, userId);
487 } else {
488 // Should not happen
489 Slog.e(TAG, "Throttled while enrolling a password");
490 }
Andres Morales8fa56652015-03-31 09:19:50 -0700491 return hash;
Jim Millerde1af082013-09-11 14:58:26 -0700492 }
493
494 @Override
Andres Morales23974272015-05-14 22:42:26 -0700495 public VerifyCredentialResponse checkPattern(String pattern, int userId) throws RemoteException {
496 return doVerifyPattern(pattern, false, 0, userId);
Andres Moralesd9fc85a2015-04-09 19:14:42 -0700497 }
Adrian Roos261d5ab2014-10-29 14:42:38 +0100498
Andres Moralesd9fc85a2015-04-09 19:14:42 -0700499 @Override
Andres Morales23974272015-05-14 22:42:26 -0700500 public VerifyCredentialResponse verifyPattern(String pattern, long challenge, int userId)
Andres Moralesd9fc85a2015-04-09 19:14:42 -0700501 throws RemoteException {
Andres Morales23974272015-05-14 22:42:26 -0700502 return doVerifyPattern(pattern, true, challenge, userId);
Andres Moralesd9fc85a2015-04-09 19:14:42 -0700503 }
504
Andres Moralese40bad82015-05-28 14:21:36 -0700505 private VerifyCredentialResponse doVerifyPattern(String pattern, boolean hasChallenge,
506 long challenge, int userId) throws RemoteException {
Andres Moralesd9fc85a2015-04-09 19:14:42 -0700507 checkPasswordReadPermission(userId);
Andres Moralesd9fc85a2015-04-09 19:14:42 -0700508 CredentialHash storedHash = mStorage.readPatternHash(userId);
Andres Moralese40bad82015-05-28 14:21:36 -0700509 boolean shouldReEnrollBaseZero = storedHash != null && storedHash.isBaseZeroPattern;
510
511 String patternToVerify;
512 if (shouldReEnrollBaseZero) {
513 patternToVerify = LockPatternUtils.patternStringToBaseZero(pattern);
514 } else {
515 patternToVerify = pattern;
516 }
517
518 VerifyCredentialResponse response = verifyCredential(userId, storedHash, patternToVerify,
519 hasChallenge, challenge,
Andres Morales23974272015-05-14 22:42:26 -0700520 new CredentialUtil() {
521 @Override
522 public void setCredential(String pattern, String oldPattern, int userId)
523 throws RemoteException {
524 setLockPattern(pattern, oldPattern, userId);
525 }
Andres Moralesd9fc85a2015-04-09 19:14:42 -0700526
Andres Morales23974272015-05-14 22:42:26 -0700527 @Override
528 public byte[] toHash(String pattern, int userId) {
Andres Moralese40bad82015-05-28 14:21:36 -0700529 return LockPatternUtils.patternToHash(
530 LockPatternUtils.stringToPattern(pattern));
Andres Morales23974272015-05-14 22:42:26 -0700531 }
532 }
533 );
Andres Moralese40bad82015-05-28 14:21:36 -0700534
535 if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK
536 && shouldReEnrollBaseZero) {
537 setLockPattern(pattern, patternToVerify, userId);
538 }
539
540 return response;
541
Amith Yamasani52c489c2012-03-28 11:42:42 -0700542 }
543
544 @Override
Andres Morales23974272015-05-14 22:42:26 -0700545 public VerifyCredentialResponse checkPassword(String password, int userId)
Andres Moralesd9fc85a2015-04-09 19:14:42 -0700546 throws RemoteException {
Andres Morales23974272015-05-14 22:42:26 -0700547 return doVerifyPassword(password, false, 0, userId);
Andres Moralesd9fc85a2015-04-09 19:14:42 -0700548 }
549
Andres Morales23974272015-05-14 22:42:26 -0700550 @Override
551 public VerifyCredentialResponse verifyPassword(String password, long challenge, int userId)
552 throws RemoteException {
553 return doVerifyPassword(password, true, challenge, userId);
554 }
555
556 private VerifyCredentialResponse doVerifyPassword(String password, boolean hasChallenge,
557 long challenge, int userId) throws RemoteException {
Andres Moralesd9fc85a2015-04-09 19:14:42 -0700558 checkPasswordReadPermission(userId);
Andres Morales8fa56652015-03-31 09:19:50 -0700559 CredentialHash storedHash = mStorage.readPasswordHash(userId);
Andres Morales23974272015-05-14 22:42:26 -0700560 return verifyCredential(userId, storedHash, password, hasChallenge, challenge,
561 new CredentialUtil() {
562 @Override
563 public void setCredential(String password, String oldPassword, int userId)
564 throws RemoteException {
565 setLockPassword(password, oldPassword, userId);
566 }
Adrian Roos261d5ab2014-10-29 14:42:38 +0100567
Andres Morales23974272015-05-14 22:42:26 -0700568 @Override
569 public byte[] toHash(String password, int userId) {
570 return mLockPatternUtils.passwordToHash(password, userId);
571 }
572 }
573 );
574 }
575
576 private VerifyCredentialResponse verifyCredential(int userId, CredentialHash storedHash,
577 String credential, boolean hasChallenge, long challenge, CredentialUtil credentialUtil)
578 throws RemoteException {
579 if ((storedHash == null || storedHash.hash.length == 0) && TextUtils.isEmpty(credential)) {
580 // don't need to pass empty credentials to GateKeeper
581 return VerifyCredentialResponse.OK;
Andres Moralesd9fc85a2015-04-09 19:14:42 -0700582 }
583
Andres Morales23974272015-05-14 22:42:26 -0700584 if (TextUtils.isEmpty(credential)) {
585 return VerifyCredentialResponse.ERROR;
Amith Yamasani52c489c2012-03-28 11:42:42 -0700586 }
Adrian Roos261d5ab2014-10-29 14:42:38 +0100587
Andres Morales8fa56652015-03-31 09:19:50 -0700588 if (storedHash.version == CredentialHash.VERSION_LEGACY) {
Andres Morales23974272015-05-14 22:42:26 -0700589 byte[] hash = credentialUtil.toHash(credential, userId);
Andres Moralesd9fc85a2015-04-09 19:14:42 -0700590 if (Arrays.equals(hash, storedHash.hash)) {
Andres Morales23974272015-05-14 22:42:26 -0700591 unlockKeystore(credential, userId);
592 // migrate credential to GateKeeper
593 credentialUtil.setCredential(credential, null, userId);
Andres Moralesd9fc85a2015-04-09 19:14:42 -0700594 if (!hasChallenge) {
Andres Morales23974272015-05-14 22:42:26 -0700595 return VerifyCredentialResponse.OK;
Andres Moralesd9fc85a2015-04-09 19:14:42 -0700596 }
597 // Fall through to get the auth token. Technically this should never happen,
Andres Morales23974272015-05-14 22:42:26 -0700598 // as a user that had a legacy credential would have to unlock their device
Andres Moralesd9fc85a2015-04-09 19:14:42 -0700599 // before getting to a flow with a challenge, but supporting for consistency.
600 } else {
Andres Morales23974272015-05-14 22:42:26 -0700601 return VerifyCredentialResponse.ERROR;
Andres Morales8fa56652015-03-31 09:19:50 -0700602 }
Andres Morales8fa56652015-03-31 09:19:50 -0700603 }
604
Andres Morales23974272015-05-14 22:42:26 -0700605 VerifyCredentialResponse response;
606 boolean shouldReEnroll = false;;
Andres Moralesd9fc85a2015-04-09 19:14:42 -0700607 if (hasChallenge) {
Andres Morales23974272015-05-14 22:42:26 -0700608 byte[] token = null;
609 GateKeeperResponse gateKeeperResponse = getGateKeeperService()
610 .verifyChallenge(userId, challenge, storedHash.hash, credential.getBytes());
611 int responseCode = gateKeeperResponse.getResponseCode();
612 if (responseCode == GateKeeperResponse.RESPONSE_RETRY) {
613 response = new VerifyCredentialResponse(gateKeeperResponse.getTimeout());
614 } else if (responseCode == GateKeeperResponse.RESPONSE_OK) {
615 token = gateKeeperResponse.getPayload();
616 if (token == null) {
617 // something's wrong if there's no payload with a challenge
618 Slog.e(TAG, "verifyChallenge response had no associated payload");
619 response = VerifyCredentialResponse.ERROR;
620 } else {
621 shouldReEnroll = gateKeeperResponse.getShouldReEnroll();
622 response = new VerifyCredentialResponse(token);
623 }
624 } else {
625 response = VerifyCredentialResponse.ERROR;
Andres Moralesd9fc85a2015-04-09 19:14:42 -0700626 }
Andres Morales23974272015-05-14 22:42:26 -0700627 } else {
628 GateKeeperResponse gateKeeperResponse = getGateKeeperService().verify(
629 userId, storedHash.hash, credential.getBytes());
630 int responseCode = gateKeeperResponse.getResponseCode();
631 if (responseCode == GateKeeperResponse.RESPONSE_RETRY) {
632 response = new VerifyCredentialResponse(gateKeeperResponse.getTimeout());
633 } else if (responseCode == GateKeeperResponse.RESPONSE_OK) {
634 shouldReEnroll = gateKeeperResponse.getShouldReEnroll();
635 response = VerifyCredentialResponse.OK;
636 } else {
637 response = VerifyCredentialResponse.ERROR;
638 }
Adrian Roos261d5ab2014-10-29 14:42:38 +0100639 }
Andres Morales8fa56652015-03-31 09:19:50 -0700640
Andres Morales23974272015-05-14 22:42:26 -0700641 if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK) {
642 // credential has matched
643 unlockKeystore(credential, userId);
644 if (shouldReEnroll) {
645 credentialUtil.setCredential(credential, credential, userId);
646 }
647 }
Amith Yamasani52c489c2012-03-28 11:42:42 -0700648
Andres Morales23974272015-05-14 22:42:26 -0700649 return response;
650 }
Andres Moralesd9fc85a2015-04-09 19:14:42 -0700651
Amith Yamasani52c489c2012-03-28 11:42:42 -0700652 @Override
Adrian Roos261d5ab2014-10-29 14:42:38 +0100653 public boolean checkVoldPassword(int userId) throws RemoteException {
Paul Lawrence945490c2014-03-27 16:37:28 +0000654 if (!mFirstCallToVold) {
655 return false;
656 }
657 mFirstCallToVold = false;
658
659 checkPasswordReadPermission(userId);
660
661 // There's no guarantee that this will safely connect, but if it fails
662 // we will simply show the lock screen when we shouldn't, so relatively
663 // benign. There is an outside chance something nasty would happen if
664 // this service restarted before vold stales out the password in this
665 // case. The nastiness is limited to not showing the lock screen when
666 // we should, within the first minute of decrypting the phone if this
667 // service can't connect to vold, it restarts, and then the new instance
668 // does successfully connect.
669 final IMountService service = getMountService();
670 String password = service.getPassword();
671 service.clearPassword();
672 if (password == null) {
673 return false;
674 }
675
676 try {
Adrian Roos9dd16eb2015-01-08 16:20:49 +0100677 if (mLockPatternUtils.isLockPatternEnabled(userId)) {
Andres Morales23974272015-05-14 22:42:26 -0700678 if (checkPattern(password, userId).getResponseCode()
679 == GateKeeperResponse.RESPONSE_OK) {
Paul Lawrence945490c2014-03-27 16:37:28 +0000680 return true;
681 }
682 }
683 } catch (Exception e) {
684 }
685
686 try {
Adrian Roos9dd16eb2015-01-08 16:20:49 +0100687 if (mLockPatternUtils.isLockPasswordEnabled(userId)) {
Andres Morales23974272015-05-14 22:42:26 -0700688 if (checkPassword(password, userId).getResponseCode()
689 == GateKeeperResponse.RESPONSE_OK) {
Paul Lawrence945490c2014-03-27 16:37:28 +0000690 return true;
691 }
692 }
693 } catch (Exception e) {
694 }
695
696 return false;
697 }
698
Adrian Roosdb0f76e2015-01-07 22:19:38 +0100699 private void removeUser(int userId) {
Adrian Roos261d5ab2014-10-29 14:42:38 +0100700 mStorage.removeUser(userId);
Robin Lee49d810c2014-09-23 13:50:22 +0100701
702 final KeyStore ks = KeyStore.getInstance();
Chad Brubaker83ce0952015-05-12 13:00:02 -0700703 ks.onUserRemoved(userId);
Amith Yamasani52c489c2012-03-28 11:42:42 -0700704 }
705
Amith Yamasani52c489c2012-03-28 11:42:42 -0700706 private static final String[] VALID_SETTINGS = new String[] {
707 LockPatternUtils.LOCKOUT_PERMANENT_KEY,
708 LockPatternUtils.LOCKOUT_ATTEMPT_DEADLINE,
709 LockPatternUtils.PATTERN_EVER_CHOSEN_KEY,
710 LockPatternUtils.PASSWORD_TYPE_KEY,
711 LockPatternUtils.PASSWORD_TYPE_ALTERNATE_KEY,
712 LockPatternUtils.LOCK_PASSWORD_SALT_KEY,
713 LockPatternUtils.DISABLE_LOCKSCREEN_KEY,
714 LockPatternUtils.LOCKSCREEN_OPTIONS,
715 LockPatternUtils.LOCKSCREEN_BIOMETRIC_WEAK_FALLBACK,
716 LockPatternUtils.BIOMETRIC_WEAK_EVER_CHOSEN_KEY,
717 LockPatternUtils.LOCKSCREEN_POWER_BUTTON_INSTANTLY_LOCKS,
718 LockPatternUtils.PASSWORD_HISTORY_KEY,
719 Secure.LOCK_PATTERN_ENABLED,
720 Secure.LOCK_BIOMETRIC_WEAK_FLAGS,
721 Secure.LOCK_PATTERN_VISIBLE,
722 Secure.LOCK_PATTERN_TACTILE_FEEDBACK_ENABLED
Jim Miller187ec582013-04-15 18:27:54 -0700723 };
724
Adrian Roos001b00d2015-02-24 17:08:48 +0100725 // Reading these settings needs the profile permission
Jim Miller2d8ecf9d2013-04-22 17:17:03 -0700726 private static final String[] READ_PROFILE_PROTECTED_SETTINGS = new String[] {
Jim Miller187ec582013-04-15 18:27:54 -0700727 Secure.LOCK_SCREEN_OWNER_INFO_ENABLED,
728 Secure.LOCK_SCREEN_OWNER_INFO
729 };
Paul Lawrence945490c2014-03-27 16:37:28 +0000730
Adrian Roos001b00d2015-02-24 17:08:48 +0100731 // Reading these settings needs the same permission as checking the password
732 private static final String[] READ_PASSWORD_PROTECTED_SETTINGS = new String[] {
733 LockPatternUtils.LOCK_PASSWORD_SALT_KEY,
734 LockPatternUtils.PASSWORD_HISTORY_KEY,
Adrian Roos855fa302015-04-02 16:01:12 +0200735 LockPatternUtils.PASSWORD_TYPE_KEY,
Adrian Roos001b00d2015-02-24 17:08:48 +0100736 };
737
Amith Yamasani072543f2015-02-13 11:09:45 -0800738 private static final String[] SETTINGS_TO_BACKUP = new String[] {
739 Secure.LOCK_SCREEN_OWNER_INFO_ENABLED,
740 Secure.LOCK_SCREEN_OWNER_INFO
741 };
742
Paul Lawrence945490c2014-03-27 16:37:28 +0000743 private IMountService getMountService() {
744 final IBinder service = ServiceManager.getService("mount");
745 if (service != null) {
746 return IMountService.Stub.asInterface(service);
747 }
748 return null;
749 }
Andres Morales8fa56652015-03-31 09:19:50 -0700750
Andres Morales301ea442015-04-17 09:15:47 -0700751 private class GateKeeperDiedRecipient implements IBinder.DeathRecipient {
752 @Override
753 public void binderDied() {
754 mGateKeeperService.asBinder().unlinkToDeath(this, 0);
755 mGateKeeperService = null;
756 }
757 }
758
759 private synchronized IGateKeeperService getGateKeeperService()
760 throws RemoteException {
Andres Morales8fa56652015-03-31 09:19:50 -0700761 if (mGateKeeperService != null) {
762 return mGateKeeperService;
763 }
764
765 final IBinder service =
766 ServiceManager.getService("android.service.gatekeeper.IGateKeeperService");
767 if (service != null) {
Andres Morales301ea442015-04-17 09:15:47 -0700768 service.linkToDeath(new GateKeeperDiedRecipient(), 0);
Andres Morales8fa56652015-03-31 09:19:50 -0700769 mGateKeeperService = IGateKeeperService.Stub.asInterface(service);
770 return mGateKeeperService;
771 }
772
773 Slog.e(TAG, "Unable to acquire GateKeeperService");
774 return null;
775 }
776
Amith Yamasani52c489c2012-03-28 11:42:42 -0700777}