Skip to content

Commit b9c1acf

Browse files
author
Christopher Tate
committed
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
1 parent ab9d5b1 commit b9c1acf

File tree

5 files changed

+171
-7
lines changed

5 files changed

+171
-7
lines changed

core/java/android/os/storage/IMountService.java

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -658,6 +658,24 @@ public int changeEncryptionPassword(String password) throws RemoteException {
658658
return _result;
659659
}
660660

661+
@Override
662+
public int verifyEncryptionPassword(String password) throws RemoteException {
663+
Parcel _data = Parcel.obtain();
664+
Parcel _reply = Parcel.obtain();
665+
int _result;
666+
try {
667+
_data.writeInterfaceToken(DESCRIPTOR);
668+
_data.writeString(password);
669+
mRemote.transact(Stub.TRANSACTION_verifyEncryptionPassword, _data, _reply, 0);
670+
_reply.readException();
671+
_result = _reply.readInt();
672+
} finally {
673+
_reply.recycle();
674+
_data.recycle();
675+
}
676+
return _result;
677+
}
678+
661679
public Parcelable[] getVolumeList() throws RemoteException {
662680
Parcel _data = Parcel.obtain();
663681
Parcel _reply = Parcel.obtain();
@@ -761,6 +779,8 @@ public String getSecureContainerFilesystemPath(String id) throws RemoteException
761779

762780
static final int TRANSACTION_getEncryptionState = IBinder.FIRST_CALL_TRANSACTION + 31;
763781

782+
static final int TRANSACTION_verifyEncryptionPassword = IBinder.FIRST_CALL_TRANSACTION + 32;
783+
764784
/**
765785
* Cast an IBinder object into an IMountService interface, generating a
766786
* proxy if needed.
@@ -1285,6 +1305,12 @@ public void unmountVolume(String mountPoint, boolean force, boolean removeEncryp
12851305
*/
12861306
public int changeEncryptionPassword(String password) throws RemoteException;
12871307

1308+
/**
1309+
* Verify the encryption password against the stored volume. This method
1310+
* may only be called by the system process.
1311+
*/
1312+
public int verifyEncryptionPassword(String password) throws RemoteException;
1313+
12881314
/**
12891315
* Returns list of all mountable volumes.
12901316
*/

packages/BackupRestoreConfirmation/res/values/strings.xml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,12 @@
3535

3636
<!-- Text for message to user that they must enter their predefined backup password in order to perform this operation. -->
3737
<string name="current_password_text">Please enter your current backup password below:</string>
38+
<!-- Text for message to user that they must enter their device encryption password in order to perform this restore operation. -->
39+
<string name="device_encryption_restore_text">Please enter your device encryption password below.</string>
40+
<!-- Text for message to user that they must enter their device encryption password in order to perform this backup operation. -->
41+
<string name="device_encryption_backup_text">Please enter your device encryption password below. This will also be used to encrypt the backup archive.</string>
3842

39-
<!-- Text for message to user that they can must enter an encryption password to use for the full backup operation. -->
43+
<!-- Text for message to user that they must enter an encryption password to use for the full backup operation. -->
4044
<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>
4145
<!-- Text for message to user that they may optionally supply an encryption password to use for a full backup operation. -->
4246
<string name="backup_enc_password_optional">If you wish to encrypt the full backup data, enter a password below:</string>

packages/BackupRestoreConfirmation/src/com/android/backupconfirm/BackupRestoreConfirmation.java

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@
2727
import android.os.Message;
2828
import android.os.RemoteException;
2929
import android.os.ServiceManager;
30+
import android.os.storage.IMountService;
31+
import android.util.Log;
3032
import android.util.Slog;
3133
import android.view.View;
3234
import android.widget.Button;
@@ -60,8 +62,10 @@ public class BackupRestoreConfirmation extends Activity {
6062

6163
Handler mHandler;
6264
IBackupManager mBackupManager;
65+
IMountService mMountService;
6366
FullObserver mObserver;
6467
int mToken;
68+
boolean mIsEncrypted;
6569
boolean mDidAcknowledge;
6670

6771
TextView mStatusView;
@@ -152,6 +156,7 @@ public void onCreate(Bundle icicle) {
152156
}
153157

154158
mBackupManager = IBackupManager.Stub.asInterface(ServiceManager.getService(Context.BACKUP_SERVICE));
159+
mMountService = IMountService.Stub.asInterface(ServiceManager.getService("mount"));
155160

156161
mHandler = new ObserverHandler(getApplicationContext());
157162
final Object oldObserver = getLastNonConfigurationInstance();
@@ -174,8 +179,23 @@ public void onCreate(Bundle icicle) {
174179
mEncPassword = (TextView) findViewById(R.id.enc_password);
175180
TextView curPwDesc = (TextView) findViewById(R.id.password_desc);
176181

177-
// We vary the password prompt depending on whether one is predefined
178-
if (!haveBackupPassword()) {
182+
// We vary the password prompt depending on whether one is predefined, and whether
183+
// the device is encrypted.
184+
mIsEncrypted = deviceIsEncrypted();
185+
if (mIsEncrypted) {
186+
Log.d(TAG, "Device is encrypted: requiring encryption pw");
187+
TextView pwPrompt = (TextView) findViewById(R.id.password_desc);
188+
// this password is mandatory; we hide the other options during backup
189+
if (layoutId == R.layout.confirm_backup) {
190+
pwPrompt.setText(R.string.device_encryption_backup_text);
191+
TextView tv = (TextView) findViewById(R.id.enc_password);
192+
tv.setVisibility(View.GONE);
193+
tv = (TextView) findViewById(R.id.enc_password_desc);
194+
tv.setVisibility(View.GONE);
195+
} else {
196+
pwPrompt.setText(R.string.device_encryption_restore_text);
197+
}
198+
} else if (!haveBackupPassword()) {
179199
curPwDesc.setVisibility(View.GONE);
180200
mCurPassword.setVisibility(View.GONE);
181201
if (layoutId == R.layout.confirm_backup) {
@@ -226,17 +246,29 @@ void sendAcknowledgement(int token, boolean allow, IFullBackupRestoreObserver ob
226246
mDidAcknowledge = true;
227247

228248
try {
249+
CharSequence encPassword = (mIsEncrypted)
250+
? mCurPassword.getText() : mEncPassword.getText();
229251
mBackupManager.acknowledgeFullBackupOrRestore(mToken,
230252
allow,
231253
String.valueOf(mCurPassword.getText()),
232-
String.valueOf(mEncPassword.getText()),
254+
String.valueOf(encPassword),
233255
mObserver);
234256
} catch (RemoteException e) {
235257
// TODO: bail gracefully if we can't contact the backup manager
236258
}
237259
}
238260
}
239261

262+
boolean deviceIsEncrypted() {
263+
try {
264+
return (mMountService.getEncryptionState() != IMountService.ENCRYPTION_STATE_NONE);
265+
} catch (Exception e) {
266+
// If we can't talk to the mount service we have a serious problem; fail
267+
// "secure" i.e. assuming that the device is encrypted.
268+
return true;
269+
}
270+
}
271+
240272
boolean haveBackupPassword() {
241273
try {
242274
return mBackupManager.hasBackupPassword();

services/java/com/android/server/BackupManagerService.java

Lines changed: 58 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,14 +62,15 @@
6262
import android.os.PowerManager;
6363
import android.os.Process;
6464
import android.os.RemoteException;
65+
import android.os.ServiceManager;
6566
import android.os.SystemClock;
6667
import android.os.WorkSource;
68+
import android.os.storage.IMountService;
6769
import android.provider.Settings;
6870
import android.util.EventLog;
6971
import android.util.Log;
7072
import android.util.Slog;
7173
import android.util.SparseArray;
72-
import android.util.SparseIntArray;
7374
import android.util.StringBuilderPrinter;
7475

7576
import com.android.internal.backup.BackupConstants;
@@ -187,6 +188,7 @@ class BackupManagerService extends IBackupManager.Stub {
187188
private IActivityManager mActivityManager;
188189
private PowerManager mPowerManager;
189190
private AlarmManager mAlarmManager;
191+
private IMountService mMountService;
190192
IBackupManager mBackupManagerBinder;
191193

192194
boolean mEnabled; // access to this is synchronized on 'this'
@@ -660,6 +662,7 @@ public BackupManagerService(Context context) {
660662

661663
mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
662664
mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
665+
mMountService = IMountService.Stub.asInterface(ServiceManager.getService("mount"));
663666

664667
mBackupManagerBinder = asInterface(asBinder());
665668

@@ -1037,6 +1040,40 @@ private byte[] randomBytes(int bits) {
10371040

10381041
// Backup password management
10391042
boolean passwordMatchesSaved(String candidatePw, int rounds) {
1043+
// First, on an encrypted device we require matching the device pw
1044+
final boolean isEncrypted;
1045+
try {
1046+
isEncrypted = (mMountService.getEncryptionState() != MountService.ENCRYPTION_STATE_NONE);
1047+
if (isEncrypted) {
1048+
if (DEBUG) {
1049+
Slog.i(TAG, "Device encrypted; verifying against device data pw");
1050+
}
1051+
// 0 means the password validated
1052+
// -2 means device not encrypted
1053+
// Any other result is either password failure or an error condition,
1054+
// so we refuse the match
1055+
final int result = mMountService.verifyEncryptionPassword(candidatePw);
1056+
if (result == 0) {
1057+
if (MORE_DEBUG) Slog.d(TAG, "Pw verifies");
1058+
return true;
1059+
} else if (result != -2) {
1060+
if (MORE_DEBUG) Slog.d(TAG, "Pw mismatch");
1061+
return false;
1062+
} else {
1063+
// ...else the device is supposedly not encrypted. HOWEVER, the
1064+
// query about the encryption state said that the device *is*
1065+
// encrypted, so ... we may have a problem. Log it and refuse
1066+
// the backup.
1067+
Slog.e(TAG, "verified encryption state mismatch against query; no match allowed");
1068+
return false;
1069+
}
1070+
}
1071+
} catch (Exception e) {
1072+
// Something went wrong talking to the mount service. This is very bad;
1073+
// assume that we fail password validation.
1074+
return false;
1075+
}
1076+
10401077
if (mPasswordHash == null) {
10411078
// no current password case -- require that 'currentPw' be null or empty
10421079
if (candidatePw == null || "".equals(candidatePw)) {
@@ -1114,7 +1151,15 @@ public boolean setBackupPassword(String currentPw, String newPw) {
11141151
public boolean hasBackupPassword() {
11151152
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
11161153
"hasBackupPassword");
1117-
return (mPasswordHash != null && mPasswordHash.length() > 0);
1154+
1155+
try {
1156+
return (mMountService.getEncryptionState() != IMountService.ENCRYPTION_STATE_NONE)
1157+
|| (mPasswordHash != null && mPasswordHash.length() > 0);
1158+
} catch (Exception e) {
1159+
// If we can't talk to the mount service we have a serious problem; fail
1160+
// "secure" i.e. assuming that we require a password
1161+
return true;
1162+
}
11181163
}
11191164

11201165
// Maintain persistent state around whether need to do an initialize operation.
@@ -5007,7 +5052,17 @@ public void acknowledgeFullBackupOrRestore(int token, boolean allow,
50075052

50085053
params.observer = observer;
50095054
params.curPassword = curPassword;
5010-
params.encryptPassword = encPpassword;
5055+
5056+
boolean isEncrypted;
5057+
try {
5058+
isEncrypted = (mMountService.getEncryptionState() != MountService.ENCRYPTION_STATE_NONE);
5059+
if (isEncrypted) Slog.w(TAG, "Device is encrypted; forcing enc password");
5060+
} catch (RemoteException e) {
5061+
// couldn't contact the mount service; fail "safe" and assume encryption
5062+
Slog.e(TAG, "Unable to contact mount service!");
5063+
isEncrypted = true;
5064+
}
5065+
params.encryptPassword = (isEncrypted) ? curPassword : encPpassword;
50115066

50125067
if (DEBUG) Slog.d(TAG, "Sending conf message with verb " + verb);
50135068
mWakelock.acquire();

services/java/com/android/server/MountService.java

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1897,6 +1897,53 @@ public int changeEncryptionPassword(String password) {
18971897
}
18981898
}
18991899

1900+
/**
1901+
* Validate a user-supplied password string with cryptfs
1902+
*/
1903+
@Override
1904+
public int verifyEncryptionPassword(String password) throws RemoteException {
1905+
// Only the system process is permitted to validate passwords
1906+
if (Binder.getCallingUid() != android.os.Process.SYSTEM_UID) {
1907+
throw new SecurityException("no permission to access the crypt keeper");
1908+
}
1909+
1910+
mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER,
1911+
"no permission to access the crypt keeper");
1912+
1913+
if (TextUtils.isEmpty(password)) {
1914+
throw new IllegalArgumentException("password cannot be empty");
1915+
}
1916+
1917+
waitForReady();
1918+
1919+
if (DEBUG_EVENTS) {
1920+
Slog.i(TAG, "validating encryption password...");
1921+
}
1922+
1923+
try {
1924+
ArrayList<String> response = mConnector.doCommand("cryptfs verifypw " + password);
1925+
String[] tokens = response.get(0).split(" ");
1926+
1927+
if (tokens == null || tokens.length != 2) {
1928+
String msg = "Unexpected result from cryptfs verifypw: {";
1929+
if (tokens == null) msg += "null";
1930+
else for (int i = 0; i < tokens.length; i++) {
1931+
if (i != 0) msg += ',';
1932+
msg += tokens[i];
1933+
}
1934+
msg += '}';
1935+
Slog.e(TAG, msg);
1936+
return -1;
1937+
}
1938+
1939+
Slog.i(TAG, "cryptfs verifypw => " + tokens[1]);
1940+
return Integer.parseInt(tokens[1]);
1941+
} catch (NativeDaemonConnectorException e) {
1942+
// Encryption failed
1943+
return e.getCode();
1944+
}
1945+
}
1946+
19001947
public Parcelable[] getVolumeList() {
19011948
synchronized(mVolumes) {
19021949
int size = mVolumes.size();

0 commit comments

Comments
 (0)