blob: a31a1a7003f8ea7badde9f3361994954fd7ed53f [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
Adrian Roos230635e2015-01-07 20:50:29 +010019import android.app.admin.DevicePolicyManager;
Amith Yamasani072543f2015-02-13 11:09:45 -080020import android.app.backup.BackupManager;
Robin Leef0246a82014-08-13 09:50:25 +010021import android.content.BroadcastReceiver;
Amith Yamasani52c489c2012-03-28 11:42:42 -070022import android.content.ContentResolver;
Amith Yamasani52c489c2012-03-28 11:42:42 -070023import android.content.Context;
Robin Leef0246a82014-08-13 09:50:25 +010024import android.content.Intent;
25import android.content.IntentFilter;
Jim Miller158fe192013-04-17 15:23:55 -070026import android.content.pm.PackageManager;
Jim Miller187ec582013-04-15 18:27:54 -070027import android.content.pm.UserInfo;
28
Adrian Roos261d5ab2014-10-29 14:42:38 +010029import static android.Manifest.permission.ACCESS_KEYGUARD_SECURE_STORAGE;
Jim Miller187ec582013-04-15 18:27:54 -070030import static android.content.Context.USER_SERVICE;
Jim Miller158fe192013-04-17 15:23:55 -070031import static android.Manifest.permission.READ_PROFILE;
Adrian Roos261d5ab2014-10-29 14:42:38 +010032
Amith Yamasani52c489c2012-03-28 11:42:42 -070033import android.database.sqlite.SQLiteDatabase;
Amith Yamasani52c489c2012-03-28 11:42:42 -070034import android.os.Binder;
Paul Lawrence945490c2014-03-27 16:37:28 +000035import android.os.IBinder;
Robin Leef0246a82014-08-13 09:50:25 +010036import android.os.Process;
Amith Yamasani52c489c2012-03-28 11:42:42 -070037import android.os.RemoteException;
Paul Lawrence945490c2014-03-27 16:37:28 +000038import android.os.storage.IMountService;
39import android.os.ServiceManager;
Amith Yamasanid1645f82012-06-12 11:53:26 -070040import android.os.SystemProperties;
Dianne Hackbornf02b60a2012-08-16 10:48:27 -070041import android.os.UserHandle;
Jim Miller187ec582013-04-15 18:27:54 -070042import android.os.UserManager;
Amith Yamasani52c489c2012-03-28 11:42:42 -070043import android.provider.Settings;
44import android.provider.Settings.Secure;
Jim Miller187ec582013-04-15 18:27:54 -070045import android.provider.Settings.SettingNotFoundException;
Jim Millerde1af082013-09-11 14:58:26 -070046import android.security.KeyStore;
Andres Morales8fa56652015-03-31 09:19:50 -070047import android.service.gatekeeper.IGateKeeperService;
Amith Yamasani52c489c2012-03-28 11:42:42 -070048import android.text.TextUtils;
49import android.util.Slog;
50
Amith Yamasani072543f2015-02-13 11:09:45 -080051import com.android.internal.util.ArrayUtils;
Jeff Sharkey7a96c392012-11-15 14:01:46 -080052import com.android.internal.widget.ILockSettings;
53import com.android.internal.widget.LockPatternUtils;
Andres Morales8fa56652015-03-31 09:19:50 -070054import com.android.server.LockSettingsStorage.CredentialHash;
Jeff Sharkey7a96c392012-11-15 14:01:46 -080055
Amith Yamasani52c489c2012-03-28 11:42:42 -070056import java.util.Arrays;
Jim Miller187ec582013-04-15 18:27:54 -070057import java.util.List;
Amith Yamasani52c489c2012-03-28 11:42:42 -070058
59/**
60 * Keeps the lock pattern/password data and related settings for each user.
61 * Used by LockPatternUtils. Needs to be a service because Settings app also needs
62 * to be able to save lockscreen information for secondary users.
63 * @hide
64 */
65public class LockSettingsService extends ILockSettings.Stub {
66
Adrian Roos261d5ab2014-10-29 14:42:38 +010067 private static final String PERMISSION = ACCESS_KEYGUARD_SECURE_STORAGE;
Adrian Roos4f788452014-05-22 20:45:59 +020068
Amith Yamasani52c489c2012-03-28 11:42:42 -070069 private static final String TAG = "LockSettingsService";
70
Amith Yamasani52c489c2012-03-28 11:42:42 -070071 private final Context mContext;
Adrian Roos261d5ab2014-10-29 14:42:38 +010072
73 private final LockSettingsStorage mStorage;
74
Jim Millerde1af082013-09-11 14:58:26 -070075 private LockPatternUtils mLockPatternUtils;
Paul Lawrence945490c2014-03-27 16:37:28 +000076 private boolean mFirstCallToVold;
Andres Morales8fa56652015-03-31 09:19:50 -070077 private IGateKeeperService mGateKeeperService;
Amith Yamasani52c489c2012-03-28 11:42:42 -070078
79 public LockSettingsService(Context context) {
80 mContext = context;
81 // Open the database
Jim Millerde1af082013-09-11 14:58:26 -070082
83 mLockPatternUtils = new LockPatternUtils(context);
Paul Lawrence945490c2014-03-27 16:37:28 +000084 mFirstCallToVold = true;
Robin Leef0246a82014-08-13 09:50:25 +010085
86 IntentFilter filter = new IntentFilter();
87 filter.addAction(Intent.ACTION_USER_ADDED);
Adrian Roos3dcae682014-10-29 14:43:56 +010088 filter.addAction(Intent.ACTION_USER_STARTING);
Adrian Roosdb0f76e2015-01-07 22:19:38 +010089 filter.addAction(Intent.ACTION_USER_REMOVED);
Robin Leef0246a82014-08-13 09:50:25 +010090 mContext.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, filter, null, null);
Adrian Roos261d5ab2014-10-29 14:42:38 +010091
92 mStorage = new LockSettingsStorage(context, new LockSettingsStorage.Callback() {
93 @Override
94 public void initialize(SQLiteDatabase db) {
95 // Get the lockscreen default from a system property, if available
96 boolean lockScreenDisable = SystemProperties.getBoolean(
97 "ro.lockscreen.disable.default", false);
98 if (lockScreenDisable) {
99 mStorage.writeKeyValue(db, LockPatternUtils.DISABLE_LOCKSCREEN_KEY, "1", 0);
100 }
101 }
102 });
Amith Yamasani52c489c2012-03-28 11:42:42 -0700103 }
104
Robin Leef0246a82014-08-13 09:50:25 +0100105 private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
106 @Override
107 public void onReceive(Context context, Intent intent) {
Robin Lee1096cf82014-09-01 16:52:47 +0100108 if (Intent.ACTION_USER_ADDED.equals(intent.getAction())) {
Robin Leef0246a82014-08-13 09:50:25 +0100109 final int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
Robin Lee49d810c2014-09-23 13:50:22 +0100110 final int userSysUid = UserHandle.getUid(userHandle, Process.SYSTEM_UID);
111 final KeyStore ks = KeyStore.getInstance();
112
113 // Clear up keystore in case anything was left behind by previous users
114 ks.resetUid(userSysUid);
115
116 // If this user has a parent, sync with its keystore password
Robin Leef0246a82014-08-13 09:50:25 +0100117 final UserManager um = (UserManager) mContext.getSystemService(USER_SERVICE);
118 final UserInfo parentInfo = um.getProfileParent(userHandle);
119 if (parentInfo != null) {
Robin Lee49d810c2014-09-23 13:50:22 +0100120 final int parentSysUid = UserHandle.getUid(parentInfo.id, Process.SYSTEM_UID);
121 ks.syncUid(parentSysUid, userSysUid);
Robin Leef0246a82014-08-13 09:50:25 +0100122 }
Adrian Roos3dcae682014-10-29 14:43:56 +0100123 } else if (Intent.ACTION_USER_STARTING.equals(intent.getAction())) {
124 final int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
125 mStorage.prefetchUser(userHandle);
Adrian Roosdb0f76e2015-01-07 22:19:38 +0100126 } else if (Intent.ACTION_USER_REMOVED.equals(intent.getAction())) {
127 final int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
128 if (userHandle > 0) {
129 removeUser(userHandle);
130 }
Robin Leef0246a82014-08-13 09:50:25 +0100131 }
132 }
133 };
134
Amith Yamasani52c489c2012-03-28 11:42:42 -0700135 public void systemReady() {
136 migrateOldData();
Andres Morales301ea442015-04-17 09:15:47 -0700137 try {
138 getGateKeeperService();
139 } catch (RemoteException e) {
140 Slog.e(TAG, "Failure retrieving IGateKeeperService", e);
141 }
Adrian Roos3dcae682014-10-29 14:43:56 +0100142 mStorage.prefetchUser(UserHandle.USER_OWNER);
Amith Yamasani52c489c2012-03-28 11:42:42 -0700143 }
144
145 private void migrateOldData() {
146 try {
Jim Miller187ec582013-04-15 18:27:54 -0700147 // These Settings moved before multi-user was enabled, so we only have to do it for the
148 // root user.
149 if (getString("migrated", null, 0) == null) {
150 final ContentResolver cr = mContext.getContentResolver();
151 for (String validSetting : VALID_SETTINGS) {
152 String value = Settings.Secure.getString(cr, validSetting);
153 if (value != null) {
154 setString(validSetting, value, 0);
155 }
156 }
157 // No need to move the password / pattern files. They're already in the right place.
158 setString("migrated", "true", 0);
159 Slog.i(TAG, "Migrated lock settings to new location");
Amith Yamasani52c489c2012-03-28 11:42:42 -0700160 }
161
Jim Miller187ec582013-04-15 18:27:54 -0700162 // These Settings changed after multi-user was enabled, hence need to be moved per user.
163 if (getString("migrated_user_specific", null, 0) == null) {
164 final UserManager um = (UserManager) mContext.getSystemService(USER_SERVICE);
165 final ContentResolver cr = mContext.getContentResolver();
166 List<UserInfo> users = um.getUsers();
167 for (int user = 0; user < users.size(); user++) {
Jim Miller2d8ecf9d2013-04-22 17:17:03 -0700168 // Migrate owner info
169 final int userId = users.get(user).id;
170 final String OWNER_INFO = Secure.LOCK_SCREEN_OWNER_INFO;
171 String ownerInfo = Settings.Secure.getStringForUser(cr, OWNER_INFO, userId);
172 if (ownerInfo != null) {
173 setString(OWNER_INFO, ownerInfo, userId);
174 Settings.Secure.putStringForUser(cr, ownerInfo, "", userId);
175 }
Jim Miller187ec582013-04-15 18:27:54 -0700176
Jim Miller2d8ecf9d2013-04-22 17:17:03 -0700177 // Migrate owner info enabled. Note there was a bug where older platforms only
178 // stored this value if the checkbox was toggled at least once. The code detects
179 // this case by handling the exception.
180 final String OWNER_INFO_ENABLED = Secure.LOCK_SCREEN_OWNER_INFO_ENABLED;
181 boolean enabled;
182 try {
183 int ivalue = Settings.Secure.getIntForUser(cr, OWNER_INFO_ENABLED, userId);
184 enabled = ivalue != 0;
185 setLong(OWNER_INFO_ENABLED, enabled ? 1 : 0, userId);
186 } catch (SettingNotFoundException e) {
187 // Setting was never stored. Store it if the string is not empty.
188 if (!TextUtils.isEmpty(ownerInfo)) {
189 setLong(OWNER_INFO_ENABLED, 1, userId);
Jim Miller187ec582013-04-15 18:27:54 -0700190 }
191 }
Jim Miller2d8ecf9d2013-04-22 17:17:03 -0700192 Settings.Secure.putIntForUser(cr, OWNER_INFO_ENABLED, 0, userId);
Amith Yamasani52c489c2012-03-28 11:42:42 -0700193 }
Jim Miller187ec582013-04-15 18:27:54 -0700194 // No need to move the password / pattern files. They're already in the right place.
195 setString("migrated_user_specific", "true", 0);
196 Slog.i(TAG, "Migrated per-user lock settings to new location");
Amith Yamasani52c489c2012-03-28 11:42:42 -0700197 }
Adrian Roos230635e2015-01-07 20:50:29 +0100198
199 // Migrates biometric weak such that the fallback mechanism becomes the primary.
200 if (getString("migrated_biometric_weak", null, 0) == null) {
201 final UserManager um = (UserManager) mContext.getSystemService(USER_SERVICE);
202 List<UserInfo> users = um.getUsers();
203 for (int i = 0; i < users.size(); i++) {
204 int userId = users.get(i).id;
205 long type = getLong(LockPatternUtils.PASSWORD_TYPE_KEY,
206 DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED,
207 userId);
208 long alternateType = getLong(LockPatternUtils.PASSWORD_TYPE_ALTERNATE_KEY,
209 DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED,
210 userId);
211 if (type == DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK) {
212 setLong(LockPatternUtils.PASSWORD_TYPE_KEY,
213 alternateType,
214 userId);
215 }
216 setLong(LockPatternUtils.PASSWORD_TYPE_ALTERNATE_KEY,
217 DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED,
218 userId);
219 }
220 setString("migrated_biometric_weak", "true", 0);
221 Slog.i(TAG, "Migrated biometric weak to use the fallback instead");
222 }
Amith Yamasani52c489c2012-03-28 11:42:42 -0700223 } catch (RemoteException re) {
Jim Miller187ec582013-04-15 18:27:54 -0700224 Slog.e(TAG, "Unable to migrate old data", re);
Amith Yamasani52c489c2012-03-28 11:42:42 -0700225 }
226 }
227
Jim Miller5ecd8112013-01-09 18:50:26 -0800228 private final void checkWritePermission(int userId) {
Jim Miller505329b2013-11-08 13:25:36 -0800229 mContext.enforceCallingOrSelfPermission(PERMISSION, "LockSettingsWrite");
Amith Yamasani52c489c2012-03-28 11:42:42 -0700230 }
231
Jim Miller5ecd8112013-01-09 18:50:26 -0800232 private final void checkPasswordReadPermission(int userId) {
Jim Miller505329b2013-11-08 13:25:36 -0800233 mContext.enforceCallingOrSelfPermission(PERMISSION, "LockSettingsRead");
Amith Yamasani52c489c2012-03-28 11:42:42 -0700234 }
235
Jim Miller158fe192013-04-17 15:23:55 -0700236 private final void checkReadPermission(String requestedKey, int userId) {
Amith Yamasani52c489c2012-03-28 11:42:42 -0700237 final int callingUid = Binder.getCallingUid();
Adrian Roos001b00d2015-02-24 17:08:48 +0100238
Jim Miller158fe192013-04-17 15:23:55 -0700239 for (int i = 0; i < READ_PROFILE_PROTECTED_SETTINGS.length; i++) {
240 String key = READ_PROFILE_PROTECTED_SETTINGS[i];
241 if (key.equals(requestedKey) && mContext.checkCallingOrSelfPermission(READ_PROFILE)
242 != PackageManager.PERMISSION_GRANTED) {
243 throw new SecurityException("uid=" + callingUid
244 + " needs permission " + READ_PROFILE + " to read "
245 + requestedKey + " for user " + userId);
246 }
Amith Yamasani52c489c2012-03-28 11:42:42 -0700247 }
Adrian Roos001b00d2015-02-24 17:08:48 +0100248
249 for (int i = 0; i < READ_PASSWORD_PROTECTED_SETTINGS.length; i++) {
250 String key = READ_PASSWORD_PROTECTED_SETTINGS[i];
251 if (key.equals(requestedKey) && mContext.checkCallingOrSelfPermission(PERMISSION)
252 != PackageManager.PERMISSION_GRANTED) {
253 throw new SecurityException("uid=" + callingUid
254 + " needs permission " + PERMISSION + " to read "
255 + requestedKey + " for user " + userId);
256 }
257 }
Amith Yamasani52c489c2012-03-28 11:42:42 -0700258 }
259
260 @Override
261 public void setBoolean(String key, boolean value, int userId) throws RemoteException {
262 checkWritePermission(userId);
Adrian Roos261d5ab2014-10-29 14:42:38 +0100263 setStringUnchecked(key, userId, value ? "1" : "0");
Amith Yamasani52c489c2012-03-28 11:42:42 -0700264 }
265
266 @Override
267 public void setLong(String key, long value, int userId) throws RemoteException {
268 checkWritePermission(userId);
Adrian Roos261d5ab2014-10-29 14:42:38 +0100269 setStringUnchecked(key, userId, Long.toString(value));
Amith Yamasani52c489c2012-03-28 11:42:42 -0700270 }
271
272 @Override
273 public void setString(String key, String value, int userId) throws RemoteException {
274 checkWritePermission(userId);
Adrian Roos261d5ab2014-10-29 14:42:38 +0100275 setStringUnchecked(key, userId, value);
276 }
Amith Yamasani52c489c2012-03-28 11:42:42 -0700277
Adrian Roos261d5ab2014-10-29 14:42:38 +0100278 private void setStringUnchecked(String key, int userId, String value) {
279 mStorage.writeKeyValue(key, value, userId);
Amith Yamasani072543f2015-02-13 11:09:45 -0800280 if (ArrayUtils.contains(SETTINGS_TO_BACKUP, key)) {
281 BackupManager.dataChanged("com.android.providers.settings");
282 }
Amith Yamasani52c489c2012-03-28 11:42:42 -0700283 }
284
285 @Override
286 public boolean getBoolean(String key, boolean defaultValue, int userId) throws RemoteException {
Jim Miller158fe192013-04-17 15:23:55 -0700287 checkReadPermission(key, userId);
Adrian Roos9dd16eb2015-01-08 16:20:49 +0100288 String value = getStringUnchecked(key, null, userId);
Amith Yamasani52c489c2012-03-28 11:42:42 -0700289 return TextUtils.isEmpty(value) ?
290 defaultValue : (value.equals("1") || value.equals("true"));
291 }
292
293 @Override
294 public long getLong(String key, long defaultValue, int userId) throws RemoteException {
Jim Miller158fe192013-04-17 15:23:55 -0700295 checkReadPermission(key, userId);
Amith Yamasani52c489c2012-03-28 11:42:42 -0700296
Adrian Roos9dd16eb2015-01-08 16:20:49 +0100297 String value = getStringUnchecked(key, null, userId);
Amith Yamasani52c489c2012-03-28 11:42:42 -0700298 return TextUtils.isEmpty(value) ? defaultValue : Long.parseLong(value);
299 }
300
301 @Override
302 public String getString(String key, String defaultValue, int userId) throws RemoteException {
Jim Miller158fe192013-04-17 15:23:55 -0700303 checkReadPermission(key, userId);
Amith Yamasani52c489c2012-03-28 11:42:42 -0700304
Adrian Roos9dd16eb2015-01-08 16:20:49 +0100305 return getStringUnchecked(key, defaultValue, userId);
306 }
307
308 public String getStringUnchecked(String key, String defaultValue, int userId) {
309 if (Settings.Secure.LOCK_PATTERN_ENABLED.equals(key)) {
310 return mLockPatternUtils.isLockPatternEnabled(userId) ? "1" : "0";
311 }
312
Adrian Roos261d5ab2014-10-29 14:42:38 +0100313 return mStorage.readKeyValue(key, defaultValue, userId);
Amith Yamasani52c489c2012-03-28 11:42:42 -0700314 }
315
Adrian Roos4f788452014-05-22 20:45:59 +0200316 @Override
Amith Yamasani52c489c2012-03-28 11:42:42 -0700317 public boolean havePassword(int userId) throws RemoteException {
318 // Do we need a permissions check here?
319
Adrian Roos261d5ab2014-10-29 14:42:38 +0100320 return mStorage.hasPassword(userId);
Amith Yamasani52c489c2012-03-28 11:42:42 -0700321 }
322
323 @Override
324 public boolean havePattern(int userId) throws RemoteException {
325 // Do we need a permissions check here?
326
Adrian Roos261d5ab2014-10-29 14:42:38 +0100327 return mStorage.hasPattern(userId);
Amith Yamasani52c489c2012-03-28 11:42:42 -0700328 }
329
Robin Leef0246a82014-08-13 09:50:25 +0100330 private void maybeUpdateKeystore(String password, int userHandle) {
331 final UserManager um = (UserManager) mContext.getSystemService(USER_SERVICE);
332 final KeyStore ks = KeyStore.getInstance();
333
334 final List<UserInfo> profiles = um.getProfiles(userHandle);
335 boolean shouldReset = TextUtils.isEmpty(password);
336
337 // For historical reasons, don't wipe a non-empty keystore if we have a single user with a
338 // single profile.
339 if (userHandle == UserHandle.USER_OWNER && profiles.size() == 1) {
340 if (!ks.isEmpty()) {
341 shouldReset = false;
342 }
343 }
344
345 for (UserInfo pi : profiles) {
346 final int profileUid = UserHandle.getUid(pi.id, Process.SYSTEM_UID);
347 if (shouldReset) {
348 ks.resetUid(profileUid);
Jim Millerde1af082013-09-11 14:58:26 -0700349 } else {
Robin Leef0246a82014-08-13 09:50:25 +0100350 ks.passwordUid(password, profileUid);
Jim Millerde1af082013-09-11 14:58:26 -0700351 }
352 }
353 }
354
Amith Yamasani52c489c2012-03-28 11:42:42 -0700355
Andres Morales8fa56652015-03-31 09:19:50 -0700356 private byte[] getCurrentHandle(int userId) {
357 CredentialHash credential;
358 byte[] currentHandle;
Jim Millerde1af082013-09-11 14:58:26 -0700359
Andres Morales8fa56652015-03-31 09:19:50 -0700360 int currentHandleType = mStorage.getStoredCredentialType(userId);
361 switch (currentHandleType) {
362 case CredentialHash.TYPE_PATTERN:
363 credential = mStorage.readPatternHash(userId);
364 currentHandle = credential != null
365 ? credential.hash
366 : null;
367 break;
368 case CredentialHash.TYPE_PASSWORD:
369 credential = mStorage.readPasswordHash(userId);
370 currentHandle = credential != null
371 ? credential.hash
372 : null;
373 break;
374 case CredentialHash.TYPE_NONE:
375 default:
376 currentHandle = null;
377 break;
378 }
379
380 // sanity check
381 if (currentHandleType != CredentialHash.TYPE_NONE && currentHandle == null) {
382 Slog.e(TAG, "Stored handle type [" + currentHandleType + "] but no handle available");
383 }
384
385 return currentHandle;
Amith Yamasani52c489c2012-03-28 11:42:42 -0700386 }
387
Andres Morales8fa56652015-03-31 09:19:50 -0700388
Amith Yamasani52c489c2012-03-28 11:42:42 -0700389 @Override
Andres Morales8fa56652015-03-31 09:19:50 -0700390 public void setLockPattern(String pattern, String savedCredential, int userId)
391 throws RemoteException {
392 byte[] currentHandle = getCurrentHandle(userId);
393
Andres Moralesd9fc85a2015-04-09 19:14:42 -0700394 if (pattern == null) {
Andres Moralescfb61602015-04-16 16:31:15 -0700395 getGateKeeperService().clearSecureUserId(userId);
Andres Moralesd9fc85a2015-04-09 19:14:42 -0700396 mStorage.writePatternHash(null, userId);
Andres Moralescfb61602015-04-16 16:31:15 -0700397 maybeUpdateKeystore(null, userId);
Andres Moralesd9fc85a2015-04-09 19:14:42 -0700398 return;
399 }
400
Andres Morales8fa56652015-03-31 09:19:50 -0700401 if (currentHandle == null) {
402 if (savedCredential != null) {
403 Slog.w(TAG, "Saved credential provided, but none stored");
404 }
405 savedCredential = null;
406 }
407
408 byte[] enrolledHandle = enrollCredential(currentHandle, savedCredential, pattern, userId);
409 if (enrolledHandle != null) {
410 mStorage.writePatternHash(enrolledHandle, userId);
411 } else {
412 Slog.e(TAG, "Failed to enroll pattern");
413 }
414 }
415
416
417 @Override
418 public void setLockPassword(String password, String savedCredential, int userId)
419 throws RemoteException {
Andres Morales8fa56652015-03-31 09:19:50 -0700420 byte[] currentHandle = getCurrentHandle(userId);
421
Andres Moralesd9fc85a2015-04-09 19:14:42 -0700422 if (password == null) {
Andres Moralescfb61602015-04-16 16:31:15 -0700423 getGateKeeperService().clearSecureUserId(userId);
Andres Moralesd9fc85a2015-04-09 19:14:42 -0700424 mStorage.writePasswordHash(null, userId);
Andres Moralescfb61602015-04-16 16:31:15 -0700425 maybeUpdateKeystore(null, userId);
Andres Moralesd9fc85a2015-04-09 19:14:42 -0700426 return;
427 }
428
Andres Morales8fa56652015-03-31 09:19:50 -0700429 if (currentHandle == null) {
430 if (savedCredential != null) {
431 Slog.w(TAG, "Saved credential provided, but none stored");
432 }
433 savedCredential = null;
434 }
435
436 byte[] enrolledHandle = enrollCredential(currentHandle, savedCredential, password, userId);
437 if (enrolledHandle != null) {
438 mStorage.writePasswordHash(enrolledHandle, userId);
439 } else {
440 Slog.e(TAG, "Failed to enroll password");
441 }
442 }
443
444 private byte[] enrollCredential(byte[] enrolledHandle,
445 String enrolledCredential, String toEnroll, int userId)
446 throws RemoteException {
Jim Millerde1af082013-09-11 14:58:26 -0700447 checkWritePermission(userId);
Andres Morales8fa56652015-03-31 09:19:50 -0700448 byte[] enrolledCredentialBytes = enrolledCredential == null
449 ? null
450 : enrolledCredential.getBytes();
451 byte[] toEnrollBytes = toEnroll == null
452 ? null
453 : toEnroll.getBytes();
454 byte[] hash = getGateKeeperService().enroll(userId, enrolledHandle, enrolledCredentialBytes,
455 toEnrollBytes);
Jim Millerde1af082013-09-11 14:58:26 -0700456
Andres Morales8fa56652015-03-31 09:19:50 -0700457 if (hash != null) {
458 maybeUpdateKeystore(toEnroll, userId);
459 }
Jim Millerde1af082013-09-11 14:58:26 -0700460
Andres Morales8fa56652015-03-31 09:19:50 -0700461 return hash;
Jim Millerde1af082013-09-11 14:58:26 -0700462 }
463
464 @Override
465 public boolean checkPattern(String pattern, int userId) throws RemoteException {
Andres Moralesd9fc85a2015-04-09 19:14:42 -0700466 try {
467 doVerifyPattern(pattern, false, 0, userId);
468 } catch (VerificationFailedException ex) {
469 return false;
470 }
Andres Morales8fa56652015-03-31 09:19:50 -0700471
Andres Moralesd9fc85a2015-04-09 19:14:42 -0700472 return true;
473 }
Adrian Roos261d5ab2014-10-29 14:42:38 +0100474
Andres Moralesd9fc85a2015-04-09 19:14:42 -0700475 @Override
476 public byte[] verifyPattern(String pattern, long challenge, int userId)
477 throws RemoteException {
478 try {
479 return doVerifyPattern(pattern, true, challenge, userId);
480 } catch (VerificationFailedException ex) {
481 return null;
482 }
483 }
484
485 private byte[] doVerifyPattern(String pattern, boolean hasChallenge, long challenge,
486 int userId) throws VerificationFailedException, RemoteException {
487 checkPasswordReadPermission(userId);
488
489 CredentialHash storedHash = mStorage.readPatternHash(userId);
490
491 if ((storedHash == null || storedHash.hash.length == 0) && TextUtils.isEmpty(pattern)) {
492 // don't need to pass empty passwords to GateKeeper
493 return null;
494 }
495
496 if (TextUtils.isEmpty(pattern)) {
497 throw new VerificationFailedException();
Amith Yamasani52c489c2012-03-28 11:42:42 -0700498 }
Adrian Roos261d5ab2014-10-29 14:42:38 +0100499
Andres Morales8fa56652015-03-31 09:19:50 -0700500 if (storedHash.version == CredentialHash.VERSION_LEGACY) {
Andres Moralesd9fc85a2015-04-09 19:14:42 -0700501 byte[] hash = mLockPatternUtils.patternToHash(
502 mLockPatternUtils.stringToPattern(pattern));
503 if (Arrays.equals(hash, storedHash.hash)) {
Andres Morales8fa56652015-03-31 09:19:50 -0700504 maybeUpdateKeystore(pattern, userId);
Andres Moralesd9fc85a2015-04-09 19:14:42 -0700505 // migrate password to GateKeeper
Andres Morales8fa56652015-03-31 09:19:50 -0700506 setLockPattern(pattern, null, userId);
Andres Moralesd9fc85a2015-04-09 19:14:42 -0700507 if (!hasChallenge) {
508 return null;
509 }
510 // Fall through to get the auth token. Technically this should never happen,
511 // as a user that had a legacy pattern would have to unlock their device
512 // before getting to a flow with a challenge, but supporting for consistency.
513 } else {
514 throw new VerificationFailedException();
Andres Morales8fa56652015-03-31 09:19:50 -0700515 }
Andres Morales8fa56652015-03-31 09:19:50 -0700516 }
517
Andres Moralesd9fc85a2015-04-09 19:14:42 -0700518 byte[] token = null;
519 if (hasChallenge) {
520 token = getGateKeeperService()
521 .verifyChallenge(userId, challenge, storedHash.hash, pattern.getBytes());
522 if (token == null) {
523 throw new VerificationFailedException();
524 }
525 } else if (!getGateKeeperService().verify(userId, storedHash.hash, pattern.getBytes())) {
526 throw new VerificationFailedException();
Adrian Roos261d5ab2014-10-29 14:42:38 +0100527 }
Andres Morales8fa56652015-03-31 09:19:50 -0700528
Andres Moralesd9fc85a2015-04-09 19:14:42 -0700529 // pattern has matched
530 maybeUpdateKeystore(pattern, userId);
531 return token;
532
Amith Yamasani52c489c2012-03-28 11:42:42 -0700533 }
534
535 @Override
Jim Millerde1af082013-09-11 14:58:26 -0700536 public boolean checkPassword(String password, int userId) throws RemoteException {
Andres Moralesd9fc85a2015-04-09 19:14:42 -0700537 try {
538 doVerifyPassword(password, false, 0, userId);
539 } catch (VerificationFailedException ex) {
540 return false;
541 }
542 return true;
543 }
544
545 @Override
546 public byte[] verifyPassword(String password, long challenge, int userId)
547 throws RemoteException {
548 try {
549 return doVerifyPassword(password, true, challenge, userId);
550 } catch (VerificationFailedException ex) {
551 return null;
552 }
553 }
554
555 private byte[] doVerifyPassword(String password, boolean hasChallenge, long challenge,
556 int userId) throws VerificationFailedException, RemoteException {
557 checkPasswordReadPermission(userId);
Amith Yamasani52c489c2012-03-28 11:42:42 -0700558
Andres Morales8fa56652015-03-31 09:19:50 -0700559 CredentialHash storedHash = mStorage.readPasswordHash(userId);
Adrian Roos261d5ab2014-10-29 14:42:38 +0100560
Andres Moralesd9fc85a2015-04-09 19:14:42 -0700561 if ((storedHash == null || storedHash.hash.length == 0) && TextUtils.isEmpty(password)) {
562 // don't need to pass empty passwords to GateKeeper
563 return null;
564 }
565
566 if (TextUtils.isEmpty(password)) {
567 throw new VerificationFailedException();
Amith Yamasani52c489c2012-03-28 11:42:42 -0700568 }
Adrian Roos261d5ab2014-10-29 14:42:38 +0100569
Andres Morales8fa56652015-03-31 09:19:50 -0700570 if (storedHash.version == CredentialHash.VERSION_LEGACY) {
571 byte[] hash = mLockPatternUtils.passwordToHash(password, userId);
Andres Moralesd9fc85a2015-04-09 19:14:42 -0700572 if (Arrays.equals(hash, storedHash.hash)) {
Andres Morales8fa56652015-03-31 09:19:50 -0700573 maybeUpdateKeystore(password, userId);
574 // migrate password to GateKeeper
575 setLockPassword(password, null, userId);
Andres Moralesd9fc85a2015-04-09 19:14:42 -0700576 if (!hasChallenge) {
577 return null;
578 }
579 // Fall through to get the auth token. Technically this should never happen,
580 // as a user that had a legacy password would have to unlock their device
581 // before getting to a flow with a challenge, but supporting for consistency.
582 } else {
583 throw new VerificationFailedException();
Andres Morales8fa56652015-03-31 09:19:50 -0700584 }
Andres Morales8fa56652015-03-31 09:19:50 -0700585 }
586
Andres Moralesd9fc85a2015-04-09 19:14:42 -0700587 byte[] token = null;
588 if (hasChallenge) {
589 token = getGateKeeperService()
590 .verifyChallenge(userId, challenge, storedHash.hash, password.getBytes());
591 if (token == null) {
592 throw new VerificationFailedException();
593 }
594 } else if (!getGateKeeperService().verify(userId, storedHash.hash, password.getBytes())) {
595 throw new VerificationFailedException();
Adrian Roos261d5ab2014-10-29 14:42:38 +0100596 }
Andres Morales8fa56652015-03-31 09:19:50 -0700597
Andres Moralesd9fc85a2015-04-09 19:14:42 -0700598 // password has matched
599 maybeUpdateKeystore(password, userId);
600 return token;
Amith Yamasani52c489c2012-03-28 11:42:42 -0700601 }
602
Andres Moralesd9fc85a2015-04-09 19:14:42 -0700603
Amith Yamasani52c489c2012-03-28 11:42:42 -0700604 @Override
Adrian Roos261d5ab2014-10-29 14:42:38 +0100605 public boolean checkVoldPassword(int userId) throws RemoteException {
Paul Lawrence945490c2014-03-27 16:37:28 +0000606 if (!mFirstCallToVold) {
607 return false;
608 }
609 mFirstCallToVold = false;
610
611 checkPasswordReadPermission(userId);
612
613 // There's no guarantee that this will safely connect, but if it fails
614 // we will simply show the lock screen when we shouldn't, so relatively
615 // benign. There is an outside chance something nasty would happen if
616 // this service restarted before vold stales out the password in this
617 // case. The nastiness is limited to not showing the lock screen when
618 // we should, within the first minute of decrypting the phone if this
619 // service can't connect to vold, it restarts, and then the new instance
620 // does successfully connect.
621 final IMountService service = getMountService();
622 String password = service.getPassword();
623 service.clearPassword();
624 if (password == null) {
625 return false;
626 }
627
628 try {
Adrian Roos9dd16eb2015-01-08 16:20:49 +0100629 if (mLockPatternUtils.isLockPatternEnabled(userId)) {
Paul Lawrence945490c2014-03-27 16:37:28 +0000630 if (checkPattern(password, userId)) {
631 return true;
632 }
633 }
634 } catch (Exception e) {
635 }
636
637 try {
Adrian Roos9dd16eb2015-01-08 16:20:49 +0100638 if (mLockPatternUtils.isLockPasswordEnabled(userId)) {
Paul Lawrence945490c2014-03-27 16:37:28 +0000639 if (checkPassword(password, userId)) {
640 return true;
641 }
642 }
643 } catch (Exception e) {
644 }
645
646 return false;
647 }
648
Adrian Roosdb0f76e2015-01-07 22:19:38 +0100649 private void removeUser(int userId) {
Adrian Roos261d5ab2014-10-29 14:42:38 +0100650 mStorage.removeUser(userId);
Robin Lee49d810c2014-09-23 13:50:22 +0100651
652 final KeyStore ks = KeyStore.getInstance();
653 final int userUid = UserHandle.getUid(userId, Process.SYSTEM_UID);
654 ks.resetUid(userUid);
Amith Yamasani52c489c2012-03-28 11:42:42 -0700655 }
656
Amith Yamasani52c489c2012-03-28 11:42:42 -0700657 private static final String[] VALID_SETTINGS = new String[] {
658 LockPatternUtils.LOCKOUT_PERMANENT_KEY,
659 LockPatternUtils.LOCKOUT_ATTEMPT_DEADLINE,
660 LockPatternUtils.PATTERN_EVER_CHOSEN_KEY,
661 LockPatternUtils.PASSWORD_TYPE_KEY,
662 LockPatternUtils.PASSWORD_TYPE_ALTERNATE_KEY,
663 LockPatternUtils.LOCK_PASSWORD_SALT_KEY,
664 LockPatternUtils.DISABLE_LOCKSCREEN_KEY,
665 LockPatternUtils.LOCKSCREEN_OPTIONS,
666 LockPatternUtils.LOCKSCREEN_BIOMETRIC_WEAK_FALLBACK,
667 LockPatternUtils.BIOMETRIC_WEAK_EVER_CHOSEN_KEY,
668 LockPatternUtils.LOCKSCREEN_POWER_BUTTON_INSTANTLY_LOCKS,
669 LockPatternUtils.PASSWORD_HISTORY_KEY,
670 Secure.LOCK_PATTERN_ENABLED,
671 Secure.LOCK_BIOMETRIC_WEAK_FLAGS,
672 Secure.LOCK_PATTERN_VISIBLE,
673 Secure.LOCK_PATTERN_TACTILE_FEEDBACK_ENABLED
Jim Miller187ec582013-04-15 18:27:54 -0700674 };
675
Adrian Roos001b00d2015-02-24 17:08:48 +0100676 // Reading these settings needs the profile permission
Jim Miller2d8ecf9d2013-04-22 17:17:03 -0700677 private static final String[] READ_PROFILE_PROTECTED_SETTINGS = new String[] {
Jim Miller187ec582013-04-15 18:27:54 -0700678 Secure.LOCK_SCREEN_OWNER_INFO_ENABLED,
679 Secure.LOCK_SCREEN_OWNER_INFO
680 };
Paul Lawrence945490c2014-03-27 16:37:28 +0000681
Adrian Roos001b00d2015-02-24 17:08:48 +0100682 // Reading these settings needs the same permission as checking the password
683 private static final String[] READ_PASSWORD_PROTECTED_SETTINGS = new String[] {
684 LockPatternUtils.LOCK_PASSWORD_SALT_KEY,
685 LockPatternUtils.PASSWORD_HISTORY_KEY,
Adrian Roos855fa302015-04-02 16:01:12 +0200686 LockPatternUtils.PASSWORD_TYPE_KEY,
Adrian Roos001b00d2015-02-24 17:08:48 +0100687 };
688
Amith Yamasani072543f2015-02-13 11:09:45 -0800689 private static final String[] SETTINGS_TO_BACKUP = new String[] {
690 Secure.LOCK_SCREEN_OWNER_INFO_ENABLED,
691 Secure.LOCK_SCREEN_OWNER_INFO
692 };
693
Paul Lawrence945490c2014-03-27 16:37:28 +0000694 private IMountService getMountService() {
695 final IBinder service = ServiceManager.getService("mount");
696 if (service != null) {
697 return IMountService.Stub.asInterface(service);
698 }
699 return null;
700 }
Andres Morales8fa56652015-03-31 09:19:50 -0700701
Andres Morales301ea442015-04-17 09:15:47 -0700702 private class GateKeeperDiedRecipient implements IBinder.DeathRecipient {
703 @Override
704 public void binderDied() {
705 mGateKeeperService.asBinder().unlinkToDeath(this, 0);
706 mGateKeeperService = null;
707 }
708 }
709
710 private synchronized IGateKeeperService getGateKeeperService()
711 throws RemoteException {
Andres Morales8fa56652015-03-31 09:19:50 -0700712 if (mGateKeeperService != null) {
713 return mGateKeeperService;
714 }
715
716 final IBinder service =
717 ServiceManager.getService("android.service.gatekeeper.IGateKeeperService");
718 if (service != null) {
Andres Morales301ea442015-04-17 09:15:47 -0700719 service.linkToDeath(new GateKeeperDiedRecipient(), 0);
Andres Morales8fa56652015-03-31 09:19:50 -0700720 mGateKeeperService = IGateKeeperService.Stub.asInterface(service);
721 return mGateKeeperService;
722 }
723
724 Slog.e(TAG, "Unable to acquire GateKeeperService");
725 return null;
726 }
727
Andres Moralesd9fc85a2015-04-09 19:14:42 -0700728 private class VerificationFailedException extends Exception {}
729
Amith Yamasani52c489c2012-03-28 11:42:42 -0700730}