diff options
Diffstat (limited to 'src/charon-nm/nm')
-rw-r--r-- | src/charon-nm/nm/nm_backend.c | 176 | ||||
-rw-r--r-- | src/charon-nm/nm/nm_backend.h | 36 | ||||
-rw-r--r-- | src/charon-nm/nm/nm_creds.c | 490 | ||||
-rw-r--r-- | src/charon-nm/nm/nm_creds.h | 102 | ||||
-rw-r--r-- | src/charon-nm/nm/nm_handler.c | 188 | ||||
-rw-r--r-- | src/charon-nm/nm/nm_handler.h | 62 | ||||
-rw-r--r-- | src/charon-nm/nm/nm_service.c | 720 | ||||
-rw-r--r-- | src/charon-nm/nm/nm_service.h | 55 |
8 files changed, 1829 insertions, 0 deletions
diff --git a/src/charon-nm/nm/nm_backend.c b/src/charon-nm/nm/nm_backend.c new file mode 100644 index 000000000..f36cf1f68 --- /dev/null +++ b/src/charon-nm/nm/nm_backend.c @@ -0,0 +1,176 @@ +/* + * Copyright (C) 2012 Tobias Brunner + * Copyright (C) 2008-2009 Martin Willi + * 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 "nm_service.h" +#include "nm_creds.h" +#include "nm_handler.h" + +#include <hydra.h> +#include <daemon.h> +#include <processing/jobs/callback_job.h> + +#ifndef CAP_DAC_OVERRIDE +#define CAP_DAC_OVERRIDE 1 +#endif + +typedef struct nm_backend_t nm_backend_t; + +/** + * Data for the NetworkManager backend. + */ +struct nm_backend_t { + + /** + * NetworkManager service (VPNPlugin) + */ + NMStrongswanPlugin *plugin; + + /** + * Glib main loop for a thread, handles DBUS calls + */ + GMainLoop *loop; + + /** + * credential set registered at the daemon + */ + nm_creds_t *creds; + + /** + * attribute handler regeisterd at the daemon + */ + nm_handler_t *handler; +}; + +/** + * Global (but private) instance of the NM backend. + */ +static nm_backend_t *nm_backend = NULL; + +/** + * NM plugin processing routine, creates and handles NMVPNPlugin + */ +static job_requeue_t run(nm_backend_t *this) +{ + this->loop = g_main_loop_new(NULL, FALSE); + g_main_loop_run(this->loop); + return JOB_REQUEUE_NONE; +} + +/** + * Cancel the GLib Main Event Loop + */ +static bool cancel(nm_backend_t *this) +{ + if (this->loop) + { + if (g_main_loop_is_running(this->loop)) + { + g_main_loop_quit(this->loop); + } + g_main_loop_unref(this->loop); + } + return TRUE; +} + +/** + * Deinitialize NetworkManager backend + */ +static void nm_backend_deinit() +{ + nm_backend_t *this = nm_backend; + + if (!this) + { + return; + } + if (this->plugin) + { + g_object_unref(this->plugin); + } + lib->credmgr->remove_set(lib->credmgr, &this->creds->set); + hydra->attributes->remove_handler(hydra->attributes, &this->handler->handler); + this->creds->destroy(this->creds); + this->handler->destroy(this->handler); + free(this); + + nm_backend = NULL; +} + +/** + * Initialize NetworkManager backend + */ +static bool nm_backend_init() +{ + nm_backend_t *this; + + g_type_init (); + if (!g_thread_supported()) + { + g_thread_init(NULL); + } + + INIT(this, + .creds = nm_creds_create(), + .handler = nm_handler_create(), + ); + this->plugin = nm_strongswan_plugin_new(this->creds, this->handler); + nm_backend = this; + + hydra->attributes->add_handler(hydra->attributes, &this->handler->handler); + lib->credmgr->add_set(lib->credmgr, &this->creds->set); + if (!this->plugin) + { + DBG1(DBG_CFG, "DBUS binding failed"); + nm_backend_deinit(); + return FALSE; + } + + /* bypass file permissions to read from users ssh-agent */ + charon->caps->keep(charon->caps, CAP_DAC_OVERRIDE); + + lib->processor->queue_job(lib->processor, + (job_t*)callback_job_create_with_prio((callback_job_cb_t)run, this, + NULL, (callback_job_cancel_t)cancel, JOB_PRIO_CRITICAL)); + return TRUE; +} + +/** + * Initialize/deinitialize NetworkManager backend + */ +static bool nm_backend_cb(void *plugin, + plugin_feature_t *feature, bool reg, void *data) +{ + if (reg) + { + return nm_backend_init(); + } + nm_backend_deinit(); + return TRUE; +} + +/* + * see header file + */ +void nm_backend_register() +{ + static plugin_feature_t features[] = { + PLUGIN_CALLBACK((plugin_feature_callback_t)nm_backend_cb, NULL), + PLUGIN_PROVIDE(CUSTOM, "NetworkManager backend"), + PLUGIN_DEPENDS(CUSTOM, "libcharon"), + }; + lib->plugins->add_static_features(lib->plugins, "nm-backend", features, + countof(features), TRUE); +} diff --git a/src/charon-nm/nm/nm_backend.h b/src/charon-nm/nm/nm_backend.h new file mode 100644 index 000000000..89dc536f6 --- /dev/null +++ b/src/charon-nm/nm/nm_backend.h @@ -0,0 +1,36 @@ +/* + * 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 charon-nm charon-nm + * + * @defgroup nm nm + * @ingroup charon-nm + * + * @defgroup nm_backend nm_backend + * @{ @ingroup nm + */ + +#ifndef NM_BACKEND_H_ +#define NM_BACKEND_H_ + +/** + * Initialize the NetworkManager backend. + * + * @return TRUE, if initialization was successful + */ +void nm_backend_register(); + +#endif /** NM_BACKEND_H_ @}*/ diff --git a/src/charon-nm/nm/nm_creds.c b/src/charon-nm/nm/nm_creds.c new file mode 100644 index 000000000..f8fae9504 --- /dev/null +++ b/src/charon-nm/nm/nm_creds.c @@ -0,0 +1,490 @@ +/* + * Copyright (C) 2008 Martin Willi + * 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 "nm_creds.h" + +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> + +#include <daemon.h> +#include <threading/rwlock.h> +#include <credentials/certificates/x509.h> + +typedef struct private_nm_creds_t private_nm_creds_t; + +/** + * private data of nm_creds + */ +struct private_nm_creds_t { + + /** + * public functions + */ + nm_creds_t public; + + /** + * List of trusted certificates, certificate_t* + */ + linked_list_t *certs; + + /** + * User name + */ + identification_t *user; + + /** + * User password + */ + char *pass; + + /** + * Private key decryption password / smartcard pin + */ + char *keypass; + + /** + * private key ID of smartcard key + */ + chunk_t keyid; + + /** + * users certificate + */ + certificate_t *usercert; + + /** + * users private key + */ + private_key_t *key; + + /** + * read/write lock + */ + rwlock_t *lock; +}; + +/** + * Enumerator for user certificate + */ +static enumerator_t *create_usercert_enumerator(private_nm_creds_t *this, + certificate_type_t cert, key_type_t key) +{ + public_key_t *public; + + if (cert != CERT_ANY && cert != this->usercert->get_type(this->usercert)) + { + return NULL; + } + if (key != KEY_ANY) + { + public = this->usercert->get_public_key(this->usercert); + if (!public) + { + return NULL; + } + if (public->get_type(public) != key) + { + public->destroy(public); + return NULL; + } + public->destroy(public); + } + this->lock->read_lock(this->lock); + return enumerator_create_cleaner( + enumerator_create_single(this->usercert, NULL), + (void*)this->lock->unlock, this->lock); +} + +/** + * CA certificate enumerator data + */ +typedef struct { + /** ref to credential credential store */ + private_nm_creds_t *this; + /** type of key we are looking for */ + key_type_t key; + /** CA certificate ID */ + identification_t *id; +} cert_data_t; + +/** + * Destroy CA certificate enumerator data + */ +static void cert_data_destroy(cert_data_t *data) +{ + data->this->lock->unlock(data->this->lock); + free(data); +} + +/** + * Filter function for certificates enumerator + */ +static bool cert_filter(cert_data_t *data, certificate_t **in, + certificate_t **out) +{ + certificate_t *cert = *in; + public_key_t *public; + + public = cert->get_public_key(cert); + if (!public) + { + return FALSE; + } + if (data->key != KEY_ANY && public->get_type(public) != data->key) + { + public->destroy(public); + return FALSE; + } + if (data->id && data->id->get_type(data->id) == ID_KEY_ID && + public->has_fingerprint(public, data->id->get_encoding(data->id))) + { + public->destroy(public); + *out = cert; + return TRUE; + } + public->destroy(public); + if (data->id && !cert->has_subject(cert, data->id)) + { + return FALSE; + } + *out = cert; + return TRUE; +} + +/** + * Create enumerator for trusted certificates + */ +static enumerator_t *create_trusted_cert_enumerator(private_nm_creds_t *this, + key_type_t key, identification_t *id) +{ + cert_data_t *data; + + INIT(data, + .this = this, + .id = id, + .key = key, + ); + + this->lock->read_lock(this->lock); + return enumerator_create_filter( + this->certs->create_enumerator(this->certs), + (void*)cert_filter, data, (void*)cert_data_destroy); +} + +METHOD(credential_set_t, create_cert_enumerator, enumerator_t*, + private_nm_creds_t *this, certificate_type_t cert, key_type_t key, + identification_t *id, bool trusted) +{ + if (id && this->usercert && + id->equals(id, this->usercert->get_subject(this->usercert))) + { + return create_usercert_enumerator(this, cert, key); + } + if (cert == CERT_X509 || cert == CERT_ANY) + { + return create_trusted_cert_enumerator(this, key, id); + } + return NULL; +} + +METHOD(credential_set_t, create_private_enumerator, enumerator_t*, + private_nm_creds_t *this, key_type_t type, identification_t *id) +{ + if (this->key == NULL) + { + return NULL; + } + if (type != KEY_ANY && type != this->key->get_type(this->key)) + { + return NULL; + } + if (id && id->get_type(id) != ID_ANY) + { + if (id->get_type(id) != ID_KEY_ID || + !this->key->has_fingerprint(this->key, id->get_encoding(id))) + { + return NULL; + } + } + this->lock->read_lock(this->lock); + return enumerator_create_cleaner(enumerator_create_single(this->key, NULL), + (void*)this->lock->unlock, this->lock); +} + +/** + * shared key enumerator implementation + */ +typedef struct { + enumerator_t public; + private_nm_creds_t *this; + shared_key_t *key; + bool done; +} shared_enumerator_t; + +METHOD(enumerator_t, shared_enumerate, bool, + shared_enumerator_t *this, shared_key_t **key, id_match_t *me, + id_match_t *other) +{ + if (this->done) + { + return FALSE; + } + *key = this->key; + if (me) + { + *me = ID_MATCH_PERFECT; + } + if (other) + { + *other = ID_MATCH_ANY; + } + this->done = TRUE; + return TRUE; +} + +METHOD(enumerator_t, shared_destroy, void, + shared_enumerator_t *this) +{ + this->key->destroy(this->key); + this->this->lock->unlock(this->this->lock); + free(this); +} + +METHOD(credential_set_t, create_shared_enumerator, enumerator_t*, + private_nm_creds_t *this, shared_key_type_t type, identification_t *me, + identification_t *other) +{ + shared_enumerator_t *enumerator; + chunk_t key; + + this->lock->read_lock(this->lock); + + switch (type) + { + case SHARED_EAP: + case SHARED_IKE: + if (!this->pass || !this->user) + { + goto no_secret; + } + if (me && !me->equals(me, this->user)) + { + goto no_secret; + } + key = chunk_create(this->pass, strlen(this->pass)); + break; + case SHARED_PRIVATE_KEY_PASS: + if (!this->keypass) + { + goto no_secret; + } + key = chunk_create(this->keypass, strlen(this->keypass)); + break; + case SHARED_PIN: + if (!this->keypass || !me || + !chunk_equals(me->get_encoding(me), this->keyid)) + { + goto no_secret; + } + key = chunk_create(this->keypass, strlen(this->keypass)); + break; + default: + goto no_secret; + } + + INIT(enumerator, + .public = { + .enumerate = (void*)_shared_enumerate, + .destroy = _shared_destroy, + }, + .this = this, + ); + enumerator->key = shared_key_create(type, chunk_clone(key)); + return &enumerator->public; + +no_secret: + this->lock->unlock(this->lock); + return NULL; +} + +METHOD(nm_creds_t, add_certificate, void, + private_nm_creds_t *this, certificate_t *cert) +{ + this->lock->write_lock(this->lock); + this->certs->insert_last(this->certs, cert); + this->lock->unlock(this->lock); +} + +/** + * Load a certificate file + */ +static void load_ca_file(private_nm_creds_t *this, char *file) +{ + certificate_t *cert; + + /* We add the CA constraint, as many CAs miss it */ + cert = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509, + BUILD_FROM_FILE, file, BUILD_END); + if (!cert) + { + DBG1(DBG_CFG, "loading CA certificate '%s' failed", file); + } + else + { + DBG2(DBG_CFG, "loaded CA certificate '%Y'", cert->get_subject(cert)); + x509_t *x509 = (x509_t*)cert; + if (!(x509->get_flags(x509) & X509_SELF_SIGNED)) + { + DBG1(DBG_CFG, "%Y is not self signed", cert->get_subject(cert)); + } + this->certs->insert_last(this->certs, cert); + } +} + +METHOD(nm_creds_t, load_ca_dir, void, + private_nm_creds_t *this, char *dir) +{ + enumerator_t *enumerator; + char *rel, *abs; + struct stat st; + + enumerator = enumerator_create_directory(dir); + if (enumerator) + { + while (enumerator->enumerate(enumerator, &rel, &abs, &st)) + { + /* skip '.', '..' and hidden files */ + if (rel[0] != '.') + { + if (S_ISDIR(st.st_mode)) + { + load_ca_dir(this, abs); + } + else if (S_ISREG(st.st_mode)) + { + load_ca_file(this, abs); + } + } + } + enumerator->destroy(enumerator); + } +} + +METHOD(nm_creds_t, set_username_password, void, + private_nm_creds_t *this, identification_t *id, char *password) +{ + this->lock->write_lock(this->lock); + DESTROY_IF(this->user); + this->user = id->clone(id); + free(this->pass); + this->pass = strdupnull(password); + this->lock->unlock(this->lock); +} + +METHOD(nm_creds_t, set_key_password, void, + private_nm_creds_t *this, char *password) +{ + this->lock->write_lock(this->lock); + free(this->keypass); + this->keypass = strdupnull(password); + this->lock->unlock(this->lock); +} + +METHOD(nm_creds_t, set_pin, void, + private_nm_creds_t *this, chunk_t keyid, char *pin) +{ + this->lock->write_lock(this->lock); + free(this->keypass); + free(this->keyid.ptr); + this->keypass = strdupnull(pin); + this->keyid = chunk_clone(keyid); + this->lock->unlock(this->lock); +} + +METHOD(nm_creds_t, set_cert_and_key, void, + private_nm_creds_t *this, certificate_t *cert, private_key_t *key) +{ + this->lock->write_lock(this->lock); + DESTROY_IF(this->key); + DESTROY_IF(this->usercert); + this->key = key; + this->usercert = cert; + this->lock->unlock(this->lock); +} + +METHOD(nm_creds_t, clear, void, + private_nm_creds_t *this) +{ + certificate_t *cert; + + while (this->certs->remove_last(this->certs, (void**)&cert) == SUCCESS) + { + cert->destroy(cert); + } + DESTROY_IF(this->user); + free(this->pass); + free(this->keypass); + free(this->keyid.ptr); + DESTROY_IF(this->usercert); + DESTROY_IF(this->key); + this->key = NULL; + this->usercert = NULL; + this->pass = NULL; + this->user = NULL; + this->keypass = NULL; + this->keyid = chunk_empty; +} + +METHOD(nm_creds_t, destroy, void, + private_nm_creds_t *this) +{ + clear(this); + this->certs->destroy(this->certs); + this->lock->destroy(this->lock); + free(this); +} + +/* + * see header file + */ +nm_creds_t *nm_creds_create() +{ + private_nm_creds_t *this; + + INIT(this, + .public = { + .set = { + .create_private_enumerator = _create_private_enumerator, + .create_cert_enumerator = _create_cert_enumerator, + .create_shared_enumerator = _create_shared_enumerator, + .create_cdp_enumerator = (void*)return_null, + .cache_cert = (void*)nop, + }, + .add_certificate = _add_certificate, + .load_ca_dir = _load_ca_dir, + .set_username_password = _set_username_password, + .set_key_password = _set_key_password, + .set_pin = _set_pin, + .set_cert_and_key = _set_cert_and_key, + .clear = _clear, + .destroy = _destroy, + }, + .lock = rwlock_create(RWLOCK_TYPE_DEFAULT), + .certs = linked_list_create(), + ); + return &this->public; +} + diff --git a/src/charon-nm/nm/nm_creds.h b/src/charon-nm/nm/nm_creds.h new file mode 100644 index 000000000..91f645c7e --- /dev/null +++ b/src/charon-nm/nm/nm_creds.h @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2008 Martin Willi + * 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 nm_creds nm_creds + * @{ @ingroup nm + */ + +#ifndef NM_CREDS_H_ +#define NM_CREDS_H_ + +#include <credentials/keys/private_key.h> +#include <credentials/credential_set.h> + +typedef struct nm_creds_t nm_creds_t; + +/** + * NetworkManager credentials helper. + */ +struct nm_creds_t { + + /** + * Implements credential_set_t + */ + credential_set_t set; + + /** + * Add a trusted gateway certificate to serve by this set. + * + * @param cert certificate to serve + */ + void (*add_certificate)(nm_creds_t *this, certificate_t *cert); + + /** + * Load CA certificates recursively from a directory. + * + * @param dir directory to PEM encoded CA certificates + */ + void (*load_ca_dir)(nm_creds_t *this, char *dir); + + /** + * Set the username/password for authentication. + * + * @param id ID of the user + * @param password password to use for authentication + */ + void (*set_username_password)(nm_creds_t *this, identification_t *id, + char *password); + + /** + * Set the passphrase to use for private key decryption. + * + * @param password password to use + */ + void (*set_key_password)(nm_creds_t *this, char *password); + + /** + * Set the PIN to unlock a smartcard. + * + * @param keyid keyid of the smartcard key + * @param pin PIN + */ + void (*set_pin)(nm_creds_t *this, chunk_t keyid, char *pin); + + /** + * Set the certificate and private key to use for client authentication. + * + * @param cert client certificate + * @param key associated private key + */ + void (*set_cert_and_key)(nm_creds_t *this, certificate_t *cert, + private_key_t *key); + + /** + * Clear the stored credentials. + */ + void (*clear)(nm_creds_t *this); + + /** + * Destroy a nm_creds instance. + */ + void (*destroy)(nm_creds_t *this); +}; + +/** + * Create a nm_creds instance. + */ +nm_creds_t *nm_creds_create(); + +#endif /** NM_CREDS_H_ @}*/ diff --git a/src/charon-nm/nm/nm_handler.c b/src/charon-nm/nm/nm_handler.c new file mode 100644 index 000000000..28aa04b31 --- /dev/null +++ b/src/charon-nm/nm/nm_handler.c @@ -0,0 +1,188 @@ +/* + * Copyright (C) 2009 Martin Willi + * 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 "nm_handler.h" + +#include <daemon.h> + +typedef struct private_nm_handler_t private_nm_handler_t; + +/** + * Private data of an nm_handler_t object. + */ +struct private_nm_handler_t { + + /** + * Public nm_handler_t interface. + */ + nm_handler_t public; + + /** + * list of received DNS server attributes, pointer to 4 byte data + */ + linked_list_t *dns; + + /** + * list of received NBNS server attributes, pointer to 4 byte data + */ + linked_list_t *nbns; +}; + +METHOD(attribute_handler_t, handle, bool, + private_nm_handler_t *this, identification_t *server, + configuration_attribute_type_t type, chunk_t data) +{ + linked_list_t *list; + + switch (type) + { + case INTERNAL_IP4_DNS: + list = this->dns; + break; + case INTERNAL_IP4_NBNS: + list = this->nbns; + break; + default: + return FALSE; + } + if (data.len != 4) + { + return FALSE; + } + list->insert_last(list, chunk_clone(data).ptr); + return TRUE; +} + +/** + * Implementation of create_attribute_enumerator().enumerate() for WINS + */ +static bool enumerate_nbns(enumerator_t *this, + configuration_attribute_type_t *type, chunk_t *data) +{ + *type = INTERNAL_IP4_NBNS; + *data = chunk_empty; + /* done */ + this->enumerate = (void*)return_false; + return TRUE; +} + +/** + * Implementation of create_attribute_enumerator().enumerate() for DNS + */ +static bool enumerate_dns(enumerator_t *this, + configuration_attribute_type_t *type, chunk_t *data) +{ + *type = INTERNAL_IP4_DNS; + *data = chunk_empty; + /* enumerate WINS server as next attribute ... */ + this->enumerate = (void*)enumerate_nbns; + return TRUE; +} + +METHOD(attribute_handler_t, create_attribute_enumerator, enumerator_t*, + private_nm_handler_t *this, identification_t *server, linked_list_t *vips) +{ + if (vips->get_count(vips)) + { + enumerator_t *enumerator; + + INIT(enumerator, + /* enumerate DNS attribute first ... */ + .enumerate = (void*)enumerate_dns, + .destroy = (void*)free, + ); + return enumerator; + } + return enumerator_create_empty(); +} + +/** + * convert plain byte ptrs to handy chunk during enumeration + */ +static bool filter_chunks(void* null, char **in, chunk_t *out) +{ + *out = chunk_create(*in, 4); + return TRUE; +} + +METHOD(nm_handler_t, create_enumerator, enumerator_t*, + private_nm_handler_t *this, configuration_attribute_type_t type) +{ + linked_list_t *list; + + switch (type) + { + case INTERNAL_IP4_DNS: + list = this->dns; + break; + case INTERNAL_IP4_NBNS: + list = this->nbns; + break; + default: + return enumerator_create_empty(); + } + return enumerator_create_filter(list->create_enumerator(list), + (void*)filter_chunks, NULL, NULL); +} + +METHOD(nm_handler_t, reset, void, + private_nm_handler_t *this) +{ + void *data; + + while (this->dns->remove_last(this->dns, (void**)&data) == SUCCESS) + { + free(data); + } + while (this->nbns->remove_last(this->nbns, (void**)&data) == SUCCESS) + { + free(data); + } +} + +METHOD(nm_handler_t, destroy, void, + private_nm_handler_t *this) +{ + reset(this); + this->dns->destroy(this->dns); + this->nbns->destroy(this->nbns); + free(this); +} + +/** + * See header + */ +nm_handler_t *nm_handler_create() +{ + private_nm_handler_t *this; + + INIT(this, + .public = { + .handler = { + .handle = _handle, + .release = nop, + .create_attribute_enumerator = _create_attribute_enumerator, + }, + .create_enumerator = _create_enumerator, + .reset = _reset, + .destroy = _destroy, + }, + .dns = linked_list_create(), + .nbns = linked_list_create(), + ); + + return &this->public; +} + diff --git a/src/charon-nm/nm/nm_handler.h b/src/charon-nm/nm/nm_handler.h new file mode 100644 index 000000000..bb35ce767 --- /dev/null +++ b/src/charon-nm/nm/nm_handler.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2009 Martin Willi + * 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 nm_handler nm_handler + * @{ @ingroup nm + */ + +#ifndef NM_HANDLER_H_ +#define NM_HANDLER_H_ + +#include <attributes/attribute_handler.h> + +typedef struct nm_handler_t nm_handler_t; + +/** + * Handles DNS/NBNS attributes to pass to NM. + */ +struct nm_handler_t { + + /** + * Implements attribute handler interface + */ + attribute_handler_t handler; + + /** + * Create an enumerator over received attributes of a given kind. + * + * @param type type of attributes to enumerate + * @return enumerator over attribute data (chunk_t) + */ + enumerator_t* (*create_enumerator)(nm_handler_t *this, + configuration_attribute_type_t type); + /** + * Reset state, flush all received attributes. + */ + void (*reset)(nm_handler_t *this); + + /** + * Destroy a nm_handler_t. + */ + void (*destroy)(nm_handler_t *this); +}; + +/** + * Create a nm_handler instance. + */ +nm_handler_t *nm_handler_create(); + +#endif /** NM_HANDLER_H_ @}*/ diff --git a/src/charon-nm/nm/nm_service.c b/src/charon-nm/nm/nm_service.c new file mode 100644 index 000000000..b7155b44b --- /dev/null +++ b/src/charon-nm/nm/nm_service.c @@ -0,0 +1,720 @@ +/* + * Copyright (C) 2008-2009 Martin Willi + * 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 <nm-setting-vpn.h> +#include <nm-setting-connection.h> +#include "nm_service.h" + +#include <daemon.h> +#include <utils/host.h> +#include <utils/identification.h> +#include <config/peer_cfg.h> +#include <credentials/certificates/x509.h> + +#include <stdio.h> + +G_DEFINE_TYPE(NMStrongswanPlugin, nm_strongswan_plugin, NM_TYPE_VPN_PLUGIN) + +/** + * Private data of NMStrongswanPlugin + */ +typedef struct { + /* implements bus listener interface */ + listener_t listener; + /* IKE_SA we are listening on */ + ike_sa_t *ike_sa; + /* backref to public plugin */ + NMVPNPlugin *plugin; + /* credentials to use for authentication */ + nm_creds_t *creds; + /* attribute handler for DNS/NBNS server information */ + nm_handler_t *handler; + /* name of the connection */ + char *name; +} NMStrongswanPluginPrivate; + +#define NM_STRONGSWAN_PLUGIN_GET_PRIVATE(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((o), \ + NM_TYPE_STRONGSWAN_PLUGIN, NMStrongswanPluginPrivate)) + +/** + * convert enumerated handler chunks to a UINT_ARRAY GValue + */ +static GValue* handler_to_val(nm_handler_t *handler, + configuration_attribute_type_t type) +{ + GValue *val; + GArray *array; + enumerator_t *enumerator; + chunk_t chunk; + + enumerator = handler->create_enumerator(handler, type); + array = g_array_new (FALSE, TRUE, sizeof (guint32)); + while (enumerator->enumerate(enumerator, &chunk)) + { + g_array_append_val (array, *(u_int32_t*)chunk.ptr); + } + enumerator->destroy(enumerator); + val = g_slice_new0 (GValue); + g_value_init (val, DBUS_TYPE_G_UINT_ARRAY); + g_value_set_boxed (val, array); + + return val; +} + +/** + * signal IPv4 config to NM, set connection as established + */ +static void signal_ipv4_config(NMVPNPlugin *plugin, + ike_sa_t *ike_sa, child_sa_t *child_sa) +{ + GValue *val; + GHashTable *config; + host_t *me; + nm_handler_t *handler; + + config = g_hash_table_new(g_str_hash, g_str_equal); + me = ike_sa->get_my_host(ike_sa); + handler = NM_STRONGSWAN_PLUGIN_GET_PRIVATE(plugin)->handler; + + /* NM requires a tundev, but netkey does not use one. Passing the physical + * interface does not work, as NM fiddles around with it. Passing the + * loopback seems to work, though... */ + val = g_slice_new0 (GValue); + g_value_init (val, G_TYPE_STRING); + g_value_set_string (val, "lo"); + g_hash_table_insert (config, NM_VPN_PLUGIN_IP4_CONFIG_TUNDEV, val); + + val = g_slice_new0(GValue); + g_value_init(val, G_TYPE_UINT); + g_value_set_uint(val, *(u_int32_t*)me->get_address(me).ptr); + g_hash_table_insert(config, NM_VPN_PLUGIN_IP4_CONFIG_ADDRESS, val); + + val = g_slice_new0(GValue); + g_value_init(val, G_TYPE_UINT); + g_value_set_uint(val, me->get_address(me).len * 8); + g_hash_table_insert(config, NM_VPN_PLUGIN_IP4_CONFIG_PREFIX, val); + + val = handler_to_val(handler, INTERNAL_IP4_DNS); + g_hash_table_insert(config, NM_VPN_PLUGIN_IP4_CONFIG_DNS, val); + + val = handler_to_val(handler, INTERNAL_IP4_NBNS); + g_hash_table_insert(config, NM_VPN_PLUGIN_IP4_CONFIG_NBNS, val); + + handler->reset(handler); + + nm_vpn_plugin_set_ip4_config(plugin, config); +} + +/** + * signal failure to NM, connecting failed + */ +static void signal_failure(NMVPNPlugin *plugin, NMVPNPluginFailure failure) +{ + nm_handler_t *handler = NM_STRONGSWAN_PLUGIN_GET_PRIVATE(plugin)->handler; + + handler->reset(handler); + + /* TODO: NM does not handle this failure!? */ + nm_vpn_plugin_failure(plugin, failure); + nm_vpn_plugin_set_state(plugin, NM_VPN_SERVICE_STATE_STOPPED); +} + +/** + * Implementation of listener_t.ike_state_change + */ +static bool ike_state_change(listener_t *listener, ike_sa_t *ike_sa, + ike_sa_state_t state) +{ + NMStrongswanPluginPrivate *private = (NMStrongswanPluginPrivate*)listener; + + if (private->ike_sa == ike_sa && state == IKE_DESTROYING) + { + signal_failure(private->plugin, NM_VPN_PLUGIN_FAILURE_LOGIN_FAILED); + return FALSE; + } + return TRUE; +} + +/** + * Implementation of listener_t.child_state_change + */ +static bool child_state_change(listener_t *listener, ike_sa_t *ike_sa, + child_sa_t *child_sa, child_sa_state_t state) +{ + NMStrongswanPluginPrivate *private = (NMStrongswanPluginPrivate*)listener; + + if (private->ike_sa == ike_sa && state == CHILD_DESTROYING) + { + signal_failure(private->plugin, NM_VPN_PLUGIN_FAILURE_CONNECT_FAILED); + return FALSE; + } + return TRUE; +} + +/** + * Implementation of listener_t.child_updown + */ +static bool child_updown(listener_t *listener, ike_sa_t *ike_sa, + child_sa_t *child_sa, bool up) +{ + NMStrongswanPluginPrivate *private = (NMStrongswanPluginPrivate*)listener; + + if (private->ike_sa == ike_sa) + { + if (up) + { /* disable initiate-failure-detection hooks */ + private->listener.ike_state_change = NULL; + private->listener.child_state_change = NULL; + signal_ipv4_config(private->plugin, ike_sa, child_sa); + } + else + { + signal_failure(private->plugin, NM_VPN_PLUGIN_FAILURE_CONNECT_FAILED); + return FALSE; + } + } + return TRUE; +} + +/** + * Implementation of listener_t.ike_rekey + */ +static bool ike_rekey(listener_t *listener, ike_sa_t *old, ike_sa_t *new) +{ + NMStrongswanPluginPrivate *private = (NMStrongswanPluginPrivate*)listener; + + if (private->ike_sa == old) + { /* follow a rekeyed IKE_SA */ + private->ike_sa = new; + } + return TRUE; +} + +/** + * Find a certificate for which we have a private key on a smartcard + */ +static identification_t *find_smartcard_key(NMStrongswanPluginPrivate *priv, + char *pin) +{ + enumerator_t *enumerator, *sans; + identification_t *id = NULL; + certificate_t *cert; + x509_t *x509; + private_key_t *key; + chunk_t keyid; + + enumerator = lib->credmgr->create_cert_enumerator(lib->credmgr, + CERT_X509, KEY_ANY, NULL, FALSE); + while (enumerator->enumerate(enumerator, &cert)) + { + x509 = (x509_t*)cert; + + /* there might be a lot of certificates, filter them by usage */ + if ((x509->get_flags(x509) & X509_CLIENT_AUTH) && + !(x509->get_flags(x509) & X509_CA)) + { + keyid = x509->get_subjectKeyIdentifier(x509); + if (keyid.ptr) + { + /* try to find a private key by the certificate keyid */ + priv->creds->set_pin(priv->creds, keyid, pin); + key = lib->creds->create(lib->creds, CRED_PRIVATE_KEY, + KEY_ANY, BUILD_PKCS11_KEYID, keyid, BUILD_END); + if (key) + { + /* prefer a more convenient subjectAltName */ + sans = x509->create_subjectAltName_enumerator(x509); + if (!sans->enumerate(sans, &id)) + { + id = cert->get_subject(cert); + } + id = id->clone(id); + sans->destroy(sans); + + DBG1(DBG_CFG, "using smartcard certificate '%Y'", id); + priv->creds->set_cert_and_key(priv->creds, + cert->get_ref(cert), key); + break; + } + } + } + } + enumerator->destroy(enumerator); + return id; +} + +/** + * Connect function called from NM via DBUS + */ +static gboolean connect_(NMVPNPlugin *plugin, NMConnection *connection, + GError **err) +{ + NMStrongswanPluginPrivate *priv; + NMSettingConnection *conn; + NMSettingVPN *vpn; + identification_t *user = NULL, *gateway = NULL; + const char *address, *str; + bool virtual, encap, ipcomp; + ike_cfg_t *ike_cfg; + peer_cfg_t *peer_cfg; + child_cfg_t *child_cfg; + traffic_selector_t *ts; + ike_sa_t *ike_sa; + auth_cfg_t *auth; + auth_class_t auth_class = AUTH_CLASS_EAP; + certificate_t *cert = NULL; + x509_t *x509; + bool agent = FALSE, smartcard = FALSE, loose_gateway_id = FALSE; + lifetime_cfg_t lifetime = { + .time = { + .life = 10800 /* 3h */, + .rekey = 10200 /* 2h50min */, + .jitter = 300 /* 5min */ + } + }; + + /** + * Read parameters + */ + priv = NM_STRONGSWAN_PLUGIN_GET_PRIVATE(plugin); + conn = NM_SETTING_CONNECTION(nm_connection_get_setting(connection, + NM_TYPE_SETTING_CONNECTION)); + vpn = NM_SETTING_VPN(nm_connection_get_setting(connection, + NM_TYPE_SETTING_VPN)); + if (priv->name) + { + free(priv->name); + } + priv->name = strdup(nm_setting_connection_get_id(conn)); + DBG1(DBG_CFG, "received initiate for NetworkManager connection %s", + priv->name); + DBG4(DBG_CFG, "%s", + nm_setting_to_string(NM_SETTING(vpn))); + address = nm_setting_vpn_get_data_item(vpn, "address"); + if (!address || !*address) + { + g_set_error(err, NM_VPN_PLUGIN_ERROR, NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS, + "Gateway address missing."); + return FALSE; + } + str = nm_setting_vpn_get_data_item(vpn, "virtual"); + virtual = str && streq(str, "yes"); + str = nm_setting_vpn_get_data_item(vpn, "encap"); + encap = str && streq(str, "yes"); + str = nm_setting_vpn_get_data_item(vpn, "ipcomp"); + ipcomp = str && streq(str, "yes"); + str = nm_setting_vpn_get_data_item(vpn, "method"); + if (str) + { + if (streq(str, "psk")) + { + auth_class = AUTH_CLASS_PSK; + } + else if (streq(str, "agent")) + { + auth_class = AUTH_CLASS_PUBKEY; + agent = TRUE; + } + else if (streq(str, "key")) + { + auth_class = AUTH_CLASS_PUBKEY; + } + else if (streq(str, "smartcard")) + { + auth_class = AUTH_CLASS_PUBKEY; + smartcard = TRUE; + } + } + + /** + * Register credentials + */ + priv->creds->clear(priv->creds); + + /* gateway/CA cert */ + str = nm_setting_vpn_get_data_item(vpn, "certificate"); + if (str) + { + cert = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509, + BUILD_FROM_FILE, str, BUILD_END); + if (!cert) + { + g_set_error(err, NM_VPN_PLUGIN_ERROR, + NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS, + "Loading gateway certificate failed."); + return FALSE; + } + priv->creds->add_certificate(priv->creds, cert); + + x509 = (x509_t*)cert; + if (!(x509->get_flags(x509) & X509_CA)) + { /* For a gateway certificate, we use the cert subject as identity. */ + gateway = cert->get_subject(cert); + gateway = gateway->clone(gateway); + DBG1(DBG_CFG, "using gateway certificate, identity '%Y'", gateway); + } + } + else + { + /* no certificate defined, fall back to system-wide CA certificates */ + priv->creds->load_ca_dir(priv->creds, NM_CA_DIR); + } + if (!gateway) + { + /* If the user configured a CA certificate, we use the IP/DNS + * of the gateway as its identity. This identity will be used for + * certificate lookup and requires the configured IP/DNS to be + * included in the gateway certificate. */ + gateway = identification_create_from_string((char*)address); + DBG1(DBG_CFG, "using CA certificate, gateway identity '%Y'", gateway); + loose_gateway_id = TRUE; + } + + if (auth_class == AUTH_CLASS_EAP) + { + /* username/password authentication ... */ + str = nm_setting_vpn_get_data_item(vpn, "user"); + if (str) + { + user = identification_create_from_string((char*)str); + str = nm_setting_vpn_get_secret(vpn, "password"); + priv->creds->set_username_password(priv->creds, user, (char*)str); + } + } + + if (auth_class == AUTH_CLASS_PUBKEY) + { + if (smartcard) + { + char *pin; + + pin = (char*)nm_setting_vpn_get_secret(vpn, "password"); + if (pin) + { + user = find_smartcard_key(priv, pin); + } + if (!user) + { + g_set_error(err, NM_VPN_PLUGIN_ERROR, + NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS, + "no usable smartcard certificate found."); + gateway->destroy(gateway); + return FALSE; + } + } + /* ... or certificate/private key authenitcation */ + else if ((str = nm_setting_vpn_get_data_item(vpn, "usercert"))) + { + public_key_t *public; + private_key_t *private = NULL; + + cert = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509, + BUILD_FROM_FILE, str, BUILD_END); + if (!cert) + { + g_set_error(err, NM_VPN_PLUGIN_ERROR, + NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS, + "Loading peer certificate failed."); + gateway->destroy(gateway); + return FALSE; + } + /* try agent */ + str = nm_setting_vpn_get_secret(vpn, "agent"); + if (agent && str) + { + public = cert->get_public_key(cert); + if (public) + { + private = lib->creds->create(lib->creds, CRED_PRIVATE_KEY, + public->get_type(public), + BUILD_AGENT_SOCKET, str, + BUILD_PUBLIC_KEY, public, + BUILD_END); + public->destroy(public); + } + if (!private) + { + g_set_error(err, NM_VPN_PLUGIN_ERROR, + NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS, + "Connecting to SSH agent failed."); + } + } + /* ... or key file */ + str = nm_setting_vpn_get_data_item(vpn, "userkey"); + if (!agent && str) + { + char *secret; + + secret = (char*)nm_setting_vpn_get_secret(vpn, "password"); + if (secret) + { + priv->creds->set_key_password(priv->creds, secret); + } + private = lib->creds->create(lib->creds, CRED_PRIVATE_KEY, + KEY_RSA, BUILD_FROM_FILE, str, BUILD_END); + if (!private) + { + g_set_error(err, NM_VPN_PLUGIN_ERROR, + NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS, + "Loading private key failed."); + } + } + if (private) + { + user = cert->get_subject(cert); + user = user->clone(user); + priv->creds->set_cert_and_key(priv->creds, cert, private); + } + else + { + DESTROY_IF(cert); + gateway->destroy(gateway); + return FALSE; + } + } + } + + if (!user) + { + g_set_error(err, NM_VPN_PLUGIN_ERROR, NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS, + "Configuration parameters missing."); + gateway->destroy(gateway); + return FALSE; + } + + /** + * Set up configurations + */ + ike_cfg = ike_cfg_create(TRUE, encap, "0.0.0.0", FALSE, + charon->socket->get_port(charon->socket, FALSE), + (char*)address, FALSE, IKEV2_UDP_PORT); + ike_cfg->add_proposal(ike_cfg, proposal_create_default(PROTO_IKE)); + peer_cfg = peer_cfg_create(priv->name, IKEV2, ike_cfg, + CERT_SEND_IF_ASKED, UNIQUE_REPLACE, 1, /* keyingtries */ + 36000, 0, /* rekey 10h, reauth none */ + 600, 600, /* jitter, over 10min */ + TRUE, FALSE, /* mobike, aggressive */ + 0, 0, /* DPD delay, timeout */ + FALSE, NULL, NULL); /* mediation */ + if (virtual) + { + peer_cfg->add_virtual_ip(peer_cfg, host_create_from_string("0.0.0.0", 0)); + } + auth = auth_cfg_create(); + auth->add(auth, AUTH_RULE_AUTH_CLASS, auth_class); + auth->add(auth, AUTH_RULE_IDENTITY, user); + peer_cfg->add_auth_cfg(peer_cfg, auth, TRUE); + auth = auth_cfg_create(); + auth->add(auth, AUTH_RULE_AUTH_CLASS, AUTH_CLASS_PUBKEY); + auth->add(auth, AUTH_RULE_IDENTITY, gateway); + auth->add(auth, AUTH_RULE_IDENTITY_LOOSE, loose_gateway_id); + peer_cfg->add_auth_cfg(peer_cfg, auth, FALSE); + + child_cfg = child_cfg_create(priv->name, &lifetime, + NULL, TRUE, MODE_TUNNEL, /* updown, hostaccess */ + ACTION_NONE, ACTION_NONE, ACTION_NONE, ipcomp, + 0, 0, NULL, NULL, 0); + child_cfg->add_proposal(child_cfg, proposal_create_default(PROTO_ESP)); + ts = traffic_selector_create_dynamic(0, 0, 65535); + child_cfg->add_traffic_selector(child_cfg, TRUE, ts); + ts = traffic_selector_create_from_string(0, TS_IPV4_ADDR_RANGE, + "0.0.0.0", 0, + "255.255.255.255", 65535); + child_cfg->add_traffic_selector(child_cfg, FALSE, ts); + peer_cfg->add_child_cfg(peer_cfg, child_cfg); + + /** + * Prepare IKE_SA + */ + ike_sa = charon->ike_sa_manager->checkout_by_config(charon->ike_sa_manager, + peer_cfg); + if (!ike_sa) + { + peer_cfg->destroy(peer_cfg); + g_set_error(err, NM_VPN_PLUGIN_ERROR, NM_VPN_PLUGIN_ERROR_LAUNCH_FAILED, + "IKE version not supported."); + return FALSE; + } + if (!ike_sa->get_peer_cfg(ike_sa)) + { + ike_sa->set_peer_cfg(ike_sa, peer_cfg); + } + peer_cfg->destroy(peer_cfg); + + /** + * Register listener, enable initiate-failure-detection hooks + */ + priv->ike_sa = ike_sa; + priv->listener.ike_state_change = ike_state_change; + priv->listener.child_state_change = child_state_change; + charon->bus->add_listener(charon->bus, &priv->listener); + + /** + * Initiate + */ + child_cfg->get_ref(child_cfg); + if (ike_sa->initiate(ike_sa, child_cfg, 0, NULL, NULL) != SUCCESS) + { + charon->bus->remove_listener(charon->bus, &priv->listener); + charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager, ike_sa); + + g_set_error(err, NM_VPN_PLUGIN_ERROR, NM_VPN_PLUGIN_ERROR_LAUNCH_FAILED, + "Initiating failed."); + return FALSE; + } + charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa); + return TRUE; +} + +/** + * NeedSecrets called from NM via DBUS + */ +static gboolean need_secrets(NMVPNPlugin *plugin, NMConnection *connection, + char **setting_name, GError **error) +{ + NMSettingVPN *settings; + const char *method, *path; + + settings = NM_SETTING_VPN(nm_connection_get_setting(connection, + NM_TYPE_SETTING_VPN)); + method = nm_setting_vpn_get_data_item(settings, "method"); + if (method) + { + if (streq(method, "eap")) + { + if (nm_setting_vpn_get_secret(settings, "password")) + { + return FALSE; + } + } + else if (streq(method, "agent")) + { + if (nm_setting_vpn_get_secret(settings, "agent")) + { + return FALSE; + } + } + else if (streq(method, "key")) + { + path = nm_setting_vpn_get_data_item(settings, "userkey"); + if (path) + { + private_key_t *key; + + /* try to load/decrypt the private key */ + key = lib->creds->create(lib->creds, CRED_PRIVATE_KEY, + KEY_RSA, BUILD_FROM_FILE, path, BUILD_END); + if (key) + { + key->destroy(key); + return FALSE; + } + } + } + else if (streq(method, "smartcard")) + { + if (nm_setting_vpn_get_secret(settings, "password")) + { + return FALSE; + } + } + } + *setting_name = NM_SETTING_VPN_SETTING_NAME; + return TRUE; +} + +/** + * Disconnect called from NM via DBUS + */ +static gboolean disconnect(NMVPNPlugin *plugin, GError **err) +{ + NMStrongswanPluginPrivate *priv = NM_STRONGSWAN_PLUGIN_GET_PRIVATE(plugin); + enumerator_t *enumerator; + ike_sa_t *ike_sa; + u_int id; + + /* our ike_sa pointer might be invalid, lookup sa */ + enumerator = charon->controller->create_ike_sa_enumerator( + charon->controller, TRUE); + while (enumerator->enumerate(enumerator, &ike_sa)) + { + if (priv->ike_sa == ike_sa) + { + id = ike_sa->get_unique_id(ike_sa); + enumerator->destroy(enumerator); + charon->controller->terminate_ike(charon->controller, id, + controller_cb_empty, NULL, 0); + return TRUE; + } + } + enumerator->destroy(enumerator); + + g_set_error(err, NM_VPN_PLUGIN_ERROR, NM_VPN_PLUGIN_ERROR_GENERAL, + "Connection not found."); + return FALSE; +} + +/** + * Initializer + */ +static void nm_strongswan_plugin_init(NMStrongswanPlugin *plugin) +{ + NMStrongswanPluginPrivate *priv; + + priv = NM_STRONGSWAN_PLUGIN_GET_PRIVATE(plugin); + priv->plugin = NM_VPN_PLUGIN(plugin); + memset(&priv->listener, 0, sizeof(listener_t)); + priv->listener.child_updown = child_updown; + priv->listener.ike_rekey = ike_rekey; +} + +/** + * Class constructor + */ +static void nm_strongswan_plugin_class_init( + NMStrongswanPluginClass *strongswan_class) +{ + NMVPNPluginClass *parent_class = NM_VPN_PLUGIN_CLASS(strongswan_class); + + g_type_class_add_private(G_OBJECT_CLASS(strongswan_class), + sizeof(NMStrongswanPluginPrivate)); + parent_class->connect = connect_; + parent_class->need_secrets = need_secrets; + parent_class->disconnect = disconnect; +} + +/** + * Object constructor + */ +NMStrongswanPlugin *nm_strongswan_plugin_new(nm_creds_t *creds, + nm_handler_t *handler) +{ + NMStrongswanPlugin *plugin = (NMStrongswanPlugin *)g_object_new ( + NM_TYPE_STRONGSWAN_PLUGIN, + NM_VPN_PLUGIN_DBUS_SERVICE_NAME, NM_DBUS_SERVICE_STRONGSWAN, + NULL); + if (plugin) + { + NMStrongswanPluginPrivate *priv; + + priv = NM_STRONGSWAN_PLUGIN_GET_PRIVATE(plugin); + priv->creds = creds; + priv->handler = handler; + priv->name = NULL; + } + return plugin; +} + diff --git a/src/charon-nm/nm/nm_service.h b/src/charon-nm/nm/nm_service.h new file mode 100644 index 000000000..828d1a452 --- /dev/null +++ b/src/charon-nm/nm/nm_service.h @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2008-2009 Martin Willi + * 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 nm_service nm_service + * @{ @ingroup nm + */ + +#ifndef NM_SERVICE_H_ +#define NM_SERVICE_H_ + +#include <glib.h> +#include <glib-object.h> +#include <nm-vpn-plugin.h> + +#include "nm_creds.h" +#include "nm_handler.h" + +#define NM_TYPE_STRONGSWAN_PLUGIN (nm_strongswan_plugin_get_type ()) +#define NM_STRONGSWAN_PLUGIN(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_STRONGSWAN_PLUGIN, NMSTRONGSWANPlugin)) +#define NM_STRONGSWAN_PLUGIN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_STRONGSWAN_PLUGIN, NMSTRONGSWANPluginClass)) +#define NM_IS_STRONGSWAN_PLUGIN(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_STRONGSWAN_PLUGIN)) +#define NM_IS_STRONGSWAN_PLUGIN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), NM_TYPE_STRONGSWAN_PLUGIN)) +#define NM_STRONGSWAN_PLUGIN_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_STRONGSWAN_PLUGIN, NMSTRONGSWANPluginClass)) + +#define NM_DBUS_SERVICE_STRONGSWAN "org.freedesktop.NetworkManager.strongswan" +#define NM_DBUS_INTERFACE_STRONGSWAN "org.freedesktop.NetworkManager.strongswan" +#define NM_DBUS_PATH_STRONGSWAN "/org/freedesktop/NetworkManager/strongswan" + +typedef struct { + NMVPNPlugin parent; +} NMStrongswanPlugin; + +typedef struct { + NMVPNPluginClass parent; +} NMStrongswanPluginClass; + +GType nm_strongswan_plugin_get_type(void); + +NMStrongswanPlugin *nm_strongswan_plugin_new(nm_creds_t *creds, + nm_handler_t *handler); + +#endif /** NM_SERVICE_H_ @}*/ |