diff options
author | Christopher Tate <ctate@google.com> | 2011-10-10 13:51:12 -0700 |
---|---|---|
committer | Chris Tate <ctate@google.com> | 2011-10-13 17:39:48 -0700 |
commit | b9c1acfb0b4a41ffb5a4d9c38ef298c3a1eb9599 (patch) | |
tree | 9e435077590878bfb5cb714308fbef333738dfd1 /services/java | |
parent | ab9d5b11737f8a460467657035186bcfa7085eb9 (diff) | |
download | frameworks_base-b9c1acfb0b4a41ffb5a4d9c38ef298c3a1eb9599.zip frameworks_base-b9c1acfb0b4a41ffb5a4d9c38ef298c3a1eb9599.tar.gz frameworks_base-b9c1acfb0b4a41ffb5a4d9c38ef298c3a1eb9599.tar.bz2 |
DO NOT MERGE - Require device encryption password for adb backup/restore
This supersedes any backup-password that the user might supply. Per
design, the device encryption password is also always used to encrypt
the backup archive.
The CL introduces two new strings, used for prompting the user for
their device encryption password rather than their settings-defined
"backup password" when confirming a full backup or restore operation.
Bug 5382487
Change-Id: I278737927a4ecbb765bfb5ecfd28a4cb8dae52ef
Diffstat (limited to 'services/java')
-rw-r--r-- | services/java/com/android/server/BackupManagerService.java | 61 | ||||
-rw-r--r-- | services/java/com/android/server/MountService.java | 47 |
2 files changed, 105 insertions, 3 deletions
diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java index f9f5458..4ef8837 100644 --- a/services/java/com/android/server/BackupManagerService.java +++ b/services/java/com/android/server/BackupManagerService.java @@ -62,14 +62,15 @@ import android.os.ParcelFileDescriptor; import android.os.PowerManager; import android.os.Process; import android.os.RemoteException; +import android.os.ServiceManager; import android.os.SystemClock; import android.os.WorkSource; +import android.os.storage.IMountService; import android.provider.Settings; import android.util.EventLog; import android.util.Log; import android.util.Slog; import android.util.SparseArray; -import android.util.SparseIntArray; import android.util.StringBuilderPrinter; import com.android.internal.backup.BackupConstants; @@ -187,6 +188,7 @@ class BackupManagerService extends IBackupManager.Stub { private IActivityManager mActivityManager; private PowerManager mPowerManager; private AlarmManager mAlarmManager; + private IMountService mMountService; IBackupManager mBackupManagerBinder; boolean mEnabled; // access to this is synchronized on 'this' @@ -660,6 +662,7 @@ class BackupManagerService extends IBackupManager.Stub { mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE); + mMountService = IMountService.Stub.asInterface(ServiceManager.getService("mount")); mBackupManagerBinder = asInterface(asBinder()); @@ -1037,6 +1040,40 @@ class BackupManagerService extends IBackupManager.Stub { // Backup password management boolean passwordMatchesSaved(String candidatePw, int rounds) { + // First, on an encrypted device we require matching the device pw + final boolean isEncrypted; + try { + isEncrypted = (mMountService.getEncryptionState() != MountService.ENCRYPTION_STATE_NONE); + if (isEncrypted) { + if (DEBUG) { + Slog.i(TAG, "Device encrypted; verifying against device data pw"); + } + // 0 means the password validated + // -2 means device not encrypted + // Any other result is either password failure or an error condition, + // so we refuse the match + final int result = mMountService.verifyEncryptionPassword(candidatePw); + if (result == 0) { + if (MORE_DEBUG) Slog.d(TAG, "Pw verifies"); + return true; + } else if (result != -2) { + if (MORE_DEBUG) Slog.d(TAG, "Pw mismatch"); + return false; + } else { + // ...else the device is supposedly not encrypted. HOWEVER, the + // query about the encryption state said that the device *is* + // encrypted, so ... we may have a problem. Log it and refuse + // the backup. + Slog.e(TAG, "verified encryption state mismatch against query; no match allowed"); + return false; + } + } + } catch (Exception e) { + // Something went wrong talking to the mount service. This is very bad; + // assume that we fail password validation. + return false; + } + if (mPasswordHash == null) { // no current password case -- require that 'currentPw' be null or empty if (candidatePw == null || "".equals(candidatePw)) { @@ -1114,7 +1151,15 @@ class BackupManagerService extends IBackupManager.Stub { public boolean hasBackupPassword() { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, "hasBackupPassword"); - return (mPasswordHash != null && mPasswordHash.length() > 0); + + try { + return (mMountService.getEncryptionState() != IMountService.ENCRYPTION_STATE_NONE) + || (mPasswordHash != null && mPasswordHash.length() > 0); + } catch (Exception e) { + // If we can't talk to the mount service we have a serious problem; fail + // "secure" i.e. assuming that we require a password + return true; + } } // Maintain persistent state around whether need to do an initialize operation. @@ -5007,7 +5052,17 @@ class BackupManagerService extends IBackupManager.Stub { params.observer = observer; params.curPassword = curPassword; - params.encryptPassword = encPpassword; + + boolean isEncrypted; + try { + isEncrypted = (mMountService.getEncryptionState() != MountService.ENCRYPTION_STATE_NONE); + if (isEncrypted) Slog.w(TAG, "Device is encrypted; forcing enc password"); + } catch (RemoteException e) { + // couldn't contact the mount service; fail "safe" and assume encryption + Slog.e(TAG, "Unable to contact mount service!"); + isEncrypted = true; + } + params.encryptPassword = (isEncrypted) ? curPassword : encPpassword; if (DEBUG) Slog.d(TAG, "Sending conf message with verb " + verb); mWakelock.acquire(); diff --git a/services/java/com/android/server/MountService.java b/services/java/com/android/server/MountService.java index 582f0ed..5425813 100644 --- a/services/java/com/android/server/MountService.java +++ b/services/java/com/android/server/MountService.java @@ -1897,6 +1897,53 @@ class MountService extends IMountService.Stub } } + /** + * Validate a user-supplied password string with cryptfs + */ + @Override + public int verifyEncryptionPassword(String password) throws RemoteException { + // Only the system process is permitted to validate passwords + if (Binder.getCallingUid() != android.os.Process.SYSTEM_UID) { + throw new SecurityException("no permission to access the crypt keeper"); + } + + mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER, + "no permission to access the crypt keeper"); + + if (TextUtils.isEmpty(password)) { + throw new IllegalArgumentException("password cannot be empty"); + } + + waitForReady(); + + if (DEBUG_EVENTS) { + Slog.i(TAG, "validating encryption password..."); + } + + try { + ArrayList<String> response = mConnector.doCommand("cryptfs verifypw " + password); + String[] tokens = response.get(0).split(" "); + + if (tokens == null || tokens.length != 2) { + String msg = "Unexpected result from cryptfs verifypw: {"; + if (tokens == null) msg += "null"; + else for (int i = 0; i < tokens.length; i++) { + if (i != 0) msg += ','; + msg += tokens[i]; + } + msg += '}'; + Slog.e(TAG, msg); + return -1; + } + + Slog.i(TAG, "cryptfs verifypw => " + tokens[1]); + return Integer.parseInt(tokens[1]); + } catch (NativeDaemonConnectorException e) { + // Encryption failed + return e.getCode(); + } + } + public Parcelable[] getVolumeList() { synchronized(mVolumes) { int size = mVolumes.size(); |