• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2014 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;
18 
19 import android.Manifest;
20 import android.app.ActivityManager;
21 import android.content.Context;
22 import android.content.pm.PackageManager;
23 import android.os.Binder;
24 import android.os.IBinder;
25 import android.os.RemoteException;
26 import android.os.SystemProperties;
27 import android.os.UserHandle;
28 import android.os.UserManager;
29 import android.provider.Settings;
30 import android.service.persistentdata.IPersistentDataBlockService;
31 import android.service.persistentdata.PersistentDataBlockManager;
32 import android.util.Slog;
33 
34 import com.android.internal.R;
35 
36 import libcore.io.IoUtils;
37 
38 import java.io.DataInputStream;
39 import java.io.DataOutputStream;
40 import java.io.File;
41 import java.io.FileInputStream;
42 import java.io.FileNotFoundException;
43 import java.io.FileOutputStream;
44 import java.io.IOException;
45 import java.nio.ByteBuffer;
46 import java.nio.channels.FileChannel;
47 import java.security.MessageDigest;
48 import java.security.NoSuchAlgorithmException;
49 import java.util.Arrays;
50 
51 /**
52  * Service for reading and writing blocks to a persistent partition.
53  * This data will live across factory resets not initiated via the Settings UI.
54  * When a device is factory reset through Settings this data is wiped.
55  *
56  * Allows writing one block at a time. Namely, each time
57  * {@link android.service.persistentdata.IPersistentDataBlockService}.write(byte[] data)
58  * is called, it will overwite the data that was previously written on the block.
59  *
60  * Clients can query the size of the currently written block via
61  * {@link android.service.persistentdata.IPersistentDataBlockService}.getTotalDataSize().
62  *
63  * Clients can any number of bytes from the currently written block up to its total size by invoking
64  * {@link android.service.persistentdata.IPersistentDataBlockService}.read(byte[] data)
65  */
66 public class PersistentDataBlockService extends SystemService {
67     private static final String TAG = PersistentDataBlockService.class.getSimpleName();
68 
69     private static final String PERSISTENT_DATA_BLOCK_PROP = "ro.frp.pst";
70     private static final int HEADER_SIZE = 8;
71     // Magic number to mark block device as adhering to the format consumed by this service
72     private static final int PARTITION_TYPE_MARKER = 0x19901873;
73     // Limit to 100k as blocks larger than this might cause strain on Binder.
74     private static final int MAX_DATA_BLOCK_SIZE = 1024 * 100;
75     public static final int DIGEST_SIZE_BYTES = 32;
76     private static final String OEM_UNLOCK_PROP = "sys.oem_unlock_allowed";
77     private static final String FLASH_LOCK_PROP = "ro.boot.flash.locked";
78     private static final String FLASH_LOCK_LOCKED = "1";
79     private static final String FLASH_LOCK_UNLOCKED = "0";
80 
81     private final Context mContext;
82     private final String mDataBlockFile;
83     private final Object mLock = new Object();
84 
85     private int mAllowedUid = -1;
86     private long mBlockDeviceSize;
87 
PersistentDataBlockService(Context context)88     public PersistentDataBlockService(Context context) {
89         super(context);
90         mContext = context;
91         mDataBlockFile = SystemProperties.get(PERSISTENT_DATA_BLOCK_PROP);
92         mBlockDeviceSize = -1; // Load lazily
93         mAllowedUid = getAllowedUid(UserHandle.USER_SYSTEM);
94     }
95 
getAllowedUid(int userHandle)96     private int getAllowedUid(int userHandle) {
97         String allowedPackage = mContext.getResources()
98                 .getString(R.string.config_persistentDataPackageName);
99         PackageManager pm = mContext.getPackageManager();
100         int allowedUid = -1;
101         try {
102             allowedUid = pm.getPackageUidAsUser(allowedPackage,
103                     PackageManager.MATCH_SYSTEM_ONLY, userHandle);
104         } catch (PackageManager.NameNotFoundException e) {
105             // not expected
106             Slog.e(TAG, "not able to find package " + allowedPackage, e);
107         }
108         return allowedUid;
109     }
110 
111     @Override
onStart()112     public void onStart() {
113         enforceChecksumValidity();
114         formatIfOemUnlockEnabled();
115         publishBinderService(Context.PERSISTENT_DATA_BLOCK_SERVICE, mService);
116     }
117 
formatIfOemUnlockEnabled()118     private void formatIfOemUnlockEnabled() {
119         boolean enabled = doGetOemUnlockEnabled();
120         if (enabled) {
121             synchronized (mLock) {
122                 formatPartitionLocked(true);
123             }
124         }
125 
126         SystemProperties.set(OEM_UNLOCK_PROP, enabled ? "1" : "0");
127     }
128 
enforceOemUnlockReadPermission()129     private void enforceOemUnlockReadPermission() {
130         if (mContext.checkCallingOrSelfPermission(Manifest.permission.READ_OEM_UNLOCK_STATE)
131                 == PackageManager.PERMISSION_DENIED
132                 && mContext.checkCallingOrSelfPermission(Manifest.permission.OEM_UNLOCK_STATE)
133                 == PackageManager.PERMISSION_DENIED) {
134             throw new SecurityException("Can't access OEM unlock state. Requires "
135                     + "READ_OEM_UNLOCK_STATE or OEM_UNLOCK_STATE permission.");
136         }
137     }
138 
enforceOemUnlockWritePermission()139     private void enforceOemUnlockWritePermission() {
140         mContext.enforceCallingOrSelfPermission(
141                 Manifest.permission.OEM_UNLOCK_STATE,
142                 "Can't modify OEM unlock state");
143     }
144 
enforceUid(int callingUid)145     private void enforceUid(int callingUid) {
146         if (callingUid != mAllowedUid) {
147             throw new SecurityException("uid " + callingUid + " not allowed to access PST");
148         }
149     }
150 
enforceIsAdmin()151     private void enforceIsAdmin() {
152         final int userId = UserHandle.getCallingUserId();
153         final boolean isAdmin = UserManager.get(mContext).isUserAdmin(userId);
154         if (!isAdmin) {
155             throw new SecurityException(
156                     "Only the Admin user is allowed to change OEM unlock state");
157         }
158     }
159 
enforceUserRestriction(String userRestriction)160     private void enforceUserRestriction(String userRestriction) {
161         if (UserManager.get(mContext).hasUserRestriction(userRestriction)) {
162             throw new SecurityException(
163                     "OEM unlock is disallowed by user restriction: " + userRestriction);
164         }
165     }
166 
getTotalDataSizeLocked(DataInputStream inputStream)167     private int getTotalDataSizeLocked(DataInputStream inputStream) throws IOException {
168         // skip over checksum
169         inputStream.skipBytes(DIGEST_SIZE_BYTES);
170 
171         int totalDataSize;
172         int blockId = inputStream.readInt();
173         if (blockId == PARTITION_TYPE_MARKER) {
174             totalDataSize = inputStream.readInt();
175         } else {
176             totalDataSize = 0;
177         }
178         return totalDataSize;
179     }
180 
getBlockDeviceSize()181     private long getBlockDeviceSize() {
182         synchronized (mLock) {
183             if (mBlockDeviceSize == -1) {
184                 mBlockDeviceSize = nativeGetBlockDeviceSize(mDataBlockFile);
185             }
186         }
187 
188         return mBlockDeviceSize;
189     }
190 
enforceChecksumValidity()191     private boolean enforceChecksumValidity() {
192         byte[] storedDigest = new byte[DIGEST_SIZE_BYTES];
193 
194         synchronized (mLock) {
195             byte[] digest = computeDigestLocked(storedDigest);
196             if (digest == null || !Arrays.equals(storedDigest, digest)) {
197                 Slog.i(TAG, "Formatting FRP partition...");
198                 formatPartitionLocked(false);
199                 return false;
200             }
201         }
202 
203         return true;
204     }
205 
computeAndWriteDigestLocked()206     private boolean computeAndWriteDigestLocked() {
207         byte[] digest = computeDigestLocked(null);
208         if (digest != null) {
209             DataOutputStream outputStream;
210             try {
211                 outputStream = new DataOutputStream(
212                         new FileOutputStream(new File(mDataBlockFile)));
213             } catch (FileNotFoundException e) {
214                 Slog.e(TAG, "partition not available?", e);
215                 return false;
216             }
217 
218             try {
219                 outputStream.write(digest, 0, DIGEST_SIZE_BYTES);
220                 outputStream.flush();
221             } catch (IOException e) {
222                 Slog.e(TAG, "failed to write block checksum", e);
223                 return false;
224             } finally {
225                 IoUtils.closeQuietly(outputStream);
226             }
227             return true;
228         } else {
229             return false;
230         }
231     }
232 
computeDigestLocked(byte[] storedDigest)233     private byte[] computeDigestLocked(byte[] storedDigest) {
234         DataInputStream inputStream;
235         try {
236             inputStream = new DataInputStream(new FileInputStream(new File(mDataBlockFile)));
237         } catch (FileNotFoundException e) {
238             Slog.e(TAG, "partition not available?", e);
239             return null;
240         }
241 
242         MessageDigest md;
243         try {
244             md = MessageDigest.getInstance("SHA-256");
245         } catch (NoSuchAlgorithmException e) {
246             // won't ever happen -- every implementation is required to support SHA-256
247             Slog.e(TAG, "SHA-256 not supported?", e);
248             IoUtils.closeQuietly(inputStream);
249             return null;
250         }
251 
252         try {
253             if (storedDigest != null && storedDigest.length == DIGEST_SIZE_BYTES) {
254                 inputStream.read(storedDigest);
255             } else {
256                 inputStream.skipBytes(DIGEST_SIZE_BYTES);
257             }
258 
259             int read;
260             byte[] data = new byte[1024];
261             md.update(data, 0, DIGEST_SIZE_BYTES); // include 0 checksum in digest
262             while ((read = inputStream.read(data)) != -1) {
263                 md.update(data, 0, read);
264             }
265         } catch (IOException e) {
266             Slog.e(TAG, "failed to read partition", e);
267             return null;
268         } finally {
269             IoUtils.closeQuietly(inputStream);
270         }
271 
272         return md.digest();
273     }
274 
formatPartitionLocked(boolean setOemUnlockEnabled)275     private void formatPartitionLocked(boolean setOemUnlockEnabled) {
276         DataOutputStream outputStream;
277         try {
278             outputStream = new DataOutputStream(new FileOutputStream(new File(mDataBlockFile)));
279         } catch (FileNotFoundException e) {
280             Slog.e(TAG, "partition not available?", e);
281             return;
282         }
283 
284         byte[] data = new byte[DIGEST_SIZE_BYTES];
285         try {
286             outputStream.write(data, 0, DIGEST_SIZE_BYTES);
287             outputStream.writeInt(PARTITION_TYPE_MARKER);
288             outputStream.writeInt(0); // data size
289             outputStream.flush();
290         } catch (IOException e) {
291             Slog.e(TAG, "failed to format block", e);
292             return;
293         } finally {
294             IoUtils.closeQuietly(outputStream);
295         }
296 
297         doSetOemUnlockEnabledLocked(setOemUnlockEnabled);
298         computeAndWriteDigestLocked();
299     }
300 
doSetOemUnlockEnabledLocked(boolean enabled)301     private void doSetOemUnlockEnabledLocked(boolean enabled) {
302         FileOutputStream outputStream;
303         try {
304             outputStream = new FileOutputStream(new File(mDataBlockFile));
305         } catch (FileNotFoundException e) {
306             Slog.e(TAG, "partition not available", e);
307             return;
308         }
309 
310         try {
311             FileChannel channel = outputStream.getChannel();
312 
313             channel.position(getBlockDeviceSize() - 1);
314 
315             ByteBuffer data = ByteBuffer.allocate(1);
316             data.put(enabled ? (byte) 1 : (byte) 0);
317             data.flip();
318             channel.write(data);
319             outputStream.flush();
320         } catch (IOException e) {
321             Slog.e(TAG, "unable to access persistent partition", e);
322             return;
323         } finally {
324             SystemProperties.set(OEM_UNLOCK_PROP, enabled ? "1" : "0");
325             IoUtils.closeQuietly(outputStream);
326         }
327     }
328 
doGetOemUnlockEnabled()329     private boolean doGetOemUnlockEnabled() {
330         DataInputStream inputStream;
331         try {
332             inputStream = new DataInputStream(new FileInputStream(new File(mDataBlockFile)));
333         } catch (FileNotFoundException e) {
334             Slog.e(TAG, "partition not available");
335             return false;
336         }
337 
338         try {
339             synchronized (mLock) {
340                 inputStream.skip(getBlockDeviceSize() - 1);
341                 return inputStream.readByte() != 0;
342             }
343         } catch (IOException e) {
344             Slog.e(TAG, "unable to access persistent partition", e);
345             return false;
346         } finally {
347             IoUtils.closeQuietly(inputStream);
348         }
349     }
350 
nativeGetBlockDeviceSize(String path)351     private native long nativeGetBlockDeviceSize(String path);
nativeWipe(String path)352     private native int nativeWipe(String path);
353 
354     private final IBinder mService = new IPersistentDataBlockService.Stub() {
355         @Override
356         public int write(byte[] data) throws RemoteException {
357             enforceUid(Binder.getCallingUid());
358 
359             // Need to ensure we don't write over the last byte
360             long maxBlockSize = getBlockDeviceSize() - HEADER_SIZE - 1;
361             if (data.length > maxBlockSize) {
362                 // partition is ~500k so shouldn't be a problem to downcast
363                 return (int) -maxBlockSize;
364             }
365 
366             DataOutputStream outputStream;
367             try {
368                 outputStream = new DataOutputStream(new FileOutputStream(new File(mDataBlockFile)));
369             } catch (FileNotFoundException e) {
370                 Slog.e(TAG, "partition not available?", e);
371                 return -1;
372             }
373 
374             ByteBuffer headerAndData = ByteBuffer.allocate(data.length + HEADER_SIZE);
375             headerAndData.putInt(PARTITION_TYPE_MARKER);
376             headerAndData.putInt(data.length);
377             headerAndData.put(data);
378 
379             synchronized (mLock) {
380                 try {
381                     byte[] checksum = new byte[DIGEST_SIZE_BYTES];
382                     outputStream.write(checksum, 0, DIGEST_SIZE_BYTES);
383                     outputStream.write(headerAndData.array());
384                     outputStream.flush();
385                 } catch (IOException e) {
386                     Slog.e(TAG, "failed writing to the persistent data block", e);
387                     return -1;
388                 } finally {
389                     IoUtils.closeQuietly(outputStream);
390                 }
391 
392                 if (computeAndWriteDigestLocked()) {
393                     return data.length;
394                 } else {
395                     return -1;
396                 }
397             }
398         }
399 
400         @Override
401         public byte[] read() {
402             enforceUid(Binder.getCallingUid());
403             if (!enforceChecksumValidity()) {
404                 return new byte[0];
405             }
406 
407             DataInputStream inputStream;
408             try {
409                 inputStream = new DataInputStream(new FileInputStream(new File(mDataBlockFile)));
410             } catch (FileNotFoundException e) {
411                 Slog.e(TAG, "partition not available?", e);
412                 return null;
413             }
414 
415             try {
416                 synchronized (mLock) {
417                     int totalDataSize = getTotalDataSizeLocked(inputStream);
418 
419                     if (totalDataSize == 0) {
420                         return new byte[0];
421                     }
422 
423                     byte[] data = new byte[totalDataSize];
424                     int read = inputStream.read(data, 0, totalDataSize);
425                     if (read < totalDataSize) {
426                         // something went wrong, not returning potentially corrupt data
427                         Slog.e(TAG, "failed to read entire data block. bytes read: " +
428                                 read + "/" + totalDataSize);
429                         return null;
430                     }
431                     return data;
432                 }
433             } catch (IOException e) {
434                 Slog.e(TAG, "failed to read data", e);
435                 return null;
436             } finally {
437                 try {
438                     inputStream.close();
439                 } catch (IOException e) {
440                     Slog.e(TAG, "failed to close OutputStream");
441                 }
442             }
443         }
444 
445         @Override
446         public void wipe() {
447             enforceOemUnlockWritePermission();
448 
449             synchronized (mLock) {
450                 int ret = nativeWipe(mDataBlockFile);
451 
452                 if (ret < 0) {
453                     Slog.e(TAG, "failed to wipe persistent partition");
454                 }
455             }
456         }
457 
458         @Override
459         public void setOemUnlockEnabled(boolean enabled) throws SecurityException {
460             // do not allow monkey to flip the flag
461             if (ActivityManager.isUserAMonkey()) {
462                 return;
463             }
464 
465             enforceOemUnlockWritePermission();
466             enforceIsAdmin();
467 
468             if (enabled) {
469                 // Do not allow oem unlock to be enabled if it's disallowed by a user restriction.
470                 enforceUserRestriction(UserManager.DISALLOW_OEM_UNLOCK);
471                 enforceUserRestriction(UserManager.DISALLOW_FACTORY_RESET);
472             }
473             synchronized (mLock) {
474                 doSetOemUnlockEnabledLocked(enabled);
475                 computeAndWriteDigestLocked();
476             }
477         }
478 
479         @Override
480         public boolean getOemUnlockEnabled() {
481             enforceOemUnlockReadPermission();
482             return doGetOemUnlockEnabled();
483         }
484 
485         @Override
486         public int getFlashLockState() {
487             enforceOemUnlockReadPermission();
488             String locked = SystemProperties.get(FLASH_LOCK_PROP);
489             switch (locked) {
490                 case FLASH_LOCK_LOCKED:
491                     return PersistentDataBlockManager.FLASH_LOCK_LOCKED;
492                 case FLASH_LOCK_UNLOCKED:
493                     return PersistentDataBlockManager.FLASH_LOCK_UNLOCKED;
494                 default:
495                     return PersistentDataBlockManager.FLASH_LOCK_UNKNOWN;
496             }
497         }
498 
499         @Override
500         public int getDataBlockSize() {
501             enforcePersistentDataBlockAccess();
502 
503             DataInputStream inputStream;
504             try {
505                 inputStream = new DataInputStream(new FileInputStream(new File(mDataBlockFile)));
506             } catch (FileNotFoundException e) {
507                 Slog.e(TAG, "partition not available");
508                 return 0;
509             }
510 
511             try {
512                 synchronized (mLock) {
513                     return getTotalDataSizeLocked(inputStream);
514                 }
515             } catch (IOException e) {
516                 Slog.e(TAG, "error reading data block size");
517                 return 0;
518             } finally {
519                 IoUtils.closeQuietly(inputStream);
520             }
521         }
522 
523         private void enforcePersistentDataBlockAccess() {
524             if (mContext.checkCallingPermission(Manifest.permission.ACCESS_PDB_STATE)
525                     != PackageManager.PERMISSION_GRANTED) {
526                 enforceUid(Binder.getCallingUid());
527             }
528         }
529 
530         @Override
531         public long getMaximumDataBlockSize() {
532             long actualSize = getBlockDeviceSize() - HEADER_SIZE - 1;
533             return actualSize <= MAX_DATA_BLOCK_SIZE ? actualSize : MAX_DATA_BLOCK_SIZE;
534         }
535     };
536 }
537