From 08e0547bd0ee88f338be27634b284012ff68ee2c Mon Sep 17 00:00:00 2001 From: Defunct Date: Tue, 6 Jun 2017 15:14:32 +0000 Subject: [PATCH 1/2] p12 import support --- CMakeLists.txt | 4 +- fuzz.cc | 2 +- main.cc | 5 +- strongswan_profile.c | 207 +++++++++++++++++++++++++++++++++++++----- strongswan_profile.h | 9 ++ strongswan_profile.hh | 16 ++++ 6 files changed, 217 insertions(+), 26 deletions(-) create mode 100644 strongswan_profile.hh diff --git a/CMakeLists.txt b/CMakeLists.txt index b6de209..9582753 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -14,12 +14,14 @@ pkg_search_module(LIBNM libnm) include_directories(${LIBNM_INCLUDE_DIRS}) include_directories(${GLIB_INCLUDE_DIRS}) include_directories(${JSON_INCLUDE_DIRS}) +include_directories(/home/ubuntu/src/strongswan/src/libstrongswan) link_libraries(${GLIB_LIBRARIES}) link_libraries(${JSON_LIBRARIES}) link_libraries(${LIBNM_LIBRARIES}) +link_libraries(/usr/local/lib/ipsec/libstrongswan.so) add_library(strongswan_profile STATIC strongswan_profile.c) set(SOURCE_FILES main.cc) -add_executable(fuzz fuzz.cc strongswan_profile.c) +add_executable(fuzz fuzz.cc strongswan_profile.c strongswan_profile.hh) target_link_libraries(fuzz /usr/lib/llvm-4.0/lib/libFuzzer.a) add_executable(strongswan-import ${SOURCE_FILES}) target_link_libraries(strongswan-import strongswan_profile) diff --git a/fuzz.cc b/fuzz.cc index bbe7375..92f0d2c 100644 --- a/fuzz.cc +++ b/fuzz.cc @@ -1,6 +1,6 @@ #include #include -#include "strongswan_profile.h" +#include "strongswan_profile.hh" extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { GError *error = NULL; diff --git a/main.cc b/main.cc index 62ecbdd..78f7011 100644 --- a/main.cc +++ b/main.cc @@ -1,9 +1,7 @@ #include #include #include -#include "strongswan_profile.h" - -//extern "C" NMConnection* strongswan_import_sswan(const char *path); +#include "strongswan_profile.hh" static void added_cb (GObject *client, GAsyncResult *result, gpointer user_data) { @@ -31,6 +29,7 @@ int main(int argc, char **argv) { GError *error = NULL; if (argc <= 1) return -1; + NMConnection *connection = strongswan_import_sswan(nullptr, argv[1], &error); if(error) { g_message("%s", error->message); diff --git a/strongswan_profile.c b/strongswan_profile.c index 5f28656..6b0411f 100644 --- a/strongswan_profile.c +++ b/strongswan_profile.c @@ -22,14 +22,22 @@ * SOFTWARE. */ +#include "/home/ubuntu/src/strongswan/config.h" #include #include +#include +#include +#include +#include +#include #include "strongswan_profile.h" static void serializable_iface_init(JsonSerializableIface *iface) {} G_DEFINE_TYPE_WITH_CODE(StrongSwanProfile, strongswan_profile, G_TYPE_OBJECT, - G_IMPLEMENT_INTERFACE(JSON_TYPE_SERIALIZABLE, serializable_iface_init)); + G_IMPLEMENT_INTERFACE(JSON_TYPE_SERIALIZABLE, serializable_iface_init)) + +char *chunk_to_str(chunk_t *chunk); static GParamSpec *properties[N_PROPERTIES] = {NULL}; @@ -135,15 +143,15 @@ static void strongswan_profile_dispose(GObject *gObject) { static void strongswan_profile_finalize(GObject *gObject) { StrongSwanProfile *priv = strongswan_profile_get_instance_private(STRONGSWAN_PROFILE_SOURCE(gObject)); - if(priv->user_key) g_free(priv->user_key); - if(priv->user_cert) g_free(priv->user_cert); - if(priv->certificate) g_free(priv->certificate); - if(priv->remote_addr) g_free(priv->remote_addr); - if(priv->name) g_free(priv->name); - if(priv->esp) g_free(priv->esp); - if(priv->ike) g_free(priv->ike); - if(priv->p12) g_free(priv->p12); - if(priv->uuid) g_free(priv->uuid); + if (priv->user_key) g_free(priv->user_key); + if (priv->user_cert) g_free(priv->user_cert); + if (priv->certificate) g_free(priv->certificate); + if (priv->remote_addr) g_free(priv->remote_addr); + if (priv->name) g_free(priv->name); + if (priv->esp) g_free(priv->esp); + if (priv->ike) g_free(priv->ike); + if (priv->p12) g_free(priv->p12); + if (priv->uuid) g_free(priv->uuid); } static void strongswan_profile_class_init(StrongSwanProfileClass *klass) { @@ -158,10 +166,10 @@ static void strongswan_profile_class_init(StrongSwanProfileClass *klass) { NULL, G_PARAM_READWRITE); properties[PROP_VPNNAME] = g_param_spec_string("name", - "Profile name", - "The name of the VPN Profile.", - NULL, - G_PARAM_READWRITE); + "Profile name", + "The name of the VPN Profile.", + NULL, + G_PARAM_READWRITE); properties[PROP_METHOD] = g_param_spec_enum("method", "VPN connection method", "The method of VPN connection; one of (key, agent, smartcard, eap)", @@ -214,15 +222,19 @@ NMConnection *parse_sswan(JsonParser *parser, GError **error) { NMSettingIPConfig *s_ip4; NMSettingVpn *s_vpn; StrongSwanProfile *profile; + gsize length = 0; + guchar *p12_data; + chunk_t chunk; + char *buf; JsonNode *root = json_parser_get_root(parser); - if(root==NULL) { return NULL; } + if (root == NULL) { return NULL; } //TODO: check for memory leaks throughout all of this connection = nm_simple_connection_new(); - profile = (StrongSwanProfile *) json_gobject_deserialize(STRONGSWAN_PROFILE_TYPE_SOURCE, root); - if(profile == NULL) { return NULL; } + profile = (StrongSwanProfile *) json_gobject_deserialize(STRONGSWAN_PROFILE_TYPE_SOURCE, root); + if (profile == NULL) { return NULL; } s_con = NM_SETTING_CONNECTION(nm_setting_connection_new()); @@ -277,8 +289,37 @@ NMConnection *parse_sswan(JsonParser *parser, GError **error) { switch (profile->method) { case METHOD_KEY: setting_vpn_add_data_item(s_vpn, "method", "key"); - if (profile->p12) - nm_setting_vpn_add_secret(s_vpn, "p12", profile->p12); + if (profile->p12) { +// nm_setting_vpn_add_secret(s_vpn, "p12", profile->p12); + p12_data = g_base64_decode(profile->p12, &length); + g_assert(p12_data != NULL); + chunk = chunk_create(p12_data, length); + profile_t *pp = load_p12(chunk); + chunk_free(&chunk); + g_assert(pp != NULL); + if (pp->ca->get_encoding(pp->ca, CERT_PEM, &chunk)) { + buf = chunk_to_str(&chunk); + nm_setting_vpn_add_secret(s_vpn, "certificate", buf); + free(buf); + chunk_free(&chunk); + } + if (pp->user_cert->get_encoding(pp->user_cert, CERT_PEM, &chunk)) { + buf = chunk_to_str(&chunk); + nm_setting_vpn_add_secret(s_vpn, "usercert", buf); + free(buf); + chunk_free(&chunk); + } + if (pp->private_key->get_encoding(pp->private_key, PRIVKEY_PEM, &chunk)) { + buf = chunk_to_str(&chunk); + nm_setting_vpn_add_secret(s_vpn, "userkey", buf); + free(buf); + chunk_free(&chunk); + } + pp->ca->destroy(pp->ca); + pp->user_cert->destroy(pp->user_cert); + pp->private_key->destroy(pp->private_key); + free(pp); + } if (profile->certificate) setting_vpn_add_data_item(s_vpn, "certificate", profile->certificate); if (profile->user_cert) @@ -321,6 +362,13 @@ NMConnection *parse_sswan(JsonParser *parser, GError **error) { return connection; } +char *chunk_to_str(chunk_t *chunk) { + char *buf = malloc((*chunk).len + 1); + memset(buf, 0, (*chunk).len + 1); + memcpy(buf, (*chunk).ptr, (*chunk).len); + return buf; +} + NMConnection *strongswan_fuzz_import(const char *data, size_t size, GError **error) { NMConnection *connection; JsonParser *parser = json_parser_new(); @@ -329,12 +377,129 @@ NMConnection *strongswan_fuzz_import(const char *data, size_t size, GError **err g_object_unref(parser); return NULL; } - connection = parse_sswan(parser, error); + connection = parse_sswan(parser, error); g_object_unref(parser); return connection; } + +/** + * Callback credential set pki uses + */ +static callback_cred_t *cb_set; + +/** + * Credential set to cache entered secrets + */ +static mem_cred_t *cb_creds; + +static shared_key_type_t prompted; + +static shared_key_t *cb(void *data, shared_key_type_t type, + identification_t *me, identification_t *other, + id_match_t *match_me, id_match_t *match_other) { + char buf[64], *label, *secret = NULL; + shared_key_t *shared; + + if (prompted == type) { + return NULL; + } + switch (type) { + case SHARED_PIN: + label = "Smartcard PIN"; + break; + case SHARED_PRIVATE_KEY_PASS: + label = "Private key passphrase"; + break; + default: + return NULL; + } + snprintf(buf, sizeof(buf), "%s: ", label); +#ifdef HAVE_GETPASS + secret = getpass(buf); +#endif + if (secret && strlen(secret)) { + prompted = type; + if (match_me) { + *match_me = ID_MATCH_PERFECT; + } + if (match_other) { + *match_other = ID_MATCH_NONE; + } + shared = shared_key_create(type, chunk_clone(chunk_from_str(secret))); + /* cache password in case it is required more than once */ + cb_creds->add_shared(cb_creds, shared, NULL); + return shared->get_ref(shared); + } + return NULL; +} + +/** + * Register PIN/Passphrase callback function + */ +static void add_callback() { + cb_set = callback_cred_create_shared(cb, NULL); + lib->credmgr->add_set(lib->credmgr, &cb_set->set); + cb_creds = mem_cred_create(); + lib->credmgr->add_set(lib->credmgr, &cb_creds->set); +} + +/** + * Unregister PIN/Passphrase callback function + */ +static void remove_callback() { + lib->credmgr->remove_set(lib->credmgr, &cb_creds->set); + cb_creds->destroy(cb_creds); + lib->credmgr->remove_set(lib->credmgr, &cb_set->set); + cb_set->destroy(cb_set); +} + +static profile_t *load_p12(chunk_t data) { + certificate_t *cert; + private_key_t *key; + chunk_t encoding; + profile_t *profile = malloc(sizeof(profile_t)); + + if (!library_init(NULL, "strongswan-import")) { + library_deinit(); + exit(SS_RC_LIBSTRONGSWAN_INTEGRITY); + } + if (lib->integrity && lib->integrity->check_file(lib->integrity, "strongswan-import", "strongswan-import")) { + exit(SS_RC_DAEMON_INTEGRITY); + } + //from config.h + //"aes des rc2 sha2 sha1 md5 random x509 revocation pkcs1 pkcs7 pkcs8 pkcs12 dnskey sshkey pem openssl gmp ecdsa curve25519 hmac" + // TODO: this uses additional config files, we could just inline this to avoid that. + bool loaded = lib->plugins->load(lib->plugins, + lib->settings->get_str(lib->settings, "strongswan-import.load", "")); + g_assert(loaded == true); + add_callback(); + + pkcs12_t *p12 = lib->creds->create(lib->creds, CRED_CONTAINER, CONTAINER_PKCS12, BUILD_BLOB, + data, BUILD_END); + g_assert(p12 != NULL); + enumerator_t *enumerator = p12->create_cert_enumerator(p12); + while (enumerator->enumerate(enumerator, &cert)) { + x509_t *x509 = (x509_t *) cert; + // TODO: this should be X509_CA not X509_SELF_SIGNED but pyOpenSSL does not permit setting x509v3 flags + if (x509->get_flags(x509) & X509_SELF_SIGNED) + profile->ca = cert->get_ref(cert); + else + profile->user_cert = cert->get_ref(cert); + } + enumerator->destroy(enumerator); + enumerator = p12->create_key_enumerator(p12); + while (enumerator->enumerate(enumerator, &key)) + profile->private_key = key->get_ref(key); + + enumerator->destroy(enumerator); + p12->container.destroy(&p12->container); + remove_callback(); + return profile; +} + NMConnection *strongswan_import_sswan(NMVpnEditorPlugin *iface, const char *path, GError **error) { + typedef struct profile_t profile_t; NMConnection *connection; JsonParser *parser = json_parser_new(); json_parser_load_from_file(parser, path, error); @@ -342,7 +507,7 @@ NMConnection *strongswan_import_sswan(NMVpnEditorPlugin *iface, const char *path g_object_unref(parser); return NULL; } - connection = parse_sswan(parser, error); + connection = parse_sswan(parser, error); g_object_unref(parser); return connection; } diff --git a/strongswan_profile.h b/strongswan_profile.h index 617817d..e74aaff 100644 --- a/strongswan_profile.h +++ b/strongswan_profile.h @@ -28,6 +28,7 @@ #include #include #include +#include G_BEGIN_DECLS @@ -79,11 +80,19 @@ typedef struct StrongSwanProfileClass_ { GObjectClass parent_class; } StrongSwanProfileClass; +// TODO: this really should provider an interface for destroying buffers +typedef struct profile_t { + certificate_t *ca; + certificate_t *user_cert; + private_key_t *private_key; +} profile_t; + GType strongswan_profile_get_type(void); GType strongswan_vpn_method_get_type(void); NMConnection *parse_sswan(JsonParser *parser, GError **error); NMConnection *strongswan_import_sswan(NMVpnEditorPlugin *iface, const char *path, GError **error); NMConnection *strongswan_fuzz_import(const char *data, size_t size, GError **error); +static profile_t* load_p12(chunk_t data); G_END_DECLS diff --git a/strongswan_profile.hh b/strongswan_profile.hh new file mode 100644 index 0000000..8d2e6c8 --- /dev/null +++ b/strongswan_profile.hh @@ -0,0 +1,16 @@ +// +// Created by ubuntu on 6/5/17. +// + +#ifndef STRONGSWAN_IMPORT_STRONGSWAN_HH_H +#define STRONGSWAN_IMPORT_STRONGSWAN_HH_H +#include + +G_BEGIN_DECLS + +NMConnection *strongswan_import_sswan(NMVpnEditorPlugin *iface, const char *path, GError **error); +NMConnection *strongswan_fuzz_import(const char *data, size_t size, GError **error); + +G_END_DECLS + +#endif //STRONGSWAN_IMPORT_STRONGSWAN_HH_H From 2b63ae2e25c66aa3c6df8cb4af6f2af9f3330c80 Mon Sep 17 00:00:00 2001 From: Defunct Date: Thu, 8 Jun 2017 02:59:50 +0000 Subject: [PATCH 2/2] switch to using openssl --- CMakeLists.txt | 25 +++-- strongswan_profile.c | 262 +++++++++++++++++-------------------------- strongswan_profile.h | 18 ++- 3 files changed, 134 insertions(+), 171 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9582753..4b1a863 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,7 @@ cmake_minimum_required(VERSION 3.7) project(strongswan-import) +set(STANDALONE_IMPORT) set(CMAKE_CXX_STANDARD 14) find_package(PkgConfig REQUIRED) set(CMAKE_CXX_COMPILER "clang++") @@ -8,20 +9,28 @@ set(CMAKE_C_COMPILER "clang") set(CMAKE_C_FLAGS "-fno-omit-frame-pointer -g -fsanitize=address -fsanitize-coverage=trace-pc-guard") set(CMAKE_CXX_FLAGS "-fno-omit-frame-pointer -g -fsanitize=address -fsanitize-coverage=trace-pc-guard") +set(SOURCE_FILES main.cc) +add_library(strongswan_profile STATIC strongswan_profile.c) +add_executable(fuzz fuzz.cc) +add_executable(strongswan-import ${SOURCE_FILES}) + +pkg_search_module(LIBSSL libssl) pkg_search_module(GLIB glib-2.0) pkg_search_module(JSON json-glib-1.0) pkg_search_module(LIBNM libnm) +pkg_search_module(LIBCRYPTO libcrypto) include_directories(${LIBNM_INCLUDE_DIRS}) include_directories(${GLIB_INCLUDE_DIRS}) -include_directories(${JSON_INCLUDE_DIRS}) -include_directories(/home/ubuntu/src/strongswan/src/libstrongswan) link_libraries(${GLIB_LIBRARIES}) -link_libraries(${JSON_LIBRARIES}) link_libraries(${LIBNM_LIBRARIES}) -link_libraries(/usr/local/lib/ipsec/libstrongswan.so) -add_library(strongswan_profile STATIC strongswan_profile.c) -set(SOURCE_FILES main.cc) -add_executable(fuzz fuzz.cc strongswan_profile.c strongswan_profile.hh) + +target_include_directories(strongswan_profile PUBLIC + ${GLIB_INCLUDE_DIRS} ${JSON_INCLUDE_DIRS} ${LIBNM_INCLUDE_DIRS} + ${JSON_INCLUDE_DIRS} ${LIBSSL_INCLUDE_DIRS} ${LIBCRYPTO_INCLUDE_DIRS}) +target_link_libraries(strongswan_profile PUBLIC + ${GLIB_LIBRARIES} ${JSON_LIBRARIES} ${LIBNM_LIBRARIES} + ${JSON_LIBRARIES} ${LIBSSL_LIBRARIES} ${LIBCRYPTO_LIBRARIES}) + target_link_libraries(fuzz /usr/lib/llvm-4.0/lib/libFuzzer.a) -add_executable(strongswan-import ${SOURCE_FILES}) target_link_libraries(strongswan-import strongswan_profile) +target_link_libraries(fuzz strongswan_profile) \ No newline at end of file diff --git a/strongswan_profile.c b/strongswan_profile.c index 6b0411f..6af2310 100644 --- a/strongswan_profile.c +++ b/strongswan_profile.c @@ -22,14 +22,12 @@ * SOFTWARE. */ -#include "/home/ubuntu/src/strongswan/config.h" +#include +#include +#include +#include #include #include -#include -#include -#include -#include -#include #include "strongswan_profile.h" static void serializable_iface_init(JsonSerializableIface *iface) {} @@ -37,8 +35,6 @@ static void serializable_iface_init(JsonSerializableIface *iface) {} G_DEFINE_TYPE_WITH_CODE(StrongSwanProfile, strongswan_profile, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE(JSON_TYPE_SERIALIZABLE, serializable_iface_init)) -char *chunk_to_str(chunk_t *chunk); - static GParamSpec *properties[N_PROPERTIES] = {NULL}; static void setting_vpn_add_data_item(NMSettingVpn *setting, const char *key, const char *value) { @@ -223,9 +219,8 @@ NMConnection *parse_sswan(JsonParser *parser, GError **error) { NMSettingVpn *s_vpn; StrongSwanProfile *profile; gsize length = 0; - guchar *p12_data; - chunk_t chunk; - char *buf; + profile_t *prof; + char *p12_data; JsonNode *root = json_parser_get_root(parser); if (root == NULL) { return NULL; } @@ -290,42 +285,22 @@ NMConnection *parse_sswan(JsonParser *parser, GError **error) { case METHOD_KEY: setting_vpn_add_data_item(s_vpn, "method", "key"); if (profile->p12) { -// nm_setting_vpn_add_secret(s_vpn, "p12", profile->p12); - p12_data = g_base64_decode(profile->p12, &length); + p12_data = (char *) g_base64_decode(profile->p12, &length); g_assert(p12_data != NULL); - chunk = chunk_create(p12_data, length); - profile_t *pp = load_p12(chunk); - chunk_free(&chunk); - g_assert(pp != NULL); - if (pp->ca->get_encoding(pp->ca, CERT_PEM, &chunk)) { - buf = chunk_to_str(&chunk); - nm_setting_vpn_add_secret(s_vpn, "certificate", buf); - free(buf); - chunk_free(&chunk); - } - if (pp->user_cert->get_encoding(pp->user_cert, CERT_PEM, &chunk)) { - buf = chunk_to_str(&chunk); - nm_setting_vpn_add_secret(s_vpn, "usercert", buf); - free(buf); - chunk_free(&chunk); - } - if (pp->private_key->get_encoding(pp->private_key, PRIVKEY_PEM, &chunk)) { - buf = chunk_to_str(&chunk); - nm_setting_vpn_add_secret(s_vpn, "userkey", buf); - free(buf); - chunk_free(&chunk); - } - pp->ca->destroy(pp->ca); - pp->user_cert->destroy(pp->user_cert); - pp->private_key->destroy(pp->private_key); - free(pp); + prof = load_p12(p12_data, length); + g_free(p12_data); + nm_setting_vpn_add_secret(s_vpn, "userkey", prof->priv_key); + nm_setting_vpn_add_secret(s_vpn, "usercert", prof->user_cert); + nm_setting_vpn_add_secret(s_vpn, "certificate", prof->ca); + prof->destroy(prof); + } else { + if (profile->certificate) + setting_vpn_add_data_item(s_vpn, "certificate", profile->certificate); + if (profile->user_cert) + setting_vpn_add_data_item(s_vpn, "usercert", profile->user_cert); + if (profile->user_key) + setting_vpn_add_data_item(s_vpn, "userkey", profile->user_key); } - if (profile->certificate) - setting_vpn_add_data_item(s_vpn, "certificate", profile->certificate); - if (profile->user_cert) - setting_vpn_add_data_item(s_vpn, "usercert", profile->user_cert); - if (profile->user_key) - setting_vpn_add_data_item(s_vpn, "userkey", profile->user_key); break; case METHOD_AGENT: case METHOD_SMARTCARD: @@ -362,12 +337,6 @@ NMConnection *parse_sswan(JsonParser *parser, GError **error) { return connection; } -char *chunk_to_str(chunk_t *chunk) { - char *buf = malloc((*chunk).len + 1); - memset(buf, 0, (*chunk).len + 1); - memcpy(buf, (*chunk).ptr, (*chunk).len); - return buf; -} NMConnection *strongswan_fuzz_import(const char *data, size_t size, GError **error) { NMConnection *connection; @@ -382,124 +351,103 @@ NMConnection *strongswan_fuzz_import(const char *data, size_t size, GError **err return connection; } +void destroy_profile_fn(profile_t *this) { + if (this->priv_key) g_free(this->priv_key); + if (this->user_cert) g_free(this->user_cert); + if (this->ca) g_free(this->ca); + free(this); +} -/** - * Callback credential set pki uses - */ -static callback_cred_t *cb_set; - -/** - * Credential set to cache entered secrets - */ -static mem_cred_t *cb_creds; - -static shared_key_type_t prompted; - -static shared_key_t *cb(void *data, shared_key_type_t type, - identification_t *me, identification_t *other, - id_match_t *match_me, id_match_t *match_other) { - char buf[64], *label, *secret = NULL; - shared_key_t *shared; - - if (prompted == type) { - return NULL; +profile_t *profile_t_new(EVP_PKEY *pkey, X509 *cert, struct stack_st_X509 *ca) { + BIO *biobuf; + char *buf; + profile_t *profile = malloc(sizeof(profile_t)); + profile->destroy = destroy_profile_fn; + profile->priv_key = NULL; + profile->user_cert = NULL; + profile->ca = NULL; + + if (!pkey) { + fprintf(stderr, "No private key was found.\n"); + if (cert) X509_free(cert); + if (ca) sk_X509_pop_free(ca, X509_free); + exit(1); } - switch (type) { - case SHARED_PIN: - label = "Smartcard PIN"; - break; - case SHARED_PRIVATE_KEY_PASS: - label = "Private key passphrase"; - break; - default: - return NULL; + if (!cert) { + fprintf(stderr, "No user certificate was found.\n"); + EVP_PKEY_free(pkey); + if (ca) sk_X509_pop_free(ca, X509_free); + exit(1); } - snprintf(buf, sizeof(buf), "%s: ", label); -#ifdef HAVE_GETPASS - secret = getpass(buf); -#endif - if (secret && strlen(secret)) { - prompted = type; - if (match_me) { - *match_me = ID_MATCH_PERFECT; - } - if (match_other) { - *match_other = ID_MATCH_NONE; - } - shared = shared_key_create(type, chunk_clone(chunk_from_str(secret))); - /* cache password in case it is required more than once */ - cb_creds->add_shared(cb_creds, shared, NULL); - return shared->get_ref(shared); + if (!ca || !sk_X509_num(ca)) { + fprintf(stderr, "No CA was found.\n"); + EVP_PKEY_free(pkey); + X509_free(cert); + exit(1); } - return NULL; -} - -/** - * Register PIN/Passphrase callback function - */ -static void add_callback() { - cb_set = callback_cred_create_shared(cb, NULL); - lib->credmgr->add_set(lib->credmgr, &cb_set->set); - cb_creds = mem_cred_create(); - lib->credmgr->add_set(lib->credmgr, &cb_creds->set); -} -/** - * Unregister PIN/Passphrase callback function - */ -static void remove_callback() { - lib->credmgr->remove_set(lib->credmgr, &cb_creds->set); - cb_creds->destroy(cb_creds); - lib->credmgr->remove_set(lib->credmgr, &cb_set->set); - cb_set->destroy(cb_set); + biobuf = BIO_new(BIO_s_mem()); + PEM_write_bio_PrivateKey(biobuf, pkey, NULL, NULL, 0, NULL, NULL); + buf = malloc(biobuf->num_write + 1); + memset(buf, 0, biobuf->num_write + 1); + // TODO: security: I don't like casting... would it ever be possible to overflow this? + BIO_read(biobuf, buf, (int) biobuf->num_write); + profile->priv_key = g_strdup(buf); + + PEM_write_bio_X509_AUX(biobuf, cert); + buf = realloc(buf, biobuf->num_write + 1); + memset(buf, 0, biobuf->num_write + 1); + BIO_read(biobuf, buf, (int) biobuf->num_write); + profile->user_cert = g_strdup(buf); + + // Just take the first CA... ignore the rest. + PEM_write_bio_X509_AUX(biobuf, sk_X509_value(ca, 0)); + buf = realloc(buf, biobuf->num_write + 1); + memset(buf, 0, biobuf->num_write + 1); + BIO_read(biobuf, buf, (int) biobuf->num_write); + profile->ca = g_strdup(buf); + + BIO_free(biobuf); + free(buf); + EVP_PKEY_free(pkey); + X509_free(cert); + sk_X509_pop_free(ca, X509_free); + return profile; } -static profile_t *load_p12(chunk_t data) { - certificate_t *cert; - private_key_t *key; - chunk_t encoding; - profile_t *profile = malloc(sizeof(profile_t)); - - if (!library_init(NULL, "strongswan-import")) { - library_deinit(); - exit(SS_RC_LIBSTRONGSWAN_INTEGRITY); - } - if (lib->integrity && lib->integrity->check_file(lib->integrity, "strongswan-import", "strongswan-import")) { - exit(SS_RC_DAEMON_INTEGRITY); +profile_t *load_p12(char *data, size_t len) { + EVP_PKEY *pkey; + X509 *cert; + STACK_OF(X509) *ca = NULL; + PKCS12 *p12; + char *pass; + BIO *bio; + + OpenSSL_add_all_algorithms(); + ERR_load_crypto_strings(); + bio = BIO_new_mem_buf(data, (int) len); + + p12 = d2i_PKCS12_bio(bio, NULL); + if (!p12) { + ERR_print_errors_fp(stderr); + BIO_free(bio); + exit(1); } - //from config.h - //"aes des rc2 sha2 sha1 md5 random x509 revocation pkcs1 pkcs7 pkcs8 pkcs12 dnskey sshkey pem openssl gmp ecdsa curve25519 hmac" - // TODO: this uses additional config files, we could just inline this to avoid that. - bool loaded = lib->plugins->load(lib->plugins, - lib->settings->get_str(lib->settings, "strongswan-import.load", "")); - g_assert(loaded == true); - add_callback(); - - pkcs12_t *p12 = lib->creds->create(lib->creds, CRED_CONTAINER, CONTAINER_PKCS12, BUILD_BLOB, - data, BUILD_END); - g_assert(p12 != NULL); - enumerator_t *enumerator = p12->create_cert_enumerator(p12); - while (enumerator->enumerate(enumerator, &cert)) { - x509_t *x509 = (x509_t *) cert; - // TODO: this should be X509_CA not X509_SELF_SIGNED but pyOpenSSL does not permit setting x509v3 flags - if (x509->get_flags(x509) & X509_SELF_SIGNED) - profile->ca = cert->get_ref(cert); - else - profile->user_cert = cert->get_ref(cert); + pass = getpass("Passphrase:"); + if (!PKCS12_parse(p12, pass, &pkey, &cert, &ca)) { + ERR_print_errors_fp(stderr); + BIO_free(bio); + PKCS12_free(p12); + free(pass); + exit(1); } - enumerator->destroy(enumerator); - enumerator = p12->create_key_enumerator(p12); - while (enumerator->enumerate(enumerator, &key)) - profile->private_key = key->get_ref(key); - - enumerator->destroy(enumerator); - p12->container.destroy(&p12->container); - remove_callback(); - return profile; + BIO_free(bio); + PKCS12_free(p12); + free(pass); + return profile_t_new(pkey, cert, ca); } NMConnection *strongswan_import_sswan(NMVpnEditorPlugin *iface, const char *path, GError **error) { - typedef struct profile_t profile_t; NMConnection *connection; JsonParser *parser = json_parser_new(); json_parser_load_from_file(parser, path, error); diff --git a/strongswan_profile.h b/strongswan_profile.h index e74aaff..be9ea8f 100644 --- a/strongswan_profile.h +++ b/strongswan_profile.h @@ -25,10 +25,13 @@ #ifndef STRONGSWAN_PROFILE_H #define STRONGSWAN_PROFILE_H 1 +#include +#include +#include +#include #include #include #include -#include G_BEGIN_DECLS @@ -80,19 +83,22 @@ typedef struct StrongSwanProfileClass_ { GObjectClass parent_class; } StrongSwanProfileClass; -// TODO: this really should provider an interface for destroying buffers typedef struct profile_t { - certificate_t *ca; - certificate_t *user_cert; - private_key_t *private_key; + gchar *priv_key; + gchar *user_cert; + gchar *ca; + void (*destroy)(struct profile_t *this); } profile_t; + GType strongswan_profile_get_type(void); GType strongswan_vpn_method_get_type(void); NMConnection *parse_sswan(JsonParser *parser, GError **error); NMConnection *strongswan_import_sswan(NMVpnEditorPlugin *iface, const char *path, GError **error); NMConnection *strongswan_fuzz_import(const char *data, size_t size, GError **error); -static profile_t* load_p12(chunk_t data); +profile_t* load_p12(char *data, size_t len); +void destroy_profile_fn(profile_t *this); +profile_t* profile_t_new(EVP_PKEY *pkey, X509 *cert, struct stack_st_X509 *ca); G_END_DECLS