Merge "Add a checksum to FRP partition" into lmp-mr1-dev
diff --git a/services/core/java/com/android/server/PersistentDataBlockService.java b/services/core/java/com/android/server/PersistentDataBlockService.java
index de90aa2..17edb53 100644
--- a/services/core/java/com/android/server/PersistentDataBlockService.java
+++ b/services/core/java/com/android/server/PersistentDataBlockService.java
@@ -41,6 +41,9 @@
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.Arrays;
/**
* Service for reading and writing blocks to a persistent partition.
@@ -63,22 +66,16 @@
private static final String PERSISTENT_DATA_BLOCK_PROP = "ro.frp.pst";
private static final int HEADER_SIZE = 8;
// Magic number to mark block device as adhering to the format consumed by this service
- private static final int PARTITION_TYPE_MARKER = 0x1990;
+ private static final int PARTITION_TYPE_MARKER = 0x19901873;
// Limit to 100k as blocks larger than this might cause strain on Binder.
- // TODO(anmorales): Consider splitting up too-large blocks in PersistentDataBlockManager
private static final int MAX_DATA_BLOCK_SIZE = 1024 * 100;
+ public static final int DIGEST_SIZE_BYTES = 32;
private final Context mContext;
private final String mDataBlockFile;
private final Object mLock = new Object();
private int mAllowedUid = -1;
- /*
- * Separate lock for OEM unlock related operations as they can happen in parallel with regular
- * block operations.
- */
- private final Object mOemLock = new Object();
-
private long mBlockDeviceSize;
public PersistentDataBlockService(Context context) {
@@ -89,7 +86,6 @@
mAllowedUid = getAllowedUid(UserHandle.USER_OWNER);
}
-
private int getAllowedUid(int userHandle) {
String allowedPackage = mContext.getResources()
.getString(R.string.config_persistentDataPackageName);
@@ -106,6 +102,7 @@
@Override
public void onStart() {
+ enforceChecksumValidity();
publishBinderService(Context.PERSISTENT_DATA_BLOCK_SERVICE, mService);
}
@@ -128,6 +125,9 @@
}
private int getTotalDataSizeLocked(DataInputStream inputStream) throws IOException {
+ // skip over checksum
+ inputStream.skipBytes(DIGEST_SIZE_BYTES);
+
int totalDataSize;
int blockId = inputStream.readInt();
if (blockId == PARTITION_TYPE_MARKER) {
@@ -148,6 +148,143 @@
return mBlockDeviceSize;
}
+ private boolean enforceChecksumValidity() {
+ byte[] storedDigest = new byte[DIGEST_SIZE_BYTES];
+
+ synchronized (mLock) {
+ byte[] digest = computeDigestLocked(storedDigest);
+ if (digest == null || !Arrays.equals(storedDigest, digest)) {
+ Slog.i(TAG, "Formatting FRP partition...");
+ formatPartitionLocked();
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ private boolean computeAndWriteDigestLocked() {
+ byte[] digest = computeDigestLocked(null);
+ if (digest != null) {
+ DataOutputStream outputStream;
+ try {
+ outputStream = new DataOutputStream(
+ new FileOutputStream(new File(mDataBlockFile)));
+ } catch (FileNotFoundException e) {
+ Slog.e(TAG, "partition not available?", e);
+ return false;
+ }
+
+ try {
+ outputStream.write(digest, 0, DIGEST_SIZE_BYTES);
+ outputStream.flush();
+ } catch (IOException e) {
+ Slog.e(TAG, "failed to write block checksum", e);
+ return false;
+ } finally {
+ IoUtils.closeQuietly(outputStream);
+ }
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ private byte[] computeDigestLocked(byte[] storedDigest) {
+ DataInputStream inputStream;
+ try {
+ inputStream = new DataInputStream(new FileInputStream(new File(mDataBlockFile)));
+ } catch (FileNotFoundException e) {
+ Slog.e(TAG, "partition not available?", e);
+ return null;
+ }
+
+ MessageDigest md;
+ try {
+ md = MessageDigest.getInstance("SHA-256");
+ } catch (NoSuchAlgorithmException e) {
+ // won't ever happen -- every implementation is required to support SHA-256
+ Slog.e(TAG, "SHA-256 not supported?", e);
+ IoUtils.closeQuietly(inputStream);
+ return null;
+ }
+
+ try {
+ if (storedDigest != null && storedDigest.length == DIGEST_SIZE_BYTES) {
+ inputStream.read(storedDigest);
+ } else {
+ inputStream.skipBytes(DIGEST_SIZE_BYTES);
+ }
+
+ int read;
+ byte[] data = new byte[1024];
+ md.update(data, 0, DIGEST_SIZE_BYTES); // include 0 checksum in digest
+ while ((read = inputStream.read(data)) != -1) {
+ md.update(data, 0, read);
+ }
+ } catch (IOException e) {
+ Slog.e(TAG, "failed to read partition", e);
+ return null;
+ } finally {
+ IoUtils.closeQuietly(inputStream);
+ }
+
+ return md.digest();
+ }
+
+ private void formatPartitionLocked() {
+ DataOutputStream outputStream;
+ try {
+ outputStream = new DataOutputStream(new FileOutputStream(new File(mDataBlockFile)));
+ } catch (FileNotFoundException e) {
+ Slog.e(TAG, "partition not available?", e);
+ return;
+ }
+
+ byte[] data = new byte[DIGEST_SIZE_BYTES];
+ try {
+ outputStream.write(data, 0, DIGEST_SIZE_BYTES);
+ outputStream.writeInt(PARTITION_TYPE_MARKER);
+ outputStream.writeInt(0); // data size
+ outputStream.flush();
+ } catch (IOException e) {
+ Slog.e(TAG, "failed to format block", e);
+ return;
+ } finally {
+ IoUtils.closeQuietly(outputStream);
+ }
+
+ doSetOemUnlockEnabledLocked(false);
+ computeAndWriteDigestLocked();
+ }
+
+ private void doSetOemUnlockEnabledLocked(boolean enabled) {
+ FileOutputStream outputStream;
+ try {
+ outputStream = new FileOutputStream(new File(mDataBlockFile));
+ } catch (FileNotFoundException e) {
+ Slog.e(TAG, "partition not available", e);
+ return;
+ }
+
+ try {
+ FileChannel channel = outputStream.getChannel();
+
+ channel.position(getBlockDeviceSize() - 1);
+
+ ByteBuffer data = ByteBuffer.allocate(1);
+ data.put(enabled ? (byte) 1 : (byte) 0);
+ data.flip();
+ channel.write(data);
+ outputStream.flush();
+ } catch (IOException e) {
+ Slog.e(TAG, "unable to access persistent partition", e);
+ return;
+ } finally {
+ IoUtils.closeQuietly(outputStream);
+ }
+ }
+
private native long nativeGetBlockDeviceSize(String path);
private native int nativeWipe(String path);
@@ -176,19 +313,23 @@
headerAndData.putInt(data.length);
headerAndData.put(data);
- try {
- synchronized (mLock) {
- outputStream.write(headerAndData.array());
- return data.length;
- }
- } catch (IOException e) {
- Slog.e(TAG, "failed writing to the persistent data block", e);
- return -1;
- } finally {
+ synchronized (mLock) {
try {
- outputStream.close();
+ byte[] checksum = new byte[DIGEST_SIZE_BYTES];
+ outputStream.write(checksum, 0, DIGEST_SIZE_BYTES);
+ outputStream.write(headerAndData.array());
+ outputStream.flush();
} catch (IOException e) {
- Slog.e(TAG, "failed closing output stream", e);
+ Slog.e(TAG, "failed writing to the persistent data block", e);
+ return -1;
+ } finally {
+ IoUtils.closeQuietly(outputStream);
+ }
+
+ if (computeAndWriteDigestLocked()) {
+ return data.length;
+ } else {
+ return -1;
}
}
}
@@ -196,6 +337,9 @@
@Override
public byte[] read() {
enforceUid(Binder.getCallingUid());
+ if (!enforceChecksumValidity()) {
+ return new byte[0];
+ }
DataInputStream inputStream;
try {
@@ -256,30 +400,10 @@
}
enforceOemUnlockPermission();
enforceIsOwner();
- FileOutputStream outputStream;
- try {
- outputStream = new FileOutputStream(new File(mDataBlockFile));
- } catch (FileNotFoundException e) {
- Slog.e(TAG, "parition not available", e);
- return;
- }
- try {
- FileChannel channel = outputStream.getChannel();
-
- channel.position(getBlockDeviceSize() - 1);
-
- ByteBuffer data = ByteBuffer.allocate(1);
- data.put(enabled ? (byte) 1 : (byte) 0);
- data.flip();
-
- synchronized (mOemLock) {
- channel.write(data);
- }
- } catch (IOException e) {
- Slog.e(TAG, "unable to access persistent partition", e);
- } finally {
- IoUtils.closeQuietly(outputStream);
+ synchronized (mLock) {
+ doSetOemUnlockEnabledLocked(enabled);
+ computeAndWriteDigestLocked();
}
}
@@ -295,8 +419,8 @@
}
try {
- inputStream.skip(getBlockDeviceSize() - 1);
- synchronized (mOemLock) {
+ synchronized (mLock) {
+ inputStream.skip(getBlockDeviceSize() - 1);
return inputStream.readByte() != 0;
}
} catch (IOException e) {
@@ -336,6 +460,5 @@
long actualSize = getBlockDeviceSize() - HEADER_SIZE - 1;
return actualSize <= MAX_DATA_BLOCK_SIZE ? actualSize : MAX_DATA_BLOCK_SIZE;
}
-
};
}