1
0
mirror of https://github.com/moparisthebest/k-9 synced 2024-11-23 18:02:15 -05:00

self-signed cert support for WebDav.

This commit is contained in:
Bradley Young 2009-01-05 00:05:43 +00:00
parent 1a7539af1d
commit 8221094570
3 changed files with 107 additions and 76 deletions

View File

@ -107,7 +107,6 @@ public final class TrustManagerFactory {
Application app = Email.app; Application app = Email.app;
keyStoreFile = new File(app.getDir("KeyStore", Context.MODE_PRIVATE) + File.separator + "KeyStore.bks"); keyStoreFile = new File(app.getDir("KeyStore", Context.MODE_PRIVATE) + File.separator + "KeyStore.bks");
keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
//TODO: read store from disk.
java.io.FileInputStream fis; java.io.FileInputStream fis;
try { try {
fis = new java.io.FileInputStream(keyStoreFile); fis = new java.io.FileInputStream(keyStoreFile);
@ -116,6 +115,9 @@ public final class TrustManagerFactory {
} }
try { try {
keyStore.load(fis, "".toCharArray()); keyStore.load(fis, "".toCharArray());
//if (fis != null) {
// fis.close();
//}
} catch (IOException e) { } catch (IOException e) {
Log.e(LOG_TAG, "KeyStore IOException while initializing TrustManagerFactory ", e); Log.e(LOG_TAG, "KeyStore IOException while initializing TrustManagerFactory ", e);
keyStore = null; keyStore = null;

View File

@ -1,39 +1,51 @@
package com.android.email.mail.store; package com.android.email.mail.store;
import java.io.BufferedReader; import java.io.BufferedReader;
import java.io.InputStreamReader;
import android.util.Log;
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream; import java.io.InputStream;
import java.io.InputStreamReader;
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.GeneralSecurityException;
import java.security.KeyManagementException; import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.text.DateFormat; import java.text.DateFormat;
import java.text.ParsePosition;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Date; 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.SSLException;
import javax.net.ssl.SSLSocketFactory; import javax.xml.parsers.ParserConfigurationException;
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 org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.Credentials;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.CookieStore;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
import org.apache.http.client.methods.HttpGet;
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.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.DefaultHandler;
import android.util.Log;
import com.android.email.Email; import com.android.email.Email;
import com.android.email.mail.CertificateValidationException; import com.android.email.mail.CertificateValidationException;
@ -44,37 +56,9 @@ import com.android.email.mail.Message;
import com.android.email.mail.MessageRetrievalListener; import com.android.email.mail.MessageRetrievalListener;
import com.android.email.mail.MessagingException; import com.android.email.mail.MessagingException;
import com.android.email.mail.Store; import com.android.email.mail.Store;
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.transport.EOLConvertingOutputStream;
import com.android.email.mail.transport.TrustedSocketFactory; import com.android.email.mail.transport.TrustedSocketFactory;
import org.apache.http.HttpResponse;
import org.apache.http.HttpEntity;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.Credentials;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.CookieStore;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
import org.apache.http.client.methods.HttpGet;
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.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.DefaultHandler;
/** /**
* <pre> * <pre>
* Uses WebDAV formatted HTTP calls to an MS Exchange server to fetch emails * Uses WebDAV formatted HTTP calls to an MS Exchange server to fetch emails
@ -107,6 +91,7 @@ public class WebDavStore extends Store {
private HashMap<String, WebDavFolder> mFolderList = new HashMap<String, WebDavFolder>(); private HashMap<String, WebDavFolder> mFolderList = new HashMap<String, WebDavFolder>();
private boolean mSecure; private boolean mSecure;
private DefaultHttpClient mHttpClient = null;
/** /**
* webdav://user:password@server:port CONNECTION_SECURITY_NONE * webdav://user:password@server:port CONNECTION_SECURITY_NONE
@ -150,9 +135,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://" + mHost; this.mUrl = "https://" + mHost + ":" + mUri.getPort();
} else { } else {
this.mUrl = "http://" + mHost; this.mUrl = "http://" + mHost + ":" + mUri.getPort();
} }
if (mUri.getUserInfo() != null) { if (mUri.getUserInfo() != null) {
@ -448,7 +433,7 @@ public class WebDavStore extends Store {
ArrayList<BasicNameValuePair> pairs = new ArrayList(); ArrayList<BasicNameValuePair> pairs = new ArrayList();
pairs.add(new BasicNameValuePair("username", username)); pairs.add(new BasicNameValuePair("username", username));
pairs.add(new BasicNameValuePair("password", password)); pairs.add(new BasicNameValuePair("password", password));
pairs.add(new BasicNameValuePair("destination", finalUrl + "/Exchange/")); pairs.add(new BasicNameValuePair("destination", finalUrl + "/exchange/" +username+"/"));
pairs.add(new BasicNameValuePair("flags", "0")); pairs.add(new BasicNameValuePair("flags", "0"));
pairs.add(new BasicNameValuePair("SubmitCreds", "Log+On")); pairs.add(new BasicNameValuePair("SubmitCreds", "Log+On"));
pairs.add(new BasicNameValuePair("forcedownlevel", "0")); pairs.add(new BasicNameValuePair("forcedownlevel", "0"));
@ -466,7 +451,7 @@ public class WebDavStore extends Store {
/** Verify success */ /** Verify success */
if (status_code < 500 && if (status_code < 500 &&
status_code > 400) { status_code >= 400) {
String errorText = ""; String errorText = "";
String requestText = ""; String requestText = "";
if (entity != null) { if (entity != null) {
@ -483,6 +468,7 @@ public class WebDavStore extends Store {
while ((tempText = reader.readLine()) != null) { while ((tempText = reader.readLine()) != null) {
requestText += tempText; requestText += tempText;
} }
requestText = requestText.replaceAll("password=.*?&", "password=(omitted)&");
throw new MessagingException("Error during authentication: "+ throw new MessagingException("Error during authentication: "+
response.getStatusLine().toString()+ "\n\nRequest: "+ response.getStatusLine().toString()+ "\n\nRequest: "+
requestText + "\n\nResponse: " + requestText + "\n\nResponse: " +
@ -538,25 +524,25 @@ public class WebDavStore extends Store {
} }
public DefaultHttpClient getTrustedHttpClient() throws KeyManagementException, NoSuchAlgorithmException{ public DefaultHttpClient getTrustedHttpClient() throws KeyManagementException, NoSuchAlgorithmException{
DefaultHttpClient httpclient = new DefaultHttpClient(); if (mHttpClient == null) {
/* mHttpClient = new DefaultHttpClient();
SchemeRegistry reg = httpclient.getConnectionManager().getSchemeRegistry(); SchemeRegistry reg = mHttpClient.getConnectionManager().getSchemeRegistry();
reg.unregister("https"); Scheme s = new Scheme("https",new TrustedSocketFactory(mHost,mSecure),443);
Scheme s = new Scheme("https",new TrustedSocketFactory(mHost,mSecure),443); reg.register(s);
reg.register(s);
*/
/* //Add credentials for NTLM/Digest/Basic Auth
//Add credentials for NTLM/Digest/Basic Auth Credentials creds = new UsernamePasswordCredentials(mUsername, mPassword);
Credentials creds = new UsernamePasswordCredentials(mUsername, mPassword); CredentialsProvider credsProvider = mHttpClient.getCredentialsProvider();
CredentialsProvider credsProvider = httpclient.getCredentialsProvider(); // setting AuthScope for 80 and 443, in case we end up getting redirected
// setting AuthScope for 80 and 443, in case we end up getting redirected // from 80 to 443.
// from 80 to 443. credsProvider.setCredentials(new AuthScope(mHost, 80, AuthScope.ANY_REALM), creds);
credsProvider.setCredentials(new AuthScope(mHost, 80, AuthScope.ANY_REALM), creds); credsProvider.setCredentials(new AuthScope(mHost, 443, AuthScope.ANY_REALM), creds);
credsProvider.setCredentials(new AuthScope(mHost, 443, AuthScope.ANY_REALM), creds); credsProvider.setCredentials(new AuthScope(mHost, mUri.getPort(), AuthScope.ANY_REALM), creds);
credsProvider.setCredentials(new AuthScope(mHost, mUri.getPort(), AuthScope.ANY_REALM), creds); mHttpClient.setCredentialsProvider(credsProvider);
httpclient.setCredentialsProvider(credsProvider); }
*/
return httpclient; return mHttpClient;
} }
/** /**
@ -622,13 +608,29 @@ public class WebDavStore extends Store {
response = httpclient.execute(httpmethod); response = httpclient.execute(httpmethod);
statusCode = response.getStatusLine().getStatusCode(); statusCode = response.getStatusLine().getStatusCode();
entity = response.getEntity();
if (statusCode < 500 &&
statusCode >= 400) {
String errorText = "";
if (entity != null) {
BufferedReader reader = new BufferedReader(new InputStreamReader(entity.getContent()), 8192);
String tempText = "";
while ((tempText = reader.readLine()) != null) {
errorText += tempText;
}
}
throw new IOException("Error during authentication: "+
response.getStatusLine().toString()+ "\n\nRequest: "+
messageBody + "\n\nResponse: " +
errorText);
}
if (statusCode < 200 || if (statusCode < 200 ||
statusCode > 300) { statusCode > 300) {
throw new IOException("Error processing request, returned HTTP Response Code was " + statusCode); throw new IOException("Error processing request, returned HTTP Response Code was " + statusCode);
} }
entity = response.getEntity();
if (entity != null && if (entity != null &&
needsParsing) { needsParsing) {
try { try {
@ -1053,7 +1055,7 @@ public class WebDavStore extends Store {
if (statusCode < 200 || if (statusCode < 200 ||
statusCode > 300) { statusCode > 300) {
throw new IOException("Status Code in invalid range"); throw new IOException("Status Code in invalid range, URL: "+wdMessage.getUrl());
} }
entity = response.getEntity(); entity = response.getEntity();
@ -1091,7 +1093,7 @@ public class WebDavStore extends Store {
} catch (URISyntaxException use) { } catch (URISyntaxException use) {
Log.e(Email.LOG_TAG, "URISyntaxException caught " + use); Log.e(Email.LOG_TAG, "URISyntaxException caught " + use);
} catch (IOException ioe) { } catch (IOException ioe) {
Log.e(Email.LOG_TAG, "Non-success response code loading message, response code was " + statusCode); Log.e(Email.LOG_TAG, "Non-success response code loading message, response code was: " + statusCode + " Error: "+ioe.getMessage());
} }
if (listener != null) { if (listener != null) {
@ -1362,6 +1364,18 @@ public class WebDavStore extends Store {
} }
public void setUrl(String url) { public void setUrl(String url) {
//TODO: bleh. This is an ugly hack, but does prevent a crash if the url is relative
//FIXME: so fix, or better yet:
//XXX: prevent URLs from getting to us that are broken.
if (! (url.toLowerCase().contains(WebDavStore.this.mUrl.toLowerCase()+this.mFolder.toString().toLowerCase()))) {
if (!(url.startsWith("/"))){
url = "/" + url;
}
url = WebDavStore.this.mUrl + this.mFolder+url;
}
if (!(url.toLowerCase().contains(WebDavStore.this.mUrl.toLowerCase()))) {
url = WebDavStore.this.mUrl + url;
}
String[] urlParts = url.split("/"); String[] urlParts = url.split("/");
int length = urlParts.length; int length = urlParts.length;
String end = urlParts[length - 1]; String end = urlParts[length - 1];

View File

@ -9,16 +9,17 @@ import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom; import java.security.SecureRandom;
import javax.net.ssl.SSLContext; import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory; import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager; import javax.net.ssl.TrustManager;
import org.apache.http.conn.ConnectTimeoutException; import org.apache.http.conn.ConnectTimeoutException;
import org.apache.http.conn.scheme.SocketFactory; import org.apache.http.conn.scheme.LayeredSocketFactory;
import org.apache.http.params.HttpParams; import org.apache.http.params.HttpParams;
import com.android.email.mail.store.TrustManagerFactory; import com.android.email.mail.store.TrustManagerFactory;
public class TrustedSocketFactory implements SocketFactory { public class TrustedSocketFactory implements LayeredSocketFactory {
private SSLSocketFactory mSocketFactory; private SSLSocketFactory mSocketFactory;
private org.apache.http.conn.ssl.SSLSocketFactory mSchemeSocketFactory; private org.apache.http.conn.ssl.SSLSocketFactory mSchemeSocketFactory;
@ -46,5 +47,19 @@ public class TrustedSocketFactory implements SocketFactory {
public boolean isSecure(Socket sock) throws IllegalArgumentException { public boolean isSecure(Socket sock) throws IllegalArgumentException {
return mSchemeSocketFactory.isSecure(sock); return mSchemeSocketFactory.isSecure(sock);
} }
public Socket createSocket(
} final Socket socket,
final String host,
final int port,
final boolean autoClose
) throws IOException, UnknownHostException {
SSLSocket sslSocket = (SSLSocket) mSocketFactory.createSocket(
socket,
host,
port,
autoClose
);
//hostnameVerifier.verify(host, sslSocket);
// verifyHostName() didn't blowup - good!
return sslSocket;
}}