diff options
author | kwadronaut <kwadronaut@leap.se> | 2016-09-19 12:38:29 +0200 |
---|---|---|
committer | kwadronaut <kwadronaut@leap.se> | 2016-09-19 12:38:29 +0200 |
commit | f9c44da143a2d29baac6260c2e5caf9a96dacbc3 (patch) | |
tree | c2435766b6a8d7ddd70f2a0dcfc501a7d93c45f4 /app/src/main/java/de/blinkt/openvpn/VpnProfile.java | |
parent | 00ebc6d66ff9e8a0fa01b808ce7dbd0335933baf (diff) | |
parent | 56ba118a5713fe0e24120815cd28db141709a497 (diff) |
Merge remote-tracking branch 'upstream/develop' into develop
Diffstat (limited to 'app/src/main/java/de/blinkt/openvpn/VpnProfile.java')
-rw-r--r-- | app/src/main/java/de/blinkt/openvpn/VpnProfile.java | 289 |
1 files changed, 141 insertions, 148 deletions
diff --git a/app/src/main/java/de/blinkt/openvpn/VpnProfile.java b/app/src/main/java/de/blinkt/openvpn/VpnProfile.java index 43e1b57c..38d76f68 100644 --- a/app/src/main/java/de/blinkt/openvpn/VpnProfile.java +++ b/app/src/main/java/de/blinkt/openvpn/VpnProfile.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2014 Arne Schwabe + * Copyright (c) 2012-2016 Arne Schwabe * Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt */ @@ -18,6 +18,7 @@ import android.os.Build; import android.preference.PreferenceManager; import android.security.KeyChain; import android.security.KeyChainException; +import android.support.annotation.NonNull; import android.text.TextUtils; import android.util.Base64; @@ -63,7 +64,7 @@ public class VpnProfile implements Serializable, Cloneable { // The Serializable documentation mentions that class name change are possible // but the how is unclear // - transient public static final long MAX_EMBED_FILE_SIZE = 2048*1024; // 2048kB + transient public static final long MAX_EMBED_FILE_SIZE = 2048 * 1024; // 2048kB // Don't change this, not all parts of the program use this constant public static final String EXTRA_PROFILEUUID = "de.blinkt.openvpn.profileUUID"; public static final String INLINE_TAG = "[[INLINE]]"; @@ -71,7 +72,7 @@ public class VpnProfile implements Serializable, Cloneable { private static final long serialVersionUID = 7085688938959334563L; public static final int MAXLOGLEVEL = 4; - public static final int CURRENT_PROFILE_VERSION = 5; + public static final int CURRENT_PROFILE_VERSION = 6; public static final int DEFAULT_MSSFIX_SIZE = 1450; public static String DEFAULT_DNS1 = "8.8.8.8"; public static String DEFAULT_DNS2 = "8.8.4.4"; @@ -149,27 +150,31 @@ public class VpnProfile implements Serializable, Cloneable { private int mProfileVersion; public String mExcludedRoutes; public String mExcludedRoutesv6; - public int mMssFix =0; // -1 is default, + public int mMssFix = 0; // -1 is default, public Connection[] mConnections = new Connection[0]; - public boolean mRemoteRandom=false; - public HashSet<String> mAllowedAppsVpn = new HashSet<String>(); + public boolean mRemoteRandom = false; + public HashSet<String> mAllowedAppsVpn = new HashSet<>(); public boolean mAllowedAppsVpnAreDisallowed = true; + + public String mCrlFilename; public String mProfileCreator; - /* Options no long used in new profiles */ + + public boolean mPushPeerInfo = false; + public static final boolean mIsOpenVPN22 = false; + + /* Options no longer used in new profiles */ public String mServerName = "openvpn.blinkt.de"; public String mServerPort = "1194"; public boolean mUseUdp = true; - - public VpnProfile(String name) { mUuid = UUID.randomUUID(); mName = name; mProfileVersion = CURRENT_PROFILE_VERSION; mConnections = new Connection[1]; - mConnections[0] = new Connection(); + mConnections[0] = new Connection(); } public static String openVpnEscape(String unescaped) { @@ -197,6 +202,7 @@ public class VpnProfile implements Serializable, Cloneable { mCheckRemoteCN = false; mPersistTun = false; mAllowLocalLAN = true; + mPushPeerInfo = false; mMssFix = 0; } @@ -206,27 +212,33 @@ public class VpnProfile implements Serializable, Cloneable { } public String getName() { - if (mName==null) + if (mName == null) return "No profile name"; return mName; } - public void upgradeProfile(){ - if(mProfileVersion< 2) { + public void upgradeProfile() { + if (mProfileVersion < 2) { /* default to the behaviour the OS used */ mAllowLocalLAN = Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT; } if (mProfileVersion < 4) { moveOptionsToConnection(); - mAllowedAppsVpnAreDisallowed=true; + mAllowedAppsVpnAreDisallowed = true; } - if (mAllowedAppsVpn==null) - mAllowedAppsVpn = new HashSet<String>(); - if (mConnections ==null) + if (mAllowedAppsVpn == null) + mAllowedAppsVpn = new HashSet<>(); + if (mConnections == null) mConnections = new Connection[0]; - mProfileVersion= CURRENT_PROFILE_VERSION; + if (mProfileVersion < 6) { + if (TextUtils.isEmpty(mProfileCreator)) + mUserEditable = true; + } + + + mProfileVersion = CURRENT_PROFILE_VERSION; } @@ -260,8 +272,12 @@ public class VpnProfile implements Serializable, Cloneable { cfg += "management-query-passwords\n"; cfg += "management-hold\n\n"; - if (!configForOvpn3) + if (!configForOvpn3) { cfg += String.format("setenv IV_GUI_VER %s \n", openVpnEscape(getVersionEnvString(context))); + String versionString = String.format("%d %s %s %s %s %s", Build.VERSION.SDK_INT, Build.VERSION.RELEASE, + NativeUtils.getNativeAPI(), Build.BRAND, Build.BOARD, Build.MODEL); + cfg += String.format("setenv IV_PLAT_VER %s\n", openVpnEscape(versionString)); + } cfg += "machine-readable-output\n"; @@ -293,7 +309,8 @@ public class VpnProfile implements Serializable, Cloneable { mConnectRetry = "5"; - cfg += "connect-retry " + mConnectRetry + "\n"; + if (!mIsOpenVPN22 || !mUseUdp) + cfg += "connect-retry " + mConnectRetry + "\n"; cfg += "resolv-retry 60\n"; @@ -304,7 +321,7 @@ public class VpnProfile implements Serializable, Cloneable { boolean canUsePlainRemotes = true; - if (mConnections.length==1) { + if (mConnections.length == 1) { cfg += mConnections[0].getConnectionBlock(); } else { for (Connection conn : mConnections) { @@ -312,7 +329,7 @@ public class VpnProfile implements Serializable, Cloneable { } if (mRemoteRandom) - cfg+="remote-random\n"; + cfg += "remote-random\n"; if (canUsePlainRemotes) { for (Connection conn : mConnections) { @@ -367,6 +384,9 @@ public class VpnProfile implements Serializable, Cloneable { cfg += insertFileData("ca", mCaFilename); } + if (!TextUtils.isEmpty(mCrlFilename)) + cfg += insertFileData("crl-verify", mCrlFilename); + if (mUseLzo) { cfg += "comp-lzo\n"; } @@ -400,13 +420,12 @@ public class VpnProfile implements Serializable, Cloneable { if (mUseDefaultRoute) routes += "route 0.0.0.0 0.0.0.0 vpn_gateway\n"; - else - { + else { for (String route : getCustomRoutes(mCustomRoutes)) { routes += "route " + route + " vpn_gateway\n"; } - for (String route: getCustomRoutes(mExcludedRoutes)) { + for (String route : getCustomRoutes(mExcludedRoutes)) { routes += "route " + route + " net_gateway\n"; } } @@ -431,11 +450,11 @@ public class VpnProfile implements Serializable, Cloneable { } - if (mMssFix !=0){ - if (mMssFix!=1450) - cfg+=String.format("mssfix %d\n", mMssFix, Locale.US); - else - cfg+="mssfix\n"; + if (mMssFix != 0) { + if (mMssFix != 1450) { + cfg += String.format("mssfix %d\n", mMssFix, Locale.US); + } else + cfg += "mssfix\n"; } if (mNobind) @@ -495,9 +514,12 @@ public class VpnProfile implements Serializable, Cloneable { cfg += "preresolve\n"; } + if (mPushPeerInfo) + cfg += "push-peer-info\n"; + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); boolean usesystemproxy = prefs.getBoolean("usesystemproxy", true); - if (usesystemproxy) { + if (usesystemproxy && !mIsOpenVPN22) { cfg += "# Use system proxy setting\n"; cfg += "management-query-proxy\n"; } @@ -523,8 +545,6 @@ public class VpnProfile implements Serializable, Cloneable { } - - return cfg; } @@ -541,10 +561,9 @@ public class VpnProfile implements Serializable, Cloneable { } //! Put inline data inline and other data as normal escaped filename - private String insertFileData(String cfgentry, String filedata) { + public static String insertFileData(String cfgentry, String filedata) { if (filedata == null) { - // TODO: generate good error - return String.format("%s %s\n", cfgentry, "missing"); + return String.format("%s %s\n", cfgentry, "file missing in config profile"); } else if (isEmbedded(filedata)) { String dataWithOutHeader = getEmbeddedContent(filedata); return String.format(Locale.ENGLISH, "<%s>\n%s\n</%s>\n", cfgentry, dataWithOutHeader, cfgentry); @@ -553,8 +572,9 @@ public class VpnProfile implements Serializable, Cloneable { } } + @NonNull private Collection<String> getCustomRoutes(String routes) { - Vector<String> cidrRoutes = new Vector<String>(); + Vector<String> cidrRoutes = new Vector<>(); if (routes == null) { // No routes set, return empty vector return cidrRoutes; @@ -563,7 +583,7 @@ public class VpnProfile implements Serializable, Cloneable { if (!route.equals("")) { String cidrroute = cidrToIPAndNetmask(route); if (cidrroute == null) - return null; + return cidrRoutes; cidrRoutes.add(cidrroute); } @@ -573,7 +593,7 @@ public class VpnProfile implements Serializable, Cloneable { } private Collection<String> getCustomRoutesv6(String routes) { - Vector<String> cidrRoutes = new Vector<String>(); + Vector<String> cidrRoutes = new Vector<>(); if (routes == null) { // No routes set, return empty vector return cidrRoutes; @@ -606,47 +626,40 @@ public class VpnProfile implements Serializable, Cloneable { return null; - long nm = 0xffffffffl; - nm = (nm << (32 - len)) & 0xffffffffl; + long nm = 0xffffffffL; + nm = (nm << (32 - len)) & 0xffffffffL; String netmask = String.format(Locale.ENGLISH, "%d.%d.%d.%d", (nm & 0xff000000) >> 24, (nm & 0xff0000) >> 16, (nm & 0xff00) >> 8, nm & 0xff); return parts[0] + " " + netmask; } - - public Intent prepareStartService(Context context) { Intent intent = getStartServiceIntent(context); + // TODO: Handle this?! +// if (mAuthenticationType == VpnProfile.TYPE_KEYSTORE || mAuthenticationType == VpnProfile.TYPE_USERPASS_KEYSTORE) { +// if (getKeyStoreCertificates(context) == null) +// return null; +// } - if (mAuthenticationType == VpnProfile.TYPE_KEYSTORE || mAuthenticationType == VpnProfile.TYPE_USERPASS_KEYSTORE) { - if (getKeyStoreCertificates(context) == null) - return null; - } - + return intent; + } - try { - FileWriter cfg = new FileWriter(VPNLaunchHelper.getConfigFilePath(context)); - cfg.write(getConfigFile(context, false)); - cfg.flush(); - cfg.close(); - } catch (IOException e) { - VpnStatus.logException(e); - } + public void writeConfigFile(Context context) throws IOException { + FileWriter cfg = new FileWriter(VPNLaunchHelper.getConfigFilePath(context)); + cfg.write(getConfigFile(context, false)); + cfg.flush(); + cfg.close(); - return intent; } public Intent getStartServiceIntent(Context context) { String prefix = context.getPackageName(); Intent intent = new Intent(context, OpenVPNService.class); - intent.putExtra(prefix + ".ARGV", VPNLaunchHelper.buildOpenvpnArgv(context)); intent.putExtra(prefix + ".profileUUID", mUuid.toString()); - ApplicationInfo info = context.getApplicationInfo(); - intent.putExtra(prefix + ".nativelib", info.nativeLibraryDir); return intent; } @@ -657,11 +670,10 @@ public class VpnProfile implements Serializable, Cloneable { public static String getDisplayName(String embeddedFile) { int start = DISPLAYNAME_TAG.length(); int end = embeddedFile.indexOf(INLINE_TAG); - return embeddedFile.substring(start,end); + return embeddedFile.substring(start, end); } - public static String getEmbeddedContent(String data) - { + public static String getEmbeddedContent(String data) { if (!data.contains(INLINE_TAG)) return data; @@ -670,7 +682,7 @@ public class VpnProfile implements Serializable, Cloneable { } public static boolean isEmbedded(String data) { - if (data==null) + if (data == null) return false; if (data.startsWith(INLINE_TAG) || data.startsWith(DISPLAYNAME_TAG)) return true; @@ -682,8 +694,8 @@ public class VpnProfile implements Serializable, Cloneable { /* This method is called when OpenVPNService is restarted */ if ((mAuthenticationType == VpnProfile.TYPE_KEYSTORE || mAuthenticationType == VpnProfile.TYPE_USERPASS_KEYSTORE) - && mPrivateKey==null) { - new Thread( new Runnable() { + && mPrivateKey == null) { + new Thread(new Runnable() { @Override public void run() { getKeyStoreCertificates(context); @@ -698,9 +710,9 @@ public class VpnProfile implements Serializable, Cloneable { VpnProfile copy = (VpnProfile) super.clone(); copy.mUuid = UUID.randomUUID(); copy.mConnections = new Connection[mConnections.length]; - int i=0; - for (Connection conn: mConnections) { - copy.mConnections[i++]=conn.clone(); + int i = 0; + for (Connection conn : mConnections) { + copy.mConnections[i++] = conn.clone(); } copy.mAllowedAppsVpn = (HashSet<String>) mAllowedAppsVpn.clone(); return copy; @@ -708,7 +720,7 @@ public class VpnProfile implements Serializable, Cloneable { public VpnProfile copy(String name) { try { - VpnProfile copy = (VpnProfile) clone(); + VpnProfile copy = clone(); copy.mName = name; return copy; @@ -720,24 +732,21 @@ public class VpnProfile implements Serializable, Cloneable { class NoCertReturnedException extends Exception { - public NoCertReturnedException (String msg) { + public NoCertReturnedException(String msg) { super(msg); } } - synchronized String[] getKeyStoreCertificates(Context context,int tries) { - PrivateKey privateKey = null; - X509Certificate[] caChain; - Exception exp; + synchronized String[] getKeyStoreCertificates(Context context, int tries) { try { - privateKey = KeyChain.getPrivateKey(context, mAlias); + PrivateKey privateKey = KeyChain.getPrivateKey(context, mAlias); mPrivateKey = privateKey; String keystoreChain = null; - caChain = KeyChain.getCertificateChain(context, mAlias); - if(caChain == null) + X509Certificate[] caChain = KeyChain.getCertificateChain(context, mAlias); + if (caChain == null) throw new NoCertReturnedException("No certificate returned from Keystore"); if (caChain.length <= 1 && TextUtils.isEmpty(mCaFilename)) { @@ -758,13 +767,14 @@ public class VpnProfile implements Serializable, Cloneable { String caout = null; if (!TextUtils.isEmpty(mCaFilename)) { try { - Certificate cacert = X509Utils.getCertificateFromFile(mCaFilename); + Certificate[] cacerts = X509Utils.getCertificatesFromFile(mCaFilename); StringWriter caoutWriter = new StringWriter(); PemWriter pw = new PemWriter(caoutWriter); - pw.writeObject(new PemObject("CERTIFICATE", cacert.getEncoded())); + for (Certificate cert : cacerts) + pw.writeObject(new PemObject("CERTIFICATE", cert.getEncoded())); pw.close(); - caout= caoutWriter.toString(); + caout = caoutWriter.toString(); } catch (Exception e) { VpnStatus.logError("Could not read CA certificate" + e.getLocalizedMessage()); @@ -787,54 +797,43 @@ public class VpnProfile implements Serializable, Cloneable { String ca, extra; - if(caout==null) { - ca =keystoreChain; - extra=null; + if (caout == null) { + ca = keystoreChain; + extra = null; } else { ca = caout; - extra=keystoreChain; + extra = keystoreChain; } return new String[]{ca, extra, user}; - } catch (InterruptedException e) { - exp=e; - } catch (FileNotFoundException e) { - exp=e; - } catch (CertificateException e) { - exp=e; - } catch (IOException e) { - exp=e; - } catch (KeyChainException e) { - exp=e; - } catch (NoCertReturnedException e) { - exp =e; - } catch (IllegalArgumentException e) { - exp =e; + } catch (InterruptedException | IOException | KeyChainException | NoCertReturnedException | IllegalArgumentException + | CertificateException e) { + e.printStackTrace(); + VpnStatus.logError(R.string.keyChainAccessError, e.getLocalizedMessage()); + + VpnStatus.logError(R.string.keychain_access); + if (Build.VERSION.SDK_INT == Build.VERSION_CODES.JELLY_BEAN) { + if (!mAlias.matches("^[a-zA-Z0-9]$")) { + VpnStatus.logError(R.string.jelly_keystore_alphanumeric_bug); + } + } + return null; + } catch (AssertionError e) { - if (tries ==0) + if (tries == 0) return null; - VpnStatus.logError(String.format("Failure getting Keystore Keys (%s), retrying",e.getLocalizedMessage())); + VpnStatus.logError(String.format("Failure getting Keystore Keys (%s), retrying", e.getLocalizedMessage())); try { Thread.sleep(3000); } catch (InterruptedException e1) { VpnStatus.logException(e1); } - return getKeyStoreCertificates(context, tries-1); + return getKeyStoreCertificates(context, tries - 1); } - exp.printStackTrace(); - VpnStatus.logError(R.string.keyChainAccessError, exp.getLocalizedMessage()); - - VpnStatus.logError(R.string.keychain_access); - if (Build.VERSION.SDK_INT == Build.VERSION_CODES.JELLY_BEAN) { - if (!mAlias.matches("^[a-zA-Z0-9]$")) { - VpnStatus.logError(R.string.jelly_keystore_alphanumeric_bug); - } - } - return null; } - //! Return an error if somethign is wrong + //! Return an error if something is wrong public int checkProfile(Context context) { if (mAuthenticationType == TYPE_KEYSTORE || mAuthenticationType == TYPE_USERPASS_KEYSTORE) { if (mAlias == null) @@ -845,15 +844,33 @@ public class VpnProfile implements Serializable, Cloneable { if (mIPv4Address == null || cidrToIPAndNetmask(mIPv4Address) == null) return R.string.ipv4_format_error; } - if (!mUseDefaultRoute && (getCustomRoutes(mCustomRoutes) == null || getCustomRoutes(mExcludedRoutes) ==null)) - return R.string.custom_route_format_error; + if (!mUseDefaultRoute) { + if (!TextUtils.isEmpty(mCustomRoutes) && getCustomRoutes(mCustomRoutes).size() == 0) + return R.string.custom_route_format_error; + + if (!TextUtils.isEmpty(mExcludedRoutes) && getCustomRoutes(mExcludedRoutes).size() == 0) + return R.string.custom_route_format_error; + + } + + if (mUseTLSAuth && TextUtils.isEmpty(mTLSAuthFilename)) + return R.string.missing_tlsauth; + + if ((mAuthenticationType == TYPE_USERPASS_CERTIFICATES || mAuthenticationType == TYPE_CERTIFICATES) + && (TextUtils.isEmpty(mClientCertFilename) || TextUtils.isEmpty(mClientKeyFilename))) + return R.string.missing_certificates; + + if ((mAuthenticationType == TYPE_CERTIFICATES || mAuthenticationType == TYPE_USERPASS_CERTIFICATES) + && TextUtils.isEmpty(mCaFilename)) + return R.string.missing_ca_certificate; + boolean noRemoteEnabled = true; for (Connection c : mConnections) if (c.mEnabled) noRemoteEnabled = false; - if(noRemoteEnabled) + if (noRemoteEnabled) return R.string.remote_no_server_selected; // Everything okay @@ -948,7 +965,7 @@ public class VpnProfile implements Serializable, Cloneable { if (isUserPWAuth() && (TextUtils.isEmpty(mUsername) || - (TextUtils.isEmpty(mPassword) && (mTransientPW == null || ignoreTransient)))) { + (TextUtils.isEmpty(mPassword) && (mTransientPW == null || ignoreTransient)))) { return R.string.password; } return 0; @@ -980,7 +997,6 @@ public class VpnProfile implements Serializable, Cloneable { public String getSignedData(String b64data) { PrivateKey privkey = getKeystoreKey(); - Exception err; byte[] data = Base64.decode(b64data, Base64.DEFAULT); @@ -1004,26 +1020,14 @@ public class VpnProfile implements Serializable, Cloneable { byte[] signed_bytes = rsaSigner.doFinal(data); return Base64.encodeToString(signed_bytes, Base64.NO_WRAP); - } catch (NoSuchAlgorithmException e) { - err = e; - } catch (InvalidKeyException e) { - err = e; - } catch (NoSuchPaddingException e) { - err = e; - } catch (IllegalBlockSizeException e) { - err = e; - } catch (BadPaddingException e) { - err = e; + } catch (NoSuchAlgorithmException | InvalidKeyException | IllegalBlockSizeException + | BadPaddingException | NoSuchPaddingException e) { + VpnStatus.logError(R.string.error_rsa_sign, e.getClass().toString(), e.getLocalizedMessage()); + return null; } - - VpnStatus.logError(R.string.error_rsa_sign, err.getClass().toString(), err.getLocalizedMessage()); - - return null; - } private String processSignJellyBeans(PrivateKey privkey, byte[] data) { - Exception err; try { Method getKey = privkey.getClass().getSuperclass().getDeclaredMethod("getOpenSSLKey"); getKey.setAccessible(true); @@ -1044,21 +1048,10 @@ public class VpnProfile implements Serializable, Cloneable { byte[] signed_bytes = NativeUtils.rsasign(data, pkey); return Base64.encodeToString(signed_bytes, Base64.NO_WRAP); - } catch (NoSuchMethodException e) { - err = e; - } catch (IllegalArgumentException e) { - err = e; - } catch (IllegalAccessException e) { - err = e; - } catch (InvocationTargetException e) { - err = e; - } catch (InvalidKeyException e) { - err = e; + } catch (NoSuchMethodException | InvalidKeyException | InvocationTargetException | IllegalAccessException | IllegalArgumentException e) { + VpnStatus.logError(R.string.error_rsa_sign, e.getClass().toString(), e.getLocalizedMessage()); + return null; } - VpnStatus.logError(R.string.error_rsa_sign, err.getClass().toString(), err.getLocalizedMessage()); - - return null; - } |