summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorArne Schwabe <arne@rfc2549.org>2021-01-21 12:24:06 +0100
committerArne Schwabe <arne@rfc2549.org>2021-02-16 16:40:04 +0100
commit96db8d0bf1110f029cf5ad90bc939cd444100600 (patch)
treeb2d7915f465bdeee6847e171e72a5c28e7bd9b72
parenta965f961b5dc5cba44d9a81a538c11c4394de24e (diff)
Implement UI support for --peer-fingerprint
-rw-r--r--main/src/main/java/de/blinkt/openvpn/VpnProfile.java24
-rw-r--r--main/src/main/java/de/blinkt/openvpn/core/ConfigParser.java14
-rwxr-xr-xmain/src/main/res/values/strings.xml4
-rw-r--r--main/src/test/java/de/blinkt/openvpn/core/TestConfigParser.kt40
-rw-r--r--main/src/ui/java/de/blinkt/openvpn/fragments/Settings_Basic.java19
-rw-r--r--main/src/ui/res/layout/basic_settings.xml14
6 files changed, 108 insertions, 7 deletions
diff --git a/main/src/main/java/de/blinkt/openvpn/VpnProfile.java b/main/src/main/java/de/blinkt/openvpn/VpnProfile.java
index 2ccff18b..b32d17ad 100644
--- a/main/src/main/java/de/blinkt/openvpn/VpnProfile.java
+++ b/main/src/main/java/de/blinkt/openvpn/VpnProfile.java
@@ -98,7 +98,7 @@ public class VpnProfile implements Serializable, Cloneable {
public String mTLSAuthFilename;
public String mClientKeyFilename;
public String mCaFilename;
- public boolean mUseLzo = true;
+ public boolean mUseLzo = false;
public String mPKCS12Filename;
public String mPKCS12Password;
public boolean mUseTLSAuth = false;
@@ -167,6 +167,8 @@ public class VpnProfile implements Serializable, Cloneable {
public String mDataCiphers = "";
public boolean mBlockUnusedAddressFamilies =true;
+ public boolean mCheckPeerFingerprint = false;
+ public String mPeerFingerPrints = "";
public VpnProfile(String name) {
mUuid = UUID.randomUUID();
@@ -436,7 +438,9 @@ public class VpnProfile implements Serializable, Cloneable {
cfg.append("auth-user-pass\n");
case VpnProfile.TYPE_CERTIFICATES:
// Ca
- cfg.append(insertFileData("ca", mCaFilename));
+ if (!TextUtils.isEmpty(mCaFilename)) {
+ cfg.append(insertFileData("ca", mCaFilename));
+ }
// Client Cert + Key
cfg.append(insertFileData("key", mClientKeyFilename));
@@ -462,7 +466,12 @@ public class VpnProfile implements Serializable, Cloneable {
String[] ks = getExternalCertificates(context);
cfg.append("### From Keystore/ext auth app ####\n");
if (ks != null) {
- cfg.append("<ca>\n").append(ks[0]).append("\n</ca>\n");
+ if (!TextUtils.isEmpty(mCaFilename)) {
+ cfg.append(insertFileData("ca", mCaFilename));
+ }
+ else if (!TextUtils.isEmpty(ks[0])) {
+ cfg.append("<ca>\n").append(ks[0]).append("\n</ca>\n");
+ }
if (!TextUtils.isEmpty(ks[1]))
cfg.append("<extra-certs>\n").append(ks[1]).append("\n</extra-certs>\n");
cfg.append("<cert>\n").append(ks[2]).append("\n</cert>\n");
@@ -484,6 +493,11 @@ public class VpnProfile implements Serializable, Cloneable {
}
}
+ if (mCheckPeerFingerprint)
+ {
+ cfg.append("<peer-fingerprint>\n").append(mPeerFingerPrints).append("\n</peer-fingerprint>\n");
+ }
+
if (isUserPWAuth()) {
if (mAuthRetry == AUTH_RETRY_NOINTERACT)
cfg.append("auth-retry nointeract\n");
@@ -963,7 +977,7 @@ public class VpnProfile implements Serializable, Cloneable {
if (mAlias == null)
return R.string.no_keystore_cert_selected;
} else if (mAuthenticationType == TYPE_CERTIFICATES || mAuthenticationType == TYPE_USERPASS_CERTIFICATES) {
- if (TextUtils.isEmpty(mCaFilename))
+ if (TextUtils.isEmpty(mCaFilename) && !mCheckPeerFingerprint)
return R.string.no_ca_cert_selected;
}
@@ -1259,7 +1273,7 @@ public class VpnProfile implements Serializable, Cloneable {
return false;
}
- class NoCertReturnedException extends Exception {
+ static class NoCertReturnedException extends Exception {
public NoCertReturnedException(String msg) {
super(msg);
}
diff --git a/main/src/main/java/de/blinkt/openvpn/core/ConfigParser.java b/main/src/main/java/de/blinkt/openvpn/core/ConfigParser.java
index 6e4d8151..d25c20c0 100644
--- a/main/src/main/java/de/blinkt/openvpn/core/ConfigParser.java
+++ b/main/src/main/java/de/blinkt/openvpn/core/ConfigParser.java
@@ -568,6 +568,20 @@ public class ConfigParser {
np.mCaFilename = ca.get(1);
}
+ Vector<Vector<String>> peerfp = getAllOption("peer-fingerprint", 1, 1);
+ if (peerfp != null)
+ {
+ np.mCheckPeerFingerprint = true;
+ for (Vector<String> fp: peerfp)
+ {
+ if (fp.get(1).startsWith(VpnProfile.INLINE_TAG))
+ np.mPeerFingerPrints+=fp.get(1).substring(VpnProfile.INLINE_TAG.length()) + "\n";
+ else
+ np.mPeerFingerPrints+=fp.get(1) + "\n";
+ }
+ }
+
+
Vector<String> cert = getOption("cert", 1, 1);
if (cert != null) {
np.mClientCertFilename = cert.get(1);
diff --git a/main/src/main/res/values/strings.xml b/main/src/main/res/values/strings.xml
index 4aa83ee9..fc4f5a74 100755
--- a/main/src/main/res/values/strings.xml
+++ b/main/src/main/res/values/strings.xml
@@ -42,7 +42,7 @@
<string name="duplicate_profile_name">Please enter a unique Profile Name</string>
<string name="profilename">Profile Name</string>
<string name="no_keystore_cert_selected">You must select a User certificate</string>
- <string name="no_ca_cert_selected">You must select a CA certificate</string>
+ <string name="no_ca_cert_selected">You must select a CA certificate or enable peer fingerprint check</string>
<string name="no_error_found">No error found</string>
<string name="config_error_found">Error in Configuration</string>
<string name="ipv4_format_error">Error parsing the IPv4 address</string>
@@ -500,5 +500,7 @@
<string name="internal_web_view">Internal WebView</string>
<string name="faq_title_ncp">Failed to negotiate cipher with server</string>
<string name="faq_ncp">There are some variation of this message depending on the exact situation. They all have in common that server and client could not agree on a common cipher. The main reasons are: &lt;ul>&lt;li> You are still relying on the fact that OpenVPN 2.4 and older allowed BF-CBC in the default configuration (if no --cipher was set). OpenVPN 2.5 does not allow it per default anymore since it is a &lt;a href="https://community.openvpn.net/openvpn/wiki/SWEET32">broken/outdated cipher&lt;/a>.&lt;/li>&lt;li>The server runs OpenVPN 2.3 (or even older) with --enable-small (at least 4-5 year old OpenVPN)&lt;/li>&lt;li>Broken configuration (e.g., mismatching data-ciphers on client and server)&lt;/li> &lt;p> The &lt;a href=\"https://github.com/OpenVPN/openvpn/blob/master/doc/man-sections/cipher-negotiation.rst\">OpenVPN manual section on cipher negotiation&lt;/a> explains the different scenarios of cipher negotiation very well and what to do in these situation.&lt;p>TP-Link devices use a at least 5 year old OpenVPN 2.3.x version (possibly older) on their devices, even in the 2019/2020 models.&lt;p>Last but not least, there is a popular VPN provider that has a broken server that always says it is using \'BF-CBC\' because its developer thought it would be a good idea to create a proprietary cipher negotiation patch that is incompatible with standard OpenVPN.&lt;p>In summary: all sane configurations should not get these errors. But (apart from the broken VPN provider\'s server) the client can be persuaded to still connect (fixing the sympton and not the real problem).</string>
+ <string name="check_peer_fingerprint">Check peer certificate fingerprint</string>
+ <string name="fingerprint">(Enter the SHA256 fingerprint of the server certificate(s))</string>
</resources>
diff --git a/main/src/test/java/de/blinkt/openvpn/core/TestConfigParser.kt b/main/src/test/java/de/blinkt/openvpn/core/TestConfigParser.kt
index 2983982d..fbaa4be2 100644
--- a/main/src/test/java/de/blinkt/openvpn/core/TestConfigParser.kt
+++ b/main/src/test/java/de/blinkt/openvpn/core/TestConfigParser.kt
@@ -348,4 +348,44 @@ verify-x509-name homevpn.evil.cloud name
Assert.assertFalse(config.contains("key-direction"))
}
+ @Test
+ @Throws(IOException::class, ConfigParser.ConfigParseError::class)
+ fun testPeerFingerprint() {
+ val conf = """
+<cert>
+dummy
+</cert>
+cipher AES-256-GCM
+client
+dev-type tun
+<key>
+dummykey
+</key>
+remote home.evil.cloud 65443 udp
+""";
+ val fps = """
+ 28:45:c7:ad:6a:c4:83:c7:a0:0a:0a:91:4b:43:e3:09:79:05:a2:ce:c2:e2:5e:c9:70:5a:2b:a4:e1:0f:97:e3
+ F8:FA:6D:CF:58:65:98:5F:E0:E7:2A:B4:25:ED:2C:DD:45:7B:21:C1:B7:46:1D:46:C3:2B:1D:1D:F7:0E:43:51
+ ef:5c:fc:a4:d5:59:78:14:e0:87:66:0b:53:df:e5:1e:a1:39:e0:1f:7a:ca:ca:87:4e:78:8b:45:c7:3d:af:c7
+ """.trimIndent()
+ val fpBlock = "<peer-fingerprint>\n${fps}\n</peer-fingerprint>"
+
+ val fpSingle = "00:11:22:33:44:55:66:77:88:99:aa:bb:cc:dd:ee:ff:00:11:22:33:44:55:66:77:88:99:aa:bb:cc:dd:ee:ff"
+ val fpSingleCmd = "peer-fingerprint ${fpSingle}\n"
+
+ val cp = ConfigParser()
+ cp.parseConfig(StringReader(conf + fpBlock))
+ val vp = cp.convertProfile()
+
+ Assert.assertTrue(vp.mCheckPeerFingerprint)
+ Assert.assertEquals(fps.trim(), vp.mPeerFingerPrints.trim())
+
+ cp.parseConfig(StringReader(conf + fpBlock + "\n" + fpSingleCmd))
+ val vp2 = cp.convertProfile()
+ Assert.assertTrue(vp2.mCheckPeerFingerprint)
+ Assert.assertEquals((fps + "\n" + fpSingle).trim(), vp2.mPeerFingerPrints.trim())
+
+
+ }
+
}
diff --git a/main/src/ui/java/de/blinkt/openvpn/fragments/Settings_Basic.java b/main/src/ui/java/de/blinkt/openvpn/fragments/Settings_Basic.java
index 294b33fb..0afb754d 100644
--- a/main/src/ui/java/de/blinkt/openvpn/fragments/Settings_Basic.java
+++ b/main/src/ui/java/de/blinkt/openvpn/fragments/Settings_Basic.java
@@ -20,7 +20,7 @@ import de.blinkt.openvpn.VpnProfile;
import de.blinkt.openvpn.core.ProfileManager;
import de.blinkt.openvpn.views.FileSelectLayout;
-public class Settings_Basic extends KeyChainSettingsFragment implements OnItemSelectedListener, FileSelectLayout.FileSelectCallback {
+public class Settings_Basic extends KeyChainSettingsFragment implements OnItemSelectedListener, FileSelectLayout.FileSelectCallback, CompoundButton.OnCheckedChangeListener {
private static final int CHOOSE_FILE_OFFSET = 1000;
private FileSelectLayout mClientCert;
@@ -36,6 +36,8 @@ public class Settings_Basic extends KeyChainSettingsFragment implements OnItemSe
private View mView;
private EditText mProfileName;
private EditText mKeyPassword;
+ private CheckBox mEnablePeerFingerprint;
+ private EditText mPeerFingerprints;
private SparseArray<FileSelectLayout> fileselects = new SparseArray<>();
private Spinner mAuthRetry;
@@ -68,6 +70,8 @@ public class Settings_Basic extends KeyChainSettingsFragment implements OnItemSe
mUseLzo = mView.findViewById(id.lzo);
mType = mView.findViewById(id.type);
mPKCS12Password = mView.findViewById(id.pkcs12password);
+ mEnablePeerFingerprint = mView.findViewById(id.enable_peer_fingerprint);
+ mPeerFingerprints = mView.findViewById(id.peer_fingerprint);
mUserName = mView.findViewById(id.auth_username);
mPassword = mView.findViewById(id.auth_password);
@@ -84,6 +88,7 @@ public class Settings_Basic extends KeyChainSettingsFragment implements OnItemSe
mType.setOnItemSelectedListener(this);
mAuthRetry.setOnItemSelectedListener(this);
+ mEnablePeerFingerprint.setOnCheckedChangeListener(this);
initKeychainViews(mView);
@@ -192,6 +197,8 @@ public class Settings_Basic extends KeyChainSettingsFragment implements OnItemSe
mPassword.setText(mProfile.mPassword);
mKeyPassword.setText(mProfile.mKeyPassword);
mAuthRetry.setSelection(mProfile.mAuthRetry);
+ mEnablePeerFingerprint.setChecked(mProfile.mCheckPeerFingerprint);
+ mPeerFingerprints.setText(mProfile.mPeerFingerPrints);
}
protected void savePreferences() {
@@ -211,6 +218,8 @@ public class Settings_Basic extends KeyChainSettingsFragment implements OnItemSe
mProfile.mUsername = mUserName.getText().toString();
mProfile.mKeyPassword = mKeyPassword.getText().toString();
mProfile.mAuthRetry = mAuthRetry.getSelectedItemPosition();
+ mProfile.mCheckPeerFingerprint = mEnablePeerFingerprint.isChecked();
+ mProfile.mPeerFingerPrints = mPeerFingerprints.getText().toString();
}
@@ -228,4 +237,12 @@ public class Settings_Basic extends KeyChainSettingsFragment implements OnItemSe
}
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ if (buttonView == mEnablePeerFingerprint)
+ {
+ mPeerFingerprints.setVisibility(isChecked ? View.VISIBLE : View.GONE);
+
+ }
+ }
}
diff --git a/main/src/ui/res/layout/basic_settings.xml b/main/src/ui/res/layout/basic_settings.xml
index 66f8b1fa..6763e52f 100644
--- a/main/src/ui/res/layout/basic_settings.xml
+++ b/main/src/ui/res/layout/basic_settings.xml
@@ -88,6 +88,20 @@
blinkt:showClear="true"
blinkt:fileTitle="@string/ca_title" />
+ <CheckBox
+ android:id="@+id/enable_peer_fingerprint"
+ style="@style/item"
+ android:text="@string/check_peer_fingerprint"
+ />
+
+ <EditText
+ android:id="@+id/peer_fingerprint"
+ style="@style/item"
+ android:inputType="textMultiLine"
+ android:layout_marginLeft="8dip"
+ android:layout_marginRight="8dip"
+ android:hint="@string/fingerprint"
+ android:visibility="gone" />
</LinearLayout>
<LinearLayout