diff options
author | Christopher Tate <ctate@google.com> | 2011-10-14 10:33:13 -0700 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2011-10-14 10:33:13 -0700 |
commit | 8f4e49a8fe4f568b2be090768f3297655ecd6d34 (patch) | |
tree | dfa4e217429bab25bad1d3720c919ae0365f9986 | |
parent | 000053859290d322625dfcbdb0467fc34d5e55f4 (diff) | |
parent | b9c1acfb0b4a41ffb5a4d9c38ef298c3a1eb9599 (diff) | |
download | frameworks_base-8f4e49a8fe4f568b2be090768f3297655ecd6d34.zip frameworks_base-8f4e49a8fe4f568b2be090768f3297655ecd6d34.tar.gz frameworks_base-8f4e49a8fe4f568b2be090768f3297655ecd6d34.tar.bz2 |
Merge "DO NOT MERGE - Require device encryption password for adb backup/restore" into ics-mr0
5 files changed, 171 insertions, 7 deletions
diff --git a/core/java/android/os/storage/IMountService.java b/core/java/android/os/storage/IMountService.java index d1dc6e5..0640d7e 100644 --- a/core/java/android/os/storage/IMountService.java +++ b/core/java/android/os/storage/IMountService.java @@ -658,6 +658,24 @@ public interface IMountService extends IInterface { return _result; } + @Override + public int verifyEncryptionPassword(String password) throws RemoteException { + Parcel _data = Parcel.obtain(); + Parcel _reply = Parcel.obtain(); + int _result; + try { + _data.writeInterfaceToken(DESCRIPTOR); + _data.writeString(password); + mRemote.transact(Stub.TRANSACTION_verifyEncryptionPassword, _data, _reply, 0); + _reply.readException(); + _result = _reply.readInt(); + } finally { + _reply.recycle(); + _data.recycle(); + } + return _result; + } + public Parcelable[] getVolumeList() throws RemoteException { Parcel _data = Parcel.obtain(); Parcel _reply = Parcel.obtain(); @@ -761,6 +779,8 @@ public interface IMountService extends IInterface { static final int TRANSACTION_getEncryptionState = IBinder.FIRST_CALL_TRANSACTION + 31; + static final int TRANSACTION_verifyEncryptionPassword = IBinder.FIRST_CALL_TRANSACTION + 32; + /** * Cast an IBinder object into an IMountService interface, generating a * proxy if needed. @@ -1286,6 +1306,12 @@ public interface IMountService extends IInterface { public int changeEncryptionPassword(String password) throws RemoteException; /** + * Verify the encryption password against the stored volume. This method + * may only be called by the system process. + */ + public int verifyEncryptionPassword(String password) throws RemoteException; + + /** * Returns list of all mountable volumes. */ public Parcelable[] getVolumeList() throws RemoteException; diff --git a/packages/BackupRestoreConfirmation/res/values/strings.xml b/packages/BackupRestoreConfirmation/res/values/strings.xml index e91c6e2..5c90fd0 100644 --- a/packages/BackupRestoreConfirmation/res/values/strings.xml +++ b/packages/BackupRestoreConfirmation/res/values/strings.xml @@ -35,8 +35,12 @@ <!-- Text for message to user that they must enter their predefined backup password in order to perform this operation. --> <string name="current_password_text">Please enter your current backup password below:</string> + <!-- Text for message to user that they must enter their device encryption password in order to perform this restore operation. --> + <string name="device_encryption_restore_text">Please enter your device encryption password below.</string> + <!-- Text for message to user that they must enter their device encryption password in order to perform this backup operation. --> + <string name="device_encryption_backup_text">Please enter your device encryption password below. This will also be used to encrypt the backup archive.</string> - <!-- Text for message to user that they can must enter an encryption password to use for the full backup operation. --> + <!-- Text for message to user that they must enter an encryption password to use for the full backup operation. --> <string name="backup_enc_password_text">Please enter a password to use for encrypting the full backup data. If this is left blank, your current backup password will be used:</string> <!-- Text for message to user that they may optionally supply an encryption password to use for a full backup operation. --> <string name="backup_enc_password_optional">If you wish to encrypt the full backup data, enter a password below:</string> diff --git a/packages/BackupRestoreConfirmation/src/com/android/backupconfirm/BackupRestoreConfirmation.java b/packages/BackupRestoreConfirmation/src/com/android/backupconfirm/BackupRestoreConfirmation.java index fbdf3cc..7f1d059 100644 --- a/packages/BackupRestoreConfirmation/src/com/android/backupconfirm/BackupRestoreConfirmation.java +++ b/packages/BackupRestoreConfirmation/src/com/android/backupconfirm/BackupRestoreConfirmation.java @@ -27,6 +27,8 @@ import android.os.Handler; import android.os.Message; import android.os.RemoteException; import android.os.ServiceManager; +import android.os.storage.IMountService; +import android.util.Log; import android.util.Slog; import android.view.View; import android.widget.Button; @@ -60,8 +62,10 @@ public class BackupRestoreConfirmation extends Activity { Handler mHandler; IBackupManager mBackupManager; + IMountService mMountService; FullObserver mObserver; int mToken; + boolean mIsEncrypted; boolean mDidAcknowledge; TextView mStatusView; @@ -152,6 +156,7 @@ public class BackupRestoreConfirmation extends Activity { } mBackupManager = IBackupManager.Stub.asInterface(ServiceManager.getService(Context.BACKUP_SERVICE)); + mMountService = IMountService.Stub.asInterface(ServiceManager.getService("mount")); mHandler = new ObserverHandler(getApplicationContext()); final Object oldObserver = getLastNonConfigurationInstance(); @@ -174,8 +179,23 @@ public class BackupRestoreConfirmation extends Activity { mEncPassword = (TextView) findViewById(R.id.enc_password); TextView curPwDesc = (TextView) findViewById(R.id.password_desc); - // We vary the password prompt depending on whether one is predefined - if (!haveBackupPassword()) { + // We vary the password prompt depending on whether one is predefined, and whether + // the device is encrypted. + mIsEncrypted = deviceIsEncrypted(); + if (mIsEncrypted) { + Log.d(TAG, "Device is encrypted: requiring encryption pw"); + TextView pwPrompt = (TextView) findViewById(R.id.password_desc); + // this password is mandatory; we hide the other options during backup + if (layoutId == R.layout.confirm_backup) { + pwPrompt.setText(R.string.device_encryption_backup_text); + TextView tv = (TextView) findViewById(R.id.enc_password); + tv.setVisibility(View.GONE); + tv = (TextView) findViewById(R.id.enc_password_desc); + tv.setVisibility(View.GONE); + } else { + pwPrompt.setText(R.string.device_encryption_restore_text); + } + } else if (!haveBackupPassword()) { curPwDesc.setVisibility(View.GONE); mCurPassword.setVisibility(View.GONE); if (layoutId == R.layout.confirm_backup) { @@ -226,10 +246,12 @@ public class BackupRestoreConfirmation extends Activity { mDidAcknowledge = true; try { + CharSequence encPassword = (mIsEncrypted) + ? mCurPassword.getText() : mEncPassword.getText(); mBackupManager.acknowledgeFullBackupOrRestore(mToken, allow, String.valueOf(mCurPassword.getText()), - String.valueOf(mEncPassword.getText()), + String.valueOf(encPassword), mObserver); } catch (RemoteException e) { // TODO: bail gracefully if we can't contact the backup manager @@ -237,6 +259,16 @@ public class BackupRestoreConfirmation extends Activity { } } + boolean deviceIsEncrypted() { + try { + return (mMountService.getEncryptionState() != IMountService.ENCRYPTION_STATE_NONE); + } catch (Exception e) { + // If we can't talk to the mount service we have a serious problem; fail + // "secure" i.e. assuming that the device is encrypted. + return true; + } + } + boolean haveBackupPassword() { try { return mBackupManager.hasBackupPassword(); 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(); |