hostapd: add support for subject validation
authorDavid Lam <david@thedavid.net>
Tue, 14 Jan 2020 08:27:28 +0000 (00:27 -0800)
committerJo-Philipp Wich <jo@mein.io>
Tue, 14 Jan 2020 16:46:27 +0000 (17:46 +0100)
The wpa_supplicant supports certificate subject validation via the
subject match(2) and altsubject_match(2) fields. domain_match(2) and
domain_suffix_match(2) fields are also supported for advanced matches.
This validation is especially important when connecting to access
points that use PAP as the Phase 2 authentication type. Without proper
validation, the user's password can be transmitted to a rogue access
point in plaintext without the user's knowledge. Most organizations
already require these attributes to be included to ensure that the
connection from the STA and the AP is secure. Includes LuCI changes via
openwrt/luci#3444.

From the documentation:

subject_match - Constraint for server certificate subject. This substring
is matched against the subject of the authentication server certificate.
If this string is set, the server sertificate is only accepted if it
contains this string in the subject. The subject string is in following
format: /C=US/ST=CA/L=San Francisco/CN=Test AS/emailAddress=as
.example.com

subject_match2 - Constraint for server certificate subject. This field is
like subject_match, but used for phase 2 (inside EAP-TTLS/PEAP/FAST
tunnel) authentication.

altsubject_match - Constraint for server certificate alt. subject.
Semicolon separated string of entries to be matched against the
alternative subject name of the authentication server certificate. If
this string is set, the server sertificate is only accepted if it
contains one of the entries in an alternative subject name extension.
altSubjectName string is in following format: TYPE:VALUE Example:
EMAIL:server@example.com Example:
DNS:server.example.com;DNS:server2.example.com Following types are
supported: EMAIL, DNS, URI

altsubject_match2 - Constraint for server certificate alt. subject. This
field is like altsubject_match, but used for phase 2 (inside
EAP-TTLS/PEAP/FAST tunnel) authentication.

domain_match - Constraint for server domain name. If set, this FQDN is
used as a full match requirement for the
server certificate in SubjectAltName dNSName element(s). If a
matching dNSName is found, this constraint is met. If no dNSName
values are present, this constraint is matched against SubjectName CN
using same full match comparison. This behavior is similar to
domain_suffix_match, but has the requirement of a full match, i.e.,
no subdomains or wildcard matches are allowed. Case-insensitive
comparison is used, so "Example.com" matches "example.com", but would
not match "test.Example.com". More than one match string can be
provided by using semicolons to
separate the strings (e.g., example.org;example.com). When multiple
strings are specified, a match with any one of the values is considered
a sufficient match for the certificate, i.e., the conditions are ORed
together.

domain_match2 - Constraint for server domain name. This field is like
domain_match, but used for phase 2 (inside EAP-TTLS/PEAP/FAST tunnel)
authentication.

domain_suffix_match - Constraint for server domain name. If set, this
FQDN is used as a suffix match requirement for the AAA server
certificate in SubjectAltName dNSName element(s). If a matching dNSName
is found, this constraint is met. If no dNSName values are present,
this constraint is matched against SubjectName CN using same suffix
match comparison. Suffix match here means that the host/domain name is
compared one label at a time starting from the top-level domain and all
the labels in domain_suffix_match shall be included in the certificate.
The certificate may include additional sub-level labels in addition to
the required labels. More than one match string can be provided by using
semicolons to separate the strings (e.g., example.org;example.com).
When multiple strings are specified, a match with any one of the values
is considered a sufficient match for the certificate, i.e., the
conditions are ORed together. For example,
domain_suffix_match=example.com would match test.example.com but would
not match test-example.com. This field is like domain_match, but used
for phase 2 (inside EAP-TTLS/PEAP/FAST tunnel) authentication.

domain_suffix_match2 - Constraint for server domain name. This field is
like domain_suffix_match, but used for phase 2 (inside
EAP-TTLS/PEAP/FAST tunnel) authentication.

Signed-off-by: David Lam <david@thedavid.net>
package/network/services/hostapd/Makefile
package/network/services/hostapd/files/hostapd.sh

index 11d0d7a9ce9b2c2349b30c4c74c9cf3ea3f1d84e..8dfcd89cc46a583a8d79678273cdee6b980541ea 100644 (file)
@@ -7,7 +7,7 @@
 include $(TOPDIR)/rules.mk
 
 PKG_NAME:=hostapd
-PKG_RELEASE:=2
+PKG_RELEASE:=3
 
 PKG_SOURCE_URL:=http://w1.fi/hostap.git
 PKG_SOURCE_PROTO:=git
