Merge branch 'android-managed-configurations'
This adds support for managed configurations via enterprise mobility management (EMM) systems. Also changes details regarding the SQL data source.
This commit is contained in:
@@ -38,6 +38,7 @@ fuzzing-corpora/
|
||||
*.tar.bz2
|
||||
*.tar.gz
|
||||
.DS_Store
|
||||
._.DS_Store
|
||||
coverage/
|
||||
*.gcno
|
||||
*.gcda
|
||||
|
||||
@@ -8,8 +8,9 @@ android {
|
||||
compileSdk 34
|
||||
minSdkVersion 21
|
||||
targetSdkVersion 33
|
||||
versionCode 80
|
||||
versionName "2.4.2"
|
||||
|
||||
versionCode 81
|
||||
versionName "2.5.0"
|
||||
|
||||
externalNativeBuild {
|
||||
ndkBuild {
|
||||
@@ -45,9 +46,10 @@ android {
|
||||
|
||||
dependencies {
|
||||
implementation 'androidx.appcompat:appcompat:1.6.1'
|
||||
implementation 'androidx.lifecycle:lifecycle-process:2.7.0'
|
||||
implementation 'androidx.preference:preference:1.2.1'
|
||||
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
|
||||
implementation 'com.google.android.material:material:1.10.0'
|
||||
testImplementation 'junit:junit:4.13.2'
|
||||
testImplementation 'org.assertj:assertj-core:3.24.2'
|
||||
testImplementation 'org.mockito:mockito-core:5.8.0'
|
||||
}
|
||||
|
||||
@@ -161,9 +161,17 @@
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<!--
|
||||
Managed configuration, called app restrictions for historical reasons
|
||||
https://developer.android.com/work/managed-configurations
|
||||
-->
|
||||
<meta-data
|
||||
android:name="android.content.APP_RESTRICTIONS"
|
||||
android:resource="@xml/managed_configuration" />
|
||||
|
||||
<service
|
||||
android:name=".logic.VpnStateService"
|
||||
android:exported="false" >
|
||||
android:exported="false">
|
||||
</service>
|
||||
<service
|
||||
android:name=".logic.CharonVpnService"
|
||||
|
||||
+279
@@ -0,0 +1,279 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Relution GmbH
|
||||
* Copyright (C) 2012-2024 Tobias Brunner
|
||||
* Copyright (C) 2012 Giuliano Grassi
|
||||
* Copyright (C) 2012 Ralf Sager
|
||||
*
|
||||
* Copyright (C) secunet Security Networks AG
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*/
|
||||
|
||||
package org.strongswan.android.data;
|
||||
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.database.sqlite.SQLiteOpenHelper;
|
||||
import android.database.sqlite.SQLiteQueryBuilder;
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
public class DatabaseHelper extends SQLiteOpenHelper
|
||||
{
|
||||
private static final String TAG = DatabaseHelper.class.getSimpleName();
|
||||
|
||||
private static final String DATABASE_NAME = "strongswan.db";
|
||||
|
||||
private static final String TABLE_NAME_VPN_PROFILE = "vpnprofile";
|
||||
private static final String TABLE_NAME_TRUSTED_CERTIFICATE = "trustedcertificate";
|
||||
private static final String TABLE_NAME_USER_CERTIFICATE = "usercertificate";
|
||||
|
||||
static final DbTable TABLE_VPN_PROFILE = new DbTable(TABLE_NAME_VPN_PROFILE, 1, new DbColumn[]{
|
||||
new DbColumn(VpnProfileDataSource.KEY_ID, "INTEGER PRIMARY KEY AUTOINCREMENT", 1),
|
||||
new DbColumn(VpnProfileDataSource.KEY_UUID, "TEXT UNIQUE", 9),
|
||||
new DbColumn(VpnProfileDataSource.KEY_NAME, "TEXT NOT NULL", 1),
|
||||
new DbColumn(VpnProfileDataSource.KEY_GATEWAY, "TEXT NOT NULL", 1),
|
||||
new DbColumn(VpnProfileDataSource.KEY_VPN_TYPE, "TEXT NOT NULL DEFAULT ''", 3),
|
||||
new DbColumn(VpnProfileDataSource.KEY_USERNAME, "TEXT", 1),
|
||||
new DbColumn(VpnProfileDataSource.KEY_PASSWORD, "TEXT", 1),
|
||||
new DbColumn(VpnProfileDataSource.KEY_CERTIFICATE, "TEXT", 1),
|
||||
new DbColumn(VpnProfileDataSource.KEY_USER_CERTIFICATE, "TEXT", 2),
|
||||
new DbColumn(VpnProfileDataSource.KEY_MTU, "INTEGER", 5),
|
||||
new DbColumn(VpnProfileDataSource.KEY_PORT, "INTEGER", 6),
|
||||
new DbColumn(VpnProfileDataSource.KEY_SPLIT_TUNNELING, "INTEGER", 7),
|
||||
new DbColumn(VpnProfileDataSource.KEY_LOCAL_ID, "TEXT", 8),
|
||||
new DbColumn(VpnProfileDataSource.KEY_REMOTE_ID, "TEXT", 8),
|
||||
new DbColumn(VpnProfileDataSource.KEY_EXCLUDED_SUBNETS, "TEXT", 10),
|
||||
new DbColumn(VpnProfileDataSource.KEY_INCLUDED_SUBNETS, "TEXT", 11),
|
||||
new DbColumn(VpnProfileDataSource.KEY_SELECTED_APPS, "INTEGER", 12),
|
||||
new DbColumn(VpnProfileDataSource.KEY_SELECTED_APPS_LIST, "TEXT", 12),
|
||||
new DbColumn(VpnProfileDataSource.KEY_NAT_KEEPALIVE, "INTEGER", 13),
|
||||
new DbColumn(VpnProfileDataSource.KEY_FLAGS, "INTEGER", 14),
|
||||
new DbColumn(VpnProfileDataSource.KEY_IKE_PROPOSAL, "TEXT", 15),
|
||||
new DbColumn(VpnProfileDataSource.KEY_ESP_PROPOSAL, "TEXT", 15),
|
||||
new DbColumn(VpnProfileDataSource.KEY_DNS_SERVERS, "TEXT", 17),
|
||||
});
|
||||
|
||||
public static final DbTable TABLE_TRUSTED_CERTIFICATE = new DbTable(TABLE_NAME_TRUSTED_CERTIFICATE, 18, new DbColumn[]{
|
||||
new DbColumn(ManagedCertificate.KEY_ID, "INTEGER PRIMARY KEY AUTOINCREMENT", 18),
|
||||
new DbColumn(ManagedCertificate.KEY_VPN_PROFILE_UUID, "TEXT UNIQUE", 18),
|
||||
new DbColumn(ManagedCertificate.KEY_ALIAS, "TEXT NOT NULL", 18),
|
||||
new DbColumn(ManagedCertificate.KEY_DATA, "TEXT NOT NULL", 18),
|
||||
});
|
||||
|
||||
public static final DbTable TABLE_USER_CERTIFICATE = new DbTable(TABLE_NAME_USER_CERTIFICATE, 18, new DbColumn[]{
|
||||
new DbColumn(ManagedCertificate.KEY_ID, "INTEGER PRIMARY KEY AUTOINCREMENT", 18),
|
||||
new DbColumn(ManagedCertificate.KEY_VPN_PROFILE_UUID, "TEXT UNIQUE", 18),
|
||||
new DbColumn(ManagedCertificate.KEY_ALIAS, "TEXT NOT NULL", 18),
|
||||
new DbColumn(ManagedCertificate.KEY_DATA, "TEXT NOT NULL", 18),
|
||||
new DbColumn(ManagedUserCertificate.KEY_PASSWORD, "TEXT", 18),
|
||||
});
|
||||
|
||||
private static final int DATABASE_VERSION = 18;
|
||||
|
||||
private static final Set<DbTable> TABLES;
|
||||
|
||||
static
|
||||
{
|
||||
TABLES = new HashSet<>();
|
||||
TABLES.add(TABLE_VPN_PROFILE);
|
||||
TABLES.add(TABLE_TRUSTED_CERTIFICATE);
|
||||
TABLES.add(TABLE_USER_CERTIFICATE);
|
||||
}
|
||||
|
||||
public DatabaseHelper(Context context)
|
||||
{
|
||||
super(context, DATABASE_NAME, null, DATABASE_VERSION);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(SQLiteDatabase database)
|
||||
{
|
||||
addNewTables(database, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)
|
||||
{
|
||||
Log.w(TAG, "Upgrading database from version " + oldVersion + " to " + newVersion);
|
||||
addNewTables(db, oldVersion);
|
||||
addNewColumns(db, oldVersion);
|
||||
|
||||
if (oldVersion < 9)
|
||||
{
|
||||
updateColumns(db, TABLE_VPN_PROFILE);
|
||||
}
|
||||
if (oldVersion < 16)
|
||||
{ /* add a UUID to all entries that haven't one yet */
|
||||
db.beginTransaction();
|
||||
try
|
||||
{
|
||||
Cursor cursor = db.query(TABLE_VPN_PROFILE.Name, TABLE_VPN_PROFILE.columnNames(), VpnProfileDataSource.KEY_UUID + " is NULL", null, null, null, null);
|
||||
for (cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext())
|
||||
{
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(VpnProfileDataSource.KEY_UUID, UUID.randomUUID().toString());
|
||||
db.update(TABLE_VPN_PROFILE.Name, values, VpnProfileDataSource.KEY_ID + " = " + cursor.getLong(cursor.getColumnIndexOrThrow(VpnProfileDataSource.KEY_ID)), null);
|
||||
}
|
||||
cursor.close();
|
||||
db.setTransactionSuccessful();
|
||||
}
|
||||
finally
|
||||
{
|
||||
db.endTransaction();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void updateColumns(SQLiteDatabase db, DbTable table)
|
||||
{
|
||||
db.beginTransaction();
|
||||
try
|
||||
{
|
||||
db.execSQL("ALTER TABLE " + table.Name + " RENAME TO tmp_" + table.Name + ";");
|
||||
db.execSQL(getTableCreate(table));
|
||||
StringBuilder insert = new StringBuilder("INSERT INTO " + table.Name + " SELECT ");
|
||||
SQLiteQueryBuilder.appendColumns(insert, table.columnNames());
|
||||
db.execSQL(insert.append(" FROM tmp_" + table.Name + ";").toString());
|
||||
db.execSQL("DROP TABLE tmp_" + table.Name + ";");
|
||||
db.setTransactionSuccessful();
|
||||
}
|
||||
finally
|
||||
{
|
||||
db.endTransaction();
|
||||
}
|
||||
}
|
||||
|
||||
private static String getTableCreate(DbTable table)
|
||||
{
|
||||
boolean first = true;
|
||||
StringBuilder create = new StringBuilder("CREATE TABLE IF NOT EXISTS ");
|
||||
create.append(table.Name);
|
||||
create.append(" (");
|
||||
|
||||
for (final DbColumn column : table.getColumns())
|
||||
{
|
||||
if (!first)
|
||||
{
|
||||
create.append(",");
|
||||
}
|
||||
first = false;
|
||||
create.append(column.Name);
|
||||
create.append(" ");
|
||||
create.append(column.Type);
|
||||
}
|
||||
create.append(");");
|
||||
return create.toString();
|
||||
}
|
||||
|
||||
private void addNewTables(final SQLiteDatabase database, final int oldVersion)
|
||||
{
|
||||
for (final String sql : getTableCreates(oldVersion))
|
||||
{
|
||||
database.execSQL(sql);
|
||||
}
|
||||
}
|
||||
|
||||
private List<String> getTableCreates(final int oldVersion)
|
||||
{
|
||||
List<String> statements = new ArrayList<>(TABLES.size());
|
||||
for (final DbTable table : TABLES)
|
||||
{
|
||||
if (table.Since > oldVersion)
|
||||
{
|
||||
statements.add(getTableCreate(table));
|
||||
}
|
||||
}
|
||||
return statements;
|
||||
}
|
||||
|
||||
private void addNewColumns(final SQLiteDatabase database, final int oldVersion)
|
||||
{
|
||||
for (final String sql : getAlterTables(oldVersion))
|
||||
{
|
||||
database.execSQL(sql);
|
||||
}
|
||||
}
|
||||
|
||||
private List<String> getAlterTables(final int oldVersion)
|
||||
{
|
||||
List<String> statements = new ArrayList<>(TABLES.size());
|
||||
for (final DbTable table : TABLES)
|
||||
{
|
||||
statements.addAll(getAlterTables(table, oldVersion));
|
||||
}
|
||||
return statements;
|
||||
}
|
||||
|
||||
private static List<String> getAlterTables(DbTable table, final int oldVersion)
|
||||
{
|
||||
final List<String> sql = new ArrayList<>();
|
||||
|
||||
for (final DbColumn column : table.getColumns())
|
||||
{
|
||||
if (column.Since > table.Since && column.Since > oldVersion)
|
||||
{
|
||||
sql.add("ALTER TABLE " + table.Name + " ADD " + column.Name + " " + column.Type + ";");
|
||||
}
|
||||
}
|
||||
return sql;
|
||||
}
|
||||
|
||||
public static class DbTable
|
||||
{
|
||||
public final String Name;
|
||||
public final int Since;
|
||||
public final DbColumn[] Columns;
|
||||
|
||||
private DbTable(final String name, final int since, final DbColumn[] columns)
|
||||
{
|
||||
Name = name;
|
||||
Since = since;
|
||||
Columns = columns;
|
||||
}
|
||||
|
||||
private DbColumn[] getColumns()
|
||||
{
|
||||
return Columns;
|
||||
}
|
||||
|
||||
public String[] columnNames()
|
||||
{
|
||||
final List<String> columnNames = new ArrayList<>(Columns.length);
|
||||
for (DbColumn column : Columns)
|
||||
{
|
||||
columnNames.add(column.Name);
|
||||
}
|
||||
return columnNames.toArray(new String[0]);
|
||||
}
|
||||
}
|
||||
|
||||
public static class DbColumn
|
||||
{
|
||||
public final String Name;
|
||||
public final String Type;
|
||||
public final int Since;
|
||||
|
||||
private DbColumn(String name, String type, int since)
|
||||
{
|
||||
Name = name;
|
||||
Type = type;
|
||||
Since = since;
|
||||
}
|
||||
}
|
||||
}
|
||||
+97
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Relution GmbH
|
||||
*
|
||||
* Copyright (C) secunet Security Networks AG
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*/
|
||||
|
||||
package org.strongswan.android.data;
|
||||
|
||||
import android.content.ContentValues;
|
||||
import android.database.Cursor;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
public abstract class ManagedCertificate
|
||||
{
|
||||
public static final String KEY_ID = "_id";
|
||||
public static final String KEY_VPN_PROFILE_UUID = "vpn_profile_uuid";
|
||||
public static final String KEY_ALIAS = "alias";
|
||||
public static final String KEY_DATA = "data";
|
||||
|
||||
long id = -1;
|
||||
|
||||
@NonNull
|
||||
final String vpnProfileUuid;
|
||||
|
||||
@NonNull
|
||||
String alias;
|
||||
|
||||
@NonNull
|
||||
final String data;
|
||||
|
||||
ManagedCertificate(
|
||||
@NonNull final String vpnProfileUuid,
|
||||
@NonNull final String alias,
|
||||
@NonNull final String data)
|
||||
{
|
||||
this.vpnProfileUuid = vpnProfileUuid;
|
||||
this.alias = alias;
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
ManagedCertificate(@NonNull final Cursor cursor)
|
||||
{
|
||||
id = cursor.getLong(cursor.getColumnIndexOrThrow(KEY_ID));
|
||||
vpnProfileUuid = cursor.getString(cursor.getColumnIndexOrThrow(KEY_VPN_PROFILE_UUID));
|
||||
alias = cursor.getString(cursor.getColumnIndexOrThrow(KEY_ALIAS));
|
||||
data = cursor.getString(cursor.getColumnIndexOrThrow(KEY_DATA));
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public ContentValues asContentValues()
|
||||
{
|
||||
final ContentValues values = new ContentValues();
|
||||
values.put(KEY_VPN_PROFILE_UUID, vpnProfileUuid);
|
||||
values.put(KEY_ALIAS, alias);
|
||||
values.put(KEY_DATA, data);
|
||||
return values;
|
||||
}
|
||||
|
||||
public long getId()
|
||||
{
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(long id)
|
||||
{
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public String getVpnProfileUuid()
|
||||
{
|
||||
return vpnProfileUuid;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public String getAlias()
|
||||
{
|
||||
return alias;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public String getData()
|
||||
{
|
||||
return data;
|
||||
}
|
||||
}
|
||||
+181
@@ -0,0 +1,181 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Relution GmbH
|
||||
*
|
||||
* Copyright (C) secunet Security Networks AG
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*/
|
||||
|
||||
package org.strongswan.android.data;
|
||||
|
||||
import android.content.ContentValues;
|
||||
import android.database.Cursor;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
public abstract class ManagedCertificateRepository<T extends ManagedCertificate>
|
||||
{
|
||||
@NonNull
|
||||
private final ManagedConfigurationService managedConfigurationService;
|
||||
|
||||
@NonNull
|
||||
private final SQLiteDatabase database;
|
||||
@NonNull
|
||||
private final DatabaseHelper.DbTable table;
|
||||
|
||||
protected ManagedCertificateRepository(
|
||||
@NonNull final ManagedConfigurationService managedConfigurationService,
|
||||
@NonNull final DatabaseHelper databaseHelper,
|
||||
@NonNull final DatabaseHelper.DbTable table)
|
||||
{
|
||||
this.managedConfigurationService = managedConfigurationService;
|
||||
|
||||
this.database = databaseHelper.getReadableDatabase();
|
||||
this.table = table;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected abstract T getCertificate(@NonNull final ManagedVpnProfile vpnProfile);
|
||||
|
||||
@NonNull
|
||||
protected abstract T createCertificate(@NonNull Cursor cursor);
|
||||
|
||||
protected abstract boolean isInstalled(@NonNull T certificate);
|
||||
|
||||
private boolean exists(@NonNull T certificate)
|
||||
{
|
||||
final String vpnProfileUuid = certificate.getVpnProfileUuid();
|
||||
try (final Cursor cursor = database.query(table.Name, table.columnNames(), ManagedCertificate.KEY_VPN_PROFILE_UUID + " = ?", new String[]{vpnProfileUuid}, null, null, null))
|
||||
{
|
||||
cursor.moveToFirst();
|
||||
if (!cursor.isAfterLast())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public List<T> getConfiguredCertificates()
|
||||
{
|
||||
managedConfigurationService.loadConfiguration();
|
||||
|
||||
final Map<String, ManagedVpnProfile> managedVpnProfiles = managedConfigurationService.getManagedProfiles();
|
||||
final List<T> certificates = new ArrayList<>(managedVpnProfiles.size());
|
||||
|
||||
for (final ManagedVpnProfile vpnProfile : managedVpnProfiles.values())
|
||||
{
|
||||
final T certificate = getCertificate(vpnProfile);
|
||||
if (certificate != null)
|
||||
{
|
||||
certificates.add(certificate);
|
||||
}
|
||||
}
|
||||
|
||||
return certificates;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the collection of certificates that were previously installed.
|
||||
* @see #addInstalledCertificate(ManagedCertificate)
|
||||
*/
|
||||
@NonNull
|
||||
private List<T> getCertificates()
|
||||
{
|
||||
try (final Cursor cursor = database.query(table.Name, table.columnNames(), null, null, null, null, null))
|
||||
{
|
||||
final List<T> certificates = new ArrayList<>();
|
||||
|
||||
cursor.moveToFirst();
|
||||
while (!cursor.isAfterLast())
|
||||
{
|
||||
final T certificate = createCertificate(cursor);
|
||||
certificates.add(certificate);
|
||||
cursor.moveToNext();
|
||||
}
|
||||
return certificates;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the collection of certificates that were previously marked as installed and are still
|
||||
* reported as installed by the OS.
|
||||
*
|
||||
* @return the collection of installed certificates.
|
||||
* @see #addInstalledCertificate(ManagedCertificate)
|
||||
*/
|
||||
@NonNull
|
||||
public List<T> getInstalledCertificates()
|
||||
{
|
||||
final List<T> certificates = getCertificates();
|
||||
final List<T> installed = new ArrayList<>(certificates.size());
|
||||
|
||||
for (final T certificate : certificates)
|
||||
{
|
||||
if (isInstalled(certificate))
|
||||
{
|
||||
installed.add(certificate);
|
||||
}
|
||||
}
|
||||
|
||||
return installed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a map containing certificates previously marked as installed, indexed by the
|
||||
* unique identifier of the VPN profile they are associated with.
|
||||
*
|
||||
* @return a map containing installed certificates, index by the VPN profile's unique
|
||||
* identifier.
|
||||
*/
|
||||
@NonNull
|
||||
public Map<String, T> getCertificateMap()
|
||||
{
|
||||
final List<T> certificates = getCertificates();
|
||||
final Map<String, T> map = new HashMap<>(certificates.size());
|
||||
|
||||
for (final T certificate : certificates)
|
||||
{
|
||||
map.put(certificate.getVpnProfileUuid(), certificate);
|
||||
}
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
public void addInstalledCertificate(@NonNull final T certificate)
|
||||
{
|
||||
final ContentValues values = certificate.asContentValues();
|
||||
|
||||
if (exists(certificate))
|
||||
{
|
||||
final String vpnProfileUuid = certificate.getVpnProfileUuid();
|
||||
database.update(table.Name, values, ManagedCertificate.KEY_VPN_PROFILE_UUID + " = ?", new String[]{vpnProfileUuid});
|
||||
}
|
||||
else
|
||||
{
|
||||
database.insert(table.Name, null, values);
|
||||
}
|
||||
}
|
||||
|
||||
public void removeInstalledCertificate(@NonNull final T certificate)
|
||||
{
|
||||
final String vpnProfileUuid = certificate.getVpnProfileUuid();
|
||||
database.delete(table.Name, ManagedCertificate.KEY_VPN_PROFILE_UUID + " = ?", new String[]{vpnProfileUuid});
|
||||
}
|
||||
}
|
||||
+179
@@ -0,0 +1,179 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Tobias Brunner
|
||||
* Copyright (C) 2023 Relution GmbH
|
||||
*
|
||||
* Copyright (C) secunet Security Networks AG
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*/
|
||||
|
||||
package org.strongswan.android.data;
|
||||
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Parcelable;
|
||||
|
||||
import org.strongswan.android.utils.Constants;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
public class ManagedConfiguration
|
||||
{
|
||||
private static final String KEY_ALLOW_PROFILE_CREATE = "allow_profile_create";
|
||||
private static final String KEY_ALLOW_PROFILE_IMPORT = "allow_profile_import";
|
||||
private static final String KEY_ALLOW_EXISTING_PROFILES = "allow_existing_profiles";
|
||||
private static final String KEY_ALLOW_CERTIFICATE_IMPORT = "allow_certificate_import";
|
||||
private static final String KEY_ALLOW_SETTINGS_ACCESS = "allow_settings_access";
|
||||
private static final String KEY_MANAGED_PROFILES = "managed_profiles";
|
||||
|
||||
private final boolean mAllowProfileCreation;
|
||||
private final boolean mAllowProfileImport;
|
||||
private final boolean mAllowExistingProfiles;
|
||||
private final boolean mAllowCertificateImport;
|
||||
|
||||
private final boolean mAllowSettingsAccess;
|
||||
private final String mDefaultVpnProfile;
|
||||
private final boolean mIgnoreBatteryOptimizations;
|
||||
|
||||
private final Map<String, ManagedVpnProfile> mManagedVpnProfiles;
|
||||
|
||||
ManagedConfiguration()
|
||||
{
|
||||
mAllowProfileCreation = true;
|
||||
mAllowProfileImport = true;
|
||||
mAllowExistingProfiles = true;
|
||||
mAllowCertificateImport = true;
|
||||
|
||||
mAllowSettingsAccess = true;
|
||||
mDefaultVpnProfile = null;
|
||||
mIgnoreBatteryOptimizations = false;
|
||||
|
||||
mManagedVpnProfiles = Collections.emptyMap();
|
||||
}
|
||||
|
||||
ManagedConfiguration(final Bundle bundle)
|
||||
{
|
||||
mAllowProfileCreation = bundle.getBoolean(KEY_ALLOW_PROFILE_CREATE, true);
|
||||
mAllowProfileImport = bundle.getBoolean(KEY_ALLOW_PROFILE_IMPORT, true);
|
||||
mAllowExistingProfiles = bundle.getBoolean(KEY_ALLOW_EXISTING_PROFILES, true);
|
||||
mAllowCertificateImport = bundle.getBoolean(KEY_ALLOW_CERTIFICATE_IMPORT, true);
|
||||
|
||||
mAllowSettingsAccess = bundle.getBoolean(KEY_ALLOW_SETTINGS_ACCESS, true);
|
||||
mDefaultVpnProfile = bundle.getString(Constants.PREF_DEFAULT_VPN_PROFILE, null);
|
||||
mIgnoreBatteryOptimizations = bundle.getBoolean(Constants.PREF_IGNORE_POWER_WHITELIST, false);
|
||||
|
||||
final List<Bundle> managedProfileBundles = getBundleArrayList(bundle, KEY_MANAGED_PROFILES);
|
||||
mManagedVpnProfiles = new HashMap<>(managedProfileBundles.size());
|
||||
|
||||
for (final Bundle managedProfileBundle : managedProfileBundles)
|
||||
{
|
||||
addManagedProfile(managedProfileBundle);
|
||||
}
|
||||
}
|
||||
|
||||
private void addManagedProfile(Bundle managedProfileBundle)
|
||||
{
|
||||
UUID uuid;
|
||||
try
|
||||
{
|
||||
uuid = UUID.fromString(managedProfileBundle.getString(VpnProfileDataSource.KEY_UUID));
|
||||
}
|
||||
catch (IllegalArgumentException e)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (mManagedVpnProfiles.containsKey(uuid.toString()))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
final ManagedVpnProfile vpnProfile = new ManagedVpnProfile(managedProfileBundle, uuid);
|
||||
mManagedVpnProfiles.put(uuid.toString(), vpnProfile);
|
||||
}
|
||||
|
||||
private List<Bundle> getBundleArrayList(final Bundle bundle, final String key)
|
||||
{
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU)
|
||||
{
|
||||
return getBundleArrayListCompat(bundle, key);
|
||||
}
|
||||
|
||||
final Bundle[] bundles = bundle.getParcelableArray(key, Bundle.class);
|
||||
if (bundles == null)
|
||||
{
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return Arrays.asList(bundles);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private static List<Bundle> getBundleArrayListCompat(final Bundle bundle, final String key)
|
||||
{
|
||||
final Parcelable[] parcelables = bundle.getParcelableArray(key);
|
||||
if (parcelables == null)
|
||||
{
|
||||
return Collections.emptyList();
|
||||
}
|
||||
final Bundle[] bundles = Arrays.copyOf(parcelables, parcelables.length, Bundle[].class);
|
||||
return Arrays.asList(bundles);
|
||||
}
|
||||
|
||||
public boolean isAllowProfileCreation()
|
||||
{
|
||||
return mAllowProfileCreation;
|
||||
}
|
||||
|
||||
public boolean isAllowProfileImport()
|
||||
{
|
||||
return mAllowProfileImport;
|
||||
}
|
||||
|
||||
public boolean isAllowExistingProfiles()
|
||||
{
|
||||
return mAllowExistingProfiles;
|
||||
}
|
||||
|
||||
public boolean isAllowCertificateImport()
|
||||
{
|
||||
return mAllowCertificateImport;
|
||||
}
|
||||
|
||||
public boolean isAllowSettingsAccess()
|
||||
{
|
||||
return mAllowSettingsAccess;
|
||||
}
|
||||
|
||||
public String getDefaultVpnProfile()
|
||||
{
|
||||
if (mDefaultVpnProfile != null && mDefaultVpnProfile.equalsIgnoreCase("mru"))
|
||||
{
|
||||
return Constants.PREF_DEFAULT_VPN_PROFILE_MRU;
|
||||
}
|
||||
return mDefaultVpnProfile;
|
||||
}
|
||||
|
||||
public boolean isIgnoreBatteryOptimizations()
|
||||
{
|
||||
return mIgnoreBatteryOptimizations;
|
||||
}
|
||||
|
||||
public Map<String, ManagedVpnProfile> getVpnProfiles()
|
||||
{
|
||||
return mManagedVpnProfiles;
|
||||
}
|
||||
}
|
||||
+90
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Relution GmbH
|
||||
*
|
||||
* Copyright (C) secunet Security Networks AG
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*/
|
||||
|
||||
package org.strongswan.android.data;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.RestrictionsManager;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
|
||||
import org.strongswan.android.utils.Constants;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
import androidx.preference.PreferenceManager;
|
||||
|
||||
public class ManagedConfigurationService
|
||||
{
|
||||
private final Context mContext;
|
||||
|
||||
private ManagedConfiguration mManagedConfiguration = new ManagedConfiguration();
|
||||
private Map<String, ManagedVpnProfile> mManagedVpnProfiles = Collections.emptyMap();
|
||||
|
||||
public ManagedConfigurationService(final Context context)
|
||||
{
|
||||
this.mContext = context;
|
||||
}
|
||||
|
||||
public void loadConfiguration()
|
||||
{
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
final RestrictionsManager restrictionsService = mContext.getSystemService(RestrictionsManager.class);
|
||||
if (restrictionsService == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
final Bundle configuration = restrictionsService.getApplicationRestrictions();
|
||||
if (configuration == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
final ManagedConfiguration managedConfiguration = new ManagedConfiguration(configuration);
|
||||
mManagedConfiguration = managedConfiguration;
|
||||
mManagedVpnProfiles = managedConfiguration.getVpnProfiles();
|
||||
}
|
||||
|
||||
public void updateSettings()
|
||||
{
|
||||
if (!mManagedConfiguration.isAllowSettingsAccess())
|
||||
{
|
||||
final SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(mContext);
|
||||
final SharedPreferences.Editor editor = pref.edit();
|
||||
editor.putBoolean(Constants.PREF_IGNORE_POWER_WHITELIST, mManagedConfiguration.isIgnoreBatteryOptimizations());
|
||||
editor.putString(Constants.PREF_DEFAULT_VPN_PROFILE, mManagedConfiguration.getDefaultVpnProfile());
|
||||
editor.apply();
|
||||
}
|
||||
}
|
||||
|
||||
public ManagedConfiguration getManagedConfiguration()
|
||||
{
|
||||
return mManagedConfiguration;
|
||||
}
|
||||
|
||||
public Map<String, ManagedVpnProfile> getManagedProfiles()
|
||||
{
|
||||
return mManagedVpnProfiles;
|
||||
}
|
||||
}
|
||||
+89
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Relution GmbH
|
||||
*
|
||||
* Copyright (C) secunet Security Networks AG
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*/
|
||||
|
||||
package org.strongswan.android.data;
|
||||
|
||||
import android.database.Cursor;
|
||||
|
||||
import org.strongswan.android.utils.Certificates;
|
||||
|
||||
import java.security.KeyStore;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.Objects;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
public class ManagedTrustedCertificate extends ManagedCertificate
|
||||
{
|
||||
public ManagedTrustedCertificate(
|
||||
@NonNull final String vpnProfileUuid,
|
||||
@NonNull final String data)
|
||||
{
|
||||
super(vpnProfileUuid, determineAlias(vpnProfileUuid, data), data);
|
||||
}
|
||||
|
||||
public ManagedTrustedCertificate(@NonNull final Cursor cursor)
|
||||
{
|
||||
super(cursor);
|
||||
}
|
||||
|
||||
private static String determineAlias(String vpnProfileUuid, String data)
|
||||
{
|
||||
/* fallback in case the certificate is invalid */
|
||||
String certAlias = "trusted:" + vpnProfileUuid;
|
||||
try
|
||||
{
|
||||
X509Certificate cert = Certificates.from(data);
|
||||
KeyStore store = KeyStore.getInstance("LocalCertificateStore");
|
||||
store.load(null, null);
|
||||
certAlias = store.getCertificateAlias(cert);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
return certAlias;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o)
|
||||
{
|
||||
if (this == o)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
ManagedTrustedCertificate that = (ManagedTrustedCertificate)o;
|
||||
return Objects.equals(vpnProfileUuid, that.vpnProfileUuid) &&
|
||||
Objects.equals(data, that.data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
return Objects.hash(vpnProfileUuid, data);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return "ManagedTrustedCertificate {" + vpnProfileUuid + ", " + alias + "}";
|
||||
}
|
||||
}
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Relution GmbH
|
||||
*
|
||||
* Copyright (C) secunet Security Networks AG
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*/
|
||||
|
||||
package org.strongswan.android.data;
|
||||
|
||||
import android.database.Cursor;
|
||||
|
||||
import org.strongswan.android.logic.TrustedCertificateManager;
|
||||
|
||||
import java.security.cert.X509Certificate;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
public class ManagedTrustedCertificateRepository extends ManagedCertificateRepository<ManagedTrustedCertificate>
|
||||
{
|
||||
private static final DatabaseHelper.DbTable TABLE = DatabaseHelper.TABLE_TRUSTED_CERTIFICATE;
|
||||
|
||||
public ManagedTrustedCertificateRepository(
|
||||
@NonNull final ManagedConfigurationService managedConfigurationService,
|
||||
@NonNull final DatabaseHelper databaseHelper)
|
||||
{
|
||||
super(managedConfigurationService, databaseHelper, TABLE);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
protected ManagedTrustedCertificate getCertificate(@NonNull ManagedVpnProfile vpnProfile)
|
||||
{
|
||||
return vpnProfile.getTrustedCertificate();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
protected ManagedTrustedCertificate createCertificate(@NonNull Cursor cursor)
|
||||
{
|
||||
return new ManagedTrustedCertificate(cursor);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isInstalled(@NonNull ManagedTrustedCertificate certificate)
|
||||
{
|
||||
TrustedCertificateManager certificateManager = TrustedCertificateManager.getInstance();
|
||||
final X509Certificate x509Certificate = certificateManager.getCACertificateFromAlias(certificate.getAlias());
|
||||
|
||||
return x509Certificate != null;
|
||||
}
|
||||
}
|
||||
+92
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Relution GmbH
|
||||
*
|
||||
* Copyright (C) secunet Security Networks AG
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*/
|
||||
|
||||
package org.strongswan.android.data;
|
||||
|
||||
import android.content.ContentValues;
|
||||
import android.database.Cursor;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
public class ManagedUserCertificate extends ManagedCertificate
|
||||
{
|
||||
public static final String KEY_PASSWORD = "password";
|
||||
|
||||
private final String privateKeyPassword;
|
||||
|
||||
public ManagedUserCertificate(
|
||||
@NonNull final String vpnProfileUuid,
|
||||
@NonNull final String data,
|
||||
@Nullable final String password)
|
||||
{
|
||||
super(vpnProfileUuid, "user:" + vpnProfileUuid, data);
|
||||
privateKeyPassword = password;
|
||||
}
|
||||
|
||||
public ManagedUserCertificate(@NonNull final Cursor cursor)
|
||||
{
|
||||
super(cursor);
|
||||
privateKeyPassword = cursor.getString(cursor.getColumnIndexOrThrow(KEY_PASSWORD));
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public ContentValues asContentValues()
|
||||
{
|
||||
final ContentValues values = super.asContentValues();
|
||||
values.put(KEY_PASSWORD, privateKeyPassword);
|
||||
return values;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getPrivateKeyPassword()
|
||||
{
|
||||
return privateKeyPassword;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o)
|
||||
{
|
||||
if (this == o)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
ManagedUserCertificate that = (ManagedUserCertificate)o;
|
||||
return Objects.equals(vpnProfileUuid, that.vpnProfileUuid) &&
|
||||
Objects.equals(data, that.data) &&
|
||||
Objects.equals(privateKeyPassword, that.privateKeyPassword);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
return Objects.hash(vpnProfileUuid, data);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return "ManagedUserCertificate {" + vpnProfileUuid + ", " + alias + "}";
|
||||
}
|
||||
}
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Relution GmbH
|
||||
*
|
||||
* Copyright (C) secunet Security Networks AG
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*/
|
||||
|
||||
package org.strongswan.android.data;
|
||||
|
||||
import android.app.admin.DevicePolicyManager;
|
||||
import android.database.Cursor;
|
||||
import android.os.Build;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
public class ManagedUserCertificateRepository extends ManagedCertificateRepository<ManagedUserCertificate>
|
||||
{
|
||||
private static final DatabaseHelper.DbTable TABLE = DatabaseHelper.TABLE_USER_CERTIFICATE;
|
||||
|
||||
@NonNull
|
||||
private final DevicePolicyManager devicePolicyManager;
|
||||
|
||||
public ManagedUserCertificateRepository(
|
||||
@NonNull final ManagedConfigurationService managedConfigurationService,
|
||||
@NonNull final DevicePolicyManager devicePolicyManager,
|
||||
@NonNull final DatabaseHelper databaseHelper)
|
||||
{
|
||||
super(managedConfigurationService, databaseHelper, TABLE);
|
||||
this.devicePolicyManager = devicePolicyManager;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
protected ManagedUserCertificate getCertificate(@NonNull ManagedVpnProfile vpnProfile)
|
||||
{
|
||||
return vpnProfile.getUserCertificate();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
protected ManagedUserCertificate createCertificate(@NonNull Cursor cursor)
|
||||
{
|
||||
return new ManagedUserCertificate(cursor);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isInstalled(@NonNull ManagedUserCertificate certificate)
|
||||
{
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S)
|
||||
{
|
||||
return devicePolicyManager.hasKeyPair(certificate.getAlias());
|
||||
}
|
||||
/* We don't know, so we assume a certificate we installed may have been removed by the
|
||||
* user, so we install it again to make sure it's still there */
|
||||
return false;
|
||||
}
|
||||
}
|
||||
+205
@@ -0,0 +1,205 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Relution GmbH
|
||||
*
|
||||
* Copyright (C) secunet Security Networks AG
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*/
|
||||
|
||||
package org.strongswan.android.data;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import org.strongswan.android.utils.Constants;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
public class ManagedVpnProfile extends VpnProfile
|
||||
{
|
||||
private static final String KEY_REMOTE = "remote";
|
||||
private static final String KEY_LOCAL = "local";
|
||||
private static final String KEY_INCLUDED_APPS = "included_apps";
|
||||
private static final String KEY_EXCLUDED_APPS = "excluded_apps";
|
||||
|
||||
private static final String KEY_TRANSPORT_IPV6_FLAG = "transport_ipv6";
|
||||
private static final String KEY_REMOTE_CERT_REQ_FLAG = "remote_cert_req";
|
||||
private static final String KEY_REMOTE_REVOCATION_CRL_FLAG = "remote_revocation_crl";
|
||||
private static final String KEY_REMOTE_REVOCATION_OCSP_FLAG = "remote_revocation_ocsp";
|
||||
private static final String KEY_REMOTE_REVOCATION_STRICT_FLAG = "remote_revocation_strict";
|
||||
private static final String KEY_LOCAL_RSA_PSS_FLAG = "local_rsa_pss";
|
||||
|
||||
private static final String KEY_SPLIT_TUNNELLING_BLOCK_IPV4_FLAG = "split_tunnelling_block_ipv4";
|
||||
private static final String KEY_SPLIT_TUNNELLING_BLOCK_IPV6_FLAG = "split_tunnelling_block_ipv6";
|
||||
|
||||
private ManagedTrustedCertificate trustedCertificate;
|
||||
private ManagedUserCertificate userCertificate;
|
||||
|
||||
ManagedVpnProfile(final Bundle bundle, final UUID uuid)
|
||||
{
|
||||
int flags = 0;
|
||||
int splitFlags = 0;
|
||||
|
||||
setReadOnly(true);
|
||||
setUUID(uuid);
|
||||
setName(bundle.getString(VpnProfileDataSource.KEY_NAME));
|
||||
setVpnType(VpnType.fromIdentifier(bundle.getString(VpnProfileDataSource.KEY_VPN_TYPE)));
|
||||
|
||||
final Bundle remote = bundle.getBundle(KEY_REMOTE);
|
||||
flags = configureRemote(uuid, remote, flags);
|
||||
|
||||
final Bundle local = bundle.getBundle(KEY_LOCAL);
|
||||
flags = configureLocal(uuid, local, flags);
|
||||
|
||||
final String includedPackageNames = bundle.getString(KEY_INCLUDED_APPS);
|
||||
final String excludedPackageNames = bundle.getString(KEY_EXCLUDED_APPS);
|
||||
configureSelectedApps(includedPackageNames, excludedPackageNames);
|
||||
|
||||
setMTU(getInt(bundle, VpnProfileDataSource.KEY_MTU, Constants.MTU_MIN, Constants.MTU_MAX));
|
||||
setNATKeepAlive(getInt(bundle, VpnProfileDataSource.KEY_NAT_KEEPALIVE, Constants.NAT_KEEPALIVE_MIN, Constants.NAT_KEEPALIVE_MAX));
|
||||
setIkeProposal(bundle.getString(VpnProfileDataSource.KEY_IKE_PROPOSAL));
|
||||
setEspProposal(bundle.getString(VpnProfileDataSource.KEY_ESP_PROPOSAL));
|
||||
setDnsServers(bundle.getString(VpnProfileDataSource.KEY_DNS_SERVERS));
|
||||
flags = addPositiveFlag(flags, bundle, KEY_TRANSPORT_IPV6_FLAG, VpnProfile.FLAGS_IPv6_TRANSPORT);
|
||||
|
||||
final Bundle splitTunneling = bundle.getBundle(VpnProfileDataSource.KEY_SPLIT_TUNNELING);
|
||||
if (splitTunneling != null)
|
||||
{
|
||||
splitFlags = addPositiveFlag(splitFlags, splitTunneling, KEY_SPLIT_TUNNELLING_BLOCK_IPV4_FLAG, VpnProfile.SPLIT_TUNNELING_BLOCK_IPV4);
|
||||
splitFlags = addPositiveFlag(splitFlags, splitTunneling, KEY_SPLIT_TUNNELLING_BLOCK_IPV6_FLAG, VpnProfile.SPLIT_TUNNELING_BLOCK_IPV6);
|
||||
|
||||
setExcludedSubnets(splitTunneling.getString(VpnProfileDataSource.KEY_EXCLUDED_SUBNETS));
|
||||
setIncludedSubnets(splitTunneling.getString(VpnProfileDataSource.KEY_INCLUDED_SUBNETS));
|
||||
}
|
||||
|
||||
setSplitTunneling(splitFlags);
|
||||
setFlags(flags);
|
||||
}
|
||||
|
||||
private void configureSelectedApps(String includedPackageNames, String excludedPackageNames)
|
||||
{
|
||||
if (!TextUtils.isEmpty(includedPackageNames))
|
||||
{
|
||||
setSelectedAppsHandling(SelectedAppsHandling.SELECTED_APPS_ONLY);
|
||||
setSelectedApps(includedPackageNames);
|
||||
}
|
||||
else if (!TextUtils.isEmpty(excludedPackageNames))
|
||||
{
|
||||
setSelectedAppsHandling(SelectedAppsHandling.SELECTED_APPS_EXCLUDE);
|
||||
setSelectedApps(excludedPackageNames);
|
||||
}
|
||||
}
|
||||
|
||||
private int configureRemote(final UUID uuid, @Nullable Bundle remote, int flags)
|
||||
{
|
||||
if (remote == null)
|
||||
{
|
||||
return flags;
|
||||
}
|
||||
|
||||
setGateway(remote.getString(VpnProfileDataSource.KEY_GATEWAY));
|
||||
setPort(getInt(remote, VpnProfileDataSource.KEY_PORT, 1, 65_535));
|
||||
setRemoteId(remote.getString(VpnProfileDataSource.KEY_REMOTE_ID));
|
||||
|
||||
final String certificateData = remote.getString(VpnProfileDataSource.KEY_CERTIFICATE);
|
||||
if (!TextUtils.isEmpty(certificateData))
|
||||
{
|
||||
trustedCertificate = new ManagedTrustedCertificate(uuid.toString(), certificateData);
|
||||
setCertificateAlias(trustedCertificate.getAlias());
|
||||
}
|
||||
|
||||
flags = addNegativeFlag(flags, remote, KEY_REMOTE_CERT_REQ_FLAG, VpnProfile.FLAGS_SUPPRESS_CERT_REQS);
|
||||
flags = addNegativeFlag(flags, remote, KEY_REMOTE_REVOCATION_CRL_FLAG, VpnProfile.FLAGS_DISABLE_CRL);
|
||||
flags = addNegativeFlag(flags, remote, KEY_REMOTE_REVOCATION_OCSP_FLAG, VpnProfile.FLAGS_DISABLE_OCSP);
|
||||
flags = addPositiveFlag(flags, remote, KEY_REMOTE_REVOCATION_STRICT_FLAG, VpnProfile.FLAGS_STRICT_REVOCATION);
|
||||
return flags;
|
||||
}
|
||||
|
||||
private int configureLocal(final UUID uuid, @Nullable Bundle local, int flags)
|
||||
{
|
||||
if (local == null)
|
||||
{
|
||||
return flags;
|
||||
}
|
||||
|
||||
setLocalId(local.getString(VpnProfileDataSource.KEY_LOCAL_ID));
|
||||
setUsername(local.getString(VpnProfileDataSource.KEY_USERNAME));
|
||||
|
||||
final String userCertificateData = local.getString(VpnProfileDataSource.KEY_USER_CERTIFICATE);
|
||||
final String userCertificatePassword = local.getString(VpnProfileDataSource.KEY_USER_CERTIFICATE_PASSWORD, "");
|
||||
if (!TextUtils.isEmpty(userCertificateData))
|
||||
{
|
||||
userCertificate = new ManagedUserCertificate(uuid.toString(), userCertificateData, userCertificatePassword);
|
||||
setUserCertificateAlias(userCertificate.getAlias());
|
||||
}
|
||||
|
||||
flags = addPositiveFlag(flags, local, KEY_LOCAL_RSA_PSS_FLAG, VpnProfile.FLAGS_RSA_PSS);
|
||||
return flags;
|
||||
}
|
||||
|
||||
private static Integer getInt(final Bundle bundle, final String key, final int min, final int max)
|
||||
{
|
||||
final int value = bundle.getInt(key);
|
||||
return value < min || value > max ? null : value;
|
||||
}
|
||||
|
||||
private static int addPositiveFlag(int flags, Bundle bundle, String key, int flag)
|
||||
{
|
||||
if (bundle.getBoolean(key))
|
||||
{
|
||||
flags |= flag;
|
||||
}
|
||||
return flags;
|
||||
}
|
||||
|
||||
private static int addNegativeFlag(int flags, Bundle bundle, String key, int flag)
|
||||
{
|
||||
if (!bundle.getBoolean(key))
|
||||
{
|
||||
flags |= flag;
|
||||
}
|
||||
return flags;
|
||||
}
|
||||
|
||||
public ManagedTrustedCertificate getTrustedCertificate()
|
||||
{
|
||||
return trustedCertificate;
|
||||
}
|
||||
|
||||
public ManagedUserCertificate getUserCertificate()
|
||||
{
|
||||
return userCertificate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o)
|
||||
{
|
||||
if (o == this)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
ManagedVpnProfile that = (ManagedVpnProfile)o;
|
||||
return Objects.equals(getUUID(), that.getUUID());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
return Objects.hash(getUUID());
|
||||
}
|
||||
}
|
||||
+38
-9
@@ -22,6 +22,7 @@ package org.strongswan.android.data;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Objects;
|
||||
import java.util.SortedSet;
|
||||
import java.util.TreeSet;
|
||||
import java.util.UUID;
|
||||
@@ -47,6 +48,8 @@ public class VpnProfile implements Cloneable
|
||||
private VpnType mVpnType;
|
||||
private UUID mUUID;
|
||||
private long mId = -1;
|
||||
private boolean mReadOnly;
|
||||
private VpnProfileDataSource mDataSource;
|
||||
|
||||
public enum SelectedAppsHandling
|
||||
{
|
||||
@@ -54,7 +57,7 @@ public class VpnProfile implements Cloneable
|
||||
SELECTED_APPS_EXCLUDE(1),
|
||||
SELECTED_APPS_ONLY(2);
|
||||
|
||||
private Integer mValue;
|
||||
private final Integer mValue;
|
||||
|
||||
SelectedAppsHandling(int value)
|
||||
{
|
||||
@@ -330,6 +333,26 @@ public class VpnProfile implements Cloneable
|
||||
this.mFlags = flags;
|
||||
}
|
||||
|
||||
public boolean isReadOnly()
|
||||
{
|
||||
return mReadOnly;
|
||||
}
|
||||
|
||||
public void setReadOnly(boolean readOnly)
|
||||
{
|
||||
this.mReadOnly = readOnly;
|
||||
}
|
||||
|
||||
public VpnProfileDataSource getDataSource()
|
||||
{
|
||||
return mDataSource;
|
||||
}
|
||||
|
||||
public void setDataSource(VpnProfileDataSource mDataSource)
|
||||
{
|
||||
this.mDataSource = mDataSource;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
@@ -339,16 +362,22 @@ public class VpnProfile implements Cloneable
|
||||
@Override
|
||||
public boolean equals(Object o)
|
||||
{
|
||||
if (o != null && o instanceof VpnProfile)
|
||||
if (o == this)
|
||||
{
|
||||
VpnProfile other = (VpnProfile)o;
|
||||
if (this.mUUID != null && other.getUUID() != null)
|
||||
{
|
||||
return this.mUUID.equals(other.getUUID());
|
||||
}
|
||||
return this.mId == other.getId();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
if (o == null || getClass() != o.getClass())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
VpnProfile that = (VpnProfile)o;
|
||||
return Objects.equals(mUUID, that.mUUID);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
return Objects.hash(mUUID);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+41
-419
@@ -1,4 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Relution GmbH
|
||||
* Copyright (C) 2012-2019 Tobias Brunner
|
||||
* Copyright (C) 2012 Giuliano Grassi
|
||||
* Copyright (C) 2012 Ralf Sager
|
||||
@@ -18,298 +19,52 @@
|
||||
|
||||
package org.strongswan.android.data;
|
||||
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.database.SQLException;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.database.sqlite.SQLiteOpenHelper;
|
||||
import android.database.sqlite.SQLiteQueryBuilder;
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
public class VpnProfileDataSource
|
||||
public interface VpnProfileDataSource
|
||||
{
|
||||
private static final String TAG = VpnProfileDataSource.class.getSimpleName();
|
||||
public static final String KEY_ID = "_id";
|
||||
public static final String KEY_UUID = "_uuid";
|
||||
public static final String KEY_NAME = "name";
|
||||
public static final String KEY_GATEWAY = "gateway";
|
||||
public static final String KEY_VPN_TYPE = "vpn_type";
|
||||
public static final String KEY_USERNAME = "username";
|
||||
public static final String KEY_PASSWORD = "password";
|
||||
public static final String KEY_CERTIFICATE = "certificate";
|
||||
public static final String KEY_USER_CERTIFICATE = "user_certificate";
|
||||
public static final String KEY_MTU = "mtu";
|
||||
public static final String KEY_PORT = "port";
|
||||
public static final String KEY_SPLIT_TUNNELING = "split_tunneling";
|
||||
public static final String KEY_LOCAL_ID = "local_id";
|
||||
public static final String KEY_REMOTE_ID = "remote_id";
|
||||
public static final String KEY_EXCLUDED_SUBNETS = "excluded_subnets";
|
||||
public static final String KEY_INCLUDED_SUBNETS = "included_subnets";
|
||||
public static final String KEY_SELECTED_APPS = "selected_apps";
|
||||
public static final String KEY_SELECTED_APPS_LIST = "selected_apps_list";
|
||||
public static final String KEY_NAT_KEEPALIVE = "nat_keepalive";
|
||||
public static final String KEY_FLAGS = "flags";
|
||||
public static final String KEY_IKE_PROPOSAL = "ike_proposal";
|
||||
public static final String KEY_ESP_PROPOSAL = "esp_proposal";
|
||||
public static final String KEY_DNS_SERVERS = "dns_servers";
|
||||
|
||||
private DatabaseHelper mDbHelper;
|
||||
private SQLiteDatabase mDatabase;
|
||||
private final Context mContext;
|
||||
|
||||
private static final String DATABASE_NAME = "strongswan.db";
|
||||
private static final String TABLE_VPNPROFILE = "vpnprofile";
|
||||
|
||||
private static final int DATABASE_VERSION = 17;
|
||||
|
||||
public static final DbColumn[] COLUMNS = new DbColumn[] {
|
||||
new DbColumn(KEY_ID, "INTEGER PRIMARY KEY AUTOINCREMENT", 1),
|
||||
new DbColumn(KEY_UUID, "TEXT UNIQUE", 9),
|
||||
new DbColumn(KEY_NAME, "TEXT NOT NULL", 1),
|
||||
new DbColumn(KEY_GATEWAY, "TEXT NOT NULL", 1),
|
||||
new DbColumn(KEY_VPN_TYPE, "TEXT NOT NULL", 3),
|
||||
new DbColumn(KEY_USERNAME, "TEXT", 1),
|
||||
new DbColumn(KEY_PASSWORD, "TEXT", 1),
|
||||
new DbColumn(KEY_CERTIFICATE, "TEXT", 1),
|
||||
new DbColumn(KEY_USER_CERTIFICATE, "TEXT", 2),
|
||||
new DbColumn(KEY_MTU, "INTEGER", 5),
|
||||
new DbColumn(KEY_PORT, "INTEGER", 5),
|
||||
new DbColumn(KEY_SPLIT_TUNNELING, "INTEGER", 7),
|
||||
new DbColumn(KEY_LOCAL_ID, "TEXT", 8),
|
||||
new DbColumn(KEY_REMOTE_ID, "TEXT", 8),
|
||||
new DbColumn(KEY_EXCLUDED_SUBNETS, "TEXT", 10),
|
||||
new DbColumn(KEY_INCLUDED_SUBNETS, "TEXT", 11),
|
||||
new DbColumn(KEY_SELECTED_APPS, "INTEGER", 12),
|
||||
new DbColumn(KEY_SELECTED_APPS_LIST, "TEXT", 12),
|
||||
new DbColumn(KEY_NAT_KEEPALIVE, "INTEGER", 13),
|
||||
new DbColumn(KEY_FLAGS, "INTEGER", 14),
|
||||
new DbColumn(KEY_IKE_PROPOSAL, "TEXT", 15),
|
||||
new DbColumn(KEY_ESP_PROPOSAL, "TEXT", 15),
|
||||
new DbColumn(KEY_DNS_SERVERS, "TEXT", 17),
|
||||
};
|
||||
|
||||
private static final String[] ALL_COLUMNS = getColumns(DATABASE_VERSION);
|
||||
|
||||
private static String getDatabaseCreate(int version)
|
||||
{
|
||||
boolean first = true;
|
||||
StringBuilder create = new StringBuilder("CREATE TABLE ");
|
||||
create.append(TABLE_VPNPROFILE);
|
||||
create.append(" (");
|
||||
for (DbColumn column : COLUMNS)
|
||||
{
|
||||
if (column.Since <= version)
|
||||
{
|
||||
if (!first)
|
||||
{
|
||||
create.append(",");
|
||||
}
|
||||
first = false;
|
||||
create.append(column.Name);
|
||||
create.append(" ");
|
||||
create.append(column.Type);
|
||||
}
|
||||
}
|
||||
create.append(");");
|
||||
return create.toString();
|
||||
}
|
||||
|
||||
private static String[] getColumns(int version)
|
||||
{
|
||||
ArrayList<String> columns = new ArrayList<>();
|
||||
for (DbColumn column : COLUMNS)
|
||||
{
|
||||
if (column.Since <= version)
|
||||
{
|
||||
columns.add(column.Name);
|
||||
}
|
||||
}
|
||||
return columns.toArray(new String[0]);
|
||||
}
|
||||
|
||||
private static class DatabaseHelper extends SQLiteOpenHelper
|
||||
{
|
||||
public DatabaseHelper(Context context)
|
||||
{
|
||||
super(context, DATABASE_NAME, null, DATABASE_VERSION);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(SQLiteDatabase database)
|
||||
{
|
||||
database.execSQL(getDatabaseCreate(DATABASE_VERSION));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)
|
||||
{
|
||||
Log.w(TAG, "Upgrading database from version " + oldVersion +
|
||||
" to " + newVersion);
|
||||
if (oldVersion < 2)
|
||||
{
|
||||
db.execSQL("ALTER TABLE " + TABLE_VPNPROFILE + " ADD " + KEY_USER_CERTIFICATE +
|
||||
" TEXT;");
|
||||
}
|
||||
if (oldVersion < 3)
|
||||
{
|
||||
db.execSQL("ALTER TABLE " + TABLE_VPNPROFILE + " ADD " + KEY_VPN_TYPE +
|
||||
" TEXT DEFAULT '';");
|
||||
}
|
||||
if (oldVersion < 4)
|
||||
{ /* remove NOT NULL constraint from username column */
|
||||
updateColumns(db, 4);
|
||||
}
|
||||
if (oldVersion < 5)
|
||||
{
|
||||
db.execSQL("ALTER TABLE " + TABLE_VPNPROFILE + " ADD " + KEY_MTU +
|
||||
" INTEGER;");
|
||||
}
|
||||
if (oldVersion < 6)
|
||||
{
|
||||
db.execSQL("ALTER TABLE " + TABLE_VPNPROFILE + " ADD " + KEY_PORT +
|
||||
" INTEGER;");
|
||||
}
|
||||
if (oldVersion < 7)
|
||||
{
|
||||
db.execSQL("ALTER TABLE " + TABLE_VPNPROFILE + " ADD " + KEY_SPLIT_TUNNELING +
|
||||
" INTEGER;");
|
||||
}
|
||||
if (oldVersion < 8)
|
||||
{
|
||||
db.execSQL("ALTER TABLE " + TABLE_VPNPROFILE + " ADD " + KEY_LOCAL_ID +
|
||||
" TEXT;");
|
||||
db.execSQL("ALTER TABLE " + TABLE_VPNPROFILE + " ADD " + KEY_REMOTE_ID +
|
||||
" TEXT;");
|
||||
}
|
||||
if (oldVersion < 9)
|
||||
{
|
||||
db.execSQL("ALTER TABLE " + TABLE_VPNPROFILE + " ADD " + KEY_UUID +
|
||||
" TEXT;");
|
||||
updateColumns(db, 9);
|
||||
}
|
||||
if (oldVersion < 10)
|
||||
{
|
||||
db.execSQL("ALTER TABLE " + TABLE_VPNPROFILE + " ADD " + KEY_EXCLUDED_SUBNETS +
|
||||
" TEXT;");
|
||||
}
|
||||
if (oldVersion < 11)
|
||||
{
|
||||
db.execSQL("ALTER TABLE " + TABLE_VPNPROFILE + " ADD " + KEY_INCLUDED_SUBNETS +
|
||||
" TEXT;");
|
||||
}
|
||||
if (oldVersion < 12)
|
||||
{
|
||||
db.execSQL("ALTER TABLE " + TABLE_VPNPROFILE + " ADD " + KEY_SELECTED_APPS +
|
||||
" INTEGER;");
|
||||
db.execSQL("ALTER TABLE " + TABLE_VPNPROFILE + " ADD " + KEY_SELECTED_APPS_LIST +
|
||||
" TEXT;");
|
||||
}
|
||||
if (oldVersion < 13)
|
||||
{
|
||||
db.execSQL("ALTER TABLE " + TABLE_VPNPROFILE + " ADD " + KEY_NAT_KEEPALIVE +
|
||||
" INTEGER;");
|
||||
}
|
||||
if (oldVersion < 14)
|
||||
{
|
||||
db.execSQL("ALTER TABLE " + TABLE_VPNPROFILE + " ADD " + KEY_FLAGS +
|
||||
" INTEGER;");
|
||||
}
|
||||
if (oldVersion < 15)
|
||||
{
|
||||
db.execSQL("ALTER TABLE " + TABLE_VPNPROFILE + " ADD " + KEY_IKE_PROPOSAL +
|
||||
" TEXT;");
|
||||
db.execSQL("ALTER TABLE " + TABLE_VPNPROFILE + " ADD " + KEY_ESP_PROPOSAL +
|
||||
" TEXT;");
|
||||
}
|
||||
if (oldVersion < 16)
|
||||
{ /* add a UUID to all entries that haven't one yet */
|
||||
db.beginTransaction();
|
||||
try
|
||||
{
|
||||
Cursor cursor = db.query(TABLE_VPNPROFILE, getColumns(16), KEY_UUID + " is NULL", null, null, null, null);
|
||||
for (cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext())
|
||||
{
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(KEY_UUID, UUID.randomUUID().toString());
|
||||
db.update(TABLE_VPNPROFILE, values, KEY_ID + " = " + cursor.getLong(cursor.getColumnIndexOrThrow(KEY_ID)), null);
|
||||
}
|
||||
cursor.close();
|
||||
db.setTransactionSuccessful();
|
||||
}
|
||||
finally
|
||||
{
|
||||
db.endTransaction();
|
||||
}
|
||||
}
|
||||
if (oldVersion < 17)
|
||||
{
|
||||
db.execSQL("ALTER TABLE " + TABLE_VPNPROFILE + " ADD " + KEY_DNS_SERVERS +
|
||||
" TEXT;");
|
||||
}
|
||||
}
|
||||
|
||||
private void updateColumns(SQLiteDatabase db, int version)
|
||||
{
|
||||
db.beginTransaction();
|
||||
try
|
||||
{
|
||||
db.execSQL("ALTER TABLE " + TABLE_VPNPROFILE + " RENAME TO tmp_" + TABLE_VPNPROFILE + ";");
|
||||
db.execSQL(getDatabaseCreate(version));
|
||||
StringBuilder insert = new StringBuilder("INSERT INTO " + TABLE_VPNPROFILE + " SELECT ");
|
||||
SQLiteQueryBuilder.appendColumns(insert, getColumns(version));
|
||||
db.execSQL(insert.append(" FROM tmp_" + TABLE_VPNPROFILE + ";").toString());
|
||||
db.execSQL("DROP TABLE tmp_" + TABLE_VPNPROFILE + ";");
|
||||
db.setTransactionSuccessful();
|
||||
}
|
||||
finally
|
||||
{
|
||||
db.endTransaction();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a new VPN profile data source. The context is used to
|
||||
* open/create the database.
|
||||
* @param context context used to access the database
|
||||
*/
|
||||
public VpnProfileDataSource(Context context)
|
||||
{
|
||||
this.mContext = context;
|
||||
}
|
||||
String KEY_ID = "_id";
|
||||
String KEY_UUID = "_uuid";
|
||||
String KEY_NAME = "name";
|
||||
String KEY_GATEWAY = "gateway";
|
||||
String KEY_VPN_TYPE = "vpn_type";
|
||||
String KEY_USERNAME = "username";
|
||||
String KEY_PASSWORD = "password";
|
||||
String KEY_CERTIFICATE = "certificate";
|
||||
String KEY_USER_CERTIFICATE = "user_certificate";
|
||||
String KEY_USER_CERTIFICATE_PASSWORD = "user_certificate_password";
|
||||
String KEY_MTU = "mtu";
|
||||
String KEY_PORT = "port";
|
||||
String KEY_SPLIT_TUNNELING = "split_tunneling";
|
||||
String KEY_LOCAL_ID = "local_id";
|
||||
String KEY_REMOTE_ID = "remote_id";
|
||||
String KEY_EXCLUDED_SUBNETS = "excluded_subnets";
|
||||
String KEY_INCLUDED_SUBNETS = "included_subnets";
|
||||
String KEY_SELECTED_APPS = "selected_apps";
|
||||
String KEY_SELECTED_APPS_LIST = "selected_apps_list";
|
||||
String KEY_NAT_KEEPALIVE = "nat_keepalive";
|
||||
String KEY_FLAGS = "flags";
|
||||
String KEY_IKE_PROPOSAL = "ike_proposal";
|
||||
String KEY_ESP_PROPOSAL = "esp_proposal";
|
||||
String KEY_DNS_SERVERS = "dns_servers";
|
||||
String KEY_READ_ONLY = "read_only";
|
||||
|
||||
/**
|
||||
* Open the VPN profile data source. The database is automatically created
|
||||
* if it does not yet exist. If that fails an exception is thrown.
|
||||
*
|
||||
* @return itself (allows to chain initialization calls)
|
||||
* @throws SQLException if the database could not be opened or created
|
||||
*/
|
||||
public VpnProfileDataSource open() throws SQLException
|
||||
{
|
||||
if (mDbHelper == null)
|
||||
{
|
||||
mDbHelper = new DatabaseHelper(mContext);
|
||||
mDatabase = mDbHelper.getWritableDatabase();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
VpnProfileDataSource open() throws SQLException;
|
||||
|
||||
/**
|
||||
* Close the data source.
|
||||
*/
|
||||
public void close()
|
||||
{
|
||||
if (mDbHelper != null)
|
||||
{
|
||||
mDbHelper.close();
|
||||
mDbHelper = null;
|
||||
}
|
||||
}
|
||||
void close();
|
||||
|
||||
/**
|
||||
* Insert the given VPN profile into the database. On success the Id of
|
||||
@@ -318,83 +73,39 @@ public class VpnProfileDataSource
|
||||
* @param profile the profile to add
|
||||
* @return the added VPN profile or null, if failed
|
||||
*/
|
||||
public VpnProfile insertProfile(VpnProfile profile)
|
||||
{
|
||||
ContentValues values = ContentValuesFromVpnProfile(profile);
|
||||
long insertId = mDatabase.insert(TABLE_VPNPROFILE, null, values);
|
||||
if (insertId == -1)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
profile.setId(insertId);
|
||||
return profile;
|
||||
}
|
||||
VpnProfile insertProfile(VpnProfile profile);
|
||||
|
||||
/**
|
||||
* Updates the given VPN profile in the database.
|
||||
*
|
||||
* @param profile the profile to update
|
||||
* @return true if update succeeded, false otherwise
|
||||
*/
|
||||
public boolean updateVpnProfile(VpnProfile profile)
|
||||
{
|
||||
long id = profile.getId();
|
||||
ContentValues values = ContentValuesFromVpnProfile(profile);
|
||||
return mDatabase.update(TABLE_VPNPROFILE, values, KEY_ID + " = " + id, null) > 0;
|
||||
}
|
||||
boolean updateVpnProfile(VpnProfile profile);
|
||||
|
||||
/**
|
||||
* Delete the given VPN profile from the database.
|
||||
*
|
||||
* @param profile the profile to delete
|
||||
* @return true if deleted, false otherwise
|
||||
*/
|
||||
public boolean deleteVpnProfile(VpnProfile profile)
|
||||
{
|
||||
long id = profile.getId();
|
||||
return mDatabase.delete(TABLE_VPNPROFILE, KEY_ID + " = " + id, null) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a single VPN profile from the database.
|
||||
* @param id the ID of the VPN profile
|
||||
* @return the profile or null, if not found
|
||||
*/
|
||||
public VpnProfile getVpnProfile(long id)
|
||||
{
|
||||
VpnProfile profile = null;
|
||||
Cursor cursor = mDatabase.query(TABLE_VPNPROFILE, ALL_COLUMNS,
|
||||
KEY_ID + "=" + id, null, null, null, null);
|
||||
if (cursor.moveToFirst())
|
||||
{
|
||||
profile = VpnProfileFromCursor(cursor);
|
||||
}
|
||||
cursor.close();
|
||||
return profile;
|
||||
}
|
||||
boolean deleteVpnProfile(VpnProfile profile);
|
||||
|
||||
/**
|
||||
* Get a single VPN profile from the database by its UUID.
|
||||
*
|
||||
* @param uuid the UUID of the VPN profile
|
||||
* @return the profile or null, if not found
|
||||
*/
|
||||
public VpnProfile getVpnProfile(UUID uuid)
|
||||
{
|
||||
VpnProfile profile = null;
|
||||
Cursor cursor = mDatabase.query(TABLE_VPNPROFILE, ALL_COLUMNS,
|
||||
KEY_UUID + "='" + uuid.toString() + "'", null, null, null, null);
|
||||
if (cursor.moveToFirst())
|
||||
{
|
||||
profile = VpnProfileFromCursor(cursor);
|
||||
}
|
||||
cursor.close();
|
||||
return profile;
|
||||
}
|
||||
VpnProfile getVpnProfile(UUID uuid);
|
||||
|
||||
/**
|
||||
* Get a single VPN profile from the database by its UUID as String.
|
||||
*
|
||||
* @param uuid the UUID of the VPN profile as String
|
||||
* @return the profile or null, if not found
|
||||
*/
|
||||
public VpnProfile getVpnProfile(String uuid)
|
||||
default VpnProfile getVpnProfile(String uuid)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -413,97 +124,8 @@ public class VpnProfileDataSource
|
||||
|
||||
/**
|
||||
* Get a list of all VPN profiles stored in the database.
|
||||
*
|
||||
* @return list of VPN profiles
|
||||
*/
|
||||
public List<VpnProfile> getAllVpnProfiles()
|
||||
{
|
||||
List<VpnProfile> vpnProfiles = new ArrayList<VpnProfile>();
|
||||
|
||||
Cursor cursor = mDatabase.query(TABLE_VPNPROFILE, ALL_COLUMNS, null, null, null, null, null);
|
||||
cursor.moveToFirst();
|
||||
while (!cursor.isAfterLast())
|
||||
{
|
||||
VpnProfile vpnProfile = VpnProfileFromCursor(cursor);
|
||||
vpnProfiles.add(vpnProfile);
|
||||
cursor.moveToNext();
|
||||
}
|
||||
cursor.close();
|
||||
return vpnProfiles;
|
||||
}
|
||||
|
||||
private VpnProfile VpnProfileFromCursor(Cursor cursor)
|
||||
{
|
||||
VpnProfile profile = new VpnProfile();
|
||||
profile.setId(cursor.getLong(cursor.getColumnIndexOrThrow(KEY_ID)));
|
||||
profile.setUUID(UUID.fromString(cursor.getString(cursor.getColumnIndexOrThrow(KEY_UUID))));
|
||||
profile.setName(cursor.getString(cursor.getColumnIndexOrThrow(KEY_NAME)));
|
||||
profile.setGateway(cursor.getString(cursor.getColumnIndexOrThrow(KEY_GATEWAY)));
|
||||
profile.setVpnType(VpnType.fromIdentifier(cursor.getString(cursor.getColumnIndexOrThrow(KEY_VPN_TYPE))));
|
||||
profile.setUsername(cursor.getString(cursor.getColumnIndexOrThrow(KEY_USERNAME)));
|
||||
profile.setPassword(cursor.getString(cursor.getColumnIndexOrThrow(KEY_PASSWORD)));
|
||||
profile.setCertificateAlias(cursor.getString(cursor.getColumnIndexOrThrow(KEY_CERTIFICATE)));
|
||||
profile.setUserCertificateAlias(cursor.getString(cursor.getColumnIndexOrThrow(KEY_USER_CERTIFICATE)));
|
||||
profile.setMTU(getInt(cursor, cursor.getColumnIndexOrThrow(KEY_MTU)));
|
||||
profile.setPort(getInt(cursor, cursor.getColumnIndexOrThrow(KEY_PORT)));
|
||||
profile.setSplitTunneling(getInt(cursor, cursor.getColumnIndexOrThrow(KEY_SPLIT_TUNNELING)));
|
||||
profile.setLocalId(cursor.getString(cursor.getColumnIndexOrThrow(KEY_LOCAL_ID)));
|
||||
profile.setRemoteId(cursor.getString(cursor.getColumnIndexOrThrow(KEY_REMOTE_ID)));
|
||||
profile.setExcludedSubnets(cursor.getString(cursor.getColumnIndexOrThrow(KEY_EXCLUDED_SUBNETS)));
|
||||
profile.setIncludedSubnets(cursor.getString(cursor.getColumnIndexOrThrow(KEY_INCLUDED_SUBNETS)));
|
||||
profile.setSelectedAppsHandling(getInt(cursor, cursor.getColumnIndexOrThrow(KEY_SELECTED_APPS)));
|
||||
profile.setSelectedApps(cursor.getString(cursor.getColumnIndexOrThrow(KEY_SELECTED_APPS_LIST)));
|
||||
profile.setNATKeepAlive(getInt(cursor, cursor.getColumnIndexOrThrow(KEY_NAT_KEEPALIVE)));
|
||||
profile.setFlags(getInt(cursor, cursor.getColumnIndexOrThrow(KEY_FLAGS)));
|
||||
profile.setIkeProposal(cursor.getString(cursor.getColumnIndexOrThrow(KEY_IKE_PROPOSAL)));
|
||||
profile.setEspProposal(cursor.getString(cursor.getColumnIndexOrThrow(KEY_ESP_PROPOSAL)));
|
||||
profile.setDnsServers(cursor.getString(cursor.getColumnIndexOrThrow(KEY_DNS_SERVERS)));
|
||||
return profile;
|
||||
}
|
||||
|
||||
private ContentValues ContentValuesFromVpnProfile(VpnProfile profile)
|
||||
{
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(KEY_UUID, profile.getUUID().toString());
|
||||
values.put(KEY_NAME, profile.getName());
|
||||
values.put(KEY_GATEWAY, profile.getGateway());
|
||||
values.put(KEY_VPN_TYPE, profile.getVpnType().getIdentifier());
|
||||
values.put(KEY_USERNAME, profile.getUsername());
|
||||
values.put(KEY_PASSWORD, profile.getPassword());
|
||||
values.put(KEY_CERTIFICATE, profile.getCertificateAlias());
|
||||
values.put(KEY_USER_CERTIFICATE, profile.getUserCertificateAlias());
|
||||
values.put(KEY_MTU, profile.getMTU());
|
||||
values.put(KEY_PORT, profile.getPort());
|
||||
values.put(KEY_SPLIT_TUNNELING, profile.getSplitTunneling());
|
||||
values.put(KEY_LOCAL_ID, profile.getLocalId());
|
||||
values.put(KEY_REMOTE_ID, profile.getRemoteId());
|
||||
values.put(KEY_EXCLUDED_SUBNETS, profile.getExcludedSubnets());
|
||||
values.put(KEY_INCLUDED_SUBNETS, profile.getIncludedSubnets());
|
||||
values.put(KEY_SELECTED_APPS, profile.getSelectedAppsHandling().getValue());
|
||||
values.put(KEY_SELECTED_APPS_LIST, profile.getSelectedApps());
|
||||
values.put(KEY_NAT_KEEPALIVE, profile.getNATKeepAlive());
|
||||
values.put(KEY_FLAGS, profile.getFlags());
|
||||
values.put(KEY_IKE_PROPOSAL, profile.getIkeProposal());
|
||||
values.put(KEY_ESP_PROPOSAL, profile.getEspProposal());
|
||||
values.put(KEY_DNS_SERVERS, profile.getDnsServers());
|
||||
return values;
|
||||
}
|
||||
|
||||
private Integer getInt(Cursor cursor, int columnIndex)
|
||||
{
|
||||
return cursor.isNull(columnIndex) ? null : cursor.getInt(columnIndex);
|
||||
}
|
||||
|
||||
private static class DbColumn
|
||||
{
|
||||
public final String Name;
|
||||
public final String Type;
|
||||
public final Integer Since;
|
||||
|
||||
public DbColumn(String name, String type, Integer since)
|
||||
{
|
||||
Name = name;
|
||||
Type = type;
|
||||
Since = since;
|
||||
}
|
||||
}
|
||||
List<VpnProfile> getAllVpnProfiles();
|
||||
}
|
||||
|
||||
+118
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Relution GmbH
|
||||
*
|
||||
* Copyright (C) secunet Security Networks AG
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*/
|
||||
|
||||
package org.strongswan.android.data;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.database.SQLException;
|
||||
|
||||
import org.strongswan.android.logic.StrongSwanApplication;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
public class VpnProfileManagedDataSource implements VpnProfileDataSource
|
||||
{
|
||||
private static final String NAME_MANAGED_VPN_PROFILES = "org.strongswan.android.data.VpnProfileManagedDataSource.preferences";
|
||||
|
||||
private final ManagedConfigurationService mManagedConfigurationService;
|
||||
private final SharedPreferences mSharedPreferences;
|
||||
|
||||
public VpnProfileManagedDataSource(final Context context)
|
||||
{
|
||||
this.mManagedConfigurationService = StrongSwanApplication.getInstance().getManagedConfigurationService();
|
||||
this.mSharedPreferences = context.getSharedPreferences(NAME_MANAGED_VPN_PROFILES, Context.MODE_PRIVATE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public VpnProfileDataSource open() throws SQLException
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close()
|
||||
{
|
||||
/* remove passwords that are no longer referenced by a VPN profile */
|
||||
final Set<String> actualKeys = mManagedConfigurationService.getManagedProfiles().keySet();
|
||||
|
||||
final Set<String> storedKeys = new HashSet<>(mSharedPreferences.getAll().keySet());
|
||||
storedKeys.removeAll(actualKeys);
|
||||
|
||||
final SharedPreferences.Editor editor = mSharedPreferences.edit();
|
||||
for (String key : storedKeys)
|
||||
{
|
||||
editor.remove(key);
|
||||
}
|
||||
|
||||
editor.apply();
|
||||
}
|
||||
|
||||
@Override
|
||||
public VpnProfile insertProfile(VpnProfile profile)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean updateVpnProfile(VpnProfile profile)
|
||||
{
|
||||
final VpnProfile existingProfile = getVpnProfile(profile.getUUID());
|
||||
if (existingProfile == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
final String password = profile.getPassword();
|
||||
existingProfile.setPassword(password);
|
||||
|
||||
final SharedPreferences.Editor editor = mSharedPreferences.edit();
|
||||
editor.putString(profile.getUUID().toString(), password);
|
||||
return editor.commit();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean deleteVpnProfile(VpnProfile profile)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public VpnProfile getVpnProfile(UUID uuid)
|
||||
{
|
||||
return mManagedConfigurationService.getManagedProfiles().get(uuid.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<VpnProfile> getAllVpnProfiles()
|
||||
{
|
||||
final Map<String, ManagedVpnProfile> managedVpnProfiles = mManagedConfigurationService.getManagedProfiles();
|
||||
final List<VpnProfile> vpnProfiles = new ArrayList<>();
|
||||
for (final VpnProfile vpnProfile : managedVpnProfiles.values())
|
||||
{
|
||||
final String password = mSharedPreferences.getString(vpnProfile.getUUID().toString(), vpnProfile.getPassword());
|
||||
vpnProfile.setPassword(password);
|
||||
vpnProfile.setDataSource(this);
|
||||
vpnProfiles.add(vpnProfile);
|
||||
}
|
||||
return vpnProfiles;
|
||||
}
|
||||
}
|
||||
+118
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Relution GmbH
|
||||
*
|
||||
* Copyright (C) secunet Security Networks AG
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*/
|
||||
|
||||
package org.strongswan.android.data;
|
||||
|
||||
import android.content.Context;
|
||||
import android.database.SQLException;
|
||||
|
||||
import org.strongswan.android.logic.StrongSwanApplication;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
public class VpnProfileSource implements VpnProfileDataSource
|
||||
{
|
||||
private final ManagedConfigurationService mManagedConfigurationService;
|
||||
private final List<VpnProfileDataSource> dataSources = new ArrayList<>();
|
||||
private final VpnProfileSqlDataSource vpnProfileSqlDataSource;
|
||||
|
||||
public VpnProfileSource(Context context)
|
||||
{
|
||||
mManagedConfigurationService = StrongSwanApplication.getInstance().getManagedConfigurationService();
|
||||
|
||||
vpnProfileSqlDataSource = new VpnProfileSqlDataSource();
|
||||
|
||||
dataSources.add(vpnProfileSqlDataSource);
|
||||
dataSources.add(new VpnProfileManagedDataSource(context));
|
||||
}
|
||||
|
||||
@Override
|
||||
public VpnProfileDataSource open() throws SQLException
|
||||
{
|
||||
for (final VpnProfileDataSource source : dataSources)
|
||||
{
|
||||
source.open();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close()
|
||||
{
|
||||
for (final VpnProfileDataSource source : dataSources)
|
||||
{
|
||||
source.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public VpnProfile insertProfile(VpnProfile profile)
|
||||
{
|
||||
return vpnProfileSqlDataSource.insertProfile(profile);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean updateVpnProfile(VpnProfile profile)
|
||||
{
|
||||
return profile.getDataSource().updateVpnProfile(profile);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean deleteVpnProfile(VpnProfile profile)
|
||||
{
|
||||
return profile.getDataSource().deleteVpnProfile(profile);
|
||||
}
|
||||
|
||||
private List<VpnProfileDataSource> getAccessibleSources()
|
||||
{
|
||||
final ManagedConfiguration managedConfiguration = mManagedConfigurationService.getManagedConfiguration();
|
||||
ArrayList<VpnProfileDataSource> sources = new ArrayList<>(dataSources);
|
||||
if (!managedConfiguration.isAllowExistingProfiles())
|
||||
{
|
||||
sources.remove(vpnProfileSqlDataSource);
|
||||
}
|
||||
return sources;
|
||||
}
|
||||
|
||||
@Override
|
||||
public VpnProfile getVpnProfile(UUID uuid)
|
||||
{
|
||||
for (final VpnProfileDataSource source : getAccessibleSources())
|
||||
{
|
||||
final VpnProfile profile = source.getVpnProfile(uuid);
|
||||
if (profile != null)
|
||||
{
|
||||
return profile;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<VpnProfile> getAllVpnProfiles()
|
||||
{
|
||||
final List<VpnProfile> profiles = new ArrayList<>();
|
||||
|
||||
for (final VpnProfileDataSource source : getAccessibleSources())
|
||||
{
|
||||
profiles.addAll(source.getAllVpnProfiles());
|
||||
}
|
||||
|
||||
return profiles;
|
||||
}
|
||||
}
|
||||
+189
@@ -0,0 +1,189 @@
|
||||
/*
|
||||
* Copyright (C) 2012-2019 Tobias Brunner
|
||||
* Copyright (C) 2012 Giuliano Grassi
|
||||
* Copyright (C) 2012 Ralf Sager
|
||||
*
|
||||
* Copyright (C) secunet Security Networks AG
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*/
|
||||
|
||||
package org.strongswan.android.data;
|
||||
|
||||
import android.content.ContentValues;
|
||||
import android.database.Cursor;
|
||||
import android.database.SQLException;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
|
||||
import org.strongswan.android.logic.StrongSwanApplication;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
public class VpnProfileSqlDataSource implements VpnProfileDataSource
|
||||
{
|
||||
private final DatabaseHelper mDbHelper;
|
||||
|
||||
private SQLiteDatabase mDatabase;
|
||||
|
||||
/**
|
||||
* Construct a new VPN profile data source. The context is used to
|
||||
* open/create the database.
|
||||
*/
|
||||
public VpnProfileSqlDataSource()
|
||||
{
|
||||
mDbHelper = StrongSwanApplication.getInstance().getDatabaseHelper();
|
||||
}
|
||||
|
||||
@Override
|
||||
public VpnProfileDataSource open() throws SQLException
|
||||
{
|
||||
if (mDatabase == null)
|
||||
{
|
||||
mDatabase = mDbHelper.getWritableDatabase();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close()
|
||||
{
|
||||
if (mDatabase != null)
|
||||
{
|
||||
mDatabase = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public VpnProfile insertProfile(VpnProfile profile)
|
||||
{
|
||||
ContentValues values = ContentValuesFromVpnProfile(profile);
|
||||
long insertId = mDatabase.insert(DatabaseHelper.TABLE_VPN_PROFILE.Name, null, values);
|
||||
if (insertId == -1)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
profile.setDataSource(this);
|
||||
profile.setId(insertId);
|
||||
return profile;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean updateVpnProfile(VpnProfile profile)
|
||||
{
|
||||
final UUID uuid = profile.getUUID();
|
||||
ContentValues values = ContentValuesFromVpnProfile(profile);
|
||||
return mDatabase.update(DatabaseHelper.TABLE_VPN_PROFILE.Name, values, KEY_UUID + " = ?", new String[]{uuid.toString()}) > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean deleteVpnProfile(VpnProfile profile)
|
||||
{
|
||||
final UUID uuid = profile.getUUID();
|
||||
return mDatabase.delete(DatabaseHelper.TABLE_VPN_PROFILE.Name, KEY_UUID + " = ?", new String[]{uuid.toString()}) > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public VpnProfile getVpnProfile(UUID uuid)
|
||||
{
|
||||
VpnProfile profile = null;
|
||||
DatabaseHelper.DbTable table = DatabaseHelper.TABLE_VPN_PROFILE;
|
||||
Cursor cursor = mDatabase.query(table.Name, table.columnNames(), KEY_UUID + " = ?", new String[]{uuid.toString()}, null, null, null);
|
||||
if (cursor.moveToFirst())
|
||||
{
|
||||
profile = VpnProfileFromCursor(cursor);
|
||||
profile.setDataSource(this);
|
||||
}
|
||||
cursor.close();
|
||||
return profile;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<VpnProfile> getAllVpnProfiles()
|
||||
{
|
||||
List<VpnProfile> vpnProfiles = new ArrayList<>();
|
||||
|
||||
DatabaseHelper.DbTable table = DatabaseHelper.TABLE_VPN_PROFILE;
|
||||
Cursor cursor = mDatabase.query(table.Name, table.columnNames(), null, null, null, null, null);
|
||||
cursor.moveToFirst();
|
||||
while (!cursor.isAfterLast())
|
||||
{
|
||||
VpnProfile vpnProfile = VpnProfileFromCursor(cursor);
|
||||
vpnProfile.setDataSource(this);
|
||||
vpnProfiles.add(vpnProfile);
|
||||
cursor.moveToNext();
|
||||
}
|
||||
cursor.close();
|
||||
return vpnProfiles;
|
||||
}
|
||||
|
||||
private VpnProfile VpnProfileFromCursor(Cursor cursor)
|
||||
{
|
||||
VpnProfile profile = new VpnProfile();
|
||||
profile.setUUID(UUID.fromString(cursor.getString(cursor.getColumnIndexOrThrow(KEY_UUID))));
|
||||
profile.setName(cursor.getString(cursor.getColumnIndexOrThrow(KEY_NAME)));
|
||||
profile.setGateway(cursor.getString(cursor.getColumnIndexOrThrow(KEY_GATEWAY)));
|
||||
profile.setVpnType(VpnType.fromIdentifier(cursor.getString(cursor.getColumnIndexOrThrow(KEY_VPN_TYPE))));
|
||||
profile.setUsername(cursor.getString(cursor.getColumnIndexOrThrow(KEY_USERNAME)));
|
||||
profile.setPassword(cursor.getString(cursor.getColumnIndexOrThrow(KEY_PASSWORD)));
|
||||
profile.setCertificateAlias(cursor.getString(cursor.getColumnIndexOrThrow(KEY_CERTIFICATE)));
|
||||
profile.setUserCertificateAlias(cursor.getString(cursor.getColumnIndexOrThrow(KEY_USER_CERTIFICATE)));
|
||||
profile.setMTU(getInt(cursor, cursor.getColumnIndexOrThrow(KEY_MTU)));
|
||||
profile.setPort(getInt(cursor, cursor.getColumnIndexOrThrow(KEY_PORT)));
|
||||
profile.setSplitTunneling(getInt(cursor, cursor.getColumnIndexOrThrow(KEY_SPLIT_TUNNELING)));
|
||||
profile.setLocalId(cursor.getString(cursor.getColumnIndexOrThrow(KEY_LOCAL_ID)));
|
||||
profile.setRemoteId(cursor.getString(cursor.getColumnIndexOrThrow(KEY_REMOTE_ID)));
|
||||
profile.setExcludedSubnets(cursor.getString(cursor.getColumnIndexOrThrow(KEY_EXCLUDED_SUBNETS)));
|
||||
profile.setIncludedSubnets(cursor.getString(cursor.getColumnIndexOrThrow(KEY_INCLUDED_SUBNETS)));
|
||||
profile.setSelectedAppsHandling(getInt(cursor, cursor.getColumnIndexOrThrow(KEY_SELECTED_APPS)));
|
||||
profile.setSelectedApps(cursor.getString(cursor.getColumnIndexOrThrow(KEY_SELECTED_APPS_LIST)));
|
||||
profile.setNATKeepAlive(getInt(cursor, cursor.getColumnIndexOrThrow(KEY_NAT_KEEPALIVE)));
|
||||
profile.setFlags(getInt(cursor, cursor.getColumnIndexOrThrow(KEY_FLAGS)));
|
||||
profile.setIkeProposal(cursor.getString(cursor.getColumnIndexOrThrow(KEY_IKE_PROPOSAL)));
|
||||
profile.setEspProposal(cursor.getString(cursor.getColumnIndexOrThrow(KEY_ESP_PROPOSAL)));
|
||||
profile.setDnsServers(cursor.getString(cursor.getColumnIndexOrThrow(KEY_DNS_SERVERS)));
|
||||
return profile;
|
||||
}
|
||||
|
||||
private ContentValues ContentValuesFromVpnProfile(VpnProfile profile)
|
||||
{
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(KEY_UUID, profile.getUUID().toString());
|
||||
values.put(KEY_NAME, profile.getName());
|
||||
values.put(KEY_GATEWAY, profile.getGateway());
|
||||
values.put(KEY_VPN_TYPE, profile.getVpnType().getIdentifier());
|
||||
values.put(KEY_USERNAME, profile.getUsername());
|
||||
values.put(KEY_PASSWORD, profile.getPassword());
|
||||
values.put(KEY_CERTIFICATE, profile.getCertificateAlias());
|
||||
values.put(KEY_USER_CERTIFICATE, profile.getUserCertificateAlias());
|
||||
values.put(KEY_MTU, profile.getMTU());
|
||||
values.put(KEY_PORT, profile.getPort());
|
||||
values.put(KEY_SPLIT_TUNNELING, profile.getSplitTunneling());
|
||||
values.put(KEY_LOCAL_ID, profile.getLocalId());
|
||||
values.put(KEY_REMOTE_ID, profile.getRemoteId());
|
||||
values.put(KEY_EXCLUDED_SUBNETS, profile.getExcludedSubnets());
|
||||
values.put(KEY_INCLUDED_SUBNETS, profile.getIncludedSubnets());
|
||||
values.put(KEY_SELECTED_APPS, profile.getSelectedAppsHandling().getValue());
|
||||
values.put(KEY_SELECTED_APPS_LIST, profile.getSelectedApps());
|
||||
values.put(KEY_NAT_KEEPALIVE, profile.getNATKeepAlive());
|
||||
values.put(KEY_FLAGS, profile.getFlags());
|
||||
values.put(KEY_IKE_PROPOSAL, profile.getIkeProposal());
|
||||
values.put(KEY_ESP_PROPOSAL, profile.getEspProposal());
|
||||
values.put(KEY_DNS_SERVERS, profile.getDnsServers());
|
||||
return values;
|
||||
}
|
||||
|
||||
private Integer getInt(Cursor cursor, int columnIndex)
|
||||
{
|
||||
return cursor.isNull(columnIndex) ? null : cursor.getInt(columnIndex);
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,10 @@
|
||||
package org.strongswan.android.data;
|
||||
|
||||
import java.util.EnumSet;
|
||||
import java.util.Objects;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
public enum VpnType
|
||||
{
|
||||
@@ -32,11 +36,19 @@ public enum VpnType
|
||||
*/
|
||||
public enum VpnTypeFeature
|
||||
{
|
||||
/** client certificate is required */
|
||||
/**
|
||||
* Client certificate is required
|
||||
*/
|
||||
CERTIFICATE,
|
||||
/** username and password are required */
|
||||
|
||||
/**
|
||||
* Username and password are required
|
||||
*/
|
||||
USER_PASS,
|
||||
/** enable BYOD features */
|
||||
|
||||
/**
|
||||
* Enable BYOD features
|
||||
*/
|
||||
BYOD;
|
||||
}
|
||||
|
||||
@@ -48,7 +60,6 @@ public enum VpnType
|
||||
*
|
||||
* @param id identifier used to store and transmit this specific type
|
||||
* @param features of the given VPN type
|
||||
* @param certificate true if a client certificate is required
|
||||
*/
|
||||
VpnType(String id, EnumSet<VpnTypeFeature> features)
|
||||
{
|
||||
@@ -58,6 +69,7 @@ public enum VpnType
|
||||
|
||||
/**
|
||||
* The identifier used to store this value in the database
|
||||
*
|
||||
* @return identifier
|
||||
*/
|
||||
public String getIdentifier()
|
||||
@@ -81,11 +93,12 @@ public enum VpnType
|
||||
* @param identifier get the enum entry with this identifier
|
||||
* @return the enum entry, or the default if not found
|
||||
*/
|
||||
public static VpnType fromIdentifier(String identifier)
|
||||
@NonNull
|
||||
public static VpnType fromIdentifier(@Nullable String identifier)
|
||||
{
|
||||
for (VpnType type : VpnType.values())
|
||||
{
|
||||
if (identifier.equals(type.mIdentifier))
|
||||
if (Objects.equals(identifier, type.mIdentifier))
|
||||
{
|
||||
return type;
|
||||
}
|
||||
|
||||
+21
-25
@@ -45,6 +45,7 @@ import org.strongswan.android.R;
|
||||
import org.strongswan.android.data.VpnProfile;
|
||||
import org.strongswan.android.data.VpnProfile.SelectedAppsHandling;
|
||||
import org.strongswan.android.data.VpnProfileDataSource;
|
||||
import org.strongswan.android.data.VpnProfileSource;
|
||||
import org.strongswan.android.data.VpnType.VpnTypeFeature;
|
||||
import org.strongswan.android.logic.VpnStateService.ErrorState;
|
||||
import org.strongswan.android.logic.VpnStateService.State;
|
||||
@@ -101,11 +102,12 @@ public class CharonVpnService extends VpnService implements Runnable, VpnStateSe
|
||||
private volatile boolean mTerminate;
|
||||
private volatile boolean mIsDisconnecting;
|
||||
private volatile boolean mShowNotification;
|
||||
private BuilderAdapter mBuilderAdapter = new BuilderAdapter();
|
||||
private final BuilderAdapter mBuilderAdapter = new BuilderAdapter();
|
||||
private Handler mHandler;
|
||||
private VpnStateService mService;
|
||||
private final Object mServiceLock = new Object();
|
||||
private final ServiceConnection mServiceConnection = new ServiceConnection() {
|
||||
private final ServiceConnection mServiceConnection = new ServiceConnection()
|
||||
{
|
||||
@Override
|
||||
public void onServiceDisconnected(ComponentName name)
|
||||
{ /* since the service is local this is theoretically only called when the process is terminated */
|
||||
@@ -195,7 +197,7 @@ public class CharonVpnService extends VpnService implements Runnable, VpnStateSe
|
||||
/* handler used to do changes in the main UI thread */
|
||||
mHandler = new Handler(getMainLooper());
|
||||
|
||||
mDataSource = new VpnProfileDataSource(this);
|
||||
mDataSource = new VpnProfileSource(this);
|
||||
mDataSource.open();
|
||||
/* use a separate thread as main thread for charon */
|
||||
mConnectionHandler = new Thread(this);
|
||||
@@ -346,7 +348,7 @@ public class CharonVpnService extends VpnService implements Runnable, VpnStateSe
|
||||
{
|
||||
synchronized (this)
|
||||
{
|
||||
if (mNextProfile != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
|
||||
if (mNextProfile != null)
|
||||
{
|
||||
mBuilderAdapter.setProfile(mNextProfile);
|
||||
mBuilderAdapter.establishBlocking();
|
||||
@@ -437,10 +439,10 @@ public class CharonVpnService extends VpnService implements Runnable, VpnStateSe
|
||||
name = profile.getName();
|
||||
}
|
||||
NotificationCompat.Builder builder = new NotificationCompat.Builder(this, NOTIFICATION_CHANNEL)
|
||||
.setSmallIcon(R.drawable.ic_notification)
|
||||
.setCategory(NotificationCompat.CATEGORY_SERVICE)
|
||||
.setVisibility(publicVersion ? NotificationCompat.VISIBILITY_PUBLIC
|
||||
: NotificationCompat.VISIBILITY_PRIVATE);
|
||||
.setSmallIcon(R.drawable.ic_notification)
|
||||
.setCategory(NotificationCompat.CATEGORY_SERVICE)
|
||||
.setVisibility(publicVersion ? NotificationCompat.VISIBILITY_PUBLIC
|
||||
: NotificationCompat.VISIBILITY_PRIVATE);
|
||||
int s = R.string.state_disabled;
|
||||
if (error != ErrorState.NO_ERROR)
|
||||
{
|
||||
@@ -460,7 +462,7 @@ public class CharonVpnService extends VpnService implements Runnable, VpnStateSe
|
||||
Intent intent = new Intent(getApplicationContext(), VpnProfileControlActivity.class);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
intent.setAction(VpnProfileControlActivity.START_PROFILE);
|
||||
intent.putExtra(VpnProfileControlActivity.EXTRA_VPN_PROFILE_ID, profile.getUUID().toString());
|
||||
intent.putExtra(VpnProfileControlActivity.EXTRA_VPN_PROFILE_UUID, profile.getUUID().toString());
|
||||
int flags = PendingIntent.FLAG_UPDATE_CURRENT;
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
|
||||
{
|
||||
@@ -527,10 +529,11 @@ public class CharonVpnService extends VpnService implements Runnable, VpnStateSe
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stateChanged() {
|
||||
public void stateChanged()
|
||||
{
|
||||
if (mShowNotification)
|
||||
{
|
||||
NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
NotificationManager manager = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
manager.notify(VPN_STATE_NOTIFICATION_ID, buildNotification(false));
|
||||
}
|
||||
}
|
||||
@@ -813,7 +816,7 @@ public class CharonVpnService extends VpnService implements Runnable, VpnStateSe
|
||||
private VpnService.Builder mBuilder;
|
||||
private BuilderCache mCache;
|
||||
private BuilderCache mEstablishedCache;
|
||||
private PacketDropper mDropper = new PacketDropper();
|
||||
private final PacketDropper mDropper = new PacketDropper();
|
||||
|
||||
public synchronized void setProfile(VpnProfile profile)
|
||||
{
|
||||
@@ -1035,9 +1038,8 @@ public class CharonVpnService extends VpnService implements Runnable, VpnStateSe
|
||||
@Override
|
||||
public synchronized void run()
|
||||
{
|
||||
try
|
||||
try (FileInputStream plain = new FileInputStream(mFd.getFileDescriptor()))
|
||||
{
|
||||
FileInputStream plain = new FileInputStream(mFd.getFileDescriptor());
|
||||
ByteBuffer packet = ByteBuffer.allocate(mCache.mMtu);
|
||||
while (true)
|
||||
{
|
||||
@@ -1071,7 +1073,7 @@ public class CharonVpnService extends VpnService implements Runnable, VpnStateSe
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (ClosedByInterruptException|InterruptedException e)
|
||||
catch (final ClosedByInterruptException | InterruptedException e)
|
||||
{
|
||||
/* regular interruption */
|
||||
}
|
||||
@@ -1234,7 +1236,6 @@ public class CharonVpnService extends VpnService implements Runnable, VpnStateSe
|
||||
}
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
|
||||
public void applyData(VpnService.Builder builder)
|
||||
{
|
||||
for (IPRange address : mAddresses)
|
||||
@@ -1277,7 +1278,7 @@ public class CharonVpnService extends VpnService implements Runnable, VpnStateSe
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
|
||||
else
|
||||
{ /* allow traffic that would otherwise be blocked to bypass the VPN */
|
||||
builder.allowFamily(OsConstants.AF_INET);
|
||||
}
|
||||
@@ -1317,7 +1318,7 @@ public class CharonVpnService extends VpnService implements Runnable, VpnStateSe
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
|
||||
else
|
||||
{
|
||||
builder.allowFamily(OsConstants.AF_INET6);
|
||||
}
|
||||
@@ -1327,8 +1328,7 @@ public class CharonVpnService extends VpnService implements Runnable, VpnStateSe
|
||||
builder.addRoute("::", 0);
|
||||
}
|
||||
/* apply selected applications */
|
||||
if (mSelectedApps.size() > 0 &&
|
||||
Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
|
||||
if (mSelectedApps.size() > 0)
|
||||
{
|
||||
switch (mAppHandling)
|
||||
{
|
||||
@@ -1372,11 +1372,7 @@ public class CharonVpnService extends VpnService implements Runnable, VpnStateSe
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (addr instanceof Inet6Address)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
return addr instanceof Inet6Address;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+87
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Relution GmbH
|
||||
*
|
||||
* Copyright (C) secunet Security Networks AG
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*/
|
||||
|
||||
package org.strongswan.android.logic;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.Log;
|
||||
|
||||
import org.strongswan.android.data.ManagedTrustedCertificate;
|
||||
import org.strongswan.android.utils.Certificates;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.security.KeyStore;
|
||||
import java.security.KeyStoreException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.security.cert.X509Certificate;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
public class ManagedTrustedCertificateInstaller
|
||||
{
|
||||
private static final String TAG = ManagedTrustedCertificateInstaller.class.getSimpleName();
|
||||
|
||||
public ManagedTrustedCertificateInstaller(@NonNull final Context context)
|
||||
{
|
||||
}
|
||||
|
||||
private boolean installTrustedCert(@NonNull ManagedTrustedCertificate trustedCertificate)
|
||||
throws CertificateException, IOException, KeyStoreException, NoSuchAlgorithmException
|
||||
{
|
||||
Log.d(TAG, "Install trusted certificate " + trustedCertificate);
|
||||
final X509Certificate certificate = Certificates.from(trustedCertificate.getData());
|
||||
|
||||
KeyStore store = KeyStore.getInstance("LocalCertificateStore");
|
||||
store.load(null, null);
|
||||
store.setCertificateEntry(trustedCertificate.getAlias(), certificate);
|
||||
return true;
|
||||
}
|
||||
|
||||
private void uninstallTrustedCert(@NonNull ManagedTrustedCertificate trustedCertificate)
|
||||
throws CertificateException, IOException, NoSuchAlgorithmException, KeyStoreException
|
||||
{
|
||||
Log.d(TAG, "Remove trusted certificate " + trustedCertificate);
|
||||
KeyStore store = KeyStore.getInstance("LocalCertificateStore");
|
||||
store.load(null, null);
|
||||
store.deleteEntry(trustedCertificate.getAlias());
|
||||
}
|
||||
|
||||
public synchronized boolean tryInstall(@NonNull ManagedTrustedCertificate trustedCertificate)
|
||||
{
|
||||
try
|
||||
{
|
||||
return installTrustedCert(trustedCertificate);
|
||||
}
|
||||
catch (final Exception e)
|
||||
{
|
||||
Log.e(TAG, "Could not install trusted certificate " + trustedCertificate, e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void tryRemove(@NonNull ManagedTrustedCertificate trustedCertificate)
|
||||
{
|
||||
try
|
||||
{
|
||||
uninstallTrustedCert(trustedCertificate);
|
||||
}
|
||||
catch (final Exception e)
|
||||
{
|
||||
Log.e(TAG, "Could not remove trusted certificate " + trustedCertificate, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
+125
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Relution GmbH
|
||||
*
|
||||
* Copyright (C) secunet Security Networks AG
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*/
|
||||
|
||||
package org.strongswan.android.logic;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Handler;
|
||||
import android.util.Log;
|
||||
|
||||
import org.strongswan.android.data.DatabaseHelper;
|
||||
import org.strongswan.android.data.ManagedConfigurationService;
|
||||
import org.strongswan.android.data.ManagedTrustedCertificate;
|
||||
import org.strongswan.android.data.ManagedTrustedCertificateRepository;
|
||||
import org.strongswan.android.utils.Difference;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.util.Pair;
|
||||
|
||||
public class ManagedTrustedCertificateManager
|
||||
{
|
||||
private static final String TAG = ManagedTrustedCertificateManager.class.getSimpleName();
|
||||
|
||||
@NonNull
|
||||
private final ExecutorService executorService;
|
||||
@NonNull
|
||||
private final Handler handler;
|
||||
|
||||
@NonNull
|
||||
private final ManagedTrustedCertificateRepository certificateRepository;
|
||||
@NonNull
|
||||
private final ManagedTrustedCertificateInstaller certificateInstaller;
|
||||
|
||||
public ManagedTrustedCertificateManager(
|
||||
@NonNull final Context context,
|
||||
@NonNull final ExecutorService executorService,
|
||||
@NonNull final Handler handler,
|
||||
@NonNull final ManagedConfigurationService managedConfigurationService,
|
||||
@NonNull final DatabaseHelper databaseHelper)
|
||||
{
|
||||
this.executorService = executorService;
|
||||
this.handler = handler;
|
||||
|
||||
this.certificateRepository = new ManagedTrustedCertificateRepository(managedConfigurationService, databaseHelper);
|
||||
this.certificateInstaller = new ManagedTrustedCertificateInstaller(context);
|
||||
}
|
||||
|
||||
public void update(@NonNull final Runnable onUpdateCompleted)
|
||||
{
|
||||
executorService.execute(() -> {
|
||||
final List<ManagedTrustedCertificate> configured = certificateRepository.getConfiguredCertificates();
|
||||
final List<ManagedTrustedCertificate> installed = certificateRepository.getInstalledCertificates();
|
||||
|
||||
final Difference<ManagedTrustedCertificate> diff = Difference.between(installed, configured, ManagedTrustedCertificate::getVpnProfileUuid);
|
||||
if (diff.isEmpty())
|
||||
{
|
||||
Log.d(TAG, "No trusted certificates changed, nothing to do");
|
||||
handler.post(onUpdateCompleted);
|
||||
return;
|
||||
}
|
||||
Log.d(TAG, "Trusted certificates changed " + diff);
|
||||
|
||||
final Set<String> protectedAliases = new HashSet<>();
|
||||
for (final ManagedTrustedCertificate unchanged : diff.getUnchanged())
|
||||
{
|
||||
protectedAliases.add(unchanged.getAlias());
|
||||
}
|
||||
|
||||
for (final ManagedTrustedCertificate delete : diff.getDeletes())
|
||||
{
|
||||
remove(delete, !protectedAliases.contains(delete.getAlias()));
|
||||
}
|
||||
|
||||
for (final Pair<ManagedTrustedCertificate, ManagedTrustedCertificate> update : diff.getUpdates())
|
||||
{
|
||||
remove(update.first, !protectedAliases.contains(update.first.getAlias()));
|
||||
install(update.second);
|
||||
}
|
||||
|
||||
for (final ManagedTrustedCertificate insert : diff.getInserts())
|
||||
{
|
||||
install(insert);
|
||||
}
|
||||
|
||||
TrustedCertificateManager.getInstance().reset();
|
||||
TrustedCertificateManager.getInstance().load();
|
||||
handler.post(onUpdateCompleted);
|
||||
});
|
||||
}
|
||||
|
||||
private void install(@NonNull final ManagedTrustedCertificate trustedCertificate)
|
||||
{
|
||||
if (certificateInstaller.tryInstall(trustedCertificate))
|
||||
{
|
||||
certificateRepository.addInstalledCertificate(trustedCertificate);
|
||||
}
|
||||
}
|
||||
|
||||
private void remove(@NonNull final ManagedTrustedCertificate trustedCertificate, boolean uninstall)
|
||||
{
|
||||
if (uninstall)
|
||||
{
|
||||
certificateInstaller.tryRemove(trustedCertificate);
|
||||
}
|
||||
certificateRepository.removeInstalledCertificate(trustedCertificate);
|
||||
}
|
||||
}
|
||||
+128
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Relution GmbH
|
||||
*
|
||||
* Copyright (C) secunet Security Networks AG
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*/
|
||||
|
||||
package org.strongswan.android.logic;
|
||||
|
||||
import android.app.admin.DevicePolicyManager;
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
import android.util.Log;
|
||||
|
||||
import org.strongswan.android.data.ManagedUserCertificate;
|
||||
import org.strongswan.android.utils.KeyPair;
|
||||
import org.strongswan.android.utils.KeyPairs;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.security.KeyStoreException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.UnrecoverableKeyException;
|
||||
import java.security.cert.Certificate;
|
||||
import java.security.cert.CertificateException;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
public class ManagedUserCertificateInstaller
|
||||
{
|
||||
private static final String TAG = ManagedUserCertificateInstaller.class.getSimpleName();
|
||||
|
||||
private final DevicePolicyManager policyManager;
|
||||
|
||||
public ManagedUserCertificateInstaller(final Context context)
|
||||
{
|
||||
this.policyManager = (DevicePolicyManager)context.getSystemService(Context.DEVICE_POLICY_SERVICE);
|
||||
}
|
||||
|
||||
private boolean installKeyPair(@NonNull ManagedUserCertificate userCertificate, @NonNull KeyPair keyPair)
|
||||
{
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P)
|
||||
{
|
||||
int flags = DevicePolicyManager.INSTALLKEY_REQUEST_CREDENTIALS_ACCESS | DevicePolicyManager.INSTALLKEY_SET_USER_SELECTABLE;
|
||||
return policyManager.installKeyPair(
|
||||
null,
|
||||
keyPair.privateKey,
|
||||
new Certificate[]{keyPair.certificate},
|
||||
userCertificate.getAlias(),
|
||||
flags);
|
||||
}
|
||||
else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
|
||||
{
|
||||
return policyManager.installKeyPair(
|
||||
null,
|
||||
keyPair.privateKey,
|
||||
new Certificate[]{keyPair.certificate},
|
||||
userCertificate.getAlias(),
|
||||
true);
|
||||
}
|
||||
|
||||
/* This effectively prevents the app from using its own certificate, so certificate based
|
||||
* authentication can only really work on Android 6+. The certificate chooser is currently
|
||||
* never shown on devices that are enrolled */
|
||||
return policyManager.installKeyPair(
|
||||
null,
|
||||
keyPair.privateKey,
|
||||
keyPair.certificate,
|
||||
userCertificate.getAlias());
|
||||
}
|
||||
|
||||
private boolean installKeyPair(@NonNull ManagedUserCertificate userCertificate)
|
||||
throws UnrecoverableKeyException, CertificateException, IOException, KeyStoreException, NoSuchAlgorithmException
|
||||
{
|
||||
final KeyPair keyPair = KeyPairs.from(userCertificate.getData(), userCertificate.getPrivateKeyPassword());
|
||||
if (keyPair == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
Log.d(TAG, "Install key pair " + userCertificate);
|
||||
return installKeyPair(userCertificate, keyPair);
|
||||
}
|
||||
|
||||
private void removeKeyPair(@NonNull ManagedUserCertificate userCertificate)
|
||||
{
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N)
|
||||
{
|
||||
Log.w(TAG, "Cannot remove key pair, unsupported on API level " + Build.VERSION.SDK_INT);
|
||||
return;
|
||||
}
|
||||
|
||||
Log.d(TAG, "Remove key pair " + userCertificate);
|
||||
policyManager.removeKeyPair(null, userCertificate.getAlias());
|
||||
}
|
||||
|
||||
public synchronized boolean tryInstall(@NonNull ManagedUserCertificate userCertificate)
|
||||
{
|
||||
try
|
||||
{
|
||||
return installKeyPair(userCertificate);
|
||||
}
|
||||
catch (final Exception e)
|
||||
{
|
||||
Log.e(TAG, "Could not install key pair " + userCertificate, e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void tryRemove(@NonNull ManagedUserCertificate userCertificate)
|
||||
{
|
||||
try
|
||||
{
|
||||
removeKeyPair(userCertificate);
|
||||
}
|
||||
catch (final Exception e)
|
||||
{
|
||||
Log.e(TAG, "Could not remove key pair " + userCertificate, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
+97
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Relution GmbH
|
||||
*
|
||||
* Copyright (C) secunet Security Networks AG
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*/
|
||||
|
||||
package org.strongswan.android.logic;
|
||||
|
||||
import android.app.admin.DevicePolicyManager;
|
||||
import android.content.Context;
|
||||
import android.util.Log;
|
||||
|
||||
import org.strongswan.android.data.DatabaseHelper;
|
||||
import org.strongswan.android.data.ManagedConfigurationService;
|
||||
import org.strongswan.android.data.ManagedUserCertificate;
|
||||
import org.strongswan.android.data.ManagedUserCertificateRepository;
|
||||
import org.strongswan.android.utils.Difference;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.util.Pair;
|
||||
|
||||
public class ManagedUserCertificateManager
|
||||
{
|
||||
private static final String TAG = ManagedUserCertificateManager.class.getSimpleName();
|
||||
|
||||
@NonNull
|
||||
private final ManagedUserCertificateRepository certificateRepository;
|
||||
@NonNull
|
||||
private final ManagedUserCertificateInstaller certificateInstaller;
|
||||
|
||||
public ManagedUserCertificateManager(
|
||||
@NonNull final Context context,
|
||||
@NonNull final ManagedConfigurationService managedConfigurationService,
|
||||
@NonNull final DatabaseHelper databaseHelper)
|
||||
{
|
||||
final DevicePolicyManager devicePolicyManager = (DevicePolicyManager)context.getSystemService(Context.DEVICE_POLICY_SERVICE);
|
||||
|
||||
this.certificateRepository = new ManagedUserCertificateRepository(managedConfigurationService, devicePolicyManager, databaseHelper);
|
||||
this.certificateInstaller = new ManagedUserCertificateInstaller(context);
|
||||
}
|
||||
|
||||
public void update()
|
||||
{
|
||||
final List<ManagedUserCertificate> configured = certificateRepository.getConfiguredCertificates();
|
||||
final List<ManagedUserCertificate> installed = certificateRepository.getInstalledCertificates();
|
||||
|
||||
final Difference<ManagedUserCertificate> diff = Difference.between(installed, configured, ManagedUserCertificate::getVpnProfileUuid);
|
||||
if (diff.isEmpty())
|
||||
{
|
||||
Log.d(TAG, "No key pairs changed, nothing to do");
|
||||
return;
|
||||
}
|
||||
Log.d(TAG, "Key pairs changed " + diff);
|
||||
|
||||
for (final ManagedUserCertificate delete : diff.getDeletes())
|
||||
{
|
||||
remove(delete);
|
||||
}
|
||||
|
||||
for (final Pair<ManagedUserCertificate, ManagedUserCertificate> update : diff.getUpdates())
|
||||
{
|
||||
remove(update.first);
|
||||
install(update.second);
|
||||
}
|
||||
|
||||
for (final ManagedUserCertificate insert : diff.getInserts())
|
||||
{
|
||||
install(insert);
|
||||
}
|
||||
}
|
||||
|
||||
private void install(@NonNull final ManagedUserCertificate userCertificate)
|
||||
{
|
||||
if (certificateInstaller.tryInstall(userCertificate))
|
||||
{
|
||||
certificateRepository.addInstalledCertificate(userCertificate);
|
||||
}
|
||||
}
|
||||
|
||||
private void remove(@NonNull final ManagedUserCertificate userCertificate)
|
||||
{
|
||||
certificateInstaller.tryRemove(userCertificate);
|
||||
certificateRepository.removeInstalledCertificate(userCertificate);
|
||||
}
|
||||
}
|
||||
+122
-27
@@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Tobias Brunner
|
||||
* Copyright (C) 2023 Relution GmbH
|
||||
* Copyright (C) 2014-2024 Tobias Brunner
|
||||
*
|
||||
* Copyright (C) secunet Security Networks AG
|
||||
*
|
||||
@@ -16,29 +17,62 @@
|
||||
|
||||
package org.strongswan.android.logic;
|
||||
|
||||
import android.app.Application;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.util.Log;
|
||||
|
||||
import org.strongswan.android.data.DatabaseHelper;
|
||||
import org.strongswan.android.data.ManagedConfigurationService;
|
||||
import org.strongswan.android.security.LocalCertificateKeyStoreProvider;
|
||||
import org.strongswan.android.utils.Constants;
|
||||
|
||||
import java.security.Security;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
import org.strongswan.android.security.LocalCertificateKeyStoreProvider;
|
||||
import org.strongswan.android.ui.MainActivity;
|
||||
|
||||
import android.app.Application;
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.os.HandlerCompat;
|
||||
import androidx.lifecycle.DefaultLifecycleObserver;
|
||||
import androidx.lifecycle.LifecycleOwner;
|
||||
import androidx.lifecycle.ProcessLifecycleOwner;
|
||||
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
|
||||
|
||||
public class StrongSwanApplication extends Application
|
||||
public class StrongSwanApplication extends Application implements DefaultLifecycleObserver
|
||||
{
|
||||
private static final String TAG = StrongSwanApplication.class.getSimpleName();
|
||||
|
||||
private static Context mContext;
|
||||
private static StrongSwanApplication mInstance;
|
||||
|
||||
private final ExecutorService mExecutorService = Executors.newFixedThreadPool(4);
|
||||
private final Handler mMainHandler = HandlerCompat.createAsync(Looper.getMainLooper());
|
||||
|
||||
static {
|
||||
private ManagedConfigurationService mManagedConfigurationService;
|
||||
private ManagedTrustedCertificateManager mTrustedCertificateManager;
|
||||
private ManagedUserCertificateManager mUserCertificateManager;
|
||||
|
||||
private DatabaseHelper mDatabaseHelper;
|
||||
|
||||
private final BroadcastReceiver mRestrictionsReceiver = new BroadcastReceiver()
|
||||
{
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent)
|
||||
{
|
||||
Log.d(TAG, "Managed configuration changed");
|
||||
reloadManagedConfigurationAndNotifyListeners();
|
||||
}
|
||||
};
|
||||
|
||||
static
|
||||
{
|
||||
Security.addProvider(new LocalCertificateKeyStoreProvider());
|
||||
}
|
||||
|
||||
@@ -47,10 +81,56 @@ public class StrongSwanApplication extends Application
|
||||
{
|
||||
super.onCreate();
|
||||
StrongSwanApplication.mContext = getApplicationContext();
|
||||
StrongSwanApplication.mInstance = this;
|
||||
|
||||
mDatabaseHelper = new DatabaseHelper(mContext);
|
||||
|
||||
mManagedConfigurationService = new ManagedConfigurationService(mContext);
|
||||
|
||||
mTrustedCertificateManager = new ManagedTrustedCertificateManager(mContext, mExecutorService, mMainHandler,
|
||||
mManagedConfigurationService, mDatabaseHelper);
|
||||
|
||||
mUserCertificateManager = new ManagedUserCertificateManager(mContext, mManagedConfigurationService, mDatabaseHelper);
|
||||
|
||||
ProcessLifecycleOwner.get().getLifecycle().addObserver(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume(@NonNull LifecycleOwner owner)
|
||||
{
|
||||
reloadManagedConfigurationAndNotifyListeners();
|
||||
|
||||
final IntentFilter restrictionsFilter = new IntentFilter(Intent.ACTION_APPLICATION_RESTRICTIONS_CHANGED);
|
||||
registerReceiver(mRestrictionsReceiver, restrictionsFilter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause(@NonNull LifecycleOwner owner)
|
||||
{
|
||||
unregisterReceiver(mRestrictionsReceiver);
|
||||
}
|
||||
|
||||
private void reloadManagedConfigurationAndNotifyListeners()
|
||||
{
|
||||
final Set<String> uuids = new HashSet<>(mManagedConfigurationService.getManagedProfiles().keySet());
|
||||
|
||||
mManagedConfigurationService.loadConfiguration();
|
||||
mManagedConfigurationService.updateSettings();
|
||||
|
||||
mUserCertificateManager.update();
|
||||
mTrustedCertificateManager.update(() -> {
|
||||
uuids.addAll(mManagedConfigurationService.getManagedProfiles().keySet());
|
||||
|
||||
Log.d(TAG, "Send profiles changed broadcast");
|
||||
Intent profilesChanged = new Intent(Constants.VPN_PROFILES_CHANGED);
|
||||
profilesChanged.putExtra(Constants.VPN_PROFILES_MULTIPLE, uuids.toArray(new String[0]));
|
||||
LocalBroadcastManager.getInstance(mContext).sendBroadcast(profilesChanged);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current application context
|
||||
*
|
||||
* @return context
|
||||
*/
|
||||
public static Context getContext()
|
||||
@@ -58,8 +138,19 @@ public class StrongSwanApplication extends Application
|
||||
return StrongSwanApplication.mContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current application object
|
||||
*
|
||||
* @return application
|
||||
*/
|
||||
public static StrongSwanApplication getInstance()
|
||||
{
|
||||
return StrongSwanApplication.mInstance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a thread pool to run tasks in separate threads
|
||||
*
|
||||
* @return thread pool
|
||||
*/
|
||||
public Executor getExecutor()
|
||||
@@ -69,6 +160,7 @@ public class StrongSwanApplication extends Application
|
||||
|
||||
/**
|
||||
* Returns a handler to execute stuff by the main thread.
|
||||
*
|
||||
* @return handler
|
||||
*/
|
||||
public Handler getHandler()
|
||||
@@ -76,27 +168,30 @@ public class StrongSwanApplication extends Application
|
||||
return mMainHandler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a service providing access to the app's managed configuration.
|
||||
*
|
||||
* @return managed configuration
|
||||
*/
|
||||
public ManagedConfigurationService getManagedConfigurationService()
|
||||
{
|
||||
return mManagedConfigurationService;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the application's database helper used to access its SQLite database
|
||||
*/
|
||||
public DatabaseHelper getDatabaseHelper()
|
||||
{
|
||||
return mDatabaseHelper;
|
||||
}
|
||||
|
||||
/*
|
||||
* The libraries are extracted to /data/data/org.strongswan.android/...
|
||||
* during installation. On newer releases most are loaded in JNI_OnLoad.
|
||||
*/
|
||||
static
|
||||
{
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2)
|
||||
{
|
||||
System.loadLibrary("strongswan");
|
||||
|
||||
if (MainActivity.USE_BYOD)
|
||||
{
|
||||
System.loadLibrary("tpmtss");
|
||||
System.loadLibrary("tncif");
|
||||
System.loadLibrary("tnccs");
|
||||
System.loadLibrary("imcv");
|
||||
}
|
||||
|
||||
System.loadLibrary("charon");
|
||||
System.loadLibrary("ipsec");
|
||||
}
|
||||
System.loadLibrary("androidbridge");
|
||||
}
|
||||
}
|
||||
|
||||
+25
-14
@@ -55,11 +55,11 @@ public class VpnStateService extends Service
|
||||
private ErrorState mError = ErrorState.NO_ERROR;
|
||||
private ImcState mImcState = ImcState.UNKNOWN;
|
||||
private final LinkedList<RemediationInstruction> mRemediationInstructions = new LinkedList<RemediationInstruction>();
|
||||
private static long RETRY_INTERVAL = 1000;
|
||||
private static final long RETRY_INTERVAL = 1000;
|
||||
/* cap the retry interval at 2 minutes */
|
||||
private static long MAX_RETRY_INTERVAL = 120000;
|
||||
private static int RETRY_MSG = 1;
|
||||
private RetryTimeoutProvider mTimeoutProvider = new RetryTimeoutProvider();
|
||||
private static final long MAX_RETRY_INTERVAL = 120000;
|
||||
private static final int RETRY_MSG = 1;
|
||||
private final RetryTimeoutProvider mTimeoutProvider = new RetryTimeoutProvider();
|
||||
private long mRetryTimeout;
|
||||
private long mRetryIn;
|
||||
|
||||
@@ -89,7 +89,7 @@ public class VpnStateService extends Service
|
||||
*/
|
||||
public interface VpnStateListener
|
||||
{
|
||||
public void stateChanged();
|
||||
void stateChanged();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -169,6 +169,7 @@ public class VpnStateService extends Service
|
||||
|
||||
/**
|
||||
* Get the total number of seconds until there is an automatic retry to reconnect.
|
||||
*
|
||||
* @return total number of seconds until the retry
|
||||
*/
|
||||
public int getRetryTimeout()
|
||||
@@ -178,6 +179,7 @@ public class VpnStateService extends Service
|
||||
|
||||
/**
|
||||
* Get the number of seconds until there is an automatic retry to reconnect.
|
||||
*
|
||||
* @return number of seconds until the retry
|
||||
*/
|
||||
public int getRetryIn()
|
||||
@@ -283,8 +285,9 @@ public class VpnStateService extends Service
|
||||
|
||||
/**
|
||||
* Connect (or reconnect) a profile
|
||||
*
|
||||
* @param profileInfo optional profile info (basically the UUID and password), taken from the
|
||||
* previous profile if null
|
||||
* previous profile if null
|
||||
* @param fromScratch true if this is a manual retry/reconnect or a completely new connection
|
||||
*/
|
||||
public void connect(Bundle profileInfo, boolean fromScratch)
|
||||
@@ -330,7 +333,7 @@ public class VpnStateService extends Service
|
||||
Intent intent = new Intent(this, VpnProfileControlActivity.class);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
intent.setAction(VpnProfileControlActivity.START_PROFILE);
|
||||
intent.putExtra(VpnProfileControlActivity.EXTRA_VPN_PROFILE_ID, mProfile.getUUID().toString());
|
||||
intent.putExtra(VpnProfileControlActivity.EXTRA_VPN_PROFILE_UUID, mProfile.getUUID().toString());
|
||||
startActivity(intent);
|
||||
/* reset the retry timer immediately in case the user needs more time to enter the password */
|
||||
notifyListeners(() -> {
|
||||
@@ -353,7 +356,8 @@ public class VpnStateService extends Service
|
||||
*/
|
||||
private void notifyListeners(final Callable<Boolean> change)
|
||||
{
|
||||
mHandler.post(new Runnable() {
|
||||
mHandler.post(new Runnable()
|
||||
{
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
@@ -386,7 +390,8 @@ public class VpnStateService extends Service
|
||||
*/
|
||||
public void startConnection(final VpnProfile profile)
|
||||
{
|
||||
notifyListeners(new Callable<Boolean>() {
|
||||
notifyListeners(new Callable<Boolean>()
|
||||
{
|
||||
@Override
|
||||
public Boolean call() throws Exception
|
||||
{
|
||||
@@ -411,7 +416,8 @@ public class VpnStateService extends Service
|
||||
*/
|
||||
public void setState(final State state)
|
||||
{
|
||||
notifyListeners(new Callable<Boolean>() {
|
||||
notifyListeners(new Callable<Boolean>()
|
||||
{
|
||||
@Override
|
||||
public Boolean call() throws Exception
|
||||
{
|
||||
@@ -438,7 +444,8 @@ public class VpnStateService extends Service
|
||||
*/
|
||||
public void setError(final ErrorState error)
|
||||
{
|
||||
notifyListeners(new Callable<Boolean>() {
|
||||
notifyListeners(new Callable<Boolean>()
|
||||
{
|
||||
@Override
|
||||
public Boolean call() throws Exception
|
||||
{
|
||||
@@ -471,7 +478,8 @@ public class VpnStateService extends Service
|
||||
*/
|
||||
public void setImcState(final ImcState state)
|
||||
{
|
||||
notifyListeners(new Callable<Boolean>() {
|
||||
notifyListeners(new Callable<Boolean>()
|
||||
{
|
||||
@Override
|
||||
public Boolean call() throws Exception
|
||||
{
|
||||
@@ -501,7 +509,8 @@ public class VpnStateService extends Service
|
||||
*/
|
||||
public void addRemediationInstruction(final RemediationInstruction instruction)
|
||||
{
|
||||
mHandler.post(new Runnable() {
|
||||
mHandler.post(new Runnable()
|
||||
{
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
@@ -535,7 +544,8 @@ public class VpnStateService extends Service
|
||||
/**
|
||||
* Special Handler subclass that handles the retry countdown (more accurate than CountDownTimer)
|
||||
*/
|
||||
private static class RetryHandler extends Handler {
|
||||
private static class RetryHandler extends Handler
|
||||
{
|
||||
WeakReference<VpnStateService> mService;
|
||||
|
||||
public RetryHandler(Looper looper, VpnStateService service)
|
||||
@@ -604,6 +614,7 @@ public class VpnStateService extends Service
|
||||
/**
|
||||
* Called each time a new retry timeout is started. The timeout increases until reset() is
|
||||
* called and the base timeout is returned again.
|
||||
*
|
||||
* @param error Error state
|
||||
*/
|
||||
public long getTimeout(ErrorState error)
|
||||
|
||||
+28
-20
@@ -21,7 +21,6 @@ package org.strongswan.android.ui;
|
||||
import android.app.Dialog;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.text.format.Formatter;
|
||||
import android.view.Menu;
|
||||
@@ -29,6 +28,8 @@ import android.view.MenuItem;
|
||||
import android.widget.Toast;
|
||||
|
||||
import org.strongswan.android.R;
|
||||
import org.strongswan.android.data.ManagedConfiguration;
|
||||
import org.strongswan.android.data.ManagedConfigurationService;
|
||||
import org.strongswan.android.data.VpnProfile;
|
||||
import org.strongswan.android.logic.StrongSwanApplication;
|
||||
import org.strongswan.android.logic.TrustedCertificateManager;
|
||||
@@ -58,6 +59,8 @@ public class MainActivity extends AppCompatActivity implements OnVpnProfileSelec
|
||||
|
||||
private static final String DIALOG_TAG = "Dialog";
|
||||
|
||||
private ManagedConfigurationService mManagedConfigurationService;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState)
|
||||
{
|
||||
@@ -73,6 +76,8 @@ public class MainActivity extends AppCompatActivity implements OnVpnProfileSelec
|
||||
((StrongSwanApplication)getApplication()).getExecutor().execute(() -> {
|
||||
TrustedCertificateManager.getInstance().load();
|
||||
});
|
||||
|
||||
mManagedConfigurationService = StrongSwanApplication.getInstance().getManagedConfigurationService();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -85,9 +90,12 @@ public class MainActivity extends AppCompatActivity implements OnVpnProfileSelec
|
||||
@Override
|
||||
public boolean onPrepareOptionsMenu(Menu menu)
|
||||
{
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT)
|
||||
final MenuItem importProfile = menu.findItem(R.id.menu_import_profile);
|
||||
if (importProfile != null)
|
||||
{
|
||||
menu.removeItem(R.id.menu_import_profile);
|
||||
final ManagedConfiguration managedConfiguration = mManagedConfigurationService.getManagedConfiguration();
|
||||
importProfile.setVisible(managedConfiguration.isAllowProfileImport());
|
||||
importProfile.setEnabled(managedConfiguration.isAllowProfileImport());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -126,7 +134,7 @@ public class MainActivity extends AppCompatActivity implements OnVpnProfileSelec
|
||||
{
|
||||
Intent intent = new Intent(this, VpnProfileControlActivity.class);
|
||||
intent.setAction(VpnProfileControlActivity.START_PROFILE);
|
||||
intent.putExtra(VpnProfileControlActivity.EXTRA_VPN_PROFILE_ID, profile.getUUID().toString());
|
||||
intent.putExtra(VpnProfileControlActivity.EXTRA_VPN_PROFILE_UUID, profile.getUUID().toString());
|
||||
startActivity(intent);
|
||||
}
|
||||
|
||||
@@ -195,26 +203,26 @@ public class MainActivity extends AppCompatActivity implements OnVpnProfileSelec
|
||||
size = Formatter.formatFileSize(getActivity(), s);
|
||||
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity())
|
||||
.setTitle(R.string.clear_crl_cache_title)
|
||||
.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener()
|
||||
.setTitle(R.string.clear_crl_cache_title)
|
||||
.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener()
|
||||
{
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which)
|
||||
{
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which)
|
||||
{
|
||||
dismiss();
|
||||
}
|
||||
})
|
||||
.setPositiveButton(R.string.clear, new DialogInterface.OnClickListener()
|
||||
dismiss();
|
||||
}
|
||||
})
|
||||
.setPositiveButton(R.string.clear, new DialogInterface.OnClickListener()
|
||||
{
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int whichButton)
|
||||
{
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int whichButton)
|
||||
for (String file : list)
|
||||
{
|
||||
for (String file : list)
|
||||
{
|
||||
getActivity().deleteFile(file);
|
||||
}
|
||||
getActivity().deleteFile(file);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
builder.setMessage(getActivity().getResources().getQuantityString(R.plurals.clear_crl_cache_msg, list.size(), list.size(), size));
|
||||
return builder.create();
|
||||
}
|
||||
|
||||
+8
-1
@@ -60,9 +60,11 @@ public class SelectedApplicationsListFragment extends ListFragment implements Lo
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
setHasOptionsMenu(true);
|
||||
|
||||
getListView().setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
|
||||
final boolean readOnly = getActivity().getIntent().getBooleanExtra(VpnProfileDataSource.KEY_READ_ONLY, false);
|
||||
getListView().setChoiceMode(readOnly ? ListView.CHOICE_MODE_NONE : ListView.CHOICE_MODE_MULTIPLE);
|
||||
|
||||
mAdapter = new SelectedApplicationsAdapter(getActivity());
|
||||
mAdapter.setReadOnly(readOnly);
|
||||
setListAdapter(mAdapter);
|
||||
setListShown(false);
|
||||
|
||||
@@ -101,6 +103,11 @@ public class SelectedApplicationsListFragment extends ListFragment implements Lo
|
||||
@Override
|
||||
public void onListItemClick(ListView l, View v, int position, long id)
|
||||
{
|
||||
if (mAdapter.isReadOnly())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
super.onListItemClick(l, v, position, id);
|
||||
SelectedApplicationEntry item = (SelectedApplicationEntry)getListView().getItemAtPosition(position);
|
||||
item.setSelected(!item.isSelected());
|
||||
|
||||
+28
-8
@@ -16,13 +16,21 @@
|
||||
|
||||
package org.strongswan.android.ui;
|
||||
|
||||
import static org.strongswan.android.utils.Constants.PREF_DEFAULT_VPN_PROFILE;
|
||||
import static org.strongswan.android.utils.Constants.PREF_DEFAULT_VPN_PROFILE_MRU;
|
||||
import static org.strongswan.android.utils.Constants.PREF_IGNORE_POWER_WHITELIST;
|
||||
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
|
||||
import org.strongswan.android.R;
|
||||
import org.strongswan.android.data.ManagedConfiguration;
|
||||
import org.strongswan.android.data.ManagedConfigurationService;
|
||||
import org.strongswan.android.data.VpnProfile;
|
||||
import org.strongswan.android.data.VpnProfileDataSource;
|
||||
import org.strongswan.android.data.VpnProfileSource;
|
||||
import org.strongswan.android.logic.StrongSwanApplication;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
@@ -33,25 +41,31 @@ import androidx.preference.ListPreference;
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceFragmentCompat;
|
||||
import androidx.preference.PreferenceManager;
|
||||
|
||||
import static org.strongswan.android.utils.Constants.PREF_DEFAULT_VPN_PROFILE;
|
||||
import static org.strongswan.android.utils.Constants.PREF_DEFAULT_VPN_PROFILE_MRU;
|
||||
import androidx.preference.SwitchPreference;
|
||||
|
||||
public class SettingsFragment extends PreferenceFragmentCompat implements Preference.OnPreferenceChangeListener
|
||||
{
|
||||
private ManagedConfigurationService mManagedConfigurationService;
|
||||
|
||||
private ListPreference mDefaultVPNProfile;
|
||||
private SwitchPreference mIgnorePowerWhitelist;
|
||||
|
||||
@Override
|
||||
public void onCreatePreferences(Bundle bundle, String s)
|
||||
{
|
||||
mManagedConfigurationService = StrongSwanApplication.getInstance().getManagedConfigurationService();
|
||||
mManagedConfigurationService.updateSettings();
|
||||
|
||||
setPreferencesFromResource(R.xml.settings, s);
|
||||
|
||||
mDefaultVPNProfile = (ListPreference)findPreference(PREF_DEFAULT_VPN_PROFILE);
|
||||
mDefaultVPNProfile = findPreference(PREF_DEFAULT_VPN_PROFILE);
|
||||
mDefaultVPNProfile.setOnPreferenceChangeListener(this);
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N)
|
||||
{
|
||||
mDefaultVPNProfile.setEnabled(false);
|
||||
}
|
||||
|
||||
mIgnorePowerWhitelist = findPreference(PREF_IGNORE_POWER_WHITELIST);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -59,11 +73,12 @@ public class SettingsFragment extends PreferenceFragmentCompat implements Prefer
|
||||
{
|
||||
super.onResume();
|
||||
|
||||
VpnProfileDataSource profiles = new VpnProfileDataSource(getActivity());
|
||||
VpnProfileDataSource profiles = new VpnProfileSource(getActivity());
|
||||
profiles.open();
|
||||
|
||||
List<VpnProfile> all = profiles.getAllVpnProfiles();
|
||||
Collections.sort(all, new Comparator<VpnProfile>() {
|
||||
Collections.sort(all, new Comparator<VpnProfile>()
|
||||
{
|
||||
@Override
|
||||
public int compare(VpnProfile lhs, VpnProfile rhs)
|
||||
{
|
||||
@@ -84,7 +99,8 @@ public class SettingsFragment extends PreferenceFragmentCompat implements Prefer
|
||||
}
|
||||
profiles.close();
|
||||
|
||||
if (entries.size() <= 1)
|
||||
final ManagedConfiguration managedConfiguration = mManagedConfigurationService.getManagedConfiguration();
|
||||
if (entries.size() <= 1 || !managedConfiguration.isAllowSettingsAccess())
|
||||
{
|
||||
mDefaultVPNProfile.setEnabled(false);
|
||||
}
|
||||
@@ -94,6 +110,10 @@ public class SettingsFragment extends PreferenceFragmentCompat implements Prefer
|
||||
mDefaultVPNProfile.setEntries(entries.toArray(new CharSequence[0]));
|
||||
mDefaultVPNProfile.setEntryValues(entryvalues.toArray(new CharSequence[0]));
|
||||
}
|
||||
if (!managedConfiguration.isAllowSettingsAccess())
|
||||
{
|
||||
mIgnorePowerWhitelist.setEnabled(false);
|
||||
}
|
||||
|
||||
SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(getActivity());
|
||||
setCurrentProfileName(pref.getString(PREF_DEFAULT_VPN_PROFILE, PREF_DEFAULT_VPN_PROFILE_MRU));
|
||||
@@ -111,7 +131,7 @@ public class SettingsFragment extends PreferenceFragmentCompat implements Prefer
|
||||
|
||||
private void setCurrentProfileName(String uuid)
|
||||
{
|
||||
VpnProfileDataSource profiles = new VpnProfileDataSource(getActivity());
|
||||
VpnProfileDataSource profiles = new VpnProfileSource(getActivity());
|
||||
profiles.open();
|
||||
|
||||
if (!uuid.equals(PREF_DEFAULT_VPN_PROFILE_MRU))
|
||||
|
||||
+1
-5
@@ -16,13 +16,11 @@
|
||||
|
||||
package org.strongswan.android.ui;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.app.Dialog;
|
||||
import android.content.ActivityNotFoundException;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.widget.Toast;
|
||||
|
||||
@@ -61,7 +59,6 @@ public class TrustedCertificateImportActivity extends AppCompatActivity
|
||||
}
|
||||
);
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.KITKAT)
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState)
|
||||
{
|
||||
@@ -78,7 +75,7 @@ public class TrustedCertificateImportActivity extends AppCompatActivity
|
||||
{
|
||||
importCertificate(intent.getData());
|
||||
}
|
||||
else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT)
|
||||
else
|
||||
{
|
||||
Intent openIntent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
|
||||
openIntent.setType("*/*");
|
||||
@@ -89,7 +86,6 @@ public class TrustedCertificateImportActivity extends AppCompatActivity
|
||||
catch (ActivityNotFoundException e)
|
||||
{ /* some devices are unable to browse for files */
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+14
-6
@@ -17,7 +17,6 @@
|
||||
package org.strongswan.android.ui;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
@@ -26,7 +25,10 @@ import com.google.android.material.tabs.TabLayout;
|
||||
import com.google.android.material.tabs.TabLayoutMediator;
|
||||
|
||||
import org.strongswan.android.R;
|
||||
import org.strongswan.android.data.ManagedConfiguration;
|
||||
import org.strongswan.android.data.ManagedConfigurationService;
|
||||
import org.strongswan.android.data.VpnProfileDataSource;
|
||||
import org.strongswan.android.logic.StrongSwanApplication;
|
||||
import org.strongswan.android.logic.TrustedCertificateManager;
|
||||
import org.strongswan.android.logic.TrustedCertificateManager.TrustedCertificateSource;
|
||||
import org.strongswan.android.security.TrustedCertificateEntry;
|
||||
@@ -52,6 +54,8 @@ public class TrustedCertificatesActivity extends AppCompatActivity implements Tr
|
||||
private ViewPager2 mPager;
|
||||
private boolean mSelect;
|
||||
|
||||
private ManagedConfigurationService mManagedConfigurationService;
|
||||
|
||||
private final ActivityResultLauncher<Intent> mImportCertificate = registerForActivityResult(
|
||||
new ActivityResultContracts.StartActivityForResult(),
|
||||
result -> {
|
||||
@@ -73,15 +77,16 @@ public class TrustedCertificatesActivity extends AppCompatActivity implements Tr
|
||||
|
||||
mAdapter = new TrustedCertificatesPagerAdapter(this);
|
||||
|
||||
mPager = (ViewPager2)findViewById(R.id.viewpager);
|
||||
mPager = findViewById(R.id.viewpager);
|
||||
mPager.setAdapter(mAdapter);
|
||||
|
||||
TabLayout tabs = (TabLayout)findViewById(R.id.tabs);
|
||||
TabLayout tabs = findViewById(R.id.tabs);
|
||||
new TabLayoutMediator(tabs, mPager, (tab, position) -> {
|
||||
tab.setText(mAdapter.getTitle(position));
|
||||
}).attach();
|
||||
|
||||
mSelect = SELECT_CERTIFICATE.equals(getIntent().getAction());
|
||||
mManagedConfigurationService = StrongSwanApplication.getInstance().getManagedConfigurationService();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -94,9 +99,12 @@ public class TrustedCertificatesActivity extends AppCompatActivity implements Tr
|
||||
@Override
|
||||
public boolean onPrepareOptionsMenu(Menu menu)
|
||||
{
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT)
|
||||
final MenuItem importCertificate = menu.findItem(R.id.menu_import_certificate);
|
||||
if (importCertificate != null)
|
||||
{
|
||||
menu.removeItem(R.id.menu_import_certificate);
|
||||
final ManagedConfiguration managedConfiguration = mManagedConfigurationService.getManagedConfiguration();
|
||||
importCertificate.setVisible(managedConfiguration.isAllowCertificateImport());
|
||||
importCertificate.setEnabled(managedConfiguration.isAllowCertificateImport());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -164,7 +172,7 @@ public class TrustedCertificatesActivity extends AppCompatActivity implements Tr
|
||||
|
||||
public static class TrustedCertificatesPagerAdapter extends FragmentStateAdapter
|
||||
{
|
||||
private TrustedCertificatesTab mTabs[];
|
||||
private final TrustedCertificatesTab[] mTabs;
|
||||
|
||||
public TrustedCertificatesPagerAdapter(@NonNull FragmentActivity fragmentActivity)
|
||||
{
|
||||
|
||||
+10
-16
@@ -42,12 +42,12 @@ import android.widget.Toast;
|
||||
import org.strongswan.android.R;
|
||||
import org.strongswan.android.data.VpnProfile;
|
||||
import org.strongswan.android.data.VpnProfileDataSource;
|
||||
import org.strongswan.android.data.VpnProfileSource;
|
||||
import org.strongswan.android.data.VpnType.VpnTypeFeature;
|
||||
import org.strongswan.android.logic.VpnStateService;
|
||||
import org.strongswan.android.logic.VpnStateService.State;
|
||||
import org.strongswan.android.utils.Constants;
|
||||
|
||||
import androidx.activity.result.ActivityResultCallback;
|
||||
import androidx.activity.result.ActivityResultLauncher;
|
||||
import androidx.activity.result.contract.ActivityResultContracts;
|
||||
import androidx.annotation.NonNull;
|
||||
@@ -64,7 +64,7 @@ public class VpnProfileControlActivity extends AppCompatActivity
|
||||
{
|
||||
public static final String START_PROFILE = "org.strongswan.android.action.START_PROFILE";
|
||||
public static final String DISCONNECT = "org.strongswan.android.action.DISCONNECT";
|
||||
public static final String EXTRA_VPN_PROFILE_ID = "org.strongswan.android.VPN_PROFILE_ID";
|
||||
public static final String EXTRA_VPN_PROFILE_UUID = "org.strongswan.android.VPN_PROFILE_UUID";
|
||||
|
||||
private static final String WAITING_FOR_RESULT = "WAITING_FOR_RESULT";
|
||||
private static final String PROFILE_NAME = "PROFILE_NAME";
|
||||
@@ -257,6 +257,7 @@ public class VpnProfileControlActivity extends AppCompatActivity
|
||||
/**
|
||||
* Check if we have permission to display notifications to the user, if necessary,
|
||||
* ask the user to allow this.
|
||||
*
|
||||
* @return true if profile can be initiated immediately
|
||||
*/
|
||||
private boolean checkNotificationPermission()
|
||||
@@ -274,6 +275,7 @@ public class VpnProfileControlActivity extends AppCompatActivity
|
||||
/**
|
||||
* Check if we are on the system's power whitelist, if necessary, or ask the user
|
||||
* to add us.
|
||||
*
|
||||
* @return true if profile can be initiated immediately
|
||||
*/
|
||||
private boolean checkPowerWhitelist()
|
||||
@@ -373,21 +375,13 @@ public class VpnProfileControlActivity extends AppCompatActivity
|
||||
{
|
||||
VpnProfile profile = null;
|
||||
|
||||
VpnProfileDataSource dataSource = new VpnProfileDataSource(this);
|
||||
VpnProfileDataSource dataSource = new VpnProfileSource(this);
|
||||
dataSource.open();
|
||||
String profileUUID = intent.getStringExtra(EXTRA_VPN_PROFILE_ID);
|
||||
String profileUUID = intent.getStringExtra(EXTRA_VPN_PROFILE_UUID);
|
||||
if (profileUUID != null)
|
||||
{
|
||||
profile = dataSource.getVpnProfile(profileUUID);
|
||||
}
|
||||
else
|
||||
{
|
||||
long profileId = intent.getLongExtra(EXTRA_VPN_PROFILE_ID, 0);
|
||||
if (profileId > 0)
|
||||
{
|
||||
profile = dataSource.getVpnProfile(profileId);
|
||||
}
|
||||
}
|
||||
dataSource.close();
|
||||
|
||||
if (profile != null)
|
||||
@@ -412,10 +406,10 @@ public class VpnProfileControlActivity extends AppCompatActivity
|
||||
|
||||
removeFragmentByTag(DIALOG_TAG);
|
||||
|
||||
String profileUUID = intent.getStringExtra(EXTRA_VPN_PROFILE_ID);
|
||||
String profileUUID = intent.getStringExtra(EXTRA_VPN_PROFILE_UUID);
|
||||
if (profileUUID != null)
|
||||
{
|
||||
VpnProfileDataSource dataSource = new VpnProfileDataSource(this);
|
||||
VpnProfileDataSource dataSource = new VpnProfileSource(this);
|
||||
dataSource.open();
|
||||
profile = dataSource.getVpnProfile(profileUUID);
|
||||
dataSource.close();
|
||||
@@ -583,9 +577,9 @@ public class VpnProfileControlActivity extends AppCompatActivity
|
||||
final Bundle profileInfo = getArguments();
|
||||
LayoutInflater inflater = getActivity().getLayoutInflater();
|
||||
View view = inflater.inflate(R.layout.login_dialog, null);
|
||||
EditText username = (EditText)view.findViewById(R.id.username);
|
||||
EditText username = view.findViewById(R.id.username);
|
||||
username.setText(profileInfo.getString(VpnProfileDataSource.KEY_USERNAME));
|
||||
final EditText password = (EditText)view.findViewById(R.id.password);
|
||||
final EditText password = view.findViewById(R.id.password);
|
||||
|
||||
AlertDialog.Builder adb = new AlertDialog.Builder(getActivity());
|
||||
adb.setView(view);
|
||||
|
||||
+127
-75
@@ -1,4 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Relution GmbH
|
||||
* Copyright (C) 2012-2020 Tobias Brunner
|
||||
* Copyright (C) 2012 Giuliano Grassi
|
||||
* Copyright (C) 2012 Ralf Sager
|
||||
@@ -22,7 +23,6 @@ import android.app.Dialog;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.security.KeyChain;
|
||||
@@ -44,7 +44,6 @@ import android.view.ViewGroup;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.AdapterView.OnItemSelectedListener;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.Button;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.CompoundButton;
|
||||
import android.widget.CompoundButton.OnCheckedChangeListener;
|
||||
@@ -58,6 +57,7 @@ import org.strongswan.android.R;
|
||||
import org.strongswan.android.data.VpnProfile;
|
||||
import org.strongswan.android.data.VpnProfile.SelectedAppsHandling;
|
||||
import org.strongswan.android.data.VpnProfileDataSource;
|
||||
import org.strongswan.android.data.VpnProfileSource;
|
||||
import org.strongswan.android.data.VpnType;
|
||||
import org.strongswan.android.data.VpnType.VpnTypeFeature;
|
||||
import org.strongswan.android.logic.StrongSwanApplication;
|
||||
@@ -89,7 +89,7 @@ import androidx.localbroadcastmanager.content.LocalBroadcastManager;
|
||||
public class VpnProfileDetailActivity extends AppCompatActivity
|
||||
{
|
||||
private VpnProfileDataSource mDataSource;
|
||||
private Long mId;
|
||||
private String mUuid;
|
||||
private TrustedCertificateEntry mCertEntry;
|
||||
private String mUserCertLoading;
|
||||
private CertificateIdentitiesAdapter mSelectUserIdAdapter;
|
||||
@@ -98,6 +98,7 @@ public class VpnProfileDetailActivity extends AppCompatActivity
|
||||
private SelectedAppsHandling mSelectedAppsHandling = SelectedAppsHandling.SELECTED_APPS_DISABLE;
|
||||
private SortedSet<String> mSelectedApps = new TreeSet<>();
|
||||
private VpnProfile mProfile;
|
||||
private View mManagedProfile;
|
||||
private MultiAutoCompleteTextView mName;
|
||||
private TextInputLayoutHelper mNameWrap;
|
||||
private EditText mGateway;
|
||||
@@ -190,69 +191,71 @@ public class VpnProfileDetailActivity extends AppCompatActivity
|
||||
/* the title is set when we load the profile, if any */
|
||||
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||
|
||||
mDataSource = new VpnProfileDataSource(this);
|
||||
mDataSource = new VpnProfileSource(this);
|
||||
mDataSource.open();
|
||||
|
||||
setContentView(R.layout.profile_detail_view);
|
||||
|
||||
mName = (MultiAutoCompleteTextView)findViewById(R.id.name);
|
||||
mNameWrap = (TextInputLayoutHelper)findViewById(R.id.name_wrap);
|
||||
mGateway = (EditText)findViewById(R.id.gateway);
|
||||
mGatewayWrap = (TextInputLayoutHelper) findViewById(R.id.gateway_wrap);
|
||||
mSelectVpnType = (Spinner)findViewById(R.id.vpn_type);
|
||||
mTncNotice = (RelativeLayout)findViewById(R.id.tnc_notice);
|
||||
mManagedProfile = findViewById(R.id.managed_profile);
|
||||
|
||||
mUsernamePassword = (ViewGroup)findViewById(R.id.username_password_group);
|
||||
mUsername = (EditText)findViewById(R.id.username);
|
||||
mUsernameWrap = (TextInputLayoutHelper) findViewById(R.id.username_wrap);
|
||||
mPassword = (EditText)findViewById(R.id.password);
|
||||
mName = findViewById(R.id.name);
|
||||
mNameWrap = findViewById(R.id.name_wrap);
|
||||
mGateway = findViewById(R.id.gateway);
|
||||
mGatewayWrap = findViewById(R.id.gateway_wrap);
|
||||
mSelectVpnType = findViewById(R.id.vpn_type);
|
||||
mTncNotice = findViewById(R.id.tnc_notice);
|
||||
|
||||
mUserCertificate = (ViewGroup)findViewById(R.id.user_certificate_group);
|
||||
mSelectUserCert = (RelativeLayout)findViewById(R.id.select_user_certificate);
|
||||
mUsernamePassword = findViewById(R.id.username_password_group);
|
||||
mUsername = findViewById(R.id.username);
|
||||
mUsernameWrap = findViewById(R.id.username_wrap);
|
||||
mPassword = findViewById(R.id.password);
|
||||
|
||||
mCheckAuto = (CheckBox)findViewById(R.id.ca_auto);
|
||||
mSelectCert = (RelativeLayout)findViewById(R.id.select_certificate);
|
||||
mUserCertificate = findViewById(R.id.user_certificate_group);
|
||||
mSelectUserCert = findViewById(R.id.select_user_certificate);
|
||||
|
||||
mShowAdvanced = (CheckBox)findViewById(R.id.show_advanced);
|
||||
mAdvancedSettings = (ViewGroup)findViewById(R.id.advanced_settings);
|
||||
mCheckAuto = findViewById(R.id.ca_auto);
|
||||
mSelectCert = findViewById(R.id.select_certificate);
|
||||
|
||||
mRemoteId = (MultiAutoCompleteTextView)findViewById(R.id.remote_id);
|
||||
mRemoteIdWrap = (TextInputLayoutHelper) findViewById(R.id.remote_id_wrap);
|
||||
mShowAdvanced = findViewById(R.id.show_advanced);
|
||||
mAdvancedSettings = findViewById(R.id.advanced_settings);
|
||||
|
||||
mRemoteId = findViewById(R.id.remote_id);
|
||||
mRemoteIdWrap = findViewById(R.id.remote_id_wrap);
|
||||
mLocalId = findViewById(R.id.local_id);
|
||||
mLocalIdWrap = findViewById(R.id.local_id_wrap);
|
||||
mDnsServers = findViewById(R.id.dns_servers);
|
||||
mDnsServersWrap = findViewById(R.id.dns_servers_wrap);
|
||||
mMTU = (EditText)findViewById(R.id.mtu);
|
||||
mMTUWrap = (TextInputLayoutHelper) findViewById(R.id.mtu_wrap);
|
||||
mPort = (EditText)findViewById(R.id.port);
|
||||
mPortWrap = (TextInputLayoutHelper) findViewById(R.id.port_wrap);
|
||||
mNATKeepalive = (EditText)findViewById(R.id.nat_keepalive);
|
||||
mNATKeepaliveWrap = (TextInputLayoutHelper) findViewById(R.id.nat_keepalive_wrap);
|
||||
mMTU = findViewById(R.id.mtu);
|
||||
mMTUWrap = findViewById(R.id.mtu_wrap);
|
||||
mPort = findViewById(R.id.port);
|
||||
mPortWrap = findViewById(R.id.port_wrap);
|
||||
mNATKeepalive = findViewById(R.id.nat_keepalive);
|
||||
mNATKeepaliveWrap = findViewById(R.id.nat_keepalive_wrap);
|
||||
mCertReq = findViewById(R.id.cert_req);
|
||||
mUseCrl = findViewById(R.id.use_crl);
|
||||
mUseOcsp = findViewById(R.id.use_ocsp);
|
||||
mStrictRevocation= findViewById(R.id.strict_revocation);
|
||||
mRsaPss= findViewById(R.id.rsa_pss);
|
||||
mIPv6Transport= findViewById(R.id.ipv6_transport);
|
||||
mIncludedSubnets = (EditText)findViewById(R.id.included_subnets);
|
||||
mIncludedSubnetsWrap = (TextInputLayoutHelper)findViewById(R.id.included_subnets_wrap);
|
||||
mExcludedSubnets = (EditText)findViewById(R.id.excluded_subnets);
|
||||
mExcludedSubnetsWrap = (TextInputLayoutHelper)findViewById(R.id.excluded_subnets_wrap);
|
||||
mBlockIPv4 = (CheckBox)findViewById(R.id.split_tunneling_v4);
|
||||
mBlockIPv6 = (CheckBox)findViewById(R.id.split_tunneling_v6);
|
||||
mStrictRevocation = findViewById(R.id.strict_revocation);
|
||||
mRsaPss = findViewById(R.id.rsa_pss);
|
||||
mIPv6Transport = findViewById(R.id.ipv6_transport);
|
||||
mIncludedSubnets = findViewById(R.id.included_subnets);
|
||||
mIncludedSubnetsWrap = findViewById(R.id.included_subnets_wrap);
|
||||
mExcludedSubnets = findViewById(R.id.excluded_subnets);
|
||||
mExcludedSubnetsWrap = findViewById(R.id.excluded_subnets_wrap);
|
||||
mBlockIPv4 = findViewById(R.id.split_tunneling_v4);
|
||||
mBlockIPv6 = findViewById(R.id.split_tunneling_v6);
|
||||
|
||||
mSelectSelectedAppsHandling = (Spinner)findViewById(R.id.apps_handling);
|
||||
mSelectApps = (RelativeLayout)findViewById(R.id.select_applications);
|
||||
mSelectSelectedAppsHandling = findViewById(R.id.apps_handling);
|
||||
mSelectApps = findViewById(R.id.select_applications);
|
||||
|
||||
mIkeProposal = (EditText)findViewById(R.id.ike_proposal);
|
||||
mIkeProposalWrap = (TextInputLayoutHelper)findViewById(R.id.ike_proposal_wrap);
|
||||
mEspProposal = (EditText)findViewById(R.id.esp_proposal);
|
||||
mEspProposalWrap = (TextInputLayoutHelper)findViewById(R.id.esp_proposal_wrap);
|
||||
mIkeProposal = findViewById(R.id.ike_proposal);
|
||||
mIkeProposalWrap = findViewById(R.id.ike_proposal_wrap);
|
||||
mEspProposal = findViewById(R.id.esp_proposal);
|
||||
mEspProposalWrap = findViewById(R.id.esp_proposal_wrap);
|
||||
/* make the link clickable */
|
||||
((TextView)findViewById(R.id.proposal_intro)).setMovementMethod(LinkMovementMethod.getInstance());
|
||||
|
||||
mProfileIdLabel = (TextView)findViewById(R.id.profile_id_label);
|
||||
mProfileId = (TextView)findViewById(R.id.profile_id);
|
||||
mProfileIdLabel = findViewById(R.id.profile_id_label);
|
||||
mProfileId = findViewById(R.id.profile_id);
|
||||
|
||||
final SpaceTokenizer spaceTokenizer = new SpaceTokenizer();
|
||||
mName.setTokenizer(spaceTokenizer);
|
||||
@@ -262,14 +265,8 @@ public class VpnProfileDetailActivity extends AppCompatActivity
|
||||
mName.setAdapter(gatewayAdapter);
|
||||
mRemoteId.setAdapter(gatewayAdapter);
|
||||
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP)
|
||||
mGateway.addTextChangedListener(new TextWatcher()
|
||||
{
|
||||
findViewById(R.id.apps).setVisibility(View.GONE);
|
||||
mSelectSelectedAppsHandling.setVisibility(View.GONE);
|
||||
mSelectApps.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
mGateway.addTextChangedListener(new TextWatcher() {
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
|
||||
|
||||
@@ -294,7 +291,8 @@ public class VpnProfileDetailActivity extends AppCompatActivity
|
||||
}
|
||||
});
|
||||
|
||||
mSelectVpnType.setOnItemSelectedListener(new OnItemSelectedListener() {
|
||||
mSelectVpnType.setOnItemSelectedListener(new OnItemSelectedListener()
|
||||
{
|
||||
@Override
|
||||
public void onItemSelected(AdapterView<?> parent, View view, int position, long id)
|
||||
{
|
||||
@@ -312,7 +310,8 @@ public class VpnProfileDetailActivity extends AppCompatActivity
|
||||
|
||||
((TextView)mTncNotice.findViewById(android.R.id.text1)).setText(R.string.tnc_notice_title);
|
||||
((TextView)mTncNotice.findViewById(android.R.id.text2)).setText(R.string.tnc_notice_subtitle);
|
||||
mTncNotice.setOnClickListener(new OnClickListener() {
|
||||
mTncNotice.setOnClickListener(new OnClickListener()
|
||||
{
|
||||
@Override
|
||||
public void onClick(View v)
|
||||
{
|
||||
@@ -321,14 +320,15 @@ public class VpnProfileDetailActivity extends AppCompatActivity
|
||||
});
|
||||
|
||||
mSelectUserCert.setOnClickListener(new SelectUserCertOnClickListener());
|
||||
((Button)findViewById(R.id.install_user_certificate)).setOnClickListener(v -> {
|
||||
findViewById(R.id.install_user_certificate).setOnClickListener(v -> {
|
||||
Intent intent = KeyChain.createInstallIntent();
|
||||
mInstallPKCS12.launch(intent);
|
||||
});
|
||||
mSelectUserIdAdapter = new CertificateIdentitiesAdapter(this);
|
||||
mLocalId.setAdapter(mSelectUserIdAdapter);
|
||||
|
||||
mCheckAuto.setOnCheckedChangeListener(new OnCheckedChangeListener() {
|
||||
mCheckAuto.setOnCheckedChangeListener(new OnCheckedChangeListener()
|
||||
{
|
||||
@Override
|
||||
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked)
|
||||
{
|
||||
@@ -336,7 +336,8 @@ public class VpnProfileDetailActivity extends AppCompatActivity
|
||||
}
|
||||
});
|
||||
|
||||
mSelectCert.setOnClickListener(new OnClickListener() {
|
||||
mSelectCert.setOnClickListener(new OnClickListener()
|
||||
{
|
||||
@Override
|
||||
public void onClick(View v)
|
||||
{
|
||||
@@ -346,7 +347,8 @@ public class VpnProfileDetailActivity extends AppCompatActivity
|
||||
}
|
||||
});
|
||||
|
||||
mShowAdvanced.setOnCheckedChangeListener(new OnCheckedChangeListener() {
|
||||
mShowAdvanced.setOnCheckedChangeListener(new OnCheckedChangeListener()
|
||||
{
|
||||
@Override
|
||||
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked)
|
||||
{
|
||||
@@ -354,7 +356,8 @@ public class VpnProfileDetailActivity extends AppCompatActivity
|
||||
}
|
||||
});
|
||||
|
||||
mSelectSelectedAppsHandling.setOnItemSelectedListener(new OnItemSelectedListener() {
|
||||
mSelectSelectedAppsHandling.setOnItemSelectedListener(new OnItemSelectedListener()
|
||||
{
|
||||
@Override
|
||||
public void onItemSelected(AdapterView<?> parent, View view, int position, long id)
|
||||
{
|
||||
@@ -370,21 +373,23 @@ public class VpnProfileDetailActivity extends AppCompatActivity
|
||||
}
|
||||
});
|
||||
|
||||
mSelectApps.setOnClickListener(new OnClickListener() {
|
||||
mSelectApps.setOnClickListener(new OnClickListener()
|
||||
{
|
||||
@Override
|
||||
public void onClick(View v)
|
||||
{
|
||||
Intent intent = new Intent(VpnProfileDetailActivity.this, SelectedApplicationsActivity.class);
|
||||
intent.putExtra(VpnProfileDataSource.KEY_SELECTED_APPS_LIST, new ArrayList<>(mSelectedApps));
|
||||
intent.putExtra(VpnProfileDataSource.KEY_READ_ONLY, mProfile.isReadOnly());
|
||||
mSelectApplications.launch(intent);
|
||||
}
|
||||
});
|
||||
|
||||
mId = savedInstanceState == null ? null : savedInstanceState.getLong(VpnProfileDataSource.KEY_ID);
|
||||
if (mId == null)
|
||||
mUuid = savedInstanceState == null ? null : savedInstanceState.getString(VpnProfileDataSource.KEY_UUID);
|
||||
if (mUuid == null)
|
||||
{
|
||||
Bundle extras = getIntent().getExtras();
|
||||
mId = extras == null ? null : extras.getLong(VpnProfileDataSource.KEY_ID);
|
||||
mUuid = extras == null ? null : extras.getString(VpnProfileDataSource.KEY_UUID);
|
||||
}
|
||||
|
||||
loadProfileData(savedInstanceState);
|
||||
@@ -406,9 +411,9 @@ public class VpnProfileDetailActivity extends AppCompatActivity
|
||||
protected void onSaveInstanceState(Bundle outState)
|
||||
{
|
||||
super.onSaveInstanceState(outState);
|
||||
if (mId != null)
|
||||
if (mUuid != null)
|
||||
{
|
||||
outState.putLong(VpnProfileDataSource.KEY_ID, mId);
|
||||
outState.putString(VpnProfileDataSource.KEY_UUID, mUuid);
|
||||
}
|
||||
if (mUserCertEntry != null)
|
||||
{
|
||||
@@ -489,7 +494,8 @@ public class VpnProfileDetailActivity extends AppCompatActivity
|
||||
AlertDialog.Builder adb = new AlertDialog.Builder(VpnProfileDetailActivity.this);
|
||||
adb.setTitle(R.string.alert_text_nocertfound_title);
|
||||
adb.setMessage(R.string.alert_text_nocertfound);
|
||||
adb.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
|
||||
adb.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener()
|
||||
{
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int id)
|
||||
{
|
||||
@@ -614,16 +620,17 @@ public class VpnProfileDetailActivity extends AppCompatActivity
|
||||
mDataSource.insertProfile(mProfile);
|
||||
}
|
||||
Intent intent = new Intent(Constants.VPN_PROFILES_CHANGED);
|
||||
intent.putExtra(Constants.VPN_PROFILES_SINGLE, mProfile.getId());
|
||||
intent.putExtra(Constants.VPN_PROFILES_SINGLE, mProfile.getUUID().toString());
|
||||
LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
|
||||
|
||||
setResult(RESULT_OK, new Intent().putExtra(VpnProfileDataSource.KEY_ID, mProfile.getId()));
|
||||
setResult(RESULT_OK, new Intent().putExtra(VpnProfileDataSource.KEY_UUID, mProfile.getUUID().toString()));
|
||||
finish();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify the user input and display error messages.
|
||||
*
|
||||
* @return true if the input is valid
|
||||
*/
|
||||
private boolean verifyInput()
|
||||
@@ -755,9 +762,9 @@ public class VpnProfileDetailActivity extends AppCompatActivity
|
||||
Integer flags = null;
|
||||
|
||||
getSupportActionBar().setTitle(R.string.add_profile);
|
||||
if (mId != null && mId != 0)
|
||||
if (mUuid != null)
|
||||
{
|
||||
mProfile = mDataSource.getVpnProfile(mId);
|
||||
mProfile = mDataSource.getVpnProfile(mUuid);
|
||||
if (mProfile != null)
|
||||
{
|
||||
mName.setText(mProfile.getName());
|
||||
@@ -785,11 +792,13 @@ public class VpnProfileDetailActivity extends AppCompatActivity
|
||||
local_id = mProfile.getLocalId();
|
||||
alias = mProfile.getCertificateAlias();
|
||||
getSupportActionBar().setTitle(mProfile.getName());
|
||||
|
||||
setReadOnly(mProfile.isReadOnly());
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.e(VpnProfileDetailActivity.class.getSimpleName(),
|
||||
"VPN profile with id " + mId + " not found");
|
||||
"VPN profile with UUID " + mUuid + " not found");
|
||||
finish();
|
||||
}
|
||||
}
|
||||
@@ -849,6 +858,46 @@ public class VpnProfileDetailActivity extends AppCompatActivity
|
||||
}
|
||||
}
|
||||
|
||||
private void setReadOnly(final boolean readOnly)
|
||||
{
|
||||
mManagedProfile.setVisibility(readOnly ? View.VISIBLE : View.GONE);
|
||||
|
||||
mName.setEnabled(!readOnly);
|
||||
mGateway.setEnabled(!readOnly);
|
||||
mUsername.setEnabled(!readOnly);
|
||||
mRemoteId.setEnabled(!readOnly);
|
||||
mLocalId.setEnabled(!readOnly);
|
||||
mMTU.setEnabled(!readOnly);
|
||||
mPort.setEnabled(!readOnly);
|
||||
mNATKeepalive.setEnabled(!readOnly);
|
||||
mIncludedSubnets.setEnabled(!readOnly);
|
||||
mExcludedSubnets.setEnabled(!readOnly);
|
||||
mBlockIPv4.setEnabled(!readOnly);
|
||||
mBlockIPv6.setEnabled(!readOnly);
|
||||
mIkeProposal.setEnabled(!readOnly);
|
||||
mEspProposal.setEnabled(!readOnly);
|
||||
mDnsServers.setEnabled(!readOnly);
|
||||
|
||||
mSelectVpnType.setEnabled(!readOnly);
|
||||
mCertReq.setEnabled(!readOnly);
|
||||
mUseCrl.setEnabled(!readOnly);
|
||||
mUseOcsp.setEnabled(!readOnly);
|
||||
mStrictRevocation.setEnabled(!readOnly);
|
||||
mRsaPss.setEnabled(!readOnly);
|
||||
mIPv6Transport.setEnabled(!readOnly);
|
||||
|
||||
mCheckAuto.setEnabled(!readOnly);
|
||||
mSelectSelectedAppsHandling.setEnabled(!readOnly);
|
||||
|
||||
findViewById(R.id.install_user_certificate).setEnabled(!readOnly);
|
||||
|
||||
if (readOnly)
|
||||
{
|
||||
mSelectCert.setOnClickListener(null);
|
||||
mSelectUserCert.setOnClickListener(null);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the string value in the given text box or null if empty
|
||||
*
|
||||
@@ -969,7 +1018,8 @@ public class VpnProfileDetailActivity extends AppCompatActivity
|
||||
{
|
||||
final X509Certificate[] chain = KeyChain.getCertificateChain(VpnProfileDetailActivity.this, alias);
|
||||
/* alias() is not called from our main thread */
|
||||
runOnUiThread(new Runnable() {
|
||||
runOnUiThread(new Runnable()
|
||||
{
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
@@ -992,7 +1042,8 @@ public class VpnProfileDetailActivity extends AppCompatActivity
|
||||
/**
|
||||
* Callback interface for the user certificate loader.
|
||||
*/
|
||||
private interface UserCertificateLoaderCallback {
|
||||
private interface UserCertificateLoaderCallback
|
||||
{
|
||||
void onComplete(X509Certificate result);
|
||||
}
|
||||
|
||||
@@ -1050,7 +1101,8 @@ public class VpnProfileDetailActivity extends AppCompatActivity
|
||||
return new AlertDialog.Builder(getActivity())
|
||||
.setTitle(R.string.tnc_notice_title)
|
||||
.setMessage(HtmlCompat.fromHtml(getString(R.string.tnc_notice_details), HtmlCompat.FROM_HTML_MODE_LEGACY))
|
||||
.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
|
||||
.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener()
|
||||
{
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int id)
|
||||
{
|
||||
@@ -1111,7 +1163,7 @@ public class VpnProfileDetailActivity extends AppCompatActivity
|
||||
if (text instanceof Spanned)
|
||||
{
|
||||
SpannableString sp = new SpannableString(text + " ");
|
||||
TextUtils.copySpansFrom((Spanned) text, 0, text.length(), Object.class, sp, 0);
|
||||
TextUtils.copySpansFrom((Spanned)text, 0, text.length(), Object.class, sp, 0);
|
||||
return sp;
|
||||
}
|
||||
else
|
||||
|
||||
+31
-28
@@ -21,7 +21,6 @@ import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.security.KeyChain;
|
||||
import android.security.KeyChainAliasCallback;
|
||||
@@ -46,6 +45,7 @@ import org.strongswan.android.R;
|
||||
import org.strongswan.android.data.VpnProfile;
|
||||
import org.strongswan.android.data.VpnProfile.SelectedAppsHandling;
|
||||
import org.strongswan.android.data.VpnProfileDataSource;
|
||||
import org.strongswan.android.data.VpnProfileSource;
|
||||
import org.strongswan.android.data.VpnType;
|
||||
import org.strongswan.android.data.VpnType.VpnTypeFeature;
|
||||
import org.strongswan.android.logic.TrustedCertificateManager;
|
||||
@@ -135,12 +135,12 @@ public class VpnProfileImportActivity extends AppCompatActivity
|
||||
}
|
||||
);
|
||||
|
||||
private LoaderManager.LoaderCallbacks<ProfileLoadResult> mProfileLoaderCallbacks = new LoaderManager.LoaderCallbacks<ProfileLoadResult>()
|
||||
private final LoaderManager.LoaderCallbacks<ProfileLoadResult> mProfileLoaderCallbacks = new LoaderManager.LoaderCallbacks<ProfileLoadResult>()
|
||||
{
|
||||
@Override
|
||||
public Loader<ProfileLoadResult> onCreateLoader(int id, Bundle args)
|
||||
{
|
||||
return new ProfileLoader(VpnProfileImportActivity.this, (Uri)args.getParcelable(PROFILE_URI));
|
||||
return new ProfileLoader(VpnProfileImportActivity.this, args.getParcelable(PROFILE_URI));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -156,7 +156,7 @@ public class VpnProfileImportActivity extends AppCompatActivity
|
||||
}
|
||||
};
|
||||
|
||||
private LoaderManager.LoaderCallbacks<TrustedCertificateEntry> mUserCertificateLoaderCallbacks = new LoaderManager.LoaderCallbacks<TrustedCertificateEntry>()
|
||||
private final LoaderManager.LoaderCallbacks<TrustedCertificateEntry> mUserCertificateLoaderCallbacks = new LoaderManager.LoaderCallbacks<TrustedCertificateEntry>()
|
||||
{
|
||||
@Override
|
||||
public Loader<TrustedCertificateEntry> onCreateLoader(int id, Bundle args)
|
||||
@@ -185,29 +185,29 @@ public class VpnProfileImportActivity extends AppCompatActivity
|
||||
getSupportActionBar().setHomeAsUpIndicator(R.drawable.ic_close_white_24dp);
|
||||
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||
|
||||
mDataSource = new VpnProfileDataSource(this);
|
||||
mDataSource = new VpnProfileSource(this);
|
||||
mDataSource.open();
|
||||
|
||||
setContentView(R.layout.profile_import_view);
|
||||
|
||||
mProgressBar = findViewById(R.id.progress_bar);
|
||||
mExistsWarning = (TextView)findViewById(R.id.exists_warning);
|
||||
mBasicDataGroup = (ViewGroup)findViewById(R.id.basic_data_group);
|
||||
mName = (TextView)findViewById(R.id.name);
|
||||
mGateway = (TextView)findViewById(R.id.gateway);
|
||||
mSelectVpnType = (TextView)findViewById(R.id.vpn_type);
|
||||
mExistsWarning = findViewById(R.id.exists_warning);
|
||||
mBasicDataGroup = findViewById(R.id.basic_data_group);
|
||||
mName = findViewById(R.id.name);
|
||||
mGateway = findViewById(R.id.gateway);
|
||||
mSelectVpnType = findViewById(R.id.vpn_type);
|
||||
|
||||
mUsernamePassword = (ViewGroup)findViewById(R.id.username_password_group);
|
||||
mUsername = (EditText)findViewById(R.id.username);
|
||||
mUsernameWrap = (TextInputLayoutHelper) findViewById(R.id.username_wrap);
|
||||
mPassword = (EditText)findViewById(R.id.password);
|
||||
mUsernamePassword = findViewById(R.id.username_password_group);
|
||||
mUsername = findViewById(R.id.username);
|
||||
mUsernameWrap = findViewById(R.id.username_wrap);
|
||||
mPassword = findViewById(R.id.password);
|
||||
|
||||
mUserCertificate = (ViewGroup)findViewById(R.id.user_certificate_group);
|
||||
mSelectUserCert = (RelativeLayout)findViewById(R.id.select_user_certificate);
|
||||
mImportUserCert = (Button)findViewById(R.id.import_user_certificate);
|
||||
mUserCertificate = findViewById(R.id.user_certificate_group);
|
||||
mSelectUserCert = findViewById(R.id.select_user_certificate);
|
||||
mImportUserCert = findViewById(R.id.import_user_certificate);
|
||||
|
||||
mRemoteCertificate = (ViewGroup)findViewById(R.id.remote_certificate_group);
|
||||
mRemoteCert = (RelativeLayout)findViewById(R.id.remote_certificate);
|
||||
mRemoteCertificate = findViewById(R.id.remote_certificate_group);
|
||||
mRemoteCert = findViewById(R.id.remote_certificate);
|
||||
|
||||
mExistsWarning.setVisibility(View.GONE);
|
||||
mBasicDataGroup.setVisibility(View.GONE);
|
||||
@@ -216,7 +216,8 @@ public class VpnProfileImportActivity extends AppCompatActivity
|
||||
mRemoteCertificate.setVisibility(View.GONE);
|
||||
|
||||
mSelectUserCert.setOnClickListener(new SelectUserCertOnClickListener());
|
||||
mImportUserCert.setOnClickListener(new View.OnClickListener() {
|
||||
mImportUserCert.setOnClickListener(new View.OnClickListener()
|
||||
{
|
||||
@Override
|
||||
public void onClick(View v)
|
||||
{
|
||||
@@ -233,7 +234,7 @@ public class VpnProfileImportActivity extends AppCompatActivity
|
||||
{
|
||||
loadProfile(getIntent().getData());
|
||||
}
|
||||
else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT)
|
||||
else
|
||||
{
|
||||
Intent openIntent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
|
||||
openIntent.setType("*/*");
|
||||
@@ -535,9 +536,9 @@ public class VpnProfileImportActivity extends AppCompatActivity
|
||||
if (split != null)
|
||||
{
|
||||
String included = getSubnets(split, "subnets");
|
||||
profile.setIncludedSubnets(included != null ? included : null);
|
||||
profile.setIncludedSubnets(included);
|
||||
String excluded = getSubnets(split, "excluded");
|
||||
profile.setExcludedSubnets(excluded != null ? excluded : null);
|
||||
profile.setExcludedSubnets(excluded);
|
||||
int st = 0;
|
||||
st |= split.optBoolean("block-ipv4") ? VpnProfile.SPLIT_TUNNELING_BLOCK_IPV4 : 0;
|
||||
st |= split.optBoolean("block-ipv6") ? VpnProfile.SPLIT_TUNNELING_BLOCK_IPV6 : 0;
|
||||
@@ -674,7 +675,7 @@ public class VpnProfileImportActivity extends AppCompatActivity
|
||||
updateProfileData();
|
||||
if (mExisting != null)
|
||||
{
|
||||
mProfile.setId(mExisting.getId());
|
||||
mProfile.setUUID(mExisting.getUUID());
|
||||
mDataSource.updateVpnProfile(mProfile);
|
||||
}
|
||||
else
|
||||
@@ -696,20 +697,21 @@ public class VpnProfileImportActivity extends AppCompatActivity
|
||||
}
|
||||
}
|
||||
Intent intent = new Intent(Constants.VPN_PROFILES_CHANGED);
|
||||
intent.putExtra(Constants.VPN_PROFILES_SINGLE, mProfile.getId());
|
||||
intent.putExtra(Constants.VPN_PROFILES_SINGLE, mProfile.getUUID().toString());
|
||||
LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
|
||||
|
||||
intent = new Intent(this, MainActivity.class);
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
startActivity(intent);
|
||||
|
||||
setResult(RESULT_OK, new Intent().putExtra(VpnProfileDataSource.KEY_ID, mProfile.getId()));
|
||||
setResult(RESULT_OK, new Intent().putExtra(VpnProfileDataSource.KEY_UUID, mProfile.getUUID().toString()));
|
||||
finish();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify the user input and display error messages.
|
||||
*
|
||||
* @return true if the input is valid
|
||||
*/
|
||||
private boolean verifyInput()
|
||||
@@ -899,14 +901,15 @@ public class VpnProfileImportActivity extends AppCompatActivity
|
||||
public void alias(final String alias)
|
||||
{
|
||||
/* alias() is not called from our main thread */
|
||||
runOnUiThread(new Runnable() {
|
||||
runOnUiThread(new Runnable()
|
||||
{
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
mUserCertLoading = alias;
|
||||
updateUserCertView();
|
||||
if (alias != null)
|
||||
{ /* otherwise the dialog was canceled, the request denied */
|
||||
{ /* otherwise the dialog was canceled, the request denied */
|
||||
LoaderManager.getInstance(VpnProfileImportActivity.this).restartLoader(USER_CERT_LOADER, null, mUserCertificateLoaderCallbacks);
|
||||
}
|
||||
}
|
||||
|
||||
+83
-32
@@ -1,4 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Relution GmbH
|
||||
* Copyright (C) 2012-2019 Tobias Brunner
|
||||
* Copyright (C) 2012 Giuliano Grassi
|
||||
* Copyright (C) 2012 Ralf Sager
|
||||
@@ -39,8 +40,12 @@ import android.widget.ListView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import org.strongswan.android.R;
|
||||
import org.strongswan.android.data.ManagedConfiguration;
|
||||
import org.strongswan.android.data.ManagedConfigurationService;
|
||||
import org.strongswan.android.data.VpnProfile;
|
||||
import org.strongswan.android.data.VpnProfileDataSource;
|
||||
import org.strongswan.android.data.VpnProfileSource;
|
||||
import org.strongswan.android.logic.StrongSwanApplication;
|
||||
import org.strongswan.android.ui.adapter.VpnProfileAdapter;
|
||||
import org.strongswan.android.utils.Constants;
|
||||
|
||||
@@ -48,6 +53,8 @@ import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
import androidx.fragment.app.Fragment;
|
||||
@@ -62,18 +69,22 @@ public class VpnProfileListFragment extends Fragment
|
||||
private VpnProfileAdapter mListAdapter;
|
||||
private ListView mListView;
|
||||
private OnVpnProfileSelectedListener mListener;
|
||||
private HashSet<Integer> mSelected;
|
||||
private Set<Integer> mSelected;
|
||||
private boolean mReadOnly;
|
||||
|
||||
private BroadcastReceiver mProfilesChanged = new BroadcastReceiver()
|
||||
private ManagedConfigurationService mManagedConfigurationService;
|
||||
|
||||
private final BroadcastReceiver mProfilesChanged = new BroadcastReceiver()
|
||||
{
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent)
|
||||
{
|
||||
long id, ids[];
|
||||
if ((id = intent.getLongExtra(Constants.VPN_PROFILES_SINGLE, 0)) > 0)
|
||||
String uuid;
|
||||
String[] uuids;
|
||||
|
||||
if ((uuid = intent.getStringExtra(Constants.VPN_PROFILES_SINGLE)) != null)
|
||||
{
|
||||
VpnProfile profile = mDataSource.getVpnProfile(id);
|
||||
VpnProfile profile = mDataSource.getVpnProfile(uuid);
|
||||
if (profile != null)
|
||||
{ /* in case this was an edit, we remove it first */
|
||||
mVpnProfiles.remove(profile);
|
||||
@@ -81,20 +92,26 @@ public class VpnProfileListFragment extends Fragment
|
||||
mListAdapter.notifyDataSetChanged();
|
||||
}
|
||||
}
|
||||
else if ((ids = intent.getLongArrayExtra(Constants.VPN_PROFILES_MULTIPLE)) != null)
|
||||
else if ((uuids = intent.getStringArrayExtra(Constants.VPN_PROFILES_MULTIPLE)) != null)
|
||||
{
|
||||
for (long i : ids)
|
||||
for (final String id : uuids)
|
||||
{
|
||||
Iterator<VpnProfile> profiles = mVpnProfiles.iterator();
|
||||
final Iterator<VpnProfile> profiles = mVpnProfiles.iterator();
|
||||
while (profiles.hasNext())
|
||||
{
|
||||
VpnProfile profile = profiles.next();
|
||||
if (profile.getId() == i)
|
||||
{
|
||||
final VpnProfile profile = profiles.next();
|
||||
if (Objects.equals(profile.getUUID().toString(), id))
|
||||
{ /* in case this was an edit, we remove it first */
|
||||
profiles.remove();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
VpnProfile profile = mDataSource.getVpnProfile(id);
|
||||
if (profile != null)
|
||||
{
|
||||
mVpnProfiles.add(profile);
|
||||
}
|
||||
}
|
||||
mListAdapter.notifyDataSetChanged();
|
||||
}
|
||||
@@ -104,7 +121,8 @@ public class VpnProfileListFragment extends Fragment
|
||||
/**
|
||||
* The activity containing this fragment should implement this interface
|
||||
*/
|
||||
public interface OnVpnProfileSelectedListener {
|
||||
public interface OnVpnProfileSelectedListener
|
||||
{
|
||||
void onVpnProfileSelected(VpnProfile profile);
|
||||
}
|
||||
|
||||
@@ -159,9 +177,11 @@ public class VpnProfileListFragment extends Fragment
|
||||
mSelected = selected != null ? new HashSet<>(selected) : new HashSet<>();
|
||||
}
|
||||
|
||||
mDataSource = new VpnProfileDataSource(this.getActivity());
|
||||
mDataSource = new VpnProfileSource(this.getActivity());
|
||||
mDataSource.open();
|
||||
|
||||
mManagedConfigurationService = StrongSwanApplication.getInstance().getManagedConfigurationService();
|
||||
|
||||
/* cached list of profiles used as backend for the ListView */
|
||||
mVpnProfiles = mDataSource.getAllVpnProfiles();
|
||||
|
||||
@@ -204,21 +224,32 @@ public class VpnProfileListFragment extends Fragment
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item)
|
||||
public void onPrepareOptionsMenu(Menu menu)
|
||||
{
|
||||
switch (item.getItemId())
|
||||
final MenuItem addProfile = menu.findItem(R.id.add_profile);
|
||||
if (addProfile != null)
|
||||
{
|
||||
case R.id.add_profile:
|
||||
Intent connectionIntent = new Intent(getActivity(),
|
||||
VpnProfileDetailActivity.class);
|
||||
startActivity(connectionIntent);
|
||||
return true;
|
||||
default:
|
||||
return super.onOptionsItemSelected(item);
|
||||
final ManagedConfiguration managedConfiguration = mManagedConfigurationService.getManagedConfiguration();
|
||||
addProfile.setVisible(managedConfiguration.isAllowProfileCreation());
|
||||
addProfile.setEnabled(managedConfiguration.isAllowProfileCreation());
|
||||
}
|
||||
}
|
||||
|
||||
private final OnItemClickListener mVpnProfileClicked = new OnItemClickListener() {
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item)
|
||||
{
|
||||
if (item.getItemId() == R.id.add_profile)
|
||||
{
|
||||
Intent connectionIntent = new Intent(getActivity(),
|
||||
VpnProfileDetailActivity.class);
|
||||
startActivity(connectionIntent);
|
||||
return true;
|
||||
}
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
private final OnItemClickListener mVpnProfileClicked = new OnItemClickListener()
|
||||
{
|
||||
@Override
|
||||
public void onItemClick(AdapterView<?> a, View v, int position, long id)
|
||||
{
|
||||
@@ -229,21 +260,31 @@ public class VpnProfileListFragment extends Fragment
|
||||
}
|
||||
};
|
||||
|
||||
private final MultiChoiceModeListener mVpnProfileSelected = new MultiChoiceModeListener() {
|
||||
private final MultiChoiceModeListener mVpnProfileSelected = new MultiChoiceModeListener()
|
||||
{
|
||||
private MenuItem mEditProfile;
|
||||
private MenuItem mCopyProfile;
|
||||
private MenuItem mDeleteProfile;
|
||||
|
||||
private boolean mCanEdit;
|
||||
private boolean mCanCopy;
|
||||
private boolean mCanDelete;
|
||||
|
||||
private int mReadOnlyCount;
|
||||
|
||||
@Override
|
||||
public boolean onPrepareActionMode(ActionMode mode, Menu menu)
|
||||
{
|
||||
mEditProfile.setEnabled(mSelected.size() == 1);
|
||||
mCopyProfile.setEnabled(mEditProfile.isEnabled());
|
||||
mEditProfile.setEnabled(mCanEdit);
|
||||
mCopyProfile.setEnabled(mCanCopy);
|
||||
mDeleteProfile.setEnabled(mCanDelete);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyActionMode(ActionMode mode)
|
||||
{
|
||||
mReadOnlyCount = 0;
|
||||
mSelected.clear();
|
||||
}
|
||||
|
||||
@@ -254,6 +295,7 @@ public class VpnProfileListFragment extends Fragment
|
||||
inflater.inflate(R.menu.profile_list_context, menu);
|
||||
mEditProfile = menu.findItem(R.id.edit_profile);
|
||||
mCopyProfile = menu.findItem(R.id.copy_profile);
|
||||
mDeleteProfile = menu.findItem(R.id.delete_profile);
|
||||
mode.setTitle(R.string.select_profiles);
|
||||
return true;
|
||||
}
|
||||
@@ -268,7 +310,7 @@ public class VpnProfileListFragment extends Fragment
|
||||
int position = mSelected.iterator().next();
|
||||
VpnProfile profile = (VpnProfile)mListView.getItemAtPosition(position);
|
||||
Intent connectionIntent = new Intent(getActivity(), VpnProfileDetailActivity.class);
|
||||
connectionIntent.putExtra(VpnProfileDataSource.KEY_ID, profile.getId());
|
||||
connectionIntent.putExtra(VpnProfileDataSource.KEY_UUID, profile.getUUID().toString());
|
||||
startActivity(connectionIntent);
|
||||
break;
|
||||
}
|
||||
@@ -282,11 +324,11 @@ public class VpnProfileListFragment extends Fragment
|
||||
mDataSource.insertProfile(profile);
|
||||
|
||||
Intent intent = new Intent(Constants.VPN_PROFILES_CHANGED);
|
||||
intent.putExtra(Constants.VPN_PROFILES_SINGLE, profile.getId());
|
||||
intent.putExtra(Constants.VPN_PROFILES_SINGLE, profile.getUUID().toString());
|
||||
LocalBroadcastManager.getInstance(getActivity()).sendBroadcast(intent);
|
||||
|
||||
Intent connectionIntent = new Intent(getActivity(), VpnProfileDetailActivity.class);
|
||||
connectionIntent.putExtra(VpnProfileDataSource.KEY_ID, profile.getId());
|
||||
connectionIntent.putExtra(VpnProfileDataSource.KEY_UUID, profile.getUUID().toString());
|
||||
startActivity(connectionIntent);
|
||||
break;
|
||||
}
|
||||
@@ -297,15 +339,15 @@ public class VpnProfileListFragment extends Fragment
|
||||
{
|
||||
profiles.add((VpnProfile)mListView.getItemAtPosition(position));
|
||||
}
|
||||
long ids[] = new long[profiles.size()];
|
||||
String[] uuids = new String[profiles.size()];
|
||||
for (int i = 0; i < profiles.size(); i++)
|
||||
{
|
||||
VpnProfile profile = profiles.get(i);
|
||||
ids[i] = profile.getId();
|
||||
uuids[i] = profile.getUUID().toString();
|
||||
mDataSource.deleteVpnProfile(profile);
|
||||
}
|
||||
Intent intent = new Intent(Constants.VPN_PROFILES_CHANGED);
|
||||
intent.putExtra(Constants.VPN_PROFILES_MULTIPLE, ids);
|
||||
intent.putExtra(Constants.VPN_PROFILES_MULTIPLE, uuids);
|
||||
LocalBroadcastManager.getInstance(getActivity()).sendBroadcast(intent);
|
||||
Toast.makeText(VpnProfileListFragment.this.getActivity(),
|
||||
R.string.profiles_deleted, Toast.LENGTH_SHORT).show();
|
||||
@@ -322,13 +364,17 @@ public class VpnProfileListFragment extends Fragment
|
||||
public void onItemCheckedStateChanged(ActionMode mode, int position,
|
||||
long id, boolean checked)
|
||||
{
|
||||
VpnProfile profile = (VpnProfile)mListView.getItemAtPosition(position);
|
||||
|
||||
if (checked)
|
||||
{
|
||||
mSelected.add(position);
|
||||
mReadOnlyCount += profile.isReadOnly() ? 1 : 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
mSelected.remove(position);
|
||||
mReadOnlyCount -= profile.isReadOnly() ? 1 : 0;
|
||||
}
|
||||
final int checkedCount = mSelected.size();
|
||||
switch (checkedCount)
|
||||
@@ -343,6 +389,11 @@ public class VpnProfileListFragment extends Fragment
|
||||
mode.setSubtitle(String.format(getString(R.string.x_profiles_selected), checkedCount));
|
||||
break;
|
||||
}
|
||||
|
||||
mCanEdit = checkedCount == 1;
|
||||
mCanCopy = checkedCount == 1 && mReadOnlyCount == 0;
|
||||
mCanDelete = checkedCount > 0 && mReadOnlyCount == 0;
|
||||
|
||||
mode.invalidate();
|
||||
}
|
||||
};
|
||||
|
||||
+1
-1
@@ -45,7 +45,7 @@ public class VpnProfileSelectActivity extends AppCompatActivity implements OnVpn
|
||||
public void onVpnProfileSelected(VpnProfile profile)
|
||||
{
|
||||
Intent shortcut = new Intent(VpnProfileControlActivity.START_PROFILE);
|
||||
shortcut.putExtra(VpnProfileControlActivity.EXTRA_VPN_PROFILE_ID, profile.getUUID().toString());
|
||||
shortcut.putExtra(VpnProfileControlActivity.EXTRA_VPN_PROFILE_UUID, profile.getUUID().toString());
|
||||
|
||||
ShortcutInfoCompat.Builder builder = new ShortcutInfoCompat.Builder(this, profile.getUUID().toString());
|
||||
builder.setIntent(shortcut);
|
||||
|
||||
+4
-3
@@ -32,6 +32,7 @@ import android.service.quicksettings.TileService;
|
||||
import org.strongswan.android.R;
|
||||
import org.strongswan.android.data.VpnProfile;
|
||||
import org.strongswan.android.data.VpnProfileDataSource;
|
||||
import org.strongswan.android.data.VpnProfileSource;
|
||||
import org.strongswan.android.data.VpnType;
|
||||
import org.strongswan.android.logic.VpnStateService;
|
||||
import org.strongswan.android.utils.Constants;
|
||||
@@ -73,7 +74,7 @@ public class VpnTileService extends TileService implements VpnStateService.VpnSt
|
||||
context.bindService(new Intent(context, VpnStateService.class),
|
||||
mServiceConnection, Service.BIND_AUTO_CREATE);
|
||||
|
||||
mDataSource = new VpnProfileDataSource(this);
|
||||
mDataSource = new VpnProfileSource(this);
|
||||
mDataSource.open();
|
||||
}
|
||||
|
||||
@@ -139,7 +140,7 @@ public class VpnTileService extends TileService implements VpnStateService.VpnSt
|
||||
}
|
||||
else if (mDataSource != null)
|
||||
{ /* always get the plain profile without cached password */
|
||||
profile = mDataSource.getVpnProfile(profile.getId());
|
||||
profile = mDataSource.getVpnProfile(profile.getUUID());
|
||||
}
|
||||
/* reconnect the profile in case of an error */
|
||||
if (mService.getErrorState() == VpnStateService.ErrorState.NO_ERROR)
|
||||
@@ -172,7 +173,7 @@ public class VpnTileService extends TileService implements VpnStateService.VpnSt
|
||||
Intent intent = new Intent(this, VpnProfileControlActivity.class);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
intent.setAction(VpnProfileControlActivity.START_PROFILE);
|
||||
intent.putExtra(VpnProfileControlActivity.EXTRA_VPN_PROFILE_ID, profile.getUUID().toString());
|
||||
intent.putExtra(VpnProfileControlActivity.EXTRA_VPN_PROFILE_UUID, profile.getUUID().toString());
|
||||
if (profile.getVpnType().has(VpnType.VpnTypeFeature.USER_PASS) &&
|
||||
profile.getPassword() == null)
|
||||
{ /* the user will have to enter the password, so collapse the drawer */
|
||||
|
||||
+16
-4
@@ -35,11 +35,12 @@ import java.util.List;
|
||||
|
||||
public class SelectedApplicationsAdapter extends BaseAdapter implements Filterable
|
||||
{
|
||||
private Context mContext;
|
||||
private final Context mContext;
|
||||
private final Object mLock = new Object();
|
||||
private List<SelectedApplicationEntry> mData;
|
||||
private final List<SelectedApplicationEntry> mData;
|
||||
private List<SelectedApplicationEntry> mDataFiltered;
|
||||
private SelectedApplicationsFilter mFilter;
|
||||
private boolean mReadOnly;
|
||||
|
||||
public SelectedApplicationsAdapter(Context context)
|
||||
{
|
||||
@@ -100,9 +101,10 @@ public class SelectedApplicationsAdapter extends BaseAdapter implements Filterab
|
||||
SelectedApplicationEntry item = getItem(position);
|
||||
CheckableLinearLayout checkable = (CheckableLinearLayout)view;
|
||||
checkable.setChecked(item.isSelected());
|
||||
ImageView icon = (ImageView)view.findViewById(R.id.app_icon);
|
||||
checkable.setEnabled(!mReadOnly);
|
||||
ImageView icon = view.findViewById(R.id.app_icon);
|
||||
icon.setImageDrawable(item.getIcon());
|
||||
TextView text = (TextView)view.findViewById(R.id.app_name);
|
||||
TextView text = view.findViewById(R.id.app_name);
|
||||
text.setText(item.toString());
|
||||
return view;
|
||||
}
|
||||
@@ -117,6 +119,16 @@ public class SelectedApplicationsAdapter extends BaseAdapter implements Filterab
|
||||
return mFilter;
|
||||
}
|
||||
|
||||
public boolean isReadOnly()
|
||||
{
|
||||
return mReadOnly;
|
||||
}
|
||||
|
||||
public void setReadOnly(final boolean readOnly)
|
||||
{
|
||||
this.mReadOnly = readOnly;
|
||||
}
|
||||
|
||||
private class SelectedApplicationsFilter extends Filter
|
||||
{
|
||||
|
||||
|
||||
+8
-5
@@ -61,11 +61,13 @@ public class VpnProfileAdapter extends ArrayAdapter<VpnProfile>
|
||||
vpnProfileView = inflater.inflate(resource, null);
|
||||
}
|
||||
VpnProfile profile = getItem(position);
|
||||
TextView tv = (TextView)vpnProfileView.findViewById(R.id.profile_item_name);
|
||||
TextView tv = vpnProfileView.findViewById(R.id.profile_item_name);
|
||||
tv.setText(profile.getName());
|
||||
tv = (TextView)vpnProfileView.findViewById(R.id.profile_item_gateway);
|
||||
tv = vpnProfileView.findViewById(R.id.profile_item_managed);
|
||||
tv.setVisibility(profile.isReadOnly() ? View.VISIBLE : View.GONE);
|
||||
tv = vpnProfileView.findViewById(R.id.profile_item_gateway);
|
||||
tv.setText(getContext().getString(R.string.profile_gateway_label) + ": " + profile.getGateway());
|
||||
tv = (TextView)vpnProfileView.findViewById(R.id.profile_item_username);
|
||||
tv = vpnProfileView.findViewById(R.id.profile_item_username);
|
||||
if (profile.getVpnType().has(VpnTypeFeature.USER_PASS))
|
||||
{ /* if the view is reused we make sure it is visible */
|
||||
tv.setVisibility(View.VISIBLE);
|
||||
@@ -81,7 +83,7 @@ public class VpnProfileAdapter extends ArrayAdapter<VpnProfile>
|
||||
{
|
||||
tv.setVisibility(View.GONE);
|
||||
}
|
||||
tv = (TextView)vpnProfileView.findViewById(R.id.profile_item_certificate);
|
||||
tv = vpnProfileView.findViewById(R.id.profile_item_certificate);
|
||||
if (profile.getVpnType().has(VpnTypeFeature.CERTIFICATE))
|
||||
{
|
||||
tv.setText(getContext().getString(R.string.profile_user_certificate_label) + ": " + profile.getUserCertificateAlias());
|
||||
@@ -103,7 +105,8 @@ public class VpnProfileAdapter extends ArrayAdapter<VpnProfile>
|
||||
|
||||
private void sortItems()
|
||||
{
|
||||
Collections.sort(this.items, new Comparator<VpnProfile>() {
|
||||
Collections.sort(this.items, new Comparator<VpnProfile>()
|
||||
{
|
||||
@Override
|
||||
public int compare(VpnProfile lhs, VpnProfile rhs)
|
||||
{
|
||||
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Relution GmbH
|
||||
*
|
||||
* Copyright (C) secunet Security Networks AG
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*/
|
||||
|
||||
package org.strongswan.android.utils;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.security.cert.CertificateFactory;
|
||||
import java.security.cert.X509Certificate;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
public class Certificates
|
||||
{
|
||||
@NonNull
|
||||
public static X509Certificate from(@NonNull final String certificateData) throws IOException, CertificateException
|
||||
{
|
||||
final byte[] bytes = android.util.Base64.decode(certificateData, android.util.Base64.DEFAULT);
|
||||
|
||||
try (final ByteArrayInputStream stream = new ByteArrayInputStream(bytes))
|
||||
{
|
||||
final CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
|
||||
return (X509Certificate)certificateFactory.generateCertificate(stream);
|
||||
}
|
||||
}
|
||||
|
||||
private Certificates() {}
|
||||
}
|
||||
@@ -0,0 +1,166 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Relution GmbH
|
||||
*
|
||||
* Copyright (C) secunet Security Networks AG
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*/
|
||||
|
||||
package org.strongswan.android.utils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.arch.core.util.Function;
|
||||
import androidx.core.util.Pair;
|
||||
|
||||
public class Difference<T>
|
||||
{
|
||||
@NonNull
|
||||
private final List<T> inserts;
|
||||
@NonNull
|
||||
private final List<Pair<T, T>> updates;
|
||||
@NonNull
|
||||
private final List<T> unchanged;
|
||||
@NonNull
|
||||
private final List<T> deletes;
|
||||
|
||||
@NonNull
|
||||
public static <K, V> Difference<V> between(
|
||||
@NonNull final List<V> existing,
|
||||
@NonNull final List<V> modified,
|
||||
@NonNull final Function<V, K> getKey)
|
||||
{
|
||||
final Map<K, V> existingMap = mapOf(existing, getKey);
|
||||
final Map<K, V> modifiedMap = mapOf(modified, getKey);
|
||||
|
||||
final List<V> inserts = notIn(existingMap, getKey, modified);
|
||||
final List<V> deletes = notIn(modifiedMap, getKey, existing);
|
||||
final List<Pair<V, V>> updates = new ArrayList<>(modifiedMap.size());
|
||||
final List<V> unchanged = new ArrayList<>(existingMap.size());
|
||||
changeBetween(existingMap, modifiedMap, updates, unchanged);
|
||||
|
||||
return new Difference<>(inserts, updates, unchanged, deletes);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private static <K, V> Map<K, V> mapOf(
|
||||
@NonNull final List<V> list,
|
||||
@NonNull final Function<V, K> getKey)
|
||||
{
|
||||
final Map<K, V> map = new HashMap<>(list.size());
|
||||
|
||||
for (final V entry : list)
|
||||
{
|
||||
final K key = getKey.apply(entry);
|
||||
map.put(key, entry);
|
||||
}
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private static <K, V> List<V> notIn(
|
||||
@NonNull final Map<K, V> map,
|
||||
@NonNull final Function<V, K> getKey,
|
||||
@NonNull final List<V> list)
|
||||
{
|
||||
final List<V> filtered = new ArrayList<>(list.size());
|
||||
|
||||
for (final V value : list)
|
||||
{
|
||||
final K key = getKey.apply(value);
|
||||
if (!map.containsKey(key))
|
||||
{
|
||||
filtered.add(value);
|
||||
}
|
||||
}
|
||||
|
||||
return filtered;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private static <K, V> void changeBetween(
|
||||
@NonNull final Map<K, V> existingMap,
|
||||
@NonNull final Map<K, V> modifiedMap,
|
||||
@NonNull List<Pair<V,V>> updates,
|
||||
@NonNull List<V> unchanged)
|
||||
{
|
||||
for (final Map.Entry<K, V> entry : modifiedMap.entrySet())
|
||||
{
|
||||
final V existingValue = existingMap.get(entry.getKey());
|
||||
final V modifiedValue = entry.getValue();
|
||||
|
||||
if (existingValue != null && !Objects.equals(existingValue, modifiedValue))
|
||||
{
|
||||
final Pair<V, V> change = Pair.create(existingValue, modifiedValue);
|
||||
updates.add(change);
|
||||
}
|
||||
else if (existingValue != null)
|
||||
{
|
||||
unchanged.add(existingValue);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public Difference(
|
||||
@NonNull List<T> inserts,
|
||||
@NonNull List<Pair<T, T>> updates,
|
||||
@NonNull List<T> unchanged,
|
||||
@NonNull List<T> deletes)
|
||||
{
|
||||
this.inserts = inserts;
|
||||
this.updates = updates;
|
||||
this.unchanged = unchanged;
|
||||
this.deletes = deletes;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public List<T> getInserts()
|
||||
{
|
||||
return inserts;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public List<Pair<T, T>> getUpdates()
|
||||
{
|
||||
return updates;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public List<T> getUnchanged()
|
||||
{
|
||||
return unchanged;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public List<T> getDeletes()
|
||||
{
|
||||
return deletes;
|
||||
}
|
||||
|
||||
public boolean isEmpty()
|
||||
{
|
||||
return inserts.isEmpty() && updates.isEmpty() && deletes.isEmpty();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return "Difference {" + inserts + ", " + updates + ", " + deletes + "}";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Relution GmbH
|
||||
*
|
||||
* Copyright (C) secunet Security Networks AG
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*/
|
||||
|
||||
package org.strongswan.android.utils;
|
||||
|
||||
import java.security.PrivateKey;
|
||||
import java.security.cert.Certificate;
|
||||
import java.util.Objects;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
/**
|
||||
* Represents a key pair, which consists of a certificate (i.e. public key) and its corresponding
|
||||
* private key.
|
||||
*/
|
||||
public class KeyPair
|
||||
{
|
||||
@NonNull
|
||||
public final Certificate certificate;
|
||||
@NonNull
|
||||
public final PrivateKey privateKey;
|
||||
|
||||
/**
|
||||
* Constructor for a {@link KeyPair}.
|
||||
*
|
||||
* @param certificate the certificate of the key pair.
|
||||
* @param privateKey the private key of the key pair.
|
||||
*/
|
||||
public KeyPair(@NonNull Certificate certificate, @NonNull PrivateKey privateKey)
|
||||
{
|
||||
this.certificate = certificate;
|
||||
this.privateKey = privateKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o)
|
||||
{
|
||||
if (this == o)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
final KeyPair that = (KeyPair)o;
|
||||
return Objects.equals(certificate, that.certificate) &&
|
||||
Objects.equals(privateKey, that.privateKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
return Objects.hash(certificate, privateKey);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return "KeyPair{" + certificate + ", " + privateKey + "}";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Relution GmbH
|
||||
*
|
||||
* Copyright (C) secunet Security Networks AG
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*/
|
||||
|
||||
package org.strongswan.android.utils;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.security.Key;
|
||||
import java.security.KeyStore;
|
||||
import java.security.KeyStoreException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.UnrecoverableKeyException;
|
||||
import java.security.cert.Certificate;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.Enumeration;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
public class KeyPairs
|
||||
{
|
||||
private static final String KEYSTORE_INSTANCE = "PKCS12";
|
||||
|
||||
@NonNull
|
||||
private static KeyStore toKeyStore(@NonNull byte[] bytes, @NonNull char[] password)
|
||||
throws IOException, KeyStoreException, CertificateException, NoSuchAlgorithmException
|
||||
{
|
||||
try (final ByteArrayInputStream stream = new ByteArrayInputStream(bytes))
|
||||
{
|
||||
final KeyStore keyStore = KeyStore.getInstance(KEYSTORE_INSTANCE);
|
||||
keyStore.load(stream, password);
|
||||
return keyStore;
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static KeyPair getKeyPair(
|
||||
@NonNull final KeyStore keyStore,
|
||||
@NonNull final String alias,
|
||||
@NonNull final char[] passwordChars)
|
||||
throws KeyStoreException, NoSuchAlgorithmException, UnrecoverableKeyException
|
||||
{
|
||||
final Certificate certificate = keyStore.getCertificate(alias);
|
||||
if (!(certificate instanceof X509Certificate))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
final Key key = keyStore.getKey(alias, passwordChars);
|
||||
if (key == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return new KeyPair(certificate, (PrivateKey)key);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static KeyPair getKeyPair(@NonNull KeyStore keyStore, @NonNull char[] passwordChars)
|
||||
throws KeyStoreException, NoSuchAlgorithmException, UnrecoverableKeyException
|
||||
{
|
||||
final Enumeration<String> aliases = keyStore.aliases();
|
||||
while (aliases.hasMoreElements())
|
||||
{
|
||||
final String alias = aliases.nextElement();
|
||||
final KeyPair keyPair = getKeyPair(keyStore, alias, passwordChars);
|
||||
if (keyPair != null)
|
||||
{
|
||||
return keyPair;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static KeyPair from(@NonNull final String userCertificate, @NonNull final String password)
|
||||
throws IOException, KeyStoreException, CertificateException, NoSuchAlgorithmException, UnrecoverableKeyException
|
||||
{
|
||||
final byte[] bytes = android.util.Base64.decode(userCertificate, android.util.Base64.DEFAULT);
|
||||
final char[] passwordChars = password.toCharArray();
|
||||
|
||||
final KeyStore keyStore = toKeyStore(bytes, passwordChars);
|
||||
return getKeyPair(keyStore, passwordChars);
|
||||
}
|
||||
|
||||
private KeyPairs() {}
|
||||
}
|
||||
@@ -16,6 +16,7 @@ Dies ist die offizielle Android-Portierung der populären strongSwan VPN-Lösung
|
||||
<li>Die IPsec-Implementierung unterstützt derzeit die AES-CBC, AES-GCM, ChaCha20/Poly1305 und SHA1/SHA2-Algorithmen</li>
|
||||
<li>Passwörter werden zurzeit als Klartext in der Datenbank gespeichert (nur wenn diese mit einem Profil gespeichert werden)</li>
|
||||
<li>VPN Profile können von Dateien importiert werden</li>
|
||||
<li>Unterstützt verwaltete Konfigurationen via Enterprise Mobility Management (EMM)</li>
|
||||
</ul>
|
||||
|
||||
Details und ein Changelog sind in unserer Dokumentation zu finden: https://docs.strongswan.org/docs/5.9/os/androidVpnClient.html
|
||||
|
||||
@@ -16,6 +16,7 @@ Official Android port of the popular strongSwan VPN solution.
|
||||
<li>The IPsec implementation currently supports the AES-CBC, AES-GCM, ChaCha20/Poly1305 and SHA1/SHA2 algorithms</li>
|
||||
<li>Passwords are currently stored as cleartext in the database (only if stored with a profile)</li>
|
||||
<li>VPN profiles may be imported from files</li>
|
||||
<li>Supports managed configurations via enterprise mobility management (EMM)</li>
|
||||
</ul>
|
||||
|
||||
Details and a changelog can be found in our documentation: https://docs.strongswan.org/docs/5.9/os/androidVpnClient.html
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
# 2.4.2 #
|
||||
# 2.5.0 #
|
||||
|
||||
- Ziel-SDK auf Android 13 erhöht und frage um Erlaubnis, um Status-Mitteilung anzuzeigen
|
||||
- Hardwarebeschleunigung in OpenSSL aktiviert
|
||||
- Verwendet einen stabileren Ansatz, um die Quell-IP zu ermitteln
|
||||
- Unterstützung für verwaltete Konfigurationen via Enterprise Mobility Management (EMM)
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
# 2.4.2 #
|
||||
# 2.5.0 #
|
||||
|
||||
- Increased target SDK to Android 13 and ask for permission to show status notification
|
||||
- Enable hardware acceleration in OpenSSL
|
||||
- Use a more stable approach to determine source IP
|
||||
- Support for managed configurations via enterprise mobility management (EMM)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -17,44 +17,60 @@
|
||||
for more details.
|
||||
-->
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?android:attr/activatedBackgroundIndicator"
|
||||
android:orientation="vertical"
|
||||
android:paddingBottom="6dip"
|
||||
android:paddingTop="4dip"
|
||||
android:background="?android:attr/activatedBackgroundIndicator" >
|
||||
android:paddingTop="4dip">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/profile_item_name"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="15dp"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium" />
|
||||
android:layout_marginStart="15dp"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium"
|
||||
tools:text="Profile name" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/profile_item_managed"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="15dp"
|
||||
android:text="@string/profile_managed"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||
android:textColor="@color/success_text"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/profile_item_gateway"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textColor="?android:textColorSecondary"
|
||||
android:layout_marginStart="15dp"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||
android:layout_marginLeft="15dp" />
|
||||
android:textColor="?android:textColorSecondary"
|
||||
tools:text="Server: vpn.example.com" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/profile_item_username"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textColor="?android:textColorSecondary"
|
||||
android:layout_marginStart="15dp"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||
android:layout_marginLeft="15dp" />
|
||||
android:textColor="?android:textColorSecondary"
|
||||
tools:text="Username" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/profile_item_certificate"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textColor="?android:textColorSecondary"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||
android:singleLine="true"
|
||||
android:layout_marginStart="15dp"
|
||||
android:ellipsize="end"
|
||||
android:layout_marginLeft="15dp" />
|
||||
android:singleLine="true"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||
android:textColor="?android:textColorSecondary"
|
||||
tools:text="Certificate" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
@@ -139,6 +139,7 @@
|
||||
<string name="profile_cert_import">Zertifikat aus VPN Profil importieren</string>
|
||||
<string name="profile_cert_alias">Zertifikat für \"%1$s\"</string>
|
||||
<string name="profile_profile_id">Profil-ID</string>
|
||||
<string name="profile_managed">Verwaltetes Profil</string>
|
||||
<!-- Warnings/Notifications in the details view -->
|
||||
<string name="alert_text_no_input_gateway">Ein Wert wird benötigt, um die Verbindung aufbauen zu können</string>
|
||||
<string name="alert_text_no_input_username">Bitte geben Sie Ihren Benutzernamen ein</string>
|
||||
@@ -148,6 +149,7 @@
|
||||
<string name="alert_text_no_subnets">Bitte geben Sie mit Leerzeichen getrennte, gültige Subnetzte und/oder IP-Adressen ein</string>
|
||||
<string name="alert_text_no_ips">Bitte geben Sie mit Leerzeichen getrennte, gültige IP-Adressen ein</string>
|
||||
<string name="alert_text_no_proposal">Bitte geben Sie eine mit Bindestrichen getrennte, gültige Liste von Algorithmen ein</string>
|
||||
<string name="alert_text_vpn_profile_read_only">Dieses Profil wird von Ihrem Administrator verwaltet und kann nicht bearbeitet werden. Nur das Passwort kann geändert werden</string>
|
||||
<string name="tnc_notice_title">EAP-TNC kann Ihre Privatsphäre beeinträchtigen</string>
|
||||
<string name="tnc_notice_subtitle">Gerätedaten werden an den Server-Betreiber gesendet</string>
|
||||
<string name="tnc_notice_details"><![CDATA[<p>Trusted Network Connect (TNC) erlaubt Server-Betreibern den Gesundheitszustand von Endgeräten zu prüfen.</p><p>Dazu kann der Betreiber Daten verlangen, wie etwa eine eindeutige Identifikationsnummer, eine Liste der installierten Pakete, Systemeinstellungen oder kryptografische Prüfsummen von Dateien.</p><b>Solche Daten werden nur übermittelt nachdem die Identität des Servers geprüft wurde.</b>]]></string>
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright (C) 2023 Relution GmbH
|
||||
|
||||
Copyright (C) secunet Security Networks AG
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it
|
||||
under the terms of the GNU General Public License as published by the
|
||||
Free Software Foundation; either version 2 of the License, or (at your
|
||||
option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
for more details.
|
||||
-->
|
||||
<resources>
|
||||
|
||||
<!-- Managed configuration -->
|
||||
<string name="managed_config_allow_profile_creation_title">Allow profile creation</string>
|
||||
<string name="managed_config_allow_profile_creation_description">Specifies whether users are allowed to add their own profiles</string>
|
||||
<string name="managed_config_allow_profile_import_title">Allow profile import</string>
|
||||
<string name="managed_config_allow_profile_import_description">Specifies whether users are allowed to import their own profiles</string>
|
||||
<string name="managed_config_allow_existing_profiles_title">Show existing profiles</string>
|
||||
<string name="managed_config_allow_existing_profiles_description">Specifies whether users can continue to see and use their previously created profiles</string>
|
||||
<string name="managed_config_allow_certificate_import_title">Allow certificate import</string>
|
||||
<string name="managed_config_allow_certificate_import_description">Specifies whether users are allowed to import certificates</string>
|
||||
<string name="managed_config_allow_settings_access_title">Allow modifying settings</string>
|
||||
<string name="managed_config_allow_settings_access_description">Specifies whether users are allowed change global app settings</string>
|
||||
<string name="managed_config_default_vpn_profile_title">@string/pref_default_vpn_profile</string>
|
||||
<string name="managed_config_default_vpn_profile_description">Unique identifier of the VPN profile to use by default, use the value \"mru\" for the most recently used profile</string>
|
||||
<string name="managed_config_ignore_battery_optimizations_title">@string/pref_power_whitelist_title</string>
|
||||
<string name="managed_config_ignore_battery_optimizations_description">@string/pref_power_whitelist_summary</string>
|
||||
<string name="managed_config_profiles_array_title">VPN profiles</string>
|
||||
<string name="managed_config_profiles_array_description">Collection of managed VPN profiles</string>
|
||||
<string name="managed_config_profile_bundle_title">VPN profile</string>
|
||||
<string name="managed_config_profile_bundle_description">A managed VPN profile</string>
|
||||
|
||||
<!-- Managed configuration, VPN profile -->
|
||||
<string name="managed_config_uuid_title">Unique identifier</string>
|
||||
<string name="managed_config_uuid_description">Unique identifier of the VPN profile. Version 4 UUIDs (random-generated) are recommended</string>
|
||||
<string name="managed_config_name_title">@string/profile_name_label</string>
|
||||
<string name="managed_config_name_description">@string/profile_name_hint</string>
|
||||
<string name="managed_config_vpn_type_title">@string/profile_vpn_type_label</string>
|
||||
<string name="managed_config_vpn_type_description">The type of client authentication used by the VPN profile</string>
|
||||
<string name="managed_config_included_package_names_title">Apps allowed to use the VPN (Optional)</string>
|
||||
<string name="managed_config_included_package_names_description">Space-separated list of package names; all other apps will not see/use the VPN</string>
|
||||
<string name="managed_config_excluded_package_names_title">Apps excluded from using the VPN (Optional)</string>
|
||||
<string name="managed_config_excluded_package_names_description">Space-separated list of package names of apps excluded from using the VPN; only used of allow list is empty</string>
|
||||
<string name="managed_config_ike_proposal_title">@string/profile_proposals_ike_label</string>
|
||||
<string name="managed_config_ike_proposal_description">@string/profile_proposals_ike_hint</string>
|
||||
<string name="managed_config_esp_proposal_title">@string/profile_proposals_esp_label</string>
|
||||
<string name="managed_config_esp_proposal_description">@string/profile_proposals_esp_hint</string>
|
||||
<string name="managed_config_mtu_title">@string/profile_mtu_label</string>
|
||||
<string name="managed_config_mtu_description">@string/profile_mtu_hint</string>
|
||||
<string name="managed_config_nat_keepalive_title">@string/profile_nat_keepalive_label</string>
|
||||
<string name="managed_config_nat_keepalive_description">@string/profile_nat_keepalive_hint</string>
|
||||
<string name="managed_config_dns_server_host_names_title">@string/profile_dns_servers_label</string>
|
||||
<string name="managed_config_dns_server_host_names_description">@string/profile_dns_servers_hint</string>
|
||||
<string name="managed_config_ipv6_transport_title">@string/profile_ipv6_transport_label</string>
|
||||
<string name="managed_config_ipv6_transport_description">@string/profile_ipv6_transport_hint</string>
|
||||
|
||||
<!-- Managed configuration, VPN profile, remote -->
|
||||
<string name="managed_config_remote_bundle_title">Remote</string>
|
||||
<string name="managed_config_remote_bundle_description">Specifies information about the server</string>
|
||||
<string name="managed_config_remote_addr_title">@string/profile_gateway_label</string>
|
||||
<string name="managed_config_remote_addr_description">@string/profile_gateway_hint</string>
|
||||
<string name="managed_config_remote_port_title">@string/profile_port_label</string>
|
||||
<string name="managed_config_remote_port_description">@string/profile_port_hint</string>
|
||||
<string name="managed_config_remote_id_title">@string/profile_remote_id_label</string>
|
||||
<string name="managed_config_remote_id_description">@string/profile_remote_id_hint</string>
|
||||
<string name="managed_config_remote_cert_title">CA or server certificate (Optional)</string>
|
||||
<string name="managed_config_remote_cert_description">Base64-encoded CA or server certificate. Is imported into the app, not the system keystore. If not set, automatic CA certificate selection is enabled</string>
|
||||
<string name="managed_config_remote_certreq_title">Send certificate requests</string>
|
||||
<string name="managed_config_remote_certreq_description">Specifies whether to send certificate requests for all installed or selected CA certificates. Disabling this may reduce the size of the IKE_AUTH message if the server does not support fragmentation. But it only works if the server doesn\'t require certificate requests to send back the server certificate</string>
|
||||
<string name="managed_config_remote_revocation_ocsp_title">@string/profile_use_ocsp_label</string>
|
||||
<string name="managed_config_remote_revocation_ocsp_description">@string/profile_use_ocsp_hint</string>
|
||||
<string name="managed_config_remote_revocation_crl_title">@string/profile_use_crl_label</string>
|
||||
<string name="managed_config_remote_revocation_crl_description">@string/profile_use_crl_hint</string>
|
||||
<string name="managed_config_remote_revocation_strict_title">@string/profile_strict_revocation_label</string>
|
||||
<string name="managed_config_remote_revocation_strict_description">@string/profile_strict_revocation_hint</string>
|
||||
|
||||
<!-- Managed configuration, VPN profile, local -->
|
||||
<string name="managed_config_local_bundle_title">Local</string>
|
||||
<string name="managed_config_local_bundle_description">Specifies information about the client</string>
|
||||
<string name="managed_config_local_eap_id_title">Identity/username for EAP authentication (Optional)</string>
|
||||
<string name="managed_config_local_eap_id_description">If this is required (for username/password-based EAP authentication) but not configured here, the user is prompted for it. If it is set, the user is not able to change it. In both cases the user may optionally enter the password</string>
|
||||
<string name="managed_config_local_id_title">@string/profile_local_id_label</string>
|
||||
<string name="managed_config_local_id_description">@string/profile_local_id_hint_user</string>
|
||||
<string name="managed_config_local_p12_title">@string/profile_user_certificate_label</string>
|
||||
<string name="managed_config_local_p12_description">Base64-encoded PKCS#12-container with the client certificate and private key and optional certificate chain (the latter might cause warnings on older Android releases, see Android VPN client configuration for details). Not necessary for username/password-based EAP authentication or if the user already has the certificate/key installed as it may be selected while importing the profile</string>
|
||||
<string name="managed_config_local_p12_password_title">User certificate password (Optional)</string>
|
||||
<string name="managed_config_local_p12_password_description">Password required to extract the private key of the PKCS#12-container for installation</string>
|
||||
<string name="managed_config_local_rsa_pss_title">@string/profile_rsa_pss_label</string>
|
||||
<string name="managed_config_local_rsa_pss_description">@string/profile_rsa_pss_hint</string>
|
||||
|
||||
<!-- Managed configuration, VPN profile, split-tunneling -->
|
||||
<string name="managed_config_split_tunneling_bundle_title">@string/profile_split_tunneling_label</string>
|
||||
<string name="managed_config_split_tunneling_bundle_description">@string/profile_split_tunneling_intro</string>
|
||||
<string name="managed_config_split_tunneling_subnets_title">@string/profile_included_subnets_label</string>
|
||||
<string name="managed_config_split_tunneling_subnets_description">@string/profile_included_subnets_hint</string>
|
||||
<string name="managed_config_split_tunneling_excluded_title">@string/profile_excluded_subnets_label</string>
|
||||
<string name="managed_config_split_tunneling_excluded_description">@string/profile_excluded_subnets_hint</string>
|
||||
<string name="managed_config_split_tunneling_block_ipv4_title">@string/profile_split_tunnelingv4_title</string>
|
||||
<string name="managed_config_split_tunneling_block_ipv4_description">Specifies whether to block IPv4 traffic that\'s not destined for the VPN. Forces all IPv4 traffic via VPN (traffic that does not match the negotiated traffic selector is then just dropped). Thus this is basically equivalent to including 0.0.0.0/0 in subnets</string>
|
||||
<string name="managed_config_split_tunneling_block_ipv6_title">@string/profile_split_tunnelingv6_title</string>
|
||||
<string name="managed_config_split_tunneling_block_ipv6_description">Specifies whether to block IPv6 traffic that\'s not destined for the VPN. Forces all IPv6 traffic via VPN (traffic that does not match the negotiated traffic selector is then just dropped). Thus this is basically equivalent to including ::/0 in subnets</string>
|
||||
|
||||
</resources>
|
||||
@@ -141,6 +141,7 @@
|
||||
<string name="profile_cert_import">Import certificate from VPN profile</string>
|
||||
<string name="profile_cert_alias">Certificate for \"%1$s\"</string>
|
||||
<string name="profile_profile_id">Profile ID</string>
|
||||
<string name="profile_managed">Managed profile</string>
|
||||
<!-- Warnings/Notifications in the details view -->
|
||||
<string name="alert_text_no_input_gateway">A value is required to initiate the connection</string>
|
||||
<string name="alert_text_no_input_username">Wprowadź swoją nazwę użytkownika</string>
|
||||
@@ -150,6 +151,7 @@
|
||||
<string name="alert_text_no_subnets">Please enter valid subnets and/or IP addresses, separated by spaces</string>
|
||||
<string name="alert_text_no_ips">Please enter valid IP addresses, separated by spaces</string>
|
||||
<string name="alert_text_no_proposal">Please enter a valid list of algorithms, separated by hyphens</string>
|
||||
<string name="alert_text_vpn_profile_read_only">This VPN profile is managed by your administrator and can\'t be modified. You can only change the password</string>
|
||||
<string name="tnc_notice_title">EAP-TNC may affect your privacy</string>
|
||||
<string name="tnc_notice_subtitle">Device data is sent to the server operator</string>
|
||||
<string name="tnc_notice_details"><![CDATA[<p>Trusted Network Connect (TNC) allows server operators to assess the health of a client device.</p><p>For that purpose the server operator may request data such as a unique identifier, a list of installed packages, system settings, or cryptographic checksums of files.</p><b>Any data will be sent only after verifying the server\'s identity.</b>]]></string>
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright (C) 2023 Relution GmbH
|
||||
|
||||
Copyright (C) secunet Security Networks AG
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it
|
||||
under the terms of the GNU General Public License as published by the
|
||||
Free Software Foundation; either version 2 of the License, or (at your
|
||||
option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
for more details.
|
||||
-->
|
||||
<resources>
|
||||
|
||||
<!-- Managed configuration -->
|
||||
<string name="managed_config_allow_profile_creation_title">Allow profile creation</string>
|
||||
<string name="managed_config_allow_profile_creation_description">Specifies whether users are allowed to add their own profiles</string>
|
||||
<string name="managed_config_allow_profile_import_title">Allow profile import</string>
|
||||
<string name="managed_config_allow_profile_import_description">Specifies whether users are allowed to import their own profiles</string>
|
||||
<string name="managed_config_allow_existing_profiles_title">Show existing profiles</string>
|
||||
<string name="managed_config_allow_existing_profiles_description">Specifies whether users can continue to see and use their previously created profiles</string>
|
||||
<string name="managed_config_allow_certificate_import_title">Allow certificate import</string>
|
||||
<string name="managed_config_allow_certificate_import_description">Specifies whether users are allowed to import certificates</string>
|
||||
<string name="managed_config_allow_settings_access_title">Allow modifying settings</string>
|
||||
<string name="managed_config_allow_settings_access_description">Specifies whether users are allowed change global app settings</string>
|
||||
<string name="managed_config_default_vpn_profile_title">@string/pref_default_vpn_profile</string>
|
||||
<string name="managed_config_default_vpn_profile_description">Unique identifier of the VPN profile to use by default, use the value \"mru\" for the most recently used profile</string>
|
||||
<string name="managed_config_ignore_battery_optimizations_title">@string/pref_power_whitelist_title</string>
|
||||
<string name="managed_config_ignore_battery_optimizations_description">@string/pref_power_whitelist_summary</string>
|
||||
<string name="managed_config_profiles_array_title">VPN profiles</string>
|
||||
<string name="managed_config_profiles_array_description">Collection of managed VPN profiles</string>
|
||||
<string name="managed_config_profile_bundle_title">VPN profile</string>
|
||||
<string name="managed_config_profile_bundle_description">A managed VPN profile</string>
|
||||
|
||||
<!-- Managed configuration, VPN profile -->
|
||||
<string name="managed_config_uuid_title">Unique identifier</string>
|
||||
<string name="managed_config_uuid_description">Unique identifier of the VPN profile. Version 4 UUIDs (random-generated) are recommended</string>
|
||||
<string name="managed_config_name_title">@string/profile_name_label</string>
|
||||
<string name="managed_config_name_description">@string/profile_name_hint</string>
|
||||
<string name="managed_config_vpn_type_title">@string/profile_vpn_type_label</string>
|
||||
<string name="managed_config_vpn_type_description">The type of client authentication used by the VPN profile</string>
|
||||
<string name="managed_config_included_package_names_title">Apps allowed to use the VPN (Optional)</string>
|
||||
<string name="managed_config_included_package_names_description">Space-separated list of package names; all other apps will not see/use the VPN</string>
|
||||
<string name="managed_config_excluded_package_names_title">Apps excluded from using the VPN (Optional)</string>
|
||||
<string name="managed_config_excluded_package_names_description">Space-separated list of package names of apps excluded from using the VPN; only used of allow list is empty</string>
|
||||
<string name="managed_config_ike_proposal_title">@string/profile_proposals_ike_label</string>
|
||||
<string name="managed_config_ike_proposal_description">@string/profile_proposals_ike_hint</string>
|
||||
<string name="managed_config_esp_proposal_title">@string/profile_proposals_esp_label</string>
|
||||
<string name="managed_config_esp_proposal_description">@string/profile_proposals_esp_hint</string>
|
||||
<string name="managed_config_mtu_title">@string/profile_mtu_label</string>
|
||||
<string name="managed_config_mtu_description">@string/profile_mtu_hint</string>
|
||||
<string name="managed_config_nat_keepalive_title">@string/profile_nat_keepalive_label</string>
|
||||
<string name="managed_config_nat_keepalive_description">@string/profile_nat_keepalive_hint</string>
|
||||
<string name="managed_config_dns_server_host_names_title">@string/profile_dns_servers_label</string>
|
||||
<string name="managed_config_dns_server_host_names_description">@string/profile_dns_servers_hint</string>
|
||||
<string name="managed_config_ipv6_transport_title">@string/profile_ipv6_transport_label</string>
|
||||
<string name="managed_config_ipv6_transport_description">@string/profile_ipv6_transport_hint</string>
|
||||
|
||||
<!-- Managed configuration, VPN profile, remote -->
|
||||
<string name="managed_config_remote_bundle_title">Remote</string>
|
||||
<string name="managed_config_remote_bundle_description">Specifies information about the server</string>
|
||||
<string name="managed_config_remote_addr_title">@string/profile_gateway_label</string>
|
||||
<string name="managed_config_remote_addr_description">@string/profile_gateway_hint</string>
|
||||
<string name="managed_config_remote_port_title">@string/profile_port_label</string>
|
||||
<string name="managed_config_remote_port_description">@string/profile_port_hint</string>
|
||||
<string name="managed_config_remote_id_title">@string/profile_remote_id_label</string>
|
||||
<string name="managed_config_remote_id_description">@string/profile_remote_id_hint</string>
|
||||
<string name="managed_config_remote_cert_title">CA or server certificate (Optional)</string>
|
||||
<string name="managed_config_remote_cert_description">Base64-encoded CA or server certificate. Is imported into the app, not the system keystore. If not set, automatic CA certificate selection is enabled</string>
|
||||
<string name="managed_config_remote_certreq_title">Send certificate requests</string>
|
||||
<string name="managed_config_remote_certreq_description">Specifies whether to send certificate requests for all installed or selected CA certificates. Disabling this may reduce the size of the IKE_AUTH message if the server does not support fragmentation. But it only works if the server doesn\'t require certificate requests to send back the server certificate</string>
|
||||
<string name="managed_config_remote_revocation_ocsp_title">@string/profile_use_ocsp_label</string>
|
||||
<string name="managed_config_remote_revocation_ocsp_description">@string/profile_use_ocsp_hint</string>
|
||||
<string name="managed_config_remote_revocation_crl_title">@string/profile_use_crl_label</string>
|
||||
<string name="managed_config_remote_revocation_crl_description">@string/profile_use_crl_hint</string>
|
||||
<string name="managed_config_remote_revocation_strict_title">@string/profile_strict_revocation_label</string>
|
||||
<string name="managed_config_remote_revocation_strict_description">@string/profile_strict_revocation_hint</string>
|
||||
|
||||
<!-- Managed configuration, VPN profile, local -->
|
||||
<string name="managed_config_local_bundle_title">Local</string>
|
||||
<string name="managed_config_local_bundle_description">Specifies information about the client</string>
|
||||
<string name="managed_config_local_eap_id_title">Identity/username for EAP authentication (Optional)</string>
|
||||
<string name="managed_config_local_eap_id_description">If this is required (for username/password-based EAP authentication) but not configured here, the user is prompted for it. If it is set, the user is not able to change it. In both cases the user may optionally enter the password</string>
|
||||
<string name="managed_config_local_id_title">@string/profile_local_id_label</string>
|
||||
<string name="managed_config_local_id_description">@string/profile_local_id_hint_user</string>
|
||||
<string name="managed_config_local_p12_title">@string/profile_user_certificate_label</string>
|
||||
<string name="managed_config_local_p12_description">Base64-encoded PKCS#12-container with the client certificate and private key and optional certificate chain (the latter might cause warnings on older Android releases, see Android VPN client configuration for details). Not necessary for username/password-based EAP authentication or if the user already has the certificate/key installed as it may be selected while importing the profile</string>
|
||||
<string name="managed_config_local_p12_password_title">User certificate password (Optional)</string>
|
||||
<string name="managed_config_local_p12_password_description">Password required to extract the private key of the PKCS#12-container for installation</string>
|
||||
<string name="managed_config_local_rsa_pss_title">@string/profile_rsa_pss_label</string>
|
||||
<string name="managed_config_local_rsa_pss_description">@string/profile_rsa_pss_hint</string>
|
||||
|
||||
<!-- Managed configuration, VPN profile, split-tunneling -->
|
||||
<string name="managed_config_split_tunneling_bundle_title">@string/profile_split_tunneling_label</string>
|
||||
<string name="managed_config_split_tunneling_bundle_description">@string/profile_split_tunneling_intro</string>
|
||||
<string name="managed_config_split_tunneling_subnets_title">@string/profile_included_subnets_label</string>
|
||||
<string name="managed_config_split_tunneling_subnets_description">@string/profile_included_subnets_hint</string>
|
||||
<string name="managed_config_split_tunneling_excluded_title">@string/profile_excluded_subnets_label</string>
|
||||
<string name="managed_config_split_tunneling_excluded_description">@string/profile_excluded_subnets_hint</string>
|
||||
<string name="managed_config_split_tunneling_block_ipv4_title">@string/profile_split_tunnelingv4_title</string>
|
||||
<string name="managed_config_split_tunneling_block_ipv4_description">Specifies whether to block IPv4 traffic that\'s not destined for the VPN. Forces all IPv4 traffic via VPN (traffic that does not match the negotiated traffic selector is then just dropped). Thus this is basically equivalent to including 0.0.0.0/0 in subnets</string>
|
||||
<string name="managed_config_split_tunneling_block_ipv6_title">@string/profile_split_tunnelingv6_title</string>
|
||||
<string name="managed_config_split_tunneling_block_ipv6_description">Specifies whether to block IPv6 traffic that\'s not destined for the VPN. Forces all IPv6 traffic via VPN (traffic that does not match the negotiated traffic selector is then just dropped). Thus this is basically equivalent to including ::/0 in subnets</string>
|
||||
|
||||
</resources>
|
||||
@@ -135,6 +135,7 @@
|
||||
<string name="profile_cert_import">Import certificate from VPN profile</string>
|
||||
<string name="profile_cert_alias">Certificate for \"%1$s\"</string>
|
||||
<string name="profile_profile_id">Profile ID</string>
|
||||
<string name="profile_managed">Managed profile</string>
|
||||
<!-- Warnings/Notifications in the details view -->
|
||||
<string name="alert_text_no_input_gateway">A value is required to initiate the connection</string>
|
||||
<string name="alert_text_no_input_username">Пожалуйста введите имя пользователя</string>
|
||||
@@ -144,6 +145,7 @@
|
||||
<string name="alert_text_no_subnets">Please enter valid subnets and/or IP addresses, separated by spaces</string>
|
||||
<string name="alert_text_no_ips">Please enter valid IP addresses, separated by spaces</string>
|
||||
<string name="alert_text_no_proposal">Please enter a valid list of algorithms, separated by hyphens</string>
|
||||
<string name="alert_text_vpn_profile_read_only">This VPN profile is managed by your administrator and can\'t be modified. You can only change the password</string>
|
||||
<string name="tnc_notice_title">EAP-TNC may affect your privacy</string>
|
||||
<string name="tnc_notice_subtitle">Device data is sent to the server operator</string>
|
||||
<string name="tnc_notice_details"><![CDATA[<p>Trusted Network Connect (TNC) allows server operators to assess the health of a client device.</p><p>For that purpose the server operator may request data such as a unique identifier, a list of installed packages, system settings, or cryptographic checksums of files.</p><b>Any data will be sent only after verifying the server\'s identity.</b>]]></string>
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright (C) 2023 Relution GmbH
|
||||
|
||||
Copyright (C) secunet Security Networks AG
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it
|
||||
under the terms of the GNU General Public License as published by the
|
||||
Free Software Foundation; either version 2 of the License, or (at your
|
||||
option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
for more details.
|
||||
-->
|
||||
<resources>
|
||||
|
||||
<!-- Managed configuration -->
|
||||
<string name="managed_config_allow_profile_creation_title">Allow profile creation</string>
|
||||
<string name="managed_config_allow_profile_creation_description">Specifies whether users are allowed to add their own profiles</string>
|
||||
<string name="managed_config_allow_profile_import_title">Allow profile import</string>
|
||||
<string name="managed_config_allow_profile_import_description">Specifies whether users are allowed to import their own profiles</string>
|
||||
<string name="managed_config_allow_existing_profiles_title">Show existing profiles</string>
|
||||
<string name="managed_config_allow_existing_profiles_description">Specifies whether users can continue to see and use their previously created profiles</string>
|
||||
<string name="managed_config_allow_certificate_import_title">Allow certificate import</string>
|
||||
<string name="managed_config_allow_certificate_import_description">Specifies whether users are allowed to import certificates</string>
|
||||
<string name="managed_config_allow_settings_access_title">Allow modifying settings</string>
|
||||
<string name="managed_config_allow_settings_access_description">Specifies whether users are allowed change global app settings</string>
|
||||
<string name="managed_config_default_vpn_profile_title">@string/pref_default_vpn_profile</string>
|
||||
<string name="managed_config_default_vpn_profile_description">Unique identifier of the VPN profile to use by default, use the value \"mru\" for the most recently used profile</string>
|
||||
<string name="managed_config_ignore_battery_optimizations_title">@string/pref_power_whitelist_title</string>
|
||||
<string name="managed_config_ignore_battery_optimizations_description">@string/pref_power_whitelist_summary</string>
|
||||
<string name="managed_config_profiles_array_title">VPN profiles</string>
|
||||
<string name="managed_config_profiles_array_description">Collection of managed VPN profiles</string>
|
||||
<string name="managed_config_profile_bundle_title">VPN profile</string>
|
||||
<string name="managed_config_profile_bundle_description">A managed VPN profile</string>
|
||||
|
||||
<!-- Managed configuration, VPN profile -->
|
||||
<string name="managed_config_uuid_title">Unique identifier</string>
|
||||
<string name="managed_config_uuid_description">Unique identifier of the VPN profile. Version 4 UUIDs (random-generated) are recommended</string>
|
||||
<string name="managed_config_name_title">@string/profile_name_label</string>
|
||||
<string name="managed_config_name_description">@string/profile_name_hint</string>
|
||||
<string name="managed_config_vpn_type_title">@string/profile_vpn_type_label</string>
|
||||
<string name="managed_config_vpn_type_description">The type of client authentication used by the VPN profile</string>
|
||||
<string name="managed_config_included_package_names_title">Apps allowed to use the VPN (Optional)</string>
|
||||
<string name="managed_config_included_package_names_description">Space-separated list of package names; all other apps will not see/use the VPN</string>
|
||||
<string name="managed_config_excluded_package_names_title">Apps excluded from using the VPN (Optional)</string>
|
||||
<string name="managed_config_excluded_package_names_description">Space-separated list of package names of apps excluded from using the VPN; only used of allow list is empty</string>
|
||||
<string name="managed_config_ike_proposal_title">@string/profile_proposals_ike_label</string>
|
||||
<string name="managed_config_ike_proposal_description">@string/profile_proposals_ike_hint</string>
|
||||
<string name="managed_config_esp_proposal_title">@string/profile_proposals_esp_label</string>
|
||||
<string name="managed_config_esp_proposal_description">@string/profile_proposals_esp_hint</string>
|
||||
<string name="managed_config_mtu_title">@string/profile_mtu_label</string>
|
||||
<string name="managed_config_mtu_description">@string/profile_mtu_hint</string>
|
||||
<string name="managed_config_nat_keepalive_title">@string/profile_nat_keepalive_label</string>
|
||||
<string name="managed_config_nat_keepalive_description">@string/profile_nat_keepalive_hint</string>
|
||||
<string name="managed_config_dns_server_host_names_title">@string/profile_dns_servers_label</string>
|
||||
<string name="managed_config_dns_server_host_names_description">@string/profile_dns_servers_hint</string>
|
||||
<string name="managed_config_ipv6_transport_title">@string/profile_ipv6_transport_label</string>
|
||||
<string name="managed_config_ipv6_transport_description">@string/profile_ipv6_transport_hint</string>
|
||||
|
||||
<!-- Managed configuration, VPN profile, remote -->
|
||||
<string name="managed_config_remote_bundle_title">Remote</string>
|
||||
<string name="managed_config_remote_bundle_description">Specifies information about the server</string>
|
||||
<string name="managed_config_remote_addr_title">@string/profile_gateway_label</string>
|
||||
<string name="managed_config_remote_addr_description">@string/profile_gateway_hint</string>
|
||||
<string name="managed_config_remote_port_title">@string/profile_port_label</string>
|
||||
<string name="managed_config_remote_port_description">@string/profile_port_hint</string>
|
||||
<string name="managed_config_remote_id_title">@string/profile_remote_id_label</string>
|
||||
<string name="managed_config_remote_id_description">@string/profile_remote_id_hint</string>
|
||||
<string name="managed_config_remote_cert_title">CA or server certificate (Optional)</string>
|
||||
<string name="managed_config_remote_cert_description">Base64-encoded CA or server certificate. Is imported into the app, not the system keystore. If not set, automatic CA certificate selection is enabled</string>
|
||||
<string name="managed_config_remote_certreq_title">Send certificate requests</string>
|
||||
<string name="managed_config_remote_certreq_description">Specifies whether to send certificate requests for all installed or selected CA certificates. Disabling this may reduce the size of the IKE_AUTH message if the server does not support fragmentation. But it only works if the server doesn\'t require certificate requests to send back the server certificate</string>
|
||||
<string name="managed_config_remote_revocation_ocsp_title">@string/profile_use_ocsp_label</string>
|
||||
<string name="managed_config_remote_revocation_ocsp_description">@string/profile_use_ocsp_hint</string>
|
||||
<string name="managed_config_remote_revocation_crl_title">@string/profile_use_crl_label</string>
|
||||
<string name="managed_config_remote_revocation_crl_description">@string/profile_use_crl_hint</string>
|
||||
<string name="managed_config_remote_revocation_strict_title">@string/profile_strict_revocation_label</string>
|
||||
<string name="managed_config_remote_revocation_strict_description">@string/profile_strict_revocation_hint</string>
|
||||
|
||||
<!-- Managed configuration, VPN profile, local -->
|
||||
<string name="managed_config_local_bundle_title">Local</string>
|
||||
<string name="managed_config_local_bundle_description">Specifies information about the client</string>
|
||||
<string name="managed_config_local_eap_id_title">Identity/username for EAP authentication (Optional)</string>
|
||||
<string name="managed_config_local_eap_id_description">If this is required (for username/password-based EAP authentication) but not configured here, the user is prompted for it. If it is set, the user is not able to change it. In both cases the user may optionally enter the password</string>
|
||||
<string name="managed_config_local_id_title">@string/profile_local_id_label</string>
|
||||
<string name="managed_config_local_id_description">@string/profile_local_id_hint_user</string>
|
||||
<string name="managed_config_local_p12_title">@string/profile_user_certificate_label</string>
|
||||
<string name="managed_config_local_p12_description">Base64-encoded PKCS#12-container with the client certificate and private key and optional certificate chain (the latter might cause warnings on older Android releases, see Android VPN client configuration for details). Not necessary for username/password-based EAP authentication or if the user already has the certificate/key installed as it may be selected while importing the profile</string>
|
||||
<string name="managed_config_local_p12_password_title">User certificate password (Optional)</string>
|
||||
<string name="managed_config_local_p12_password_description">Password required to extract the private key of the PKCS#12-container for installation</string>
|
||||
<string name="managed_config_local_rsa_pss_title">@string/profile_rsa_pss_label</string>
|
||||
<string name="managed_config_local_rsa_pss_description">@string/profile_rsa_pss_hint</string>
|
||||
|
||||
<!-- Managed configuration, VPN profile, split-tunneling -->
|
||||
<string name="managed_config_split_tunneling_bundle_title">@string/profile_split_tunneling_label</string>
|
||||
<string name="managed_config_split_tunneling_bundle_description">@string/profile_split_tunneling_intro</string>
|
||||
<string name="managed_config_split_tunneling_subnets_title">@string/profile_included_subnets_label</string>
|
||||
<string name="managed_config_split_tunneling_subnets_description">@string/profile_included_subnets_hint</string>
|
||||
<string name="managed_config_split_tunneling_excluded_title">@string/profile_excluded_subnets_label</string>
|
||||
<string name="managed_config_split_tunneling_excluded_description">@string/profile_excluded_subnets_hint</string>
|
||||
<string name="managed_config_split_tunneling_block_ipv4_title">@string/profile_split_tunnelingv4_title</string>
|
||||
<string name="managed_config_split_tunneling_block_ipv4_description">Specifies whether to block IPv4 traffic that\'s not destined for the VPN. Forces all IPv4 traffic via VPN (traffic that does not match the negotiated traffic selector is then just dropped). Thus this is basically equivalent to including 0.0.0.0/0 in subnets</string>
|
||||
<string name="managed_config_split_tunneling_block_ipv6_title">@string/profile_split_tunnelingv6_title</string>
|
||||
<string name="managed_config_split_tunneling_block_ipv6_description">Specifies whether to block IPv6 traffic that\'s not destined for the VPN. Forces all IPv6 traffic via VPN (traffic that does not match the negotiated traffic selector is then just dropped). Thus this is basically equivalent to including ::/0 in subnets</string>
|
||||
|
||||
</resources>
|
||||
@@ -136,6 +136,7 @@
|
||||
<string name="profile_cert_import">Import certificate from VPN profile</string>
|
||||
<string name="profile_cert_alias">Certificate for \"%1$s\"</string>
|
||||
<string name="profile_profile_id">Profile ID</string>
|
||||
<string name="profile_managed">Managed profile</string>
|
||||
<!-- Warnings/Notifications in the details view -->
|
||||
<string name="alert_text_no_input_gateway">A value is required to initiate the connection</string>
|
||||
<string name="alert_text_no_input_username">Введіть ім\'я користувача </string>
|
||||
@@ -145,6 +146,7 @@
|
||||
<string name="alert_text_no_subnets">Please enter valid subnets and/or IP addresses, separated by spaces</string>
|
||||
<string name="alert_text_no_ips">Please enter valid IP addresses, separated by spaces</string>
|
||||
<string name="alert_text_no_proposal">Please enter a valid list of algorithms, separated by hyphens</string>
|
||||
<string name="alert_text_vpn_profile_read_only">This VPN profile is managed by your administrator and can\'t be modified. You can only change the password</string>
|
||||
<string name="tnc_notice_title">EAP-TNC may affect your privacy</string>
|
||||
<string name="tnc_notice_subtitle">Device data is sent to the server operator</string>
|
||||
<string name="tnc_notice_details"><![CDATA[<p>Trusted Network Connect (TNC) allows server operators to assess the health of a client device.</p><p>For that purpose the server operator may request data such as a unique identifier, a list of installed packages, system settings, or cryptographic checksums of files.</p><b>Any data will be sent only after verifying the server\'s identity.</b>]]></string>
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright (C) 2023 Relution GmbH
|
||||
|
||||
Copyright (C) secunet Security Networks AG
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it
|
||||
under the terms of the GNU General Public License as published by the
|
||||
Free Software Foundation; either version 2 of the License, or (at your
|
||||
option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
for more details.
|
||||
-->
|
||||
<resources>
|
||||
|
||||
<!-- Managed configuration -->
|
||||
<string name="managed_config_allow_profile_creation_title">Allow profile creation</string>
|
||||
<string name="managed_config_allow_profile_creation_description">Specifies whether users are allowed to add their own profiles</string>
|
||||
<string name="managed_config_allow_profile_import_title">Allow profile import</string>
|
||||
<string name="managed_config_allow_profile_import_description">Specifies whether users are allowed to import their own profiles</string>
|
||||
<string name="managed_config_allow_existing_profiles_title">Show existing profiles</string>
|
||||
<string name="managed_config_allow_existing_profiles_description">Specifies whether users can continue to see and use their previously created profiles</string>
|
||||
<string name="managed_config_allow_certificate_import_title">Allow certificate import</string>
|
||||
<string name="managed_config_allow_certificate_import_description">Specifies whether users are allowed to import certificates</string>
|
||||
<string name="managed_config_allow_settings_access_title">Allow modifying settings</string>
|
||||
<string name="managed_config_allow_settings_access_description">Specifies whether users are allowed change global app settings</string>
|
||||
<string name="managed_config_default_vpn_profile_title">@string/pref_default_vpn_profile</string>
|
||||
<string name="managed_config_default_vpn_profile_description">Unique identifier of the VPN profile to use by default, use the value \"mru\" for the most recently used profile</string>
|
||||
<string name="managed_config_ignore_battery_optimizations_title">@string/pref_power_whitelist_title</string>
|
||||
<string name="managed_config_ignore_battery_optimizations_description">@string/pref_power_whitelist_summary</string>
|
||||
<string name="managed_config_profiles_array_title">VPN profiles</string>
|
||||
<string name="managed_config_profiles_array_description">Collection of managed VPN profiles</string>
|
||||
<string name="managed_config_profile_bundle_title">VPN profile</string>
|
||||
<string name="managed_config_profile_bundle_description">A managed VPN profile</string>
|
||||
|
||||
<!-- Managed configuration, VPN profile -->
|
||||
<string name="managed_config_uuid_title">Unique identifier</string>
|
||||
<string name="managed_config_uuid_description">Unique identifier of the VPN profile. Version 4 UUIDs (random-generated) are recommended</string>
|
||||
<string name="managed_config_name_title">@string/profile_name_label</string>
|
||||
<string name="managed_config_name_description">@string/profile_name_hint</string>
|
||||
<string name="managed_config_vpn_type_title">@string/profile_vpn_type_label</string>
|
||||
<string name="managed_config_vpn_type_description">The type of client authentication used by the VPN profile</string>
|
||||
<string name="managed_config_included_package_names_title">Apps allowed to use the VPN (Optional)</string>
|
||||
<string name="managed_config_included_package_names_description">Space-separated list of package names; all other apps will not see/use the VPN</string>
|
||||
<string name="managed_config_excluded_package_names_title">Apps excluded from using the VPN (Optional)</string>
|
||||
<string name="managed_config_excluded_package_names_description">Space-separated list of package names of apps excluded from using the VPN; only used of allow list is empty</string>
|
||||
<string name="managed_config_ike_proposal_title">@string/profile_proposals_ike_label</string>
|
||||
<string name="managed_config_ike_proposal_description">@string/profile_proposals_ike_hint</string>
|
||||
<string name="managed_config_esp_proposal_title">@string/profile_proposals_esp_label</string>
|
||||
<string name="managed_config_esp_proposal_description">@string/profile_proposals_esp_hint</string>
|
||||
<string name="managed_config_mtu_title">@string/profile_mtu_label</string>
|
||||
<string name="managed_config_mtu_description">@string/profile_mtu_hint</string>
|
||||
<string name="managed_config_nat_keepalive_title">@string/profile_nat_keepalive_label</string>
|
||||
<string name="managed_config_nat_keepalive_description">@string/profile_nat_keepalive_hint</string>
|
||||
<string name="managed_config_dns_server_host_names_title">@string/profile_dns_servers_label</string>
|
||||
<string name="managed_config_dns_server_host_names_description">@string/profile_dns_servers_hint</string>
|
||||
<string name="managed_config_ipv6_transport_title">@string/profile_ipv6_transport_label</string>
|
||||
<string name="managed_config_ipv6_transport_description">@string/profile_ipv6_transport_hint</string>
|
||||
|
||||
<!-- Managed configuration, VPN profile, remote -->
|
||||
<string name="managed_config_remote_bundle_title">Remote</string>
|
||||
<string name="managed_config_remote_bundle_description">Specifies information about the server</string>
|
||||
<string name="managed_config_remote_addr_title">@string/profile_gateway_label</string>
|
||||
<string name="managed_config_remote_addr_description">@string/profile_gateway_hint</string>
|
||||
<string name="managed_config_remote_port_title">@string/profile_port_label</string>
|
||||
<string name="managed_config_remote_port_description">@string/profile_port_hint</string>
|
||||
<string name="managed_config_remote_id_title">@string/profile_remote_id_label</string>
|
||||
<string name="managed_config_remote_id_description">@string/profile_remote_id_hint</string>
|
||||
<string name="managed_config_remote_cert_title">CA or server certificate (Optional)</string>
|
||||
<string name="managed_config_remote_cert_description">Base64-encoded CA or server certificate. Is imported into the app, not the system keystore. If not set, automatic CA certificate selection is enabled</string>
|
||||
<string name="managed_config_remote_certreq_title">Send certificate requests</string>
|
||||
<string name="managed_config_remote_certreq_description">Specifies whether to send certificate requests for all installed or selected CA certificates. Disabling this may reduce the size of the IKE_AUTH message if the server does not support fragmentation. But it only works if the server doesn\'t require certificate requests to send back the server certificate</string>
|
||||
<string name="managed_config_remote_revocation_ocsp_title">@string/profile_use_ocsp_label</string>
|
||||
<string name="managed_config_remote_revocation_ocsp_description">@string/profile_use_ocsp_hint</string>
|
||||
<string name="managed_config_remote_revocation_crl_title">@string/profile_use_crl_label</string>
|
||||
<string name="managed_config_remote_revocation_crl_description">@string/profile_use_crl_hint</string>
|
||||
<string name="managed_config_remote_revocation_strict_title">@string/profile_strict_revocation_label</string>
|
||||
<string name="managed_config_remote_revocation_strict_description">@string/profile_strict_revocation_hint</string>
|
||||
|
||||
<!-- Managed configuration, VPN profile, local -->
|
||||
<string name="managed_config_local_bundle_title">Local</string>
|
||||
<string name="managed_config_local_bundle_description">Specifies information about the client</string>
|
||||
<string name="managed_config_local_eap_id_title">Identity/username for EAP authentication (Optional)</string>
|
||||
<string name="managed_config_local_eap_id_description">If this is required (for username/password-based EAP authentication) but not configured here, the user is prompted for it. If it is set, the user is not able to change it. In both cases the user may optionally enter the password</string>
|
||||
<string name="managed_config_local_id_title">@string/profile_local_id_label</string>
|
||||
<string name="managed_config_local_id_description">@string/profile_local_id_hint_user</string>
|
||||
<string name="managed_config_local_p12_title">@string/profile_user_certificate_label</string>
|
||||
<string name="managed_config_local_p12_description">Base64-encoded PKCS#12-container with the client certificate and private key and optional certificate chain (the latter might cause warnings on older Android releases, see Android VPN client configuration for details). Not necessary for username/password-based EAP authentication or if the user already has the certificate/key installed as it may be selected while importing the profile</string>
|
||||
<string name="managed_config_local_p12_password_title">User certificate password (Optional)</string>
|
||||
<string name="managed_config_local_p12_password_description">Password required to extract the private key of the PKCS#12-container for installation</string>
|
||||
<string name="managed_config_local_rsa_pss_title">@string/profile_rsa_pss_label</string>
|
||||
<string name="managed_config_local_rsa_pss_description">@string/profile_rsa_pss_hint</string>
|
||||
|
||||
<!-- Managed configuration, VPN profile, split-tunneling -->
|
||||
<string name="managed_config_split_tunneling_bundle_title">@string/profile_split_tunneling_label</string>
|
||||
<string name="managed_config_split_tunneling_bundle_description">@string/profile_split_tunneling_intro</string>
|
||||
<string name="managed_config_split_tunneling_subnets_title">@string/profile_included_subnets_label</string>
|
||||
<string name="managed_config_split_tunneling_subnets_description">@string/profile_included_subnets_hint</string>
|
||||
<string name="managed_config_split_tunneling_excluded_title">@string/profile_excluded_subnets_label</string>
|
||||
<string name="managed_config_split_tunneling_excluded_description">@string/profile_excluded_subnets_hint</string>
|
||||
<string name="managed_config_split_tunneling_block_ipv4_title">@string/profile_split_tunnelingv4_title</string>
|
||||
<string name="managed_config_split_tunneling_block_ipv4_description">Specifies whether to block IPv4 traffic that\'s not destined for the VPN. Forces all IPv4 traffic via VPN (traffic that does not match the negotiated traffic selector is then just dropped). Thus this is basically equivalent to including 0.0.0.0/0 in subnets</string>
|
||||
<string name="managed_config_split_tunneling_block_ipv6_title">@string/profile_split_tunnelingv6_title</string>
|
||||
<string name="managed_config_split_tunneling_block_ipv6_description">Specifies whether to block IPv6 traffic that\'s not destined for the VPN. Forces all IPv6 traffic via VPN (traffic that does not match the negotiated traffic selector is then just dropped). Thus this is basically equivalent to including ::/0 in subnets</string>
|
||||
|
||||
</resources>
|
||||
@@ -135,6 +135,7 @@
|
||||
<string name="profile_cert_import">从VPN配置导入证书</string>
|
||||
<string name="profile_cert_alias">\"%1$s\" 所对应的证书</string>
|
||||
<string name="profile_profile_id">配置文件ID</string>
|
||||
<string name="profile_managed">Managed profile</string>
|
||||
<!-- Warnings/Notifications in the details view -->
|
||||
<string name="alert_text_no_input_gateway">必填信息以初始化连接</string>
|
||||
<string name="alert_text_no_input_username">请输入您的用户名</string>
|
||||
@@ -144,6 +145,7 @@
|
||||
<string name="alert_text_no_subnets">请输入有效的子网和/或IP地址,用空格分隔</string>
|
||||
<string name="alert_text_no_ips">请输入有效的IP地址,以空格分隔</string>
|
||||
<string name="alert_text_no_proposal">请输入用连字符分隔的有效算法列表</string>
|
||||
<string name="alert_text_vpn_profile_read_only">This VPN profile is managed by your administrator and can\'t be modified. You can only change the password</string>
|
||||
<string name="tnc_notice_title">EAP-TNC可能会影响您的隐私</string>
|
||||
<string name="tnc_notice_subtitle">设备数据已被发送至服务器管理员</string>
|
||||
<string name="tnc_notice_details"><![CDATA[<p>可信网络连接t (TNC) 允许服务器管理员评定一个用户设备的状况。</p><p>出于此目的,服务器管理员可能要求以下数据如独立ID、已安装软件列表、系统设置、或加密过的文件校验值。</p><b>任何数据都仅将在验证过服务器的身份ID之后被发出。</b>]]></string>
|
||||
|
||||
+109
@@ -0,0 +1,109 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright (C) 2023 Relution GmbH
|
||||
|
||||
Copyright (C) secunet Security Networks AG
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it
|
||||
under the terms of the GNU General Public License as published by the
|
||||
Free Software Foundation; either version 2 of the License, or (at your
|
||||
option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
for more details.
|
||||
-->
|
||||
<resources>
|
||||
|
||||
<!-- Managed configuration -->
|
||||
<string name="managed_config_allow_profile_creation_title">Allow profile creation</string>
|
||||
<string name="managed_config_allow_profile_creation_description">Specifies whether users are allowed to add their own profiles</string>
|
||||
<string name="managed_config_allow_profile_import_title">Allow profile import</string>
|
||||
<string name="managed_config_allow_profile_import_description">Specifies whether users are allowed to import their own profiles</string>
|
||||
<string name="managed_config_allow_existing_profiles_title">Show existing profiles</string>
|
||||
<string name="managed_config_allow_existing_profiles_description">Specifies whether users can continue to see and use their previously created profiles</string>
|
||||
<string name="managed_config_allow_certificate_import_title">Allow certificate import</string>
|
||||
<string name="managed_config_allow_certificate_import_description">Specifies whether users are allowed to import certificates</string>
|
||||
<string name="managed_config_allow_settings_access_title">Allow modifying settings</string>
|
||||
<string name="managed_config_allow_settings_access_description">Specifies whether users are allowed change global app settings</string>
|
||||
<string name="managed_config_default_vpn_profile_title">@string/pref_default_vpn_profile</string>
|
||||
<string name="managed_config_default_vpn_profile_description">Unique identifier of the VPN profile to use by default, use the value \"mru\" for the most recently used profile</string>
|
||||
<string name="managed_config_ignore_battery_optimizations_title">@string/pref_power_whitelist_title</string>
|
||||
<string name="managed_config_ignore_battery_optimizations_description">@string/pref_power_whitelist_summary</string>
|
||||
<string name="managed_config_profiles_array_title">VPN profiles</string>
|
||||
<string name="managed_config_profiles_array_description">Collection of managed VPN profiles</string>
|
||||
<string name="managed_config_profile_bundle_title">VPN profile</string>
|
||||
<string name="managed_config_profile_bundle_description">A managed VPN profile</string>
|
||||
|
||||
<!-- Managed configuration, VPN profile -->
|
||||
<string name="managed_config_uuid_title">Unique identifier</string>
|
||||
<string name="managed_config_uuid_description">Unique identifier of the VPN profile. Version 4 UUIDs (random-generated) are recommended</string>
|
||||
<string name="managed_config_name_title">@string/profile_name_label</string>
|
||||
<string name="managed_config_name_description">@string/profile_name_hint</string>
|
||||
<string name="managed_config_vpn_type_title">@string/profile_vpn_type_label</string>
|
||||
<string name="managed_config_vpn_type_description">The type of client authentication used by the VPN profile</string>
|
||||
<string name="managed_config_included_package_names_title">Apps allowed to use the VPN (Optional)</string>
|
||||
<string name="managed_config_included_package_names_description">Space-separated list of package names; all other apps will not see/use the VPN</string>
|
||||
<string name="managed_config_excluded_package_names_title">Apps excluded from using the VPN (Optional)</string>
|
||||
<string name="managed_config_excluded_package_names_description">Space-separated list of package names of apps excluded from using the VPN; only used of allow list is empty</string>
|
||||
<string name="managed_config_ike_proposal_title">@string/profile_proposals_ike_label</string>
|
||||
<string name="managed_config_ike_proposal_description">@string/profile_proposals_ike_hint</string>
|
||||
<string name="managed_config_esp_proposal_title">@string/profile_proposals_esp_label</string>
|
||||
<string name="managed_config_esp_proposal_description">@string/profile_proposals_esp_hint</string>
|
||||
<string name="managed_config_mtu_title">@string/profile_mtu_label</string>
|
||||
<string name="managed_config_mtu_description">@string/profile_mtu_hint</string>
|
||||
<string name="managed_config_nat_keepalive_title">@string/profile_nat_keepalive_label</string>
|
||||
<string name="managed_config_nat_keepalive_description">@string/profile_nat_keepalive_hint</string>
|
||||
<string name="managed_config_dns_server_host_names_title">@string/profile_dns_servers_label</string>
|
||||
<string name="managed_config_dns_server_host_names_description">@string/profile_dns_servers_hint</string>
|
||||
<string name="managed_config_ipv6_transport_title">@string/profile_ipv6_transport_label</string>
|
||||
<string name="managed_config_ipv6_transport_description">@string/profile_ipv6_transport_hint</string>
|
||||
|
||||
<!-- Managed configuration, VPN profile, remote -->
|
||||
<string name="managed_config_remote_bundle_title">Remote</string>
|
||||
<string name="managed_config_remote_bundle_description">Specifies information about the server</string>
|
||||
<string name="managed_config_remote_addr_title">@string/profile_gateway_label</string>
|
||||
<string name="managed_config_remote_addr_description">@string/profile_gateway_hint</string>
|
||||
<string name="managed_config_remote_port_title">@string/profile_port_label</string>
|
||||
<string name="managed_config_remote_port_description">@string/profile_port_hint</string>
|
||||
<string name="managed_config_remote_id_title">@string/profile_remote_id_label</string>
|
||||
<string name="managed_config_remote_id_description">@string/profile_remote_id_hint</string>
|
||||
<string name="managed_config_remote_cert_title">CA or server certificate (Optional)</string>
|
||||
<string name="managed_config_remote_cert_description">Base64-encoded CA or server certificate. Is imported into the app, not the system keystore. If not set, automatic CA certificate selection is enabled</string>
|
||||
<string name="managed_config_remote_certreq_title">Send certificate requests</string>
|
||||
<string name="managed_config_remote_certreq_description">Specifies whether to send certificate requests for all installed or selected CA certificates. Disabling this may reduce the size of the IKE_AUTH message if the server does not support fragmentation. But it only works if the server doesn\'t require certificate requests to send back the server certificate</string>
|
||||
<string name="managed_config_remote_revocation_ocsp_title">@string/profile_use_ocsp_label</string>
|
||||
<string name="managed_config_remote_revocation_ocsp_description">@string/profile_use_ocsp_hint</string>
|
||||
<string name="managed_config_remote_revocation_crl_title">@string/profile_use_crl_label</string>
|
||||
<string name="managed_config_remote_revocation_crl_description">@string/profile_use_crl_hint</string>
|
||||
<string name="managed_config_remote_revocation_strict_title">@string/profile_strict_revocation_label</string>
|
||||
<string name="managed_config_remote_revocation_strict_description">@string/profile_strict_revocation_hint</string>
|
||||
|
||||
<!-- Managed configuration, VPN profile, local -->
|
||||
<string name="managed_config_local_bundle_title">Local</string>
|
||||
<string name="managed_config_local_bundle_description">Specifies information about the client</string>
|
||||
<string name="managed_config_local_eap_id_title">Identity/username for EAP authentication (Optional)</string>
|
||||
<string name="managed_config_local_eap_id_description">If this is required (for username/password-based EAP authentication) but not configured here, the user is prompted for it. If it is set, the user is not able to change it. In both cases the user may optionally enter the password</string>
|
||||
<string name="managed_config_local_id_title">@string/profile_local_id_label</string>
|
||||
<string name="managed_config_local_id_description">@string/profile_local_id_hint_user</string>
|
||||
<string name="managed_config_local_p12_title">@string/profile_user_certificate_label</string>
|
||||
<string name="managed_config_local_p12_description">Base64-encoded PKCS#12-container with the client certificate and private key and optional certificate chain (the latter might cause warnings on older Android releases, see Android VPN client configuration for details). Not necessary for username/password-based EAP authentication or if the user already has the certificate/key installed as it may be selected while importing the profile</string>
|
||||
<string name="managed_config_local_p12_password_title">User certificate password (Optional)</string>
|
||||
<string name="managed_config_local_p12_password_description">Password required to extract the private key of the PKCS#12-container for installation</string>
|
||||
<string name="managed_config_local_rsa_pss_title">@string/profile_rsa_pss_label</string>
|
||||
<string name="managed_config_local_rsa_pss_description">@string/profile_rsa_pss_hint</string>
|
||||
|
||||
<!-- Managed configuration, VPN profile, split-tunneling -->
|
||||
<string name="managed_config_split_tunneling_bundle_title">@string/profile_split_tunneling_label</string>
|
||||
<string name="managed_config_split_tunneling_bundle_description">@string/profile_split_tunneling_intro</string>
|
||||
<string name="managed_config_split_tunneling_subnets_title">@string/profile_included_subnets_label</string>
|
||||
<string name="managed_config_split_tunneling_subnets_description">@string/profile_included_subnets_hint</string>
|
||||
<string name="managed_config_split_tunneling_excluded_title">@string/profile_excluded_subnets_label</string>
|
||||
<string name="managed_config_split_tunneling_excluded_description">@string/profile_excluded_subnets_hint</string>
|
||||
<string name="managed_config_split_tunneling_block_ipv4_title">@string/profile_split_tunnelingv4_title</string>
|
||||
<string name="managed_config_split_tunneling_block_ipv4_description">Specifies whether to block IPv4 traffic that\'s not destined for the VPN. Forces all IPv4 traffic via VPN (traffic that does not match the negotiated traffic selector is then just dropped). Thus this is basically equivalent to including 0.0.0.0/0 in subnets</string>
|
||||
<string name="managed_config_split_tunneling_block_ipv6_title">@string/profile_split_tunnelingv6_title</string>
|
||||
<string name="managed_config_split_tunneling_block_ipv6_description">Specifies whether to block IPv6 traffic that\'s not destined for the VPN. Forces all IPv6 traffic via VPN (traffic that does not match the negotiated traffic selector is then just dropped). Thus this is basically equivalent to including ::/0 in subnets</string>
|
||||
|
||||
</resources>
|
||||
@@ -135,6 +135,7 @@
|
||||
<string name="profile_cert_import">從VPN設定檔匯入憑證</string>
|
||||
<string name="profile_cert_alias">\"%1$s\" 對應的憑證</string>
|
||||
<string name="profile_profile_id">Profile ID</string>
|
||||
<string name="profile_managed">Managed profile</string>
|
||||
<!-- Warnings/Notifications in the details view -->
|
||||
<string name="alert_text_no_input_gateway">請填寫必要訊息才能初始化連線</string>
|
||||
<string name="alert_text_no_input_username">請輸入您的用戶名稱</string>
|
||||
@@ -144,6 +145,7 @@
|
||||
<string name="alert_text_no_subnets">Please enter valid subnets and/or IP addresses, separated by spaces</string>
|
||||
<string name="alert_text_no_ips">Please enter valid IP addresses, separated by spaces</string>
|
||||
<string name="alert_text_no_proposal">Please enter a valid list of algorithms, separated by hyphens</string>
|
||||
<string name="alert_text_vpn_profile_read_only">This VPN profile is managed by your administrator and can\'t be modified. You can only change the password</string>
|
||||
<string name="tnc_notice_title">EAP-TNC可能會影響您的隱私安全</string>
|
||||
<string name="tnc_notice_subtitle">裝置資料已經發送給伺服器管理者</string>
|
||||
<string name="tnc_notice_details"><![CDATA[<p>Trusted Network Connect (TNC) 可以讓伺服器管理者評估用戶裝置的狀況。</p><p>在這個目的下,伺服器管理者可能會要求以下資料,例如ID、已安裝的App項目、系統設定、或加密檔案驗證值。</p><b>任何資料都只有在驗證伺服器的身分ID之後才會被送出。</b>]]></string>
|
||||
|
||||
+109
@@ -0,0 +1,109 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright (C) 2023 Relution GmbH
|
||||
|
||||
Copyright (C) secunet Security Networks AG
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it
|
||||
under the terms of the GNU General Public License as published by the
|
||||
Free Software Foundation; either version 2 of the License, or (at your
|
||||
option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
for more details.
|
||||
-->
|
||||
<resources>
|
||||
|
||||
<!-- Managed configuration -->
|
||||
<string name="managed_config_allow_profile_creation_title">Allow profile creation</string>
|
||||
<string name="managed_config_allow_profile_creation_description">Specifies whether users are allowed to add their own profiles</string>
|
||||
<string name="managed_config_allow_profile_import_title">Allow profile import</string>
|
||||
<string name="managed_config_allow_profile_import_description">Specifies whether users are allowed to import their own profiles</string>
|
||||
<string name="managed_config_allow_existing_profiles_title">Show existing profiles</string>
|
||||
<string name="managed_config_allow_existing_profiles_description">Specifies whether users can continue to see and use their previously created profiles</string>
|
||||
<string name="managed_config_allow_certificate_import_title">Allow certificate import</string>
|
||||
<string name="managed_config_allow_certificate_import_description">Specifies whether users are allowed to import certificates</string>
|
||||
<string name="managed_config_allow_settings_access_title">Allow modifying settings</string>
|
||||
<string name="managed_config_allow_settings_access_description">Specifies whether users are allowed change global app settings</string>
|
||||
<string name="managed_config_default_vpn_profile_title">@string/pref_default_vpn_profile</string>
|
||||
<string name="managed_config_default_vpn_profile_description">Unique identifier of the VPN profile to use by default, use the value \"mru\" for the most recently used profile</string>
|
||||
<string name="managed_config_ignore_battery_optimizations_title">@string/pref_power_whitelist_title</string>
|
||||
<string name="managed_config_ignore_battery_optimizations_description">@string/pref_power_whitelist_summary</string>
|
||||
<string name="managed_config_profiles_array_title">VPN profiles</string>
|
||||
<string name="managed_config_profiles_array_description">Collection of managed VPN profiles</string>
|
||||
<string name="managed_config_profile_bundle_title">VPN profile</string>
|
||||
<string name="managed_config_profile_bundle_description">A managed VPN profile</string>
|
||||
|
||||
<!-- Managed configuration, VPN profile -->
|
||||
<string name="managed_config_uuid_title">Unique identifier</string>
|
||||
<string name="managed_config_uuid_description">Unique identifier of the VPN profile. Version 4 UUIDs (random-generated) are recommended</string>
|
||||
<string name="managed_config_name_title">@string/profile_name_label</string>
|
||||
<string name="managed_config_name_description">@string/profile_name_hint</string>
|
||||
<string name="managed_config_vpn_type_title">@string/profile_vpn_type_label</string>
|
||||
<string name="managed_config_vpn_type_description">The type of client authentication used by the VPN profile</string>
|
||||
<string name="managed_config_included_package_names_title">Apps allowed to use the VPN (Optional)</string>
|
||||
<string name="managed_config_included_package_names_description">Space-separated list of package names; all other apps will not see/use the VPN</string>
|
||||
<string name="managed_config_excluded_package_names_title">Apps excluded from using the VPN (Optional)</string>
|
||||
<string name="managed_config_excluded_package_names_description">Space-separated list of package names of apps excluded from using the VPN; only used of allow list is empty</string>
|
||||
<string name="managed_config_ike_proposal_title">@string/profile_proposals_ike_label</string>
|
||||
<string name="managed_config_ike_proposal_description">@string/profile_proposals_ike_hint</string>
|
||||
<string name="managed_config_esp_proposal_title">@string/profile_proposals_esp_label</string>
|
||||
<string name="managed_config_esp_proposal_description">@string/profile_proposals_esp_hint</string>
|
||||
<string name="managed_config_mtu_title">@string/profile_mtu_label</string>
|
||||
<string name="managed_config_mtu_description">@string/profile_mtu_hint</string>
|
||||
<string name="managed_config_nat_keepalive_title">@string/profile_nat_keepalive_label</string>
|
||||
<string name="managed_config_nat_keepalive_description">@string/profile_nat_keepalive_hint</string>
|
||||
<string name="managed_config_dns_server_host_names_title">@string/profile_dns_servers_label</string>
|
||||
<string name="managed_config_dns_server_host_names_description">@string/profile_dns_servers_hint</string>
|
||||
<string name="managed_config_ipv6_transport_title">@string/profile_ipv6_transport_label</string>
|
||||
<string name="managed_config_ipv6_transport_description">@string/profile_ipv6_transport_hint</string>
|
||||
|
||||
<!-- Managed configuration, VPN profile, remote -->
|
||||
<string name="managed_config_remote_bundle_title">Remote</string>
|
||||
<string name="managed_config_remote_bundle_description">Specifies information about the server</string>
|
||||
<string name="managed_config_remote_addr_title">@string/profile_gateway_label</string>
|
||||
<string name="managed_config_remote_addr_description">@string/profile_gateway_hint</string>
|
||||
<string name="managed_config_remote_port_title">@string/profile_port_label</string>
|
||||
<string name="managed_config_remote_port_description">@string/profile_port_hint</string>
|
||||
<string name="managed_config_remote_id_title">@string/profile_remote_id_label</string>
|
||||
<string name="managed_config_remote_id_description">@string/profile_remote_id_hint</string>
|
||||
<string name="managed_config_remote_cert_title">CA or server certificate (Optional)</string>
|
||||
<string name="managed_config_remote_cert_description">Base64-encoded CA or server certificate. Is imported into the app, not the system keystore. If not set, automatic CA certificate selection is enabled</string>
|
||||
<string name="managed_config_remote_certreq_title">Send certificate requests</string>
|
||||
<string name="managed_config_remote_certreq_description">Specifies whether to send certificate requests for all installed or selected CA certificates. Disabling this may reduce the size of the IKE_AUTH message if the server does not support fragmentation. But it only works if the server doesn\'t require certificate requests to send back the server certificate</string>
|
||||
<string name="managed_config_remote_revocation_ocsp_title">@string/profile_use_ocsp_label</string>
|
||||
<string name="managed_config_remote_revocation_ocsp_description">@string/profile_use_ocsp_hint</string>
|
||||
<string name="managed_config_remote_revocation_crl_title">@string/profile_use_crl_label</string>
|
||||
<string name="managed_config_remote_revocation_crl_description">@string/profile_use_crl_hint</string>
|
||||
<string name="managed_config_remote_revocation_strict_title">@string/profile_strict_revocation_label</string>
|
||||
<string name="managed_config_remote_revocation_strict_description">@string/profile_strict_revocation_hint</string>
|
||||
|
||||
<!-- Managed configuration, VPN profile, local -->
|
||||
<string name="managed_config_local_bundle_title">Local</string>
|
||||
<string name="managed_config_local_bundle_description">Specifies information about the client</string>
|
||||
<string name="managed_config_local_eap_id_title">Identity/username for EAP authentication (Optional)</string>
|
||||
<string name="managed_config_local_eap_id_description">If this is required (for username/password-based EAP authentication) but not configured here, the user is prompted for it. If it is set, the user is not able to change it. In both cases the user may optionally enter the password</string>
|
||||
<string name="managed_config_local_id_title">@string/profile_local_id_label</string>
|
||||
<string name="managed_config_local_id_description">@string/profile_local_id_hint_user</string>
|
||||
<string name="managed_config_local_p12_title">@string/profile_user_certificate_label</string>
|
||||
<string name="managed_config_local_p12_description">Base64-encoded PKCS#12-container with the client certificate and private key and optional certificate chain (the latter might cause warnings on older Android releases, see Android VPN client configuration for details). Not necessary for username/password-based EAP authentication or if the user already has the certificate/key installed as it may be selected while importing the profile</string>
|
||||
<string name="managed_config_local_p12_password_title">User certificate password (Optional)</string>
|
||||
<string name="managed_config_local_p12_password_description">Password required to extract the private key of the PKCS#12-container for installation</string>
|
||||
<string name="managed_config_local_rsa_pss_title">@string/profile_rsa_pss_label</string>
|
||||
<string name="managed_config_local_rsa_pss_description">@string/profile_rsa_pss_hint</string>
|
||||
|
||||
<!-- Managed configuration, VPN profile, split-tunneling -->
|
||||
<string name="managed_config_split_tunneling_bundle_title">@string/profile_split_tunneling_label</string>
|
||||
<string name="managed_config_split_tunneling_bundle_description">@string/profile_split_tunneling_intro</string>
|
||||
<string name="managed_config_split_tunneling_subnets_title">@string/profile_included_subnets_label</string>
|
||||
<string name="managed_config_split_tunneling_subnets_description">@string/profile_included_subnets_hint</string>
|
||||
<string name="managed_config_split_tunneling_excluded_title">@string/profile_excluded_subnets_label</string>
|
||||
<string name="managed_config_split_tunneling_excluded_description">@string/profile_excluded_subnets_hint</string>
|
||||
<string name="managed_config_split_tunneling_block_ipv4_title">@string/profile_split_tunnelingv4_title</string>
|
||||
<string name="managed_config_split_tunneling_block_ipv4_description">Specifies whether to block IPv4 traffic that\'s not destined for the VPN. Forces all IPv4 traffic via VPN (traffic that does not match the negotiated traffic selector is then just dropped). Thus this is basically equivalent to including 0.0.0.0/0 in subnets</string>
|
||||
<string name="managed_config_split_tunneling_block_ipv6_title">@string/profile_split_tunnelingv6_title</string>
|
||||
<string name="managed_config_split_tunneling_block_ipv6_description">Specifies whether to block IPv6 traffic that\'s not destined for the VPN. Forces all IPv6 traffic via VPN (traffic that does not match the negotiated traffic selector is then just dropped). Thus this is basically equivalent to including ::/0 in subnets</string>
|
||||
|
||||
</resources>
|
||||
@@ -24,6 +24,15 @@
|
||||
<item>IKEv2 EAP-TNC (Username/Password)</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="managed_config_vpn_type_values" translatable="false">
|
||||
<item>ikev2-eap</item>
|
||||
<item>ikev2-cert</item>
|
||||
<item>ikev2-cert-eap</item>
|
||||
<item>ikev2-eap-tls</item>
|
||||
<item>ikev2-byod-eap</item>
|
||||
</string-array>
|
||||
<string name="managed_config_vpn_type_default_value" translatable="false">ikev2-eap</string>
|
||||
|
||||
<!-- the order here must match the enum entries in VpnProfile.java -->
|
||||
<string-array name="apps_handling">
|
||||
<item>All applications use the VPN</item>
|
||||
|
||||
@@ -139,6 +139,7 @@
|
||||
<string name="profile_cert_import">Import certificate from VPN profile</string>
|
||||
<string name="profile_cert_alias">Certificate for \"%1$s\"</string>
|
||||
<string name="profile_profile_id">Profile ID</string>
|
||||
<string name="profile_managed">Managed profile</string>
|
||||
<!-- Warnings/Notifications in the details view -->
|
||||
<string name="alert_text_no_input_gateway">A value is required to initiate the connection</string>
|
||||
<string name="alert_text_no_input_username">Please enter your username </string>
|
||||
@@ -148,6 +149,7 @@
|
||||
<string name="alert_text_no_subnets">Please enter valid subnets and/or IP addresses, separated by spaces</string>
|
||||
<string name="alert_text_no_ips">Please enter valid IP addresses, separated by spaces</string>
|
||||
<string name="alert_text_no_proposal">Please enter a valid list of algorithms, separated by hyphens</string>
|
||||
<string name="alert_text_vpn_profile_read_only">This VPN profile is managed by your administrator and can\'t be modified. You can only change the password</string>
|
||||
<string name="tnc_notice_title">EAP-TNC may affect your privacy</string>
|
||||
<string name="tnc_notice_subtitle">Device data is sent to the server operator</string>
|
||||
<string name="tnc_notice_details"><![CDATA[<p>Trusted Network Connect (TNC) allows server operators to assess the health of a client device.</p><p>For that purpose the server operator may request data such as a unique identifier, a list of installed packages, system settings, or cryptographic checksums of files.</p><b>Any data will be sent only after verifying the server\'s identity.</b>]]></string>
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright (C) 2023 Relution GmbH
|
||||
|
||||
Copyright (C) secunet Security Networks AG
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it
|
||||
under the terms of the GNU General Public License as published by the
|
||||
Free Software Foundation; either version 2 of the License, or (at your
|
||||
option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
for more details.
|
||||
-->
|
||||
<resources>
|
||||
|
||||
<!-- Managed configuration -->
|
||||
<string name="managed_config_allow_profile_creation_title">Allow profile creation</string>
|
||||
<string name="managed_config_allow_profile_creation_description">Specifies whether users are allowed to add their own profiles</string>
|
||||
<string name="managed_config_allow_profile_import_title">Allow profile import</string>
|
||||
<string name="managed_config_allow_profile_import_description">Specifies whether users are allowed to import their own profiles</string>
|
||||
<string name="managed_config_allow_existing_profiles_title">Show existing profiles</string>
|
||||
<string name="managed_config_allow_existing_profiles_description">Specifies whether users can continue to see and use their previously created profiles</string>
|
||||
<string name="managed_config_allow_certificate_import_title">Allow certificate import</string>
|
||||
<string name="managed_config_allow_certificate_import_description">Specifies whether users are allowed to import certificates</string>
|
||||
<string name="managed_config_allow_settings_access_title">Allow modifying settings</string>
|
||||
<string name="managed_config_allow_settings_access_description">Specifies whether users are allowed change global app settings</string>
|
||||
<string name="managed_config_default_vpn_profile_title">@string/pref_default_vpn_profile</string>
|
||||
<string name="managed_config_default_vpn_profile_description">Unique identifier of the VPN profile to use by default, use the value \"mru\" for the most recently used profile</string>
|
||||
<string name="managed_config_ignore_battery_optimizations_title">@string/pref_power_whitelist_title</string>
|
||||
<string name="managed_config_ignore_battery_optimizations_description">@string/pref_power_whitelist_summary</string>
|
||||
<string name="managed_config_profiles_array_title">VPN profiles</string>
|
||||
<string name="managed_config_profiles_array_description">Collection of managed VPN profiles</string>
|
||||
<string name="managed_config_profile_bundle_title">VPN profile</string>
|
||||
<string name="managed_config_profile_bundle_description">A managed VPN profile</string>
|
||||
|
||||
<!-- Managed configuration, VPN profile -->
|
||||
<string name="managed_config_uuid_title">Unique identifier</string>
|
||||
<string name="managed_config_uuid_description">Unique identifier of the VPN profile. Version 4 UUIDs (random-generated) are recommended</string>
|
||||
<string name="managed_config_name_title">@string/profile_name_label</string>
|
||||
<string name="managed_config_name_description">@string/profile_name_hint</string>
|
||||
<string name="managed_config_vpn_type_title">@string/profile_vpn_type_label</string>
|
||||
<string name="managed_config_vpn_type_description">The type of client authentication used by the VPN profile</string>
|
||||
<string name="managed_config_included_package_names_title">Apps allowed to use the VPN (Optional)</string>
|
||||
<string name="managed_config_included_package_names_description">Space-separated list of package names; all other apps will not see/use the VPN</string>
|
||||
<string name="managed_config_excluded_package_names_title">Apps excluded from using the VPN (Optional)</string>
|
||||
<string name="managed_config_excluded_package_names_description">Space-separated list of package names of apps excluded from using the VPN; only used of allow list is empty</string>
|
||||
<string name="managed_config_ike_proposal_title">@string/profile_proposals_ike_label</string>
|
||||
<string name="managed_config_ike_proposal_description">@string/profile_proposals_ike_hint</string>
|
||||
<string name="managed_config_esp_proposal_title">@string/profile_proposals_esp_label</string>
|
||||
<string name="managed_config_esp_proposal_description">@string/profile_proposals_esp_hint</string>
|
||||
<string name="managed_config_mtu_title">@string/profile_mtu_label</string>
|
||||
<string name="managed_config_mtu_description">@string/profile_mtu_hint</string>
|
||||
<string name="managed_config_nat_keepalive_title">@string/profile_nat_keepalive_label</string>
|
||||
<string name="managed_config_nat_keepalive_description">@string/profile_nat_keepalive_hint</string>
|
||||
<string name="managed_config_dns_server_host_names_title">@string/profile_dns_servers_label</string>
|
||||
<string name="managed_config_dns_server_host_names_description">@string/profile_dns_servers_hint</string>
|
||||
<string name="managed_config_ipv6_transport_title">@string/profile_ipv6_transport_label</string>
|
||||
<string name="managed_config_ipv6_transport_description">@string/profile_ipv6_transport_hint</string>
|
||||
|
||||
<!-- Managed configuration, VPN profile, remote -->
|
||||
<string name="managed_config_remote_bundle_title">Remote</string>
|
||||
<string name="managed_config_remote_bundle_description">Specifies information about the server</string>
|
||||
<string name="managed_config_remote_addr_title">@string/profile_gateway_label</string>
|
||||
<string name="managed_config_remote_addr_description">@string/profile_gateway_hint</string>
|
||||
<string name="managed_config_remote_port_title">@string/profile_port_label</string>
|
||||
<string name="managed_config_remote_port_description">@string/profile_port_hint</string>
|
||||
<string name="managed_config_remote_id_title">@string/profile_remote_id_label</string>
|
||||
<string name="managed_config_remote_id_description">@string/profile_remote_id_hint</string>
|
||||
<string name="managed_config_remote_cert_title">CA or server certificate (Optional)</string>
|
||||
<string name="managed_config_remote_cert_description">Base64-encoded CA or server certificate. Is imported into the app, not the system keystore. If not set, automatic CA certificate selection is enabled</string>
|
||||
<string name="managed_config_remote_certreq_title">Send certificate requests</string>
|
||||
<string name="managed_config_remote_certreq_description">Specifies whether to send certificate requests for all installed or selected CA certificates. Disabling this may reduce the size of the IKE_AUTH message if the server does not support fragmentation. But it only works if the server doesn\'t require certificate requests to send back the server certificate</string>
|
||||
<string name="managed_config_remote_revocation_ocsp_title">@string/profile_use_ocsp_label</string>
|
||||
<string name="managed_config_remote_revocation_ocsp_description">@string/profile_use_ocsp_hint</string>
|
||||
<string name="managed_config_remote_revocation_crl_title">@string/profile_use_crl_label</string>
|
||||
<string name="managed_config_remote_revocation_crl_description">@string/profile_use_crl_hint</string>
|
||||
<string name="managed_config_remote_revocation_strict_title">@string/profile_strict_revocation_label</string>
|
||||
<string name="managed_config_remote_revocation_strict_description">@string/profile_strict_revocation_hint</string>
|
||||
|
||||
<!-- Managed configuration, VPN profile, local -->
|
||||
<string name="managed_config_local_bundle_title">Local</string>
|
||||
<string name="managed_config_local_bundle_description">Specifies information about the client</string>
|
||||
<string name="managed_config_local_eap_id_title">Identity/username for EAP authentication (Optional)</string>
|
||||
<string name="managed_config_local_eap_id_description">If this is required (for username/password-based EAP authentication) but not configured here, the user is prompted for it. If it is set, the user is not able to change it. In both cases the user may optionally enter the password</string>
|
||||
<string name="managed_config_local_id_title">@string/profile_local_id_label</string>
|
||||
<string name="managed_config_local_id_description">@string/profile_local_id_hint_user</string>
|
||||
<string name="managed_config_local_p12_title">@string/profile_user_certificate_label</string>
|
||||
<string name="managed_config_local_p12_description">Base64-encoded PKCS#12-container with the client certificate and private key and optional certificate chain (the latter might cause warnings on older Android releases, see Android VPN client configuration for details). Not necessary for username/password-based EAP authentication or if the user already has the certificate/key installed as it may be selected while importing the profile</string>
|
||||
<string name="managed_config_local_p12_password_title">User certificate password (Optional)</string>
|
||||
<string name="managed_config_local_p12_password_description">Password required to extract the private key of the PKCS#12-container for installation</string>
|
||||
<string name="managed_config_local_rsa_pss_title">@string/profile_rsa_pss_label</string>
|
||||
<string name="managed_config_local_rsa_pss_description">@string/profile_rsa_pss_hint</string>
|
||||
|
||||
<!-- Managed configuration, VPN profile, split-tunneling -->
|
||||
<string name="managed_config_split_tunneling_bundle_title">@string/profile_split_tunneling_label</string>
|
||||
<string name="managed_config_split_tunneling_bundle_description">@string/profile_split_tunneling_intro</string>
|
||||
<string name="managed_config_split_tunneling_subnets_title">@string/profile_included_subnets_label</string>
|
||||
<string name="managed_config_split_tunneling_subnets_description">@string/profile_included_subnets_hint</string>
|
||||
<string name="managed_config_split_tunneling_excluded_title">@string/profile_excluded_subnets_label</string>
|
||||
<string name="managed_config_split_tunneling_excluded_description">@string/profile_excluded_subnets_hint</string>
|
||||
<string name="managed_config_split_tunneling_block_ipv4_title">@string/profile_split_tunnelingv4_title</string>
|
||||
<string name="managed_config_split_tunneling_block_ipv4_description">Specifies whether to block IPv4 traffic that\'s not destined for the VPN. Forces all IPv4 traffic via VPN (traffic that does not match the negotiated traffic selector is then just dropped). Thus this is basically equivalent to including 0.0.0.0/0 in subnets</string>
|
||||
<string name="managed_config_split_tunneling_block_ipv6_title">@string/profile_split_tunnelingv6_title</string>
|
||||
<string name="managed_config_split_tunneling_block_ipv6_description">Specifies whether to block IPv6 traffic that\'s not destined for the VPN. Forces all IPv6 traffic via VPN (traffic that does not match the negotiated traffic selector is then just dropped). Thus this is basically equivalent to including ::/0 in subnets</string>
|
||||
|
||||
</resources>
|
||||
@@ -0,0 +1,303 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright (C) 2023 Relution GmbH
|
||||
|
||||
Copyright (C) secunet Security Networks AG
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it
|
||||
under the terms of the GNU General Public License as published by the
|
||||
Free Software Foundation; either version 2 of the License, or (at your
|
||||
option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
for more details.
|
||||
-->
|
||||
<restrictions xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<restriction
|
||||
android:defaultValue="false"
|
||||
android:description="@string/managed_config_allow_profile_creation_description"
|
||||
android:key="allow_profile_create"
|
||||
android:restrictionType="bool"
|
||||
android:title="@string/managed_config_allow_profile_creation_title" />
|
||||
|
||||
<restriction
|
||||
android:defaultValue="false"
|
||||
android:description="@string/managed_config_allow_profile_import_description"
|
||||
android:key="allow_profile_import"
|
||||
android:restrictionType="bool"
|
||||
android:title="@string/managed_config_allow_profile_import_title" />
|
||||
|
||||
<restriction
|
||||
android:defaultValue="false"
|
||||
android:description="@string/managed_config_allow_existing_profiles_description"
|
||||
android:key="allow_existing_profiles"
|
||||
android:restrictionType="bool"
|
||||
android:title="@string/managed_config_allow_existing_profiles_title" />
|
||||
|
||||
<restriction
|
||||
android:defaultValue="false"
|
||||
android:description="@string/managed_config_allow_certificate_import_description"
|
||||
android:key="allow_certificate_import"
|
||||
android:restrictionType="bool"
|
||||
android:title="@string/managed_config_allow_certificate_import_title" />
|
||||
|
||||
<restriction
|
||||
android:defaultValue="false"
|
||||
android:description="@string/managed_config_allow_settings_access_description"
|
||||
android:key="allow_settings_access"
|
||||
android:restrictionType="bool"
|
||||
android:title="@string/managed_config_allow_settings_access_title" />
|
||||
|
||||
<restriction
|
||||
android:defaultValue=""
|
||||
android:description="@string/managed_config_default_vpn_profile_description"
|
||||
android:key="pref_default_vpn_profile"
|
||||
android:restrictionType="string"
|
||||
android:title="@string/managed_config_default_vpn_profile_title" />
|
||||
|
||||
<restriction
|
||||
android:defaultValue="false"
|
||||
android:description="@string/managed_config_ignore_battery_optimizations_description"
|
||||
android:key="pref_ignore_power_whitelist"
|
||||
android:restrictionType="bool"
|
||||
android:title="@string/managed_config_ignore_battery_optimizations_title" />
|
||||
|
||||
<restriction
|
||||
android:description="@string/managed_config_profiles_array_description"
|
||||
android:key="managed_profiles"
|
||||
android:restrictionType="bundle_array"
|
||||
android:title="@string/managed_config_profiles_array_title">
|
||||
|
||||
<restriction
|
||||
android:description="@string/managed_config_profile_bundle_description"
|
||||
android:key="managed_profile"
|
||||
android:restrictionType="bundle"
|
||||
android:title="@string/managed_config_profile_bundle_title">
|
||||
|
||||
<restriction
|
||||
android:defaultValue=""
|
||||
android:description="@string/managed_config_uuid_description"
|
||||
android:key="_uuid"
|
||||
android:restrictionType="string"
|
||||
android:title="@string/managed_config_uuid_title" />
|
||||
|
||||
<restriction
|
||||
android:defaultValue=""
|
||||
android:description="@string/managed_config_name_description"
|
||||
android:key="name"
|
||||
android:restrictionType="string"
|
||||
android:title="@string/managed_config_name_title" />
|
||||
|
||||
<restriction
|
||||
android:defaultValue="@string/managed_config_vpn_type_default_value"
|
||||
android:description="@string/managed_config_vpn_type_description"
|
||||
android:entries="@array/vpn_types"
|
||||
android:entryValues="@array/managed_config_vpn_type_values"
|
||||
android:key="vpn_type"
|
||||
android:restrictionType="choice"
|
||||
android:title="@string/managed_config_vpn_type_title" />
|
||||
|
||||
<restriction
|
||||
android:description="@string/managed_config_remote_bundle_description"
|
||||
android:key="remote"
|
||||
android:restrictionType="bundle"
|
||||
android:title="@string/managed_config_remote_bundle_title">
|
||||
|
||||
<restriction
|
||||
android:defaultValue=""
|
||||
android:description="@string/managed_config_remote_addr_description"
|
||||
android:key="gateway"
|
||||
android:restrictionType="string"
|
||||
android:title="@string/managed_config_remote_addr_title" />
|
||||
|
||||
<restriction
|
||||
android:defaultValue="500"
|
||||
android:description="@string/managed_config_remote_port_description"
|
||||
android:key="port"
|
||||
android:restrictionType="integer"
|
||||
android:title="@string/managed_config_remote_port_title" />
|
||||
|
||||
<restriction
|
||||
android:defaultValue=""
|
||||
android:description="@string/managed_config_remote_id_description"
|
||||
android:key="remote_id"
|
||||
android:restrictionType="string"
|
||||
android:title="@string/managed_config_remote_id_title" />
|
||||
|
||||
<restriction
|
||||
android:defaultValue=""
|
||||
android:description="@string/managed_config_remote_cert_description"
|
||||
android:key="certificate"
|
||||
android:restrictionType="string"
|
||||
android:title="@string/managed_config_remote_cert_title" />
|
||||
|
||||
<restriction
|
||||
android:defaultValue="false"
|
||||
android:description="@string/managed_config_remote_certreq_description"
|
||||
android:key="remote_cert_req"
|
||||
android:restrictionType="bool"
|
||||
android:title="@string/managed_config_remote_certreq_title" />
|
||||
|
||||
<restriction
|
||||
android:defaultValue="true"
|
||||
android:description="@string/managed_config_remote_revocation_ocsp_description"
|
||||
android:key="remote_revocation_ocsp"
|
||||
android:restrictionType="bool"
|
||||
android:title="@string/managed_config_remote_revocation_ocsp_title" />
|
||||
|
||||
<restriction
|
||||
android:defaultValue="true"
|
||||
android:description="@string/managed_config_remote_revocation_crl_description"
|
||||
android:key="remote_revocation_crl"
|
||||
android:restrictionType="bool"
|
||||
android:title="@string/managed_config_remote_revocation_crl_title" />
|
||||
|
||||
<restriction
|
||||
android:defaultValue="false"
|
||||
android:description="@string/managed_config_remote_revocation_strict_description"
|
||||
android:key="remote_revocation_strict"
|
||||
android:restrictionType="bool"
|
||||
android:title="@string/managed_config_remote_revocation_strict_title" />
|
||||
</restriction>
|
||||
|
||||
<restriction
|
||||
android:description="@string/managed_config_local_bundle_description"
|
||||
android:key="local"
|
||||
android:restrictionType="bundle"
|
||||
android:title="@string/managed_config_local_bundle_title">
|
||||
|
||||
<restriction
|
||||
android:defaultValue=""
|
||||
android:description="@string/managed_config_local_eap_id_description"
|
||||
android:key="username"
|
||||
android:restrictionType="string"
|
||||
android:title="@string/managed_config_local_eap_id_title" />
|
||||
|
||||
<restriction
|
||||
android:defaultValue=""
|
||||
android:description="@string/managed_config_local_id_description"
|
||||
android:key="local_id"
|
||||
android:restrictionType="string"
|
||||
android:title="@string/managed_config_local_id_title" />
|
||||
|
||||
<restriction
|
||||
android:defaultValue=""
|
||||
android:description="@string/managed_config_local_p12_description"
|
||||
android:key="user_certificate"
|
||||
android:restrictionType="string"
|
||||
android:title="@string/managed_config_local_p12_title" />
|
||||
|
||||
<restriction
|
||||
android:defaultValue=""
|
||||
android:description="@string/managed_config_local_p12_password_description"
|
||||
android:key="user_certificate_password"
|
||||
android:restrictionType="string"
|
||||
android:title="@string/managed_config_local_p12_password_title" />
|
||||
|
||||
<restriction
|
||||
android:defaultValue="false"
|
||||
android:description="@string/managed_config_local_rsa_pss_description"
|
||||
android:key="local_rsa_pss"
|
||||
android:restrictionType="bool"
|
||||
android:title="@string/managed_config_local_rsa_pss_title" />
|
||||
|
||||
</restriction>
|
||||
|
||||
<restriction
|
||||
android:defaultValue=""
|
||||
android:description="@string/managed_config_included_package_names_description"
|
||||
android:key="included_apps"
|
||||
android:restrictionType="string"
|
||||
android:title="@string/managed_config_included_package_names_title" />
|
||||
|
||||
<restriction
|
||||
android:defaultValue=""
|
||||
android:description="@string/managed_config_excluded_package_names_description"
|
||||
android:key="excluded_apps"
|
||||
android:restrictionType="string"
|
||||
android:title="@string/managed_config_excluded_package_names_title" />
|
||||
|
||||
<restriction
|
||||
android:defaultValue=""
|
||||
android:description="@string/managed_config_ike_proposal_description"
|
||||
android:key="ike_proposal"
|
||||
android:restrictionType="string"
|
||||
android:title="@string/managed_config_ike_proposal_title" />
|
||||
|
||||
<restriction
|
||||
android:defaultValue=""
|
||||
android:description="@string/managed_config_esp_proposal_description"
|
||||
android:key="esp_proposal"
|
||||
android:restrictionType="string"
|
||||
android:title="@string/managed_config_esp_proposal_title" />
|
||||
|
||||
<restriction
|
||||
android:defaultValue="-1"
|
||||
android:description="@string/managed_config_mtu_description"
|
||||
android:key="mtu"
|
||||
android:restrictionType="integer"
|
||||
android:title="@string/managed_config_mtu_title" />
|
||||
|
||||
<restriction
|
||||
android:defaultValue="-1"
|
||||
android:description="@string/managed_config_nat_keepalive_description"
|
||||
android:key="nat_keepalive"
|
||||
android:restrictionType="integer"
|
||||
android:title="@string/managed_config_nat_keepalive_title" />
|
||||
|
||||
<restriction
|
||||
android:defaultValue=""
|
||||
android:description="@string/managed_config_dns_server_host_names_description"
|
||||
android:key="dns_servers"
|
||||
android:restrictionType="string"
|
||||
android:title="@string/managed_config_dns_server_host_names_title" />
|
||||
|
||||
<restriction
|
||||
android:defaultValue="false"
|
||||
android:description="@string/managed_config_ipv6_transport_description"
|
||||
android:key="transport_ipv6"
|
||||
android:restrictionType="bool"
|
||||
android:title="@string/managed_config_ipv6_transport_title" />
|
||||
|
||||
<restriction
|
||||
android:description="@string/managed_config_split_tunneling_bundle_description"
|
||||
android:key="split_tunnelling"
|
||||
android:restrictionType="bundle"
|
||||
android:title="@string/managed_config_split_tunneling_bundle_title">
|
||||
|
||||
<restriction
|
||||
android:defaultValue=""
|
||||
android:description="@string/managed_config_split_tunneling_subnets_description"
|
||||
android:key="included_subnets"
|
||||
android:restrictionType="string"
|
||||
android:title="@string/managed_config_split_tunneling_subnets_title" />
|
||||
|
||||
<restriction
|
||||
android:defaultValue=""
|
||||
android:description="@string/managed_config_split_tunneling_excluded_description"
|
||||
android:key="excluded_subnets"
|
||||
android:restrictionType="string"
|
||||
android:title="@string/managed_config_split_tunneling_excluded_title" />
|
||||
|
||||
<restriction
|
||||
android:defaultValue="false"
|
||||
android:description="@string/managed_config_split_tunneling_block_ipv4_description"
|
||||
android:key="split_tunnelling_block_IPv4"
|
||||
android:restrictionType="bool"
|
||||
android:title="@string/managed_config_split_tunneling_block_ipv4_title" />
|
||||
|
||||
<restriction
|
||||
android:defaultValue="false"
|
||||
android:description="@string/managed_config_split_tunneling_block_ipv6_description"
|
||||
android:key="split_tunnelling_block_IPv6"
|
||||
android:restrictionType="bool"
|
||||
android:title="@string/managed_config_split_tunneling_block_ipv6_title" />
|
||||
|
||||
</restriction>
|
||||
|
||||
</restriction>
|
||||
</restriction>
|
||||
</restrictions>
|
||||
+151
@@ -0,0 +1,151 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Relution GmbH
|
||||
*
|
||||
* Copyright (C) secunet Security Networks AG
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*/
|
||||
|
||||
package org.strongswan.android.utils;
|
||||
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import androidx.core.util.Pair;
|
||||
|
||||
public class DifferenceTest
|
||||
{
|
||||
@Test
|
||||
public void testElementAdded()
|
||||
{
|
||||
final Element element = new Element("a", 0);
|
||||
final List<Element> existing = List.of();
|
||||
final List<Element> modified = List.of(element);
|
||||
|
||||
final Difference<Element> diff = Difference.between(existing, modified, Element::getKey);
|
||||
|
||||
assertThat(diff.getInserts()).containsExactly(element);
|
||||
assertThat(diff.getUpdates()).isEmpty();
|
||||
assertThat(diff.getUnchanged()).isEmpty();
|
||||
assertThat(diff.getDeletes()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testElementRemoved()
|
||||
{
|
||||
final Element element = new Element("a", 0);
|
||||
final List<Element> existing = List.of(element);
|
||||
final List<Element> modified = List.of();
|
||||
|
||||
final Difference<Element> diff = Difference.between(existing, modified, Element::getKey);
|
||||
|
||||
assertThat(diff.getInserts()).isEmpty();
|
||||
assertThat(diff.getUpdates()).isEmpty();
|
||||
assertThat(diff.getUnchanged()).isEmpty();
|
||||
assertThat(diff.getDeletes()).containsExactly(element);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testElementIdentical()
|
||||
{
|
||||
final Element element0 = new Element("a", 0);
|
||||
final Element element1 = new Element("a", 0);
|
||||
final List<Element> existing = List.of(element0);
|
||||
final List<Element> modified = List.of(element1);
|
||||
|
||||
final Difference<Element> diff = Difference.between(existing, modified, Element::getKey);
|
||||
|
||||
assertThat(diff.getInserts()).isEmpty();
|
||||
assertThat(diff.getUpdates()).isEmpty();
|
||||
assertThat(diff.getUnchanged()).containsExactly(element0);
|
||||
assertThat(diff.getDeletes()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testElementSwap()
|
||||
{
|
||||
final Element elementA = new Element("a", 0);
|
||||
final Element elementB = new Element("b", 0);
|
||||
final List<Element> existing = List.of(elementA);
|
||||
final List<Element> modified = List.of(elementB);
|
||||
|
||||
final Difference<Element> diff = Difference.between(existing, modified, Element::getKey);
|
||||
|
||||
assertThat(diff.getInserts()).containsExactly(elementB);
|
||||
assertThat(diff.getUpdates()).isEmpty();
|
||||
assertThat(diff.getUnchanged()).isEmpty();
|
||||
assertThat(diff.getDeletes()).containsExactly(elementA);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testElementUpdate()
|
||||
{
|
||||
final Element elementA0 = new Element("a", 0);
|
||||
final Element elementA1 = new Element("a", 1);
|
||||
final List<Element> existing = List.of(elementA0);
|
||||
final List<Element> modified = List.of(elementA1);
|
||||
|
||||
final Difference<Element> diff = Difference.between(existing, modified, Element::getKey);
|
||||
|
||||
assertThat(diff.getInserts()).isEmpty();
|
||||
assertThat(diff.getUpdates()).containsExactly(Pair.create(elementA0, elementA1));
|
||||
assertThat(diff.getUnchanged()).isEmpty();
|
||||
assertThat(diff.getDeletes()).isEmpty();
|
||||
}
|
||||
|
||||
private static class Element
|
||||
{
|
||||
private final String key;
|
||||
private final int value;
|
||||
|
||||
public Element(final String key, final int value)
|
||||
{
|
||||
this.key = key;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public String getKey()
|
||||
{
|
||||
return key;
|
||||
}
|
||||
|
||||
public int getValue()
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o)
|
||||
{
|
||||
if (this == o)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
Element element = (Element)o;
|
||||
return value == element.value && Objects.equals(key, element.key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
return Objects.hash(key, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user