blob: 7b767b86f0d492c9ecb8ec66fb501beb1ba0cb7a [file] [log] [blame]
Jorim Jaggi2fef6f72016-11-01 19:06:25 -07001/*
2 * Copyright (C) 2016 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
Andrew Scull507d11c2017-05-03 17:19:01 +010017package com.android.server.locksettings;
Jorim Jaggi2fef6f72016-11-01 19:06:25 -070018
Pavel Grafova182b9c2019-10-16 14:43:46 +010019import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_NONE;
20
21import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_NONE;
22import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PATTERN;
23
Sudheer Shankadc589ac2016-11-10 15:30:17 -080024import android.app.ActivityManager;
Pavel Grafova182b9c2019-10-16 14:43:46 +010025import android.app.admin.PasswordMetrics;
Jorim Jaggi2fef6f72016-11-01 19:06:25 -070026import android.os.ShellCommand;
Rubin Xu62b56b32020-01-16 14:53:34 +000027import android.text.TextUtils;
Jorim Jaggi2fef6f72016-11-01 19:06:25 -070028
Jorim Jaggi2fef6f72016-11-01 19:06:25 -070029import com.android.internal.widget.LockPatternUtils;
30import com.android.internal.widget.LockPatternUtils.RequestThrottledException;
Rubin Xua58125d2019-09-06 20:11:48 +010031import com.android.internal.widget.LockscreenCredential;
Pavel Grafova182b9c2019-10-16 14:43:46 +010032import com.android.internal.widget.PasswordValidationError;
Jorim Jaggi2fef6f72016-11-01 19:06:25 -070033
Felipe Leme1fc9c812018-07-11 10:02:23 -070034import java.io.PrintWriter;
Pavel Grafova182b9c2019-10-16 14:43:46 +010035import java.util.List;
Felipe Leme1fc9c812018-07-11 10:02:23 -070036
Jorim Jaggi2fef6f72016-11-01 19:06:25 -070037class LockSettingsShellCommand extends ShellCommand {
38
39 private static final String COMMAND_SET_PATTERN = "set-pattern";
40 private static final String COMMAND_SET_PIN = "set-pin";
41 private static final String COMMAND_SET_PASSWORD = "set-password";
42 private static final String COMMAND_CLEAR = "clear";
Rubin Xu3bf722a2016-12-15 16:07:38 +000043 private static final String COMMAND_SP = "sp";
Jeff Sharkey65440992017-03-31 09:45:46 -060044 private static final String COMMAND_SET_DISABLED = "set-disabled";
Pavel Grafov5f679b22017-06-26 18:39:10 +010045 private static final String COMMAND_VERIFY = "verify";
chaviw5953e662017-08-21 10:46:11 -070046 private static final String COMMAND_GET_DISABLED = "get-disabled";
Rubin Xua3c71a12019-12-04 15:25:02 +000047 private static final String COMMAND_REMOVE_CACHE = "remove-cache";
Felipe Leme1fc9c812018-07-11 10:02:23 -070048 private static final String COMMAND_HELP = "help";
Jorim Jaggi2fef6f72016-11-01 19:06:25 -070049
50 private int mCurrentUserId;
51 private final LockPatternUtils mLockPatternUtils;
Jorim Jaggi2fef6f72016-11-01 19:06:25 -070052 private String mOld = "";
53 private String mNew = "";
54
Felipe Leme1fc9c812018-07-11 10:02:23 -070055 LockSettingsShellCommand(LockPatternUtils lockPatternUtils) {
Jorim Jaggi2fef6f72016-11-01 19:06:25 -070056 mLockPatternUtils = lockPatternUtils;
57 }
58
59 @Override
60 public int onCommand(String cmd) {
Felipe Leme1fc9c812018-07-11 10:02:23 -070061 if (cmd == null) {
62 return handleDefaultCommands(cmd);
63 }
Jorim Jaggi2fef6f72016-11-01 19:06:25 -070064 try {
Sudheer Shankadc589ac2016-11-10 15:30:17 -080065 mCurrentUserId = ActivityManager.getService().getCurrentUser().id;
Jorim Jaggi2fef6f72016-11-01 19:06:25 -070066
67 parseArgs();
Lenka Trochtova66c492a2018-12-06 11:29:21 +010068 if (!mLockPatternUtils.hasSecureLockScreen()) {
69 switch (cmd) {
70 case COMMAND_HELP:
71 case COMMAND_GET_DISABLED:
72 case COMMAND_SET_DISABLED:
73 break;
74 default:
75 getErrPrintWriter().println(
76 "The device does not support lock screen - ignoring the command.");
77 return -1;
78 }
79 }
Rubin Xua3c71a12019-12-04 15:25:02 +000080 switch (cmd) {
81 // Commands that do not require authentication go here.
82 case COMMAND_REMOVE_CACHE:
83 runRemoveCache();
84 return 0;
85 case COMMAND_HELP:
86 onHelp();
87 return 0;
88 }
Jorim Jaggi2fef6f72016-11-01 19:06:25 -070089 if (!checkCredential()) {
90 return -1;
91 }
Pavel Grafova182b9c2019-10-16 14:43:46 +010092 boolean success = true;
Jorim Jaggi2fef6f72016-11-01 19:06:25 -070093 switch (cmd) {
94 case COMMAND_SET_PATTERN:
Pavel Grafova182b9c2019-10-16 14:43:46 +010095 success = runSetPattern();
Jorim Jaggi2fef6f72016-11-01 19:06:25 -070096 break;
97 case COMMAND_SET_PASSWORD:
Pavel Grafova182b9c2019-10-16 14:43:46 +010098 success = runSetPassword();
Jorim Jaggi2fef6f72016-11-01 19:06:25 -070099 break;
100 case COMMAND_SET_PIN:
Pavel Grafova182b9c2019-10-16 14:43:46 +0100101 success = runSetPin();
Jorim Jaggi2fef6f72016-11-01 19:06:25 -0700102 break;
103 case COMMAND_CLEAR:
Pavel Grafova182b9c2019-10-16 14:43:46 +0100104 success = runClear();
Jorim Jaggi2fef6f72016-11-01 19:06:25 -0700105 break;
Rubin Xu3bf722a2016-12-15 16:07:38 +0000106 case COMMAND_SP:
Paul Crowley7a0cc0a2017-05-31 22:12:57 +0000107 runChangeSp();
Rubin Xu3bf722a2016-12-15 16:07:38 +0000108 break;
Jeff Sharkey65440992017-03-31 09:45:46 -0600109 case COMMAND_SET_DISABLED:
110 runSetDisabled();
111 break;
Pavel Grafov5f679b22017-06-26 18:39:10 +0100112 case COMMAND_VERIFY:
113 runVerify();
114 break;
chaviw5953e662017-08-21 10:46:11 -0700115 case COMMAND_GET_DISABLED:
116 runGetDisabled();
117 break;
Jorim Jaggi2fef6f72016-11-01 19:06:25 -0700118 default:
119 getErrPrintWriter().println("Unknown command: " + cmd);
120 break;
121 }
Pavel Grafova182b9c2019-10-16 14:43:46 +0100122 return success ? 0 : -1;
Jorim Jaggi2fef6f72016-11-01 19:06:25 -0700123 } catch (Exception e) {
Rubin Xu0cbc19e2016-12-09 14:00:21 +0000124 getErrPrintWriter().println("Error while executing command: " + cmd);
125 e.printStackTrace(getErrPrintWriter());
Jorim Jaggi2fef6f72016-11-01 19:06:25 -0700126 return -1;
127 }
128 }
129
Pavel Grafov5f679b22017-06-26 18:39:10 +0100130 private void runVerify() {
131 // The command is only run if the credential is correct.
132 getOutPrintWriter().println("Lock credential verified successfully");
133 }
134
Jorim Jaggi2fef6f72016-11-01 19:06:25 -0700135 @Override
136 public void onHelp() {
Felipe Leme1fc9c812018-07-11 10:02:23 -0700137 try (final PrintWriter pw = getOutPrintWriter();) {
138 pw.println("lockSettings service commands:");
139 pw.println("");
140 pw.println("NOTE: when lock screen is set, all commands require the --old <CREDENTIAL>"
141 + " argument.");
142 pw.println("");
143 pw.println(" help");
144 pw.println(" Prints this help text.");
145 pw.println("");
146 pw.println(" get-disabled [--old <CREDENTIAL>] [--user USER_ID]");
147 pw.println(" Checks whether lock screen is disabled.");
148 pw.println("");
149 pw.println(" set-disabled [--old <CREDENTIAL>] [--user USER_ID] <true|false>");
150 pw.println(" When true, disables lock screen.");
151 pw.println("");
152 pw.println(" set-pattern [--old <CREDENTIAL>] [--user USER_ID] <PATTERN>");
153 pw.println(" Sets the lock screen as pattern, using the given PATTERN to unlock.");
154 pw.println("");
155 pw.println(" set-pin [--old <CREDENTIAL>] [--user USER_ID] <PIN>");
156 pw.println(" Sets the lock screen as PIN, using the given PIN to unlock.");
157 pw.println("");
158 pw.println(" set-pin [--old <CREDENTIAL>] [--user USER_ID] <PASSWORD>");
159 pw.println(" Sets the lock screen as password, using the given PASSOWRD to unlock.");
160 pw.println("");
161 pw.println(" sp [--old <CREDENTIAL>] [--user USER_ID]");
162 pw.println(" Gets whether synthetic password is enabled.");
163 pw.println("");
164 pw.println(" sp [--old <CREDENTIAL>] [--user USER_ID] <1|0>");
165 pw.println(" Enables / disables synthetic password.");
166 pw.println("");
167 pw.println(" clear [--old <CREDENTIAL>] [--user USER_ID]");
168 pw.println(" Clears the lock credentials.");
169 pw.println("");
170 pw.println(" verify [--old <CREDENTIAL>] [--user USER_ID]");
171 pw.println(" Verifies the lock credentials.");
172 pw.println("");
Rubin Xua3c71a12019-12-04 15:25:02 +0000173 pw.println(" remove-cache [--user USER_ID]");
174 pw.println(" Removes cached unified challenge for the managed profile.");
175 pw.println("");
Felipe Leme1fc9c812018-07-11 10:02:23 -0700176 }
Jorim Jaggi2fef6f72016-11-01 19:06:25 -0700177 }
178
179 private void parseArgs() {
180 String opt;
181 while ((opt = getNextOption()) != null) {
182 if ("--old".equals(opt)) {
183 mOld = getNextArgRequired();
Rubin Xu3bf722a2016-12-15 16:07:38 +0000184 } else if ("--user".equals(opt)) {
185 mCurrentUserId = Integer.parseInt(getNextArgRequired());
Jorim Jaggi2fef6f72016-11-01 19:06:25 -0700186 } else {
187 getErrPrintWriter().println("Unknown option: " + opt);
188 throw new IllegalArgumentException();
189 }
190 }
191 mNew = getNextArg();
192 }
193
Paul Crowley7a0cc0a2017-05-31 22:12:57 +0000194 private void runChangeSp() {
195 if (mNew != null ) {
196 if ("1".equals(mNew)) {
197 mLockPatternUtils.enableSyntheticPassword();
198 getOutPrintWriter().println("Synthetic password enabled");
199 } else if ("0".equals(mNew)) {
200 mLockPatternUtils.disableSyntheticPassword();
201 getOutPrintWriter().println("Synthetic password disabled");
202 }
Rubin Xu3bf722a2016-12-15 16:07:38 +0000203 }
204 getOutPrintWriter().println(String.format("SP Enabled = %b",
205 mLockPatternUtils.isSyntheticPasswordEnabled()));
206 }
207
Rubin Xua58125d2019-09-06 20:11:48 +0100208 private LockscreenCredential getOldCredential() {
Rubin Xu62b56b32020-01-16 14:53:34 +0000209 if (TextUtils.isEmpty(mOld)) {
210 return LockscreenCredential.createNone();
211 }
Rubin Xua58125d2019-09-06 20:11:48 +0100212 if (mLockPatternUtils.isLockPasswordEnabled(mCurrentUserId)) {
213 final int quality = mLockPatternUtils.getKeyguardStoredPasswordQuality(mCurrentUserId);
214 if (LockPatternUtils.isQualityAlphabeticPassword(quality)) {
215 return LockscreenCredential.createPassword(mOld);
216 } else {
217 return LockscreenCredential.createPin(mOld);
218 }
Rubin Xu62b56b32020-01-16 14:53:34 +0000219 }
220 if (mLockPatternUtils.isLockPatternEnabled(mCurrentUserId)) {
Rubin Xua58125d2019-09-06 20:11:48 +0100221 return LockscreenCredential.createPattern(LockPatternUtils.byteArrayToPattern(
222 mOld.getBytes()));
Rubin Xua58125d2019-09-06 20:11:48 +0100223 }
Rubin Xu62b56b32020-01-16 14:53:34 +0000224 // User supplied some old credential but the device has neither password nor pattern,
225 // so just return a password credential (and let it be rejected during LSS verification)
226 return LockscreenCredential.createPassword(mOld);
227
Rubin Xua58125d2019-09-06 20:11:48 +0100228 }
229
Pavel Grafova182b9c2019-10-16 14:43:46 +0100230 private boolean runSetPattern() {
231 final LockscreenCredential pattern = LockscreenCredential.createPattern(
232 LockPatternUtils.byteArrayToPattern(mNew.getBytes()));
233 if (!isNewCredentialSufficient(pattern)) {
234 return false;
235 }
236 mLockPatternUtils.setLockCredential(pattern, getOldCredential(), mCurrentUserId);
Jorim Jaggi2fef6f72016-11-01 19:06:25 -0700237 getOutPrintWriter().println("Pattern set to '" + mNew + "'");
Pavel Grafova182b9c2019-10-16 14:43:46 +0100238 return true;
Jorim Jaggi2fef6f72016-11-01 19:06:25 -0700239 }
240
Pavel Grafova182b9c2019-10-16 14:43:46 +0100241 private boolean runSetPassword() {
242 final LockscreenCredential password = LockscreenCredential.createPassword(mNew);
243 if (!isNewCredentialSufficient(password)) {
244 return false;
245 }
246 mLockPatternUtils.setLockCredential(password, getOldCredential(), mCurrentUserId);
Jorim Jaggi2fef6f72016-11-01 19:06:25 -0700247 getOutPrintWriter().println("Password set to '" + mNew + "'");
Pavel Grafova182b9c2019-10-16 14:43:46 +0100248 return true;
Jorim Jaggi2fef6f72016-11-01 19:06:25 -0700249 }
250
Pavel Grafova182b9c2019-10-16 14:43:46 +0100251 private boolean runSetPin() {
252 final LockscreenCredential pin = LockscreenCredential.createPin(mNew);
253 if (!isNewCredentialSufficient(pin)) {
254 return false;
255 }
256 mLockPatternUtils.setLockCredential(pin, getOldCredential(), mCurrentUserId);
Jorim Jaggi2fef6f72016-11-01 19:06:25 -0700257 getOutPrintWriter().println("Pin set to '" + mNew + "'");
Pavel Grafova182b9c2019-10-16 14:43:46 +0100258 return true;
Jorim Jaggi2fef6f72016-11-01 19:06:25 -0700259 }
260
Pavel Grafova182b9c2019-10-16 14:43:46 +0100261 private boolean runClear() {
262 LockscreenCredential none = LockscreenCredential.createNone();
263 if (!isNewCredentialSufficient(none)) {
264 return false;
265 }
266 mLockPatternUtils.setLockCredential(none, getOldCredential(), mCurrentUserId);
Jorim Jaggi2fef6f72016-11-01 19:06:25 -0700267 getOutPrintWriter().println("Lock credential cleared");
Pavel Grafova182b9c2019-10-16 14:43:46 +0100268 return true;
269 }
270
271 private boolean isNewCredentialSufficient(LockscreenCredential credential) {
272 final PasswordMetrics requiredMetrics =
273 mLockPatternUtils.getRequestedPasswordMetrics(mCurrentUserId);
274 final List<PasswordValidationError> errors;
275 if (credential.isPassword() || credential.isPin()) {
276 errors = PasswordMetrics.validatePassword(requiredMetrics, PASSWORD_COMPLEXITY_NONE,
277 credential.isPin(), credential.getCredential());
278 } else {
279 PasswordMetrics metrics = new PasswordMetrics(
280 credential.isPattern() ? CREDENTIAL_TYPE_PATTERN : CREDENTIAL_TYPE_NONE);
281 errors = PasswordMetrics.validatePasswordMetrics(
282 requiredMetrics, PASSWORD_COMPLEXITY_NONE, false /* isPin */, metrics);
283 }
284 if (!errors.isEmpty()) {
285 getOutPrintWriter().println(
286 "New credential doesn't satisfy admin policies: " + errors.get(0));
287 return false;
288 }
289 return true;
Jorim Jaggi2fef6f72016-11-01 19:06:25 -0700290 }
291
Felipe Leme1fc9c812018-07-11 10:02:23 -0700292 private void runSetDisabled() {
Jeff Sharkey65440992017-03-31 09:45:46 -0600293 final boolean disabled = Boolean.parseBoolean(mNew);
294 mLockPatternUtils.setLockScreenDisabled(disabled, mCurrentUserId);
295 getOutPrintWriter().println("Lock screen disabled set to " + disabled);
296 }
297
chaviw5953e662017-08-21 10:46:11 -0700298 private void runGetDisabled() {
299 boolean isLockScreenDisabled = mLockPatternUtils.isLockScreenDisabled(mCurrentUserId);
300 getOutPrintWriter().println(isLockScreenDisabled);
301 }
302
Felipe Leme1fc9c812018-07-11 10:02:23 -0700303 private boolean checkCredential() {
Rubin Xu5e891bc2019-10-14 10:22:23 +0100304 if (mLockPatternUtils.isSecure(mCurrentUserId)) {
Pavel Grafovfc135c72017-07-25 16:38:04 +0100305 if (mLockPatternUtils.isManagedProfileWithUnifiedChallenge(mCurrentUserId)) {
306 getOutPrintWriter().println("Profile uses unified challenge");
307 return false;
308 }
309
Pavel Grafovc07067d2017-07-05 16:30:04 +0100310 try {
Rubin Xua58125d2019-09-06 20:11:48 +0100311 final boolean result = mLockPatternUtils.checkCredential(getOldCredential(),
312 mCurrentUserId, null);
Pavel Grafovc07067d2017-07-05 16:30:04 +0100313 if (!result) {
Pavel Grafovb3191252017-07-14 12:30:31 +0100314 if (!mLockPatternUtils.isManagedProfileWithUnifiedChallenge(mCurrentUserId)) {
315 mLockPatternUtils.reportFailedPasswordAttempt(mCurrentUserId);
316 }
Pavel Grafovc07067d2017-07-05 16:30:04 +0100317 getOutPrintWriter().println("Old password '" + mOld + "' didn't match");
Irina Dumitrescu472dae52018-07-19 18:07:58 +0100318 } else {
319 // Resets the counter for failed password attempts to 0.
320 mLockPatternUtils.reportSuccessfulPasswordAttempt(mCurrentUserId);
Pavel Grafovc07067d2017-07-05 16:30:04 +0100321 }
322 return result;
323 } catch (RequestThrottledException e) {
324 getOutPrintWriter().println("Request throttled");
Jorim Jaggi2fef6f72016-11-01 19:06:25 -0700325 return false;
326 }
327 } else {
Rubin Xuca7007b2019-03-25 11:44:41 +0000328 if (!mOld.isEmpty()) {
329 getOutPrintWriter().println("Old password provided but user has no password");
330 return false;
331 }
Jorim Jaggi2fef6f72016-11-01 19:06:25 -0700332 return true;
333 }
334 }
Rubin Xua3c71a12019-12-04 15:25:02 +0000335
336 private void runRemoveCache() {
337 mLockPatternUtils.removeCachedUnifiedChallenge(mCurrentUserId);
338 getOutPrintWriter().println("Password cached removed for user " + mCurrentUserId);
339 }
Jorim Jaggi2fef6f72016-11-01 19:06:25 -0700340}