Throw CertificateValidationException if EXTERNAL authentication fails

This is done when the SASL EXTERNAL mechanism isn't advertised (indicating
the possibility that the server did not accept the client certificate) or
when the command for authenticating with SASL EXTERNAL fails.

The CertificateValidationException will trigger a notification to the user
that there's an authentication problem that needs addressing.

Also, there were instances where CertificateValidationException was being
thrown with a new CertificateException as the cause for the purpose of
notifying the user when STARTTLS is not available.  This has been slightly
simplified by eliminating the need to include a new CertificateException
as a cause.
This commit is contained in:
Joe Steele 2014-08-07 13:54:14 -04:00
parent b557ba008c
commit 301ac48a38
4 changed files with 55 additions and 26 deletions

View File

@ -16,7 +16,11 @@ public class CertificateValidationException extends MessagingException {
public CertificateValidationException(String message) { public CertificateValidationException(String message) {
super(message); super(message);
scanForCause(); /*
* Instances created without a Throwable parameter as a cause are
* presumed to need user attention.
*/
mNeedsUserAttention = true;
} }
public CertificateValidationException(final String message, Throwable throwable) { public CertificateValidationException(final String message, Throwable throwable) {
@ -45,10 +49,9 @@ public class CertificateValidationException extends MessagingException {
* *
* The various mail protocol handlers (IMAP, POP3, ...) will catch an * The various mail protocol handlers (IMAP, POP3, ...) will catch an
* SSLException and throw a CertificateValidationException (this class) * SSLException and throw a CertificateValidationException (this class)
* with the SSLException as the cause. They may also throw a * with the SSLException as the cause. (They may also throw a
* CertificateValidationException with a new CertificateException as the * CertificateValidationException when STARTTLS is not available, just
* cause when STARTTLS is not available, just for the purpose of * for the purpose of triggering a user notification.)
* triggering a user notification.
* *
* SSLHandshakeException is also known to occur if the *client* * SSLHandshakeException is also known to occur if the *client*
* certificate was not accepted by the server (unknown CA, certificate * certificate was not accepted by the server (unknown CA, certificate

View File

@ -26,7 +26,6 @@ import java.nio.charset.CharsetDecoder;
import java.nio.charset.CodingErrorAction; import java.nio.charset.CodingErrorAction;
import java.security.GeneralSecurityException; import java.security.GeneralSecurityException;
import java.security.Security; import java.security.Security;
import java.security.cert.CertificateException;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
@ -2511,8 +2510,7 @@ public class ImapStore extends Store {
* "STARTTLS (if available)" setting. * "STARTTLS (if available)" setting.
*/ */
throw new CertificateValidationException( throw new CertificateValidationException(
"STARTTLS connection security not available", "STARTTLS connection security not available");
new CertificateException());
} }
} }
@ -2539,12 +2537,10 @@ public class ImapStore extends Store {
case EXTERNAL: case EXTERNAL:
if (hasCapability(CAPABILITY_AUTH_EXTERNAL)) { if (hasCapability(CAPABILITY_AUTH_EXTERNAL)) {
executeSimpleCommand( saslAuthExternal();
String.format("AUTHENTICATE EXTERNAL %s",
Utility.base64Encode(mSettings.getUsername())), false);
} else { } else {
throw new MessagingException( // Provide notification to user of a problem authenticating using client certificates
"EXTERNAL authentication not advertised by server"); throw new CertificateValidationException(K9.app.getString(R.string.auth_external_error));
} }
break; break;
@ -2729,6 +2725,23 @@ public class ImapStore extends Store {
} }
} }
private void saslAuthExternal() throws IOException, MessagingException {
try {
receiveCapabilities(executeSimpleCommand(
String.format("AUTHENTICATE EXTERNAL %s",
Utility.base64Encode(mSettings.getUsername())), false));
} catch (ImapException e) {
/*
* Provide notification to the user of a problem authenticating
* using client certificates. We don't use an
* AuthenticationFailedException because that would trigger a
* "Username or password incorrect" notification in
* AccountSetupCheckSettings.
*/
throw new CertificateValidationException(e.getMessage());
}
}
protected ImapResponse readContinuationResponse(String tag) protected ImapResponse readContinuationResponse(String tag)
throws IOException, MessagingException { throws IOException, MessagingException {
ImapResponse response; ImapResponse response;

View File

@ -5,6 +5,7 @@ import android.util.Log;
import com.fsck.k9.Account; import com.fsck.k9.Account;
import com.fsck.k9.K9; import com.fsck.k9.K9;
import com.fsck.k9.R;
import com.fsck.k9.controller.MessageRetrievalListener; import com.fsck.k9.controller.MessageRetrievalListener;
import com.fsck.k9.helper.Utility; import com.fsck.k9.helper.Utility;
import com.fsck.k9.mail.*; import com.fsck.k9.mail.*;
@ -20,7 +21,6 @@ import java.net.*;
import java.security.GeneralSecurityException; import java.security.GeneralSecurityException;
import java.security.MessageDigest; import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Date; import java.util.Date;
@ -354,8 +354,7 @@ public class Pop3Store extends Store {
* "STARTTLS (if available)" setting. * "STARTTLS (if available)" setting.
*/ */
throw new CertificateValidationException( throw new CertificateValidationException(
"STARTTLS connection security not available", "STARTTLS connection security not available");
new CertificateException());
} }
} }
@ -378,13 +377,11 @@ public class Pop3Store extends Store {
case EXTERNAL: case EXTERNAL:
if (mCapabilities.external) { if (mCapabilities.external) {
executeSimpleCommand( authExternal();
String.format("AUTH EXTERNAL %s", } else {
Utility.base64Encode(mUsername)), false); // Provide notification to user of a problem authenticating using client certificates
} else { throw new CertificateValidationException(K9.app.getString(R.string.auth_external_error));
throw new MessagingException( }
"EXTERNAL authentication not advertised by server");
}
break; break;
default: default:
@ -470,6 +467,24 @@ public class Pop3Store extends Store {
} }
} }
private void authExternal() throws MessagingException {
try {
executeSimpleCommand(
String.format("AUTH EXTERNAL %s",
Utility.base64Encode(mUsername)), false);
} catch (Pop3ErrorResponse e) {
/*
* Provide notification to the user of a problem authenticating
* using client certificates. We don't use an
* AuthenticationFailedException because that would trigger a
* "Username or password incorrect" notification in
* AccountSetupCheckSettings.
*/
throw new CertificateValidationException(
"POP3 client certificate authentication failed: " + e.getMessage(), e);
}
}
@Override @Override
public boolean isOpen() { public boolean isOpen() {
return (mIn != null && mOut != null && mSocket != null return (mIn != null && mOut != null && mSocket != null

View File

@ -26,7 +26,6 @@ import java.io.OutputStream;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import java.net.*; import java.net.*;
import java.security.GeneralSecurityException; import java.security.GeneralSecurityException;
import java.security.cert.CertificateException;
import java.util.*; import java.util.*;
public class SmtpTransport extends Transport { public class SmtpTransport extends Transport {
@ -300,8 +299,7 @@ public class SmtpTransport extends Transport {
* "STARTTLS (if available)" setting. * "STARTTLS (if available)" setting.
*/ */
throw new CertificateValidationException( throw new CertificateValidationException(
"STARTTLS connection security not available", "STARTTLS connection security not available");
new CertificateException());
} }
} }