diff options
author | Tobias Brunner <tobias@strongswan.org> | 2014-11-06 16:33:01 +0100 |
---|---|---|
committer | Tobias Brunner <tobias@strongswan.org> | 2014-11-06 16:53:34 +0100 |
commit | 2d19ff462acbb0c26a238d4bae205f3db25148ba (patch) | |
tree | ab242f5dc2c71e607fd10d96f0c420706b1718b8 | |
parent | 0de4ba58ce39d7df4fa402c06eae81f1b2c9b2ba (diff) | |
parent | 0e44999867d746123329a132ceea7e25942ce3d8 (diff) | |
download | strongswan-2d19ff462acbb0c26a238d4bae205f3db25148ba.tar.bz2 strongswan-2d19ff462acbb0c26a238d4bae205f3db25148ba.tar.xz |
Merge branch 'android-eap-tls'
This adds support for EAP-TLS authentication on Android.
EAP-only authentication is currently not allowed because the AAA identity
is not configurable, so to prevent anyone with a valid certificate from
impersonating the AAA server and thus the gateway, we authenticate the
gateway (like we do with other authentication methods).
Also, it's currently not possible to select a specific CA certificate to
authenticate the AAA server certificate, so it either must be issued by the
same CA as that of the gateway or automatic CA certificate selection must
be used.
14 files changed, 101 insertions, 87 deletions
diff --git a/src/frontends/android/jni/Android.mk b/src/frontends/android/jni/Android.mk index 2af0d2412..670e83de1 100644 --- a/src/frontends/android/jni/Android.mk +++ b/src/frontends/android/jni/Android.mk @@ -7,7 +7,7 @@ strongswan_USE_BYOD := true strongswan_CHARON_PLUGINS := android-log openssl fips-prf random nonce pubkey \ pkcs1 pkcs8 pem xcbc hmac socket-default kernel-netlink \ - eap-identity eap-mschapv2 eap-md5 eap-gtc + eap-identity eap-mschapv2 eap-md5 eap-gtc eap-tls ifneq ($(strongswan_USE_BYOD),) strongswan_BYOD_PLUGINS := eap-ttls eap-tnc tnc-imc tnc-tnccs tnccs-20 diff --git a/src/frontends/android/jni/libandroidbridge/backend/android_creds.c b/src/frontends/android/jni/libandroidbridge/backend/android_creds.c index e0f6e8e6f..ddc032638 100644 --- a/src/frontends/android/jni/libandroidbridge/backend/android_creds.c +++ b/src/frontends/android/jni/libandroidbridge/backend/android_creds.c @@ -92,7 +92,7 @@ METHOD(credential_set_t, create_cert_enumerator, enumerator_t*, { enumerator_t *enumerator; - if (!trusted || (cert != CERT_ANY && cert != CERT_X509)) + if (cert != CERT_ANY && cert != CERT_X509) { return NULL; } diff --git a/src/frontends/android/jni/libandroidbridge/backend/android_private_key.c b/src/frontends/android/jni/libandroidbridge/backend/android_private_key.c index 1985f0e98..769ea3f31 100644 --- a/src/frontends/android/jni/libandroidbridge/backend/android_private_key.c +++ b/src/frontends/android/jni/libandroidbridge/backend/android_private_key.c @@ -68,6 +68,9 @@ METHOD(private_key_t, sign, bool, case KEY_RSA: switch (scheme) { + case SIGN_RSA_EMSA_PKCS1_NULL: + method = "NONEwithRSA"; + break; case SIGN_RSA_EMSA_PKCS1_MD5: method = "MD5withRSA"; break; @@ -93,12 +96,18 @@ METHOD(private_key_t, sign, bool, case KEY_ECDSA: switch (scheme) { + case SIGN_ECDSA_WITH_SHA1_DER: + method = "SHA1withECDSA"; + break; + case SIGN_ECDSA_WITH_SHA256_DER: case SIGN_ECDSA_256: method = "SHA256withECDSA"; break; + case SIGN_ECDSA_WITH_SHA384_DER: case SIGN_ECDSA_384: method = "SHA384withECDSA"; break; + case SIGN_ECDSA_WITH_SHA512_DER: case SIGN_ECDSA_521: method = "SHA512withECDSA"; break; @@ -189,30 +198,36 @@ METHOD(private_key_t, sign, bool, default: break; } - - /* we get an ASN.1 encoded sequence of integers r and s */ - parse = encoded = chunk_from_byte_array(env, jsigarray); - if (asn1_unwrap(&parse, &parse) != ASN1_SEQUENCE || - asn1_unwrap(&parse, &r) != ASN1_INTEGER || - asn1_unwrap(&parse, &s) != ASN1_INTEGER) + if (len) { + /* we get an ASN.1 encoded sequence of integers r and s */ + parse = encoded = chunk_from_byte_array(env, jsigarray); + if (asn1_unwrap(&parse, &parse) != ASN1_SEQUENCE || + asn1_unwrap(&parse, &r) != ASN1_INTEGER || + asn1_unwrap(&parse, &s) != ASN1_INTEGER) + { + chunk_free(&encoded); + goto failed; + } + r = chunk_skip_zero(r); + s = chunk_skip_zero(s); + if (r.len > len || s.len > len) + { + chunk_free(&encoded); + goto failed; + } + + /* concatenate r and s (forced to the defined length) */ + *signature = chunk_alloc(2*len); + memset(signature->ptr, 0, signature->len); + memcpy(signature->ptr + (len - r.len), r.ptr, r.len); + memcpy(signature->ptr + len + (len - s.len), s.ptr, s.len); chunk_free(&encoded); - goto failed; } - r = chunk_skip_zero(r); - s = chunk_skip_zero(s); - if (r.len > len || s.len > len) + else { - chunk_free(&encoded); - goto failed; + *signature = chunk_from_byte_array(env, jsigarray); } - - /* concatenate r and s (forced to the defined length) */ - *signature = chunk_alloc(2*len); - memset(signature->ptr, 0, signature->len); - memcpy(signature->ptr + (len - r.len), r.ptr, r.len); - memcpy(signature->ptr + len + (len - s.len), s.ptr, s.len); - chunk_free(&encoded); } else { diff --git a/src/frontends/android/jni/libandroidbridge/backend/android_service.c b/src/frontends/android/jni/libandroidbridge/backend/android_service.c index 881ff00f1..960edbee5 100644 --- a/src/frontends/android/jni/libandroidbridge/backend/android_service.c +++ b/src/frontends/android/jni/libandroidbridge/backend/android_service.c @@ -617,8 +617,8 @@ METHOD(listener_t, ike_reestablish_post, bool, return TRUE; } -static void add_auth_cfg_eap(private_android_service_t *this, - peer_cfg_t *peer_cfg, bool byod) +static void add_auth_cfg_pw(private_android_service_t *this, + peer_cfg_t *peer_cfg, bool byod) { identification_t *user; auth_cfg_t *auth; @@ -653,7 +653,17 @@ static bool add_auth_cfg_cert(private_android_service_t *this, } auth = auth_cfg_create(); - auth->add(auth, AUTH_RULE_AUTH_CLASS, AUTH_CLASS_PUBKEY); + if (strpfx("ikev2-eap-tls", this->type)) + { + auth->add(auth, AUTH_RULE_AUTH_CLASS, AUTH_CLASS_EAP); + auth->add(auth, AUTH_RULE_EAP_TYPE, EAP_TLS); + id = identification_create_from_string("%any"); + auth->add(auth, AUTH_RULE_AAA_IDENTITY, id); + } + else + { + auth->add(auth, AUTH_RULE_AUTH_CLASS, AUTH_CLASS_PUBKEY); + } auth->add(auth, AUTH_RULE_SUBJECT_CERT, cert); id = cert->get_subject(cert); @@ -698,7 +708,8 @@ static job_requeue_t initiate(private_android_service_t *this) /* local auth config */ if (streq("ikev2-cert", this->type) || - streq("ikev2-cert-eap", this->type)) + streq("ikev2-cert-eap", this->type) || + streq("ikev2-eap-tls", this->type)) { if (!add_auth_cfg_cert(this, peer_cfg)) { @@ -712,15 +723,15 @@ static job_requeue_t initiate(private_android_service_t *this) streq("ikev2-cert-eap", this->type) || streq("ikev2-byod-eap", this->type)) { - add_auth_cfg_eap(this, peer_cfg, strpfx(this->type, "ikev2-byod")); + add_auth_cfg_pw(this, peer_cfg, strpfx(this->type, "ikev2-byod")); } /* remote auth config */ auth = auth_cfg_create(); - auth->add(auth, AUTH_RULE_AUTH_CLASS, AUTH_CLASS_PUBKEY); gateway = identification_create_from_string(this->gateway); auth->add(auth, AUTH_RULE_IDENTITY, gateway); auth->add(auth, AUTH_RULE_IDENTITY_LOOSE, TRUE); + auth->add(auth, AUTH_RULE_AUTH_CLASS, AUTH_CLASS_PUBKEY); peer_cfg->add_auth_cfg(peer_cfg, auth, FALSE); child_cfg = child_cfg_create("android", &lifetime, NULL, TRUE, MODE_TUNNEL, diff --git a/src/frontends/android/res/values-de/arrays.xml b/src/frontends/android/res/values-de/arrays.xml index 30578c0a8..d05140165 100644 --- a/src/frontends/android/res/values-de/arrays.xml +++ b/src/frontends/android/res/values-de/arrays.xml @@ -19,6 +19,7 @@ <item>IKEv2 EAP (Benutzername/Passwort)</item> <item>IKEv2 Zertifikat</item> <item>IKEv2 Zertifikat + EAP (Benutzername/Passwort)</item> + <item>IKEv2 EAP-TLS (Zertifikat)</item> <item>IKEv2 EAP-TNC (Benutzername/Passwort)</item> </string-array> </resources>
\ No newline at end of file diff --git a/src/frontends/android/res/values-pl/arrays.xml b/src/frontends/android/res/values-pl/arrays.xml index 1a04cbf2e..30e43f1fb 100644 --- a/src/frontends/android/res/values-pl/arrays.xml +++ b/src/frontends/android/res/values-pl/arrays.xml @@ -19,6 +19,7 @@ <item>IKEv2 EAP (użytkownik/hasło)</item> <item>IKEv2 certyfikat</item> <item>IKEv2 certyfikat + EAP (użytkownik/hasło)</item> + <item>IKEv2 EAP-TLS (certyfikat)</item> <item>IKEv2 EAP-TNC (użytkownik/hasło)</item> </string-array> </resources>
\ No newline at end of file diff --git a/src/frontends/android/res/values-ru/arrays.xml b/src/frontends/android/res/values-ru/arrays.xml index 713f8e404..5fbd43168 100644 --- a/src/frontends/android/res/values-ru/arrays.xml +++ b/src/frontends/android/res/values-ru/arrays.xml @@ -18,6 +18,7 @@ <item>IKEv2 EAP (Логин/Пароль)</item> <item>IKEv2 Сертификат</item> <item>IKEv2 Сертификат + EAP (Логин/Пароль)</item> + <item>IKEv2 EAP-TLS (Сертификат)</item> <item>IKEv2 EAP-TNC (Логин/Пароль)</item> </string-array> </resources> diff --git a/src/frontends/android/res/values-ua/arrays.xml b/src/frontends/android/res/values-ua/arrays.xml index 4bd92fe0a..1acc0d769 100644 --- a/src/frontends/android/res/values-ua/arrays.xml +++ b/src/frontends/android/res/values-ua/arrays.xml @@ -18,6 +18,7 @@ <item>IKEv2 EAP (Логін/Пароль)</item> <item>IKEv2 Сертифікати</item> <item>IKEv2 Сертифікати + EAP (Логін/Пароль)</item> + <item>IKEv2 EAP-TLS (Сертифікати)</item> <item>IKEv2 EAP-TNC (Логін/Пароль)</item> </string-array> </resources> diff --git a/src/frontends/android/res/values/arrays.xml b/src/frontends/android/res/values/arrays.xml index 29f999d9a..b324b594f 100644 --- a/src/frontends/android/res/values/arrays.xml +++ b/src/frontends/android/res/values/arrays.xml @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> <!-- - Copyright (C) 2012-2013 Tobias Brunner + Copyright (C) 2012-2014 Tobias Brunner Hochschule fuer Technik Rapperswil This program is free software; you can redistribute it and/or modify it @@ -19,6 +19,7 @@ <item>IKEv2 EAP (Username/Password)</item> <item>IKEv2 Certificate</item> <item>IKEv2 Certificate + EAP (Username/Password)</item> + <item>IKEv2 EAP-TLS (Certificate)</item> <item>IKEv2 EAP-TNC (Username/Password)</item> </string-array> </resources>
\ No newline at end of file diff --git a/src/frontends/android/src/org/strongswan/android/data/VpnType.java b/src/frontends/android/src/org/strongswan/android/data/VpnType.java index f62c96d76..bffa8384c 100644 --- a/src/frontends/android/src/org/strongswan/android/data/VpnType.java +++ b/src/frontends/android/src/org/strongswan/android/data/VpnType.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012-2013 Tobias Brunner + * Copyright (C) 2012-2014 Tobias Brunner * Hochschule fuer Technik Rapperswil * * This program is free software; you can redistribute it and/or modify it @@ -15,45 +15,44 @@ package org.strongswan.android.data; +import java.util.EnumSet; + public enum VpnType { /* the order here must match the items in R.array.vpn_types */ - IKEV2_EAP("ikev2-eap", true, false), - IKEV2_CERT("ikev2-cert", false, true), - IKEV2_CERT_EAP("ikev2-cert-eap", true, true), - IKEV2_BYOD_EAP("ikev2-byod-eap", true, false, true); - - private String mIdentifier; - private boolean mCertificate; - private boolean mUsernamePassword; - private boolean mBYOD; + IKEV2_EAP("ikev2-eap", EnumSet.of(VpnTypeFeature.USER_PASS)), + IKEV2_CERT("ikev2-cert", EnumSet.of(VpnTypeFeature.CERTIFICATE)), + IKEV2_CERT_EAP("ikev2-cert-eap", EnumSet.of(VpnTypeFeature.USER_PASS, VpnTypeFeature.CERTIFICATE)), + IKEV2_EAP_TLS("ikev2-eap-tls", EnumSet.of(VpnTypeFeature.CERTIFICATE)), + IKEV2_BYOD_EAP("ikev2-byod-eap", EnumSet.of(VpnTypeFeature.USER_PASS, VpnTypeFeature.CERTIFICATE, VpnTypeFeature.BYOD)); /** - * Enum which provides additional information about the supported VPN types. - * - * @param id identifier used to store and transmit this specific type - * @param userpass true if username and password are required - * @param certificate true if a client certificate is required + * Features of a VPN type. */ - VpnType(String id, boolean userpass, boolean certificate) + public enum VpnTypeFeature { - this(id, userpass, certificate, false); + /** client certificate is required */ + CERTIFICATE, + /** username and password are required */ + USER_PASS, + /** enable BYOD features */ + BYOD; } + private String mIdentifier; + private EnumSet<VpnTypeFeature> mFeatures; + /** * Enum which provides additional information about the supported VPN types. * * @param id identifier used to store and transmit this specific type - * @param userpass true if username and password are required + * @param features of the given VPN type * @param certificate true if a client certificate is required - * @param byod true to enable BYOD features */ - VpnType(String id, boolean userpass, boolean certificate, boolean byod) + VpnType(String id, EnumSet<VpnTypeFeature> features) { mIdentifier = id; - mUsernamePassword = userpass; - mCertificate = certificate; - mBYOD = byod; + mFeatures = features; } /** @@ -66,33 +65,13 @@ public enum VpnType } /** - * Whether username and password are required for this type of VPN. - * - * @return true if username and password are required - */ - public boolean getRequiresUsernamePassword() - { - return mUsernamePassword; - } - - /** - * Whether a certificate is required for this type of VPN. - * - * @return true if a certificate is required - */ - public boolean getRequiresCertificate() - { - return mCertificate; - } - - /** - * Whether BYOD features should be enabled. + * Checks whether a feature is supported/required by this type of VPN. * - * @return true if BYOD features are to be enabled + * @return true if the feature is supported/required */ - public boolean getEnableBYOD() + public boolean has(VpnTypeFeature feature) { - return mBYOD; + return mFeatures.contains(feature); } /** diff --git a/src/frontends/android/src/org/strongswan/android/logic/CharonVpnService.java b/src/frontends/android/src/org/strongswan/android/logic/CharonVpnService.java index c8f3b344f..7cdaee735 100644 --- a/src/frontends/android/src/org/strongswan/android/logic/CharonVpnService.java +++ b/src/frontends/android/src/org/strongswan/android/logic/CharonVpnService.java @@ -26,6 +26,7 @@ import java.util.List; 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; @@ -211,7 +212,7 @@ public class CharonVpnService extends VpnService implements Runnable mIsDisconnecting = false; BuilderAdapter builder = new BuilderAdapter(mCurrentProfile.getName()); - if (initializeCharon(builder, mLogFile, mCurrentProfile.getVpnType().getEnableBYOD())) + if (initializeCharon(builder, mLogFile, mCurrentProfile.getVpnType().has(VpnTypeFeature.BYOD))) { Log.i(TAG, "charon started"); initiate(mCurrentProfile.getVpnType().getIdentifier(), diff --git a/src/frontends/android/src/org/strongswan/android/ui/MainActivity.java b/src/frontends/android/src/org/strongswan/android/ui/MainActivity.java index a2ad80e82..e1b3e0783 100644 --- a/src/frontends/android/src/org/strongswan/android/ui/MainActivity.java +++ b/src/frontends/android/src/org/strongswan/android/ui/MainActivity.java @@ -20,6 +20,7 @@ package org.strongswan.android.ui; 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.CharonVpnService; import org.strongswan.android.logic.TrustedCertificateManager; import org.strongswan.android.logic.VpnStateService; @@ -219,7 +220,7 @@ public class MainActivity extends Activity implements OnVpnProfileSelectedListen profileInfo.putLong(VpnProfileDataSource.KEY_ID, profile.getId()); profileInfo.putString(VpnProfileDataSource.KEY_USERNAME, profile.getUsername()); profileInfo.putString(VpnProfileDataSource.KEY_PASSWORD, profile.getPassword()); - profileInfo.putBoolean(PROFILE_REQUIRES_PASSWORD, profile.getVpnType().getRequiresUsernamePassword()); + profileInfo.putBoolean(PROFILE_REQUIRES_PASSWORD, profile.getVpnType().has(VpnTypeFeature.USER_PASS)); profileInfo.putString(PROFILE_NAME, profile.getName()); removeFragmentByTag(DIALOG_TAG); diff --git a/src/frontends/android/src/org/strongswan/android/ui/VpnProfileDetailActivity.java b/src/frontends/android/src/org/strongswan/android/ui/VpnProfileDetailActivity.java index 39d37005d..41cd6e98c 100644 --- a/src/frontends/android/src/org/strongswan/android/ui/VpnProfileDetailActivity.java +++ b/src/frontends/android/src/org/strongswan/android/ui/VpnProfileDetailActivity.java @@ -23,6 +23,7 @@ import org.strongswan.android.R; import org.strongswan.android.data.VpnProfile; import org.strongswan.android.data.VpnProfileDataSource; import org.strongswan.android.data.VpnType; +import org.strongswan.android.data.VpnType.VpnTypeFeature; import org.strongswan.android.logic.TrustedCertificateManager; import org.strongswan.android.security.TrustedCertificateEntry; @@ -240,11 +241,11 @@ public class VpnProfileDetailActivity extends Activity */ private void updateCredentialView() { - mUsernamePassword.setVisibility(mVpnType.getRequiresUsernamePassword() ? View.VISIBLE : View.GONE); - mUserCertificate.setVisibility(mVpnType.getRequiresCertificate() ? View.VISIBLE : View.GONE); - mTncNotice.setVisibility(mVpnType.getEnableBYOD() ? View.VISIBLE : View.GONE); + mUsernamePassword.setVisibility(mVpnType.has(VpnTypeFeature.USER_PASS) ? View.VISIBLE : View.GONE); + mUserCertificate.setVisibility(mVpnType.has(VpnTypeFeature.CERTIFICATE) ? View.VISIBLE : View.GONE); + mTncNotice.setVisibility(mVpnType.has(VpnTypeFeature.BYOD) ? View.VISIBLE : View.GONE); - if (mVpnType.getRequiresCertificate()) + if (mVpnType.has(VpnTypeFeature.CERTIFICATE)) { if (mUserCertLoading != null) { @@ -349,7 +350,7 @@ public class VpnProfileDetailActivity extends Activity mGateway.setError(getString(R.string.alert_text_no_input_gateway)); valid = false; } - if (mVpnType.getRequiresUsernamePassword()) + if (mVpnType.has(VpnTypeFeature.USER_PASS)) { if (mUsername.getText().toString().trim().isEmpty()) { @@ -357,7 +358,7 @@ public class VpnProfileDetailActivity extends Activity valid = false; } } - if (mVpnType.getRequiresCertificate() && mUserCertEntry == null) + if (mVpnType.has(VpnTypeFeature.CERTIFICATE) && mUserCertEntry == null) { /* let's show an error icon */ ((TextView)mSelectUserCert.findViewById(android.R.id.text1)).setError(""); valid = false; @@ -381,14 +382,14 @@ public class VpnProfileDetailActivity extends Activity mProfile.setName(name.isEmpty() ? gateway : name); mProfile.setGateway(gateway); mProfile.setVpnType(mVpnType); - if (mVpnType.getRequiresUsernamePassword()) + if (mVpnType.has(VpnTypeFeature.USER_PASS)) { mProfile.setUsername(mUsername.getText().toString().trim()); String password = mPassword.getText().toString().trim(); password = password.isEmpty() ? null : password; mProfile.setPassword(password); } - if (mVpnType.getRequiresCertificate()) + if (mVpnType.has(VpnTypeFeature.CERTIFICATE)) { mProfile.setUserCertificateAlias(mUserCertEntry.getAlias()); } diff --git a/src/frontends/android/src/org/strongswan/android/ui/adapter/VpnProfileAdapter.java b/src/frontends/android/src/org/strongswan/android/ui/adapter/VpnProfileAdapter.java index 85dc8370a..f3bb271bc 100644 --- a/src/frontends/android/src/org/strongswan/android/ui/adapter/VpnProfileAdapter.java +++ b/src/frontends/android/src/org/strongswan/android/ui/adapter/VpnProfileAdapter.java @@ -23,6 +23,7 @@ import java.util.List; import org.strongswan.android.R; import org.strongswan.android.data.VpnProfile; +import org.strongswan.android.data.VpnType.VpnTypeFeature; import android.content.Context; import android.view.LayoutInflater; @@ -64,7 +65,7 @@ public class VpnProfileAdapter extends ArrayAdapter<VpnProfile> tv = (TextView)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); - if (profile.getVpnType().getRequiresUsernamePassword()) + if (profile.getVpnType().has(VpnTypeFeature.USER_PASS)) { /* if the view is reused we make sure it is visible */ tv.setVisibility(View.VISIBLE); tv.setText(getContext().getString(R.string.profile_username_label) + " " + profile.getUsername()); @@ -74,7 +75,7 @@ public class VpnProfileAdapter extends ArrayAdapter<VpnProfile> tv.setVisibility(View.GONE); } tv = (TextView)vpnProfileView.findViewById(R.id.profile_item_certificate); - if (profile.getVpnType().getRequiresCertificate()) + if (profile.getVpnType().has(VpnTypeFeature.CERTIFICATE)) { tv.setText(getContext().getString(R.string.profile_user_certificate_label) + " " + profile.getUserCertificateAlias()); tv.setVisibility(View.VISIBLE); |