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