blob: 4477e8ba98093580c94553aed1fd0cad1edb26d5 [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;
Jeff Sharkeybd91e2f2016-03-22 15:32:31 -060045import android.os.Bundle;
Paul Lawrence945490c2014-03-27 16:37:28 +000046import android.os.IBinder;
Jeff Sharkeybd91e2f2016-03-22 15:32:31 -060047import android.os.IProgressListener;
48import android.os.Parcel;
Amith Yamasani52c489c2012-03-28 11:42:42 -070049import android.os.RemoteException;
Paul Lawrence945490c2014-03-27 16:37:28 +000050import android.os.storage.IMountService;
51import android.os.ServiceManager;
Amith Yamasanid1645f82012-06-12 11:53:26 -070052import android.os.SystemProperties;
Dianne Hackbornf02b60a2012-08-16 10:48:27 -070053import android.os.UserHandle;
Jim Miller187ec582013-04-15 18:27:54 -070054import android.os.UserManager;
Amith Yamasani52c489c2012-03-28 11:42:42 -070055import android.provider.Settings;
56import android.provider.Settings.Secure;
Jim Miller187ec582013-04-15 18:27:54 -070057import android.provider.Settings.SettingNotFoundException;
Jim Millerde1af082013-09-11 14:58:26 -070058import android.security.KeyStore;
Ricky Waidc283a82016-03-24 19:55:08 +000059import android.security.keystore.AndroidKeyStoreProvider;
60import android.security.keystore.KeyProperties;
61import android.security.keystore.KeyProtection;
Andres Morales23974272015-05-14 22:42:26 -070062import android.service.gatekeeper.GateKeeperResponse;
Andres Morales8fa56652015-03-31 09:19:50 -070063import android.service.gatekeeper.IGateKeeperService;
Amith Yamasani52c489c2012-03-28 11:42:42 -070064import android.text.TextUtils;
Jeff Sharkeybd91e2f2016-03-22 15:32:31 -060065import android.util.Log;
Amith Yamasani52c489c2012-03-28 11:42:42 -070066import android.util.Slog;
67
Amith Yamasani072543f2015-02-13 11:09:45 -080068import com.android.internal.util.ArrayUtils;
Jeff Sharkey7a96c392012-11-15 14:01:46 -080069import com.android.internal.widget.ILockSettings;
70import com.android.internal.widget.LockPatternUtils;
Andres Morales23974272015-05-14 22:42:26 -070071import com.android.internal.widget.VerifyCredentialResponse;
Andres Morales8fa56652015-03-31 09:19:50 -070072import com.android.server.LockSettingsStorage.CredentialHash;
Jeff Sharkey7a96c392012-11-15 14:01:46 -080073
Ricky Waidc283a82016-03-24 19:55:08 +000074import libcore.util.HexEncoding;
75
76import java.io.ByteArrayOutputStream;
77import java.io.FileNotFoundException;
78import java.io.IOException;
Paul Crowleyfaeb3eb2016-02-08 15:58:29 +000079import java.nio.charset.StandardCharsets;
Ricky Waidc283a82016-03-24 19:55:08 +000080import java.security.InvalidAlgorithmParameterException;
81import java.security.InvalidKeyException;
82import java.security.KeyStoreException;
Paul Crowleyfaeb3eb2016-02-08 15:58:29 +000083import java.security.MessageDigest;
84import java.security.NoSuchAlgorithmException;
Ricky Waidc283a82016-03-24 19:55:08 +000085import java.security.SecureRandom;
86import java.security.UnrecoverableKeyException;
87import java.security.cert.CertificateException;
Amith Yamasani52c489c2012-03-28 11:42:42 -070088import java.util.Arrays;
Jim Miller187ec582013-04-15 18:27:54 -070089import java.util.List;
Jeff Sharkeybd91e2f2016-03-22 15:32:31 -060090import java.util.concurrent.CountDownLatch;
91import java.util.concurrent.TimeUnit;
Amith Yamasani52c489c2012-03-28 11:42:42 -070092
Ricky Waidc283a82016-03-24 19:55:08 +000093import javax.crypto.BadPaddingException;
94import javax.crypto.Cipher;
95import javax.crypto.IllegalBlockSizeException;
96import javax.crypto.KeyGenerator;
97import javax.crypto.NoSuchPaddingException;
98import javax.crypto.SecretKey;
99import javax.crypto.spec.GCMParameterSpec;
100
Amith Yamasani52c489c2012-03-28 11:42:42 -0700101/**
102 * Keeps the lock pattern/password data and related settings for each user.
103 * Used by LockPatternUtils. Needs to be a service because Settings app also needs
104 * to be able to save lockscreen information for secondary users.
105 * @hide
106 */
107public class LockSettingsService extends ILockSettings.Stub {
Amith Yamasani52c489c2012-03-28 11:42:42 -0700108 private static final String TAG = "LockSettingsService";
Jim Miller4f93c582016-01-27 19:05:43 -0800109 private static final String PERMISSION = ACCESS_KEYGUARD_SECURE_STORAGE;
110 private static final Intent ACTION_NULL; // hack to ensure notification shows the bouncer
111 private static final int FBE_ENCRYPTED_NOTIFICATION = 0;
112 private static final boolean DEBUG = false;
Amith Yamasani52c489c2012-03-28 11:42:42 -0700113
Ricky Waidc283a82016-03-24 19:55:08 +0000114 private static final String PROFILE_KEY_NAME_ENCRYPT = "profile_key_name_encrypt_";
115 private static final String PROFILE_KEY_NAME_DECRYPT = "profile_key_name_decrypt_";
116 private static final int PROFILE_KEY_IV_SIZE = 12;
117 private static final String SEPARATE_PROFILE_CHALLENGE_KEY = "lockscreen.profilechallenge";
118 private final Object mSeparateChallengeLock = new Object();
119
Amith Yamasani52c489c2012-03-28 11:42:42 -0700120 private final Context mContext;
Adrian Roos261d5ab2014-10-29 14:42:38 +0100121 private final LockSettingsStorage mStorage;
Rakesh Iyera7aa4d62016-01-19 17:27:23 -0800122 private final LockSettingsStrongAuth mStrongAuth;
Victor Changa0940d32016-05-16 19:36:08 +0100123 private final SynchronizedStrongAuthTracker mStrongAuthTracker;
Adrian Roos261d5ab2014-10-29 14:42:38 +0100124
Jim Millerde1af082013-09-11 14:58:26 -0700125 private LockPatternUtils mLockPatternUtils;
Paul Lawrence945490c2014-03-27 16:37:28 +0000126 private boolean mFirstCallToVold;
Andres Morales8fa56652015-03-31 09:19:50 -0700127 private IGateKeeperService mGateKeeperService;
Jim Miller4f93c582016-01-27 19:05:43 -0800128 private NotificationManager mNotificationManager;
129 private UserManager mUserManager;
130
131 static {
132 // Just launch the home screen, which happens anyway
133 ACTION_NULL = new Intent(Intent.ACTION_MAIN);
134 ACTION_NULL.addCategory(Intent.CATEGORY_HOME);
135 }
Amith Yamasani52c489c2012-03-28 11:42:42 -0700136
Andres Morales23974272015-05-14 22:42:26 -0700137 private interface CredentialUtil {
138 void setCredential(String credential, String savedCredential, int userId)
139 throws RemoteException;
140 byte[] toHash(String credential, int userId);
Andres Morales59ef1262015-06-26 13:56:39 -0700141 String adjustForKeystore(String credential);
Andres Morales23974272015-05-14 22:42:26 -0700142 }
143
Jim Miller4f93c582016-01-27 19:05:43 -0800144 // This class manages life cycle events for encrypted users on File Based Encryption (FBE)
145 // devices. The most basic of these is to show/hide notifications about missing features until
146 // the user unlocks the account and credential-encrypted storage is available.
147 public static final class Lifecycle extends SystemService {
148 private LockSettingsService mLockSettingsService;
149
150 public Lifecycle(Context context) {
151 super(context);
152 }
153
154 @Override
155 public void onStart() {
Ricky Waidc283a82016-03-24 19:55:08 +0000156 AndroidKeyStoreProvider.install();
Jim Miller4f93c582016-01-27 19:05:43 -0800157 mLockSettingsService = new LockSettingsService(getContext());
158 publishBinderService("lock_settings", mLockSettingsService);
159 }
160
161 @Override
162 public void onBootPhase(int phase) {
163 if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) {
Kenny Guyb1b30262016-02-09 16:02:35 +0000164 mLockSettingsService.maybeShowEncryptionNotifications();
Jim Miller4f93c582016-01-27 19:05:43 -0800165 } else if (phase == SystemService.PHASE_BOOT_COMPLETED) {
166 // TODO
167 }
168 }
169
170 @Override
171 public void onUnlockUser(int userHandle) {
172 mLockSettingsService.onUnlockUser(userHandle);
173 }
174
175 @Override
176 public void onCleanupUser(int userHandle) {
177 mLockSettingsService.onCleanupUser(userHandle);
178 }
179 }
180
Victor Changa0940d32016-05-16 19:36:08 +0100181 private class SynchronizedStrongAuthTracker extends LockPatternUtils.StrongAuthTracker {
182 public SynchronizedStrongAuthTracker(Context context) {
183 super(context);
184 }
185
186 @Override
187 protected void handleStrongAuthRequiredChanged(int strongAuthFlags, int userId) {
188 synchronized (this) {
189 super.handleStrongAuthRequiredChanged(strongAuthFlags, userId);
190 }
191 }
192
193 @Override
194 public int getStrongAuthForUser(int userId) {
195 synchronized (this) {
196 return super.getStrongAuthForUser(userId);
197 }
198 }
199
200 void register() {
201 mStrongAuth.registerStrongAuthTracker(this.mStub);
202 }
203 }
204
Ricky Waidc283a82016-03-24 19:55:08 +0000205 /**
206 * Tie managed profile to primary profile if it is in unified mode and not tied before.
207 *
208 * @param managedUserId Managed profile user Id
209 * @param managedUserPassword Managed profile original password (when it has separated lock).
210 * NULL when it does not have a separated lock before.
211 */
212 public void tieManagedProfileLockIfNecessary(int managedUserId, String managedUserPassword) {
213 if (DEBUG) Slog.v(TAG, "Check child profile lock for user: " + managedUserId);
214 // Only for managed profile
215 if (!UserManager.get(mContext).getUserInfo(managedUserId).isManagedProfile()) {
216 return;
217 }
218 // Do not tie managed profile when work challenge is enabled
219 if (mLockPatternUtils.isSeparateProfileChallengeEnabled(managedUserId)) {
220 return;
221 }
222 // Do not tie managed profile to parent when it's done already
223 if (mStorage.hasChildProfileLock(managedUserId)) {
224 return;
225 }
226 // Do not tie it to parent when parent does not have a screen lock
227 final int parentId = mUserManager.getProfileParent(managedUserId).id;
228 if (!mStorage.hasPassword(parentId) && !mStorage.hasPattern(parentId)) {
229 if (DEBUG) Slog.v(TAG, "Parent does not have a screen lock");
230 return;
231 }
232 if (DEBUG) Slog.v(TAG, "Tie managed profile to parent now!");
233 byte[] randomLockSeed = new byte[] {};
234 try {
235 randomLockSeed = SecureRandom.getInstance("SHA1PRNG").generateSeed(40);
236 String newPassword = String.valueOf(HexEncoding.encode(randomLockSeed));
237 setLockPasswordInternal(newPassword, managedUserPassword, managedUserId);
238 tieProfileLockToParent(managedUserId, newPassword);
239 } catch (NoSuchAlgorithmException | RemoteException e) {
240 Slog.e(TAG, "Fail to tie managed profile", e);
241 // Nothing client can do to fix this issue, so we do not throw exception out
242 }
243 }
244
Amith Yamasani52c489c2012-03-28 11:42:42 -0700245 public LockSettingsService(Context context) {
246 mContext = context;
Rakesh Iyera7aa4d62016-01-19 17:27:23 -0800247 mStrongAuth = new LockSettingsStrongAuth(context);
Amith Yamasani52c489c2012-03-28 11:42:42 -0700248 // Open the database
Jim Millerde1af082013-09-11 14:58:26 -0700249
250 mLockPatternUtils = new LockPatternUtils(context);
Paul Lawrence945490c2014-03-27 16:37:28 +0000251 mFirstCallToVold = true;
Robin Leef0246a82014-08-13 09:50:25 +0100252
253 IntentFilter filter = new IntentFilter();
254 filter.addAction(Intent.ACTION_USER_ADDED);
Adrian Roos3dcae682014-10-29 14:43:56 +0100255 filter.addAction(Intent.ACTION_USER_STARTING);
Adrian Roosdb0f76e2015-01-07 22:19:38 +0100256 filter.addAction(Intent.ACTION_USER_REMOVED);
Robin Leef0246a82014-08-13 09:50:25 +0100257 mContext.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, filter, null, null);
Adrian Roos261d5ab2014-10-29 14:42:38 +0100258
259 mStorage = new LockSettingsStorage(context, new LockSettingsStorage.Callback() {
260 @Override
261 public void initialize(SQLiteDatabase db) {
262 // Get the lockscreen default from a system property, if available
263 boolean lockScreenDisable = SystemProperties.getBoolean(
264 "ro.lockscreen.disable.default", false);
265 if (lockScreenDisable) {
266 mStorage.writeKeyValue(db, LockPatternUtils.DISABLE_LOCKSCREEN_KEY, "1", 0);
267 }
268 }
269 });
Jim Miller4f93c582016-01-27 19:05:43 -0800270 mNotificationManager = (NotificationManager)
271 mContext.getSystemService(Context.NOTIFICATION_SERVICE);
272 mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
Victor Changa0940d32016-05-16 19:36:08 +0100273 mStrongAuthTracker = new SynchronizedStrongAuthTracker(mContext);
274 mStrongAuthTracker.register();
275
Jim Miller4f93c582016-01-27 19:05:43 -0800276 }
277
278 /**
279 * If the account is credential-encrypted, show notification requesting the user to unlock
280 * the device.
281 */
Kenny Guyb1b30262016-02-09 16:02:35 +0000282 private void maybeShowEncryptionNotifications() {
283 final List<UserInfo> users = mUserManager.getUsers();
284 for (int i = 0; i < users.size(); i++) {
285 UserInfo user = users.get(i);
286 UserHandle userHandle = user.getUserHandle();
Jeff Sharkeyce18c812016-04-27 16:00:41 -0600287 if (!mUserManager.isUserUnlockingOrUnlocked(userHandle)) {
Kenny Guyb1b30262016-02-09 16:02:35 +0000288 if (!user.isManagedProfile()) {
289 showEncryptionNotification(userHandle);
290 } else {
291 UserInfo parent = mUserManager.getProfileParent(user.id);
Benjamin Franzf52709c2016-04-01 15:49:13 +0100292 if (parent != null &&
Jeff Sharkeyce18c812016-04-27 16:00:41 -0600293 mUserManager.isUserUnlockingOrUnlocked(parent.getUserHandle()) &&
Benjamin Franzf52709c2016-04-01 15:49:13 +0100294 !mUserManager.isQuietModeEnabled(userHandle)) {
Kenny Guyb1b30262016-02-09 16:02:35 +0000295 // Only show notifications for managed profiles once their parent
296 // user is unlocked.
297 showEncryptionNotificationForProfile(userHandle);
298 }
Jim Miller4f93c582016-01-27 19:05:43 -0800299 }
300 }
Jim Miller4f93c582016-01-27 19:05:43 -0800301 }
302 }
303
Kenny Guyb1b30262016-02-09 16:02:35 +0000304 private void showEncryptionNotificationForProfile(UserHandle user) {
305 Resources r = mContext.getResources();
306 CharSequence title = r.getText(
307 com.android.internal.R.string.user_encrypted_title);
308 CharSequence message = r.getText(
309 com.android.internal.R.string.profile_encrypted_message);
310 CharSequence detail = r.getText(
311 com.android.internal.R.string.profile_encrypted_detail);
312
313 final KeyguardManager km = (KeyguardManager) mContext.getSystemService(KEYGUARD_SERVICE);
314 final Intent unlockIntent = km.createConfirmDeviceCredentialIntent(null, null, user.getIdentifier());
315 if (unlockIntent == null) {
316 return;
317 }
318 unlockIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
319 PendingIntent intent = PendingIntent.getActivity(mContext, 0, unlockIntent,
320 PendingIntent.FLAG_UPDATE_CURRENT);
321
322 showEncryptionNotification(user, title, message, detail, intent);
323 }
324
Jim Miller4f93c582016-01-27 19:05:43 -0800325 private void showEncryptionNotification(UserHandle user) {
Jim Miller4f93c582016-01-27 19:05:43 -0800326 Resources r = mContext.getResources();
327 CharSequence title = r.getText(
328 com.android.internal.R.string.user_encrypted_title);
329 CharSequence message = r.getText(
330 com.android.internal.R.string.user_encrypted_message);
331 CharSequence detail = r.getText(
332 com.android.internal.R.string.user_encrypted_detail);
333
334 PendingIntent intent = PendingIntent.getBroadcast(mContext, 0, ACTION_NULL,
335 PendingIntent.FLAG_UPDATE_CURRENT);
336
Kenny Guyb1b30262016-02-09 16:02:35 +0000337 showEncryptionNotification(user, title, message, detail, intent);
338 }
339
340 private void showEncryptionNotification(UserHandle user, CharSequence title, CharSequence message,
341 CharSequence detail, PendingIntent intent) {
342 if (DEBUG) Slog.v(TAG, "showing encryption notification, user: " + user.getIdentifier());
Jim Miller4f93c582016-01-27 19:05:43 -0800343 Notification notification = new Notification.Builder(mContext)
Jim Millerb1135b72016-02-02 17:08:56 -0800344 .setSmallIcon(com.android.internal.R.drawable.ic_user_secure)
Jim Miller4f93c582016-01-27 19:05:43 -0800345 .setWhen(0)
346 .setOngoing(true)
347 .setTicker(title)
348 .setDefaults(0) // please be quiet
349 .setPriority(Notification.PRIORITY_MAX)
350 .setColor(mContext.getColor(
351 com.android.internal.R.color.system_notification_accent_color))
352 .setContentTitle(title)
353 .setContentText(message)
Selim Cinek0f9dd1e2016-04-05 17:03:40 -0700354 .setSubText(detail)
Jim Miller4f93c582016-01-27 19:05:43 -0800355 .setVisibility(Notification.VISIBILITY_PUBLIC)
356 .setContentIntent(intent)
357 .build();
358 mNotificationManager.notifyAsUser(null, FBE_ENCRYPTED_NOTIFICATION, notification, user);
359 }
360
361 public void hideEncryptionNotification(UserHandle userHandle) {
362 if (DEBUG) Slog.v(TAG, "hide encryption notification, user: "+ userHandle.getIdentifier());
363 mNotificationManager.cancelAsUser(null, FBE_ENCRYPTED_NOTIFICATION, userHandle);
364 }
365
366 public void onCleanupUser(int userId) {
367 hideEncryptionNotification(new UserHandle(userId));
368 }
369
Kenny Guyb1b30262016-02-09 16:02:35 +0000370 public void onUnlockUser(int userId) {
Ricky Wai84812582016-05-10 20:11:59 +0100371 // Hide notification first, as tie managed profile lock takes time
Kenny Guyb1b30262016-02-09 16:02:35 +0000372 hideEncryptionNotification(new UserHandle(userId));
Ricky Wai84812582016-05-10 20:11:59 +0100373 tieManagedProfileLockIfNecessary(userId, null);
Kenny Guyb1b30262016-02-09 16:02:35 +0000374
375 // Now we have unlocked the parent user we should show notifications
376 // about any profiles that exist.
377 List<UserInfo> profiles = mUserManager.getProfiles(userId);
378 for (int i = 0; i < profiles.size(); i++) {
379 UserInfo profile = profiles.get(i);
380 if (profile.isManagedProfile()) {
381 UserHandle userHandle = profile.getUserHandle();
Jeff Sharkeyce18c812016-04-27 16:00:41 -0600382 if (!mUserManager.isUserUnlockingOrUnlocked(userHandle) &&
Benjamin Franzf52709c2016-04-01 15:49:13 +0100383 !mUserManager.isQuietModeEnabled(userHandle)) {
Kenny Guyb1b30262016-02-09 16:02:35 +0000384 showEncryptionNotificationForProfile(userHandle);
385 }
386 }
387 }
Amith Yamasani52c489c2012-03-28 11:42:42 -0700388 }
389
Robin Leef0246a82014-08-13 09:50:25 +0100390 private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
391 @Override
392 public void onReceive(Context context, Intent intent) {
Robin Lee1096cf82014-09-01 16:52:47 +0100393 if (Intent.ACTION_USER_ADDED.equals(intent.getAction())) {
Chad Brubaker83ce0952015-05-12 13:00:02 -0700394 // Notify keystore that a new user was added.
Robin Leef0246a82014-08-13 09:50:25 +0100395 final int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
Robin Lee49d810c2014-09-23 13:50:22 +0100396 final KeyStore ks = KeyStore.getInstance();
Ricky Waidc283a82016-03-24 19:55:08 +0000397 final UserInfo parentInfo = mUserManager.getProfileParent(userHandle);
Chad Brubaker83ce0952015-05-12 13:00:02 -0700398 final int parentHandle = parentInfo != null ? parentInfo.id : -1;
399 ks.onUserAdded(userHandle, parentHandle);
Adrian Roos3dcae682014-10-29 14:43:56 +0100400 } else if (Intent.ACTION_USER_STARTING.equals(intent.getAction())) {
401 final int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
402 mStorage.prefetchUser(userHandle);
Adrian Roosdb0f76e2015-01-07 22:19:38 +0100403 } else if (Intent.ACTION_USER_REMOVED.equals(intent.getAction())) {
404 final int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
405 if (userHandle > 0) {
406 removeUser(userHandle);
407 }
Robin Leef0246a82014-08-13 09:50:25 +0100408 }
409 }
410 };
411
Jim Miller4f93c582016-01-27 19:05:43 -0800412 @Override // binder interface
Amith Yamasani52c489c2012-03-28 11:42:42 -0700413 public void systemReady() {
414 migrateOldData();
Andres Morales301ea442015-04-17 09:15:47 -0700415 try {
416 getGateKeeperService();
417 } catch (RemoteException e) {
418 Slog.e(TAG, "Failure retrieving IGateKeeperService", e);
419 }
Xiaohui Chen7c696362015-09-16 09:56:14 -0700420 // TODO: maybe skip this for split system user mode.
421 mStorage.prefetchUser(UserHandle.USER_SYSTEM);
Amith Yamasani52c489c2012-03-28 11:42:42 -0700422 }
423
424 private void migrateOldData() {
425 try {
Jim Miller187ec582013-04-15 18:27:54 -0700426 // These Settings moved before multi-user was enabled, so we only have to do it for the
427 // root user.
428 if (getString("migrated", null, 0) == null) {
429 final ContentResolver cr = mContext.getContentResolver();
430 for (String validSetting : VALID_SETTINGS) {
431 String value = Settings.Secure.getString(cr, validSetting);
432 if (value != null) {
433 setString(validSetting, value, 0);
434 }
435 }
436 // No need to move the password / pattern files. They're already in the right place.
437 setString("migrated", "true", 0);
438 Slog.i(TAG, "Migrated lock settings to new location");
Amith Yamasani52c489c2012-03-28 11:42:42 -0700439 }
440
Jim Miller187ec582013-04-15 18:27:54 -0700441 // These Settings changed after multi-user was enabled, hence need to be moved per user.
442 if (getString("migrated_user_specific", null, 0) == null) {
Jim Miller187ec582013-04-15 18:27:54 -0700443 final ContentResolver cr = mContext.getContentResolver();
Ricky Waidc283a82016-03-24 19:55:08 +0000444 List<UserInfo> users = mUserManager.getUsers();
Jim Miller187ec582013-04-15 18:27:54 -0700445 for (int user = 0; user < users.size(); user++) {
Jim Miller2d8ecf9d2013-04-22 17:17:03 -0700446 // Migrate owner info
447 final int userId = users.get(user).id;
448 final String OWNER_INFO = Secure.LOCK_SCREEN_OWNER_INFO;
449 String ownerInfo = Settings.Secure.getStringForUser(cr, OWNER_INFO, userId);
Jim Miller325c5672016-03-01 19:21:47 -0800450 if (!TextUtils.isEmpty(ownerInfo)) {
Jim Miller2d8ecf9d2013-04-22 17:17:03 -0700451 setString(OWNER_INFO, ownerInfo, userId);
Jim Miller325c5672016-03-01 19:21:47 -0800452 Settings.Secure.putStringForUser(cr, OWNER_INFO, "", userId);
Jim Miller2d8ecf9d2013-04-22 17:17:03 -0700453 }
Jim Miller187ec582013-04-15 18:27:54 -0700454
Jim Miller2d8ecf9d2013-04-22 17:17:03 -0700455 // Migrate owner info enabled. Note there was a bug where older platforms only
456 // stored this value if the checkbox was toggled at least once. The code detects
457 // this case by handling the exception.
458 final String OWNER_INFO_ENABLED = Secure.LOCK_SCREEN_OWNER_INFO_ENABLED;
459 boolean enabled;
460 try {
461 int ivalue = Settings.Secure.getIntForUser(cr, OWNER_INFO_ENABLED, userId);
462 enabled = ivalue != 0;
463 setLong(OWNER_INFO_ENABLED, enabled ? 1 : 0, userId);
464 } catch (SettingNotFoundException e) {
465 // Setting was never stored. Store it if the string is not empty.
466 if (!TextUtils.isEmpty(ownerInfo)) {
467 setLong(OWNER_INFO_ENABLED, 1, userId);
Jim Miller187ec582013-04-15 18:27:54 -0700468 }
469 }
Jim Miller2d8ecf9d2013-04-22 17:17:03 -0700470 Settings.Secure.putIntForUser(cr, OWNER_INFO_ENABLED, 0, userId);
Amith Yamasani52c489c2012-03-28 11:42:42 -0700471 }
Jim Miller187ec582013-04-15 18:27:54 -0700472 // No need to move the password / pattern files. They're already in the right place.
473 setString("migrated_user_specific", "true", 0);
474 Slog.i(TAG, "Migrated per-user lock settings to new location");
Amith Yamasani52c489c2012-03-28 11:42:42 -0700475 }
Adrian Roos230635e2015-01-07 20:50:29 +0100476
477 // Migrates biometric weak such that the fallback mechanism becomes the primary.
478 if (getString("migrated_biometric_weak", null, 0) == null) {
Ricky Waidc283a82016-03-24 19:55:08 +0000479 List<UserInfo> users = mUserManager.getUsers();
Adrian Roos230635e2015-01-07 20:50:29 +0100480 for (int i = 0; i < users.size(); i++) {
481 int userId = users.get(i).id;
482 long type = getLong(LockPatternUtils.PASSWORD_TYPE_KEY,
483 DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED,
484 userId);
485 long alternateType = getLong(LockPatternUtils.PASSWORD_TYPE_ALTERNATE_KEY,
486 DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED,
487 userId);
488 if (type == DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK) {
489 setLong(LockPatternUtils.PASSWORD_TYPE_KEY,
490 alternateType,
491 userId);
492 }
493 setLong(LockPatternUtils.PASSWORD_TYPE_ALTERNATE_KEY,
494 DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED,
495 userId);
496 }
497 setString("migrated_biometric_weak", "true", 0);
498 Slog.i(TAG, "Migrated biometric weak to use the fallback instead");
499 }
Adrian Roos43830582015-04-21 16:04:43 -0700500
501 // Migrates lockscreen.disabled. Prior to M, the flag was ignored when more than one
502 // user was present on the system, so if we're upgrading to M and there is more than one
503 // user we disable the flag to remain consistent.
504 if (getString("migrated_lockscreen_disabled", null, 0) == null) {
Ricky Waidc283a82016-03-24 19:55:08 +0000505 final List<UserInfo> users = mUserManager.getUsers();
Adrian Roos43830582015-04-21 16:04:43 -0700506 final int userCount = users.size();
507 int switchableUsers = 0;
508 for (int i = 0; i < userCount; i++) {
509 if (users.get(i).supportsSwitchTo()) {
510 switchableUsers++;
511 }
512 }
513
514 if (switchableUsers > 1) {
515 for (int i = 0; i < userCount; i++) {
516 int id = users.get(i).id;
517
518 if (getBoolean(LockPatternUtils.DISABLE_LOCKSCREEN_KEY, false, id)) {
519 setBoolean(LockPatternUtils.DISABLE_LOCKSCREEN_KEY, false, id);
520 }
521 }
522 }
523
524 setString("migrated_lockscreen_disabled", "true", 0);
525 Slog.i(TAG, "Migrated lockscreen disabled flag");
526 }
Amith Yamasani52c489c2012-03-28 11:42:42 -0700527 } catch (RemoteException re) {
Jim Miller187ec582013-04-15 18:27:54 -0700528 Slog.e(TAG, "Unable to migrate old data", re);
Amith Yamasani52c489c2012-03-28 11:42:42 -0700529 }
530 }
531
Jim Miller5ecd8112013-01-09 18:50:26 -0800532 private final void checkWritePermission(int userId) {
Jim Miller505329b2013-11-08 13:25:36 -0800533 mContext.enforceCallingOrSelfPermission(PERMISSION, "LockSettingsWrite");
Amith Yamasani52c489c2012-03-28 11:42:42 -0700534 }
535
Jim Miller5ecd8112013-01-09 18:50:26 -0800536 private final void checkPasswordReadPermission(int userId) {
Jim Miller505329b2013-11-08 13:25:36 -0800537 mContext.enforceCallingOrSelfPermission(PERMISSION, "LockSettingsRead");
Amith Yamasani52c489c2012-03-28 11:42:42 -0700538 }
539
Jim Miller158fe192013-04-17 15:23:55 -0700540 private final void checkReadPermission(String requestedKey, int userId) {
Amith Yamasani52c489c2012-03-28 11:42:42 -0700541 final int callingUid = Binder.getCallingUid();
Adrian Roos001b00d2015-02-24 17:08:48 +0100542
Svetoslav Ganov6d2c0e52015-06-23 16:33:36 +0000543 for (int i = 0; i < READ_CONTACTS_PROTECTED_SETTINGS.length; i++) {
544 String key = READ_CONTACTS_PROTECTED_SETTINGS[i];
545 if (key.equals(requestedKey) && mContext.checkCallingOrSelfPermission(READ_CONTACTS)
Jim Miller158fe192013-04-17 15:23:55 -0700546 != PackageManager.PERMISSION_GRANTED) {
547 throw new SecurityException("uid=" + callingUid
Svetoslav Ganov6d2c0e52015-06-23 16:33:36 +0000548 + " needs permission " + READ_CONTACTS + " to read "
Jim Miller158fe192013-04-17 15:23:55 -0700549 + requestedKey + " for user " + userId);
550 }
Amith Yamasani52c489c2012-03-28 11:42:42 -0700551 }
Adrian Roos001b00d2015-02-24 17:08:48 +0100552
553 for (int i = 0; i < READ_PASSWORD_PROTECTED_SETTINGS.length; i++) {
554 String key = READ_PASSWORD_PROTECTED_SETTINGS[i];
555 if (key.equals(requestedKey) && mContext.checkCallingOrSelfPermission(PERMISSION)
556 != PackageManager.PERMISSION_GRANTED) {
557 throw new SecurityException("uid=" + callingUid
558 + " needs permission " + PERMISSION + " to read "
559 + requestedKey + " for user " + userId);
560 }
561 }
Amith Yamasani52c489c2012-03-28 11:42:42 -0700562 }
563
564 @Override
Ricky Waidc283a82016-03-24 19:55:08 +0000565 public boolean getSeparateProfileChallengeEnabled(int userId) throws RemoteException {
566 synchronized (mSeparateChallengeLock) {
567 return getBoolean(SEPARATE_PROFILE_CHALLENGE_KEY, false, userId);
568 }
569 }
570
571 @Override
572 public void setSeparateProfileChallengeEnabled(int userId, boolean enabled,
573 String managedUserPassword) throws RemoteException {
574 synchronized (mSeparateChallengeLock) {
575 setBoolean(SEPARATE_PROFILE_CHALLENGE_KEY, enabled, userId);
576 if (enabled) {
577 mStorage.removeChildProfileLock(userId);
578 removeKeystoreProfileKey(userId);
579 } else {
580 tieManagedProfileLockIfNecessary(userId, managedUserPassword);
581 }
582 }
583 }
584
585 @Override
Amith Yamasani52c489c2012-03-28 11:42:42 -0700586 public void setBoolean(String key, boolean value, int userId) throws RemoteException {
587 checkWritePermission(userId);
Adrian Roos261d5ab2014-10-29 14:42:38 +0100588 setStringUnchecked(key, userId, value ? "1" : "0");
Amith Yamasani52c489c2012-03-28 11:42:42 -0700589 }
590
591 @Override
592 public void setLong(String key, long value, int userId) throws RemoteException {
593 checkWritePermission(userId);
Adrian Roos261d5ab2014-10-29 14:42:38 +0100594 setStringUnchecked(key, userId, Long.toString(value));
Amith Yamasani52c489c2012-03-28 11:42:42 -0700595 }
596
597 @Override
598 public void setString(String key, String value, int userId) throws RemoteException {
599 checkWritePermission(userId);
Adrian Roos261d5ab2014-10-29 14:42:38 +0100600 setStringUnchecked(key, userId, value);
601 }
Amith Yamasani52c489c2012-03-28 11:42:42 -0700602
Adrian Roos261d5ab2014-10-29 14:42:38 +0100603 private void setStringUnchecked(String key, int userId, String value) {
604 mStorage.writeKeyValue(key, value, userId);
Amith Yamasani072543f2015-02-13 11:09:45 -0800605 if (ArrayUtils.contains(SETTINGS_TO_BACKUP, key)) {
606 BackupManager.dataChanged("com.android.providers.settings");
607 }
Amith Yamasani52c489c2012-03-28 11:42:42 -0700608 }
609
610 @Override
611 public boolean getBoolean(String key, boolean defaultValue, int userId) throws RemoteException {
Jim Miller158fe192013-04-17 15:23:55 -0700612 checkReadPermission(key, userId);
Adrian Roos9dd16eb2015-01-08 16:20:49 +0100613 String value = getStringUnchecked(key, null, userId);
Amith Yamasani52c489c2012-03-28 11:42:42 -0700614 return TextUtils.isEmpty(value) ?
615 defaultValue : (value.equals("1") || value.equals("true"));
616 }
617
618 @Override
619 public long getLong(String key, long defaultValue, int userId) throws RemoteException {
Jim Miller158fe192013-04-17 15:23:55 -0700620 checkReadPermission(key, userId);
Amith Yamasani52c489c2012-03-28 11:42:42 -0700621
Adrian Roos9dd16eb2015-01-08 16:20:49 +0100622 String value = getStringUnchecked(key, null, userId);
Amith Yamasani52c489c2012-03-28 11:42:42 -0700623 return TextUtils.isEmpty(value) ? defaultValue : Long.parseLong(value);
624 }
625
626 @Override
627 public String getString(String key, String defaultValue, int userId) throws RemoteException {
Jim Miller158fe192013-04-17 15:23:55 -0700628 checkReadPermission(key, userId);
Amith Yamasani52c489c2012-03-28 11:42:42 -0700629
Adrian Roos9dd16eb2015-01-08 16:20:49 +0100630 return getStringUnchecked(key, defaultValue, userId);
631 }
632
633 public String getStringUnchecked(String key, String defaultValue, int userId) {
634 if (Settings.Secure.LOCK_PATTERN_ENABLED.equals(key)) {
Adrian Roos7811d9f2015-07-27 15:10:13 -0700635 long ident = Binder.clearCallingIdentity();
636 try {
637 return mLockPatternUtils.isLockPatternEnabled(userId) ? "1" : "0";
638 } finally {
639 Binder.restoreCallingIdentity(ident);
640 }
Adrian Roos9dd16eb2015-01-08 16:20:49 +0100641 }
642
Bryce Lee46145962015-12-14 14:39:10 -0800643 if (LockPatternUtils.LEGACY_LOCK_PATTERN_ENABLED.equals(key)) {
644 key = Settings.Secure.LOCK_PATTERN_ENABLED;
645 }
646
Adrian Roos261d5ab2014-10-29 14:42:38 +0100647 return mStorage.readKeyValue(key, defaultValue, userId);
Amith Yamasani52c489c2012-03-28 11:42:42 -0700648 }
649
Adrian Roos4f788452014-05-22 20:45:59 +0200650 @Override
Amith Yamasani52c489c2012-03-28 11:42:42 -0700651 public boolean havePassword(int userId) throws RemoteException {
652 // Do we need a permissions check here?
Adrian Roos261d5ab2014-10-29 14:42:38 +0100653 return mStorage.hasPassword(userId);
Amith Yamasani52c489c2012-03-28 11:42:42 -0700654 }
655
656 @Override
657 public boolean havePattern(int userId) throws RemoteException {
658 // Do we need a permissions check here?
Adrian Roos261d5ab2014-10-29 14:42:38 +0100659 return mStorage.hasPattern(userId);
Amith Yamasani52c489c2012-03-28 11:42:42 -0700660 }
661
Chad Brubakera91a8502015-05-07 10:02:22 -0700662 private void setKeystorePassword(String password, int userHandle) {
Robin Leef0246a82014-08-13 09:50:25 +0100663 final KeyStore ks = KeyStore.getInstance();
Ricky Waidc283a82016-03-24 19:55:08 +0000664 ks.onUserPasswordChanged(userHandle, password);
Chad Brubakera91a8502015-05-07 10:02:22 -0700665 }
666
667 private void unlockKeystore(String password, int userHandle) {
Ricky Waidc283a82016-03-24 19:55:08 +0000668 if (DEBUG) Slog.v(TAG, "Unlock keystore for user: " + userHandle);
Chad Brubakera91a8502015-05-07 10:02:22 -0700669 final KeyStore ks = KeyStore.getInstance();
Ricky Waidc283a82016-03-24 19:55:08 +0000670 ks.unlock(userHandle, password);
671 }
Chad Brubakera91a8502015-05-07 10:02:22 -0700672
Ricky Waidc283a82016-03-24 19:55:08 +0000673 private String getDecryptedPasswordForTiedProfile(int userId)
674 throws KeyStoreException, UnrecoverableKeyException,
675 NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException,
676 InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException,
677 CertificateException, IOException {
678 if (DEBUG) Slog.v(TAG, "Unlock keystore for child profile");
679 byte[] storedData = mStorage.readChildProfileLock(userId);
680 if (storedData == null) {
681 throw new FileNotFoundException("Child profile lock file not found");
682 }
683 byte[] iv = Arrays.copyOfRange(storedData, 0, PROFILE_KEY_IV_SIZE);
684 byte[] encryptedPassword = Arrays.copyOfRange(storedData, PROFILE_KEY_IV_SIZE,
685 storedData.length);
686 byte[] decryptionResult;
687 java.security.KeyStore keyStore = java.security.KeyStore.getInstance("AndroidKeyStore");
688 keyStore.load(null);
689 SecretKey decryptionKey = (SecretKey) keyStore.getKey(
690 PROFILE_KEY_NAME_DECRYPT + userId, null);
691
692 Cipher cipher = Cipher.getInstance(KeyProperties.KEY_ALGORITHM_AES + "/"
693 + KeyProperties.BLOCK_MODE_GCM + "/" + KeyProperties.ENCRYPTION_PADDING_NONE);
694
695 cipher.init(Cipher.DECRYPT_MODE, decryptionKey, new GCMParameterSpec(128, iv));
696 decryptionResult = cipher.doFinal(encryptedPassword);
697 return new String(decryptionResult, StandardCharsets.UTF_8);
698 }
699
700 private void unlockChildProfile(int profileHandle) throws RemoteException {
701 try {
702 doVerifyPassword(getDecryptedPasswordForTiedProfile(profileHandle), false,
703 0 /* no challenge */, profileHandle);
704 } catch (UnrecoverableKeyException | InvalidKeyException | KeyStoreException
705 | NoSuchAlgorithmException | NoSuchPaddingException
706 | InvalidAlgorithmParameterException | IllegalBlockSizeException
707 | BadPaddingException | CertificateException | IOException e) {
708 if (e instanceof FileNotFoundException) {
709 Slog.i(TAG, "Child profile key not found");
Clara Bayarri0a587d22016-02-23 14:49:41 -0800710 } else {
Ricky Waidc283a82016-03-24 19:55:08 +0000711 Slog.e(TAG, "Failed to decrypt child profile key", e);
Clara Bayarri0a587d22016-02-23 14:49:41 -0800712 }
Jim Millerde1af082013-09-11 14:58:26 -0700713 }
714 }
715
Paul Crowleyfaeb3eb2016-02-08 15:58:29 +0000716 private void unlockUser(int userId, byte[] token, byte[] secret) {
Jeff Sharkeybd91e2f2016-03-22 15:32:31 -0600717 // TODO: make this method fully async so we can update UI with progress strings
718 final CountDownLatch latch = new CountDownLatch(1);
719 final IProgressListener listener = new IProgressListener.Stub() {
720 @Override
721 public void onStarted(int id, Bundle extras) throws RemoteException {
Jeff Sharkeyfd241082016-04-19 15:58:24 -0600722 Log.d(TAG, "unlockUser started");
Jeff Sharkeybd91e2f2016-03-22 15:32:31 -0600723 }
724
725 @Override
726 public void onProgress(int id, int progress, Bundle extras) throws RemoteException {
Jeff Sharkeyfd241082016-04-19 15:58:24 -0600727 Log.d(TAG, "unlockUser progress " + progress);
Jeff Sharkeybd91e2f2016-03-22 15:32:31 -0600728 }
729
730 @Override
731 public void onFinished(int id, Bundle extras) throws RemoteException {
Jeff Sharkeyfd241082016-04-19 15:58:24 -0600732 Log.d(TAG, "unlockUser finished");
Jeff Sharkeybd91e2f2016-03-22 15:32:31 -0600733 latch.countDown();
734 }
735 };
736
Ricky Wai7881cf82016-04-15 17:20:12 +0100737 // Turn off quite mode if it's enabled, only managed profile can return true for now, it
738 // will return false if it is not a managed profile.
739 if (mUserManager.isQuietModeEnabled(new UserHandle(userId))) {
Benjamin Franzf02420c2016-04-04 18:52:21 +0100740 mUserManager.setQuietModeEnabled(userId, false);
741 }
742
Jeff Sharkey8924e872015-11-30 12:52:10 -0700743 try {
Jeff Sharkeybd91e2f2016-03-22 15:32:31 -0600744 ActivityManagerNative.getDefault().unlockUser(userId, token, secret, listener);
Jeff Sharkey8924e872015-11-30 12:52:10 -0700745 } catch (RemoteException e) {
746 throw e.rethrowAsRuntimeException();
747 }
Jeff Sharkeybd91e2f2016-03-22 15:32:31 -0600748
749 try {
750 latch.await(15, TimeUnit.SECONDS);
751 } catch (InterruptedException e) {
752 Thread.currentThread().interrupt();
753 }
Ricky Waidc283a82016-03-24 19:55:08 +0000754 try {
755 if (!mUserManager.getUserInfo(userId).isManagedProfile()) {
756 final List<UserInfo> profiles = mUserManager.getProfiles(userId);
757 for (UserInfo pi : profiles) {
758 // Unlock managed profile with unified lock
759 if (pi.isManagedProfile()
760 && !mLockPatternUtils.isSeparateProfileChallengeEnabled(pi.id)
Ricky Wai7881cf82016-04-15 17:20:12 +0100761 && !pi.isQuietModeEnabled()
Ricky Waidc283a82016-03-24 19:55:08 +0000762 && mStorage.hasChildProfileLock(pi.id)) {
763 unlockChildProfile(pi.id);
764 }
765 }
766 }
767 } catch (RemoteException e) {
768 Log.d(TAG, "Failed to unlock child profile", e);
769 }
Jeff Sharkey8924e872015-11-30 12:52:10 -0700770 }
Amith Yamasani52c489c2012-03-28 11:42:42 -0700771
Andres Morales8fa56652015-03-31 09:19:50 -0700772 private byte[] getCurrentHandle(int userId) {
773 CredentialHash credential;
774 byte[] currentHandle;
Jim Millerde1af082013-09-11 14:58:26 -0700775
Andres Morales8fa56652015-03-31 09:19:50 -0700776 int currentHandleType = mStorage.getStoredCredentialType(userId);
777 switch (currentHandleType) {
778 case CredentialHash.TYPE_PATTERN:
779 credential = mStorage.readPatternHash(userId);
780 currentHandle = credential != null
781 ? credential.hash
782 : null;
783 break;
784 case CredentialHash.TYPE_PASSWORD:
785 credential = mStorage.readPasswordHash(userId);
786 currentHandle = credential != null
787 ? credential.hash
788 : null;
789 break;
790 case CredentialHash.TYPE_NONE:
791 default:
792 currentHandle = null;
793 break;
794 }
795
796 // sanity check
797 if (currentHandleType != CredentialHash.TYPE_NONE && currentHandle == null) {
798 Slog.e(TAG, "Stored handle type [" + currentHandleType + "] but no handle available");
799 }
800
801 return currentHandle;
Amith Yamasani52c489c2012-03-28 11:42:42 -0700802 }
803
Ricky Waidc283a82016-03-24 19:55:08 +0000804 private void onUserLockChanged(int userId) throws RemoteException {
805 if (mUserManager.getUserInfo(userId).isManagedProfile()) {
806 return;
807 }
808 final boolean isSecure = mStorage.hasPassword(userId) || mStorage.hasPattern(userId);
809 final List<UserInfo> profiles = mUserManager.getProfiles(userId);
810 final int size = profiles.size();
811 for (int i = 0; i < size; i++) {
812 final UserInfo profile = profiles.get(i);
813 if (profile.isManagedProfile()) {
814 final int managedUserId = profile.id;
815 if (mLockPatternUtils.isSeparateProfileChallengeEnabled(managedUserId)) {
816 continue;
817 }
818 if (isSecure) {
819 tieManagedProfileLockIfNecessary(managedUserId, null);
820 } else {
Paul Crowleycc701552016-05-17 14:18:49 -0700821 clearUserKeyProtection(managedUserId);
Ricky Waidc283a82016-03-24 19:55:08 +0000822 getGateKeeperService().clearSecureUserId(managedUserId);
823 mStorage.writePatternHash(null, managedUserId);
824 setKeystorePassword(null, managedUserId);
Paul Crowleycc701552016-05-17 14:18:49 -0700825 fixateNewestUserKeyAuth(managedUserId);
Ricky Waidc283a82016-03-24 19:55:08 +0000826 mStorage.removeChildProfileLock(managedUserId);
827 removeKeystoreProfileKey(managedUserId);
828 }
829 }
830 }
831 }
Andres Morales8fa56652015-03-31 09:19:50 -0700832
Ricky Waidc283a82016-03-24 19:55:08 +0000833 private boolean isManagedProfileWithUnifiedLock(int userId) {
834 return mUserManager.getUserInfo(userId).isManagedProfile()
835 && !mLockPatternUtils.isSeparateProfileChallengeEnabled(userId);
836 }
837
838 private boolean isManagedProfileWithSeparatedLock(int userId) {
839 return mUserManager.getUserInfo(userId).isManagedProfile()
840 && mLockPatternUtils.isSeparateProfileChallengeEnabled(userId);
841 }
842
843 // This method should be called by LockPatternUtil only, all internal methods in this class
844 // should call setLockPatternInternal.
Amith Yamasani52c489c2012-03-28 11:42:42 -0700845 @Override
Andres Morales8fa56652015-03-31 09:19:50 -0700846 public void setLockPattern(String pattern, String savedCredential, int userId)
847 throws RemoteException {
Jim Millere484eaf2016-04-13 16:35:36 -0700848 checkWritePermission(userId);
Ricky Waidc283a82016-03-24 19:55:08 +0000849 synchronized (mSeparateChallengeLock) {
850 setLockPatternInternal(pattern, savedCredential, userId);
851 setSeparateProfileChallengeEnabled(userId, true, null);
852 }
853 }
854
855 public void setLockPatternInternal(String pattern, String savedCredential, int userId)
856 throws RemoteException {
Andres Morales8fa56652015-03-31 09:19:50 -0700857 byte[] currentHandle = getCurrentHandle(userId);
858
Andres Moralesd9fc85a2015-04-09 19:14:42 -0700859 if (pattern == null) {
Paul Crowleycc701552016-05-17 14:18:49 -0700860 clearUserKeyProtection(userId);
Andres Moralescfb61602015-04-16 16:31:15 -0700861 getGateKeeperService().clearSecureUserId(userId);
Andres Moralesd9fc85a2015-04-09 19:14:42 -0700862 mStorage.writePatternHash(null, userId);
Chad Brubakera91a8502015-05-07 10:02:22 -0700863 setKeystorePassword(null, userId);
Paul Crowleycc701552016-05-17 14:18:49 -0700864 fixateNewestUserKeyAuth(userId);
Ricky Waidc283a82016-03-24 19:55:08 +0000865 onUserLockChanged(userId);
Andres Moralesd9fc85a2015-04-09 19:14:42 -0700866 return;
867 }
868
Ricky Waidc283a82016-03-24 19:55:08 +0000869 if (isManagedProfileWithUnifiedLock(userId)) {
870 // get credential from keystore when managed profile has unified lock
871 try {
872 savedCredential = getDecryptedPasswordForTiedProfile(userId);
873 } catch (UnrecoverableKeyException | InvalidKeyException | KeyStoreException
874 | NoSuchAlgorithmException | NoSuchPaddingException
875 | InvalidAlgorithmParameterException | IllegalBlockSizeException
876 | BadPaddingException | CertificateException | IOException e) {
877 if (e instanceof FileNotFoundException) {
878 Slog.i(TAG, "Child profile key not found");
879 } else {
880 Slog.e(TAG, "Failed to decrypt child profile key", e);
881 }
Andres Morales8fa56652015-03-31 09:19:50 -0700882 }
Ricky Waidc283a82016-03-24 19:55:08 +0000883 } else {
884 if (currentHandle == null) {
885 if (savedCredential != null) {
886 Slog.w(TAG, "Saved credential provided, but none stored");
887 }
888 savedCredential = null;
889 }
Andres Morales8fa56652015-03-31 09:19:50 -0700890 }
891
892 byte[] enrolledHandle = enrollCredential(currentHandle, savedCredential, pattern, userId);
893 if (enrolledHandle != null) {
Paul Crowleycc701552016-05-17 14:18:49 -0700894 CredentialHash willStore
895 = new CredentialHash(enrolledHandle, CredentialHash.VERSION_GATEKEEPER);
896 setUserKeyProtection(userId, pattern,
897 doVerifyPattern(pattern, willStore, true, 0, userId));
Andres Morales8fa56652015-03-31 09:19:50 -0700898 mStorage.writePatternHash(enrolledHandle, userId);
Paul Crowleycc701552016-05-17 14:18:49 -0700899 fixateNewestUserKeyAuth(userId);
Ricky Waidc283a82016-03-24 19:55:08 +0000900 onUserLockChanged(userId);
Andres Morales8fa56652015-03-31 09:19:50 -0700901 } else {
Andres Morales2c4a5732015-07-09 16:11:00 -0700902 throw new RemoteException("Failed to enroll pattern");
Andres Morales8fa56652015-03-31 09:19:50 -0700903 }
904 }
905
Ricky Waidc283a82016-03-24 19:55:08 +0000906 // This method should be called by LockPatternUtil only, all internal methods in this class
907 // should call setLockPasswordInternal.
Andres Morales8fa56652015-03-31 09:19:50 -0700908 @Override
909 public void setLockPassword(String password, String savedCredential, int userId)
910 throws RemoteException {
Jim Millere484eaf2016-04-13 16:35:36 -0700911 checkWritePermission(userId);
Ricky Waidc283a82016-03-24 19:55:08 +0000912 synchronized (mSeparateChallengeLock) {
913 setLockPasswordInternal(password, savedCredential, userId);
914 setSeparateProfileChallengeEnabled(userId, true, null);
915 }
916 }
Andres Morales8fa56652015-03-31 09:19:50 -0700917
Ricky Waidc283a82016-03-24 19:55:08 +0000918 public void setLockPasswordInternal(String password, String savedCredential, int userId)
919 throws RemoteException {
920 byte[] currentHandle = getCurrentHandle(userId);
Andres Moralesd9fc85a2015-04-09 19:14:42 -0700921 if (password == null) {
Paul Crowleycc701552016-05-17 14:18:49 -0700922 clearUserKeyProtection(userId);
Andres Moralescfb61602015-04-16 16:31:15 -0700923 getGateKeeperService().clearSecureUserId(userId);
Andres Moralesd9fc85a2015-04-09 19:14:42 -0700924 mStorage.writePasswordHash(null, userId);
Chad Brubakera91a8502015-05-07 10:02:22 -0700925 setKeystorePassword(null, userId);
Paul Crowleycc701552016-05-17 14:18:49 -0700926 fixateNewestUserKeyAuth(userId);
Ricky Waidc283a82016-03-24 19:55:08 +0000927 onUserLockChanged(userId);
Andres Moralesd9fc85a2015-04-09 19:14:42 -0700928 return;
929 }
930
Ricky Waidc283a82016-03-24 19:55:08 +0000931 if (isManagedProfileWithUnifiedLock(userId)) {
932 // get credential from keystore when managed profile has unified lock
933 try {
934 savedCredential = getDecryptedPasswordForTiedProfile(userId);
Ricky Waide3a0682016-05-03 14:50:03 +0100935 } catch (FileNotFoundException e) {
936 Slog.i(TAG, "Child profile key not found");
Ricky Waidc283a82016-03-24 19:55:08 +0000937 } catch (UnrecoverableKeyException | InvalidKeyException | KeyStoreException
938 | NoSuchAlgorithmException | NoSuchPaddingException
939 | InvalidAlgorithmParameterException | IllegalBlockSizeException
940 | BadPaddingException | CertificateException | IOException e) {
Ricky Waide3a0682016-05-03 14:50:03 +0100941 Slog.e(TAG, "Failed to decrypt child profile key", e);
Andres Morales8fa56652015-03-31 09:19:50 -0700942 }
Ricky Waidc283a82016-03-24 19:55:08 +0000943 } else {
944 if (currentHandle == null) {
945 if (savedCredential != null) {
946 Slog.w(TAG, "Saved credential provided, but none stored");
947 }
948 savedCredential = null;
949 }
Andres Morales8fa56652015-03-31 09:19:50 -0700950 }
951
952 byte[] enrolledHandle = enrollCredential(currentHandle, savedCredential, password, userId);
953 if (enrolledHandle != null) {
Paul Crowleycc701552016-05-17 14:18:49 -0700954 CredentialHash willStore
955 = new CredentialHash(enrolledHandle, CredentialHash.VERSION_GATEKEEPER);
956 setUserKeyProtection(userId, password,
957 doVerifyPassword(password, willStore, true, 0, userId));
Andres Morales8fa56652015-03-31 09:19:50 -0700958 mStorage.writePasswordHash(enrolledHandle, userId);
Paul Crowleycc701552016-05-17 14:18:49 -0700959 fixateNewestUserKeyAuth(userId);
Ricky Waidc283a82016-03-24 19:55:08 +0000960 onUserLockChanged(userId);
Andres Morales8fa56652015-03-31 09:19:50 -0700961 } else {
Andres Morales2c4a5732015-07-09 16:11:00 -0700962 throw new RemoteException("Failed to enroll password");
Andres Morales8fa56652015-03-31 09:19:50 -0700963 }
964 }
965
Ricky Waidc283a82016-03-24 19:55:08 +0000966 private void tieProfileLockToParent(int userId, String password) {
967 if (DEBUG) Slog.v(TAG, "tieProfileLockToParent for user: " + userId);
968 byte[] randomLockSeed = password.getBytes(StandardCharsets.UTF_8);
969 byte[] encryptionResult;
970 byte[] iv;
971 try {
972 KeyGenerator keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES);
973 keyGenerator.init(new SecureRandom());
974 SecretKey secretKey = keyGenerator.generateKey();
975
976 java.security.KeyStore keyStore = java.security.KeyStore.getInstance("AndroidKeyStore");
977 keyStore.load(null);
978 keyStore.setEntry(
979 PROFILE_KEY_NAME_ENCRYPT + userId,
980 new java.security.KeyStore.SecretKeyEntry(secretKey),
981 new KeyProtection.Builder(KeyProperties.PURPOSE_ENCRYPT)
982 .setBlockModes(KeyProperties.BLOCK_MODE_GCM)
983 .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
984 .build());
985 keyStore.setEntry(
986 PROFILE_KEY_NAME_DECRYPT + userId,
987 new java.security.KeyStore.SecretKeyEntry(secretKey),
988 new KeyProtection.Builder(KeyProperties.PURPOSE_DECRYPT)
989 .setBlockModes(KeyProperties.BLOCK_MODE_GCM)
990 .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
991 .setUserAuthenticationRequired(true)
992 .setUserAuthenticationValidityDurationSeconds(30)
993 .build());
994
995 // Key imported, obtain a reference to it.
996 SecretKey keyStoreEncryptionKey = (SecretKey) keyStore.getKey(
997 PROFILE_KEY_NAME_ENCRYPT + userId, null);
998 // The original key can now be discarded.
999
1000 Cipher cipher = Cipher.getInstance(
1001 KeyProperties.KEY_ALGORITHM_AES + "/" + KeyProperties.BLOCK_MODE_GCM + "/"
1002 + KeyProperties.ENCRYPTION_PADDING_NONE);
1003 cipher.init(Cipher.ENCRYPT_MODE, keyStoreEncryptionKey);
1004 encryptionResult = cipher.doFinal(randomLockSeed);
1005 iv = cipher.getIV();
1006 } catch (CertificateException | UnrecoverableKeyException
1007 | IOException | BadPaddingException | IllegalBlockSizeException | KeyStoreException
1008 | NoSuchPaddingException | NoSuchAlgorithmException | InvalidKeyException e) {
1009 throw new RuntimeException("Failed to encrypt key", e);
1010 }
1011 ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
1012 try {
1013 if (iv.length != PROFILE_KEY_IV_SIZE) {
1014 throw new RuntimeException("Invalid iv length: " + iv.length);
1015 }
1016 outputStream.write(iv);
1017 outputStream.write(encryptionResult);
1018 } catch (IOException e) {
1019 throw new RuntimeException("Failed to concatenate byte arrays", e);
1020 }
1021 mStorage.writeChildProfileLock(userId, outputStream.toByteArray());
1022 }
1023
Andres Morales8fa56652015-03-31 09:19:50 -07001024 private byte[] enrollCredential(byte[] enrolledHandle,
1025 String enrolledCredential, String toEnroll, int userId)
1026 throws RemoteException {
Jim Millerde1af082013-09-11 14:58:26 -07001027 checkWritePermission(userId);
Andres Morales8fa56652015-03-31 09:19:50 -07001028 byte[] enrolledCredentialBytes = enrolledCredential == null
1029 ? null
1030 : enrolledCredential.getBytes();
1031 byte[] toEnrollBytes = toEnroll == null
1032 ? null
1033 : toEnroll.getBytes();
Andres Morales23974272015-05-14 22:42:26 -07001034 GateKeeperResponse response = getGateKeeperService().enroll(userId, enrolledHandle,
1035 enrolledCredentialBytes, toEnrollBytes);
Jim Millerde1af082013-09-11 14:58:26 -07001036
Andres Morales23974272015-05-14 22:42:26 -07001037 if (response == null) {
1038 return null;
Andres Morales8fa56652015-03-31 09:19:50 -07001039 }
Jim Millerde1af082013-09-11 14:58:26 -07001040
Andres Morales23974272015-05-14 22:42:26 -07001041 byte[] hash = response.getPayload();
1042 if (hash != null) {
1043 setKeystorePassword(toEnroll, userId);
1044 } else {
1045 // Should not happen
1046 Slog.e(TAG, "Throttled while enrolling a password");
1047 }
Andres Morales8fa56652015-03-31 09:19:50 -07001048 return hash;
Jim Millerde1af082013-09-11 14:58:26 -07001049 }
1050
Paul Crowleyfaeb3eb2016-02-08 15:58:29 +00001051 private void setUserKeyProtection(int userId, String credential, VerifyCredentialResponse vcr)
1052 throws RemoteException {
1053 if (vcr == null) {
1054 throw new RemoteException("Null response verifying a credential we just set");
1055 }
1056 if (vcr.getResponseCode() != VerifyCredentialResponse.RESPONSE_OK) {
1057 throw new RemoteException("Non-OK response verifying a credential we just set: "
1058 + vcr.getResponseCode());
1059 }
1060 byte[] token = vcr.getPayload();
1061 if (token == null) {
1062 throw new RemoteException("Empty payload verifying a credential we just set");
1063 }
Paul Crowleycc701552016-05-17 14:18:49 -07001064 addUserKeyAuth(userId, token, secretFromCredential(credential));
Paul Crowleyfaeb3eb2016-02-08 15:58:29 +00001065 }
1066
1067 private void clearUserKeyProtection(int userId) throws RemoteException {
Paul Crowleycc701552016-05-17 14:18:49 -07001068 addUserKeyAuth(userId, null, null);
Paul Crowleyfaeb3eb2016-02-08 15:58:29 +00001069 }
1070
1071 private static byte[] secretFromCredential(String credential) throws RemoteException {
1072 try {
1073 MessageDigest digest = MessageDigest.getInstance("SHA-512");
1074 // Personalize the hash
1075 byte[] personalization = "Android FBE credential hash"
1076 .getBytes(StandardCharsets.UTF_8);
1077 // Pad it to the block size of the hash function
1078 personalization = Arrays.copyOf(personalization, 128);
1079 digest.update(personalization);
1080 digest.update(credential.getBytes(StandardCharsets.UTF_8));
1081 return digest.digest();
1082 } catch (NoSuchAlgorithmException e) {
1083 throw new RuntimeException("NoSuchAlgorithmException for SHA-512");
1084 }
1085 }
1086
Paul Crowleycc701552016-05-17 14:18:49 -07001087 private void addUserKeyAuth(int userId, byte[] token, byte[] secret)
Paul Crowleyfaeb3eb2016-02-08 15:58:29 +00001088 throws RemoteException {
1089 final UserInfo userInfo = UserManager.get(mContext).getUserInfo(userId);
Paul Crowley815036f2016-03-29 14:14:48 -07001090 final IMountService mountService = getMountService();
1091 final long callingId = Binder.clearCallingIdentity();
1092 try {
Paul Crowleycc701552016-05-17 14:18:49 -07001093 mountService.addUserKeyAuth(userId, userInfo.serialNumber, token, secret);
Paul Crowley815036f2016-03-29 14:14:48 -07001094 } finally {
1095 Binder.restoreCallingIdentity(callingId);
1096 }
Paul Crowleyfaeb3eb2016-02-08 15:58:29 +00001097 }
1098
Paul Crowleycc701552016-05-17 14:18:49 -07001099 private void fixateNewestUserKeyAuth(int userId)
1100 throws RemoteException {
1101 getMountService().fixateNewestUserKeyAuth(userId);
1102 }
1103
Jim Millerde1af082013-09-11 14:58:26 -07001104 @Override
Andres Morales23974272015-05-14 22:42:26 -07001105 public VerifyCredentialResponse checkPattern(String pattern, int userId) throws RemoteException {
1106 return doVerifyPattern(pattern, false, 0, userId);
Andres Moralesd9fc85a2015-04-09 19:14:42 -07001107 }
Adrian Roos261d5ab2014-10-29 14:42:38 +01001108
Andres Moralesd9fc85a2015-04-09 19:14:42 -07001109 @Override
Andres Morales23974272015-05-14 22:42:26 -07001110 public VerifyCredentialResponse verifyPattern(String pattern, long challenge, int userId)
Andres Moralesd9fc85a2015-04-09 19:14:42 -07001111 throws RemoteException {
Andres Morales23974272015-05-14 22:42:26 -07001112 return doVerifyPattern(pattern, true, challenge, userId);
Andres Moralesd9fc85a2015-04-09 19:14:42 -07001113 }
1114
Andres Moralese40bad82015-05-28 14:21:36 -07001115 private VerifyCredentialResponse doVerifyPattern(String pattern, boolean hasChallenge,
1116 long challenge, int userId) throws RemoteException {
Andres Moralesd9fc85a2015-04-09 19:14:42 -07001117 checkPasswordReadPermission(userId);
Andres Moralesd9fc85a2015-04-09 19:14:42 -07001118 CredentialHash storedHash = mStorage.readPatternHash(userId);
Paul Crowleycc701552016-05-17 14:18:49 -07001119 return doVerifyPattern(pattern, storedHash, hasChallenge, challenge, userId);
1120 }
1121
1122 private VerifyCredentialResponse doVerifyPattern(String pattern, CredentialHash storedHash,
1123 boolean hasChallenge, long challenge, int userId) throws RemoteException {
Andres Moralese40bad82015-05-28 14:21:36 -07001124 boolean shouldReEnrollBaseZero = storedHash != null && storedHash.isBaseZeroPattern;
1125
1126 String patternToVerify;
1127 if (shouldReEnrollBaseZero) {
1128 patternToVerify = LockPatternUtils.patternStringToBaseZero(pattern);
1129 } else {
1130 patternToVerify = pattern;
1131 }
1132
1133 VerifyCredentialResponse response = verifyCredential(userId, storedHash, patternToVerify,
1134 hasChallenge, challenge,
Andres Morales23974272015-05-14 22:42:26 -07001135 new CredentialUtil() {
1136 @Override
1137 public void setCredential(String pattern, String oldPattern, int userId)
1138 throws RemoteException {
Ricky Waidc283a82016-03-24 19:55:08 +00001139 setLockPatternInternal(pattern, oldPattern, userId);
Andres Morales23974272015-05-14 22:42:26 -07001140 }
Andres Moralesd9fc85a2015-04-09 19:14:42 -07001141
Andres Morales23974272015-05-14 22:42:26 -07001142 @Override
1143 public byte[] toHash(String pattern, int userId) {
Andres Moralese40bad82015-05-28 14:21:36 -07001144 return LockPatternUtils.patternToHash(
1145 LockPatternUtils.stringToPattern(pattern));
Andres Morales23974272015-05-14 22:42:26 -07001146 }
Andres Morales59ef1262015-06-26 13:56:39 -07001147
1148 @Override
1149 public String adjustForKeystore(String pattern) {
1150 return LockPatternUtils.patternStringToBaseZero(pattern);
1151 }
Andres Morales23974272015-05-14 22:42:26 -07001152 }
1153 );
Andres Moralese40bad82015-05-28 14:21:36 -07001154
1155 if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK
1156 && shouldReEnrollBaseZero) {
Ricky Waidc283a82016-03-24 19:55:08 +00001157 setLockPatternInternal(pattern, patternToVerify, userId);
Andres Moralese40bad82015-05-28 14:21:36 -07001158 }
1159
1160 return response;
Amith Yamasani52c489c2012-03-28 11:42:42 -07001161 }
1162
1163 @Override
Andres Morales23974272015-05-14 22:42:26 -07001164 public VerifyCredentialResponse checkPassword(String password, int userId)
Andres Moralesd9fc85a2015-04-09 19:14:42 -07001165 throws RemoteException {
Andres Morales23974272015-05-14 22:42:26 -07001166 return doVerifyPassword(password, false, 0, userId);
Andres Moralesd9fc85a2015-04-09 19:14:42 -07001167 }
1168
Andres Morales23974272015-05-14 22:42:26 -07001169 @Override
1170 public VerifyCredentialResponse verifyPassword(String password, long challenge, int userId)
1171 throws RemoteException {
1172 return doVerifyPassword(password, true, challenge, userId);
1173 }
1174
Ricky Wai53940d42016-04-05 15:29:24 +01001175 @Override
1176 public VerifyCredentialResponse verifyTiedProfileChallenge(String password, boolean isPattern,
1177 long challenge, int userId) throws RemoteException {
1178 checkPasswordReadPermission(userId);
1179 if (!isManagedProfileWithUnifiedLock(userId)) {
1180 throw new RemoteException("User id must be managed profile with unified lock");
1181 }
1182 final int parentProfileId = mUserManager.getProfileParent(userId).id;
1183 // Unlock parent by using parent's challenge
1184 final VerifyCredentialResponse parentResponse = isPattern
1185 ? doVerifyPattern(password, true, challenge, parentProfileId)
1186 : doVerifyPassword(password, true, challenge, parentProfileId);
1187 if (parentResponse.getResponseCode() != VerifyCredentialResponse.RESPONSE_OK) {
1188 // Failed, just return parent's response
1189 return parentResponse;
1190 }
1191
1192 try {
1193 // Unlock work profile, and work profile with unified lock must use password only
1194 return doVerifyPassword(getDecryptedPasswordForTiedProfile(userId), true,
1195 challenge,
1196 userId);
1197 } catch (UnrecoverableKeyException | InvalidKeyException | KeyStoreException
1198 | NoSuchAlgorithmException | NoSuchPaddingException
1199 | InvalidAlgorithmParameterException | IllegalBlockSizeException
1200 | BadPaddingException | CertificateException | IOException e) {
1201 Slog.e(TAG, "Failed to decrypt child profile key", e);
1202 throw new RemoteException("Unable to get tied profile token");
1203 }
1204 }
1205
Andres Morales23974272015-05-14 22:42:26 -07001206 private VerifyCredentialResponse doVerifyPassword(String password, boolean hasChallenge,
1207 long challenge, int userId) throws RemoteException {
Andres Moralesd9fc85a2015-04-09 19:14:42 -07001208 checkPasswordReadPermission(userId);
Andres Morales8fa56652015-03-31 09:19:50 -07001209 CredentialHash storedHash = mStorage.readPasswordHash(userId);
Paul Crowleycc701552016-05-17 14:18:49 -07001210 return doVerifyPassword(password, storedHash, hasChallenge, challenge, userId);
1211 }
1212
1213 private VerifyCredentialResponse doVerifyPassword(String password, CredentialHash storedHash,
1214 boolean hasChallenge, long challenge, int userId) throws RemoteException {
Andres Morales23974272015-05-14 22:42:26 -07001215 return verifyCredential(userId, storedHash, password, hasChallenge, challenge,
1216 new CredentialUtil() {
1217 @Override
1218 public void setCredential(String password, String oldPassword, int userId)
1219 throws RemoteException {
Ricky Waidc283a82016-03-24 19:55:08 +00001220 setLockPasswordInternal(password, oldPassword, userId);
Andres Morales23974272015-05-14 22:42:26 -07001221 }
Adrian Roos261d5ab2014-10-29 14:42:38 +01001222
Andres Morales23974272015-05-14 22:42:26 -07001223 @Override
1224 public byte[] toHash(String password, int userId) {
1225 return mLockPatternUtils.passwordToHash(password, userId);
1226 }
Andres Morales59ef1262015-06-26 13:56:39 -07001227
1228 @Override
1229 public String adjustForKeystore(String password) {
1230 return password;
1231 }
Andres Morales23974272015-05-14 22:42:26 -07001232 }
1233 );
1234 }
1235
1236 private VerifyCredentialResponse verifyCredential(int userId, CredentialHash storedHash,
1237 String credential, boolean hasChallenge, long challenge, CredentialUtil credentialUtil)
1238 throws RemoteException {
1239 if ((storedHash == null || storedHash.hash.length == 0) && TextUtils.isEmpty(credential)) {
1240 // don't need to pass empty credentials to GateKeeper
1241 return VerifyCredentialResponse.OK;
Andres Moralesd9fc85a2015-04-09 19:14:42 -07001242 }
1243
Andres Morales23974272015-05-14 22:42:26 -07001244 if (TextUtils.isEmpty(credential)) {
1245 return VerifyCredentialResponse.ERROR;
Amith Yamasani52c489c2012-03-28 11:42:42 -07001246 }
Adrian Roos261d5ab2014-10-29 14:42:38 +01001247
Andres Morales8fa56652015-03-31 09:19:50 -07001248 if (storedHash.version == CredentialHash.VERSION_LEGACY) {
Andres Morales23974272015-05-14 22:42:26 -07001249 byte[] hash = credentialUtil.toHash(credential, userId);
Andres Moralesd9fc85a2015-04-09 19:14:42 -07001250 if (Arrays.equals(hash, storedHash.hash)) {
Andres Morales59ef1262015-06-26 13:56:39 -07001251 unlockKeystore(credentialUtil.adjustForKeystore(credential), userId);
Jeff Sharkeyb9fe5372015-12-03 15:23:08 -07001252
Paul Crowleyfaeb3eb2016-02-08 15:58:29 +00001253 // Users with legacy credentials don't have credential-backed
1254 // FBE keys, so just pass through a fake token/secret
1255 Slog.i(TAG, "Unlocking user with fake token: " + userId);
1256 final byte[] fakeToken = String.valueOf(userId).getBytes();
1257 unlockUser(userId, fakeToken, fakeToken);
Jeff Sharkeyb9fe5372015-12-03 15:23:08 -07001258
Andres Morales23974272015-05-14 22:42:26 -07001259 // migrate credential to GateKeeper
1260 credentialUtil.setCredential(credential, null, userId);
Andres Moralesd9fc85a2015-04-09 19:14:42 -07001261 if (!hasChallenge) {
Andres Morales23974272015-05-14 22:42:26 -07001262 return VerifyCredentialResponse.OK;
Andres Moralesd9fc85a2015-04-09 19:14:42 -07001263 }
1264 // Fall through to get the auth token. Technically this should never happen,
Andres Morales23974272015-05-14 22:42:26 -07001265 // as a user that had a legacy credential would have to unlock their device
Andres Moralesd9fc85a2015-04-09 19:14:42 -07001266 // before getting to a flow with a challenge, but supporting for consistency.
1267 } else {
Andres Morales23974272015-05-14 22:42:26 -07001268 return VerifyCredentialResponse.ERROR;
Andres Morales8fa56652015-03-31 09:19:50 -07001269 }
Andres Morales8fa56652015-03-31 09:19:50 -07001270 }
1271
Andres Morales23974272015-05-14 22:42:26 -07001272 VerifyCredentialResponse response;
Paul Crowley98e0a262016-02-04 09:41:53 +00001273 boolean shouldReEnroll = false;
1274 GateKeeperResponse gateKeeperResponse = getGateKeeperService()
1275 .verifyChallenge(userId, challenge, storedHash.hash, credential.getBytes());
1276 int responseCode = gateKeeperResponse.getResponseCode();
1277 if (responseCode == GateKeeperResponse.RESPONSE_RETRY) {
1278 response = new VerifyCredentialResponse(gateKeeperResponse.getTimeout());
1279 } else if (responseCode == GateKeeperResponse.RESPONSE_OK) {
1280 byte[] token = gateKeeperResponse.getPayload();
1281 if (token == null) {
1282 // something's wrong if there's no payload with a challenge
1283 Slog.e(TAG, "verifyChallenge response had no associated payload");
Andres Morales23974272015-05-14 22:42:26 -07001284 response = VerifyCredentialResponse.ERROR;
Paul Crowley98e0a262016-02-04 09:41:53 +00001285 } else {
1286 shouldReEnroll = gateKeeperResponse.getShouldReEnroll();
1287 response = new VerifyCredentialResponse(token);
Andres Moralesd9fc85a2015-04-09 19:14:42 -07001288 }
Andres Morales23974272015-05-14 22:42:26 -07001289 } else {
Paul Crowley98e0a262016-02-04 09:41:53 +00001290 response = VerifyCredentialResponse.ERROR;
Adrian Roos261d5ab2014-10-29 14:42:38 +01001291 }
Andres Morales8fa56652015-03-31 09:19:50 -07001292
Andres Morales23974272015-05-14 22:42:26 -07001293 if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK) {
1294 // credential has matched
1295 unlockKeystore(credential, userId);
Jeff Sharkeyb9fe5372015-12-03 15:23:08 -07001296
Paul Crowleyfaeb3eb2016-02-08 15:58:29 +00001297 Slog.i(TAG, "Unlocking user " + userId +
1298 " with token length " + response.getPayload().length);
1299 unlockUser(userId, response.getPayload(), secretFromCredential(credential));
Jeff Sharkeyb9fe5372015-12-03 15:23:08 -07001300
Ricky Waidc283a82016-03-24 19:55:08 +00001301 if (isManagedProfileWithSeparatedLock(userId)) {
Clara Bayarri56878a92015-10-29 15:43:55 +00001302 TrustManager trustManager =
1303 (TrustManager) mContext.getSystemService(Context.TRUST_SERVICE);
1304 trustManager.setDeviceLockedForUser(userId, false);
1305 }
Andres Morales23974272015-05-14 22:42:26 -07001306 if (shouldReEnroll) {
1307 credentialUtil.setCredential(credential, credential, userId);
1308 }
Adrian Roos873010d2015-08-25 15:59:00 -07001309 } else if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_RETRY) {
1310 if (response.getTimeout() > 0) {
1311 requireStrongAuth(STRONG_AUTH_REQUIRED_AFTER_LOCKOUT, userId);
1312 }
Andres Morales23974272015-05-14 22:42:26 -07001313 }
Amith Yamasani52c489c2012-03-28 11:42:42 -07001314
Andres Morales23974272015-05-14 22:42:26 -07001315 return response;
1316 }
Andres Moralesd9fc85a2015-04-09 19:14:42 -07001317
Amith Yamasani52c489c2012-03-28 11:42:42 -07001318 @Override
Adrian Roos261d5ab2014-10-29 14:42:38 +01001319 public boolean checkVoldPassword(int userId) throws RemoteException {
Paul Lawrence945490c2014-03-27 16:37:28 +00001320 if (!mFirstCallToVold) {
1321 return false;
1322 }
1323 mFirstCallToVold = false;
1324
1325 checkPasswordReadPermission(userId);
1326
1327 // There's no guarantee that this will safely connect, but if it fails
1328 // we will simply show the lock screen when we shouldn't, so relatively
1329 // benign. There is an outside chance something nasty would happen if
1330 // this service restarted before vold stales out the password in this
1331 // case. The nastiness is limited to not showing the lock screen when
1332 // we should, within the first minute of decrypting the phone if this
1333 // service can't connect to vold, it restarts, and then the new instance
1334 // does successfully connect.
1335 final IMountService service = getMountService();
Paul Lawrence0bbd1082016-04-26 15:21:02 -07001336 String password;
1337 long identity = Binder.clearCallingIdentity();
1338 try {
1339 password = service.getPassword();
1340 service.clearPassword();
1341 } finally {
1342 Binder.restoreCallingIdentity(identity);
1343 }
Paul Lawrence945490c2014-03-27 16:37:28 +00001344 if (password == null) {
1345 return false;
1346 }
1347
1348 try {
Adrian Roos9dd16eb2015-01-08 16:20:49 +01001349 if (mLockPatternUtils.isLockPatternEnabled(userId)) {
Andres Morales23974272015-05-14 22:42:26 -07001350 if (checkPattern(password, userId).getResponseCode()
1351 == GateKeeperResponse.RESPONSE_OK) {
Paul Lawrence945490c2014-03-27 16:37:28 +00001352 return true;
1353 }
1354 }
1355 } catch (Exception e) {
1356 }
1357
1358 try {
Adrian Roos9dd16eb2015-01-08 16:20:49 +01001359 if (mLockPatternUtils.isLockPasswordEnabled(userId)) {
Andres Morales23974272015-05-14 22:42:26 -07001360 if (checkPassword(password, userId).getResponseCode()
1361 == GateKeeperResponse.RESPONSE_OK) {
Paul Lawrence945490c2014-03-27 16:37:28 +00001362 return true;
1363 }
1364 }
1365 } catch (Exception e) {
1366 }
1367
1368 return false;
1369 }
1370
Adrian Roosdb0f76e2015-01-07 22:19:38 +01001371 private void removeUser(int userId) {
Adrian Roos261d5ab2014-10-29 14:42:38 +01001372 mStorage.removeUser(userId);
Adrian Roosb5e47222015-08-14 15:53:06 -07001373 mStrongAuth.removeUser(userId);
Robin Lee49d810c2014-09-23 13:50:22 +01001374
1375 final KeyStore ks = KeyStore.getInstance();
Chad Brubaker83ce0952015-05-12 13:00:02 -07001376 ks.onUserRemoved(userId);
Andres Morales070fe632015-06-24 10:37:10 -07001377
1378 try {
1379 final IGateKeeperService gk = getGateKeeperService();
1380 if (gk != null) {
1381 gk.clearSecureUserId(userId);
1382 }
1383 } catch (RemoteException ex) {
1384 Slog.w(TAG, "unable to clear GK secure user id");
1385 }
Ricky Waidc283a82016-03-24 19:55:08 +00001386 if (mUserManager.getUserInfo(userId).isManagedProfile()) {
1387 removeKeystoreProfileKey(userId);
1388 }
1389 }
1390
1391 private void removeKeystoreProfileKey(int targetUserId) {
1392 if (DEBUG) Slog.v(TAG, "Remove keystore profile key for user: " + targetUserId);
1393 try {
1394 java.security.KeyStore keyStore = java.security.KeyStore.getInstance("AndroidKeyStore");
1395 keyStore.load(null);
1396 keyStore.deleteEntry(PROFILE_KEY_NAME_ENCRYPT + targetUserId);
1397 keyStore.deleteEntry(PROFILE_KEY_NAME_DECRYPT + targetUserId);
1398 } catch (KeyStoreException | NoSuchAlgorithmException | CertificateException
1399 | IOException e) {
1400 // We have tried our best to remove all keys
1401 Slog.e(TAG, "Unable to remove keystore profile key for user:" + targetUserId, e);
1402 }
Amith Yamasani52c489c2012-03-28 11:42:42 -07001403 }
1404
Adrian Roosb5e47222015-08-14 15:53:06 -07001405 @Override
1406 public void registerStrongAuthTracker(IStrongAuthTracker tracker) {
1407 checkPasswordReadPermission(UserHandle.USER_ALL);
1408 mStrongAuth.registerStrongAuthTracker(tracker);
1409 }
1410
1411 @Override
1412 public void unregisterStrongAuthTracker(IStrongAuthTracker tracker) {
1413 checkPasswordReadPermission(UserHandle.USER_ALL);
1414 mStrongAuth.unregisterStrongAuthTracker(tracker);
1415 }
1416
1417 @Override
1418 public void requireStrongAuth(int strongAuthReason, int userId) {
1419 checkWritePermission(userId);
1420 mStrongAuth.requireStrongAuth(strongAuthReason, userId);
1421 }
1422
Adrian Roos4ab7e592016-04-13 15:38:13 -07001423 @Override
1424 public void userPresent(int userId) {
1425 checkWritePermission(userId);
1426 mStrongAuth.reportUnlock(userId);
1427 }
1428
Victor Changa0940d32016-05-16 19:36:08 +01001429 @Override
1430 public int getStrongAuthForUser(int userId) {
1431 checkPasswordReadPermission(userId);
1432 return mStrongAuthTracker.getStrongAuthForUser(userId);
1433 }
1434
Amith Yamasani52c489c2012-03-28 11:42:42 -07001435 private static final String[] VALID_SETTINGS = new String[] {
1436 LockPatternUtils.LOCKOUT_PERMANENT_KEY,
1437 LockPatternUtils.LOCKOUT_ATTEMPT_DEADLINE,
1438 LockPatternUtils.PATTERN_EVER_CHOSEN_KEY,
1439 LockPatternUtils.PASSWORD_TYPE_KEY,
1440 LockPatternUtils.PASSWORD_TYPE_ALTERNATE_KEY,
1441 LockPatternUtils.LOCK_PASSWORD_SALT_KEY,
1442 LockPatternUtils.DISABLE_LOCKSCREEN_KEY,
1443 LockPatternUtils.LOCKSCREEN_OPTIONS,
1444 LockPatternUtils.LOCKSCREEN_BIOMETRIC_WEAK_FALLBACK,
1445 LockPatternUtils.BIOMETRIC_WEAK_EVER_CHOSEN_KEY,
1446 LockPatternUtils.LOCKSCREEN_POWER_BUTTON_INSTANTLY_LOCKS,
1447 LockPatternUtils.PASSWORD_HISTORY_KEY,
1448 Secure.LOCK_PATTERN_ENABLED,
1449 Secure.LOCK_BIOMETRIC_WEAK_FLAGS,
1450 Secure.LOCK_PATTERN_VISIBLE,
1451 Secure.LOCK_PATTERN_TACTILE_FEEDBACK_ENABLED
Jim Miller187ec582013-04-15 18:27:54 -07001452 };
1453
Svetoslav Ganov6d2c0e52015-06-23 16:33:36 +00001454 // Reading these settings needs the contacts permission
1455 private static final String[] READ_CONTACTS_PROTECTED_SETTINGS = new String[] {
Jim Miller187ec582013-04-15 18:27:54 -07001456 Secure.LOCK_SCREEN_OWNER_INFO_ENABLED,
1457 Secure.LOCK_SCREEN_OWNER_INFO
1458 };
Paul Lawrence945490c2014-03-27 16:37:28 +00001459
Adrian Roos001b00d2015-02-24 17:08:48 +01001460 // Reading these settings needs the same permission as checking the password
1461 private static final String[] READ_PASSWORD_PROTECTED_SETTINGS = new String[] {
1462 LockPatternUtils.LOCK_PASSWORD_SALT_KEY,
1463 LockPatternUtils.PASSWORD_HISTORY_KEY,
Adrian Roos855fa302015-04-02 16:01:12 +02001464 LockPatternUtils.PASSWORD_TYPE_KEY,
Adrian Roos001b00d2015-02-24 17:08:48 +01001465 };
1466
Amith Yamasani072543f2015-02-13 11:09:45 -08001467 private static final String[] SETTINGS_TO_BACKUP = new String[] {
1468 Secure.LOCK_SCREEN_OWNER_INFO_ENABLED,
1469 Secure.LOCK_SCREEN_OWNER_INFO
1470 };
1471
Paul Lawrence945490c2014-03-27 16:37:28 +00001472 private IMountService getMountService() {
1473 final IBinder service = ServiceManager.getService("mount");
1474 if (service != null) {
1475 return IMountService.Stub.asInterface(service);
1476 }
1477 return null;
1478 }
Andres Morales8fa56652015-03-31 09:19:50 -07001479
Andres Morales301ea442015-04-17 09:15:47 -07001480 private class GateKeeperDiedRecipient implements IBinder.DeathRecipient {
1481 @Override
1482 public void binderDied() {
1483 mGateKeeperService.asBinder().unlinkToDeath(this, 0);
1484 mGateKeeperService = null;
1485 }
1486 }
1487
1488 private synchronized IGateKeeperService getGateKeeperService()
1489 throws RemoteException {
Andres Morales8fa56652015-03-31 09:19:50 -07001490 if (mGateKeeperService != null) {
1491 return mGateKeeperService;
1492 }
1493
1494 final IBinder service =
1495 ServiceManager.getService("android.service.gatekeeper.IGateKeeperService");
1496 if (service != null) {
Andres Morales301ea442015-04-17 09:15:47 -07001497 service.linkToDeath(new GateKeeperDiedRecipient(), 0);
Andres Morales8fa56652015-03-31 09:19:50 -07001498 mGateKeeperService = IGateKeeperService.Stub.asInterface(service);
1499 return mGateKeeperService;
1500 }
1501
1502 Slog.e(TAG, "Unable to acquire GateKeeperService");
1503 return null;
1504 }
Amith Yamasani52c489c2012-03-28 11:42:42 -07001505}