blob: ba2a2e05d3d8dfafee4fbd72a83a4d94cda3b5de [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
Clara Bayarri0a587d22016-02-23 14:49:41 -0800548 if (um.getUserInfo(userHandle).isManagedProfile()) {
549 if (mLockPatternUtils.isSeparateProfileChallengeEnabled(userHandle)) {
550 ks.onUserPasswordChanged(userHandle, password);
551 } else {
552 throw new RuntimeException("Can't set keystore password on a profile that "
553 + "doesn't have a profile challenge.");
554 }
555 } else {
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);
563 }
564 }
Chad Brubakera91a8502015-05-07 10:02:22 -0700565 }
566 }
567
568 private void unlockKeystore(String password, int userHandle) {
569 final UserManager um = (UserManager) mContext.getSystemService(USER_SERVICE);
570 final KeyStore ks = KeyStore.getInstance();
571
Clara Bayarri0a587d22016-02-23 14:49:41 -0800572 if (um.getUserInfo(userHandle).isManagedProfile()) {
573 if (mLockPatternUtils.isSeparateProfileChallengeEnabled(userHandle)) {
574 ks.unlock(userHandle, password);
575 } else {
576 throw new RuntimeException("Can't unlock a profile explicitly if it "
577 + "doesn't have a profile challenge.");
578 }
579 } else {
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);
587 }
588 }
Jim Millerde1af082013-09-11 14:58:26 -0700589 }
590 }
591
Paul Crowleyfaeb3eb2016-02-08 15:58:29 +0000592 private void unlockUser(int userId, byte[] token, byte[] secret) {
Jeff Sharkey8924e872015-11-30 12:52:10 -0700593 try {
Paul Crowleyfaeb3eb2016-02-08 15:58:29 +0000594 ActivityManagerNative.getDefault().unlockUser(userId, token, secret);
Jeff Sharkey8924e872015-11-30 12:52:10 -0700595 } catch (RemoteException e) {
596 throw e.rethrowAsRuntimeException();
597 }
598 }
Amith Yamasani52c489c2012-03-28 11:42:42 -0700599
Andres Morales8fa56652015-03-31 09:19:50 -0700600 private byte[] getCurrentHandle(int userId) {
601 CredentialHash credential;
602 byte[] currentHandle;
Jim Millerde1af082013-09-11 14:58:26 -0700603
Andres Morales8fa56652015-03-31 09:19:50 -0700604 int currentHandleType = mStorage.getStoredCredentialType(userId);
605 switch (currentHandleType) {
606 case CredentialHash.TYPE_PATTERN:
607 credential = mStorage.readPatternHash(userId);
608 currentHandle = credential != null
609 ? credential.hash
610 : null;
611 break;
612 case CredentialHash.TYPE_PASSWORD:
613 credential = mStorage.readPasswordHash(userId);
614 currentHandle = credential != null
615 ? credential.hash
616 : null;
617 break;
618 case CredentialHash.TYPE_NONE:
619 default:
620 currentHandle = null;
621 break;
622 }
623
624 // sanity check
625 if (currentHandleType != CredentialHash.TYPE_NONE && currentHandle == null) {
626 Slog.e(TAG, "Stored handle type [" + currentHandleType + "] but no handle available");
627 }
628
629 return currentHandle;
Amith Yamasani52c489c2012-03-28 11:42:42 -0700630 }
631
Andres Morales8fa56652015-03-31 09:19:50 -0700632
Amith Yamasani52c489c2012-03-28 11:42:42 -0700633 @Override
Andres Morales8fa56652015-03-31 09:19:50 -0700634 public void setLockPattern(String pattern, String savedCredential, int userId)
635 throws RemoteException {
636 byte[] currentHandle = getCurrentHandle(userId);
637
Andres Moralesd9fc85a2015-04-09 19:14:42 -0700638 if (pattern == null) {
Andres Moralescfb61602015-04-16 16:31:15 -0700639 getGateKeeperService().clearSecureUserId(userId);
Andres Moralesd9fc85a2015-04-09 19:14:42 -0700640 mStorage.writePatternHash(null, userId);
Chad Brubakera91a8502015-05-07 10:02:22 -0700641 setKeystorePassword(null, userId);
Paul Crowleyfaeb3eb2016-02-08 15:58:29 +0000642 clearUserKeyProtection(userId);
Andres Moralesd9fc85a2015-04-09 19:14:42 -0700643 return;
644 }
645
Andres Morales8fa56652015-03-31 09:19:50 -0700646 if (currentHandle == null) {
647 if (savedCredential != null) {
648 Slog.w(TAG, "Saved credential provided, but none stored");
649 }
650 savedCredential = null;
651 }
652
653 byte[] enrolledHandle = enrollCredential(currentHandle, savedCredential, pattern, userId);
654 if (enrolledHandle != null) {
655 mStorage.writePatternHash(enrolledHandle, userId);
Paul Crowleyfaeb3eb2016-02-08 15:58:29 +0000656 setUserKeyProtection(userId, pattern, verifyPattern(pattern, 0, userId));
Andres Morales8fa56652015-03-31 09:19:50 -0700657 } else {
Andres Morales2c4a5732015-07-09 16:11:00 -0700658 throw new RemoteException("Failed to enroll pattern");
Andres Morales8fa56652015-03-31 09:19:50 -0700659 }
660 }
661
662
663 @Override
664 public void setLockPassword(String password, String savedCredential, int userId)
665 throws RemoteException {
Andres Morales8fa56652015-03-31 09:19:50 -0700666 byte[] currentHandle = getCurrentHandle(userId);
667
Andres Moralesd9fc85a2015-04-09 19:14:42 -0700668 if (password == null) {
Andres Moralescfb61602015-04-16 16:31:15 -0700669 getGateKeeperService().clearSecureUserId(userId);
Andres Moralesd9fc85a2015-04-09 19:14:42 -0700670 mStorage.writePasswordHash(null, userId);
Chad Brubakera91a8502015-05-07 10:02:22 -0700671 setKeystorePassword(null, userId);
Paul Crowleyfaeb3eb2016-02-08 15:58:29 +0000672 clearUserKeyProtection(userId);
Andres Moralesd9fc85a2015-04-09 19:14:42 -0700673 return;
674 }
675
Andres Morales8fa56652015-03-31 09:19:50 -0700676 if (currentHandle == null) {
677 if (savedCredential != null) {
678 Slog.w(TAG, "Saved credential provided, but none stored");
679 }
680 savedCredential = null;
681 }
682
683 byte[] enrolledHandle = enrollCredential(currentHandle, savedCredential, password, userId);
684 if (enrolledHandle != null) {
685 mStorage.writePasswordHash(enrolledHandle, userId);
Paul Crowleyfaeb3eb2016-02-08 15:58:29 +0000686 setUserKeyProtection(userId, password, verifyPassword(password, 0, userId));
Andres Morales8fa56652015-03-31 09:19:50 -0700687 } else {
Andres Morales2c4a5732015-07-09 16:11:00 -0700688 throw new RemoteException("Failed to enroll password");
Andres Morales8fa56652015-03-31 09:19:50 -0700689 }
690 }
691
692 private byte[] enrollCredential(byte[] enrolledHandle,
693 String enrolledCredential, String toEnroll, int userId)
694 throws RemoteException {
Jim Millerde1af082013-09-11 14:58:26 -0700695 checkWritePermission(userId);
Andres Morales8fa56652015-03-31 09:19:50 -0700696 byte[] enrolledCredentialBytes = enrolledCredential == null
697 ? null
698 : enrolledCredential.getBytes();
699 byte[] toEnrollBytes = toEnroll == null
700 ? null
701 : toEnroll.getBytes();
Andres Morales23974272015-05-14 22:42:26 -0700702 GateKeeperResponse response = getGateKeeperService().enroll(userId, enrolledHandle,
703 enrolledCredentialBytes, toEnrollBytes);
Jim Millerde1af082013-09-11 14:58:26 -0700704
Andres Morales23974272015-05-14 22:42:26 -0700705 if (response == null) {
706 return null;
Andres Morales8fa56652015-03-31 09:19:50 -0700707 }
Jim Millerde1af082013-09-11 14:58:26 -0700708
Andres Morales23974272015-05-14 22:42:26 -0700709 byte[] hash = response.getPayload();
710 if (hash != null) {
711 setKeystorePassword(toEnroll, userId);
712 } else {
713 // Should not happen
714 Slog.e(TAG, "Throttled while enrolling a password");
715 }
Andres Morales8fa56652015-03-31 09:19:50 -0700716 return hash;
Jim Millerde1af082013-09-11 14:58:26 -0700717 }
718
Paul Crowleyfaeb3eb2016-02-08 15:58:29 +0000719 private void setUserKeyProtection(int userId, String credential, VerifyCredentialResponse vcr)
720 throws RemoteException {
721 if (vcr == null) {
722 throw new RemoteException("Null response verifying a credential we just set");
723 }
724 if (vcr.getResponseCode() != VerifyCredentialResponse.RESPONSE_OK) {
725 throw new RemoteException("Non-OK response verifying a credential we just set: "
726 + vcr.getResponseCode());
727 }
728 byte[] token = vcr.getPayload();
729 if (token == null) {
730 throw new RemoteException("Empty payload verifying a credential we just set");
731 }
732 changeUserKey(userId, token, secretFromCredential(credential));
733 }
734
735 private void clearUserKeyProtection(int userId) throws RemoteException {
736 changeUserKey(userId, null, null);
737 }
738
739 private static byte[] secretFromCredential(String credential) throws RemoteException {
740 try {
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");
752 }
753 }
754
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);
759 }
760
Jim Millerde1af082013-09-11 14:58:26 -0700761 @Override
Andres Morales23974272015-05-14 22:42:26 -0700762 public VerifyCredentialResponse checkPattern(String pattern, int userId) throws RemoteException {
763 return doVerifyPattern(pattern, false, 0, userId);
Andres Moralesd9fc85a2015-04-09 19:14:42 -0700764 }
Adrian Roos261d5ab2014-10-29 14:42:38 +0100765
Andres Moralesd9fc85a2015-04-09 19:14:42 -0700766 @Override
Andres Morales23974272015-05-14 22:42:26 -0700767 public VerifyCredentialResponse verifyPattern(String pattern, long challenge, int userId)
Andres Moralesd9fc85a2015-04-09 19:14:42 -0700768 throws RemoteException {
Andres Morales23974272015-05-14 22:42:26 -0700769 return doVerifyPattern(pattern, true, challenge, userId);
Andres Moralesd9fc85a2015-04-09 19:14:42 -0700770 }
771
Andres Moralese40bad82015-05-28 14:21:36 -0700772 private VerifyCredentialResponse doVerifyPattern(String pattern, boolean hasChallenge,
773 long challenge, int userId) throws RemoteException {
Andres Moralesd9fc85a2015-04-09 19:14:42 -0700774 checkPasswordReadPermission(userId);
Andres Moralesd9fc85a2015-04-09 19:14:42 -0700775 CredentialHash storedHash = mStorage.readPatternHash(userId);
Andres Moralese40bad82015-05-28 14:21:36 -0700776 boolean shouldReEnrollBaseZero = storedHash != null && storedHash.isBaseZeroPattern;
777
778 String patternToVerify;
779 if (shouldReEnrollBaseZero) {
780 patternToVerify = LockPatternUtils.patternStringToBaseZero(pattern);
781 } else {
782 patternToVerify = pattern;
783 }
784
785 VerifyCredentialResponse response = verifyCredential(userId, storedHash, patternToVerify,
786 hasChallenge, challenge,
Andres Morales23974272015-05-14 22:42:26 -0700787 new CredentialUtil() {
788 @Override
789 public void setCredential(String pattern, String oldPattern, int userId)
790 throws RemoteException {
791 setLockPattern(pattern, oldPattern, userId);
792 }
Andres Moralesd9fc85a2015-04-09 19:14:42 -0700793
Andres Morales23974272015-05-14 22:42:26 -0700794 @Override
795 public byte[] toHash(String pattern, int userId) {
Andres Moralese40bad82015-05-28 14:21:36 -0700796 return LockPatternUtils.patternToHash(
797 LockPatternUtils.stringToPattern(pattern));
Andres Morales23974272015-05-14 22:42:26 -0700798 }
Andres Morales59ef1262015-06-26 13:56:39 -0700799
800 @Override
801 public String adjustForKeystore(String pattern) {
802 return LockPatternUtils.patternStringToBaseZero(pattern);
803 }
Andres Morales23974272015-05-14 22:42:26 -0700804 }
805 );
Andres Moralese40bad82015-05-28 14:21:36 -0700806
807 if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK
808 && shouldReEnrollBaseZero) {
809 setLockPattern(pattern, patternToVerify, userId);
810 }
811
812 return response;
813
Amith Yamasani52c489c2012-03-28 11:42:42 -0700814 }
815
816 @Override
Andres Morales23974272015-05-14 22:42:26 -0700817 public VerifyCredentialResponse checkPassword(String password, int userId)
Andres Moralesd9fc85a2015-04-09 19:14:42 -0700818 throws RemoteException {
Andres Morales23974272015-05-14 22:42:26 -0700819 return doVerifyPassword(password, false, 0, userId);
Andres Moralesd9fc85a2015-04-09 19:14:42 -0700820 }
821
Andres Morales23974272015-05-14 22:42:26 -0700822 @Override
823 public VerifyCredentialResponse verifyPassword(String password, long challenge, int userId)
824 throws RemoteException {
825 return doVerifyPassword(password, true, challenge, userId);
826 }
827
828 private VerifyCredentialResponse doVerifyPassword(String password, boolean hasChallenge,
829 long challenge, int userId) throws RemoteException {
Andres Moralesd9fc85a2015-04-09 19:14:42 -0700830 checkPasswordReadPermission(userId);
Andres Morales8fa56652015-03-31 09:19:50 -0700831 CredentialHash storedHash = mStorage.readPasswordHash(userId);
Andres Morales23974272015-05-14 22:42:26 -0700832 return verifyCredential(userId, storedHash, password, hasChallenge, challenge,
833 new CredentialUtil() {
834 @Override
835 public void setCredential(String password, String oldPassword, int userId)
836 throws RemoteException {
837 setLockPassword(password, oldPassword, userId);
838 }
Adrian Roos261d5ab2014-10-29 14:42:38 +0100839
Andres Morales23974272015-05-14 22:42:26 -0700840 @Override
841 public byte[] toHash(String password, int userId) {
842 return mLockPatternUtils.passwordToHash(password, userId);
843 }
Andres Morales59ef1262015-06-26 13:56:39 -0700844
845 @Override
846 public String adjustForKeystore(String password) {
847 return password;
848 }
Andres Morales23974272015-05-14 22:42:26 -0700849 }
850 );
851 }
852
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;
Andres Moralesd9fc85a2015-04-09 19:14:42 -0700859 }
860
Andres Morales23974272015-05-14 22:42:26 -0700861 if (TextUtils.isEmpty(credential)) {
862 return VerifyCredentialResponse.ERROR;
Amith Yamasani52c489c2012-03-28 11:42:42 -0700863 }
Adrian Roos261d5ab2014-10-29 14:42:38 +0100864
Andres Morales8fa56652015-03-31 09:19:50 -0700865 if (storedHash.version == CredentialHash.VERSION_LEGACY) {
Andres Morales23974272015-05-14 22:42:26 -0700866 byte[] hash = credentialUtil.toHash(credential, userId);
Andres Moralesd9fc85a2015-04-09 19:14:42 -0700867 if (Arrays.equals(hash, storedHash.hash)) {
Andres Morales59ef1262015-06-26 13:56:39 -0700868 unlockKeystore(credentialUtil.adjustForKeystore(credential), userId);
Jeff Sharkeyb9fe5372015-12-03 15:23:08 -0700869
Paul Crowleyfaeb3eb2016-02-08 15:58:29 +0000870 // 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);
Jeff Sharkeyb9fe5372015-12-03 15:23:08 -0700875
Andres Morales23974272015-05-14 22:42:26 -0700876 // migrate credential to GateKeeper
877 credentialUtil.setCredential(credential, null, userId);
Andres Moralesd9fc85a2015-04-09 19:14:42 -0700878 if (!hasChallenge) {
Andres Morales23974272015-05-14 22:42:26 -0700879 return VerifyCredentialResponse.OK;
Andres Moralesd9fc85a2015-04-09 19:14:42 -0700880 }
881 // Fall through to get the auth token. Technically this should never happen,
Andres Morales23974272015-05-14 22:42:26 -0700882 // as a user that had a legacy credential would have to unlock their device
Andres Moralesd9fc85a2015-04-09 19:14:42 -0700883 // before getting to a flow with a challenge, but supporting for consistency.
884 } else {
Andres Morales23974272015-05-14 22:42:26 -0700885 return VerifyCredentialResponse.ERROR;
Andres Morales8fa56652015-03-31 09:19:50 -0700886 }
Andres Morales8fa56652015-03-31 09:19:50 -0700887 }
888
Andres Morales23974272015-05-14 22:42:26 -0700889 VerifyCredentialResponse response;
Paul Crowley98e0a262016-02-04 09:41:53 +0000890 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();
898 if (token == null) {
899 // something's wrong if there's no payload with a challenge
900 Slog.e(TAG, "verifyChallenge response had no associated payload");
Andres Morales23974272015-05-14 22:42:26 -0700901 response = VerifyCredentialResponse.ERROR;
Paul Crowley98e0a262016-02-04 09:41:53 +0000902 } else {
903 shouldReEnroll = gateKeeperResponse.getShouldReEnroll();
904 response = new VerifyCredentialResponse(token);
Andres Moralesd9fc85a2015-04-09 19:14:42 -0700905 }
Andres Morales23974272015-05-14 22:42:26 -0700906 } else {
Paul Crowley98e0a262016-02-04 09:41:53 +0000907 response = VerifyCredentialResponse.ERROR;
Adrian Roos261d5ab2014-10-29 14:42:38 +0100908 }
Andres Morales8fa56652015-03-31 09:19:50 -0700909
Andres Morales23974272015-05-14 22:42:26 -0700910 if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK) {
911 // credential has matched
912 unlockKeystore(credential, userId);
Jeff Sharkeyb9fe5372015-12-03 15:23:08 -0700913
Paul Crowleyfaeb3eb2016-02-08 15:58:29 +0000914 Slog.i(TAG, "Unlocking user " + userId +
915 " with token length " + response.getPayload().length);
916 unlockUser(userId, response.getPayload(), secretFromCredential(credential));
Jeff Sharkeyb9fe5372015-12-03 15:23:08 -0700917
Clara Bayarri56878a92015-10-29 15:43:55 +0000918 UserInfo info = UserManager.get(mContext).getUserInfo(userId);
Clara Bayarria1771112015-12-18 16:29:18 +0000919 if (mLockPatternUtils.isSeparateProfileChallengeEnabled(userId)) {
Clara Bayarri56878a92015-10-29 15:43:55 +0000920 TrustManager trustManager =
921 (TrustManager) mContext.getSystemService(Context.TRUST_SERVICE);
922 trustManager.setDeviceLockedForUser(userId, false);
923 }
Andres Morales23974272015-05-14 22:42:26 -0700924 if (shouldReEnroll) {
925 credentialUtil.setCredential(credential, credential, userId);
926 }
Adrian Roos873010d2015-08-25 15:59:00 -0700927 } else if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_RETRY) {
928 if (response.getTimeout() > 0) {
929 requireStrongAuth(STRONG_AUTH_REQUIRED_AFTER_LOCKOUT, userId);
930 }
Andres Morales23974272015-05-14 22:42:26 -0700931 }
Amith Yamasani52c489c2012-03-28 11:42:42 -0700932
Andres Morales23974272015-05-14 22:42:26 -0700933 return response;
934 }
Andres Moralesd9fc85a2015-04-09 19:14:42 -0700935
Amith Yamasani52c489c2012-03-28 11:42:42 -0700936 @Override
Adrian Roos261d5ab2014-10-29 14:42:38 +0100937 public boolean checkVoldPassword(int userId) throws RemoteException {
Paul Lawrence945490c2014-03-27 16:37:28 +0000938 if (!mFirstCallToVold) {
939 return false;
940 }
941 mFirstCallToVold = false;
942
943 checkPasswordReadPermission(userId);
944
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) {
957 return false;
958 }
959
960 try {
Adrian Roos9dd16eb2015-01-08 16:20:49 +0100961 if (mLockPatternUtils.isLockPatternEnabled(userId)) {
Andres Morales23974272015-05-14 22:42:26 -0700962 if (checkPattern(password, userId).getResponseCode()
963 == GateKeeperResponse.RESPONSE_OK) {
Paul Lawrence945490c2014-03-27 16:37:28 +0000964 return true;
965 }
966 }
967 } catch (Exception e) {
968 }
969
970 try {
Adrian Roos9dd16eb2015-01-08 16:20:49 +0100971 if (mLockPatternUtils.isLockPasswordEnabled(userId)) {
Andres Morales23974272015-05-14 22:42:26 -0700972 if (checkPassword(password, userId).getResponseCode()
973 == GateKeeperResponse.RESPONSE_OK) {
Paul Lawrence945490c2014-03-27 16:37:28 +0000974 return true;
975 }
976 }
977 } catch (Exception e) {
978 }
979
980 return false;
981 }
982
Adrian Roosdb0f76e2015-01-07 22:19:38 +0100983 private void removeUser(int userId) {
Adrian Roos261d5ab2014-10-29 14:42:38 +0100984 mStorage.removeUser(userId);
Adrian Roosb5e47222015-08-14 15:53:06 -0700985 mStrongAuth.removeUser(userId);
Robin Lee49d810c2014-09-23 13:50:22 +0100986
987 final KeyStore ks = KeyStore.getInstance();
Chad Brubaker83ce0952015-05-12 13:00:02 -0700988 ks.onUserRemoved(userId);
Andres Morales070fe632015-06-24 10:37:10 -0700989
990 try {
991 final IGateKeeperService gk = getGateKeeperService();
992 if (gk != null) {
993 gk.clearSecureUserId(userId);
994 }
995 } catch (RemoteException ex) {
996 Slog.w(TAG, "unable to clear GK secure user id");
997 }
Amith Yamasani52c489c2012-03-28 11:42:42 -0700998 }
999
Adrian Roosb5e47222015-08-14 15:53:06 -07001000 @Override
1001 public void registerStrongAuthTracker(IStrongAuthTracker tracker) {
1002 checkPasswordReadPermission(UserHandle.USER_ALL);
1003 mStrongAuth.registerStrongAuthTracker(tracker);
1004 }
1005
1006 @Override
1007 public void unregisterStrongAuthTracker(IStrongAuthTracker tracker) {
1008 checkPasswordReadPermission(UserHandle.USER_ALL);
1009 mStrongAuth.unregisterStrongAuthTracker(tracker);
1010 }
1011
1012 @Override
1013 public void requireStrongAuth(int strongAuthReason, int userId) {
1014 checkWritePermission(userId);
1015 mStrongAuth.requireStrongAuth(strongAuthReason, userId);
1016 }
1017
Amith Yamasani52c489c2012-03-28 11:42:42 -07001018 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
Jim Miller187ec582013-04-15 18:27:54 -07001035 };
1036
Svetoslav Ganov6d2c0e52015-06-23 16:33:36 +00001037 // Reading these settings needs the contacts permission
1038 private static final String[] READ_CONTACTS_PROTECTED_SETTINGS = new String[] {
Jim Miller187ec582013-04-15 18:27:54 -07001039 Secure.LOCK_SCREEN_OWNER_INFO_ENABLED,
1040 Secure.LOCK_SCREEN_OWNER_INFO
1041 };
Paul Lawrence945490c2014-03-27 16:37:28 +00001042
Adrian Roos001b00d2015-02-24 17:08:48 +01001043 // 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,
Adrian Roos855fa302015-04-02 16:01:12 +02001047 LockPatternUtils.PASSWORD_TYPE_KEY,
Adrian Roos001b00d2015-02-24 17:08:48 +01001048 };
1049
Amith Yamasani072543f2015-02-13 11:09:45 -08001050 private static final String[] SETTINGS_TO_BACKUP = new String[] {
1051 Secure.LOCK_SCREEN_OWNER_INFO_ENABLED,
1052 Secure.LOCK_SCREEN_OWNER_INFO
1053 };
1054
Paul Lawrence945490c2014-03-27 16:37:28 +00001055 private IMountService getMountService() {
1056 final IBinder service = ServiceManager.getService("mount");
1057 if (service != null) {
1058 return IMountService.Stub.asInterface(service);
1059 }
1060 return null;
1061 }
Andres Morales8fa56652015-03-31 09:19:50 -07001062
Andres Morales301ea442015-04-17 09:15:47 -07001063 private class GateKeeperDiedRecipient implements IBinder.DeathRecipient {
1064 @Override
1065 public void binderDied() {
1066 mGateKeeperService.asBinder().unlinkToDeath(this, 0);
1067 mGateKeeperService = null;
1068 }
1069 }
1070
1071 private synchronized IGateKeeperService getGateKeeperService()
1072 throws RemoteException {
Andres Morales8fa56652015-03-31 09:19:50 -07001073 if (mGateKeeperService != null) {
1074 return mGateKeeperService;
1075 }
1076
1077 final IBinder service =
1078 ServiceManager.getService("android.service.gatekeeper.IGateKeeperService");
1079 if (service != null) {
Andres Morales301ea442015-04-17 09:15:47 -07001080 service.linkToDeath(new GateKeeperDiedRecipient(), 0);
Andres Morales8fa56652015-03-31 09:19:50 -07001081 mGateKeeperService = IGateKeeperService.Stub.asInterface(service);
1082 return mGateKeeperService;
1083 }
1084
1085 Slog.e(TAG, "Unable to acquire GateKeeperService");
1086 return null;
1087 }
Amith Yamasani52c489c2012-03-28 11:42:42 -07001088}