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