index 3d4e57db25c0edee7de8fd7ba6a68957a7ec62ea..dcbabaf8f12dc1e0cc52534fe18c5390f4bb9936 100644 (file)
@@ -202,6 +202,9 @@ hostapd_common_add_bss_config() {
        config_add_string radius_client_addr
        config_add_string iapp_interface
        config_add_string eap_type ca_cert client_cert identity anonymous_identity auth priv_key priv_key_pwd
+       config_add_string subject_match subject_match2
+       config_add_array altsubject_match altsubject_match2
+       config_add_array domain_match domain_match2 domain_suffix_match domain_suffix_match2
        config_add_string ieee80211w_mgmt_cipher
 
        config_add_int dynamic_vlan vlan_naming
@@ -872,6 +875,36 @@ wpa_supplicant_add_network() {
                                        append network_data "client_cert=\"$client_cert\"" "$N$T"
                                        append network_data "private_key=\"$priv_key\"" "$N$T"
                                        append network_data "private_key_passwd=\"$priv_key_pwd\"" "$N$T"
+
+                                       json_get_vars subject_match
+                                       [ -n "$subject_match" ] && append network_data "subject_match=\"$subject_match\"" "$N$T"
+
+                                       json_get_values altsubject_match altsubject_match
+                                       if [ -n "$altsubject_match" ]; then
+                                               local list=
+                                               for x in $altsubject_match; do
+                                                       append list "$x" ";"
+                                               done
+                                               append network_data "altsubject_match=\"$list\"" "$N$T"
+                                       fi
+
+                                       json_get_values domain_match domain_match
+                                       if [ -n "$domain_match" ]; then
+                                               local list=
+                                               for x in $domain_match; do
+                                                       append list "$x" ";"
+                                               done
+                                               append network_data "domain_match=\"$list\"" "$N$T"
+                                       fi
+
+                                       json_get_values domain_suffix_match domain_suffix_match
+                                       if [ -n "$domain_suffix_match" ]; then
+                                               local list=
+                                               for x in $domain_suffix_match; do
+                                                       append list "$x" ";"
+                                               done
+                                               append network_data "domain_suffix_match=\"$list\"" "$N$T"
+                                       fi
                                ;;
                                fast|peap|ttls)
                                        json_get_vars auth password ca_cert2 client_cert2 priv_key2 priv_key2_pwd
@@ -887,6 +920,36 @@ wpa_supplicant_add_network() {
                                                append network_data "password=\"$password\"" "$N$T"
                                        fi
 
+                                       json_get_vars subject_match
+                                       [ -n "$subject_match" ] && append network_data "subject_match=\"$subject_match\"" "$N$T"
+
+                                       json_get_values altsubject_match altsubject_match
+                                       if [ -n "$altsubject_match" ]; then
+                                               local list=
+                                               for x in $altsubject_match; do
+                                                       append list "$x" ";"
+                                               done
+                                               append network_data "altsubject_match=\"$list\"" "$N$T"
+                                       fi
+
+                                       json_get_values domain_match domain_match
+                                       if [ -n "$domain_match" ]; then
+                                               local list=
+                                               for x in $domain_match; do
+                                                       append list "$x" ";"
+                                               done
+                                               append network_data "domain_match=\"$list\"" "$N$T"
+                                       fi
+
+                                       json_get_values domain_suffix_match domain_suffix_match
+                                       if [ -n "$domain_suffix_match" ]; then
+                                               local list=
+                                               for x in $domain_suffix_match; do
+                                                       append list "$x" ";"
+                                               done
+                                               append network_data "domain_suffix_match=\"$list\"" "$N$T"
+                                       fi
+
                                        phase2proto="auth="
                                        case "$auth" in
                                                "auth"*)
@@ -896,6 +959,35 @@ wpa_supplicant_add_network() {
                                                        auth="$(echo $auth | cut -b 5- )"
                                                        [ "$eap_type" = "ttls" ] &&
                                                                phase2proto="autheap="
+                                                       json_get_vars subject_match2
+                                                       [ -n "$subject_match2" ] && append network_data "subject_match2=\"$subject_match2\"" "$N$T"
+
+                                                       json_get_values altsubject_match2 altsubject_match2
+                                                       if [ -n "$altsubject_match2" ]; then
+                                                               local list=
+                                                               for x in $altsubject_match2; do
+                                                                       append list "$x" ";"
+                                                               done
+                                                               append network_data "altsubject_match2=\"$list\"" "$N$T"
+                                                       fi
+
+                                                       json_get_values domain_match2 domain_match2
+                                                       if [ -n "$domain_match2" ]; then
+                                                               local list=
+                                                               for x in $domain_match2; do
+                                                                       append list "$x" ";"
+                                                               done
+                                                               append network_data "domain_match2=\"$list\"" "$N$T"
+                                                       fi
+
+                                                       json_get_values domain_suffix_match2 domain_suffix_match2
+                                                       if [ -n "$domain_suffix_match2" ]; then
+                                                               local list=
+                                                               for x in $domain_suffix_match2; do
+                                                                       append list "$x" ";"
+                                                               done
+                                                               append network_data "domain_suffix_match2=\"$list\"" "$N$T"
+                                                       fi
                                                ;;
                                        esac
                                        append network_data "phase2=\"$phase2proto$auth\"" "$N$T"