1 /* 2 * Copyright (C) 2020 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 17 package com.android.server.locksettings; 18 19 import static android.os.UserHandle.USER_SYSTEM; 20 21 import android.annotation.NonNull; 22 import android.annotation.Nullable; 23 import android.annotation.UserIdInt; 24 import android.content.Context; 25 import android.content.pm.UserInfo; 26 import android.hardware.rebootescrow.IRebootEscrow; 27 import android.os.RemoteException; 28 import android.os.ServiceManager; 29 import android.os.ServiceSpecificException; 30 import android.os.SystemClock; 31 import android.os.UserManager; 32 import android.provider.Settings; 33 import android.util.Slog; 34 35 import com.android.internal.annotations.GuardedBy; 36 import com.android.internal.annotations.VisibleForTesting; 37 import com.android.internal.util.FrameworkStatsLog; 38 import com.android.internal.util.IndentingPrintWriter; 39 import com.android.internal.widget.RebootEscrowListener; 40 41 import java.io.IOException; 42 import java.text.SimpleDateFormat; 43 import java.util.ArrayList; 44 import java.util.Date; 45 import java.util.List; 46 import java.util.Locale; 47 import java.util.NoSuchElementException; 48 49 class RebootEscrowManager { 50 private static final String TAG = "RebootEscrowManager"; 51 52 /** 53 * Used in the database storage to indicate the boot count at which the reboot escrow was 54 * previously armed. 55 */ 56 @VisibleForTesting 57 public static final String REBOOT_ESCROW_ARMED_KEY = "reboot_escrow_armed_count"; 58 59 /** 60 * Number of boots until we consider the escrow data to be stale for the purposes of metrics. 61 * <p> 62 * If the delta between the current boot number and the boot number stored when the mechanism 63 * was armed is under this number and the escrow mechanism fails, we report it as a failure of 64 * the mechanism. 65 * <p> 66 * If the delta over this number and escrow fails, we will not report the metric as failed 67 * since there most likely was some other issue if the device rebooted several times before 68 * getting to the escrow restore code. 69 */ 70 private static final int BOOT_COUNT_TOLERANCE = 5; 71 72 /** 73 * Logs events for later debugging in bugreports. 74 */ 75 private final RebootEscrowEventLog mEventLog; 76 77 /** 78 * Used to track when the reboot escrow is wanted. Should stay true once escrow is requested 79 * unless clearRebootEscrow is called. This will allow all the active users to be unlocked 80 * after reboot. 81 */ 82 private boolean mRebootEscrowWanted; 83 84 /** Used to track when reboot escrow is ready. */ 85 private boolean mRebootEscrowReady; 86 87 /** Notified when mRebootEscrowReady changes. */ 88 private RebootEscrowListener mRebootEscrowListener; 89 90 /** 91 * Hold this lock when checking or generating the reboot escrow key. 92 */ 93 private final Object mKeyGenerationLock = new Object(); 94 95 /** 96 * Stores the reboot escrow data between when it's supplied and when 97 * {@link #armRebootEscrowIfNeeded()} is called. 98 */ 99 @GuardedBy("mKeyGenerationLock") 100 private RebootEscrowKey mPendingRebootEscrowKey; 101 102 private final UserManager mUserManager; 103 104 private final Injector mInjector; 105 106 private final LockSettingsStorage mStorage; 107 108 private final Callbacks mCallbacks; 109 110 interface Callbacks { isUserSecure(int userId)111 boolean isUserSecure(int userId); 112 onRebootEscrowRestored(byte spVersion, byte[] syntheticPassword, int userId)113 void onRebootEscrowRestored(byte spVersion, byte[] syntheticPassword, int userId); 114 } 115 116 static class Injector { 117 protected Context mContext; 118 Injector(Context context)119 Injector(Context context) { 120 mContext = context; 121 } 122 getContext()123 public Context getContext() { 124 return mContext; 125 } 126 getUserManager()127 public UserManager getUserManager() { 128 return (UserManager) mContext.getSystemService(Context.USER_SERVICE); 129 } 130 131 @Nullable getRebootEscrow()132 public IRebootEscrow getRebootEscrow() { 133 try { 134 return IRebootEscrow.Stub.asInterface(ServiceManager.getService( 135 "android.hardware.rebootescrow.IRebootEscrow/default")); 136 } catch (NoSuchElementException e) { 137 Slog.i(TAG, "Device doesn't implement RebootEscrow HAL"); 138 } 139 return null; 140 } 141 getBootCount()142 public int getBootCount() { 143 return Settings.Global.getInt(mContext.getContentResolver(), Settings.Global.BOOT_COUNT, 144 0); 145 } 146 reportMetric(boolean success)147 public void reportMetric(boolean success) { 148 FrameworkStatsLog.write(FrameworkStatsLog.REBOOT_ESCROW_RECOVERY_REPORTED, success); 149 } 150 getEventLog()151 public RebootEscrowEventLog getEventLog() { 152 return new RebootEscrowEventLog(); 153 } 154 } 155 RebootEscrowManager(Context context, Callbacks callbacks, LockSettingsStorage storage)156 RebootEscrowManager(Context context, Callbacks callbacks, LockSettingsStorage storage) { 157 this(new Injector(context), callbacks, storage); 158 } 159 160 @VisibleForTesting RebootEscrowManager(Injector injector, Callbacks callbacks, LockSettingsStorage storage)161 RebootEscrowManager(Injector injector, Callbacks callbacks, 162 LockSettingsStorage storage) { 163 mInjector = injector; 164 mCallbacks = callbacks; 165 mStorage = storage; 166 mUserManager = injector.getUserManager(); 167 mEventLog = injector.getEventLog(); 168 } 169 loadRebootEscrowDataIfAvailable()170 void loadRebootEscrowDataIfAvailable() { 171 List<UserInfo> users = mUserManager.getUsers(); 172 List<UserInfo> rebootEscrowUsers = new ArrayList<>(); 173 for (UserInfo user : users) { 174 if (mCallbacks.isUserSecure(user.id) && mStorage.hasRebootEscrow(user.id)) { 175 rebootEscrowUsers.add(user); 176 } 177 } 178 179 if (rebootEscrowUsers.isEmpty()) { 180 return; 181 } 182 183 RebootEscrowKey escrowKey = getAndClearRebootEscrowKey(); 184 if (escrowKey == null) { 185 Slog.w(TAG, "Had reboot escrow data for users, but no key; removing escrow storage."); 186 for (UserInfo user : users) { 187 mStorage.removeRebootEscrow(user.id); 188 } 189 onEscrowRestoreComplete(false); 190 return; 191 } 192 193 mEventLog.addEntry(RebootEscrowEvent.FOUND_ESCROW_DATA); 194 195 boolean allUsersUnlocked = true; 196 for (UserInfo user : rebootEscrowUsers) { 197 allUsersUnlocked &= restoreRebootEscrowForUser(user.id, escrowKey); 198 } 199 onEscrowRestoreComplete(allUsersUnlocked); 200 } 201 onEscrowRestoreComplete(boolean success)202 private void onEscrowRestoreComplete(boolean success) { 203 int previousBootCount = mStorage.getInt(REBOOT_ESCROW_ARMED_KEY, -1, USER_SYSTEM); 204 mStorage.removeKey(REBOOT_ESCROW_ARMED_KEY, USER_SYSTEM); 205 206 int bootCountDelta = mInjector.getBootCount() - previousBootCount; 207 if (success || (previousBootCount != -1 && bootCountDelta <= BOOT_COUNT_TOLERANCE)) { 208 mInjector.reportMetric(success); 209 } 210 } 211 getAndClearRebootEscrowKey()212 private RebootEscrowKey getAndClearRebootEscrowKey() { 213 IRebootEscrow rebootEscrow = mInjector.getRebootEscrow(); 214 if (rebootEscrow == null) { 215 Slog.w(TAG, "Had reboot escrow data for users, but RebootEscrow HAL is unavailable"); 216 return null; 217 } 218 219 try { 220 byte[] escrowKeyBytes = rebootEscrow.retrieveKey(); 221 if (escrowKeyBytes == null) { 222 Slog.w(TAG, "Had reboot escrow data for users, but could not retrieve key"); 223 return null; 224 } else if (escrowKeyBytes.length != 32) { 225 Slog.e(TAG, "IRebootEscrow returned key of incorrect size " 226 + escrowKeyBytes.length); 227 return null; 228 } 229 230 // Make sure we didn't get the null key. 231 int zero = 0; 232 for (int i = 0; i < escrowKeyBytes.length; i++) { 233 zero |= escrowKeyBytes[i]; 234 } 235 if (zero == 0) { 236 Slog.w(TAG, "IRebootEscrow returned an all-zeroes key"); 237 return null; 238 } 239 240 // Overwrite the existing key with the null key 241 rebootEscrow.storeKey(new byte[32]); 242 243 mEventLog.addEntry(RebootEscrowEvent.RETRIEVED_STORED_KEK); 244 return RebootEscrowKey.fromKeyBytes(escrowKeyBytes); 245 } catch (RemoteException e) { 246 Slog.w(TAG, "Could not retrieve escrow data"); 247 return null; 248 } catch (ServiceSpecificException e) { 249 Slog.w(TAG, "Got service-specific exception: " + e.errorCode); 250 return null; 251 } 252 } 253 restoreRebootEscrowForUser(@serIdInt int userId, RebootEscrowKey key)254 private boolean restoreRebootEscrowForUser(@UserIdInt int userId, RebootEscrowKey key) { 255 if (!mStorage.hasRebootEscrow(userId)) { 256 return false; 257 } 258 259 try { 260 byte[] blob = mStorage.readRebootEscrow(userId); 261 mStorage.removeRebootEscrow(userId); 262 263 RebootEscrowData escrowData = RebootEscrowData.fromEncryptedData(key, blob); 264 265 mCallbacks.onRebootEscrowRestored(escrowData.getSpVersion(), 266 escrowData.getSyntheticPassword(), userId); 267 Slog.i(TAG, "Restored reboot escrow data for user " + userId); 268 mEventLog.addEntry(RebootEscrowEvent.RETRIEVED_LSKF_FOR_USER, userId); 269 return true; 270 } catch (IOException e) { 271 Slog.w(TAG, "Could not load reboot escrow data for user " + userId, e); 272 return false; 273 } 274 } 275 callToRebootEscrowIfNeeded(@serIdInt int userId, byte spVersion, byte[] syntheticPassword)276 void callToRebootEscrowIfNeeded(@UserIdInt int userId, byte spVersion, 277 byte[] syntheticPassword) { 278 if (!mRebootEscrowWanted) { 279 return; 280 } 281 282 IRebootEscrow rebootEscrow = mInjector.getRebootEscrow(); 283 if (rebootEscrow == null) { 284 Slog.w(TAG, "Reboot escrow requested, but RebootEscrow HAL is unavailable"); 285 return; 286 } 287 288 RebootEscrowKey escrowKey = generateEscrowKeyIfNeeded(); 289 if (escrowKey == null) { 290 Slog.e(TAG, "Could not generate escrow key"); 291 return; 292 } 293 294 final RebootEscrowData escrowData; 295 try { 296 escrowData = RebootEscrowData.fromSyntheticPassword(escrowKey, spVersion, 297 syntheticPassword); 298 } catch (IOException e) { 299 setRebootEscrowReady(false); 300 Slog.w(TAG, "Could not escrow reboot data", e); 301 return; 302 } 303 304 mStorage.writeRebootEscrow(userId, escrowData.getBlob()); 305 mEventLog.addEntry(RebootEscrowEvent.STORED_LSKF_FOR_USER, userId); 306 307 setRebootEscrowReady(true); 308 } 309 generateEscrowKeyIfNeeded()310 private RebootEscrowKey generateEscrowKeyIfNeeded() { 311 synchronized (mKeyGenerationLock) { 312 if (mPendingRebootEscrowKey != null) { 313 return mPendingRebootEscrowKey; 314 } 315 316 RebootEscrowKey key; 317 try { 318 key = RebootEscrowKey.generate(); 319 } catch (IOException e) { 320 Slog.w(TAG, "Could not generate reboot escrow key"); 321 return null; 322 } 323 324 mPendingRebootEscrowKey = key; 325 return key; 326 } 327 } 328 clearRebootEscrowIfNeeded()329 private void clearRebootEscrowIfNeeded() { 330 mRebootEscrowWanted = false; 331 setRebootEscrowReady(false); 332 333 IRebootEscrow rebootEscrow = mInjector.getRebootEscrow(); 334 if (rebootEscrow == null) { 335 return; 336 } 337 338 mStorage.removeKey(REBOOT_ESCROW_ARMED_KEY, USER_SYSTEM); 339 340 try { 341 rebootEscrow.storeKey(new byte[32]); 342 } catch (RemoteException | ServiceSpecificException e) { 343 Slog.w(TAG, "Could not call RebootEscrow HAL to shred key"); 344 } 345 346 List<UserInfo> users = mUserManager.getUsers(); 347 for (UserInfo user : users) { 348 mStorage.removeRebootEscrow(user.id); 349 } 350 351 mEventLog.addEntry(RebootEscrowEvent.CLEARED_LSKF_REQUEST); 352 } 353 armRebootEscrowIfNeeded()354 boolean armRebootEscrowIfNeeded() { 355 if (!mRebootEscrowReady) { 356 return false; 357 } 358 359 IRebootEscrow rebootEscrow = mInjector.getRebootEscrow(); 360 if (rebootEscrow == null) { 361 Slog.w(TAG, "Escrow marked as ready, but RebootEscrow HAL is unavailable"); 362 return false; 363 } 364 365 RebootEscrowKey escrowKey; 366 synchronized (mKeyGenerationLock) { 367 escrowKey = mPendingRebootEscrowKey; 368 } 369 370 if (escrowKey == null) { 371 Slog.e(TAG, "Escrow key is null, but escrow was marked as ready"); 372 return false; 373 } 374 375 boolean armedRebootEscrow = false; 376 try { 377 rebootEscrow.storeKey(escrowKey.getKeyBytes()); 378 armedRebootEscrow = true; 379 Slog.i(TAG, "Reboot escrow key stored with RebootEscrow HAL"); 380 } catch (RemoteException | ServiceSpecificException e) { 381 Slog.e(TAG, "Failed escrow secret to RebootEscrow HAL", e); 382 } 383 384 if (armedRebootEscrow) { 385 mStorage.setInt(REBOOT_ESCROW_ARMED_KEY, mInjector.getBootCount(), USER_SYSTEM); 386 mEventLog.addEntry(RebootEscrowEvent.SET_ARMED_STATUS); 387 } 388 389 return armedRebootEscrow; 390 } 391 setRebootEscrowReady(boolean ready)392 private void setRebootEscrowReady(boolean ready) { 393 if (mRebootEscrowReady != ready) { 394 mRebootEscrowListener.onPreparedForReboot(ready); 395 } 396 mRebootEscrowReady = ready; 397 } 398 prepareRebootEscrow()399 boolean prepareRebootEscrow() { 400 if (mInjector.getRebootEscrow() == null) { 401 return false; 402 } 403 404 clearRebootEscrowIfNeeded(); 405 mRebootEscrowWanted = true; 406 mEventLog.addEntry(RebootEscrowEvent.REQUESTED_LSKF); 407 return true; 408 } 409 clearRebootEscrow()410 boolean clearRebootEscrow() { 411 if (mInjector.getRebootEscrow() == null) { 412 return false; 413 } 414 415 clearRebootEscrowIfNeeded(); 416 return true; 417 } 418 setRebootEscrowListener(RebootEscrowListener listener)419 void setRebootEscrowListener(RebootEscrowListener listener) { 420 mRebootEscrowListener = listener; 421 } 422 423 @VisibleForTesting 424 public static class RebootEscrowEvent { 425 static final int FOUND_ESCROW_DATA = 1; 426 static final int SET_ARMED_STATUS = 2; 427 static final int CLEARED_LSKF_REQUEST = 3; 428 static final int RETRIEVED_STORED_KEK = 4; 429 static final int REQUESTED_LSKF = 5; 430 static final int STORED_LSKF_FOR_USER = 6; 431 static final int RETRIEVED_LSKF_FOR_USER = 7; 432 433 final int mEventId; 434 final Integer mUserId; 435 final long mWallTime; 436 final long mTimestamp; 437 RebootEscrowEvent(int eventId)438 RebootEscrowEvent(int eventId) { 439 this(eventId, null); 440 } 441 RebootEscrowEvent(int eventId, Integer userId)442 RebootEscrowEvent(int eventId, Integer userId) { 443 mEventId = eventId; 444 mUserId = userId; 445 mTimestamp = SystemClock.uptimeMillis(); 446 mWallTime = System.currentTimeMillis(); 447 } 448 getEventDescription()449 String getEventDescription() { 450 switch (mEventId) { 451 case FOUND_ESCROW_DATA: 452 return "Found escrow data"; 453 case SET_ARMED_STATUS: 454 return "Set armed status"; 455 case CLEARED_LSKF_REQUEST: 456 return "Cleared request for LSKF"; 457 case RETRIEVED_STORED_KEK: 458 return "Retrieved stored KEK"; 459 case REQUESTED_LSKF: 460 return "Requested LSKF"; 461 case STORED_LSKF_FOR_USER: 462 return "Stored LSKF for user"; 463 case RETRIEVED_LSKF_FOR_USER: 464 return "Retrieved LSKF for user"; 465 default: 466 return "Unknown event ID " + mEventId; 467 } 468 } 469 } 470 471 @VisibleForTesting 472 public static class RebootEscrowEventLog { 473 private RebootEscrowEvent[] mEntries = new RebootEscrowEvent[16]; 474 private int mNextIndex = 0; 475 addEntry(int eventId)476 void addEntry(int eventId) { 477 addEntryInternal(new RebootEscrowEvent(eventId)); 478 } 479 addEntry(int eventId, int userId)480 void addEntry(int eventId, int userId) { 481 addEntryInternal(new RebootEscrowEvent(eventId, userId)); 482 } 483 addEntryInternal(RebootEscrowEvent event)484 private void addEntryInternal(RebootEscrowEvent event) { 485 final int index = mNextIndex; 486 mEntries[index] = event; 487 mNextIndex = (mNextIndex + 1) % mEntries.length; 488 } 489 dump(@onNull IndentingPrintWriter pw)490 void dump(@NonNull IndentingPrintWriter pw) { 491 SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS", Locale.US); 492 493 for (int i = 0; i < mEntries.length; ++i) { 494 RebootEscrowEvent event = mEntries[(i + mNextIndex) % mEntries.length]; 495 if (event == null) { 496 continue; 497 } 498 499 pw.print("Event #"); 500 pw.println(i); 501 502 pw.println(" time=" + sdf.format(new Date(event.mWallTime)) 503 + " (timestamp=" + event.mTimestamp + ")"); 504 505 pw.print(" event="); 506 pw.println(event.getEventDescription()); 507 508 if (event.mUserId != null) { 509 pw.print(" user="); 510 pw.println(event.mUserId); 511 } 512 } 513 } 514 } 515 dump(@onNull IndentingPrintWriter pw)516 void dump(@NonNull IndentingPrintWriter pw) { 517 pw.print("mRebootEscrowWanted="); 518 pw.println(mRebootEscrowWanted); 519 520 pw.print("mRebootEscrowReady="); 521 pw.println(mRebootEscrowReady); 522 523 pw.print("mRebootEscrowListener="); 524 pw.println(mRebootEscrowListener); 525 526 boolean keySet; 527 synchronized (mKeyGenerationLock) { 528 keySet = mPendingRebootEscrowKey != null; 529 } 530 531 pw.print("mPendingRebootEscrowKey is "); 532 pw.println(keySet ? "set" : "not set"); 533 534 pw.println(); 535 pw.println("Event log:"); 536 pw.increaseIndent(); 537 mEventLog.dump(pw); 538 pw.println(); 539 pw.decreaseIndent(); 540 } 541 } 542