summaryrefslogtreecommitdiff
path: root/main
diff options
context:
space:
mode:
authorArne Schwabe <arne@rfc2549.org>2018-07-02 18:47:24 +0200
committerArne Schwabe <arne@rfc2549.org>2018-07-02 18:47:24 +0200
commit270290b7f36038876383673f123b805e31eb50db (patch)
tree49b05499f2212a04689cc4e94b87b4bd0d8948b6 /main
parentd987d942d15481080e968463a8ffe84a41f88574 (diff)
Implement app restrictions for MDM managed profiles
Diffstat (limited to 'main')
-rw-r--r--main/src/main/AndroidManifest.xml9
-rw-r--r--main/src/main/java/android/support/v4n/view/ViewPager.java4
-rw-r--r--main/src/main/java/de/blinkt/openvpn/VpnProfile.java11
-rw-r--r--main/src/main/java/de/blinkt/openvpn/activities/BaseActivity.java16
-rw-r--r--main/src/main/java/de/blinkt/openvpn/activities/MainActivity.java1
-rw-r--r--main/src/main/java/de/blinkt/openvpn/api/AppRestrictions.java182
-rw-r--r--main/src/main/java/de/blinkt/openvpn/core/OpenVPNService.java2
-rw-r--r--main/src/main/java/de/blinkt/openvpn/fragments/KeyChainSettingsFragment.java146
-rw-r--r--main/src/main/java/de/blinkt/openvpn/fragments/Settings_Basic.java166
-rw-r--r--main/src/main/java/de/blinkt/openvpn/fragments/Settings_UserEditable.java40
-rw-r--r--main/src/main/java/de/blinkt/openvpn/fragments/VPNProfileList.java41
-rw-r--r--main/src/main/res/layout/settings_usereditable.xml31
-rwxr-xr-xmain/src/main/res/values/strings.xml1
-rw-r--r--main/src/main/res/values/untranslatable.xml13
-rw-r--r--main/src/main/res/xml/app_restrictions.xml64
15 files changed, 530 insertions, 197 deletions
diff --git a/main/src/main/AndroidManifest.xml b/main/src/main/AndroidManifest.xml
index 94be45c2..22c4c6b9 100644
--- a/main/src/main/AndroidManifest.xml
+++ b/main/src/main/AndroidManifest.xml
@@ -22,6 +22,8 @@
android:name="android.hardware.touchscreen"
android:required="false" />
+
+
<application
android:name=".core.ICSOpenVPNApplication"
android:allowBackup="true"
@@ -32,6 +34,11 @@
android:supportsRtl="true"
android:theme="@style/blinkt"
tools:ignore="UnusedAttribute">
+
+
+ <meta-data android:name="android.content.APP_RESTRICTIONS"
+ android:resource="@xml/app_restrictions" />
+
<activity
android:name=".activities.VPNPreferences"
android:exported="false"
@@ -94,6 +101,7 @@
<meta-data
android:name="android.service.quicksettings.ACTIVE_TILE"
android:value="false" />
+
</service>
<activity android:name=".api.GrantPermissionsActivity">
@@ -236,6 +244,7 @@
android:name=".api.ConnectVPN"
android:targetActivity=".api.RemoteAction" />
+
</application>
</manifest> \ No newline at end of file
diff --git a/main/src/main/java/android/support/v4n/view/ViewPager.java b/main/src/main/java/android/support/v4n/view/ViewPager.java
index 53daa70d..943b1396 100644
--- a/main/src/main/java/android/support/v4n/view/ViewPager.java
+++ b/main/src/main/java/android/support/v4n/view/ViewPager.java
@@ -2719,7 +2719,7 @@ public class ViewPager extends ViewGroup {
@Override
public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
// Dispatch scroll events from this ViewPager.
- if (event.getEventType() == AccessibilityEventCompat.TYPE_VIEW_SCROLLED) {
+ if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_SCROLLED) {
return super.dispatchPopulateAccessibilityEvent(event);
}
@@ -2767,7 +2767,7 @@ public class ViewPager extends ViewGroup {
event.setClassName(ViewPager.class.getName());
final AccessibilityRecordCompat recordCompat = AccessibilityRecordCompat.obtain();
recordCompat.setScrollable(canScroll());
- if (event.getEventType() == AccessibilityEventCompat.TYPE_VIEW_SCROLLED
+ if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_SCROLLED
&& mAdapter != null) {
recordCompat.setItemCount(mAdapter.getCount());
recordCompat.setFromIndex(mCurItem);
diff --git a/main/src/main/java/de/blinkt/openvpn/VpnProfile.java b/main/src/main/java/de/blinkt/openvpn/VpnProfile.java
index 74c0b595..d89bb362 100644
--- a/main/src/main/java/de/blinkt/openvpn/VpnProfile.java
+++ b/main/src/main/java/de/blinkt/openvpn/VpnProfile.java
@@ -165,12 +165,16 @@ public class VpnProfile implements Serializable, Cloneable {
// timestamp when the profile was last used
public long mLastUsed;
+
+ public String importedProfileHash;
+
/* Options no longer used in new profiles */
public String mServerName = "openvpn.example.com";
public String mServerPort = "1194";
public boolean mUseUdp = true;
+
public VpnProfile(String name) {
mUuid = UUID.randomUUID();
mName = name;
@@ -226,6 +230,11 @@ public class VpnProfile implements Serializable, Cloneable {
}
+ // Only used for the special case of managed profiles
+ public void setUUID(UUID uuid){
+ mUuid = uuid;
+ }
+
public String getName() {
if (TextUtils.isEmpty(mName))
return "No profile name";
@@ -1100,7 +1109,7 @@ public class VpnProfile implements Serializable, Cloneable {
}
public String getUUIDString() {
- return mUuid.toString();
+ return mUuid.toString().toLowerCase(Locale.ENGLISH);
}
public PrivateKey getKeystoreKey() {
diff --git a/main/src/main/java/de/blinkt/openvpn/activities/BaseActivity.java b/main/src/main/java/de/blinkt/openvpn/activities/BaseActivity.java
index 8cdc1e90..08f2787b 100644
--- a/main/src/main/java/de/blinkt/openvpn/activities/BaseActivity.java
+++ b/main/src/main/java/de/blinkt/openvpn/activities/BaseActivity.java
@@ -5,11 +5,17 @@
package de.blinkt.openvpn.activities;
+import android.annotation.TargetApi;
import android.app.Activity;
import android.app.UiModeManager;
+import android.content.Context;
+import android.content.RestrictionsManager;
import android.content.res.Configuration;
+import android.os.Build;
import android.os.Bundle;
+import android.os.UserManager;
import android.view.Window;
+import de.blinkt.openvpn.api.AppRestrictions;
public class BaseActivity extends Activity {
private boolean isAndroidTV() {
@@ -24,4 +30,14 @@ public class BaseActivity extends Activity {
}
super.onCreate(savedInstanceState);
}
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ AppRestrictions.getInstance(this).checkRestrictions(this);
+
+ }
+ }
+
}
diff --git a/main/src/main/java/de/blinkt/openvpn/activities/MainActivity.java b/main/src/main/java/de/blinkt/openvpn/activities/MainActivity.java
index 5b06c833..f7c46d01 100644
--- a/main/src/main/java/de/blinkt/openvpn/activities/MainActivity.java
+++ b/main/src/main/java/de/blinkt/openvpn/activities/MainActivity.java
@@ -127,4 +127,5 @@ public class MainActivity extends BaseActivity {
}
+
}
diff --git a/main/src/main/java/de/blinkt/openvpn/api/AppRestrictions.java b/main/src/main/java/de/blinkt/openvpn/api/AppRestrictions.java
new file mode 100644
index 00000000..311b600d
--- /dev/null
+++ b/main/src/main/java/de/blinkt/openvpn/api/AppRestrictions.java
@@ -0,0 +1,182 @@
+/*
+ * Copyright (c) 2012-2018 Arne Schwabe
+ * Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt
+ */
+
+package de.blinkt.openvpn.api;
+
+import android.annotation.TargetApi;
+import android.content.*;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Parcelable;
+import de.blinkt.openvpn.VpnProfile;
+import de.blinkt.openvpn.core.ConfigParser;
+import de.blinkt.openvpn.core.ProfileManager;
+import de.blinkt.openvpn.core.VpnStatus;
+
+import java.io.IOException;
+import java.io.StringReader;
+import java.math.BigInteger;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.*;
+
+
+@TargetApi(Build.VERSION_CODES.LOLLIPOP)
+public class AppRestrictions {
+ public static final String PROFILE_CREATOR = "de.blinkt.openvpn.api.AppRestrictions";
+ final static int CONFIG_VERSION = 1;
+ static boolean alreadyChecked = false;
+ private static AppRestrictions mInstance;
+ private RestrictionsManager mRestrictionsMgr;
+
+ private AppRestrictions(Context c) {
+
+ }
+
+ public static AppRestrictions getInstance(Context c) {
+ if (mInstance == null)
+ mInstance = new AppRestrictions(c);
+ return mInstance;
+ }
+
+ private void addChangesListener(Context c) {
+ IntentFilter restrictionsFilter =
+ new IntentFilter(Intent.ACTION_APPLICATION_RESTRICTIONS_CHANGED);
+ BroadcastReceiver restrictionsReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ applyRestrictions(context);
+ }
+ };
+ c.registerReceiver(restrictionsReceiver, restrictionsFilter);
+ }
+
+ private String hashConfig(String config) {
+ MessageDigest digest;
+ try {
+ digest = MessageDigest.getInstance("SHA1");
+ byte utf8_bytes[] = config.getBytes();
+ digest.update(utf8_bytes, 0, utf8_bytes.length);
+ return new BigInteger(1, digest.digest()).toString(16);
+ } catch (NoSuchAlgorithmException e) {
+ e.printStackTrace();
+ return null;
+ }
+ }
+
+ private void applyRestrictions(Context c) {
+ mRestrictionsMgr = (RestrictionsManager) c.getSystemService(Context.RESTRICTIONS_SERVICE);
+ if (mRestrictionsMgr == null)
+ return;
+ Bundle restrictions = mRestrictionsMgr.getApplicationRestrictions();
+ if (restrictions == null)
+ return;
+
+ String configVersion = restrictions.getString("version", "(not set)");
+ try {
+ if (Integer.parseInt(configVersion) != CONFIG_VERSION)
+ throw new NumberFormatException("Wrong version");
+ } catch (NumberFormatException nex) {
+ VpnStatus.logError(String.format(Locale.US, "App restriction version %s does not match expected version %d", configVersion, CONFIG_VERSION));
+ return;
+ }
+ Parcelable[] profileList = restrictions.getParcelableArray(("vpn_configuration_list"));
+ if (profileList == null) {
+ VpnStatus.logError("App restriction does not contain a profile list (vpn_configuration_list)");
+ return;
+ }
+
+ Set<String> provisionedUuids = new HashSet<>();
+
+ ProfileManager pm = ProfileManager.getInstance(c);
+ for (Parcelable profile : profileList) {
+ if (!(profile instanceof Bundle)) {
+ VpnStatus.logError("App restriction profile has wrong type");
+ continue;
+ }
+ Bundle p = (Bundle) profile;
+
+ String uuid = p.getString("uuid");
+ String ovpn = p.getString("ovpn");
+ String name = p.getString("name");
+
+ if (uuid == null || ovpn == null || name == null) {
+ VpnStatus.logError("App restriction profile misses uuid, ovpn or name key");
+ continue;
+ }
+
+ String ovpnHash = hashConfig(ovpn);
+
+ provisionedUuids.add(uuid.toLowerCase(Locale.ENGLISH));
+ // Check if the profile already exists
+ VpnProfile vpnProfile = ProfileManager.get(c, uuid);
+
+
+ if (vpnProfile != null) {
+ // Profile exists, check if need to update it
+ if (ovpnHash.equals(vpnProfile.importedProfileHash))
+ // not modified skip to next profile
+ continue;
+
+ }
+ addProfile(c, ovpn, uuid, name, vpnProfile);
+ }
+
+ Vector<VpnProfile> profilesToRemove = new Vector<>();
+ // get List of all managed profiles
+ for (VpnProfile vp: pm.getProfiles())
+ {
+ if (PROFILE_CREATOR.equals(vp.mProfileCreator)) {
+ if (!provisionedUuids.contains(vp.getUUIDString()))
+ profilesToRemove.add(vp);
+ }
+ }
+ for (VpnProfile vp: profilesToRemove) {
+ VpnStatus.logInfo("Remove with uuid: %s and name: %s since it is no longer in the list of managed profiles");
+ pm.removeProfile(c, vp);
+ }
+
+ }
+
+ private void addProfile(Context c, String config, String uuid, String name, VpnProfile vpnProfile) {
+ ConfigParser cp = new ConfigParser();
+ try {
+ cp.parseConfig(new StringReader(config));
+ VpnProfile vp = cp.convertProfile();
+ vp.mProfileCreator = PROFILE_CREATOR;
+
+ // We don't want provisioned profiles to be editable
+ vp.mUserEditable = false;
+
+ vp.mName = name;
+ vp.setUUID(UUID.fromString(uuid));
+ vp.importedProfileHash = hashConfig(config);
+
+ ProfileManager pm = ProfileManager.getInstance(c);
+
+ if (vpnProfile != null) {
+ vp.mVersion = vpnProfile.mVersion + 1;
+ vp.mAlias = vpnProfile.mAlias;
+ }
+
+ // The add method will replace any older profiles with the same UUID
+ pm.addProfile(vp);
+ pm.saveProfile(c, vp);
+ pm.saveProfileList(c);
+
+ } catch (ConfigParser.ConfigParseError | IOException | IllegalArgumentException e) {
+ VpnStatus.logException("Error during import of managed profile", e);
+ }
+ }
+
+ public void checkRestrictions(Context c) {
+ if (alreadyChecked) {
+ return;
+ }
+ alreadyChecked = true;
+ addChangesListener(c);
+ applyRestrictions(c);
+ }
+}
diff --git a/main/src/main/java/de/blinkt/openvpn/core/OpenVPNService.java b/main/src/main/java/de/blinkt/openvpn/core/OpenVPNService.java
index b5afbb0e..264052b4 100644
--- a/main/src/main/java/de/blinkt/openvpn/core/OpenVPNService.java
+++ b/main/src/main/java/de/blinkt/openvpn/core/OpenVPNService.java
@@ -845,6 +845,8 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac
session = getString(R.string.session_ipv6string, session, mLocalIP, mLocalIPv6);
else if (mLocalIP != null)
session = getString(R.string.session_ipv4string, session, mLocalIP);
+ else
+ session = getString(R.string.session_ipv4string, session, mLocalIPv6);
builder.setSession(session);
diff --git a/main/src/main/java/de/blinkt/openvpn/fragments/KeyChainSettingsFragment.java b/main/src/main/java/de/blinkt/openvpn/fragments/KeyChainSettingsFragment.java
new file mode 100644
index 00000000..7ec72f78
--- /dev/null
+++ b/main/src/main/java/de/blinkt/openvpn/fragments/KeyChainSettingsFragment.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright (c) 2012-2018 Arne Schwabe
+ * Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt
+ */
+
+package de.blinkt.openvpn.fragments;
+
+import android.annotation.TargetApi;
+import android.app.AlertDialog;
+import android.content.ActivityNotFoundException;
+import android.os.Build;
+import android.os.Handler;
+import android.os.Message;
+import android.security.KeyChain;
+import android.security.KeyChainException;
+import android.view.View;
+import android.widget.TextView;
+import de.blinkt.openvpn.R;
+import de.blinkt.openvpn.VpnProfile;
+import de.blinkt.openvpn.core.ProfileManager;
+import de.blinkt.openvpn.core.X509Utils;
+
+import java.security.cert.X509Certificate;
+
+abstract class KeyChainSettingsFragment extends Settings_Fragment implements View.OnClickListener, Handler.Callback {
+ private static final int UPDATE_ALIAS = 20;
+
+
+ private TextView mAliasCertificate;
+ private TextView mAliasName;
+ private Handler mHandler;
+
+
+
+ private void setAlias() {
+ if(mProfile.mAlias == null) {
+ mAliasName.setText(R.string.client_no_certificate);
+ mAliasCertificate.setText("");
+ } else {
+ mAliasCertificate.setText("Loading certificate from Keystore...");
+ mAliasName.setText(mProfile.mAlias);
+ setKeystoreCertficate();
+ }
+ }
+
+ protected void setKeystoreCertficate()
+ {
+ new Thread() {
+ public void run() {
+ String certstr="";
+ try {
+ X509Certificate cert = KeyChain.getCertificateChain(getActivity().getApplicationContext(), mProfile.mAlias)[0];
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
+ {
+ if (isInHardwareKeystore())
+ certstr+=getString(R.string.hwkeychain);
+ }
+ }
+ certstr+= X509Utils.getCertificateValidityString(cert, getResources());
+ certstr+=X509Utils.getCertificateFriendlyName(cert);
+
+ } catch (Exception e) {
+ certstr="Could not get certificate from Keystore: " +e.getLocalizedMessage();
+ }
+
+ final String certStringCopy=certstr;
+ getActivity().runOnUiThread(new Runnable() {
+
+ @Override
+ public void run() {
+ mAliasCertificate.setText(certStringCopy);
+ }
+ });
+
+ }
+ }.start();
+ }
+
+ @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
+ private boolean isInHardwareKeystore() throws KeyChainException, InterruptedException {
+ String algorithm = KeyChain.getPrivateKey(getActivity().getApplicationContext(), mProfile.mAlias).getAlgorithm();
+ return KeyChain.isBoundKeyAlgorithm(algorithm);
+ }
+
+ protected void initKeychainViews(View v) {
+ v.findViewById(R.id.select_keystore_button).setOnClickListener(this);
+ mAliasCertificate = v.findViewById(R.id.alias_certificate);
+ mAliasName = v.findViewById(R.id.aliasname);
+ if (mHandler == null) {
+ mHandler = new Handler(this);
+ }
+ }
+
+ @Override
+ public void onClick(View v) {
+ if (v == v.findViewById(R.id.select_keystore_button)) {
+ showCertDialog();
+ }
+ }
+
+ @Override
+ protected void savePreferences() {
+
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ loadPreferences();
+ }
+
+ @SuppressWarnings("WrongConstant")
+ public void showCertDialog () {
+ try {
+ KeyChain.choosePrivateKeyAlias(getActivity(),
+ alias -> {
+ // Credential alias selected. Remember the alias selection for future use.
+ mProfile.mAlias=alias;
+ mHandler.sendEmptyMessage(UPDATE_ALIAS);
+ },
+ new String[] {"RSA"}, // List of acceptable key types. null for any
+ null, // issuer, null for any
+ mProfile.mServerName, // host name of server requesting the cert, null if unavailable
+ -1, // port of server requesting the cert, -1 if unavailable
+ mProfile.mAlias); // alias to preselect, null if unavailable
+ } catch (ActivityNotFoundException anf) {
+ AlertDialog.Builder ab = new AlertDialog.Builder(getActivity());
+ ab.setTitle(R.string.broken_image_cert_title);
+ ab.setMessage(R.string.broken_image_cert);
+ ab.setPositiveButton(android.R.string.ok, null);
+ ab.show();
+ }
+ }
+
+ protected void loadPreferences()
+ {
+ setAlias();
+ }
+
+ @Override
+ public boolean handleMessage(Message msg) {
+ setAlias();
+ return true;
+ }
+}
diff --git a/main/src/main/java/de/blinkt/openvpn/fragments/Settings_Basic.java b/main/src/main/java/de/blinkt/openvpn/fragments/Settings_Basic.java
index b6c6aad2..faefcf86 100644
--- a/main/src/main/java/de/blinkt/openvpn/fragments/Settings_Basic.java
+++ b/main/src/main/java/de/blinkt/openvpn/fragments/Settings_Basic.java
@@ -5,55 +5,32 @@
package de.blinkt.openvpn.fragments;
-import android.annotation.TargetApi;
import android.app.Activity;
-import android.app.AlertDialog;
-import android.app.AlertDialog.Builder;
-import android.content.ActivityNotFoundException;
import android.content.Intent;
-import android.os.Build;
import android.os.Bundle;
-import android.os.Handler;
-import android.os.Handler.Callback;
-import android.os.Message;
-import android.security.KeyChain;
-import android.security.KeyChainAliasCallback;
-import android.security.KeyChainException;
import android.util.SparseArray;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
-import android.widget.AdapterView;
+import android.widget.*;
import android.widget.AdapterView.OnItemSelectedListener;
-import android.widget.CheckBox;
-import android.widget.EditText;
-import android.widget.Spinner;
-import android.widget.TextView;
-
-import java.security.cert.X509Certificate;
-
import de.blinkt.openvpn.R;
import de.blinkt.openvpn.R.id;
import de.blinkt.openvpn.VpnProfile;
import de.blinkt.openvpn.core.ProfileManager;
-import de.blinkt.openvpn.core.X509Utils;
import de.blinkt.openvpn.views.FileSelectLayout;
-public class Settings_Basic extends Settings_Fragment implements View.OnClickListener, OnItemSelectedListener, Callback, FileSelectLayout.FileSelectCallback {
+public class Settings_Basic extends KeyChainSettingsFragment implements OnItemSelectedListener, FileSelectLayout.FileSelectCallback {
private static final int CHOOSE_FILE_OFFSET = 1000;
- private static final int UPDATE_ALIAS = 20;
private FileSelectLayout mClientCert;
private FileSelectLayout mCaCert;
private FileSelectLayout mClientKey;
- private TextView mAliasName;
- private TextView mAliasCertificate;
private CheckBox mUseLzo;
private Spinner mType;
private FileSelectLayout mpkcs12;
private FileSelectLayout mCrlFile;
private TextView mPKCS12Password;
- private Handler mHandler;
private EditText mUserName;
private EditText mPassword;
private View mView;
@@ -76,45 +53,6 @@ public class Settings_Basic extends Settings_Fragment implements View.OnClickLis
}
- private void setKeystoreCertficate()
- {
- new Thread() {
- public void run() {
- String certstr="";
- try {
- X509Certificate cert = KeyChain.getCertificateChain(getActivity().getApplicationContext(), mProfile.mAlias)[0];
-
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
- {
- if (isInHardwareKeystore())
- certstr+=getString(R.string.hwkeychain);
- }
- }
- certstr+=X509Utils.getCertificateValidityString(cert, getResources());
- certstr+=X509Utils.getCertificateFriendlyName(cert);
-
- } catch (Exception e) {
- certstr="Could not get certificate from Keystore: " +e.getLocalizedMessage();
- }
-
- final String certStringCopy=certstr;
- getActivity().runOnUiThread(new Runnable() {
-
- @Override
- public void run() {
- mAliasCertificate.setText(certStringCopy);
- }
- });
-
- }
- }.start();
- }
-
- @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
- private boolean isInHardwareKeystore() throws KeyChainException, InterruptedException {
- String algorithm = KeyChain.getPrivateKey(getActivity().getApplicationContext(), mProfile.mAlias).getAlgorithm();
- return KeyChain.isBoundKeyAlgorithm(algorithm);
- }
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
@@ -122,22 +60,20 @@ public class Settings_Basic extends Settings_Fragment implements View.OnClickLis
mView = inflater.inflate(R.layout.basic_settings,container,false);
- mProfileName = (EditText) mView.findViewById(R.id.profilename);
- mClientCert = (FileSelectLayout) mView.findViewById(R.id.certselect);
- mClientKey = (FileSelectLayout) mView.findViewById(R.id.keyselect);
- mCaCert = (FileSelectLayout) mView.findViewById(R.id.caselect);
- mpkcs12 = (FileSelectLayout) mView.findViewById(R.id.pkcs12select);
- mCrlFile = (FileSelectLayout) mView.findViewById(id.crlfile);
- mUseLzo = (CheckBox) mView.findViewById(R.id.lzo);
- mType = (Spinner) mView.findViewById(R.id.type);
- mPKCS12Password = (TextView) mView.findViewById(R.id.pkcs12password);
- mAliasName = (TextView) mView.findViewById(R.id.aliasname);
- mAliasCertificate = (TextView) mView.findViewById(id.alias_certificate);
-
- mUserName = (EditText) mView.findViewById(R.id.auth_username);
- mPassword = (EditText) mView.findViewById(R.id.auth_password);
- mKeyPassword = (EditText) mView.findViewById(R.id.key_password);
- mAuthRetry = (Spinner) mView.findViewById(id.auth_retry);
+ mProfileName = mView.findViewById(id.profilename);
+ mClientCert = mView.findViewById(id.certselect);
+ mClientKey = mView.findViewById(id.keyselect);
+ mCaCert = mView.findViewById(id.caselect);
+ mpkcs12 = mView.findViewById(id.pkcs12select);
+ mCrlFile = mView.findViewById(id.crlfile);
+ mUseLzo = mView.findViewById(id.lzo);
+ mType = mView.findViewById(id.type);
+ mPKCS12Password = mView.findViewById(id.pkcs12password);
+
+ mUserName = mView.findViewById(id.auth_username);
+ mPassword = mView.findViewById(id.auth_password);
+ mKeyPassword = mView.findViewById(id.key_password);
+ mAuthRetry = mView.findViewById(id.auth_retry);
addFileSelectLayout(mCaCert, Utils.FileType.CA_CERTIFICATE);
addFileSelectLayout(mClientCert, Utils.FileType.CLIENT_CERTIFICATE);
@@ -150,25 +86,14 @@ public class Settings_Basic extends Settings_Fragment implements View.OnClickLis
mType.setOnItemSelectedListener(this);
mAuthRetry.setOnItemSelectedListener(this);
- mView.findViewById(R.id.select_keystore_button).setOnClickListener(this);
- if (mHandler == null) {
- mHandler = new Handler(this);
- }
+ initKeychainViews(mView);
return mView;
}
- @Override
- public void onStart() {
- super.onStart();
- String profileUuid =getArguments().getString(getActivity().getPackageName() + ".profileUUID");
- mProfile=ProfileManager.get(getActivity(),profileUuid);
- loadPreferences();
-
- }
@Override
public void onActivityResult(int request, int result, Intent data) {
@@ -245,7 +170,8 @@ public class Settings_Basic extends Settings_Fragment implements View.OnClickLis
}
- private void loadPreferences() {
+ protected void loadPreferences() {
+ super.loadPreferences();
mProfileName.setText(mProfile.mName);
mClientCert.setData(mProfile.mClientCertFilename, getActivity());
mClientKey.setData(mProfile.mClientKeyFilename, getActivity());
@@ -260,13 +186,10 @@ public class Settings_Basic extends Settings_Fragment implements View.OnClickLis
mPassword.setText(mProfile.mPassword);
mKeyPassword.setText(mProfile.mKeyPassword);
mAuthRetry.setSelection(mProfile.mAuthRetry);
-
- setAlias();
-
}
protected void savePreferences() {
-
+ super.savePreferences();
mProfile.mName = mProfileName.getText().toString();
mProfile.mCaFilename = mCaCert.getData();
mProfile.mClientCertFilename = mClientCert.getData();
@@ -285,48 +208,6 @@ public class Settings_Basic extends Settings_Fragment implements View.OnClickLis
}
-
- private void setAlias() {
- if(mProfile.mAlias == null) {
- mAliasName.setText(R.string.client_no_certificate);
- mAliasCertificate.setText("");
- } else {
- mAliasCertificate.setText("Loading certificate from Keystore...");
- mAliasName.setText(mProfile.mAlias);
- setKeystoreCertficate();
- }
- }
-
- @SuppressWarnings("WrongConstant")
- public void showCertDialog () {
- try {
- KeyChain.choosePrivateKeyAlias(getActivity(),
- alias -> {
- // Credential alias selected. Remember the alias selection for future use.
- mProfile.mAlias=alias;
- mHandler.sendEmptyMessage(UPDATE_ALIAS);
- },
- new String[] {"RSA"}, // List of acceptable key types. null for any
- null, // issuer, null for any
- mProfile.mServerName, // host name of server requesting the cert, null if unavailable
- -1, // port of server requesting the cert, -1 if unavailable
- mProfile.mAlias); // alias to preselect, null if unavailable
- } catch (ActivityNotFoundException anf) {
- Builder ab = new AlertDialog.Builder(getActivity());
- ab.setTitle(R.string.broken_image_cert_title);
- ab.setMessage(R.string.broken_image_cert);
- ab.setPositiveButton(android.R.string.ok, null);
- ab.show();
- }
- }
-
- @Override
- public void onClick(View v) {
- if (v == mView.findViewById(R.id.select_keystore_button)) {
- showCertDialog();
- }
- }
-
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
@@ -341,11 +222,4 @@ public class Settings_Basic extends Settings_Fragment implements View.OnClickLis
}
- @Override
- public boolean handleMessage(Message msg) {
- setAlias();
- return true;
- }
-
-
}
diff --git a/main/src/main/java/de/blinkt/openvpn/fragments/Settings_UserEditable.java b/main/src/main/java/de/blinkt/openvpn/fragments/Settings_UserEditable.java
index f9477a85..98ebb55b 100644
--- a/main/src/main/java/de/blinkt/openvpn/fragments/Settings_UserEditable.java
+++ b/main/src/main/java/de/blinkt/openvpn/fragments/Settings_UserEditable.java
@@ -14,36 +14,50 @@ import android.view.ViewGroup;
import android.widget.TextView;
import de.blinkt.openvpn.R;
+import de.blinkt.openvpn.VpnProfile;
+import de.blinkt.openvpn.api.AppRestrictions;
-public class Settings_UserEditable extends OpenVpnPreferencesFragment {
- @Override
- protected void loadSettings() {
-
- }
-
- @Override
- protected void saveSettings() {
+public class Settings_UserEditable extends KeyChainSettingsFragment implements View.OnClickListener {
- }
+ private View mView;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
- View v = inflater.inflate(R.layout.settings_usereditable, container, false);
- TextView messageView = (TextView) v.findViewById(R.id.messageUserEdit);
+ mView = inflater.inflate(R.layout.settings_usereditable, container, false);
+ TextView messageView = (TextView) mView.findViewById(R.id.messageUserEdit);
messageView.setText(getString(R.string.message_no_user_edit, getPackageString(mProfile.mProfileCreator)));
- return v;
+ initKeychainViews(this.mView);
+ return mView;
}
private String getPackageString(String packageName) {
+
+ if (AppRestrictions.PROFILE_CREATOR.equals(packageName))
+ return "Android Enterprise Management";
+
final PackageManager pm = getActivity().getPackageManager();
ApplicationInfo ai;
try {
- ai = pm.getApplicationInfo( packageName, 0);
+ ai = pm.getApplicationInfo(packageName, 0);
} catch (final PackageManager.NameNotFoundException e) {
ai = null;
}
final String applicationName = (String) (ai != null ? pm.getApplicationLabel(ai) : "(unknown)");
return String.format("%s (%s)", applicationName, packageName);
}
+
+ @Override
+ protected void savePreferences() {
+
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ mView.findViewById(R.id.keystore).setVisibility(View.GONE);
+ if (mProfile.mAuthenticationType == VpnProfile.TYPE_USERPASS_KEYSTORE ||
+ mProfile.mAuthenticationType == VpnProfile.TYPE_KEYSTORE)
+ mView.findViewById(R.id.keystore).setVisibility(View.VISIBLE);
+ }
}
diff --git a/main/src/main/java/de/blinkt/openvpn/fragments/VPNProfileList.java b/main/src/main/java/de/blinkt/openvpn/fragments/VPNProfileList.java
index a0aac73d..7ad13aaf 100644
--- a/main/src/main/java/de/blinkt/openvpn/fragments/VPNProfileList.java
+++ b/main/src/main/java/de/blinkt/openvpn/fragments/VPNProfileList.java
@@ -526,32 +526,25 @@ public class VPNProfileList extends ListFragment implements OnClickListener, Vpn
dialog.setView(entry);
dialog.setNeutralButton(R.string.menu_import_short,
- new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- startImportConfigFilePicker();
- }
- });
+ (dialog1, which) -> startImportConfigFilePicker());
dialog.setPositiveButton(android.R.string.ok,
- new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- String name = entry.getText().toString();
- if (getPM().getProfileByName(name) == null) {
- VpnProfile profile;
- if (mCopyProfile != null)
- profile = mCopyProfile.copy(name);
- else
- profile = new VpnProfile(name);
-
- addProfile(profile);
- editVPN(profile);
- } else {
- Toast.makeText(getActivity(), R.string.duplicate_profile_name, Toast.LENGTH_LONG).show();
- }
+ (dialog12, which) -> {
+ String name = entry.getText().toString();
+ if (getPM().getProfileByName(name) == null) {
+ VpnProfile profile;
+ if (mCopyProfile != null) {
+ profile = mCopyProfile.copy(name);
+ // Remove restrictions on copy profile
+ profile.mProfileCreator = null;
+ profile.mUserEditable = true;
+ } else
+ profile = new VpnProfile(name);
+
+ addProfile(profile);
+ editVPN(profile);
+ } else {
+ Toast.makeText(getActivity(), R.string.duplicate_profile_name, Toast.LENGTH_LONG).show();
}
-
-
});
dialog.setNegativeButton(android.R.string.cancel, null);
dialog.create().show();
diff --git a/main/src/main/res/layout/settings_usereditable.xml b/main/src/main/res/layout/settings_usereditable.xml
index cc7f1ff5..6e954116 100644
--- a/main/src/main/res/layout/settings_usereditable.xml
+++ b/main/src/main/res/layout/settings_usereditable.xml
@@ -5,17 +5,26 @@
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
- android:orientation="vertical"
- android:padding="@dimen/stdpadding"
- android:layout_width="match_parent"
- android:gravity="center"
- android:layout_height="match_parent">
+ xmlns:tools="http://schemas.android.com/tools"
+ android:orientation="vertical"
+ android:padding="@dimen/stdpadding"
+ android:layout_width="match_parent"
+ android:gravity="center"
+ android:layout_height="match_parent">
<TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- tools:text="@string/message_no_user_edit"
- android:id="@+id/messageUserEdit"
- android:layout_gravity="center_horizontal" />
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ tools:text="@string/message_no_user_edit"
+ android:id="@+id/messageUserEdit"
+ android:layout_gravity="center_horizontal"/>
+ <LinearLayout
+ android:id="@+id/keystore"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:visibility="gone"
+ tools:visibility="gone">
+ <include layout="@layout/keystore_selector"/>
+ </LinearLayout>
</LinearLayout> \ No newline at end of file
diff --git a/main/src/main/res/values/strings.xml b/main/src/main/res/values/strings.xml
index 5590acf3..10ccaa86 100755
--- a/main/src/main/res/values/strings.xml
+++ b/main/src/main/res/values/strings.xml
@@ -482,4 +482,5 @@
</string>
<string name="openurl_requested">Open URL to continue VPN authentication</string>
<string name="state_auth_pending">Authentication pending</string>
+
</resources>
diff --git a/main/src/main/res/values/untranslatable.xml b/main/src/main/res/values/untranslatable.xml
index f8f0cc96..94012670 100644
--- a/main/src/main/res/values/untranslatable.xml
+++ b/main/src/main/res/values/untranslatable.xml
@@ -50,4 +50,17 @@
<string name="state_user_vpn_permission_cancelled" translatable="false">VPN API permission dialog cancelled</string>
<string name="default_cipherlist_test" translatable="false">aes-256-gcm bf-cbc sha1</string>
+ <!-- APP restriction strings -->
+ <string name="apprest_uuid_desc">Unique UUID that identifies the profile (example:
+ 0E910C15–9A85-4DD9-AE0D-E6862392E638). Generate using uuidgen or similar tools</string>
+ <string name="apprest_uuid">UUID</string>
+ <string name="apprest_ovpn_desc">Content of the OpenVPN configuration file</string>
+ <string name="apprest_ovpn">Config</string>
+ <string name="apprest_name_desc">Name of the VPN profile</string>
+ <string name="apprest_name">Name</string>
+ <string name="apprest_vpnlist">List of VPN configurations</string>
+ <string name="apprest_vpnconf">VPN configuration</string>
+ <string name="apprest_ver">Version of the managed configuration schema</string>
+
+
</resources>
diff --git a/main/src/main/res/xml/app_restrictions.xml b/main/src/main/res/xml/app_restrictions.xml
new file mode 100644
index 00000000..6387712c
--- /dev/null
+++ b/main/src/main/res/xml/app_restrictions.xml
@@ -0,0 +1,64 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (c) 2012-2018 Arne Schwabe
+ ~ Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt
+ -->
+
+<restrictions xmlns:tools="http://schemas.android.com/tools" xmlns:android="http://schemas.android.com/apk/res/android"
+ tools:targetApi="lollipop">
+
+ <restriction
+
+ android:key="version"
+ android:title="@string/apprest_ver"
+ android:restrictionType="hidden"
+ android:defaultValue="1"
+ />
+
+ <restriction
+ android:key="vpn_configuration_list"
+ android:title="@string/apprest_vpnlist"
+ android:restrictionType="bundle_array">
+
+ <restriction
+ android:title="@string/apprest_vpnconf"
+ android:key="vpn_configuration"
+ android:restrictionType="bundle">
+
+ <restriction
+ android:key="uuid"
+ android:restrictionType="string"
+ android:description="@string/apprest_uuid_desc"
+ android:title="@string/apprest_uuid"
+ />
+
+ <restriction
+ android:key="name"
+ android:restrictionType="string"
+ android:title="@string/apprest_name"
+ android:description="@string/apprest_name_desc"
+
+ />
+
+ <restriction
+ android:key="ovpn"
+ android:title="@string/apprest_ovpn"
+ android:description="@string/apprest_ovpn_desc"
+ android:restrictionType="string"/>
+
+ <!--
+ <restriction
+ android:key="ovpn_list"
+ android:title="@string/apprest_ovpn_list"
+ android:describition="@string/apprest_ovpn_list_esc"
+ android:restrictionType="bundle_array">
+ <restriction
+ android:key="ovpn_configline"
+ android:title="@string/apprest_ovpn"
+ android:description="@string/apprest_ovpn_desc"
+ android:restrictionType="string"/>
+ </restriction>
+ -->
+ </restriction>
+ </restriction>
+</restrictions> \ No newline at end of file