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) {
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) {
@ -45,10 +49,9 @@ public class CertificateValidationException extends MessagingException {
*
* The various mail protocol handlers (IMAP, POP3, ...) will catch an
* SSLException and throw a CertificateValidationException (this class)
* with the SSLException as the cause. They may also throw a
* CertificateValidationException with a new CertificateException as the
* cause when STARTTLS is not available, just for the purpose of
* triggering a user notification.
* with the SSLException as the cause. (They may also throw a
* CertificateValidationException when STARTTLS is not available, just
* for the purpose of triggering a user notification.)
*
* SSLHandshakeException is also known to occur if the *client*
* 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.security.GeneralSecurityException;
import java.security.Security;
import java.security.cert.CertificateException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
@ -2511,8 +2510,7 @@ public class ImapStore extends Store {
* "STARTTLS (if available)" setting.
*/
throw new CertificateValidationException(
"STARTTLS connection security not available",
new CertificateException());
"STARTTLS connection security not available");
}
}
@ -2539,12 +2537,10 @@ public class ImapStore extends Store {
case EXTERNAL:
if (hasCapability(CAPABILITY_AUTH_EXTERNAL)) {
executeSimpleCommand(
String.format("AUTHENTICATE EXTERNAL %s",
Utility.base64Encode(mSettings.getUsername())), false);
saslAuthExternal();
} else {
throw new MessagingException(
"EXTERNAL authentication not advertised by server");
// Provide notification to user of a problem authenticating using client certificates
throw new CertificateValidationException(K9.app.getString(R.string.auth_external_error));
}
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)
throws IOException, MessagingException {
ImapResponse response;

View file

@ -5,6 +5,7 @@ import android.util.Log;
import com.fsck.k9.Account;
import com.fsck.k9.K9;
import com.fsck.k9.R;
import com.fsck.k9.controller.MessageRetrievalListener;
import com.fsck.k9.helper.Utility;
import com.fsck.k9.mail.*;
@ -20,7 +21,6 @@ import java.net.*;
import java.security.GeneralSecurityException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
@ -354,8 +354,7 @@ public class Pop3Store extends Store {
* "STARTTLS (if available)" setting.
*/
throw new CertificateValidationException(
"STARTTLS connection security not available",
new CertificateException());
"STARTTLS connection security not available");
}
}
@ -378,13 +377,11 @@ public class Pop3Store extends Store {
case EXTERNAL:
if (mCapabilities.external) {
executeSimpleCommand(
String.format("AUTH EXTERNAL %s",
Utility.base64Encode(mUsername)), false);
} else {
throw new MessagingException(
"EXTERNAL authentication not advertised by server");
}
authExternal();
} else {
// Provide notification to user of a problem authenticating using client certificates
throw new CertificateValidationException(K9.app.getString(R.string.auth_external_error));
}
break;
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
public boolean isOpen() {
return (mIn != null && mOut != null && mSocket != null

View file

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