Initial implementation of EAS security
* This first implementation integrates with early CLs for Email app integration with device security * Check for policies added to validation process * If the server has no policy requirements, there is no change to the existing process * We automatically declare a set of policies to be unsupported if any are known to the sync adapter to be unsupported (e.g. no attachments or password history) * We call isSupported (PolicySet) to determine whether other policies use values that the OS can support (e.g. password length, inactivity time, etc.) * Depending on whether the server's policies are unsupported or supported, we throw the proper exception back to the caller Change-Id: I704cb2151dd87f54c83c2aa23976a8ac8e2c501a
This commit is contained in:
parent
3d2b3b3b35
commit
72a5e7d7c1
@ -40,6 +40,7 @@ import com.android.exchange.adapter.EmailSyncAdapter;
|
||||
import com.android.exchange.adapter.FolderSyncParser;
|
||||
import com.android.exchange.adapter.MeetingResponseParser;
|
||||
import com.android.exchange.adapter.PingParser;
|
||||
import com.android.exchange.adapter.ProvisionParser;
|
||||
import com.android.exchange.adapter.Serializer;
|
||||
import com.android.exchange.adapter.Tags;
|
||||
import com.android.exchange.adapter.Parser.EasParserException;
|
||||
@ -142,6 +143,9 @@ public class EasSyncService extends AbstractSyncService {
|
||||
static private final int PING_FALLBACK_INBOX = 5;
|
||||
static private final int PING_FALLBACK_PIM = 25;
|
||||
|
||||
// MSFT's custom HTTP result code indicating the need to provision
|
||||
static private final int HTTP_NEED_PROVISIONING = 449;
|
||||
|
||||
// Reasonable default
|
||||
public String mProtocolVersion = DEFAULT_PROTOCOL_VERSION;
|
||||
public Double mProtocolVersionDouble;
|
||||
@ -224,6 +228,19 @@ public class EasSyncService extends AbstractSyncService {
|
||||
return (code == HttpStatus.SC_UNAUTHORIZED) || (code == HttpStatus.SC_FORBIDDEN);
|
||||
}
|
||||
|
||||
private void setupProtocolVersion(EasSyncService service, Header versionHeader) {
|
||||
String versions = versionHeader.getValue();
|
||||
if (versions != null) {
|
||||
if (versions.contains("12.0")) {
|
||||
service.mProtocolVersion = "12.0";
|
||||
}
|
||||
service.mProtocolVersionDouble = Double.parseDouble(service.mProtocolVersion);
|
||||
if (service.mAccount != null) {
|
||||
service.mAccount.mProtocolVersion = service.mProtocolVersion;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void validateAccount(String hostAddress, String userName, String password, int port,
|
||||
boolean ssl, boolean trustCertificates, Context context) throws MessagingException {
|
||||
@ -251,6 +268,9 @@ public class EasSyncService extends AbstractSyncService {
|
||||
throw new MessagingException(MessagingException.IOERROR);
|
||||
}
|
||||
|
||||
// Make sure we've got the right protocol version set up
|
||||
setupProtocolVersion(svc, versions);
|
||||
|
||||
// Run second test here for provisioning failures...
|
||||
Serializer s = new Serializer();
|
||||
userLog("Try folder sync");
|
||||
@ -258,20 +278,17 @@ public class EasSyncService extends AbstractSyncService {
|
||||
.end().end().done();
|
||||
resp = svc.sendHttpClientPost("FolderSync", s.toByteArray());
|
||||
code = resp.getStatusLine().getStatusCode();
|
||||
if (code == HttpStatus.SC_FORBIDDEN) {
|
||||
throw new MessagingException(MessagingException.SECURITY_POLICIES_UNSUPPORTED);
|
||||
// We'll get one of the following responses if policies are required by the server
|
||||
if (code == HttpStatus.SC_FORBIDDEN || code == HTTP_NEED_PROVISIONING) {
|
||||
// Get the policies and see if we are able to support them
|
||||
if (svc.canProvision()) {
|
||||
// If so, send the advisory Exception (the account may be created later)
|
||||
throw new MessagingException(MessagingException.SECURITY_POLICIES_REQUIRED);
|
||||
} else
|
||||
// If not, send the unsupported Exception (the account won't be created)
|
||||
throw new MessagingException(
|
||||
MessagingException.SECURITY_POLICIES_UNSUPPORTED);
|
||||
}
|
||||
// PLACEHOLDER: Replace the above simple check with a more sophisticated
|
||||
// check of server-mandated security policy support. There are three outcomes.
|
||||
// 1. As below, if no policies required, simply return here as-is.
|
||||
// 2. As above, if policies are required that we do not support, throw
|
||||
// MessagingException.SECURITY_POLICIES_UNSUPPORTED. This is a validation
|
||||
// failure.
|
||||
// 3. New code: If policies are required that we *do* support, throw
|
||||
// MessagingException.SECURITY_POLICIES_REQUIRED. This is an advisory to the
|
||||
// UI that new policies will be required in order to use this account.
|
||||
// See also: isSupported(PolicySet policies)
|
||||
|
||||
userLog("Validation successful");
|
||||
return;
|
||||
}
|
||||
@ -882,6 +899,29 @@ public class EasSyncService extends AbstractSyncService {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO This is Exchange 2007 only at this point
|
||||
private boolean canProvision() throws IOException {
|
||||
Serializer s = new Serializer();
|
||||
s.start(Tags.PROVISION_PROVISION).start(Tags.PROVISION_POLICIES);
|
||||
s.start(Tags.PROVISION_POLICY).data(Tags.PROVISION_POLICY_TYPE, "MS-EAS-Provisioning-WBXML")
|
||||
.end().end().end().done();
|
||||
HttpResponse resp = sendHttpClientPost("Provision", s.toByteArray());
|
||||
int code = resp.getStatusLine().getStatusCode();
|
||||
if (code == HttpStatus.SC_OK) {
|
||||
InputStream is = resp.getEntity().getContent();
|
||||
ProvisionParser pp = new ProvisionParser(is, this);
|
||||
if (pp.parse()) {
|
||||
// If true, we received policies from the server
|
||||
// Retrieve them and write them to the framework
|
||||
PolicySet ps = pp.getPolicySet();
|
||||
if (SecurityPolicy.getInstance(mContext).isSupported(ps)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs FolderSync
|
||||
*
|
||||
|
178
src/com/android/exchange/adapter/ProvisionParser.java
Normal file
178
src/com/android/exchange/adapter/ProvisionParser.java
Normal file
@ -0,0 +1,178 @@
|
||||
/* Copyright (C) 2010 The Android Open Source Project.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.exchange.adapter;
|
||||
|
||||
import com.android.email.SecurityPolicy;
|
||||
import com.android.email.SecurityPolicy.PolicySet;
|
||||
import com.android.exchange.EasSyncService;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
/**
|
||||
* Parse the result of the Provision command
|
||||
*
|
||||
* Assuming a successful parse, we store the PolicySet and the policy key
|
||||
*/
|
||||
public class ProvisionParser extends Parser {
|
||||
private EasSyncService mService;
|
||||
PolicySet mPolicySet = null;
|
||||
String mPolicyKey = null;
|
||||
|
||||
public ProvisionParser(InputStream in, EasSyncService service) throws IOException {
|
||||
super(in);
|
||||
mService = service;
|
||||
setDebug(true);
|
||||
}
|
||||
|
||||
public PolicySet getPolicySet() {
|
||||
return mPolicySet;
|
||||
}
|
||||
|
||||
public String getPolicyKey() {
|
||||
return mPolicyKey;
|
||||
}
|
||||
|
||||
public void parseProvisionDoc() throws IOException {
|
||||
int minPasswordLength = 0;
|
||||
int passwordMode = PolicySet.PASSWORD_MODE_NONE;
|
||||
int maxPasswordFails = 0;
|
||||
int maxScreenLockTime = 0;
|
||||
boolean canSupport = false;
|
||||
|
||||
while (nextTag(Tags.PROVISION_EAS_PROVISION_DOC) != END) {
|
||||
switch (tag) {
|
||||
case Tags.PROVISION_DEVICE_PASSWORD_ENABLED:
|
||||
if (getValueInt() == 1) {
|
||||
if (passwordMode == PolicySet.PASSWORD_MODE_NONE) {
|
||||
passwordMode = PolicySet.PASSWORD_MODE_SIMPLE;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case Tags.PROVISION_MIN_DEVICE_PASSWORD_LENGTH:
|
||||
minPasswordLength = getValueInt();
|
||||
break;
|
||||
case Tags.PROVISION_ALPHA_DEVICE_PASSWORD_ENABLED:
|
||||
if (getValueInt() == 1) {
|
||||
passwordMode = PolicySet.PASSWORD_MODE_STRONG;
|
||||
}
|
||||
break;
|
||||
case Tags.PROVISION_MAX_INACTIVITY_TIME_DEVICE_LOCK:
|
||||
// EAS gives us seconds, which is, happily, what the PolicySet requires
|
||||
maxScreenLockTime = getValueInt();
|
||||
break;
|
||||
case Tags.PROVISION_MAX_DEVICE_PASSWORD_FAILED_ATTEMPTS:
|
||||
maxPasswordFails = getValueInt();
|
||||
break;
|
||||
case Tags.PROVISION_ALLOW_SIMPLE_DEVICE_PASSWORD:
|
||||
// Ignore this unless there's any MSFT documentation for what this means
|
||||
// Hint: I haven't seen any that's more specific than "simple"
|
||||
getValue();
|
||||
break;
|
||||
// The following policy, if false, can't be supported at the moment
|
||||
case Tags.PROVISION_ATTACHMENTS_ENABLED:
|
||||
if (getValueInt() == 0) {
|
||||
canSupport = false;
|
||||
}
|
||||
break;
|
||||
// The following policies, if true, can't be supported at the moment
|
||||
case Tags.PROVISION_DEVICE_ENCRYPTION_ENABLED:
|
||||
case Tags.PROVISION_PASSWORD_RECOVERY_ENABLED:
|
||||
case Tags.PROVISION_DEVICE_PASSWORD_EXPIRATION:
|
||||
case Tags.PROVISION_DEVICE_PASSWORD_HISTORY:
|
||||
case Tags.PROVISION_MAX_ATTACHMENT_SIZE:
|
||||
if (getValueInt() == 1) {
|
||||
canSupport = false;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
skipTag();
|
||||
}
|
||||
}
|
||||
|
||||
if (canSupport) {
|
||||
mPolicySet = new SecurityPolicy.PolicySet(minPasswordLength, passwordMode,
|
||||
maxPasswordFails, maxScreenLockTime, true);
|
||||
}
|
||||
}
|
||||
|
||||
public void parseProvisionData() throws IOException {
|
||||
while (nextTag(Tags.PROVISION_DATA) != END) {
|
||||
if (tag == Tags.PROVISION_EAS_PROVISION_DOC) {
|
||||
parseProvisionDoc();
|
||||
} else {
|
||||
skipTag();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void parsePolicy() throws IOException {
|
||||
while (nextTag(Tags.PROVISION_POLICY) != END) {
|
||||
switch (tag) {
|
||||
case Tags.PROVISION_POLICIES:
|
||||
parsePolicies();
|
||||
break;
|
||||
case Tags.PROVISION_POLICY_TYPE:
|
||||
mService.userLog("Policy type: ", getValue());
|
||||
break;
|
||||
case Tags.PROVISION_POLICY_KEY:
|
||||
mPolicyKey = getValue();
|
||||
break;
|
||||
case Tags.PROVISION_STATUS:
|
||||
mService.userLog("Policy status: ", getValue());
|
||||
break;
|
||||
case Tags.PROVISION_DATA:
|
||||
parseProvisionData();
|
||||
break;
|
||||
default:
|
||||
skipTag();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void parsePolicies() throws IOException {
|
||||
while (nextTag(Tags.PROVISION_POLICIES) != END) {
|
||||
if (tag == Tags.PROVISION_POLICY) {
|
||||
parsePolicy();
|
||||
} else {
|
||||
skipTag();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean parse() throws IOException {
|
||||
boolean res = false;
|
||||
if (nextTag(START_DOCUMENT) != Tags.PROVISION_PROVISION) {
|
||||
throw new IOException();
|
||||
}
|
||||
while (nextTag(START_DOCUMENT) != END_DOCUMENT) {
|
||||
switch (tag) {
|
||||
case Tags.PROVISION_STATUS:
|
||||
int status = getValueInt();
|
||||
mService.userLog("Provision status: ", status);
|
||||
break;
|
||||
case Tags.PROVISION_POLICIES:
|
||||
parsePolicies();
|
||||
return (mPolicySet != null);
|
||||
default:
|
||||
skipTag();
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
@ -43,6 +43,7 @@ public class Tags {
|
||||
public static final int TASK = 0x09;
|
||||
public static final int CONTACTS2 = 0x0C;
|
||||
public static final int PING = 0x0D;
|
||||
public static final int PROVISION = 0x0E;
|
||||
public static final int GAL = 0x10;
|
||||
public static final int BASE = 0x11;
|
||||
|
||||
@ -345,7 +346,6 @@ public class Tags {
|
||||
public static final int CONTACTS2_NICKNAME = CONTACTS2_PAGE + 0xD;
|
||||
public static final int CONTACTS2_MMS = CONTACTS2_PAGE + 0xE;
|
||||
|
||||
// The Ping constants are used by EasSyncService, and need to be public
|
||||
public static final int PING_PAGE = PING << PAGE_SHIFT;
|
||||
public static final int PING_PING = PING_PAGE + 5;
|
||||
public static final int PING_AUTD_STATE = PING_PAGE + 6;
|
||||
@ -357,6 +357,66 @@ public class Tags {
|
||||
public static final int PING_CLASS = PING_PAGE + 0xC;
|
||||
public static final int PING_MAX_FOLDERS = PING_PAGE + 0xD;
|
||||
|
||||
public static final int PROVISION_PAGE = PROVISION << PAGE_SHIFT;
|
||||
// EAS 2.5
|
||||
public static final int PROVISION_PROVISION = PROVISION_PAGE + 5;
|
||||
public static final int PROVISION_POLICIES = PROVISION_PAGE + 6;
|
||||
public static final int PROVISION_POLICY = PROVISION_PAGE + 7;
|
||||
public static final int PROVISION_POLICY_TYPE = PROVISION_PAGE + 8;
|
||||
public static final int PROVISION_POLICY_KEY = PROVISION_PAGE + 9;
|
||||
public static final int PROVISION_DATA = PROVISION_PAGE + 0xA;
|
||||
public static final int PROVISION_STATUS = PROVISION_PAGE + 0xB;
|
||||
public static final int PROVISION_REMOTE_WIPE = PROVISION_PAGE + 0xC;
|
||||
// EAS 12.0
|
||||
public static final int PROVISION_EAS_PROVISION_DOC = PROVISION_PAGE + 0xD;
|
||||
public static final int PROVISION_DEVICE_PASSWORD_ENABLED = PROVISION_PAGE + 0xE;
|
||||
public static final int PROVISION_ALPHA_DEVICE_PASSWORD_ENABLED = PROVISION_PAGE + 0xF;
|
||||
public static final int PROVISION_DEVICE_ENCRYPTION_ENABLED = PROVISION_PAGE + 0x10;
|
||||
public static final int PROVISION_PASSWORD_RECOVERY_ENABLED = PROVISION_PAGE + 0x11;
|
||||
public static final int PROVISION_ATTACHMENTS_ENABLED = PROVISION_PAGE + 0x13;
|
||||
public static final int PROVISION_MIN_DEVICE_PASSWORD_LENGTH = PROVISION_PAGE + 0x14;
|
||||
public static final int PROVISION_MAX_INACTIVITY_TIME_DEVICE_LOCK = PROVISION_PAGE + 0x15;
|
||||
public static final int PROVISION_MAX_DEVICE_PASSWORD_FAILED_ATTEMPTS = PROVISION_PAGE + 0x16;
|
||||
public static final int PROVISION_MAX_ATTACHMENT_SIZE = PROVISION_PAGE + 0x17;
|
||||
public static final int PROVISION_ALLOW_SIMPLE_DEVICE_PASSWORD = PROVISION_PAGE + 0x18;
|
||||
public static final int PROVISION_DEVICE_PASSWORD_EXPIRATION = PROVISION_PAGE + 0x19;
|
||||
public static final int PROVISION_DEVICE_PASSWORD_HISTORY = PROVISION_PAGE + 0x1A;
|
||||
public static final int PROVISION_MAX_SUPPORTED_TAG = PROVISION_DEVICE_PASSWORD_HISTORY;
|
||||
|
||||
// EAS 12.1
|
||||
public static final int PROVISION_ALLOW_STORAGE_CARD = PROVISION_PAGE + 0x1B;
|
||||
public static final int PROVISION_ALLOW_CAMERA = PROVISION_PAGE + 0x1C;
|
||||
public static final int PROVISION_REQUIRE_DEVICE_ENCRYPTION = PROVISION_PAGE + 0x1D;
|
||||
public static final int PROVISION_ALLOW_UNSIGNED_APPLICATIONS = PROVISION_PAGE + 0x1E;
|
||||
public static final int PROVISION_ALLOW_UNSIGNED_INSTALLATION_PACKAGES = PROVISION_PAGE + 0x1F;
|
||||
public static final int PROVISION_MIN_DEVICE_PASSWORD_COMPLEX_CHARS = PROVISION_PAGE + 0x20;
|
||||
public static final int PROVISION_ALLOW_WIFI = PROVISION_PAGE + 0x21;
|
||||
public static final int PROVISION_ALLOW_TEXT_MESSAGING = PROVISION_PAGE + 0x22;
|
||||
public static final int PROVISION_ALLOW_POP_IMAP_EMAIL = PROVISION_PAGE + 0x23;
|
||||
public static final int PROVISION_ALLOW_BLUETOOTH = PROVISION_PAGE + 0x24;
|
||||
public static final int PROVISION_ALLOW_IRDA = PROVISION_PAGE + 0x25;
|
||||
public static final int PROVISION_REQUIRE_MANUAL_SYNC_WHEN_ROAMING = PROVISION_PAGE + 0x26;
|
||||
public static final int PROVISION_ALLOW_DESKTOP_SYNC = PROVISION_PAGE + 0x27;
|
||||
public static final int PROVISION_MAX_CALENDAR_AGE_FILTER = PROVISION_PAGE + 0x28;
|
||||
public static final int PROVISION_ALLOW_HTML_EMAIL = PROVISION_PAGE + 0x29;
|
||||
public static final int PROVISION_MAX_EMAIL_AGE_FILTER = PROVISION_PAGE + 0x2A;
|
||||
public static final int PROVISION_MAX_EMAIL_BODY_TRUNCATION_SIZE = PROVISION_PAGE + 0x2B;
|
||||
public static final int PROVISION_MAX_EMAIL_HTML_BODY_TRUNCATION_SIZE = PROVISION_PAGE + 0x2C;
|
||||
public static final int PROVISION_REQUIRE_SIGNED_SMIME_MESSAGES = PROVISION_PAGE + 0x2D;
|
||||
public static final int PROVISION_REQUIRE_ENCRYPTED_SMIME_MESSAGES = PROVISION_PAGE + 0x2E;
|
||||
public static final int PROVISION_REQUIRE_SIGNED_SMIME_ALGORITHM = PROVISION_PAGE + 0x2F;
|
||||
public static final int PROVISION_REQUIRE_ENCRYPTION_SMIME_ALGORITHM = PROVISION_PAGE + 0x30;
|
||||
public static final int PROVISION_ALLOW_SMIME_ENCRYPTION_NEGOTIATION = PROVISION_PAGE + 0x31;
|
||||
public static final int PROVISION_ALLOW_SMIME_SOFT_CERTS = PROVISION_PAGE + 0x32;
|
||||
public static final int PROVISION_ALLOW_BROWSER = PROVISION_PAGE + 0x33;
|
||||
public static final int PROVISION_ALLOW_CONSUMER_EMAIL = PROVISION_PAGE + 0x34;
|
||||
public static final int PROVISION_ALLOW_REMOTE_DESKTOP = PROVISION_PAGE + 0x35;
|
||||
public static final int PROVISION_ALLOW_INTERNET_SHARING = PROVISION_PAGE + 0x36;
|
||||
public static final int PROVISION_UNAPPROVED_IN_ROM_APPLICATION_LIST = PROVISION_PAGE + 0x37;
|
||||
public static final int PROVISION_APPLICATION_NAME = PROVISION_PAGE + 0x38;
|
||||
public static final int PROVISION_APPROVED_APPLICATION_LIST = PROVISION_PAGE + 0x39;
|
||||
public static final int PROVISION_HASH = PROVISION_PAGE + 0x3A;
|
||||
|
||||
public static final int BASE_PAGE = BASE << PAGE_SHIFT;
|
||||
public static final int BASE_BODY_PREFERENCE = BASE_PAGE + 5;
|
||||
public static final int BASE_TYPE = BASE_PAGE + 6;
|
||||
@ -487,7 +547,8 @@ public class Tags {
|
||||
"Provision", "Policies", "Policy", "PolicyType", "PolicyKey", "Data", "ProvisionStatus",
|
||||
"RemoteWipe", "EASProvidionDoc", "DevicePasswordEnabled",
|
||||
"AlphanumericDevicePasswordRequired",
|
||||
"DeviceEncryptionEnabled", "-unused-", "AttachmentsEnabled", "MinDevicePasswordLength",
|
||||
"DeviceEncryptionEnabled", "PasswordRecoveryEnabled", "-unused-", "AttachmentsEnabled",
|
||||
"MinDevicePasswordLength",
|
||||
"MaxInactivityTimeDeviceLock", "MaxDevicePasswordFailedAttempts", "MaxAttachmentSize",
|
||||
"AllowSimpleDevicePassword", "DevicePasswordExpiration", "DevicePasswordHistory",
|
||||
"AllowStorageCard", "AllowCamera", "RequireDeviceEncryption",
|
||||
|
Loading…
Reference in New Issue
Block a user