aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/charon-nm/nm/nm_service.c3
-rw-r--r--src/conftest/README1
-rw-r--r--src/conftest/config.c3
-rw-r--r--src/conftest/conftest.c22
-rw-r--r--src/conftest/hooks/reset_seq.c77
-rw-r--r--src/frontends/android/jni/Android.mk1
-rw-r--r--src/frontends/android/jni/libandroidbridge/android_jni.h32
-rw-r--r--src/frontends/android/jni/libandroidbridge/backend/android_private_key.c24
-rw-r--r--src/frontends/android/jni/libandroidbridge/backend/android_service.c3
-rw-r--r--src/frontends/android/jni/libandroidbridge/kernel/android_net.c11
-rw-r--r--src/frontends/android/jni/libandroidbridge/kernel/network_manager.c45
-rw-r--r--src/frontends/android/jni/libandroidbridge/kernel/network_manager.h11
-rw-r--r--src/frontends/android/res/values-ru/arrays.xml21
-rw-r--r--src/frontends/android/res/values-ru/strings.xml105
-rw-r--r--src/frontends/android/res/values-ua/arrays.xml21
-rw-r--r--src/frontends/android/res/values-ua/strings.xml106
-rw-r--r--src/frontends/android/src/org/strongswan/android/logic/NetworkManager.java31
-rw-r--r--src/libcharon/Android.mk1
-rw-r--r--src/libcharon/Makefile.am8
-rw-r--r--src/libcharon/bus/bus.c4
-rw-r--r--src/libcharon/bus/bus.h25
-rw-r--r--src/libcharon/config/ike_cfg.c17
-rw-r--r--src/libcharon/config/ike_cfg.h27
-rw-r--r--src/libcharon/encoding/message.c26
-rw-r--r--src/libcharon/encoding/payloads/cert_payload.c18
-rw-r--r--src/libcharon/encoding/payloads/cert_payload.h14
-rw-r--r--src/libcharon/encoding/payloads/certreq_payload.h2
-rw-r--r--src/libcharon/encoding/payloads/eap_payload.c8
-rw-r--r--src/libcharon/encoding/payloads/fragment_payload.c225
-rw-r--r--src/libcharon/encoding/payloads/fragment_payload.h94
-rw-r--r--src/libcharon/encoding/payloads/id_payload.c5
-rw-r--r--src/libcharon/encoding/payloads/ike_header.h2
-rw-r--r--src/libcharon/encoding/payloads/payload.c60
-rw-r--r--src/libcharon/encoding/payloads/payload.h17
-rw-r--r--src/libcharon/encoding/payloads/proposal_substructure.c34
-rw-r--r--src/libcharon/encoding/payloads/proposal_substructure.h22
-rw-r--r--src/libcharon/encoding/payloads/sa_payload.c8
-rw-r--r--src/libcharon/encoding/payloads/sa_payload.h8
-rw-r--r--src/libcharon/network/sender.c6
-rw-r--r--src/libcharon/plugins/android/android_service.c2
-rw-r--r--src/libcharon/plugins/duplicheck/duplicheck_listener.c1
-rw-r--r--src/libcharon/plugins/error_notify/.gitignore1
-rw-r--r--src/libcharon/plugins/error_notify/Makefile.am23
-rw-r--r--src/libcharon/plugins/error_notify/error_notify.c62
-rw-r--r--src/libcharon/plugins/error_notify/error_notify_listener.c203
-rw-r--r--src/libcharon/plugins/error_notify/error_notify_listener.h51
-rw-r--r--src/libcharon/plugins/error_notify/error_notify_msg.h66
-rw-r--r--src/libcharon/plugins/error_notify/error_notify_plugin.c83
-rw-r--r--src/libcharon/plugins/error_notify/error_notify_plugin.h42
-rw-r--r--src/libcharon/plugins/error_notify/error_notify_socket.c213
-rw-r--r--src/libcharon/plugins/error_notify/error_notify_socket.h59
-rw-r--r--src/libcharon/plugins/ha/ha_tunnel.c2
-rw-r--r--src/libcharon/plugins/load_tester/load_tester_config.c6
-rw-r--r--src/libcharon/plugins/lookip/lookip_listener.c2
-rw-r--r--src/libcharon/plugins/maemo/maemo_service.c2
-rw-r--r--src/libcharon/plugins/medcli/medcli_config.c5
-rw-r--r--src/libcharon/plugins/medsrv/medsrv_config.c3
-rw-r--r--src/libcharon/plugins/sql/sql_config.c3
-rw-r--r--src/libcharon/plugins/stroke/stroke_config.c8
-rw-r--r--src/libcharon/plugins/tnccs_20/tnccs_20.c44
-rw-r--r--src/libcharon/plugins/uci/uci_config.c6
-rw-r--r--src/libcharon/plugins/unity/unity_handler.c2
-rw-r--r--src/libcharon/processing/jobs/delete_ike_sa_job.c1
-rw-r--r--src/libcharon/processing/jobs/process_message_job.c5
-rw-r--r--src/libcharon/sa/ike_sa.c3
-rw-r--r--src/libcharon/sa/ike_sa.h12
-rw-r--r--src/libcharon/sa/ike_sa_manager.c31
-rw-r--r--src/libcharon/sa/ikev1/task_manager_v1.c347
-rw-r--r--src/libcharon/sa/ikev1/tasks/aggressive_mode.c6
-rw-r--r--src/libcharon/sa/ikev1/tasks/isakmp_cert_pre.c155
-rw-r--r--src/libcharon/sa/ikev1/tasks/isakmp_natd.c41
-rw-r--r--src/libcharon/sa/ikev1/tasks/isakmp_vendor.c173
-rw-r--r--src/libcharon/sa/ikev1/tasks/main_mode.c6
-rw-r--r--src/libcharon/sa/ikev1/tasks/quick_mode.c65
-rw-r--r--src/libcharon/sa/ikev2/tasks/child_create.c11
-rw-r--r--src/libcharon/sa/ikev2/tasks/ike_auth.c17
-rw-r--r--src/libcharon/sa/ikev2/tasks/ike_cert_pre.c253
-rw-r--r--src/libcharon/sa/ikev2/tasks/ike_config.c2
-rw-r--r--src/libcharon/sa/ikev2/tasks/ike_init.c7
-rw-r--r--src/libimcv/imv/imv_msg.c18
-rw-r--r--src/libimcv/imv/imv_msg.h6
-rwxr-xr-xsrc/libimcv/plugins/imv_os/pacman.sh22
-rw-r--r--src/libpts/plugins/imv_attestation/imv_attestation.c4
-rw-r--r--src/libstrongswan/Android.mk6
-rw-r--r--src/libstrongswan/Makefile.am22
-rw-r--r--src/libstrongswan/bio/bio_writer.h7
-rw-r--r--src/libstrongswan/credentials/builder.c3
-rw-r--r--src/libstrongswan/credentials/builder.h8
-rw-r--r--src/libstrongswan/credentials/containers/container.c23
-rw-r--r--src/libstrongswan/credentials/containers/container.h93
-rw-r--r--src/libstrongswan/credentials/containers/pkcs7.h63
-rw-r--r--src/libstrongswan/credentials/credential_factory.c21
-rw-r--r--src/libstrongswan/credentials/credential_factory.h5
-rw-r--r--src/libstrongswan/crypto/nonce_gen.h4
-rw-r--r--src/libstrongswan/crypto/pkcs7.c1061
-rw-r--r--src/libstrongswan/crypto/pkcs7.h178
-rw-r--r--src/libstrongswan/crypto/pkcs9.c369
-rw-r--r--src/libstrongswan/library.h3
-rw-r--r--src/libstrongswan/networking/host.c6
-rw-r--r--src/libstrongswan/plugins/gmp/gmp_rsa_private_key.c2
-rw-r--r--src/libstrongswan/plugins/openssl/Makefile.am1
-rw-r--r--src/libstrongswan/plugins/openssl/openssl_pkcs7.c790
-rw-r--r--src/libstrongswan/plugins/openssl/openssl_pkcs7.h37
-rw-r--r--src/libstrongswan/plugins/openssl/openssl_plugin.c5
-rw-r--r--src/libstrongswan/plugins/openssl/openssl_x509.c4
-rw-r--r--src/libstrongswan/plugins/pkcs7/Makefile.am20
-rw-r--r--src/libstrongswan/plugins/pkcs7/pkcs7_attributes.c273
-rw-r--r--src/libstrongswan/plugins/pkcs7/pkcs7_attributes.h (renamed from src/libstrongswan/crypto/pkcs9.h)51
-rw-r--r--src/libstrongswan/plugins/pkcs7/pkcs7_data.c156
-rw-r--r--src/libstrongswan/plugins/pkcs7/pkcs7_data.h46
-rw-r--r--src/libstrongswan/plugins/pkcs7/pkcs7_enveloped_data.c613
-rw-r--r--src/libstrongswan/plugins/pkcs7/pkcs7_enveloped_data.h44
-rw-r--r--src/libstrongswan/plugins/pkcs7/pkcs7_generic.c126
-rw-r--r--src/libstrongswan/plugins/pkcs7/pkcs7_generic.h38
-rw-r--r--src/libstrongswan/plugins/pkcs7/pkcs7_plugin.c84
-rw-r--r--src/libstrongswan/plugins/pkcs7/pkcs7_plugin.h42
-rw-r--r--src/libstrongswan/plugins/pkcs7/pkcs7_signed_data.c678
-rw-r--r--src/libstrongswan/plugins/pkcs7/pkcs7_signed_data.h44
-rw-r--r--src/libstrongswan/plugins/plugin_feature.c25
-rw-r--r--src/libstrongswan/plugins/plugin_feature.h11
-rw-r--r--src/libstrongswan/plugins/rdrand/Makefile.am16
-rw-r--r--src/libstrongswan/plugins/rdrand/rdrand_plugin.c137
-rw-r--r--src/libstrongswan/plugins/rdrand/rdrand_plugin.h42
-rw-r--r--src/libstrongswan/plugins/rdrand/rdrand_rng.c442
-rw-r--r--src/libstrongswan/plugins/rdrand/rdrand_rng.h47
-rw-r--r--src/libstrongswan/plugins/x509/x509_cert.c4
-rw-r--r--src/pki/Makefile.am1
-rw-r--r--src/pki/commands/pkcs7.c458
-rw-r--r--src/scepclient/scep.c174
-rw-r--r--src/scepclient/scep.h5
-rw-r--r--src/scepclient/scepclient.c93
-rw-r--r--src/starter/args.c8
-rw-r--r--src/starter/confread.h10
-rw-r--r--src/starter/keywords.h1
-rw-r--r--src/starter/keywords.txt1
-rw-r--r--src/starter/starterstroke.c1
-rw-r--r--src/stroke/stroke.c5
-rw-r--r--src/stroke/stroke_msg.h1
138 files changed, 7491 insertions, 2139 deletions
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, &section))
@@ -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">Загрузка&#8230;</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">Соединение&#8230;</string>
+ <string name="state_connected">Соединен</string>
+ <string name="state_disconnecting">Отключение&#8230;</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">Завантаження&#8230;</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">Підключення&#8230;</string>
+ <string name="state_connected">Підключений</string>
+ <string name="state_disconnecting">Роз\'єднання&#8230;</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;