Add auth method pinning
This commit is contained in:
parent
35bf13f5ef
commit
847877f9d2
|
@ -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";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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 "";
|
||||||
}
|
}
|
||||||
|
|
|
@ -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";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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,23 +633,32 @@ 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());
|
|
||||||
}
|
}
|
||||||
if (!saslMechanism.getClientFirstMessage().isEmpty()) {
|
final JSONObject keys = account.getKeys();
|
||||||
auth.setContent(saslMechanism.getClientFirstMessage());
|
try {
|
||||||
}
|
if (keys.has(Account.PINNED_MECHANISM_KEY) &&
|
||||||
tagWriter.writeElement(auth);
|
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()) {
|
||||||
|
auth.setContent(saslMechanism.getClientFirstMessage());
|
||||||
|
}
|
||||||
|
tagWriter.writeElement(auth);
|
||||||
} else if (this.streamFeatures.hasChild("sm", "urn:xmpp:sm:"
|
} else if (this.streamFeatures.hasChild("sm", "urn:xmpp:sm:"
|
||||||
+ smVersion)
|
+ smVersion)
|
||||||
&& streamId != null) {
|
&& streamId != null) {
|
||||||
|
|
Loading…
Reference in a new issue