diff options
author | Tobias Brunner <tobias@strongswan.org> | 2016-12-08 17:33:11 +0100 |
---|---|---|
committer | Tobias Brunner <tobias@strongswan.org> | 2016-12-08 17:33:11 +0100 |
commit | 7e1c8407532c429121b42569710a55bbe34392fa (patch) | |
tree | bf3f8af7fb67d8e0d172063b1fa89e931487e25b | |
parent | 8c50bb6c365510b76346b132e5796ae585bb7285 (diff) | |
parent | 3e85b5a492aa001d6064ee7d88feb2e36ad14359 (diff) | |
download | strongswan-7e1c8407532c429121b42569710a55bbe34392fa.tar.bz2 strongswan-7e1c8407532c429121b42569710a55bbe34392fa.tar.xz |
Merge branch 'android-updates'
Adds a permanent notification while connected (or connecting), which
allows running as a foreground service, which in turn should prevent
Android from terminating the service when low on memory.
Also adds support for ChaCha20/Poly1305 AEAD and Curve25519 DH.
16 files changed, 175 insertions, 44 deletions
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 a6b9fc52d..0048a2daf 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 @@ -17,29 +17,9 @@ package org.strongswan.android.logic; -import java.io.File; -import java.net.Inet4Address; -import java.net.Inet6Address; -import java.net.InetAddress; -import java.net.UnknownHostException; -import java.security.PrivateKey; -import java.security.cert.CertificateEncodingException; -import java.security.cert.X509Certificate; -import java.util.ArrayList; -import java.util.List; -import java.util.Locale; - -import org.strongswan.android.data.VpnProfile; -import org.strongswan.android.data.VpnProfileDataSource; -import org.strongswan.android.data.VpnType.VpnTypeFeature; -import org.strongswan.android.logic.VpnStateService.ErrorState; -import org.strongswan.android.logic.VpnStateService.State; -import org.strongswan.android.logic.imc.ImcState; -import org.strongswan.android.logic.imc.RemediationInstruction; -import org.strongswan.android.ui.MainActivity; -import org.strongswan.android.utils.SettingsWriter; - import android.annotation.TargetApi; +import android.app.Notification; +import android.app.NotificationManager; import android.app.PendingIntent; import android.app.Service; import android.content.ComponentName; @@ -53,13 +33,39 @@ import android.os.IBinder; import android.os.ParcelFileDescriptor; import android.security.KeyChain; import android.security.KeyChainException; +import android.support.v4.app.NotificationCompat; +import android.support.v4.content.ContextCompat; import android.system.OsConstants; import android.util.Log; -public class CharonVpnService extends VpnService implements Runnable +import org.strongswan.android.R; +import org.strongswan.android.data.VpnProfile; +import org.strongswan.android.data.VpnProfileDataSource; +import org.strongswan.android.data.VpnType.VpnTypeFeature; +import org.strongswan.android.logic.VpnStateService.ErrorState; +import org.strongswan.android.logic.VpnStateService.State; +import org.strongswan.android.logic.imc.ImcState; +import org.strongswan.android.logic.imc.RemediationInstruction; +import org.strongswan.android.ui.MainActivity; +import org.strongswan.android.utils.SettingsWriter; + +import java.io.File; +import java.net.Inet4Address; +import java.net.Inet6Address; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.security.PrivateKey; +import java.security.cert.CertificateEncodingException; +import java.security.cert.X509Certificate; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; + +public class CharonVpnService extends VpnService implements Runnable, VpnStateService.VpnStateListener { private static final String TAG = CharonVpnService.class.getSimpleName(); public static final String LOG_FILE = "charon.log"; + public static final int VPN_STATE_NOTIFICATION_ID = 1; private String mLogFile; private VpnProfileDataSource mDataSource; @@ -71,6 +77,7 @@ public class CharonVpnService extends VpnService implements Runnable private volatile boolean mProfileUpdated; private volatile boolean mTerminate; private volatile boolean mIsDisconnecting; + private volatile boolean mShowNotification; private VpnStateService mService; private final Object mServiceLock = new Object(); private final ServiceConnection mServiceConnection = new ServiceConnection() { @@ -91,6 +98,7 @@ public class CharonVpnService extends VpnService implements Runnable mService = ((VpnStateService.LocalBinder)service).getService(); } /* we are now ready to start the handler thread */ + mService.registerListener(CharonVpnService.this); mConnectionHandler.start(); } }; @@ -163,6 +171,7 @@ public class CharonVpnService extends VpnService implements Runnable } if (mService != null) { + mService.unregisterListener(this); unbindService(mServiceConnection); } mDataSource.close(); @@ -220,6 +229,7 @@ public class CharonVpnService extends VpnService implements Runnable startConnection(mCurrentProfile); mIsDisconnecting = false; + addNotification(); BuilderAdapter builder = new BuilderAdapter(mCurrentProfile.getName(), mCurrentProfile.getSplitTunneling()); if (initializeCharon(builder, mLogFile, mCurrentProfile.getVpnType().has(VpnTypeFeature.BYOD))) { @@ -268,11 +278,99 @@ public class CharonVpnService extends VpnService implements Runnable deinitializeCharon(); Log.i(TAG, "charon stopped"); mCurrentProfile = null; + removeNotification(); } } } /** + * Add a permanent notification while we are connected to avoid the service getting killed by + * the system when low on memory. + */ + private void addNotification() + { + mShowNotification = true; + startForeground(VPN_STATE_NOTIFICATION_ID, buildNotification(false)); + } + + /** + * Remove the permanent notification. + */ + private void removeNotification() + { + mShowNotification = false; + stopForeground(true); + } + + /** + * Build a notification matching the current state + */ + private Notification buildNotification(boolean publicVersion) + { + VpnProfile profile = mService.getProfile(); + State state = mService.getState(); + ErrorState error = mService.getErrorState(); + String name = ""; + + if (profile != null) + { + name = profile.getName(); + } + android.support.v4.app.NotificationCompat.Builder builder = new NotificationCompat.Builder(this) + .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) + { + s = R.string.state_error; + builder.setSmallIcon(R.drawable.ic_notification_warning); + builder.setColor(ContextCompat.getColor(this, R.color.error_text)); + } + else + { + switch (state) + { + case CONNECTING: + s = R.string.state_connecting; + builder.setSmallIcon(R.drawable.ic_notification_warning); + builder.setColor(ContextCompat.getColor(this, R.color.warning_text)); + break; + case CONNECTED: + s = R.string.state_connected; + builder.setColor(ContextCompat.getColor(this, R.color.success_text)); + builder.setUsesChronometer(true); + break; + case DISCONNECTING: + s = R.string.state_disconnecting; + break; + } + } + builder.setContentTitle(getString(s)); + if (!publicVersion) + { + builder.setContentText(name); + builder.setPublicVersion(buildNotification(true)); + } + + Intent intent = new Intent(getApplicationContext(), MainActivity.class); + PendingIntent pending = PendingIntent.getActivity(getApplicationContext(), 0, intent, + PendingIntent.FLAG_UPDATE_CURRENT); + builder.setContentIntent(pending); + return builder.build(); + } + + @Override + public void stateChanged() { + if (mShowNotification) + { + NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); + manager.notify(VPN_STATE_NOTIFICATION_ID, buildNotification(false)); + } + } + + /** * Notify the state service about a new connection attempt. * Called by the handler thread. * @@ -444,7 +542,7 @@ public class CharonVpnService extends VpnService implements Runnable private byte[][] getTrustedCertificates() { ArrayList<byte[]> certs = new ArrayList<byte[]>(); - TrustedCertificateManager certman = TrustedCertificateManager.getInstance(); + TrustedCertificateManager certman = TrustedCertificateManager.getInstance().load(); try { String alias = this.mCurrentCertificateAlias; @@ -521,7 +619,7 @@ public class CharonVpnService extends VpnService implements Runnable * * @param builder BuilderAdapter for this connection * @param logfile absolute path to the logfile - * @param boyd enable BYOD features + * @param byod enable BYOD features * @return TRUE if initialization was successful */ public native boolean initializeCharon(BuilderAdapter builder, String logfile, boolean byod); diff --git a/src/frontends/android/app/src/main/java/org/strongswan/android/logic/VpnStateService.java b/src/frontends/android/app/src/main/java/org/strongswan/android/logic/VpnStateService.java index 7b40e942f..e35277d8c 100644 --- a/src/frontends/android/app/src/main/java/org/strongswan/android/logic/VpnStateService.java +++ b/src/frontends/android/app/src/main/java/org/strongswan/android/logic/VpnStateService.java @@ -315,7 +315,7 @@ public class VpnStateService extends Service * * May be called from threads other than the main thread. * - * @param error error state + * @param state IMC state */ public void setImcState(final ImcState state) { 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 dbbfaefb1..30fb101be 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 @@ -496,14 +496,12 @@ public class VpnProfileDetailActivity extends AppCompatActivity showCertificateAlert(); valid = false; } - Integer mtu = getInteger(mMTU); - if (mtu != null && (mtu < MTU_MIN || mtu > MTU_MAX)) + if (!validateInteger(mMTU, MTU_MIN, MTU_MAX)) { mMTUWrap.setError(String.format(getString(R.string.alert_text_out_of_range), MTU_MIN, MTU_MAX)); valid = false; } - Integer port = getInteger(mPort); - if (port != null && (port < 1 || port > 65535)) + if (!validateInteger(mPort, 1, 65535)) { mPortWrap.setError(String.format(getString(R.string.alert_text_out_of_range), 1, 65535)); valid = false; @@ -633,6 +631,31 @@ public class VpnProfileDetailActivity extends AppCompatActivity } } + /** + * Check that the value in the given text box is a valid integer in the given range + * + * @param view text box (numeric entry assumed) + * @param min minimum value (inclusive) + * @param max maximum value (inclusive) + */ + private boolean validateInteger(EditText view, Integer min, Integer max) + { + String value = view.getText().toString().trim(); + try + { + if (value.isEmpty()) + { + return true; + } + Integer val = Integer.valueOf(value); + return min <= val && val <= max; + } + catch (NumberFormatException e) + { + return false; + } + } + private class SelectUserCertOnClickListener implements OnClickListener, KeyChainAliasCallback { @Override diff --git a/src/frontends/android/app/src/main/java/org/strongswan/android/ui/VpnStateFragment.java b/src/frontends/android/app/src/main/java/org/strongswan/android/ui/VpnStateFragment.java index 0b093d78f..1ea01515c 100644 --- a/src/frontends/android/app/src/main/java/org/strongswan/android/ui/VpnStateFragment.java +++ b/src/frontends/android/app/src/main/java/org/strongswan/android/ui/VpnStateFragment.java @@ -187,12 +187,11 @@ public class VpnStateFragment extends Fragment implements VpnStateListener State state = mService.getState(); ErrorState error = mService.getErrorState(); ImcState imcState = mService.getImcState(); - String name = "", gateway = ""; + String name = ""; if (profile != null) { name = profile.getName(); - gateway = profile.getGateway(); } if (reportError(connectionID, name, error, imcState)) diff --git a/src/frontends/android/app/src/main/jni/Android.mk b/src/frontends/android/app/src/main/jni/Android.mk index 849bdec31..51a61b883 100644 --- a/src/frontends/android/app/src/main/jni/Android.mk +++ b/src/frontends/android/app/src/main/jni/Android.mk @@ -6,7 +6,7 @@ include $(CLEAR_VARS) strongswan_USE_BYOD := true strongswan_CHARON_PLUGINS := android-log openssl fips-prf random nonce pubkey \ - pkcs1 pkcs8 pem xcbc hmac socket-default \ + chapoly curve25519 pkcs1 pkcs8 pem xcbc hmac socket-default \ eap-identity eap-mschapv2 eap-md5 eap-gtc eap-tls ifneq ($(strongswan_USE_BYOD),) diff --git a/src/frontends/android/app/src/main/jni/libandroidbridge/backend/android_attr.c b/src/frontends/android/app/src/main/jni/libandroidbridge/backend/android_attr.c index 8015b7c38..47933d1f2 100644 --- a/src/frontends/android/app/src/main/jni/libandroidbridge/backend/android_attr.c +++ b/src/frontends/android/app/src/main/jni/libandroidbridge/backend/android_attr.c @@ -58,7 +58,7 @@ METHOD(attribute_handler_t, handle, bool, DESTROY_IF(dns); return FALSE; } - + DBG1(DBG_IKE, "installing DNS server %H", dns); builder = charonservice->get_vpnservice_builder(charonservice); builder->add_dns(builder, dns); dns->destroy(dns); diff --git a/src/frontends/android/app/src/main/jni/libandroidbridge/backend/android_service.c b/src/frontends/android/app/src/main/jni/libandroidbridge/backend/android_service.c index 2532402bb..33585df32 100644 --- a/src/frontends/android/app/src/main/jni/libandroidbridge/backend/android_service.c +++ b/src/frontends/android/app/src/main/jni/libandroidbridge/backend/android_service.c @@ -550,6 +550,8 @@ METHOD(listener_t, alert, bool, private_android_service_t *this, ike_sa_t *ike_sa, alert_t alert, va_list args) { + bool stay_registered = TRUE; + if (this->ike_sa == ike_sa) { switch (alert) @@ -557,11 +559,13 @@ METHOD(listener_t, alert, bool, case ALERT_PEER_ADDR_FAILED: charonservice->update_status(charonservice, CHARONSERVICE_LOOKUP_ERROR); - break; + return FALSE; + case ALERT_PEER_AUTH_FAILED: charonservice->update_status(charonservice, CHARONSERVICE_PEER_AUTH_ERROR); - break; + return FALSE; + case ALERT_KEEP_ON_CHILD_SA_FAILURE: { uint32_t *id = malloc_thing(uint32_t); @@ -593,6 +597,7 @@ METHOD(listener_t, alert, bool, (job_t*)callback_job_create_with_prio( (callback_job_cb_t)terminate, id, free, (callback_job_cancel_t)return_false, JOB_PRIO_HIGH)); + stay_registered = FALSE; } else { @@ -609,6 +614,7 @@ METHOD(listener_t, alert, bool, { charonservice->update_status(charonservice, CHARONSERVICE_UNREACHABLE_ERROR); + stay_registered = FALSE; } } this->lock->unlock(this->lock); @@ -617,7 +623,7 @@ METHOD(listener_t, alert, bool, break; } } - return TRUE; + return stay_registered; } static void add_auth_cfg_pw(private_android_service_t *this, @@ -789,17 +795,18 @@ static job_requeue_t initiate(private_android_service_t *this) /* create ESP proposals with and without DH groups, let responder decide * if PFS is used */ child_cfg->add_proposal(child_cfg, proposal_create_from_string(PROTO_ESP, - "aes128gcm16-aes256gcm16-ecp256")); + "aes128gcm16-aes256gcm16-chacha20poly1305-" + "curve25519-ecp256-modp3072")); child_cfg->add_proposal(child_cfg, proposal_create_from_string(PROTO_ESP, - "aes128-sha256-ecp256-modp3072")); + "aes128-sha256-curve25519-ecp256-modp3072")); child_cfg->add_proposal(child_cfg, proposal_create_from_string(PROTO_ESP, "aes256-sha384-ecp521-modp8192")); child_cfg->add_proposal(child_cfg, proposal_create_from_string(PROTO_ESP, "aes128-aes192-aes256-sha1-sha256-sha384-sha512-" - "ecp256-ecp384-ecp521-" + "curve25519-ecp256-ecp384-ecp521-" "modp2048-modp3072-modp4096-modp1024")); child_cfg->add_proposal(child_cfg, proposal_create_from_string(PROTO_ESP, - "aes128gcm16-aes256gcm16")); + "aes128gcm16-aes256gcm16-chacha20poly1305")); child_cfg->add_proposal(child_cfg, proposal_create_from_string(PROTO_ESP, "aes128-sha256")); child_cfg->add_proposal(child_cfg, proposal_create_from_string(PROTO_ESP, diff --git a/src/frontends/android/app/src/main/res/drawable-hdpi/ic_notification.png b/src/frontends/android/app/src/main/res/drawable-hdpi/ic_notification.png Binary files differnew file mode 100644 index 000000000..d723ee611 --- /dev/null +++ b/src/frontends/android/app/src/main/res/drawable-hdpi/ic_notification.png diff --git a/src/frontends/android/app/src/main/res/drawable-hdpi/ic_notification_warning.png b/src/frontends/android/app/src/main/res/drawable-hdpi/ic_notification_warning.png Binary files differnew file mode 100644 index 000000000..05198c810 --- /dev/null +++ b/src/frontends/android/app/src/main/res/drawable-hdpi/ic_notification_warning.png diff --git a/src/frontends/android/app/src/main/res/drawable-mdpi/ic_notification.png b/src/frontends/android/app/src/main/res/drawable-mdpi/ic_notification.png Binary files differnew file mode 100644 index 000000000..fa5d642c5 --- /dev/null +++ b/src/frontends/android/app/src/main/res/drawable-mdpi/ic_notification.png diff --git a/src/frontends/android/app/src/main/res/drawable-mdpi/ic_notification_warning.png b/src/frontends/android/app/src/main/res/drawable-mdpi/ic_notification_warning.png Binary files differnew file mode 100644 index 000000000..f6cd212be --- /dev/null +++ b/src/frontends/android/app/src/main/res/drawable-mdpi/ic_notification_warning.png diff --git a/src/frontends/android/app/src/main/res/drawable-xhdpi/ic_notification.png b/src/frontends/android/app/src/main/res/drawable-xhdpi/ic_notification.png Binary files differnew file mode 100644 index 000000000..9961c0ae5 --- /dev/null +++ b/src/frontends/android/app/src/main/res/drawable-xhdpi/ic_notification.png diff --git a/src/frontends/android/app/src/main/res/drawable-xhdpi/ic_notification_warning.png b/src/frontends/android/app/src/main/res/drawable-xhdpi/ic_notification_warning.png Binary files differnew file mode 100644 index 000000000..1b5be812f --- /dev/null +++ b/src/frontends/android/app/src/main/res/drawable-xhdpi/ic_notification_warning.png diff --git a/src/frontends/android/build.gradle b/src/frontends/android/build.gradle index 33b908c6d..3d5ba798f 100644 --- a/src/frontends/android/build.gradle +++ b/src/frontends/android/build.gradle @@ -3,7 +3,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:2.1.2' + classpath 'com.android.tools.build:gradle:2.2.3' } } diff --git a/src/frontends/android/gradle/wrapper/gradle-wrapper.properties b/src/frontends/android/gradle/wrapper/gradle-wrapper.properties index 33892269f..a6bd91c6c 100644 --- a/src/frontends/android/gradle/wrapper/gradle-wrapper.properties +++ b/src/frontends/android/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Wed Apr 13 11:22:32 CEST 2016 +#Tue Sep 20 17:56:35 CEST 2016 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-2.10-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip diff --git a/src/libstrongswan/Android.mk b/src/libstrongswan/Android.mk index 0e8f7f3c8..b594dabcc 100644 --- a/src/libstrongswan/Android.mk +++ b/src/libstrongswan/Android.mk @@ -64,12 +64,16 @@ LOCAL_SRC_FILES := $(libstrongswan_la_SOURCES) LOCAL_SRC_FILES += $(call add_plugin, aes) +LOCAL_SRC_FILES += $(call add_plugin, chapoly) + LOCAL_SRC_FILES += $(call add_plugin, curl) ifneq ($(call plugin_enabled, curl),) LOCAL_C_INCLUDES += $(libcurl_PATH) LOCAL_SHARED_LIBRARIES += libcurl endif +LOCAL_SRC_FILES += $(call add_plugin, curve25519) + LOCAL_SRC_FILES += $(call add_plugin, des) LOCAL_SRC_FILES += $(call add_plugin, fips-prf) |