espressif / esp-idf

Espressif IoT Development Framework. Official development framework for Espressif SoCs.
Apache License 2.0
13.76k stars 7.3k forks source link

Unable to validate authentication server when connecting to a WPA2-Enterprise network (IDFGH-47) #247

Closed joostd closed 5 years ago

joostd commented 7 years ago

Requiring the RADIUS server's X.509 certificate to be issued by a particular CA using esp_wifi_sta_wpa2_ent_set_ca_cert helps but is insufficient to protect against rogue Access Points.

What is missing from esp-idf/components/esp32/include/esp_wpa2.h is a function to restrict the certificate's subject Common Name to a particular hostname or IP address ("Connect to radius.mycompany.com instead of just any RADIUS server carrying a letsencrypt certificate").

This function should be easy to implement and I would be happy to submit a PR myself, but unfortunately the libwpa2 library is not open source yet. Any chance of someone with access to the source code implementing this? It would make the esp32 an awesome platform for global roaming networks like eduroam.

TimXia commented 7 years ago

Do you mean the Common Name which is restricted when generating CA certificate? If so, it has nothing to do with esp32. Because the CA certificate in esp32 is got from the Radius server.

negativekelvin commented 7 years ago

@joostd are you saying it will not reject servers that do not match the realm in the identity or username?

joostd commented 7 years ago

To elaborate a bit: consider connecting to a WPA2-Enterprise WiFi network where the RADIUS server is configured to require EAP-TTLS or PEAP, which both build a TLS tunnel between the esp32 and the RADIUS server used for authentication. I can use esp_wifi_sta_wpa2_ent_set_ca_cert() to verify that the RADIUS server's TLS certificate is issued by a particular CA (Digicert, for instance), but apart from that, any RADIUS server with a certificate issued by that CA will be accepted by the supplicant on the esp32.

This is a problem since a rogue access point can simply advertise the SSID of my network waiting for supplicants to associate, directing them to a rogue RADIUS server. By using a certificate issued by the same CA, the esp32 is unable to detect the rogue RADIUS server.

This attack is usually mitigated on end-user devices by presenting the RADIUS certificate to the end-user for approval, which is clearly not an option for embedded systems. In such cases the client typically restricts the server certificate by matching against its subject, for instance using the Common Name field.

This may sound like a corner case but really it is not. Bear in mind that:

This is basically the TL;DR from the PEAP talk at defcon 20

To mitigate these problems, we need to be able to detect a rogue access point by verifying not only the CA certificate, but also the RADIUS server certificate issued by that CA. As that certificate is renewed periodically, it is most practical to do the verification based on the certificate's subject Common Name, typically containing a hostname that is verified by the CA issuing the certificate (i.e. only the domain owner of google.com can obtain certificates for a hostname like radius.google.com).

I believe libwpa2 is based on wpa_supplicant, so it makes sense to have a look at wpa_supplicant.conf. There several configuration options exist to restrict the certificate subject. For instance subject_match can be used to only accept certificates for radius.mycompany.com.

negativekelvin commented 7 years ago

So setting the outer identity to anonymous@radius.mycompany.com or the inner identity to username@radius.mycompany.com does not cause those realms to be checked against the certificate? Would that be enough or you suggest there should be a whitelist or callback?

joostd commented 7 years ago

The realm used in the outer identity is unrelated to the Common Name used in the certificate (same holds for the inner identity). In fact, using a domain name as a realm is only a convention.

What I suggest is adding a function to libwpa2 similar to wpa_supplicant's tls_connection_set_subject_match.

Then I can restrict both the CA and the subject of the server certificate to be what I expect, mitigating a man in the middle attack.

TimXia commented 7 years ago

@joostd Can a rogue access point forge a certificate with the same Common Name?

joostd commented 7 years ago

Only when using a rogue CA. Hence the need for both esp_wifi_sta_wpa2_ent_set_ca_cert and tls_connection_set_subject_match.

See also the comments in wpa_supplicant's eap_config.h

TimXia commented 7 years ago

@joostd I have understood you. A rogue AP can forge server certificate, but can not forge Common Name. Is that right?

negativekelvin commented 7 years ago

@joostd from WPA_supplicant.conf re: subject match

Note: Since this is a substring match, this cannot be used securely to do a suffix match against a possible domain name in the CN entry. For such a use case, domain_suffix_match or domain_match should be used instead.

So guess it would be preferred to have those options as well.

Also see Android's setAltSubjectMatch

joostd commented 7 years ago

@TimXia That's about right.

More precisely: a rogue access point can relay EAP messages to a rogue RADIUS server. This rogue RADIUS server can provide an arbitrary TLS certificate it has a corresponding key for, including a self-signed certificate.

The eps32 needs to authenticate the RADIUS server before sending user credentials to it for client authentication. This authentication must be based on the RADIUS server certificate, as all communication between the esp32 and the RADIUS server is based on (EAP) messages relayed by the access point.

The esp32 should only trust the RADIUS server if its certificate belongs to the organisation operating the RADIUS server. A common way to verify this is to

  1. verify the hostname in the certificate to match a "known good value" (e.g. radius.example.com).
  2. verify the CA that issued this certificate (e.g. Digicert's root CA certificate)

The crux is that trusted public CA's like Digicert will verify that the organisation that applies for a certificate for radius.example.com actually owns the domain name example.com before issuing the certificate.

Any adversary can generate a certificate containing the hostname radius.example.com, but what they cannot do is have this certificate signed by Digicert unless they own the domain name example.com.

Or conversely: any adversary can obtain a certificate from Digicert, but the cannot obtain a certificate from Digicert for radius.example.com unless they own the domain name example.com.

joostd commented 7 years ago

@negativekelvin Sure, supporting all those options is even better. You are right that the behaviour I describe actually corresponds to domain_match, not subject_match.

TimXia commented 7 years ago

@joostd Thank you for your kindly explanation. We will add an API to restrict the certificate's subject Common Name to a particular hostname or IP address in the next version of esp32 IDF.

mattblyde commented 5 years ago

Hi @TimXia /others is there still a plan to add this feature? Or does it already exist and I've I overlooked it?

This appears to be fairly critical to safely accepting CA signed RADIUS server certificates (I'm far from an expert though).

liuzfesp commented 5 years ago

HI @mattblyde I'm afraid that this function is still not ready because of low priority, I think the whole supplicant will be open source in IDF soon and you can customize the WPA2 as you want before we official provide the API.

Alvin1Zhang commented 5 years ago

@joostd Thanks for reporting the issue, feel free to reopen if it still exists. Thanks.