Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 8f4e49a8 authored by Christopher Tate's avatar Christopher Tate Committed by Android (Google) Code Review
Browse files

Merge "DO NOT MERGE - Require device encryption password for adb backup/restore" into ics-mr0

parents 00005385 b9c1acfb
Loading
Loading
Loading
Loading
+26 −0
Original line number Diff line number Diff line
@@ -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.
@@ -1285,6 +1305,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.
     */
+5 −1
Original line number Diff line number Diff line
@@ -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>
+35 −3
Original line number Diff line number Diff line
@@ -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();
+58 −3
Original line number Diff line number Diff line
@@ -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();
+47 −0
Original line number Diff line number Diff line
@@ -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();