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