Do "same certificate" checking when using "trust certificates"
* Refactor/simplify Transport/MailTransport * Add serverCert column to HostAuth table in EmailProvider * During first connection to server, save the server certificate in the HostAuth; on subsequent connections, ensure that the certificate presented has the same public key as the one stored * For now, we'll just fail to connect (with a CertificateException) if there's a mismatch TODO: Add some UI to handle different certificates Bug: 6888866 Change-Id: Ia79497e89eaad8d43617b50d3771121b2ed7f687
This commit is contained in:
parent
691d4311a1
commit
7d5e2a7c08
@ -52,16 +52,6 @@ public interface Transport extends Cloneable {
|
||||
*/
|
||||
public Transport clone();
|
||||
|
||||
/**
|
||||
* Sets the host
|
||||
*/
|
||||
public void setHost(String host);
|
||||
|
||||
/**
|
||||
* Sets the port
|
||||
*/
|
||||
public void setPort(int port);
|
||||
|
||||
/**
|
||||
* Returns the host or {@code null} if none was specified.
|
||||
*/
|
||||
@ -72,18 +62,6 @@ public interface Transport extends Cloneable {
|
||||
*/
|
||||
public int getPort();
|
||||
|
||||
/**
|
||||
* Set the desired security mode for this connection.
|
||||
* @param connectionSecurity A value indicating the desired security mode.
|
||||
* @param trustAllCertificates true to allow unverifiable certificates to be used
|
||||
*/
|
||||
public void setSecurity(int connectionSecurity, boolean trustAllCertificates);
|
||||
|
||||
/**
|
||||
* @return Returns the desired security mode for this connection.
|
||||
*/
|
||||
public int getSecurity();
|
||||
|
||||
/**
|
||||
* @return true if the security mode indicates that SSL is possible
|
||||
*/
|
||||
|
@ -1538,6 +1538,8 @@ public abstract class EmailContent {
|
||||
static final String CLIENT_CERT_ALIAS = "certAlias";
|
||||
// DEPRECATED - Will not be set or stored
|
||||
static final String ACCOUNT_KEY = "accountKey";
|
||||
// A blob containing an X509 server certificate
|
||||
static final String SERVER_CERT = "serverCert";
|
||||
}
|
||||
|
||||
public interface PolicyColumns {
|
||||
|
@ -67,6 +67,8 @@ public final class HostAuth extends EmailContent implements HostAuthColumns, Par
|
||||
public String mPassword;
|
||||
public String mDomain;
|
||||
public String mClientCertAlias = null;
|
||||
// NOTE: The server certificate is NEVER automatically retrieved from EmailProvider
|
||||
public byte[] mServerCert = null;
|
||||
|
||||
public static final int CONTENT_ID_COLUMN = 0;
|
||||
public static final int CONTENT_PROTOCOL_COLUMN = 1;
|
||||
@ -368,6 +370,7 @@ public final class HostAuth extends EmailContent implements HostAuthColumns, Par
|
||||
&& Utility.areStringsEqual(mPassword, that.mPassword)
|
||||
&& Utility.areStringsEqual(mDomain, that.mDomain)
|
||||
&& Utility.areStringsEqual(mClientCertAlias, that.mClientCertAlias);
|
||||
// We don't care about the server certificate for equals
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -60,9 +60,11 @@ public class EmailClientConnectionManager extends ThreadSafeClientConnManager {
|
||||
mTrackingKeyManager = keyManager;
|
||||
}
|
||||
|
||||
public static EmailClientConnectionManager newInstance(HttpParams params, boolean ssl,
|
||||
int port) {
|
||||
public static EmailClientConnectionManager newInstance(Context context, HttpParams params,
|
||||
HostAuth hostAuth) {
|
||||
TrackingKeyManager keyManager = new TrackingKeyManager();
|
||||
boolean ssl = hostAuth.shouldUseSsl();
|
||||
int port = hostAuth.mPort;
|
||||
|
||||
// Create a registry for our three schemes; http and https will use built-in factories
|
||||
SchemeRegistry registry = new SchemeRegistry();
|
||||
@ -70,10 +72,11 @@ public class EmailClientConnectionManager extends ThreadSafeClientConnManager {
|
||||
ssl ? STANDARD_PORT : port));
|
||||
// Register https with the secure factory
|
||||
registry.register(new Scheme("https",
|
||||
SSLUtils.getHttpSocketFactory(false, keyManager), ssl ? port : STANDARD_SSL_PORT));
|
||||
SSLUtils.getHttpSocketFactory(context, hostAuth, keyManager, false),
|
||||
ssl ? port : STANDARD_SSL_PORT));
|
||||
// Register the httpts scheme with our insecure factory
|
||||
registry.register(new Scheme("httpts",
|
||||
SSLUtils.getHttpSocketFactory(true /*insecure*/, keyManager),
|
||||
SSLUtils.getHttpSocketFactory(context, hostAuth, keyManager, true),
|
||||
ssl ? port : STANDARD_SSL_PORT));
|
||||
|
||||
return new EmailClientConnectionManager(params, registry, keyManager);
|
||||
@ -99,8 +102,8 @@ public class EmailClientConnectionManager extends ThreadSafeClientConnManager {
|
||||
}
|
||||
KeyManager keyManager =
|
||||
KeyChainKeyManager.fromAlias(context, hostAuth.mClientCertAlias);
|
||||
SSLCertificateSocketFactory underlying = SSLUtils.getSSLSocketFactory(
|
||||
hostAuth.shouldTrustAllServerCerts());
|
||||
SSLCertificateSocketFactory underlying = SSLUtils.getSSLSocketFactory(context, hostAuth,
|
||||
false);
|
||||
underlying.setKeyManagers(new KeyManager[] { keyManager });
|
||||
registry.register(
|
||||
new Scheme(schemeName, new SSLSocketFactory(underlying), hostAuth.mPort));
|
||||
|
@ -16,46 +16,139 @@
|
||||
|
||||
package com.android.emailcommon.utility;
|
||||
|
||||
import android.content.ContentUris;
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.net.SSLCertificateSocketFactory;
|
||||
import android.security.KeyChain;
|
||||
import android.security.KeyChainException;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.emailcommon.provider.EmailContent.HostAuthColumns;
|
||||
import com.android.emailcommon.provider.HostAuth;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.net.InetAddress;
|
||||
import java.net.Socket;
|
||||
import java.security.Principal;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.PublicKey;
|
||||
import java.security.cert.Certificate;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.security.cert.CertificateFactory;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.Arrays;
|
||||
|
||||
import javax.net.ssl.KeyManager;
|
||||
import javax.net.ssl.TrustManager;
|
||||
import javax.net.ssl.X509ExtendedKeyManager;
|
||||
import javax.net.ssl.X509TrustManager;
|
||||
|
||||
public class SSLUtils {
|
||||
private static SSLCertificateSocketFactory sInsecureFactory;
|
||||
// All secure factories are the same; all insecure factories are associated with HostAuth's
|
||||
private static SSLCertificateSocketFactory sSecureFactory;
|
||||
|
||||
private static final boolean LOG_ENABLED = false;
|
||||
private static final String TAG = "Email.Ssl";
|
||||
|
||||
/**
|
||||
* A trust manager specific to a particular HostAuth. The first time a server certificate is
|
||||
* encountered for the HostAuth, its certificate is saved; subsequent checks determine whether
|
||||
* the PublicKey of the certificate presented matches that of the saved certificate
|
||||
* TODO: UI to ask user about changed certificates
|
||||
*/
|
||||
private static class SameCertificateCheckingTrustManager implements X509TrustManager {
|
||||
private final HostAuth mHostAuth;
|
||||
private final Context mContext;
|
||||
// The public key associated with the HostAuth; we'll lazily initialize it
|
||||
private PublicKey mPublicKey;
|
||||
|
||||
SameCertificateCheckingTrustManager(Context context, HostAuth hostAuth) {
|
||||
mContext = context;
|
||||
mHostAuth = hostAuth;
|
||||
// We must load the server cert manually (the ContentCache won't handle blobs
|
||||
Cursor c = context.getContentResolver().query(HostAuth.CONTENT_URI,
|
||||
new String[] {HostAuthColumns.SERVER_CERT}, HostAuth.ID + "=?",
|
||||
new String[] {Long.toString(hostAuth.mId)}, null);
|
||||
if (c != null) {
|
||||
try {
|
||||
if (c.moveToNext()) {
|
||||
mHostAuth.mServerCert = c.getBlob(0);
|
||||
}
|
||||
} finally {
|
||||
c.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkClientTrusted(X509Certificate[] chain, String authType)
|
||||
throws CertificateException {
|
||||
// We don't check client certificates
|
||||
throw new CertificateException("We don't check client certificates");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkServerTrusted(X509Certificate[] chain, String authType)
|
||||
throws CertificateException {
|
||||
if (chain.length == 0) {
|
||||
throw new CertificateException("No certificates?");
|
||||
} else {
|
||||
X509Certificate serverCert = chain[0];
|
||||
if (mHostAuth.mServerCert != null) {
|
||||
// Compare with the current public key
|
||||
if (mPublicKey == null) {
|
||||
ByteArrayInputStream bais = new ByteArrayInputStream(mHostAuth.mServerCert);
|
||||
Certificate storedCert =
|
||||
CertificateFactory.getInstance("X509").generateCertificate(bais);
|
||||
mPublicKey = storedCert.getPublicKey();
|
||||
try {
|
||||
bais.close();
|
||||
} catch (IOException e) {
|
||||
// Yeah, right.
|
||||
}
|
||||
}
|
||||
if (!mPublicKey.equals(serverCert.getPublicKey())) {
|
||||
throw new CertificateException(
|
||||
"PublicKey has changed since initial connection!");
|
||||
}
|
||||
} else {
|
||||
// First time; save this away
|
||||
byte[] encodedCert = serverCert.getEncoded();
|
||||
mHostAuth.mServerCert = encodedCert;
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(HostAuthColumns.SERVER_CERT, encodedCert);
|
||||
mContext.getContentResolver().update(
|
||||
ContentUris.withAppendedId(HostAuth.CONTENT_URI, mHostAuth.mId),
|
||||
values, null, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public X509Certificate[] getAcceptedIssuers() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@link javax.net.ssl.SSLSocketFactory}.
|
||||
* Optionally bypass all SSL certificate checks.
|
||||
*
|
||||
* @param insecure if true, bypass all SSL certificate checks
|
||||
*/
|
||||
public synchronized static SSLCertificateSocketFactory getSSLSocketFactory(
|
||||
boolean insecure) {
|
||||
public synchronized static SSLCertificateSocketFactory getSSLSocketFactory(Context context,
|
||||
HostAuth hostAuth, boolean insecure) {
|
||||
if (insecure) {
|
||||
if (sInsecureFactory == null) {
|
||||
sInsecureFactory = (SSLCertificateSocketFactory)
|
||||
SSLCertificateSocketFactory.getInsecure(0, null);
|
||||
}
|
||||
return sInsecureFactory;
|
||||
SSLCertificateSocketFactory insecureFactory = (SSLCertificateSocketFactory)
|
||||
SSLCertificateSocketFactory.getDefault(0, null);
|
||||
insecureFactory.setTrustManagers(
|
||||
new TrustManager[] {
|
||||
new SameCertificateCheckingTrustManager(context, hostAuth)});
|
||||
return insecureFactory;
|
||||
} else {
|
||||
if (sSecureFactory == null) {
|
||||
sSecureFactory = (SSLCertificateSocketFactory)
|
||||
@ -69,8 +162,9 @@ public class SSLUtils {
|
||||
* Returns a {@link org.apache.http.conn.ssl.SSLSocketFactory SSLSocketFactory} for use with the
|
||||
* Apache HTTP stack.
|
||||
*/
|
||||
public static SSLSocketFactory getHttpSocketFactory(boolean insecure, KeyManager keyManager) {
|
||||
SSLCertificateSocketFactory underlying = getSSLSocketFactory(insecure);
|
||||
public static SSLSocketFactory getHttpSocketFactory(Context context, HostAuth hostAuth,
|
||||
KeyManager keyManager, boolean insecure) {
|
||||
SSLCertificateSocketFactory underlying = getSSLSocketFactory(context, hostAuth, insecure);
|
||||
if (keyManager != null) {
|
||||
underlying.setKeyManagers(new KeyManager[] { keyManager });
|
||||
}
|
||||
|
@ -204,9 +204,9 @@ public abstract class SyncManager extends Service implements Runnable {
|
||||
protected static Thread sServiceThread = null;
|
||||
// Cached unique device id
|
||||
protected static String sDeviceId = null;
|
||||
// HashMap of ConnectionManagers that all EAS threads can use (by ssl/port pair)
|
||||
private static HashMap<Integer, EmailClientConnectionManager> sClientConnectionManagers =
|
||||
new HashMap<Integer, EmailClientConnectionManager>();
|
||||
// HashMap of ConnectionManagers that all EAS threads can use (by HostAuth id)
|
||||
private static HashMap<Long, EmailClientConnectionManager> sClientConnectionManagers =
|
||||
new HashMap<Long, EmailClientConnectionManager>();
|
||||
// Count of ClientConnectionManager shutdowns
|
||||
private static volatile int sClientConnectionManagerShutdownCount = 0;
|
||||
|
||||
@ -877,11 +877,10 @@ public abstract class SyncManager extends Service implements Runnable {
|
||||
}
|
||||
};
|
||||
|
||||
static public synchronized EmailClientConnectionManager getClientConnectionManager(boolean ssl,
|
||||
int port) {
|
||||
static public synchronized EmailClientConnectionManager getClientConnectionManager(
|
||||
Context context, HostAuth hostAuth) {
|
||||
// We'll use a different connection manager for each ssl/port pair
|
||||
int key = (ssl ? 0x10000 : 0) + port;
|
||||
EmailClientConnectionManager mgr = sClientConnectionManagers.get(key);
|
||||
EmailClientConnectionManager mgr = sClientConnectionManagers.get(hostAuth.mId);
|
||||
if (mgr == null) {
|
||||
// After two tries, kill the process. Most likely, this will happen in the background
|
||||
// The service will restart itself after about 5 seconds
|
||||
@ -892,9 +891,11 @@ public abstract class SyncManager extends Service implements Runnable {
|
||||
HttpParams params = new BasicHttpParams();
|
||||
params.setIntParameter(ConnManagerPNames.MAX_TOTAL_CONNECTIONS, 25);
|
||||
params.setParameter(ConnManagerPNames.MAX_CONNECTIONS_PER_ROUTE, sConnPerRoute);
|
||||
mgr = EmailClientConnectionManager.newInstance(params, ssl, port);
|
||||
boolean ssl = hostAuth.shouldUseSsl();
|
||||
int port = hostAuth.mPort;
|
||||
mgr = EmailClientConnectionManager.newInstance(context, params, hostAuth);
|
||||
log("Creating connection manager for port " + port + ", ssl: " + ssl);
|
||||
sClientConnectionManagers.put(key, mgr);
|
||||
sClientConnectionManagers.put(hostAuth.mId, mgr);
|
||||
}
|
||||
// Null is a valid return result if we get an exception
|
||||
return mgr;
|
||||
|
@ -1600,8 +1600,8 @@ public class Imap2SyncService extends AbstractSyncService {
|
||||
if (tlsSocket != null) {
|
||||
// Start secure connection on top of existing one
|
||||
boolean trust = (hostAuth.mFlags & HostAuth.FLAG_TRUST_ALL) != 0;
|
||||
socket = SSLUtils.getSSLSocketFactory(trust).createSocket(tlsSocket,
|
||||
hostAuth.mAddress, hostAuth.mPort, true);
|
||||
socket = SSLUtils.getSSLSocketFactory(mContext, hostAuth, trust)
|
||||
.createSocket(tlsSocket, hostAuth.mAddress, hostAuth.mPort, true);
|
||||
|
||||
} else {
|
||||
socket = getSocket(hostAuth);
|
||||
@ -2241,7 +2241,7 @@ public class Imap2SyncService extends AbstractSyncService {
|
||||
boolean trust = (hostAuth.mFlags & HostAuth.FLAG_TRUST_ALL) != 0;
|
||||
SocketAddress socketAddress = new InetSocketAddress(hostAuth.mAddress, hostAuth.mPort);
|
||||
if (ssl) {
|
||||
socket = SSLUtils.getSSLSocketFactory(trust).createSocket();
|
||||
socket = SSLUtils.getSSLSocketFactory(mContext, hostAuth, trust).createSocket();
|
||||
} else {
|
||||
socket = new Socket();
|
||||
}
|
||||
|
@ -20,8 +20,10 @@ import com.android.emailcommon.Logging;
|
||||
import com.android.emailcommon.mail.CertificateValidationException;
|
||||
import com.android.emailcommon.mail.MessagingException;
|
||||
import com.android.emailcommon.mail.Transport;
|
||||
import com.android.emailcommon.provider.HostAuth;
|
||||
import com.android.emailcommon.utility.SSLUtils;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
@ -55,8 +57,8 @@ public class MailTransport implements Transport {
|
||||
private static final HostnameVerifier HOSTNAME_VERIFIER =
|
||||
HttpsURLConnection.getDefaultHostnameVerifier();
|
||||
|
||||
private String mHost;
|
||||
private int mPort;
|
||||
private final HostAuth mHostAuth;
|
||||
private final Context mContext;
|
||||
private String[] mUserInfoParts;
|
||||
|
||||
/**
|
||||
@ -75,14 +77,11 @@ public class MailTransport implements Transport {
|
||||
private OutputStream mOut;
|
||||
private boolean mLog = true; // STOPSHIP Don't ship with this set to true
|
||||
|
||||
/**
|
||||
* Simple constructor for starting from scratch. Call setUri() and setSecurity() to
|
||||
* complete the configuration.
|
||||
* @param debugLabel Label used for Log.d calls
|
||||
*/
|
||||
public MailTransport(boolean log) {
|
||||
|
||||
public MailTransport(Context context, boolean log, HostAuth hostAuth) {
|
||||
super();
|
||||
mLog = log;
|
||||
mContext = context;
|
||||
mHostAuth = hostAuth;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -92,48 +91,17 @@ public class MailTransport implements Transport {
|
||||
*/
|
||||
@Override
|
||||
public Transport clone() {
|
||||
MailTransport newObject = new MailTransport(mLog);
|
||||
|
||||
newObject.mLog = mLog;
|
||||
newObject.mHost = mHost;
|
||||
newObject.mPort = mPort;
|
||||
if (mUserInfoParts != null) {
|
||||
newObject.mUserInfoParts = mUserInfoParts.clone();
|
||||
}
|
||||
newObject.mConnectionSecurity = mConnectionSecurity;
|
||||
newObject.mTrustCertificates = mTrustCertificates;
|
||||
return newObject;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setHost(String host) {
|
||||
mHost = host;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPort(int port) {
|
||||
mPort = port;
|
||||
return new MailTransport(mContext, mLog, mHostAuth);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHost() {
|
||||
return mHost;
|
||||
return mHostAuth.mAddress;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPort() {
|
||||
return mPort;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSecurity(int connectionSecurity, boolean trustAllCertificates) {
|
||||
mConnectionSecurity = connectionSecurity;
|
||||
mTrustCertificates = trustAllCertificates;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSecurity() {
|
||||
return mConnectionSecurity;
|
||||
return mHostAuth.mPort;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -165,7 +133,8 @@ public class MailTransport implements Transport {
|
||||
try {
|
||||
SocketAddress socketAddress = new InetSocketAddress(getHost(), getPort());
|
||||
if (canTrySslSecurity()) {
|
||||
mSocket = SSLUtils.getSSLSocketFactory(canTrustAllCertificates()).createSocket();
|
||||
mSocket = SSLUtils.getSSLSocketFactory(
|
||||
mContext, mHostAuth, canTrustAllCertificates()).createSocket();
|
||||
} else {
|
||||
mSocket = new Socket();
|
||||
}
|
||||
@ -201,7 +170,7 @@ public class MailTransport implements Transport {
|
||||
@Override
|
||||
public void reopenTls() throws MessagingException {
|
||||
try {
|
||||
mSocket = SSLUtils.getSSLSocketFactory(canTrustAllCertificates())
|
||||
mSocket = SSLUtils.getSSLSocketFactory(mContext, mHostAuth, canTrustAllCertificates())
|
||||
.createSocket(mSocket, getHost(), getPort(), true);
|
||||
mSocket.setSoTimeout(SOCKET_READ_TIMEOUT);
|
||||
mIn = new BufferedInputStream(mSocket.getInputStream(), 1024);
|
||||
|
@ -60,26 +60,7 @@ public class SmtpSender {
|
||||
mContext = context;
|
||||
mLog = log;
|
||||
HostAuth sendAuth = account.getOrCreateHostAuthSend(context);
|
||||
// defaults, which can be changed by security modifiers
|
||||
int connectionSecurity = Transport.CONNECTION_SECURITY_NONE;
|
||||
int defaultPort = DEFAULT_SMTP_PORT;
|
||||
|
||||
// check for security flags and apply changes
|
||||
if ((sendAuth.mFlags & HostAuth.FLAG_SSL) != 0) {
|
||||
connectionSecurity = Transport.CONNECTION_SECURITY_SSL;
|
||||
defaultPort = DEFAULT_SMTP_SSL_PORT;
|
||||
} else if ((sendAuth.mFlags & HostAuth.FLAG_TLS) != 0) {
|
||||
connectionSecurity = Transport.CONNECTION_SECURITY_TLS;
|
||||
}
|
||||
boolean trustCertificates = ((sendAuth.mFlags & HostAuth.FLAG_TRUST_ALL) != 0);
|
||||
int port = defaultPort;
|
||||
if (sendAuth.mPort != HostAuth.PORT_UNKNOWN) {
|
||||
port = sendAuth.mPort;
|
||||
}
|
||||
mTransport = new MailTransport(mLog);
|
||||
mTransport.setHost(sendAuth.mAddress);
|
||||
mTransport.setPort(port);
|
||||
mTransport.setSecurity(connectionSecurity, trustCertificates);
|
||||
mTransport = new MailTransport(context, mLog, sendAuth);
|
||||
|
||||
String[] userInfoParts = sendAuth.getLogin();
|
||||
if (userInfoParts != null) {
|
||||
|
@ -110,26 +110,7 @@ public class ImapStore extends Store {
|
||||
if (recvAuth == null || !HostAuth.LEGACY_SCHEME_IMAP.equalsIgnoreCase(recvAuth.mProtocol)) {
|
||||
throw new MessagingException("Unsupported protocol");
|
||||
}
|
||||
// defaults, which can be changed by security modifiers
|
||||
int connectionSecurity = Transport.CONNECTION_SECURITY_NONE;
|
||||
int defaultPort = 143;
|
||||
|
||||
// check for security flags and apply changes
|
||||
if ((recvAuth.mFlags & HostAuth.FLAG_SSL) != 0) {
|
||||
connectionSecurity = Transport.CONNECTION_SECURITY_SSL;
|
||||
defaultPort = 993;
|
||||
} else if ((recvAuth.mFlags & HostAuth.FLAG_TLS) != 0) {
|
||||
connectionSecurity = Transport.CONNECTION_SECURITY_TLS;
|
||||
}
|
||||
boolean trustCertificates = ((recvAuth.mFlags & HostAuth.FLAG_TRUST_ALL) != 0);
|
||||
int port = defaultPort;
|
||||
if (recvAuth.mPort != HostAuth.PORT_UNKNOWN) {
|
||||
port = recvAuth.mPort;
|
||||
}
|
||||
mTransport = new MailTransport("IMAP");
|
||||
mTransport.setHost(recvAuth.mAddress);
|
||||
mTransport.setPort(port);
|
||||
mTransport.setSecurity(connectionSecurity, trustCertificates);
|
||||
mTransport = new MailTransport(context, "IMAP", recvAuth);
|
||||
|
||||
String[] userInfo = recvAuth.getLogin();
|
||||
if (userInfo != null) {
|
||||
|
@ -103,28 +103,7 @@ public class Pop3Store extends Store {
|
||||
if (recvAuth == null || !HostAuth.LEGACY_SCHEME_POP3.equalsIgnoreCase(recvAuth.mProtocol)) {
|
||||
throw new MessagingException("Unsupported protocol");
|
||||
}
|
||||
// defaults, which can be changed by security modifiers
|
||||
int connectionSecurity = Transport.CONNECTION_SECURITY_NONE;
|
||||
int defaultPort = 110;
|
||||
|
||||
// check for security flags and apply changes
|
||||
if ((recvAuth.mFlags & HostAuth.FLAG_SSL) != 0) {
|
||||
connectionSecurity = Transport.CONNECTION_SECURITY_SSL;
|
||||
defaultPort = 995;
|
||||
} else if ((recvAuth.mFlags & HostAuth.FLAG_TLS) != 0) {
|
||||
connectionSecurity = Transport.CONNECTION_SECURITY_TLS;
|
||||
}
|
||||
boolean trustCertificates = ((recvAuth.mFlags & HostAuth.FLAG_TRUST_ALL) != 0);
|
||||
|
||||
int port = defaultPort;
|
||||
if (recvAuth.mPort != HostAuth.PORT_UNKNOWN) {
|
||||
port = recvAuth.mPort;
|
||||
}
|
||||
mTransport = new MailTransport("POP3");
|
||||
mTransport.setHost(recvAuth.mAddress);
|
||||
mTransport.setPort(port);
|
||||
mTransport.setSecurity(connectionSecurity, trustCertificates);
|
||||
|
||||
mTransport = new MailTransport(context, "POP3", recvAuth);
|
||||
String[] userInfoParts = recvAuth.getLogin();
|
||||
if (userInfoParts != null) {
|
||||
mUsername = userInfoParts[0];
|
||||
|
@ -21,8 +21,10 @@ import com.android.emailcommon.Logging;
|
||||
import com.android.emailcommon.mail.CertificateValidationException;
|
||||
import com.android.emailcommon.mail.MessagingException;
|
||||
import com.android.emailcommon.mail.Transport;
|
||||
import com.android.emailcommon.provider.HostAuth;
|
||||
import com.android.emailcommon.utility.SSLUtils;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
@ -56,101 +58,54 @@ public class MailTransport implements Transport {
|
||||
private static final HostnameVerifier HOSTNAME_VERIFIER =
|
||||
HttpsURLConnection.getDefaultHostnameVerifier();
|
||||
|
||||
private String mDebugLabel;
|
||||
|
||||
private String mHost;
|
||||
private int mPort;
|
||||
private String[] mUserInfoParts;
|
||||
|
||||
/**
|
||||
* One of the {@code Transport.CONNECTION_SECURITY_*} values.
|
||||
*/
|
||||
private int mConnectionSecurity;
|
||||
|
||||
/**
|
||||
* Whether or not to trust all server certificates (i.e. skip host verification) in SSL
|
||||
* handshakes
|
||||
*/
|
||||
private boolean mTrustCertificates;
|
||||
private final String mDebugLabel;
|
||||
private final Context mContext;
|
||||
private final HostAuth mHostAuth;
|
||||
|
||||
private Socket mSocket;
|
||||
private InputStream mIn;
|
||||
private OutputStream mOut;
|
||||
|
||||
/**
|
||||
* Simple constructor for starting from scratch. Call setUri() and setSecurity() to
|
||||
* complete the configuration.
|
||||
* @param debugLabel Label used for Log.d calls
|
||||
*/
|
||||
public MailTransport(String debugLabel) {
|
||||
public MailTransport(Context context, String debugLabel, HostAuth hostAuth) {
|
||||
super();
|
||||
mContext = context;
|
||||
mDebugLabel = debugLabel;
|
||||
mHostAuth = hostAuth;
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* Returns a new transport, using the current transport as a model. The new transport is
|
||||
* configured identically (as if {@link #setSecurity(int, boolean)}, {@link #setPort(int)}
|
||||
* and {@link #setHost(String)} were invoked), but not opened or connected in any way.
|
||||
*/
|
||||
@Override
|
||||
public Transport clone() {
|
||||
MailTransport newObject = new MailTransport(mDebugLabel);
|
||||
|
||||
newObject.mDebugLabel = mDebugLabel;
|
||||
newObject.mHost = mHost;
|
||||
newObject.mPort = mPort;
|
||||
if (mUserInfoParts != null) {
|
||||
newObject.mUserInfoParts = mUserInfoParts.clone();
|
||||
}
|
||||
newObject.mConnectionSecurity = mConnectionSecurity;
|
||||
newObject.mTrustCertificates = mTrustCertificates;
|
||||
return newObject;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setHost(String host) {
|
||||
mHost = host;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPort(int port) {
|
||||
mPort = port;
|
||||
return new MailTransport(mContext, mDebugLabel, mHostAuth);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHost() {
|
||||
return mHost;
|
||||
return mHostAuth.mAddress;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPort() {
|
||||
return mPort;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSecurity(int connectionSecurity, boolean trustAllCertificates) {
|
||||
mConnectionSecurity = connectionSecurity;
|
||||
mTrustCertificates = trustAllCertificates;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSecurity() {
|
||||
return mConnectionSecurity;
|
||||
return mHostAuth.mPort;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canTrySslSecurity() {
|
||||
return mConnectionSecurity == Transport.CONNECTION_SECURITY_SSL;
|
||||
return (mHostAuth.mFlags & HostAuth.FLAG_SSL) != 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canTryTlsSecurity() {
|
||||
return mConnectionSecurity == Transport.CONNECTION_SECURITY_TLS;
|
||||
return (mHostAuth.mFlags & HostAuth.FLAG_TLS) != 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canTrustAllCertificates() {
|
||||
return mTrustCertificates;
|
||||
return (mHostAuth.mFlags & HostAuth.FLAG_TRUST_ALL) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -167,7 +122,8 @@ public class MailTransport implements Transport {
|
||||
try {
|
||||
SocketAddress socketAddress = new InetSocketAddress(getHost(), getPort());
|
||||
if (canTrySslSecurity()) {
|
||||
mSocket = SSLUtils.getSSLSocketFactory(canTrustAllCertificates()).createSocket();
|
||||
mSocket = SSLUtils.getSSLSocketFactory(
|
||||
mContext, mHostAuth, canTrustAllCertificates()).createSocket();
|
||||
} else {
|
||||
mSocket = new Socket();
|
||||
}
|
||||
@ -203,7 +159,7 @@ public class MailTransport implements Transport {
|
||||
@Override
|
||||
public void reopenTls() throws MessagingException {
|
||||
try {
|
||||
mSocket = SSLUtils.getSSLSocketFactory(canTrustAllCertificates())
|
||||
mSocket = SSLUtils.getSSLSocketFactory(mContext, mHostAuth, canTrustAllCertificates())
|
||||
.createSocket(mSocket, getHost(), getPort(), true);
|
||||
mSocket.setSoTimeout(SOCKET_READ_TIMEOUT);
|
||||
mIn = new BufferedInputStream(mSocket.getInputStream(), 1024);
|
||||
|
@ -46,9 +46,6 @@ import javax.net.ssl.SSLException;
|
||||
*/
|
||||
public class SmtpSender extends Sender {
|
||||
|
||||
private static final int DEFAULT_SMTP_PORT = 587;
|
||||
private static final int DEFAULT_SMTP_SSL_PORT = 465;
|
||||
|
||||
private final Context mContext;
|
||||
private Transport mTransport;
|
||||
private String mUsername;
|
||||
@ -70,27 +67,7 @@ public class SmtpSender extends Sender {
|
||||
if (sendAuth == null || !"smtp".equalsIgnoreCase(sendAuth.mProtocol)) {
|
||||
throw new MessagingException("Unsupported protocol");
|
||||
}
|
||||
// defaults, which can be changed by security modifiers
|
||||
int connectionSecurity = Transport.CONNECTION_SECURITY_NONE;
|
||||
int defaultPort = DEFAULT_SMTP_PORT;
|
||||
|
||||
// check for security flags and apply changes
|
||||
if ((sendAuth.mFlags & HostAuth.FLAG_SSL) != 0) {
|
||||
connectionSecurity = Transport.CONNECTION_SECURITY_SSL;
|
||||
defaultPort = DEFAULT_SMTP_SSL_PORT;
|
||||
} else if ((sendAuth.mFlags & HostAuth.FLAG_TLS) != 0) {
|
||||
connectionSecurity = Transport.CONNECTION_SECURITY_TLS;
|
||||
}
|
||||
boolean trustCertificates = ((sendAuth.mFlags & HostAuth.FLAG_TRUST_ALL) != 0);
|
||||
int port = defaultPort;
|
||||
if (sendAuth.mPort != HostAuth.PORT_UNKNOWN) {
|
||||
port = sendAuth.mPort;
|
||||
}
|
||||
mTransport = new MailTransport("IMAP");
|
||||
mTransport.setHost(sendAuth.mAddress);
|
||||
mTransport.setPort(port);
|
||||
mTransport.setSecurity(connectionSecurity, trustCertificates);
|
||||
|
||||
mTransport = new MailTransport(context, "SMTP", sendAuth);
|
||||
String[] userInfoParts = sendAuth.getLogin();
|
||||
if (userInfoParts != null) {
|
||||
mUsername = userInfoParts[0];
|
||||
|
@ -127,8 +127,9 @@ public final class DBHelper {
|
||||
// Version 101 SHOULD NOT BE USED
|
||||
// Version 102&103: Add hierarchicalName to Mailbox
|
||||
// Version 104&105: add syncData to Message
|
||||
// Version 106: Add certificate to HostAuth
|
||||
|
||||
public static final int DATABASE_VERSION = 105;
|
||||
public static final int DATABASE_VERSION = 106;
|
||||
|
||||
// Any changes to the database format *must* include update-in-place code.
|
||||
// Original version: 2
|
||||
@ -373,7 +374,8 @@ public final class DBHelper {
|
||||
+ HostAuthColumns.PASSWORD + " text, "
|
||||
+ HostAuthColumns.DOMAIN + " text, "
|
||||
+ HostAuthColumns.ACCOUNT_KEY + " integer,"
|
||||
+ HostAuthColumns.CLIENT_CERT_ALIAS + " text"
|
||||
+ HostAuthColumns.CLIENT_CERT_ALIAS + " text,"
|
||||
+ HostAuthColumns.SERVER_CERT + "blob"
|
||||
+ ");";
|
||||
db.execSQL("create table " + HostAuth.TABLE_NAME + s);
|
||||
}
|
||||
@ -970,7 +972,7 @@ public final class DBHelper {
|
||||
+ " add " + MailboxColumns.HIERARCHICAL_NAME + " text");
|
||||
} catch (SQLException e) {
|
||||
// Shouldn't be needed unless we're debugging and interrupt the process
|
||||
Log.w(TAG, "Exception upgrading EmailProviderBody.db from v10x to v103", e);
|
||||
Log.w(TAG, "Exception upgrading EmailProvider.db from v10x to v103", e);
|
||||
}
|
||||
oldVersion = 103;
|
||||
}
|
||||
@ -980,7 +982,7 @@ public final class DBHelper {
|
||||
+ " add " + MessageColumns.SYNC_DATA + " text");
|
||||
} catch (SQLException e) {
|
||||
// Shouldn't be needed unless we're debugging and interrupt the process
|
||||
Log.w(TAG, "Exception upgrading EmailProviderBody.db from v103 to v104", e);
|
||||
Log.w(TAG, "Exception upgrading EmailProvider.db from v103 to v104", e);
|
||||
}
|
||||
oldVersion = 104;
|
||||
}
|
||||
@ -992,10 +994,20 @@ public final class DBHelper {
|
||||
+ " add " + MessageColumns.SYNC_DATA + " text");
|
||||
} catch (SQLException e) {
|
||||
// Shouldn't be needed unless we're debugging and interrupt the process
|
||||
Log.w(TAG, "Exception upgrading EmailProviderBody.db from v104 to v105", e);
|
||||
Log.w(TAG, "Exception upgrading EmailProvider.db from v104 to v105", e);
|
||||
}
|
||||
oldVersion = 105;
|
||||
}
|
||||
if (oldVersion == 105) {
|
||||
try {
|
||||
db.execSQL("alter table " + HostAuth.TABLE_NAME
|
||||
+ " add " + HostAuthColumns.SERVER_CERT + " blob");
|
||||
} catch (SQLException e) {
|
||||
// Shouldn't be needed unless we're debugging and interrupt the process
|
||||
Log.w(TAG, "Exception upgrading EmailProvider.db from v105 to v106", e);
|
||||
}
|
||||
oldVersion = 106;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
Loading…
Reference in New Issue
Block a user