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