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 <jouni@qca.qualcomm.com>
This commit is contained in:
Jouni Malinen 2012-02-29 16:40:17 +02:00 committed by Jouni Malinen
parent 10531d2166
commit 11e4f46a10
3 changed files with 104 additions and 3 deletions

View file

@ -1782,6 +1782,9 @@ void wpa_config_free_cred(struct wpa_cred *cred)
os_free(cred->username); os_free(cred->username);
os_free(cred->password); os_free(cred->password);
os_free(cred->ca_cert); 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->imsi);
os_free(cred->milenage); os_free(cred->milenage);
os_free(cred->domain); os_free(cred->domain);
@ -2257,6 +2260,24 @@ int wpa_config_set_cred(struct wpa_cred *cred, const char *var,
return 0; 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) { if (os_strcmp(var, "imsi") == 0) {
os_free(cred->imsi); os_free(cred->imsi);
cred->imsi = val; cred->imsi = val;

View file

@ -80,6 +80,51 @@ struct wpa_cred {
*/ */
char *ca_cert; 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 <MCC> | <MNC> | '-' | <MSIN> format * imsi - IMSI in <MCC> | <MNC> | '-' | <MSIN> format
*/ */

View file

@ -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, static struct nai_realm_eap * nai_realm_find_eap(struct wpa_cred *cred,
struct nai_realm *realm) struct nai_realm *realm)
{ {
@ -425,13 +439,19 @@ static struct nai_realm_eap * nai_realm_find_eap(struct wpa_cred *cred,
if (cred == NULL || if (cred == NULL ||
cred->username == NULL || cred->username == NULL ||
cred->username[0] == '\0' || cred->username[0] == '\0' ||
cred->password == NULL || ((cred->password == NULL ||
cred->password[0] == '\0') cred->password[0] == '\0') &&
(cred->private_key == NULL ||
cred->private_key[0] == '\0')))
return NULL; return NULL;
for (e = 0; e < realm->eap_count; e++) { for (e = 0; e < realm->eap_count; e++) {
struct nai_realm_eap *eap = &realm->eap[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; 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) wpa_config_set_quoted(ssid, "password", cred->password) < 0)
goto fail; 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) { switch (eap->method) {
case EAP_TYPE_TTLS: case EAP_TYPE_TTLS:
if (eap->inner_method) { 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) if (wpa_config_set(ssid, "phase2", buf, 0) < 0)
goto fail; goto fail;
break; break;
case EAP_TYPE_TLS:
break;
} }
if (cred->ca_cert && cred->ca_cert[0] && if (cred->ca_cert && cred->ca_cert[0] &&