Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 6722598e authored by Andres Morales's avatar Andres Morales Committed by Android (Google) Code Review
Browse files

Merge "Add a checksum to FRP partition" into lmp-mr1-dev

parents b22462c5 28301307
Loading
Loading
Loading
Loading
+169 −46
Original line number Original line Diff line number Diff line
@@ -41,6 +41,9 @@ import java.io.FileOutputStream;
import java.io.IOException;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
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.
 * Service for reading and writing blocks to a persistent partition.
@@ -63,22 +66,16 @@ public class PersistentDataBlockService extends SystemService {
    private static final String PERSISTENT_DATA_BLOCK_PROP = "ro.frp.pst";
    private static final String PERSISTENT_DATA_BLOCK_PROP = "ro.frp.pst";
    private static final int HEADER_SIZE = 8;
    private static final int HEADER_SIZE = 8;
    // Magic number to mark block device as adhering to the format consumed by this service
    // 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.
    // 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;
    private static final int MAX_DATA_BLOCK_SIZE = 1024 * 100;
    public static final int DIGEST_SIZE_BYTES = 32;


    private final Context mContext;
    private final Context mContext;
    private final String mDataBlockFile;
    private final String mDataBlockFile;
    private final Object mLock = new Object();
    private final Object mLock = new Object();


    private int mAllowedUid = -1;
    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;
    private long mBlockDeviceSize;


    public PersistentDataBlockService(Context context) {
    public PersistentDataBlockService(Context context) {
@@ -89,7 +86,6 @@ public class PersistentDataBlockService extends SystemService {
        mAllowedUid = getAllowedUid(UserHandle.USER_OWNER);
        mAllowedUid = getAllowedUid(UserHandle.USER_OWNER);
    }
    }



    private int getAllowedUid(int userHandle) {
    private int getAllowedUid(int userHandle) {
        String allowedPackage = mContext.getResources()
        String allowedPackage = mContext.getResources()
                .getString(R.string.config_persistentDataPackageName);
                .getString(R.string.config_persistentDataPackageName);
@@ -106,6 +102,7 @@ public class PersistentDataBlockService extends SystemService {


    @Override
    @Override
    public void onStart() {
    public void onStart() {
        enforceChecksumValidity();
        publishBinderService(Context.PERSISTENT_DATA_BLOCK_SERVICE, mService);
        publishBinderService(Context.PERSISTENT_DATA_BLOCK_SERVICE, mService);
    }
    }


@@ -128,6 +125,9 @@ public class PersistentDataBlockService extends SystemService {
    }
    }


    private int getTotalDataSizeLocked(DataInputStream inputStream) throws IOException {
    private int getTotalDataSizeLocked(DataInputStream inputStream) throws IOException {
        // skip over checksum
        inputStream.skipBytes(DIGEST_SIZE_BYTES);

        int totalDataSize;
        int totalDataSize;
        int blockId = inputStream.readInt();
        int blockId = inputStream.readInt();
        if (blockId == PARTITION_TYPE_MARKER) {
        if (blockId == PARTITION_TYPE_MARKER) {
@@ -148,6 +148,143 @@ public class PersistentDataBlockService extends SystemService {
        return mBlockDeviceSize;
        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 long nativeGetBlockDeviceSize(String path);
    private native int nativeWipe(String path);
    private native int nativeWipe(String path);


@@ -176,19 +313,23 @@ public class PersistentDataBlockService extends SystemService {
            headerAndData.putInt(data.length);
            headerAndData.putInt(data.length);
            headerAndData.put(data);
            headerAndData.put(data);


            try {
            synchronized (mLock) {
            synchronized (mLock) {
                try {
                    byte[] checksum = new byte[DIGEST_SIZE_BYTES];
                    outputStream.write(checksum, 0, DIGEST_SIZE_BYTES);
                    outputStream.write(headerAndData.array());
                    outputStream.write(headerAndData.array());
                    return data.length;
                    outputStream.flush();
                }
                } catch (IOException e) {
                } catch (IOException e) {
                    Slog.e(TAG, "failed writing to the persistent data block", e);
                    Slog.e(TAG, "failed writing to the persistent data block", e);
                    return -1;
                    return -1;
                } finally {
                } finally {
                try {
                    IoUtils.closeQuietly(outputStream);
                    outputStream.close();
                }
                } catch (IOException e) {

                    Slog.e(TAG, "failed closing output stream", e);
                if (computeAndWriteDigestLocked()) {
                    return data.length;
                } else {
                    return -1;
                }
                }
            }
            }
        }
        }
@@ -196,6 +337,9 @@ public class PersistentDataBlockService extends SystemService {
        @Override
        @Override
        public byte[] read() {
        public byte[] read() {
            enforceUid(Binder.getCallingUid());
            enforceUid(Binder.getCallingUid());
            if (!enforceChecksumValidity()) {
                return new byte[0];
            }


            DataInputStream inputStream;
            DataInputStream inputStream;
            try {
            try {
@@ -256,30 +400,10 @@ public class PersistentDataBlockService extends SystemService {
            }
            }
            enforceOemUnlockPermission();
            enforceOemUnlockPermission();
            enforceIsOwner();
            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);
            synchronized (mLock) {

                doSetOemUnlockEnabledLocked(enabled);
                ByteBuffer data = ByteBuffer.allocate(1);
                computeAndWriteDigestLocked();
                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);
            }
            }
        }
        }


@@ -295,8 +419,8 @@ public class PersistentDataBlockService extends SystemService {
            }
            }


            try {
            try {
                synchronized (mLock) {
                    inputStream.skip(getBlockDeviceSize() - 1);
                    inputStream.skip(getBlockDeviceSize() - 1);
                synchronized (mOemLock) {
                    return inputStream.readByte() != 0;
                    return inputStream.readByte() != 0;
                }
                }
            } catch (IOException e) {
            } catch (IOException e) {
@@ -336,6 +460,5 @@ public class PersistentDataBlockService extends SystemService {
            long actualSize = getBlockDeviceSize() - HEADER_SIZE - 1;
            long actualSize = getBlockDeviceSize() - HEADER_SIZE - 1;
            return actualSize <= MAX_DATA_BLOCK_SIZE ? actualSize : MAX_DATA_BLOCK_SIZE;
            return actualSize <= MAX_DATA_BLOCK_SIZE ? actualSize : MAX_DATA_BLOCK_SIZE;
        }
        }

    };
    };
}
}