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