diff --git a/res/values/strings.xml b/res/values/strings.xml index fa5c2d76b..a36c7f583 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -1106,7 +1106,9 @@ Please submit bug reports, contribute new features and ask questions at Sending message Saving draft Fetching attachment… - + + Unable to authenticate. The server does not advertise the SASL EXTERNAL capability. This could be due to a problem with the client certificate (expired, unknown certificate authority) or some other configuration problem. + Decrypting/Verifying… Successful decryption diff --git a/src/com/fsck/k9/mail/transport/SmtpTransport.java b/src/com/fsck/k9/mail/transport/SmtpTransport.java index 89f4e7173..4e78a573e 100644 --- a/src/com/fsck/k9/mail/transport/SmtpTransport.java +++ b/src/com/fsck/k9/mail/transport/SmtpTransport.java @@ -5,9 +5,10 @@ import android.util.Log; import com.fsck.k9.Account; import com.fsck.k9.K9; +import com.fsck.k9.R; +import com.fsck.k9.helper.Utility; import com.fsck.k9.mail.*; import com.fsck.k9.mail.Message.RecipientType; -import com.fsck.k9.mail.filter.Base64; import com.fsck.k9.mail.filter.EOLConvertingOutputStream; import com.fsck.k9.mail.filter.LineWrapOutputStream; import com.fsck.k9.mail.filter.PeekableInputStream; @@ -307,11 +308,13 @@ public class SmtpTransport extends Transport { boolean authLoginSupported = false; boolean authPlainSupported = false; boolean authCramMD5Supported = false; + boolean authExternalSupported = false; if (extensions.containsKey("AUTH")) { List saslMech = Arrays.asList(extensions.get("AUTH").split(" ")); authLoginSupported = saslMech.contains("LOGIN"); authPlainSupported = saslMech.contains("PLAIN"); authCramMD5Supported = saslMech.contains("CRAM-MD5"); + authExternalSupported = saslMech.contains("EXTERNAL"); } if (extensions.containsKey("SIZE")) { try { @@ -323,8 +326,10 @@ public class SmtpTransport extends Transport { } } - if (mUsername != null && mUsername.length() > 0 && - mPassword != null && mPassword.length() > 0) { + if (mUsername != null + && mUsername.length() > 0 + && (mPassword != null && mPassword.length() > 0 || AuthType.EXTERNAL + .equals(mAuthType))) { switch (mAuthType) { @@ -353,6 +358,24 @@ public class SmtpTransport extends Transport { } break; + case EXTERNAL: + if (authExternalSupported) { + saslAuthExternal(mUsername); + } else { + /* + * Some SMTP servers are known to provide no error + * indication when a client certificate fails to + * validate, other than to not offer the AUTH EXTERNAL + * capability. + * + * So, we treat it is an error to not offer AUTH + * EXTERNAL when using client certificates. That way, the + * user can be notified of a problem during account setup. + */ + throw new MessagingException(K9.app.getString(R.string.auth_external_error)); + } + break; + /* * AUTOMATIC is an obsolete option which is unavailable to users, * but it still may exist in a user's settings from a previous @@ -688,8 +711,8 @@ public class SmtpTransport extends Transport { AuthenticationFailedException, IOException { try { executeSimpleCommand("AUTH LOGIN"); - executeSimpleCommand(new String(Base64.encodeBase64(username.getBytes())), true); - executeSimpleCommand(new String(Base64.encodeBase64(password.getBytes())), true); + executeSimpleCommand(Utility.base64Encode(username), true); + executeSimpleCommand(Utility.base64Encode(password), true); } catch (MessagingException me) { if (me.getMessage().length() > 1 && me.getMessage().charAt(1) == '3') { throw new AuthenticationFailedException("AUTH LOGIN failed (" + me.getMessage() @@ -701,10 +724,9 @@ public class SmtpTransport extends Transport { private void saslAuthPlain(String username, String password) throws MessagingException, AuthenticationFailedException, IOException { - byte[] data = ("\000" + username + "\000" + password).getBytes(); - data = new Base64().encode(data); + String data = Utility.base64Encode("\000" + username + "\000" + password); try { - executeSimpleCommand("AUTH PLAIN " + new String(data), true); + executeSimpleCommand("AUTH PLAIN " + data, true); } catch (MessagingException me) { if (me.getMessage().length() > 1 && me.getMessage().charAt(1) == '3') { throw new AuthenticationFailedException("AUTH PLAIN failed (" + me.getMessage() @@ -732,6 +754,12 @@ public class SmtpTransport extends Transport { } } + private void saslAuthExternal(String username) throws MessagingException, IOException { + executeSimpleCommand( + String.format("AUTH EXTERNAL %s", + Utility.base64Encode(username)), false); + } + /** * Exception that is thrown when the server sends a negative reply (reply codes 4xx or 5xx). */