blob: 5b6117d5563312619efde9f8ffe60ff82dbfdc4f [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;
Adrian Roos261d5ab2014-10-29 14:42:38 +0100123
Jim Millerde1af082013-09-11 14:58:26 -0700124 private LockPatternUtils mLockPatternUtils;
Paul Lawrence945490c2014-03-27 16:37:28 +0000125 private boolean mFirstCallToVold;
Andres Morales8fa56652015-03-31 09:19:50 -0700126 private IGateKeeperService mGateKeeperService;
Jim Miller4f93c582016-01-27 19:05:43 -0800127 private NotificationManager mNotificationManager;
128 private UserManager mUserManager;
129
130 static {
131 // Just launch the home screen, which happens anyway
132 ACTION_NULL = new Intent(Intent.ACTION_MAIN);
133 ACTION_NULL.addCategory(Intent.CATEGORY_HOME);
134 }
Amith Yamasani52c489c2012-03-28 11:42:42 -0700135
Andres Morales23974272015-05-14 22:42:26 -0700136 private interface CredentialUtil {
137 void setCredential(String credential, String savedCredential, int userId)
138 throws RemoteException;
139 byte[] toHash(String credential, int userId);
Andres Morales59ef1262015-06-26 13:56:39 -0700140 String adjustForKeystore(String credential);
Andres Morales23974272015-05-14 22:42:26 -0700141 }
142
Jim Miller4f93c582016-01-27 19:05:43 -0800143 // This class manages life cycle events for encrypted users on File Based Encryption (FBE)
144 // devices. The most basic of these is to show/hide notifications about missing features until
145 // the user unlocks the account and credential-encrypted storage is available.
146 public static final class Lifecycle extends SystemService {
147 private LockSettingsService mLockSettingsService;
148
149 public Lifecycle(Context context) {
150 super(context);
151 }
152
153 @Override
154 public void onStart() {
Ricky Waidc283a82016-03-24 19:55:08 +0000155 AndroidKeyStoreProvider.install();
Jim Miller4f93c582016-01-27 19:05:43 -0800156 mLockSettingsService = new LockSettingsService(getContext());
157 publishBinderService("lock_settings", mLockSettingsService);
158 }
159
160 @Override
161 public void onBootPhase(int phase) {
162 if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) {
Kenny Guyb1b30262016-02-09 16:02:35 +0000163 mLockSettingsService.maybeShowEncryptionNotifications();
Jim Miller4f93c582016-01-27 19:05:43 -0800164 } else if (phase == SystemService.PHASE_BOOT_COMPLETED) {
165 // TODO
166 }
167 }
168
169 @Override
170 public void onUnlockUser(int userHandle) {
171 mLockSettingsService.onUnlockUser(userHandle);
172 }
173
174 @Override
175 public void onCleanupUser(int userHandle) {
176 mLockSettingsService.onCleanupUser(userHandle);
177 }
178 }
179
Ricky Waidc283a82016-03-24 19:55:08 +0000180 /**
181 * Tie managed profile to primary profile if it is in unified mode and not tied before.
182 *
183 * @param managedUserId Managed profile user Id
184 * @param managedUserPassword Managed profile original password (when it has separated lock).
185 * NULL when it does not have a separated lock before.
186 */
187 public void tieManagedProfileLockIfNecessary(int managedUserId, String managedUserPassword) {
188 if (DEBUG) Slog.v(TAG, "Check child profile lock for user: " + managedUserId);
189 // Only for managed profile
190 if (!UserManager.get(mContext).getUserInfo(managedUserId).isManagedProfile()) {
191 return;
192 }
193 // Do not tie managed profile when work challenge is enabled
194 if (mLockPatternUtils.isSeparateProfileChallengeEnabled(managedUserId)) {
195 return;
196 }
197 // Do not tie managed profile to parent when it's done already
198 if (mStorage.hasChildProfileLock(managedUserId)) {
199 return;
200 }
201 // Do not tie it to parent when parent does not have a screen lock
202 final int parentId = mUserManager.getProfileParent(managedUserId).id;
203 if (!mStorage.hasPassword(parentId) && !mStorage.hasPattern(parentId)) {
204 if (DEBUG) Slog.v(TAG, "Parent does not have a screen lock");
205 return;
206 }
207 if (DEBUG) Slog.v(TAG, "Tie managed profile to parent now!");
208 byte[] randomLockSeed = new byte[] {};
209 try {
210 randomLockSeed = SecureRandom.getInstance("SHA1PRNG").generateSeed(40);
211 String newPassword = String.valueOf(HexEncoding.encode(randomLockSeed));
212 setLockPasswordInternal(newPassword, managedUserPassword, managedUserId);
213 tieProfileLockToParent(managedUserId, newPassword);
214 } catch (NoSuchAlgorithmException | RemoteException e) {
215 Slog.e(TAG, "Fail to tie managed profile", e);
216 // Nothing client can do to fix this issue, so we do not throw exception out
217 }
218 }
219
Amith Yamasani52c489c2012-03-28 11:42:42 -0700220 public LockSettingsService(Context context) {
221 mContext = context;
Rakesh Iyera7aa4d62016-01-19 17:27:23 -0800222 mStrongAuth = new LockSettingsStrongAuth(context);
Amith Yamasani52c489c2012-03-28 11:42:42 -0700223 // Open the database
Jim Millerde1af082013-09-11 14:58:26 -0700224
225 mLockPatternUtils = new LockPatternUtils(context);
Paul Lawrence945490c2014-03-27 16:37:28 +0000226 mFirstCallToVold = true;
Robin Leef0246a82014-08-13 09:50:25 +0100227
228 IntentFilter filter = new IntentFilter();
229 filter.addAction(Intent.ACTION_USER_ADDED);
Adrian Roos3dcae682014-10-29 14:43:56 +0100230 filter.addAction(Intent.ACTION_USER_STARTING);
Adrian Roosdb0f76e2015-01-07 22:19:38 +0100231 filter.addAction(Intent.ACTION_USER_REMOVED);
Robin Leef0246a82014-08-13 09:50:25 +0100232 mContext.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, filter, null, null);
Adrian Roos261d5ab2014-10-29 14:42:38 +0100233
234 mStorage = new LockSettingsStorage(context, new LockSettingsStorage.Callback() {
235 @Override
236 public void initialize(SQLiteDatabase db) {
237 // Get the lockscreen default from a system property, if available
238 boolean lockScreenDisable = SystemProperties.getBoolean(
239 "ro.lockscreen.disable.default", false);
240 if (lockScreenDisable) {
241 mStorage.writeKeyValue(db, LockPatternUtils.DISABLE_LOCKSCREEN_KEY, "1", 0);
242 }
243 }
244 });
Jim Miller4f93c582016-01-27 19:05:43 -0800245 mNotificationManager = (NotificationManager)
246 mContext.getSystemService(Context.NOTIFICATION_SERVICE);
247 mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
248 }
249
250 /**
251 * If the account is credential-encrypted, show notification requesting the user to unlock
252 * the device.
253 */
Kenny Guyb1b30262016-02-09 16:02:35 +0000254 private void maybeShowEncryptionNotifications() {
255 final List<UserInfo> users = mUserManager.getUsers();
256 for (int i = 0; i < users.size(); i++) {
257 UserInfo user = users.get(i);
258 UserHandle userHandle = user.getUserHandle();
Jeff Sharkeyce18c812016-04-27 16:00:41 -0600259 if (!mUserManager.isUserUnlockingOrUnlocked(userHandle)) {
Kenny Guyb1b30262016-02-09 16:02:35 +0000260 if (!user.isManagedProfile()) {
261 showEncryptionNotification(userHandle);
262 } else {
263 UserInfo parent = mUserManager.getProfileParent(user.id);
Benjamin Franzf52709c2016-04-01 15:49:13 +0100264 if (parent != null &&
Jeff Sharkeyce18c812016-04-27 16:00:41 -0600265 mUserManager.isUserUnlockingOrUnlocked(parent.getUserHandle()) &&
Benjamin Franzf52709c2016-04-01 15:49:13 +0100266 !mUserManager.isQuietModeEnabled(userHandle)) {
Kenny Guyb1b30262016-02-09 16:02:35 +0000267 // Only show notifications for managed profiles once their parent
268 // user is unlocked.
269 showEncryptionNotificationForProfile(userHandle);
270 }
Jim Miller4f93c582016-01-27 19:05:43 -0800271 }
272 }
Jim Miller4f93c582016-01-27 19:05:43 -0800273 }
274 }
275
Kenny Guyb1b30262016-02-09 16:02:35 +0000276 private void showEncryptionNotificationForProfile(UserHandle user) {
277 Resources r = mContext.getResources();
278 CharSequence title = r.getText(
279 com.android.internal.R.string.user_encrypted_title);
280 CharSequence message = r.getText(
281 com.android.internal.R.string.profile_encrypted_message);
282 CharSequence detail = r.getText(
283 com.android.internal.R.string.profile_encrypted_detail);
284
285 final KeyguardManager km = (KeyguardManager) mContext.getSystemService(KEYGUARD_SERVICE);
286 final Intent unlockIntent = km.createConfirmDeviceCredentialIntent(null, null, user.getIdentifier());
287 if (unlockIntent == null) {
288 return;
289 }
290 unlockIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
291 PendingIntent intent = PendingIntent.getActivity(mContext, 0, unlockIntent,
292 PendingIntent.FLAG_UPDATE_CURRENT);
293
294 showEncryptionNotification(user, title, message, detail, intent);
295 }
296
Jim Miller4f93c582016-01-27 19:05:43 -0800297 private void showEncryptionNotification(UserHandle user) {
Jim Miller4f93c582016-01-27 19:05:43 -0800298 Resources r = mContext.getResources();
299 CharSequence title = r.getText(
300 com.android.internal.R.string.user_encrypted_title);
301 CharSequence message = r.getText(
302 com.android.internal.R.string.user_encrypted_message);
303 CharSequence detail = r.getText(
304 com.android.internal.R.string.user_encrypted_detail);
305
306 PendingIntent intent = PendingIntent.getBroadcast(mContext, 0, ACTION_NULL,
307 PendingIntent.FLAG_UPDATE_CURRENT);
308
Kenny Guyb1b30262016-02-09 16:02:35 +0000309 showEncryptionNotification(user, title, message, detail, intent);
310 }
311
312 private void showEncryptionNotification(UserHandle user, CharSequence title, CharSequence message,
313 CharSequence detail, PendingIntent intent) {
314 if (DEBUG) Slog.v(TAG, "showing encryption notification, user: " + user.getIdentifier());
Jim Miller4f93c582016-01-27 19:05:43 -0800315 Notification notification = new Notification.Builder(mContext)
Jim Millerb1135b72016-02-02 17:08:56 -0800316 .setSmallIcon(com.android.internal.R.drawable.ic_user_secure)
Jim Miller4f93c582016-01-27 19:05:43 -0800317 .setWhen(0)
318 .setOngoing(true)
319 .setTicker(title)
320 .setDefaults(0) // please be quiet
321 .setPriority(Notification.PRIORITY_MAX)
322 .setColor(mContext.getColor(
323 com.android.internal.R.color.system_notification_accent_color))
324 .setContentTitle(title)
325 .setContentText(message)
Selim Cinek0f9dd1e2016-04-05 17:03:40 -0700326 .setSubText(detail)
Jim Miller4f93c582016-01-27 19:05:43 -0800327 .setVisibility(Notification.VISIBILITY_PUBLIC)
328 .setContentIntent(intent)
329 .build();
330 mNotificationManager.notifyAsUser(null, FBE_ENCRYPTED_NOTIFICATION, notification, user);
331 }
332
333 public void hideEncryptionNotification(UserHandle userHandle) {
334 if (DEBUG) Slog.v(TAG, "hide encryption notification, user: "+ userHandle.getIdentifier());
335 mNotificationManager.cancelAsUser(null, FBE_ENCRYPTED_NOTIFICATION, userHandle);
336 }
337
338 public void onCleanupUser(int userId) {
339 hideEncryptionNotification(new UserHandle(userId));
340 }
341
Kenny Guyb1b30262016-02-09 16:02:35 +0000342 public void onUnlockUser(int userId) {
Ricky Wai84812582016-05-10 20:11:59 +0100343 // Hide notification first, as tie managed profile lock takes time
Kenny Guyb1b30262016-02-09 16:02:35 +0000344 hideEncryptionNotification(new UserHandle(userId));
Ricky Wai84812582016-05-10 20:11:59 +0100345 tieManagedProfileLockIfNecessary(userId, null);
Kenny Guyb1b30262016-02-09 16:02:35 +0000346
347 // Now we have unlocked the parent user we should show notifications
348 // about any profiles that exist.
349 List<UserInfo> profiles = mUserManager.getProfiles(userId);
350 for (int i = 0; i < profiles.size(); i++) {
351 UserInfo profile = profiles.get(i);
352 if (profile.isManagedProfile()) {
353 UserHandle userHandle = profile.getUserHandle();
Jeff Sharkeyce18c812016-04-27 16:00:41 -0600354 if (!mUserManager.isUserUnlockingOrUnlocked(userHandle) &&
Benjamin Franzf52709c2016-04-01 15:49:13 +0100355 !mUserManager.isQuietModeEnabled(userHandle)) {
Kenny Guyb1b30262016-02-09 16:02:35 +0000356 showEncryptionNotificationForProfile(userHandle);
357 }
358 }
359 }
Amith Yamasani52c489c2012-03-28 11:42:42 -0700360 }
361
Robin Leef0246a82014-08-13 09:50:25 +0100362 private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
363 @Override
364 public void onReceive(Context context, Intent intent) {
Robin Lee1096cf82014-09-01 16:52:47 +0100365 if (Intent.ACTION_USER_ADDED.equals(intent.getAction())) {
Chad Brubaker83ce0952015-05-12 13:00:02 -0700366 // Notify keystore that a new user was added.
Robin Leef0246a82014-08-13 09:50:25 +0100367 final int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
Robin Lee49d810c2014-09-23 13:50:22 +0100368 final KeyStore ks = KeyStore.getInstance();
Ricky Waidc283a82016-03-24 19:55:08 +0000369 final UserInfo parentInfo = mUserManager.getProfileParent(userHandle);
Chad Brubaker83ce0952015-05-12 13:00:02 -0700370 final int parentHandle = parentInfo != null ? parentInfo.id : -1;
371 ks.onUserAdded(userHandle, parentHandle);
Adrian Roos3dcae682014-10-29 14:43:56 +0100372 } else if (Intent.ACTION_USER_STARTING.equals(intent.getAction())) {
373 final int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
374 mStorage.prefetchUser(userHandle);
Adrian Roosdb0f76e2015-01-07 22:19:38 +0100375 } else if (Intent.ACTION_USER_REMOVED.equals(intent.getAction())) {
376 final int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
377 if (userHandle > 0) {
378 removeUser(userHandle);
379 }
Robin Leef0246a82014-08-13 09:50:25 +0100380 }
381 }
382 };
383
Jim Miller4f93c582016-01-27 19:05:43 -0800384 @Override // binder interface
Amith Yamasani52c489c2012-03-28 11:42:42 -0700385 public void systemReady() {
386 migrateOldData();
Andres Morales301ea442015-04-17 09:15:47 -0700387 try {
388 getGateKeeperService();
389 } catch (RemoteException e) {
390 Slog.e(TAG, "Failure retrieving IGateKeeperService", e);
391 }
Xiaohui Chen7c696362015-09-16 09:56:14 -0700392 // TODO: maybe skip this for split system user mode.
393 mStorage.prefetchUser(UserHandle.USER_SYSTEM);
Amith Yamasani52c489c2012-03-28 11:42:42 -0700394 }
395
396 private void migrateOldData() {
397 try {
Jim Miller187ec582013-04-15 18:27:54 -0700398 // These Settings moved before multi-user was enabled, so we only have to do it for the
399 // root user.
400 if (getString("migrated", null, 0) == null) {
401 final ContentResolver cr = mContext.getContentResolver();
402 for (String validSetting : VALID_SETTINGS) {
403 String value = Settings.Secure.getString(cr, validSetting);
404 if (value != null) {
405 setString(validSetting, value, 0);
406 }
407 }
408 // No need to move the password / pattern files. They're already in the right place.
409 setString("migrated", "true", 0);
410 Slog.i(TAG, "Migrated lock settings to new location");
Amith Yamasani52c489c2012-03-28 11:42:42 -0700411 }
412
Jim Miller187ec582013-04-15 18:27:54 -0700413 // These Settings changed after multi-user was enabled, hence need to be moved per user.
414 if (getString("migrated_user_specific", null, 0) == null) {
Jim Miller187ec582013-04-15 18:27:54 -0700415 final ContentResolver cr = mContext.getContentResolver();
Ricky Waidc283a82016-03-24 19:55:08 +0000416 List<UserInfo> users = mUserManager.getUsers();
Jim Miller187ec582013-04-15 18:27:54 -0700417 for (int user = 0; user < users.size(); user++) {
Jim Miller2d8ecf9d2013-04-22 17:17:03 -0700418 // Migrate owner info
419 final int userId = users.get(user).id;
420 final String OWNER_INFO = Secure.LOCK_SCREEN_OWNER_INFO;
421 String ownerInfo = Settings.Secure.getStringForUser(cr, OWNER_INFO, userId);
Jim Miller325c5672016-03-01 19:21:47 -0800422 if (!TextUtils.isEmpty(ownerInfo)) {
Jim Miller2d8ecf9d2013-04-22 17:17:03 -0700423 setString(OWNER_INFO, ownerInfo, userId);
Jim Miller325c5672016-03-01 19:21:47 -0800424 Settings.Secure.putStringForUser(cr, OWNER_INFO, "", userId);
Jim Miller2d8ecf9d2013-04-22 17:17:03 -0700425 }
Jim Miller187ec582013-04-15 18:27:54 -0700426
Jim Miller2d8ecf9d2013-04-22 17:17:03 -0700427 // Migrate owner info enabled. Note there was a bug where older platforms only
428 // stored this value if the checkbox was toggled at least once. The code detects
429 // this case by handling the exception.
430 final String OWNER_INFO_ENABLED = Secure.LOCK_SCREEN_OWNER_INFO_ENABLED;
431 boolean enabled;
432 try {
433 int ivalue = Settings.Secure.getIntForUser(cr, OWNER_INFO_ENABLED, userId);
434 enabled = ivalue != 0;
435 setLong(OWNER_INFO_ENABLED, enabled ? 1 : 0, userId);
436 } catch (SettingNotFoundException e) {
437 // Setting was never stored. Store it if the string is not empty.
438 if (!TextUtils.isEmpty(ownerInfo)) {
439 setLong(OWNER_INFO_ENABLED, 1, userId);
Jim Miller187ec582013-04-15 18:27:54 -0700440 }
441 }
Jim Miller2d8ecf9d2013-04-22 17:17:03 -0700442 Settings.Secure.putIntForUser(cr, OWNER_INFO_ENABLED, 0, userId);
Amith Yamasani52c489c2012-03-28 11:42:42 -0700443 }
Jim Miller187ec582013-04-15 18:27:54 -0700444 // No need to move the password / pattern files. They're already in the right place.
445 setString("migrated_user_specific", "true", 0);
446 Slog.i(TAG, "Migrated per-user lock settings to new location");
Amith Yamasani52c489c2012-03-28 11:42:42 -0700447 }
Adrian Roos230635e2015-01-07 20:50:29 +0100448
449 // Migrates biometric weak such that the fallback mechanism becomes the primary.
450 if (getString("migrated_biometric_weak", null, 0) == null) {
Ricky Waidc283a82016-03-24 19:55:08 +0000451 List<UserInfo> users = mUserManager.getUsers();
Adrian Roos230635e2015-01-07 20:50:29 +0100452 for (int i = 0; i < users.size(); i++) {
453 int userId = users.get(i).id;
454 long type = getLong(LockPatternUtils.PASSWORD_TYPE_KEY,
455 DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED,
456 userId);
457 long alternateType = getLong(LockPatternUtils.PASSWORD_TYPE_ALTERNATE_KEY,
458 DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED,
459 userId);
460 if (type == DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK) {
461 setLong(LockPatternUtils.PASSWORD_TYPE_KEY,
462 alternateType,
463 userId);
464 }
465 setLong(LockPatternUtils.PASSWORD_TYPE_ALTERNATE_KEY,
466 DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED,
467 userId);
468 }
469 setString("migrated_biometric_weak", "true", 0);
470 Slog.i(TAG, "Migrated biometric weak to use the fallback instead");
471 }
Adrian Roos43830582015-04-21 16:04:43 -0700472
473 // Migrates lockscreen.disabled. Prior to M, the flag was ignored when more than one
474 // user was present on the system, so if we're upgrading to M and there is more than one
475 // user we disable the flag to remain consistent.
476 if (getString("migrated_lockscreen_disabled", null, 0) == null) {
Ricky Waidc283a82016-03-24 19:55:08 +0000477 final List<UserInfo> users = mUserManager.getUsers();
Adrian Roos43830582015-04-21 16:04:43 -0700478 final int userCount = users.size();
479 int switchableUsers = 0;
480 for (int i = 0; i < userCount; i++) {
481 if (users.get(i).supportsSwitchTo()) {
482 switchableUsers++;
483 }
484 }
485
486 if (switchableUsers > 1) {
487 for (int i = 0; i < userCount; i++) {
488 int id = users.get(i).id;
489
490 if (getBoolean(LockPatternUtils.DISABLE_LOCKSCREEN_KEY, false, id)) {
491 setBoolean(LockPatternUtils.DISABLE_LOCKSCREEN_KEY, false, id);
492 }
493 }
494 }
495
496 setString("migrated_lockscreen_disabled", "true", 0);
497 Slog.i(TAG, "Migrated lockscreen disabled flag");
498 }
Amith Yamasani52c489c2012-03-28 11:42:42 -0700499 } catch (RemoteException re) {
Jim Miller187ec582013-04-15 18:27:54 -0700500 Slog.e(TAG, "Unable to migrate old data", re);
Amith Yamasani52c489c2012-03-28 11:42:42 -0700501 }
502 }
503
Jim Miller5ecd8112013-01-09 18:50:26 -0800504 private final void checkWritePermission(int userId) {
Jim Miller505329b2013-11-08 13:25:36 -0800505 mContext.enforceCallingOrSelfPermission(PERMISSION, "LockSettingsWrite");
Amith Yamasani52c489c2012-03-28 11:42:42 -0700506 }
507
Jim Miller5ecd8112013-01-09 18:50:26 -0800508 private final void checkPasswordReadPermission(int userId) {
Jim Miller505329b2013-11-08 13:25:36 -0800509 mContext.enforceCallingOrSelfPermission(PERMISSION, "LockSettingsRead");
Amith Yamasani52c489c2012-03-28 11:42:42 -0700510 }
511
Jim Miller158fe192013-04-17 15:23:55 -0700512 private final void checkReadPermission(String requestedKey, int userId) {
Amith Yamasani52c489c2012-03-28 11:42:42 -0700513 final int callingUid = Binder.getCallingUid();
Adrian Roos001b00d2015-02-24 17:08:48 +0100514
Svetoslav Ganov6d2c0e52015-06-23 16:33:36 +0000515 for (int i = 0; i < READ_CONTACTS_PROTECTED_SETTINGS.length; i++) {
516 String key = READ_CONTACTS_PROTECTED_SETTINGS[i];
517 if (key.equals(requestedKey) && mContext.checkCallingOrSelfPermission(READ_CONTACTS)
Jim Miller158fe192013-04-17 15:23:55 -0700518 != PackageManager.PERMISSION_GRANTED) {
519 throw new SecurityException("uid=" + callingUid
Svetoslav Ganov6d2c0e52015-06-23 16:33:36 +0000520 + " needs permission " + READ_CONTACTS + " to read "
Jim Miller158fe192013-04-17 15:23:55 -0700521 + requestedKey + " for user " + userId);
522 }
Amith Yamasani52c489c2012-03-28 11:42:42 -0700523 }
Adrian Roos001b00d2015-02-24 17:08:48 +0100524
525 for (int i = 0; i < READ_PASSWORD_PROTECTED_SETTINGS.length; i++) {
526 String key = READ_PASSWORD_PROTECTED_SETTINGS[i];
527 if (key.equals(requestedKey) && mContext.checkCallingOrSelfPermission(PERMISSION)
528 != PackageManager.PERMISSION_GRANTED) {
529 throw new SecurityException("uid=" + callingUid
530 + " needs permission " + PERMISSION + " to read "
531 + requestedKey + " for user " + userId);
532 }
533 }
Amith Yamasani52c489c2012-03-28 11:42:42 -0700534 }
535
536 @Override
Ricky Waidc283a82016-03-24 19:55:08 +0000537 public boolean getSeparateProfileChallengeEnabled(int userId) throws RemoteException {
538 synchronized (mSeparateChallengeLock) {
539 return getBoolean(SEPARATE_PROFILE_CHALLENGE_KEY, false, userId);
540 }
541 }
542
543 @Override
544 public void setSeparateProfileChallengeEnabled(int userId, boolean enabled,
545 String managedUserPassword) throws RemoteException {
546 synchronized (mSeparateChallengeLock) {
547 setBoolean(SEPARATE_PROFILE_CHALLENGE_KEY, enabled, userId);
548 if (enabled) {
549 mStorage.removeChildProfileLock(userId);
550 removeKeystoreProfileKey(userId);
551 } else {
552 tieManagedProfileLockIfNecessary(userId, managedUserPassword);
553 }
554 }
555 }
556
557 @Override
Amith Yamasani52c489c2012-03-28 11:42:42 -0700558 public void setBoolean(String key, boolean value, int userId) throws RemoteException {
559 checkWritePermission(userId);
Adrian Roos261d5ab2014-10-29 14:42:38 +0100560 setStringUnchecked(key, userId, value ? "1" : "0");
Amith Yamasani52c489c2012-03-28 11:42:42 -0700561 }
562
563 @Override
564 public void setLong(String key, long value, int userId) throws RemoteException {
565 checkWritePermission(userId);
Adrian Roos261d5ab2014-10-29 14:42:38 +0100566 setStringUnchecked(key, userId, Long.toString(value));
Amith Yamasani52c489c2012-03-28 11:42:42 -0700567 }
568
569 @Override
570 public void setString(String key, String value, int userId) throws RemoteException {
571 checkWritePermission(userId);
Adrian Roos261d5ab2014-10-29 14:42:38 +0100572 setStringUnchecked(key, userId, value);
573 }
Amith Yamasani52c489c2012-03-28 11:42:42 -0700574
Adrian Roos261d5ab2014-10-29 14:42:38 +0100575 private void setStringUnchecked(String key, int userId, String value) {
576 mStorage.writeKeyValue(key, value, userId);
Amith Yamasani072543f2015-02-13 11:09:45 -0800577 if (ArrayUtils.contains(SETTINGS_TO_BACKUP, key)) {
578 BackupManager.dataChanged("com.android.providers.settings");
579 }
Amith Yamasani52c489c2012-03-28 11:42:42 -0700580 }
581
582 @Override
583 public boolean getBoolean(String key, boolean defaultValue, int userId) throws RemoteException {
Jim Miller158fe192013-04-17 15:23:55 -0700584 checkReadPermission(key, userId);
Adrian Roos9dd16eb2015-01-08 16:20:49 +0100585 String value = getStringUnchecked(key, null, userId);
Amith Yamasani52c489c2012-03-28 11:42:42 -0700586 return TextUtils.isEmpty(value) ?
587 defaultValue : (value.equals("1") || value.equals("true"));
588 }
589
590 @Override
591 public long getLong(String key, long defaultValue, int userId) throws RemoteException {
Jim Miller158fe192013-04-17 15:23:55 -0700592 checkReadPermission(key, userId);
Amith Yamasani52c489c2012-03-28 11:42:42 -0700593
Adrian Roos9dd16eb2015-01-08 16:20:49 +0100594 String value = getStringUnchecked(key, null, userId);
Amith Yamasani52c489c2012-03-28 11:42:42 -0700595 return TextUtils.isEmpty(value) ? defaultValue : Long.parseLong(value);
596 }
597
598 @Override
599 public String getString(String key, String defaultValue, int userId) throws RemoteException {
Jim Miller158fe192013-04-17 15:23:55 -0700600 checkReadPermission(key, userId);
Amith Yamasani52c489c2012-03-28 11:42:42 -0700601
Adrian Roos9dd16eb2015-01-08 16:20:49 +0100602 return getStringUnchecked(key, defaultValue, userId);
603 }
604
605 public String getStringUnchecked(String key, String defaultValue, int userId) {
606 if (Settings.Secure.LOCK_PATTERN_ENABLED.equals(key)) {
Adrian Roos7811d9f2015-07-27 15:10:13 -0700607 long ident = Binder.clearCallingIdentity();
608 try {
609 return mLockPatternUtils.isLockPatternEnabled(userId) ? "1" : "0";
610 } finally {
611 Binder.restoreCallingIdentity(ident);
612 }
Adrian Roos9dd16eb2015-01-08 16:20:49 +0100613 }
614
Bryce Lee46145962015-12-14 14:39:10 -0800615 if (LockPatternUtils.LEGACY_LOCK_PATTERN_ENABLED.equals(key)) {
616 key = Settings.Secure.LOCK_PATTERN_ENABLED;
617 }
618
Adrian Roos261d5ab2014-10-29 14:42:38 +0100619 return mStorage.readKeyValue(key, defaultValue, userId);
Amith Yamasani52c489c2012-03-28 11:42:42 -0700620 }
621
Adrian Roos4f788452014-05-22 20:45:59 +0200622 @Override
Amith Yamasani52c489c2012-03-28 11:42:42 -0700623 public boolean havePassword(int userId) throws RemoteException {
624 // Do we need a permissions check here?
Adrian Roos261d5ab2014-10-29 14:42:38 +0100625 return mStorage.hasPassword(userId);
Amith Yamasani52c489c2012-03-28 11:42:42 -0700626 }
627
628 @Override
629 public boolean havePattern(int userId) throws RemoteException {
630 // Do we need a permissions check here?
Adrian Roos261d5ab2014-10-29 14:42:38 +0100631 return mStorage.hasPattern(userId);
Amith Yamasani52c489c2012-03-28 11:42:42 -0700632 }
633
Chad Brubakera91a8502015-05-07 10:02:22 -0700634 private void setKeystorePassword(String password, int userHandle) {
Robin Leef0246a82014-08-13 09:50:25 +0100635 final KeyStore ks = KeyStore.getInstance();
Ricky Waidc283a82016-03-24 19:55:08 +0000636 ks.onUserPasswordChanged(userHandle, password);
Chad Brubakera91a8502015-05-07 10:02:22 -0700637 }
638
639 private void unlockKeystore(String password, int userHandle) {
Ricky Waidc283a82016-03-24 19:55:08 +0000640 if (DEBUG) Slog.v(TAG, "Unlock keystore for user: " + userHandle);
Chad Brubakera91a8502015-05-07 10:02:22 -0700641 final KeyStore ks = KeyStore.getInstance();
Ricky Waidc283a82016-03-24 19:55:08 +0000642 ks.unlock(userHandle, password);
643 }
Chad Brubakera91a8502015-05-07 10:02:22 -0700644
Ricky Waidc283a82016-03-24 19:55:08 +0000645 private String getDecryptedPasswordForTiedProfile(int userId)
646 throws KeyStoreException, UnrecoverableKeyException,
647 NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException,
648 InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException,
649 CertificateException, IOException {
650 if (DEBUG) Slog.v(TAG, "Unlock keystore for child profile");
651 byte[] storedData = mStorage.readChildProfileLock(userId);
652 if (storedData == null) {
653 throw new FileNotFoundException("Child profile lock file not found");
654 }
655 byte[] iv = Arrays.copyOfRange(storedData, 0, PROFILE_KEY_IV_SIZE);
656 byte[] encryptedPassword = Arrays.copyOfRange(storedData, PROFILE_KEY_IV_SIZE,
657 storedData.length);
658 byte[] decryptionResult;
659 java.security.KeyStore keyStore = java.security.KeyStore.getInstance("AndroidKeyStore");
660 keyStore.load(null);
661 SecretKey decryptionKey = (SecretKey) keyStore.getKey(
662 PROFILE_KEY_NAME_DECRYPT + userId, null);
663
664 Cipher cipher = Cipher.getInstance(KeyProperties.KEY_ALGORITHM_AES + "/"
665 + KeyProperties.BLOCK_MODE_GCM + "/" + KeyProperties.ENCRYPTION_PADDING_NONE);
666
667 cipher.init(Cipher.DECRYPT_MODE, decryptionKey, new GCMParameterSpec(128, iv));
668 decryptionResult = cipher.doFinal(encryptedPassword);
669 return new String(decryptionResult, StandardCharsets.UTF_8);
670 }
671
672 private void unlockChildProfile(int profileHandle) throws RemoteException {
673 try {
674 doVerifyPassword(getDecryptedPasswordForTiedProfile(profileHandle), false,
675 0 /* no challenge */, profileHandle);
676 } catch (UnrecoverableKeyException | InvalidKeyException | KeyStoreException
677 | NoSuchAlgorithmException | NoSuchPaddingException
678 | InvalidAlgorithmParameterException | IllegalBlockSizeException
679 | BadPaddingException | CertificateException | IOException e) {
680 if (e instanceof FileNotFoundException) {
681 Slog.i(TAG, "Child profile key not found");
Clara Bayarri0a587d22016-02-23 14:49:41 -0800682 } else {
Ricky Waidc283a82016-03-24 19:55:08 +0000683 Slog.e(TAG, "Failed to decrypt child profile key", e);
Clara Bayarri0a587d22016-02-23 14:49:41 -0800684 }
Jim Millerde1af082013-09-11 14:58:26 -0700685 }
686 }
687
Paul Crowleyfaeb3eb2016-02-08 15:58:29 +0000688 private void unlockUser(int userId, byte[] token, byte[] secret) {
Jeff Sharkeybd91e2f2016-03-22 15:32:31 -0600689 // TODO: make this method fully async so we can update UI with progress strings
690 final CountDownLatch latch = new CountDownLatch(1);
691 final IProgressListener listener = new IProgressListener.Stub() {
692 @Override
693 public void onStarted(int id, Bundle extras) throws RemoteException {
Jeff Sharkeyfd241082016-04-19 15:58:24 -0600694 Log.d(TAG, "unlockUser started");
Jeff Sharkeybd91e2f2016-03-22 15:32:31 -0600695 }
696
697 @Override
698 public void onProgress(int id, int progress, Bundle extras) throws RemoteException {
Jeff Sharkeyfd241082016-04-19 15:58:24 -0600699 Log.d(TAG, "unlockUser progress " + progress);
Jeff Sharkeybd91e2f2016-03-22 15:32:31 -0600700 }
701
702 @Override
703 public void onFinished(int id, Bundle extras) throws RemoteException {
Jeff Sharkeyfd241082016-04-19 15:58:24 -0600704 Log.d(TAG, "unlockUser finished");
Jeff Sharkeybd91e2f2016-03-22 15:32:31 -0600705 latch.countDown();
706 }
707 };
708
Ricky Wai7881cf82016-04-15 17:20:12 +0100709 // Turn off quite mode if it's enabled, only managed profile can return true for now, it
710 // will return false if it is not a managed profile.
711 if (mUserManager.isQuietModeEnabled(new UserHandle(userId))) {
Benjamin Franzf02420c2016-04-04 18:52:21 +0100712 mUserManager.setQuietModeEnabled(userId, false);
713 }
714
Jeff Sharkey8924e872015-11-30 12:52:10 -0700715 try {
Jeff Sharkeybd91e2f2016-03-22 15:32:31 -0600716 ActivityManagerNative.getDefault().unlockUser(userId, token, secret, listener);
Jeff Sharkey8924e872015-11-30 12:52:10 -0700717 } catch (RemoteException e) {
718 throw e.rethrowAsRuntimeException();
719 }
Jeff Sharkeybd91e2f2016-03-22 15:32:31 -0600720
721 try {
722 latch.await(15, TimeUnit.SECONDS);
723 } catch (InterruptedException e) {
724 Thread.currentThread().interrupt();
725 }
Ricky Waidc283a82016-03-24 19:55:08 +0000726 try {
727 if (!mUserManager.getUserInfo(userId).isManagedProfile()) {
728 final List<UserInfo> profiles = mUserManager.getProfiles(userId);
729 for (UserInfo pi : profiles) {
730 // Unlock managed profile with unified lock
731 if (pi.isManagedProfile()
732 && !mLockPatternUtils.isSeparateProfileChallengeEnabled(pi.id)
Ricky Wai7881cf82016-04-15 17:20:12 +0100733 && !pi.isQuietModeEnabled()
Ricky Waidc283a82016-03-24 19:55:08 +0000734 && mStorage.hasChildProfileLock(pi.id)) {
735 unlockChildProfile(pi.id);
736 }
737 }
738 }
739 } catch (RemoteException e) {
740 Log.d(TAG, "Failed to unlock child profile", e);
741 }
Jeff Sharkey8924e872015-11-30 12:52:10 -0700742 }
Amith Yamasani52c489c2012-03-28 11:42:42 -0700743
Andres Morales8fa56652015-03-31 09:19:50 -0700744 private byte[] getCurrentHandle(int userId) {
745 CredentialHash credential;
746 byte[] currentHandle;
Jim Millerde1af082013-09-11 14:58:26 -0700747
Andres Morales8fa56652015-03-31 09:19:50 -0700748 int currentHandleType = mStorage.getStoredCredentialType(userId);
749 switch (currentHandleType) {
750 case CredentialHash.TYPE_PATTERN:
751 credential = mStorage.readPatternHash(userId);
752 currentHandle = credential != null
753 ? credential.hash
754 : null;
755 break;
756 case CredentialHash.TYPE_PASSWORD:
757 credential = mStorage.readPasswordHash(userId);
758 currentHandle = credential != null
759 ? credential.hash
760 : null;
761 break;
762 case CredentialHash.TYPE_NONE:
763 default:
764 currentHandle = null;
765 break;
766 }
767
768 // sanity check
769 if (currentHandleType != CredentialHash.TYPE_NONE && currentHandle == null) {
770 Slog.e(TAG, "Stored handle type [" + currentHandleType + "] but no handle available");
771 }
772
773 return currentHandle;
Amith Yamasani52c489c2012-03-28 11:42:42 -0700774 }
775
Ricky Waidc283a82016-03-24 19:55:08 +0000776 private void onUserLockChanged(int userId) throws RemoteException {
777 if (mUserManager.getUserInfo(userId).isManagedProfile()) {
778 return;
779 }
780 final boolean isSecure = mStorage.hasPassword(userId) || mStorage.hasPattern(userId);
781 final List<UserInfo> profiles = mUserManager.getProfiles(userId);
782 final int size = profiles.size();
783 for (int i = 0; i < size; i++) {
784 final UserInfo profile = profiles.get(i);
785 if (profile.isManagedProfile()) {
786 final int managedUserId = profile.id;
787 if (mLockPatternUtils.isSeparateProfileChallengeEnabled(managedUserId)) {
788 continue;
789 }
790 if (isSecure) {
791 tieManagedProfileLockIfNecessary(managedUserId, null);
792 } else {
793 getGateKeeperService().clearSecureUserId(managedUserId);
794 mStorage.writePatternHash(null, managedUserId);
795 setKeystorePassword(null, managedUserId);
Paul Crowleya1eb7502016-05-10 20:36:56 +0000796 clearUserKeyProtection(managedUserId);
Ricky Waidc283a82016-03-24 19:55:08 +0000797 mStorage.removeChildProfileLock(managedUserId);
798 removeKeystoreProfileKey(managedUserId);
799 }
800 }
801 }
802 }
Andres Morales8fa56652015-03-31 09:19:50 -0700803
Ricky Waidc283a82016-03-24 19:55:08 +0000804 private boolean isManagedProfileWithUnifiedLock(int userId) {
805 return mUserManager.getUserInfo(userId).isManagedProfile()
806 && !mLockPatternUtils.isSeparateProfileChallengeEnabled(userId);
807 }
808
809 private boolean isManagedProfileWithSeparatedLock(int userId) {
810 return mUserManager.getUserInfo(userId).isManagedProfile()
811 && mLockPatternUtils.isSeparateProfileChallengeEnabled(userId);
812 }
813
814 // This method should be called by LockPatternUtil only, all internal methods in this class
815 // should call setLockPatternInternal.
Amith Yamasani52c489c2012-03-28 11:42:42 -0700816 @Override
Andres Morales8fa56652015-03-31 09:19:50 -0700817 public void setLockPattern(String pattern, String savedCredential, int userId)
818 throws RemoteException {
Jim Millere484eaf2016-04-13 16:35:36 -0700819 checkWritePermission(userId);
Ricky Waidc283a82016-03-24 19:55:08 +0000820 synchronized (mSeparateChallengeLock) {
821 setLockPatternInternal(pattern, savedCredential, userId);
822 setSeparateProfileChallengeEnabled(userId, true, null);
823 }
824 }
825
826 public void setLockPatternInternal(String pattern, String savedCredential, int userId)
827 throws RemoteException {
Andres Morales8fa56652015-03-31 09:19:50 -0700828 byte[] currentHandle = getCurrentHandle(userId);
829
Andres Moralesd9fc85a2015-04-09 19:14:42 -0700830 if (pattern == null) {
Andres Moralescfb61602015-04-16 16:31:15 -0700831 getGateKeeperService().clearSecureUserId(userId);
Andres Moralesd9fc85a2015-04-09 19:14:42 -0700832 mStorage.writePatternHash(null, userId);
Chad Brubakera91a8502015-05-07 10:02:22 -0700833 setKeystorePassword(null, userId);
Paul Crowleya1eb7502016-05-10 20:36:56 +0000834 clearUserKeyProtection(userId);
Ricky Waidc283a82016-03-24 19:55:08 +0000835 onUserLockChanged(userId);
Andres Moralesd9fc85a2015-04-09 19:14:42 -0700836 return;
837 }
838
Ricky Waidc283a82016-03-24 19:55:08 +0000839 if (isManagedProfileWithUnifiedLock(userId)) {
840 // get credential from keystore when managed profile has unified lock
841 try {
842 savedCredential = getDecryptedPasswordForTiedProfile(userId);
843 } catch (UnrecoverableKeyException | InvalidKeyException | KeyStoreException
844 | NoSuchAlgorithmException | NoSuchPaddingException
845 | InvalidAlgorithmParameterException | IllegalBlockSizeException
846 | BadPaddingException | CertificateException | IOException e) {
847 if (e instanceof FileNotFoundException) {
848 Slog.i(TAG, "Child profile key not found");
849 } else {
850 Slog.e(TAG, "Failed to decrypt child profile key", e);
851 }
Andres Morales8fa56652015-03-31 09:19:50 -0700852 }
Ricky Waidc283a82016-03-24 19:55:08 +0000853 } else {
854 if (currentHandle == null) {
855 if (savedCredential != null) {
856 Slog.w(TAG, "Saved credential provided, but none stored");
857 }
858 savedCredential = null;
859 }
Andres Morales8fa56652015-03-31 09:19:50 -0700860 }
861
862 byte[] enrolledHandle = enrollCredential(currentHandle, savedCredential, pattern, userId);
863 if (enrolledHandle != null) {
864 mStorage.writePatternHash(enrolledHandle, userId);
Paul Crowleya1eb7502016-05-10 20:36:56 +0000865 setUserKeyProtection(userId, pattern, verifyPattern(pattern, 0, userId));
Ricky Waidc283a82016-03-24 19:55:08 +0000866 onUserLockChanged(userId);
Andres Morales8fa56652015-03-31 09:19:50 -0700867 } else {
Andres Morales2c4a5732015-07-09 16:11:00 -0700868 throw new RemoteException("Failed to enroll pattern");
Andres Morales8fa56652015-03-31 09:19:50 -0700869 }
870 }
871
Ricky Waidc283a82016-03-24 19:55:08 +0000872 // This method should be called by LockPatternUtil only, all internal methods in this class
873 // should call setLockPasswordInternal.
Andres Morales8fa56652015-03-31 09:19:50 -0700874 @Override
875 public void setLockPassword(String password, String savedCredential, int userId)
876 throws RemoteException {
Jim Millere484eaf2016-04-13 16:35:36 -0700877 checkWritePermission(userId);
Ricky Waidc283a82016-03-24 19:55:08 +0000878 synchronized (mSeparateChallengeLock) {
879 setLockPasswordInternal(password, savedCredential, userId);
880 setSeparateProfileChallengeEnabled(userId, true, null);
881 }
882 }
Andres Morales8fa56652015-03-31 09:19:50 -0700883
Ricky Waidc283a82016-03-24 19:55:08 +0000884 public void setLockPasswordInternal(String password, String savedCredential, int userId)
885 throws RemoteException {
886 byte[] currentHandle = getCurrentHandle(userId);
Andres Moralesd9fc85a2015-04-09 19:14:42 -0700887 if (password == null) {
Andres Moralescfb61602015-04-16 16:31:15 -0700888 getGateKeeperService().clearSecureUserId(userId);
Andres Moralesd9fc85a2015-04-09 19:14:42 -0700889 mStorage.writePasswordHash(null, userId);
Chad Brubakera91a8502015-05-07 10:02:22 -0700890 setKeystorePassword(null, userId);
Paul Crowleya1eb7502016-05-10 20:36:56 +0000891 clearUserKeyProtection(userId);
Ricky Waidc283a82016-03-24 19:55:08 +0000892 onUserLockChanged(userId);
Andres Moralesd9fc85a2015-04-09 19:14:42 -0700893 return;
894 }
895
Ricky Waidc283a82016-03-24 19:55:08 +0000896 if (isManagedProfileWithUnifiedLock(userId)) {
897 // get credential from keystore when managed profile has unified lock
898 try {
899 savedCredential = getDecryptedPasswordForTiedProfile(userId);
Ricky Waide3a0682016-05-03 14:50:03 +0100900 } catch (FileNotFoundException e) {
901 Slog.i(TAG, "Child profile key not found");
Ricky Waidc283a82016-03-24 19:55:08 +0000902 } catch (UnrecoverableKeyException | InvalidKeyException | KeyStoreException
903 | NoSuchAlgorithmException | NoSuchPaddingException
904 | InvalidAlgorithmParameterException | IllegalBlockSizeException
905 | BadPaddingException | CertificateException | IOException e) {
Ricky Waide3a0682016-05-03 14:50:03 +0100906 Slog.e(TAG, "Failed to decrypt child profile key", e);
Andres Morales8fa56652015-03-31 09:19:50 -0700907 }
Ricky Waidc283a82016-03-24 19:55:08 +0000908 } else {
909 if (currentHandle == null) {
910 if (savedCredential != null) {
911 Slog.w(TAG, "Saved credential provided, but none stored");
912 }
913 savedCredential = null;
914 }
Andres Morales8fa56652015-03-31 09:19:50 -0700915 }
916
917 byte[] enrolledHandle = enrollCredential(currentHandle, savedCredential, password, userId);
918 if (enrolledHandle != null) {
919 mStorage.writePasswordHash(enrolledHandle, userId);
Paul Crowleya1eb7502016-05-10 20:36:56 +0000920 setUserKeyProtection(userId, password, verifyPassword(password, 0, userId));
Ricky Waidc283a82016-03-24 19:55:08 +0000921 onUserLockChanged(userId);
Andres Morales8fa56652015-03-31 09:19:50 -0700922 } else {
Andres Morales2c4a5732015-07-09 16:11:00 -0700923 throw new RemoteException("Failed to enroll password");
Andres Morales8fa56652015-03-31 09:19:50 -0700924 }
925 }
926
Ricky Waidc283a82016-03-24 19:55:08 +0000927 private void tieProfileLockToParent(int userId, String password) {
928 if (DEBUG) Slog.v(TAG, "tieProfileLockToParent for user: " + userId);
929 byte[] randomLockSeed = password.getBytes(StandardCharsets.UTF_8);
930 byte[] encryptionResult;
931 byte[] iv;
932 try {
933 KeyGenerator keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES);
934 keyGenerator.init(new SecureRandom());
935 SecretKey secretKey = keyGenerator.generateKey();
936
937 java.security.KeyStore keyStore = java.security.KeyStore.getInstance("AndroidKeyStore");
938 keyStore.load(null);
939 keyStore.setEntry(
940 PROFILE_KEY_NAME_ENCRYPT + userId,
941 new java.security.KeyStore.SecretKeyEntry(secretKey),
942 new KeyProtection.Builder(KeyProperties.PURPOSE_ENCRYPT)
943 .setBlockModes(KeyProperties.BLOCK_MODE_GCM)
944 .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
945 .build());
946 keyStore.setEntry(
947 PROFILE_KEY_NAME_DECRYPT + userId,
948 new java.security.KeyStore.SecretKeyEntry(secretKey),
949 new KeyProtection.Builder(KeyProperties.PURPOSE_DECRYPT)
950 .setBlockModes(KeyProperties.BLOCK_MODE_GCM)
951 .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
952 .setUserAuthenticationRequired(true)
953 .setUserAuthenticationValidityDurationSeconds(30)
954 .build());
955
956 // Key imported, obtain a reference to it.
957 SecretKey keyStoreEncryptionKey = (SecretKey) keyStore.getKey(
958 PROFILE_KEY_NAME_ENCRYPT + userId, null);
959 // The original key can now be discarded.
960
961 Cipher cipher = Cipher.getInstance(
962 KeyProperties.KEY_ALGORITHM_AES + "/" + KeyProperties.BLOCK_MODE_GCM + "/"
963 + KeyProperties.ENCRYPTION_PADDING_NONE);
964 cipher.init(Cipher.ENCRYPT_MODE, keyStoreEncryptionKey);
965 encryptionResult = cipher.doFinal(randomLockSeed);
966 iv = cipher.getIV();
967 } catch (CertificateException | UnrecoverableKeyException
968 | IOException | BadPaddingException | IllegalBlockSizeException | KeyStoreException
969 | NoSuchPaddingException | NoSuchAlgorithmException | InvalidKeyException e) {
970 throw new RuntimeException("Failed to encrypt key", e);
971 }
972 ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
973 try {
974 if (iv.length != PROFILE_KEY_IV_SIZE) {
975 throw new RuntimeException("Invalid iv length: " + iv.length);
976 }
977 outputStream.write(iv);
978 outputStream.write(encryptionResult);
979 } catch (IOException e) {
980 throw new RuntimeException("Failed to concatenate byte arrays", e);
981 }
982 mStorage.writeChildProfileLock(userId, outputStream.toByteArray());
983 }
984
Andres Morales8fa56652015-03-31 09:19:50 -0700985 private byte[] enrollCredential(byte[] enrolledHandle,
986 String enrolledCredential, String toEnroll, int userId)
987 throws RemoteException {
Jim Millerde1af082013-09-11 14:58:26 -0700988 checkWritePermission(userId);
Andres Morales8fa56652015-03-31 09:19:50 -0700989 byte[] enrolledCredentialBytes = enrolledCredential == null
990 ? null
991 : enrolledCredential.getBytes();
992 byte[] toEnrollBytes = toEnroll == null
993 ? null
994 : toEnroll.getBytes();
Andres Morales23974272015-05-14 22:42:26 -0700995 GateKeeperResponse response = getGateKeeperService().enroll(userId, enrolledHandle,
996 enrolledCredentialBytes, toEnrollBytes);
Jim Millerde1af082013-09-11 14:58:26 -0700997
Andres Morales23974272015-05-14 22:42:26 -0700998 if (response == null) {
999 return null;
Andres Morales8fa56652015-03-31 09:19:50 -07001000 }
Jim Millerde1af082013-09-11 14:58:26 -07001001
Andres Morales23974272015-05-14 22:42:26 -07001002 byte[] hash = response.getPayload();
1003 if (hash != null) {
1004 setKeystorePassword(toEnroll, userId);
1005 } else {
1006 // Should not happen
1007 Slog.e(TAG, "Throttled while enrolling a password");
1008 }
Andres Morales8fa56652015-03-31 09:19:50 -07001009 return hash;
Jim Millerde1af082013-09-11 14:58:26 -07001010 }
1011
Paul Crowleyfaeb3eb2016-02-08 15:58:29 +00001012 private void setUserKeyProtection(int userId, String credential, VerifyCredentialResponse vcr)
1013 throws RemoteException {
1014 if (vcr == null) {
1015 throw new RemoteException("Null response verifying a credential we just set");
1016 }
1017 if (vcr.getResponseCode() != VerifyCredentialResponse.RESPONSE_OK) {
1018 throw new RemoteException("Non-OK response verifying a credential we just set: "
1019 + vcr.getResponseCode());
1020 }
1021 byte[] token = vcr.getPayload();
1022 if (token == null) {
1023 throw new RemoteException("Empty payload verifying a credential we just set");
1024 }
Paul Crowleya1eb7502016-05-10 20:36:56 +00001025 changeUserKey(userId, token, secretFromCredential(credential));
Paul Crowleyfaeb3eb2016-02-08 15:58:29 +00001026 }
1027
1028 private void clearUserKeyProtection(int userId) throws RemoteException {
Paul Crowleya1eb7502016-05-10 20:36:56 +00001029 changeUserKey(userId, null, null);
Paul Crowleyfaeb3eb2016-02-08 15:58:29 +00001030 }
1031
1032 private static byte[] secretFromCredential(String credential) throws RemoteException {
1033 try {
1034 MessageDigest digest = MessageDigest.getInstance("SHA-512");
1035 // Personalize the hash
1036 byte[] personalization = "Android FBE credential hash"
1037 .getBytes(StandardCharsets.UTF_8);
1038 // Pad it to the block size of the hash function
1039 personalization = Arrays.copyOf(personalization, 128);
1040 digest.update(personalization);
1041 digest.update(credential.getBytes(StandardCharsets.UTF_8));
1042 return digest.digest();
1043 } catch (NoSuchAlgorithmException e) {
1044 throw new RuntimeException("NoSuchAlgorithmException for SHA-512");
1045 }
1046 }
1047
Paul Crowleya1eb7502016-05-10 20:36:56 +00001048 private void changeUserKey(int userId, byte[] token, byte[] secret)
Paul Crowleyfaeb3eb2016-02-08 15:58:29 +00001049 throws RemoteException {
1050 final UserInfo userInfo = UserManager.get(mContext).getUserInfo(userId);
Paul Crowley815036f2016-03-29 14:14:48 -07001051 final IMountService mountService = getMountService();
1052 final long callingId = Binder.clearCallingIdentity();
1053 try {
Paul Crowleya1eb7502016-05-10 20:36:56 +00001054 mountService.changeUserKey(userId, userInfo.serialNumber, token, null, secret);
Paul Crowley815036f2016-03-29 14:14:48 -07001055 } finally {
1056 Binder.restoreCallingIdentity(callingId);
1057 }
Paul Crowleyfaeb3eb2016-02-08 15:58:29 +00001058 }
1059
Jim Millerde1af082013-09-11 14:58:26 -07001060 @Override
Andres Morales23974272015-05-14 22:42:26 -07001061 public VerifyCredentialResponse checkPattern(String pattern, int userId) throws RemoteException {
1062 return doVerifyPattern(pattern, false, 0, userId);
Andres Moralesd9fc85a2015-04-09 19:14:42 -07001063 }
Adrian Roos261d5ab2014-10-29 14:42:38 +01001064
Andres Moralesd9fc85a2015-04-09 19:14:42 -07001065 @Override
Andres Morales23974272015-05-14 22:42:26 -07001066 public VerifyCredentialResponse verifyPattern(String pattern, long challenge, int userId)
Andres Moralesd9fc85a2015-04-09 19:14:42 -07001067 throws RemoteException {
Andres Morales23974272015-05-14 22:42:26 -07001068 return doVerifyPattern(pattern, true, challenge, userId);
Andres Moralesd9fc85a2015-04-09 19:14:42 -07001069 }
1070
Andres Moralese40bad82015-05-28 14:21:36 -07001071 private VerifyCredentialResponse doVerifyPattern(String pattern, boolean hasChallenge,
1072 long challenge, int userId) throws RemoteException {
Andres Moralesd9fc85a2015-04-09 19:14:42 -07001073 checkPasswordReadPermission(userId);
Andres Moralesd9fc85a2015-04-09 19:14:42 -07001074 CredentialHash storedHash = mStorage.readPatternHash(userId);
Andres Moralese40bad82015-05-28 14:21:36 -07001075 boolean shouldReEnrollBaseZero = storedHash != null && storedHash.isBaseZeroPattern;
1076
1077 String patternToVerify;
1078 if (shouldReEnrollBaseZero) {
1079 patternToVerify = LockPatternUtils.patternStringToBaseZero(pattern);
1080 } else {
1081 patternToVerify = pattern;
1082 }
1083
1084 VerifyCredentialResponse response = verifyCredential(userId, storedHash, patternToVerify,
1085 hasChallenge, challenge,
Andres Morales23974272015-05-14 22:42:26 -07001086 new CredentialUtil() {
1087 @Override
1088 public void setCredential(String pattern, String oldPattern, int userId)
1089 throws RemoteException {
Ricky Waidc283a82016-03-24 19:55:08 +00001090 setLockPatternInternal(pattern, oldPattern, userId);
Andres Morales23974272015-05-14 22:42:26 -07001091 }
Andres Moralesd9fc85a2015-04-09 19:14:42 -07001092
Andres Morales23974272015-05-14 22:42:26 -07001093 @Override
1094 public byte[] toHash(String pattern, int userId) {
Andres Moralese40bad82015-05-28 14:21:36 -07001095 return LockPatternUtils.patternToHash(
1096 LockPatternUtils.stringToPattern(pattern));
Andres Morales23974272015-05-14 22:42:26 -07001097 }
Andres Morales59ef1262015-06-26 13:56:39 -07001098
1099 @Override
1100 public String adjustForKeystore(String pattern) {
1101 return LockPatternUtils.patternStringToBaseZero(pattern);
1102 }
Andres Morales23974272015-05-14 22:42:26 -07001103 }
1104 );
Andres Moralese40bad82015-05-28 14:21:36 -07001105
1106 if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK
1107 && shouldReEnrollBaseZero) {
Ricky Waidc283a82016-03-24 19:55:08 +00001108 setLockPatternInternal(pattern, patternToVerify, userId);
Andres Moralese40bad82015-05-28 14:21:36 -07001109 }
1110
1111 return response;
Paul Crowleya1eb7502016-05-10 20:36:56 +00001112
Amith Yamasani52c489c2012-03-28 11:42:42 -07001113 }
1114
1115 @Override
Andres Morales23974272015-05-14 22:42:26 -07001116 public VerifyCredentialResponse checkPassword(String password, int userId)
Andres Moralesd9fc85a2015-04-09 19:14:42 -07001117 throws RemoteException {
Andres Morales23974272015-05-14 22:42:26 -07001118 return doVerifyPassword(password, false, 0, userId);
Andres Moralesd9fc85a2015-04-09 19:14:42 -07001119 }
1120
Andres Morales23974272015-05-14 22:42:26 -07001121 @Override
1122 public VerifyCredentialResponse verifyPassword(String password, long challenge, int userId)
1123 throws RemoteException {
1124 return doVerifyPassword(password, true, challenge, userId);
1125 }
1126
Ricky Wai53940d42016-04-05 15:29:24 +01001127 @Override
1128 public VerifyCredentialResponse verifyTiedProfileChallenge(String password, boolean isPattern,
1129 long challenge, int userId) throws RemoteException {
1130 checkPasswordReadPermission(userId);
1131 if (!isManagedProfileWithUnifiedLock(userId)) {
1132 throw new RemoteException("User id must be managed profile with unified lock");
1133 }
1134 final int parentProfileId = mUserManager.getProfileParent(userId).id;
1135 // Unlock parent by using parent's challenge
1136 final VerifyCredentialResponse parentResponse = isPattern
1137 ? doVerifyPattern(password, true, challenge, parentProfileId)
1138 : doVerifyPassword(password, true, challenge, parentProfileId);
1139 if (parentResponse.getResponseCode() != VerifyCredentialResponse.RESPONSE_OK) {
1140 // Failed, just return parent's response
1141 return parentResponse;
1142 }
1143
1144 try {
1145 // Unlock work profile, and work profile with unified lock must use password only
1146 return doVerifyPassword(getDecryptedPasswordForTiedProfile(userId), true,
1147 challenge,
1148 userId);
1149 } catch (UnrecoverableKeyException | InvalidKeyException | KeyStoreException
1150 | NoSuchAlgorithmException | NoSuchPaddingException
1151 | InvalidAlgorithmParameterException | IllegalBlockSizeException
1152 | BadPaddingException | CertificateException | IOException e) {
1153 Slog.e(TAG, "Failed to decrypt child profile key", e);
1154 throw new RemoteException("Unable to get tied profile token");
1155 }
1156 }
1157
Andres Morales23974272015-05-14 22:42:26 -07001158 private VerifyCredentialResponse doVerifyPassword(String password, boolean hasChallenge,
1159 long challenge, int userId) throws RemoteException {
Andres Moralesd9fc85a2015-04-09 19:14:42 -07001160 checkPasswordReadPermission(userId);
Andres Morales8fa56652015-03-31 09:19:50 -07001161 CredentialHash storedHash = mStorage.readPasswordHash(userId);
Andres Morales23974272015-05-14 22:42:26 -07001162 return verifyCredential(userId, storedHash, password, hasChallenge, challenge,
1163 new CredentialUtil() {
1164 @Override
1165 public void setCredential(String password, String oldPassword, int userId)
1166 throws RemoteException {
Ricky Waidc283a82016-03-24 19:55:08 +00001167 setLockPasswordInternal(password, oldPassword, userId);
Andres Morales23974272015-05-14 22:42:26 -07001168 }
Adrian Roos261d5ab2014-10-29 14:42:38 +01001169
Andres Morales23974272015-05-14 22:42:26 -07001170 @Override
1171 public byte[] toHash(String password, int userId) {
1172 return mLockPatternUtils.passwordToHash(password, userId);
1173 }
Andres Morales59ef1262015-06-26 13:56:39 -07001174
1175 @Override
1176 public String adjustForKeystore(String password) {
1177 return password;
1178 }
Andres Morales23974272015-05-14 22:42:26 -07001179 }
1180 );
1181 }
1182
1183 private VerifyCredentialResponse verifyCredential(int userId, CredentialHash storedHash,
1184 String credential, boolean hasChallenge, long challenge, CredentialUtil credentialUtil)
1185 throws RemoteException {
1186 if ((storedHash == null || storedHash.hash.length == 0) && TextUtils.isEmpty(credential)) {
1187 // don't need to pass empty credentials to GateKeeper
1188 return VerifyCredentialResponse.OK;
Andres Moralesd9fc85a2015-04-09 19:14:42 -07001189 }
1190
Andres Morales23974272015-05-14 22:42:26 -07001191 if (TextUtils.isEmpty(credential)) {
1192 return VerifyCredentialResponse.ERROR;
Amith Yamasani52c489c2012-03-28 11:42:42 -07001193 }
Adrian Roos261d5ab2014-10-29 14:42:38 +01001194
Andres Morales8fa56652015-03-31 09:19:50 -07001195 if (storedHash.version == CredentialHash.VERSION_LEGACY) {
Andres Morales23974272015-05-14 22:42:26 -07001196 byte[] hash = credentialUtil.toHash(credential, userId);
Andres Moralesd9fc85a2015-04-09 19:14:42 -07001197 if (Arrays.equals(hash, storedHash.hash)) {
Andres Morales59ef1262015-06-26 13:56:39 -07001198 unlockKeystore(credentialUtil.adjustForKeystore(credential), userId);
Jeff Sharkeyb9fe5372015-12-03 15:23:08 -07001199
Paul Crowleyfaeb3eb2016-02-08 15:58:29 +00001200 // Users with legacy credentials don't have credential-backed
1201 // FBE keys, so just pass through a fake token/secret
1202 Slog.i(TAG, "Unlocking user with fake token: " + userId);
1203 final byte[] fakeToken = String.valueOf(userId).getBytes();
1204 unlockUser(userId, fakeToken, fakeToken);
Jeff Sharkeyb9fe5372015-12-03 15:23:08 -07001205
Andres Morales23974272015-05-14 22:42:26 -07001206 // migrate credential to GateKeeper
1207 credentialUtil.setCredential(credential, null, userId);
Andres Moralesd9fc85a2015-04-09 19:14:42 -07001208 if (!hasChallenge) {
Andres Morales23974272015-05-14 22:42:26 -07001209 return VerifyCredentialResponse.OK;
Andres Moralesd9fc85a2015-04-09 19:14:42 -07001210 }
1211 // Fall through to get the auth token. Technically this should never happen,
Andres Morales23974272015-05-14 22:42:26 -07001212 // as a user that had a legacy credential would have to unlock their device
Andres Moralesd9fc85a2015-04-09 19:14:42 -07001213 // before getting to a flow with a challenge, but supporting for consistency.
1214 } else {
Andres Morales23974272015-05-14 22:42:26 -07001215 return VerifyCredentialResponse.ERROR;
Andres Morales8fa56652015-03-31 09:19:50 -07001216 }
Andres Morales8fa56652015-03-31 09:19:50 -07001217 }
1218
Andres Morales23974272015-05-14 22:42:26 -07001219 VerifyCredentialResponse response;
Paul Crowley98e0a262016-02-04 09:41:53 +00001220 boolean shouldReEnroll = false;
1221 GateKeeperResponse gateKeeperResponse = getGateKeeperService()
1222 .verifyChallenge(userId, challenge, storedHash.hash, credential.getBytes());
1223 int responseCode = gateKeeperResponse.getResponseCode();
1224 if (responseCode == GateKeeperResponse.RESPONSE_RETRY) {
1225 response = new VerifyCredentialResponse(gateKeeperResponse.getTimeout());
1226 } else if (responseCode == GateKeeperResponse.RESPONSE_OK) {
1227 byte[] token = gateKeeperResponse.getPayload();
1228 if (token == null) {
1229 // something's wrong if there's no payload with a challenge
1230 Slog.e(TAG, "verifyChallenge response had no associated payload");
Andres Morales23974272015-05-14 22:42:26 -07001231 response = VerifyCredentialResponse.ERROR;
Paul Crowley98e0a262016-02-04 09:41:53 +00001232 } else {
1233 shouldReEnroll = gateKeeperResponse.getShouldReEnroll();
1234 response = new VerifyCredentialResponse(token);
Andres Moralesd9fc85a2015-04-09 19:14:42 -07001235 }
Andres Morales23974272015-05-14 22:42:26 -07001236 } else {
Paul Crowley98e0a262016-02-04 09:41:53 +00001237 response = VerifyCredentialResponse.ERROR;
Adrian Roos261d5ab2014-10-29 14:42:38 +01001238 }
Andres Morales8fa56652015-03-31 09:19:50 -07001239
Andres Morales23974272015-05-14 22:42:26 -07001240 if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK) {
1241 // credential has matched
1242 unlockKeystore(credential, userId);
Jeff Sharkeyb9fe5372015-12-03 15:23:08 -07001243
Paul Crowleyfaeb3eb2016-02-08 15:58:29 +00001244 Slog.i(TAG, "Unlocking user " + userId +
1245 " with token length " + response.getPayload().length);
1246 unlockUser(userId, response.getPayload(), secretFromCredential(credential));
Jeff Sharkeyb9fe5372015-12-03 15:23:08 -07001247
Ricky Waidc283a82016-03-24 19:55:08 +00001248 if (isManagedProfileWithSeparatedLock(userId)) {
Clara Bayarri56878a92015-10-29 15:43:55 +00001249 TrustManager trustManager =
1250 (TrustManager) mContext.getSystemService(Context.TRUST_SERVICE);
1251 trustManager.setDeviceLockedForUser(userId, false);
1252 }
Andres Morales23974272015-05-14 22:42:26 -07001253 if (shouldReEnroll) {
1254 credentialUtil.setCredential(credential, credential, userId);
1255 }
Adrian Roos873010d2015-08-25 15:59:00 -07001256 } else if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_RETRY) {
1257 if (response.getTimeout() > 0) {
1258 requireStrongAuth(STRONG_AUTH_REQUIRED_AFTER_LOCKOUT, userId);
1259 }
Andres Morales23974272015-05-14 22:42:26 -07001260 }
Amith Yamasani52c489c2012-03-28 11:42:42 -07001261
Andres Morales23974272015-05-14 22:42:26 -07001262 return response;
1263 }
Andres Moralesd9fc85a2015-04-09 19:14:42 -07001264
Amith Yamasani52c489c2012-03-28 11:42:42 -07001265 @Override
Adrian Roos261d5ab2014-10-29 14:42:38 +01001266 public boolean checkVoldPassword(int userId) throws RemoteException {
Paul Lawrence945490c2014-03-27 16:37:28 +00001267 if (!mFirstCallToVold) {
1268 return false;
1269 }
1270 mFirstCallToVold = false;
1271
1272 checkPasswordReadPermission(userId);
1273
1274 // There's no guarantee that this will safely connect, but if it fails
1275 // we will simply show the lock screen when we shouldn't, so relatively
1276 // benign. There is an outside chance something nasty would happen if
1277 // this service restarted before vold stales out the password in this
1278 // case. The nastiness is limited to not showing the lock screen when
1279 // we should, within the first minute of decrypting the phone if this
1280 // service can't connect to vold, it restarts, and then the new instance
1281 // does successfully connect.
1282 final IMountService service = getMountService();
Paul Lawrence0bbd1082016-04-26 15:21:02 -07001283 String password;
1284 long identity = Binder.clearCallingIdentity();
1285 try {
1286 password = service.getPassword();
1287 service.clearPassword();
1288 } finally {
1289 Binder.restoreCallingIdentity(identity);
1290 }
Paul Lawrence945490c2014-03-27 16:37:28 +00001291 if (password == null) {
1292 return false;
1293 }
1294
1295 try {
Adrian Roos9dd16eb2015-01-08 16:20:49 +01001296 if (mLockPatternUtils.isLockPatternEnabled(userId)) {
Andres Morales23974272015-05-14 22:42:26 -07001297 if (checkPattern(password, userId).getResponseCode()
1298 == GateKeeperResponse.RESPONSE_OK) {
Paul Lawrence945490c2014-03-27 16:37:28 +00001299 return true;
1300 }
1301 }
1302 } catch (Exception e) {
1303 }
1304
1305 try {
Adrian Roos9dd16eb2015-01-08 16:20:49 +01001306 if (mLockPatternUtils.isLockPasswordEnabled(userId)) {
Andres Morales23974272015-05-14 22:42:26 -07001307 if (checkPassword(password, userId).getResponseCode()
1308 == GateKeeperResponse.RESPONSE_OK) {
Paul Lawrence945490c2014-03-27 16:37:28 +00001309 return true;
1310 }
1311 }
1312 } catch (Exception e) {
1313 }
1314
1315 return false;
1316 }
1317
Adrian Roosdb0f76e2015-01-07 22:19:38 +01001318 private void removeUser(int userId) {
Adrian Roos261d5ab2014-10-29 14:42:38 +01001319 mStorage.removeUser(userId);
Adrian Roosb5e47222015-08-14 15:53:06 -07001320 mStrongAuth.removeUser(userId);
Robin Lee49d810c2014-09-23 13:50:22 +01001321
1322 final KeyStore ks = KeyStore.getInstance();
Chad Brubaker83ce0952015-05-12 13:00:02 -07001323 ks.onUserRemoved(userId);
Andres Morales070fe632015-06-24 10:37:10 -07001324
1325 try {
1326 final IGateKeeperService gk = getGateKeeperService();
1327 if (gk != null) {
1328 gk.clearSecureUserId(userId);
1329 }
1330 } catch (RemoteException ex) {
1331 Slog.w(TAG, "unable to clear GK secure user id");
1332 }
Ricky Waidc283a82016-03-24 19:55:08 +00001333 if (mUserManager.getUserInfo(userId).isManagedProfile()) {
1334 removeKeystoreProfileKey(userId);
1335 }
1336 }
1337
1338 private void removeKeystoreProfileKey(int targetUserId) {
1339 if (DEBUG) Slog.v(TAG, "Remove keystore profile key for user: " + targetUserId);
1340 try {
1341 java.security.KeyStore keyStore = java.security.KeyStore.getInstance("AndroidKeyStore");
1342 keyStore.load(null);
1343 keyStore.deleteEntry(PROFILE_KEY_NAME_ENCRYPT + targetUserId);
1344 keyStore.deleteEntry(PROFILE_KEY_NAME_DECRYPT + targetUserId);
1345 } catch (KeyStoreException | NoSuchAlgorithmException | CertificateException
1346 | IOException e) {
1347 // We have tried our best to remove all keys
1348 Slog.e(TAG, "Unable to remove keystore profile key for user:" + targetUserId, e);
1349 }
Amith Yamasani52c489c2012-03-28 11:42:42 -07001350 }
1351
Adrian Roosb5e47222015-08-14 15:53:06 -07001352 @Override
1353 public void registerStrongAuthTracker(IStrongAuthTracker tracker) {
1354 checkPasswordReadPermission(UserHandle.USER_ALL);
1355 mStrongAuth.registerStrongAuthTracker(tracker);
1356 }
1357
1358 @Override
1359 public void unregisterStrongAuthTracker(IStrongAuthTracker tracker) {
1360 checkPasswordReadPermission(UserHandle.USER_ALL);
1361 mStrongAuth.unregisterStrongAuthTracker(tracker);
1362 }
1363
1364 @Override
1365 public void requireStrongAuth(int strongAuthReason, int userId) {
1366 checkWritePermission(userId);
1367 mStrongAuth.requireStrongAuth(strongAuthReason, userId);
1368 }
1369
Adrian Roos4ab7e592016-04-13 15:38:13 -07001370 @Override
1371 public void userPresent(int userId) {
1372 checkWritePermission(userId);
1373 mStrongAuth.reportUnlock(userId);
1374 }
1375
Amith Yamasani52c489c2012-03-28 11:42:42 -07001376 private static final String[] VALID_SETTINGS = new String[] {
1377 LockPatternUtils.LOCKOUT_PERMANENT_KEY,
1378 LockPatternUtils.LOCKOUT_ATTEMPT_DEADLINE,
1379 LockPatternUtils.PATTERN_EVER_CHOSEN_KEY,
1380 LockPatternUtils.PASSWORD_TYPE_KEY,
1381 LockPatternUtils.PASSWORD_TYPE_ALTERNATE_KEY,
1382 LockPatternUtils.LOCK_PASSWORD_SALT_KEY,
1383 LockPatternUtils.DISABLE_LOCKSCREEN_KEY,
1384 LockPatternUtils.LOCKSCREEN_OPTIONS,
1385 LockPatternUtils.LOCKSCREEN_BIOMETRIC_WEAK_FALLBACK,
1386 LockPatternUtils.BIOMETRIC_WEAK_EVER_CHOSEN_KEY,
1387 LockPatternUtils.LOCKSCREEN_POWER_BUTTON_INSTANTLY_LOCKS,
1388 LockPatternUtils.PASSWORD_HISTORY_KEY,
1389 Secure.LOCK_PATTERN_ENABLED,
1390 Secure.LOCK_BIOMETRIC_WEAK_FLAGS,
1391 Secure.LOCK_PATTERN_VISIBLE,
1392 Secure.LOCK_PATTERN_TACTILE_FEEDBACK_ENABLED
Jim Miller187ec582013-04-15 18:27:54 -07001393 };
1394
Svetoslav Ganov6d2c0e52015-06-23 16:33:36 +00001395 // Reading these settings needs the contacts permission
1396 private static final String[] READ_CONTACTS_PROTECTED_SETTINGS = new String[] {
Jim Miller187ec582013-04-15 18:27:54 -07001397 Secure.LOCK_SCREEN_OWNER_INFO_ENABLED,
1398 Secure.LOCK_SCREEN_OWNER_INFO
1399 };
Paul Lawrence945490c2014-03-27 16:37:28 +00001400
Adrian Roos001b00d2015-02-24 17:08:48 +01001401 // Reading these settings needs the same permission as checking the password
1402 private static final String[] READ_PASSWORD_PROTECTED_SETTINGS = new String[] {
1403 LockPatternUtils.LOCK_PASSWORD_SALT_KEY,
1404 LockPatternUtils.PASSWORD_HISTORY_KEY,
Adrian Roos855fa302015-04-02 16:01:12 +02001405 LockPatternUtils.PASSWORD_TYPE_KEY,
Adrian Roos001b00d2015-02-24 17:08:48 +01001406 };
1407
Amith Yamasani072543f2015-02-13 11:09:45 -08001408 private static final String[] SETTINGS_TO_BACKUP = new String[] {
1409 Secure.LOCK_SCREEN_OWNER_INFO_ENABLED,
1410 Secure.LOCK_SCREEN_OWNER_INFO
1411 };
1412
Paul Lawrence945490c2014-03-27 16:37:28 +00001413 private IMountService getMountService() {
1414 final IBinder service = ServiceManager.getService("mount");
1415 if (service != null) {
1416 return IMountService.Stub.asInterface(service);
1417 }
1418 return null;
1419 }
Andres Morales8fa56652015-03-31 09:19:50 -07001420
Andres Morales301ea442015-04-17 09:15:47 -07001421 private class GateKeeperDiedRecipient implements IBinder.DeathRecipient {
1422 @Override
1423 public void binderDied() {
1424 mGateKeeperService.asBinder().unlinkToDeath(this, 0);
1425 mGateKeeperService = null;
1426 }
1427 }
1428
1429 private synchronized IGateKeeperService getGateKeeperService()
1430 throws RemoteException {
Andres Morales8fa56652015-03-31 09:19:50 -07001431 if (mGateKeeperService != null) {
1432 return mGateKeeperService;
1433 }
1434
1435 final IBinder service =
1436 ServiceManager.getService("android.service.gatekeeper.IGateKeeperService");
1437 if (service != null) {
Andres Morales301ea442015-04-17 09:15:47 -07001438 service.linkToDeath(new GateKeeperDiedRecipient(), 0);
Andres Morales8fa56652015-03-31 09:19:50 -07001439 mGateKeeperService = IGateKeeperService.Stub.asInterface(service);
1440 return mGateKeeperService;
1441 }
1442
1443 Slog.e(TAG, "Unable to acquire GateKeeperService");
1444 return null;
1445 }
Amith Yamasani52c489c2012-03-28 11:42:42 -07001446}