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