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