From 11e4f46a10ddc49c4a4cfc66483071c62e5224f5 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Wed, 29 Feb 2012 16:40:17 +0200 Subject: [PATCH] Interworking: Add support for certificate credentials (EAP-TLS) This allows Interworking network selection to be used with EAP-TLS (client certificate/private key based credential). Signed-hostap: Jouni Malinen --- wpa_supplicant/config.c | 21 ++++++++++++++++++++ wpa_supplicant/config.h | 45 +++++++++++++++++++++++++++++++++++++++++++ wpa_supplicant/interworking.c | 41 ++++++++++++++++++++++++++++++++++++--- 3 files changed, 104 insertions(+), 3 deletions(-) diff --git a/wpa_supplicant/config.c b/wpa_supplicant/config.c index 0aae8af..fc3205e 100644 --- a/wpa_supplicant/config.c +++ b/wpa_supplicant/config.c @@ -1782,6 +1782,9 @@ void wpa_config_free_cred(struct wpa_cred *cred) os_free(cred->username); os_free(cred->password); os_free(cred->ca_cert); + os_free(cred->client_cert); + os_free(cred->private_key); + os_free(cred->private_key_passwd); os_free(cred->imsi); os_free(cred->milenage); os_free(cred->domain); @@ -2257,6 +2260,24 @@ int wpa_config_set_cred(struct wpa_cred *cred, const char *var, return 0; } + if (os_strcmp(var, "client_cert") == 0) { + os_free(cred->client_cert); + cred->client_cert = val; + return 0; + } + + if (os_strcmp(var, "private_key") == 0) { + os_free(cred->private_key); + cred->private_key = val; + return 0; + } + + if (os_strcmp(var, "private_key_passwd") == 0) { + os_free(cred->private_key_passwd); + cred->private_key_passwd = val; + return 0; + } + if (os_strcmp(var, "imsi") == 0) { os_free(cred->imsi); cred->imsi = val; diff --git a/wpa_supplicant/config.h b/wpa_supplicant/config.h index 0e654fc..0dcb4c8 100644 --- a/wpa_supplicant/config.h +++ b/wpa_supplicant/config.h @@ -81,6 +81,51 @@ struct wpa_cred { char *ca_cert; /** + * client_cert - File path to client certificate file (PEM/DER) + * + * This field is used with Interworking networking selection for a case + * where client certificate/private key is used for authentication + * (EAP-TLS). Full path to the file should be used since working + * directory may change when wpa_supplicant is run in the background. + * + * Alternatively, a named configuration blob can be used by setting + * this to blob://blob_name. + */ + char *client_cert; + + /** + * private_key - File path to client private key file (PEM/DER/PFX) + * + * When PKCS#12/PFX file (.p12/.pfx) is used, client_cert should be + * commented out. Both the private key and certificate will be read + * from the PKCS#12 file in this case. Full path to the file should be + * used since working directory may change when wpa_supplicant is run + * in the background. + * + * Windows certificate store can be used by leaving client_cert out and + * configuring private_key in one of the following formats: + * + * cert://substring_to_match + * + * hash://certificate_thumbprint_in_hex + * + * For example: private_key="hash://63093aa9c47f56ae88334c7b65a4" + * + * Note that when running wpa_supplicant as an application, the user + * certificate store (My user account) is used, whereas computer store + * (Computer account) is used when running wpasvc as a service. + * + * Alternatively, a named configuration blob can be used by setting + * this to blob://blob_name. + */ + char *private_key; + + /** + * private_key_passwd - Password for private key file + */ + char *private_key_passwd; + + /** * imsi - IMSI in | | '-' | format */ char *imsi; diff --git a/wpa_supplicant/interworking.c b/wpa_supplicant/interworking.c index be0e784..72aa20e 100644 --- a/wpa_supplicant/interworking.c +++ b/wpa_supplicant/interworking.c @@ -417,6 +417,20 @@ static int nai_realm_cred_username(struct nai_realm_eap *eap) } +static int nai_realm_cred_cert(struct nai_realm_eap *eap) +{ + if (eap_get_name(EAP_VENDOR_IETF, eap->method) == NULL) + return 0; /* method not supported */ + + if (eap->method != EAP_TYPE_TLS) { + /* Only EAP-TLS supported for credential authentication */ + return 0; + } + + return 1; +} + + static struct nai_realm_eap * nai_realm_find_eap(struct wpa_cred *cred, struct nai_realm *realm) { @@ -425,13 +439,19 @@ static struct nai_realm_eap * nai_realm_find_eap(struct wpa_cred *cred, if (cred == NULL || cred->username == NULL || cred->username[0] == '\0' || - cred->password == NULL || - cred->password[0] == '\0') + ((cred->password == NULL || + cred->password[0] == '\0') && + (cred->private_key == NULL || + cred->private_key[0] == '\0'))) return NULL; for (e = 0; e < realm->eap_count; e++) { struct nai_realm_eap *eap = &realm->eap[e]; - if (nai_realm_cred_username(eap)) + if (cred->password && cred->password[0] && + nai_realm_cred_username(eap)) + return eap; + if (cred->private_key && cred->private_key[0] && + nai_realm_cred_cert(eap)) return eap; } @@ -757,6 +777,19 @@ int interworking_connect(struct wpa_supplicant *wpa_s, struct wpa_bss *bss) wpa_config_set_quoted(ssid, "password", cred->password) < 0) goto fail; + if (cred->client_cert && cred->client_cert[0] && + wpa_config_set_quoted(ssid, "client_cert", cred->client_cert) < 0) + goto fail; + + if (cred->private_key && cred->private_key[0] && + wpa_config_set_quoted(ssid, "private_key", cred->private_key) < 0) + goto fail; + + if (cred->private_key_passwd && cred->private_key_passwd[0] && + wpa_config_set_quoted(ssid, "private_key_passwd", + cred->private_key_passwd) < 0) + goto fail; + switch (eap->method) { case EAP_TYPE_TTLS: if (eap->inner_method) { @@ -796,6 +829,8 @@ int interworking_connect(struct wpa_supplicant *wpa_s, struct wpa_bss *bss) if (wpa_config_set(ssid, "phase2", buf, 0) < 0) goto fail; break; + case EAP_TYPE_TLS: + break; } if (cred->ca_cert && cred->ca_cert[0] && -- 2.1.4