Finalize and fix DNSSEC and DANE verification
This commit is contained in:
parent
611adaabf2
commit
ba211f119d
6 changed files with 188 additions and 120 deletions
|
@ -1510,14 +1510,14 @@ public class EditAccountActivity extends OmemoActivity implements OnAccountUpdat
|
|||
this.binding.verificationBox.setVisibility(View.VISIBLE);
|
||||
if (mAccount.getXmppConnection() != null && mAccount.getXmppConnection().resolverAuthenticated()) {
|
||||
if (mAccount.getXmppConnection().daneVerified()) {
|
||||
this.binding.verificationMessage.setText("DNSSEC + DANE Verified");
|
||||
this.binding.verificationMessage.setText(R.string.dnssec_dane_verified);
|
||||
this.binding.verificationIndicator.setImageResource(R.drawable.shield_verified);
|
||||
} else {
|
||||
this.binding.verificationMessage.setText("DNSSEC Verified");
|
||||
this.binding.verificationMessage.setText(R.string.dnssec_verified);
|
||||
this.binding.verificationIndicator.setImageResource(R.drawable.shield);
|
||||
}
|
||||
} else {
|
||||
this.binding.verificationMessage.setText("Not DNSSEC Verified");
|
||||
this.binding.verificationMessage.setText(R.string.not_dnssec_verified);
|
||||
this.binding.verificationIndicator.setImageResource(R.drawable.shield_question);
|
||||
}
|
||||
} else {
|
||||
|
|
|
@ -23,7 +23,6 @@ import java.util.concurrent.ExecutorService;
|
|||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
//import de.gultsch.minidns.AndroidDNSClient;
|
||||
import org.minidns.AbstractDnsClient;
|
||||
import org.minidns.DnsCache;
|
||||
import org.minidns.DnsClient;
|
||||
|
@ -421,6 +420,43 @@ public class Resolver {
|
|||
'}';
|
||||
}
|
||||
|
||||
public void connect() {
|
||||
if (this.socket != null) {
|
||||
this.disconnect();
|
||||
}
|
||||
if (this.ip == null || this.port == 0) {
|
||||
Log.d(Config.LOGTAG, "Resolver did not get IP:port (" + this.ip + ":" + this.port + ")");
|
||||
return;
|
||||
}
|
||||
final InetSocketAddress addr = new InetSocketAddress(this.ip, this.port);
|
||||
this.socket = new Socket();
|
||||
try {
|
||||
long time = System.currentTimeMillis();
|
||||
this.socket.connect(addr, Config.SOCKET_TIMEOUT * 1000);
|
||||
time = System.currentTimeMillis() - time;
|
||||
if (this.logID != null && !this.logID.isEmpty()) {
|
||||
Log.d(Config.LOGTAG, Resolver.class.getSimpleName() + ": Result (" + this.logID + ") connect: " + toString() + " after: " + time + " ms");
|
||||
} else {
|
||||
Log.d(Config.LOGTAG, Resolver.class.getSimpleName() + ": Result connect: " + toString() + " after: " + time + " ms");
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
this.disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
public void disconnect() {
|
||||
if (this.socket != null) {
|
||||
FileBackend.close(this.socket);
|
||||
this.socket = null;
|
||||
if (this.logID != null && !this.logID.isEmpty()) {
|
||||
Log.d(Config.LOGTAG, Resolver.class.getSimpleName() + ": Result (" + this.logID + ") disconnect: " + toString());
|
||||
} else {
|
||||
Log.d(Config.LOGTAG, Resolver.class.getSimpleName() + ": Result disconnect: " + toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(@NonNull Result result) {
|
||||
if (result.priority == priority) {
|
||||
|
@ -444,6 +480,13 @@ public class Resolver {
|
|||
}
|
||||
}
|
||||
|
||||
public Result call() throws Exception {
|
||||
this.connect();
|
||||
if (this.socket != null && this.socket.isConnected()) {
|
||||
return this;
|
||||
}
|
||||
throw new Exception("Resolver.Result was not possible to connect - should be catched by executor");
|
||||
}
|
||||
|
||||
public static Result fromCursor(Cursor cursor) {
|
||||
final Result result = new Result();
|
||||
|
|
|
@ -425,7 +425,7 @@ public class XmppConnection implements Runnable {
|
|||
}
|
||||
} else {
|
||||
final String domain = account.getServer();
|
||||
List<Resolver.Result> results;
|
||||
final List<Resolver.Result> results;
|
||||
final boolean hardcoded = extended && !account.getHostname().isEmpty();
|
||||
if (hardcoded) {
|
||||
results = Resolver.fromHardCoded(account.getHostname(), account.getPort());
|
||||
|
@ -436,7 +436,7 @@ public class XmppConnection implements Runnable {
|
|||
Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": Thread was interrupted");
|
||||
return;
|
||||
}
|
||||
if (results == null) {
|
||||
if (results.size() == 0) {
|
||||
Log.e(
|
||||
Config.LOGTAG,
|
||||
account.getJid().asBareJid() + ": Resolver results were empty");
|
||||
|
@ -448,8 +448,8 @@ public class XmppConnection implements Runnable {
|
|||
} else {
|
||||
storedBackupResult =
|
||||
mXmppConnectionService.databaseBackend.findResolverResult(domain);
|
||||
if (storedBackupResult != null && results != storedBackupResult && !storedBackupResult.isOutdated()) {
|
||||
results = (List<Resolver.Result>) storedBackupResult;
|
||||
if (storedBackupResult != null && !results.contains(storedBackupResult) && !storedBackupResult.isOutdated()) {
|
||||
results.add(storedBackupResult);
|
||||
Log.d(
|
||||
Config.LOGTAG,
|
||||
account.getJid().asBareJid()
|
||||
|
@ -464,90 +464,109 @@ public class XmppConnection implements Runnable {
|
|||
}
|
||||
for (final Iterator<Resolver.Result> iterator = results.iterator();
|
||||
iterator.hasNext(); ) {
|
||||
final Resolver.Result result = iterator.next();
|
||||
|
||||
if (results == null || result.getSocket() == null) {
|
||||
results = Resolver.resolve(domain);
|
||||
}
|
||||
if (results == null) {
|
||||
throw new UnknownHostException();
|
||||
}
|
||||
if (Thread.currentThread().isInterrupted()) {
|
||||
Log.d(
|
||||
Config.LOGTAG,
|
||||
account.getJid().asBareJid() + ": Thread was interrupted");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
// if tls is true, encryption is implied and must not be started
|
||||
features.encryptionEnabled = result.isDirectTls();
|
||||
verifiedHostname = result.isAuthenticated() ? result.getHostname().toString() : null;
|
||||
Log.d(Config.LOGTAG, account.getJid().asBareJid().toString()
|
||||
+ ": using values from resolver " + results.toString());
|
||||
|
||||
localSocket = result.getSocket();
|
||||
|
||||
if (features.encryptionEnabled) {
|
||||
localSocket = upgradeSocketToTls(localSocket);
|
||||
final Resolver.Result result = iterator.next();
|
||||
if (Thread.currentThread().isInterrupted()) {
|
||||
Log.d(
|
||||
Config.LOGTAG,
|
||||
account.getJid().asBareJid() + ": Thread was interrupted");
|
||||
return;
|
||||
}
|
||||
|
||||
localSocket.setSoTimeout(Config.SOCKET_TIMEOUT * 1000);
|
||||
if (startXmpp(localSocket)) {
|
||||
localSocket.setSoTimeout(
|
||||
0); // reset to 0; once the connection is established we don’t
|
||||
// want this
|
||||
if (!hardcoded && !result.equals(storedBackupResult)) {
|
||||
mXmppConnectionService.databaseBackend.saveResolverResult(
|
||||
domain, result);
|
||||
try {
|
||||
// if tls is true, encryption is implied and must not be started
|
||||
features.encryptionEnabled = result.isDirectTls();
|
||||
verifiedHostname =
|
||||
result.isAuthenticated() ? result.getHostname().toString() : null;
|
||||
final InetSocketAddress addr;
|
||||
if (result.getIp() != null) {
|
||||
addr = new InetSocketAddress(result.getIp(), result.getPort());
|
||||
Log.d(
|
||||
Config.LOGTAG,
|
||||
account.getJid().asBareJid().toString()
|
||||
+ ": using values from resolver "
|
||||
+ (result.getHostname() == null
|
||||
? ""
|
||||
: result.getHostname().toString() + "/")
|
||||
+ result.getIp().getHostAddress()
|
||||
+ ":"
|
||||
+ result.getPort()
|
||||
+ " tls: "
|
||||
+ features.encryptionEnabled);
|
||||
} else {
|
||||
addr =
|
||||
new InetSocketAddress(
|
||||
IDN.toASCII(result.getHostname().toString()),
|
||||
result.getPort());
|
||||
Log.d(
|
||||
Config.LOGTAG,
|
||||
account.getJid().asBareJid().toString()
|
||||
+ ": using values from resolver "
|
||||
+ result.getHostname().toString()
|
||||
+ ":"
|
||||
+ result.getPort()
|
||||
+ " tls: "
|
||||
+ features.encryptionEnabled);
|
||||
}
|
||||
|
||||
localSocket = new Socket();
|
||||
localSocket.connect(addr, Config.SOCKET_TIMEOUT * 1000);
|
||||
|
||||
if (features.encryptionEnabled) {
|
||||
localSocket = upgradeSocketToTls(localSocket);
|
||||
}
|
||||
|
||||
localSocket.setSoTimeout(Config.SOCKET_TIMEOUT * 1000);
|
||||
if (startXmpp(localSocket)) {
|
||||
localSocket.setSoTimeout(
|
||||
0); // reset to 0; once the connection is established we don’t
|
||||
// want this
|
||||
if (!hardcoded && !result.equals(storedBackupResult)) {
|
||||
mXmppConnectionService.databaseBackend.saveResolverResult(
|
||||
domain, result);
|
||||
}
|
||||
this.currentResolverResult = result;
|
||||
this.seeOtherHostResolverResult = null;
|
||||
break; // successfully connected to server that speaks xmpp
|
||||
} else {
|
||||
FileBackend.close(localSocket);
|
||||
throw new StateChangingException(Account.State.STREAM_OPENING_ERROR);
|
||||
}
|
||||
} catch (final StateChangingException e) {
|
||||
if (!iterator.hasNext()) {
|
||||
throw e;
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
Log.d(
|
||||
Config.LOGTAG,
|
||||
account.getJid().asBareJid()
|
||||
+ ": thread was interrupted before beginning stream");
|
||||
return;
|
||||
} catch (final Throwable e) {
|
||||
Log.d(
|
||||
Config.LOGTAG,
|
||||
account.getJid().asBareJid().toString()
|
||||
+ ": "
|
||||
+ e.getMessage()
|
||||
+ "("
|
||||
+ e.getClass().getName()
|
||||
+ ")");
|
||||
if (!iterator.hasNext()) {
|
||||
throw new UnknownHostException();
|
||||
}
|
||||
this.currentResolverResult = result;
|
||||
this.seeOtherHostResolverResult = null;
|
||||
break; // successfully connected to server that speaks xmpp
|
||||
} else {
|
||||
FileBackend.close(localSocket);
|
||||
throw new StateChangingException(Account.State.STREAM_OPENING_ERROR);
|
||||
}
|
||||
} catch (final StateChangingException e) {
|
||||
throw e;
|
||||
} catch (InterruptedException e) {
|
||||
Log.d(
|
||||
Config.LOGTAG,
|
||||
account.getJid().asBareJid()
|
||||
+ ": thread was interrupted before beginning stream");
|
||||
return;
|
||||
} catch (final Throwable e) {
|
||||
Log.d(
|
||||
Config.LOGTAG,
|
||||
account.getJid().asBareJid().toString()
|
||||
+ ": "
|
||||
+ e.getMessage()
|
||||
+ "("
|
||||
+ e.getClass().getName()
|
||||
+ ")");
|
||||
throw new UnknownHostException();
|
||||
}
|
||||
}
|
||||
}
|
||||
processStream();
|
||||
} catch (final SecurityException e) {
|
||||
this.changeStatus(Account.State.MISSING_INTERNET_PERMISSION);
|
||||
} catch (final StateChangingException e) {
|
||||
this.changeStatus(e.state);
|
||||
} catch (final UnknownHostException
|
||||
| ConnectException
|
||||
| SocksSocketFactory.HostNotFoundException e) {
|
||||
| ConnectException
|
||||
| SocksSocketFactory.HostNotFoundException e) {
|
||||
this.changeStatus(Account.State.SERVER_NOT_FOUND);
|
||||
} catch (final SocksSocketFactory.SocksProxyNotFoundException e) {
|
||||
if (!account.isI2P()) {
|
||||
this.changeStatus(Account.State.TOR_NOT_AVAILABLE);
|
||||
} else {
|
||||
this.changeStatus(Account.State.I2P_NOT_AVAILABLE);
|
||||
}
|
||||
} catch (final IOException e) {
|
||||
Log.d(Config.LOGTAG, account.getJid().asBareJid().toString() + ": " + e.getMessage());
|
||||
this.changeStatus(Account.State.OFFLINE);
|
||||
this.attempt = Math.max(0, this.attempt - 1);
|
||||
} catch (final XmlPullParserException e) {
|
||||
this.changeStatus(Account.State.TOR_NOT_AVAILABLE);
|
||||
} catch (final IOException | XmlPullParserException e) {
|
||||
Log.d(Config.LOGTAG, account.getJid().asBareJid().toString() + ": " + e.getMessage());
|
||||
this.changeStatus(Account.State.OFFLINE);
|
||||
this.attempt = Math.max(0, this.attempt - 1);
|
||||
|
@ -1380,7 +1399,7 @@ public class XmppConnection implements Runnable {
|
|||
this.dane = false;
|
||||
final SSLSocketFactory sslSocketFactory;
|
||||
try {
|
||||
sslSocketFactory = getSSLSocketFactory(socket.getPort(), (d) -> this.dane = d);
|
||||
sslSocketFactory = getSSLSocketFactory(account.getPort(), (d) -> this.dane = d);
|
||||
} catch (final NoSuchAlgorithmException | KeyManagementException e) {
|
||||
throw new StateChangingException(Account.State.TLS_ERROR);
|
||||
}
|
||||
|
@ -1388,7 +1407,7 @@ public class XmppConnection implements Runnable {
|
|||
final SSLSocket sslSocket =
|
||||
(SSLSocket)
|
||||
sslSocketFactory.createSocket(
|
||||
socket, address.getHostAddress(), socket.getPort(), true);
|
||||
socket, address.getHostAddress(), account.getPort(), true);
|
||||
SSLSockets.setSecurity(sslSocket);
|
||||
SSLSockets.setHostname(sslSocket, IDN.toASCII(account.getServer()));
|
||||
SSLSockets.setApplicationProtocol(sslSocket, "xmpp-client");
|
||||
|
|
|
@ -44,6 +44,7 @@
|
|||
android:layout_height="wrap_content"
|
||||
android:padding="@dimen/card_padding_regular">
|
||||
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/jid_password_box"
|
||||
android:layout_width="match_parent"
|
||||
|
@ -77,7 +78,7 @@
|
|||
<eu.siacs.conversations.ui.widget.TextInputEditText
|
||||
android:id="@+id/hostname"
|
||||
style="@style/Widget.Material3.TextInputEditText.FilledBox"
|
||||
android:fontFamily="notosansregular"
|
||||
android:fontFamily="notosansregular"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:inputType="textWebEmailAddress" />
|
||||
|
@ -146,7 +147,7 @@ android:fontFamily="notosansregular"
|
|||
<eu.siacs.conversations.ui.widget.TextInputEditText
|
||||
android:id="@+id/account_password"
|
||||
style="@style/Widget.Material3.TextInputEditText.FilledBox"
|
||||
android:fontFamily="notosansregular"
|
||||
android:fontFamily="notosansregular"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="@string/password"
|
||||
|
@ -155,6 +156,42 @@ android:fontFamily="notosansregular"
|
|||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
|
||||
<RelativeLayout
|
||||
android:id="@+id/verification_box"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:layout_marginTop="12dp">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_toLeftOf="@+id/verification_indicator"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/verification_message"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/not_dnssec_verified"
|
||||
android:textAppearance="@style/TextAppearance.Conversations.Body1.Tertiary" />
|
||||
</LinearLayout>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/verification_indicator"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentRight="true"
|
||||
android:layout_centerVertical="true"
|
||||
android:alpha="?attr/icon_alpha"
|
||||
android:background="?attr/selectableItemBackgroundBorderless"
|
||||
android:padding="@dimen/image_button_padding"
|
||||
android:src="@drawable/shield_question"
|
||||
android:visibility="visible" />
|
||||
</RelativeLayout>
|
||||
|
||||
<RelativeLayout
|
||||
android:id="@+id/account_color_box"
|
||||
android:layout_width="wrap_content"
|
||||
|
@ -269,42 +306,6 @@ android:fontFamily="notosansregular"
|
|||
android:visibility="gone" />
|
||||
</LinearLayout>
|
||||
|
||||
|
||||
<RelativeLayout
|
||||
android:id="@+id/verification_box"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginTop="12dp">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_toLeftOf="@+id/verification_indicator"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/verification_message"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Not DNSSEC Verified"
|
||||
android:textAppearance="@style/TextAppearance.Conversations.Body1.Tertiary" />
|
||||
</LinearLayout>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/verification_indicator"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentRight="true"
|
||||
android:layout_centerVertical="true"
|
||||
android:alpha="?attr/icon_alpha"
|
||||
android:background="?attr/selectableItemBackgroundBorderless"
|
||||
android:padding="@dimen/image_button_padding"
|
||||
android:src="@drawable/shield_question"
|
||||
android:visibility="visible" />
|
||||
</RelativeLayout>
|
||||
|
||||
<RelativeLayout
|
||||
android:id="@+id/your_name_box"
|
||||
android:layout_width="wrap_content"
|
||||
|
@ -444,7 +445,6 @@ android:fontFamily="notosansregular"
|
|||
android:maxHeight="@dimen/avatar_on_details_screen_size"
|
||||
app:strokeColor="@color/custom_theme_accent"
|
||||
app:riv_corner_radius="5dp" />
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
<com.google.android.material.materialswitch.MaterialSwitch
|
||||
|
|
|
@ -1396,4 +1396,7 @@
|
|||
<string name="log_in">Anmelden</string>
|
||||
<string name="contact_uses_unverified_keys">Dein Kontakt nutzt nicht verifizierte Geräte. Scanne seinen QR-Code, um ihn zu verifizieren und "MITM"-Angriffe aktiv zu verhindern.</string>
|
||||
<string name="unverified_devices">Du benutzt nicht verifizierte Geräte. Scanne den QR-Code an deinen anderen Geräten um sie zu verifizieren und "MITM"-Angriffe aktiv zu verhindern.</string>
|
||||
<string name="not_dnssec_verified">Nicht DNSSEC verifiziert</string>
|
||||
<string name="dnssec_dane_verified">DNSSEC + DANE verifiziert</string>
|
||||
<string name="dnssec_verified">DNSSEC verifiziert</string>
|
||||
</resources>
|
||||
|
|
|
@ -1359,4 +1359,7 @@
|
|||
<string name="security">Security</string>
|
||||
<string name="verified_with_qr_code"> - verified with QR-code</string>
|
||||
<string name="key_automatically_accepted"> - key not verified</string>
|
||||
<string name="not_dnssec_verified">Not DNSSEC verified</string>
|
||||
<string name="dnssec_dane_verified">DNSSEC + DANE verified</string>
|
||||
<string name="dnssec_verified">DNSSEC verified</string>
|
||||
</resources>
|
||||
|
|
Loading…
Add table
Reference in a new issue