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