diff --git a/src/frontends/android/app/src/main/java/org/strongswan/android/logic/CharonVpnService.java b/src/frontends/android/app/src/main/java/org/strongswan/android/logic/CharonVpnService.java index d8e07a873..ba3d434ce 100644 --- a/src/frontends/android/app/src/main/java/org/strongswan/android/logic/CharonVpnService.java +++ b/src/frontends/android/app/src/main/java/org/strongswan/android/logic/CharonVpnService.java @@ -74,6 +74,7 @@ public class CharonVpnService extends VpnService implements Runnable, VpnStateSe public static final int VPN_STATE_NOTIFICATION_ID = 1; private String mLogFile; + private String mAppDir; private VpnProfileDataSource mDataSource; private Thread mConnectionHandler; private VpnProfile mCurrentProfile; @@ -152,6 +153,7 @@ public class CharonVpnService extends VpnService implements Runnable, VpnStateSe public void onCreate() { mLogFile = getFilesDir().getAbsolutePath() + File.separator + LOG_FILE; + mAppDir = getFilesDir().getAbsolutePath(); mDataSource = new VpnProfileDataSource(this); mDataSource.open(); @@ -244,7 +246,7 @@ public class CharonVpnService extends VpnService implements Runnable, VpnStateSe addNotification(); BuilderAdapter builder = new BuilderAdapter(mCurrentProfile); - if (initializeCharon(builder, mLogFile, mCurrentProfile.getVpnType().has(VpnTypeFeature.BYOD))) + if (initializeCharon(builder, mLogFile, mAppDir, mCurrentProfile.getVpnType().has(VpnTypeFeature.BYOD))) { Log.i(TAG, "charon started"); SettingsWriter writer = new SettingsWriter(); @@ -645,10 +647,11 @@ public class CharonVpnService extends VpnService implements Runnable, VpnStateSe * * @param builder BuilderAdapter for this connection * @param logfile absolute path to the logfile + * @param appdir absolute path to the data directory of the app * @param byod enable BYOD features * @return TRUE if initialization was successful */ - public native boolean initializeCharon(BuilderAdapter builder, String logfile, boolean byod); + public native boolean initializeCharon(BuilderAdapter builder, String logfile, String appdir, boolean byod); /** * Deinitialize charon, provided by libandroidbridge.so @@ -974,7 +977,8 @@ public class CharonVpnService extends VpnService implements Runnable, VpnStateSe builder.addRoute("::", 0); } /* apply selected applications */ - if (mSelectedApps.size() > 0) + if (mSelectedApps.size() > 0 && + Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { switch (mAppHandling) { diff --git a/src/frontends/android/app/src/main/java/org/strongswan/android/logic/SimpleFetcher.java b/src/frontends/android/app/src/main/java/org/strongswan/android/logic/SimpleFetcher.java index e7f029ba2..b7334cee1 100644 --- a/src/frontends/android/app/src/main/java/org/strongswan/android/logic/SimpleFetcher.java +++ b/src/frontends/android/app/src/main/java/org/strongswan/android/logic/SimpleFetcher.java @@ -17,16 +17,18 @@ package org.strongswan.android.logic; import android.support.annotation.Keep; +import java.io.BufferedOutputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; +import java.io.OutputStream; import java.net.HttpURLConnection; import java.net.URL; @Keep public class SimpleFetcher { - public static byte[] fetch(String uri) throws IOException + public static byte[] fetch(String uri, byte[] data, String contentType) throws IOException { URL url = new URL(uri); HttpURLConnection conn = (HttpURLConnection)url.openConnection(); @@ -34,6 +36,18 @@ public class SimpleFetcher conn.setReadTimeout(10000); try { + if (contentType != null) + { + conn.setRequestProperty("Content-Type", contentType); + } + if (data != null) + { + conn.setDoOutput(true); + conn.setFixedLengthStreamingMode(data.length); + OutputStream out = new BufferedOutputStream(conn.getOutputStream()); + out.write(data); + out.close(); + } return streamToArray(conn.getInputStream()); } finally @@ -42,7 +56,7 @@ public class SimpleFetcher } } - private static byte[] streamToArray(InputStream in) + private static byte[] streamToArray(InputStream in) throws IOException { ByteArrayOutputStream out = new ByteArrayOutputStream(); byte[] buf = new byte[1024]; @@ -60,6 +74,10 @@ public class SimpleFetcher { e.printStackTrace(); } + finally + { + in.close(); + } return null; } } diff --git a/src/frontends/android/app/src/main/java/org/strongswan/android/security/TrustedCertificateEntry.java b/src/frontends/android/app/src/main/java/org/strongswan/android/security/TrustedCertificateEntry.java index 5e9873d1b..4a1dc8e5f 100644 --- a/src/frontends/android/app/src/main/java/org/strongswan/android/security/TrustedCertificateEntry.java +++ b/src/frontends/android/app/src/main/java/org/strongswan/android/security/TrustedCertificateEntry.java @@ -43,29 +43,38 @@ public class TrustedCertificateEntry implements Comparable list = new ArrayList<>(); + + for (String file : fileList()) + { + if (file.startsWith(FILE_PREFIX)) + { + list.add(file); + } + } + if (list.size() == 0) + { + Toast.makeText(this, R.string.clear_crl_cache_msg_none, Toast.LENGTH_SHORT).show(); + return; + } + removeFragmentByTag(DIALOG_TAG); + + Bundle args = new Bundle(); + args.putStringArrayList(EXTRA_CRL_LIST, list); + + CRLCacheDialog dialog = new CRLCacheDialog(); + dialog.setArguments(args); + dialog.show(this.getSupportFragmentManager(), DIALOG_TAG); + } + /** * Class that loads the cached CA certificates. */ @@ -429,42 +474,65 @@ public class MainActivity extends AppCompatActivity implements OnVpnProfileSelec button = R.string.disconnect; } - return new AlertDialog.Builder(getActivity()) + DialogInterface.OnClickListener connectListener = new DialogInterface.OnClickListener() + { + @Override + public void onClick(DialogInterface dialog, int which) + { + MainActivity activity = (MainActivity)getActivity(); + activity.startVpnProfile(profileInfo); + } + }; + DialogInterface.OnClickListener disconnectListener = new DialogInterface.OnClickListener() + { + @Override + public void onClick(DialogInterface dialog, int which) + { + MainActivity activity = (MainActivity)getActivity(); + if (activity.mService != null) + { + activity.mService.disconnect(); + } + } + }; + DialogInterface.OnClickListener cancelListener = new DialogInterface.OnClickListener() + { + @Override + public void onClick(DialogInterface dialog, int which) + { + dismiss(); + if (!profileInfo.getBoolean(PROFILE_FOREGROUND)) + { /* if the app was not in the foreground before this action was triggered + * externally, we just close the activity if canceled */ + getActivity().finish(); + } + } + }; + + AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()) .setIcon(icon) .setTitle(String.format(getString(title), profileInfo.getString(PROFILE_NAME))) - .setMessage(message) - .setPositiveButton(button, new DialogInterface.OnClickListener() - { - @Override - public void onClick(DialogInterface dialog, int whichButton) - { - MainActivity activity = (MainActivity)getActivity(); - if (profileInfo.getBoolean(PROFILE_DISCONNECT)) - { - if (activity.mService != null) - { - activity.mService.disconnect(); - } - } - else - { - activity.startVpnProfile(profileInfo); - } - } - }) - .setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() - { - @Override - public void onClick(DialogInterface dialog, int which) - { - dismiss(); - if (!profileInfo.getBoolean(PROFILE_FOREGROUND)) - { /* if the app was not in the foreground before this action was triggered - * externally, we just close the activity if canceled */ - getActivity().finish(); - } - } - }).create(); + .setMessage(message); + + if (profileInfo.getBoolean(PROFILE_DISCONNECT)) + { + builder.setPositiveButton(button, disconnectListener); + } + else + { + builder.setPositiveButton(button, connectListener); + } + + if (profileInfo.getBoolean(PROFILE_RECONNECT)) + { + builder.setNegativeButton(R.string.disconnect, disconnectListener); + builder.setNeutralButton(android.R.string.cancel, cancelListener); + } + else + { + builder.setNegativeButton(android.R.string.cancel, cancelListener); + } + return builder.create(); } } @@ -545,4 +613,49 @@ public class MainActivity extends AppCompatActivity implements OnVpnProfileSelec }).create(); } } + + /** + * Confirmation dialog to clear CRL cache + */ + public static class CRLCacheDialog extends AppCompatDialogFragment + { + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) + { + final List list = getArguments().getStringArrayList(EXTRA_CRL_LIST); + String size; + long s = 0; + + for (String file : list) + { + File crl = getActivity().getFileStreamPath(file); + s += crl.length(); + } + 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() + { + @Override + public void onClick(DialogInterface dialog, int which) + { + dismiss(); + } + }) + .setPositiveButton(R.string.clear, new DialogInterface.OnClickListener() + { + @Override + public void onClick(DialogInterface dialog, int whichButton) + { + for (String file : list) + { + getActivity().deleteFile(file); + } + } + }); + builder.setMessage(getActivity().getResources().getQuantityString(R.plurals.clear_crl_cache_msg, list.size(), list.size(), size)); + return builder.create(); + } + } } diff --git a/src/frontends/android/app/src/main/java/org/strongswan/android/ui/VpnProfileDetailActivity.java b/src/frontends/android/app/src/main/java/org/strongswan/android/ui/VpnProfileDetailActivity.java index 8bf5fd2b2..8792fd7e4 100644 --- a/src/frontends/android/app/src/main/java/org/strongswan/android/ui/VpnProfileDetailActivity.java +++ b/src/frontends/android/app/src/main/java/org/strongswan/android/ui/VpnProfileDetailActivity.java @@ -22,6 +22,7 @@ import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.os.AsyncTask; +import android.os.Build; import android.os.Bundle; import android.security.KeyChain; import android.security.KeyChainAliasCallback; @@ -187,6 +188,13 @@ public class VpnProfileDetailActivity extends AppCompatActivity mName.setAdapter(completeAdapter); mRemoteId.setAdapter(completeAdapter); + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) + { + 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) {} diff --git a/src/frontends/android/app/src/main/java/org/strongswan/android/ui/VpnProfileImportActivity.java b/src/frontends/android/app/src/main/java/org/strongswan/android/ui/VpnProfileImportActivity.java index 5cfcf4895..cee95c36f 100644 --- a/src/frontends/android/app/src/main/java/org/strongswan/android/ui/VpnProfileImportActivity.java +++ b/src/frontends/android/app/src/main/java/org/strongswan/android/ui/VpnProfileImportActivity.java @@ -65,6 +65,7 @@ import java.io.ByteArrayOutputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; +import java.lang.OutOfMemoryError; import java.net.URL; import java.net.UnknownHostException; import java.security.KeyStore; @@ -715,7 +716,14 @@ public class VpnProfileImportActivity extends AppCompatActivity } if (in != null) { - result.Profile = streamToString(in); + try + { + result.Profile = streamToString(in); + } + catch (OutOfMemoryError e) + { /* just use a generic exception */ + result.ThrownException = new RuntimeException(); + } } return result; } diff --git a/src/frontends/android/app/src/main/jni/Android.mk b/src/frontends/android/app/src/main/jni/Android.mk index 1cffa5938..a46139421 100644 --- a/src/frontends/android/app/src/main/jni/Android.mk +++ b/src/frontends/android/app/src/main/jni/Android.mk @@ -7,7 +7,7 @@ strongswan_USE_BYOD := true strongswan_CHARON_PLUGINS := android-log openssl fips-prf random nonce pubkey \ chapoly curve25519 pkcs1 pkcs8 pem xcbc hmac socket-default revocation \ - eap-identity eap-mschapv2 eap-md5 eap-gtc eap-tls + eap-identity eap-mschapv2 eap-md5 eap-gtc eap-tls x509 ifneq ($(strongswan_USE_BYOD),) strongswan_BYOD_PLUGINS := eap-ttls eap-tnc tnc-imc tnc-tnccs tnccs-20 diff --git a/src/frontends/android/app/src/main/jni/libandroidbridge/backend/android_creds.c b/src/frontends/android/app/src/main/jni/libandroidbridge/backend/android_creds.c index ddc032638..0c460a9cf 100644 --- a/src/frontends/android/app/src/main/jni/libandroidbridge/backend/android_creds.c +++ b/src/frontends/android/app/src/main/jni/libandroidbridge/backend/android_creds.c @@ -1,6 +1,6 @@ /* - * Copyright (C) 2012 Tobias Brunner - * Hochschule fuer Technik Rapperswil + * Copyright (C) 2012-2017 Tobias Brunner + * HSR Hochschule fuer Technik Rapperswil * * 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 @@ -13,6 +13,11 @@ * for more details. */ +#include +#include +#include +#include + #include "android_creds.h" #include "../charonservice.h" @@ -21,6 +26,8 @@ #include #include +#define CRL_PREFIX "crl-" + typedef struct private_android_creds_t private_android_creds_t; /** @@ -47,6 +54,11 @@ struct private_android_creds_t { * TRUE if certificates have been loaded via JNI */ bool loaded; + + /** + * Directory for CRLs + */ + char *crldir; }; /** @@ -86,15 +98,79 @@ static void load_trusted_certificates(private_android_creds_t *this) } } +/** + * Load a CRL from a file + */ +static void load_crl(private_android_creds_t *this, char *file) +{ + certificate_t *cert; + time_t now, notAfter; + + cert = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509_CRL, + BUILD_FROM_FILE, file, BUILD_END); + if (cert) + { + now = time(NULL); + if (cert->get_validity(cert, &now, NULL, ¬After)) + { + DBG1(DBG_CFG, "loaded crl issued by '%Y'", cert->get_issuer(cert)); + this->creds->add_crl(this->creds, (crl_t*)cert); + } + else + { + DBG1(DBG_CFG, "deleted crl issued by '%Y', expired (%V ago)", + cert->get_issuer(cert), &now, ¬After); + unlink(file); + } + } + else + { + DBG1(DBG_CFG, "loading crl failed"); + unlink(file); + } +} + +/** + * Load cached CRLs + */ +static void load_crls(private_android_creds_t *this) +{ + enumerator_t *enumerator; + struct stat st; + char *rel, *abs; + + enumerator = enumerator_create_directory(this->crldir); + if (enumerator) + { + while (enumerator->enumerate(enumerator, &rel, &abs, &st)) + { + if (S_ISREG(st.st_mode) && strpfx(rel, CRL_PREFIX)) + { + load_crl(this, abs); + } + } + enumerator->destroy(enumerator); + } + else + { + DBG1(DBG_CFG, " reading directory '%s' failed", this->crldir); + } +} + METHOD(credential_set_t, create_cert_enumerator, enumerator_t*, private_android_creds_t *this, certificate_type_t cert, key_type_t key, identification_t *id, bool trusted) { enumerator_t *enumerator; - if (cert != CERT_ANY && cert != CERT_X509) + switch (cert) { - return NULL; + case CERT_ANY: + case CERT_X509: + case CERT_X509_CRL: + break; + default: + return NULL; } this->lock->read_lock(this->lock); if (!this->loaded) @@ -104,6 +180,7 @@ METHOD(credential_set_t, create_cert_enumerator, enumerator_t*, /* check again after acquiring the write lock */ if (!this->loaded) { + load_crls(this); load_trusted_certificates(this); this->loaded = TRUE; } @@ -116,6 +193,46 @@ METHOD(credential_set_t, create_cert_enumerator, enumerator_t*, this->lock); } +METHOD(credential_set_t, cache_cert, void, + private_android_creds_t *this, certificate_t *cert) +{ + if (this->crldir && cert->get_type(cert) == CERT_X509_CRL) + { + /* CRLs get written to ///crl-[-delta] */ + crl_t *crl = (crl_t*)cert; + + cert->get_ref(cert); + if (this->creds->add_crl(this->creds, crl)) + { + char buf[BUF_LEN]; + chunk_t chunk, hex; + bool is_delta_crl; + + is_delta_crl = crl->is_delta_crl(crl, NULL); + chunk = crl->get_authKeyIdentifier(crl); + hex = chunk_to_hex(chunk, NULL, FALSE); + snprintf(buf, sizeof(buf), "%s/%s%s%s", this->crldir, CRL_PREFIX, + hex.ptr, is_delta_crl ? "-delta" : ""); + free(hex.ptr); + + if (cert->get_encoding(cert, CERT_ASN1_DER, &chunk)) + { + if (chunk_write(chunk, buf, 022, TRUE)) + { + DBG1(DBG_CFG, " written crl to file (%d bytes)", + chunk.len); + } + else + { + DBG1(DBG_CFG, " writing crl to file failed: %s", + strerror(errno)); + } + free(chunk.ptr); + } + } + } +} + METHOD(android_creds_t, add_username_password, void, private_android_creds_t *this, char *username, char *password) { @@ -224,13 +341,14 @@ METHOD(android_creds_t, destroy, void, clear(this); this->creds->destroy(this->creds); this->lock->destroy(this->lock); + free(this->crldir); free(this); } /** * Described in header. */ -android_creds_t *android_creds_create() +android_creds_t *android_creds_create(char *crldir) { private_android_creds_t *this; @@ -241,7 +359,7 @@ android_creds_t *android_creds_create() .create_shared_enumerator = _create_shared_enumerator, .create_private_enumerator = _create_private_enumerator, .create_cdp_enumerator = (void*)return_null, - .cache_cert = (void*)nop, + .cache_cert = _cache_cert, }, .add_username_password = _add_username_password, .load_user_certificate = _load_user_certificate, @@ -250,6 +368,7 @@ android_creds_t *android_creds_create() }, .creds = mem_cred_create(), .lock = rwlock_create(RWLOCK_TYPE_DEFAULT), + .crldir = strdupnull(crldir), ); return &this->public; diff --git a/src/frontends/android/app/src/main/jni/libandroidbridge/backend/android_creds.h b/src/frontends/android/app/src/main/jni/libandroidbridge/backend/android_creds.h index 918708f14..869e0afea 100644 --- a/src/frontends/android/app/src/main/jni/libandroidbridge/backend/android_creds.h +++ b/src/frontends/android/app/src/main/jni/libandroidbridge/backend/android_creds.h @@ -1,6 +1,6 @@ /* - * Copyright (C) 2012 Tobias Brunner - * Hochschule fuer Technik Rapperswil + * Copyright (C) 2012-2017 Tobias Brunner + * HSR Hochschule fuer Technik Rapperswil * * 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 @@ -67,8 +67,10 @@ struct android_creds_t { /** * Create an android_creds instance. + * + * @param crldir directory for cached CRLs */ -android_creds_t *android_creds_create(); +android_creds_t *android_creds_create(char *crldir); #endif /** ANDROID_CREDS_H_ @}*/ diff --git a/src/frontends/android/app/src/main/jni/libandroidbridge/backend/android_fetcher.c b/src/frontends/android/app/src/main/jni/libandroidbridge/backend/android_fetcher.c index ce714106b..2e23f8b10 100644 --- a/src/frontends/android/app/src/main/jni/libandroidbridge/backend/android_fetcher.c +++ b/src/frontends/android/app/src/main/jni/libandroidbridge/backend/android_fetcher.c @@ -31,6 +31,16 @@ struct android_fetcher_t { * Callback function */ fetcher_callback_t cb; + + /** + * Data to POST + */ + chunk_t data; + + /** + * Type of data to POST + */ + char *request_type; }; METHOD(fetcher_t, fetch, status_t, @@ -38,8 +48,8 @@ METHOD(fetcher_t, fetch, status_t, { JNIEnv *env; jmethodID method_id; - jobjectArray jdata; - jstring juri; + jobjectArray jdata = NULL; + jstring juri, jct = NULL; chunk_t data; status_t status = FAILED; @@ -51,7 +61,7 @@ METHOD(fetcher_t, fetch, status_t, androidjni_attach_thread(&env); /* can't use FindClass here as this is not called by the main thread */ method_id = (*env)->GetStaticMethodID(env, android_simple_fetcher_class, - "fetch", "(Ljava/lang/String;)[B"); + "fetch", "(Ljava/lang/String;[BLjava/lang/String;)[B"); if (!method_id) { goto failed; @@ -61,8 +71,24 @@ METHOD(fetcher_t, fetch, status_t, { goto failed; } + if (this->request_type) + { + jct = (*env)->NewStringUTF(env, this->request_type); + if (!jct) + { + goto failed; + } + } + if (this->data.ptr) + { + jdata = byte_array_from_chunk(env, this->data); + if (!jdata) + { + goto failed; + } + } jdata = (*env)->CallStaticObjectMethod(env, android_simple_fetcher_class, - method_id, juri); + method_id, juri, jdata, jct); if (!jdata || androidjni_exception_occurred(env)) { goto failed; @@ -97,6 +123,16 @@ METHOD(fetcher_t, set_option, bool, this->cb = va_arg(args, fetcher_callback_t); break; } + case FETCH_REQUEST_DATA: + { + this->data = chunk_clone(va_arg(args, chunk_t)); + break; + } + case FETCH_REQUEST_TYPE: + { + this->request_type = strdup(va_arg(args, char*)); + break; + } default: supported = FALSE; break; @@ -108,6 +144,8 @@ METHOD(fetcher_t, set_option, bool, METHOD(fetcher_t, destroy, void, android_fetcher_t *this) { + chunk_clear(&this->data); + free(this->request_type); free(this); } diff --git a/src/frontends/android/app/src/main/jni/libandroidbridge/charonservice.c b/src/frontends/android/app/src/main/jni/libandroidbridge/charonservice.c index f74936edb..62427338a 100644 --- a/src/frontends/android/app/src/main/jni/libandroidbridge/charonservice.c +++ b/src/frontends/android/app/src/main/jni/libandroidbridge/charonservice.c @@ -515,7 +515,7 @@ static void set_options(char *logfile) * Initialize the charonservice object */ static void charonservice_init(JNIEnv *env, jobject service, jobject builder, - jboolean byod) + char *appdir, jboolean byod) { private_charonservice_t *this; static plugin_feature_t features[] = { @@ -526,6 +526,7 @@ static void charonservice_init(JNIEnv *env, jobject service, jobject builder, PLUGIN_CALLBACK(charonservice_register, NULL), PLUGIN_PROVIDE(CUSTOM, "android-backend"), PLUGIN_DEPENDS(CUSTOM, "libcharon"), + PLUGIN_DEPENDS(CERT_DECODE, CERT_X509_CRL), PLUGIN_REGISTER(FETCHER, android_fetcher_create), PLUGIN_PROVIDE(FETCHER, "http://"), PLUGIN_PROVIDE(FETCHER, "https://"), @@ -544,7 +545,7 @@ static void charonservice_init(JNIEnv *env, jobject service, jobject builder, .get_network_manager = _get_network_manager, }, .attr = android_attr_create(), - .creds = android_creds_create(), + .creds = android_creds_create(appdir), .builder = vpnservice_builder_create(builder), .network_manager = network_manager_create(service), .sockets = linked_list_create(), @@ -602,11 +603,11 @@ static void segv_handler(int signal) * Initialize charon and the libraries via JNI */ JNI_METHOD(CharonVpnService, initializeCharon, jboolean, - jobject builder, jstring jlogfile, jboolean byod) + jobject builder, jstring jlogfile, jstring jappdir, jboolean byod) { struct sigaction action; struct utsname utsname; - char *logfile, *plugins; + char *logfile, *appdir, *plugins; /* logging for library during initialization, as we have no bus yet */ dbg = dbg_android; @@ -640,7 +641,9 @@ JNI_METHOD(CharonVpnService, initializeCharon, jboolean, charon->load_loggers(charon); - charonservice_init(env, this, builder, byod); + appdir = androidjni_convert_jstring(env, jappdir); + charonservice_init(env, this, builder, appdir, byod); + free(appdir); if (uname(&utsname) != 0) { diff --git a/src/frontends/android/app/src/main/res/layout/profile_detail_view.xml b/src/frontends/android/app/src/main/res/layout/profile_detail_view.xml index 199a7dfee..f765dbcf1 100644 --- a/src/frontends/android/app/src/main/res/layout/profile_detail_view.xml +++ b/src/frontends/android/app/src/main/res/layout/profile_detail_view.xml @@ -341,6 +341,7 @@ android:text="@string/profile_split_tunnelingv6_title" /> Status: diff --git a/src/frontends/android/app/src/main/res/values-pl/strings.xml b/src/frontends/android/app/src/main/res/values-pl/strings.xml index 424837adf..c6a8b53f3 100644 --- a/src/frontends/android/app/src/main/res/values-pl/strings.xml +++ b/src/frontends/android/app/src/main/res/values-pl/strings.xml @@ -129,6 +129,14 @@ Import certificate Certificate successfully imported Failed to import certificate + CRL cache + Clear CRL cache? + The CRL cache is empty + + The CRL cache contains %1$d file (%2$s). + The CRL cache contains %1$d files (%2$s). + + Clear Status: diff --git a/src/frontends/android/app/src/main/res/values-ru/strings.xml b/src/frontends/android/app/src/main/res/values-ru/strings.xml index a53772014..b47c4993a 100644 --- a/src/frontends/android/app/src/main/res/values-ru/strings.xml +++ b/src/frontends/android/app/src/main/res/values-ru/strings.xml @@ -126,6 +126,14 @@ Import certificate Certificate successfully imported Failed to import certificate + CRL cache + Clear CRL cache? + The CRL cache is empty + + The CRL cache contains %1$d file (%2$s). + The CRL cache contains %1$d files (%2$s). + + Clear Статус: diff --git a/src/frontends/android/app/src/main/res/values-ua/strings.xml b/src/frontends/android/app/src/main/res/values-ua/strings.xml index 4d3600952..69258b048 100644 --- a/src/frontends/android/app/src/main/res/values-ua/strings.xml +++ b/src/frontends/android/app/src/main/res/values-ua/strings.xml @@ -127,6 +127,14 @@ Import certificate Certificate successfully imported Failed to import certificate + CRL cache + Clear CRL cache? + The CRL cache is empty + + The CRL cache contains %1$d file (%2$s). + The CRL cache contains %1$d files (%2$s). + + Clear Статус: diff --git a/src/frontends/android/app/src/main/res/values-zh-rCN/strings.xml b/src/frontends/android/app/src/main/res/values-zh-rCN/strings.xml index 606bd2783..a88e7aa9e 100644 --- a/src/frontends/android/app/src/main/res/values-zh-rCN/strings.xml +++ b/src/frontends/android/app/src/main/res/values-zh-rCN/strings.xml @@ -126,6 +126,14 @@ 导入证书 证书已成功被导入 证书导入失败 + CRL cache + Clear CRL cache? + The CRL cache is empty + + The CRL cache contains %1$d file (%2$s). + The CRL cache contains %1$d files (%2$s). + + Clear 状态: diff --git a/src/frontends/android/app/src/main/res/values-zh-rTW/strings.xml b/src/frontends/android/app/src/main/res/values-zh-rTW/strings.xml index 52f21f8da..8ef0feb2e 100644 --- a/src/frontends/android/app/src/main/res/values-zh-rTW/strings.xml +++ b/src/frontends/android/app/src/main/res/values-zh-rTW/strings.xml @@ -126,6 +126,14 @@ 導入憑證 憑證已經成功匯入 憑證匯入失敗 + CRL cache + Clear CRL cache? + The CRL cache is empty + + The CRL cache contains %1$d file (%2$s). + The CRL cache contains %1$d files (%2$s). + + Clear 狀態: diff --git a/src/frontends/android/app/src/main/res/values/strings.xml b/src/frontends/android/app/src/main/res/values/strings.xml index 4ee6be390..a02de6b1c 100644 --- a/src/frontends/android/app/src/main/res/values/strings.xml +++ b/src/frontends/android/app/src/main/res/values/strings.xml @@ -129,6 +129,14 @@ Import certificate Certificate successfully imported Failed to import certificate + CRL cache + Clear CRL cache? + The CRL cache is empty + + The CRL cache contains %1$d file (%2$s). + The CRL cache contains %1$d files (%2$s). + + Clear Status: