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