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:
Marc Blank 2012-07-31 15:47:49 -07:00
parent 691d4311a1
commit 7d5e2a7c08
14 changed files with 184 additions and 248 deletions

View File

@ -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
*/

View File

@ -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 {

View File

@ -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
}
/**

View File

@ -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));

View File

@ -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 });
}

View File

@ -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;

View File

@ -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();
}

View File

@ -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);

View File

@ -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) {

View File

@ -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) {

View File

@ -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];

View File

@ -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);

View File

@ -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];

View File

@ -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