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