Add auth method pinning

This commit is contained in:
Sam Whited 2014-11-15 09:42:40 -05:00
parent 35bf13f5ef
commit 847877f9d2
6 changed files with 58 additions and 16 deletions

View file

@ -17,7 +17,13 @@ public class DigestMd5 extends SaslMechanism {
super(tagWriter, account, rng); super(tagWriter, account, rng);
} }
public static String getMechanism() { @Override
public int getPriority() {
return 10;
}
@Override
public String getMechanism() {
return "DIGEST-MD5"; return "DIGEST-MD5";
} }

View file

@ -12,7 +12,13 @@ public class Plain extends SaslMechanism {
super(tagWriter, account, null); super(tagWriter, account, null);
} }
public static String getMechanism() { @Override
public int getPriority() {
return 0;
}
@Override
public String getMechanism() {
return "PLAIN"; return "PLAIN";
} }

View file

@ -44,6 +44,15 @@ public abstract class SaslMechanism {
this.rng = rng; this.rng = rng;
} }
/**
* The priority is used to pin the authentication mechanism. If authentication fails, it MAY be retried with another
* mechanism of the same priority, but MUST NOT be tried with a mechanism of lower priority (to prevent downgrade
* attacks).
* @return An arbitrary int representing the priority
*/
public abstract int getPriority();
public abstract String getMechanism();
public String getClientFirstMessage() { public String getClientFirstMessage() {
return ""; return "";
} }

View file

@ -43,7 +43,13 @@ public class ScramSha1 extends SaslMechanism {
clientFirstMessageBare = ""; clientFirstMessageBare = "";
} }
public static String getMechanism() { @Override
public int getPriority() {
return 20;
}
@Override
public String getMechanism() {
return "SCRAM-SHA-1"; return "SCRAM-SHA-1";
} }

View file

@ -34,6 +34,8 @@ public class Account extends AbstractEntity {
public static final String KEYS = "keys"; public static final String KEYS = "keys";
public static final String AVATAR = "avatar"; public static final String AVATAR = "avatar";
public static final String PINNED_MECHANISM_KEY = "pinned_mechanism";
public static final int OPTION_USETLS = 0; public static final int OPTION_USETLS = 0;
public static final int OPTION_DISABLED = 1; public static final int OPTION_DISABLED = 1;
public static final int OPTION_REGISTER = 2; public static final int OPTION_REGISTER = 2;

View file

@ -12,6 +12,8 @@ import android.util.Log;
import android.util.SparseArray; import android.util.SparseArray;
import org.apache.http.conn.ssl.StrictHostnameVerifier; import org.apache.http.conn.ssl.StrictHostnameVerifier;
import org.json.JSONException;
import org.json.JSONObject;
import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException; import java.io.IOException;
@ -291,6 +293,8 @@ public class XmppConnection implements Runnable {
Log.e(Config.LOGTAG, String.valueOf(e)); Log.e(Config.LOGTAG, String.valueOf(e));
} }
Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() + ": logged in"); Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() + ": logged in");
account.setKey(Account.PINNED_MECHANISM_KEY,
String.valueOf(saslMechanism.getPriority()));
tagReader.reset(); tagReader.reset();
sendStartStream(); sendStartStream();
processStream(tagReader.readTag()); processStream(tagReader.readTag());
@ -629,19 +633,28 @@ public class XmppConnection implements Runnable {
.findChild("mechanisms")); .findChild("mechanisms"));
final Element auth = new Element("auth"); final Element auth = new Element("auth");
auth.setAttribute("xmlns", "urn:ietf:params:xml:ns:xmpp-sasl"); auth.setAttribute("xmlns", "urn:ietf:params:xml:ns:xmpp-sasl");
if (mechanisms.contains(ScramSha1.getMechanism())) { if (mechanisms.contains("SCRAM-SHA-1")) {
saslMechanism = new ScramSha1(tagWriter, account, mXmppConnectionService.getRNG()); saslMechanism = new ScramSha1(tagWriter, account, mXmppConnectionService.getRNG());
Log.d(Config.LOGTAG, "Authenticating with " + ScramSha1.getMechanism()); } else if (mechanisms.contains("DIGEST-MD5")) {
auth.setAttribute("mechanism", ScramSha1.getMechanism());
} else if (mechanisms.contains(DigestMd5.getMechanism())) {
Log.d(Config.LOGTAG, "Authenticating with " + DigestMd5.getMechanism());
saslMechanism = new DigestMd5(tagWriter, account, mXmppConnectionService.getRNG()); saslMechanism = new DigestMd5(tagWriter, account, mXmppConnectionService.getRNG());
auth.setAttribute("mechanism", DigestMd5.getMechanism()); } else if (mechanisms.contains("PLAIN")) {
} else if (mechanisms.contains(Plain.getMechanism())) {
Log.d(Config.LOGTAG, "Authenticating with " + Plain.getMechanism());
saslMechanism = new Plain(tagWriter, account); saslMechanism = new Plain(tagWriter, account);
auth.setAttribute("mechanism", Plain.getMechanism());
} }
final JSONObject keys = account.getKeys();
try {
if (keys.has(Account.PINNED_MECHANISM_KEY) &&
keys.getInt(Account.PINNED_MECHANISM_KEY) > saslMechanism.getPriority() ) {
Log.e(Config.LOGTAG, "Auth failed. Authentication mechanism " + saslMechanism.getMechanism() +
" has lower priority (" + String.valueOf(saslMechanism.getPriority()) +
") than pinned priority (" + keys.getInt(Account.PINNED_MECHANISM_KEY) +
"). Possible downgrade attack?");
disconnect(true);
}
} catch (final JSONException e) {
Log.d(Config.LOGTAG, "Parse error while checking pinned auth mechanism");
}
Log.d(Config.LOGTAG, "Authenticating with " + saslMechanism.getMechanism());
auth.setAttribute("mechanism", saslMechanism.getMechanism());
if (!saslMechanism.getClientFirstMessage().isEmpty()) { if (!saslMechanism.getClientFirstMessage().isEmpty()) {
auth.setContent(saslMechanism.getClientFirstMessage()); auth.setContent(saslMechanism.getClientFirstMessage());
} }