mirror of
https://github.com/moparisthebest/k-9
synced 2024-11-23 18:02:15 -05:00
Added self-signed/mismatched domain handling for HTTP over SSL connections in WebDav.
This commit is contained in:
parent
4cc1ea489a
commit
51b6a03f8c
@ -11,8 +11,14 @@ import java.io.InputStream;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.io.UnsupportedEncodingException;
|
import java.io.UnsupportedEncodingException;
|
||||||
|
import java.net.MalformedURLException;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.net.URISyntaxException;
|
import java.net.URISyntaxException;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.security.GeneralSecurityException;
|
||||||
|
import java.security.KeyManagementException;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.security.SecureRandom;
|
||||||
import java.text.DateFormat;
|
import java.text.DateFormat;
|
||||||
import java.text.ParsePosition;
|
import java.text.ParsePosition;
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
@ -21,11 +27,16 @@ import java.util.Date;
|
|||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Stack;
|
import java.util.Stack;
|
||||||
|
|
||||||
|
import javax.net.ssl.SSLContext;
|
||||||
|
import javax.net.ssl.SSLException;
|
||||||
|
import javax.net.ssl.SSLSocketFactory;
|
||||||
|
import javax.net.ssl.TrustManager;
|
||||||
import javax.xml.parsers.SAXParser;
|
import javax.xml.parsers.SAXParser;
|
||||||
import javax.xml.parsers.SAXParserFactory;
|
import javax.xml.parsers.SAXParserFactory;
|
||||||
import javax.xml.parsers.ParserConfigurationException;
|
import javax.xml.parsers.ParserConfigurationException;
|
||||||
|
|
||||||
import com.android.email.Email;
|
import com.android.email.Email;
|
||||||
|
import com.android.email.mail.CertificateValidationException;
|
||||||
import com.android.email.mail.FetchProfile;
|
import com.android.email.mail.FetchProfile;
|
||||||
import com.android.email.mail.Flag;
|
import com.android.email.mail.Flag;
|
||||||
import com.android.email.mail.Folder;
|
import com.android.email.mail.Folder;
|
||||||
@ -37,6 +48,7 @@ import com.android.email.mail.internet.MimeBodyPart;
|
|||||||
import com.android.email.mail.internet.MimeMessage;
|
import com.android.email.mail.internet.MimeMessage;
|
||||||
import com.android.email.mail.internet.TextBody;
|
import com.android.email.mail.internet.TextBody;
|
||||||
import com.android.email.mail.transport.EOLConvertingOutputStream;
|
import com.android.email.mail.transport.EOLConvertingOutputStream;
|
||||||
|
import com.android.email.mail.transport.TrustedSocketFactory;
|
||||||
|
|
||||||
import org.apache.http.HttpResponse;
|
import org.apache.http.HttpResponse;
|
||||||
import org.apache.http.HttpEntity;
|
import org.apache.http.HttpEntity;
|
||||||
@ -45,6 +57,9 @@ import org.apache.http.client.entity.UrlEncodedFormEntity;
|
|||||||
import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
|
import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
|
||||||
import org.apache.http.client.methods.HttpGet;
|
import org.apache.http.client.methods.HttpGet;
|
||||||
import org.apache.http.client.methods.HttpPost;
|
import org.apache.http.client.methods.HttpPost;
|
||||||
|
import org.apache.http.conn.scheme.Scheme;
|
||||||
|
import org.apache.http.conn.scheme.SchemeRegistry;
|
||||||
|
import org.apache.http.conn.scheme.SocketFactory;
|
||||||
import org.apache.http.entity.StringEntity;
|
import org.apache.http.entity.StringEntity;
|
||||||
import org.apache.http.impl.client.DefaultHttpClient;
|
import org.apache.http.impl.client.DefaultHttpClient;
|
||||||
import org.apache.http.message.BasicNameValuePair;
|
import org.apache.http.message.BasicNameValuePair;
|
||||||
@ -77,6 +92,7 @@ public class WebDavStore extends Store {
|
|||||||
private String alias;
|
private String alias;
|
||||||
private String mPassword; /* Stores the password for authentications */
|
private String mPassword; /* Stores the password for authentications */
|
||||||
private String mUrl; /* Stores the base URL for the server */
|
private String mUrl; /* Stores the base URL for the server */
|
||||||
|
private String mHost; /* Stores the host name for the server */
|
||||||
|
|
||||||
private CookieStore mAuthCookies; /* Stores cookies from authentication */
|
private CookieStore mAuthCookies; /* Stores cookies from authentication */
|
||||||
private boolean mAuthenticated = false; /* Stores authentication state */
|
private boolean mAuthenticated = false; /* Stores authentication state */
|
||||||
@ -84,6 +100,8 @@ public class WebDavStore extends Store {
|
|||||||
private long mAuthTimeout = 5 * 60;
|
private long mAuthTimeout = 5 * 60;
|
||||||
|
|
||||||
private HashMap<String, WebDavFolder> mFolderList = new HashMap<String, WebDavFolder>();
|
private HashMap<String, WebDavFolder> mFolderList = new HashMap<String, WebDavFolder>();
|
||||||
|
private boolean mSecure;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* webdav://user:password@server:port CONNECTION_SECURITY_NONE
|
* webdav://user:password@server:port CONNECTION_SECURITY_NONE
|
||||||
* webdav+tls://user:password@server:port CONNECTION_SECURITY_TLS_OPTIONAL
|
* webdav+tls://user:password@server:port CONNECTION_SECURITY_TLS_OPTIONAL
|
||||||
@ -116,12 +134,11 @@ public class WebDavStore extends Store {
|
|||||||
throw new MessagingException("Unsupported protocol");
|
throw new MessagingException("Unsupported protocol");
|
||||||
}
|
}
|
||||||
|
|
||||||
String host = uri.getHost();
|
mHost = uri.getHost();
|
||||||
|
if (mHost.startsWith("http")) {
|
||||||
if (host.startsWith("http")) {
|
String[] hostParts = mHost.split("://", 2);
|
||||||
String[] hostParts = host.split("://", 2);
|
|
||||||
if (hostParts.length > 1) {
|
if (hostParts.length > 1) {
|
||||||
host = hostParts[1];
|
mHost = hostParts[1];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -129,9 +146,9 @@ public class WebDavStore extends Store {
|
|||||||
mConnectionSecurity == CONNECTION_SECURITY_SSL_REQUIRED ||
|
mConnectionSecurity == CONNECTION_SECURITY_SSL_REQUIRED ||
|
||||||
mConnectionSecurity == CONNECTION_SECURITY_TLS_OPTIONAL ||
|
mConnectionSecurity == CONNECTION_SECURITY_TLS_OPTIONAL ||
|
||||||
mConnectionSecurity == CONNECTION_SECURITY_SSL_OPTIONAL) {
|
mConnectionSecurity == CONNECTION_SECURITY_SSL_OPTIONAL) {
|
||||||
this.mUrl = "https://" + host;
|
this.mUrl = "https://" + mHost;
|
||||||
} else {
|
} else {
|
||||||
this.mUrl = "http://" + host;
|
this.mUrl = "http://" + mHost;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (uri.getUserInfo() != null) {
|
if (uri.getUserInfo() != null) {
|
||||||
@ -148,6 +165,7 @@ public class WebDavStore extends Store {
|
|||||||
mPassword = userInfoParts[1];
|
mPassword = userInfoParts[1];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
mSecure = mConnectionSecurity == CONNECTION_SECURITY_SSL_REQUIRED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -360,8 +378,9 @@ public class WebDavStore extends Store {
|
|||||||
/**
|
/**
|
||||||
* Performs Form Based authentication regardless of the current
|
* Performs Form Based authentication regardless of the current
|
||||||
* authentication state
|
* authentication state
|
||||||
|
* @throws MessagingException
|
||||||
*/
|
*/
|
||||||
public void authenticate() {
|
public void authenticate() throws MessagingException {
|
||||||
try {
|
try {
|
||||||
this.mAuthCookies = doAuthentication(this.mUsername, this.mPassword, this.mUrl);
|
this.mAuthCookies = doAuthentication(this.mUsername, this.mPassword, this.mUrl);
|
||||||
} catch (IOException ioe) {
|
} catch (IOException ioe) {
|
||||||
@ -398,9 +417,10 @@ public class WebDavStore extends Store {
|
|||||||
/**
|
/**
|
||||||
* Performs the Form Based Authentication
|
* Performs the Form Based Authentication
|
||||||
* Returns the CookieStore object for later use or null
|
* Returns the CookieStore object for later use or null
|
||||||
|
* @throws MessagingException
|
||||||
*/
|
*/
|
||||||
public CookieStore doAuthentication(String username, String password,
|
public CookieStore doAuthentication(String username, String password,
|
||||||
String url) throws IOException {
|
String url) throws IOException, MessagingException {
|
||||||
String authPath = "/exchweb/bin/auth/owaauth.dll";
|
String authPath = "/exchweb/bin/auth/owaauth.dll";
|
||||||
CookieStore cookies = null;
|
CookieStore cookies = null;
|
||||||
String[] urlParts = url.split("/");
|
String[] urlParts = url.split("/");
|
||||||
@ -414,9 +434,9 @@ public class WebDavStore extends Store {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
/* Browser Client */
|
/* Browser Client */
|
||||||
DefaultHttpClient httpclient = new DefaultHttpClient();
|
DefaultHttpClient httpclient = getTrustedHttpClient();
|
||||||
|
|
||||||
/* Post Method */
|
/* Post Method */
|
||||||
HttpPost httppost = new HttpPost(finalUrl + authPath);
|
HttpPost httppost = new HttpPost(finalUrl + authPath);
|
||||||
|
|
||||||
@ -469,6 +489,12 @@ public class WebDavStore extends Store {
|
|||||||
} catch (UnsupportedEncodingException uee) {
|
} catch (UnsupportedEncodingException uee) {
|
||||||
Log.e(Email.LOG_TAG, "Error encoding POST data for authencation");
|
Log.e(Email.LOG_TAG, "Error encoding POST data for authencation");
|
||||||
}
|
}
|
||||||
|
} catch (SSLException e) {
|
||||||
|
throw new CertificateValidationException(e.getMessage(), e);
|
||||||
|
} catch (GeneralSecurityException gse) {
|
||||||
|
throw new MessagingException(
|
||||||
|
"Unable to open connection to SMTP server due to security error.", gse);
|
||||||
|
}
|
||||||
return cookies;
|
return cookies;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -484,6 +510,15 @@ public class WebDavStore extends Store {
|
|||||||
return mUrl;
|
return mUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public DefaultHttpClient getTrustedHttpClient() throws KeyManagementException, NoSuchAlgorithmException{
|
||||||
|
DefaultHttpClient httpclient = new DefaultHttpClient();
|
||||||
|
SchemeRegistry reg = httpclient.getConnectionManager().getSchemeRegistry();
|
||||||
|
reg.unregister("https");
|
||||||
|
Scheme s = new Scheme("https",new TrustedSocketFactory(mHost,mSecure),443);
|
||||||
|
reg.register(s);
|
||||||
|
return httpclient;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Performs an httprequest to the supplied url using the supplied method.
|
* Performs an httprequest to the supplied url using the supplied method.
|
||||||
* messageBody and headers are optional as not all requests will need them.
|
* messageBody and headers are optional as not all requests will need them.
|
||||||
@ -495,15 +530,27 @@ public class WebDavStore extends Store {
|
|||||||
|
|
||||||
private ParsedDataSet processRequest(String url, String method, String messageBody, HashMap<String, String> headers, boolean needsParsing) {
|
private ParsedDataSet processRequest(String url, String method, String messageBody, HashMap<String, String> headers, boolean needsParsing) {
|
||||||
ParsedDataSet dataset = new ParsedDataSet();
|
ParsedDataSet dataset = new ParsedDataSet();
|
||||||
DefaultHttpClient httpclient = new DefaultHttpClient();
|
DefaultHttpClient httpclient;
|
||||||
|
|
||||||
if (url == null ||
|
if (url == null ||
|
||||||
method == null) {
|
method == null) {
|
||||||
return dataset;
|
return dataset;
|
||||||
}
|
}
|
||||||
|
try {
|
||||||
|
httpclient = getTrustedHttpClient();
|
||||||
|
} catch (KeyManagementException e) {
|
||||||
|
Log.e(Email.LOG_TAG, "Generated KeyManagementException during authentication" + e.getStackTrace());
|
||||||
|
return dataset;
|
||||||
|
} catch (NoSuchAlgorithmException e) {
|
||||||
|
Log.e(Email.LOG_TAG, "Generated NoSuchAlgorithmException during authentication" + e.getStackTrace());
|
||||||
|
return dataset;
|
||||||
|
}
|
||||||
if (needAuth()) {
|
if (needAuth()) {
|
||||||
|
try {
|
||||||
authenticate();
|
authenticate();
|
||||||
|
} catch (MessagingException e) {
|
||||||
|
Log.e(Email.LOG_TAG, "Generated MessagingException during authentication" + e.getStackTrace());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.mAuthenticated == false ||
|
if (this.mAuthenticated == false ||
|
||||||
@ -620,7 +667,11 @@ public class WebDavStore extends Store {
|
|||||||
* Perform an authentication to get the appropriate URLs in place again
|
* Perform an authentication to get the appropriate URLs in place again
|
||||||
*/
|
*/
|
||||||
if (needAuth()) {
|
if (needAuth()) {
|
||||||
|
try {
|
||||||
authenticate();
|
authenticate();
|
||||||
|
} catch (MessagingException e) {
|
||||||
|
Log.e(Email.LOG_TAG, "Generated MessagingException during authentication" + e.getStackTrace());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (encodedName.equals("INBOX")) {
|
if (encodedName.equals("INBOX")) {
|
||||||
@ -901,7 +952,14 @@ public class WebDavStore extends Store {
|
|||||||
* Fetches the full messages or up to lines lines and passes them to the message parser.
|
* Fetches the full messages or up to lines lines and passes them to the message parser.
|
||||||
*/
|
*/
|
||||||
private void fetchMessages(Message[] messages, MessageRetrievalListener listener, int lines) throws MessagingException {
|
private void fetchMessages(Message[] messages, MessageRetrievalListener listener, int lines) throws MessagingException {
|
||||||
DefaultHttpClient httpclient = new DefaultHttpClient();
|
DefaultHttpClient httpclient;
|
||||||
|
try {
|
||||||
|
httpclient = getTrustedHttpClient();
|
||||||
|
} catch (KeyManagementException e) {
|
||||||
|
throw new MessagingException("KeyManagement Exception in fetchMessages()."+ e.getStackTrace());
|
||||||
|
} catch (NoSuchAlgorithmException e) {
|
||||||
|
throw new MessagingException("NoSuchAlgorithm Exception in fetchMessages():" + e.getStackTrace());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* We can't hand off to processRequest() since we need the stream to parse.
|
* We can't hand off to processRequest() since we need the stream to parse.
|
||||||
|
@ -0,0 +1,56 @@
|
|||||||
|
package com.android.email.mail.transport;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.InetAddress;
|
||||||
|
import java.net.Socket;
|
||||||
|
import java.net.SocketAddress;
|
||||||
|
import java.net.UnknownHostException;
|
||||||
|
import java.security.KeyManagementException;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.security.SecureRandom;
|
||||||
|
|
||||||
|
import javax.net.ssl.SSLContext;
|
||||||
|
import javax.net.ssl.SSLSocketFactory;
|
||||||
|
import javax.net.ssl.TrustManager;
|
||||||
|
|
||||||
|
import org.apache.http.conn.ConnectTimeoutException;
|
||||||
|
import org.apache.http.conn.scheme.SocketFactory;
|
||||||
|
import org.apache.http.params.HttpParams;
|
||||||
|
|
||||||
|
import com.android.email.mail.store.TrustManagerFactory;
|
||||||
|
|
||||||
|
public class TrustedSocketFactory implements SocketFactory {
|
||||||
|
private SSLSocketFactory mSocketFactory;
|
||||||
|
private org.apache.http.conn.ssl.SSLSocketFactory mSchemeSocketFactory;
|
||||||
|
|
||||||
|
public TrustedSocketFactory(String host, boolean secure) throws NoSuchAlgorithmException, KeyManagementException{
|
||||||
|
SSLContext sslContext = SSLContext.getInstance("TLS");
|
||||||
|
sslContext.init(null, new TrustManager[] {
|
||||||
|
TrustManagerFactory.get(host, secure)
|
||||||
|
}, new SecureRandom());
|
||||||
|
mSocketFactory = sslContext.getSocketFactory();
|
||||||
|
mSchemeSocketFactory = org.apache.http.conn.ssl.SSLSocketFactory.getSocketFactory();
|
||||||
|
mSchemeSocketFactory.setHostnameVerifier(
|
||||||
|
org.apache.http.conn.ssl.SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Socket connectSocket(Socket sock, String host, int port,
|
||||||
|
InetAddress localAddress, int localPort, HttpParams params)
|
||||||
|
throws IOException, UnknownHostException, ConnectTimeoutException {
|
||||||
|
return mSchemeSocketFactory.connectSocket(sock, host, port, localAddress, localPort, params);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Socket createSocket() throws IOException {
|
||||||
|
// TODO Auto-generated method stub
|
||||||
|
return mSocketFactory.createSocket();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isSecure(Socket arg0) throws IllegalArgumentException {
|
||||||
|
// TODO Auto-generated method stub
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -16,6 +16,8 @@ import java.net.URI;
|
|||||||
import java.net.URISyntaxException;
|
import java.net.URISyntaxException;
|
||||||
import java.net.URLEncoder;
|
import java.net.URLEncoder;
|
||||||
import java.security.GeneralSecurityException;
|
import java.security.GeneralSecurityException;
|
||||||
|
import java.security.KeyManagementException;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
import java.security.SecureRandom;
|
import java.security.SecureRandom;
|
||||||
|
|
||||||
import javax.net.ssl.SSLContext;
|
import javax.net.ssl.SSLContext;
|
||||||
@ -118,7 +120,16 @@ public class WebDavTransport extends Transport {
|
|||||||
public void sendMessage(Message message) throws MessagingException {
|
public void sendMessage(Message message) throws MessagingException {
|
||||||
Log.d(Email.LOG_TAG, ">>> sendMessage called.");
|
Log.d(Email.LOG_TAG, ">>> sendMessage called.");
|
||||||
|
|
||||||
DefaultHttpClient httpclient = new DefaultHttpClient();
|
DefaultHttpClient httpclient;
|
||||||
|
try {
|
||||||
|
httpclient = store.getTrustedHttpClient();
|
||||||
|
} catch (KeyManagementException e) {
|
||||||
|
Log.e(Email.LOG_TAG, "KeyManagementException while creating HttpClient: " + e);
|
||||||
|
throw new MessagingException("KeyManagementException while creating HttpClient: " + e);
|
||||||
|
} catch (NoSuchAlgorithmException e) {
|
||||||
|
Log.e(Email.LOG_TAG, "NoSuchAlgorithmException while creating HttpClient: " + e);
|
||||||
|
throw new MessagingException("NoSuchAlgorithmException while creating HttpClient: " + e);
|
||||||
|
}
|
||||||
HttpGeneric httpmethod;
|
HttpGeneric httpmethod;
|
||||||
HttpResponse response;
|
HttpResponse response;
|
||||||
HttpEntity responseEntity;
|
HttpEntity responseEntity;
|
||||||
@ -160,11 +171,6 @@ public class WebDavTransport extends Transport {
|
|||||||
throw new IOException("Error sending message, status code was " + statusCode);
|
throw new IOException("Error sending message, status code was " + statusCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
//responseEntity = response.getEntity();
|
|
||||||
//DefaultHttpClient movehttpclient = new DefaultHttpClient();
|
|
||||||
//HttpGeneric movehttpmethod;
|
|
||||||
//HttpResponse moveresponse;
|
|
||||||
//HttpEntity moveresponseEntity;
|
|
||||||
httpmethod = store.new HttpGeneric(generateTempURI(subject));
|
httpmethod = store.new HttpGeneric(generateTempURI(subject));
|
||||||
httpmethod.setMethod("MOVE");
|
httpmethod.setMethod("MOVE");
|
||||||
httpmethod.setHeader("Destination", generateSendURI());
|
httpmethod.setHeader("Destination", generateSendURI());
|
||||||
|
Loading…
Reference in New Issue
Block a user