LockSettingsService.java revision db0f76e1d85f8cb878a9540ac1b692636f9fd89e
1/* 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 17package com.android.server; 18 19import android.app.admin.DevicePolicyManager; 20import android.content.BroadcastReceiver; 21import android.content.ContentResolver; 22import android.content.Context; 23import android.content.Intent; 24import android.content.IntentFilter; 25import android.content.pm.PackageManager; 26import android.content.pm.UserInfo; 27 28import static android.Manifest.permission.ACCESS_KEYGUARD_SECURE_STORAGE; 29import static android.content.Context.USER_SERVICE; 30import static android.Manifest.permission.READ_PROFILE; 31 32import android.database.sqlite.SQLiteDatabase; 33import android.os.Binder; 34import android.os.IBinder; 35import android.os.Process; 36import android.os.RemoteException; 37import android.os.storage.IMountService; 38import android.os.ServiceManager; 39import android.os.SystemProperties; 40import android.os.UserHandle; 41import android.os.UserManager; 42import android.provider.Settings; 43import android.provider.Settings.Secure; 44import android.provider.Settings.SettingNotFoundException; 45import android.security.KeyStore; 46import android.text.TextUtils; 47import android.util.Slog; 48 49import com.android.internal.widget.ILockSettings; 50import com.android.internal.widget.LockPatternUtils; 51 52import java.util.ArrayList; 53import java.util.Arrays; 54import java.util.List; 55 56/** 57 * Keeps the lock pattern/password data and related settings for each user. 58 * Used by LockPatternUtils. Needs to be a service because Settings app also needs 59 * to be able to save lockscreen information for secondary users. 60 * @hide 61 */ 62public class LockSettingsService extends ILockSettings.Stub { 63 64 private static final String PERMISSION = ACCESS_KEYGUARD_SECURE_STORAGE; 65 66 private static final String TAG = "LockSettingsService"; 67 68 private final Context mContext; 69 70 private final LockSettingsStorage mStorage; 71 72 private LockPatternUtils mLockPatternUtils; 73 private boolean mFirstCallToVold; 74 75 public LockSettingsService(Context context) { 76 mContext = context; 77 // Open the database 78 79 mLockPatternUtils = new LockPatternUtils(context); 80 mFirstCallToVold = true; 81 82 IntentFilter filter = new IntentFilter(); 83 filter.addAction(Intent.ACTION_USER_ADDED); 84 filter.addAction(Intent.ACTION_USER_STARTING); 85 filter.addAction(Intent.ACTION_USER_REMOVED); 86 mContext.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, filter, null, null); 87 88 mStorage = new LockSettingsStorage(context, new LockSettingsStorage.Callback() { 89 @Override 90 public void initialize(SQLiteDatabase db) { 91 // Get the lockscreen default from a system property, if available 92 boolean lockScreenDisable = SystemProperties.getBoolean( 93 "ro.lockscreen.disable.default", false); 94 if (lockScreenDisable) { 95 mStorage.writeKeyValue(db, LockPatternUtils.DISABLE_LOCKSCREEN_KEY, "1", 0); 96 } 97 } 98 }); 99 } 100 101 private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { 102 @Override 103 public void onReceive(Context context, Intent intent) { 104 if (Intent.ACTION_USER_ADDED.equals(intent.getAction())) { 105 final int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0); 106 final int userSysUid = UserHandle.getUid(userHandle, Process.SYSTEM_UID); 107 final KeyStore ks = KeyStore.getInstance(); 108 109 // Clear up keystore in case anything was left behind by previous users 110 ks.resetUid(userSysUid); 111 112 // If this user has a parent, sync with its keystore password 113 final UserManager um = (UserManager) mContext.getSystemService(USER_SERVICE); 114 final UserInfo parentInfo = um.getProfileParent(userHandle); 115 if (parentInfo != null) { 116 final int parentSysUid = UserHandle.getUid(parentInfo.id, Process.SYSTEM_UID); 117 ks.syncUid(parentSysUid, userSysUid); 118 } 119 } else if (Intent.ACTION_USER_STARTING.equals(intent.getAction())) { 120 final int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0); 121 mStorage.prefetchUser(userHandle); 122 } else if (Intent.ACTION_USER_REMOVED.equals(intent.getAction())) { 123 final int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0); 124 if (userHandle > 0) { 125 removeUser(userHandle); 126 } 127 } 128 } 129 }; 130 131 public void systemReady() { 132 migrateOldData(); 133 mStorage.prefetchUser(UserHandle.USER_OWNER); 134 } 135 136 private void migrateOldData() { 137 try { 138 // These Settings moved before multi-user was enabled, so we only have to do it for the 139 // root user. 140 if (getString("migrated", null, 0) == null) { 141 final ContentResolver cr = mContext.getContentResolver(); 142 for (String validSetting : VALID_SETTINGS) { 143 String value = Settings.Secure.getString(cr, validSetting); 144 if (value != null) { 145 setString(validSetting, value, 0); 146 } 147 } 148 // No need to move the password / pattern files. They're already in the right place. 149 setString("migrated", "true", 0); 150 Slog.i(TAG, "Migrated lock settings to new location"); 151 } 152 153 // These Settings changed after multi-user was enabled, hence need to be moved per user. 154 if (getString("migrated_user_specific", null, 0) == null) { 155 final UserManager um = (UserManager) mContext.getSystemService(USER_SERVICE); 156 final ContentResolver cr = mContext.getContentResolver(); 157 List<UserInfo> users = um.getUsers(); 158 for (int user = 0; user < users.size(); user++) { 159 // Migrate owner info 160 final int userId = users.get(user).id; 161 final String OWNER_INFO = Secure.LOCK_SCREEN_OWNER_INFO; 162 String ownerInfo = Settings.Secure.getStringForUser(cr, OWNER_INFO, userId); 163 if (ownerInfo != null) { 164 setString(OWNER_INFO, ownerInfo, userId); 165 Settings.Secure.putStringForUser(cr, ownerInfo, "", userId); 166 } 167 168 // Migrate owner info enabled. Note there was a bug where older platforms only 169 // stored this value if the checkbox was toggled at least once. The code detects 170 // this case by handling the exception. 171 final String OWNER_INFO_ENABLED = Secure.LOCK_SCREEN_OWNER_INFO_ENABLED; 172 boolean enabled; 173 try { 174 int ivalue = Settings.Secure.getIntForUser(cr, OWNER_INFO_ENABLED, userId); 175 enabled = ivalue != 0; 176 setLong(OWNER_INFO_ENABLED, enabled ? 1 : 0, userId); 177 } catch (SettingNotFoundException e) { 178 // Setting was never stored. Store it if the string is not empty. 179 if (!TextUtils.isEmpty(ownerInfo)) { 180 setLong(OWNER_INFO_ENABLED, 1, userId); 181 } 182 } 183 Settings.Secure.putIntForUser(cr, OWNER_INFO_ENABLED, 0, userId); 184 } 185 // No need to move the password / pattern files. They're already in the right place. 186 setString("migrated_user_specific", "true", 0); 187 Slog.i(TAG, "Migrated per-user lock settings to new location"); 188 } 189 190 // Migrates biometric weak such that the fallback mechanism becomes the primary. 191 if (getString("migrated_biometric_weak", null, 0) == null) { 192 final UserManager um = (UserManager) mContext.getSystemService(USER_SERVICE); 193 List<UserInfo> users = um.getUsers(); 194 for (int i = 0; i < users.size(); i++) { 195 int userId = users.get(i).id; 196 long type = getLong(LockPatternUtils.PASSWORD_TYPE_KEY, 197 DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, 198 userId); 199 long alternateType = getLong(LockPatternUtils.PASSWORD_TYPE_ALTERNATE_KEY, 200 DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, 201 userId); 202 if (type == DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK) { 203 setLong(LockPatternUtils.PASSWORD_TYPE_KEY, 204 alternateType, 205 userId); 206 } 207 setLong(LockPatternUtils.PASSWORD_TYPE_ALTERNATE_KEY, 208 DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, 209 userId); 210 } 211 setString("migrated_biometric_weak", "true", 0); 212 Slog.i(TAG, "Migrated biometric weak to use the fallback instead"); 213 } 214 } catch (RemoteException re) { 215 Slog.e(TAG, "Unable to migrate old data", re); 216 } 217 } 218 219 private final void checkWritePermission(int userId) { 220 mContext.enforceCallingOrSelfPermission(PERMISSION, "LockSettingsWrite"); 221 } 222 223 private final void checkPasswordReadPermission(int userId) { 224 mContext.enforceCallingOrSelfPermission(PERMISSION, "LockSettingsRead"); 225 } 226 227 private final void checkReadPermission(String requestedKey, int userId) { 228 final int callingUid = Binder.getCallingUid(); 229 for (int i = 0; i < READ_PROFILE_PROTECTED_SETTINGS.length; i++) { 230 String key = READ_PROFILE_PROTECTED_SETTINGS[i]; 231 if (key.equals(requestedKey) && mContext.checkCallingOrSelfPermission(READ_PROFILE) 232 != PackageManager.PERMISSION_GRANTED) { 233 throw new SecurityException("uid=" + callingUid 234 + " needs permission " + READ_PROFILE + " to read " 235 + requestedKey + " for user " + userId); 236 } 237 } 238 } 239 240 @Override 241 public void setBoolean(String key, boolean value, int userId) throws RemoteException { 242 checkWritePermission(userId); 243 setStringUnchecked(key, userId, value ? "1" : "0"); 244 } 245 246 @Override 247 public void setLong(String key, long value, int userId) throws RemoteException { 248 checkWritePermission(userId); 249 setStringUnchecked(key, userId, Long.toString(value)); 250 } 251 252 @Override 253 public void setString(String key, String value, int userId) throws RemoteException { 254 checkWritePermission(userId); 255 setStringUnchecked(key, userId, value); 256 } 257 258 private void setStringUnchecked(String key, int userId, String value) { 259 mStorage.writeKeyValue(key, value, userId); 260 } 261 262 @Override 263 public boolean getBoolean(String key, boolean defaultValue, int userId) throws RemoteException { 264 checkReadPermission(key, userId); 265 266 String value = mStorage.readKeyValue(key, null, userId); 267 return TextUtils.isEmpty(value) ? 268 defaultValue : (value.equals("1") || value.equals("true")); 269 } 270 271 @Override 272 public long getLong(String key, long defaultValue, int userId) throws RemoteException { 273 checkReadPermission(key, userId); 274 275 String value = mStorage.readKeyValue(key, null, userId); 276 return TextUtils.isEmpty(value) ? defaultValue : Long.parseLong(value); 277 } 278 279 @Override 280 public String getString(String key, String defaultValue, int userId) throws RemoteException { 281 checkReadPermission(key, userId); 282 283 return mStorage.readKeyValue(key, defaultValue, userId); 284 } 285 286 @Override 287 public boolean havePassword(int userId) throws RemoteException { 288 // Do we need a permissions check here? 289 290 return mStorage.hasPassword(userId); 291 } 292 293 @Override 294 public boolean havePattern(int userId) throws RemoteException { 295 // Do we need a permissions check here? 296 297 return mStorage.hasPattern(userId); 298 } 299 300 private void maybeUpdateKeystore(String password, int userHandle) { 301 final UserManager um = (UserManager) mContext.getSystemService(USER_SERVICE); 302 final KeyStore ks = KeyStore.getInstance(); 303 304 final List<UserInfo> profiles = um.getProfiles(userHandle); 305 boolean shouldReset = TextUtils.isEmpty(password); 306 307 // For historical reasons, don't wipe a non-empty keystore if we have a single user with a 308 // single profile. 309 if (userHandle == UserHandle.USER_OWNER && profiles.size() == 1) { 310 if (!ks.isEmpty()) { 311 shouldReset = false; 312 } 313 } 314 315 for (UserInfo pi : profiles) { 316 final int profileUid = UserHandle.getUid(pi.id, Process.SYSTEM_UID); 317 if (shouldReset) { 318 ks.resetUid(profileUid); 319 } else { 320 ks.passwordUid(password, profileUid); 321 } 322 } 323 } 324 325 @Override 326 public void setLockPattern(String pattern, int userId) throws RemoteException { 327 checkWritePermission(userId); 328 329 maybeUpdateKeystore(pattern, userId); 330 331 final byte[] hash = LockPatternUtils.patternToHash( 332 LockPatternUtils.stringToPattern(pattern)); 333 mStorage.writePatternHash(hash, userId); 334 } 335 336 @Override 337 public void setLockPassword(String password, int userId) throws RemoteException { 338 checkWritePermission(userId); 339 340 maybeUpdateKeystore(password, userId); 341 342 mStorage.writePasswordHash(mLockPatternUtils.passwordToHash(password, userId), userId); 343 } 344 345 @Override 346 public boolean checkPattern(String pattern, int userId) throws RemoteException { 347 checkPasswordReadPermission(userId); 348 byte[] hash = LockPatternUtils.patternToHash(LockPatternUtils.stringToPattern(pattern)); 349 byte[] storedHash = mStorage.readPatternHash(userId); 350 351 if (storedHash == null) { 352 return true; 353 } 354 355 boolean matched = Arrays.equals(hash, storedHash); 356 if (matched && !TextUtils.isEmpty(pattern)) { 357 maybeUpdateKeystore(pattern, userId); 358 } 359 return matched; 360 } 361 362 @Override 363 public boolean checkPassword(String password, int userId) throws RemoteException { 364 checkPasswordReadPermission(userId); 365 366 byte[] hash = mLockPatternUtils.passwordToHash(password, userId); 367 byte[] storedHash = mStorage.readPasswordHash(userId); 368 369 if (storedHash == null) { 370 return true; 371 } 372 373 boolean matched = Arrays.equals(hash, storedHash); 374 if (matched && !TextUtils.isEmpty(password)) { 375 maybeUpdateKeystore(password, userId); 376 } 377 return matched; 378 } 379 380 @Override 381 public boolean checkVoldPassword(int userId) throws RemoteException { 382 if (!mFirstCallToVold) { 383 return false; 384 } 385 mFirstCallToVold = false; 386 387 checkPasswordReadPermission(userId); 388 389 // There's no guarantee that this will safely connect, but if it fails 390 // we will simply show the lock screen when we shouldn't, so relatively 391 // benign. There is an outside chance something nasty would happen if 392 // this service restarted before vold stales out the password in this 393 // case. The nastiness is limited to not showing the lock screen when 394 // we should, within the first minute of decrypting the phone if this 395 // service can't connect to vold, it restarts, and then the new instance 396 // does successfully connect. 397 final IMountService service = getMountService(); 398 String password = service.getPassword(); 399 service.clearPassword(); 400 if (password == null) { 401 return false; 402 } 403 404 try { 405 if (mLockPatternUtils.isLockPatternEnabled()) { 406 if (checkPattern(password, userId)) { 407 return true; 408 } 409 } 410 } catch (Exception e) { 411 } 412 413 try { 414 if (mLockPatternUtils.isLockPasswordEnabled()) { 415 if (checkPassword(password, userId)) { 416 return true; 417 } 418 } 419 } catch (Exception e) { 420 } 421 422 return false; 423 } 424 425 private void removeUser(int userId) { 426 mStorage.removeUser(userId); 427 428 final KeyStore ks = KeyStore.getInstance(); 429 final int userUid = UserHandle.getUid(userId, Process.SYSTEM_UID); 430 ks.resetUid(userUid); 431 } 432 433 private static final String[] VALID_SETTINGS = new String[] { 434 LockPatternUtils.LOCKOUT_PERMANENT_KEY, 435 LockPatternUtils.LOCKOUT_ATTEMPT_DEADLINE, 436 LockPatternUtils.PATTERN_EVER_CHOSEN_KEY, 437 LockPatternUtils.PASSWORD_TYPE_KEY, 438 LockPatternUtils.PASSWORD_TYPE_ALTERNATE_KEY, 439 LockPatternUtils.LOCK_PASSWORD_SALT_KEY, 440 LockPatternUtils.DISABLE_LOCKSCREEN_KEY, 441 LockPatternUtils.LOCKSCREEN_OPTIONS, 442 LockPatternUtils.LOCKSCREEN_BIOMETRIC_WEAK_FALLBACK, 443 LockPatternUtils.BIOMETRIC_WEAK_EVER_CHOSEN_KEY, 444 LockPatternUtils.LOCKSCREEN_POWER_BUTTON_INSTANTLY_LOCKS, 445 LockPatternUtils.PASSWORD_HISTORY_KEY, 446 Secure.LOCK_PATTERN_ENABLED, 447 Secure.LOCK_BIOMETRIC_WEAK_FLAGS, 448 Secure.LOCK_PATTERN_VISIBLE, 449 Secure.LOCK_PATTERN_TACTILE_FEEDBACK_ENABLED 450 }; 451 452 // These are protected with a read permission 453 private static final String[] READ_PROFILE_PROTECTED_SETTINGS = new String[] { 454 Secure.LOCK_SCREEN_OWNER_INFO_ENABLED, 455 Secure.LOCK_SCREEN_OWNER_INFO 456 }; 457 458 private IMountService getMountService() { 459 final IBinder service = ServiceManager.getService("mount"); 460 if (service != null) { 461 return IMountService.Stub.asInterface(service); 462 } 463 return null; 464 } 465} 466