2 * Copyright (C) 2012 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com.android.server;
19 import android.app.ActivityManagerNative;
20 import android.app.KeyguardManager;
21 import android.app.Notification;
22 import android.app.NotificationManager;
23 import android.app.PendingIntent;
24 import android.app.admin.DevicePolicyManager;
25 import android.app.backup.BackupManager;
26 import android.app.trust.IStrongAuthTracker;
27 import android.app.trust.TrustManager;
28 import android.content.BroadcastReceiver;
29 import android.content.ContentResolver;
30 import android.content.Context;
31 import android.content.Intent;
32 import android.content.IntentFilter;
33 import android.content.pm.PackageManager;
34 import android.content.pm.UserInfo;
35 import android.content.res.Resources;
37 import static android.Manifest.permission.ACCESS_KEYGUARD_SECURE_STORAGE;
38 import static android.content.Context.KEYGUARD_SERVICE;
39 import static android.content.Context.USER_SERVICE;
40 import static android.Manifest.permission.READ_CONTACTS;
41 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_LOCKOUT;
43 import android.database.sqlite.SQLiteDatabase;
44 import android.os.Binder;
45 import android.os.IBinder;
46 import android.os.RemoteException;
47 import android.os.storage.IMountService;
48 import android.os.ServiceManager;
49 import android.os.SystemProperties;
50 import android.os.UserHandle;
51 import android.os.UserManager;
52 import android.provider.Settings;
53 import android.provider.Settings.Secure;
54 import android.provider.Settings.SettingNotFoundException;
55 import android.security.KeyStore;
56 import android.service.gatekeeper.GateKeeperResponse;
57 import android.service.gatekeeper.IGateKeeperService;
58 import android.text.TextUtils;
59 import android.util.Slog;
61 import com.android.internal.util.ArrayUtils;
62 import com.android.internal.widget.ILockSettings;
63 import com.android.internal.widget.LockPatternUtils;
64 import com.android.internal.widget.VerifyCredentialResponse;
65 import com.android.server.LockSettingsStorage.CredentialHash;
67 import java.nio.charset.StandardCharsets;
68 import java.security.MessageDigest;
69 import java.security.NoSuchAlgorithmException;
71 import java.util.Arrays;
72 import java.util.List;
75 * Keeps the lock pattern/password data and related settings for each user.
76 * Used by LockPatternUtils. Needs to be a service because Settings app also needs
77 * to be able to save lockscreen information for secondary users.
80 public class LockSettingsService extends ILockSettings.Stub {
81 private static final String TAG = "LockSettingsService";
82 private static final String PERMISSION = ACCESS_KEYGUARD_SECURE_STORAGE;
83 private static final Intent ACTION_NULL; // hack to ensure notification shows the bouncer
84 private static final int FBE_ENCRYPTED_NOTIFICATION = 0;
85 private static final boolean DEBUG = false;
87 private final Context mContext;
88 private final LockSettingsStorage mStorage;
89 private final LockSettingsStrongAuth mStrongAuth;
91 private LockPatternUtils mLockPatternUtils;
92 private boolean mFirstCallToVold;
93 private IGateKeeperService mGateKeeperService;
94 private NotificationManager mNotificationManager;
95 private UserManager mUserManager;
98 // Just launch the home screen, which happens anyway
99 ACTION_NULL = new Intent(Intent.ACTION_MAIN);
100 ACTION_NULL.addCategory(Intent.CATEGORY_HOME);
103 private interface CredentialUtil {
104 void setCredential(String credential, String savedCredential, int userId)
105 throws RemoteException;
106 byte[] toHash(String credential, int userId);
107 String adjustForKeystore(String credential);
110 // This class manages life cycle events for encrypted users on File Based Encryption (FBE)
111 // devices. The most basic of these is to show/hide notifications about missing features until
112 // the user unlocks the account and credential-encrypted storage is available.
113 public static final class Lifecycle extends SystemService {
114 private LockSettingsService mLockSettingsService;
116 public Lifecycle(Context context) {
121 public void onStart() {
122 mLockSettingsService = new LockSettingsService(getContext());
123 publishBinderService("lock_settings", mLockSettingsService);
127 public void onBootPhase(int phase) {
128 if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) {
129 mLockSettingsService.maybeShowEncryptionNotifications();
130 } else if (phase == SystemService.PHASE_BOOT_COMPLETED) {
136 public void onUnlockUser(int userHandle) {
137 mLockSettingsService.onUnlockUser(userHandle);
141 public void onCleanupUser(int userHandle) {
142 mLockSettingsService.onCleanupUser(userHandle);
146 public LockSettingsService(Context context) {
148 mStrongAuth = new LockSettingsStrongAuth(context);
151 mLockPatternUtils = new LockPatternUtils(context);
152 mFirstCallToVold = true;
154 IntentFilter filter = new IntentFilter();
155 filter.addAction(Intent.ACTION_USER_ADDED);
156 filter.addAction(Intent.ACTION_USER_STARTING);
157 filter.addAction(Intent.ACTION_USER_REMOVED);
158 filter.addAction(Intent.ACTION_USER_PRESENT);
159 mContext.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, filter, null, null);
161 mStorage = new LockSettingsStorage(context, new LockSettingsStorage.Callback() {
163 public void initialize(SQLiteDatabase db) {
164 // Get the lockscreen default from a system property, if available
165 boolean lockScreenDisable = SystemProperties.getBoolean(
166 "ro.lockscreen.disable.default", false);
167 if (lockScreenDisable) {
168 mStorage.writeKeyValue(db, LockPatternUtils.DISABLE_LOCKSCREEN_KEY, "1", 0);
172 mNotificationManager = (NotificationManager)
173 mContext.getSystemService(Context.NOTIFICATION_SERVICE);
174 mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
178 * If the account is credential-encrypted, show notification requesting the user to unlock
181 private void maybeShowEncryptionNotifications() {
182 final List<UserInfo> users = mUserManager.getUsers();
183 for (int i = 0; i < users.size(); i++) {
184 UserInfo user = users.get(i);
185 UserHandle userHandle = user.getUserHandle();
186 if (!mUserManager.isUserUnlocked(userHandle)) {
187 if (!user.isManagedProfile()) {
188 showEncryptionNotification(userHandle);
190 UserInfo parent = mUserManager.getProfileParent(user.id);
191 if (parent != null && mUserManager.isUserUnlocked(parent.getUserHandle())) {
192 // Only show notifications for managed profiles once their parent
194 showEncryptionNotificationForProfile(userHandle);
201 private void showEncryptionNotificationForProfile(UserHandle user) {
202 Resources r = mContext.getResources();
203 CharSequence title = r.getText(
204 com.android.internal.R.string.user_encrypted_title);
205 CharSequence message = r.getText(
206 com.android.internal.R.string.profile_encrypted_message);
207 CharSequence detail = r.getText(
208 com.android.internal.R.string.profile_encrypted_detail);
210 final KeyguardManager km = (KeyguardManager) mContext.getSystemService(KEYGUARD_SERVICE);
211 final Intent unlockIntent = km.createConfirmDeviceCredentialIntent(null, null, user.getIdentifier());
212 if (unlockIntent == null) {
215 unlockIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
216 PendingIntent intent = PendingIntent.getActivity(mContext, 0, unlockIntent,
217 PendingIntent.FLAG_UPDATE_CURRENT);
219 showEncryptionNotification(user, title, message, detail, intent);
222 private void showEncryptionNotification(UserHandle user) {
223 Resources r = mContext.getResources();
224 CharSequence title = r.getText(
225 com.android.internal.R.string.user_encrypted_title);
226 CharSequence message = r.getText(
227 com.android.internal.R.string.user_encrypted_message);
228 CharSequence detail = r.getText(
229 com.android.internal.R.string.user_encrypted_detail);
231 PendingIntent intent = PendingIntent.getBroadcast(mContext, 0, ACTION_NULL,
232 PendingIntent.FLAG_UPDATE_CURRENT);
234 showEncryptionNotification(user, title, message, detail, intent);
237 private void showEncryptionNotification(UserHandle user, CharSequence title, CharSequence message,
238 CharSequence detail, PendingIntent intent) {
239 if (DEBUG) Slog.v(TAG, "showing encryption notification, user: " + user.getIdentifier());
240 Notification notification = new Notification.Builder(mContext)
241 .setSmallIcon(com.android.internal.R.drawable.ic_user_secure)
245 .setDefaults(0) // please be quiet
246 .setPriority(Notification.PRIORITY_MAX)
247 .setColor(mContext.getColor(
248 com.android.internal.R.color.system_notification_accent_color))
249 .setContentTitle(title)
250 .setContentText(message)
251 .setContentInfo(detail)
252 .setVisibility(Notification.VISIBILITY_PUBLIC)
253 .setContentIntent(intent)
255 mNotificationManager.notifyAsUser(null, FBE_ENCRYPTED_NOTIFICATION, notification, user);
258 public void hideEncryptionNotification(UserHandle userHandle) {
259 if (DEBUG) Slog.v(TAG, "hide encryption notification, user: "+ userHandle.getIdentifier());
260 mNotificationManager.cancelAsUser(null, FBE_ENCRYPTED_NOTIFICATION, userHandle);
263 public void onCleanupUser(int userId) {
264 hideEncryptionNotification(new UserHandle(userId));
267 public void onUnlockUser(int userId) {
268 hideEncryptionNotification(new UserHandle(userId));
270 // Now we have unlocked the parent user we should show notifications
271 // about any profiles that exist.
272 List<UserInfo> profiles = mUserManager.getProfiles(userId);
273 for (int i = 0; i < profiles.size(); i++) {
274 UserInfo profile = profiles.get(i);
275 if (profile.isManagedProfile()) {
276 UserHandle userHandle = profile.getUserHandle();
277 if (!mUserManager.isUserUnlocked(userHandle)) {
278 showEncryptionNotificationForProfile(userHandle);
284 private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
286 public void onReceive(Context context, Intent intent) {
287 if (Intent.ACTION_USER_ADDED.equals(intent.getAction())) {
288 // Notify keystore that a new user was added.
289 final int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
290 final KeyStore ks = KeyStore.getInstance();
291 final UserManager um = (UserManager) mContext.getSystemService(USER_SERVICE);
292 final UserInfo parentInfo = um.getProfileParent(userHandle);
293 final int parentHandle = parentInfo != null ? parentInfo.id : -1;
294 ks.onUserAdded(userHandle, parentHandle);
295 } else if (Intent.ACTION_USER_STARTING.equals(intent.getAction())) {
296 final int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
297 mStorage.prefetchUser(userHandle);
298 } else if (Intent.ACTION_USER_PRESENT.equals(intent.getAction())) {
299 mStrongAuth.reportUnlock(getSendingUserId());
300 } else if (Intent.ACTION_USER_REMOVED.equals(intent.getAction())) {
301 final int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
302 if (userHandle > 0) {
303 removeUser(userHandle);
309 @Override // binder interface
310 public void systemReady() {
313 getGateKeeperService();
314 } catch (RemoteException e) {
315 Slog.e(TAG, "Failure retrieving IGateKeeperService", e);
317 // TODO: maybe skip this for split system user mode.
318 mStorage.prefetchUser(UserHandle.USER_SYSTEM);
321 private void migrateOldData() {
323 // These Settings moved before multi-user was enabled, so we only have to do it for the
325 if (getString("migrated", null, 0) == null) {
326 final ContentResolver cr = mContext.getContentResolver();
327 for (String validSetting : VALID_SETTINGS) {
328 String value = Settings.Secure.getString(cr, validSetting);
330 setString(validSetting, value, 0);
333 // No need to move the password / pattern files. They're already in the right place.
334 setString("migrated", "true", 0);
335 Slog.i(TAG, "Migrated lock settings to new location");
338 // These Settings changed after multi-user was enabled, hence need to be moved per user.
339 if (getString("migrated_user_specific", null, 0) == null) {
340 final UserManager um = (UserManager) mContext.getSystemService(USER_SERVICE);
341 final ContentResolver cr = mContext.getContentResolver();
342 List<UserInfo> users = um.getUsers();
343 for (int user = 0; user < users.size(); user++) {
344 // Migrate owner info
345 final int userId = users.get(user).id;
346 final String OWNER_INFO = Secure.LOCK_SCREEN_OWNER_INFO;
347 String ownerInfo = Settings.Secure.getStringForUser(cr, OWNER_INFO, userId);
348 if (!TextUtils.isEmpty(ownerInfo)) {
349 setString(OWNER_INFO, ownerInfo, userId);
350 Settings.Secure.putStringForUser(cr, OWNER_INFO, "", userId);
353 // Migrate owner info enabled. Note there was a bug where older platforms only
354 // stored this value if the checkbox was toggled at least once. The code detects
355 // this case by handling the exception.
356 final String OWNER_INFO_ENABLED = Secure.LOCK_SCREEN_OWNER_INFO_ENABLED;
359 int ivalue = Settings.Secure.getIntForUser(cr, OWNER_INFO_ENABLED, userId);
360 enabled = ivalue != 0;
361 setLong(OWNER_INFO_ENABLED, enabled ? 1 : 0, userId);
362 } catch (SettingNotFoundException e) {
363 // Setting was never stored. Store it if the string is not empty.
364 if (!TextUtils.isEmpty(ownerInfo)) {
365 setLong(OWNER_INFO_ENABLED, 1, userId);
368 Settings.Secure.putIntForUser(cr, OWNER_INFO_ENABLED, 0, userId);
370 // No need to move the password / pattern files. They're already in the right place.
371 setString("migrated_user_specific", "true", 0);
372 Slog.i(TAG, "Migrated per-user lock settings to new location");
375 // Migrates biometric weak such that the fallback mechanism becomes the primary.
376 if (getString("migrated_biometric_weak", null, 0) == null) {
377 final UserManager um = (UserManager) mContext.getSystemService(USER_SERVICE);
378 List<UserInfo> users = um.getUsers();
379 for (int i = 0; i < users.size(); i++) {
380 int userId = users.get(i).id;
381 long type = getLong(LockPatternUtils.PASSWORD_TYPE_KEY,
382 DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED,
384 long alternateType = getLong(LockPatternUtils.PASSWORD_TYPE_ALTERNATE_KEY,
385 DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED,
387 if (type == DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK) {
388 setLong(LockPatternUtils.PASSWORD_TYPE_KEY,
392 setLong(LockPatternUtils.PASSWORD_TYPE_ALTERNATE_KEY,
393 DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED,
396 setString("migrated_biometric_weak", "true", 0);
397 Slog.i(TAG, "Migrated biometric weak to use the fallback instead");
400 // Migrates lockscreen.disabled. Prior to M, the flag was ignored when more than one
401 // user was present on the system, so if we're upgrading to M and there is more than one
402 // user we disable the flag to remain consistent.
403 if (getString("migrated_lockscreen_disabled", null, 0) == null) {
404 final UserManager um = (UserManager) mContext.getSystemService(USER_SERVICE);
406 final List<UserInfo> users = um.getUsers();
407 final int userCount = users.size();
408 int switchableUsers = 0;
409 for (int i = 0; i < userCount; i++) {
410 if (users.get(i).supportsSwitchTo()) {
415 if (switchableUsers > 1) {
416 for (int i = 0; i < userCount; i++) {
417 int id = users.get(i).id;
419 if (getBoolean(LockPatternUtils.DISABLE_LOCKSCREEN_KEY, false, id)) {
420 setBoolean(LockPatternUtils.DISABLE_LOCKSCREEN_KEY, false, id);
425 setString("migrated_lockscreen_disabled", "true", 0);
426 Slog.i(TAG, "Migrated lockscreen disabled flag");
428 } catch (RemoteException re) {
429 Slog.e(TAG, "Unable to migrate old data", re);
433 private final void checkWritePermission(int userId) {
434 mContext.enforceCallingOrSelfPermission(PERMISSION, "LockSettingsWrite");
437 private final void checkPasswordReadPermission(int userId) {
438 mContext.enforceCallingOrSelfPermission(PERMISSION, "LockSettingsRead");
441 private final void checkReadPermission(String requestedKey, int userId) {
442 final int callingUid = Binder.getCallingUid();
444 for (int i = 0; i < READ_CONTACTS_PROTECTED_SETTINGS.length; i++) {
445 String key = READ_CONTACTS_PROTECTED_SETTINGS[i];
446 if (key.equals(requestedKey) && mContext.checkCallingOrSelfPermission(READ_CONTACTS)
447 != PackageManager.PERMISSION_GRANTED) {
448 throw new SecurityException("uid=" + callingUid
449 + " needs permission " + READ_CONTACTS + " to read "
450 + requestedKey + " for user " + userId);
454 for (int i = 0; i < READ_PASSWORD_PROTECTED_SETTINGS.length; i++) {
455 String key = READ_PASSWORD_PROTECTED_SETTINGS[i];
456 if (key.equals(requestedKey) && mContext.checkCallingOrSelfPermission(PERMISSION)
457 != PackageManager.PERMISSION_GRANTED) {
458 throw new SecurityException("uid=" + callingUid
459 + " needs permission " + PERMISSION + " to read "
460 + requestedKey + " for user " + userId);
466 public void setBoolean(String key, boolean value, int userId) throws RemoteException {
467 checkWritePermission(userId);
468 setStringUnchecked(key, userId, value ? "1" : "0");
472 public void setLong(String key, long value, int userId) throws RemoteException {
473 checkWritePermission(userId);
474 setStringUnchecked(key, userId, Long.toString(value));
478 public void setString(String key, String value, int userId) throws RemoteException {
479 checkWritePermission(userId);
480 setStringUnchecked(key, userId, value);
483 private void setStringUnchecked(String key, int userId, String value) {
484 mStorage.writeKeyValue(key, value, userId);
485 if (ArrayUtils.contains(SETTINGS_TO_BACKUP, key)) {
486 BackupManager.dataChanged("com.android.providers.settings");
491 public boolean getBoolean(String key, boolean defaultValue, int userId) throws RemoteException {
492 checkReadPermission(key, userId);
493 String value = getStringUnchecked(key, null, userId);
494 return TextUtils.isEmpty(value) ?
495 defaultValue : (value.equals("1") || value.equals("true"));
499 public long getLong(String key, long defaultValue, int userId) throws RemoteException {
500 checkReadPermission(key, userId);
502 String value = getStringUnchecked(key, null, userId);
503 return TextUtils.isEmpty(value) ? defaultValue : Long.parseLong(value);
507 public String getString(String key, String defaultValue, int userId) throws RemoteException {
508 checkReadPermission(key, userId);
510 return getStringUnchecked(key, defaultValue, userId);
513 public String getStringUnchecked(String key, String defaultValue, int userId) {
514 if (Settings.Secure.LOCK_PATTERN_ENABLED.equals(key)) {
515 long ident = Binder.clearCallingIdentity();
517 return mLockPatternUtils.isLockPatternEnabled(userId) ? "1" : "0";
519 Binder.restoreCallingIdentity(ident);
523 if (LockPatternUtils.LEGACY_LOCK_PATTERN_ENABLED.equals(key)) {
524 key = Settings.Secure.LOCK_PATTERN_ENABLED;
527 return mStorage.readKeyValue(key, defaultValue, userId);
531 public boolean havePassword(int userId) throws RemoteException {
532 // Do we need a permissions check here?
534 return mStorage.hasPassword(userId);
538 public boolean havePattern(int userId) throws RemoteException {
539 // Do we need a permissions check here?
541 return mStorage.hasPattern(userId);
544 private void setKeystorePassword(String password, int userHandle) {
545 final UserManager um = (UserManager) mContext.getSystemService(USER_SERVICE);
546 final KeyStore ks = KeyStore.getInstance();
548 if (um.getUserInfo(userHandle).isManagedProfile()) {
549 if (mLockPatternUtils.isSeparateProfileChallengeEnabled(userHandle)) {
550 ks.onUserPasswordChanged(userHandle, password);
552 throw new RuntimeException("Can't set keystore password on a profile that "
553 + "doesn't have a profile challenge.");
556 final List<UserInfo> profiles = um.getProfiles(userHandle);
557 for (UserInfo pi : profiles) {
558 // Change password on the given user and all its profiles that don't have
559 // their own profile challenge enabled.
560 if (pi.id == userHandle || (pi.isManagedProfile()
561 && !mLockPatternUtils.isSeparateProfileChallengeEnabled(pi.id))) {
562 ks.onUserPasswordChanged(pi.id, password);
568 private void unlockKeystore(String password, int userHandle) {
569 final UserManager um = (UserManager) mContext.getSystemService(USER_SERVICE);
570 final KeyStore ks = KeyStore.getInstance();
572 if (um.getUserInfo(userHandle).isManagedProfile()) {
573 if (mLockPatternUtils.isSeparateProfileChallengeEnabled(userHandle)) {
574 ks.unlock(userHandle, password);
576 throw new RuntimeException("Can't unlock a profile explicitly if it "
577 + "doesn't have a profile challenge.");
580 final List<UserInfo> profiles = um.getProfiles(userHandle);
581 for (UserInfo pi : profiles) {
582 // Unlock the given user and all its profiles that don't have
583 // their own profile challenge enabled.
584 if (pi.id == userHandle || (pi.isManagedProfile()
585 && !mLockPatternUtils.isSeparateProfileChallengeEnabled(pi.id))) {
586 ks.unlock(pi.id, password);
592 private void unlockUser(int userId, byte[] token, byte[] secret) {
594 ActivityManagerNative.getDefault().unlockUser(userId, token, secret);
595 } catch (RemoteException e) {
596 throw e.rethrowAsRuntimeException();
600 private byte[] getCurrentHandle(int userId) {
601 CredentialHash credential;
602 byte[] currentHandle;
604 int currentHandleType = mStorage.getStoredCredentialType(userId);
605 switch (currentHandleType) {
606 case CredentialHash.TYPE_PATTERN:
607 credential = mStorage.readPatternHash(userId);
608 currentHandle = credential != null
612 case CredentialHash.TYPE_PASSWORD:
613 credential = mStorage.readPasswordHash(userId);
614 currentHandle = credential != null
618 case CredentialHash.TYPE_NONE:
620 currentHandle = null;
625 if (currentHandleType != CredentialHash.TYPE_NONE && currentHandle == null) {
626 Slog.e(TAG, "Stored handle type [" + currentHandleType + "] but no handle available");
629 return currentHandle;
634 public void setLockPattern(String pattern, String savedCredential, int userId)
635 throws RemoteException {
636 byte[] currentHandle = getCurrentHandle(userId);
638 if (pattern == null) {
639 getGateKeeperService().clearSecureUserId(userId);
640 mStorage.writePatternHash(null, userId);
641 setKeystorePassword(null, userId);
642 clearUserKeyProtection(userId);
646 if (currentHandle == null) {
647 if (savedCredential != null) {
648 Slog.w(TAG, "Saved credential provided, but none stored");
650 savedCredential = null;
653 byte[] enrolledHandle = enrollCredential(currentHandle, savedCredential, pattern, userId);
654 if (enrolledHandle != null) {
655 mStorage.writePatternHash(enrolledHandle, userId);
656 setUserKeyProtection(userId, pattern, verifyPattern(pattern, 0, userId));
658 throw new RemoteException("Failed to enroll pattern");
664 public void setLockPassword(String password, String savedCredential, int userId)
665 throws RemoteException {
666 byte[] currentHandle = getCurrentHandle(userId);
668 if (password == null) {
669 getGateKeeperService().clearSecureUserId(userId);
670 mStorage.writePasswordHash(null, userId);
671 setKeystorePassword(null, userId);
672 clearUserKeyProtection(userId);
676 if (currentHandle == null) {
677 if (savedCredential != null) {
678 Slog.w(TAG, "Saved credential provided, but none stored");
680 savedCredential = null;
683 byte[] enrolledHandle = enrollCredential(currentHandle, savedCredential, password, userId);
684 if (enrolledHandle != null) {
685 mStorage.writePasswordHash(enrolledHandle, userId);
686 setUserKeyProtection(userId, password, verifyPassword(password, 0, userId));
688 throw new RemoteException("Failed to enroll password");
692 private byte[] enrollCredential(byte[] enrolledHandle,
693 String enrolledCredential, String toEnroll, int userId)
694 throws RemoteException {
695 checkWritePermission(userId);
696 byte[] enrolledCredentialBytes = enrolledCredential == null
698 : enrolledCredential.getBytes();
699 byte[] toEnrollBytes = toEnroll == null
701 : toEnroll.getBytes();
702 GateKeeperResponse response = getGateKeeperService().enroll(userId, enrolledHandle,
703 enrolledCredentialBytes, toEnrollBytes);
705 if (response == null) {
709 byte[] hash = response.getPayload();
711 setKeystorePassword(toEnroll, userId);
714 Slog.e(TAG, "Throttled while enrolling a password");
719 private void setUserKeyProtection(int userId, String credential, VerifyCredentialResponse vcr)
720 throws RemoteException {
722 throw new RemoteException("Null response verifying a credential we just set");
724 if (vcr.getResponseCode() != VerifyCredentialResponse.RESPONSE_OK) {
725 throw new RemoteException("Non-OK response verifying a credential we just set: "
726 + vcr.getResponseCode());
728 byte[] token = vcr.getPayload();
730 throw new RemoteException("Empty payload verifying a credential we just set");
732 changeUserKey(userId, token, secretFromCredential(credential));
735 private void clearUserKeyProtection(int userId) throws RemoteException {
736 changeUserKey(userId, null, null);
739 private static byte[] secretFromCredential(String credential) throws RemoteException {
741 MessageDigest digest = MessageDigest.getInstance("SHA-512");
742 // Personalize the hash
743 byte[] personalization = "Android FBE credential hash"
744 .getBytes(StandardCharsets.UTF_8);
745 // Pad it to the block size of the hash function
746 personalization = Arrays.copyOf(personalization, 128);
747 digest.update(personalization);
748 digest.update(credential.getBytes(StandardCharsets.UTF_8));
749 return digest.digest();
750 } catch (NoSuchAlgorithmException e) {
751 throw new RuntimeException("NoSuchAlgorithmException for SHA-512");
755 private void changeUserKey(int userId, byte[] token, byte[] secret)
756 throws RemoteException {
757 final UserInfo userInfo = UserManager.get(mContext).getUserInfo(userId);
758 getMountService().changeUserKey(userId, userInfo.serialNumber, token, null, secret);
762 public VerifyCredentialResponse checkPattern(String pattern, int userId) throws RemoteException {
763 return doVerifyPattern(pattern, false, 0, userId);
767 public VerifyCredentialResponse verifyPattern(String pattern, long challenge, int userId)
768 throws RemoteException {
769 return doVerifyPattern(pattern, true, challenge, userId);
772 private VerifyCredentialResponse doVerifyPattern(String pattern, boolean hasChallenge,
773 long challenge, int userId) throws RemoteException {
774 checkPasswordReadPermission(userId);
775 CredentialHash storedHash = mStorage.readPatternHash(userId);
776 boolean shouldReEnrollBaseZero = storedHash != null && storedHash.isBaseZeroPattern;
778 String patternToVerify;
779 if (shouldReEnrollBaseZero) {
780 patternToVerify = LockPatternUtils.patternStringToBaseZero(pattern);
782 patternToVerify = pattern;
785 VerifyCredentialResponse response = verifyCredential(userId, storedHash, patternToVerify,
786 hasChallenge, challenge,
787 new CredentialUtil() {
789 public void setCredential(String pattern, String oldPattern, int userId)
790 throws RemoteException {
791 setLockPattern(pattern, oldPattern, userId);
795 public byte[] toHash(String pattern, int userId) {
796 return LockPatternUtils.patternToHash(
797 LockPatternUtils.stringToPattern(pattern));
801 public String adjustForKeystore(String pattern) {
802 return LockPatternUtils.patternStringToBaseZero(pattern);
807 if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK
808 && shouldReEnrollBaseZero) {
809 setLockPattern(pattern, patternToVerify, userId);
817 public VerifyCredentialResponse checkPassword(String password, int userId)
818 throws RemoteException {
819 return doVerifyPassword(password, false, 0, userId);
823 public VerifyCredentialResponse verifyPassword(String password, long challenge, int userId)
824 throws RemoteException {
825 return doVerifyPassword(password, true, challenge, userId);
828 private VerifyCredentialResponse doVerifyPassword(String password, boolean hasChallenge,
829 long challenge, int userId) throws RemoteException {
830 checkPasswordReadPermission(userId);
831 CredentialHash storedHash = mStorage.readPasswordHash(userId);
832 return verifyCredential(userId, storedHash, password, hasChallenge, challenge,
833 new CredentialUtil() {
835 public void setCredential(String password, String oldPassword, int userId)
836 throws RemoteException {
837 setLockPassword(password, oldPassword, userId);
841 public byte[] toHash(String password, int userId) {
842 return mLockPatternUtils.passwordToHash(password, userId);
846 public String adjustForKeystore(String password) {
853 private VerifyCredentialResponse verifyCredential(int userId, CredentialHash storedHash,
854 String credential, boolean hasChallenge, long challenge, CredentialUtil credentialUtil)
855 throws RemoteException {
856 if ((storedHash == null || storedHash.hash.length == 0) && TextUtils.isEmpty(credential)) {
857 // don't need to pass empty credentials to GateKeeper
858 return VerifyCredentialResponse.OK;
861 if (TextUtils.isEmpty(credential)) {
862 return VerifyCredentialResponse.ERROR;
865 if (storedHash.version == CredentialHash.VERSION_LEGACY) {
866 byte[] hash = credentialUtil.toHash(credential, userId);
867 if (Arrays.equals(hash, storedHash.hash)) {
868 unlockKeystore(credentialUtil.adjustForKeystore(credential), userId);
870 // Users with legacy credentials don't have credential-backed
871 // FBE keys, so just pass through a fake token/secret
872 Slog.i(TAG, "Unlocking user with fake token: " + userId);
873 final byte[] fakeToken = String.valueOf(userId).getBytes();
874 unlockUser(userId, fakeToken, fakeToken);
876 // migrate credential to GateKeeper
877 credentialUtil.setCredential(credential, null, userId);
879 return VerifyCredentialResponse.OK;
881 // Fall through to get the auth token. Technically this should never happen,
882 // as a user that had a legacy credential would have to unlock their device
883 // before getting to a flow with a challenge, but supporting for consistency.
885 return VerifyCredentialResponse.ERROR;
889 VerifyCredentialResponse response;
890 boolean shouldReEnroll = false;
891 GateKeeperResponse gateKeeperResponse = getGateKeeperService()
892 .verifyChallenge(userId, challenge, storedHash.hash, credential.getBytes());
893 int responseCode = gateKeeperResponse.getResponseCode();
894 if (responseCode == GateKeeperResponse.RESPONSE_RETRY) {
895 response = new VerifyCredentialResponse(gateKeeperResponse.getTimeout());
896 } else if (responseCode == GateKeeperResponse.RESPONSE_OK) {
897 byte[] token = gateKeeperResponse.getPayload();
899 // something's wrong if there's no payload with a challenge
900 Slog.e(TAG, "verifyChallenge response had no associated payload");
901 response = VerifyCredentialResponse.ERROR;
903 shouldReEnroll = gateKeeperResponse.getShouldReEnroll();
904 response = new VerifyCredentialResponse(token);
907 response = VerifyCredentialResponse.ERROR;
910 if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK) {
911 // credential has matched
912 unlockKeystore(credential, userId);
914 Slog.i(TAG, "Unlocking user " + userId +
915 " with token length " + response.getPayload().length);
916 unlockUser(userId, response.getPayload(), secretFromCredential(credential));
918 UserInfo info = UserManager.get(mContext).getUserInfo(userId);
919 if (mLockPatternUtils.isSeparateProfileChallengeEnabled(userId)) {
920 TrustManager trustManager =
921 (TrustManager) mContext.getSystemService(Context.TRUST_SERVICE);
922 trustManager.setDeviceLockedForUser(userId, false);
924 if (shouldReEnroll) {
925 credentialUtil.setCredential(credential, credential, userId);
927 } else if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_RETRY) {
928 if (response.getTimeout() > 0) {
929 requireStrongAuth(STRONG_AUTH_REQUIRED_AFTER_LOCKOUT, userId);
937 public boolean checkVoldPassword(int userId) throws RemoteException {
938 if (!mFirstCallToVold) {
941 mFirstCallToVold = false;
943 checkPasswordReadPermission(userId);
945 // There's no guarantee that this will safely connect, but if it fails
946 // we will simply show the lock screen when we shouldn't, so relatively
947 // benign. There is an outside chance something nasty would happen if
948 // this service restarted before vold stales out the password in this
949 // case. The nastiness is limited to not showing the lock screen when
950 // we should, within the first minute of decrypting the phone if this
951 // service can't connect to vold, it restarts, and then the new instance
952 // does successfully connect.
953 final IMountService service = getMountService();
954 String password = service.getPassword();
955 service.clearPassword();
956 if (password == null) {
961 if (mLockPatternUtils.isLockPatternEnabled(userId)) {
962 if (checkPattern(password, userId).getResponseCode()
963 == GateKeeperResponse.RESPONSE_OK) {
967 } catch (Exception e) {
971 if (mLockPatternUtils.isLockPasswordEnabled(userId)) {
972 if (checkPassword(password, userId).getResponseCode()
973 == GateKeeperResponse.RESPONSE_OK) {
977 } catch (Exception e) {
983 private void removeUser(int userId) {
984 mStorage.removeUser(userId);
985 mStrongAuth.removeUser(userId);
987 final KeyStore ks = KeyStore.getInstance();
988 ks.onUserRemoved(userId);
991 final IGateKeeperService gk = getGateKeeperService();
993 gk.clearSecureUserId(userId);
995 } catch (RemoteException ex) {
996 Slog.w(TAG, "unable to clear GK secure user id");
1001 public void registerStrongAuthTracker(IStrongAuthTracker tracker) {
1002 checkPasswordReadPermission(UserHandle.USER_ALL);
1003 mStrongAuth.registerStrongAuthTracker(tracker);
1007 public void unregisterStrongAuthTracker(IStrongAuthTracker tracker) {
1008 checkPasswordReadPermission(UserHandle.USER_ALL);
1009 mStrongAuth.unregisterStrongAuthTracker(tracker);
1013 public void requireStrongAuth(int strongAuthReason, int userId) {
1014 checkWritePermission(userId);
1015 mStrongAuth.requireStrongAuth(strongAuthReason, userId);
1018 private static final String[] VALID_SETTINGS = new String[] {
1019 LockPatternUtils.LOCKOUT_PERMANENT_KEY,
1020 LockPatternUtils.LOCKOUT_ATTEMPT_DEADLINE,
1021 LockPatternUtils.PATTERN_EVER_CHOSEN_KEY,
1022 LockPatternUtils.PASSWORD_TYPE_KEY,
1023 LockPatternUtils.PASSWORD_TYPE_ALTERNATE_KEY,
1024 LockPatternUtils.LOCK_PASSWORD_SALT_KEY,
1025 LockPatternUtils.DISABLE_LOCKSCREEN_KEY,
1026 LockPatternUtils.LOCKSCREEN_OPTIONS,
1027 LockPatternUtils.LOCKSCREEN_BIOMETRIC_WEAK_FALLBACK,
1028 LockPatternUtils.BIOMETRIC_WEAK_EVER_CHOSEN_KEY,
1029 LockPatternUtils.LOCKSCREEN_POWER_BUTTON_INSTANTLY_LOCKS,
1030 LockPatternUtils.PASSWORD_HISTORY_KEY,
1031 Secure.LOCK_PATTERN_ENABLED,
1032 Secure.LOCK_BIOMETRIC_WEAK_FLAGS,
1033 Secure.LOCK_PATTERN_VISIBLE,
1034 Secure.LOCK_PATTERN_TACTILE_FEEDBACK_ENABLED
1037 // Reading these settings needs the contacts permission
1038 private static final String[] READ_CONTACTS_PROTECTED_SETTINGS = new String[] {
1039 Secure.LOCK_SCREEN_OWNER_INFO_ENABLED,
1040 Secure.LOCK_SCREEN_OWNER_INFO
1043 // Reading these settings needs the same permission as checking the password
1044 private static final String[] READ_PASSWORD_PROTECTED_SETTINGS = new String[] {
1045 LockPatternUtils.LOCK_PASSWORD_SALT_KEY,
1046 LockPatternUtils.PASSWORD_HISTORY_KEY,
1047 LockPatternUtils.PASSWORD_TYPE_KEY,
1050 private static final String[] SETTINGS_TO_BACKUP = new String[] {
1051 Secure.LOCK_SCREEN_OWNER_INFO_ENABLED,
1052 Secure.LOCK_SCREEN_OWNER_INFO
1055 private IMountService getMountService() {
1056 final IBinder service = ServiceManager.getService("mount");
1057 if (service != null) {
1058 return IMountService.Stub.asInterface(service);
1063 private class GateKeeperDiedRecipient implements IBinder.DeathRecipient {
1065 public void binderDied() {
1066 mGateKeeperService.asBinder().unlinkToDeath(this, 0);
1067 mGateKeeperService = null;
1071 private synchronized IGateKeeperService getGateKeeperService()
1072 throws RemoteException {
1073 if (mGateKeeperService != null) {
1074 return mGateKeeperService;
1077 final IBinder service =
1078 ServiceManager.getService("android.service.gatekeeper.IGateKeeperService");
1079 if (service != null) {
1080 service.linkToDeath(new GateKeeperDiedRecipient(), 0);
1081 mGateKeeperService = IGateKeeperService.Stub.asInterface(service);
1082 return mGateKeeperService;
1085 Slog.e(TAG, "Unable to acquire GateKeeperService");