blob: c318140ae7e8c3c0925927070d009e79a4e182b4 [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
Jeff Sharkey8924e872015-11-30 12:52:10 -070019import android.app.ActivityManagerNative;
Kenny Guyb1b30262016-02-09 16:02:35 +000020import android.app.KeyguardManager;
Jim Miller4f93c582016-01-27 19:05:43 -080021import android.app.Notification;
22import android.app.NotificationManager;
23import android.app.PendingIntent;
Adrian Roos230635e2015-01-07 20:50:29 +010024import android.app.admin.DevicePolicyManager;
Amith Yamasani072543f2015-02-13 11:09:45 -080025import android.app.backup.BackupManager;
Adrian Roosb5e47222015-08-14 15:53:06 -070026import android.app.trust.IStrongAuthTracker;
Clara Bayarri56878a92015-10-29 15:43:55 +000027import android.app.trust.TrustManager;
Robin Leef0246a82014-08-13 09:50:25 +010028import android.content.BroadcastReceiver;
Amith Yamasani52c489c2012-03-28 11:42:42 -070029import android.content.ContentResolver;
Amith Yamasani52c489c2012-03-28 11:42:42 -070030import android.content.Context;
Robin Leef0246a82014-08-13 09:50:25 +010031import android.content.Intent;
32import android.content.IntentFilter;
Jim Miller158fe192013-04-17 15:23:55 -070033import android.content.pm.PackageManager;
Jim Miller187ec582013-04-15 18:27:54 -070034import android.content.pm.UserInfo;
Jim Miller4f93c582016-01-27 19:05:43 -080035import android.content.res.Resources;
36
Adrian Roos261d5ab2014-10-29 14:42:38 +010037import static android.Manifest.permission.ACCESS_KEYGUARD_SECURE_STORAGE;
Kenny Guyb1b30262016-02-09 16:02:35 +000038import static android.content.Context.KEYGUARD_SERVICE;
Jim Miller187ec582013-04-15 18:27:54 -070039import static android.content.Context.USER_SERVICE;
Svetoslav Ganov6d2c0e52015-06-23 16:33:36 +000040import static android.Manifest.permission.READ_CONTACTS;
Adrian Roos873010d2015-08-25 15:59:00 -070041import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_LOCKOUT;
42
Amith Yamasani52c489c2012-03-28 11:42:42 -070043import android.database.sqlite.SQLiteDatabase;
Amith Yamasani52c489c2012-03-28 11:42:42 -070044import android.os.Binder;
Paul Lawrence945490c2014-03-27 16:37:28 +000045import android.os.IBinder;
Amith Yamasani52c489c2012-03-28 11:42:42 -070046import android.os.RemoteException;
Paul Lawrence945490c2014-03-27 16:37:28 +000047import android.os.storage.IMountService;
48import android.os.ServiceManager;
Amith Yamasanid1645f82012-06-12 11:53:26 -070049import android.os.SystemProperties;
Dianne Hackbornf02b60a2012-08-16 10:48:27 -070050import android.os.UserHandle;
Jim Miller187ec582013-04-15 18:27:54 -070051import android.os.UserManager;
Amith Yamasani52c489c2012-03-28 11:42:42 -070052import android.provider.Settings;
53import android.provider.Settings.Secure;
Jim Miller187ec582013-04-15 18:27:54 -070054import android.provider.Settings.SettingNotFoundException;
Jim Millerde1af082013-09-11 14:58:26 -070055import android.security.KeyStore;
Andres Morales23974272015-05-14 22:42:26 -070056import android.service.gatekeeper.GateKeeperResponse;
Andres Morales8fa56652015-03-31 09:19:50 -070057import android.service.gatekeeper.IGateKeeperService;
Amith Yamasani52c489c2012-03-28 11:42:42 -070058import android.text.TextUtils;
59import android.util.Slog;
60
Amith Yamasani072543f2015-02-13 11:09:45 -080061import com.android.internal.util.ArrayUtils;
Jeff Sharkey7a96c392012-11-15 14:01:46 -080062import com.android.internal.widget.ILockSettings;
63import com.android.internal.widget.LockPatternUtils;
Andres Morales23974272015-05-14 22:42:26 -070064import com.android.internal.widget.VerifyCredentialResponse;
Andres Morales8fa56652015-03-31 09:19:50 -070065import com.android.server.LockSettingsStorage.CredentialHash;
Jeff Sharkey7a96c392012-11-15 14:01:46 -080066
Paul Crowleyfaeb3eb2016-02-08 15:58:29 +000067import java.nio.charset.StandardCharsets;
68import java.security.MessageDigest;
69import java.security.NoSuchAlgorithmException;
70
Amith Yamasani52c489c2012-03-28 11:42:42 -070071import java.util.Arrays;
Jim Miller187ec582013-04-15 18:27:54 -070072import java.util.List;
Amith Yamasani52c489c2012-03-28 11:42:42 -070073
74/**
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.
78 * @hide
79 */
80public class LockSettingsService extends ILockSettings.Stub {
Amith Yamasani52c489c2012-03-28 11:42:42 -070081 private static final String TAG = "LockSettingsService";
Jim Miller4f93c582016-01-27 19:05:43 -080082 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;
Amith Yamasani52c489c2012-03-28 11:42:42 -070086
Amith Yamasani52c489c2012-03-28 11:42:42 -070087 private final Context mContext;
Adrian Roos261d5ab2014-10-29 14:42:38 +010088 private final LockSettingsStorage mStorage;
Rakesh Iyera7aa4d62016-01-19 17:27:23 -080089 private final LockSettingsStrongAuth mStrongAuth;
Adrian Roos261d5ab2014-10-29 14:42:38 +010090
Jim Millerde1af082013-09-11 14:58:26 -070091 private LockPatternUtils mLockPatternUtils;
Paul Lawrence945490c2014-03-27 16:37:28 +000092 private boolean mFirstCallToVold;
Andres Morales8fa56652015-03-31 09:19:50 -070093 private IGateKeeperService mGateKeeperService;
Jim Miller4f93c582016-01-27 19:05:43 -080094 private NotificationManager mNotificationManager;
95 private UserManager mUserManager;
96
97 static {
98 // Just launch the home screen, which happens anyway
99 ACTION_NULL = new Intent(Intent.ACTION_MAIN);
100 ACTION_NULL.addCategory(Intent.CATEGORY_HOME);
101 }
Amith Yamasani52c489c2012-03-28 11:42:42 -0700102
Andres Morales23974272015-05-14 22:42:26 -0700103 private interface CredentialUtil {
104 void setCredential(String credential, String savedCredential, int userId)
105 throws RemoteException;
106 byte[] toHash(String credential, int userId);
Andres Morales59ef1262015-06-26 13:56:39 -0700107 String adjustForKeystore(String credential);
Andres Morales23974272015-05-14 22:42:26 -0700108 }
109
Jim Miller4f93c582016-01-27 19:05:43 -0800110 // 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;
115
116 public Lifecycle(Context context) {
117 super(context);
118 }
119
120 @Override
121 public void onStart() {
122 mLockSettingsService = new LockSettingsService(getContext());
123 publishBinderService("lock_settings", mLockSettingsService);
124 }
125
126 @Override
127 public void onBootPhase(int phase) {
128 if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) {
Kenny Guyb1b30262016-02-09 16:02:35 +0000129 mLockSettingsService.maybeShowEncryptionNotifications();
Jim Miller4f93c582016-01-27 19:05:43 -0800130 } else if (phase == SystemService.PHASE_BOOT_COMPLETED) {
131 // TODO
132 }
133 }
134
135 @Override
136 public void onUnlockUser(int userHandle) {
137 mLockSettingsService.onUnlockUser(userHandle);
138 }
139
140 @Override
141 public void onCleanupUser(int userHandle) {
142 mLockSettingsService.onCleanupUser(userHandle);
143 }
144 }
145
Amith Yamasani52c489c2012-03-28 11:42:42 -0700146 public LockSettingsService(Context context) {
147 mContext = context;
Rakesh Iyera7aa4d62016-01-19 17:27:23 -0800148 mStrongAuth = new LockSettingsStrongAuth(context);
Amith Yamasani52c489c2012-03-28 11:42:42 -0700149 // Open the database
Jim Millerde1af082013-09-11 14:58:26 -0700150
151 mLockPatternUtils = new LockPatternUtils(context);
Paul Lawrence945490c2014-03-27 16:37:28 +0000152 mFirstCallToVold = true;
Robin Leef0246a82014-08-13 09:50:25 +0100153
154 IntentFilter filter = new IntentFilter();
155 filter.addAction(Intent.ACTION_USER_ADDED);
Adrian Roos3dcae682014-10-29 14:43:56 +0100156 filter.addAction(Intent.ACTION_USER_STARTING);
Adrian Roosdb0f76e2015-01-07 22:19:38 +0100157 filter.addAction(Intent.ACTION_USER_REMOVED);
Adrian Roosb5e47222015-08-14 15:53:06 -0700158 filter.addAction(Intent.ACTION_USER_PRESENT);
Robin Leef0246a82014-08-13 09:50:25 +0100159 mContext.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, filter, null, null);
Adrian Roos261d5ab2014-10-29 14:42:38 +0100160
161 mStorage = new LockSettingsStorage(context, new LockSettingsStorage.Callback() {
162 @Override
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);
169 }
170 }
171 });
Jim Miller4f93c582016-01-27 19:05:43 -0800172 mNotificationManager = (NotificationManager)
173 mContext.getSystemService(Context.NOTIFICATION_SERVICE);
174 mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
175 }
176
177 /**
178 * If the account is credential-encrypted, show notification requesting the user to unlock
179 * the device.
180 */
Kenny Guyb1b30262016-02-09 16:02:35 +0000181 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);
189 } else {
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
193 // user is unlocked.
194 showEncryptionNotificationForProfile(userHandle);
195 }
Jim Miller4f93c582016-01-27 19:05:43 -0800196 }
197 }
Jim Miller4f93c582016-01-27 19:05:43 -0800198 }
199 }
200
Kenny Guyb1b30262016-02-09 16:02:35 +0000201 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);
209
210 final KeyguardManager km = (KeyguardManager) mContext.getSystemService(KEYGUARD_SERVICE);
211 final Intent unlockIntent = km.createConfirmDeviceCredentialIntent(null, null, user.getIdentifier());
212 if (unlockIntent == null) {
213 return;
214 }
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);
218
219 showEncryptionNotification(user, title, message, detail, intent);
220 }
221
Jim Miller4f93c582016-01-27 19:05:43 -0800222 private void showEncryptionNotification(UserHandle user) {
Jim Miller4f93c582016-01-27 19:05:43 -0800223 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);
230
231 PendingIntent intent = PendingIntent.getBroadcast(mContext, 0, ACTION_NULL,
232 PendingIntent.FLAG_UPDATE_CURRENT);
233
Kenny Guyb1b30262016-02-09 16:02:35 +0000234 showEncryptionNotification(user, title, message, detail, intent);
235 }
236
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());
Jim Miller4f93c582016-01-27 19:05:43 -0800240 Notification notification = new Notification.Builder(mContext)
Jim Millerb1135b72016-02-02 17:08:56 -0800241 .setSmallIcon(com.android.internal.R.drawable.ic_user_secure)
Jim Miller4f93c582016-01-27 19:05:43 -0800242 .setWhen(0)
243 .setOngoing(true)
244 .setTicker(title)
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)
254 .build();
255 mNotificationManager.notifyAsUser(null, FBE_ENCRYPTED_NOTIFICATION, notification, user);
256 }
257
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);
261 }
262
263 public void onCleanupUser(int userId) {
264 hideEncryptionNotification(new UserHandle(userId));
265 }
266
Kenny Guyb1b30262016-02-09 16:02:35 +0000267 public void onUnlockUser(int userId) {
268 hideEncryptionNotification(new UserHandle(userId));
269
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);
279 }
280 }
281 }
Amith Yamasani52c489c2012-03-28 11:42:42 -0700282 }
283
Robin Leef0246a82014-08-13 09:50:25 +0100284 private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
285 @Override
286 public void onReceive(Context context, Intent intent) {
Robin Lee1096cf82014-09-01 16:52:47 +0100287 if (Intent.ACTION_USER_ADDED.equals(intent.getAction())) {
Chad Brubaker83ce0952015-05-12 13:00:02 -0700288 // Notify keystore that a new user was added.
Robin Leef0246a82014-08-13 09:50:25 +0100289 final int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
Robin Lee49d810c2014-09-23 13:50:22 +0100290 final KeyStore ks = KeyStore.getInstance();
Robin Leef0246a82014-08-13 09:50:25 +0100291 final UserManager um = (UserManager) mContext.getSystemService(USER_SERVICE);
292 final UserInfo parentInfo = um.getProfileParent(userHandle);
Chad Brubaker83ce0952015-05-12 13:00:02 -0700293 final int parentHandle = parentInfo != null ? parentInfo.id : -1;
294 ks.onUserAdded(userHandle, parentHandle);
Adrian Roos3dcae682014-10-29 14:43:56 +0100295 } else if (Intent.ACTION_USER_STARTING.equals(intent.getAction())) {
296 final int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
297 mStorage.prefetchUser(userHandle);
Adrian Roosb5e47222015-08-14 15:53:06 -0700298 } else if (Intent.ACTION_USER_PRESENT.equals(intent.getAction())) {
299 mStrongAuth.reportUnlock(getSendingUserId());
Adrian Roosdb0f76e2015-01-07 22:19:38 +0100300 } 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);
304 }
Robin Leef0246a82014-08-13 09:50:25 +0100305 }
306 }
307 };
308
Jim Miller4f93c582016-01-27 19:05:43 -0800309 @Override // binder interface
Amith Yamasani52c489c2012-03-28 11:42:42 -0700310 public void systemReady() {
311 migrateOldData();
Andres Morales301ea442015-04-17 09:15:47 -0700312 try {
313 getGateKeeperService();
314 } catch (RemoteException e) {
315 Slog.e(TAG, "Failure retrieving IGateKeeperService", e);
316 }
Xiaohui Chen7c696362015-09-16 09:56:14 -0700317 // TODO: maybe skip this for split system user mode.
318 mStorage.prefetchUser(UserHandle.USER_SYSTEM);
Amith Yamasani52c489c2012-03-28 11:42:42 -0700319 }
320
321 private void migrateOldData() {
322 try {
Jim Miller187ec582013-04-15 18:27:54 -0700323 // These Settings moved before multi-user was enabled, so we only have to do it for the
324 // root user.
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);
329 if (value != null) {
330 setString(validSetting, value, 0);
331 }
332 }
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");
Amith Yamasani52c489c2012-03-28 11:42:42 -0700336 }
337
Jim Miller187ec582013-04-15 18:27:54 -0700338 // 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++) {
Jim Miller2d8ecf9d2013-04-22 17:17:03 -0700344 // 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 (ownerInfo != null) {
349 setString(OWNER_INFO, ownerInfo, userId);
350 Settings.Secure.putStringForUser(cr, ownerInfo, "", userId);
351 }
Jim Miller187ec582013-04-15 18:27:54 -0700352
Jim Miller2d8ecf9d2013-04-22 17:17:03 -0700353 // 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;
357 boolean enabled;
358 try {
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);
Jim Miller187ec582013-04-15 18:27:54 -0700366 }
367 }
Jim Miller2d8ecf9d2013-04-22 17:17:03 -0700368 Settings.Secure.putIntForUser(cr, OWNER_INFO_ENABLED, 0, userId);
Amith Yamasani52c489c2012-03-28 11:42:42 -0700369 }
Jim Miller187ec582013-04-15 18:27:54 -0700370 // 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");
Amith Yamasani52c489c2012-03-28 11:42:42 -0700373 }
Adrian Roos230635e2015-01-07 20:50:29 +0100374
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,
383 userId);
384 long alternateType = getLong(LockPatternUtils.PASSWORD_TYPE_ALTERNATE_KEY,
385 DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED,
386 userId);
387 if (type == DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK) {
388 setLong(LockPatternUtils.PASSWORD_TYPE_KEY,
389 alternateType,
390 userId);
391 }
392 setLong(LockPatternUtils.PASSWORD_TYPE_ALTERNATE_KEY,
393 DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED,
394 userId);
395 }
396 setString("migrated_biometric_weak", "true", 0);
397 Slog.i(TAG, "Migrated biometric weak to use the fallback instead");
398 }
Adrian Roos43830582015-04-21 16:04:43 -0700399
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);
405
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()) {
411 switchableUsers++;
412 }
413 }
414
415 if (switchableUsers > 1) {
416 for (int i = 0; i < userCount; i++) {
417 int id = users.get(i).id;
418
419 if (getBoolean(LockPatternUtils.DISABLE_LOCKSCREEN_KEY, false, id)) {
420 setBoolean(LockPatternUtils.DISABLE_LOCKSCREEN_KEY, false, id);
421 }
422 }
423 }
424
425 setString("migrated_lockscreen_disabled", "true", 0);
426 Slog.i(TAG, "Migrated lockscreen disabled flag");
427 }
Amith Yamasani52c489c2012-03-28 11:42:42 -0700428 } catch (RemoteException re) {
Jim Miller187ec582013-04-15 18:27:54 -0700429 Slog.e(TAG, "Unable to migrate old data", re);
Amith Yamasani52c489c2012-03-28 11:42:42 -0700430 }
431 }
432
Jim Miller5ecd8112013-01-09 18:50:26 -0800433 private final void checkWritePermission(int userId) {
Jim Miller505329b2013-11-08 13:25:36 -0800434 mContext.enforceCallingOrSelfPermission(PERMISSION, "LockSettingsWrite");
Amith Yamasani52c489c2012-03-28 11:42:42 -0700435 }
436
Jim Miller5ecd8112013-01-09 18:50:26 -0800437 private final void checkPasswordReadPermission(int userId) {
Jim Miller505329b2013-11-08 13:25:36 -0800438 mContext.enforceCallingOrSelfPermission(PERMISSION, "LockSettingsRead");
Amith Yamasani52c489c2012-03-28 11:42:42 -0700439 }
440
Jim Miller158fe192013-04-17 15:23:55 -0700441 private final void checkReadPermission(String requestedKey, int userId) {
Amith Yamasani52c489c2012-03-28 11:42:42 -0700442 final int callingUid = Binder.getCallingUid();
Adrian Roos001b00d2015-02-24 17:08:48 +0100443
Svetoslav Ganov6d2c0e52015-06-23 16:33:36 +0000444 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)
Jim Miller158fe192013-04-17 15:23:55 -0700447 != PackageManager.PERMISSION_GRANTED) {
448 throw new SecurityException("uid=" + callingUid
Svetoslav Ganov6d2c0e52015-06-23 16:33:36 +0000449 + " needs permission " + READ_CONTACTS + " to read "
Jim Miller158fe192013-04-17 15:23:55 -0700450 + requestedKey + " for user " + userId);
451 }
Amith Yamasani52c489c2012-03-28 11:42:42 -0700452 }
Adrian Roos001b00d2015-02-24 17:08:48 +0100453
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);
461 }
462 }
Amith Yamasani52c489c2012-03-28 11:42:42 -0700463 }
464
465 @Override
466 public void setBoolean(String key, boolean value, int userId) throws RemoteException {
467 checkWritePermission(userId);
Adrian Roos261d5ab2014-10-29 14:42:38 +0100468 setStringUnchecked(key, userId, value ? "1" : "0");
Amith Yamasani52c489c2012-03-28 11:42:42 -0700469 }
470
471 @Override
472 public void setLong(String key, long value, int userId) throws RemoteException {
473 checkWritePermission(userId);
Adrian Roos261d5ab2014-10-29 14:42:38 +0100474 setStringUnchecked(key, userId, Long.toString(value));
Amith Yamasani52c489c2012-03-28 11:42:42 -0700475 }
476
477 @Override
478 public void setString(String key, String value, int userId) throws RemoteException {
479 checkWritePermission(userId);
Adrian Roos261d5ab2014-10-29 14:42:38 +0100480 setStringUnchecked(key, userId, value);
481 }
Amith Yamasani52c489c2012-03-28 11:42:42 -0700482
Adrian Roos261d5ab2014-10-29 14:42:38 +0100483 private void setStringUnchecked(String key, int userId, String value) {
484 mStorage.writeKeyValue(key, value, userId);
Amith Yamasani072543f2015-02-13 11:09:45 -0800485 if (ArrayUtils.contains(SETTINGS_TO_BACKUP, key)) {
486 BackupManager.dataChanged("com.android.providers.settings");
487 }
Amith Yamasani52c489c2012-03-28 11:42:42 -0700488 }
489
490 @Override
491 public boolean getBoolean(String key, boolean defaultValue, int userId) throws RemoteException {
Jim Miller158fe192013-04-17 15:23:55 -0700492 checkReadPermission(key, userId);
Adrian Roos9dd16eb2015-01-08 16:20:49 +0100493 String value = getStringUnchecked(key, null, userId);
Amith Yamasani52c489c2012-03-28 11:42:42 -0700494 return TextUtils.isEmpty(value) ?
495 defaultValue : (value.equals("1") || value.equals("true"));
496 }
497
498 @Override
499 public long getLong(String key, long defaultValue, int userId) throws RemoteException {
Jim Miller158fe192013-04-17 15:23:55 -0700500 checkReadPermission(key, userId);
Amith Yamasani52c489c2012-03-28 11:42:42 -0700501
Adrian Roos9dd16eb2015-01-08 16:20:49 +0100502 String value = getStringUnchecked(key, null, userId);
Amith Yamasani52c489c2012-03-28 11:42:42 -0700503 return TextUtils.isEmpty(value) ? defaultValue : Long.parseLong(value);
504 }
505
506 @Override
507 public String getString(String key, String defaultValue, int userId) throws RemoteException {
Jim Miller158fe192013-04-17 15:23:55 -0700508 checkReadPermission(key, userId);
Amith Yamasani52c489c2012-03-28 11:42:42 -0700509
Adrian Roos9dd16eb2015-01-08 16:20:49 +0100510 return getStringUnchecked(key, defaultValue, userId);
511 }
512
513 public String getStringUnchecked(String key, String defaultValue, int userId) {
514 if (Settings.Secure.LOCK_PATTERN_ENABLED.equals(key)) {
Adrian Roos7811d9f2015-07-27 15:10:13 -0700515 long ident = Binder.clearCallingIdentity();
516 try {
517 return mLockPatternUtils.isLockPatternEnabled(userId) ? "1" : "0";
518 } finally {
519 Binder.restoreCallingIdentity(ident);
520 }
Adrian Roos9dd16eb2015-01-08 16:20:49 +0100521 }
522
Bryce Lee46145962015-12-14 14:39:10 -0800523 if (LockPatternUtils.LEGACY_LOCK_PATTERN_ENABLED.equals(key)) {
524 key = Settings.Secure.LOCK_PATTERN_ENABLED;
525 }
526
Adrian Roos261d5ab2014-10-29 14:42:38 +0100527 return mStorage.readKeyValue(key, defaultValue, userId);
Amith Yamasani52c489c2012-03-28 11:42:42 -0700528 }
529
Adrian Roos4f788452014-05-22 20:45:59 +0200530 @Override
Amith Yamasani52c489c2012-03-28 11:42:42 -0700531 public boolean havePassword(int userId) throws RemoteException {
532 // Do we need a permissions check here?
533
Adrian Roos261d5ab2014-10-29 14:42:38 +0100534 return mStorage.hasPassword(userId);
Amith Yamasani52c489c2012-03-28 11:42:42 -0700535 }
536
537 @Override
538 public boolean havePattern(int userId) throws RemoteException {
539 // Do we need a permissions check here?
540
Adrian Roos261d5ab2014-10-29 14:42:38 +0100541 return mStorage.hasPattern(userId);
Amith Yamasani52c489c2012-03-28 11:42:42 -0700542 }
543
Chad Brubakera91a8502015-05-07 10:02:22 -0700544 private void setKeystorePassword(String password, int userHandle) {
Robin Leef0246a82014-08-13 09:50:25 +0100545 final UserManager um = (UserManager) mContext.getSystemService(USER_SERVICE);
546 final KeyStore ks = KeyStore.getInstance();
547
548 final List<UserInfo> profiles = um.getProfiles(userHandle);
Robin Leef0246a82014-08-13 09:50:25 +0100549 for (UserInfo pi : profiles) {
Chad Brubakera91a8502015-05-07 10:02:22 -0700550 ks.onUserPasswordChanged(pi.id, password);
551 }
552 }
553
554 private void unlockKeystore(String password, int userHandle) {
555 final UserManager um = (UserManager) mContext.getSystemService(USER_SERVICE);
556 final KeyStore ks = KeyStore.getInstance();
557
558 final List<UserInfo> profiles = um.getProfiles(userHandle);
559 for (UserInfo pi : profiles) {
560 ks.unlock(pi.id, password);
Jim Millerde1af082013-09-11 14:58:26 -0700561 }
562 }
563
Paul Crowleyfaeb3eb2016-02-08 15:58:29 +0000564 private void unlockUser(int userId, byte[] token, byte[] secret) {
Jeff Sharkey8924e872015-11-30 12:52:10 -0700565 try {
Paul Crowleyfaeb3eb2016-02-08 15:58:29 +0000566 ActivityManagerNative.getDefault().unlockUser(userId, token, secret);
Jeff Sharkey8924e872015-11-30 12:52:10 -0700567 } catch (RemoteException e) {
568 throw e.rethrowAsRuntimeException();
569 }
570 }
Amith Yamasani52c489c2012-03-28 11:42:42 -0700571
Andres Morales8fa56652015-03-31 09:19:50 -0700572 private byte[] getCurrentHandle(int userId) {
573 CredentialHash credential;
574 byte[] currentHandle;
Jim Millerde1af082013-09-11 14:58:26 -0700575
Andres Morales8fa56652015-03-31 09:19:50 -0700576 int currentHandleType = mStorage.getStoredCredentialType(userId);
577 switch (currentHandleType) {
578 case CredentialHash.TYPE_PATTERN:
579 credential = mStorage.readPatternHash(userId);
580 currentHandle = credential != null
581 ? credential.hash
582 : null;
583 break;
584 case CredentialHash.TYPE_PASSWORD:
585 credential = mStorage.readPasswordHash(userId);
586 currentHandle = credential != null
587 ? credential.hash
588 : null;
589 break;
590 case CredentialHash.TYPE_NONE:
591 default:
592 currentHandle = null;
593 break;
594 }
595
596 // sanity check
597 if (currentHandleType != CredentialHash.TYPE_NONE && currentHandle == null) {
598 Slog.e(TAG, "Stored handle type [" + currentHandleType + "] but no handle available");
599 }
600
601 return currentHandle;
Amith Yamasani52c489c2012-03-28 11:42:42 -0700602 }
603
Andres Morales8fa56652015-03-31 09:19:50 -0700604
Amith Yamasani52c489c2012-03-28 11:42:42 -0700605 @Override
Andres Morales8fa56652015-03-31 09:19:50 -0700606 public void setLockPattern(String pattern, String savedCredential, int userId)
607 throws RemoteException {
608 byte[] currentHandle = getCurrentHandle(userId);
609
Andres Moralesd9fc85a2015-04-09 19:14:42 -0700610 if (pattern == null) {
Andres Moralescfb61602015-04-16 16:31:15 -0700611 getGateKeeperService().clearSecureUserId(userId);
Andres Moralesd9fc85a2015-04-09 19:14:42 -0700612 mStorage.writePatternHash(null, userId);
Chad Brubakera91a8502015-05-07 10:02:22 -0700613 setKeystorePassword(null, userId);
Paul Crowleyfaeb3eb2016-02-08 15:58:29 +0000614 clearUserKeyProtection(userId);
Andres Moralesd9fc85a2015-04-09 19:14:42 -0700615 return;
616 }
617
Andres Morales8fa56652015-03-31 09:19:50 -0700618 if (currentHandle == null) {
619 if (savedCredential != null) {
620 Slog.w(TAG, "Saved credential provided, but none stored");
621 }
622 savedCredential = null;
623 }
624
625 byte[] enrolledHandle = enrollCredential(currentHandle, savedCredential, pattern, userId);
626 if (enrolledHandle != null) {
627 mStorage.writePatternHash(enrolledHandle, userId);
Paul Crowleyfaeb3eb2016-02-08 15:58:29 +0000628 setUserKeyProtection(userId, pattern, verifyPattern(pattern, 0, userId));
Andres Morales8fa56652015-03-31 09:19:50 -0700629 } else {
Andres Morales2c4a5732015-07-09 16:11:00 -0700630 throw new RemoteException("Failed to enroll pattern");
Andres Morales8fa56652015-03-31 09:19:50 -0700631 }
632 }
633
634
635 @Override
636 public void setLockPassword(String password, String savedCredential, int userId)
637 throws RemoteException {
Andres Morales8fa56652015-03-31 09:19:50 -0700638 byte[] currentHandle = getCurrentHandle(userId);
639
Andres Moralesd9fc85a2015-04-09 19:14:42 -0700640 if (password == null) {
Andres Moralescfb61602015-04-16 16:31:15 -0700641 getGateKeeperService().clearSecureUserId(userId);
Andres Moralesd9fc85a2015-04-09 19:14:42 -0700642 mStorage.writePasswordHash(null, userId);
Chad Brubakera91a8502015-05-07 10:02:22 -0700643 setKeystorePassword(null, userId);
Paul Crowleyfaeb3eb2016-02-08 15:58:29 +0000644 clearUserKeyProtection(userId);
Andres Moralesd9fc85a2015-04-09 19:14:42 -0700645 return;
646 }
647
Andres Morales8fa56652015-03-31 09:19:50 -0700648 if (currentHandle == null) {
649 if (savedCredential != null) {
650 Slog.w(TAG, "Saved credential provided, but none stored");
651 }
652 savedCredential = null;
653 }
654
655 byte[] enrolledHandle = enrollCredential(currentHandle, savedCredential, password, userId);
656 if (enrolledHandle != null) {
657 mStorage.writePasswordHash(enrolledHandle, userId);
Paul Crowleyfaeb3eb2016-02-08 15:58:29 +0000658 setUserKeyProtection(userId, password, verifyPassword(password, 0, userId));
Andres Morales8fa56652015-03-31 09:19:50 -0700659 } else {
Andres Morales2c4a5732015-07-09 16:11:00 -0700660 throw new RemoteException("Failed to enroll password");
Andres Morales8fa56652015-03-31 09:19:50 -0700661 }
662 }
663
664 private byte[] enrollCredential(byte[] enrolledHandle,
665 String enrolledCredential, String toEnroll, int userId)
666 throws RemoteException {
Jim Millerde1af082013-09-11 14:58:26 -0700667 checkWritePermission(userId);
Andres Morales8fa56652015-03-31 09:19:50 -0700668 byte[] enrolledCredentialBytes = enrolledCredential == null
669 ? null
670 : enrolledCredential.getBytes();
671 byte[] toEnrollBytes = toEnroll == null
672 ? null
673 : toEnroll.getBytes();
Andres Morales23974272015-05-14 22:42:26 -0700674 GateKeeperResponse response = getGateKeeperService().enroll(userId, enrolledHandle,
675 enrolledCredentialBytes, toEnrollBytes);
Jim Millerde1af082013-09-11 14:58:26 -0700676
Andres Morales23974272015-05-14 22:42:26 -0700677 if (response == null) {
678 return null;
Andres Morales8fa56652015-03-31 09:19:50 -0700679 }
Jim Millerde1af082013-09-11 14:58:26 -0700680
Andres Morales23974272015-05-14 22:42:26 -0700681 byte[] hash = response.getPayload();
682 if (hash != null) {
683 setKeystorePassword(toEnroll, userId);
684 } else {
685 // Should not happen
686 Slog.e(TAG, "Throttled while enrolling a password");
687 }
Andres Morales8fa56652015-03-31 09:19:50 -0700688 return hash;
Jim Millerde1af082013-09-11 14:58:26 -0700689 }
690
Paul Crowleyfaeb3eb2016-02-08 15:58:29 +0000691 private void setUserKeyProtection(int userId, String credential, VerifyCredentialResponse vcr)
692 throws RemoteException {
693 if (vcr == null) {
694 throw new RemoteException("Null response verifying a credential we just set");
695 }
696 if (vcr.getResponseCode() != VerifyCredentialResponse.RESPONSE_OK) {
697 throw new RemoteException("Non-OK response verifying a credential we just set: "
698 + vcr.getResponseCode());
699 }
700 byte[] token = vcr.getPayload();
701 if (token == null) {
702 throw new RemoteException("Empty payload verifying a credential we just set");
703 }
704 changeUserKey(userId, token, secretFromCredential(credential));
705 }
706
707 private void clearUserKeyProtection(int userId) throws RemoteException {
708 changeUserKey(userId, null, null);
709 }
710
711 private static byte[] secretFromCredential(String credential) throws RemoteException {
712 try {
713 MessageDigest digest = MessageDigest.getInstance("SHA-512");
714 // Personalize the hash
715 byte[] personalization = "Android FBE credential hash"
716 .getBytes(StandardCharsets.UTF_8);
717 // Pad it to the block size of the hash function
718 personalization = Arrays.copyOf(personalization, 128);
719 digest.update(personalization);
720 digest.update(credential.getBytes(StandardCharsets.UTF_8));
721 return digest.digest();
722 } catch (NoSuchAlgorithmException e) {
723 throw new RuntimeException("NoSuchAlgorithmException for SHA-512");
724 }
725 }
726
727 private void changeUserKey(int userId, byte[] token, byte[] secret)
728 throws RemoteException {
729 final UserInfo userInfo = UserManager.get(mContext).getUserInfo(userId);
730 getMountService().changeUserKey(userId, userInfo.serialNumber, token, null, secret);
731 }
732
Jim Millerde1af082013-09-11 14:58:26 -0700733 @Override
Andres Morales23974272015-05-14 22:42:26 -0700734 public VerifyCredentialResponse checkPattern(String pattern, int userId) throws RemoteException {
735 return doVerifyPattern(pattern, false, 0, userId);
Andres Moralesd9fc85a2015-04-09 19:14:42 -0700736 }
Adrian Roos261d5ab2014-10-29 14:42:38 +0100737
Andres Moralesd9fc85a2015-04-09 19:14:42 -0700738 @Override
Andres Morales23974272015-05-14 22:42:26 -0700739 public VerifyCredentialResponse verifyPattern(String pattern, long challenge, int userId)
Andres Moralesd9fc85a2015-04-09 19:14:42 -0700740 throws RemoteException {
Andres Morales23974272015-05-14 22:42:26 -0700741 return doVerifyPattern(pattern, true, challenge, userId);
Andres Moralesd9fc85a2015-04-09 19:14:42 -0700742 }
743
Andres Moralese40bad82015-05-28 14:21:36 -0700744 private VerifyCredentialResponse doVerifyPattern(String pattern, boolean hasChallenge,
745 long challenge, int userId) throws RemoteException {
Andres Moralesd9fc85a2015-04-09 19:14:42 -0700746 checkPasswordReadPermission(userId);
Andres Moralesd9fc85a2015-04-09 19:14:42 -0700747 CredentialHash storedHash = mStorage.readPatternHash(userId);
Andres Moralese40bad82015-05-28 14:21:36 -0700748 boolean shouldReEnrollBaseZero = storedHash != null && storedHash.isBaseZeroPattern;
749
750 String patternToVerify;
751 if (shouldReEnrollBaseZero) {
752 patternToVerify = LockPatternUtils.patternStringToBaseZero(pattern);
753 } else {
754 patternToVerify = pattern;
755 }
756
757 VerifyCredentialResponse response = verifyCredential(userId, storedHash, patternToVerify,
758 hasChallenge, challenge,
Andres Morales23974272015-05-14 22:42:26 -0700759 new CredentialUtil() {
760 @Override
761 public void setCredential(String pattern, String oldPattern, int userId)
762 throws RemoteException {
763 setLockPattern(pattern, oldPattern, userId);
764 }
Andres Moralesd9fc85a2015-04-09 19:14:42 -0700765
Andres Morales23974272015-05-14 22:42:26 -0700766 @Override
767 public byte[] toHash(String pattern, int userId) {
Andres Moralese40bad82015-05-28 14:21:36 -0700768 return LockPatternUtils.patternToHash(
769 LockPatternUtils.stringToPattern(pattern));
Andres Morales23974272015-05-14 22:42:26 -0700770 }
Andres Morales59ef1262015-06-26 13:56:39 -0700771
772 @Override
773 public String adjustForKeystore(String pattern) {
774 return LockPatternUtils.patternStringToBaseZero(pattern);
775 }
Andres Morales23974272015-05-14 22:42:26 -0700776 }
777 );
Andres Moralese40bad82015-05-28 14:21:36 -0700778
779 if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK
780 && shouldReEnrollBaseZero) {
781 setLockPattern(pattern, patternToVerify, userId);
782 }
783
784 return response;
785
Amith Yamasani52c489c2012-03-28 11:42:42 -0700786 }
787
788 @Override
Andres Morales23974272015-05-14 22:42:26 -0700789 public VerifyCredentialResponse checkPassword(String password, int userId)
Andres Moralesd9fc85a2015-04-09 19:14:42 -0700790 throws RemoteException {
Andres Morales23974272015-05-14 22:42:26 -0700791 return doVerifyPassword(password, false, 0, userId);
Andres Moralesd9fc85a2015-04-09 19:14:42 -0700792 }
793
Andres Morales23974272015-05-14 22:42:26 -0700794 @Override
795 public VerifyCredentialResponse verifyPassword(String password, long challenge, int userId)
796 throws RemoteException {
797 return doVerifyPassword(password, true, challenge, userId);
798 }
799
800 private VerifyCredentialResponse doVerifyPassword(String password, boolean hasChallenge,
801 long challenge, int userId) throws RemoteException {
Andres Moralesd9fc85a2015-04-09 19:14:42 -0700802 checkPasswordReadPermission(userId);
Andres Morales8fa56652015-03-31 09:19:50 -0700803 CredentialHash storedHash = mStorage.readPasswordHash(userId);
Andres Morales23974272015-05-14 22:42:26 -0700804 return verifyCredential(userId, storedHash, password, hasChallenge, challenge,
805 new CredentialUtil() {
806 @Override
807 public void setCredential(String password, String oldPassword, int userId)
808 throws RemoteException {
809 setLockPassword(password, oldPassword, userId);
810 }
Adrian Roos261d5ab2014-10-29 14:42:38 +0100811
Andres Morales23974272015-05-14 22:42:26 -0700812 @Override
813 public byte[] toHash(String password, int userId) {
814 return mLockPatternUtils.passwordToHash(password, userId);
815 }
Andres Morales59ef1262015-06-26 13:56:39 -0700816
817 @Override
818 public String adjustForKeystore(String password) {
819 return password;
820 }
Andres Morales23974272015-05-14 22:42:26 -0700821 }
822 );
823 }
824
825 private VerifyCredentialResponse verifyCredential(int userId, CredentialHash storedHash,
826 String credential, boolean hasChallenge, long challenge, CredentialUtil credentialUtil)
827 throws RemoteException {
828 if ((storedHash == null || storedHash.hash.length == 0) && TextUtils.isEmpty(credential)) {
829 // don't need to pass empty credentials to GateKeeper
830 return VerifyCredentialResponse.OK;
Andres Moralesd9fc85a2015-04-09 19:14:42 -0700831 }
832
Andres Morales23974272015-05-14 22:42:26 -0700833 if (TextUtils.isEmpty(credential)) {
834 return VerifyCredentialResponse.ERROR;
Amith Yamasani52c489c2012-03-28 11:42:42 -0700835 }
Adrian Roos261d5ab2014-10-29 14:42:38 +0100836
Andres Morales8fa56652015-03-31 09:19:50 -0700837 if (storedHash.version == CredentialHash.VERSION_LEGACY) {
Andres Morales23974272015-05-14 22:42:26 -0700838 byte[] hash = credentialUtil.toHash(credential, userId);
Andres Moralesd9fc85a2015-04-09 19:14:42 -0700839 if (Arrays.equals(hash, storedHash.hash)) {
Andres Morales59ef1262015-06-26 13:56:39 -0700840 unlockKeystore(credentialUtil.adjustForKeystore(credential), userId);
Jeff Sharkeyb9fe5372015-12-03 15:23:08 -0700841
Paul Crowleyfaeb3eb2016-02-08 15:58:29 +0000842 // Users with legacy credentials don't have credential-backed
843 // FBE keys, so just pass through a fake token/secret
844 Slog.i(TAG, "Unlocking user with fake token: " + userId);
845 final byte[] fakeToken = String.valueOf(userId).getBytes();
846 unlockUser(userId, fakeToken, fakeToken);
Jeff Sharkeyb9fe5372015-12-03 15:23:08 -0700847
Andres Morales23974272015-05-14 22:42:26 -0700848 // migrate credential to GateKeeper
849 credentialUtil.setCredential(credential, null, userId);
Andres Moralesd9fc85a2015-04-09 19:14:42 -0700850 if (!hasChallenge) {
Andres Morales23974272015-05-14 22:42:26 -0700851 return VerifyCredentialResponse.OK;
Andres Moralesd9fc85a2015-04-09 19:14:42 -0700852 }
853 // Fall through to get the auth token. Technically this should never happen,
Andres Morales23974272015-05-14 22:42:26 -0700854 // as a user that had a legacy credential would have to unlock their device
Andres Moralesd9fc85a2015-04-09 19:14:42 -0700855 // before getting to a flow with a challenge, but supporting for consistency.
856 } else {
Andres Morales23974272015-05-14 22:42:26 -0700857 return VerifyCredentialResponse.ERROR;
Andres Morales8fa56652015-03-31 09:19:50 -0700858 }
Andres Morales8fa56652015-03-31 09:19:50 -0700859 }
860
Andres Morales23974272015-05-14 22:42:26 -0700861 VerifyCredentialResponse response;
Paul Crowley98e0a262016-02-04 09:41:53 +0000862 boolean shouldReEnroll = false;
863 GateKeeperResponse gateKeeperResponse = getGateKeeperService()
864 .verifyChallenge(userId, challenge, storedHash.hash, credential.getBytes());
865 int responseCode = gateKeeperResponse.getResponseCode();
866 if (responseCode == GateKeeperResponse.RESPONSE_RETRY) {
867 response = new VerifyCredentialResponse(gateKeeperResponse.getTimeout());
868 } else if (responseCode == GateKeeperResponse.RESPONSE_OK) {
869 byte[] token = gateKeeperResponse.getPayload();
870 if (token == null) {
871 // something's wrong if there's no payload with a challenge
872 Slog.e(TAG, "verifyChallenge response had no associated payload");
Andres Morales23974272015-05-14 22:42:26 -0700873 response = VerifyCredentialResponse.ERROR;
Paul Crowley98e0a262016-02-04 09:41:53 +0000874 } else {
875 shouldReEnroll = gateKeeperResponse.getShouldReEnroll();
876 response = new VerifyCredentialResponse(token);
Andres Moralesd9fc85a2015-04-09 19:14:42 -0700877 }
Andres Morales23974272015-05-14 22:42:26 -0700878 } else {
Paul Crowley98e0a262016-02-04 09:41:53 +0000879 response = VerifyCredentialResponse.ERROR;
Adrian Roos261d5ab2014-10-29 14:42:38 +0100880 }
Andres Morales8fa56652015-03-31 09:19:50 -0700881
Andres Morales23974272015-05-14 22:42:26 -0700882 if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK) {
883 // credential has matched
884 unlockKeystore(credential, userId);
Jeff Sharkeyb9fe5372015-12-03 15:23:08 -0700885
Paul Crowleyfaeb3eb2016-02-08 15:58:29 +0000886 Slog.i(TAG, "Unlocking user " + userId +
887 " with token length " + response.getPayload().length);
888 unlockUser(userId, response.getPayload(), secretFromCredential(credential));
Jeff Sharkeyb9fe5372015-12-03 15:23:08 -0700889
Clara Bayarri56878a92015-10-29 15:43:55 +0000890 UserInfo info = UserManager.get(mContext).getUserInfo(userId);
Clara Bayarria1771112015-12-18 16:29:18 +0000891 if (mLockPatternUtils.isSeparateProfileChallengeEnabled(userId)) {
Clara Bayarri56878a92015-10-29 15:43:55 +0000892 TrustManager trustManager =
893 (TrustManager) mContext.getSystemService(Context.TRUST_SERVICE);
894 trustManager.setDeviceLockedForUser(userId, false);
895 }
Andres Morales23974272015-05-14 22:42:26 -0700896 if (shouldReEnroll) {
897 credentialUtil.setCredential(credential, credential, userId);
898 }
Adrian Roos873010d2015-08-25 15:59:00 -0700899 } else if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_RETRY) {
900 if (response.getTimeout() > 0) {
901 requireStrongAuth(STRONG_AUTH_REQUIRED_AFTER_LOCKOUT, userId);
902 }
Andres Morales23974272015-05-14 22:42:26 -0700903 }
Amith Yamasani52c489c2012-03-28 11:42:42 -0700904
Andres Morales23974272015-05-14 22:42:26 -0700905 return response;
906 }
Andres Moralesd9fc85a2015-04-09 19:14:42 -0700907
Amith Yamasani52c489c2012-03-28 11:42:42 -0700908 @Override
Adrian Roos261d5ab2014-10-29 14:42:38 +0100909 public boolean checkVoldPassword(int userId) throws RemoteException {
Paul Lawrence945490c2014-03-27 16:37:28 +0000910 if (!mFirstCallToVold) {
911 return false;
912 }
913 mFirstCallToVold = false;
914
915 checkPasswordReadPermission(userId);
916
917 // There's no guarantee that this will safely connect, but if it fails
918 // we will simply show the lock screen when we shouldn't, so relatively
919 // benign. There is an outside chance something nasty would happen if
920 // this service restarted before vold stales out the password in this
921 // case. The nastiness is limited to not showing the lock screen when
922 // we should, within the first minute of decrypting the phone if this
923 // service can't connect to vold, it restarts, and then the new instance
924 // does successfully connect.
925 final IMountService service = getMountService();
926 String password = service.getPassword();
927 service.clearPassword();
928 if (password == null) {
929 return false;
930 }
931
932 try {
Adrian Roos9dd16eb2015-01-08 16:20:49 +0100933 if (mLockPatternUtils.isLockPatternEnabled(userId)) {
Andres Morales23974272015-05-14 22:42:26 -0700934 if (checkPattern(password, userId).getResponseCode()
935 == GateKeeperResponse.RESPONSE_OK) {
Paul Lawrence945490c2014-03-27 16:37:28 +0000936 return true;
937 }
938 }
939 } catch (Exception e) {
940 }
941
942 try {
Adrian Roos9dd16eb2015-01-08 16:20:49 +0100943 if (mLockPatternUtils.isLockPasswordEnabled(userId)) {
Andres Morales23974272015-05-14 22:42:26 -0700944 if (checkPassword(password, userId).getResponseCode()
945 == GateKeeperResponse.RESPONSE_OK) {
Paul Lawrence945490c2014-03-27 16:37:28 +0000946 return true;
947 }
948 }
949 } catch (Exception e) {
950 }
951
952 return false;
953 }
954
Adrian Roosdb0f76e2015-01-07 22:19:38 +0100955 private void removeUser(int userId) {
Adrian Roos261d5ab2014-10-29 14:42:38 +0100956 mStorage.removeUser(userId);
Adrian Roosb5e47222015-08-14 15:53:06 -0700957 mStrongAuth.removeUser(userId);
Robin Lee49d810c2014-09-23 13:50:22 +0100958
959 final KeyStore ks = KeyStore.getInstance();
Chad Brubaker83ce0952015-05-12 13:00:02 -0700960 ks.onUserRemoved(userId);
Andres Morales070fe632015-06-24 10:37:10 -0700961
962 try {
963 final IGateKeeperService gk = getGateKeeperService();
964 if (gk != null) {
965 gk.clearSecureUserId(userId);
966 }
967 } catch (RemoteException ex) {
968 Slog.w(TAG, "unable to clear GK secure user id");
969 }
Amith Yamasani52c489c2012-03-28 11:42:42 -0700970 }
971
Adrian Roosb5e47222015-08-14 15:53:06 -0700972 @Override
973 public void registerStrongAuthTracker(IStrongAuthTracker tracker) {
974 checkPasswordReadPermission(UserHandle.USER_ALL);
975 mStrongAuth.registerStrongAuthTracker(tracker);
976 }
977
978 @Override
979 public void unregisterStrongAuthTracker(IStrongAuthTracker tracker) {
980 checkPasswordReadPermission(UserHandle.USER_ALL);
981 mStrongAuth.unregisterStrongAuthTracker(tracker);
982 }
983
984 @Override
985 public void requireStrongAuth(int strongAuthReason, int userId) {
986 checkWritePermission(userId);
987 mStrongAuth.requireStrongAuth(strongAuthReason, userId);
988 }
989
Amith Yamasani52c489c2012-03-28 11:42:42 -0700990 private static final String[] VALID_SETTINGS = new String[] {
991 LockPatternUtils.LOCKOUT_PERMANENT_KEY,
992 LockPatternUtils.LOCKOUT_ATTEMPT_DEADLINE,
993 LockPatternUtils.PATTERN_EVER_CHOSEN_KEY,
994 LockPatternUtils.PASSWORD_TYPE_KEY,
995 LockPatternUtils.PASSWORD_TYPE_ALTERNATE_KEY,
996 LockPatternUtils.LOCK_PASSWORD_SALT_KEY,
997 LockPatternUtils.DISABLE_LOCKSCREEN_KEY,
998 LockPatternUtils.LOCKSCREEN_OPTIONS,
999 LockPatternUtils.LOCKSCREEN_BIOMETRIC_WEAK_FALLBACK,
1000 LockPatternUtils.BIOMETRIC_WEAK_EVER_CHOSEN_KEY,
1001 LockPatternUtils.LOCKSCREEN_POWER_BUTTON_INSTANTLY_LOCKS,
1002 LockPatternUtils.PASSWORD_HISTORY_KEY,
1003 Secure.LOCK_PATTERN_ENABLED,
1004 Secure.LOCK_BIOMETRIC_WEAK_FLAGS,
1005 Secure.LOCK_PATTERN_VISIBLE,
1006 Secure.LOCK_PATTERN_TACTILE_FEEDBACK_ENABLED
Jim Miller187ec582013-04-15 18:27:54 -07001007 };
1008
Svetoslav Ganov6d2c0e52015-06-23 16:33:36 +00001009 // Reading these settings needs the contacts permission
1010 private static final String[] READ_CONTACTS_PROTECTED_SETTINGS = new String[] {
Jim Miller187ec582013-04-15 18:27:54 -07001011 Secure.LOCK_SCREEN_OWNER_INFO_ENABLED,
1012 Secure.LOCK_SCREEN_OWNER_INFO
1013 };
Paul Lawrence945490c2014-03-27 16:37:28 +00001014
Adrian Roos001b00d2015-02-24 17:08:48 +01001015 // Reading these settings needs the same permission as checking the password
1016 private static final String[] READ_PASSWORD_PROTECTED_SETTINGS = new String[] {
1017 LockPatternUtils.LOCK_PASSWORD_SALT_KEY,
1018 LockPatternUtils.PASSWORD_HISTORY_KEY,
Adrian Roos855fa302015-04-02 16:01:12 +02001019 LockPatternUtils.PASSWORD_TYPE_KEY,
Adrian Roos001b00d2015-02-24 17:08:48 +01001020 };
1021
Amith Yamasani072543f2015-02-13 11:09:45 -08001022 private static final String[] SETTINGS_TO_BACKUP = new String[] {
1023 Secure.LOCK_SCREEN_OWNER_INFO_ENABLED,
1024 Secure.LOCK_SCREEN_OWNER_INFO
1025 };
1026
Paul Lawrence945490c2014-03-27 16:37:28 +00001027 private IMountService getMountService() {
1028 final IBinder service = ServiceManager.getService("mount");
1029 if (service != null) {
1030 return IMountService.Stub.asInterface(service);
1031 }
1032 return null;
1033 }
Andres Morales8fa56652015-03-31 09:19:50 -07001034
Andres Morales301ea442015-04-17 09:15:47 -07001035 private class GateKeeperDiedRecipient implements IBinder.DeathRecipient {
1036 @Override
1037 public void binderDied() {
1038 mGateKeeperService.asBinder().unlinkToDeath(this, 0);
1039 mGateKeeperService = null;
1040 }
1041 }
1042
1043 private synchronized IGateKeeperService getGateKeeperService()
1044 throws RemoteException {
Andres Morales8fa56652015-03-31 09:19:50 -07001045 if (mGateKeeperService != null) {
1046 return mGateKeeperService;
1047 }
1048
1049 final IBinder service =
1050 ServiceManager.getService("android.service.gatekeeper.IGateKeeperService");
1051 if (service != null) {
Andres Morales301ea442015-04-17 09:15:47 -07001052 service.linkToDeath(new GateKeeperDiedRecipient(), 0);
Andres Morales8fa56652015-03-31 09:19:50 -07001053 mGateKeeperService = IGateKeeperService.Stub.asInterface(service);
1054 return mGateKeeperService;
1055 }
1056
1057 Slog.e(TAG, "Unable to acquire GateKeeperService");
1058 return null;
1059 }
Amith Yamasani52c489c2012-03-28 11:42:42 -07001060}