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