diff options
Diffstat (limited to 'src/libcharon/plugins/nm')
-rw-r--r-- | src/libcharon/plugins/nm/Makefile.in | 20 | ||||
-rw-r--r-- | src/libcharon/plugins/nm/nm_creds.c | 97 | ||||
-rw-r--r-- | src/libcharon/plugins/nm/nm_creds.h | 17 | ||||
-rw-r--r-- | src/libcharon/plugins/nm/nm_plugin.c | 2 | ||||
-rw-r--r-- | src/libcharon/plugins/nm/nm_service.c | 108 |
5 files changed, 209 insertions, 35 deletions
diff --git a/src/libcharon/plugins/nm/Makefile.in b/src/libcharon/plugins/nm/Makefile.in index 1b3e4c5a6..2f5c20971 100644 --- a/src/libcharon/plugins/nm/Makefile.in +++ b/src/libcharon/plugins/nm/Makefile.in @@ -44,6 +44,7 @@ am__aclocal_m4_deps = $(top_srcdir)/m4/config/libtool.m4 \ $(top_srcdir)/m4/config/lt~obsolete.m4 \ $(top_srcdir)/m4/macros/with.m4 \ $(top_srcdir)/m4/macros/enable-disable.m4 \ + $(top_srcdir)/m4/macros/add-plugin.m4 \ $(top_srcdir)/configure.in am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) @@ -166,6 +167,8 @@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PERL = @PERL@ PKG_CONFIG = @PKG_CONFIG@ +PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ +PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ PTHREADLIB = @PTHREADLIB@ RANLIB = @RANLIB@ RTLIB = @RTLIB@ @@ -197,14 +200,17 @@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ +c_plugins = @c_plugins@ datadir = @datadir@ datarootdir = @datarootdir@ +dbusservicedir = @dbusservicedir@ default_pkcs11 = @default_pkcs11@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ gtk_CFLAGS = @gtk_CFLAGS@ gtk_LIBS = @gtk_LIBS@ +h_plugins = @h_plugins@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ @@ -219,24 +225,31 @@ ipsecgid = @ipsecgid@ ipsecgroup = @ipsecgroup@ ipsecuid = @ipsecuid@ ipsecuser = @ipsecuser@ +libcharon_plugins = @libcharon_plugins@ libdir = @libdir@ libexecdir = @libexecdir@ -libhydra_plugins = @libhydra_plugins@ -libstrongswan_plugins = @libstrongswan_plugins@ linux_headers = @linux_headers@ localedir = @localedir@ localstatedir = @localstatedir@ lt_ECHO = @lt_ECHO@ +maemo_CFLAGS = @maemo_CFLAGS@ +maemo_LIBS = @maemo_LIBS@ +manager_plugins = @manager_plugins@ mandir = @mandir@ +medsrv_plugins = @medsrv_plugins@ mkdir_p = @mkdir_p@ nm_CFLAGS = @nm_CFLAGS@ nm_LIBS = @nm_LIBS@ nm_ca_dir = @nm_ca_dir@ oldincludedir = @oldincludedir@ +openac_plugins = @openac_plugins@ +p_plugins = @p_plugins@ pdfdir = @pdfdir@ piddir = @piddir@ +pki_plugins = @pki_plugins@ plugindir = @plugindir@ pluto_plugins = @pluto_plugins@ +pool_plugins = @pool_plugins@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ @@ -244,7 +257,10 @@ random_device = @random_device@ resolv_conf = @resolv_conf@ routing_table = @routing_table@ routing_table_prio = @routing_table_prio@ +s_plugins = @s_plugins@ sbindir = @sbindir@ +scepclient_plugins = @scepclient_plugins@ +scripts_plugins = @scripts_plugins@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ strongswan_conf = @strongswan_conf@ diff --git a/src/libcharon/plugins/nm/nm_creds.c b/src/libcharon/plugins/nm/nm_creds.c index 193838e6b..638787019 100644 --- a/src/libcharon/plugins/nm/nm_creds.c +++ b/src/libcharon/plugins/nm/nm_creds.c @@ -51,6 +51,16 @@ struct private_nm_creds_t { char *pass; /** + * Private key decryption password / smartcard pin + */ + char *keypass; + + /** + * private key ID of smartcard key + */ + chunk_t keyid; + + /** * users certificate */ certificate_t *usercert; @@ -239,8 +249,14 @@ static bool shared_enumerate(shared_enumerator_t *this, shared_key_t **key, return FALSE; } *key = this->key; - *me = ID_MATCH_PERFECT; - *other = ID_MATCH_ANY; + if (me) + { + *me = ID_MATCH_PERFECT; + } + if (other) + { + *other = ID_MATCH_ANY; + } this->done = TRUE; return TRUE; } @@ -262,18 +278,39 @@ static enumerator_t* create_shared_enumerator(private_nm_creds_t *this, identification_t *other) { shared_enumerator_t *enumerator; + chunk_t key; - if (!this->pass || !this->user) + switch (type) { - return NULL; - } - if (type != SHARED_EAP && type != SHARED_IKE) - { - return NULL; - } - if (me && !me->equals(me, this->user)) - { - return NULL; + case SHARED_EAP: + case SHARED_IKE: + if (!this->pass || !this->user) + { + return NULL; + } + if (me && !me->equals(me, this->user)) + { + return NULL; + } + key = chunk_create(this->pass, strlen(this->pass)); + break; + case SHARED_PRIVATE_KEY_PASS: + if (!this->keypass) + { + return NULL; + } + key = chunk_create(this->keypass, strlen(this->keypass)); + break; + case SHARED_PIN: + if (!this->keypass || !me || + !chunk_equals(me->get_encoding(me), this->keyid)) + { + return NULL; + } + key = chunk_create(this->keypass, strlen(this->keypass)); + break; + default: + return NULL; } enumerator = malloc_thing(shared_enumerator_t); @@ -282,9 +319,7 @@ static enumerator_t* create_shared_enumerator(private_nm_creds_t *this, enumerator->this = this; enumerator->done = FALSE; this->lock->read_lock(this->lock); - enumerator->key = shared_key_create(type, - chunk_clone(chunk_create(this->pass, - strlen(this->pass)))); + enumerator->key = shared_key_create(type, chunk_clone(key)); return &enumerator->public; } @@ -370,6 +405,30 @@ static void set_username_password(private_nm_creds_t *this, identification_t *id } /** + * Implementation of nm_creds_t.set_key_password + */ +static void set_key_password(private_nm_creds_t *this, char *password) +{ + this->lock->write_lock(this->lock); + free(this->keypass); + this->keypass = password ? strdup(password) : NULL; + this->lock->unlock(this->lock); +} + +/** + * Implementation of nm_creds_t.set_pin + */ +static void set_pin(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 = pin ? strdup(pin) : NULL; + this->keyid = chunk_clone(keyid); + this->lock->unlock(this->lock); +} + +/** * Implementation of nm_creds_t.set_cert_and_key */ static void set_cert_and_key(private_nm_creds_t *this, certificate_t *cert, @@ -396,12 +455,16 @@ static void clear(private_nm_creds_t *this) } 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; } /** @@ -430,6 +493,8 @@ nm_creds_t *nm_creds_create() this->public.add_certificate = (void(*)(nm_creds_t*, certificate_t *cert))add_certificate; this->public.load_ca_dir = (void(*)(nm_creds_t*, char *dir))load_ca_dir; this->public.set_username_password = (void(*)(nm_creds_t*, identification_t *id, char *password))set_username_password; + this->public.set_key_password = (void(*)(nm_creds_t*, char *password))set_key_password; + this->public.set_pin = (void(*)(nm_creds_t*, chunk_t keyid, char *pin))set_pin; this->public.set_cert_and_key = (void(*)(nm_creds_t*, certificate_t *cert, private_key_t *key))set_cert_and_key; this->public.clear = (void(*)(nm_creds_t*))clear; this->public.destroy = (void(*)(nm_creds_t*))destroy; @@ -441,6 +506,8 @@ nm_creds_t *nm_creds_create() this->pass = NULL; this->usercert = NULL; this->key = NULL; + this->keypass = NULL; + this->keyid = chunk_empty; return &this->public; } diff --git a/src/libcharon/plugins/nm/nm_creds.h b/src/libcharon/plugins/nm/nm_creds.h index b55cff31e..91f645c7e 100644 --- a/src/libcharon/plugins/nm/nm_creds.h +++ b/src/libcharon/plugins/nm/nm_creds.h @@ -58,6 +58,22 @@ struct nm_creds_t { */ 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. * @@ -66,6 +82,7 @@ struct nm_creds_t { */ void (*set_cert_and_key)(nm_creds_t *this, certificate_t *cert, private_key_t *key); + /** * Clear the stored credentials. */ diff --git a/src/libcharon/plugins/nm/nm_plugin.c b/src/libcharon/plugins/nm/nm_plugin.c index 250e6f7f9..fd0580bd6 100644 --- a/src/libcharon/plugins/nm/nm_plugin.c +++ b/src/libcharon/plugins/nm/nm_plugin.c @@ -122,7 +122,7 @@ plugin_t *nm_plugin_create() /* bypass file permissions to read from users ssh-agent */ charon->keep_cap(charon, CAP_DAC_OVERRIDE); - charon->processor->queue_job(charon->processor, + lib->processor->queue_job(lib->processor, (job_t*)callback_job_create((callback_job_cb_t)run, this, NULL, NULL)); return &this->public.plugin; diff --git a/src/libcharon/plugins/nm/nm_service.c b/src/libcharon/plugins/nm/nm_service.c index 07318bbbf..72c5bbbb5 100644 --- a/src/libcharon/plugins/nm/nm_service.c +++ b/src/libcharon/plugins/nm/nm_service.c @@ -204,6 +204,59 @@ static bool ike_rekey(listener_t *listener, ike_sa_t *old, ike_sa_t *new) } /** + * 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, @@ -224,7 +277,7 @@ static gboolean connect_(NMVPNPlugin *plugin, NMConnection *connection, auth_class_t auth_class = AUTH_CLASS_EAP; certificate_t *cert = NULL; x509_t *x509; - bool agent = FALSE; + bool agent = FALSE, smartcard = FALSE; lifetime_cfg_t lifetime = { .time = { .life = 10800 /* 3h */, @@ -279,6 +332,11 @@ static gboolean connect_(NMVPNPlugin *plugin, NMConnection *connection, { auth_class = AUTH_CLASS_PUBKEY; } + else if (streq(str, "smartcard")) + { + auth_class = AUTH_CLASS_PUBKEY; + smartcard = TRUE; + } } /** @@ -338,9 +396,26 @@ static gboolean connect_(NMVPNPlugin *plugin, NMConnection *connection, 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 */ - str = nm_setting_vpn_get_data_item(vpn, "usercert"); - if (str) + else if ((str = nm_setting_vpn_get_data_item(vpn, "usercert"))) { public_key_t *public; private_key_t *private = NULL; @@ -380,16 +455,15 @@ static gboolean connect_(NMVPNPlugin *plugin, NMConnection *connection, str = nm_setting_vpn_get_data_item(vpn, "userkey"); if (!agent && str) { - chunk_t secret; + char *secret; - secret.ptr = (char*)nm_setting_vpn_get_secret(vpn, "password"); - if (secret.ptr) + secret = (char*)nm_setting_vpn_get_secret(vpn, "password"); + if (secret) { - secret.len = strlen(secret.ptr); + priv->creds->set_key_password(priv->creds, secret); } private = lib->creds->create(lib->creds, CRED_PRIVATE_KEY, - KEY_RSA, BUILD_FROM_FILE, str, - BUILD_PASSPHRASE, secret, BUILD_END); + KEY_RSA, BUILD_FROM_FILE, str, BUILD_END); if (!private) { g_set_error(err, NM_VPN_PLUGIN_ERROR, @@ -524,17 +598,10 @@ static gboolean need_secrets(NMVPNPlugin *plugin, NMConnection *connection, if (path) { private_key_t *key; - chunk_t secret; - secret.ptr = (char*)nm_setting_vpn_get_secret(settings, "password"); - if (secret.ptr) - { - secret.len = strlen(secret.ptr); - } /* try to load/decrypt the private key */ key = lib->creds->create(lib->creds, CRED_PRIVATE_KEY, - KEY_RSA, BUILD_FROM_FILE, path, - BUILD_PASSPHRASE, secret, BUILD_END); + KEY_RSA, BUILD_FROM_FILE, path, BUILD_END); if (key) { key->destroy(key); @@ -542,6 +609,13 @@ static gboolean need_secrets(NMVPNPlugin *plugin, NMConnection *connection, } } } + else if streq(method, "smartcard") + { + if (nm_setting_vpn_get_secret(settings, "password")) + { + return FALSE; + } + } } *setting_name = NM_SETTING_VPN_SETTING_NAME; return TRUE; |