Interworking: Add support for certificate credentials (EAP-TLS)
authorJouni Malinen <jouni@qca.qualcomm.com>
Wed, 29 Feb 2012 14:40:17 +0000 (16:40 +0200)
committerJouni Malinen <j@w1.fi>
Sat, 3 Mar 2012 08:38:10 +0000 (10:38 +0200)
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>

wpa_supplicant/config.c
wpa_supplicant/config.h
wpa_supplicant/interworking.c

index 0aae8af..fc3205e 100644 (file)
@@ -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;
index 0e654fc..0dcb4c8 100644 (file)
@@ -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 <MCC> | <MNC> | '-' | <MSIN> format
         */
        char *imsi;
index be0e784..72aa20e 100644 (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,
                                                 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] &&