diff options
143 files changed, 7544 insertions, 2141 deletions
diff --git a/Android.mk b/Android.mk index 663b363cf..0b8da5b8d 100644 --- a/Android.mk +++ b/Android.mk @@ -22,7 +22,7 @@ strongswan_CHARON_PLUGINS := android-log openssl fips-prf random nonce pubkey \ ifneq ($(strongswan_BUILD_SCEPCLIENT),) # plugins loaded by scepclient -strongswan_SCEPCLIENT_PLUGINS := openssl curl fips-prf random pkcs1 pem +strongswan_SCEPCLIENT_PLUGINS := openssl curl fips-prf random pkcs1 pkcs7 pem endif strongswan_STARTER_PLUGINS := kernel-netlink @@ -67,6 +67,7 @@ strongswan_CFLAGS := \ -DHAVE_STRUCT_SADB_X_POLICY_SADB_X_POLICY_PRIORITY \ -DHAVE_IPSEC_MODE_BEET \ -DHAVE_IPSEC_DIR_FWD \ + -DOPENSSL_NO_CMS \ -DOPENSSL_NO_EC \ -DOPENSSL_NO_ECDSA \ -DOPENSSL_NO_ECDH \ @@ -12,6 +12,9 @@ strongswan-5.0.2 tunnels. The "ipsec lookip" command can be used to query such information or receive notifications. +- The new error-notify plugin catches some common error conditions and allows + an external application to receive notifications for them over a UNIX socket. + - IKE proposals can now use a PRF algorithm different to that defined for integrity protection. If an algorithm with a "prf" prefix is defined explicitly (such as prfsha1 or prfsha256), no implicit PRF algorithm based on @@ -27,6 +30,22 @@ strongswan-5.0.2 batches can be triggered and monitored externally using the "ipsec load-tester" tool. +- PKCS#7 container parsing has been modularized, and the openssl plugin + gained an alternative implementation to decrypt and verify such files. + In contrast to our own DER parser, OpenSSL can handle BER files, which is + required for interoperability of our scepclient with EJBCA. + +- Support for the proprietary IKEv1 fragmentation extension has been added. + Fragments are always handled on receipt but only sent if supported by the peer + and if enabled with the new fragmentation ipsec.conf option. + +- IKEv1 in charon can now parse certificates received in PKCS#7 containers and + supports NAT traversal as used by Windows clients. Patches courtesy of + Volker Rümelin. + +- The new rdrand plugin provides a high quality / high performance random + source using the Intel rdrand instruction found on Ivy Bridge processors. + - The integration test environment was updated and now uses KVM and reproducible guest images based on Debian. diff --git a/configure.in b/configure.in index 44f2031fc..a3c7590d8 100644 --- a/configure.in +++ b/configure.in @@ -16,7 +16,7 @@ dnl =========================== dnl initialize & set some vars dnl =========================== -AC_INIT(strongSwan,5.0.2dr4) +AC_INIT(strongSwan,5.0.2rc1) AM_INIT_AUTOMAKE(tar-ustar) AC_CONFIG_MACRO_DIR([m4/config]) AC_CONFIG_HEADERS([config.h]) @@ -112,6 +112,7 @@ ARG_DISBL_SET([sha1], [disable SHA1 software implementation plugin.]) ARG_DISBL_SET([sha2], [disable SHA256/SHA384/SHA512 software implementation plugin.]) ARG_DISBL_SET([fips-prf], [disable FIPS PRF software implementation plugin.]) ARG_DISBL_SET([gmp], [disable GNU MP (libgmp) based crypto implementation plugin.]) +ARG_ENABL_SET([rdrand], [enable Intel RDRAND random generator plugin.]) ARG_DISBL_SET([random], [disable RNG implementation on top of /dev/(u)random.]) ARG_DISBL_SET([nonce], [disable nonce generation plugin.]) ARG_DISBL_SET([x509], [disable X509 certificate implementation plugin.]) @@ -119,6 +120,7 @@ ARG_DISBL_SET([revocation], [disable X509 CRL/OCSP revocation check plugin.] ARG_DISBL_SET([constraints], [disable advanced X509 constraint checking plugin.]) ARG_DISBL_SET([pubkey], [disable RAW public key support plugin.]) ARG_DISBL_SET([pkcs1], [disable PKCS1 key decoding plugin.]) +ARG_DISBL_SET([pkcs7], [disable PKCS7 container support plugin.]) ARG_DISBL_SET([pkcs8], [disable PKCS8 private key decoding plugin.]) ARG_DISBL_SET([pgp], [disable PGP key decoding plugin.]) ARG_DISBL_SET([dnskey], [disable DNS RR key decoding plugin.]) @@ -218,6 +220,7 @@ ARG_ENABL_SET([nm], [enable NetworkManager backend.]) ARG_ENABL_SET([ha], [enable high availability cluster plugin.]) ARG_ENABL_SET([whitelist], [enable peer identity whitelisting plugin.]) ARG_ENABL_SET([lookip], [enable fast virtual IP lookup and notification plugin.]) +ARG_ENABL_SET([error-notify], [enable error notification plugin.]) ARG_ENABL_SET([certexpire], [enable CSV export of expiration dates of used certificates.]) ARG_ENABL_SET([led], [enable plugin to control LEDs on IKEv2 activity using the Linux kernel LED subsystem.]) ARG_ENABL_SET([duplicheck], [advanced duplicate checking plugin using liveness checks.]) @@ -888,6 +891,7 @@ ADD_PLUGIN([sha1], [s charon openac scepclient pki scripts medsr ADD_PLUGIN([sha2], [s charon openac scepclient pki scripts medsrv attest nm]) ADD_PLUGIN([md4], [s charon openac manager scepclient pki nm]) ADD_PLUGIN([md5], [s charon openac scepclient pki scripts attest nm]) +ADD_PLUGIN([rdrand], [s charon openac scepclient pki scripts medsrv attest nm]) ADD_PLUGIN([random], [s charon openac scepclient pki scripts medsrv attest nm]) ADD_PLUGIN([nonce], [s charon nm]) ADD_PLUGIN([x509], [s charon openac scepclient pki scripts attest nm]) @@ -895,6 +899,7 @@ ADD_PLUGIN([revocation], [s charon nm]) ADD_PLUGIN([constraints], [s charon nm]) ADD_PLUGIN([pubkey], [s charon]) ADD_PLUGIN([pkcs1], [s charon openac scepclient pki scripts manager medsrv attest nm]) +ADD_PLUGIN([pkcs7], [s scepclient pki]) ADD_PLUGIN([pkcs8], [s charon openac scepclient pki scripts manager medsrv attest nm]) ADD_PLUGIN([pgp], [s charon]) ADD_PLUGIN([dnskey], [s charon]) @@ -964,6 +969,7 @@ ADD_PLUGIN([android-log], [c charon]) ADD_PLUGIN([ha], [c charon]) ADD_PLUGIN([whitelist], [c charon]) ADD_PLUGIN([lookip], [c charon]) +ADD_PLUGIN([error-notify], [c charon]) ADD_PLUGIN([certexpire], [c charon]) ADD_PLUGIN([led], [c charon]) ADD_PLUGIN([duplicheck], [c charon]) @@ -1011,6 +1017,7 @@ AM_CONDITIONAL(USE_SHA1, test x$sha1 = xtrue) AM_CONDITIONAL(USE_SHA2, test x$sha2 = xtrue) AM_CONDITIONAL(USE_FIPS_PRF, test x$fips_prf = xtrue) AM_CONDITIONAL(USE_GMP, test x$gmp = xtrue) +AM_CONDITIONAL(USE_RDRAND, test x$rdrand = xtrue) AM_CONDITIONAL(USE_RANDOM, test x$random = xtrue) AM_CONDITIONAL(USE_NONCE, test x$nonce = xtrue) AM_CONDITIONAL(USE_X509, test x$x509 = xtrue) @@ -1018,6 +1025,7 @@ AM_CONDITIONAL(USE_REVOCATION, test x$revocation = xtrue) AM_CONDITIONAL(USE_CONSTRAINTS, test x$constraints = xtrue) AM_CONDITIONAL(USE_PUBKEY, test x$pubkey = xtrue) AM_CONDITIONAL(USE_PKCS1, test x$pkcs1 = xtrue) +AM_CONDITIONAL(USE_PKCS7, test x$pkcs7 = xtrue) AM_CONDITIONAL(USE_PKCS8, test x$pkcs8 = xtrue) AM_CONDITIONAL(USE_PGP, test x$pgp = xtrue) AM_CONDITIONAL(USE_DNSKEY, test x$dnskey = xtrue) @@ -1055,6 +1063,7 @@ AM_CONDITIONAL(USE_LOAD_TESTER, test x$load_tester = xtrue) AM_CONDITIONAL(USE_HA, test x$ha = xtrue) AM_CONDITIONAL(USE_WHITELIST, test x$whitelist = xtrue) AM_CONDITIONAL(USE_LOOKIP, test x$lookip = xtrue) +AM_CONDITIONAL(USE_ERROR_NOTIFY, test x$error_notify = xtrue) AM_CONDITIONAL(USE_CERTEXPIRE, test x$certexpire = xtrue) AM_CONDITIONAL(USE_LED, test x$led = xtrue) AM_CONDITIONAL(USE_DUPLICHECK, test x$duplicheck = xtrue) @@ -1192,6 +1201,7 @@ AC_OUTPUT( src/libstrongswan/plugins/sha2/Makefile src/libstrongswan/plugins/fips_prf/Makefile src/libstrongswan/plugins/gmp/Makefile + src/libstrongswan/plugins/rdrand/Makefile src/libstrongswan/plugins/random/Makefile src/libstrongswan/plugins/nonce/Makefile src/libstrongswan/plugins/hmac/Makefile @@ -1201,6 +1211,7 @@ AC_OUTPUT( src/libstrongswan/plugins/constraints/Makefile src/libstrongswan/plugins/pubkey/Makefile src/libstrongswan/plugins/pkcs1/Makefile + src/libstrongswan/plugins/pkcs7/Makefile src/libstrongswan/plugins/pkcs8/Makefile src/libstrongswan/plugins/pgp/Makefile src/libstrongswan/plugins/dnskey/Makefile @@ -1289,6 +1300,7 @@ AC_OUTPUT( src/libcharon/plugins/ha/Makefile src/libcharon/plugins/whitelist/Makefile src/libcharon/plugins/lookip/Makefile + src/libcharon/plugins/error_notify/Makefile src/libcharon/plugins/certexpire/Makefile src/libcharon/plugins/led/Makefile src/libcharon/plugins/duplicheck/Makefile diff --git a/man/ipsec.conf.5.in b/man/ipsec.conf.5.in index 303fb78fa..2766cc4ed 100644 --- a/man/ipsec.conf.5.in +++ b/man/ipsec.conf.5.in @@ -403,6 +403,21 @@ force UDP encapsulation for ESP packets even if no NAT situation is detected. This may help to surmount restrictive firewalls. In order to force the peer to encapsulate packets, NAT detection payloads are faked. .TP +.BR fragmentation " = yes | force | " no +whether to use IKE fragmentation (proprietary IKEv1 extension). Acceptable +values are +.BR yes , +.B force +and +.B no +(the default). Fragmented messages sent by a peer are always accepted +irrespective of the value of this option. If set to +.BR yes , +and the peer supports it, larger IKE messages will be sent in fragments. +If set to +.B force +the initial IKE message will already be fragmented if required. +.TP .BR ike " = <cipher suites>" comma-separated list of IKE/ISAKMP SA encryption/authentication algorithms to be used, e.g. diff --git a/man/strongswan.conf.5.in b/man/strongswan.conf.5.in index 8000951ab..3f4e6389e 100644 --- a/man/strongswan.conf.5.in +++ b/man/strongswan.conf.5.in @@ -164,6 +164,10 @@ are released to free memory once an IKE_SA is established. Enabling this might conflict with plugins that later need access to e.g. the used certificates. .TP +.BR charon.fragment_size " [512]" +Maximum size (in bytes) of a sent fragment when using the proprietary IKEv1 +fragmentation extension. +.TP .BR charon.half_open_timeout " [30]" Timeout in seconds for connecting IKE_SAs (also see IKE_SA_INIT DROPPING). .TP diff --git a/src/charon-nm/nm/nm_service.c b/src/charon-nm/nm/nm_service.c index 6fa5e51a4..eb187496d 100644 --- a/src/charon-nm/nm/nm_service.c +++ b/src/charon-nm/nm/nm_service.c @@ -500,7 +500,8 @@ static gboolean connect_(NMVPNPlugin *plugin, NMConnection *connection, */ ike_cfg = ike_cfg_create(IKEV2, TRUE, encap, "0.0.0.0", FALSE, charon->socket->get_port(charon->socket, FALSE), - (char*)address, FALSE, IKEV2_UDP_PORT); + (char*)address, FALSE, IKEV2_UDP_PORT, + FRAGMENTATION_NO); ike_cfg->add_proposal(ike_cfg, proposal_create_default(PROTO_IKE)); peer_cfg = peer_cfg_create(priv->name, ike_cfg, CERT_SEND_IF_ASKED, UNIQUE_REPLACE, 1, /* keyingtries */ diff --git a/src/conftest/README b/src/conftest/README index a1b1b3e3a..133ae603c 100644 --- a/src/conftest/README +++ b/src/conftest/README @@ -239,6 +239,7 @@ Currently, the following hooks are defined with the following options: rebuild_auth: rebuild AUTH payload, i.e. if ID payload changed reset_seq: Reset sequence numbers of an ESP SA delay: Seconds to delay reset after SA established + oseq: Sequence number to set, default is 0 set_critical: Set critical bit on existing payloads: request: yes to set in request, no in response id: IKEv2 message identifier of message to mangle payloads diff --git a/src/conftest/config.c b/src/conftest/config.c index f896b9537..22c9d23e0 100644 --- a/src/conftest/config.c +++ b/src/conftest/config.c @@ -106,7 +106,8 @@ static ike_cfg_t *load_ike_config(private_config_t *this, settings->get_str(settings, "configs.%s.lhost", "%any", config), FALSE, settings->get_int(settings, "configs.%s.lport", 500, config), settings->get_str(settings, "configs.%s.rhost", "%any", config), FALSE, - settings->get_int(settings, "configs.%s.rport", 500, config)); + settings->get_int(settings, "configs.%s.rport", 500, config), + FRAGMENTATION_NO); token = settings->get_str(settings, "configs.%s.proposal", NULL, config); if (token) { diff --git a/src/conftest/conftest.c b/src/conftest/conftest.c index bb4c5ed97..c2251effa 100644 --- a/src/conftest/conftest.c +++ b/src/conftest/conftest.c @@ -378,6 +378,22 @@ static void load_log_levels(file_logger_t *logger, char *section) } /** + * Load logger options for a logger from section + */ +static void load_logger_options(file_logger_t *logger, char *section) +{ + bool ike_name; + char *time_format; + + time_format = conftest->test->get_str(conftest->test, + "log.%s.time_format", NULL, section); + ike_name = conftest->test->get_bool(conftest->test, + "log.%s.ike_name", FALSE, section); + + logger->set_options(logger, time_format, ike_name); +} + +/** * Load logger configuration */ static void load_loggers(file_logger_t *logger) @@ -386,6 +402,10 @@ static void load_loggers(file_logger_t *logger) char *section; load_log_levels(logger, "stdout"); + load_logger_options(logger, "stdout"); + /* Re-add the logger to propagate configuration changes to the + * logging system */ + charon->bus->add_logger(charon->bus, &logger->logger); enumerator = conftest->test->create_section_enumerator(conftest->test, "log"); while (enumerator->enumerate(enumerator, §ion)) @@ -393,7 +413,7 @@ static void load_loggers(file_logger_t *logger) if (!streq(section, "stdout")) { logger = file_logger_create(section); - logger->set_options(logger, NULL, FALSE); + load_logger_options(logger, section); logger->open(logger, FALSE, FALSE); load_log_levels(logger, section); charon->bus->add_logger(charon->bus, &logger->logger); diff --git a/src/conftest/hooks/reset_seq.c b/src/conftest/hooks/reset_seq.c index 6fb7a2e4b..100977324 100644 --- a/src/conftest/hooks/reset_seq.c +++ b/src/conftest/hooks/reset_seq.c @@ -12,6 +12,27 @@ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. */ +/* + * Copyright (C) 2012 achelos GmbH + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ #include "hook.h" @@ -40,21 +61,46 @@ struct private_reset_seq_t { * Delay for reset */ int delay; + + /** + * Sequence number to set for outgoing packages + */ + int oseq; +}; + +typedef struct reset_cb_data_t reset_cb_data_t; + +/** + * Data needed for the callback job + */ +struct reset_cb_data_t { + + /** + * The SA to modify + */ + struct xfrm_usersa_id usersa; + + /** + * Sequence number to set for outgoing packages + */ + int oseq; }; /** * Callback job */ -static job_requeue_t reset_cb(struct xfrm_usersa_id *data) +static job_requeue_t reset_cb(struct reset_cb_data_t *data) { netlink_buf_t request; struct nlmsghdr *hdr; struct xfrm_aevent_id *id; struct rtattr *rthdr; + struct xfrm_replay_state *rpstate; struct sockaddr_nl addr; int s, len; - DBG1(DBG_CFG, "resetting sequence number of SPI 0x%x", htonl(data->spi)); + DBG1(DBG_CFG, "setting sequence number of SPI 0x%x to %d", + htonl(data->usersa.spi), data->oseq); memset(&request, 0, sizeof(request)); @@ -66,13 +112,22 @@ static job_requeue_t reset_cb(struct xfrm_usersa_id *data) hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct xfrm_aevent_id)); id = (struct xfrm_aevent_id*)NLMSG_DATA(hdr); - id->sa_id = *data; + id->sa_id = data->usersa; rthdr = XFRM_RTA(hdr, struct xfrm_aevent_id); rthdr->rta_type = XFRMA_REPLAY_VAL; rthdr->rta_len = RTA_LENGTH(sizeof(struct xfrm_replay_state)); hdr->nlmsg_len += rthdr->rta_len; + /* xfrm_replay_state is the structure the kernel uses for + * replay detection, and the oseq element contains the + * sequence number for outgoing packets. Currently, this + * function sets the other elements seq (records the number of + * incoming packets) and bitmask to zero, but they could be + * adjusted in the same way as oseq if required. */ + rpstate = (struct xfrm_replay_state*)RTA_DATA(rthdr); + rpstate->oseq = data->oseq; + s = socket(AF_NETLINK, SOCK_RAW, NETLINK_XFRM); if (s == -1) { @@ -97,17 +152,21 @@ static job_requeue_t reset_cb(struct xfrm_usersa_id *data) static void schedule_reset_job(private_reset_seq_t *this, host_t *dst, u_int32_t spi) { - struct xfrm_usersa_id *data; + struct reset_cb_data_t *data; chunk_t chunk; INIT(data, - .spi = spi, - .family = dst->get_family(dst), - .proto = IPPROTO_ESP, + .usersa = { + .spi = spi, + .family = dst->get_family(dst), + .proto = IPPROTO_ESP, + }, + .oseq = this->oseq, ); chunk = dst->get_address(dst); - memcpy(&data->daddr, chunk.ptr, min(chunk.len, sizeof(xfrm_address_t))); + memcpy(&data->usersa.daddr, chunk.ptr, + min(chunk.len, sizeof(xfrm_address_t))); lib->scheduler->schedule_job(lib->scheduler, (job_t*)callback_job_create( @@ -149,6 +208,8 @@ hook_t *reset_seq_hook_create(char *name) }, .delay = conftest->test->get_int(conftest->test, "hooks.%s.delay", 10, name), + .oseq = conftest->test->get_int(conftest->test, + "hooks.%s.oseq", 0, name), ); return &this->hook; diff --git a/src/frontends/android/jni/Android.mk b/src/frontends/android/jni/Android.mk index 83f8b0fca..ce544d2c3 100644 --- a/src/frontends/android/jni/Android.mk +++ b/src/frontends/android/jni/Android.mk @@ -35,6 +35,7 @@ strongswan_CFLAGS := \ -DHAVE_STRUCT_SADB_X_POLICY_SADB_X_POLICY_PRIORITY \ -DHAVE_IPSEC_MODE_BEET \ -DHAVE_IPSEC_DIR_FWD \ + -DOPENSSL_NO_CMS \ -DOPENSSL_NO_ENGINE \ -DCONFIG_H_INCLUDED \ -DCAPABILITIES \ diff --git a/src/frontends/android/jni/libandroidbridge/android_jni.h b/src/frontends/android/jni/libandroidbridge/android_jni.h index b89fd3ad5..99c0bc2cd 100644 --- a/src/frontends/android/jni/libandroidbridge/android_jni.h +++ b/src/frontends/android/jni/libandroidbridge/android_jni.h @@ -122,4 +122,36 @@ static inline char *androidjni_convert_jstring(JNIEnv *env, jstring jstr) return str; } +/** + * Converts the given Java byte array to a chunk + * + * @param env JNIEnv + * @param jbytearray Java byte array + * @return allocated chunk + */ +static inline chunk_t chunk_from_byte_array(JNIEnv *env, jbyteArray jbytearray) +{ + chunk_t chunk; + + chunk = chunk_alloc((*env)->GetArrayLength(env, jbytearray)); + (*env)->GetByteArrayRegion(env, jbytearray, 0, chunk.len, chunk.ptr); + return chunk; +} + +/** + * Converts the given chunk to a Java byte array + * + * @param env JNIEnv + * @param chunk native chunk + * @return allocated Java byte array + */ +static inline jbyteArray byte_array_from_chunk(JNIEnv *env, chunk_t chunk) +{ + jbyteArray jbytearray; + + jbytearray = (*env)->NewByteArray(env, chunk.len); + (*env)->SetByteArrayRegion(env, jbytearray, 0, chunk.len, chunk.ptr); + return jbytearray; +} + #endif /** ANDROID_JNI_H_ @}*/ 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 21b743767..1aeabac2f 100644 --- a/src/frontends/android/jni/libandroidbridge/backend/android_private_key.c +++ b/src/frontends/android/jni/libandroidbridge/backend/android_private_key.c @@ -51,30 +51,6 @@ struct private_private_key_t { refcount_t ref; }; -/** - * Converts the given Java byte array to a chunk - */ -static chunk_t chunk_from_byte_array(JNIEnv *env, jbyteArray jbytearray) -{ - chunk_t chunk; - - chunk = chunk_alloc((*env)->GetArrayLength(env, jbytearray)); - (*env)->GetByteArrayRegion(env, jbytearray, 0, chunk.len, chunk.ptr); - return chunk; -} - -/** - * Converts the given chunk to a Java byte array - */ -static jbyteArray byte_array_from_chunk(JNIEnv *env, chunk_t chunk) -{ - jbyteArray jbytearray; - - jbytearray = (*env)->NewByteArray(env, chunk.len); - (*env)->SetByteArrayRegion(env, jbytearray, 0, chunk.len, chunk.ptr); - return jbytearray; -} - METHOD(private_key_t, sign, bool, private_private_key_t *this, signature_scheme_t scheme, chunk_t data, chunk_t *signature) diff --git a/src/frontends/android/jni/libandroidbridge/backend/android_service.c b/src/frontends/android/jni/libandroidbridge/backend/android_service.c index 6c23bf268..cce5ff0d4 100644 --- a/src/frontends/android/jni/libandroidbridge/backend/android_service.c +++ b/src/frontends/android/jni/libandroidbridge/backend/android_service.c @@ -471,7 +471,8 @@ static job_requeue_t initiate(private_android_service_t *this) ike_cfg = ike_cfg_create(IKEV2, TRUE, TRUE, "0.0.0.0", FALSE, charon->socket->get_port(charon->socket, FALSE), - this->gateway, FALSE, IKEV2_UDP_PORT); + this->gateway, FALSE, IKEV2_UDP_PORT, + FRAGMENTATION_NO); ike_cfg->add_proposal(ike_cfg, proposal_create_default(PROTO_IKE)); peer_cfg = peer_cfg_create("android", ike_cfg, CERT_SEND_IF_ASKED, diff --git a/src/frontends/android/jni/libandroidbridge/kernel/android_net.c b/src/frontends/android/jni/libandroidbridge/kernel/android_net.c index bc3395916..4b42b55a8 100644 --- a/src/frontends/android/jni/libandroidbridge/kernel/android_net.c +++ b/src/frontends/android/jni/libandroidbridge/kernel/android_net.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012 Tobias Brunner + * Copyright (C) 2012-2013 Tobias Brunner * Hochschule fuer Technik Rapperswil * * This program is free software; you can redistribute it and/or modify it @@ -91,6 +91,13 @@ METHOD(kernel_net_t, get_source_addr, host_t*, dest->get_family(dest) == AF_INET); } +METHOD(kernel_net_t, get_interface, bool, + private_kernel_android_net_t *this, host_t *host, char **name) +{ + return this->network_manager->get_interface(this->network_manager, host, + name); +} + METHOD(kernel_net_t, add_ip, status_t, private_kernel_android_net_t *this, host_t *virtual_ip, int prefix, char *iface) @@ -120,7 +127,7 @@ kernel_android_net_t *kernel_android_net_create() .interface = { .get_source_addr = _get_source_addr, .get_nexthop = (void*)return_null, - .get_interface = (void*)return_null, + .get_interface = _get_interface, .create_address_enumerator = (void*)enumerator_create_empty, .add_ip = _add_ip, .del_ip = (void*)return_failed, diff --git a/src/frontends/android/jni/libandroidbridge/kernel/network_manager.c b/src/frontends/android/jni/libandroidbridge/kernel/network_manager.c index 17bde6a84..c8950cbd1 100644 --- a/src/frontends/android/jni/libandroidbridge/kernel/network_manager.c +++ b/src/frontends/android/jni/libandroidbridge/kernel/network_manager.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012 Tobias Brunner + * Copyright (C) 2012-2013 Tobias Brunner * Hochschule fuer Technik Rapperswil * * This program is free software; you can redistribute it and/or modify it @@ -69,7 +69,7 @@ METHOD(network_manager_t, get_local_address, host_t*, goto failed; } jaddr = (*env)->CallObjectMethod(env, this->obj, method_id, ipv4); - if (!jaddr) + if (!jaddr || androidjni_exception_occurred(env)) { goto failed; } @@ -85,6 +85,46 @@ failed: return NULL; } +METHOD(network_manager_t, get_interface, bool, + private_network_manager_t *this, host_t *ip, char **name) +{ + JNIEnv *env; + jmethodID method_id; + jbyteArray jaddr; + jstring jinterface; + + if (ip->is_anyaddr(ip)) + { + return FALSE; + } + + androidjni_attach_thread(&env); + + method_id = (*env)->GetMethodID(env, this->cls, "getInterface", + "([B)Ljava/lang/String;"); + if (!method_id) + { + goto failed; + } + jaddr = byte_array_from_chunk(env, ip->get_address(ip)); + jinterface = (*env)->CallObjectMethod(env, this->obj, method_id, jaddr); + if (!jinterface || androidjni_exception_occurred(env)) + { + goto failed; + } + if (name) + { + *name = androidjni_convert_jstring(env, jinterface); + } + androidjni_detach_thread(); + return TRUE; + +failed: + androidjni_exception_occurred(env); + androidjni_detach_thread(); + return FALSE; +} + JNI_METHOD(NetworkManager, networkChanged, void, bool disconnected) { @@ -206,6 +246,7 @@ network_manager_t *network_manager_create(jobject context) INIT(this, .public = { .get_local_address = _get_local_address, + .get_interface = _get_interface, .add_connectivity_cb = _add_connectivity_cb, .remove_connectivity_cb = _remove_connectivity_cb, .destroy = _destroy, diff --git a/src/frontends/android/jni/libandroidbridge/kernel/network_manager.h b/src/frontends/android/jni/libandroidbridge/kernel/network_manager.h index a35103b89..2638b5aa0 100644 --- a/src/frontends/android/jni/libandroidbridge/kernel/network_manager.h +++ b/src/frontends/android/jni/libandroidbridge/kernel/network_manager.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012 Tobias Brunner + * Copyright (C) 2012-2013 Tobias Brunner * Hochschule fuer Technik Rapperswil * * This program is free software; you can redistribute it and/or modify it @@ -56,6 +56,15 @@ struct network_manager_t { host_t *(*get_local_address)(network_manager_t *this, bool ipv4); /** + * Get the name of the interface on which the given IP address is installed + * + * @param ip the IP address to look for + * @param name returns the name of the interface (optional) + * @return TRUE if found + */ + bool (*get_interface)(network_manager_t *this, host_t *ip, char **name); + + /** * Register a callback that is called if connectivity changes * * @note Only the first registered callback is currently used diff --git a/src/frontends/android/res/values-ru/arrays.xml b/src/frontends/android/res/values-ru/arrays.xml new file mode 100644 index 000000000..48a7219cc --- /dev/null +++ b/src/frontends/android/res/values-ru/arrays.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2012 Dmitry Korzhevin + + 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> + <!-- the order here must match the enum entries in VpnType.java --> + <string-array name="vpn_types"> + <item>IKEv2 EAP (Логин/Пароль)</item> + <item>Сертификат IKEv2</item> + </string-array> +</resources> diff --git a/src/frontends/android/res/values-ru/strings.xml b/src/frontends/android/res/values-ru/strings.xml new file mode 100644 index 000000000..afa21361b --- /dev/null +++ b/src/frontends/android/res/values-ru/strings.xml @@ -0,0 +1,105 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2012 Dmitry Korzhevin + + 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> + + <!-- Application --> + <string name="app_name">Клиент strongSwan VPN</string> + <string name="main_activity_name">strongSwan</string> + <string name="reload_trusted_certs">Обновить сертификат CA</string> + <string name="show_log">Журнал</string> + <string name="search">Поиск</string> + <string name="vpn_not_supported_title">VPN не поддерживается</string> + <string name="vpn_not_supported">Ваше устройство не поддерживат VPN приложение.\nПожалуйста свяжитесь с производителем.</string> + <string name="vpn_not_supported_during_lockdown">VPN соединения не поддерживаются в режиме lockdown.</string> + <string name="loading">Загрузка…</string> + <string name="profile_not_found">Профиль не найден</string> + <string name="strongswan_shortcut">Ссылка на strongSwan</string> + + <!-- Log view --> + <string name="log_title">Журнал</string> + <string name="send_log">Отправить журнал</string> + <string name="empty_log">Журнал пуст</string> + <string name="log_mail_subject">strongSwan %1$s журнал</string> + + <!-- VPN profile list --> + <string name="no_profiles">VPN профили не обнаружены.</string> + <string name="add_profile">Добавить VPN профиль</string> + <string name="edit_profile">Редактировать</string> + <string name="delete_profile">Удалить</string> + <string name="select_profiles">Выбрать профили</string> + <string name="profiles_deleted">Выбранные профили удалены</string> + <string name="no_profile_selected">Профили не выбраны</string> + <string name="one_profile_selected">Выбран профиль</string> + <string name="x_profiles_selected">%1$d прифиля(ей) выбрано"</string> + + <!-- VPN profile details --> + <string name="profile_edit_save">Сохранить</string> + <string name="profile_edit_cancel">Отмена</string> + <string name="profile_name_label">Название профиля:</string> + <string name="profile_name_hint">(адрес шлюза)</string> + <string name="profile_gateway_label">Шлюз:</string> + <string name="profile_vpn_type_label">Тип:</string> + <string name="profile_username_label">Логин:</string> + <string name="profile_password_label">Пароль:</string> + <string name="profile_password_hint">(спросить если нужно)</string> + <string name="profile_user_certificate_label">Сертификат пользователя:</string> + <string name="profile_user_select_certificate_label">Выбрать сертификат пользователя</string> + <string name="profile_user_select_certificate">Выбрать сертификат пользователя</string> + <string name="profile_ca_label">Сертификат CA:</string> + <string name="profile_ca_auto_label">Выбрать автоматически</string> + <string name="profile_ca_select_certificate_label">Выбрать сертификат CA</string> + <string name="profile_ca_select_certificate">Выбрать CA сертификат</string> + <!-- Warnings/Notifications in the details view --> + <string name="alert_text_no_input_gateway">Пожалуйста введите адрес шлюза</string> + <string name="alert_text_no_input_username">Пожалуйста введите имя пользователя</string> + <string name="alert_text_nocertfound_title">Не выбран сертификат CA</string> + <string name="alert_text_nocertfound">Пожалуйста выберите один <i>Выбрать автоматически</i></string> + + <!-- Trusted certificate selection --> + <string name="trusted_certs_title">Сертификаты CA</string> + <string name="no_certificates">Нет доступных сертификатов</string> + <string name="system_tab">Система</string> + <string name="user_tab">Пользователь</string> + + <!-- VPN state fragment --> + <string name="state_label">Статус:</string> + <string name="profile_label">Профиль:</string> + <string name="disconnect">Отключить</string> + <string name="state_connecting">Соединение…</string> + <string name="state_connected">Соединен</string> + <string name="state_disconnecting">Отключение…</string> + <string name="state_disabled">Нет активных VPN</string> + <string name="state_error">Ошибка</string> + + <!-- Dialogs --> + <string name="login_title">Введите пароль для соединения</string> + <string name="login_confirm">Соединить</string> + <string name="error_introduction">Ошибка подключения к VPN:</string> + <string name="error_lookup_failed">Не найден адрес шлюза.</string> + <string name="error_unreachable">Шлюз недоступен.</string> + <string name="error_peer_auth_failed">Ошибка авторизаци при подключении к шлюзу.</string> + <string name="error_auth_failed">Ошибка авторизации пользователя.</string> + <string name="error_generic">Неизвестная ошибка.</string> + <string name="connecting_title">Подключение: %1$s</string> + <string name="connecting_message">Подключение к VPN с \""%1$s\".</string> + <string name="vpn_connected">Соединение с VPN установлено</string> + <string name="vpn_profile_connected">Подключение к этому профилю VPN уже существует!</string> + <string name="reconnect">Переподключить</string> + <string name="connect_profile_question">Подключить %1$s?</string> + <string name="replaces_active_connection">Это заменит ваше текущее VPN соединение!</string> + <string name="connect">Соединить</string> + +</resources> + diff --git a/src/frontends/android/res/values-ua/arrays.xml b/src/frontends/android/res/values-ua/arrays.xml new file mode 100644 index 000000000..bd4366405 --- /dev/null +++ b/src/frontends/android/res/values-ua/arrays.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2012 Dmitry Korzhevin + + 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> + <!-- the order here must match the enum entries in VpnType.java --> + <string-array name="vpn_types"> + <item>IKEv2 EAP (Логін/Пароль)</item> + <item>Сертифікати IKEv2</item> + </string-array> +</resources> diff --git a/src/frontends/android/res/values-ua/strings.xml b/src/frontends/android/res/values-ua/strings.xml new file mode 100644 index 000000000..953ba0a54 --- /dev/null +++ b/src/frontends/android/res/values-ua/strings.xml @@ -0,0 +1,106 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2012 Dmitry Korzhevin + Copyright (C) 2013 Pavel Kopchyk + + 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> + + <!-- Application --> + <string name="app_name">strongSwan VPN клієнт</string> + <string name="main_activity_name">strongSwan</string> + <string name="reload_trusted_certs">Перезавантажити CA сертифікати</string> + <string name="show_log">Перегляд журналу</string> + <string name="search">Пошук</string> + <string name="vpn_not_supported_title">VPN не підтримуеться</string> + <string name="vpn_not_supported">Ваш пристрій не підтримує VPN.\nЗв\'яжіться з виробником.</string> + <string name="vpn_not_supported_during_lockdown">VPN з\'єднання не пітримується у режимі lockdown.</string> + <string name="loading">Завантаження…</string> + <string name="profile_not_found">Профіль не знайдено</string> + <string name="strongswan_shortcut">strongSwan посилання</string> + + <!-- Log view --> + <string name="log_title">Журнал</string> + <string name="send_log">Відправити файл журналу</string> + <string name="empty_log">Журнал порожній</string> + <string name="log_mail_subject">strongSwan %1$s файл журналу</string> + + <!-- VPN profile list --> + <string name="no_profiles">Немає VPN профілів</string> + <string name="add_profile">Додати VPN профіль</string> + <string name="edit_profile">Редагувати</string> + <string name="delete_profile">Видалити</string> + <string name="select_profiles">Обрати профіль</string> + <string name="profiles_deleted">Обрані профілі видалено</string> + <string name="no_profile_selected">Профіль не обрано</string> + <string name="one_profile_selected">Один профіль обрано</string> + <string name="x_profiles_selected">%1$d профілів обрано"</string> + + <!-- VPN profile details --> + <string name="profile_edit_save">Зберегти</string> + <string name="profile_edit_cancel">Відміна</string> + <string name="profile_name_label">Назва профілю:</string> + <string name="profile_name_hint">(використовувати адресу шлюза)</string> + <string name="profile_gateway_label">Шлюз:</string> + <string name="profile_vpn_type_label">Тип:</string> + <string name="profile_username_label">Логін:</string> + <string name="profile_password_label">Пароль:</string> + <string name="profile_password_hint">(запитати якщо потрібно)</string> + <string name="profile_user_certificate_label">Сертифікат користувача:</string> + <string name="profile_user_select_certificate_label">Виберіть сертифікат користувача</string> + <string name="profile_user_select_certificate">Вибрати спеціальний сертифікат користувача</string> + <string name="profile_ca_label">Сертифікат CA:</string> + <string name="profile_ca_auto_label">Вибрати автоматично</string> + <string name="profile_ca_select_certificate_label">Вибрати сертифікат CA</string> + <string name="profile_ca_select_certificate">Вибрати спеціальний сертифікат CA</string> + <!-- Warnings/Notifications in the details view --> + <string name="alert_text_no_input_gateway">Введіть адресу шлюза тут</string> + <string name="alert_text_no_input_username">Введіть ім\'я користувача тут</string> + <string name="alert_text_nocertfound_title">Не вибрано сертифікат CA</string> + <string name="alert_text_nocertfound">Будь ласка виберіть один <i>Вибрати автоматично</i></string> + + <!-- Trusted certificate selection --> + <string name="trusted_certs_title">Сертифікати CA</string> + <string name="no_certificates">Немає сертифікатів</string> + <string name="system_tab">Система</string> + <string name="user_tab">Користувач</string> + + <!-- VPN state fragment --> + <string name="state_label">Статус:</string> + <string name="profile_label">Профіль:</string> + <string name="disconnect">Роз\'єднати</string> + <string name="state_connecting">Підключення…</string> + <string name="state_connected">Підключений</string> + <string name="state_disconnecting">Роз\'єднання…</string> + <string name="state_disabled">Немає активних VPN</string> + <string name="state_error">Помилка</string> + + <!-- Dialogs --> + <string name="login_title">Введіть пароль для з\'єднання</string> + <string name="login_confirm">Підключити</string> + <string name="error_introduction">Помилка підлючення VPN:</string> + <string name="error_lookup_failed">Помилка пошуку адреси шлюза.</string> + <string name="error_unreachable">Немає зв\'язку зі шлюзом.</string> + <string name="error_peer_auth_failed">Помилка перевірки данних аутентифікації шлюза.</string> + <string name="error_auth_failed">Помилка аутентифікації користувача.</string> + <string name="error_generic">Невідома помилка під час підключення.</string> + <string name="connecting_title">Підключення: %1$s</string> + <string name="connecting_message">Підключення VPN з \""%1$s\".</string> + <string name="vpn_connected">VPN підключено</string> + <string name="vpn_profile_connected">Цей VPN профіль зараз підключений!</string> + <string name="reconnect">Перепідключитися</string> + <string name="connect_profile_question">Підключити %1$s?</string> + <string name="replaces_active_connection">Ця дія замінить ваше поточне VPN з\'єднання!</string> + <string name="connect">Підключити</string> + +</resources> + diff --git a/src/frontends/android/src/org/strongswan/android/logic/NetworkManager.java b/src/frontends/android/src/org/strongswan/android/logic/NetworkManager.java index 160865fb7..168341a68 100644 --- a/src/frontends/android/src/org/strongswan/android/logic/NetworkManager.java +++ b/src/frontends/android/src/org/strongswan/android/logic/NetworkManager.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012 Tobias Brunner + * Copyright (C) 2012-2013 Tobias Brunner * Hochschule fuer Technik Rapperswil * * This program is free software; you can redistribute it and/or modify it @@ -20,6 +20,7 @@ import java.net.Inet6Address; import java.net.InetAddress; import java.net.NetworkInterface; import java.net.SocketException; +import java.net.UnknownHostException; import java.util.Enumeration; import android.content.BroadcastReceiver; @@ -110,4 +111,32 @@ public class NetworkManager extends BroadcastReceiver } return null; } + + /** + * Search for an interface that has the given address installed. + * + * @param addr network-order byte encoding of the address to look for + * @return name of the interface, or null if not found + */ + public String getInterface(byte[] addr) + { + try + { + InetAddress inetAddress = InetAddress.getByAddress(addr); + NetworkInterface intf = NetworkInterface.getByInetAddress(inetAddress); + if (intf != null) + { + return intf.getName(); + } + } + catch (UnknownHostException e) + { + e.printStackTrace(); + } + catch (SocketException e) + { + e.printStackTrace(); + } + return null; + } } diff --git a/src/libcharon/Android.mk b/src/libcharon/Android.mk index 9eb864f50..b2d6c3128 100644 --- a/src/libcharon/Android.mk +++ b/src/libcharon/Android.mk @@ -42,6 +42,7 @@ encoding/payloads/ts_payload.c encoding/payloads/ts_payload.h \ encoding/payloads/unknown_payload.c encoding/payloads/unknown_payload.h \ encoding/payloads/vendor_id_payload.c encoding/payloads/vendor_id_payload.h \ encoding/payloads/hash_payload.c encoding/payloads/hash_payload.h \ +encoding/payloads/fragment_payload.c encoding/payloads/fragment_payload.h \ kernel/kernel_handler.c kernel/kernel_handler.h \ network/receiver.c network/receiver.h network/sender.c network/sender.h \ network/socket.c network/socket.h \ diff --git a/src/libcharon/Makefile.am b/src/libcharon/Makefile.am index 368dbc1c9..5203890ff 100644 --- a/src/libcharon/Makefile.am +++ b/src/libcharon/Makefile.am @@ -40,6 +40,7 @@ encoding/payloads/ts_payload.c encoding/payloads/ts_payload.h \ encoding/payloads/unknown_payload.c encoding/payloads/unknown_payload.h \ encoding/payloads/vendor_id_payload.c encoding/payloads/vendor_id_payload.h \ encoding/payloads/hash_payload.c encoding/payloads/hash_payload.h \ +encoding/payloads/fragment_payload.c encoding/payloads/fragment_payload.h \ kernel/kernel_handler.c kernel/kernel_handler.h \ network/receiver.c network/receiver.h network/sender.c network/sender.h \ network/socket.c network/socket.h \ @@ -491,6 +492,13 @@ if MONOLITHIC endif endif +if USE_ERROR_NOTIFY + SUBDIRS += plugins/error_notify +if MONOLITHIC + libcharon_la_LIBADD += plugins/error_notify/libstrongswan-error-notify.la +endif +endif + if USE_CERTEXPIRE SUBDIRS += plugins/certexpire if MONOLITHIC diff --git a/src/libcharon/bus/bus.c b/src/libcharon/bus/bus.c index 144a06908..b5cdaaa89 100644 --- a/src/libcharon/bus/bus.c +++ b/src/libcharon/bus/bus.c @@ -720,6 +720,10 @@ METHOD(bus_t, authorize, bool, } enumerator->destroy(enumerator); this->mutex->unlock(this->mutex); + if (!success) + { + alert(this, ALERT_AUTHORIZATION_FAILED); + } return success; } diff --git a/src/libcharon/bus/bus.h b/src/libcharon/bus/bus.h index cc378b496..4645bbde6 100644 --- a/src/libcharon/bus/bus.h +++ b/src/libcharon/bus/bus.h @@ -86,6 +86,8 @@ enum alert_t { ALERT_RADIUS_NOT_RESPONDING, /** a shutdown signal has been received, argument is the signal (int) */ ALERT_SHUTDOWN_SIGNAL, + /** creating local authentication data failed, no arguments */ + ALERT_LOCAL_AUTH_FAILED, /** peer authentication failed, no arguments */ ALERT_PEER_AUTH_FAILED, /** failed to resolve peer address, no arguments */ @@ -105,6 +107,29 @@ enum alert_t { ALERT_RETRANSMIT_SEND_TIMEOUT, /** received a retransmit for a message, argument is message_t */ ALERT_RETRANSMIT_RECEIVE, + /** received half-open timeout before IKE_SA established, no argument */ + ALERT_HALF_OPEN_TIMEOUT, + /** IKE proposals do not match, argument is linked_list_t of proposal_t */ + ALERT_PROPOSAL_MISMATCH_IKE, + /** CHILD proposals do not match, argument is linked_list_t of proposal_t */ + ALERT_PROPOSAL_MISMATCH_CHILD, + /** traffic selectors do not match, arguments are two linked_list_t + * containing traffic_selector_t for initiator and for responder */ + ALERT_TS_MISMATCH, + /** Installation of IPsec SAs failed, argument is child_sa_t */ + ALERT_INSTALL_CHILD_SA_FAILED, + /** Installation of IPsec Policy failed, argument is child_sa_t */ + ALERT_INSTALL_CHILD_POLICY_FAILED, + /** IKE_SA deleted because of "replace" unique policy, no argument */ + ALERT_UNIQUE_REPLACE, + /** IKE_SA deleted because of "keep" unique policy, no argument */ + ALERT_UNIQUE_KEEP, + /** IKE_SA kept on failed child SA establishment, no argument */ + ALERT_KEEP_ON_CHILD_SA_FAILURE, + /** allocating virtual IP failed, linked_list_t of host_t requested */ + ALERT_VIP_FAILURE, + /** an authorize() hook failed, no argument */ + ALERT_AUTHORIZATION_FAILED, }; /** diff --git a/src/libcharon/config/ike_cfg.c b/src/libcharon/config/ike_cfg.c index 1006fc295..5e5fbba42 100644 --- a/src/libcharon/config/ike_cfg.c +++ b/src/libcharon/config/ike_cfg.c @@ -90,6 +90,11 @@ struct private_ike_cfg_t { bool force_encap; /** + * use IKEv1 fragmentation + */ + fragmentation_t fragmentation; + + /** * List of proposals to use */ linked_list_t *proposals; @@ -113,6 +118,12 @@ METHOD(ike_cfg_t, force_encap_, bool, return this->force_encap; } +METHOD(ike_cfg_t, fragmentation, fragmentation_t, + private_ike_cfg_t *this) +{ + return this->fragmentation; +} + METHOD(ike_cfg_t, get_my_addr, char*, private_ike_cfg_t *this, bool *allow_any) { @@ -268,6 +279,7 @@ METHOD(ike_cfg_t, equals, bool, this->version == other->version && this->certreq == other->certreq && this->force_encap == other->force_encap && + this->fragmentation == other->fragmentation && streq(this->me, other->me) && streq(this->other, other->other) && this->my_port == other->my_port && @@ -299,7 +311,8 @@ METHOD(ike_cfg_t, destroy, void, */ ike_cfg_t *ike_cfg_create(ike_version_t version, bool certreq, bool force_encap, char *me, bool my_allow_any, u_int16_t my_port, - char *other, bool other_allow_any, u_int16_t other_port) + char *other, bool other_allow_any, u_int16_t other_port, + fragmentation_t fragmentation) { private_ike_cfg_t *this; @@ -308,6 +321,7 @@ ike_cfg_t *ike_cfg_create(ike_version_t version, bool certreq, bool force_encap, .get_version = _get_version, .send_certreq = _send_certreq, .force_encap = _force_encap_, + .fragmentation = _fragmentation, .get_my_addr = _get_my_addr, .get_other_addr = _get_other_addr, .get_my_port = _get_my_port, @@ -324,6 +338,7 @@ ike_cfg_t *ike_cfg_create(ike_version_t version, bool certreq, bool force_encap, .version = version, .certreq = certreq, .force_encap = force_encap, + .fragmentation = fragmentation, .me = strdup(me), .other = strdup(other), .my_allow_any = my_allow_any, diff --git a/src/libcharon/config/ike_cfg.h b/src/libcharon/config/ike_cfg.h index fa2aaa325..5a7fae1e9 100644 --- a/src/libcharon/config/ike_cfg.h +++ b/src/libcharon/config/ike_cfg.h @@ -1,4 +1,5 @@ /* + * Copyright (C) 2012 Tobias Brunner * Copyright (C) 2005-2007 Martin Willi * Copyright (C) 2005 Jan Hutter * Hochschule fuer Technik Rapperswil @@ -23,6 +24,7 @@ #define IKE_CFG_H_ typedef enum ike_version_t ike_version_t; +typedef enum fragmentation_t fragmentation_t; typedef struct ike_cfg_t ike_cfg_t; #include <library.h> @@ -45,6 +47,18 @@ enum ike_version_t { }; /** + * Proprietary IKEv1 fragmentation + */ +enum fragmentation_t { + /** disable fragmentation */ + FRAGMENTATION_NO, + /** enable fragmentation if supported by peer */ + FRAGMENTATION_YES, + /** force use of fragmentation (even for the first message) */ + FRAGMENTATION_FORCE, +}; + +/** * enum strings fro ike_version_t */ extern enum_name_t *ike_version_names; @@ -134,11 +148,18 @@ struct ike_cfg_t { /** * Enforce UDP encapsulation by faking NATD notifies? * - * @return TRUE to enfoce UDP encapsulation + * @return TRUE to enforce UDP encapsulation */ bool (*force_encap) (ike_cfg_t *this); /** + * Use proprietary IKEv1 fragmentation + * + * @return TRUE to use fragmentation + */ + fragmentation_t (*fragmentation) (ike_cfg_t *this); + + /** * Get the DH group to use for IKE_SA setup. * * @return dh group to use for initialization @@ -183,10 +204,12 @@ struct ike_cfg_t { * @param other address/DNS name of remote peer * @param other_allow_any allow override of remote address by any address * @param other_port IKE port to use as dest, 500 uses IKEv2 port floating + * @param fragmentation use IKEv1 fragmentation * @return ike_cfg_t object. */ ike_cfg_t *ike_cfg_create(ike_version_t version, bool certreq, bool force_encap, char *me, bool my_allow_any, u_int16_t my_port, - char *other, bool other_allow_any, u_int16_t other_port); + char *other, bool other_allow_any, u_int16_t other_port, + fragmentation_t fragmentation); #endif /** IKE_CFG_H_ @}*/ diff --git a/src/libcharon/encoding/message.c b/src/libcharon/encoding/message.c index 5a59b6c09..28fdda735 100644 --- a/src/libcharon/encoding/message.c +++ b/src/libcharon/encoding/message.c @@ -437,10 +437,12 @@ static payload_rule_t id_prot_i_rules[] = { {VENDOR_ID_V1, 0, MAX_VID_PAYLOADS, FALSE, FALSE}, {CERTIFICATE_REQUEST_V1, 0, MAX_CERTREQ_PAYLOADS, FALSE, FALSE}, {NAT_D_V1, 0, MAX_NAT_D_PAYLOADS, FALSE, FALSE}, + {NAT_D_DRAFT_00_03_V1, 0, MAX_NAT_D_PAYLOADS, FALSE, FALSE}, {ID_V1, 0, 1, TRUE, FALSE}, {CERTIFICATE_V1, 0, 2, TRUE, FALSE}, {SIGNATURE_V1, 0, 1, TRUE, FALSE}, {HASH_V1, 0, 1, TRUE, FALSE}, + {FRAGMENT_V1, 0, 1, FALSE, TRUE}, }; /** @@ -459,6 +461,8 @@ static payload_order_t id_prot_i_order[] = { {NOTIFY_V1, 0}, {VENDOR_ID_V1, 0}, {NAT_D_V1, 0}, + {NAT_D_DRAFT_00_03_V1, 0}, + {FRAGMENT_V1, 0}, }; /** @@ -473,10 +477,12 @@ static payload_rule_t id_prot_r_rules[] = { {VENDOR_ID_V1, 0, MAX_VID_PAYLOADS, FALSE, FALSE}, {CERTIFICATE_REQUEST_V1, 0, MAX_CERTREQ_PAYLOADS, FALSE, FALSE}, {NAT_D_V1, 0, MAX_NAT_D_PAYLOADS, FALSE, FALSE}, + {NAT_D_DRAFT_00_03_V1, 0, MAX_NAT_D_PAYLOADS, FALSE, FALSE}, {ID_V1, 0, 1, TRUE, FALSE}, {CERTIFICATE_V1, 0, 2, TRUE, FALSE}, {SIGNATURE_V1, 0, 1, TRUE, FALSE}, {HASH_V1, 0, 1, TRUE, FALSE}, + {FRAGMENT_V1, 0, 1, FALSE, TRUE}, }; /** @@ -495,6 +501,8 @@ static payload_order_t id_prot_r_order[] = { {NOTIFY_V1, 0}, {VENDOR_ID_V1, 0}, {NAT_D_V1, 0}, + {NAT_D_DRAFT_00_03_V1, 0}, + {FRAGMENT_V1, 0}, }; /** @@ -509,10 +517,12 @@ static payload_rule_t aggressive_i_rules[] = { {VENDOR_ID_V1, 0, MAX_VID_PAYLOADS, FALSE, FALSE}, {CERTIFICATE_REQUEST_V1, 0, MAX_CERTREQ_PAYLOADS, FALSE, FALSE}, {NAT_D_V1, 0, MAX_NAT_D_PAYLOADS, FALSE, FALSE}, + {NAT_D_DRAFT_00_03_V1, 0, MAX_NAT_D_PAYLOADS, FALSE, FALSE}, {ID_V1, 0, 1, FALSE, FALSE}, {CERTIFICATE_V1, 0, 1, TRUE, FALSE}, {SIGNATURE_V1, 0, 1, TRUE, FALSE}, {HASH_V1, 0, 1, TRUE, FALSE}, + {FRAGMENT_V1, 0, 1, FALSE, TRUE}, }; /** @@ -526,11 +536,13 @@ static payload_order_t aggressive_i_order[] = { {ID_V1, 0}, {CERTIFICATE_V1, 0}, {NAT_D_V1, 0}, + {NAT_D_DRAFT_00_03_V1, 0}, {SIGNATURE_V1, 0}, {HASH_V1, 0}, {CERTIFICATE_REQUEST_V1, 0}, {NOTIFY_V1, 0}, {VENDOR_ID_V1, 0}, + {FRAGMENT_V1, 0}, }; /** @@ -545,10 +557,12 @@ static payload_rule_t aggressive_r_rules[] = { {VENDOR_ID_V1, 0, MAX_VID_PAYLOADS, FALSE, FALSE}, {CERTIFICATE_REQUEST_V1, 0, MAX_CERTREQ_PAYLOADS, FALSE, FALSE}, {NAT_D_V1, 0, MAX_NAT_D_PAYLOADS, FALSE, FALSE}, + {NAT_D_DRAFT_00_03_V1, 0, MAX_NAT_D_PAYLOADS, FALSE, FALSE}, {ID_V1, 0, 1, FALSE, FALSE}, {CERTIFICATE_V1, 0, 1, FALSE, FALSE}, {SIGNATURE_V1, 0, 1, FALSE, FALSE}, {HASH_V1, 0, 1, FALSE, FALSE}, + {FRAGMENT_V1, 0, 1, FALSE, TRUE}, }; /** @@ -562,11 +576,13 @@ static payload_order_t aggressive_r_order[] = { {ID_V1, 0}, {CERTIFICATE_V1, 0}, {NAT_D_V1, 0}, + {NAT_D_DRAFT_00_03_V1, 0}, {SIGNATURE_V1, 0}, {HASH_V1, 0}, {CERTIFICATE_REQUEST_V1, 0}, {NOTIFY_V1, 0}, {VENDOR_ID_V1, 0}, + {FRAGMENT_V1, 0}, }; /** @@ -624,6 +640,7 @@ static payload_rule_t quick_mode_i_rules[] = { {KEY_EXCHANGE_V1, 0, 1, TRUE, FALSE}, {ID_V1, 0, 2, TRUE, FALSE}, {NAT_OA_V1, 0, 2, TRUE, FALSE}, + {NAT_OA_DRAFT_00_03_V1, 0, 2, TRUE, FALSE}, }; /** @@ -639,6 +656,7 @@ static payload_order_t quick_mode_i_order[] = { {KEY_EXCHANGE_V1, 0}, {ID_V1, 0}, {NAT_OA_V1, 0}, + {NAT_OA_DRAFT_00_03_V1, 0}, }; /** @@ -654,6 +672,7 @@ static payload_rule_t quick_mode_r_rules[] = { {KEY_EXCHANGE_V1, 0, 1, TRUE, FALSE}, {ID_V1, 0, 2, TRUE, FALSE}, {NAT_OA_V1, 0, 2, TRUE, FALSE}, + {NAT_OA_DRAFT_00_03_V1, 0, 2, TRUE, FALSE}, }; /** @@ -669,6 +688,7 @@ static payload_order_t quick_mode_r_order[] = { {KEY_EXCHANGE_V1, 0}, {ID_V1, 0}, {NAT_OA_V1, 0}, + {NAT_OA_DRAFT_00_03_V1, 0}, }; /** @@ -1681,6 +1701,12 @@ METHOD(message_t, parse_header, status_t, } this->first_payload = ike_header->payload_interface.get_next_type( &ike_header->payload_interface); + if (this->first_payload == FRAGMENT_V1 && this->is_encrypted) + { /* racoon sets the encryted bit when sending a fragment, but these + * messages are really not encrypted */ + this->is_encrypted = FALSE; + } + for (i = 0; i < countof(this->reserved); i++) { reserved = payload_get_field(&ike_header->payload_interface, diff --git a/src/libcharon/encoding/payloads/cert_payload.c b/src/libcharon/encoding/payloads/cert_payload.c index 3a230b91e..a32f5705d 100644 --- a/src/libcharon/encoding/payloads/cert_payload.c +++ b/src/libcharon/encoding/payloads/cert_payload.c @@ -234,6 +234,23 @@ METHOD(cert_payload_t, get_cert, certificate_t*, BUILD_BLOB_ASN1_DER, this->data, BUILD_END); } +METHOD(cert_payload_t, get_container, container_t*, + private_cert_payload_t *this) +{ + int type; + + switch (this->encoding) + { + case ENC_PKCS7_WRAPPED_X509: + type = CONTAINER_PKCS7; + break; + default: + return NULL; + } + return lib->creds->create(lib->creds, CRED_CONTAINER, type, + BUILD_BLOB_ASN1_DER, this->data, BUILD_END); +} + METHOD(cert_payload_t, get_hash, chunk_t, private_cert_payload_t *this) { @@ -289,6 +306,7 @@ cert_payload_t *cert_payload_create(payload_type_t type) .destroy = _destroy, }, .get_cert = _get_cert, + .get_container = _get_container, .get_cert_encoding = _get_cert_encoding, .get_hash = _get_hash, .get_url = _get_url, diff --git a/src/libcharon/encoding/payloads/cert_payload.h b/src/libcharon/encoding/payloads/cert_payload.h index 19ed2ccd2..834f35d60 100644 --- a/src/libcharon/encoding/payloads/cert_payload.h +++ b/src/libcharon/encoding/payloads/cert_payload.h @@ -28,10 +28,11 @@ typedef enum cert_encoding_t cert_encoding_t; #include <library.h> #include <credentials/certificates/certificate.h> +#include <credentials/containers/container.h> #include <encoding/payloads/payload.h> /** - * Certifcate encodings, as in RFC4306 + * Certificate encodings, as in RFC4306 */ enum cert_encoding_t { ENC_PKCS7_WRAPPED_X509 = 1, @@ -65,13 +66,20 @@ struct cert_payload_t { payload_t payload_interface; /** - * Get the playoads encoded certifcate. + * Get the payloads encoded certificate. * - * @return certifcate copy + * @return certificate copy */ certificate_t *(*get_cert)(cert_payload_t *this); /** + * Get the payloads certificate container. + * + * @return container copy + */ + container_t *(*get_container)(cert_payload_t *this); + + /** * Get the encoding of the certificate. * * @return encoding diff --git a/src/libcharon/encoding/payloads/certreq_payload.h b/src/libcharon/encoding/payloads/certreq_payload.h index cce71c0ad..2915decf3 100644 --- a/src/libcharon/encoding/payloads/certreq_payload.h +++ b/src/libcharon/encoding/payloads/certreq_payload.h @@ -56,7 +56,7 @@ struct certreq_payload_t { /** * Add a certificates keyid to the payload (IKEv2 only). * - * @param keyid keyid of the trusted certifcate + * @param keyid keyid of the trusted certificate * @return */ void (*add_keyid)(certreq_payload_t *this, chunk_t keyid); diff --git a/src/libcharon/encoding/payloads/eap_payload.c b/src/libcharon/encoding/payloads/eap_payload.c index dd2e25795..f2f35aa69 100644 --- a/src/libcharon/encoding/payloads/eap_payload.c +++ b/src/libcharon/encoding/payloads/eap_payload.c @@ -410,14 +410,15 @@ eap_payload_t *eap_payload_create_nak(u_int8_t identifier, eap_type_t type, eap_type_t reg_type; u_int32_t reg_vendor; bio_writer_t *writer; - chunk_t length, data; + chunk_t data; bool added_any = FALSE, found_vendor = FALSE; eap_payload_t *payload; writer = bio_writer_create(12); writer->write_uint8(writer, EAP_RESPONSE); writer->write_uint8(writer, identifier); - length = writer->skip(writer, 2); + /* write zero length, we update it once we know the length */ + writer->write_uint16(writer, 0); write_type(writer, EAP_NAK, 0, expanded); @@ -453,10 +454,9 @@ eap_payload_t *eap_payload_create_nak(u_int8_t identifier, eap_type_t type, /* set length */ data = writer->get_buf(writer); - htoun16(length.ptr, data.len); + htoun16(data.ptr + offsetof(eap_packet_t, length), data.len); payload = eap_payload_create_data(data); writer->destroy(writer); return payload; } - diff --git a/src/libcharon/encoding/payloads/fragment_payload.c b/src/libcharon/encoding/payloads/fragment_payload.c new file mode 100644 index 000000000..1a6b3234b --- /dev/null +++ b/src/libcharon/encoding/payloads/fragment_payload.c @@ -0,0 +1,225 @@ +/* + * Copyright (C) 2012 Tobias Brunner + * 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 + * 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. + */ + +#include "fragment_payload.h" + +#include <encoding/payloads/encodings.h> + +/** Flag that is set in case the given fragment is the last for the message */ +#define LAST_FRAGMENT 0x01 + +typedef struct private_fragment_payload_t private_fragment_payload_t; + +/** + * Private data of an fragment_payload_t object. + */ +struct private_fragment_payload_t { + + /** + * Public fragment_payload_t interface. + */ + fragment_payload_t public; + + /** + * Next payload type. + */ + u_int8_t next_payload; + + /** + * Reserved byte + */ + u_int8_t reserved; + + /** + * Length of this payload. + */ + u_int16_t payload_length; + + /** + * Fragment ID. + */ + u_int16_t fragment_id; + + /** + * Fragment number. + */ + u_int8_t fragment_number; + + /** + * Flags + */ + u_int8_t flags; + + /** + * The contained fragment data. + */ + chunk_t data; +}; + +/** + * Encoding rules for an IKEv1 fragment payload + */ +static encoding_rule_t encodings[] = { + /* 1 Byte next payload type, stored in the field next_payload */ + { U_INT_8, offsetof(private_fragment_payload_t, next_payload) }, + { RESERVED_BYTE, offsetof(private_fragment_payload_t, reserved) }, + /* Length of the whole payload*/ + { PAYLOAD_LENGTH, offsetof(private_fragment_payload_t, payload_length) }, + { U_INT_16, offsetof(private_fragment_payload_t, fragment_id) }, + { U_INT_8, offsetof(private_fragment_payload_t, fragment_number) }, + { U_INT_8, offsetof(private_fragment_payload_t, flags) }, + /* Fragment data is of variable size */ + { CHUNK_DATA, offsetof(private_fragment_payload_t, data) }, +}; + +/* + 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ! Next Payload ! RESERVED ! Payload Length ! + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ! Fragment ID ! Fragment Num ! Flags ! + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ! ! + ~ Fragment Data ~ + ! ! + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +*/ + +METHOD(payload_t, verify, status_t, + private_fragment_payload_t *this) +{ + if (this->fragment_number == 0) + { + return FAILED; + } + return SUCCESS; +} + +METHOD(payload_t, get_encoding_rules, int, + private_fragment_payload_t *this, encoding_rule_t **rules) +{ + *rules = encodings; + return countof(encodings); +} + +METHOD(payload_t, get_header_length, int, + private_fragment_payload_t *this) +{ + return 8; +} + +METHOD(payload_t, get_type, payload_type_t, + private_fragment_payload_t *this) +{ + return FRAGMENT_V1; +} + +METHOD(payload_t, get_next_type, payload_type_t, + private_fragment_payload_t *this) +{ + return this->next_payload; +} + +METHOD(payload_t, set_next_type, void, + private_fragment_payload_t *this, payload_type_t type) +{ + this->next_payload = type; +} + +METHOD(payload_t, get_length, size_t, + private_fragment_payload_t *this) +{ + return this->payload_length; +} + +METHOD(fragment_payload_t, get_id, u_int16_t, + private_fragment_payload_t *this) +{ + return this->fragment_id; +} + +METHOD(fragment_payload_t, get_number, u_int8_t, + private_fragment_payload_t *this) +{ + return this->fragment_number; +} + +METHOD(fragment_payload_t, is_last, bool, + private_fragment_payload_t *this) +{ + return (this->flags & LAST_FRAGMENT) == LAST_FRAGMENT; +} + +METHOD(fragment_payload_t, get_data, chunk_t, + private_fragment_payload_t *this) +{ + return this->data; +} + +METHOD2(payload_t, fragment_payload_t, destroy, void, + private_fragment_payload_t *this) +{ + free(this->data.ptr); + free(this); +} + +/* + * Described in header + */ +fragment_payload_t *fragment_payload_create() +{ + private_fragment_payload_t *this; + + INIT(this, + .public = { + .payload_interface = { + .verify = _verify, + .get_encoding_rules = _get_encoding_rules, + .get_header_length = _get_header_length, + .get_length = _get_length, + .get_next_type = _get_next_type, + .set_next_type = _set_next_type, + .get_type = _get_type, + .destroy = _destroy, + }, + .get_id = _get_id, + .get_number = _get_number, + .is_last = _is_last, + .get_data = _get_data, + .destroy = _destroy, + }, + .next_payload = NO_PAYLOAD, + ); + this->payload_length = get_header_length(this); + return &this->public; +} + +/* + * Described in header + */ +fragment_payload_t *fragment_payload_create_from_data(u_int8_t num, bool last, + chunk_t data) +{ + private_fragment_payload_t *this; + + this = (private_fragment_payload_t*)fragment_payload_create(); + this->fragment_id = 1; + this->fragment_number = num; + this->flags |= (last ? LAST_FRAGMENT : 0); + this->data = chunk_clone(data); + this->payload_length = get_header_length(this) + data.len; + return &this->public; +}
\ No newline at end of file diff --git a/src/libcharon/encoding/payloads/fragment_payload.h b/src/libcharon/encoding/payloads/fragment_payload.h new file mode 100644 index 000000000..a49cf32dd --- /dev/null +++ b/src/libcharon/encoding/payloads/fragment_payload.h @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2012 Tobias Brunner + * 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 + * 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. + */ + +/** + * @defgroup fragment_payload fragment_payload + * @{ @ingroup payloads + */ + +#ifndef FRAGMENT_PAYLOAD_H_ +#define FRAGMENT_PAYLOAD_H_ + +typedef struct fragment_payload_t fragment_payload_t; + +#include <library.h> +#include <encoding/payloads/payload.h> + +/** + * Object representing an IKEv1 fragment payload. + */ +struct fragment_payload_t { + + /** + * The payload_t interface. + */ + payload_t payload_interface; + + /** + * Get the fragment ID. Identifies the fragments for a particular IKE + * message. + * + * @return fragment ID + */ + u_int16_t (*get_id)(fragment_payload_t *this); + + /** + * Get the fragment number. Defines the order of the fragments. + * + * @return fragment number + */ + u_int8_t (*get_number)(fragment_payload_t *this); + + /** + * Check if this is the last fragment. + * + * @return TRUE if this is the last fragment + */ + bool (*is_last)(fragment_payload_t *this); + + /** + * Get the fragment data. + * + * @return chunkt to internal fragment data + */ + chunk_t (*get_data)(fragment_payload_t *this); + + /** + * Destroys an fragment_payload_t object. + */ + void (*destroy)(fragment_payload_t *this); +}; + +/** + * Creates an empty fragment_payload_t object. + * + * @return fragment_payload_t object + */ +fragment_payload_t *fragment_payload_create(); + +/** + * Creates a fragment payload from the given data. All fragments currently + * have the same fragment ID (1), which seems what other implementations are + * doing. + * + * @param num fragment number (first one should be 1) + * @param last TRUE to indicate that this is the last fragment + * @param data fragment data (gets cloned) + * @return fragment_payload_t object + */ +fragment_payload_t *fragment_payload_create_from_data(u_int8_t num, bool last, + chunk_t data); + +#endif /** FRAGMENT_PAYLOAD_H_ @}*/ diff --git a/src/libcharon/encoding/payloads/id_payload.c b/src/libcharon/encoding/payloads/id_payload.c index 02b07d691..7470bb3b4 100644 --- a/src/libcharon/encoding/payloads/id_payload.c +++ b/src/libcharon/encoding/payloads/id_payload.c @@ -165,7 +165,7 @@ METHOD(payload_t, verify, status_t, { bool bad_length = FALSE; - if (this->type == NAT_OA_V1 && + if ((this->type == NAT_OA_V1 || this->type == NAT_OA_DRAFT_00_03_V1) && this->id_type != ID_IPV4_ADDR && this->id_type != ID_IPV6_ADDR) { DBG1(DBG_ENC, "invalid ID type %N for %N payload", id_type_names, @@ -195,7 +195,8 @@ METHOD(payload_t, verify, status_t, METHOD(payload_t, get_encoding_rules, int, private_id_payload_t *this, encoding_rule_t **rules) { - if (this->type == ID_V1 || this->type == NAT_OA_V1) + if (this->type == ID_V1 || + this->type == NAT_OA_V1 || this->type == NAT_OA_DRAFT_00_03_V1) { *rules = encodings_v1; return countof(encodings_v1); diff --git a/src/libcharon/encoding/payloads/ike_header.h b/src/libcharon/encoding/payloads/ike_header.h index e6b7d0dff..d9a44dd0c 100644 --- a/src/libcharon/encoding/payloads/ike_header.h +++ b/src/libcharon/encoding/payloads/ike_header.h @@ -72,7 +72,7 @@ enum exchange_type_t{ AUTH_ONLY = 3, /** - * Aggresive (Aggressive mode) + * Aggressive (Aggressive mode) */ AGGRESSIVE = 4, diff --git a/src/libcharon/encoding/payloads/payload.c b/src/libcharon/encoding/payloads/payload.c index dc158476b..f9dd33edb 100644 --- a/src/libcharon/encoding/payloads/payload.c +++ b/src/libcharon/encoding/payloads/payload.c @@ -36,6 +36,7 @@ #include <encoding/payloads/configuration_attribute.h> #include <encoding/payloads/eap_payload.h> #include <encoding/payloads/hash_payload.h> +#include <encoding/payloads/fragment_payload.h> #include <encoding/payloads/unknown_payload.h> ENUM_BEGIN(payload_type_names, NO_PAYLOAD, NO_PAYLOAD, @@ -79,20 +80,17 @@ ENUM_NEXT(payload_type_names, SECURITY_ASSOCIATION, GENERIC_SECURE_PASSWORD_METH #ifdef ME ENUM_NEXT(payload_type_names, ID_PEER, ID_PEER, GENERIC_SECURE_PASSWORD_METHOD, "ID_PEER"); -ENUM_NEXT(payload_type_names, HEADER, ENCRYPTED_V1, ID_PEER, - "HEADER", - "PROPOSAL_SUBSTRUCTURE", - "PROPOSAL_SUBSTRUCTURE_V1", - "TRANSFORM_SUBSTRUCTURE", - "TRANSFORM_SUBSTRUCTURE_V1", - "TRANSFORM_ATTRIBUTE", - "TRANSFORM_ATTRIBUTE_V1", - "TRAFFIC_SELECTOR_SUBSTRUCTURE", - "CONFIGURATION_ATTRIBUTE", - "CONFIGURATION_ATTRIBUTE_V1", - "ENCRYPTED_V1"); +ENUM_NEXT(payload_type_names, NAT_D_DRAFT_00_03_V1, FRAGMENT_V1, ID_PEER, + "NAT_D_DRAFT_V1", + "NAT_OA_DRAFT_V1", + "FRAGMENT"); #else -ENUM_NEXT(payload_type_names, HEADER, ENCRYPTED_V1, GENERIC_SECURE_PASSWORD_METHOD, +ENUM_NEXT(payload_type_names, NAT_D_DRAFT_00_03_V1, FRAGMENT_V1, GENERIC_SECURE_PASSWORD_METHOD, + "NAT_D_DRAFT_V1", + "NAT_OA_DRAFT_V1", + "FRAGMENT"); +#endif /* ME */ +ENUM_NEXT(payload_type_names, HEADER, ENCRYPTED_V1, FRAGMENT_V1, "HEADER", "PROPOSAL_SUBSTRUCTURE", "PROPOSAL_SUBSTRUCTURE_V1", @@ -104,7 +102,6 @@ ENUM_NEXT(payload_type_names, HEADER, ENCRYPTED_V1, GENERIC_SECURE_PASSWORD_METH "CONFIGURATION_ATTRIBUTE", "CONFIGURATION_ATTRIBUTE_V1", "ENCRYPTED_V1"); -#endif /* ME */ ENUM_END(payload_type_names, ENCRYPTED_V1); /* short forms of payload names */ @@ -147,23 +144,19 @@ ENUM_NEXT(payload_type_short_names, SECURITY_ASSOCIATION, GENERIC_SECURE_PASSWOR "EAP", "GSPM"); #ifdef ME -ENUM_NEXT(payload_type_short_names, ID_PEER, ID_PEER, - GENERIC_SECURE_PASSWORD_METHOD, +ENUM_NEXT(payload_type_short_names, ID_PEER, ID_PEER, GENERIC_SECURE_PASSWORD_METHOD, "IDp"); -ENUM_NEXT(payload_type_short_names, HEADER, ENCRYPTED_V1, ID_PEER, - "HDR", - "PROP", - "PROP", - "TRANS", - "TRANS", - "TRANSATTR", - "TRANSATTR", - "TSSUB", - "CATTR", - "CATTR", - "E"); +ENUM_NEXT(payload_type_short_names, NAT_D_DRAFT_00_03_V1, FRAGMENT_V1, ID_PEER, + "NAT-D", + "NAT-OA", + "FRAG"); #else -ENUM_NEXT(payload_type_short_names, HEADER, ENCRYPTED_V1, GENERIC_SECURE_PASSWORD_METHOD, +ENUM_NEXT(payload_type_short_names, NAT_D_DRAFT_00_03_V1, FRAGMENT_V1, GENERIC_SECURE_PASSWORD_METHOD, + "NAT-D", + "NAT-OA", + "FRAG"); +#endif /* ME */ +ENUM_NEXT(payload_type_short_names, HEADER, ENCRYPTED_V1, FRAGMENT_V1, "HDR", "PROP", "PROP", @@ -175,7 +168,6 @@ ENUM_NEXT(payload_type_short_names, HEADER, ENCRYPTED_V1, GENERIC_SECURE_PASSWOR "CATTR", "CATTR", "E"); -#endif /* ME */ ENUM_END(payload_type_short_names, ENCRYPTED_V1); /* @@ -206,6 +198,7 @@ payload_t *payload_create(payload_type_t type) case ID_RESPONDER: case ID_V1: case NAT_OA_V1: + case NAT_OA_DRAFT_00_03_V1: #ifdef ME case ID_PEER: #endif /* ME */ @@ -239,6 +232,7 @@ payload_t *payload_create(payload_type_t type) case HASH_V1: case SIGNATURE_V1: case NAT_D_V1: + case NAT_D_DRAFT_00_03_V1: return (payload_t*)hash_payload_create(type); case CONFIGURATION: case CONFIGURATION_V1: @@ -251,6 +245,8 @@ payload_t *payload_create(payload_type_t type) case ENCRYPTED: case ENCRYPTED_V1: return (payload_t*)encryption_payload_create(type); + case FRAGMENT_V1: + return (payload_t*)fragment_payload_create(); default: return (payload_t*)unknown_payload_create(type); } @@ -283,6 +279,10 @@ bool payload_is_known(payload_type_t type) return TRUE; } #endif + if (type >= NAT_D_DRAFT_00_03_V1 && type <= FRAGMENT_V1) + { + return TRUE; + } return FALSE; } diff --git a/src/libcharon/encoding/payloads/payload.h b/src/libcharon/encoding/payloads/payload.h index d5e862601..0e8a9267b 100644 --- a/src/libcharon/encoding/payloads/payload.h +++ b/src/libcharon/encoding/payloads/payload.h @@ -123,7 +123,7 @@ enum payload_type_t { NAT_D_V1 = 20, /** - * NAT original address payload (NAT-OA) + * NAT original address payload (NAT-OA). */ NAT_OA_V1 = 21, @@ -221,6 +221,21 @@ enum payload_type_t { #endif /* ME */ /** + * NAT discovery payload (NAT-D) (drafts). + */ + NAT_D_DRAFT_00_03_V1 = 130, + + /** + * NAT original address payload (NAT-OA) (drafts). + */ + NAT_OA_DRAFT_00_03_V1 = 131, + + /** + * IKE fragment (proprietary IKEv1 extension) + */ + FRAGMENT_V1 = 132, + + /** * Header has a value of PRIVATE USE space. * * This type and all the following are never sent over wire and are diff --git a/src/libcharon/encoding/payloads/proposal_substructure.c b/src/libcharon/encoding/payloads/proposal_substructure.c index 1616e8283..ae0fce991 100644 --- a/src/libcharon/encoding/payloads/proposal_substructure.c +++ b/src/libcharon/encoding/payloads/proposal_substructure.c @@ -253,6 +253,8 @@ typedef enum { IKEV1_ENCAP_TRANSPORT = 2, IKEV1_ENCAP_UDP_TUNNEL = 3, IKEV1_ENCAP_UDP_TRANSPORT = 4, + IKEV1_ENCAP_UDP_TUNNEL_DRAFT_00_03 = 61443, + IKEV1_ENCAP_UDP_TRANSPORT_DRAFT_00_03 = 61444, } ikev1_esp_encap_t; /** @@ -810,14 +812,30 @@ static u_int16_t get_ikev1_auth(auth_method_t method) /** * Get IKEv1 encapsulation mode */ -static u_int16_t get_ikev1_mode(ipsec_mode_t mode, bool udp) +static u_int16_t get_ikev1_mode(ipsec_mode_t mode, encap_t udp) { switch (mode) { case MODE_TUNNEL: - return udp ? IKEV1_ENCAP_UDP_TUNNEL : IKEV1_ENCAP_TUNNEL; + switch (udp) + { + case ENCAP_UDP: + return IKEV1_ENCAP_UDP_TUNNEL; + case ENCAP_UDP_DRAFT_00_03: + return IKEV1_ENCAP_UDP_TUNNEL_DRAFT_00_03; + default: + return IKEV1_ENCAP_TUNNEL; + } case MODE_TRANSPORT: - return udp ? IKEV1_ENCAP_UDP_TRANSPORT : IKEV1_ENCAP_TRANSPORT; + switch (udp) + { + case ENCAP_UDP: + return IKEV1_ENCAP_UDP_TRANSPORT; + case ENCAP_UDP_DRAFT_00_03: + return IKEV1_ENCAP_UDP_TRANSPORT_DRAFT_00_03; + default: + return IKEV1_ENCAP_TRANSPORT; + } default: return IKEV1_ENCAP_TUNNEL; } @@ -1125,9 +1143,11 @@ METHOD(proposal_substructure_t, get_encap_mode, ipsec_mode_t, case IKEV1_ENCAP_TUNNEL: return MODE_TUNNEL; case IKEV1_ENCAP_UDP_TRANSPORT: + case IKEV1_ENCAP_UDP_TRANSPORT_DRAFT_00_03: *udp = TRUE; return MODE_TRANSPORT; case IKEV1_ENCAP_UDP_TUNNEL: + case IKEV1_ENCAP_UDP_TUNNEL_DRAFT_00_03: *udp = TRUE; return MODE_TUNNEL; default: @@ -1263,7 +1283,7 @@ static void set_from_proposal_v1_ike(private_proposal_substructure_t *this, */ static void set_from_proposal_v1_esp(private_proposal_substructure_t *this, proposal_t *proposal, u_int32_t lifetime, u_int64_t lifebytes, - ipsec_mode_t mode, bool udp, int number) + ipsec_mode_t mode, encap_t udp, int number) { transform_substructure_t *transform = NULL; u_int16_t alg, key_size; @@ -1459,7 +1479,7 @@ proposal_substructure_t *proposal_substructure_create_from_proposal_v2( */ proposal_substructure_t *proposal_substructure_create_from_proposal_v1( proposal_t *proposal, u_int32_t lifetime, u_int64_t lifebytes, - auth_method_t auth, ipsec_mode_t mode, bool udp) + auth_method_t auth, ipsec_mode_t mode, encap_t udp) { private_proposal_substructure_t *this; @@ -1487,7 +1507,7 @@ proposal_substructure_t *proposal_substructure_create_from_proposal_v1( */ proposal_substructure_t *proposal_substructure_create_from_proposals_v1( linked_list_t *proposals, u_int32_t lifetime, u_int64_t lifebytes, - auth_method_t auth, ipsec_mode_t mode, bool udp) + auth_method_t auth, ipsec_mode_t mode, encap_t udp) { private_proposal_substructure_t *this = NULL; enumerator_t *enumerator; @@ -1531,7 +1551,7 @@ proposal_substructure_t *proposal_substructure_create_from_proposals_v1( */ proposal_substructure_t *proposal_substructure_create_for_ipcomp_v1( u_int32_t lifetime, u_int64_t lifebytes, u_int16_t cpi, - ipsec_mode_t mode, bool udp, u_int8_t proposal_number) + ipsec_mode_t mode, encap_t udp, u_int8_t proposal_number) { private_proposal_substructure_t *this; transform_substructure_t *transform; diff --git a/src/libcharon/encoding/payloads/proposal_substructure.h b/src/libcharon/encoding/payloads/proposal_substructure.h index bfa8b9daa..c8e7adfd8 100644 --- a/src/libcharon/encoding/payloads/proposal_substructure.h +++ b/src/libcharon/encoding/payloads/proposal_substructure.h @@ -23,6 +23,7 @@ #ifndef PROPOSAL_SUBSTRUCTURE_H_ #define PROPOSAL_SUBSTRUCTURE_H_ +typedef enum encap_t encap_t; typedef struct proposal_substructure_t proposal_substructure_t; #include <library.h> @@ -34,6 +35,15 @@ typedef struct proposal_substructure_t proposal_substructure_t; #include <sa/authenticator.h> /** + * Encap type for proposal substructure + */ +enum encap_t { + ENCAP_NONE = 0, + ENCAP_UDP, + ENCAP_UDP_DRAFT_00_03, +}; + +/** * Class representing an IKEv1/IKEv2 proposal substructure. */ struct proposal_substructure_t { @@ -179,12 +189,12 @@ proposal_substructure_t *proposal_substructure_create_from_proposal_v2( * @param lifebytes lifebytes, in bytes * @param auth authentication method to use, or AUTH_NONE * @param mode IPsec encapsulation mode, TRANSPORT or TUNNEL - * @param udp TRUE to use UDP encapsulation + * @param udp ENCAP_UDP to use UDP encapsulation * @return proposal_substructure_t object PROPOSAL_SUBSTRUCTURE_V1 */ proposal_substructure_t *proposal_substructure_create_from_proposal_v1( proposal_t *proposal, u_int32_t lifetime, u_int64_t lifebytes, - auth_method_t auth, ipsec_mode_t mode, bool udp); + auth_method_t auth, ipsec_mode_t mode, encap_t udp); /** * Creates an IKEv1 proposal_substructure_t from a list of proposal_t. @@ -194,12 +204,12 @@ proposal_substructure_t *proposal_substructure_create_from_proposal_v1( * @param lifebytes lifebytes, in bytes * @param auth authentication method to use, or AUTH_NONE * @param mode IPsec encapsulation mode, TRANSPORT or TUNNEL - * @param udp TRUE to use UDP encapsulation + * @param udp ENCAP_UDP to use UDP encapsulation * @return IKEv1 proposal_substructure_t PROPOSAL_SUBSTRUCTURE_V1 */ proposal_substructure_t *proposal_substructure_create_from_proposals_v1( linked_list_t *proposals, u_int32_t lifetime, u_int64_t lifebytes, - auth_method_t auth, ipsec_mode_t mode, bool udp); + auth_method_t auth, ipsec_mode_t mode, encap_t udp); /** * Creates an IKEv1 proposal_substructure_t for IPComp with the given @@ -209,12 +219,12 @@ proposal_substructure_t *proposal_substructure_create_from_proposals_v1( * @param lifebytes lifebytes, in bytes * @param cpi the CPI to be used * @param mode IPsec encapsulation mode, TRANSPORT or TUNNEL - * @param udp TRUE to use UDP encapsulation + * @param udp ENCAP_UDP to use UDP encapsulation * @param proposal_number the proposal number of the proposal to be linked * @return IKEv1 proposal_substructure_t PROPOSAL_SUBSTRUCTURE_V1 */ proposal_substructure_t *proposal_substructure_create_for_ipcomp_v1( u_int32_t lifetime, u_int64_t lifebytes, u_int16_t cpi, - ipsec_mode_t mode, bool udp, u_int8_t proposal_number); + ipsec_mode_t mode, encap_t udp, u_int8_t proposal_number); #endif /** PROPOSAL_SUBSTRUCTURE_H_ @}*/ diff --git a/src/libcharon/encoding/payloads/sa_payload.c b/src/libcharon/encoding/payloads/sa_payload.c index 3a8d3a760..a588d4e97 100644 --- a/src/libcharon/encoding/payloads/sa_payload.c +++ b/src/libcharon/encoding/payloads/sa_payload.c @@ -552,8 +552,8 @@ sa_payload_t *sa_payload_create_from_proposal_v2(proposal_t *proposal) */ sa_payload_t *sa_payload_create_from_proposals_v1(linked_list_t *proposals, u_int32_t lifetime, u_int64_t lifebytes, - auth_method_t auth, ipsec_mode_t mode, bool udp, - u_int16_t cpi) + auth_method_t auth, ipsec_mode_t mode, + encap_t udp, u_int16_t cpi) { proposal_substructure_t *substruct; private_sa_payload_t *this; @@ -591,8 +591,8 @@ sa_payload_t *sa_payload_create_from_proposals_v1(linked_list_t *proposals, */ sa_payload_t *sa_payload_create_from_proposal_v1(proposal_t *proposal, u_int32_t lifetime, u_int64_t lifebytes, - auth_method_t auth, ipsec_mode_t mode, bool udp, - u_int16_t cpi) + auth_method_t auth, ipsec_mode_t mode, + encap_t udp, u_int16_t cpi) { private_sa_payload_t *this; linked_list_t *proposals; diff --git a/src/libcharon/encoding/payloads/sa_payload.h b/src/libcharon/encoding/payloads/sa_payload.h index dac3043bf..b62a341d8 100644 --- a/src/libcharon/encoding/payloads/sa_payload.h +++ b/src/libcharon/encoding/payloads/sa_payload.h @@ -133,13 +133,13 @@ sa_payload_t *sa_payload_create_from_proposal_v2(proposal_t *proposal); * @param lifebytes lifebytes, in bytes * @param auth authentication method to use, or AUTH_NONE * @param mode IPsec encapsulation mode, TRANSPORT or TUNNEL - * @param udp TRUE to use UDP encapsulation + * @param udp ENCAP_UDP to use UDP encapsulation * @param cpi CPI in case IPComp should be used * @return sa_payload_t object */ sa_payload_t *sa_payload_create_from_proposals_v1(linked_list_t *proposals, u_int32_t lifetime, u_int64_t lifebytes, - auth_method_t auth, ipsec_mode_t mode, bool udp, + auth_method_t auth, ipsec_mode_t mode, encap_t udp, u_int16_t cpi); /** @@ -150,13 +150,13 @@ sa_payload_t *sa_payload_create_from_proposals_v1(linked_list_t *proposals, * @param lifebytes lifebytes, in bytes * @param auth authentication method to use, or AUTH_NONE * @param mode IPsec encapsulation mode, TRANSPORT or TUNNEL - * @param udp TRUE to use UDP encapsulation + * @param udp ENCAP_UDP to use UDP encapsulation * @param cpi CPI in case IPComp should be used * @return sa_payload_t object */ sa_payload_t *sa_payload_create_from_proposal_v1(proposal_t *proposal, u_int32_t lifetime, u_int64_t lifebytes, - auth_method_t auth, ipsec_mode_t mode, bool udp, + auth_method_t auth, ipsec_mode_t mode, encap_t udp, u_int16_t cpi); #endif /** SA_PAYLOAD_H_ @}*/ diff --git a/src/libcharon/network/sender.c b/src/libcharon/network/sender.c index 059f24b39..dd8efc1ec 100644 --- a/src/libcharon/network/sender.c +++ b/src/libcharon/network/sender.c @@ -94,10 +94,11 @@ METHOD(sender_t, send_, void, { host_t *src, *dst; - /* if neither source nor destination port is 500 we add a Non-ESP marker */ src = packet->get_source(packet); dst = packet->get_destination(packet); - DBG1(DBG_NET, "sending packet: from %#H to %#H", src, dst); + + DBG1(DBG_NET, "sending packet: from %#H to %#H (%zu bytes)", src, dst, + packet->get_data(packet).len); if (this->send_delay) { @@ -120,6 +121,7 @@ METHOD(sender_t, send_, void, message->destroy(message); } + /* if neither source nor destination port is 500 we add a Non-ESP marker */ if (dst->get_port(dst) != IKEV2_UDP_PORT && src->get_port(src) != IKEV2_UDP_PORT) { diff --git a/src/libcharon/plugins/android/android_service.c b/src/libcharon/plugins/android/android_service.c index d398b136f..6af35e5df 100644 --- a/src/libcharon/plugins/android/android_service.c +++ b/src/libcharon/plugins/android/android_service.c @@ -266,7 +266,7 @@ static job_requeue_t initiate(private_android_service_t *this) ike_cfg = ike_cfg_create(IKEV2, TRUE, FALSE, "0.0.0.0", FALSE, charon->socket->get_port(charon->socket, FALSE), - hostname, FALSE, IKEV2_UDP_PORT); + hostname, FALSE, IKEV2_UDP_PORT, FRAGMENTATION_NO); ike_cfg->add_proposal(ike_cfg, proposal_create_default(PROTO_IKE)); peer_cfg = peer_cfg_create("android", ike_cfg, CERT_SEND_IF_ASKED, diff --git a/src/libcharon/plugins/duplicheck/duplicheck_listener.c b/src/libcharon/plugins/duplicheck/duplicheck_listener.c index 7c6c13786..1b0df1e8b 100644 --- a/src/libcharon/plugins/duplicheck/duplicheck_listener.c +++ b/src/libcharon/plugins/duplicheck/duplicheck_listener.c @@ -191,6 +191,7 @@ METHOD(listener_t, message_hook, bool, { DBG1(DBG_CFG, "got a response on a duplicate IKE_SA for '%Y', " "deleting new IKE_SA", id); + charon->bus->alert(charon->bus, ALERT_UNIQUE_KEEP); entry_destroy(entry); this->mutex->lock(this->mutex); entry = this->active->remove(this->active, id); diff --git a/src/libcharon/plugins/error_notify/.gitignore b/src/libcharon/plugins/error_notify/.gitignore new file mode 100644 index 000000000..3a4dbe385 --- /dev/null +++ b/src/libcharon/plugins/error_notify/.gitignore @@ -0,0 +1 @@ +error-notify diff --git a/src/libcharon/plugins/error_notify/Makefile.am b/src/libcharon/plugins/error_notify/Makefile.am new file mode 100644 index 000000000..fccd25201 --- /dev/null +++ b/src/libcharon/plugins/error_notify/Makefile.am @@ -0,0 +1,23 @@ + +INCLUDES = -I$(top_srcdir)/src/libstrongswan -I$(top_srcdir)/src/libhydra \ + -I$(top_srcdir)/src/libcharon + +AM_CFLAGS = -rdynamic \ + -DIPSEC_PIDDIR=\"${piddir}\" + +if MONOLITHIC +noinst_LTLIBRARIES = libstrongswan-error-notify.la +else +plugin_LTLIBRARIES = libstrongswan-error-notify.la +endif + +libstrongswan_error_notify_la_SOURCES = \ + error_notify_plugin.h error_notify_plugin.c \ + error_notify_socket.h error_notify_socket.c \ + error_notify_listener.h error_notify_listener.c \ + error_notify_msg.h + +libstrongswan_error_notify_la_LDFLAGS = -module -avoid-version + +ipsec_PROGRAMS = error-notify +error_notify_SOURCES = error_notify.c diff --git a/src/libcharon/plugins/error_notify/error_notify.c b/src/libcharon/plugins/error_notify/error_notify.c new file mode 100644 index 000000000..fec35a45d --- /dev/null +++ b/src/libcharon/plugins/error_notify/error_notify.c @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2012 Martin Willi + * Copyright (C) 2012 revosec 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. + */ + +#include "error_notify_msg.h" + +#include <stdio.h> +#include <unistd.h> +#include <sys/stat.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <errno.h> + +/** + * Example of a simple notification listener + */ +int main(int argc, char *argv[]) +{ + struct sockaddr_un addr; + error_notify_msg_t msg; + int s; + + addr.sun_family = AF_UNIX; + strcpy(addr.sun_path, ERROR_NOTIFY_SOCKET); + + s = socket(AF_UNIX, SOCK_SEQPACKET, 0); + if (s < 0) + { + fprintf(stderr, "opening socket failed: %s\n", strerror(errno)); + return 1; + } + if (connect(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) + { + fprintf(stderr, "connect failed: %s\n", strerror(errno)); + close(s); + return 1; + } + while (1) + { + if (read(s, &msg, sizeof(msg)) != sizeof(msg)) + { + fprintf(stderr, "read failed: %s\n", strerror(errno)); + close(s); + return 1; + } + printf("%d %s %s %s %s\n", + msg.type, msg.name, msg.id, msg.ip, msg.str); + } + close(s); + return 0; +} diff --git a/src/libcharon/plugins/error_notify/error_notify_listener.c b/src/libcharon/plugins/error_notify/error_notify_listener.c new file mode 100644 index 000000000..9a6383cbe --- /dev/null +++ b/src/libcharon/plugins/error_notify/error_notify_listener.c @@ -0,0 +1,203 @@ +/* + * Copyright (C) 2012 Martin Willi + * Copyright (C) 2012 revosec 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. + */ + +#include "error_notify_listener.h" + +#include <daemon.h> + +typedef struct private_error_notify_listener_t private_error_notify_listener_t; + +/** + * Private data of an error_notify_listener_t object. + */ +struct private_error_notify_listener_t { + + /** + * Public error_notify_listener_t interface. + */ + error_notify_listener_t public; + + /** + * Socket to send notifications over + */ + error_notify_socket_t *socket; +}; + +METHOD(listener_t, alert, bool, + private_error_notify_listener_t *this, ike_sa_t *ike_sa, + alert_t alert, va_list args) +{ + error_notify_msg_t msg; + message_t *message; + host_t *host; + identification_t *id; + linked_list_t *list, *list2; + peer_cfg_t *peer_cfg; + + if (!this->socket->has_listeners(this->socket)) + { + return TRUE; + } + + memset(&msg, 0, sizeof(msg)); + + switch (alert) + { + case ALERT_RADIUS_NOT_RESPONDING: + msg.type = ERROR_NOTIFY_RADIUS_NOT_RESPONDING; + snprintf(msg.str, sizeof(msg.str), + "a RADIUS request message timed out"); + break; + case ALERT_LOCAL_AUTH_FAILED: + msg.type = ERROR_NOTIFY_LOCAL_AUTH_FAILED; + snprintf(msg.str, sizeof(msg.str), + "creating local authentication data failed"); + break; + case ALERT_PEER_AUTH_FAILED: + msg.type = ERROR_NOTIFY_PEER_AUTH_FAILED; + snprintf(msg.str, sizeof(msg.str), "peer authentication failed"); + break; + case ALERT_PARSE_ERROR_HEADER: + msg.type = ERROR_NOTIFY_PARSE_ERROR_HEADER; + message = va_arg(args, message_t*); + snprintf(msg.str, sizeof(msg.str), "parsing IKE header from " + "%#H failed", message->get_source(message)); + break; + case ALERT_PARSE_ERROR_BODY: + msg.type = ERROR_NOTIFY_PARSE_ERROR_BODY; + message = va_arg(args, message_t*); + snprintf(msg.str, sizeof(msg.str), "parsing IKE message from " + "%#H failed", message->get_source(message)); + break; + case ALERT_RETRANSMIT_SEND_TIMEOUT: + msg.type = ERROR_NOTIFY_RETRANSMIT_SEND_TIMEOUT; + snprintf(msg.str, sizeof(msg.str), + "IKE message retransmission timed out"); + break; + case ALERT_HALF_OPEN_TIMEOUT: + msg.type = ERROR_NOTIFY_HALF_OPEN_TIMEOUT; + snprintf(msg.str, sizeof(msg.str), "IKE_SA timed out before it " + "could be established"); + break; + case ALERT_PROPOSAL_MISMATCH_IKE: + msg.type = ERROR_NOTIFY_PROPOSAL_MISMATCH_IKE; + list = va_arg(args, linked_list_t*); + snprintf(msg.str, sizeof(msg.str), "the received IKE_SA poposals " + "did not match: %#P", list); + break; + case ALERT_PROPOSAL_MISMATCH_CHILD: + msg.type = ERROR_NOTIFY_PROPOSAL_MISMATCH_CHILD; + list = va_arg(args, linked_list_t*); + snprintf(msg.str, sizeof(msg.str), "the received CHILD_SA poposals " + "did not match: %#P", list); + break; + case ALERT_TS_MISMATCH: + msg.type = ERROR_NOTIFY_TS_MISMATCH; + list = va_arg(args, linked_list_t*); + list2 = va_arg(args, linked_list_t*); + snprintf(msg.str, sizeof(msg.str), "the received traffic selectors " + "did not match: %#R=== %#R", list, list2); + break; + case ALERT_INSTALL_CHILD_SA_FAILED: + msg.type = ERROR_NOTIFY_INSTALL_CHILD_SA_FAILED; + snprintf(msg.str, sizeof(msg.str), "installing IPsec SA failed"); + break; + case ALERT_INSTALL_CHILD_POLICY_FAILED: + msg.type = ERROR_NOTIFY_INSTALL_CHILD_POLICY_FAILED; + snprintf(msg.str, sizeof(msg.str), "installing IPsec policy failed"); + break; + case ALERT_UNIQUE_REPLACE: + msg.type = ERROR_NOTIFY_UNIQUE_REPLACE; + snprintf(msg.str, sizeof(msg.str), + "replaced old IKE_SA due to uniqueness policy"); + break; + case ALERT_UNIQUE_KEEP: + msg.type = ERROR_NOTIFY_UNIQUE_KEEP; + snprintf(msg.str, sizeof(msg.str), "keep existing in favor of " + "rejected new IKE_SA due to uniqueness policy"); + break; + case ALERT_VIP_FAILURE: + msg.type = ERROR_NOTIFY_VIP_FAILURE; + list = va_arg(args, linked_list_t*); + if (list->get_first(list, (void**)&host) == SUCCESS) + { + snprintf(msg.str, sizeof(msg.str), + "allocating a virtual IP failed, requested was %H", host); + } + else + { + snprintf(msg.str, sizeof(msg.str), + "expected a virtual IP request, but none found"); + } + break; + case ALERT_AUTHORIZATION_FAILED: + msg.type = ERROR_NOTIFY_AUTHORIZATION_FAILED; + snprintf(msg.str, sizeof(msg.str), "an authorization plugin " + "prevented establishment of an IKE_SA"); + break; + default: + return TRUE; + } + + if (ike_sa) + { + id = ike_sa->get_other_eap_id(ike_sa); + if (id->get_type(id) != ID_ANY) + { + snprintf(msg.id, sizeof(msg.id), "%Y", id); + } + host = ike_sa->get_other_host(ike_sa); + if (!host->is_anyaddr(host)) + { + snprintf(msg.ip, sizeof(msg.ip), "%#H", host); + } + peer_cfg = ike_sa->get_peer_cfg(ike_sa); + if (peer_cfg) + { + snprintf(msg.name, sizeof(msg.name), "%s", + peer_cfg->get_name(peer_cfg)); + } + } + + this->socket->notify(this->socket, &msg); + + return TRUE; +} + +METHOD(error_notify_listener_t, destroy, void, + private_error_notify_listener_t *this) +{ + free(this); +} + +/** + * See header + */ +error_notify_listener_t *error_notify_listener_create(error_notify_socket_t *s) +{ + private_error_notify_listener_t *this; + + INIT(this, + .public = { + .listener = { + .alert = _alert, + }, + .destroy = _destroy, + }, + .socket = s, + ); + + return &this->public; +} diff --git a/src/libcharon/plugins/error_notify/error_notify_listener.h b/src/libcharon/plugins/error_notify/error_notify_listener.h new file mode 100644 index 000000000..70be9d1ad --- /dev/null +++ b/src/libcharon/plugins/error_notify/error_notify_listener.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2012 Martin Willi + * Copyright (C) 2012 revosec 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. + */ + +/** + * @defgroup error_notify_listener error_notify_listener + * @{ @ingroup error_notify + */ + +#ifndef ERROR_NOTIFY_LISTENER_H_ +#define ERROR_NOTIFY_LISTENER_H_ + +typedef struct error_notify_listener_t error_notify_listener_t; + +#include <bus/listeners/listener.h> + +#include "error_notify_socket.h" + +/** + * Listener catching bus alerts. + */ +struct error_notify_listener_t { + + /** + * Implements listener_t interface. + */ + listener_t listener; + + /** + * Destroy a error_notify_listener_t. + */ + void (*destroy)(error_notify_listener_t *this); +}; + +/** + * Create a error_notify_listener instance. + */ +error_notify_listener_t *error_notify_listener_create(error_notify_socket_t *s); + +#endif /** ERROR_NOTIFY_LISTENER_H_ @}*/ diff --git a/src/libcharon/plugins/error_notify/error_notify_msg.h b/src/libcharon/plugins/error_notify/error_notify_msg.h new file mode 100644 index 000000000..e3cdd67e9 --- /dev/null +++ b/src/libcharon/plugins/error_notify/error_notify_msg.h @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2012 Martin Willi + * Copyright (C) 2012 revosec 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. + */ + +/** + * @defgroup error_notify_msg error_notify_msg + * @{ @ingroup error_notify + */ + +#ifndef ERROR_NOTIFY_MSG_H_ +#define ERROR_NOTIFY_MSG_H_ + +#define ERROR_NOTIFY_SOCKET IPSEC_PIDDIR "/charon.enfy" + +typedef struct error_notify_msg_t error_notify_msg_t; + +/** + * Message type, these are mapped to ALERT_* types. + */ +enum { + ERROR_NOTIFY_RADIUS_NOT_RESPONDING = 1, + ERROR_NOTIFY_LOCAL_AUTH_FAILED = 2, + ERROR_NOTIFY_PEER_AUTH_FAILED = 3, + ERROR_NOTIFY_PARSE_ERROR_HEADER = 4, + ERROR_NOTIFY_PARSE_ERROR_BODY = 5, + ERROR_NOTIFY_RETRANSMIT_SEND_TIMEOUT = 6, + ERROR_NOTIFY_HALF_OPEN_TIMEOUT = 7, + ERROR_NOTIFY_PROPOSAL_MISMATCH_IKE = 8, + ERROR_NOTIFY_PROPOSAL_MISMATCH_CHILD = 9, + ERROR_NOTIFY_TS_MISMATCH = 10, + ERROR_NOTIFY_INSTALL_CHILD_SA_FAILED = 11, + ERROR_NOTIFY_INSTALL_CHILD_POLICY_FAILED = 12, + ERROR_NOTIFY_UNIQUE_REPLACE = 13, + ERROR_NOTIFY_UNIQUE_KEEP = 14, + ERROR_NOTIFY_VIP_FAILURE = 15, + ERROR_NOTIFY_AUTHORIZATION_FAILED = 16, +}; + +/** + * Message to exchange over notify socket, strings are null-terminated. + */ +struct error_notify_msg_t { + /** message type */ + int type; + /** string with an error description */ + char str[128]; + /** connection name, if known */ + char name[64]; + /** peer identity, if known */ + char id[128]; + /** peer address and port, if known */ + char ip[60]; +}; + +#endif /** ERROR_NOTIFY_MSG_H_ @}*/ diff --git a/src/libcharon/plugins/error_notify/error_notify_plugin.c b/src/libcharon/plugins/error_notify/error_notify_plugin.c new file mode 100644 index 000000000..f4f0647fb --- /dev/null +++ b/src/libcharon/plugins/error_notify/error_notify_plugin.c @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2012 Martin Willi + * Copyright (C) 2012 revosec 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. + */ + +#include "error_notify_plugin.h" + +#include "error_notify_listener.h" +#include "error_notify_socket.h" + +#include <daemon.h> + +typedef struct private_error_notify_plugin_t private_error_notify_plugin_t; + +/** + * private data of error_notify plugin + */ +struct private_error_notify_plugin_t { + + /** + * Implements plugin interface + */ + error_notify_plugin_t public; + + /** + * Listener catching error alerts + */ + error_notify_listener_t *listener; + + /** + * Socket sending notifications + */ + error_notify_socket_t *socket; +}; + +METHOD(plugin_t, get_name, char*, + private_error_notify_plugin_t *this) +{ + return "error-notify"; +} + +METHOD(plugin_t, destroy, void, + private_error_notify_plugin_t *this) +{ + charon->bus->remove_listener(charon->bus, &this->listener->listener); + this->listener->destroy(this->listener); + this->socket->destroy(this->socket); + free(this); +} + +/** + * Plugin constructor + */ +plugin_t *error_notify_plugin_create() +{ + private_error_notify_plugin_t *this; + + INIT(this, + .public = { + .plugin = { + .get_name = _get_name, + .reload = (void*)return_false, + .destroy = _destroy, + }, + }, + .socket = error_notify_socket_create(), + ); + + this->listener = error_notify_listener_create(this->socket); + charon->bus->add_listener(charon->bus, &this->listener->listener); + + return &this->public.plugin; +} diff --git a/src/libcharon/plugins/error_notify/error_notify_plugin.h b/src/libcharon/plugins/error_notify/error_notify_plugin.h new file mode 100644 index 000000000..ed5303a91 --- /dev/null +++ b/src/libcharon/plugins/error_notify/error_notify_plugin.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2012 Martin Willi + * Copyright (C) 2012 revosec 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. + */ + +/** + * @defgroup error_notify error_notify + * @ingroup cplugins + * + * @defgroup error_notify_plugin error_notify_plugin + * @{ @ingroup error_notify + */ + +#ifndef ERROR_NOTIFY_PLUGIN_H_ +#define ERROR_NOTIFY_PLUGIN_H_ + +#include <plugins/plugin.h> + +typedef struct error_notify_plugin_t error_notify_plugin_t; + +/** + * Plugin sending error notifications over a UNIX socket. + */ +struct error_notify_plugin_t { + + /** + * Implements plugin interface. + */ + plugin_t plugin; +}; + +#endif /** ERROR_NOTIFY_PLUGIN_H_ @}*/ diff --git a/src/libcharon/plugins/error_notify/error_notify_socket.c b/src/libcharon/plugins/error_notify/error_notify_socket.c new file mode 100644 index 000000000..fe3b6355d --- /dev/null +++ b/src/libcharon/plugins/error_notify/error_notify_socket.c @@ -0,0 +1,213 @@ +/* + * Copyright (C) 2012 Martin Willi + * Copyright (C) 2012 revosec 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. + */ + +#include "error_notify_socket.h" + +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <unistd.h> +#include <errno.h> + +#include <daemon.h> +#include <threading/thread.h> +#include <threading/mutex.h> +#include <collections/linked_list.h> +#include <processing/jobs/callback_job.h> + +#include "error_notify_msg.h" + +typedef struct private_error_notify_socket_t private_error_notify_socket_t; + +/** + * Private data of an error_notify_socket_t object. + */ +struct private_error_notify_socket_t { + + /** + * Public error_notify_socket_t interface. + */ + error_notify_socket_t public; + + /** + * Unix socket file descriptor + */ + int socket; + + /** + * List of connected clients, as uintptr_t FD + */ + linked_list_t *connected; + + /** + * Mutex to lock clients list + */ + mutex_t *mutex; +}; + +/** + * Open error notify unix socket + */ +static bool open_socket(private_error_notify_socket_t *this) +{ + struct sockaddr_un addr; + mode_t old; + + addr.sun_family = AF_UNIX; + strcpy(addr.sun_path, ERROR_NOTIFY_SOCKET); + + this->socket = socket(AF_UNIX, SOCK_SEQPACKET, 0); + if (this->socket == -1) + { + DBG1(DBG_CFG, "creating notify socket failed"); + return FALSE; + } + unlink(addr.sun_path); + old = umask(~(S_IRWXU | S_IRWXG)); + if (bind(this->socket, (struct sockaddr*)&addr, sizeof(addr)) < 0) + { + DBG1(DBG_CFG, "binding notify socket failed: %s", strerror(errno)); + close(this->socket); + return FALSE; + } + umask(old); + if (chown(addr.sun_path, charon->caps->get_uid(charon->caps), + charon->caps->get_gid(charon->caps)) != 0) + { + DBG1(DBG_CFG, "changing notify socket permissions failed: %s", + strerror(errno)); + } + if (listen(this->socket, 10) < 0) + { + DBG1(DBG_CFG, "listening on notify socket failed: %s", strerror(errno)); + close(this->socket); + unlink(addr.sun_path); + return FALSE; + } + return TRUE; +} + +METHOD(error_notify_socket_t, has_listeners, bool, + private_error_notify_socket_t *this) +{ + int count; + + this->mutex->lock(this->mutex); + count = this->connected->get_count(this->connected); + this->mutex->unlock(this->mutex); + + return count != 0; +} + +METHOD(error_notify_socket_t, notify, void, + private_error_notify_socket_t *this, error_notify_msg_t *msg) +{ + enumerator_t *enumerator; + uintptr_t fd; + + this->mutex->lock(this->mutex); + enumerator = this->connected->create_enumerator(this->connected); + while (enumerator->enumerate(enumerator, (void*)&fd)) + { + while (send(fd, msg, sizeof(*msg), 0) <= 0) + { + switch (errno) + { + case EINTR: + continue; + case ECONNRESET: + case EPIPE: + /* disconnect, remove this listener */ + this->connected->remove_at(this->connected, enumerator); + close(fd); + break; + default: + DBG1(DBG_CFG, "sending notify failed: %s", strerror(errno)); + break; + } + break; + } + } + enumerator->destroy(enumerator); + this->mutex->unlock(this->mutex); +} + +/** + * Accept client connections, dispatch + */ +static job_requeue_t accept_(private_error_notify_socket_t *this) +{ + struct sockaddr_un addr; + int fd, len; + bool oldstate; + + len = sizeof(addr); + oldstate = thread_cancelability(TRUE); + fd = accept(this->socket, (struct sockaddr*)&addr, &len); + thread_cancelability(oldstate); + + if (fd != -1) + { + this->mutex->lock(this->mutex); + this->connected->insert_last(this->connected, (void*)(uintptr_t)fd); + this->mutex->unlock(this->mutex); + } + else + { + DBG1(DBG_CFG, "accepting notify connection failed: %s", + strerror(errno)); + } + return JOB_REQUEUE_DIRECT; +} + +METHOD(error_notify_socket_t, destroy, void, + private_error_notify_socket_t *this) +{ + this->connected->destroy(this->connected); + this->mutex->destroy(this->mutex); + close(this->socket); + free(this); +} + +/** + * See header + */ +error_notify_socket_t *error_notify_socket_create() +{ + private_error_notify_socket_t *this; + + INIT(this, + .public = { + .notify = _notify, + .has_listeners = _has_listeners, + .destroy = _destroy, + }, + .connected = linked_list_create(), + .mutex = mutex_create(MUTEX_TYPE_DEFAULT), + ); + + if (!open_socket(this)) + { + free(this); + return NULL; + } + + lib->processor->queue_job(lib->processor, + (job_t*)callback_job_create_with_prio((callback_job_cb_t)accept_, this, + NULL, (callback_job_cancel_t)return_false, JOB_PRIO_CRITICAL)); + + return &this->public; +} diff --git a/src/libcharon/plugins/error_notify/error_notify_socket.h b/src/libcharon/plugins/error_notify/error_notify_socket.h new file mode 100644 index 000000000..cb35b5584 --- /dev/null +++ b/src/libcharon/plugins/error_notify/error_notify_socket.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2012 Martin Willi + * Copyright (C) 2012 revosec 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. + */ + +/** + * @defgroup error_notify_socket error_notify_socket + * @{ @ingroup error_notify + */ + +#ifndef ERROR_NOTIFY_SOCKET_H_ +#define ERROR_NOTIFY_SOCKET_H_ + +typedef struct error_notify_socket_t error_notify_socket_t; + +#include "error_notify_listener.h" +#include "error_notify_msg.h" + +/** + * Error notification socket. + */ +struct error_notify_socket_t { + + /** + * Send an error notification message to all registered listeners. + * + * @param msg msg to send + */ + void (*notify)(error_notify_socket_t *this, error_notify_msg_t *msg); + + /** + * Check if we have active listeners on the socket. + * + * @return TRUE if listeners active + */ + bool (*has_listeners)(error_notify_socket_t *this); + + /** + * Destroy a error_notify_socket_t. + */ + void (*destroy)(error_notify_socket_t *this); +}; + +/** + * Create a error_notify_socket instance. + */ +error_notify_socket_t *error_notify_socket_create(); + +#endif /** ERROR_NOTIFY_SOCKET_H_ @}*/ diff --git a/src/libcharon/plugins/ha/ha_tunnel.c b/src/libcharon/plugins/ha/ha_tunnel.c index 05f522e8e..130c86e48 100644 --- a/src/libcharon/plugins/ha/ha_tunnel.c +++ b/src/libcharon/plugins/ha/ha_tunnel.c @@ -205,7 +205,7 @@ static void setup_tunnel(private_ha_tunnel_t *this, /* create config and backend */ ike_cfg = ike_cfg_create(IKEV2, FALSE, FALSE, local, FALSE, charon->socket->get_port(charon->socket, FALSE), - remote, FALSE, IKEV2_UDP_PORT); + remote, FALSE, IKEV2_UDP_PORT, FRAGMENTATION_NO); ike_cfg->add_proposal(ike_cfg, proposal_create_default(PROTO_IKE)); peer_cfg = peer_cfg_create("ha", ike_cfg, CERT_NEVER_SEND, UNIQUE_KEEP, 0, 86400, 0, 7200, 3600, FALSE, FALSE, 30, diff --git a/src/libcharon/plugins/load_tester/load_tester_config.c b/src/libcharon/plugins/load_tester/load_tester_config.c index 9e1615686..c6288c5d9 100644 --- a/src/libcharon/plugins/load_tester/load_tester_config.c +++ b/src/libcharon/plugins/load_tester/load_tester_config.c @@ -490,14 +490,16 @@ static peer_cfg_t* generate_config(private_load_tester_config_t *this, uint num) { ike_cfg = ike_cfg_create(this->version, TRUE, FALSE, local, FALSE, this->port + num - 1, - remote, FALSE, IKEV2_NATT_PORT); + remote, FALSE, IKEV2_NATT_PORT, + FRAGMENTATION_NO); } else { ike_cfg = ike_cfg_create(this->version, TRUE, FALSE, local, FALSE, charon->socket->get_port(charon->socket, FALSE), - remote, FALSE, IKEV2_UDP_PORT); + remote, FALSE, IKEV2_UDP_PORT, + FRAGMENTATION_NO); } ike_cfg->add_proposal(ike_cfg, this->proposal->clone(this->proposal)); peer_cfg = peer_cfg_create("load-test", ike_cfg, diff --git a/src/libcharon/plugins/lookip/lookip_listener.c b/src/libcharon/plugins/lookip/lookip_listener.c index f1f630f37..caf336a2e 100644 --- a/src/libcharon/plugins/lookip/lookip_listener.c +++ b/src/libcharon/plugins/lookip/lookip_listener.c @@ -232,7 +232,7 @@ METHOD(listener_t, ike_rekey, bool, { /* During IKE_SA rekey, the unique identifier changes. Fire update events * and update the cached entry. During the invocation of this hook, the - * virutal IPs have been migrated to new, hence remove that entry. */ + * virtual IPs have been migrated to new, hence remove that entry. */ remove_entry(this, new); add_entry(this, new); diff --git a/src/libcharon/plugins/maemo/maemo_service.c b/src/libcharon/plugins/maemo/maemo_service.c index dca01fbaa..806e4cd65 100644 --- a/src/libcharon/plugins/maemo/maemo_service.c +++ b/src/libcharon/plugins/maemo/maemo_service.c @@ -325,7 +325,7 @@ static gboolean initiate_connection(private_maemo_service_t *this, ike_cfg = ike_cfg_create(IKEV2, TRUE, FALSE, "0.0.0.0", FALSE, charon->socket->get_port(charon->socket, FALSE), - hostname, FALSE, IKEV2_UDP_PORT); + hostname, FALSE, IKEV2_UDP_PORT, FRAGMENTATION_NO); ike_cfg->add_proposal(ike_cfg, proposal_create_default(PROTO_IKE)); peer_cfg = peer_cfg_create(this->current, ike_cfg, diff --git a/src/libcharon/plugins/medcli/medcli_config.c b/src/libcharon/plugins/medcli/medcli_config.c index 42c158231..4be3dea02 100644 --- a/src/libcharon/plugins/medcli/medcli_config.c +++ b/src/libcharon/plugins/medcli/medcli_config.c @@ -105,7 +105,7 @@ METHOD(backend_t, get_peer_cfg_by_name, peer_cfg_t*, ike_cfg = ike_cfg_create(IKEV2, FALSE, FALSE, "0.0.0.0", FALSE, charon->socket->get_port(charon->socket, FALSE), - address, FALSE, IKEV2_UDP_PORT); + address, FALSE, IKEV2_UDP_PORT, FRAGMENTATION_NO); ike_cfg->add_proposal(ike_cfg, proposal_create_default(PROTO_IKE)); med_cfg = peer_cfg_create( "mediation", ike_cfg, @@ -380,7 +380,8 @@ medcli_config_t *medcli_config_create(database_t *db) .ike = ike_cfg_create(IKEV2, FALSE, FALSE, "0.0.0.0", FALSE, charon->socket->get_port(charon->socket, FALSE), - "0.0.0.0", FALSE, IKEV2_UDP_PORT), + "0.0.0.0", FALSE, IKEV2_UDP_PORT, + FRAGMENTATION_NO), ); this->ike->add_proposal(this->ike, proposal_create_default(PROTO_IKE)); diff --git a/src/libcharon/plugins/medsrv/medsrv_config.c b/src/libcharon/plugins/medsrv/medsrv_config.c index a2d7489f4..be14380ea 100644 --- a/src/libcharon/plugins/medsrv/medsrv_config.c +++ b/src/libcharon/plugins/medsrv/medsrv_config.c @@ -142,7 +142,8 @@ medsrv_config_t *medsrv_config_create(database_t *db) .ike = ike_cfg_create(IKEV2, FALSE, FALSE, "0.0.0.0", FALSE, charon->socket->get_port(charon->socket, FALSE), - "0.0.0.0", FALSE, IKEV2_UDP_PORT), + "0.0.0.0", FALSE, IKEV2_UDP_PORT, + FRAGMENTATION_NO), ); this->ike->add_proposal(this->ike, proposal_create_default(PROTO_IKE)); diff --git a/src/libcharon/plugins/sql/sql_config.c b/src/libcharon/plugins/sql/sql_config.c index 565c8584b..37bd86671 100644 --- a/src/libcharon/plugins/sql/sql_config.c +++ b/src/libcharon/plugins/sql/sql_config.c @@ -261,7 +261,8 @@ static ike_cfg_t *build_ike_cfg(private_sql_config_t *this, enumerator_t *e, ike_cfg = ike_cfg_create(IKEV2, certreq, force_encap, local, FALSE, charon->socket->get_port(charon->socket, FALSE), - remote, FALSE, IKEV2_UDP_PORT); + remote, FALSE, IKEV2_UDP_PORT, + FRAGMENTATION_NO); add_ike_proposals(this, ike_cfg, id); return ike_cfg; } diff --git a/src/libcharon/plugins/stroke/stroke_config.c b/src/libcharon/plugins/stroke/stroke_config.c index de3f4472e..9f6124dc9 100644 --- a/src/libcharon/plugins/stroke/stroke_config.c +++ b/src/libcharon/plugins/stroke/stroke_config.c @@ -233,7 +233,8 @@ static ike_cfg_t *build_ike_cfg(private_stroke_config_t *this, stroke_msg_t *msg ikeport, msg->add_conn.other.address, msg->add_conn.other.allow_any, - msg->add_conn.other.ikeport); + msg->add_conn.other.ikeport, + msg->add_conn.fragmentation); add_proposals(this, msg->add_conn.algorithms.ike, ike_cfg, NULL); return ike_cfg; } @@ -413,7 +414,7 @@ static auth_cfg_t *build_auth_cfg(private_stroke_config_t *this, ca = other_end->ca2; } } - if (id && *id == '%' && !streq(id, "%any")) + if (id && *id == '%' && !streq(id, "%any") && !streq(id, "%any6")) { /* has only an effect on rightid/2 */ loose = !local; id++; @@ -442,7 +443,7 @@ static auth_cfg_t *build_auth_cfg(private_stroke_config_t *this, cfg = auth_cfg_create(); - /* add identity and peer certifcate */ + /* add identity and peer certificate */ identity = identification_create_from_string(id); if (cert) { @@ -1311,4 +1312,3 @@ stroke_config_t *stroke_config_create(stroke_ca_t *ca, stroke_cred_t *cred, return &this->public; } - diff --git a/src/libcharon/plugins/tnccs_20/tnccs_20.c b/src/libcharon/plugins/tnccs_20/tnccs_20.c index e1ba56642..6239b152d 100644 --- a/src/libcharon/plugins/tnccs_20/tnccs_20.c +++ b/src/libcharon/plugins/tnccs_20/tnccs_20.c @@ -646,28 +646,40 @@ METHOD(tls_t, build, status_t, if (this->batch_type == PB_BATCH_NONE) { - if (this->is_server && state == PB_STATE_SERVER_WORKING) + if (this->is_server) { - if (this->state_machine->get_empty_cdata(this->state_machine)) - { - check_and_build_recommendation(this); - } - else + if (state == PB_STATE_SERVER_WORKING) { - DBG2(DBG_TNC, "no recommendation available yet, " - "sending empty PB-TNC SDATA batch"); - this->batch_type = PB_BATCH_SDATA; + if (this->state_machine->get_empty_cdata(this->state_machine)) + { + check_and_build_recommendation(this); + } + else + { + DBG2(DBG_TNC, "no recommendation available yet, " + "sending empty PB-TNC SDATA batch"); + this->batch_type = PB_BATCH_SDATA; + } } } else - { - /** - * In the DECIDED state and if no CRETRY is under way, - * a PB-TNC client replies with an empty CLOSE batch. - */ - if (state == PB_STATE_DECIDED) + { + switch (state) { - this->batch_type = PB_BATCH_CLOSE; + case PB_STATE_CLIENT_WORKING: + DBG2(DBG_TNC, "no client data to send, " + "sending empty PB-TNC CDATA batch"); + this->batch_type = PB_BATCH_CDATA; + break; + case PB_STATE_DECIDED: + /** + * In the DECIDED state and if no CRETRY is under way, + * a PB-TNC client replies with an empty CLOSE batch. + */ + this->batch_type = PB_BATCH_CLOSE; + break; + default: + break; } } } diff --git a/src/libcharon/plugins/uci/uci_config.c b/src/libcharon/plugins/uci/uci_config.c index 76dce125b..de0bf91af 100644 --- a/src/libcharon/plugins/uci/uci_config.c +++ b/src/libcharon/plugins/uci/uci_config.c @@ -155,7 +155,8 @@ METHOD(enumerator_t, peer_enumerator_enumerate, bool, ike_cfg = ike_cfg_create(IKEV2, FALSE, FALSE, local_addr, FALSE, charon->socket->get_port(charon->socket, FALSE), - remote_addr, FALSE, IKEV2_UDP_PORT); + remote_addr, FALSE, IKEV2_UDP_PORT, + FRAGMENTATION_NO); ike_cfg->add_proposal(ike_cfg, create_proposal(ike_proposal, PROTO_IKE)); this->peer_cfg = peer_cfg_create( name, ike_cfg, CERT_SEND_IF_ASKED, UNIQUE_NO, @@ -253,7 +254,8 @@ METHOD(enumerator_t, ike_enumerator_enumerate, bool, this->ike_cfg = ike_cfg_create(IKEV2, FALSE, FALSE, local_addr, FALSE, charon->socket->get_port(charon->socket, FALSE), - remote_addr, FALSE, IKEV2_UDP_PORT); + remote_addr, FALSE, IKEV2_UDP_PORT, + FRAGMENTATION_NO); this->ike_cfg->add_proposal(this->ike_cfg, create_proposal(ike_proposal, PROTO_IKE)); diff --git a/src/libcharon/plugins/unity/unity_handler.c b/src/libcharon/plugins/unity/unity_handler.c index b8d571dde..31d13add2 100644 --- a/src/libcharon/plugins/unity/unity_handler.c +++ b/src/libcharon/plugins/unity/unity_handler.c @@ -115,7 +115,7 @@ static bool add_include(private_unity_handler_t *this, chunk_t subnet) } /** - * Rempve a subnet from the inclusion list for this IKE_SA + * Remove a subnet from the inclusion list for this IKE_SA */ static bool remove_include(private_unity_handler_t *this, chunk_t subnet) { diff --git a/src/libcharon/processing/jobs/delete_ike_sa_job.c b/src/libcharon/processing/jobs/delete_ike_sa_job.c index 08b41af8c..3a8c2e1cd 100644 --- a/src/libcharon/processing/jobs/delete_ike_sa_job.c +++ b/src/libcharon/processing/jobs/delete_ike_sa_job.c @@ -84,6 +84,7 @@ METHOD(job_t, execute, job_requeue_t, else { DBG1(DBG_JOB, "deleting half open IKE_SA after timeout"); + charon->bus->alert(charon->bus, ALERT_HALF_OPEN_TIMEOUT); charon->ike_sa_manager->checkin_and_destroy( charon->ike_sa_manager, ike_sa); } diff --git a/src/libcharon/processing/jobs/process_message_job.c b/src/libcharon/processing/jobs/process_message_job.c index 71a2cb45d..606135b0b 100644 --- a/src/libcharon/processing/jobs/process_message_job.c +++ b/src/libcharon/processing/jobs/process_message_job.c @@ -67,9 +67,10 @@ METHOD(job_t, execute, job_requeue_t, this->message); if (ike_sa) { - DBG1(DBG_NET, "received packet: from %#H to %#H", + DBG1(DBG_NET, "received packet: from %#H to %#H (%zu bytes)", this->message->get_source(this->message), - this->message->get_destination(this->message)); + this->message->get_destination(this->message), + this->message->get_packet_data(this->message).len); if (ike_sa->process_message(ike_sa, this->message) == DESTROY_ME) { charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager, diff --git a/src/libcharon/sa/ike_sa.c b/src/libcharon/sa/ike_sa.c index de050cd3c..4029db11d 100644 --- a/src/libcharon/sa/ike_sa.c +++ b/src/libcharon/sa/ike_sa.c @@ -1232,7 +1232,8 @@ METHOD(ike_sa_t, process_message, status_t, case IKE_SA_INIT: case IKE_AUTH: if (this->state != IKE_CREATED && - this->state != IKE_CONNECTING) + this->state != IKE_CONNECTING && + message->get_first_payload_type(message) != FRAGMENT_V1) { DBG1(DBG_IKE, "ignoring %N in established IKE_SA state", exchange_type_names, message->get_exchange_type(message)); diff --git a/src/libcharon/sa/ike_sa.h b/src/libcharon/sa/ike_sa.h index b610055d6..37a20e7f3 100644 --- a/src/libcharon/sa/ike_sa.h +++ b/src/libcharon/sa/ike_sa.h @@ -72,6 +72,7 @@ enum ike_extension_t { /** * peer supports NAT traversal as specified in RFC4306 or RFC3947 + * including some RFC3947 drafts */ EXT_NATT = (1<<0), @@ -119,6 +120,17 @@ enum ike_extension_t { * peer supports Cisco Unity configuration attributes */ EXT_CISCO_UNITY = (1<<9), + + /** + * peer supports NAT traversal as specified in + * draft-ietf-ipsec-nat-t-ike-02 .. -03 + */ + EXT_NATT_DRAFT_02_03 = (1<<10), + + /** + * peer support proprietary IKE fragmentation + */ + EXT_IKE_FRAGMENTATION = (1<<11), }; /** diff --git a/src/libcharon/sa/ike_sa_manager.c b/src/libcharon/sa/ike_sa_manager.c index 5c126362b..2ac8c3123 100644 --- a/src/libcharon/sa/ike_sa_manager.c +++ b/src/libcharon/sa/ike_sa_manager.c @@ -968,14 +968,37 @@ static u_int64_t get_spi(private_ike_sa_manager_t *this) static bool get_init_hash(private_ike_sa_manager_t *this, message_t *message, chunk_t *hash) { + host_t *src; + if (!this->hasher) { /* this might be the case when flush() has been called */ return FALSE; } + if (message->get_first_payload_type(message) == FRAGMENT_V1) + { /* only hash the source IP, port and SPI for fragmented init messages */ + u_int16_t port; + u_int64_t spi; + + src = message->get_source(message); + if (!this->hasher->allocate_hash(this->hasher, + src->get_address(src), NULL)) + { + return FALSE; + } + port = src->get_port(src); + if (!this->hasher->allocate_hash(this->hasher, + chunk_from_thing(port), NULL)) + { + return FALSE; + } + spi = message->get_initiator_spi(message); + return this->hasher->allocate_hash(this->hasher, + chunk_from_thing(spi), hash); + } if (message->get_exchange_type(message) == ID_PROT) { /* include the source for Main Mode as the hash will be the same if * SPIs are reused by two initiators that use the same proposal */ - host_t *src = message->get_source(message); + src = message->get_source(message); if (!this->hasher->allocate_hash(this->hasher, src->get_address(src), NULL)) @@ -1280,7 +1303,10 @@ METHOD(ike_sa_manager_t, checkout_by_message, ike_sa_t*, ike_id = entry->ike_sa->get_id(entry->ike_sa); entry->checked_out = TRUE; - entry->message_id = message->get_message_id(message); + if (message->get_first_payload_type(message) != FRAGMENT_V1) + { + entry->message_id = message->get_message_id(message); + } if (ike_id->get_responder_spi(ike_id) == 0) { ike_id->set_responder_spi(ike_id, id->get_responder_spi(id)); @@ -1769,6 +1795,7 @@ METHOD(ike_sa_manager_t, check_uniqueness, bool, switch (policy) { case UNIQUE_REPLACE: + charon->bus->alert(charon->bus, ALERT_UNIQUE_REPLACE); DBG1(DBG_IKE, "deleting duplicate IKE_SA for peer " "'%Y' due to uniqueness policy", other); status = duplicate->delete(duplicate); diff --git a/src/libcharon/sa/ikev1/task_manager_v1.c b/src/libcharon/sa/ikev1/task_manager_v1.c index 1ee833cce..8a4761d5c 100644 --- a/src/libcharon/sa/ikev1/task_manager_v1.c +++ b/src/libcharon/sa/ikev1/task_manager_v1.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007-2011 Tobias Brunner + * Copyright (C) 2007-2013 Tobias Brunner * Copyright (C) 2007-2011 Martin Willi * Hochschule fuer Technik Rapperswil * @@ -36,6 +36,10 @@ #include <processing/jobs/retransmit_job.h> #include <processing/jobs/delete_ike_sa_job.h> #include <processing/jobs/dpd_timeout_job.h> +#include <processing/jobs/process_message_job.h> + +#include <encoding/payloads/fragment_payload.h> +#include <bio/bio_writer.h> /** * Number of old messages hashes we keep for retransmission. @@ -47,6 +51,20 @@ #define MAX_OLD_HASHES 2 /** + * Maximum packet size for fragmented packets (same as in sockets) + */ +#define MAX_PACKET 10000 + +/** + * Maximum size of fragment data when sending packets (currently the same is + * used for IPv4 and IPv6, even though the latter has a higher minimum datagram + * size). 576 (= min. IPv4) - 20 (= IP header) - 8 (= UDP header) - + * - 28 (= IKE header) - 8 (= fragment header) = 512 + * This is reduced by 4 in case of NAT-T (due to the non-ESP marker). + */ +#define MAX_FRAGMENT_SIZE 512 + +/** * First sequence number of responding packets. * * To distinguish retransmission jobs for initiating and responding packets, @@ -160,19 +178,65 @@ struct private_task_manager_t { packet_t *packet; /** - * type of the initated exchange + * type of the initiated exchange */ exchange_type_t type; } initiating; /** + * Data used to reassemble a fragmented message + */ + struct { + + /** + * Fragment ID (currently only one is supported at a time) + */ + u_int16_t id; + + /** + * The number of the last fragment (in case we receive the fragments out + * of order), since the first starts with 1 this defines the number of + * fragments we expect + */ + u_int8_t last; + + /** + * List of fragments (fragment_t*) + */ + linked_list_t *list; + + /** + * Length of all currently received fragments + */ + size_t len; + + /** + * Maximum length of a fragmented packet + */ + size_t max_packet; + + /** + * Maximum length of a single fragment (when sending) + */ + size_t size; + + /** + * The exchange type we use for fragments. Always the initial type even + * for fragmented quick mode or transaction messages (i.e. either + * ID_PROT or AGGRESSIVE) + */ + exchange_type_t exchange; + + } frag; + + /** * List of queued tasks not yet in action */ linked_list_t *queued_tasks; /** - * List of active tasks, initiated by ourselve + * List of active tasks, initiated by ourselves */ linked_list_t *active_tasks; @@ -212,6 +276,34 @@ struct private_task_manager_t { u_int32_t dpd_recv; }; +/** + * A single fragment within a fragmented message + */ +typedef struct { + + /** fragment number */ + u_int8_t num; + + /** fragment data */ + chunk_t data; + +} fragment_t; + +static void fragment_destroy(fragment_t *this) +{ + chunk_free(&this->data); + free(this); +} + +static void clear_fragments(private_task_manager_t *this, u_int16_t id) +{ + DESTROY_FUNCTION_IF(this->frag.list, (void*)fragment_destroy); + this->frag.list = NULL; + this->frag.last = 0; + this->frag.len = 0; + this->frag.id = id; +} + METHOD(task_manager_t, flush_queue, void, private_task_manager_t *this, task_queue_t queue) { @@ -283,10 +375,96 @@ static bool activate_task(private_task_manager_t *this, task_type_t type) } /** + * Send a single fragment with the given data + */ +static bool send_fragment(private_task_manager_t *this, bool request, + host_t *src, host_t *dst, fragment_payload_t *fragment) +{ + message_t *message; + packet_t *packet; + status_t status; + + message = message_create(IKEV1_MAJOR_VERSION, IKEV1_MINOR_VERSION); + /* other implementations seem to just use 0 as message ID, so here we go */ + message->set_message_id(message, 0); + message->set_request(message, request); + message->set_source(message, src->clone(src)); + message->set_destination(message, dst->clone(dst)); + message->set_exchange_type(message, this->frag.exchange); + message->add_payload(message, (payload_t*)fragment); + + status = this->ike_sa->generate_message(this->ike_sa, message, &packet); + if (status != SUCCESS) + { + DBG1(DBG_IKE, "failed to generate IKE fragment"); + message->destroy(message); + return FALSE; + } + charon->sender->send(charon->sender, packet); + message->destroy(message); + return TRUE; +} + +/** + * Send a packet, if supported and required do so in fragments + */ +static bool send_packet(private_task_manager_t *this, bool request, + packet_t *packet) +{ + fragmentation_t fragmentation = FRAGMENTATION_NO; + ike_cfg_t *ike_cfg; + host_t *src, *dst; + chunk_t data; + + ike_cfg = this->ike_sa->get_ike_cfg(this->ike_sa); + if (ike_cfg) + { + fragmentation = ike_cfg->fragmentation(ike_cfg); + } + data = packet->get_data(packet); + if (data.len > this->frag.size && (fragmentation == FRAGMENTATION_FORCE || + (this->ike_sa->supports_extension(this->ike_sa, EXT_IKE_FRAGMENTATION) && + fragmentation == FRAGMENTATION_YES))) + { + fragment_payload_t *fragment; + u_int8_t num, count; + size_t len, frag_size; + bool nat; + + /* reduce size due to non-ESP marker */ + nat = this->ike_sa->has_condition(this->ike_sa, COND_NAT_ANY); + frag_size = this->frag.size - (nat ? 4 : 0); + + src = packet->get_source(packet); + dst = packet->get_destination(packet); + count = (data.len / (frag_size + 1)) + 1; + + DBG1(DBG_IKE, "sending IKE message with length of %zu bytes in " + "%hhu fragments", data.len, count); + for (num = 1; num <= count; num++) + { + len = min(data.len, frag_size); + fragment = fragment_payload_create_from_data(num, num == count, + chunk_create(data.ptr, len)); + if (!send_fragment(this, request, src, dst, fragment)) + { + packet->destroy(packet); + return FALSE; + } + data = chunk_skip(data, len); + } + packet->destroy(packet); + return TRUE; + } + charon->sender->send(charon->sender, packet); + return TRUE; +} + +/** * Retransmit a packet, either as initiator or as responder */ -static status_t retransmit_packet(private_task_manager_t *this, u_int32_t seqnr, - u_int mid, u_int retransmitted, packet_t *packet) +static status_t retransmit_packet(private_task_manager_t *this, bool request, + u_int32_t seqnr, u_int mid, u_int retransmitted, packet_t *packet) { u_int32_t t; @@ -305,7 +483,10 @@ static status_t retransmit_packet(private_task_manager_t *this, u_int32_t seqnr, mid, seqnr < RESPONDING_SEQ ? seqnr : seqnr - RESPONDING_SEQ); charon->bus->alert(charon->bus, ALERT_RETRANSMIT_SEND, packet); } - charon->sender->send(charon->sender, packet->clone(packet)); + if (!send_packet(this, request, packet->clone(packet))) + { + return DESTROY_ME; + } lib->scheduler->schedule_job_ms(lib->scheduler, (job_t*) retransmit_job_create(seqnr, this->ike_sa->get_id(this->ike_sa)), t); return NEED_MORE; @@ -318,7 +499,7 @@ METHOD(task_manager_t, retransmit, status_t, if (seqnr == this->initiating.seqnr && this->initiating.packet) { - status = retransmit_packet(this, seqnr, this->initiating.mid, + status = retransmit_packet(this, TRUE, seqnr, this->initiating.mid, this->initiating.retransmitted, this->initiating.packet); if (status == NEED_MORE) { @@ -328,7 +509,7 @@ METHOD(task_manager_t, retransmit, status_t, } if (seqnr == this->responding.seqnr && this->responding.packet) { - status = retransmit_packet(this, seqnr, this->responding.mid, + status = retransmit_packet(this, FALSE, seqnr, this->responding.mid, this->responding.retransmitted, this->responding.packet); if (status == NEED_MORE) { @@ -604,12 +785,12 @@ METHOD(task_manager_t, initiate, status_t, } if (keep) { /* keep the packet for retransmission, the responder might request it */ - charon->sender->send(charon->sender, + send_packet(this, TRUE, this->initiating.packet->clone(this->initiating.packet)); } else { - charon->sender->send(charon->sender, this->initiating.packet); + send_packet(this, TRUE, this->initiating.packet); this->initiating.packet = NULL; } message->destroy(message); @@ -713,8 +894,8 @@ static status_t build_response(private_task_manager_t *this, message_t *request) { return retransmit(this, this->responding.seqnr); } - charon->sender->send(charon->sender, - this->responding.packet->clone(this->responding.packet)); + send_packet(this, FALSE, + this->responding.packet->clone(this->responding.packet)); if (delete) { return DESTROY_ME; @@ -769,7 +950,7 @@ static void send_notify(private_task_manager_t *this, message_t *request, if (this->ike_sa->generate_message(this->ike_sa, response, &packet) == SUCCESS) { - charon->sender->send(charon->sender, packet); + send_packet(this, TRUE, packet); } response->destroy(response); } @@ -868,6 +1049,7 @@ static status_t process_request(private_task_manager_t *this, this->passive_tasks->insert_last(this->passive_tasks, task); task = (task_t *)isakmp_natd_create(this->ike_sa, FALSE); this->passive_tasks->insert_last(this->passive_tasks, task); + this->frag.exchange = AGGRESSIVE; break; case QUICK_MODE: if (this->ike_sa->get_state(this->ike_sa) != IKE_ESTABLISHED) @@ -1038,6 +1220,114 @@ static status_t process_response(private_task_manager_t *this, return initiate(this); } +static status_t handle_fragment(private_task_manager_t *this, message_t *msg) +{ + fragment_payload_t *payload; + enumerator_t *enumerator; + fragment_t *fragment; + status_t status = SUCCESS; + chunk_t data; + u_int8_t num; + + payload = (fragment_payload_t*)msg->get_payload(msg, FRAGMENT_V1); + if (!payload) + { + return FAILED; + } + + if (this->frag.id != payload->get_id(payload)) + { + clear_fragments(this, payload->get_id(payload)); + this->frag.list = linked_list_create(); + } + + num = payload->get_number(payload); + if (!this->frag.last && payload->is_last(payload)) + { + this->frag.last = num; + } + + enumerator = this->frag.list->create_enumerator(this->frag.list); + while (enumerator->enumerate(enumerator, &fragment)) + { + if (fragment->num == num) + { /* ignore a duplicate fragment */ + DBG1(DBG_IKE, "received duplicate fragment #%hhu", num); + enumerator->destroy(enumerator); + return NEED_MORE; + } + if (fragment->num > num) + { + break; + } + } + + data = payload->get_data(payload); + this->frag.len += data.len; + if (this->frag.len > this->frag.max_packet) + { + DBG1(DBG_IKE, "fragmented IKE message is too large"); + enumerator->destroy(enumerator); + clear_fragments(this, 0); + return FAILED; + } + + INIT(fragment, + .num = num, + .data = chunk_clone(data), + ); + + this->frag.list->insert_before(this->frag.list, enumerator, fragment); + enumerator->destroy(enumerator); + + if (this->frag.list->get_count(this->frag.list) == this->frag.last) + { + message_t *message; + packet_t *pkt; + host_t *src, *dst; + bio_writer_t *writer; + + writer = bio_writer_create(this->frag.len); + DBG1(DBG_IKE, "received fragment #%hhu, reassembling fragmented IKE " + "message", num); + enumerator = this->frag.list->create_enumerator(this->frag.list); + while (enumerator->enumerate(enumerator, &fragment)) + { + writer->write_data(writer, fragment->data); + } + enumerator->destroy(enumerator); + + src = msg->get_source(msg); + dst = msg->get_destination(msg); + pkt = packet_create_from_data(src->clone(src), dst->clone(dst), + writer->extract_buf(writer)); + writer->destroy(writer); + + message = message_create_from_packet(pkt); + if (message->parse_header(message) != SUCCESS) + { + DBG1(DBG_IKE, "failed to parse header of reassembled IKE message"); + message->destroy(message); + status = FAILED; + } + else + { + lib->processor->queue_job(lib->processor, + (job_t*)process_message_job_create(message)); + status = NEED_MORE; + + } + clear_fragments(this, 0); + } + else + { /* there are some fragments missing */ + DBG1(DBG_IKE, "received fragment #%hhu, waiting for complete IKE " + "message", num); + status = NEED_MORE; + } + return status; +} + /** * Parse the given message and verify that it is valid. */ @@ -1085,6 +1375,11 @@ static status_t parse_message(private_task_manager_t *this, message_t *msg) return DESTROY_ME; } } + + if (msg->get_first_payload_type(msg) == FRAGMENT_V1) + { + return handle_fragment(this, msg); + } return status; } @@ -1111,8 +1406,8 @@ METHOD(task_manager_t, process_message, status_t, { DBG1(DBG_IKE, "received retransmit of response with ID %u, " "resending last request", mid); - charon->sender->send(charon->sender, - this->initiating.packet->clone(this->initiating.packet)); + send_packet(this, TRUE, + this->initiating.packet->clone(this->initiating.packet)); return SUCCESS; } DBG1(DBG_IKE, "received retransmit of response with ID %u, " @@ -1129,6 +1424,10 @@ METHOD(task_manager_t, process_message, status_t, msg->set_request(msg, FALSE); charon->bus->message(charon->bus, msg, TRUE, FALSE); status = parse_message(this, msg); + if (status == NEED_MORE) + { + return SUCCESS; + } if (status != SUCCESS) { return status; @@ -1153,7 +1452,7 @@ METHOD(task_manager_t, process_message, status_t, { DBG1(DBG_IKE, "received retransmit of request with ID %u, " "retransmitting response", mid); - charon->sender->send(charon->sender, + send_packet(this, FALSE, this->responding.packet->clone(this->responding.packet)); } else if (this->initiating.packet && @@ -1161,7 +1460,7 @@ METHOD(task_manager_t, process_message, status_t, { DBG1(DBG_IKE, "received retransmit of DPD request, " "retransmitting response"); - charon->sender->send(charon->sender, + send_packet(this, TRUE, this->initiating.packet->clone(this->initiating.packet)); } else @@ -1196,6 +1495,10 @@ METHOD(task_manager_t, process_message, status_t, msg->set_request(msg, TRUE); charon->bus->message(charon->bus, msg, TRUE, FALSE); status = parse_message(this, msg); + if (status == NEED_MORE) + { + return SUCCESS; + } if (status != SUCCESS) { return status; @@ -1288,6 +1591,7 @@ METHOD(task_manager_t, queue_ike, void, { queue_task(this, (task_t*)aggressive_mode_create(this->ike_sa, TRUE)); } + this->frag.exchange = AGGRESSIVE; } else { @@ -1591,6 +1895,7 @@ METHOD(task_manager_t, reset, void, this->initiating.seqnr = 0; this->initiating.retransmitted = 0; this->initiating.type = EXCHANGE_TYPE_UNDEFINED; + clear_fragments(this, 0); if (initiate != UINT_MAX) { this->dpd_send = initiate; @@ -1641,6 +1946,7 @@ METHOD(task_manager_t, destroy, void, this->active_tasks->destroy(this->active_tasks); this->queued_tasks->destroy(this->queued_tasks); this->passive_tasks->destroy(this->passive_tasks); + clear_fragments(this, 0); DESTROY_IF(this->queued); DESTROY_IF(this->responding.packet); @@ -1687,6 +1993,13 @@ task_manager_v1_t *task_manager_v1_create(ike_sa_t *ike_sa) .responding = { .seqnr = RESPONDING_SEQ, }, + .frag = { + .exchange = ID_PROT, + .max_packet = lib->settings->get_int(lib->settings, + "%s.max_packet", MAX_PACKET, charon->name), + .size = lib->settings->get_int(lib->settings, + "%s.fragment_size", MAX_FRAGMENT_SIZE, charon->name), + }, .ike_sa = ike_sa, .rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK), .queued_tasks = linked_list_create(), diff --git a/src/libcharon/sa/ikev1/tasks/aggressive_mode.c b/src/libcharon/sa/ikev1/tasks/aggressive_mode.c index 954dea880..7336d5d64 100644 --- a/src/libcharon/sa/ikev1/tasks/aggressive_mode.c +++ b/src/libcharon/sa/ikev1/tasks/aggressive_mode.c @@ -235,7 +235,8 @@ METHOD(task_t, build_i, status_t, this->lifetime += this->peer_cfg->get_over_time(this->peer_cfg); proposals = this->ike_cfg->get_proposals(this->ike_cfg); sa_payload = sa_payload_create_from_proposals_v1(proposals, - this->lifetime, 0, this->method, MODE_NONE, FALSE, 0); + this->lifetime, 0, this->method, MODE_NONE, + ENCAP_NONE, 0); proposals->destroy_offset(proposals, offsetof(proposal_t, destroy)); message->add_payload(message, &sa_payload->payload_interface); @@ -520,7 +521,8 @@ METHOD(task_t, build_r, status_t, identification_t *id; sa_payload = sa_payload_create_from_proposal_v1(this->proposal, - this->lifetime, 0, this->method, MODE_NONE, FALSE, 0); + this->lifetime, 0, this->method, MODE_NONE, + ENCAP_NONE, 0); message->add_payload(message, &sa_payload->payload_interface); if (!this->ph1->add_nonce_ke(this->ph1, message)) diff --git a/src/libcharon/sa/ikev1/tasks/isakmp_cert_pre.c b/src/libcharon/sa/ikev1/tasks/isakmp_cert_pre.c index d48484f09..43a0aaa36 100644 --- a/src/libcharon/sa/ikev1/tasks/isakmp_cert_pre.c +++ b/src/libcharon/sa/ikev1/tasks/isakmp_cert_pre.c @@ -13,6 +13,28 @@ * for more details. */ +/* + * Copyright (C) 2013 Volker Rümelin + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + #include "isakmp_cert_pre.h" #include <daemon.h> @@ -21,6 +43,7 @@ #include <encoding/payloads/sa_payload.h> #include <encoding/payloads/certreq_payload.h> #include <credentials/certificates/x509.h> +#include <credentials/containers/pkcs7.h> typedef struct private_isakmp_cert_pre_t private_isakmp_cert_pre_t; @@ -132,7 +155,106 @@ static void process_certreqs(private_isakmp_cert_pre_t *this, message_t *message } /** - * Import receuved certificates + * Process an X509 certificate payload + */ +static void process_x509(cert_payload_t *payload, auth_cfg_t *auth, bool *first) +{ + certificate_t *cert; + + cert = payload->get_cert(payload); + if (cert) + { + if (*first) + { /* the first is an end entity certificate */ + DBG1(DBG_IKE, "received end entity cert \"%Y\"", + cert->get_subject(cert)); + auth->add(auth, AUTH_HELPER_SUBJECT_CERT, cert); + *first = FALSE; + } + else + { + DBG1(DBG_IKE, "received issuer cert \"%Y\"", + cert->get_subject(cert)); + auth->add(auth, AUTH_HELPER_IM_CERT, cert); + } + } +} + +/** + * Process a CRL certificate payload + */ +static void process_crl(cert_payload_t *payload, auth_cfg_t *auth) +{ + certificate_t *cert; + + cert = payload->get_cert(payload); + if (cert) + { + DBG1(DBG_IKE, "received CRL \"%Y\"", cert->get_subject(cert)); + auth->add(auth, AUTH_HELPER_REVOCATION_CERT, cert); + } +} + +/** + * Process a PKCS7 certificate payload + */ +static void process_pkcs7(cert_payload_t *payload, auth_cfg_t *auth) +{ + enumerator_t *enumerator; + container_t *container; + certificate_t *cert; + pkcs7_t *pkcs7; + + container = payload->get_container(payload); + if (!container) + { + return; + } + switch (container->get_type(container)) + { + case CONTAINER_PKCS7_DATA: + case CONTAINER_PKCS7_SIGNED_DATA: + case CONTAINER_PKCS7_ENVELOPED_DATA: + break; + default: + container->destroy(container); + return; + } + + pkcs7 = (pkcs7_t *)container; + enumerator = pkcs7->create_cert_enumerator(pkcs7); + while (enumerator->enumerate(enumerator, &cert)) + { + if (cert->get_type(cert) == CERT_X509) + { + x509_t *x509 = (x509_t*)cert; + + if (x509->get_flags(x509) & X509_CA) + { + DBG1(DBG_IKE, "received issuer cert \"%Y\"", + cert->get_subject(cert)); + auth->add(auth, AUTH_HELPER_IM_CERT, cert->get_ref(cert)); + } + else + { + DBG1(DBG_IKE, "received end entity cert \"%Y\"", + cert->get_subject(cert)); + auth->add(auth, AUTH_HELPER_SUBJECT_CERT, cert->get_ref(cert)); + } + } + else + { + DBG1(DBG_IKE, "received unsupported cert type %N", + certificate_type_names, cert->get_type(cert)); + } + } + enumerator->destroy(enumerator); + + container->destroy(container); +} + +/** + * Import received certificates */ static void process_certs(private_isakmp_cert_pre_t *this, message_t *message) { @@ -150,7 +272,6 @@ static void process_certs(private_isakmp_cert_pre_t *this, message_t *message) { cert_payload_t *cert_payload; cert_encoding_t encoding; - certificate_t *cert; cert_payload = (cert_payload_t*)payload; encoding = cert_payload->get_cert_encoding(cert_payload); @@ -158,36 +279,14 @@ static void process_certs(private_isakmp_cert_pre_t *this, message_t *message) switch (encoding) { case ENC_X509_SIGNATURE: - { - cert = cert_payload->get_cert(cert_payload); - if (cert) - { - if (first) - { /* the first is an end entity certificate */ - DBG1(DBG_IKE, "received end entity cert \"%Y\"", - cert->get_subject(cert)); - auth->add(auth, AUTH_HELPER_SUBJECT_CERT, cert); - first = FALSE; - } - else - { - DBG1(DBG_IKE, "received issuer cert \"%Y\"", - cert->get_subject(cert)); - auth->add(auth, AUTH_HELPER_IM_CERT, cert); - } - } + process_x509(cert_payload, auth, &first); break; - } case ENC_CRL: - cert = cert_payload->get_cert(cert_payload); - if (cert) - { - DBG1(DBG_IKE, "received CRL \"%Y\"", - cert->get_subject(cert)); - auth->add(auth, AUTH_HELPER_REVOCATION_CERT, cert); - } + process_crl(cert_payload, auth); break; case ENC_PKCS7_WRAPPED_X509: + process_pkcs7(cert_payload, auth); + break; case ENC_PGP: case ENC_DNS_SIGNED_KEY: case ENC_KERBEROS_TOKEN: diff --git a/src/libcharon/sa/ikev1/tasks/isakmp_natd.c b/src/libcharon/sa/ikev1/tasks/isakmp_natd.c index 50bf1612d..5a779ff62 100644 --- a/src/libcharon/sa/ikev1/tasks/isakmp_natd.c +++ b/src/libcharon/sa/ikev1/tasks/isakmp_natd.c @@ -15,6 +15,28 @@ * for more details. */ +/* + * Copyright (C) 2012 Volker Rümelin + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + #include "isakmp_natd.h" #include <string.h> @@ -75,6 +97,18 @@ struct private_isakmp_natd_t { }; /** + * Get NAT-D payload type (RFC 3947 or RFC 3947 drafts). + */ +static payload_type_t get_nat_d_payload_type(ike_sa_t *ike_sa) +{ + if (ike_sa->supports_extension(ike_sa, EXT_NATT_DRAFT_02_03)) + { + return NAT_D_DRAFT_00_03_V1; + } + return NAT_D_V1; +} + +/** * Build NAT detection hash for a host. */ static chunk_t generate_natd_hash(private_isakmp_natd_t *this, @@ -162,7 +196,7 @@ static hash_payload_t *build_natd_payload(private_isakmp_natd_t *this, bool src, { return NULL; } - payload = hash_payload_create(NAT_D_V1); + payload = hash_payload_create(get_nat_d_payload_type(this->ike_sa)); payload->set_hash(payload, hash); chunk_free(&hash); return payload; @@ -221,7 +255,8 @@ static void process_payloads(private_isakmp_natd_t *this, message_t *message) enumerator = message->create_payload_enumerator(message); while (enumerator->enumerate(enumerator, &payload)) { - if (payload->get_type(payload) != NAT_D_V1) + if (payload->get_type(payload) != NAT_D_V1 && + payload->get_type(payload) != NAT_D_DRAFT_00_03_V1) { continue; } @@ -350,7 +385,7 @@ METHOD(task_t, process_r, status_t, switch (message->get_exchange_type(message)) { case AGGRESSIVE: - { /* proccess NAT-D payloads in the second request, already added ours + { /* process NAT-D payloads in the second request, already added ours * in the first response */ result = SUCCESS; /* fall */ diff --git a/src/libcharon/sa/ikev1/tasks/isakmp_vendor.c b/src/libcharon/sa/ikev1/tasks/isakmp_vendor.c index 4c6f823f4..2ff2b55e9 100644 --- a/src/libcharon/sa/ikev1/tasks/isakmp_vendor.c +++ b/src/libcharon/sa/ikev1/tasks/isakmp_vendor.c @@ -1,4 +1,5 @@ /* + * Copyright (C) 2012-2013 Tobias Brunner * Copyright (C) 2009 Martin Willi * Hochschule fuer Technik Rapperswil * @@ -13,6 +14,28 @@ * for more details. */ +/* + * Copyright (C) 2012 Volker Rümelin + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + #include "isakmp_vendor.h" #include <daemon.h> @@ -39,6 +62,11 @@ struct private_isakmp_vendor_t { * Are we the inititator of this task */ bool initiator; + + /** + * Index of best nat traversal VID found + */ + int best_natt_ext; }; /** @@ -65,70 +93,113 @@ static struct { { "XAuth", EXT_XAUTH, TRUE, 8, "\x09\x00\x26\x89\xdf\xd6\xb7\x12"}, - /* NAT-Traversal, MD5("RFC 3947") */ - { "NAT-T (RFC 3947)", EXT_NATT, TRUE, 16, - "\x4a\x13\x1c\x81\x07\x03\x58\x45\x5c\x57\x28\xf2\x0e\x95\x45\x2f"}, - /* Dead peer detection, RFC 3706 */ { "DPD", EXT_DPD, TRUE, 16, "\xaf\xca\xd7\x13\x68\xa1\xf1\xc9\x6b\x86\x96\xfc\x77\x57\x01\x00"}, - { "draft-stenberg-ipsec-nat-traversal-01", 0, FALSE, 16, - "\x27\xba\xb5\xdc\x01\xea\x07\x60\xea\x4e\x31\x90\xac\x27\xc0\xd0"}, + { "Cisco Unity", EXT_CISCO_UNITY, FALSE, 16, + "\x12\xf5\xf2\x8c\x45\x71\x68\xa9\x70\x2d\x9f\xe2\x74\xcc\x01\x00"}, - { "draft-stenberg-ipsec-nat-traversal-02", 0, FALSE, 16, - "\x61\x05\xc4\x22\xe7\x68\x47\xe4\x3f\x96\x84\x80\x12\x92\xae\xcd"}, + /* Proprietary IKE fragmentation extension. Capabilities are handled + * specially on receipt of this VID. */ + { "FRAGMENTATION", EXT_IKE_FRAGMENTATION, FALSE, 20, + "\x40\x48\xb7\xd5\x6e\xbc\xe8\x85\x25\xe7\xde\x7f\x00\xd6\xc2\xd3\x80\x00\x00\x00"}, - { "draft-ietf-ipsec-nat-t-ike", 0, FALSE, 16, - "\x4d\xf3\x79\x28\xe9\xfc\x4f\xd1\xb3\x26\x21\x70\xd5\x15\xc6\x62"}, +}, vendor_natt_ids[] = { - { "draft-ietf-ipsec-nat-t-ike-00", 0, FALSE, 16, - "\x44\x85\x15\x2d\x18\xb6\xbb\xcd\x0b\xe8\xa8\x46\x95\x79\xdd\xcc"}, + /* NAT-Traversal VIDs ordered by preference */ + + /* NAT-Traversal, MD5("RFC 3947") */ + { "NAT-T (RFC 3947)", EXT_NATT, TRUE, 16, + "\x4a\x13\x1c\x81\x07\x03\x58\x45\x5c\x57\x28\xf2\x0e\x95\x45\x2f"}, - { "draft-ietf-ipsec-nat-t-ike-02", 0, FALSE, 16, + { "draft-ietf-ipsec-nat-t-ike-03", EXT_NATT | EXT_NATT_DRAFT_02_03, + FALSE, 16, + "\x7d\x94\x19\xa6\x53\x10\xca\x6f\x2c\x17\x9d\x92\x15\x52\x9d\x56"}, + + { "draft-ietf-ipsec-nat-t-ike-02", EXT_NATT | EXT_NATT_DRAFT_02_03, + FALSE, 16, "\xcd\x60\x46\x43\x35\xdf\x21\xf8\x7c\xfd\xb2\xfc\x68\xb6\xa4\x48"}, - { "draft-ietf-ipsec-nat-t-ike-02\\n", 0, FALSE, 16, + { "draft-ietf-ipsec-nat-t-ike-02\\n", EXT_NATT | EXT_NATT_DRAFT_02_03, + TRUE, 16, "\x90\xcb\x80\x91\x3e\xbb\x69\x6e\x08\x63\x81\xb5\xec\x42\x7b\x1f"}, - { "draft-ietf-ipsec-nat-t-ike-03", 0, FALSE, 16, - "\x7d\x94\x19\xa6\x53\x10\xca\x6f\x2c\x17\x9d\x92\x15\x52\x9d\x56"}, + { "draft-ietf-ipsec-nat-t-ike-08", 0, FALSE, 16, + "\x8f\x8d\x83\x82\x6d\x24\x6b\x6f\xc7\xa8\xa6\xa4\x28\xc1\x1d\xe8"}, - { "draft-ietf-ipsec-nat-t-ike-04", 0, FALSE, 16, - "\x99\x09\xb6\x4e\xed\x93\x7c\x65\x73\xde\x52\xac\xe9\x52\xfa\x6b"}, + { "draft-ietf-ipsec-nat-t-ike-07", 0, FALSE, 16, + "\x43\x9b\x59\xf8\xba\x67\x6c\x4c\x77\x37\xae\x22\xea\xb8\xf5\x82"}, + + { "draft-ietf-ipsec-nat-t-ike-06", 0, FALSE, 16, + "\x4d\x1e\x0e\x13\x6d\xea\xfa\x34\xc4\xf3\xea\x9f\x02\xec\x72\x85"}, { "draft-ietf-ipsec-nat-t-ike-05", 0, FALSE, 16, "\x80\xd0\xbb\x3d\xef\x54\x56\x5e\xe8\x46\x45\xd4\xc8\x5c\xe3\xee"}, - { "draft-ietf-ipsec-nat-t-ike-06", 0, FALSE, 16, - "\x4d\x1e\x0e\x13\x6d\xea\xfa\x34\xc4\xf3\xea\x9f\x02\xec\x72\x85"}, + { "draft-ietf-ipsec-nat-t-ike-04", 0, FALSE, 16, + "\x99\x09\xb6\x4e\xed\x93\x7c\x65\x73\xde\x52\xac\xe9\x52\xfa\x6b"}, - { "draft-ietf-ipsec-nat-t-ike-07", 0, FALSE, 16, - "\x43\x9b\x59\xf8\xba\x67\x6c\x4c\x77\x37\xae\x22\xea\xb8\xf5\x82"}, + { "draft-ietf-ipsec-nat-t-ike-00", 0, FALSE, 16, + "\x44\x85\x15\x2d\x18\xb6\xbb\xcd\x0b\xe8\xa8\x46\x95\x79\xdd\xcc"}, - { "draft-ietf-ipsec-nat-t-ike-08", 0, FALSE, 16, - "\x8f\x8d\x83\x82\x6d\x24\x6b\x6f\xc7\xa8\xa6\xa4\x28\xc1\x1d\xe8"}, + { "draft-ietf-ipsec-nat-t-ike", 0, FALSE, 16, + "\x4d\xf3\x79\x28\xe9\xfc\x4f\xd1\xb3\x26\x21\x70\xd5\x15\xc6\x62"}, + + { "draft-stenberg-ipsec-nat-traversal-02", 0, FALSE, 16, + "\x61\x05\xc4\x22\xe7\x68\x47\xe4\x3f\x96\x84\x80\x12\x92\xae\xcd"}, + + { "draft-stenberg-ipsec-nat-traversal-01", 0, FALSE, 16, + "\x27\xba\xb5\xdc\x01\xea\x07\x60\xea\x4e\x31\x90\xac\x27\xc0\xd0"}, - { "Cisco Unity", EXT_CISCO_UNITY, FALSE, 16, - "\x12\xf5\xf2\x8c\x45\x71\x68\xa9\x70\x2d\x9f\xe2\x74\xcc\x01\x00"}, }; +/** + * According to racoon 0x80000000 seems to indicate support for fragmentation + * of Aggressive and Main mode messages. 0x40000000 seems to indicate support + * for fragmentation of base ISAKMP messages (Cisco adds that and thus sends + * 0xc0000000) + */ +static const u_int32_t fragmentation_ike = 0x80000000; + +/** + * Check if the given vendor ID indicate support for fragmentation + */ +static bool fragmentation_supported(chunk_t data, int i) +{ + if (vendor_ids[i].extension == EXT_IKE_FRAGMENTATION && + data.len == 20 && memeq(data.ptr, vendor_ids[i].id, 16)) + { + return untoh32(&data.ptr[16]) & fragmentation_ike; + } + return FALSE; +} + METHOD(task_t, build, status_t, private_isakmp_vendor_t *this, message_t *message) { vendor_id_payload_t *vid_payload; - bool strongswan, cisco_unity; + bool strongswan, cisco_unity, fragmentation; + ike_cfg_t *ike_cfg; int i; strongswan = lib->settings->get_bool(lib->settings, - "%s.send_vendor_id", FALSE, charon->name); + "%s.send_vendor_id", FALSE, charon->name); cisco_unity = lib->settings->get_bool(lib->settings, - "%s.cisco_unity", FALSE, charon->name); + "%s.cisco_unity", FALSE, charon->name); + ike_cfg = this->ike_sa->get_ike_cfg(this->ike_sa); + fragmentation = ike_cfg->fragmentation(ike_cfg) != FRAGMENTATION_NO; + if (!this->initiator && fragmentation) + { + fragmentation = this->ike_sa->supports_extension(this->ike_sa, + EXT_IKE_FRAGMENTATION); + } for (i = 0; i < countof(vendor_ids); i++) { if (vendor_ids[i].send || (vendor_ids[i].extension == EXT_STRONGSWAN && strongswan) || - (vendor_ids[i].extension == EXT_CISCO_UNITY && cisco_unity)) + (vendor_ids[i].extension == EXT_CISCO_UNITY && cisco_unity) || + (vendor_ids[i].extension == EXT_IKE_FRAGMENTATION && fragmentation)) { DBG2(DBG_IKE, "sending %s vendor ID", vendor_ids[i].desc); vid_payload = vendor_id_payload_create_data(VENDOR_ID_V1, @@ -136,6 +207,18 @@ METHOD(task_t, build, status_t, message->add_payload(message, &vid_payload->payload_interface); } } + for (i = 0; i < countof(vendor_natt_ids); i++) + { + if ((this->initiator && vendor_natt_ids[i].send) || + this->best_natt_ext == i) + { + DBG2(DBG_IKE, "sending %s vendor ID", vendor_natt_ids[i].desc); + vid_payload = vendor_id_payload_create_data(VENDOR_ID_V1, + chunk_clone(chunk_create(vendor_natt_ids[i].id, + vendor_natt_ids[i].len))); + message->add_payload(message, &vid_payload->payload_interface); + } + } return this->initiator ? NEED_MORE : SUCCESS; } @@ -161,7 +244,8 @@ METHOD(task_t, process, status_t, for (i = 0; i < countof(vendor_ids); i++) { if (chunk_equals(data, chunk_create(vendor_ids[i].id, - vendor_ids[i].len))) + vendor_ids[i].len)) || + fragmentation_supported(data, i)) { DBG1(DBG_IKE, "received %s vendor ID", vendor_ids[i].desc); if (vendor_ids[i].extension) @@ -170,6 +254,26 @@ METHOD(task_t, process, status_t, vendor_ids[i].extension); } found = TRUE; + break; + } + } + if (!found) + { + for (i = 0; i < countof(vendor_natt_ids); i++) + { + if (chunk_equals(data, chunk_create(vendor_natt_ids[i].id, + vendor_natt_ids[i].len))) + { + DBG1(DBG_IKE, "received %s vendor ID", + vendor_natt_ids[i].desc); + if (vendor_natt_ids[i].extension && + (i < this->best_natt_ext || this->best_natt_ext < 0)) + { + this->best_natt_ext = i; + } + found = TRUE; + break; + } } } if (!found) @@ -180,6 +284,12 @@ METHOD(task_t, process, status_t, } enumerator->destroy(enumerator); + if (this->best_natt_ext >= 0) + { + this->ike_sa->enable_extension(this->ike_sa, + vendor_natt_ids[this->best_natt_ext].extension); + } + return this->initiator ? SUCCESS : NEED_MORE; } @@ -220,6 +330,7 @@ isakmp_vendor_t *isakmp_vendor_create(ike_sa_t *ike_sa, bool initiator) }, .initiator = initiator, .ike_sa = ike_sa, + .best_natt_ext = -1, ); return &this->public; diff --git a/src/libcharon/sa/ikev1/tasks/main_mode.c b/src/libcharon/sa/ikev1/tasks/main_mode.c index 9ccf9abf5..bc9d4bbc3 100644 --- a/src/libcharon/sa/ikev1/tasks/main_mode.c +++ b/src/libcharon/sa/ikev1/tasks/main_mode.c @@ -241,7 +241,8 @@ METHOD(task_t, build_i, status_t, this->lifetime += this->peer_cfg->get_over_time(this->peer_cfg); proposals = this->ike_cfg->get_proposals(this->ike_cfg); sa_payload = sa_payload_create_from_proposals_v1(proposals, - this->lifetime, 0, this->method, MODE_NONE, FALSE, 0); + this->lifetime, 0, this->method, MODE_NONE, + ENCAP_NONE, 0); proposals->destroy_offset(proposals, offsetof(proposal_t, destroy)); message->add_payload(message, &sa_payload->payload_interface); @@ -455,7 +456,8 @@ METHOD(task_t, build_r, status_t, sa_payload_t *sa_payload; sa_payload = sa_payload_create_from_proposal_v1(this->proposal, - this->lifetime, 0, this->method, MODE_NONE, FALSE, 0); + this->lifetime, 0, this->method, MODE_NONE, + ENCAP_NONE, 0); message->add_payload(message, &sa_payload->payload_interface); return NEED_MORE; diff --git a/src/libcharon/sa/ikev1/tasks/quick_mode.c b/src/libcharon/sa/ikev1/tasks/quick_mode.c index 86ddcc9d9..1eae6aa93 100644 --- a/src/libcharon/sa/ikev1/tasks/quick_mode.c +++ b/src/libcharon/sa/ikev1/tasks/quick_mode.c @@ -16,6 +16,28 @@ * for more details. */ +/* + * Copyright (C) 2012 Volker Rümelin + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + #include "quick_mode.h" #include <string.h> @@ -596,6 +618,34 @@ static bool get_ts(private_quick_mode_t *this, message_t *message) } /** + * Get encap + */ +static encap_t get_encap(ike_sa_t* ike_sa, bool udp) +{ + if (!udp) + { + return ENCAP_NONE; + } + if (ike_sa->supports_extension(ike_sa, EXT_NATT_DRAFT_02_03)) + { + return ENCAP_UDP_DRAFT_00_03; + } + return ENCAP_UDP; +} + +/** + * Get NAT-OA payload type (RFC 3947 or RFC 3947 drafts). + */ +static payload_type_t get_nat_oa_payload_type(ike_sa_t *ike_sa) +{ + if (ike_sa->supports_extension(ike_sa, EXT_NATT_DRAFT_02_03)) + { + return NAT_OA_DRAFT_00_03_V1; + } + return NAT_OA_V1; +} + +/** * Add NAT-OA payloads */ static void add_nat_oa_payloads(private_quick_mode_t *this, message_t *message) @@ -603,6 +653,7 @@ static void add_nat_oa_payloads(private_quick_mode_t *this, message_t *message) identification_t *id; id_payload_t *nat_oa; host_t *src, *dst; + payload_type_t nat_oa_payload_type; src = message->get_source(message); dst = message->get_destination(message); @@ -610,15 +661,17 @@ static void add_nat_oa_payloads(private_quick_mode_t *this, message_t *message) src = this->initiator ? src : dst; dst = this->initiator ? dst : src; + nat_oa_payload_type = get_nat_oa_payload_type(this->ike_sa); + /* first NAT-OA is the initiator's address */ id = identification_create_from_sockaddr(src->get_sockaddr(src)); - nat_oa = id_payload_create_from_identification(NAT_OA_V1, id); + nat_oa = id_payload_create_from_identification(nat_oa_payload_type, id); message->add_payload(message, (payload_t*)nat_oa); id->destroy(id); /* second NAT-OA is that of the responder */ id = identification_create_from_sockaddr(dst->get_sockaddr(dst)); - nat_oa = id_payload_create_from_identification(NAT_OA_V1, id); + nat_oa = id_payload_create_from_identification(nat_oa_payload_type, id); message->add_payload(message, (payload_t*)nat_oa); id->destroy(id); } @@ -697,6 +750,7 @@ METHOD(task_t, build_i, status_t, linked_list_t *list, *tsi, *tsr; proposal_t *proposal; diffie_hellman_group_t group; + encap_t encap; this->udp = this->ike_sa->has_condition(this->ike_sa, COND_NAT_ANY); this->mode = this->config->get_mode(this->config); @@ -767,9 +821,10 @@ METHOD(task_t, build_i, status_t, enumerator->destroy(enumerator); get_lifetimes(this); + encap = get_encap(this->ike_sa, this->udp); sa_payload = sa_payload_create_from_proposals_v1(list, this->lifetime, this->lifebytes, AUTH_NONE, - this->mode, this->udp, this->cpi_i); + this->mode, encap, this->cpi_i); list->destroy_offset(list, offsetof(proposal_t, destroy)); message->add_payload(message, &sa_payload->payload_interface); @@ -1060,6 +1115,7 @@ METHOD(task_t, build_r, status_t, case QM_INIT: { sa_payload_t *sa_payload; + encap_t encap; this->spi_r = this->child_sa->alloc_spi(this->child_sa, PROTO_ESP); if (!this->spi_r) @@ -1086,9 +1142,10 @@ METHOD(task_t, build_r, status_t, add_nat_oa_payloads(this, message); } + encap = get_encap(this->ike_sa, this->udp); sa_payload = sa_payload_create_from_proposal_v1(this->proposal, this->lifetime, this->lifebytes, AUTH_NONE, - this->mode, this->udp, this->cpi_r); + this->mode, encap, this->cpi_r); message->add_payload(message, &sa_payload->payload_interface); if (!add_nonce(this, &this->nonce_r, message)) diff --git a/src/libcharon/sa/ikev2/tasks/child_create.c b/src/libcharon/sa/ikev2/tasks/child_create.c index 46a165546..eb3972c29 100644 --- a/src/libcharon/sa/ikev2/tasks/child_create.c +++ b/src/libcharon/sa/ikev2/tasks/child_create.c @@ -377,6 +377,8 @@ static status_t select_and_install(private_child_create_t *this, if (this->proposal == NULL) { DBG1(DBG_IKE, "no acceptable proposal found"); + charon->bus->alert(charon->bus, ALERT_PROPOSAL_MISMATCH_CHILD, + this->proposals); return FAILED; } this->other_spi = this->proposal->get_spi(this->proposal); @@ -452,6 +454,7 @@ static status_t select_and_install(private_child_create_t *this, if (my_ts->get_count(my_ts) == 0 || other_ts->get_count(other_ts) == 0) { + charon->bus->alert(charon->bus, ALERT_TS_MISMATCH, this->tsi, this->tsr); my_ts->destroy_offset(my_ts, offsetof(traffic_selector_t, destroy)); other_ts->destroy_offset(other_ts, offsetof(traffic_selector_t, destroy)); DBG1(DBG_IKE, "no acceptable traffic selectors found"); @@ -549,6 +552,8 @@ static status_t select_and_install(private_child_create_t *this, (status_i != SUCCESS) ? "inbound " : "", (status_i != SUCCESS && status_o != SUCCESS) ? "and ": "", (status_o != SUCCESS) ? "outbound " : ""); + charon->bus->alert(charon->bus, ALERT_INSTALL_CHILD_SA_FAILED, + this->child_sa); return FAILED; } @@ -581,6 +586,8 @@ static status_t select_and_install(private_child_create_t *this, if (status != SUCCESS) { DBG1(DBG_IKE, "unable to install IPsec policies (SPD) in kernel"); + charon->bus->alert(charon->bus, ALERT_INSTALL_CHILD_POLICY_FAILED, + this->child_sa); return NOT_FOUND; } @@ -982,6 +989,7 @@ static void handle_child_sa_failure(private_child_create_t *this, else { DBG1(DBG_IKE, "failed to establish CHILD_SA, keeping IKE_SA"); + charon->bus->alert(charon->bus, ALERT_KEEP_ON_CHILD_SA_FAILURE); } } @@ -1040,6 +1048,7 @@ METHOD(task_t, build_r, status_t, { DBG1(DBG_IKE, "traffic selectors %#R=== %#R inacceptable", this->tsr, this->tsi); + charon->bus->alert(charon->bus, ALERT_TS_MISMATCH, this->tsi, this->tsr); message->add_notify(message, FALSE, TS_UNACCEPTABLE, chunk_empty); handle_child_sa_failure(this, message); return SUCCESS; @@ -1154,7 +1163,7 @@ METHOD(task_t, process_i, status_t, break; } - /* check for erronous notifies */ + /* check for erroneous notifies */ enumerator = message->create_payload_enumerator(message); while (enumerator->enumerate(enumerator, &payload)) { diff --git a/src/libcharon/sa/ikev2/tasks/ike_auth.c b/src/libcharon/sa/ikev2/tasks/ike_auth.c index b80bc6c80..70efcd7af 100644 --- a/src/libcharon/sa/ikev2/tasks/ike_auth.c +++ b/src/libcharon/sa/ikev2/tasks/ike_auth.c @@ -457,6 +457,7 @@ METHOD(task_t, build_i, status_t, this->reserved); if (!this->my_auth) { + charon->bus->alert(charon->bus, ALERT_LOCAL_AUTH_FAILED); return FAILED; } } @@ -473,6 +474,7 @@ METHOD(task_t, build_i, status_t, case NEED_MORE: break; default: + charon->bus->alert(charon->bus, ALERT_LOCAL_AUTH_FAILED); return FAILED; } @@ -748,7 +750,7 @@ METHOD(task_t, build_r, status_t, this->reserved); if (!this->my_auth) { - goto peer_auth_failed; + goto local_auth_failed; } } } @@ -786,9 +788,7 @@ METHOD(task_t, build_r, status_t, case NEED_MORE: break; default: - message->add_notify(message, TRUE, AUTHENTICATION_FAILED, - chunk_empty); - return FAILED; + goto local_auth_failed; } } @@ -807,6 +807,7 @@ METHOD(task_t, build_r, status_t, this->ike_sa, FALSE)) { DBG1(DBG_IKE, "cancelling IKE_SA setup due to uniqueness policy"); + charon->bus->alert(charon->bus, ALERT_UNIQUE_KEEP); message->add_notify(message, TRUE, AUTHENTICATION_FAILED, chunk_empty); return FAILED; @@ -830,11 +831,14 @@ METHOD(task_t, build_r, status_t, return NEED_MORE; peer_auth_failed: - message->add_notify(message, TRUE, AUTHENTICATION_FAILED, - chunk_empty); + message->add_notify(message, TRUE, AUTHENTICATION_FAILED, chunk_empty); peer_auth_failed_no_notify: charon->bus->alert(charon->bus, ALERT_PEER_AUTH_FAILED); return FAILED; +local_auth_failed: + message->add_notify(message, TRUE, AUTHENTICATION_FAILED, chunk_empty); + charon->bus->alert(charon->bus, ALERT_LOCAL_AUTH_FAILED); + return FAILED; } METHOD(task_t, process_i, status_t, @@ -987,6 +991,7 @@ METHOD(task_t, process_i, status_t, case NEED_MORE: break; default: + charon->bus->alert(charon->bus, ALERT_LOCAL_AUTH_FAILED); return FAILED; } } diff --git a/src/libcharon/sa/ikev2/tasks/ike_cert_pre.c b/src/libcharon/sa/ikev2/tasks/ike_cert_pre.c index 60e878777..2cbe8f8c5 100644 --- a/src/libcharon/sa/ikev2/tasks/ike_cert_pre.c +++ b/src/libcharon/sa/ikev2/tasks/ike_cert_pre.c @@ -57,6 +57,72 @@ struct private_ike_cert_pre_t { }; /** + * Process a single certificate request payload + */ +static void process_certreq(private_ike_cert_pre_t *this, + certreq_payload_t *certreq, auth_cfg_t *auth) +{ + enumerator_t *enumerator; + u_int unknown = 0; + chunk_t keyid; + + this->ike_sa->set_condition(this->ike_sa, COND_CERTREQ_SEEN, TRUE); + + if (certreq->get_cert_type(certreq) != CERT_X509) + { + DBG1(DBG_IKE, "cert payload %N not supported - ignored", + certificate_type_names, certreq->get_cert_type(certreq)); + return; + } + + enumerator = certreq->create_keyid_enumerator(certreq); + while (enumerator->enumerate(enumerator, &keyid)) + { + identification_t *id; + certificate_t *cert; + + id = identification_create_from_encoding(ID_KEY_ID, keyid); + cert = lib->credmgr->get_cert(lib->credmgr, + CERT_X509, KEY_ANY, id, TRUE); + if (cert) + { + DBG1(DBG_IKE, "received cert request for \"%Y\"", + cert->get_subject(cert)); + auth->add(auth, AUTH_RULE_CA_CERT, cert); + } + else + { + DBG2(DBG_IKE, "received cert request for unknown ca with keyid %Y", + id); + unknown++; + } + id->destroy(id); + } + enumerator->destroy(enumerator); + if (unknown) + { + DBG1(DBG_IKE, "received %u cert requests for an unknown ca", + unknown); + } +} + +/** + * Process a single notify payload + */ +static void process_notify(private_ike_cert_pre_t *this, + notify_payload_t *notify) +{ + switch (notify->get_notify_type(notify)) + { + case HTTP_CERT_LOOKUP_SUPPORTED: + this->ike_sa->enable_extension(this->ike_sa, EXT_HASH_AND_URL); + break; + default: + break; + } +} + +/** * read certificate requests */ static void process_certreqs(private_ike_cert_pre_t *this, message_t *message) @@ -73,62 +139,11 @@ static void process_certreqs(private_ike_cert_pre_t *this, message_t *message) switch (payload->get_type(payload)) { case CERTIFICATE_REQUEST: - { - certreq_payload_t *certreq = (certreq_payload_t*)payload; - enumerator_t *enumerator; - u_int unknown = 0; - chunk_t keyid; - - this->ike_sa->set_condition(this->ike_sa, COND_CERTREQ_SEEN, TRUE); - - if (certreq->get_cert_type(certreq) != CERT_X509) - { - DBG1(DBG_IKE, "cert payload %N not supported - ignored", - certificate_type_names, certreq->get_cert_type(certreq)); - break; - } - enumerator = certreq->create_keyid_enumerator(certreq); - while (enumerator->enumerate(enumerator, &keyid)) - { - identification_t *id; - certificate_t *cert; - - id = identification_create_from_encoding(ID_KEY_ID, keyid); - cert = lib->credmgr->get_cert(lib->credmgr, - CERT_X509, KEY_ANY, id, TRUE); - if (cert) - { - DBG1(DBG_IKE, "received cert request for \"%Y\"", - cert->get_subject(cert)); - auth->add(auth, AUTH_RULE_CA_CERT, cert); - } - else - { - DBG2(DBG_IKE, "received cert request for unknown ca " - "with keyid %Y", id); - unknown++; - } - id->destroy(id); - } - enumerator->destroy(enumerator); - if (unknown) - { - DBG1(DBG_IKE, "received %u cert requests for an unknown ca", - unknown); - } + process_certreq(this, (certreq_payload_t*)payload, auth); break; - } case NOTIFY: - { - notify_payload_t *notify = (notify_payload_t*)payload; - - /* we only handle one type of notify here */ - if (notify->get_notify_type(notify) == HTTP_CERT_LOOKUP_SUPPORTED) - { - this->ike_sa->enable_extension(this->ike_sa, EXT_HASH_AND_URL); - } + process_notify(this, (notify_payload_t*)payload); break; - } default: /* ignore other payloads here, these are handled elsewhere */ break; @@ -177,7 +192,75 @@ static certificate_t *try_get_cert(cert_payload_t *cert_payload) } /** - * import certificates + * Process a X509 certificate payload + */ +static void process_x509(cert_payload_t *payload, auth_cfg_t *auth, + cert_encoding_t encoding, bool *first) +{ + certificate_t *cert; + char *url; + + cert = try_get_cert(payload); + if (cert) + { + if (*first) + { /* the first is an end entity certificate */ + DBG1(DBG_IKE, "received end entity cert \"%Y\"", + cert->get_subject(cert)); + auth->add(auth, AUTH_HELPER_SUBJECT_CERT, cert); + *first = FALSE; + } + else + { + DBG1(DBG_IKE, "received issuer cert \"%Y\"", + cert->get_subject(cert)); + auth->add(auth, AUTH_HELPER_IM_CERT, cert); + } + } + else if (encoding == ENC_X509_HASH_AND_URL) + { + /* we fetch the certificate not yet, but only if + * it is really needed during authentication */ + url = payload->get_url(payload); + if (!url) + { + DBG1(DBG_IKE, "received invalid hash-and-url " + "encoded cert, ignore"); + return; + } + url = strdup(url); + if (first) + { /* first URL is for an end entity certificate */ + DBG1(DBG_IKE, "received hash-and-url for end entity cert \"%s\"", + url); + auth->add(auth, AUTH_HELPER_SUBJECT_HASH_URL, url); + first = FALSE; + } + else + { + DBG1(DBG_IKE, "received hash-and-url for issuer cert \"%s\"", url); + auth->add(auth, AUTH_HELPER_IM_HASH_URL, url); + } + } +} + +/** + * Process a CRL certificate payload + */ +static void process_crl(cert_payload_t *payload, auth_cfg_t *auth) +{ + certificate_t *cert; + + cert = payload->get_cert(payload); + if (cert) + { + DBG1(DBG_IKE, "received CRL \"%Y\"", cert->get_subject(cert)); + auth->add(auth, AUTH_HELPER_REVOCATION_CERT, cert); + } +} + +/** + * Process certificate payloads */ static void process_certs(private_ike_cert_pre_t *this, message_t *message) { @@ -195,8 +278,6 @@ static void process_certs(private_ike_cert_pre_t *this, message_t *message) { cert_payload_t *cert_payload; cert_encoding_t encoding; - certificate_t *cert; - char *url; cert_payload = (cert_payload_t*)payload; encoding = cert_payload->get_cert_encoding(cert_payload); @@ -204,70 +285,18 @@ static void process_certs(private_ike_cert_pre_t *this, message_t *message) switch (encoding) { case ENC_X509_HASH_AND_URL: - { if (!this->do_http_lookup) { - DBG1(DBG_IKE, "received hash-and-url encoded cert, but" - " we don't accept them, ignore"); + DBG1(DBG_IKE, "received hash-and-url encoded cert, but " + "we don't accept them, ignore"); break; } /* FALL */ - } case ENC_X509_SIGNATURE: - { - cert = try_get_cert(cert_payload); - if (cert) - { - if (first) - { /* the first is an end entity certificate */ - DBG1(DBG_IKE, "received end entity cert \"%Y\"", - cert->get_subject(cert)); - auth->add(auth, AUTH_HELPER_SUBJECT_CERT, cert); - first = FALSE; - } - else - { - DBG1(DBG_IKE, "received issuer cert \"%Y\"", - cert->get_subject(cert)); - auth->add(auth, AUTH_HELPER_IM_CERT, cert); - } - } - else if (encoding == ENC_X509_HASH_AND_URL) - { - /* we fetch the certificate not yet, but only if - * it is really needed during authentication */ - url = cert_payload->get_url(cert_payload); - if (!url) - { - DBG1(DBG_IKE, "received invalid hash-and-url " - "encoded cert, ignore"); - break; - } - url = strdup(url); - if (first) - { /* first URL is for an end entity certificate */ - DBG1(DBG_IKE, "received hash-and-url for end" - " entity cert \"%s\"", url); - auth->add(auth, AUTH_HELPER_SUBJECT_HASH_URL, url); - first = FALSE; - } - else - { - DBG1(DBG_IKE, "received hash-and-url for issuer" - " cert \"%s\"", url); - auth->add(auth, AUTH_HELPER_IM_HASH_URL, url); - } - } + process_x509(cert_payload, auth, encoding, &first); break; - } case ENC_CRL: - cert = cert_payload->get_cert(cert_payload); - if (cert) - { - DBG1(DBG_IKE, "received CRL \"%Y\"", - cert->get_subject(cert)); - auth->add(auth, AUTH_HELPER_REVOCATION_CERT, cert); - } + process_crl(cert_payload, auth); break; case ENC_PKCS7_WRAPPED_X509: case ENC_PGP: diff --git a/src/libcharon/sa/ikev2/tasks/ike_config.c b/src/libcharon/sa/ikev2/tasks/ike_config.c index c44f0452c..d637c26fe 100644 --- a/src/libcharon/sa/ikev2/tasks/ike_config.c +++ b/src/libcharon/sa/ikev2/tasks/ike_config.c @@ -380,6 +380,7 @@ METHOD(task_t, build_r, status_t, { DBG1(DBG_IKE, "no virtual IP found, sending %N", notify_type_names, INTERNAL_ADDRESS_FAILURE); + charon->bus->alert(charon->bus, ALERT_VIP_FAILURE, this->vips); message->add_notify(message, FALSE, INTERNAL_ADDRESS_FAILURE, chunk_empty); vips->destroy_offset(vips, offsetof(host_t, destroy)); @@ -390,6 +391,7 @@ METHOD(task_t, build_r, status_t, { DBG1(DBG_IKE, "expected a virtual IP request, sending %N", notify_type_names, FAILED_CP_REQUIRED); + charon->bus->alert(charon->bus, ALERT_VIP_FAILURE, this->vips); message->add_notify(message, FALSE, FAILED_CP_REQUIRED, chunk_empty); vips->destroy_offset(vips, offsetof(host_t, destroy)); pools->destroy(pools); diff --git a/src/libcharon/sa/ikev2/tasks/ike_init.c b/src/libcharon/sa/ikev2/tasks/ike_init.c index f2a06735e..7542937b3 100644 --- a/src/libcharon/sa/ikev2/tasks/ike_init.c +++ b/src/libcharon/sa/ikev2/tasks/ike_init.c @@ -187,6 +187,11 @@ static void process_payloads(private_ike_init_t *this, message_t *message) EXT_STRONGSWAN); this->proposal = this->config->select_proposal(this->config, proposal_list, private); + if (!this->proposal) + { + charon->bus->alert(charon->bus, ALERT_PROPOSAL_MISMATCH_IKE, + proposal_list); + } proposal_list->destroy_offset(proposal_list, offsetof(proposal_t, destroy)); break; @@ -421,7 +426,7 @@ METHOD(task_t, process_i, status_t, enumerator_t *enumerator; payload_t *payload; - /* check for erronous notifies */ + /* check for erroneous notifies */ enumerator = message->create_payload_enumerator(message); while (enumerator->enumerate(enumerator, &payload)) { diff --git a/src/libimcv/imv/imv_msg.c b/src/libimcv/imv/imv_msg.c index 81209ce57..4ed19dd13 100644 --- a/src/libimcv/imv/imv_msg.c +++ b/src/libimcv/imv/imv_msg.c @@ -195,6 +195,12 @@ METHOD(imv_msg_t, send_assessment, TNC_Result, char *lang_code = NULL, *uri = NULL; enumerator_t *e; + /* Remove any attributes that have already been constructed */ + while (this->attr_list->remove_last(this->attr_list, (void**)&attr) == SUCCESS) + { + attr->destroy(attr); + } + /* Send an IETF Assessment Result attribute if enabled */ if (lib->settings->get_bool(lib->settings, "libimcv.assessment_result", TRUE)) @@ -306,17 +312,6 @@ METHOD(imv_msg_t, receive, TNC_Result, return TNC_RESULT_SUCCESS; } -METHOD(imv_msg_t, delete_attributes, void, - private_imv_msg_t *this) -{ - pa_tnc_attr_t *attr; - - while (this->attr_list->remove_last(this->attr_list, (void**)&attr) == SUCCESS) - { - attr->destroy(attr); - } -} - METHOD(imv_msg_t, create_attribute_enumerator, enumerator_t*, private_imv_msg_t *this) { @@ -361,7 +356,6 @@ imv_msg_t *imv_msg_create(imv_agent_t *agent, imv_state_t *state, .send_assessment = _send_assessment, .receive = _receive, .add_attribute = _add_attribute, - .delete_attributes = _delete_attributes, .create_attribute_enumerator = _create_attribute_enumerator, .get_encoding = _get_encoding, .destroy = _destroy, diff --git a/src/libimcv/imv/imv_msg.h b/src/libimcv/imv/imv_msg.h index 92347934f..b639712e8 100644 --- a/src/libimcv/imv/imv_msg.h +++ b/src/libimcv/imv/imv_msg.h @@ -85,12 +85,6 @@ struct imv_msg_t { void (*add_attribute)(imv_msg_t *this, pa_tnc_attr_t *attr); /** - * Delete all PA-TNC attributes in the send queue - * - */ - void (*delete_attributes)(imv_msg_t *this); - - /** * Enumerator over PA-TNC attributes contained in the PA-TNC message * * @return PA-TNC attribute enumerator diff --git a/src/libimcv/plugins/imv_os/pacman.sh b/src/libimcv/plugins/imv_os/pacman.sh index a17a0839a..e9134ea5d 100755 --- a/src/libimcv/plugins/imv_os/pacman.sh +++ b/src/libimcv/plugins/imv_os/pacman.sh @@ -5,6 +5,10 @@ DEBIAN=http://packages.debian.org UBUNTU=http://packages.ubuntu.com UBUNTU_VERSIONS="quantal precise oneiric lucid" PACKAGES=allpackages?format=txt.gz +PACMAN=/usr/libexec/ipsec/pacman +DIR=/etc/pts + +cd $DIR for v in $UBUNTU_VERSIONS do @@ -15,22 +19,22 @@ done wget $DEBIAN/stable/$PACKAGES -O $DATE-squeeze.txt.gz gunzip *.gz -ipsec pacman --product "Ubuntu 12.10" --file $DATE-quantal.txt +$PACMAN --product "Ubuntu 12.10" --file $DATE-quantal.txt echo -ipsec pacman --product "Ubuntu 12.10" --file $DATE-quantal-updates.txt --update +$PACMAN --product "Ubuntu 12.10" --file $DATE-quantal-updates.txt --update echo -ipsec pacman --product "Ubuntu 12.04" --file $DATE-precise.txt +$PACMAN --product "Ubuntu 12.04" --file $DATE-precise.txt echo -ipsec pacman --product "Ubuntu 12.04" --file $DATE-precise-updates.txt --update +$PACMAN --product "Ubuntu 12.04" --file $DATE-precise-updates.txt --update echo -ipsec pacman --product "Ubuntu 11.10" --file $DATE-oneiric.txt +$PACMAN --product "Ubuntu 11.10" --file $DATE-oneiric.txt echo -ipsec pacman --product "Ubuntu 11.10" --file $DATE-oneiric-updates.txt --update +$PACMAN --product "Ubuntu 11.10" --file $DATE-oneiric-updates.txt --update echo -ipsec pacman --product "Ubuntu 10.04" --file $DATE-lucid.txt +$PACMAN --product "Ubuntu 10.04" --file $DATE-lucid.txt echo -ipsec pacman --product "Ubuntu 10.04" --file $DATE-lucid-updates.txt --update +$PACMAN --product "Ubuntu 10.04" --file $DATE-lucid-updates.txt --update echo -ipsec pacman --product "Debian squeeze" --file $DATE-squeeze.txt +$PACMAN --product "Debian squeeze" --file $DATE-squeeze.txt cp config.db config.db-$DATE diff --git a/src/libpts/plugins/imv_attestation/imv_attestation.c b/src/libpts/plugins/imv_attestation/imv_attestation.c index 237300f31..3c5488eba 100644 --- a/src/libpts/plugins/imv_attestation/imv_attestation.c +++ b/src/libpts/plugins/imv_attestation/imv_attestation.c @@ -285,9 +285,8 @@ static TNC_Result receive_message(imv_state_t *state, imv_msg_t *in_msg) if (fatal_error || result != TNC_RESULT_SUCCESS) { - out_msg->delete_attributes(out_msg); state->set_recommendation(state, - TNC_IMV_ACTION_RECOMMENDATION_ISOLATE, + TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION, TNC_IMV_EVALUATION_RESULT_ERROR); result = out_msg->send_assessment(out_msg); out_msg->destroy(out_msg); @@ -315,7 +314,6 @@ static TNC_Result receive_message(imv_state_t *state, imv_msg_t *in_msg) state->set_recommendation(state, TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION, TNC_IMV_EVALUATION_RESULT_ERROR); - out_msg->delete_attributes(out_msg); result = out_msg->send_assessment(out_msg); out_msg->destroy(out_msg); if (result != TNC_RESULT_SUCCESS) diff --git a/src/libstrongswan/Android.mk b/src/libstrongswan/Android.mk index c7c5d4bcc..65cfe5292 100644 --- a/src/libstrongswan/Android.mk +++ b/src/libstrongswan/Android.mk @@ -6,8 +6,7 @@ LOCAL_SRC_FILES := \ library.c \ asn1/asn1.c asn1/asn1_parser.c asn1/oid.c bio/bio_reader.c bio/bio_writer.c \ collections/blocking_queue.c collections/enumerator.c collections/hashtable.c \ -collections/linked_list.c \ -crypto/crypters/crypter.c crypto/hashers/hasher.c crypto/pkcs7.c crypto/pkcs9.c \ +collections/linked_list.c crypto/crypters/crypter.c crypto/hashers/hasher.c \ crypto/proposal/proposal_keywords.c crypto/proposal/proposal_keywords_static.c \ crypto/prfs/prf.c crypto/prfs/mac_prf.c \ crypto/rngs/rng.c crypto/prf_plus.c crypto/signers/signer.c \ @@ -18,6 +17,7 @@ credentials/cred_encoding.c credentials/keys/private_key.c \ credentials/keys/public_key.c credentials/keys/shared_key.c \ credentials/certificates/certificate.c credentials/certificates/crl.c \ credentials/certificates/ocsp_response.c \ +credentials/containers/container.c \ credentials/ietf_attributes/ietf_attributes.c credentials/credential_manager.c \ credentials/sets/auth_cfg_wrapper.c credentials/sets/ocsp_response_wrapper.c \ credentials/sets/cert_cache.c credentials/sets/mem_cred.c \ @@ -72,6 +72,8 @@ LOCAL_SRC_FILES += $(call add_plugin, pem) LOCAL_SRC_FILES += $(call add_plugin, pkcs1) +LOCAL_SRC_FILES += $(call add_plugin, pkcs7) + LOCAL_SRC_FILES += $(call add_plugin, pkcs8) LOCAL_SRC_FILES += $(call add_plugin, pkcs11) diff --git a/src/libstrongswan/Makefile.am b/src/libstrongswan/Makefile.am index 56e28d7bc..9c4665eeb 100644 --- a/src/libstrongswan/Makefile.am +++ b/src/libstrongswan/Makefile.am @@ -4,8 +4,7 @@ libstrongswan_la_SOURCES = \ library.c \ asn1/asn1.c asn1/asn1_parser.c asn1/oid.c bio/bio_reader.c bio/bio_writer.c \ collections/blocking_queue.c collections/enumerator.c collections/hashtable.c \ -collections/linked_list.c \ -crypto/crypters/crypter.c crypto/hashers/hasher.c crypto/pkcs7.c crypto/pkcs9.c \ +collections/linked_list.c crypto/crypters/crypter.c crypto/hashers/hasher.c \ crypto/proposal/proposal_keywords.c crypto/proposal/proposal_keywords_static.c \ crypto/prfs/prf.c crypto/prfs/mac_prf.c \ crypto/rngs/rng.c crypto/prf_plus.c crypto/signers/signer.c \ @@ -16,6 +15,7 @@ credentials/cred_encoding.c credentials/keys/private_key.c \ credentials/keys/public_key.c credentials/keys/shared_key.c \ credentials/certificates/certificate.c credentials/certificates/crl.c \ credentials/certificates/ocsp_response.c \ +credentials/containers/container.c \ credentials/ietf_attributes/ietf_attributes.c credentials/credential_manager.c \ credentials/sets/auth_cfg_wrapper.c credentials/sets/ocsp_response_wrapper.c \ credentials/sets/cert_cache.c credentials/sets/mem_cred.c \ @@ -40,8 +40,7 @@ asn1/asn1.h asn1/asn1_parser.h asn1/oid.h bio/bio_reader.h bio/bio_writer.h \ collections/blocking_queue.h collections/enumerator.h collections/hashtable.h \ collections/linked_list.h \ crypto/crypters/crypter.h crypto/hashers/hasher.h crypto/mac.h \ -crypto/pkcs7.h crypto/pkcs9.h crypto/proposal/proposal_keywords.h \ -crypto/proposal/proposal_keywords_static.h \ +crypto/proposal/proposal_keywords.h crypto/proposal/proposal_keywords_static.h \ crypto/prfs/prf.h crypto/prfs/mac_prf.h crypto/rngs/rng.h crypto/nonce_gen.h \ crypto/prf_plus.h crypto/signers/signer.h crypto/signers/mac_signer.h \ crypto/crypto_factory.h crypto/crypto_tester.h crypto/diffie_hellman.h \ @@ -54,6 +53,7 @@ credentials/certificates/ac.h credentials/certificates/crl.h \ credentials/certificates/pkcs10.h credentials/certificates/ocsp_request.h \ credentials/certificates/ocsp_response.h \ credentials/certificates/pgp_certificate.h \ +credentials/containers/container.h credentials/containers/pkcs7.h \ credentials/ietf_attributes/ietf_attributes.h \ credentials/credential_manager.h credentials/sets/auth_cfg_wrapper.h \ credentials/sets/ocsp_response_wrapper.h credentials/sets/cert_cache.h \ @@ -204,6 +204,13 @@ if MONOLITHIC endif endif +if USE_RDRAND + SUBDIRS += plugins/rdrand +if MONOLITHIC + libstrongswan_la_LIBADD += plugins/rdrand/libstrongswan-rdrand.la +endif +endif + if USE_RANDOM SUBDIRS += plugins/random if MONOLITHIC @@ -274,6 +281,13 @@ if MONOLITHIC endif endif +if USE_PKCS7 + SUBDIRS += plugins/pkcs7 +if MONOLITHIC + libstrongswan_la_LIBADD += plugins/pkcs7/libstrongswan-pkcs7.la +endif +endif + if USE_PKCS8 SUBDIRS += plugins/pkcs8 if MONOLITHIC diff --git a/src/libstrongswan/bio/bio_writer.h b/src/libstrongswan/bio/bio_writer.h index 57a5c3d38..2ac4f3556 100644 --- a/src/libstrongswan/bio/bio_writer.h +++ b/src/libstrongswan/bio/bio_writer.h @@ -126,8 +126,11 @@ struct bio_writer_t { void (*wrap32)(bio_writer_t *this); /** - * Skips len bytes in the buffer before the next data is written, returns - * a chunk covering the skipped bytes. + * Skips len bytes in the buffer, return chunk of skipped data. + * + * The returned chunk is not valid after calling any other writer function + * (except get_buf()), because a buffer reallocation might move the + * internal buffer to a different memory location! * * @param len number of bytes to skip * @return chunk pointing to skipped bytes in the internal buffer diff --git a/src/libstrongswan/credentials/builder.c b/src/libstrongswan/credentials/builder.c index b86ca5e2f..f5858382f 100644 --- a/src/libstrongswan/credentials/builder.c +++ b/src/libstrongswan/credentials/builder.c @@ -19,6 +19,7 @@ ENUM(builder_part_names, BUILD_FROM_FILE, BUILD_END, "BUILD_FROM_FILE", "BUILD_FROM_FD", "BUILD_AGENT_SOCKET", + "BUILD_BLOB", "BUILD_BLOB_ASN1_DER", "BUILD_BLOB_PEM", "BUILD_BLOB_PGP", @@ -36,6 +37,7 @@ ENUM(builder_part_names, BUILD_FROM_FILE, BUILD_END, "BUILD_NOT_AFTER_TIME", "BUILD_SERIAL", "BUILD_DIGEST_ALG", + "BUILD_ENCRYPTION_ALG", "BUILD_IETF_GROUP_ATTR", "BUILD_CA_CERT", "BUILD_CERT", @@ -53,6 +55,7 @@ ENUM(builder_part_names, BUILD_FROM_FILE, BUILD_END, "BUILD_REVOKED_ENUMERATOR", "BUILD_BASE_CRL", "BUILD_CHALLENGE_PWD", + "BUILD_PKCS7_ATTRIBUTE", "BUILD_PKCS11_MODULE", "BUILD_PKCS11_SLOT", "BUILD_PKCS11_KEYID", diff --git a/src/libstrongswan/credentials/builder.h b/src/libstrongswan/credentials/builder.h index 23bd1d5dc..740041aac 100644 --- a/src/libstrongswan/credentials/builder.h +++ b/src/libstrongswan/credentials/builder.h @@ -49,6 +49,8 @@ enum builder_part_t { BUILD_FROM_FD, /** unix socket of a ssh/pgp agent, char* */ BUILD_AGENT_SOCKET, + /** An arbitrary blob of data, chunk_t */ + BUILD_BLOB, /** DER encoded ASN.1 blob, chunk_t */ BUILD_BLOB_ASN1_DER, /** PEM encoded ASN.1/PGP blob, chunk_t */ @@ -81,8 +83,10 @@ enum builder_part_t { BUILD_NOT_AFTER_TIME, /** a serial number in binary form, chunk_t */ BUILD_SERIAL, - /** digest algorithm to be used for signature, int */ + /** digest algorithm to be used for signature, hash_algorithm_t */ BUILD_DIGEST_ALG, + /** encryption algorithm to use, encryption_algorithm_t */ + BUILD_ENCRYPTION_ALG, /** a comma-separated list of ietf group attributes, char* */ BUILD_IETF_GROUP_ATTR, /** a ca certificate, certificate_t* */ @@ -117,6 +121,8 @@ enum builder_part_t { BUILD_BASE_CRL, /** PKCS#10 challenge password */ BUILD_CHALLENGE_PWD, + /** PKCS#7 attribute, int oid, chunk_t with ASN1 type encoded value */ + BUILD_PKCS7_ATTRIBUTE, /** friendly name of a PKCS#11 module, null terminated char* */ BUILD_PKCS11_MODULE, /** slot specifier for a token in a PKCS#11 module, int */ diff --git a/src/libstrongswan/credentials/containers/container.c b/src/libstrongswan/credentials/containers/container.c new file mode 100644 index 000000000..d1e67b21b --- /dev/null +++ b/src/libstrongswan/credentials/containers/container.c @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2012 Martin Willi + * Copyright (C) 2012 revosec 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. + */ + +#include "container.h" + +ENUM(container_type_names, CONTAINER_PKCS7, CONTAINER_PKCS7_ENVELOPED_DATA, + "PKCS7", + "PKCS7_DATA", + "PKCS7_SIGNED_DATA", + "PKCS7_ENVELOPED_DATA", +); diff --git a/src/libstrongswan/credentials/containers/container.h b/src/libstrongswan/credentials/containers/container.h new file mode 100644 index 000000000..fc5c09041 --- /dev/null +++ b/src/libstrongswan/credentials/containers/container.h @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2012 Martin Willi + * Copyright (C) 2012 revosec 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. + */ + +/** + * @defgroup container container + * @{ @ingroup containers + */ + +#ifndef CONTAINER_H_ +#define CONTAINER_H_ + +typedef struct container_t container_t; +typedef enum container_type_t container_type_t; + +#include <utils/chunk.h> +#include <collections/enumerator.h> + +/** + * Type of the container. + */ +enum container_type_t { + /** Any kind of PKCS7/CMS container */ + CONTAINER_PKCS7, + /** PKCS7/CMS plain "data" */ + CONTAINER_PKCS7_DATA, + /** PKCS7/CMS "signed-data" */ + CONTAINER_PKCS7_SIGNED_DATA, + /** PKCS7/CMS "enveloped-data" */ + CONTAINER_PKCS7_ENVELOPED_DATA, +}; + +/** + * Enum names for container_type_t + */ +extern enum_name_t *container_type_names; + +/** + * Generic interface for cryptographic containers. + */ +struct container_t { + + /** + * Get the type of the container. + * + * @return container type + */ + container_type_t (*get_type)(container_t *this); + + /** + * Create an enumerator over trustchains for valid container signatures. + * + * @return enumerator over auth_cfg_t* + */ + enumerator_t* (*create_signature_enumerator)(container_t *this); + + /** + * Get signed/decrypted data wrapped in this container. + * + * This function does not verify any associated signatures, use + * create_signature_enumerator() to verify them. + * + * @param data allocated data wrapped in this container + * @return TRUE if data decrypted successfully + */ + bool (*get_data)(container_t *this, chunk_t *data); + + /** + * Get the encoding of the full signed/encrypted container. + * + * @param data allocated container encoding + * @return TRUE if encodign successful + */ + bool (*get_encoding)(container_t *this, chunk_t *encoding); + + /** + * Destroy a container_t. + */ + void (*destroy)(container_t *this); +}; + +#endif /** CONTAINER_H_ @}*/ diff --git a/src/libstrongswan/credentials/containers/pkcs7.h b/src/libstrongswan/credentials/containers/pkcs7.h new file mode 100644 index 000000000..d42d82b0b --- /dev/null +++ b/src/libstrongswan/credentials/containers/pkcs7.h @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2012 Martin Willi + * Copyright (C) 2012 revosec 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. + */ + +/** + * @defgroup pkcs7 pkcs7 + * @{ @ingroup containers + */ + +#ifndef PKCS7_H_ +#define PKCS7_H_ + +#include <credentials/containers/container.h> + +typedef struct pkcs7_t pkcs7_t; + +/** + * PKCS#7/CMS container type. + */ +struct pkcs7_t { + + /** + * Implements container_t. + */ + container_t container; + + /** + * Get an authenticated PKCS#9 attribute from PKCS#7 signerInfo. + * + * To select the signerInfo structure to get the attribute from, pass + * the enumerator position from container_t.create_signature_enumerator(). + * + * The attribute returned does not contain type information and must be + * freed after use. + * + * @param oid OID from the attribute to get + * @param enumerator enumerator to select signerInfo + * @param value chunk receiving attribute value, allocated + * @return TRUE if attribute found + */ + bool (*get_attribute)(pkcs7_t *this, int oid, enumerator_t *enumerator, + chunk_t *value); + + /** + * Create an enumerator over attached certificates. + * + * @return enumerator over certificate_t + */ + enumerator_t* (*create_cert_enumerator)(pkcs7_t *this); +}; + +#endif /** PKCS7_H_ @}*/ diff --git a/src/libstrongswan/credentials/credential_factory.c b/src/libstrongswan/credentials/credential_factory.c index 71a3f9e07..94c7820e1 100644 --- a/src/libstrongswan/credentials/credential_factory.c +++ b/src/libstrongswan/credentials/credential_factory.c @@ -22,12 +22,13 @@ #include <threading/thread_value.h> #include <threading/rwlock.h> #include <credentials/certificates/x509.h> +#include <credentials/containers/container.h> -ENUM(credential_type_names, CRED_PRIVATE_KEY, CRED_CERTIFICATE, +ENUM(credential_type_names, CRED_PRIVATE_KEY, CRED_CONTAINER, "CRED_PRIVATE_KEY", "CRED_PUBLIC_KEY", "CRED_CERTIFICATE", - "CRED_PLUTO_CERT", + "CRED_CONTAINER", ); typedef struct private_credential_factory_t private_credential_factory_t; @@ -139,11 +140,21 @@ METHOD(credential_factory_t, create, void*, if (!construct && !level) { - enum_name_t *names = key_type_names; + enum_name_t *names; - if (type == CRED_CERTIFICATE) + switch (type) { - names = certificate_type_names; + case CRED_CERTIFICATE: + names = certificate_type_names; + break; + case CRED_CONTAINER: + names = container_type_names; + break; + case CRED_PRIVATE_KEY: + case CRED_PUBLIC_KEY: + default: + names = key_type_names; + break; } DBG1(DBG_LIB, "building %N - %N failed, tried %d builders", credential_type_names, type, names, subtype, failures); diff --git a/src/libstrongswan/credentials/credential_factory.h b/src/libstrongswan/credentials/credential_factory.h index c31601245..55b669529 100644 --- a/src/libstrongswan/credentials/credential_factory.h +++ b/src/libstrongswan/credentials/credential_factory.h @@ -28,6 +28,9 @@ typedef enum credential_type_t credential_type_t; /** * Kind of credential. + * + * While crypto containers are not really credentials, we still use the + * credential factory and builders create them. */ enum credential_type_t { /** private key, implemented in private_key_t */ @@ -36,6 +39,8 @@ enum credential_type_t { CRED_PUBLIC_KEY, /** certificates, implemented in certificate_t */ CRED_CERTIFICATE, + /** crypto container, implemented in container_t */ + CRED_CONTAINER, }; /** diff --git a/src/libstrongswan/crypto/nonce_gen.h b/src/libstrongswan/crypto/nonce_gen.h index 50f3c0090..7dae4f776 100644 --- a/src/libstrongswan/crypto/nonce_gen.h +++ b/src/libstrongswan/crypto/nonce_gen.h @@ -35,7 +35,7 @@ struct nonce_gen_t { * * @param size size of nonce in bytes * @param buffer pointer where the generated nonce will be written - * @return TRUE if nonce allocation was succesful, FALSE otherwise + * @return TRUE if nonce allocation was successful, FALSE otherwise */ bool (*get_nonce)(nonce_gen_t *this, size_t size, u_int8_t *buffer) __attribute__((warn_unused_result)); @@ -45,7 +45,7 @@ struct nonce_gen_t { * * @param size size of nonce in bytes * @param chunk chunk which will hold the generated nonce - * @return TRUE if nonce allocation was succesful, FALSE otherwise + * @return TRUE if nonce allocation was successful, FALSE otherwise */ bool (*allocate_nonce)(nonce_gen_t *this, size_t size, chunk_t *chunk) __attribute__((warn_unused_result)); diff --git a/src/libstrongswan/crypto/pkcs7.c b/src/libstrongswan/crypto/pkcs7.c deleted file mode 100644 index 687603589..000000000 --- a/src/libstrongswan/crypto/pkcs7.c +++ /dev/null @@ -1,1061 +0,0 @@ -/* - * Copyright (C) 2012 Tobias Brunner - * Copyright (C) 2002-2008 Andreas Steffen - * Copyright (C) 2005 Jan Hutter, Martin Willi - * Hochschule fuer Technik Rapperswil, Switzerland - * - * 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. - */ - -#include <stdlib.h> -#include <string.h> -#include <time.h> - -#include <library.h> -#include <utils/debug.h> - -#include <asn1/oid.h> -#include <asn1/asn1.h> -#include <asn1/asn1_parser.h> -#include <credentials/certificates/x509.h> -#include <credentials/keys/public_key.h> -#include <crypto/pkcs9.h> -#include <crypto/hashers/hasher.h> -#include <crypto/crypters/crypter.h> -#include <collections/linked_list.h> - -#include "pkcs7.h" - -typedef struct private_pkcs7_t private_pkcs7_t; - -/** - * Private data of a pkcs7_t object. - */ -struct private_pkcs7_t { - /** - * Public interface for this certificate. - */ - pkcs7_t public; - - /** - * contentInfo type - */ - int type; - - /** - * ASN.1 encoded content - */ - chunk_t content; - - /** - * ASN.1 parsing start level - */ - u_int level; - - /** - * retrieved data - */ - chunk_t data; - - /** - * ASN.1 encoded attributes - */ - pkcs9_t *attributes; - - /** - * Linked list of X.509 certificates - */ - linked_list_t *certs; -}; - -METHOD(pkcs7_t, is_data, bool, - private_pkcs7_t *this) -{ - return this->type == OID_PKCS7_DATA; -} - -METHOD(pkcs7_t, is_signedData, bool, - private_pkcs7_t *this) -{ - return this->type == OID_PKCS7_SIGNED_DATA; -} - -METHOD(pkcs7_t, is_envelopedData, bool, - private_pkcs7_t *this) -{ - return this->type == OID_PKCS7_ENVELOPED_DATA; -} - -/** - * ASN.1 definition of the PKCS#7 ContentInfo type - */ -static const asn1Object_t contentInfoObjects[] = { - { 0, "contentInfo", ASN1_SEQUENCE, ASN1_NONE }, /* 0 */ - { 1, "contentType", ASN1_OID, ASN1_BODY }, /* 1 */ - { 1, "content", ASN1_CONTEXT_C_0, ASN1_OPT | - ASN1_BODY }, /* 2 */ - { 1, "end opt", ASN1_EOC, ASN1_END }, /* 3 */ - { 0, "exit", ASN1_EOC, ASN1_EXIT } -}; -#define PKCS7_INFO_TYPE 1 -#define PKCS7_INFO_CONTENT 2 - -/** - * Parse PKCS#7 contentInfo object - */ -static bool parse_contentInfo(private_pkcs7_t *this) -{ - asn1_parser_t *parser; - chunk_t object; - int objectID; - bool success = FALSE; - - if (!this->data.ptr) - { - return FALSE; - } - - parser = asn1_parser_create(contentInfoObjects, this->data); - parser->set_top_level(parser, this->level); - - while (parser->iterate(parser, &objectID, &object)) - { - if (objectID == PKCS7_INFO_TYPE) - { - this->type = asn1_known_oid(object); - if (this->type < OID_PKCS7_DATA || - this->type > OID_PKCS7_ENCRYPTED_DATA) - { - DBG1(DBG_LIB, "unknown pkcs7 content type"); - goto end; - } - } - else if (objectID == PKCS7_INFO_CONTENT && object.len > 0) - { - chunk_free(&this->content); - this->content = chunk_clone(object); - } - } - success = parser->success(parser); - - if (success) - { - this->level += 2; - chunk_free(&this->data); - } - -end: - parser->destroy(parser); - return success; -} - -/** - * Check whether to abort the requested parsing - */ -static bool abort_parsing(private_pkcs7_t *this, int type) -{ - if (this->type != type) - { - DBG1(DBG_LIB, "pkcs7 content to be parsed is not of type '%s'", - oid_names[type].name); - return TRUE; - } - return FALSE; -} - -METHOD(pkcs7_t, parse_data, bool, - private_pkcs7_t *this) -{ - chunk_t data; - - if (!parse_contentInfo(this) || - abort_parsing(this, OID_PKCS7_DATA)) - { - return FALSE; - } - data = this->content; - if (data.len == 0) - { - this->data = chunk_empty; - return TRUE; - } - if (asn1_parse_simple_object(&data, ASN1_OCTET_STRING, - this->level, "data")) - { - this->data = chunk_clone(data); - return TRUE; - } - return FALSE; -} - -/** - * ASN.1 definition of the PKCS#7 signedData type - */ -static const asn1Object_t signedDataObjects[] = { - { 0, "signedData", ASN1_SEQUENCE, ASN1_NONE }, /* 0 */ - { 1, "version", ASN1_INTEGER, ASN1_BODY }, /* 1 */ - { 1, "digestAlgorithms", ASN1_SET, ASN1_LOOP }, /* 2 */ - { 2, "algorithm", ASN1_EOC, ASN1_RAW }, /* 3 */ - { 1, "end loop", ASN1_EOC, ASN1_END }, /* 4 */ - { 1, "contentInfo", ASN1_EOC, ASN1_RAW }, /* 5 */ - { 1, "certificates", ASN1_CONTEXT_C_0, ASN1_OPT | - ASN1_LOOP }, /* 6 */ - { 2, "certificate", ASN1_SEQUENCE, ASN1_OBJ }, /* 7 */ - { 1, "end opt or loop", ASN1_EOC, ASN1_END }, /* 8 */ - { 1, "crls", ASN1_CONTEXT_C_1, ASN1_OPT | - ASN1_LOOP }, /* 9 */ - { 2, "crl", ASN1_SEQUENCE, ASN1_OBJ }, /* 10 */ - { 1, "end opt or loop", ASN1_EOC, ASN1_END }, /* 11 */ - { 1, "signerInfos", ASN1_SET, ASN1_LOOP }, /* 12 */ - { 2, "signerInfo", ASN1_SEQUENCE, ASN1_NONE }, /* 13 */ - { 3, "version", ASN1_INTEGER, ASN1_BODY }, /* 14 */ - { 3, "issuerAndSerialNumber", ASN1_SEQUENCE, ASN1_BODY }, /* 15 */ - { 4, "issuer", ASN1_SEQUENCE, ASN1_OBJ }, /* 16 */ - { 4, "serial", ASN1_INTEGER, ASN1_BODY }, /* 17 */ - { 3, "digestAlgorithm", ASN1_EOC, ASN1_RAW }, /* 18 */ - { 3, "authenticatedAttributes", ASN1_CONTEXT_C_0, ASN1_OPT | - ASN1_OBJ }, /* 19 */ - { 3, "end opt", ASN1_EOC, ASN1_END }, /* 20 */ - { 3, "digestEncryptionAlgorithm", ASN1_EOC, ASN1_RAW }, /* 21 */ - { 3, "encryptedDigest", ASN1_OCTET_STRING, ASN1_BODY }, /* 22 */ - { 3, "unauthenticatedAttributes", ASN1_CONTEXT_C_1, ASN1_OPT }, /* 23 */ - { 3, "end opt", ASN1_EOC, ASN1_END }, /* 24 */ - { 1, "end loop", ASN1_EOC, ASN1_END }, /* 25 */ - { 0, "exit", ASN1_EOC, ASN1_EXIT } -}; -#define PKCS7_SIGNED_VERSION 1 -#define PKCS7_DIGEST_ALG 3 -#define PKCS7_SIGNED_CONTENT_INFO 5 -#define PKCS7_SIGNED_CERT 7 -#define PKCS7_SIGNER_INFO 13 -#define PKCS7_SIGNER_INFO_VERSION 14 -#define PKCS7_SIGNED_ISSUER 16 -#define PKCS7_SIGNED_SERIAL_NUMBER 17 -#define PKCS7_DIGEST_ALGORITHM 18 -#define PKCS7_AUTH_ATTRIBUTES 19 -#define PKCS7_DIGEST_ENC_ALGORITHM 21 -#define PKCS7_ENCRYPTED_DIGEST 22 - -METHOD(pkcs7_t, parse_signedData, bool, - private_pkcs7_t *this, certificate_t *cacert) -{ - asn1_parser_t *parser; - chunk_t object; - int objectID, version; - int digest_alg = OID_UNKNOWN; - int enc_alg = OID_UNKNOWN; - int signerInfos = 0; - bool success = FALSE; - - chunk_t encrypted_digest = chunk_empty; - - if (!parse_contentInfo(this) || - abort_parsing(this, OID_PKCS7_SIGNED_DATA)) - { - return FALSE; - } - - parser = asn1_parser_create(signedDataObjects, this->content); - parser->set_top_level(parser, this->level); - - while (parser->iterate(parser, &objectID, &object)) - { - u_int level = parser->get_level(parser); - - switch (objectID) - { - case PKCS7_SIGNED_VERSION: - version = object.len ? (int)*object.ptr : 0; - DBG2(DBG_LIB, " v%d", version); - break; - case PKCS7_DIGEST_ALG: - digest_alg = asn1_parse_algorithmIdentifier(object, level, NULL); - break; - case PKCS7_SIGNED_CONTENT_INFO: - { - pkcs7_t *data = pkcs7_create_from_chunk(object, level+1); - - if (!data || !data->parse_data(data)) - { - DESTROY_IF(data); - goto end; - } - this->data = chunk_clone(data->get_data(data)); - data->destroy(data); - break; - } - case PKCS7_SIGNED_CERT: - { - certificate_t *cert; - - DBG2(DBG_LIB, " parsing pkcs7-wrapped certificate"); - cert = lib->creds->create(lib->creds, - CRED_CERTIFICATE, CERT_X509, - BUILD_BLOB_ASN1_DER, object, - BUILD_END); - if (cert) - { - this->certs->insert_last(this->certs, cert); - } - break; - } - case PKCS7_SIGNER_INFO: - signerInfos++; - DBG2(DBG_LIB, " signer #%d", signerInfos); - break; - case PKCS7_SIGNER_INFO_VERSION: - version = object.len ? (int)*object.ptr : 0; - DBG2(DBG_LIB, " v%d", version); - break; - case PKCS7_SIGNED_ISSUER: - { - identification_t *issuer; - - issuer = identification_create_from_encoding(ID_DER_ASN1_DN, object); - DBG2(DBG_LIB, " '%Y'", issuer); - issuer->destroy(issuer); - break; - } - case PKCS7_AUTH_ATTRIBUTES: - *object.ptr = ASN1_SET; - this->attributes = pkcs9_create_from_chunk(object, level+1); - *object.ptr = ASN1_CONTEXT_C_0; - break; - case PKCS7_DIGEST_ALGORITHM: - digest_alg = asn1_parse_algorithmIdentifier(object, level, NULL); - break; - case PKCS7_DIGEST_ENC_ALGORITHM: - enc_alg = asn1_parse_algorithmIdentifier(object, level, NULL); - break; - case PKCS7_ENCRYPTED_DIGEST: - encrypted_digest = object; - } - } - success = parser->success(parser); - -end: - parser->destroy(parser); - if (!success) - { - return FALSE; - } - - /* check the signature only if a cacert is available */ - if (cacert != NULL) - { - signature_scheme_t scheme; - public_key_t *key; - - scheme = signature_scheme_from_oid(digest_alg); - if (scheme == SIGN_UNKNOWN) - { - DBG1(DBG_LIB, "unsupported signature scheme"); - return FALSE; - } - if (signerInfos == 0) - { - DBG1(DBG_LIB, "no signerInfo object found"); - return FALSE; - } - else if (signerInfos > 1) - { - DBG1(DBG_LIB, "more than one signerInfo object found"); - return FALSE; - } - if (this->attributes == NULL) - { - DBG1(DBG_LIB, "no authenticatedAttributes object found"); - return FALSE; - } - if (enc_alg != OID_RSA_ENCRYPTION) - { - DBG1(DBG_LIB, "only RSA digest encryption supported"); - return FALSE; - } - - /* verify the signature */ - key = cacert->get_public_key(cacert); - if (key == NULL) - { - DBG1(DBG_LIB, "no public key found in CA certificate"); - return FALSE; - } - if (key->verify(key, scheme, - this->attributes->get_encoding(this->attributes), encrypted_digest)) - { - DBG2(DBG_LIB, "signature is valid"); - } - else - { - DBG1(DBG_LIB, "invalid signature"); - key->destroy(key); - return FALSE; - } - key->destroy(key); - - if (this->data.ptr != NULL) - { - chunk_t messageDigest; - - messageDigest = this->attributes->get_attribute(this->attributes, - OID_PKCS9_MESSAGE_DIGEST); - if (messageDigest.ptr == NULL) - { - DBG1(DBG_LIB, "messageDigest attribute not found"); - return FALSE; - } - else - { - hash_algorithm_t algorithm; - hasher_t *hasher; - chunk_t hash; - bool valid; - - algorithm = hasher_algorithm_from_oid(digest_alg); - hasher = lib->crypto->create_hasher(lib->crypto, algorithm); - if (!hasher || !hasher->allocate_hash(hasher, this->data, &hash)) - { - DESTROY_IF(hasher); - DBG1(DBG_LIB, "hash algorithm %N not supported", - hash_algorithm_names, algorithm); - return FALSE; - } - hasher->destroy(hasher); - DBG3(DBG_LIB, "hash: %B", &hash); - - valid = chunk_equals(messageDigest, hash); - free(hash.ptr); - if (valid) - { - DBG2(DBG_LIB, "messageDigest is valid"); - } - else - { - DBG1(DBG_LIB, "invalid messageDigest"); - return FALSE; - } - } - } - } - return TRUE; -} - -/** - * ASN.1 definition of the PKCS#7 envelopedData type - */ -static const asn1Object_t envelopedDataObjects[] = { - { 0, "envelopedData", ASN1_SEQUENCE, ASN1_NONE }, /* 0 */ - { 1, "version", ASN1_INTEGER, ASN1_BODY }, /* 1 */ - { 1, "recipientInfos", ASN1_SET, ASN1_LOOP }, /* 2 */ - { 2, "recipientInfo", ASN1_SEQUENCE, ASN1_BODY }, /* 3 */ - { 3, "version", ASN1_INTEGER, ASN1_BODY }, /* 4 */ - { 3, "issuerAndSerialNumber", ASN1_SEQUENCE, ASN1_BODY }, /* 5 */ - { 4, "issuer", ASN1_SEQUENCE, ASN1_OBJ }, /* 6 */ - { 4, "serial", ASN1_INTEGER, ASN1_BODY }, /* 7 */ - { 3, "encryptionAlgorithm", ASN1_EOC, ASN1_RAW }, /* 8 */ - { 3, "encryptedKey", ASN1_OCTET_STRING, ASN1_BODY }, /* 9 */ - { 1, "end loop", ASN1_EOC, ASN1_END }, /* 10 */ - { 1, "encryptedContentInfo", ASN1_SEQUENCE, ASN1_OBJ }, /* 11 */ - { 2, "contentType", ASN1_OID, ASN1_BODY }, /* 12 */ - { 2, "contentEncryptionAlgorithm", ASN1_EOC, ASN1_RAW }, /* 13 */ - { 2, "encryptedContent", ASN1_CONTEXT_S_0, ASN1_BODY }, /* 14 */ - { 0, "exit", ASN1_EOC, ASN1_EXIT } -}; -#define PKCS7_ENVELOPED_VERSION 1 -#define PKCS7_RECIPIENT_INFO_VERSION 4 -#define PKCS7_ISSUER 6 -#define PKCS7_SERIAL_NUMBER 7 -#define PKCS7_ENCRYPTION_ALG 8 -#define PKCS7_ENCRYPTED_KEY 9 -#define PKCS7_CONTENT_TYPE 12 -#define PKCS7_CONTENT_ENC_ALGORITHM 13 -#define PKCS7_ENCRYPTED_CONTENT 14 - -METHOD(pkcs7_t, parse_envelopedData, bool, - private_pkcs7_t *this, chunk_t serialNumber, private_key_t *key) -{ - asn1_parser_t *parser; - chunk_t object; - int objectID, version; - bool success = FALSE; - - chunk_t iv = chunk_empty; - chunk_t symmetric_key = chunk_empty; - chunk_t encrypted_content = chunk_empty; - - crypter_t *crypter = NULL; - - if (!parse_contentInfo(this) || - abort_parsing(this, OID_PKCS7_ENVELOPED_DATA)) - { - return FALSE; - } - - parser = asn1_parser_create(envelopedDataObjects, this->content); - parser->set_top_level(parser, this->level); - - while (parser->iterate(parser, &objectID, &object)) - { - u_int level = parser->get_level(parser); - - switch (objectID) - { - case PKCS7_ENVELOPED_VERSION: - { - version = object.len ? (int)*object.ptr : 0; - DBG2(DBG_LIB, " v%d", version); - if (version != 0) - { - DBG1(DBG_LIB, "envelopedData version is not 0"); - goto end; - } - break; - } - case PKCS7_RECIPIENT_INFO_VERSION: - { - version = object.len ? (int)*object.ptr : 0; - DBG2(DBG_LIB, " v%d", version); - if (version != 0) - { - DBG1(DBG_LIB, "recipient info version is not 0"); - goto end; - } - break; - } - case PKCS7_ISSUER: - { - identification_t *issuer; - - issuer = identification_create_from_encoding(ID_DER_ASN1_DN, - object); - DBG2(DBG_LIB, " '%Y'", issuer); - issuer->destroy(issuer); - break; - } - case PKCS7_SERIAL_NUMBER: - { - if (!chunk_equals(serialNumber, object)) - { - DBG1(DBG_LIB, "serial numbers do not match"); - goto end; - } - break; - } - case PKCS7_ENCRYPTION_ALG: - { - int alg; - - alg = asn1_parse_algorithmIdentifier(object, level, NULL); - if (alg != OID_RSA_ENCRYPTION) - { - DBG1(DBG_LIB, "only rsa encryption supported"); - goto end; - } - break; - } - case PKCS7_ENCRYPTED_KEY: - { - if (!key->decrypt(key, ENCRYPT_RSA_PKCS1, object, &symmetric_key)) - { - DBG1(DBG_LIB, "symmetric key could not be decrypted with rsa"); - goto end; - } - DBG4(DBG_LIB, "symmetric key %B", &symmetric_key); - break; - } - case PKCS7_CONTENT_TYPE: - { - if (asn1_known_oid(object) != OID_PKCS7_DATA) - { - DBG1(DBG_LIB, "encrypted content not of type pkcs7 data"); - goto end; - } - break; - } - case PKCS7_CONTENT_ENC_ALGORITHM: - { - encryption_algorithm_t enc_alg; - size_t key_size; - int alg; - - alg = asn1_parse_algorithmIdentifier(object, level, &iv); - enc_alg = encryption_algorithm_from_oid(alg, &key_size); - if (enc_alg == ENCR_UNDEFINED) - { - DBG1(DBG_LIB, "unsupported content encryption algorithm"); - goto end; - } - crypter = lib->crypto->create_crypter(lib->crypto, enc_alg, - key_size); - if (crypter == NULL) - { - DBG1(DBG_LIB, "crypter %N not available", - encryption_algorithm_names, enc_alg); - goto end; - } - if (symmetric_key.len != crypter->get_key_size(crypter)) - { - DBG1(DBG_LIB, "symmetric key length %d is wrong", - symmetric_key.len); - goto end; - } - if (!asn1_parse_simple_object(&iv, ASN1_OCTET_STRING, - level + 1, "IV")) - { - DBG1(DBG_LIB, "IV could not be parsed"); - goto end; - } - if (iv.len != crypter->get_iv_size(crypter)) - { - DBG1(DBG_LIB, "IV length %d is wrong", iv.len); - goto end; - } - break; - } - case PKCS7_ENCRYPTED_CONTENT: - { - encrypted_content = object; - break; - } - } - } - success = parser->success(parser); - -end: - parser->destroy(parser); - if (!success) - { - goto failed; - } - success = FALSE; - - /* decrypt the content */ - if (!crypter->set_key(crypter, symmetric_key) || - !crypter->decrypt(crypter, encrypted_content, iv, &this->data)) - { - success = FALSE; - goto failed; - } - DBG4(DBG_LIB, "decrypted content with padding: %B", &this->data); - - /* remove the padding */ - { - u_char *pos = this->data.ptr + this->data.len - 1; - u_char pattern = *pos; - size_t padding = pattern; - - if (padding > this->data.len) - { - DBG1(DBG_LIB, "padding greater than data length"); - goto failed; - } - this->data.len -= padding; - - while (padding-- > 0) - { - if (*pos-- != pattern) - { - DBG1(DBG_LIB, "wrong padding pattern"); - goto failed; - } - } - } - success = TRUE; - -failed: - DESTROY_IF(crypter); - chunk_clear(&symmetric_key); - if (!success) - { - chunk_free(&this->data); - } - return success; -} - -METHOD(pkcs7_t, get_data, chunk_t, - private_pkcs7_t *this) -{ - return this->data; -} - -METHOD(pkcs7_t, get_contentInfo, chunk_t, - private_pkcs7_t *this) -{ - chunk_t content_type; - - /* create DER-encoded OID for pkcs7_contentInfo type */ - switch(this->type) - { - case OID_PKCS7_DATA: - case OID_PKCS7_SIGNED_DATA: - case OID_PKCS7_ENVELOPED_DATA: - case OID_PKCS7_SIGNED_ENVELOPED_DATA: - case OID_PKCS7_DIGESTED_DATA: - case OID_PKCS7_ENCRYPTED_DATA: - content_type = asn1_build_known_oid(this->type); - break; - case OID_UNKNOWN: - default: - DBG1(DBG_LIB, "invalid pkcs7 contentInfo type"); - return chunk_empty; - } - - return this->content.ptr == NULL - ? asn1_wrap(ASN1_SEQUENCE, "m", content_type) - : asn1_wrap(ASN1_SEQUENCE, "mm", content_type, - asn1_simple_object(ASN1_CONTEXT_C_0, this->content)); -} - -METHOD(pkcs7_t, create_certificate_enumerator, enumerator_t*, - private_pkcs7_t *this) -{ - return this->certs->create_enumerator(this->certs); -} - -METHOD(pkcs7_t, set_certificate, void, - private_pkcs7_t *this, certificate_t *cert) -{ - if (cert) - { - this->certs->insert_last(this->certs, cert); - } -} - -METHOD(pkcs7_t, set_attributes, void, - private_pkcs7_t *this, pkcs9_t *attributes) -{ - this->attributes = attributes; -} - -METHOD(pkcs7_t, get_attributes, pkcs9_t*, - private_pkcs7_t *this) -{ - return this->attributes; -} - -/** - * build a DER-encoded issuerAndSerialNumber object - */ -chunk_t pkcs7_build_issuerAndSerialNumber(certificate_t *cert) -{ - identification_t *issuer = cert->get_issuer(cert); - chunk_t serial = chunk_empty; - - if (cert->get_type(cert) == CERT_X509) - { - x509_t *x509 = (x509_t*)cert; - serial = x509->get_serial(x509); - } - - return asn1_wrap(ASN1_SEQUENCE, "cm", - issuer->get_encoding(issuer), - asn1_integer("c", serial)); -} - -METHOD(pkcs7_t, build_envelopedData, bool, - private_pkcs7_t *this, certificate_t *cert, encryption_algorithm_t alg, - size_t key_size) -{ - chunk_t iv, symmetricKey, protectedKey, in, out; - crypter_t *crypter; - int alg_oid; - - /* select OID of symmetric encryption algorithm */ - alg_oid = encryption_algorithm_to_oid(alg, key_size); - if (alg_oid == OID_UNKNOWN) - { - DBG1(DBG_LIB, " encryption algorithm %N not supported", - encryption_algorithm_names, alg); - return FALSE; - } - crypter = lib->crypto->create_crypter(lib->crypto, alg, key_size / 8); - if (crypter == NULL) - { - DBG1(DBG_LIB, " could not create crypter for algorithm %N", - encryption_algorithm_names, alg); - return FALSE; - } - - /* generate a true random symmetric encryption key - * and a pseudo-random iv - */ - { - rng_t *rng; - - rng = lib->crypto->create_rng(lib->crypto, RNG_TRUE); - if (!rng || !rng->allocate_bytes(rng, crypter->get_key_size(crypter), - &symmetricKey)) - { - DBG1(DBG_LIB, " failed to allocate symmetric encryption key"); - DESTROY_IF(rng); - return FALSE; - } - DBG4(DBG_LIB, " symmetric encryption key: %B", &symmetricKey); - rng->destroy(rng); - - rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK); - if (!rng || !rng->allocate_bytes(rng, crypter->get_iv_size(crypter), - &iv)) - { - DBG1(DBG_LIB, " failed to allocate initialization vector"); - DESTROY_IF(rng); - return FALSE; - } - DBG4(DBG_LIB, " initialization vector: %B", &iv); - rng->destroy(rng); - } - - /* pad the data so that the total length becomes - * a multiple of the block size - */ - { - size_t block_size = crypter->get_block_size(crypter); - size_t padding = block_size - this->data.len % block_size; - - in.len = this->data.len + padding; - in.ptr = malloc(in.len); - - DBG2(DBG_LIB, " padding %d bytes of data to multiple block size of %d bytes", - (int)this->data.len, (int)in.len); - - /* copy data */ - memcpy(in.ptr, this->data.ptr, this->data.len); - /* append padding */ - memset(in.ptr + this->data.len, padding, padding); - } - DBG3(DBG_LIB, " padded unencrypted data: %B", &in); - - /* symmetric encryption of data object */ - if (!crypter->set_key(crypter, symmetricKey) || - !crypter->encrypt(crypter, in, iv, &out)) - { - crypter->destroy(crypter); - chunk_clear(&in); - chunk_clear(&symmetricKey); - chunk_free(&iv); - return FALSE; - } - crypter->destroy(crypter); - chunk_clear(&in); - DBG3(DBG_LIB, " encrypted data: %B", &out); - - /* protect symmetric key by public key encryption */ - { - public_key_t *key = cert->get_public_key(cert); - - if (key == NULL) - { - DBG1(DBG_LIB, " public key not found in encryption certificate"); - chunk_clear(&symmetricKey); - chunk_free(&iv); - chunk_free(&out); - return FALSE; - } - key->encrypt(key, ENCRYPT_RSA_PKCS1, symmetricKey, &protectedKey); - key->destroy(key); - chunk_clear(&symmetricKey); - } - - /* build pkcs7 enveloped data object */ - { - chunk_t contentEncryptionAlgorithm = asn1_wrap(ASN1_SEQUENCE, "mm", - asn1_build_known_oid(alg_oid), - asn1_wrap(ASN1_OCTET_STRING, "m", iv)); - - chunk_t encryptedContentInfo = asn1_wrap(ASN1_SEQUENCE, "mmm", - asn1_build_known_oid(OID_PKCS7_DATA), - contentEncryptionAlgorithm, - asn1_wrap(ASN1_CONTEXT_S_0, "m", out)); - - chunk_t encryptedKey = asn1_wrap(ASN1_OCTET_STRING, "m", protectedKey); - - chunk_t recipientInfo = asn1_wrap(ASN1_SEQUENCE, "cmmm", - ASN1_INTEGER_0, - pkcs7_build_issuerAndSerialNumber(cert), - asn1_algorithmIdentifier(OID_RSA_ENCRYPTION), - encryptedKey); - - this->content = asn1_wrap(ASN1_SEQUENCE, "cmm", - ASN1_INTEGER_0, - asn1_wrap(ASN1_SET, "m", recipientInfo), - encryptedContentInfo); - chunk_free(&this->data); - this->type = OID_PKCS7_ENVELOPED_DATA; - this->data = get_contentInfo(this); - } - return TRUE; -} - -METHOD(pkcs7_t, build_signedData, bool, - private_pkcs7_t *this, private_key_t *private_key, hash_algorithm_t alg) -{ - chunk_t authenticatedAttributes = chunk_empty; - chunk_t encryptedDigest = chunk_empty; - chunk_t signerInfo, encoding = chunk_empty; - signature_scheme_t scheme; - int digest_oid; - certificate_t *cert; - - if (this->certs->get_first(this->certs, (void**)&cert) != SUCCESS) - { - DBG1(DBG_LIB, " no pkcs7 signer certificate found"); - return FALSE; - } - digest_oid = hasher_algorithm_to_oid(alg); - scheme = signature_scheme_from_oid(digest_oid); - - if (this->attributes != NULL) - { - if (this->data.ptr != NULL) - { - chunk_t messageDigest, signingTime, attributes; - hasher_t *hasher; - time_t now; - - hasher = lib->crypto->create_hasher(lib->crypto, alg); - if (!hasher || - !hasher->allocate_hash(hasher, this->data, &messageDigest)) - { - DESTROY_IF(hasher); - DBG1(DBG_LIB, " hash algorithm %N not support", - hash_algorithm_names, alg); - return FALSE; - } - hasher->destroy(hasher); - this->attributes->set_attribute(this->attributes, - OID_PKCS9_MESSAGE_DIGEST, - messageDigest); - free(messageDigest.ptr); - - /* take the current time as signingTime */ - now = time(NULL); - signingTime = asn1_from_time(&now, ASN1_UTCTIME); - this->attributes->set_attribute_raw(this->attributes, - OID_PKCS9_SIGNING_TIME, signingTime); - this->attributes->set_attribute_raw(this->attributes, - OID_PKCS9_CONTENT_TYPE, - asn1_build_known_oid(OID_PKCS7_DATA)); - - attributes = this->attributes->get_encoding(this->attributes); - - private_key->sign(private_key, scheme, attributes, &encryptedDigest); - authenticatedAttributes = chunk_clone(attributes); - *authenticatedAttributes.ptr = ASN1_CONTEXT_C_0; - } - } - else if (this->data.ptr != NULL) - { - private_key->sign(private_key, scheme, this->data, &encryptedDigest); - } - if (encryptedDigest.ptr) - { - encryptedDigest = asn1_wrap(ASN1_OCTET_STRING, "m", encryptedDigest); - } - signerInfo = asn1_wrap(ASN1_SEQUENCE, "cmmmmm", - ASN1_INTEGER_1, - pkcs7_build_issuerAndSerialNumber(cert), - asn1_algorithmIdentifier(digest_oid), - authenticatedAttributes, - asn1_algorithmIdentifier(OID_RSA_ENCRYPTION), - encryptedDigest); - - if (this->data.ptr != NULL) - { - chunk_free(&this->content); - this->content = asn1_simple_object(ASN1_OCTET_STRING, this->data); - chunk_free(&this->data); - } - this->type = OID_PKCS7_DATA; - this->data = get_contentInfo(this); - chunk_free(&this->content); - - cert->get_encoding(cert, CERT_ASN1_DER, &encoding); - - this->content = asn1_wrap(ASN1_SEQUENCE, "cmcmm", - ASN1_INTEGER_1, - asn1_wrap(ASN1_SET, "m", asn1_algorithmIdentifier(digest_oid)), - this->data, - asn1_wrap(ASN1_CONTEXT_C_0, "m", encoding), - asn1_wrap(ASN1_SET, "m", signerInfo)); - chunk_free(&this->data); - this->type = OID_PKCS7_SIGNED_DATA; - this->data = get_contentInfo(this); - - return TRUE; -} - -METHOD(pkcs7_t, destroy, void, - private_pkcs7_t *this) -{ - DESTROY_IF(this->attributes); - this->certs->destroy_offset(this->certs, offsetof(certificate_t, destroy)); - free(this->content.ptr); - free(this->data.ptr); - free(this); -} - -/** - * Generic private constructor - */ -static private_pkcs7_t *pkcs7_create_empty(void) -{ - private_pkcs7_t *this; - - INIT(this, - .public = { - .is_data = _is_data, - .is_signedData = _is_signedData, - .is_envelopedData = _is_envelopedData, - .parse_data = _parse_data, - .parse_signedData = _parse_signedData, - .parse_envelopedData = _parse_envelopedData, - .get_data = _get_data, - .get_contentInfo = _get_contentInfo, - .create_certificate_enumerator = _create_certificate_enumerator, - .set_certificate = _set_certificate, - .set_attributes = _set_attributes, - .get_attributes = _get_attributes, - .build_envelopedData = _build_envelopedData, - .build_signedData = _build_signedData, - .destroy = _destroy, - }, - .type = OID_UNKNOWN, - .certs = linked_list_create(), - ); - - return this; -} - -/* - * Described in header. - */ -pkcs7_t *pkcs7_create_from_chunk(chunk_t chunk, u_int level) -{ - private_pkcs7_t *this = pkcs7_create_empty(); - - this->level = level; - this->data = chunk_clone(chunk); - - return &this->public; -} - -/* - * Described in header. - */ -pkcs7_t *pkcs7_create_from_data(chunk_t data) -{ - private_pkcs7_t *this = pkcs7_create_empty(); - - this->data = chunk_clone(data); - - return &this->public; -} - diff --git a/src/libstrongswan/crypto/pkcs7.h b/src/libstrongswan/crypto/pkcs7.h deleted file mode 100644 index 5af36d29e..000000000 --- a/src/libstrongswan/crypto/pkcs7.h +++ /dev/null @@ -1,178 +0,0 @@ -/* - * Copyright (C) 2005 Jan Hutter, Martin Willi - * Copyright (C) 2002-2008 Andreas Steffen - * Hochschule fuer Technik Rapperswil, Switzerland - * - * 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. - */ - -/** - * @defgroup pkcs7 pkcs7 - * @{ @ingroup crypto - */ - -#ifndef PKCS7_H_ -#define PKCS7_H_ - -typedef struct pkcs7_t pkcs7_t; - -#include <library.h> -#include <credentials/keys/private_key.h> -#include <crypto/pkcs9.h> -#include <crypto/crypters/crypter.h> -#include <collections/enumerator.h> - -/** - * PKCS#7 contentInfo object. - */ -struct pkcs7_t { - - /** - * Check if the PKCS#7 contentType is data - * - * @return TRUE if the contentType is data - */ - bool (*is_data) (pkcs7_t *this); - - /** - * Check if the PKCS#7 contentType is signedData - * - * @return TRUE if the contentType is signedData - */ - bool (*is_signedData) (pkcs7_t *this); - - /** - * Check if the PKCS#7 contentType is envelopedData - * - * @return TRUE if the contentType is envelopedData - */ - bool (*is_envelopedData) (pkcs7_t *this); - - /** - * Parse a PKCS#7 data content. - * - * @return TRUE if parsing was successful - */ - bool (*parse_data) (pkcs7_t *this); - - /** - * Parse a PKCS#7 signedData content. The contained PKCS#7 data is parsed - * and verified. - * - * @param cacert cacert used to verify the signature - * @return TRUE if parsing was successful - */ - bool (*parse_signedData) (pkcs7_t *this, certificate_t *cacert); - - /** - * Parse a PKCS#7 envelopedData content. - * - * @param serialNumber serialNumber of the request - * @param key private key used to decrypt the symmetric key - * @return TRUE if parsing was successful - */ - bool (*parse_envelopedData) (pkcs7_t *this, chunk_t serialNumber, - private_key_t *key); - - /** - * Returns the parsed data object - * - * @return chunk containing the data object - */ - chunk_t (*get_data) (pkcs7_t *this); - - /** - * Returns the a DER-encoded contentInfo object - * - * @return chunk containing the contentInfo object - */ - chunk_t (*get_contentInfo) (pkcs7_t *this); - - /** - * Create an enumerator for the certificates. - * - * @return enumerator for the certificates - */ - enumerator_t *(*create_certificate_enumerator) (pkcs7_t *this); - - /** - * Add a certificate. - * - * @param cert certificate to be included (gets adopted) - */ - void (*set_certificate) (pkcs7_t *this, certificate_t *cert); - - /** - * Add authenticated attributes. - * - * @param attributes attributes to be included (gets adopted) - */ - void (*set_attributes) (pkcs7_t *this, pkcs9_t *attributes); - - /** - * Get attributes. - * - * @return attributes (internal data) - */ - pkcs9_t *(*get_attributes) (pkcs7_t *this); - - /** - * Build a data object - * - * @return TRUE if build was successful - */ - bool (*build_data) (pkcs7_t *this); - - /** - * Build an envelopedData object - * - * @param cert receivers's certificate - * @param alg encryption algorithm - * @param key_size key size to use - * @return TRUE if build was successful - */ - bool (*build_envelopedData) (pkcs7_t *this, certificate_t *cert, - encryption_algorithm_t alg, size_t key_size); - - /** - * Build an signedData object - * - * @param key signer's private key - * @param alg digest algorithm used for signature - * @return TRUE if build was successful - */ - bool (*build_signedData) (pkcs7_t *this, private_key_t *key, - hash_algorithm_t alg); - - /** - * Destroys the contentInfo object. - */ - void (*destroy) (pkcs7_t *this); -}; - -/** - * Read a PKCS#7 contentInfo object from a DER encoded chunk. - * - * @param chunk chunk containing DER encoded data - * @param level ASN.1 parsing start level - * @return created pkcs7_contentInfo object, or NULL if invalid. - */ -pkcs7_t *pkcs7_create_from_chunk(chunk_t chunk, u_int level); - -/** - * Create a PKCS#7 contentInfo object - * - * @param data chunk containing data - * @return created pkcs7_contentInfo object. - */ -pkcs7_t *pkcs7_create_from_data(chunk_t data); - -#endif /** PKCS7_H_ @}*/ diff --git a/src/libstrongswan/crypto/pkcs9.c b/src/libstrongswan/crypto/pkcs9.c deleted file mode 100644 index 73dfdc447..000000000 --- a/src/libstrongswan/crypto/pkcs9.c +++ /dev/null @@ -1,369 +0,0 @@ -/* - * Copyright (C) 2012 Tobias Brunner - * Copyright (C) 2008 Andreas Steffen - * Hochschule fuer Technik Rapperswil, Switzerland - * - * 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. - */ - -#include <library.h> -#include <utils/debug.h> - -#include <asn1/oid.h> -#include <asn1/asn1.h> -#include <asn1/asn1_parser.h> -#include <collections/linked_list.h> - -#include "pkcs9.h" - -typedef struct private_pkcs9_t private_pkcs9_t; - -/** - * Private data of a pkcs9_t attribute list. - */ -struct private_pkcs9_t { - /** - * Public interface - */ - pkcs9_t public; - - /** - * DER encoding of PKCS#9 attributes - */ - chunk_t encoding; - - /** - * Linked list of PKCS#9 attributes - */ - linked_list_t *attributes; -}; - -typedef struct attribute_t attribute_t; - -/** - * Definition of an attribute_t object. - */ -struct attribute_t { - /** - * Object Identifier (OID) - */ - int oid; - - /** - * Attribute value - */ - chunk_t value; - - /** - * ASN.1 encoding - */ - chunk_t encoding; - - /** - * Destroys the attribute. - */ - void (*destroy) (attribute_t *this); - -}; - -/** - * return the ASN.1 encoding of a PKCS#9 attribute - */ -static asn1_t asn1_attributeType(int oid) -{ - asn1_t type; - - switch (oid) - { - case OID_PKCS9_CONTENT_TYPE: - type = ASN1_OID; - break; - case OID_PKCS9_SIGNING_TIME: - type = ASN1_UTCTIME; - break; - case OID_PKCS9_MESSAGE_DIGEST: - type = ASN1_OCTET_STRING; - break; - case OID_PKI_MESSAGE_TYPE: - type = ASN1_PRINTABLESTRING; - break; - case OID_PKI_STATUS: - type = ASN1_PRINTABLESTRING; - break; - case OID_PKI_FAIL_INFO: - type = ASN1_PRINTABLESTRING; - break; - case OID_PKI_SENDER_NONCE: - type = ASN1_OCTET_STRING; - break; - case OID_PKI_RECIPIENT_NONCE: - type = ASN1_OCTET_STRING; - break; - case OID_PKI_TRANS_ID: - type = ASN1_PRINTABLESTRING; - break; - default: - type = ASN1_EOC; - } - return type; -} - -/** - * Destroy an attribute_t object. - */ -static void attribute_destroy(attribute_t *this) -{ - free(this->value.ptr); - free(this->encoding.ptr); - free(this); -} - -/** - * Create an attribute_t object. - */ -static attribute_t *attribute_create(int oid, chunk_t value) -{ - attribute_t *this; - - INIT(this, - .destroy = attribute_destroy, - .oid = oid, - .value = chunk_clone(value), - .encoding = asn1_wrap(ASN1_SEQUENCE, "mm", - asn1_build_known_oid(oid), - asn1_simple_object(ASN1_SET, value)), - ); - - return this; -} - -METHOD(pkcs9_t, build_encoding, void, - private_pkcs9_t *this) -{ - enumerator_t *enumerator; - attribute_t *attribute; - u_int attributes_len = 0; - - if (this->encoding.ptr) - { - chunk_free(&this->encoding); - } - if (this->attributes->get_count(this->attributes) == 0) - { - return; - } - - /* compute the total length of the encoded attributes */ - enumerator = this->attributes->create_enumerator(this->attributes); - - while (enumerator->enumerate(enumerator, (void**)&attribute)) - { - attributes_len += attribute->encoding.len; - } - enumerator->destroy(enumerator); - - /* allocate memory for the attributes and build the encoding */ - { - u_char *pos = asn1_build_object(&this->encoding, ASN1_SET, attributes_len); - - enumerator = this->attributes->create_enumerator(this->attributes); - - while (enumerator->enumerate(enumerator, (void**)&attribute)) - { - memcpy(pos, attribute->encoding.ptr, attribute->encoding.len); - pos += attribute->encoding.len; - } - enumerator->destroy(enumerator); - } -} - -METHOD(pkcs9_t, get_encoding, chunk_t, - private_pkcs9_t *this) -{ - if (this->encoding.ptr == NULL) - { - build_encoding(this); - } - return this->encoding; -} - -METHOD(pkcs9_t, get_attribute, chunk_t, - private_pkcs9_t *this, int oid) -{ - enumerator_t *enumerator; - chunk_t value = chunk_empty; - attribute_t *attribute; - - enumerator = this->attributes->create_enumerator(this->attributes); - while (enumerator->enumerate(enumerator, (void**)&attribute)) - { - if (attribute->oid == oid) - { - value = attribute->value; - break; - } - } - enumerator->destroy(enumerator); - if (value.ptr && - !asn1_parse_simple_object(&value, asn1_attributeType(oid), 0, - oid_names[oid].name)) - { - return chunk_empty; - } - return value; -} - -METHOD(pkcs9_t, set_attribute_raw, void, - private_pkcs9_t *this, int oid, chunk_t value) -{ - attribute_t *attribute = attribute_create(oid, value); - - this->attributes->insert_last(this->attributes, attribute); - chunk_free(&value); -} - -METHOD(pkcs9_t, set_attribute, void, - private_pkcs9_t *this, int oid, chunk_t value) -{ - chunk_t attr = asn1_simple_object(asn1_attributeType(oid), value); - - set_attribute_raw(this, oid, attr); -} - -METHOD(pkcs9_t, destroy, void, - private_pkcs9_t *this) -{ - this->attributes->destroy_offset(this->attributes, offsetof(attribute_t, destroy)); - free(this->encoding.ptr); - free(this); -} - -/** - * Generic private constructor - */ -static private_pkcs9_t *pkcs9_create_empty(void) -{ - private_pkcs9_t *this; - - INIT(this, - .public = { - .build_encoding = _build_encoding, - .get_encoding = _get_encoding, - .get_attribute = _get_attribute, - .set_attribute = _set_attribute, - .set_attribute_raw = _set_attribute_raw, - .destroy = _destroy, - }, - .attributes = linked_list_create(), - ); - - return this; -} - -/* - * Described in header. - */ -pkcs9_t *pkcs9_create(void) -{ - private_pkcs9_t *this = pkcs9_create_empty(); - - return &this->public; -} - -/** - * ASN.1 definition of the X.501 atttribute type - */ -static const asn1Object_t attributesObjects[] = { - { 0, "attributes", ASN1_SET, ASN1_LOOP }, /* 0 */ - { 1, "attribute", ASN1_SEQUENCE, ASN1_NONE }, /* 1 */ - { 2, "type", ASN1_OID, ASN1_BODY }, /* 2 */ - { 2, "values", ASN1_SET, ASN1_LOOP }, /* 3 */ - { 3, "value", ASN1_EOC, ASN1_RAW }, /* 4 */ - { 2, "end loop", ASN1_EOC, ASN1_END }, /* 5 */ - { 0, "end loop", ASN1_EOC, ASN1_END }, /* 6 */ - { 0, "exit", ASN1_EOC, ASN1_EXIT } -}; -#define ATTRIBUTE_OBJ_TYPE 2 -#define ATTRIBUTE_OBJ_VALUE 4 - -/** - * Parse a PKCS#9 attribute list - */ -static bool parse_attributes(chunk_t chunk, int level0, private_pkcs9_t* this) -{ - asn1_parser_t *parser; - chunk_t object; - int objectID; - int oid = OID_UNKNOWN; - bool success = FALSE; - - parser = asn1_parser_create(attributesObjects, chunk); - parser->set_top_level(parser, level0); - - while (parser->iterate(parser, &objectID, &object)) - { - switch (objectID) - { - case ATTRIBUTE_OBJ_TYPE: - oid = asn1_known_oid(object); - break; - case ATTRIBUTE_OBJ_VALUE: - if (oid == OID_UNKNOWN) - { - break; - } - /* add the attribute to a linked list */ - { - attribute_t *attribute = attribute_create(oid, object); - - this->attributes->insert_last(this->attributes, - (void*)attribute); - } - /* parse known attributes */ - { - asn1_t type = asn1_attributeType(oid); - - if (type != ASN1_EOC) - { - if (!asn1_parse_simple_object(&object, type, - parser->get_level(parser)+1, - oid_names[oid].name)) - { - goto end; - } - } - } - } - } - success = parser->success(parser); - -end: - parser->destroy(parser); - return success; -} - - - /* - * Described in header. - */ -pkcs9_t *pkcs9_create_from_chunk(chunk_t chunk, u_int level) -{ - private_pkcs9_t *this = pkcs9_create_empty(); - - this->encoding = chunk_clone(chunk); - - if (!parse_attributes(chunk, level, this)) - { - destroy(this); - return NULL; - } - return &this->public; -} diff --git a/src/libstrongswan/library.h b/src/libstrongswan/library.h index 6f455a617..f164a6052 100644 --- a/src/libstrongswan/library.h +++ b/src/libstrongswan/library.h @@ -34,6 +34,9 @@ * @defgroup certificates certificates * @ingroup credentials * + * @defgroup containers containers + * @ingroup credentials + * * @defgroup sets sets * @ingroup credentials * diff --git a/src/libstrongswan/networking/host.c b/src/libstrongswan/networking/host.c index ef5c7c04e..81af1da27 100644 --- a/src/libstrongswan/networking/host.c +++ b/src/libstrongswan/networking/host.c @@ -367,9 +367,13 @@ host_t *host_create_from_string_and_family(char *string, int family, struct sockaddr_in6 v6; } addr; + if (streq(string, "%any")) + { + return host_create_any_port(family ? family : AF_INET, port); + } if (family == AF_UNSPEC || family == AF_INET) { - if (streq(string, "%any") || streq(string, "0.0.0.0")) + if (streq(string, "%any4") || streq(string, "0.0.0.0")) { return host_create_any_port(AF_INET, port); } diff --git a/src/libstrongswan/plugins/gmp/gmp_rsa_private_key.c b/src/libstrongswan/plugins/gmp/gmp_rsa_private_key.c index 74d99cafd..052b10741 100644 --- a/src/libstrongswan/plugins/gmp/gmp_rsa_private_key.c +++ b/src/libstrongswan/plugins/gmp/gmp_rsa_private_key.c @@ -211,7 +211,7 @@ static status_t compute_prime(size_t prime_size, bool safe, mpz_t *p, mpz_t *q) rng->destroy(rng); - /* additonally return p-1 */ + /* additionally return p-1 */ mpz_sub_ui(*q, *p, 1); return SUCCESS; diff --git a/src/libstrongswan/plugins/openssl/Makefile.am b/src/libstrongswan/plugins/openssl/Makefile.am index c59888663..f971a5e08 100644 --- a/src/libstrongswan/plugins/openssl/Makefile.am +++ b/src/libstrongswan/plugins/openssl/Makefile.am @@ -23,6 +23,7 @@ libstrongswan_openssl_la_SOURCES = \ openssl_ec_public_key.c openssl_ec_public_key.h \ openssl_x509.c openssl_x509.h \ openssl_crl.c openssl_crl.h \ + openssl_pkcs7.c openssl_pkcs7.h \ openssl_rng.c openssl_rng.h \ openssl_hmac.c openssl_hmac.h diff --git a/src/libstrongswan/plugins/openssl/openssl_pkcs7.c b/src/libstrongswan/plugins/openssl/openssl_pkcs7.c new file mode 100644 index 000000000..663397e59 --- /dev/null +++ b/src/libstrongswan/plugins/openssl/openssl_pkcs7.c @@ -0,0 +1,790 @@ +/* + * Copyright (C) 2012 Martin Willi + * Copyright (C) 2012 revosec 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. + */ + +#include <openssl/opensslconf.h> + +#ifndef OPENSSL_NO_CMS + +#include "openssl_pkcs7.h" +#include "openssl_util.h" + +#include <library.h> +#include <utils/debug.h> +#include <asn1/oid.h> +#include <credentials/sets/mem_cred.h> + +#include <openssl/cms.h> + +typedef struct private_openssl_pkcs7_t private_openssl_pkcs7_t; + +/** + * Private data of an openssl_pkcs7_t object. + */ +struct private_openssl_pkcs7_t { + + /** + * Public pkcs7_t interface. + */ + pkcs7_t public; + + /** + * Type of this container + */ + container_type_t type; + + /** + * OpenSSL CMS structure + */ + CMS_ContentInfo *cms; +}; + +/** + * OpenSSL does not allow us to read the signature to verify it with our own + * crypto API. We define the internal CMS_SignerInfo structure here to get it. + */ +struct CMS_SignerInfo_st { + long version; + void *sid; + X509_ALGOR *digestAlgorithm; + STACK_OF(X509_ATTRIBUTE) *signedAttrs; + X509_ALGOR *signatureAlgorithm; + ASN1_OCTET_STRING *signature; + /* and more... */ +}; + +/** + * And we also need access to the wrappend CMS_KeyTransRecipientInfo to + * read the encrypted key + */ +struct CMS_KeyTransRecipientInfo_st { + long version; + void *rid; + X509_ALGOR *keyEncryptionAlgorithm; + ASN1_OCTET_STRING *encryptedKey; +}; + +struct CMS_RecipientInfo_st { + int type; + struct CMS_KeyTransRecipientInfo_st *ktri; + /* and more in union... */ +}; + +struct CMS_EncryptedContentInfo_st { + ASN1_OBJECT *contentType; + X509_ALGOR *contentEncryptionAlgorithm; + ASN1_OCTET_STRING *encryptedContent; + /* and more... */ +}; + +struct CMS_EnvelopedData_st { + long version; + void *originatorInfo; + STACK_OF(CMS_RecipientInfo) *recipientInfos; + struct CMS_EncryptedContentInfo_st *encryptedContentInfo; + /* and more... */ +}; + +struct CMS_ContentInfo_st { + ASN1_OBJECT *contentType; + struct CMS_EnvelopedData_st *envelopedData; + /* and more in union... */ +}; + +/** + * We can't include asn1.h, declare function prototypes directly + */ +chunk_t asn1_wrap(int, const char *mode, ...); +int asn1_unwrap(chunk_t*, chunk_t*); + +/** + * Enumerator over certificates + */ +typedef struct { + /** implements enumerator_t */ + enumerator_t public; + /** Stack of X509 certificates */ + STACK_OF(X509) *certs; + /** current enumerator position in certificates */ + int i; + /** currently enumerating certificate_t */ + certificate_t *cert; +} cert_enumerator_t; + +METHOD(enumerator_t, cert_destroy, void, + cert_enumerator_t *this) +{ + DESTROY_IF(this->cert); + free(this); +} + +METHOD(enumerator_t, cert_enumerate, bool, + cert_enumerator_t *this, certificate_t **out) +{ + if (!this->certs) + { + return FALSE; + } + while (this->i < sk_X509_num(this->certs)) + { + chunk_t encoding; + X509 *x509; + + /* clean up previous round */ + DESTROY_IF(this->cert); + this->cert = NULL; + + x509 = sk_X509_value(this->certs, this->i++); + encoding = openssl_i2chunk(X509, x509); + this->cert = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509, + BUILD_BLOB_ASN1_DER, encoding, + BUILD_END); + free(encoding.ptr); + if (!this->cert) + { + continue; + } + *out = this->cert; + return TRUE; + } + return FALSE; +} + +METHOD(pkcs7_t, create_cert_enumerator, enumerator_t*, + private_openssl_pkcs7_t *this) +{ + cert_enumerator_t *enumerator; + + if (this->type == CONTAINER_PKCS7_SIGNED_DATA) + { + INIT(enumerator, + .public = { + .enumerate = (void*)_cert_enumerate, + .destroy = _cert_destroy, + }, + .certs = CMS_get1_certs(this->cms), + ); + return &enumerator->public; + } + return enumerator_create_empty(); +} + +/** + * Enumerator for signatures + */ +typedef struct { + /** implements enumerator_t */ + enumerator_t public; + /** Stack of signerinfos */ + STACK_OF(CMS_SignerInfo) *signers; + /** current enumerator position in signers */ + int i; + /** currently enumerating auth config */ + auth_cfg_t *auth; + /** full CMS */ + CMS_ContentInfo *cms; + /** credential set containing wrapped certificates */ + mem_cred_t *creds; +} signature_enumerator_t; + +/** + * Verify signerInfo signature + */ +static auth_cfg_t *verify_signature(CMS_SignerInfo *si, int hash_oid) +{ + enumerator_t *enumerator; + public_key_t *key; + certificate_t *cert; + auth_cfg_t *auth, *found = NULL; + identification_t *issuer, *serial; + chunk_t attrs = chunk_empty, sig, attr; + X509_NAME *name; + ASN1_INTEGER *snr; + int i; + + if (CMS_SignerInfo_get0_signer_id(si, NULL, &name, &snr) != 1) + { + return NULL; + } + issuer = openssl_x509_name2id(name); + if (!issuer) + { + return NULL; + } + serial = identification_create_from_encoding( + ID_KEY_ID, openssl_asn1_str2chunk(snr)); + + /* reconstruct DER encoded attributes to verify signature */ + for (i = 0; i < CMS_signed_get_attr_count(si); i++) + { + attr = openssl_i2chunk(X509_ATTRIBUTE, CMS_signed_get_attr(si, i)); + attrs = chunk_cat("mm", attrs, attr); + } + /* wrap in a ASN1_SET */ + attrs = asn1_wrap(0x31, "m", attrs); + + /* TODO: find a better way to access and verify the signature */ + sig = openssl_asn1_str2chunk(si->signature); + enumerator = lib->credmgr->create_trusted_enumerator(lib->credmgr, + KEY_RSA, serial, FALSE); + while (enumerator->enumerate(enumerator, &cert, &auth)) + { + if (issuer->equals(issuer, cert->get_issuer(cert))) + { + key = cert->get_public_key(cert); + if (key) + { + if (key->verify(key, signature_scheme_from_oid(hash_oid), + attrs, sig)) + { + found = auth->clone(auth); + key->destroy(key); + break; + } + key->destroy(key); + } + } + } + enumerator->destroy(enumerator); + issuer->destroy(issuer); + serial->destroy(serial); + free(attrs.ptr); + + return found; +} + +/** + * Verify the message digest in the signerInfo attributes + */ +static bool verify_digest(CMS_ContentInfo *cms, CMS_SignerInfo *si, int hash_oid) +{ + ASN1_OCTET_STRING *os, **osp; + hash_algorithm_t hash_alg; + chunk_t digest, content, hash; + hasher_t *hasher; + + os = CMS_signed_get0_data_by_OBJ(si, + OBJ_nid2obj(NID_pkcs9_messageDigest), -3, V_ASN1_OCTET_STRING); + if (!os) + { + return FALSE; + } + digest = openssl_asn1_str2chunk(os); + osp = CMS_get0_content(cms); + if (!osp) + { + return FALSE; + } + content = openssl_asn1_str2chunk(*osp); + + hash_alg = hasher_algorithm_from_oid(hash_oid); + hasher = lib->crypto->create_hasher(lib->crypto, hash_alg); + if (!hasher) + { + DBG1(DBG_LIB, "hash algorithm %N not supported", + hash_algorithm_names, hash_alg); + return FALSE; + } + if (!hasher->allocate_hash(hasher, content, &hash)) + { + hasher->destroy(hasher); + return FALSE; + } + hasher->destroy(hasher); + + if (!chunk_equals(digest, hash)) + { + free(hash.ptr); + DBG1(DBG_LIB, "invalid messageDigest"); + return FALSE; + } + free(hash.ptr); + return TRUE; +} + +METHOD(enumerator_t, signature_enumerate, bool, + signature_enumerator_t *this, auth_cfg_t **out) +{ + if (!this->signers) + { + return FALSE; + } + while (this->i < sk_CMS_SignerInfo_num(this->signers)) + { + CMS_SignerInfo *si; + X509_ALGOR *digest, *sig; + int hash_oid; + + /* clean up previous round */ + DESTROY_IF(this->auth); + this->auth = NULL; + + si = sk_CMS_SignerInfo_value(this->signers, this->i++); + + CMS_SignerInfo_get0_algs(si, NULL, NULL, &digest, &sig); + hash_oid = openssl_asn1_known_oid(digest->algorithm); + if (openssl_asn1_known_oid(sig->algorithm) != OID_RSA_ENCRYPTION) + { + DBG1(DBG_LIB, "only RSA digest encryption supported"); + continue; + } + this->auth = verify_signature(si, hash_oid); + if (!this->auth) + { + DBG1(DBG_LIB, "unable to verify pkcs7 attributes signature"); + continue; + } + if (!verify_digest(this->cms, si, hash_oid)) + { + continue; + } + *out = this->auth; + return TRUE; + } + return FALSE; +} + +METHOD(enumerator_t, signature_destroy, void, + signature_enumerator_t *this) +{ + lib->credmgr->remove_local_set(lib->credmgr, &this->creds->set); + this->creds->destroy(this->creds); + DESTROY_IF(this->auth); + free(this); +} + +METHOD(container_t, create_signature_enumerator, enumerator_t*, + private_openssl_pkcs7_t *this) +{ + signature_enumerator_t *enumerator; + + if (this->type == CONTAINER_PKCS7_SIGNED_DATA) + { + enumerator_t *certs; + certificate_t *cert; + + INIT(enumerator, + .public = { + .enumerate = (void*)_signature_enumerate, + .destroy = _signature_destroy, + }, + .cms = this->cms, + .signers = CMS_get0_SignerInfos(this->cms), + .creds = mem_cred_create(), + ); + + /* make available wrapped certs during signature checking */ + certs = create_cert_enumerator(this); + while (certs->enumerate(certs, &cert)) + { + enumerator->creds->add_cert(enumerator->creds, FALSE, + cert->get_ref(cert)); + } + certs->destroy(certs); + + lib->credmgr->add_local_set(lib->credmgr, &enumerator->creds->set, + FALSE); + + return &enumerator->public; + } + return enumerator_create_empty(); +} + + +METHOD(container_t, get_type, container_type_t, + private_openssl_pkcs7_t *this) +{ + return this->type; +} + +METHOD(pkcs7_t, get_attribute, bool, + private_openssl_pkcs7_t *this, int oid, + enumerator_t *enumerator, chunk_t *value) +{ + signature_enumerator_t *e; + CMS_SignerInfo *si; + X509_ATTRIBUTE *attr; + ASN1_TYPE *type; + chunk_t chunk, wrapped; + int i; + + e = (signature_enumerator_t*)enumerator; + if (e->i <= 0) + { + return FALSE; + } + + /* "i" gets incremeneted after enumerate(), hence read from previous */ + si = sk_CMS_SignerInfo_value(e->signers, e->i - 1); + for (i = 0; i < CMS_signed_get_attr_count(si); i++) + { + attr = CMS_signed_get_attr(si, i); + if (!attr->single && sk_ASN1_TYPE_num(attr->value.set) == 1 && + openssl_asn1_known_oid(attr->object) == oid) + { + /* get first value in SET */ + type = sk_ASN1_TYPE_value(attr->value.set, 0); + chunk = wrapped = openssl_i2chunk(ASN1_TYPE, type); + if (asn1_unwrap(&chunk, &chunk) != 0x100 /* ASN1_INVALID */) + { + *value = chunk_clone(chunk); + free(wrapped.ptr); + return TRUE; + } + free(wrapped.ptr); + } + } + return FALSE; +} + +/** + * Find a private key for issuerAndSerialNumber + */ +static private_key_t *find_private(identification_t *issuer, + identification_t *serial) +{ + enumerator_t *enumerator; + certificate_t *cert; + public_key_t *public; + private_key_t *private = NULL; + identification_t *id; + chunk_t fp; + + enumerator = lib->credmgr->create_cert_enumerator(lib->credmgr, + CERT_X509, KEY_RSA, serial, FALSE); + while (enumerator->enumerate(enumerator, &cert)) + { + if (issuer->equals(issuer, cert->get_issuer(cert))) + { + public = cert->get_public_key(cert); + if (public) + { + if (public->get_fingerprint(public, KEYID_PUBKEY_SHA1, &fp)) + { + id = identification_create_from_encoding(ID_KEY_ID, fp); + private = lib->credmgr->get_private(lib->credmgr, + KEY_ANY, id, NULL); + id->destroy(id); + } + public->destroy(public); + } + } + if (private) + { + break; + } + } + enumerator->destroy(enumerator); + return private; +} + +/** + * Decrypt enveloped-data with a decrypted symmetric key + */ +static bool decrypt_symmetric(private_openssl_pkcs7_t *this, chunk_t key, + chunk_t encrypted, chunk_t *plain) +{ + encryption_algorithm_t encr; + X509_ALGOR *alg; + crypter_t *crypter; + chunk_t iv; + size_t key_size; + + /* read encryption algorithm from interal structures; TODO fixup */ + alg = this->cms->envelopedData->encryptedContentInfo-> + contentEncryptionAlgorithm; + encr = encryption_algorithm_from_oid(openssl_asn1_known_oid(alg->algorithm), + &key_size); + if (alg->parameter->type != V_ASN1_OCTET_STRING) + { + return FALSE; + } + iv = openssl_asn1_str2chunk(alg->parameter->value.octet_string); + + crypter = lib->crypto->create_crypter(lib->crypto, encr, key_size / 8); + if (!crypter) + { + DBG1(DBG_LIB, "crypter %N-%d not available", + encryption_algorithm_names, alg, key_size); + return FALSE; + } + if (key.len != crypter->get_key_size(crypter)) + { + DBG1(DBG_LIB, "symmetric key length is wrong"); + crypter->destroy(crypter); + return FALSE; + } + if (iv.len != crypter->get_iv_size(crypter)) + { + DBG1(DBG_LIB, "IV length is wrong"); + crypter->destroy(crypter); + return FALSE; + } + if (!crypter->set_key(crypter, key) || + !crypter->decrypt(crypter, encrypted, iv, plain)) + { + crypter->destroy(crypter); + return FALSE; + } + crypter->destroy(crypter); + return TRUE; +} + +/** + * Remove enveloped-data PKCS#7 padding from plain data + */ +static bool remove_padding(chunk_t *data) +{ + u_char *pos; + u_char pattern; + size_t padding; + + if (!data->len) + { + return FALSE; + } + pos = data->ptr + data->len - 1; + padding = pattern = *pos; + + if (padding > data->len) + { + DBG1(DBG_LIB, "padding greater than data length"); + return FALSE; + } + data->len -= padding; + + while (padding-- > 0) + { + if (*pos-- != pattern) + { + DBG1(DBG_LIB, "wrong padding pattern"); + return FALSE; + } + } + return TRUE; +} + +/** + * Decrypt PKCS#7 enveloped-data + */ +static bool decrypt(private_openssl_pkcs7_t *this, + chunk_t encrypted, chunk_t *plain) +{ + STACK_OF(CMS_RecipientInfo) *ris; + CMS_RecipientInfo *ri; + chunk_t chunk, key = chunk_empty; + int i; + + ris = CMS_get0_RecipientInfos(this->cms); + for (i = 0; i < sk_CMS_RecipientInfo_num(ris); i++) + { + ri = sk_CMS_RecipientInfo_value(ris, i); + if (CMS_RecipientInfo_type(ri) == CMS_RECIPINFO_TRANS) + { + identification_t *serial, *issuer; + private_key_t *private; + X509_ALGOR *alg; + X509_NAME *name; + ASN1_INTEGER *sn; + u_char zero = 0; + int oid; + + if (CMS_RecipientInfo_ktri_get0_algs(ri, NULL, NULL, &alg) == 1 && + CMS_RecipientInfo_ktri_get0_signer_id(ri, NULL, &name, &sn) == 1) + { + oid = openssl_asn1_known_oid(alg->algorithm); + if (oid != OID_RSA_ENCRYPTION) + { + DBG1(DBG_LIB, "only RSA encryption supported in PKCS#7"); + continue; + } + issuer = openssl_x509_name2id(name); + if (!issuer) + { + continue; + } + chunk = openssl_asn1_str2chunk(sn); + if (chunk.len && chunk.ptr[0] | 0x80) + { /* if MSB is set, append a zero to make it non-negative */ + chunk = chunk_cata("cc", chunk_from_thing(zero), chunk); + } + serial = identification_create_from_encoding(ID_KEY_ID, chunk); + private = find_private(issuer, serial); + issuer->destroy(issuer); + serial->destroy(serial); + + if (private) + { + /* get encryptedKey from internal structure; TODO fixup */ + chunk = openssl_asn1_str2chunk(ri->ktri->encryptedKey); + if (private->decrypt(private, ENCRYPT_RSA_PKCS1, + chunk, &key)) + { + private->destroy(private); + break; + } + private->destroy(private); + } + } + } + } + if (!key.len) + { + DBG1(DBG_LIB, "no private key found to decrypt PKCS#7"); + return FALSE; + } + if (!decrypt_symmetric(this, key, encrypted, plain)) + { + chunk_clear(&key); + return FALSE; + } + chunk_clear(&key); + if (!remove_padding(plain)) + { + free(plain->ptr); + return FALSE; + } + return TRUE; +} + +METHOD(container_t, get_data, bool, + private_openssl_pkcs7_t *this, chunk_t *data) +{ + ASN1_OCTET_STRING **os; + chunk_t chunk; + + os = CMS_get0_content(this->cms); + if (os) + { + chunk = openssl_asn1_str2chunk(*os); + switch (this->type) + { + case CONTAINER_PKCS7_DATA: + case CONTAINER_PKCS7_SIGNED_DATA: + *data = chunk_clone(chunk); + return TRUE; + case CONTAINER_PKCS7_ENVELOPED_DATA: + return decrypt(this, chunk, data); + default: + break; + } + } + return FALSE; +} + +METHOD(container_t, get_encoding, bool, + private_openssl_pkcs7_t *this, chunk_t *data) +{ + return FALSE; +} + +METHOD(container_t, destroy, void, + private_openssl_pkcs7_t *this) +{ + CMS_ContentInfo_free(this->cms); + free(this); +} + +/** + * Generic constructor + */ +static private_openssl_pkcs7_t* create_empty() +{ + private_openssl_pkcs7_t *this; + + INIT(this, + .public = { + .container = { + .get_type = _get_type, + .create_signature_enumerator = _create_signature_enumerator, + .get_data = _get_data, + .get_encoding = _get_encoding, + .destroy = _destroy, + }, + .get_attribute = _get_attribute, + .create_cert_enumerator = _create_cert_enumerator, + }, + ); + + return this; +} + +/** + * Parse a PKCS#7 container + */ +static bool parse(private_openssl_pkcs7_t *this, chunk_t blob) +{ + BIO *bio; + + bio = BIO_new_mem_buf(blob.ptr, blob.len); + this->cms = d2i_CMS_bio(bio, NULL); + BIO_free(bio); + + if (!this->cms) + { + return FALSE; + } + switch (openssl_asn1_known_oid((ASN1_OBJECT*)CMS_get0_type(this->cms))) + { + case OID_PKCS7_DATA: + this->type = CONTAINER_PKCS7_DATA; + break; + case OID_PKCS7_SIGNED_DATA: + this->type = CONTAINER_PKCS7_SIGNED_DATA; + break; + case OID_PKCS7_ENVELOPED_DATA: + this->type = CONTAINER_PKCS7_ENVELOPED_DATA; + break; + default: + return FALSE; + } + + return TRUE; +} + +/** + * See header + */ +pkcs7_t *openssl_pkcs7_load(container_type_t type, va_list args) +{ + chunk_t blob = chunk_empty; + private_openssl_pkcs7_t *this; + + while (TRUE) + { + switch (va_arg(args, builder_part_t)) + { + case BUILD_BLOB_ASN1_DER: + blob = va_arg(args, chunk_t); + continue; + case BUILD_END: + break; + default: + return NULL; + } + break; + } + if (blob.len) + { + this = create_empty(); + if (parse(this, blob)) + { + return &this->public; + } + destroy(this); + } + return NULL; +} + +#endif /* OPENSSL_NO_CMS */ diff --git a/src/libstrongswan/plugins/openssl/openssl_pkcs7.h b/src/libstrongswan/plugins/openssl/openssl_pkcs7.h new file mode 100644 index 000000000..2c7939ebd --- /dev/null +++ b/src/libstrongswan/plugins/openssl/openssl_pkcs7.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2012 Martin Willi + * Copyright (C) 2012 revosec 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. + */ + +/** + * @defgroup openssl_pkcs7 openssl_pkcs7 + * @{ @ingroup openssl_p + */ + +#ifndef OPENSSL_PKCS7_H_ +#define OPENSSL_PKCS7_H_ + +#include <credentials/containers/pkcs7.h> + +/** + * Load a generic PKCS#7 container. + * + * The argument list must contain a single BUILD_BLOB_ASN1_DER argument. + * + * @param type type of the container, CONTAINER_PKCS7 + * @param args builder_part_t argument list + * @return container, NULL on failure + */ +pkcs7_t *openssl_pkcs7_load(container_type_t type, va_list args); + +#endif /** OPENSSL_PKCS7_H_ @}*/ diff --git a/src/libstrongswan/plugins/openssl/openssl_plugin.c b/src/libstrongswan/plugins/openssl/openssl_plugin.c index 1f7497a70..dd6a379d2 100644 --- a/src/libstrongswan/plugins/openssl/openssl_plugin.c +++ b/src/libstrongswan/plugins/openssl/openssl_plugin.c @@ -40,6 +40,7 @@ #include "openssl_ec_public_key.h" #include "openssl_x509.h" #include "openssl_crl.h" +#include "openssl_pkcs7.h" #include "openssl_rng.h" #include "openssl_hmac.h" @@ -365,6 +366,10 @@ METHOD(plugin_t, get_features, int, PLUGIN_SDEPEND(PUBKEY, KEY_DSA), PLUGIN_REGISTER(CERT_DECODE, openssl_crl_load, TRUE), PLUGIN_PROVIDE(CERT_DECODE, CERT_X509_CRL), +#ifndef OPENSSL_NO_CMS + PLUGIN_REGISTER(CONTAINER_DECODE, openssl_pkcs7_load, TRUE), + PLUGIN_PROVIDE(CONTAINER_DECODE, CONTAINER_PKCS7), +#endif /* OPENSSL_NO_CMS */ #ifndef OPENSSL_NO_ECDH /* EC DH groups */ PLUGIN_REGISTER(DH, openssl_ec_diffie_hellman_create), diff --git a/src/libstrongswan/plugins/openssl/openssl_x509.c b/src/libstrongswan/plugins/openssl/openssl_x509.c index b847c0a63..676b97f7a 100644 --- a/src/libstrongswan/plugins/openssl/openssl_x509.c +++ b/src/libstrongswan/plugins/openssl/openssl_x509.c @@ -327,6 +327,10 @@ METHOD(certificate_t, has_subject, id_match_t, { return ID_MATCH_PERFECT; } + if (chunk_equals(get_serial(this), encoding)) + { + return ID_MATCH_PERFECT; + } } best = this->subject->matches(this->subject, subject); enumerator = create_subjectAltName_enumerator(this); diff --git a/src/libstrongswan/plugins/pkcs7/Makefile.am b/src/libstrongswan/plugins/pkcs7/Makefile.am new file mode 100644 index 000000000..6310daece --- /dev/null +++ b/src/libstrongswan/plugins/pkcs7/Makefile.am @@ -0,0 +1,20 @@ + +INCLUDES = -I$(top_srcdir)/src/libstrongswan + +AM_CFLAGS = -rdynamic + +if MONOLITHIC +noinst_LTLIBRARIES = libstrongswan-pkcs7.la +else +plugin_LTLIBRARIES = libstrongswan-pkcs7.la +endif + +libstrongswan_pkcs7_la_SOURCES = \ + pkcs7_generic.h pkcs7_generic.c \ + pkcs7_signed_data.h pkcs7_signed_data.c \ + pkcs7_enveloped_data.h pkcs7_enveloped_data.c \ + pkcs7_data.h pkcs7_data.c \ + pkcs7_attributes.h pkcs7_attributes.c \ + pkcs7_plugin.h pkcs7_plugin.c + +libstrongswan_pkcs7_la_LDFLAGS = -module -avoid-version diff --git a/src/libstrongswan/plugins/pkcs7/pkcs7_attributes.c b/src/libstrongswan/plugins/pkcs7/pkcs7_attributes.c new file mode 100644 index 000000000..ca6899786 --- /dev/null +++ b/src/libstrongswan/plugins/pkcs7/pkcs7_attributes.c @@ -0,0 +1,273 @@ +/* + * Copyright (C) 2012 Tobias Brunner + * Copyright (C) 2008 Andreas Steffen + * Hochschule fuer Technik Rapperswil, Switzerland + * + * 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. + */ + +#include <library.h> +#include <utils/debug.h> + +#include <asn1/oid.h> +#include <asn1/asn1.h> +#include <asn1/asn1_parser.h> +#include <collections/linked_list.h> + +#include "pkcs7_attributes.h" + +typedef struct private_pkcs7_attributes_t private_pkcs7_attributes_t; +typedef struct attribute_t attribute_t; + +/** + * Private data of a pkcs7_attributes_t attribute list. + */ +struct private_pkcs7_attributes_t { + /** + * Public interface + */ + pkcs7_attributes_t public; + + /** + * DER encoding of PKCS#9 attributes + */ + chunk_t encoding; + + /** + * Linked list of PKCS#9 attributes + */ + linked_list_t *attributes; +}; + +/** + * Definition of an attribute_t object. + */ +struct attribute_t { + + /** + * Object Identifier (OID) + */ + int oid; + + /** + * Attribute value + */ + chunk_t value; + + /** + * ASN.1 encoding + */ + chunk_t encoding; +}; + +/** + * Destroy an attribute_t object. + */ +static void attribute_destroy(attribute_t *this) +{ + free(this->value.ptr); + free(this); +} + +/** + * Create an attribute_t object. + */ +static attribute_t *attribute_create(int oid, chunk_t value) +{ + attribute_t *this; + + INIT(this, + .oid = oid, + .value = chunk_clone(value), + ); + + return this; +} + +/** + * Build encoding of the attribute list + */ +static void build_encoding(private_pkcs7_attributes_t *this) +{ + enumerator_t *enumerator; + attribute_t *attribute; + u_int len = 0, count, i = 0; + chunk_t *chunks; + u_char *pos; + + count = this->attributes->get_count(this->attributes); + chunks = malloc(sizeof(chunk_t) * count); + + enumerator = this->attributes->create_enumerator(this->attributes); + while (enumerator->enumerate(enumerator, &attribute)) + { + chunks[i] = asn1_wrap(ASN1_SEQUENCE, "mm", + asn1_build_known_oid(attribute->oid), + asn1_wrap(ASN1_SET, "c", attribute->value)); + len += chunks[i].len; + i++; + } + enumerator->destroy(enumerator); + + pos = asn1_build_object(&this->encoding, ASN1_SET, len); + for (i = 0; i < count; i++) + { + memcpy(pos, chunks[i].ptr, chunks[i].len); + pos += chunks[i].len; + free(chunks[i].ptr); + } + free(chunks); +} + +METHOD(pkcs7_attributes_t, get_encoding, chunk_t, + private_pkcs7_attributes_t *this) +{ + if (!this->encoding.len) + { + build_encoding(this); + } + return this->encoding; +} + +METHOD(pkcs7_attributes_t, get_attribute, chunk_t, + private_pkcs7_attributes_t *this, int oid) +{ + enumerator_t *enumerator; + chunk_t value = chunk_empty; + attribute_t *attribute; + + enumerator = this->attributes->create_enumerator(this->attributes); + while (enumerator->enumerate(enumerator, &attribute)) + { + if (attribute->oid == oid) + { + value = attribute->value; + break; + } + } + enumerator->destroy(enumerator); + if (value.len && asn1_unwrap(&value, &value) != ASN1_INVALID) + { + return value; + } + return chunk_empty; +} + +METHOD(pkcs7_attributes_t, add_attribute, void, + private_pkcs7_attributes_t *this, int oid, chunk_t value) +{ + this->attributes->insert_last(this->attributes, + attribute_create(oid, value)); + chunk_free(&value); + + /* rebuild encoding when adding attributes */ + chunk_free(&this->encoding); +} + +METHOD(pkcs7_attributes_t, destroy, void, + private_pkcs7_attributes_t *this) +{ + this->attributes->destroy_function(this->attributes, + (void*)attribute_destroy); + free(this->encoding.ptr); + free(this); +} + +/* + * Described in header. + */ +pkcs7_attributes_t *pkcs7_attributes_create(void) +{ + private_pkcs7_attributes_t *this; + + INIT(this, + .public = { + .get_encoding = _get_encoding, + .get_attribute = _get_attribute, + .add_attribute = _add_attribute, + .destroy = _destroy, + }, + .attributes = linked_list_create(), + ); + + return &this->public; +} + +/** + * ASN.1 definition of the X.501 atttribute type + */ +static const asn1Object_t attributesObjects[] = { + { 0, "attributes", ASN1_SET, ASN1_LOOP }, /* 0 */ + { 1, "attribute", ASN1_SEQUENCE, ASN1_NONE }, /* 1 */ + { 2, "type", ASN1_OID, ASN1_BODY }, /* 2 */ + { 2, "values", ASN1_SET, ASN1_LOOP }, /* 3 */ + { 3, "value", ASN1_EOC, ASN1_RAW }, /* 4 */ + { 2, "end loop", ASN1_EOC, ASN1_END }, /* 5 */ + { 0, "end loop", ASN1_EOC, ASN1_END }, /* 6 */ + { 0, "exit", ASN1_EOC, ASN1_EXIT } +}; +#define ATTRIBUTE_OBJ_TYPE 2 +#define ATTRIBUTE_OBJ_VALUE 4 + +/** + * Parse a PKCS#9 attribute list + */ +static bool parse_attributes(chunk_t chunk, int level0, + private_pkcs7_attributes_t* this) +{ + asn1_parser_t *parser; + chunk_t object; + int objectID; + int oid = OID_UNKNOWN; + bool success = FALSE; + + parser = asn1_parser_create(attributesObjects, chunk); + parser->set_top_level(parser, level0); + + while (parser->iterate(parser, &objectID, &object)) + { + switch (objectID) + { + case ATTRIBUTE_OBJ_TYPE: + oid = asn1_known_oid(object); + break; + case ATTRIBUTE_OBJ_VALUE: + if (oid != OID_UNKNOWN) + { + this->attributes->insert_last(this->attributes, + attribute_create(oid, object)); + } + break; + } + } + success = parser->success(parser); + + parser->destroy(parser); + return success; +} + + /* + * Described in header. + */ +pkcs7_attributes_t *pkcs7_attributes_create_from_chunk(chunk_t chunk, + u_int level) +{ + private_pkcs7_attributes_t *this; + + this = (private_pkcs7_attributes_t*)pkcs7_attributes_create(); + this->encoding = chunk_clone(chunk); + if (!parse_attributes(chunk, level, this)) + { + destroy(this); + return NULL; + } + return &this->public; +} diff --git a/src/libstrongswan/crypto/pkcs9.h b/src/libstrongswan/plugins/pkcs7/pkcs7_attributes.h index c442d4441..d5f6156a1 100644 --- a/src/libstrongswan/crypto/pkcs9.h +++ b/src/libstrongswan/plugins/pkcs7/pkcs7_attributes.h @@ -15,78 +15,65 @@ */ /** - * @defgroup pkcs9 pkcs9 - * @{ @ingroup crypto + * @defgroup pkcs7_attributes pkcs7_attributes + * @{ @ingroup pkcs7p */ -#ifndef PKCS9_H_ -#define PKCS9_H_ +#ifndef PKCS7_ATTRIBUTES_H_ +#define PKCS7_ATTRIBUTES_H_ -typedef struct pkcs9_t pkcs9_t; +typedef struct pkcs7_attributes_t pkcs7_attributes_t; #include <library.h> /** - * PKCS#9 attributes. + * PKCS#7 attribute lists, aka PKCS#9. */ -struct pkcs9_t { +struct pkcs7_attributes_t { /** - * Generate ASN.1 encoding of attribute list - */ - void (*build_encoding) (pkcs9_t *this); - - /** - * Gets ASN.1 encoding of PKCS#9 attribute list + * Gets ASN.1 encoding of PKCS#9 attribute list. * * @return ASN.1 encoded PKCSI#9 list */ - chunk_t (*get_encoding) (pkcs9_t *this); + chunk_t (*get_encoding) (pkcs7_attributes_t *this); /** - * Gets a PKCS#9 attribute + * Gets a PKCS#9 attribute from the list. * * @param oid OID of the attribute * @return value of the attribute (internal data) */ - chunk_t (*get_attribute) (pkcs9_t *this, int oid); - - /** - * Adds a PKCS#9 attribute - * - * @param oid OID of the attribute - * @param value value of the attribute (gets cloned) - */ - void (*set_attribute) (pkcs9_t *this, int oid, chunk_t value); + chunk_t (*get_attribute) (pkcs7_attributes_t *this, int oid); /** - * Adds a ASN.1 encoded PKCS#9 attribute + * Adds a PKCS#9 attribute. * * @param oid OID of the attribute - * @param value ASN.1 encoded value of the attribute (gets adopted) + * @param value value of the attribute, with ASN1 type (gets owned) */ - void (*set_attribute_raw) (pkcs9_t *this, int oid, chunk_t value); + void (*add_attribute) (pkcs7_attributes_t *this, int oid, chunk_t value); /** * Destroys the PKCS#9 attribute list. */ - void (*destroy) (pkcs9_t *this); + void (*destroy) (pkcs7_attributes_t *this); }; /** - * Read a PKCS#9 attribute list from a DER encoded chunk. + * Read a PKCS#7 attribute list (aka PKCS#9) from a DER encoded chunk. * * @param chunk chunk containing DER encoded data * @param level ASN.1 parsing start level * @return created pkcs9 attribute list, or NULL if invalid. */ -pkcs9_t *pkcs9_create_from_chunk(chunk_t chunk, u_int level); +pkcs7_attributes_t *pkcs7_attributes_create_from_chunk(chunk_t chunk, u_int level); /** - * Create an empty PKCS#9 attribute list + * Create an empty PKCS#7 attribute list, aka PKCS#9. * * @return created pkcs9 attribute list. */ -pkcs9_t *pkcs9_create(void); +pkcs7_attributes_t *pkcs7_attributes_create(void); #endif /** PKCS9_H_ @}*/ diff --git a/src/libstrongswan/plugins/pkcs7/pkcs7_data.c b/src/libstrongswan/plugins/pkcs7/pkcs7_data.c new file mode 100644 index 000000000..06816095c --- /dev/null +++ b/src/libstrongswan/plugins/pkcs7/pkcs7_data.c @@ -0,0 +1,156 @@ +/* + * Copyright (C) 2012 Martin Willi + * Copyright (C) 2012 revosec 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. + */ + +#include "pkcs7_data.h" + +#include <asn1/asn1.h> +#include <asn1/oid.h> + +typedef struct private_pkcs7_data_t private_pkcs7_data_t; + +/** + * Private data of a PKCS#7 signed-data container. + */ +struct private_pkcs7_data_t { + + /** + * Implements pkcs7_t. + */ + pkcs7_t public; + + /** + * Encoded data + */ + chunk_t content; + + /** + * Encoded PKCS#7 data + */ + chunk_t encoding; +}; + +METHOD(container_t, get_type, container_type_t, + private_pkcs7_data_t *this) +{ + return CONTAINER_PKCS7_DATA; +} + +METHOD(container_t, create_signature_enumerator, enumerator_t*, + private_pkcs7_data_t *this) +{ + return enumerator_create_empty(); +} + +METHOD(container_t, get_data, bool, + private_pkcs7_data_t *this, chunk_t *data) +{ + chunk_t chunk; + + chunk = this->content; + if (asn1_unwrap(&chunk, &chunk) == ASN1_OCTET_STRING) + { + *data = chunk_clone(chunk); + return TRUE; + } + return FALSE; +} + +METHOD(container_t, get_encoding, bool, + private_pkcs7_data_t *this, chunk_t *data) +{ + *data = chunk_clone(this->encoding); + return TRUE; +} + +METHOD(container_t, destroy, void, + private_pkcs7_data_t *this) +{ + free(this->content.ptr); + free(this->encoding.ptr); + free(this); +} + +/** + * Create an empty container + */ +static private_pkcs7_data_t* create_empty() +{ + private_pkcs7_data_t *this; + + INIT(this, + .public = { + .container = { + .get_type = _get_type, + .create_signature_enumerator = _create_signature_enumerator, + .get_data = _get_data, + .get_encoding = _get_encoding, + .destroy = _destroy, + }, + .get_attribute = (void*)return_false, + .create_cert_enumerator = (void*)enumerator_create_empty, + }, + ); + + return this; +} + +/** + * See header. + */ +pkcs7_t *pkcs7_data_load(chunk_t encoding, chunk_t content) +{ + private_pkcs7_data_t *this = create_empty(); + + this->encoding = chunk_clone(encoding); + this->content = chunk_clone(content); + + return &this->public; +} + +/** + * See header. + */ +pkcs7_t *pkcs7_data_gen(container_type_t type, va_list args) +{ + private_pkcs7_data_t *this; + chunk_t blob = chunk_empty; + + while (TRUE) + { + switch (va_arg(args, builder_part_t)) + { + case BUILD_BLOB: + blob = va_arg(args, chunk_t); + continue; + case BUILD_END: + break; + default: + return NULL; + } + break; + } + + if (blob.len) + { + this = create_empty(); + + this->content = asn1_wrap(ASN1_OCTET_STRING, "c", blob); + this->encoding = asn1_wrap(ASN1_SEQUENCE, "mm", + asn1_build_known_oid(OID_PKCS7_DATA), + asn1_wrap(ASN1_CONTEXT_C_0, "c", this->content)); + return &this->public; + } + return NULL; +} diff --git a/src/libstrongswan/plugins/pkcs7/pkcs7_data.h b/src/libstrongswan/plugins/pkcs7/pkcs7_data.h new file mode 100644 index 000000000..86512b76f --- /dev/null +++ b/src/libstrongswan/plugins/pkcs7/pkcs7_data.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2012 Martin Willi + * Copyright (C) 2012 revosec 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. + */ + +/** + * @defgroup pkcs7_data pkcs7_data + * @{ @ingroup pkcs7p + */ + +#ifndef PKCS7_DATA_H_ +#define PKCS7_DATA_H_ + +#include <credentials/builder.h> +#include <credentials/containers/pkcs7.h> + +/** + * Parse a PKCS#7 "data" container. + * + * @param encoding full contentInfo encoding + * @param content DER encoded content from contentInfo + * @return CONTAINER_PKCS7_DATA container, NULL on failure + */ +pkcs7_t *pkcs7_data_load(chunk_t encoding, chunk_t content); + +/** + * Generate a PKCS#7 data container. + * + * The only accepted builder argument is BUILDER_BLOB. + * + * @param type container type, must be CONTAINER_PKCS7_DATA + * @param args builder_t arguments to use. + */ +pkcs7_t *pkcs7_data_gen(container_type_t type, va_list args); + +#endif /** PKCS7_DATA_H_ @}*/ diff --git a/src/libstrongswan/plugins/pkcs7/pkcs7_enveloped_data.c b/src/libstrongswan/plugins/pkcs7/pkcs7_enveloped_data.c new file mode 100644 index 000000000..5cd0d8f93 --- /dev/null +++ b/src/libstrongswan/plugins/pkcs7/pkcs7_enveloped_data.c @@ -0,0 +1,613 @@ +/* + * Copyright (C) 2012 Martin Willi + * Copyright (C) 2012 revosec AG + * Copyright (C) 2012 Tobias Brunner + * Copyright (C) 2002-2008 Andreas Steffen + * Copyright (C) 2005 Jan Hutter, Martin Willi + * Hochschule fuer Technik Rapperswil, Switzerland + * + * 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. + */ + +#include "pkcs7_enveloped_data.h" + +#include <asn1/asn1.h> +#include <asn1/asn1_parser.h> +#include <asn1/oid.h> +#include <credentials/certificates/x509.h> +#include <utils/debug.h> + +typedef struct private_pkcs7_enveloped_data_t private_pkcs7_enveloped_data_t; + +/** + * Private data of a PKCS#7 signed-data container. + */ +struct private_pkcs7_enveloped_data_t { + + /** + * Implements pkcs7_t. + */ + pkcs7_t public; + + /** + * Decrypted content + */ + chunk_t content; + + /** + * Encrypted and encoded PKCS#7 enveloped-data + */ + chunk_t encoding; +}; + +/** + * ASN.1 definition of the PKCS#7 envelopedData type + */ +static const asn1Object_t envelopedDataObjects[] = { + { 0, "envelopedData", ASN1_SEQUENCE, ASN1_NONE }, /* 0 */ + { 1, "version", ASN1_INTEGER, ASN1_BODY }, /* 1 */ + { 1, "recipientInfos", ASN1_SET, ASN1_LOOP }, /* 2 */ + { 2, "recipientInfo", ASN1_SEQUENCE, ASN1_BODY }, /* 3 */ + { 3, "version", ASN1_INTEGER, ASN1_BODY }, /* 4 */ + { 3, "issuerAndSerialNumber", ASN1_SEQUENCE, ASN1_BODY }, /* 5 */ + { 4, "issuer", ASN1_SEQUENCE, ASN1_OBJ }, /* 6 */ + { 4, "serial", ASN1_INTEGER, ASN1_BODY }, /* 7 */ + { 3, "encryptionAlgorithm", ASN1_EOC, ASN1_RAW }, /* 8 */ + { 3, "encryptedKey", ASN1_OCTET_STRING, ASN1_BODY }, /* 9 */ + { 1, "end loop", ASN1_EOC, ASN1_END }, /* 10 */ + { 1, "encryptedContentInfo", ASN1_SEQUENCE, ASN1_OBJ }, /* 11 */ + { 2, "contentType", ASN1_OID, ASN1_BODY }, /* 12 */ + { 2, "contentEncryptionAlgorithm", ASN1_EOC, ASN1_RAW }, /* 13 */ + { 2, "encryptedContent", ASN1_CONTEXT_S_0, ASN1_BODY }, /* 14 */ + { 0, "exit", ASN1_EOC, ASN1_EXIT } +}; +#define PKCS7_VERSION 1 +#define PKCS7_RECIPIENT_INFO_VERSION 4 +#define PKCS7_ISSUER 6 +#define PKCS7_SERIAL_NUMBER 7 +#define PKCS7_ENCRYPTION_ALG 8 +#define PKCS7_ENCRYPTED_KEY 9 +#define PKCS7_CONTENT_TYPE 12 +#define PKCS7_CONTENT_ENC_ALGORITHM 13 +#define PKCS7_ENCRYPTED_CONTENT 14 + +/** + * Find a private key for issuerAndSerialNumber + */ +static private_key_t *find_private(identification_t *issuer, + identification_t *serial) +{ + enumerator_t *enumerator; + certificate_t *cert; + public_key_t *public; + private_key_t *private = NULL; + identification_t *id; + chunk_t fp; + + enumerator = lib->credmgr->create_cert_enumerator(lib->credmgr, + CERT_X509, KEY_RSA, serial, FALSE); + while (enumerator->enumerate(enumerator, &cert)) + { + if (issuer->equals(issuer, cert->get_issuer(cert))) + { + public = cert->get_public_key(cert); + if (public) + { + if (public->get_fingerprint(public, KEYID_PUBKEY_SHA1, &fp)) + { + id = identification_create_from_encoding(ID_KEY_ID, fp); + private = lib->credmgr->get_private(lib->credmgr, + KEY_ANY, id, NULL); + id->destroy(id); + } + public->destroy(public); + } + } + if (private) + { + break; + } + } + enumerator->destroy(enumerator); + return private; +} + +/** + * Decrypt content using a private key from "issuer" + */ +static bool decrypt(private_key_t *private, chunk_t key, chunk_t iv, int oid, + chunk_t encrypted, chunk_t *plain) +{ + encryption_algorithm_t alg; + chunk_t plain_key; + crypter_t *crypter; + size_t key_size; + + alg = encryption_algorithm_from_oid(oid, &key_size); + if (alg == ENCR_UNDEFINED) + { + DBG1(DBG_LIB, "unsupported content encryption algorithm"); + return FALSE; + } + if (!private->decrypt(private, ENCRYPT_RSA_PKCS1, key, &plain_key)) + { + DBG1(DBG_LIB, "symmetric key could not be decrypted with rsa"); + return FALSE; + } + crypter = lib->crypto->create_crypter(lib->crypto, alg, key_size / 8); + if (!crypter) + { + DBG1(DBG_LIB, "crypter %N-%d not available", + encryption_algorithm_names, alg, key_size); + free(plain_key.ptr); + return FALSE; + } + if (plain_key.len != crypter->get_key_size(crypter)) + { + DBG1(DBG_LIB, "symmetric key length %d is wrong", plain_key.len); + free(plain_key.ptr); + crypter->destroy(crypter); + return FALSE; + } + if (iv.len != crypter->get_iv_size(crypter)) + { + DBG1(DBG_LIB, "IV length %d is wrong", iv.len); + free(plain_key.ptr); + crypter->destroy(crypter); + return FALSE; + } + if (!crypter->set_key(crypter, plain_key) || + !crypter->decrypt(crypter, encrypted, iv, plain)) + { + free(plain_key.ptr); + crypter->destroy(crypter); + return FALSE; + } + DBG4(DBG_LIB, "decrypted content with padding: %B", plain); + free(plain_key.ptr); + crypter->destroy(crypter); + return TRUE; +} + +/** + * Remove the padding from plain data + */ +static bool remove_padding(private_pkcs7_enveloped_data_t *this) +{ + u_char *pos = this->content.ptr + this->content.len - 1; + u_char pattern = *pos; + size_t padding = pattern; + + if (padding > this->content.len) + { + DBG1(DBG_LIB, "padding greater than data length"); + return FALSE; + } + this->content.len -= padding; + + while (padding-- > 0) + { + if (*pos-- != pattern) + { + DBG1(DBG_LIB, "wrong padding pattern"); + return FALSE; + } + } + return TRUE; +} + +/** + * Parse and decrypt enveloped-data + */ +static bool parse(private_pkcs7_enveloped_data_t *this, chunk_t content) +{ + asn1_parser_t *parser; + chunk_t object; + int objectID, version, alg = OID_UNKNOWN; + bool success = FALSE; + identification_t *issuer = NULL, *serial = NULL; + private_key_t *private = NULL; + chunk_t iv = chunk_empty, key = chunk_empty, encrypted = chunk_empty; + + parser = asn1_parser_create(envelopedDataObjects, content); + parser->set_top_level(parser, 0); + + while (parser->iterate(parser, &objectID, &object)) + { + u_int level = parser->get_level(parser); + + switch (objectID) + { + case PKCS7_VERSION: + version = object.len ? (int)*object.ptr : 0; + DBG2(DBG_LIB, " v%d", version); + if (version != 0) + { + DBG1(DBG_LIB, "envelopedData version is not 0"); + goto end; + } + break; + case PKCS7_RECIPIENT_INFO_VERSION: + version = object.len ? (int)*object.ptr : 0; + DBG2(DBG_LIB, " v%d", version); + if (version != 0) + { + DBG1(DBG_LIB, "recipient info version is not 0"); + goto end; + } + break; + case PKCS7_ISSUER: + if (!issuer) + { + issuer = identification_create_from_encoding(ID_DER_ASN1_DN, + object); + } + break; + case PKCS7_SERIAL_NUMBER: + if (!serial) + { + serial = identification_create_from_encoding(ID_KEY_ID, + object); + } + break; + case PKCS7_ENCRYPTION_ALG: + if (asn1_parse_algorithmIdentifier(object, level, + NULL) != OID_RSA_ENCRYPTION) + { + DBG1(DBG_LIB, "only rsa encryption supported"); + goto end; + } + break; + case PKCS7_ENCRYPTED_KEY: + key = object; + break; + case PKCS7_CONTENT_TYPE: + if (asn1_known_oid(object) != OID_PKCS7_DATA) + { + DBG1(DBG_LIB, "encrypted content not of type pkcs7 data"); + goto end; + } + break; + case PKCS7_CONTENT_ENC_ALGORITHM: + alg = asn1_parse_algorithmIdentifier(object, level, &iv); + if (!asn1_parse_simple_object(&iv, ASN1_OCTET_STRING, + level + 1, "IV")) + { + DBG1(DBG_LIB, "IV could not be parsed"); + goto end; + } + break; + case PKCS7_ENCRYPTED_CONTENT: + encrypted = object; + break; + } + } + success = parser->success(parser); + +end: + parser->destroy(parser); + if (!success) + { + goto failed; + } + success = FALSE; + if (!issuer) + { + goto failed; + } + private = find_private(issuer, serial); + if (!private) + { + DBG1(DBG_LIB, "no private key found to decrypt pkcs7"); + goto failed; + } + if (!decrypt(private, key, iv, alg, encrypted, &this->content)) + { + goto failed; + } + if (!remove_padding(this)) + { + goto failed; + } + + success = TRUE; +failed: + DESTROY_IF(issuer); + DESTROY_IF(serial); + DESTROY_IF(private); + return success; +} + +METHOD(container_t, get_type, container_type_t, + private_pkcs7_enveloped_data_t *this) +{ + return CONTAINER_PKCS7_ENVELOPED_DATA; +} + +METHOD(container_t, create_signature_enumerator, enumerator_t*, + private_pkcs7_enveloped_data_t *this) +{ + return enumerator_create_empty(); +} + +METHOD(container_t, get_data, bool, + private_pkcs7_enveloped_data_t *this, chunk_t *data) +{ + if (this->content.len) + { + *data = chunk_clone(this->content); + return TRUE; + } + return FALSE; +} + +METHOD(container_t, get_encoding, bool, + private_pkcs7_enveloped_data_t *this, chunk_t *data) +{ + *data = chunk_clone(this->encoding); + return TRUE; +} + +METHOD(container_t, destroy, void, + private_pkcs7_enveloped_data_t *this) +{ + free(this->content.ptr); + free(this->encoding.ptr); + free(this); +} + +/** + * Generic constructor + */ +static private_pkcs7_enveloped_data_t* create_empty() +{ + private_pkcs7_enveloped_data_t *this; + + INIT(this, + .public = { + .container = { + .get_type = _get_type, + .create_signature_enumerator = _create_signature_enumerator, + .get_data = _get_data, + .get_encoding = _get_encoding, + .destroy = _destroy, + }, + .create_cert_enumerator = (void*)enumerator_create_empty, + .get_attribute = (void*)return_false, + }, + ); + + return this; +} + +/** + * See header. + */ +pkcs7_t *pkcs7_enveloped_data_load(chunk_t encoding, chunk_t content) +{ + private_pkcs7_enveloped_data_t *this = create_empty(); + + this->encoding = chunk_clone(encoding); + if (!parse(this, content)) + { + destroy(this); + return NULL; + } + + return &this->public; +} + +/** + * Allocate data with an RNG + */ +static bool get_random(rng_quality_t quality, size_t size, chunk_t *out) +{ + rng_t *rng; + + rng = lib->crypto->create_rng(lib->crypto, quality); + if (!rng) + { + return FALSE; + } + if (!rng->allocate_bytes(rng, size, out)) + { + rng->destroy(rng); + return FALSE; + } + rng->destroy(rng); + return TRUE; +} + +/** + * Encrypt symmetric key using a public key from a certificate + */ +static bool encrypt_key(certificate_t *cert, chunk_t in, chunk_t *out) +{ + public_key_t *key; + + key = cert->get_public_key(cert); + if (!key) + { + return FALSE; + } + if (!key->encrypt(key, ENCRYPT_RSA_PKCS1, in, out)) + { + key->destroy(key); + return FALSE; + } + key->destroy(key); + return TRUE; +} + +/** + * build a DER-encoded issuerAndSerialNumber object + */ +static chunk_t build_issuerAndSerialNumber(certificate_t *cert) +{ + identification_t *issuer = cert->get_issuer(cert); + chunk_t serial = chunk_empty; + + if (cert->get_type(cert) == CERT_X509) + { + x509_t *x509 = (x509_t*)cert; + serial = x509->get_serial(x509); + } + + return asn1_wrap(ASN1_SEQUENCE, "cm", + issuer->get_encoding(issuer), + asn1_integer("c", serial)); +} + +/** + * Generate a new PKCS#7 enveloped-data container + */ +static bool generate(private_pkcs7_enveloped_data_t *this, + certificate_t *cert, encryption_algorithm_t alg, int key_size) +{ + chunk_t contentEncryptionAlgorithm, encryptedContentInfo, recipientInfo; + chunk_t iv, symmetricKey, protectedKey, content; + crypter_t *crypter; + size_t bs, padding; + int alg_oid; + + alg_oid = encryption_algorithm_to_oid(alg, key_size); + if (alg_oid == OID_UNKNOWN) + { + DBG1(DBG_LIB, " encryption algorithm %N not supported", + encryption_algorithm_names, alg); + return FALSE; + } + crypter = lib->crypto->create_crypter(lib->crypto, alg, key_size / 8); + if (crypter == NULL) + { + DBG1(DBG_LIB, " could not create crypter for algorithm %N", + encryption_algorithm_names, alg); + return FALSE; + } + + if (!get_random(RNG_TRUE, crypter->get_key_size(crypter), &symmetricKey)) + { + DBG1(DBG_LIB, " failed to allocate symmetric encryption key"); + crypter->destroy(crypter); + return FALSE; + } + DBG4(DBG_LIB, " symmetric encryption key: %B", &symmetricKey); + + if (!get_random(RNG_WEAK, crypter->get_iv_size(crypter), &iv)) + { + DBG1(DBG_LIB, " failed to allocate initialization vector"); + crypter->destroy(crypter); + return FALSE; + } + DBG4(DBG_LIB, " initialization vector: %B", &iv); + + bs = crypter->get_block_size(crypter); + padding = bs - this->content.len % bs; + content = chunk_alloc(this->content.len + padding); + memcpy(content.ptr, this->content.ptr, this->content.len); + memset(content.ptr + this->content.len, padding, padding); + DBG3(DBG_LIB, " padded unencrypted data: %B", &content); + + /* symmetric inline encryption of content */ + if (!crypter->set_key(crypter, symmetricKey) || + !crypter->encrypt(crypter, content, iv, NULL)) + { + crypter->destroy(crypter); + chunk_clear(&symmetricKey); + chunk_free(&iv); + return FALSE; + } + crypter->destroy(crypter); + DBG3(DBG_LIB, " encrypted data: %B", &content); + + if (!encrypt_key(cert, symmetricKey, &protectedKey)) + { + DBG1(DBG_LIB, " encrypting symmetric key failed"); + chunk_clear(&symmetricKey); + chunk_free(&iv); + chunk_free(&content); + return FALSE; + } + chunk_clear(&symmetricKey); + + contentEncryptionAlgorithm = asn1_wrap(ASN1_SEQUENCE, "mm", + asn1_build_known_oid(alg_oid), + asn1_wrap(ASN1_OCTET_STRING, "m", iv)); + + encryptedContentInfo = asn1_wrap(ASN1_SEQUENCE, "mmm", + asn1_build_known_oid(OID_PKCS7_DATA), + contentEncryptionAlgorithm, + asn1_wrap(ASN1_CONTEXT_S_0, "m", content)); + + recipientInfo = asn1_wrap(ASN1_SEQUENCE, "cmmm", + ASN1_INTEGER_0, + build_issuerAndSerialNumber(cert), + asn1_algorithmIdentifier(OID_RSA_ENCRYPTION), + asn1_wrap(ASN1_OCTET_STRING, "m", protectedKey)); + + this->encoding = asn1_wrap(ASN1_SEQUENCE, "mm", + asn1_build_known_oid(OID_PKCS7_ENVELOPED_DATA), + asn1_wrap(ASN1_CONTEXT_C_0, "m", + asn1_wrap(ASN1_SEQUENCE, "cmm", + ASN1_INTEGER_0, + asn1_wrap(ASN1_SET, "m", recipientInfo), + encryptedContentInfo))); + + return TRUE; +} + +/** + * See header. + */ +pkcs7_t *pkcs7_enveloped_data_gen(container_type_t type, va_list args) +{ + private_pkcs7_enveloped_data_t *this; + chunk_t blob = chunk_empty; + encryption_algorithm_t alg = ENCR_AES_CBC; + certificate_t *cert = NULL; + int key_size = 128; + + while (TRUE) + { + switch (va_arg(args, builder_part_t)) + { + case BUILD_CERT: + cert = va_arg(args, certificate_t*); + continue; + case BUILD_ENCRYPTION_ALG: + alg = va_arg(args, int); + continue; + case BUILD_KEY_SIZE: + key_size = va_arg(args, int); + continue; + case BUILD_BLOB: + blob = va_arg(args, chunk_t); + continue; + case BUILD_END: + break; + default: + return NULL; + } + break; + } + if (blob.len && cert) + { + this = create_empty(); + + this->content = chunk_clone(blob); + if (generate(this, cert, alg, key_size)) + { + return &this->public; + } + destroy(this); + } + return NULL; +} diff --git a/src/libstrongswan/plugins/pkcs7/pkcs7_enveloped_data.h b/src/libstrongswan/plugins/pkcs7/pkcs7_enveloped_data.h new file mode 100644 index 000000000..5e35abd54 --- /dev/null +++ b/src/libstrongswan/plugins/pkcs7/pkcs7_enveloped_data.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2012 Martin Willi + * Copyright (C) 2012 revosec 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. + */ + +/** + * @defgroup pkcs7_enveloped_data pkcs7_enveloped_data + * @{ @ingroup pkcs7p + */ + +#ifndef PKCS7_ENVELOPED_DATA_H_ +#define PKCS7_ENVELOPED_DATA_H_ + +#include <credentials/builder.h> +#include <credentials/containers/pkcs7.h> + +/** + * Parse a PKCS#7 enveloped-data container. + * + * @param encoding full contentInfo encoding + * @param content DER encoded content from contentInfo + * @return CONTAINER_PKCS7_ENVELOPED_DATA container, NULL on failure + */ +pkcs7_t *pkcs7_enveloped_data_load(chunk_t encoding, chunk_t content); + +/** + * Generate a PKCS#7 enveloped-data container. + * + * @param type container type, must be CONTAINER_PKCS7_ENVELOPED_DATA + * @param args builder_t arguments to use. + */ +pkcs7_t *pkcs7_enveloped_data_gen(container_type_t type, va_list args); + +#endif /** PKCS7_ENVELOPED_DATA_H_ @}*/ diff --git a/src/libstrongswan/plugins/pkcs7/pkcs7_generic.c b/src/libstrongswan/plugins/pkcs7/pkcs7_generic.c new file mode 100644 index 000000000..35d8d11a7 --- /dev/null +++ b/src/libstrongswan/plugins/pkcs7/pkcs7_generic.c @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2012 Martin Willi + * Copyright (C) 2012 revosec AG + * Copyright (C) 2012 Tobias Brunner + * Copyright (C) 2002-2008 Andreas Steffen + * Copyright (C) 2005 Jan Hutter, Martin Willi + * Hochschule fuer Technik Rapperswil, Switzerland + * + * 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. + */ + +#include "pkcs7_generic.h" +#include "pkcs7_data.h" +#include "pkcs7_signed_data.h" +#include "pkcs7_enveloped_data.h" + +#include <utils/debug.h> +#include <asn1/oid.h> +#include <asn1/asn1.h> +#include <asn1/asn1_parser.h> + +/** + * ASN.1 definition of the PKCS#7 ContentInfo type + */ +static const asn1Object_t contentInfoObjects[] = { + { 0, "contentInfo", ASN1_SEQUENCE, ASN1_NONE }, /* 0 */ + { 1, "contentType", ASN1_OID, ASN1_BODY }, /* 1 */ + { 1, "content", ASN1_CONTEXT_C_0, ASN1_OPT | + ASN1_BODY }, /* 2 */ + { 1, "end opt", ASN1_EOC, ASN1_END }, /* 3 */ + { 0, "exit", ASN1_EOC, ASN1_EXIT } +}; +#define PKCS7_INFO_TYPE 1 +#define PKCS7_INFO_CONTENT 2 + +/** + * Parse PKCS#7 contentInfo object + */ +static pkcs7_t* parse_contentInfo(chunk_t blob) +{ + asn1_parser_t *parser; + chunk_t object, content = chunk_empty; + int objectID, type = OID_UNKNOWN; + bool success = FALSE; + + parser = asn1_parser_create(contentInfoObjects, blob); + parser->set_top_level(parser, 0); + + while (parser->iterate(parser, &objectID, &object)) + { + if (objectID == PKCS7_INFO_TYPE) + { + type = asn1_known_oid(object); + if (type < OID_PKCS7_DATA || type > OID_PKCS7_ENCRYPTED_DATA) + { + DBG1(DBG_ASN, "unknown pkcs7 content type"); + goto end; + } + } + else if (objectID == PKCS7_INFO_CONTENT) + { + content = object; + } + } + success = parser->success(parser); + +end: + parser->destroy(parser); + + if (success) + { + switch (type) + { + case OID_PKCS7_DATA: + return pkcs7_data_load(blob, content); + case OID_PKCS7_SIGNED_DATA: + return pkcs7_signed_data_load(blob, content); + case OID_PKCS7_ENVELOPED_DATA: + return pkcs7_enveloped_data_load(blob, content); + default: + DBG1(DBG_ASN, "pkcs7 content type %d not supported", type); + return NULL; + } + } + return NULL; +} + + +pkcs7_t *pkcs7_generic_load(container_type_t type, va_list args) +{ + chunk_t blob = chunk_empty; + + while (TRUE) + { + switch (va_arg(args, builder_part_t)) + { + case BUILD_BLOB_ASN1_DER: + blob = va_arg(args, chunk_t); + continue; + case BUILD_END: + break; + default: + return NULL; + } + break; + } + if (blob.len) + { + if (blob.len >= 2 && + blob.ptr[0] == ASN1_SEQUENCE && blob.ptr[1] == 0x80) + { /* looks like infinite length BER encoding, but we can't handle it. + * ignore silently, our openssl backend can handle it */ + return NULL; + } + return parse_contentInfo(blob); + } + return NULL; +} diff --git a/src/libstrongswan/plugins/pkcs7/pkcs7_generic.h b/src/libstrongswan/plugins/pkcs7/pkcs7_generic.h new file mode 100644 index 000000000..819343c4d --- /dev/null +++ b/src/libstrongswan/plugins/pkcs7/pkcs7_generic.h @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2012 Martin Willi + * Copyright (C) 2012 revosec 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. + */ + +/** + * @defgroup pkcs7_generic pkcs7_generic + * @{ @ingroup pkcs7p + */ + +#ifndef PKCS7_GENERIC_H_ +#define PKCS7_GENERIC_H_ + +#include <credentials/builder.h> +#include <credentials/containers/pkcs7.h> + +/** + * Load a generic PKCS#7 container. + * + * The argument list must contain a single BUILD_BLOB_ASN1_DER argument. + * + * @param type type of the container, CONTAINER_PKCS7 + * @param args builder_part_t argument list + * @return container, NULL on failure + */ +pkcs7_t *pkcs7_generic_load(container_type_t type, va_list args); + +#endif /** PKCS7_GENERIC_H_ @}*/ diff --git a/src/libstrongswan/plugins/pkcs7/pkcs7_plugin.c b/src/libstrongswan/plugins/pkcs7/pkcs7_plugin.c new file mode 100644 index 000000000..7d350155d --- /dev/null +++ b/src/libstrongswan/plugins/pkcs7/pkcs7_plugin.c @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2012 Martin Willi + * Copyright (C) 2012 revosec 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. + */ + +#include "pkcs7_plugin.h" +#include "pkcs7_generic.h" +#include "pkcs7_data.h" +#include "pkcs7_signed_data.h" +#include "pkcs7_enveloped_data.h" + +#include <library.h> + +typedef struct private_pkcs7_plugin_t private_pkcs7_plugin_t; + +/** + * private data of pkcs7_plugin + */ +struct private_pkcs7_plugin_t { + + /** + * public functions + */ + pkcs7_plugin_t public; +}; + +METHOD(plugin_t, get_name, char*, + private_pkcs7_plugin_t *this) +{ + return "pkcs7"; +} + +METHOD(plugin_t, get_features, int, + private_pkcs7_plugin_t *this, plugin_feature_t *features[]) +{ + static plugin_feature_t f[] = { + PLUGIN_REGISTER(CONTAINER_DECODE, pkcs7_generic_load, TRUE), + PLUGIN_PROVIDE(CONTAINER_DECODE, CONTAINER_PKCS7), + PLUGIN_REGISTER(CONTAINER_ENCODE, pkcs7_data_gen, TRUE), + PLUGIN_PROVIDE(CONTAINER_ENCODE, CONTAINER_PKCS7_DATA), + PLUGIN_REGISTER(CONTAINER_ENCODE, pkcs7_signed_data_gen, TRUE), + PLUGIN_PROVIDE(CONTAINER_ENCODE, CONTAINER_PKCS7_SIGNED_DATA), + PLUGIN_REGISTER(CONTAINER_ENCODE, pkcs7_enveloped_data_gen, TRUE), + PLUGIN_PROVIDE(CONTAINER_ENCODE, CONTAINER_PKCS7_ENVELOPED_DATA), + }; + *features = f; + return countof(f); +} + +METHOD(plugin_t, destroy, void, + private_pkcs7_plugin_t *this) +{ + free(this); +} + +/* + * see header file + */ +plugin_t *pkcs7_plugin_create() +{ + private_pkcs7_plugin_t *this; + + INIT(this, + .public = { + .plugin = { + .get_name = _get_name, + .get_features = _get_features, + .destroy = _destroy, + }, + }, + ); + + return &this->public.plugin; +} diff --git a/src/libstrongswan/plugins/pkcs7/pkcs7_plugin.h b/src/libstrongswan/plugins/pkcs7/pkcs7_plugin.h new file mode 100644 index 000000000..3d582c7c6 --- /dev/null +++ b/src/libstrongswan/plugins/pkcs7/pkcs7_plugin.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2012 Martin Willi + * Copyright (C) 2012 revosec 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 PURPSE. See the GNU General Public License + * for more details. + */ + +/** + * @defgroup pkcs7p pkcs7 + * @ingroup plugins + * + * @defgroup pkcs7_plugin pkcs7_plugin + * @{ @ingroup pkcs7p + */ + +#ifndef PKCS7_PLUGIN_H_ +#define PKCS7_PLUGIN_H_ + +#include <plugins/plugin.h> + +typedef struct pkcs7_plugin_t pkcs7_plugin_t; + +/** + * Plugin providing PKCS#7 container functionality. + */ +struct pkcs7_plugin_t { + + /** + * Implements plugin interface. + */ + plugin_t plugin; +}; + +#endif /** PKCS7_PLUGIN_H_ @}*/ diff --git a/src/libstrongswan/plugins/pkcs7/pkcs7_signed_data.c b/src/libstrongswan/plugins/pkcs7/pkcs7_signed_data.c new file mode 100644 index 000000000..48fb5e6a4 --- /dev/null +++ b/src/libstrongswan/plugins/pkcs7/pkcs7_signed_data.c @@ -0,0 +1,678 @@ +/* + * Copyright (C) 2012 Martin Willi + * Copyright (C) 2012 revosec 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. + */ + +#include "pkcs7_signed_data.h" +#include "pkcs7_attributes.h" + +#include <time.h> + +#include <utils/debug.h> +#include <asn1/oid.h> +#include <asn1/asn1.h> +#include <asn1/asn1_parser.h> +#include <credentials/sets/mem_cred.h> +#include <credentials/certificates/x509.h> +#include <credentials/keys/private_key.h> + +typedef struct private_pkcs7_signed_data_t private_pkcs7_signed_data_t; + +/** + * Private data of a PKCS#7 signed-data container. + */ +struct private_pkcs7_signed_data_t { + + /** + * Implements pkcs7_t. + */ + pkcs7_t public; + + /** + * Signed content data + */ + container_t *content; + + /** + * Encoded PKCS#7 signed-data + */ + chunk_t encoding; + + /** + * list of signerInfos, signerinfo_t + */ + linked_list_t *signerinfos; + + /** + * Contained certificates + */ + mem_cred_t *creds; +}; + +/** + * A single signerInfo + */ +typedef struct { + + /** + * Signed attributes of signerInfo + */ + pkcs7_attributes_t *attributes; + + /** + * Serial of signing certificate + */ + identification_t *serial; + + /** + * Issuer of signing certificate + */ + identification_t *issuer; + + /** + * EncryptedDigest + */ + chunk_t encrypted_digest; + + /** + * Digesting algorithm OID + */ + int digest_alg; + + /** + * Public key encryption algorithm OID + */ + int enc_alg; + +} signerinfo_t; + +/** + * Destroy a signerinfo_t entry + */ +void signerinfo_destroy(signerinfo_t *this) +{ + DESTROY_IF(this->attributes); + DESTROY_IF(this->serial); + DESTROY_IF(this->issuer); + free(this->encrypted_digest.ptr); + free(this); +} + +/** + * ASN.1 definition of the PKCS#7 signedData type + */ +static const asn1Object_t signedDataObjects[] = { + { 0, "signedData", ASN1_SEQUENCE, ASN1_NONE }, /* 0 */ + { 1, "version", ASN1_INTEGER, ASN1_BODY }, /* 1 */ + { 1, "digestAlgorithms", ASN1_SET, ASN1_LOOP }, /* 2 */ + { 2, "algorithm", ASN1_EOC, ASN1_RAW }, /* 3 */ + { 1, "end loop", ASN1_EOC, ASN1_END }, /* 4 */ + { 1, "contentInfo", ASN1_EOC, ASN1_RAW }, /* 5 */ + { 1, "certificates", ASN1_CONTEXT_C_0, ASN1_OPT | + ASN1_LOOP }, /* 6 */ + { 2, "certificate", ASN1_SEQUENCE, ASN1_OBJ }, /* 7 */ + { 1, "end opt or loop", ASN1_EOC, ASN1_END }, /* 8 */ + { 1, "crls", ASN1_CONTEXT_C_1, ASN1_OPT | + ASN1_LOOP }, /* 9 */ + { 2, "crl", ASN1_SEQUENCE, ASN1_OBJ }, /* 10 */ + { 1, "end opt or loop", ASN1_EOC, ASN1_END }, /* 11 */ + { 1, "signerInfos", ASN1_SET, ASN1_LOOP }, /* 12 */ + { 2, "signerInfo", ASN1_SEQUENCE, ASN1_NONE }, /* 13 */ + { 3, "version", ASN1_INTEGER, ASN1_BODY }, /* 14 */ + { 3, "issuerAndSerialNumber", ASN1_SEQUENCE, ASN1_BODY }, /* 15 */ + { 4, "issuer", ASN1_SEQUENCE, ASN1_OBJ }, /* 16 */ + { 4, "serial", ASN1_INTEGER, ASN1_BODY }, /* 17 */ + { 3, "digestAlgorithm", ASN1_EOC, ASN1_RAW }, /* 18 */ + { 3, "authenticatedAttributes", ASN1_CONTEXT_C_0, ASN1_OPT | + ASN1_OBJ }, /* 19 */ + { 3, "end opt", ASN1_EOC, ASN1_END }, /* 20 */ + { 3, "digestEncryptionAlgorithm", ASN1_EOC, ASN1_RAW }, /* 21 */ + { 3, "encryptedDigest", ASN1_OCTET_STRING, ASN1_BODY }, /* 22 */ + { 3, "unauthenticatedAttributes", ASN1_CONTEXT_C_1, ASN1_OPT }, /* 23 */ + { 3, "end opt", ASN1_EOC, ASN1_END }, /* 24 */ + { 1, "end loop", ASN1_EOC, ASN1_END }, /* 25 */ + { 0, "exit", ASN1_EOC, ASN1_EXIT } +}; +#define PKCS7_VERSION 1 +#define PKCS7_DIGEST_ALG 3 +#define PKCS7_CONTENT_INFO 5 +#define PKCS7_CERT 7 +#define PKCS7_SIGNER_INFO 13 +#define PKCS7_SIGNER_INFO_VERSION 14 +#define PKCS7_ISSUER 16 +#define PKCS7_SERIAL_NUMBER 17 +#define PKCS7_DIGEST_ALGORITHM 18 +#define PKCS7_AUTH_ATTRIBUTES 19 +#define PKCS7_DIGEST_ENC_ALGORITHM 21 +#define PKCS7_ENCRYPTED_DIGEST 22 + +METHOD(container_t, get_type, container_type_t, + private_pkcs7_signed_data_t *this) +{ + return CONTAINER_PKCS7_SIGNED_DATA; +} + +/** + * Signature enumerator implementation + */ +typedef struct { + /** implements enumerator */ + enumerator_t public; + /** inner signerinfos enumerator */ + enumerator_t *inner; + /** currently enumerated auth_cfg */ + auth_cfg_t *auth; + /** currently enumerating signerinfo */ + signerinfo_t *info; + /** reference to container */ + private_pkcs7_signed_data_t *this; +} signature_enumerator_t; + +METHOD(enumerator_t, enumerate, bool, + signature_enumerator_t *this, auth_cfg_t **out) +{ + signerinfo_t *info; + signature_scheme_t scheme; + hash_algorithm_t algorithm; + enumerator_t *enumerator; + certificate_t *cert; + public_key_t *key; + auth_cfg_t *auth; + chunk_t chunk, hash, content; + hasher_t *hasher; + bool valid; + + while (this->inner->enumerate(this->inner, &info)) + { + /* clean up previous round */ + DESTROY_IF(this->auth); + this->auth = NULL; + + scheme = signature_scheme_from_oid(info->digest_alg); + if (scheme == SIGN_UNKNOWN) + { + DBG1(DBG_LIB, "unsupported signature scheme"); + continue; + } + if (!info->attributes) + { + DBG1(DBG_LIB, "no authenticatedAttributes object found"); + continue; + } + if (info->enc_alg != OID_RSA_ENCRYPTION) + { + DBG1(DBG_LIB, "only RSA digest encryption supported"); + continue; + } + + enumerator = lib->credmgr->create_trusted_enumerator(lib->credmgr, + KEY_RSA, info->serial, FALSE); + while (enumerator->enumerate(enumerator, &cert, &auth)) + { + if (info->issuer->equals(info->issuer, cert->get_issuer(cert))) + { + key = cert->get_public_key(cert); + if (key) + { + chunk = info->attributes->get_encoding(info->attributes); + if (key->verify(key, scheme, chunk, info->encrypted_digest)) + { + this->auth = auth->clone(auth); + key->destroy(key); + break; + } + key->destroy(key); + } + } + } + enumerator->destroy(enumerator); + + if (!this->auth) + { + DBG1(DBG_LIB, "unable to verify pkcs7 attributes signature"); + continue; + } + + chunk = info->attributes->get_attribute(info->attributes, + OID_PKCS9_MESSAGE_DIGEST); + if (!chunk.len) + { + DBG1(DBG_LIB, "messageDigest attribute not found"); + continue; + } + if (!this->this->content->get_data(this->this->content, &content)) + { + continue; + } + + algorithm = hasher_algorithm_from_oid(info->digest_alg); + hasher = lib->crypto->create_hasher(lib->crypto, algorithm); + if (!hasher || !hasher->allocate_hash(hasher, content, &hash)) + { + free(content.ptr); + DESTROY_IF(hasher); + DBG1(DBG_LIB, "hash algorithm %N not supported", + hash_algorithm_names, algorithm); + continue; + } + free(content.ptr); + hasher->destroy(hasher); + DBG3(DBG_LIB, "hash: %B", &hash); + + valid = chunk_equals(chunk, hash); + free(hash.ptr); + if (!valid) + { + DBG1(DBG_LIB, "invalid messageDigest"); + continue; + } + *out = this->auth; + this->info = info; + return TRUE; + } + this->info = NULL; + return FALSE; +} + +METHOD(enumerator_t, enumerator_destroy, void, + signature_enumerator_t *this) +{ + lib->credmgr->remove_local_set(lib->credmgr, &this->this->creds->set); + this->inner->destroy(this->inner); + DESTROY_IF(this->auth); + free(this); +} + +METHOD(container_t, create_signature_enumerator, enumerator_t*, + private_pkcs7_signed_data_t *this) +{ + signature_enumerator_t *enumerator; + + INIT(enumerator, + .public = { + .enumerate = (void*)_enumerate, + .destroy = _enumerator_destroy, + }, + .inner = this->signerinfos->create_enumerator(this->signerinfos), + .this = this, + ); + + lib->credmgr->add_local_set(lib->credmgr, &this->creds->set, FALSE); + return &enumerator->public; +} + +METHOD(pkcs7_t, get_attribute, bool, + private_pkcs7_signed_data_t *this, int oid, enumerator_t *enumerator, chunk_t *value) +{ + signature_enumerator_t *e; + chunk_t chunk; + + e = (signature_enumerator_t*)enumerator; + if (e->info) + { + chunk = e->info->attributes->get_attribute(e->info->attributes, oid); + if (chunk.len) + { + *value = chunk_clone(chunk); + return TRUE; + } + } + return FALSE; +} + +METHOD(pkcs7_t, create_cert_enumerator, enumerator_t*, + private_pkcs7_signed_data_t *this) +{ + return this->creds->set.create_cert_enumerator(&this->creds->set, + CERT_ANY, KEY_ANY, NULL, FALSE); +} + +METHOD(container_t, get_data, bool, + private_pkcs7_signed_data_t *this, chunk_t *data) +{ + if (this->content) + { + return this->content->get_data(this->content, data); + } + return FALSE; +} + +METHOD(container_t, get_encoding, bool, + private_pkcs7_signed_data_t *this, chunk_t *data) +{ + *data = chunk_clone(this->encoding); + return TRUE; +} + +METHOD(container_t, destroy, void, + private_pkcs7_signed_data_t *this) +{ + this->creds->destroy(this->creds); + this->signerinfos->destroy_function(this->signerinfos, + (void*)signerinfo_destroy); + DESTROY_IF(this->content); + free(this->encoding.ptr); + free(this); +} + +/** + * Create an empty PKCS#7 signed-data container. + */ +static private_pkcs7_signed_data_t* create_empty() +{ + private_pkcs7_signed_data_t *this; + + INIT(this, + .public = { + .container = { + .get_type = _get_type, + .create_signature_enumerator = _create_signature_enumerator, + .get_data = _get_data, + .get_encoding = _get_encoding, + .destroy = _destroy, + }, + .get_attribute = _get_attribute, + .create_cert_enumerator = _create_cert_enumerator, + }, + .creds = mem_cred_create(), + .signerinfos = linked_list_create(), + ); + + return this; +} + +/** + * Parse PKCS#7 signed data + */ +static bool parse(private_pkcs7_signed_data_t *this, chunk_t content) +{ + asn1_parser_t *parser; + chunk_t object; + int objectID, version; + signerinfo_t *info = NULL; + bool success = FALSE; + + parser = asn1_parser_create(signedDataObjects, content); + parser->set_top_level(parser, 0); + while (parser->iterate(parser, &objectID, &object)) + { + u_int level = parser->get_level(parser); + + switch (objectID) + { + case PKCS7_VERSION: + version = object.len ? (int)*object.ptr : 0; + DBG2(DBG_LIB, " v%d", version); + break; + case PKCS7_CONTENT_INFO: + this->content = lib->creds->create(lib->creds, + CRED_CONTAINER, CONTAINER_PKCS7, + BUILD_BLOB_ASN1_DER, object, BUILD_END); + break; + case PKCS7_CERT: + { + certificate_t *cert; + + DBG2(DBG_LIB, " parsing pkcs7-wrapped certificate"); + cert = lib->creds->create(lib->creds, + CRED_CERTIFICATE, CERT_X509, + BUILD_BLOB_ASN1_DER, object, + BUILD_END); + if (cert) + { + this->creds->add_cert(this->creds, FALSE, cert); + } + break; + } + case PKCS7_SIGNER_INFO: + INIT(info, + .digest_alg = OID_UNKNOWN, + .enc_alg = OID_UNKNOWN, + ); + this->signerinfos->insert_last(this->signerinfos, info); + break; + case PKCS7_SIGNER_INFO_VERSION: + version = object.len ? (int)*object.ptr : 0; + DBG2(DBG_LIB, " v%d", version); + break; + case PKCS7_ISSUER: + info->issuer = identification_create_from_encoding( + ID_DER_ASN1_DN, object); + break; + case PKCS7_SERIAL_NUMBER: + info->serial = identification_create_from_encoding( + ID_KEY_ID, object); + break; + case PKCS7_AUTH_ATTRIBUTES: + *object.ptr = ASN1_SET; + info->attributes = pkcs7_attributes_create_from_chunk( + object, level+1); + *object.ptr = ASN1_CONTEXT_C_0; + break; + case PKCS7_DIGEST_ALGORITHM: + info->digest_alg = asn1_parse_algorithmIdentifier(object, + level, NULL); + break; + case PKCS7_DIGEST_ENC_ALGORITHM: + info->enc_alg = asn1_parse_algorithmIdentifier(object, + level, NULL); + break; + case PKCS7_ENCRYPTED_DIGEST: + info->encrypted_digest = chunk_clone(object); + break; + } + } + success = parser->success(parser); + parser->destroy(parser); + + return success; +} + +/** + * See header. + */ +pkcs7_t *pkcs7_signed_data_load(chunk_t encoding, chunk_t content) +{ + private_pkcs7_signed_data_t *this = create_empty(); + + this->encoding = chunk_clone(encoding); + if (!parse(this, content)) + { + destroy(this); + return NULL; + } + return &this->public; +} + +/** + * build a DER-encoded issuerAndSerialNumber object + */ +static chunk_t build_issuerAndSerialNumber(certificate_t *cert) +{ + identification_t *issuer = cert->get_issuer(cert); + chunk_t serial = chunk_empty; + + if (cert->get_type(cert) == CERT_X509) + { + x509_t *x509 = (x509_t*)cert; + serial = x509->get_serial(x509); + } + + return asn1_wrap(ASN1_SEQUENCE, "cm", + issuer->get_encoding(issuer), + asn1_integer("c", serial)); +} + +/** + * Generate a new PKCS#7 signed-data container + */ +static bool generate(private_pkcs7_signed_data_t *this, private_key_t *key, + certificate_t *cert, hash_algorithm_t alg, + pkcs7_attributes_t *pkcs9) +{ + chunk_t authenticatedAttributes = chunk_empty; + chunk_t encryptedDigest = chunk_empty; + chunk_t data, signerInfo, encoding = chunk_empty; + chunk_t messageDigest, signingTime, attributes; + signature_scheme_t scheme; + hasher_t *hasher; + time_t now; + int digest_oid; + + digest_oid = hasher_algorithm_to_oid(alg); + scheme = signature_scheme_from_oid(digest_oid); + + if (!this->content->get_data(this->content, &data)) + { + return FALSE; + } + + hasher = lib->crypto->create_hasher(lib->crypto, alg); + if (!hasher || !hasher->allocate_hash(hasher, data, &messageDigest)) + { + DESTROY_IF(hasher); + DBG1(DBG_LIB, " hash algorithm %N not support", + hash_algorithm_names, alg); + free(data.ptr); + return FALSE; + } + hasher->destroy(hasher); + pkcs9->add_attribute(pkcs9, + OID_PKCS9_MESSAGE_DIGEST, + asn1_wrap(ASN1_OCTET_STRING, "m", messageDigest)); + + /* take the current time as signingTime */ + now = time(NULL); + signingTime = asn1_from_time(&now, ASN1_UTCTIME); + pkcs9->add_attribute(pkcs9, OID_PKCS9_SIGNING_TIME, signingTime); + pkcs9->add_attribute(pkcs9, OID_PKCS9_CONTENT_TYPE, + asn1_build_known_oid(OID_PKCS7_DATA)); + + attributes = pkcs9->get_encoding(pkcs9); + + if (!key->sign(key, scheme, attributes, &encryptedDigest)) + { + free(data.ptr); + return FALSE; + } + authenticatedAttributes = chunk_clone(attributes); + *authenticatedAttributes.ptr = ASN1_CONTEXT_C_0; + + free(data.ptr); + if (encryptedDigest.ptr) + { + encryptedDigest = asn1_wrap(ASN1_OCTET_STRING, "m", encryptedDigest); + } + signerInfo = asn1_wrap(ASN1_SEQUENCE, "cmmmmm", + ASN1_INTEGER_1, + build_issuerAndSerialNumber(cert), + asn1_algorithmIdentifier(digest_oid), + authenticatedAttributes, + asn1_algorithmIdentifier(OID_RSA_ENCRYPTION), + encryptedDigest); + + if (!cert->get_encoding(cert, CERT_ASN1_DER, &encoding)) + { + free(signerInfo.ptr); + return FALSE; + } + if (!this->content->get_encoding(this->content, &data)) + { + free(encoding.ptr); + free(signerInfo.ptr); + return FALSE; + } + + this->encoding = asn1_wrap(ASN1_SEQUENCE, "mm", + asn1_build_known_oid(OID_PKCS7_SIGNED_DATA), + asn1_wrap(ASN1_CONTEXT_C_0, "m", + asn1_wrap(ASN1_SEQUENCE, "cmmmm", + ASN1_INTEGER_1, + asn1_wrap(ASN1_SET, "m", asn1_algorithmIdentifier(digest_oid)), + data, + asn1_wrap(ASN1_CONTEXT_C_0, "m", encoding), + asn1_wrap(ASN1_SET, "m", signerInfo)))); + + + pkcs9->destroy(pkcs9); + /* TODO: create signerInfos entry */ + return TRUE; +} + +/** + * See header. + */ +pkcs7_t *pkcs7_signed_data_gen(container_type_t type, va_list args) +{ + private_pkcs7_signed_data_t *this; + chunk_t blob = chunk_empty; + hash_algorithm_t alg = HASH_SHA1; + private_key_t *key = NULL; + certificate_t *cert = NULL; + pkcs7_attributes_t *pkcs9; + chunk_t value; + int oid; + + pkcs9 = pkcs7_attributes_create(); + + while (TRUE) + { + switch (va_arg(args, builder_part_t)) + { + case BUILD_SIGNING_KEY: + key = va_arg(args, private_key_t*); + continue; + case BUILD_SIGNING_CERT: + cert = va_arg(args, certificate_t*); + continue; + case BUILD_DIGEST_ALG: + alg = va_arg(args, int); + continue; + case BUILD_BLOB: + blob = va_arg(args, chunk_t); + continue; + case BUILD_PKCS7_ATTRIBUTE: + oid = va_arg(args, int); + value = va_arg(args, chunk_t); + pkcs9->add_attribute(pkcs9, oid, chunk_clone(value)); + continue; + case BUILD_END: + break; + default: + pkcs9->destroy(pkcs9); + return NULL; + } + break; + } + if (blob.len && key && cert) + { + this = create_empty(); + + this->creds->add_cert(this->creds, FALSE, cert->get_ref(cert)); + this->content = lib->creds->create(lib->creds, + CRED_CONTAINER, CONTAINER_PKCS7_DATA, + BUILD_BLOB, blob, BUILD_END); + + if (this->content && generate(this, key, cert, alg, pkcs9)) + { + return &this->public; + } + pkcs9->destroy(pkcs9); + destroy(this); + } + else + { + pkcs9->destroy(pkcs9); + } + return NULL; +} diff --git a/src/libstrongswan/plugins/pkcs7/pkcs7_signed_data.h b/src/libstrongswan/plugins/pkcs7/pkcs7_signed_data.h new file mode 100644 index 000000000..5de672117 --- /dev/null +++ b/src/libstrongswan/plugins/pkcs7/pkcs7_signed_data.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2012 Martin Willi + * Copyright (C) 2012 revosec 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. + */ + +/** + * @defgroup pkcs7_signed_data pkcs7_signed_data + * @{ @ingroup pkcs7p + */ + +#ifndef PKCS7_SIGNED_DATA_H_ +#define PKCS7_SIGNED_DATA_H_ + +#include <credentials/builder.h> +#include <credentials/containers/pkcs7.h> + +/** + * Parse a PKCS#7 signed-data container. + * + * @param encoding full contentInfo encoding + * @param content DER encoded content from contentInfo + * @return CONTAINER_PKCS7_SIGNED_DATA container, NULL on failure + */ +pkcs7_t *pkcs7_signed_data_load(chunk_t encoding, chunk_t content); + +/** + * Generate a PKCS#7 signed-data container. + * + * @param type container type, must be CONTAINER_PKCS7_SIGNED_DATA + * @param args builder_t arguments to use. + */ +pkcs7_t *pkcs7_signed_data_gen(container_type_t type, va_list args); + +#endif /** PKCS7_SIGNED_DATA_H_ @}*/ diff --git a/src/libstrongswan/plugins/plugin_feature.c b/src/libstrongswan/plugins/plugin_feature.c index d39fa6eee..6c954f76d 100644 --- a/src/libstrongswan/plugins/plugin_feature.c +++ b/src/libstrongswan/plugins/plugin_feature.c @@ -42,6 +42,8 @@ ENUM(plugin_feature_names, FEATURE_NONE, FEATURE_CUSTOM, "PUBKEY_ENCRYPT", "CERT_DECODE", "CERT_ENCODE", + "CONTAINER_DECODE", + "CONTAINER_ENCODE", "EAP_SERVER", "EAP_CLIENT", "XAUTH_SERVER", @@ -83,6 +85,8 @@ u_int32_t plugin_feature_hash(plugin_feature_t *feature) case FEATURE_PUBKEY_ENCRYPT: case FEATURE_CERT_DECODE: case FEATURE_CERT_ENCODE: + case FEATURE_CONTAINER_DECODE: + case FEATURE_CONTAINER_ENCODE: case FEATURE_EAP_SERVER: case FEATURE_EAP_PEER: data = chunk_from_thing(feature->arg); @@ -143,6 +147,9 @@ bool plugin_feature_matches(plugin_feature_t *a, plugin_feature_t *b) case FEATURE_CERT_DECODE: case FEATURE_CERT_ENCODE: return a->arg.cert == b->arg.cert; + case FEATURE_CONTAINER_DECODE: + case FEATURE_CONTAINER_ENCODE: + return a->arg.container == b->arg.container; case FEATURE_EAP_SERVER: case FEATURE_EAP_PEER: return a->arg.eap == b->arg.eap; @@ -267,6 +274,14 @@ char* plugin_feature_get_string(plugin_feature_t *feature) return str; } break; + case FEATURE_CONTAINER_DECODE: + case FEATURE_CONTAINER_ENCODE: + if (asprintf(&str, "%N:%N", plugin_feature_names, feature->type, + container_type_names, feature->arg.container) > 0) + { + return str; + } + break; case FEATURE_EAP_SERVER: case FEATURE_EAP_PEER: if (asprintf(&str, "%N:%N", plugin_feature_names, feature->type, @@ -385,6 +400,12 @@ bool plugin_feature_load(plugin_t *plugin, plugin_feature_t *feature, feature->arg.cert, reg->arg.reg.final, reg->arg.reg.f); break; + case FEATURE_CONTAINER_DECODE: + case FEATURE_CONTAINER_ENCODE: + lib->creds->add_builder(lib->creds, CRED_CONTAINER, + feature->arg.container, reg->arg.reg.final, + reg->arg.reg.f); + break; case FEATURE_DATABASE: lib->db->add_database(lib->db, reg->arg.reg.f); break; @@ -454,6 +475,10 @@ bool plugin_feature_unload(plugin_t *plugin, plugin_feature_t *feature, case FEATURE_CERT_ENCODE: lib->creds->remove_builder(lib->creds, reg->arg.reg.f); break; + case FEATURE_CONTAINER_DECODE: + case FEATURE_CONTAINER_ENCODE: + lib->creds->remove_builder(lib->creds, reg->arg.reg.f); + break; case FEATURE_DATABASE: lib->db->remove_database(lib->db, reg->arg.reg.f); break; diff --git a/src/libstrongswan/plugins/plugin_feature.h b/src/libstrongswan/plugins/plugin_feature.h index 90f8a948a..7667fff3e 100644 --- a/src/libstrongswan/plugins/plugin_feature.h +++ b/src/libstrongswan/plugins/plugin_feature.h @@ -29,6 +29,7 @@ typedef struct plugin_feature_t plugin_feature_t; #include <library.h> #include <eap/eap.h> #include <plugins/plugin.h> +#include <credentials/containers/container.h> /** * Callback function of a plugin to (un-)register a specified feature. @@ -133,6 +134,10 @@ struct plugin_feature_t { FEATURE_CERT_DECODE, /** generating certificates */ FEATURE_CERT_ENCODE, + /** parsing containers */ + FEATURE_CONTAINER_DECODE, + /** generating containers */ + FEATURE_CONTAINER_ENCODE, /** EAP server implementation */ FEATURE_EAP_SERVER, /** EAP peer implementation */ @@ -186,6 +191,8 @@ struct plugin_feature_t { encryption_scheme_t pubkey_encrypt; /** FEATURE_CERT_DECODE/ENCODE */ certificate_type_t cert; + /** FEATURE_CONTAINER_DECODE/ENCODE */ + container_type_t container; /** FEATURE_EAP_SERVER/CLIENT */ eap_type_t eap; /** FEATURE_DATABASE */ @@ -281,6 +288,8 @@ struct plugin_feature_t { #define _PLUGIN_FEATURE_PUBKEY_ENCRYPT(kind, scheme) __PLUGIN_FEATURE(kind, PUBKEY_ENCRYPT, .pubkey_encrypt = scheme) #define _PLUGIN_FEATURE_CERT_DECODE(kind, type) __PLUGIN_FEATURE(kind, CERT_DECODE, .cert = type) #define _PLUGIN_FEATURE_CERT_ENCODE(kind, type) __PLUGIN_FEATURE(kind, CERT_ENCODE, .cert = type) +#define _PLUGIN_FEATURE_CONTAINER_DECODE(kind, type) __PLUGIN_FEATURE(kind, CONTAINER_DECODE, .container = type) +#define _PLUGIN_FEATURE_CONTAINER_ENCODE(kind, type) __PLUGIN_FEATURE(kind, CONTAINER_ENCODE, .container = type) #define _PLUGIN_FEATURE_EAP_SERVER(kind, type) __PLUGIN_FEATURE(kind, EAP_SERVER, .eap = type) #define _PLUGIN_FEATURE_EAP_PEER(kind, type) __PLUGIN_FEATURE(kind, EAP_PEER, .eap = type) #define _PLUGIN_FEATURE_DATABASE(kind, type) __PLUGIN_FEATURE(kind, DATABASE, .database = type) @@ -304,6 +313,8 @@ struct plugin_feature_t { #define _PLUGIN_FEATURE_REGISTER_PUBKEY(type, f, final) __PLUGIN_FEATURE_REGISTER_BUILDER(type, f, final) #define _PLUGIN_FEATURE_REGISTER_CERT_DECODE(type, f, final)__PLUGIN_FEATURE_REGISTER_BUILDER(type, f, final) #define _PLUGIN_FEATURE_REGISTER_CERT_ENCODE(type, f, final)__PLUGIN_FEATURE_REGISTER_BUILDER(type, f, final) +#define _PLUGIN_FEATURE_REGISTER_CONTAINER_DECODE(type, f, final)__PLUGIN_FEATURE_REGISTER_BUILDER(type, f, final) +#define _PLUGIN_FEATURE_REGISTER_CONTAINER_ENCODE(type, f, final)__PLUGIN_FEATURE_REGISTER_BUILDER(type, f, final) #define _PLUGIN_FEATURE_REGISTER_DATABASE(type, f) __PLUGIN_FEATURE_REGISTER(type, f) #define _PLUGIN_FEATURE_REGISTER_FETCHER(type, f) __PLUGIN_FEATURE_REGISTER(type, f) diff --git a/src/libstrongswan/plugins/rdrand/Makefile.am b/src/libstrongswan/plugins/rdrand/Makefile.am new file mode 100644 index 000000000..4be7b7215 --- /dev/null +++ b/src/libstrongswan/plugins/rdrand/Makefile.am @@ -0,0 +1,16 @@ + +INCLUDES = -I$(top_srcdir)/src/libstrongswan + +AM_CFLAGS = -rdynamic + +if MONOLITHIC +noinst_LTLIBRARIES = libstrongswan-rdrand.la +else +plugin_LTLIBRARIES = libstrongswan-rdrand.la +endif + +libstrongswan_rdrand_la_SOURCES = \ + rdrand_plugin.h rdrand_plugin.c \ + rdrand_rng.h rdrand_rng.c + +libstrongswan_rdrand_la_LDFLAGS = -module -avoid-version diff --git a/src/libstrongswan/plugins/rdrand/rdrand_plugin.c b/src/libstrongswan/plugins/rdrand/rdrand_plugin.c new file mode 100644 index 000000000..4bdfc258e --- /dev/null +++ b/src/libstrongswan/plugins/rdrand/rdrand_plugin.c @@ -0,0 +1,137 @@ +/* + * Copyright (C) 2012 Martin Willi + * Copyright (C) 2012 revosec 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. + */ + +#include "rdrand_plugin.h" +#include "rdrand_rng.h" + +#include <stdio.h> + +#include <library.h> +#include <utils/debug.h> + +typedef struct private_rdrand_plugin_t private_rdrand_plugin_t; +typedef enum cpuid_feature_t cpuid_feature_t; + +/** + * private data of rdrand_plugin + */ +struct private_rdrand_plugin_t { + + /** + * public functions + */ + rdrand_plugin_t public; +}; + +/** + * CPU feature flags, returned via cpuid(1) + */ +enum cpuid_feature_t { + CPUID_RDRAND = (1<<30), +}; + +/** + * Get cpuid for info, return eax, ebx, ecx and edx. + * -fPIC requires to save ebx on IA-32. + */ +static void cpuid(u_int op, u_int *a, u_int *b, u_int *c, u_int *d) +{ +#ifdef __x86_64__ + asm("cpuid" : "=a" (*a), "=b" (*b), "=c" (*c), "=d" (*d) : "a" (op)); +#else /* __i386__ */ + asm("pushl %%ebx;" + "cpuid;" + "movl %%ebx, %1;" + "popl %%ebx;" + : "=a" (*a), "=r" (*b), "=c" (*c), "=d" (*d) : "a" (op)); +#endif /* __x86_64__ / __i386__*/ +} + +/** + * Check if we have RDRAND instruction + */ +static bool have_rdrand() +{ + char vendor[3 * sizeof(u_int32_t) + 1]; + u_int a, b, c, d; + + cpuid(0, &a, &b, &c, &d); + /* VendorID string is in b-d-c (yes, in this order) */ + snprintf(vendor, sizeof(vendor), "%.4s%.4s%.4s", &b, &d, &c); + + /* check if we have an Intel CPU */ + if (streq(vendor, "GenuineIntel")) + { + cpuid(1, &a, &b, &c, &d); + if (c & CPUID_RDRAND) + { + DBG1(DBG_LIB, "detected RDRAND support on %s CPU", vendor); + return TRUE; + } + } + DBG1(DBG_LIB, "no RDRAND support on %s CPU, disabled", vendor); + return FALSE; +} + +METHOD(plugin_t, get_name, char*, + private_rdrand_plugin_t *this) +{ + return "rdrand"; +} + +METHOD(plugin_t, get_features, int, + private_rdrand_plugin_t *this, plugin_feature_t *features[]) +{ + static plugin_feature_t f[] = { + PLUGIN_REGISTER(RNG, rdrand_rng_create), + PLUGIN_PROVIDE(RNG, RNG_WEAK), + PLUGIN_PROVIDE(RNG, RNG_STRONG), + PLUGIN_PROVIDE(RNG, RNG_TRUE), + PLUGIN_DEPENDS(CRYPTER, ENCR_AES_CBC, 16), + }; + *features = f; + return countof(f); +} + +METHOD(plugin_t, destroy, void, + private_rdrand_plugin_t *this) +{ + free(this); +} + +/* + * see header file + */ +plugin_t *rdrand_plugin_create() +{ + private_rdrand_plugin_t *this; + + INIT(this, + .public = { + .plugin = { + .get_name = _get_name, + .reload = (void*)return_false, + .destroy = _destroy, + }, + }, + ); + + if (have_rdrand()) + { + this->public.plugin.get_features = _get_features; + } + + return &this->public.plugin; +} diff --git a/src/libstrongswan/plugins/rdrand/rdrand_plugin.h b/src/libstrongswan/plugins/rdrand/rdrand_plugin.h new file mode 100644 index 000000000..6f0e55313 --- /dev/null +++ b/src/libstrongswan/plugins/rdrand/rdrand_plugin.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2012 Martin Willi + * Copyright (C) 2012 revosec 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. + */ + +/** + * @defgroup rdrand_p rdrand + * @ingroup plugins + * + * @defgroup rdrand_plugin rdrand_plugin + * @{ @ingroup rdrand_p + */ + +#ifndef RDRAND_PLUGIN_H_ +#define RDRAND_PLUGIN_H_ + +#include <plugins/plugin.h> + +typedef struct rdrand_plugin_t rdrand_plugin_t; + +/** + * Plugin providing random generators based on Intels RDRAND instruction. + */ +struct rdrand_plugin_t { + + /** + * implements plugin interface + */ + plugin_t plugin; +}; + +#endif /** RDRAND_PLUGIN_H_ @}*/ diff --git a/src/libstrongswan/plugins/rdrand/rdrand_rng.c b/src/libstrongswan/plugins/rdrand/rdrand_rng.c new file mode 100644 index 000000000..fa66f3ad7 --- /dev/null +++ b/src/libstrongswan/plugins/rdrand/rdrand_rng.c @@ -0,0 +1,442 @@ +/* + * Copyright (C) 2012 Martin Willi + * Copyright (C) 2012 revosec 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. + */ + +#include "rdrand_rng.h" + +#include <unistd.h> + +typedef struct private_rdrand_rng_t private_rdrand_rng_t; + +/** + * Private data of an rdrand_rng_t object. + */ +struct private_rdrand_rng_t { + + /** + * Public rdrand_rng_t interface. + */ + rdrand_rng_t public; + + /** + * Quality we produce RNG data + */ + rng_quality_t quality; +}; + +/** + * Retries for failed RDRAND instructions + */ +#define MAX_TRIES 16 + +/** + * After how many bytes should we reseed for RNG_STRONG + * (must be a power of two >= 8) + */ +#define FORCE_RESEED 16 + +/** + * How many times we mix reseeded RDRAND output when using RNG_TRUE + */ +#define MIX_ROUNDS 32 + +/** + * Get a two byte word using RDRAND + */ +static bool rdrand16(u_int16_t *out) +{ + u_char res; + int i; + + for (i = 0; i < MAX_TRIES; i++) + { + asm(".byte 0x66;.byte 0x0f;.byte 0xc7;.byte 0xf0; " /* rdrand */ + "setc %1;" + : "=a"(*out), "=qm"(res)); + + if (res) + { + return TRUE; + } + } + return FALSE; +} + +/** + * Get a four byte word using RDRAND + */ +static bool rdrand32(u_int32_t *out) +{ + u_char res; + int i; + + for (i = 0; i < MAX_TRIES; i++) + { + asm(".byte 0x0f;.byte 0xc7;.byte 0xf0;" /* rdrand */ + "setc %1;" + : "=a"(*out), "=qm"(res)); + + if (res) + { + return TRUE; + } + } + return FALSE; +} + +#ifdef __x86_64__ +/** + * Get a eight byte word using RDRAND + */ +static bool rdrand64(u_int64_t *out) +{ + u_char res; + int i; + + for (i = 0; i < MAX_TRIES; i++) + { + asm(".byte 0x48;.byte 0x0f;.byte 0xc7;.byte 0xf0;" /* rdrand */ + "setc %1;" + : "=a"(*out), "=qm"(res)); + + if (res) + { + return TRUE; + } + } + return FALSE; +} +#endif /* __x86_64__ */ + +/** + * Get a one byte word using RDRAND + */ +static bool rdrand8(u_int8_t *out) +{ + u_int16_t u16; + + if (!rdrand16(&u16)) + { + return FALSE; + } + *out = u16; + return TRUE; +} + +/** + * Get a 16 byte word using RDRAND + */ +static bool rdrand128(void *out) +{ +#ifdef __x86_64__ + if (!rdrand64(out) || + !rdrand64(out + sizeof(u_int64_t))) + { + return FALSE; + } +#else /* __i386__ */ + if (!rdrand32(out) || + !rdrand32(out + 1 * sizeof(u_int32_t)) || + !rdrand32(out + 2 * sizeof(u_int32_t)) || + !rdrand32(out + 3 * sizeof(u_int32_t))) + { + return FALSE; + } +#endif /* __x86_64__ / __i386__ */ + return TRUE; +} + +/** + * Enforce a DRNG reseed by reading 511 128-bit samples + */ +static bool reseed() +{ + int i; + +#ifdef __x86_64__ + u_int64_t tmp; + + for (i = 0; i < 511 * 16 / sizeof(u_int64_t); i++) + { + if (!rdrand64(&tmp)) + { + return FALSE; + } + } +#else /* __i386__ */ + u_int32_t tmp; + + for (i = 0; i < 511 * 16 / sizeof(u_int32_t); i++) + { + if (!rdrand32(&tmp)) + { + return FALSE; + } + } +#endif /* __x86_64__ / __i386__ */ + return TRUE; +} + +/** + * Fill a preallocated chunk of data with random bytes + */ +static bool rdrand_chunk(private_rdrand_rng_t *this, chunk_t chunk) +{ + if (this->quality == RNG_STRONG) + { + if (!reseed()) + { + return FALSE; + } + } + + /* align to 2 byte */ + if (chunk.len >= sizeof(u_int8_t)) + { + if ((uintptr_t)chunk.ptr % 2) + { + if (!rdrand8((u_int8_t*)chunk.ptr)) + { + return FALSE; + } + chunk = chunk_skip(chunk, sizeof(u_int8_t)); + } + } + + /* align to 4 byte */ + if (chunk.len >= sizeof(u_int16_t)) + { + if ((uintptr_t)chunk.ptr % 4) + { + if (!rdrand16((u_int16_t*)chunk.ptr)) + { + return FALSE; + } + chunk = chunk_skip(chunk, sizeof(u_int16_t)); + } + } + +#ifdef __x86_64__ + + /* align to 8 byte */ + if (chunk.len >= sizeof(u_int32_t)) + { + if ((uintptr_t)chunk.ptr % 8) + { + if (!rdrand32((u_int32_t*)chunk.ptr)) + { + return FALSE; + } + chunk = chunk_skip(chunk, sizeof(u_int32_t)); + } + } + + /* fill with 8 byte words */ + while (chunk.len >= sizeof(u_int64_t)) + { + if (this->quality == RNG_STRONG && chunk.len % FORCE_RESEED == 0) + { + if (!reseed()) + { + return FALSE; + } + } + if (!rdrand64((u_int64_t*)chunk.ptr)) + { + return FALSE; + } + chunk = chunk_skip(chunk, sizeof(u_int64_t)); + } + + /* append 4 byte word */ + if (chunk.len >= sizeof(u_int32_t)) + { + if (!rdrand32((u_int32_t*)chunk.ptr)) + { + return FALSE; + } + chunk = chunk_skip(chunk, sizeof(u_int32_t)); + } + +#else /* __i386__ */ + + /* fill with 4 byte words */ + while (chunk.len >= sizeof(u_int32_t)) + { + if (this->quality == RNG_STRONG && chunk.len % FORCE_RESEED == 0) + { + if (!reseed()) + { + return FALSE; + } + } + if (!rdrand32((u_int32_t*)chunk.ptr)) + { + return FALSE; + } + chunk = chunk_skip(chunk, sizeof(u_int32_t)); + } + +#endif /* __x86_64__ / __i386__ */ + + if (this->quality == RNG_STRONG) + { + if (!reseed()) + { + return FALSE; + } + } + + /* append 2 byte word */ + if (chunk.len >= sizeof(u_int16_t)) + { + if (!rdrand16((u_int16_t*)chunk.ptr)) + { + return FALSE; + } + chunk = chunk_skip(chunk, sizeof(u_int16_t)); + } + + /* append 1 byte word */ + if (chunk.len >= sizeof(u_int8_t)) + { + if (!rdrand8((u_int8_t*)chunk.ptr)) + { + return FALSE; + } + chunk = chunk_skip(chunk, sizeof(u_int8_t)); + } + + return TRUE; +} + +/** + * Stronger variant mixing reseeded results of rdrand output + * + * This is based on the Intel DRNG "Software Implementation Guide", using + * AES-CBC to mix several reseeded RDRAND outputs. + */ +static bool rdrand_mixed(private_rdrand_rng_t *this, chunk_t chunk) +{ + u_char block[16], forward[16], key[16], iv[16]; + crypter_t *crypter; + int i, len; + + memset(iv, 0, sizeof(iv)); + crypter = lib->crypto->create_crypter(lib->crypto, ENCR_AES_CBC, 16); + if (!crypter) + { + return FALSE; + } + for (i = 0; i < sizeof(key); i++) + { + key[i] = i; + } + if (!crypter->set_key(crypter, chunk_from_thing(key))) + { + crypter->destroy(crypter); + return FALSE; + } + while (chunk.len > 0) + { + memset(forward, 0, sizeof(forward)); + for (i = 0; i < MIX_ROUNDS; i++) + { + /* sleep to reseed PRNG */ + usleep(10); + if (!rdrand128(block)) + { + crypter->destroy(crypter); + return FALSE; + } + memxor(forward, block, sizeof(block)); + if (!crypter->encrypt(crypter, chunk_from_thing(forward), + chunk_from_thing(iv), NULL)) + { + crypter->destroy(crypter); + return FALSE; + } + } + len = min(chunk.len, sizeof(forward)); + memcpy(chunk.ptr, forward, len); + chunk = chunk_skip(chunk, len); + } + crypter->destroy(crypter); + + return TRUE; +} + +METHOD(rng_t, get_bytes, bool, + private_rdrand_rng_t *this, size_t bytes, u_int8_t *buffer) +{ + switch (this->quality) + { + case RNG_WEAK: + case RNG_STRONG: + return rdrand_chunk(this, chunk_create(buffer, bytes)); + case RNG_TRUE: + return rdrand_mixed(this, chunk_create(buffer, bytes)); + default: + return FALSE; + } +} + +METHOD(rng_t, allocate_bytes, bool, + private_rdrand_rng_t *this, size_t bytes, chunk_t *chunk) +{ + *chunk = chunk_alloc(bytes); + if (get_bytes(this, bytes, chunk->ptr)) + { + return TRUE; + } + free(chunk->ptr); + return FALSE; +} + +METHOD(rng_t, destroy, void, + private_rdrand_rng_t *this) +{ + free(this); +} + +/* + * Described in header. + */ +rdrand_rng_t *rdrand_rng_create(rng_quality_t quality) +{ + private_rdrand_rng_t *this; + + switch (quality) + { + case RNG_WEAK: + case RNG_STRONG: + case RNG_TRUE: + break; + default: + return NULL; + } + + INIT(this, + .public = { + .rng = { + .get_bytes = _get_bytes, + .allocate_bytes = _allocate_bytes, + .destroy = _destroy, + }, + }, + .quality = quality, + ); + + return &this->public; +} diff --git a/src/libstrongswan/plugins/rdrand/rdrand_rng.h b/src/libstrongswan/plugins/rdrand/rdrand_rng.h new file mode 100644 index 000000000..d15a48224 --- /dev/null +++ b/src/libstrongswan/plugins/rdrand/rdrand_rng.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2012 Martin Willi + * Copyright (C) 2012 revosec 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. + */ + +/** + * @defgroup rdrand_rng rdrand_rng + * @{ @ingroup rdrand + */ + +#ifndef RDRAND_RNG_H_ +#define RDRAND_RNG_H_ + +#include <crypto/rngs/rng.h> + +typedef struct rdrand_rng_t rdrand_rng_t; + +/** + * RNG implemented with Intels RDRAND instructions, introduced in Ivy Bridge. + */ +struct rdrand_rng_t { + + /** + * Implements rng_t interface. + */ + rng_t rng; +}; + +/** + * Create a rdrand_rng instance. + * + * @param quality RNG quality + * @return RNG instance + */ +rdrand_rng_t *rdrand_rng_create(rng_quality_t quality); + +#endif /** RDRAND_RNG_H_ @}*/ diff --git a/src/libstrongswan/plugins/x509/x509_cert.c b/src/libstrongswan/plugins/x509/x509_cert.c index c93b83bef..85c481552 100644 --- a/src/libstrongswan/plugins/x509/x509_cert.c +++ b/src/libstrongswan/plugins/x509/x509_cert.c @@ -1547,6 +1547,10 @@ METHOD(certificate_t, has_subject, id_match_t, { return ID_MATCH_PERFECT; } + if (chunk_equals(this->serialNumber, encoding)) + { + return ID_MATCH_PERFECT; + } } best = this->subject->matches(this->subject, subject); enumerator = this->subjectAltNames->create_enumerator(this->subjectAltNames); diff --git a/src/pki/Makefile.am b/src/pki/Makefile.am index 482f83834..be74e5d00 100644 --- a/src/pki/Makefile.am +++ b/src/pki/Makefile.am @@ -9,6 +9,7 @@ pki_SOURCES = pki.c pki.h command.c command.h \ commands/self.c \ commands/print.c \ commands/signcrl.c \ + commands/pkcs7.c \ commands/verify.c pki_LDADD = $(top_builddir)/src/libstrongswan/libstrongswan.la diff --git a/src/pki/commands/pkcs7.c b/src/pki/commands/pkcs7.c new file mode 100644 index 000000000..3d53ace00 --- /dev/null +++ b/src/pki/commands/pkcs7.c @@ -0,0 +1,458 @@ +/* + * Copyright (C) 2012 Martin Willi + * Copyright (C) 2012 revosec 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. + */ + +#include "pki.h" + +#include <asn1/oid.h> +#include <asn1/asn1.h> +#include <credentials/containers/pkcs7.h> +#include <credentials/sets/mem_cred.h> + +/** + * Read input data as chunk + */ +static chunk_t read_from_stream(FILE *stream) +{ + char buf[8096]; + size_t len, total = 0; + + while (TRUE) + { + len = fread(buf + total, 1, sizeof(buf) - total, stream); + if (len < 0) + { + return chunk_empty; + } + if (len == 0) + { + return chunk_clone(chunk_create(buf, total)); + } + total += len; + if (total == sizeof(buf)) + { + fprintf(stderr, "buffer too small to read input!\n"); + return chunk_empty; + } + } +} + +/** + * Write output data from chunk to stream + */ +static bool write_to_stream(FILE *stream, chunk_t data) +{ + size_t len, total = 0; + + while (total < data.len) + { + len = fwrite(data.ptr + total, 1, data.len - total, stream); + if (len <= 0) + { + return FALSE; + } + total += len; + } + return TRUE; +} + +/** + * Verify PKCS#7 signed-data + */ +static int verify(chunk_t chunk) +{ + container_t *container; + pkcs7_t *pkcs7; + enumerator_t *enumerator; + certificate_t *cert; + auth_cfg_t *auth; + chunk_t data; + time_t t; + bool verified = FALSE; + + container = lib->creds->create(lib->creds, CRED_CONTAINER, CONTAINER_PKCS7, + BUILD_BLOB_ASN1_DER, chunk, BUILD_END); + if (!container) + { + return 1; + } + + if (container->get_type(container) != CONTAINER_PKCS7_SIGNED_DATA) + { + fprintf(stderr, "verification failed, container is %N\n", + container_type_names, container->get_type(container)); + container->destroy(container); + return 1; + } + + pkcs7 = (pkcs7_t*)container; + enumerator = container->create_signature_enumerator(container); + while (enumerator->enumerate(enumerator, &auth)) + { + verified = TRUE; + cert = auth->get(auth, AUTH_RULE_SUBJECT_CERT); + if (cert) + { + fprintf(stderr, "signed by '%Y'", cert->get_subject(cert)); + + if (pkcs7->get_attribute(pkcs7, OID_PKCS9_SIGNING_TIME, + enumerator, &data)) + { + t = asn1_to_time(&data, ASN1_UTCTIME); + if (t != UNDEFINED_TIME) + { + fprintf(stderr, " at %T", &t, FALSE); + } + free(data.ptr); + } + fprintf(stderr, "\n"); + } + } + enumerator->destroy(enumerator); + + if (!verified) + { + fprintf(stderr, "no trusted signature found\n"); + } + + if (verified) + { + if (container->get_data(container, &data)) + { + write_to_stream(stdout, data); + free(data.ptr); + } + else + { + verified = FALSE; + } + } + container->destroy(container); + + return verified ? 0 : 1; +} + +/** + * Sign data into PKCS#7 signed-data + */ +static int sign(chunk_t chunk, certificate_t *cert, private_key_t *key) +{ + container_t *container; + chunk_t encoding; + int res = 1; + + container = lib->creds->create(lib->creds, + CRED_CONTAINER, CONTAINER_PKCS7_SIGNED_DATA, + BUILD_BLOB, chunk, + BUILD_SIGNING_CERT, cert, + BUILD_SIGNING_KEY, key, + BUILD_END); + if (container) + { + if (container->get_encoding(container, &encoding)) + { + write_to_stream(stdout, encoding); + free(encoding.ptr); + } + container->destroy(container); + } + return res; +} + +/** + * Encrypt data to a PKCS#7 enveloped-data + */ +static int encrypt(chunk_t chunk, certificate_t *cert) +{ + container_t *container; + chunk_t encoding; + int res = 1; + + container = lib->creds->create(lib->creds, + CRED_CONTAINER, CONTAINER_PKCS7_ENVELOPED_DATA, + BUILD_BLOB, chunk, BUILD_CERT, cert, + BUILD_END); + if (container) + { + if (container->get_encoding(container, &encoding)) + { + write_to_stream(stdout, encoding); + free(encoding.ptr); + } + container->destroy(container); + } + return res; +} + +/** + * Decrypt PKCS#7 enveloped-data + */ +static int decrypt(chunk_t chunk) +{ + container_t *container; + chunk_t data; + + container = lib->creds->create(lib->creds, CRED_CONTAINER, CONTAINER_PKCS7, + BUILD_BLOB_ASN1_DER, chunk, BUILD_END); + if (!container) + { + return 1; + } + if (container->get_type(container) != CONTAINER_PKCS7_ENVELOPED_DATA) + { + fprintf(stderr, "decryption failed, container is %N\n", + container_type_names, container->get_type(container)); + container->destroy(container); + return 1; + } + if (!container->get_data(container, &data)) + { + fprintf(stderr, "PKCS#7 decryption failed\n"); + container->destroy(container); + return 1; + } + container->destroy(container); + + write_to_stream(stdout, data); + free(data.ptr); + + return 0; +} + +/** + * Show info about PKCS#7 container + */ +static int show(chunk_t chunk) +{ + container_t *container; + pkcs7_t *pkcs7; + enumerator_t *enumerator; + certificate_t *cert; + chunk_t data; + + container = lib->creds->create(lib->creds, CRED_CONTAINER, CONTAINER_PKCS7, + BUILD_BLOB_ASN1_DER, chunk, BUILD_END); + if (!container) + { + return 1; + } + fprintf(stderr, "%N\n", container_type_names, container->get_type(container)); + + if (container->get_type(container) == CONTAINER_PKCS7_SIGNED_DATA) + { + pkcs7 = (pkcs7_t*)container; + enumerator = pkcs7->create_cert_enumerator(pkcs7); + while (enumerator->enumerate(enumerator, &cert)) + { + if (cert->get_encoding(cert, CERT_PEM, &data)) + { + printf("%.*s", (int)data.len, data.ptr); + free(data.ptr); + } + } + enumerator->destroy(enumerator); + } + container->destroy(container); + return 0; +} + +/** + * Wrap/Unwrap PKCs#7 containers + */ +static int pkcs7() +{ + char *arg, *file = NULL; + private_key_t *key = NULL; + certificate_t *cert = NULL; + chunk_t data = chunk_empty; + mem_cred_t *creds; + int res = 1; + FILE *in; + enum { + OP_NONE, + OP_SIGN, + OP_VERIFY, + OP_ENCRYPT, + OP_DECRYPT, + OP_SHOW, + } op = OP_NONE; + + creds = mem_cred_create(); + + while (TRUE) + { + switch (command_getopt(&arg)) + { + case 'h': + return command_usage(NULL); + case 'i': + file = arg; + continue; + case 's': + if (op != OP_NONE) + { + goto invalid; + } + op = OP_SIGN; + continue; + case 'u': + if (op != OP_NONE) + { + goto invalid; + } + op = OP_VERIFY; + continue; + case 'e': + if (op != OP_NONE) + { + goto invalid; + } + op = OP_ENCRYPT; + continue; + case 'd': + if (op != OP_NONE) + { + goto invalid; + } + op = OP_DECRYPT; + continue; + case 'p': + if (op != OP_NONE) + { + goto invalid; + } + op = OP_SHOW; + continue; + case 'k': + key = lib->creds->create(lib->creds, + CRED_PRIVATE_KEY, KEY_RSA, + BUILD_FROM_FILE, arg, BUILD_END); + if (!key) + { + fprintf(stderr, "parsing private key failed\n"); + goto end; + } + creds->add_key(creds, key); + continue; + case 'c': + cert = lib->creds->create(lib->creds, + CRED_CERTIFICATE, CERT_X509, + BUILD_FROM_FILE, arg, BUILD_END); + if (!cert) + { + fprintf(stderr, "parsing certificate failed\n"); + goto end; + } + creds->add_cert(creds, TRUE, cert); + continue; + case EOF: + break; + default: + invalid: + creds->destroy(creds); + return command_usage("invalid --pkcs7 option"); + } + break; + } + + if (file) + { + in = fopen(file, "r"); + if (in) + { + data = read_from_stream(in); + fclose(in); + } + } + else + { + data = read_from_stream(stdin); + } + + if (!data.len) + { + fprintf(stderr, "reading input failed!\n"); + goto end; + } + if (op != OP_SHOW && !cert) + { + fprintf(stderr, "requiring a certificate!\n"); + goto end; + } + + lib->credmgr->add_local_set(lib->credmgr, &creds->set, FALSE); + + switch (op) + { + case OP_SIGN: + if (!key) + { + fprintf(stderr, "signing requires a private key\n"); + res = 1; + break; + } + res = sign(data, cert, key); + break; + case OP_VERIFY: + res = verify(data); + break; + case OP_ENCRYPT: + res = encrypt(data, cert); + break; + case OP_DECRYPT: + if (!key) + { + fprintf(stderr, "decryption requires a private key\n"); + res = 1; + break; + } + res = decrypt(data); + break; + case OP_SHOW: + res = show(data); + break; + default: + res = 1; + break; + } + lib->credmgr->remove_local_set(lib->credmgr, &creds->set); + +end: + creds->destroy(creds); + free(data.ptr); + return res; +} + +/** + * Register the command. + */ +static void __attribute__ ((constructor))reg() +{ + command_register((command_t) { + pkcs7, '7', "pkcs7", "PKCS#7 wrap/unwrap functions", + {"--sign | --verify | --encrypt | --decrypt", + "--certificate+ [--key]"}, + { + {"help", 'h', 0, "show usage information"}, + {"sign", 's', 0, "create PKCS#7 signed-data"}, + {"verify", 'u', 0, "verify PKCS#7 signed-data"}, + {"encrypt", 'e', 0, "create PKCS#7 enveloped-data"}, + {"decrypt", 'd', 0, "decrypt PKCS#7 enveloped-data"}, + {"show", 'p', 0, "show info about PKCS#7, print certificates"}, + {"in", 'i', 1, "input file, default: stdin"}, + {"key", 'k', 1, "path to private key for sign/decryp"}, + {"cert", 'c', 1, "path to certificate for sign/verify/encryp"}, + } + }); +} diff --git a/src/scepclient/scep.c b/src/scepclient/scep.c index 938340d21..f2090274c 100644 --- a/src/scepclient/scep.c +++ b/src/scepclient/scep.c @@ -22,7 +22,6 @@ #include <asn1/asn1.h> #include <asn1/asn1_parser.h> #include <asn1/oid.h> -#include <crypto/pkcs9.h> #include <crypto/rngs/rng.h> #include <crypto/hashers/hasher.h> @@ -68,13 +67,12 @@ const scep_attributes_t empty_scep_attributes = { /** * Extract X.501 attributes */ -void extract_attributes(pkcs7_t *pkcs7, scep_attributes_t *attrs) +void extract_attributes(pkcs7_t *pkcs7, enumerator_t *enumerator, + scep_attributes_t *attrs) { - pkcs9_t *attributes = pkcs7->get_attributes(pkcs7); chunk_t attr; - attr = attributes->get_attribute(attributes, OID_PKI_MESSAGE_TYPE); - if (attr.ptr) + if (pkcs7->get_attribute(pkcs7, OID_PKI_MESSAGE_TYPE, enumerator, &attr)) { scep_msg_t m; @@ -86,9 +84,9 @@ void extract_attributes(pkcs7_t *pkcs7, scep_attributes_t *attrs) } } DBG2(DBG_APP, "messageType: %s", msgType_names[attrs->msgType]); + free(attr.ptr); } - attr = attributes->get_attribute(attributes, OID_PKI_STATUS); - if (attr.ptr) + if (pkcs7->get_attribute(pkcs7, OID_PKI_STATUS, enumerator, &attr)) { pkiStatus_t s; @@ -100,9 +98,9 @@ void extract_attributes(pkcs7_t *pkcs7, scep_attributes_t *attrs) } } DBG2(DBG_APP, "pkiStatus: %s", pkiStatus_names[attrs->pkiStatus]); + free(attr.ptr); } - attr = attributes->get_attribute(attributes, OID_PKI_FAIL_INFO); - if (attr.ptr) + if (pkcs7->get_attribute(pkcs7, OID_PKI_FAIL_INFO, enumerator, &attr)) { if (attr.len == 1 && *attr.ptr >= '0' && *attr.ptr <= '4') { @@ -112,13 +110,15 @@ void extract_attributes(pkcs7_t *pkcs7, scep_attributes_t *attrs) { DBG1(DBG_APP, "failInfo: %s", failInfo_reasons[attrs->failInfo]); } + free(attr.ptr); } - attrs->senderNonce = attributes->get_attribute(attributes, - OID_PKI_SENDER_NONCE); - attrs->recipientNonce = attributes->get_attribute(attributes, - OID_PKI_RECIPIENT_NONCE); - attrs->transID = attributes->get_attribute(attributes, - OID_PKI_TRANS_ID); + + pkcs7->get_attribute(pkcs7, OID_PKI_SENDER_NONCE, enumerator, + &attrs->senderNonce); + pkcs7->get_attribute(pkcs7, OID_PKI_RECIPIENT_NONCE, enumerator, + &attrs->recipientNonce); + pkcs7->get_attribute(pkcs7, OID_PKI_TRANS_ID, enumerator, + &attrs->transID); } /** @@ -188,68 +188,81 @@ void scep_generate_transaction_id(public_key_t *key, chunk_t *transID, } /** - * Adds a senderNonce attribute to the given pkcs9 attribute list + * Builds a pkcs7 enveloped and signed scep request */ -static bool add_senderNonce_attribute(pkcs9_t *pkcs9) +chunk_t scep_build_request(chunk_t data, chunk_t transID, scep_msg_t msg, + certificate_t *enc_cert, encryption_algorithm_t enc_alg, + size_t key_size, certificate_t *signer_cert, + hash_algorithm_t digest_alg, private_key_t *private_key) { - const size_t nonce_len = 16; - u_char nonce_buf[nonce_len]; - chunk_t senderNonce = { nonce_buf, nonce_len }; + chunk_t request; + container_t *container; + char nonce[16]; rng_t *rng; + chunk_t senderNonce, msgType; + /* generate senderNonce */ rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK); - if (!rng || !rng->get_bytes(rng, nonce_len, nonce_buf)) + if (!rng || !rng->get_bytes(rng, sizeof(nonce), nonce)) { DESTROY_IF(rng); - return FALSE; + return chunk_empty; } rng->destroy(rng); - pkcs9->set_attribute(pkcs9, OID_PKI_SENDER_NONCE, senderNonce); - return TRUE; -} - -/** - * Builds a pkcs7 enveloped and signed scep request - */ -chunk_t scep_build_request(chunk_t data, chunk_t transID, scep_msg_t msg, - certificate_t *enc_cert, encryption_algorithm_t enc_alg, - size_t key_size, certificate_t *signer_cert, - hash_algorithm_t digest_alg, private_key_t *private_key) -{ - chunk_t request, msgType = { - (u_char*)msgType_values[msg], - strlen(msgType_values[msg]), - }; - pkcs7_t *pkcs7; - pkcs9_t *pkcs9; - - pkcs7 = pkcs7_create_from_data(data); - if (!pkcs7->build_envelopedData(pkcs7, enc_cert, enc_alg, key_size)) + /* encrypt data in enveloped-data PKCS#7 */ + container = lib->creds->create(lib->creds, + CRED_CONTAINER, CONTAINER_PKCS7_ENVELOPED_DATA, + BUILD_BLOB, data, + BUILD_CERT, enc_cert, + BUILD_ENCRYPTION_ALG, enc_alg, + BUILD_KEY_SIZE, (int)key_size, + BUILD_END); + if (!container) { - pkcs7->destroy(pkcs7); return chunk_empty; } - - pkcs9 = pkcs9_create(); - pkcs9->set_attribute(pkcs9, OID_PKI_TRANS_ID, transID); - pkcs9->set_attribute(pkcs9, OID_PKI_MESSAGE_TYPE, msgType); - if (!add_senderNonce_attribute(pkcs9)) + if (!container->get_encoding(container, &request)) { - pkcs9->destroy(pkcs9); - pkcs7->destroy(pkcs7); + container->destroy(container); return chunk_empty; } - - pkcs7->set_attributes(pkcs7, pkcs9); - pkcs7->set_certificate(pkcs7, signer_cert->get_ref(signer_cert)); - if (!pkcs7->build_signedData(pkcs7, private_key, digest_alg)) + container->destroy(container); + + /* sign enveloped-data in a signed-data PKCS#7 */ + senderNonce = asn1_wrap(ASN1_OCTET_STRING, "c", chunk_from_thing(nonce)); + transID = asn1_wrap(ASN1_PRINTABLESTRING, "c", transID); + msgType = asn1_wrap(ASN1_PRINTABLESTRING, "c", + chunk_create((char*)msgType_values[msg], + strlen(msgType_values[msg]))); + + container = lib->creds->create(lib->creds, + CRED_CONTAINER, CONTAINER_PKCS7_SIGNED_DATA, + BUILD_BLOB, request, + BUILD_SIGNING_CERT, signer_cert, + BUILD_SIGNING_KEY, private_key, + BUILD_DIGEST_ALG, digest_alg, + BUILD_PKCS7_ATTRIBUTE, OID_PKI_SENDER_NONCE, senderNonce, + BUILD_PKCS7_ATTRIBUTE, OID_PKI_TRANS_ID, transID, + BUILD_PKCS7_ATTRIBUTE, OID_PKI_MESSAGE_TYPE, msgType, + BUILD_END); + + free(request.ptr); + free(senderNonce.ptr); + free(transID.ptr); + free(msgType.ptr); + + if (!container) + { + return chunk_empty; + } + if (!container->get_encoding(container, &request)) { - pkcs7->destroy(pkcs7); + container->destroy(container); return chunk_empty; } - request = pkcs7->get_contentInfo(pkcs7); - pkcs7->destroy(pkcs7); + container->destroy(container); + return request; } @@ -397,23 +410,44 @@ bool scep_http_request(const char *url, chunk_t msg, scep_op_t op, return (status == SUCCESS); } -err_t scep_parse_response(chunk_t response, chunk_t transID, pkcs7_t **data, - scep_attributes_t *attrs, certificate_t *signer_cert) +err_t scep_parse_response(chunk_t response, chunk_t transID, + container_t **out, scep_attributes_t *attrs) { - pkcs7_t *pkcs7; - - pkcs7 = pkcs7_create_from_chunk(response, 0); - if (!pkcs7 || !pkcs7->parse_signedData(pkcs7, signer_cert)) + enumerator_t *enumerator; + bool verified = FALSE; + container_t *container; + auth_cfg_t *auth; + + container = lib->creds->create(lib->creds, CRED_CONTAINER, CONTAINER_PKCS7, + BUILD_BLOB_ASN1_DER, response, BUILD_END); + if (!container) { - DESTROY_IF(pkcs7); return "error parsing the scep response"; } - extract_attributes(pkcs7, attrs); - if (!chunk_equals(transID, attrs->transID)) + if (container->get_type(container) != CONTAINER_PKCS7_SIGNED_DATA) + { + container->destroy(container); + return "scep response is not PKCS#7 signed-data"; + } + + enumerator = container->create_signature_enumerator(container); + while (enumerator->enumerate(enumerator, &auth)) + { + verified = TRUE; + extract_attributes((pkcs7_t*)container, enumerator, attrs); + if (!chunk_equals(transID, attrs->transID)) + { + enumerator->destroy(enumerator); + container->destroy(container); + return "transaction ID of scep response does not match"; + } + } + enumerator->destroy(enumerator); + if (!verified) { - pkcs7->destroy(pkcs7); - return "transaction ID of scep response does not match"; + container->destroy(container); + return "unable to verify PKCS#7 container"; } - *data = pkcs7; + *out = container; return NULL; } diff --git a/src/scepclient/scep.h b/src/scepclient/scep.h index f0c180a71..30551d2db 100644 --- a/src/scepclient/scep.h +++ b/src/scepclient/scep.h @@ -17,7 +17,7 @@ #ifndef _SCEP_H #define _SCEP_H -#include <crypto/pkcs7.h> +#include <credentials/containers/pkcs7.h> #include <credentials/certificates/certificate.h> /* supported SCEP operation types */ @@ -81,7 +81,6 @@ chunk_t scep_build_request(chunk_t data, chunk_t transID, scep_msg_t msg, bool scep_http_request(const char *url, chunk_t message, scep_op_t op, bool http_get_request, chunk_t *response); err_t scep_parse_response(chunk_t response, chunk_t transID, - pkcs7_t **data, scep_attributes_t *attrs, - certificate_t *signer_cert); + container_t **out, scep_attributes_t *attrs); #endif /* _SCEP_H */ diff --git a/src/scepclient/scepclient.c b/src/scepclient/scepclient.c index e33934581..83b5d6219 100644 --- a/src/scepclient/scepclient.c +++ b/src/scepclient/scepclient.c @@ -40,6 +40,7 @@ #include <credentials/certificates/certificate.h> #include <credentials/certificates/x509.h> #include <credentials/certificates/pkcs10.h> +#include <credentials/sets/mem_cred.h> #include <plugins/plugin.h> #include "scep.h" @@ -140,6 +141,8 @@ certificate_t *x509_ca_enc = NULL; certificate_t *x509_ca_sig = NULL; certificate_t *pkcs10_req = NULL; +mem_cred_t *creds = NULL; + /* logging */ static bool log_to_stderr = TRUE; static bool log_to_syslog = TRUE; @@ -255,6 +258,12 @@ static void exit_scepclient(err_t message, ...) { int status = 0; + if (creds) + { + lib->credmgr->remove_set(lib->credmgr, &creds->set); + creds->destroy(creds); + } + DESTROY_IF(subject); DESTROY_IF(private_key); DESTROY_IF(public_key); @@ -926,6 +935,7 @@ int main(int argc, char **argv) if (request_ca_certificate) { char ca_path[PATH_MAX]; + container_t *container; pkcs7_t *pkcs7; if (!scep_http_request(scep_url, chunk_create(ca_name, strlen(ca_name)), @@ -936,10 +946,13 @@ int main(int argc, char **argv) join_paths(ca_path, sizeof(ca_path), CA_CERT_PATH, file_out_ca_cert); - pkcs7 = pkcs7_create_from_chunk(scep_response, 0); - if (!pkcs7 || !pkcs7->parse_signedData(pkcs7, NULL)) + pkcs7 = lib->creds->create(lib->creds, CRED_CONTAINER, CONTAINER_PKCS7, + BUILD_BLOB_ASN1_DER, scep_response, BUILD_END); + + if (!pkcs7) { /* no PKCS#7 encoded CA+RA certificates, assume simple CA cert */ - DESTROY_IF(pkcs7); + + DBG1(DBG_APP, "unable to parse PKCS#7, assuming plain CA cert"); if (!chunk_write(scep_response, ca_path, "ca cert", 0022, force)) { exit_scepclient("could not write ca cert file '%s'", ca_path); @@ -952,7 +965,7 @@ int main(int argc, char **argv) int ra_certs = 0, ca_certs = 0; int ra_index = 1, ca_index = 1; - enumerator = pkcs7->create_certificate_enumerator(pkcs7); + enumerator = pkcs7->create_cert_enumerator(pkcs7); while (enumerator->enumerate(enumerator, &cert)) { x509_t *x509 = (x509_t*)cert; @@ -967,7 +980,7 @@ int main(int argc, char **argv) } enumerator->destroy(enumerator); - enumerator = pkcs7->create_certificate_enumerator(pkcs7); + enumerator = pkcs7->create_cert_enumerator(pkcs7); while (enumerator->enumerate(enumerator, &cert)) { x509_t *x509 = (x509_t*)cert; @@ -1004,11 +1017,15 @@ int main(int argc, char **argv) chunk_free(&encoding); } enumerator->destroy(enumerator); - pkcs7->destroy(pkcs7); + container = &pkcs7->container; + container->destroy(container); } exit_scepclient(NULL); /* no further output required */ } + creds = mem_cred_create(); + lib->credmgr->add_set(lib->credmgr, &creds->set); + /* * input of PKCS#1 file */ @@ -1031,6 +1048,7 @@ int main(int argc, char **argv) { exit_scepclient("no RSA private key available"); } + creds->add_key(creds, private_key->get_ref(private_key)); public_key = private_key->get_public_key(private_key); /* check for minimum key length */ @@ -1181,6 +1199,7 @@ int main(int argc, char **argv) exit_scepclient("generating certificate failed"); } } + creds->add_cert(creds, TRUE, x509_signer->get_ref(x509_signer)); /* * output of self-signed X.509 certificate file @@ -1281,7 +1300,9 @@ int main(int argc, char **argv) enumerator_t *enumerator; char path[PATH_MAX]; time_t poll_start = 0; - pkcs7_t *data = NULL; + pkcs7_t *p7; + container_t *container = NULL; + chunk_t chunk; scep_attributes_t attrs = empty_scep_attributes; join_paths(path, sizeof(path), CA_CERT_PATH, file_in_cacert_sig); @@ -1293,13 +1314,14 @@ int main(int argc, char **argv) exit_scepclient("could not load signature cacert file '%s'", path); } + creds->add_cert(creds, TRUE, x509_ca_sig->get_ref(x509_ca_sig)); + if (!scep_http_request(scep_url, pkcs7, SCEP_PKI_OPERATION, http_get_request, &scep_response)) { exit_scepclient("did not receive a valid scep response"); } - ugh = scep_parse_response(scep_response, transID, &data, &attrs, - x509_ca_sig); + ugh = scep_parse_response(scep_response, transID, &container, &attrs); if (ugh != NULL) { exit_scepclient(ugh); @@ -1328,7 +1350,7 @@ int main(int argc, char **argv) DBG2(DBG_APP, "going to sleep for %d seconds", poll_interval); sleep(poll_interval); free(scep_response.ptr); - data->destroy(data); + container->destroy(container); DBG2(DBG_APP, "fingerprint: %.*s", (int)fingerprint.len, fingerprint.ptr); @@ -1349,8 +1371,7 @@ int main(int argc, char **argv) { exit_scepclient("did not receive a valid scep response"); } - ugh = scep_parse_response(scep_response, transID, &data, &attrs, - x509_ca_sig); + ugh = scep_parse_response(scep_response, transID, &container, &attrs); if (ugh != NULL) { exit_scepclient(ugh); @@ -1359,25 +1380,53 @@ int main(int argc, char **argv) if (attrs.pkiStatus != SCEP_SUCCESS) { - data->destroy(data); + container->destroy(container); exit_scepclient("reply status is not 'SUCCESS'"); } - if (!data->parse_envelopedData(data, serialNumber, private_key)) + if (!container->get_data(container, &chunk)) + { + container->destroy(container); + exit_scepclient("extracting signed-data failed"); + } + container->destroy(container); + + /* decrypt enveloped-data container */ + container = lib->creds->create(lib->creds, + CRED_CONTAINER, CONTAINER_PKCS7, + BUILD_BLOB_ASN1_DER, chunk, + BUILD_END); + free(chunk.ptr); + if (!container) { - data->destroy(data); exit_scepclient("could not decrypt envelopedData"); } - if (!data->parse_signedData(data, NULL)) + + if (!container->get_data(container, &chunk)) { - data->destroy(data); - exit_scepclient("error parsing the scep response"); + container->destroy(container); + exit_scepclient("extracting encrypted-data failed"); } + container->destroy(container); + + /* parse signed-data container */ + container = lib->creds->create(lib->creds, + CRED_CONTAINER, CONTAINER_PKCS7, + BUILD_BLOB_ASN1_DER, chunk, + BUILD_END); + free(chunk.ptr); + if (!container) + { + exit_scepclient("could not parse singed-data"); + } + /* no need to verify the signed-data container, the signature does NOT + * cover the contained certificates */ /* store the end entity certificate */ join_paths(path, sizeof(path), HOST_CERT_PATH, file_out_cert); - enumerator = data->create_certificate_enumerator(data); + p7 = (pkcs7_t*)container; + enumerator = p7->create_cert_enumerator(p7); while (enumerator->enumerate(enumerator, &cert)) { x509_t *x509 = (x509_t*)cert; @@ -1398,7 +1447,11 @@ int main(int argc, char **argv) } } enumerator->destroy(enumerator); - data->destroy(data); + container->destroy(container); + chunk_free(&attrs.transID); + chunk_free(&attrs.senderNonce); + chunk_free(&attrs.recipientNonce); + filetype_out &= ~CERT; /* delete CERT flag */ } diff --git a/src/starter/args.c b/src/starter/args.c index 1c16e0b9c..390062a99 100644 --- a/src/starter/args.c +++ b/src/starter/args.c @@ -108,6 +108,13 @@ static const char *LST_authby[] = { NULL }; +static const char *LST_fragmentation[] = { + "no", + "yes", + "force", + NULL +}; + typedef struct { arg_t type; size_t offset; @@ -138,6 +145,7 @@ static const token_info_t token_info[] = { ARG_STR, offsetof(starter_conn_t, aaa_identity), NULL }, { ARG_MISC, 0, NULL /* KW_MOBIKE */ }, { ARG_MISC, 0, NULL /* KW_FORCEENCAPS */ }, + { ARG_ENUM, offsetof(starter_conn_t, fragmentation), LST_fragmentation }, { ARG_TIME, offsetof(starter_conn_t, sa_ike_life_seconds), NULL }, { ARG_TIME, offsetof(starter_conn_t, sa_ipsec_life_seconds), NULL }, { ARG_TIME, offsetof(starter_conn_t, sa_rekey_margin), NULL }, diff --git a/src/starter/confread.h b/src/starter/confread.h index 3f2079883..a0f6234f9 100644 --- a/src/starter/confread.h +++ b/src/starter/confread.h @@ -50,7 +50,7 @@ typedef enum { typedef enum { STRICT_NO, STRICT_YES, - STRICT_IFURI + STRICT_IFURI, } strict_t; typedef enum { @@ -70,6 +70,13 @@ typedef enum { } dpd_action_t; typedef enum { + /* same as in ike_cfg.h */ + FRAGMENTATION_NO, + FRAGMENTATION_YES, + FRAGMENTATION_FORCE, +} fragmentation_t; + +typedef enum { /* IPsec options */ SA_OPTION_AUTHENTICATE = 1 << 0, /* use AH instead of ESP? */ SA_OPTION_COMPRESS = 1 << 1, /* use IPComp */ @@ -140,6 +147,7 @@ struct starter_conn { char *authby; ipsec_mode_t mode; bool proxy_mode; + fragmentation_t fragmentation; sa_option_t options; time_t sa_ike_life_seconds; time_t sa_ipsec_life_seconds; diff --git a/src/starter/keywords.h b/src/starter/keywords.h index 537bceb07..f776f33c9 100644 --- a/src/starter/keywords.h +++ b/src/starter/keywords.h @@ -42,6 +42,7 @@ typedef enum { KW_AAA_IDENTITY, KW_MOBIKE, KW_FORCEENCAPS, + KW_FRAGMENTATION, KW_IKELIFETIME, KW_KEYLIFE, KW_REKEYMARGIN, diff --git a/src/starter/keywords.txt b/src/starter/keywords.txt index 8366f5209..1f1641287 100644 --- a/src/starter/keywords.txt +++ b/src/starter/keywords.txt @@ -40,6 +40,7 @@ eap_identity, KW_EAP_IDENTITY aaa_identity, KW_AAA_IDENTITY mobike, KW_MOBIKE forceencaps, KW_FORCEENCAPS +fragmentation, KW_FRAGMENTATION ikelifetime, KW_IKELIFETIME lifetime, KW_KEYLIFE keylife, KW_KEYLIFE diff --git a/src/starter/starterstroke.c b/src/starter/starterstroke.c index 68e6fd5ea..4f9e8fb14 100644 --- a/src/starter/starterstroke.c +++ b/src/starter/starterstroke.c @@ -180,6 +180,7 @@ int starter_stroke_add_conn(starter_config_t *cfg, starter_conn_t *conn) } msg.add_conn.mobike = conn->options & SA_OPTION_MOBIKE; msg.add_conn.force_encap = conn->options & SA_OPTION_FORCE_ENCAP; + msg.add_conn.fragmentation = conn->fragmentation; msg.add_conn.ipcomp = conn->options & SA_OPTION_COMPRESS; msg.add_conn.install_policy = conn->install_policy; msg.add_conn.aggressive = conn->aggressive; diff --git a/src/stroke/stroke.c b/src/stroke/stroke.c index 5dae53ea2..e289296c1 100644 --- a/src/stroke/stroke.c +++ b/src/stroke/stroke.c @@ -144,6 +144,7 @@ static int add_connection(char *name, msg.add_conn.mode = 1; msg.add_conn.mobike = 1; msg.add_conn.dpd.action = 1; + msg.add_conn.install_policy = 1; msg.add_conn.me.id = push_string(&msg, my_id); msg.add_conn.me.address = push_string(&msg, my_addr); @@ -389,7 +390,7 @@ static void exit_usage(char *error) printf("Usage:\n"); printf(" Add a connection:\n"); printf(" stroke add NAME MY_ID OTHER_ID MY_ADDR OTHER_ADDR\\\n"); - printf(" MY_NET OTHER_NET MY_NETBITS OTHER_NETBITS\n"); + printf(" MY_NET OTHER_NET\n"); printf(" where: ID is any IKEv2 ID \n"); printf(" ADDR is a IPv4 address\n"); printf(" NET is a IPv4 subnet in CIDR notation\n"); @@ -470,7 +471,7 @@ int main(int argc, char *argv[]) switch (token->kw) { case STROKE_ADD: - if (argc < 11) + if (argc < 9) { exit_usage("\"add\" needs more parameters..."); } diff --git a/src/stroke/stroke_msg.h b/src/stroke/stroke_msg.h index 1ce44f9eb..e972a5984 100644 --- a/src/stroke/stroke_msg.h +++ b/src/stroke/stroke_msg.h @@ -254,6 +254,7 @@ struct stroke_msg_t { int mobike; int aggressive; int force_encap; + int fragmentation; int ipcomp; time_t inactivity; int proxy_mode; |