From fc4685abecf24c841eff3f6f70c27bd612796b63 Mon Sep 17 00:00:00 2001 From: Andrew Zaborowski Date: Fri, 23 Aug 2019 03:30:23 +0200 Subject: [PATCH] eap-tls: Add ServerDomainMask config option Allow users to provide a glob string that the contents of the server certificate's subject DN should be matched against as a primitive protection against rogue APs using certificates purchased from commercial CAs trusted by the client. If the network uses an AP certificate emitted by a commerical CA and the clients are configured to trust those CAs so that the client configurations don't have to be updated when the AP renews its certificate, this new option can be used to check if the CN in the AP certificate's DN matches the known domain name. This logic assumes that the commercial CAs provide enough assurance that only the owner of the domain can buy a certificate with that domain in the CN field. The format of this option is similar to apple's TLSTrustedServerNames and wpa_supplicant's domain_match/domain_suffix_match format, the exact syntax is documented in ell/tls.c. --- src/eap-tls-common.c | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/src/eap-tls-common.c b/src/eap-tls-common.c index 7e566c44..b069fcfd 100644 --- a/src/eap-tls-common.c +++ b/src/eap-tls-common.c @@ -117,6 +117,7 @@ struct eap_tls_state { char *client_cert; char *client_key; char *passphrase; + char **domain_mask; const struct eap_tls_variant_ops *variant_ops; void *variant_data; @@ -186,6 +187,7 @@ void eap_tls_common_state_free(struct eap_state *eap) l_free(eap_tls->passphrase); } + l_strv_free(eap_tls->domain_mask); l_free(eap_tls); } @@ -552,6 +554,9 @@ static bool eap_tls_tunnel_init(struct eap_state *eap) return false; } + if (eap_tls->domain_mask) + l_tls_set_domain_mask(eap_tls->tunnel, eap_tls->domain_mask); + if (!l_tls_start(eap_tls->tunnel)) { l_error("%s: Failed to start the TLS client", eap_get_method_name(eap)); @@ -765,6 +770,7 @@ int eap_tls_common_settings_check(struct l_settings *settings, ssize_t result; uint8_t *encrypted, *decrypted; struct l_key *pub_key; + const char *domain_mask_str; L_AUTO_FREE_VAR(char *, path); L_AUTO_FREE_VAR(char *, client_cert) = NULL; @@ -930,6 +936,22 @@ int eap_tls_common_settings_check(struct l_settings *settings, goto done; } + /* + * Require CACert if ServerDomainMask is present. If the server + * certificate is not being checked against any trusted certificates + * there's no point validating its contents and we wouldn't even + * receive the subject DN from ell, because it may be freely made up. + */ + snprintf(setting_key, sizeof(setting_key), "%sServerDomainMask", + prefix); + domain_mask_str = l_settings_get_value(settings, "Security", + setting_key); + if (domain_mask_str && !cacerts) { + l_error("%s was set but no CA Certificates given", setting_key); + ret = -EINVAL; + goto done; + } + ret = 0; done: l_queue_destroy(cacerts, @@ -950,6 +972,7 @@ bool eap_tls_common_settings_load(struct eap_state *eap, { struct eap_tls_state *eap_tls; char setting_key[72]; + char *domain_mask_str; eap_tls = l_new(struct eap_tls_state, 1); @@ -974,6 +997,16 @@ bool eap_tls_common_settings_load(struct eap_state *eap, eap_tls->passphrase = l_settings_get_string(settings, "Security", setting_key); + snprintf(setting_key, sizeof(setting_key), "%sServerDomainMask", + prefix); + domain_mask_str = l_settings_get_string(settings, "Security", + setting_key); + + if (domain_mask_str) { + eap_tls->domain_mask = l_strsplit(domain_mask_str, ';'); + l_free(domain_mask_str); + } + eap_set_data(eap, eap_tls); return true;