diff options
author | Hai Shalom <haishalom@google.com> | 2019-02-06 16:32:13 -0800 |
---|---|---|
committer | Hai Shalom <haishalom@google.com> | 2019-02-11 18:36:10 +0000 |
commit | 39bc25d3a79c1375de430a7918d949c1a86f70c6 (patch) | |
tree | 28a9b8d123fef7d5e7cf2bfbd0c110f37756beda /hs20 | |
parent | e6747a12928845398fe963c9b522acb4aedc075b (diff) | |
download | wpa_supplicant_8-39bc25d3a79c1375de430a7918d949c1a86f70c6.tar.gz |
[wpa_supplicant] Cumulative patch from c4e90da6d
Bug: 124017368
Test: Device boots up and connects to WPA3/OWE wifi networks, run traffic.
Test: Able to turn on/off softap, associate wifi STA, run traffic.
Test: DPP functional test.
Test: Regression test passed (Bug: 124052942)
c4e90da6d MBO: Move the WNM-Notification subtype definitions to common location
105b14f54 HS 2.0: Update the T&C Acceptance subtype value
65b487ae5 HS 2.0: Add QUIET=1 support for building hs20-osu-client
73f285dad Add FT-PSK to GET_CAPABILITY key_mgmt
6110753b1 nl80211: Clear PMKID add command message buffer
0fa33e05b nl80211: Clear connect command message buffer
b14e8ea1d nl80211: Request kernel to trim off payload of netlink requests from acks
789b48bb4 EAP peer: Clear temporary message buffers before freeing
8f99a3c26 Clear config item writing buffer before freeing it
a68e9b698 D-Bus: Fix P2P DeleteService dict iteration
0607346f1 D-Bus: Fix a memory leak in DeleteService handler
d05dda61d PEAP: Explicitly clear temporary keys from memory when using CMK
4e1cd3468 EAP-PEAP: Derive EMSK and use 128-octet derivation for MSK
d8c20ec59 DPP: Clear dpp_listen_freq on remain-on-channel failure
59fa20538 P2P: Allow the avoid channels for P2P discovery/negotiation
e34cd9f06 WNM: Fix WNM-Sleep Mode Request bounds checking
159a7fbde crl_reload_interval: Add CRL reloading support
83c860813 AP: Add wpa_psk_file reloading in runtime
ec5c39a55 AP: Allow identifying which passphrase station used with wpa_psk_file
b08c9ad0c AP: Expose PMK outside of wpa_auth module
89896c000 tests: Use python3 compatible print statement
bab493b90 tests: Use python3 compatible "except" statement
0dab47733 Write multi_ap_backhaul_sta to wpa_supplicant config
98251c6f2 dbus: Document more possible BSS/RSA/KeyMgmt values
1e591df06 Check supported types in wpas_mac_addr_rand_scan_set()
c85249aa1 Fix test compilation error related to sme_event_unprot_disconnect()
42d308635 SAE: Advertise Password Identifier use
59c693064 HS 2.0 server: Command line option to fetch the version information
2d1762fa4 HS 2.0 server: Alternative subrem updateNode for certificate credentials
d97cf2a11 HS 2.0 server: Use noMOUpdate in client certificate subrem
13a200a92 FILS: Remove notes about experimental implementation
86d4e0537 dbus: Expose support of SAE key management in BSS properties
Change-Id: I83ffca34ff5349c226db6215ff1ae35c3b7ab335
Diffstat (limited to 'hs20')
-rw-r--r-- | hs20/client/.gitignore | 1 | ||||
-rw-r--r-- | hs20/client/Makefile | 5 | ||||
-rw-r--r-- | hs20/client/est.c | 13 | ||||
-rw-r--r-- | hs20/client/oma_dm_client.c | 6 | ||||
-rw-r--r-- | hs20/client/osu_client.c | 31 | ||||
-rw-r--r-- | hs20/server/Makefile | 10 | ||||
-rw-r--r-- | hs20/server/hs20-osu-server.txt | 5 | ||||
-rw-r--r-- | hs20/server/hs20_spp_server.c | 18 | ||||
-rw-r--r-- | hs20/server/spp_server.c | 630 | ||||
-rw-r--r-- | hs20/server/spp_server.h | 3 | ||||
-rw-r--r-- | hs20/server/sql.txt | 13 | ||||
-rw-r--r-- | hs20/server/www/est.php | 50 | ||||
-rw-r--r-- | hs20/server/www/spp.php | 34 | ||||
-rw-r--r-- | hs20/server/www/users.php | 14 |
14 files changed, 738 insertions, 95 deletions
diff --git a/hs20/client/.gitignore b/hs20/client/.gitignore new file mode 100644 index 00000000..d2fd60fb --- /dev/null +++ b/hs20/client/.gitignore @@ -0,0 +1 @@ +hs20-osu-client diff --git a/hs20/client/Makefile b/hs20/client/Makefile index fc9b6194..67f6f55c 100644 --- a/hs20/client/Makefile +++ b/hs20/client/Makefile @@ -8,12 +8,17 @@ ifndef LDO LDO=$(CC) endif +ifeq ($(QUIET), 1) +Q=@ +E=true +else Q=@ E=echo ifeq ($(V), 1) Q= E=true endif +endif ifndef CFLAGS CFLAGS = -MMD -O2 -Wall -g diff --git a/hs20/client/est.c b/hs20/client/est.c index b1aacb8f..db65334b 100644 --- a/hs20/client/est.c +++ b/hs20/client/est.c @@ -16,6 +16,7 @@ #include <openssl/asn1t.h> #include <openssl/x509.h> #include <openssl/x509v3.h> +#include <openssl/opensslv.h> #ifdef OPENSSL_IS_BORINGSSL #include <openssl/buf.h> #endif /* OPENSSL_IS_BORINGSSL */ @@ -219,6 +220,10 @@ typedef struct { } d; } AttrOrOID; +#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(OPENSSL_IS_BORINGSSL) +DEFINE_STACK_OF(AttrOrOID) +#endif + typedef struct { int type; STACK_OF(AttrOrOID) *attrs; @@ -352,9 +357,17 @@ static void add_csrattrs(struct hs20_osu_client *ctx, CsrAttrs *csrattrs, } } #else /* OPENSSL_IS_BORINGSSL */ +#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(OPENSSL_IS_BORINGSSL) + num = sk_AttrOrOID_num(csrattrs->attrs); +#else num = SKM_sk_num(AttrOrOID, csrattrs->attrs); +#endif for (i = 0; i < num; i++) { +#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(OPENSSL_IS_BORINGSSL) + AttrOrOID *ao = sk_AttrOrOID_value(csrattrs->attrs, i); +#else AttrOrOID *ao = SKM_sk_value(AttrOrOID, csrattrs->attrs, i); +#endif switch (ao->type) { case 0: add_csrattrs_oid(ctx, ao->d.oid, exts); diff --git a/hs20/client/oma_dm_client.c b/hs20/client/oma_dm_client.c index 5854b726..d75c8456 100644 --- a/hs20/client/oma_dm_client.c +++ b/hs20/client/oma_dm_client.c @@ -111,6 +111,12 @@ static xml_node_t * oma_dm_build_hdr(struct hs20_osu_client *ctx, xml_node_t *syncml, *synchdr; xml_namespace_t *ns; + if (!ctx->devid) { + wpa_printf(MSG_ERROR, + "DevId from devinfo.xml is not available - cannot use OMA DM"); + return NULL; + } + syncml = xml_node_create_root(ctx->xml, "SYNCML:SYNCML1.2", NULL, &ns, "SyncML"); diff --git a/hs20/client/osu_client.c b/hs20/client/osu_client.c index 9e1b0c72..b48903dc 100644 --- a/hs20/client/osu_client.c +++ b/hs20/client/osu_client.c @@ -117,8 +117,8 @@ static int android_update_permission(const char *path, mode_t mode) /* Allow processes running with Group ID as AID_WIFI, * to read files from SP, SP/<fqdn>, Cert and osu-info directories */ - if (chown(path, -1, AID_WIFI)) { - wpa_printf(MSG_INFO, "CTRL: Could not chown directory: %s", + if (lchown(path, -1, AID_WIFI)) { + wpa_printf(MSG_INFO, "CTRL: Could not lchown directory: %s", strerror(errno)); return -1; } @@ -3068,24 +3068,17 @@ static int init_ctx(struct hs20_osu_client *ctx) return -1; devinfo = node_from_file(ctx->xml, "devinfo.xml"); - if (!devinfo) { - wpa_printf(MSG_ERROR, "devinfo.xml not found"); - return -1; - } - - devid = get_node(ctx->xml, devinfo, "DevId"); - if (devid) { - char *tmp = xml_node_get_text(ctx->xml, devid); - if (tmp) { - ctx->devid = os_strdup(tmp); - xml_node_get_text_free(ctx->xml, tmp); + if (devinfo) { + devid = get_node(ctx->xml, devinfo, "DevId"); + if (devid) { + char *tmp = xml_node_get_text(ctx->xml, devid); + + if (tmp) { + ctx->devid = os_strdup(tmp); + xml_node_get_text_free(ctx->xml, tmp); + } } - } - xml_node_free(ctx->xml, devinfo); - - if (ctx->devid == NULL) { - wpa_printf(MSG_ERROR, "Could not fetch DevId from devinfo.xml"); - return -1; + xml_node_free(ctx->xml, devinfo); } ctx->http = http_init_ctx(ctx, ctx->xml); diff --git a/hs20/server/Makefile b/hs20/server/Makefile index 248ed5cc..9b737279 100644 --- a/hs20/server/Makefile +++ b/hs20/server/Makefile @@ -21,6 +21,16 @@ LIBS += -lsqlite3 # Using glibc < 2.17 requires -lrt for clock_gettime() LIBS += -lrt +ifndef CONFIG_NO_GITVER +# Add VERSION_STR postfix for builds from a git repository +ifeq ($(wildcard ../../.git),../../.git) +GITVER := $(shell git describe --dirty=+) +ifneq ($(GITVER),) +CFLAGS += -DGIT_VERSION_STR_POSTFIX=\"-$(GITVER)\" +endif +endif +endif + OBJS=spp_server.o OBJS += hs20_spp_server.o OBJS += ../../src/utils/xml-utils.o diff --git a/hs20/server/hs20-osu-server.txt b/hs20/server/hs20-osu-server.txt index 70f13135..22478ad9 100644 --- a/hs20/server/hs20-osu-server.txt +++ b/hs20/server/hs20-osu-server.txt @@ -228,12 +228,17 @@ Add following block just before "SSL Engine Switch" line": Options Indexes MultiViews FollowSymLinks AllowOverride None Require all granted + SSLOptions +StdEnvVars </Directory> Update SSL configuration to use the OSU server certificate/key. They keys and certs are called 'server.key' and 'server.pem' from ca/setup.sh. +To support subscription remediation using client certificates, set +"SSLVerifyClient optional" and configure the trust root CA(s) for the +client certificates with SSLCACertificateFile. + Enable default-ssl site and restart Apache2: sudo a2ensite default-ssl sudo a2enmod ssl diff --git a/hs20/server/hs20_spp_server.c b/hs20/server/hs20_spp_server.c index abd6867d..6c74f541 100644 --- a/hs20/server/hs20_spp_server.c +++ b/hs20/server/hs20_spp_server.c @@ -11,6 +11,7 @@ #include <sqlite3.h> #include "common.h" +#include "common/version.h" #include "xml-utils.h" #include "spp_server.h" @@ -89,6 +90,18 @@ static int process(struct hs20_svc *ctx) return -1; } + ctx->imsi = getenv("HS20IMSI"); + if (ctx->imsi) + debug_print(ctx, 1, "IMSI %s", ctx->imsi); + + ctx->eap_method = getenv("HS20EAPMETHOD"); + if (ctx->eap_method) + debug_print(ctx, 1, "EAP method %s", ctx->eap_method); + + ctx->id_hash = getenv("HS20IDHASH"); + if (ctx->id_hash) + debug_print(ctx, 1, "ID-HASH %s", ctx->id_hash); + soap = xml_node_from_buf(ctx->xml, post); if (soap == NULL) { debug_print(ctx, 1, "Could not parse SOAP data"); @@ -146,7 +159,7 @@ int main(int argc, char *argv[]) os_memset(&ctx, 0, sizeof(ctx)); for (;;) { - int c = getopt(argc, argv, "f:r:"); + int c = getopt(argc, argv, "f:r:v"); if (c < 0) break; switch (c) { @@ -162,6 +175,9 @@ int main(int argc, char *argv[]) case 'r': ctx.root_dir = optarg; break; + case 'v': + printf("hs20_spp_server v" VERSION_STR "\n"); + return 0; default: usage(); return -1; diff --git a/hs20/server/spp_server.c b/hs20/server/spp_server.c index e5af4c25..4bef0ffb 100644 --- a/hs20/server/spp_server.c +++ b/hs20/server/spp_server.c @@ -41,6 +41,8 @@ enum hs20_session_operation { POLICY_REMEDIATION, POLICY_UPDATE, FREE_REMEDIATION, + CLEAR_REMEDIATION, + CERT_REENROLL, }; @@ -51,6 +53,11 @@ static char * db_get_osu_config_val(struct hs20_svc *ctx, const char *realm, const char *field); static xml_node_t * build_policy(struct hs20_svc *ctx, const char *user, const char *realm, int use_dmacc); +static xml_node_t * spp_exec_get_certificate(struct hs20_svc *ctx, + const char *session_id, + const char *user, + const char *realm, + int add_est_user); static int db_add_session(struct hs20_svc *ctx, @@ -204,6 +211,61 @@ static void db_add_session_devdetail(struct hs20_svc *ctx, } +static void db_add_session_dmacc(struct hs20_svc *ctx, const char *sessionid, + const char *username, const char *password) +{ + char *sql; + + sql = sqlite3_mprintf("UPDATE sessions SET osu_user=%Q, osu_password=%Q WHERE id=%Q", + username, password, sessionid); + if (!sql) + return; + debug_print(ctx, 1, "DB: %s", sql); + if (sqlite3_exec(ctx->db, sql, NULL, NULL, NULL) != SQLITE_OK) { + debug_print(ctx, 1, "Failed to add session DMAcc: %s", + sqlite3_errmsg(ctx->db)); + } + sqlite3_free(sql); +} + + +static void db_add_session_eap_method(struct hs20_svc *ctx, + const char *sessionid, + const char *method) +{ + char *sql; + + sql = sqlite3_mprintf("UPDATE sessions SET eap_method=%Q WHERE id=%Q", + method, sessionid); + if (!sql) + return; + debug_print(ctx, 1, "DB: %s", sql); + if (sqlite3_exec(ctx->db, sql, NULL, NULL, NULL) != SQLITE_OK) { + debug_print(ctx, 1, "Failed to add session EAP method: %s", + sqlite3_errmsg(ctx->db)); + } + sqlite3_free(sql); +} + + +static void db_add_session_id_hash(struct hs20_svc *ctx, const char *sessionid, + const char *id_hash) +{ + char *sql; + + sql = sqlite3_mprintf("UPDATE sessions SET mobile_identifier_hash=%Q WHERE id=%Q", + id_hash, sessionid); + if (!sql) + return; + debug_print(ctx, 1, "DB: %s", sql); + if (sqlite3_exec(ctx->db, sql, NULL, NULL, NULL) != SQLITE_OK) { + debug_print(ctx, 1, "Failed to add session ID hash: %s", + sqlite3_errmsg(ctx->db)); + } + sqlite3_free(sql); +} + + static void db_remove_session(struct hs20_svc *ctx, const char *user, const char *realm, const char *sessionid) @@ -290,8 +352,7 @@ static void db_update_mo_str(struct hs20_svc *ctx, const char *user, char *sql; if (user == NULL || realm == NULL || name == NULL) return; - sql = sqlite3_mprintf("UPDATE users SET %s=%Q " - "WHERE identity=%Q AND realm=%Q AND phase2=1", + sql = sqlite3_mprintf("UPDATE users SET %s=%Q WHERE identity=%Q AND realm=%Q AND (phase2=1 OR methods='TLS')", name, str, user, realm); if (sql == NULL) return; @@ -413,8 +474,7 @@ static char * db_get_val(struct hs20_svc *ctx, const char *user, char *cmd; struct get_db_field_data data; - cmd = sqlite3_mprintf("SELECT %s FROM users WHERE " - "%s=%Q AND realm=%Q AND phase2=1", + cmd = sqlite3_mprintf("SELECT %s FROM users WHERE %s=%Q AND realm=%Q AND (phase2=1 OR methods='TLS')", field, dmacc ? "osu_user" : "identity", user, realm); if (cmd == NULL) @@ -443,8 +503,7 @@ static int db_update_val(struct hs20_svc *ctx, const char *user, char *cmd; int ret; - cmd = sqlite3_mprintf("UPDATE users SET %s=%Q WHERE " - "%s=%Q AND realm=%Q AND phase2=1", + cmd = sqlite3_mprintf("UPDATE users SET %s=%Q WHERE %s=%Q AND realm=%Q AND (phase2=1 OR methods='TLS')", field, val, dmacc ? "osu_user" : "identity", user, realm); if (cmd == NULL) @@ -524,6 +583,27 @@ static int update_password(struct hs20_svc *ctx, const char *user, } +static int clear_remediation(struct hs20_svc *ctx, const char *user, + const char *realm, int dmacc) +{ + char *cmd; + + cmd = sqlite3_mprintf("UPDATE users SET remediation='' WHERE %s=%Q", + dmacc ? "osu_user" : "identity", + user); + if (cmd == NULL) + return -1; + debug_print(ctx, 1, "DB: %s", cmd); + if (sqlite3_exec(ctx->db, cmd, NULL, NULL, NULL) != SQLITE_OK) { + debug_print(ctx, 1, "Failed to update database for user '%s'", + user); + } + sqlite3_free(cmd); + + return 0; +} + + static int add_eap_ttls(struct hs20_svc *ctx, xml_node_t *parent) { xml_node_t *node; @@ -545,6 +625,7 @@ static xml_node_t * build_username_password(struct hs20_svc *ctx, { xml_node_t *node; char *b64; + size_t len; node = xml_node_create(ctx->xml, parent, NULL, "UsernamePassword"); if (node == NULL) @@ -555,6 +636,9 @@ static xml_node_t * build_username_password(struct hs20_svc *ctx, b64 = (char *) base64_encode((unsigned char *) pw, strlen(pw), NULL); if (b64 == NULL) return NULL; + len = os_strlen(b64); + if (len > 0 && b64[len - 1] == '\n') + b64[len - 1] = '\0'; add_text_node(ctx, node, "Password", b64); free(b64); @@ -706,6 +790,45 @@ static int add_update_node(struct hs20_svc *ctx, xml_node_t *spp_node, } +static xml_node_t * read_subrem_file(struct hs20_svc *ctx, + const char *subrem_id, + char *uri, size_t uri_size) +{ + char fname[200]; + char *buf, *buf2, *pos; + size_t len; + xml_node_t *node; + + os_snprintf(fname, sizeof(fname), "%s/spp/subrem/%s", + ctx->root_dir, subrem_id); + debug_print(ctx, 1, "Use subrem file %s", fname); + + buf = os_readfile(fname, &len); + if (!buf) + return NULL; + buf2 = os_realloc(buf, len + 1); + if (!buf2) { + os_free(buf); + return NULL; + } + buf = buf2; + buf[len] = '\0'; + + pos = os_strchr(buf, '\n'); + if (!pos) { + os_free(buf); + return NULL; + } + *pos++ = '\0'; + os_strlcpy(uri, buf, uri_size); + + node = xml_node_from_buf(ctx->xml, pos); + os_free(buf); + + return node; +} + + static xml_node_t * build_sub_rem_resp(struct hs20_svc *ctx, const char *user, const char *realm, const char *session_id, @@ -715,28 +838,48 @@ static xml_node_t * build_sub_rem_resp(struct hs20_svc *ctx, xml_node_t *spp_node, *cred; char buf[400]; char new_pw[33]; - char *real_user = NULL; char *status; char *cert; - if (dmacc) { - real_user = db_get_val(ctx, user, realm, "identity", dmacc); - if (real_user == NULL) { - debug_print(ctx, 1, "Could not find user identity for " - "dmacc user '%s'", user); - return NULL; - } - } - cert = db_get_val(ctx, user, realm, "cert", dmacc); - if (cert && cert[0] == '\0') + if (cert && cert[0] == '\0') { + os_free(cert); cert = NULL; + } if (cert) { - cred = build_credential_cert(ctx, real_user ? real_user : user, - realm, cert); + char *subrem; + + /* No change needed in PPS MO unless specifically asked to */ + cred = NULL; + buf[0] = '\0'; + + subrem = db_get_val(ctx, user, realm, "subrem", dmacc); + if (subrem && subrem[0]) { + cred = read_subrem_file(ctx, subrem, buf, sizeof(buf)); + if (!cred) { + debug_print(ctx, 1, + "Could not create updateNode from subrem file"); + os_free(subrem); + os_free(cert); + return NULL; + } + } + os_free(subrem); } else { + char *real_user = NULL; char *pw; + if (dmacc) { + real_user = db_get_val(ctx, user, realm, "identity", + dmacc); + if (!real_user) { + debug_print(ctx, 1, + "Could not find user identity for dmacc user '%s'", + user); + return NULL; + } + } + pw = db_get_session_val(ctx, user, realm, session_id, "password"); if (pw && pw[0]) { @@ -752,11 +895,17 @@ static xml_node_t * build_sub_rem_resp(struct hs20_svc *ctx, real_user ? real_user : user, realm, new_pw, sizeof(new_pw)); } - } - free(real_user); - if (!cred) { - debug_print(ctx, 1, "Could not build credential"); - return NULL; + + free(real_user); + if (!cred) { + debug_print(ctx, 1, "Could not build credential"); + os_free(cert); + return NULL; + } + + snprintf(buf, sizeof(buf), + "./Wi-Fi/%s/PerProviderSubscription/Cred01/Credential", + realm); } status = "Remediation complete, request sppUpdateResponse"; @@ -764,16 +913,15 @@ static xml_node_t * build_sub_rem_resp(struct hs20_svc *ctx, NULL); if (spp_node == NULL) { debug_print(ctx, 1, "Could not build sppPostDevDataResponse"); + os_free(cert); return NULL; } - snprintf(buf, sizeof(buf), - "./Wi-Fi/%s/PerProviderSubscription/Cred01/Credential", - realm); - - if (add_update_node(ctx, spp_node, ns, buf, cred) < 0) { + if ((cred && add_update_node(ctx, spp_node, ns, buf, cred) < 0) || + (!cred && !xml_node_create(ctx->xml, spp_node, ns, "noMOUpdate"))) { debug_print(ctx, 1, "Could not add update node"); xml_node_free(ctx->xml, spp_node); + os_free(cert); return NULL; } @@ -783,14 +931,16 @@ static xml_node_t * build_sub_rem_resp(struct hs20_svc *ctx, xml_node_free(ctx->xml, cred); if (cert) { - debug_print(ctx, 1, "Certificate credential - no need for DB " - "password update on success notification"); + debug_print(ctx, 1, "Request DB remediation clearing on success notification (certificate credential)"); + db_add_session(ctx, user, realm, session_id, NULL, NULL, + CLEAR_REMEDIATION, NULL); } else { debug_print(ctx, 1, "Request DB password update on success " "notification"); db_add_session(ctx, user, realm, session_id, new_pw, NULL, UPDATE_PASSWORD, NULL); } + os_free(cert); return spp_node; } @@ -805,6 +955,17 @@ static xml_node_t * machine_remediation(struct hs20_svc *ctx, } +static xml_node_t * cert_reenroll(struct hs20_svc *ctx, + const char *user, + const char *realm, + const char *session_id) +{ + db_add_session(ctx, user, realm, session_id, NULL, NULL, + CERT_REENROLL, NULL); + return spp_exec_get_certificate(ctx, session_id, user, realm, 0); +} + + static xml_node_t * policy_remediation(struct hs20_svc *ctx, const char *user, const char *realm, const char *session_id, int dmacc) @@ -989,6 +1150,8 @@ static xml_node_t * hs20_subscription_remediation(struct hs20_svc *ctx, ret = policy_remediation(ctx, user, realm, session_id, dmacc); else if (type && strcmp(type, "machine") == 0) ret = machine_remediation(ctx, user, realm, session_id, dmacc); + else if (type && strcmp(type, "reenroll") == 0) + ret = cert_reenroll(ctx, user, realm, session_id); else ret = no_sub_rem(ctx, user, realm, session_id); free(type); @@ -997,11 +1160,41 @@ static xml_node_t * hs20_subscription_remediation(struct hs20_svc *ctx, } +static xml_node_t * read_policy_file(struct hs20_svc *ctx, + const char *policy_id) +{ + char fname[200]; + + snprintf(fname, sizeof(fname), "%s/spp/policy/%s.xml", + ctx->root_dir, policy_id); + debug_print(ctx, 1, "Use policy file %s", fname); + + return node_from_file(ctx->xml, fname); +} + + +static void update_policy_update_uri(struct hs20_svc *ctx, const char *realm, + xml_node_t *policy) +{ + xml_node_t *node; + char *url; + + node = get_node_uri(ctx->xml, policy, "Policy/PolicyUpdate/URI"); + if (!node) + return; + + url = db_get_osu_config_val(ctx, realm, "policy_url"); + if (!url) + return; + xml_node_set_text(ctx->xml, node, url); + free(url); +} + + static xml_node_t * build_policy(struct hs20_svc *ctx, const char *user, const char *realm, int use_dmacc) { char *policy_id; - char fname[200]; xml_node_t *policy, *node; policy_id = db_get_val(ctx, user, realm, "policy", use_dmacc); @@ -1011,27 +1204,12 @@ static xml_node_t * build_policy(struct hs20_svc *ctx, const char *user, if (policy_id == NULL) return NULL; } - - snprintf(fname, sizeof(fname), "%s/spp/policy/%s.xml", - ctx->root_dir, policy_id); + policy = read_policy_file(ctx, policy_id); free(policy_id); - debug_print(ctx, 1, "Use policy file %s", fname); - - policy = node_from_file(ctx->xml, fname); if (policy == NULL) return NULL; - node = get_node_uri(ctx->xml, policy, "Policy/PolicyUpdate/URI"); - if (node) { - char *url; - url = db_get_osu_config_val(ctx, realm, "policy_url"); - if (url == NULL) { - xml_node_free(ctx->xml, policy); - return NULL; - } - xml_node_set_text(ctx->xml, node, url); - free(url); - } + update_policy_update_uri(ctx, realm, policy); node = get_node_uri(ctx->xml, policy, "Policy/PolicyUpdate"); if (node && use_dmacc) { @@ -1264,15 +1442,20 @@ static char * db_get_osu_config_val(struct hs20_svc *ctx, const char *realm, static xml_node_t * build_pps(struct hs20_svc *ctx, const char *user, const char *realm, const char *pw, const char *cert, - int machine_managed, const char *test) + int machine_managed, const char *test, + const char *imsi, const char *dmacc_username, + const char *dmacc_password, + xml_node_t *policy_node) { xml_node_t *pps, *c, *trust, *aaa, *aaa1, *upd, *homesp, *p; xml_node_t *cred, *eap, *userpw; pps = xml_node_create_root(ctx->xml, NULL, NULL, NULL, "PerProviderSubscription"); - if (pps == NULL) + if (!pps) { + xml_node_free(ctx->xml, policy_node); return NULL; + } add_text_node(ctx, pps, "UpdateIdentifier", "1"); @@ -1280,6 +1463,8 @@ static xml_node_t * build_pps(struct hs20_svc *ctx, add_text_node(ctx, c, "CredentialPriority", "1"); + if (imsi) + goto skip_aaa_trust_root; aaa = xml_node_create(ctx->xml, c, NULL, "AAAServerTrustRoot"); aaa1 = xml_node_create(ctx->xml, aaa, NULL, "AAA1"); add_text_node_conf(ctx, realm, aaa1, "CertURL", @@ -1311,6 +1496,7 @@ static xml_node_t * build_pps(struct hs20_svc *ctx, "CertSHA256Fingerprint", "policy_trust_root_cert_fingerprint"); } +skip_aaa_trust_root: upd = xml_node_create(ctx->xml, c, NULL, "SubscriptionUpdate"); add_text_node(ctx, upd, "UpdateInterval", "4294967295"); @@ -1330,6 +1516,17 @@ static xml_node_t * build_pps(struct hs20_svc *ctx, "trust_root_cert_fingerprint"); } + if (dmacc_username && + !build_username_password(ctx, upd, dmacc_username, + dmacc_password)) { + xml_node_free(ctx->xml, pps); + xml_node_free(ctx->xml, policy_node); + return NULL; + } + + if (policy_node) + xml_node_add_child(ctx->xml, c, policy_node); + homesp = xml_node_create(ctx->xml, c, NULL, "HomeSP"); add_text_node_conf(ctx, realm, homesp, "FriendlyName", "friendly_name"); add_text_node_conf(ctx, realm, homesp, "FQDN", "fqdn"); @@ -1338,7 +1535,19 @@ static xml_node_t * build_pps(struct hs20_svc *ctx, cred = xml_node_create(ctx->xml, c, NULL, "Credential"); add_creation_date(ctx, cred); - if (cert) { + if (imsi) { + xml_node_t *sim; + const char *type = "18"; /* default to EAP-SIM */ + + sim = xml_node_create(ctx->xml, cred, NULL, "SIM"); + add_text_node(ctx, sim, "IMSI", imsi); + if (ctx->eap_method && os_strcmp(ctx->eap_method, "AKA") == 0) + type = "23"; + else if (ctx->eap_method && + os_strcmp(ctx->eap_method, "AKA'") == 0) + type = "50"; + add_text_node(ctx, sim, "EAPType", type); + } else if (cert) { xml_node_t *dc; dc = xml_node_create(ctx->xml, cred, NULL, "DigitalCertificate"); @@ -1361,7 +1570,8 @@ static xml_node_t * build_pps(struct hs20_svc *ctx, static xml_node_t * spp_exec_get_certificate(struct hs20_svc *ctx, const char *session_id, const char *user, - const char *realm) + const char *realm, + int add_est_user) { xml_namespace_t *ns; xml_node_t *spp_node, *enroll, *exec_node; @@ -1369,7 +1579,7 @@ static xml_node_t * spp_exec_get_certificate(struct hs20_svc *ctx, char password[11]; char *b64; - if (new_password(password, sizeof(password)) < 0) + if (add_est_user && new_password(password, sizeof(password)) < 0) return NULL; spp_node = build_post_dev_data_response(ctx, &ns, session_id, "OK", @@ -1386,6 +1596,10 @@ static xml_node_t * spp_exec_get_certificate(struct hs20_svc *ctx, xml_node_create_text(ctx->xml, enroll, ns, "enrollmentServerURI", val ? val : ""); os_free(val); + + if (!add_est_user) + return spp_node; + xml_node_create_text(ctx->xml, enroll, ns, "estUserID", user); b64 = (char *) base64_encode((unsigned char *) password, @@ -1445,7 +1659,7 @@ static xml_node_t * hs20_user_input_registration(struct hs20_svc *ctx, xml_node_t *ret; hs20_eventlog(ctx, user, realm, session_id, "request client certificate enrollment", NULL); - ret = spp_exec_get_certificate(ctx, session_id, user, realm); + ret = spp_exec_get_certificate(ctx, session_id, user, realm, 1); free(user); free(realm); free(pw); @@ -1477,7 +1691,7 @@ static xml_node_t * hs20_user_input_registration(struct hs20_svc *ctx, test); pps = build_pps(ctx, user, realm, pw, fingerprint ? fingerprint : NULL, machine_managed, - test); + test, NULL, NULL, NULL, NULL); free(fingerprint); free(test); if (!pps) { @@ -1618,6 +1832,72 @@ static xml_node_t * hs20_user_input_complete(struct hs20_svc *ctx, } +static xml_node_t * hs20_cert_reenroll_complete(struct hs20_svc *ctx, + const char *session_id) +{ + char *user, *realm, *cert; + char *status; + xml_namespace_t *ns; + xml_node_t *spp_node, *cred; + char buf[400]; + + user = db_get_session_val(ctx, NULL, NULL, session_id, "user"); + realm = db_get_session_val(ctx, NULL, NULL, session_id, "realm"); + cert = db_get_session_val(ctx, NULL, NULL, session_id, "cert"); + if (!user || !realm || !cert) { + debug_print(ctx, 1, + "Could not find session info from DB for certificate reenrollment"); + free(user); + free(realm); + free(cert); + return NULL; + } + + cred = build_credential_cert(ctx, user, realm, cert); + if (!cred) { + debug_print(ctx, 1, "Could not build credential"); + free(user); + free(realm); + free(cert); + return NULL; + } + + status = "Remediation complete, request sppUpdateResponse"; + spp_node = build_post_dev_data_response(ctx, &ns, session_id, status, + NULL); + if (spp_node == NULL) { + debug_print(ctx, 1, "Could not build sppPostDevDataResponse"); + free(user); + free(realm); + free(cert); + xml_node_free(ctx->xml, cred); + return NULL; + } + + snprintf(buf, sizeof(buf), + "./Wi-Fi/%s/PerProviderSubscription/Cred01/Credential", + realm); + + if (add_update_node(ctx, spp_node, ns, buf, cred) < 0) { + debug_print(ctx, 1, "Could not add update node"); + xml_node_free(ctx->xml, spp_node); + free(user); + free(realm); + free(cert); + return NULL; + } + + hs20_eventlog_node(ctx, user, realm, session_id, + "certificate reenrollment", cred); + xml_node_free(ctx->xml, cred); + + free(user); + free(realm); + free(cert); + return spp_node; +} + + static xml_node_t * hs20_cert_enroll_completed(struct hs20_svc *ctx, const char *user, const char *realm, int dmacc, @@ -1626,7 +1906,7 @@ static xml_node_t * hs20_cert_enroll_completed(struct hs20_svc *ctx, char *val; enum hs20_session_operation oper; - val = db_get_session_val(ctx, user, realm, session_id, "operation"); + val = db_get_session_val(ctx, NULL, NULL, session_id, "operation"); if (val == NULL) { debug_print(ctx, 1, "No session %s found to continue", session_id); @@ -1637,6 +1917,8 @@ static xml_node_t * hs20_cert_enroll_completed(struct hs20_svc *ctx, if (oper == SUBSCRIPTION_REGISTRATION) return hs20_user_input_registration(ctx, session_id, 1); + if (oper == CERT_REENROLL) + return hs20_cert_reenroll_complete(ctx, session_id); debug_print(ctx, 1, "User session %s not in state for certificate " "enrollment completion", session_id); @@ -1684,6 +1966,103 @@ static xml_node_t * hs20_cert_enroll_failed(struct hs20_svc *ctx, } +static xml_node_t * hs20_sim_provisioning(struct hs20_svc *ctx, + const char *user, + const char *realm, int dmacc, + const char *session_id) +{ + xml_namespace_t *ns; + xml_node_t *spp_node, *node = NULL; + xml_node_t *pps, *tnds; + char buf[400]; + char *str; + const char *status; + char dmacc_username[32]; + char dmacc_password[32]; + char *policy; + xml_node_t *policy_node = NULL; + + if (!ctx->imsi) { + debug_print(ctx, 1, "IMSI not available for SIM provisioning"); + return NULL; + } + + if (new_password(dmacc_username, sizeof(dmacc_username)) < 0 || + new_password(dmacc_password, sizeof(dmacc_password)) < 0) { + debug_print(ctx, 1, + "Failed to generate DMAcc username/password"); + return NULL; + } + + status = "Provisioning complete, request sppUpdateResponse"; + spp_node = build_post_dev_data_response(ctx, &ns, session_id, status, + NULL); + if (!spp_node) + return NULL; + + policy = db_get_osu_config_val(ctx, realm, "sim_policy"); + if (policy) { + policy_node = read_policy_file(ctx, policy); + os_free(policy); + if (!policy_node) { + xml_node_free(ctx->xml, spp_node); + return NULL; + } + update_policy_update_uri(ctx, realm, policy_node); + node = get_node_uri(ctx->xml, policy_node, + "Policy/PolicyUpdate"); + if (node) + build_username_password(ctx, node, dmacc_username, + dmacc_password); + } + + pps = build_pps(ctx, NULL, realm, NULL, NULL, 0, NULL, ctx->imsi, + dmacc_username, dmacc_password, policy_node); + if (!pps) { + xml_node_free(ctx->xml, spp_node); + return NULL; + } + + debug_print(ctx, 1, + "Request DB subscription registration on success notification"); + if (!user || !user[0]) + user = ctx->imsi; + db_add_session(ctx, user, realm, session_id, NULL, NULL, + SUBSCRIPTION_REGISTRATION, NULL); + db_add_session_dmacc(ctx, session_id, dmacc_username, dmacc_password); + if (ctx->eap_method) + db_add_session_eap_method(ctx, session_id, ctx->eap_method); + if (ctx->id_hash) + db_add_session_id_hash(ctx, session_id, ctx->id_hash); + db_add_session_pps(ctx, user, realm, session_id, pps); + + hs20_eventlog_node(ctx, user, realm, session_id, + "new subscription", pps); + + tnds = mo_to_tnds(ctx->xml, pps, 0, URN_HS20_PPS, NULL); + xml_node_free(ctx->xml, pps); + if (!tnds) { + xml_node_free(ctx->xml, spp_node); + return NULL; + } + + str = xml_node_to_str(ctx->xml, tnds); + xml_node_free(ctx->xml, tnds); + if (!str) { + xml_node_free(ctx->xml, spp_node); + return NULL; + } + + node = xml_node_create_text(ctx->xml, spp_node, ns, "addMO", str); + free(str); + snprintf(buf, sizeof(buf), "./Wi-Fi/%s/PerProviderSubscription", realm); + xml_node_add_attr(ctx->xml, node, ns, "managementTreeURI", buf); + xml_node_add_attr(ctx->xml, node, ns, "moURN", URN_HS20_PPS); + + return spp_node; +} + + static xml_node_t * hs20_spp_post_dev_data(struct hs20_svc *ctx, xml_node_t *node, const char *user, @@ -1964,6 +2343,15 @@ static xml_node_t * hs20_spp_post_dev_data(struct hs20_svc *ctx, goto out; } + if (strcasecmp(req_reason, "Subscription provisioning") == 0) { + ret = hs20_sim_provisioning(ctx, user, realm, dmacc, + session_id); + hs20_eventlog_node(ctx, user, realm, session_id, + "subscription provisioning response", + ret); + goto out; + } + debug_print(ctx, 1, "Unsupported requestReason '%s' user '%s'", req_reason, user); out: @@ -2006,6 +2394,8 @@ static xml_node_t * build_spp_exchange_complete(struct hs20_svc *ctx, static int add_subscription(struct hs20_svc *ctx, const char *session_id) { char *user, *realm, *pw, *pw_mm, *pps, *str; + char *osu_user, *osu_password, *eap_method; + char *policy = NULL; char *sql; int ret = -1; char *free_account; @@ -2013,6 +2403,7 @@ static int add_subscription(struct hs20_svc *ctx, const char *session_id) char *type; int cert = 0; char *cert_pem, *fingerprint; + const char *method; user = db_get_session_val(ctx, NULL, NULL, session_id, "user"); realm = db_get_session_val(ctx, NULL, NULL, session_id, "realm"); @@ -2026,6 +2417,11 @@ static int add_subscription(struct hs20_svc *ctx, const char *session_id) if (type && strcmp(type, "cert") == 0) cert = 1; free(type); + osu_user = db_get_session_val(ctx, NULL, NULL, session_id, "osu_user"); + osu_password = db_get_session_val(ctx, NULL, NULL, session_id, + "osu_password"); + eap_method = db_get_session_val(ctx, NULL, NULL, session_id, + "eap_method"); if (!user || !realm || !pw) { debug_print(ctx, 1, "Could not find session info from DB for " @@ -2037,6 +2433,8 @@ static int add_subscription(struct hs20_svc *ctx, const char *session_id) free_acc = free_account && strcmp(free_account, user) == 0; free(free_account); + policy = db_get_osu_config_val(ctx, realm, "sim_policy"); + debug_print(ctx, 1, "New subscription: user='%s' realm='%s' free_acc=%d", user, realm, free_acc); @@ -2065,12 +2463,20 @@ static int add_subscription(struct hs20_svc *ctx, const char *session_id) str = db_get_session_val(ctx, NULL, NULL, session_id, "mac_addr"); - sql = sqlite3_mprintf("INSERT INTO users(identity,realm,phase2,methods,cert,cert_pem,machine_managed,mac_addr) VALUES (%Q,%Q,1,%Q,%Q,%Q,%d,%Q)", - user, realm, cert ? "TLS" : "TTLS-MSCHAPV2", + if (eap_method && eap_method[0]) + method = eap_method; + else + method = cert ? "TLS" : "TTLS-MSCHAPV2"; + sql = sqlite3_mprintf("INSERT INTO users(identity,realm,phase2,methods,cert,cert_pem,machine_managed,mac_addr,osu_user,osu_password,policy) VALUES (%Q,%Q,%d,%Q,%Q,%Q,%d,%Q,%Q,%Q,%Q)", + user, realm, cert ? 0 : 1, + method, fingerprint ? fingerprint : "", cert_pem ? cert_pem : "", pw_mm && atoi(pw_mm) ? 1 : 0, - str ? str : ""); + str ? str : "", + osu_user ? osu_user : "", + osu_password ? osu_password : "", + policy ? policy : ""); free(str); if (sql == NULL) goto out; @@ -2088,8 +2494,7 @@ static int add_subscription(struct hs20_svc *ctx, const char *session_id) else ret = update_password(ctx, user, realm, pw, 0); if (ret < 0) { - sql = sqlite3_mprintf("DELETE FROM users WHERE identity=%Q AND " - "realm=%Q AND phase2=1", + sql = sqlite3_mprintf("DELETE FROM users WHERE identity=%Q AND realm=%Q AND (phase2=1 OR methods='TLS')", user, realm); if (sql) { debug_print(ctx, 1, "DB: %s", sql); @@ -2139,6 +2544,24 @@ static int add_subscription(struct hs20_svc *ctx, const char *session_id) } } + str = db_get_session_val(ctx, NULL, NULL, session_id, + "mobile_identifier_hash"); + if (str) { + sql = sqlite3_mprintf("DELETE FROM sim_provisioning WHERE mobile_identifier_hash=%Q", + str); + if (sql) { + debug_print(ctx, 1, "DB: %s", sql); + if (sqlite3_exec(ctx->db, sql, NULL, NULL, NULL) != + SQLITE_OK) { + debug_print(ctx, 1, + "Failed to delete pending sim_provisioning entry: %s", + sqlite3_errmsg(ctx->db)); + } + sqlite3_free(sql); + } + os_free(str); + } + if (ret == 0) { hs20_eventlog(ctx, user, realm, session_id, "completed subscription registration", NULL); @@ -2152,6 +2575,10 @@ out: free(pps); free(cert_pem); free(fingerprint); + free(osu_user); + free(osu_password); + free(eap_method); + os_free(policy); return ret; } @@ -2178,11 +2605,11 @@ static xml_node_t * hs20_spp_update_response(struct hs20_svc *ctx, debug_print(ctx, 1, "sppUpdateResponse: sppStatus: %s sessionID: %s", status, session_id); - val = db_get_session_val(ctx, user, realm, session_id, "operation"); + val = db_get_session_val(ctx, NULL, NULL, session_id, "operation"); if (!val) { debug_print(ctx, 1, - "No session active for user: %s sessionID: %s", - user, session_id); + "No session active for sessionID: %s", + session_id); oper = NO_OPERATION; } else oper = atoi(val); @@ -2239,6 +2666,29 @@ static xml_node_t * hs20_spp_update_response(struct hs20_svc *ctx, session_id, "Updated user password " "in database", NULL); } + if (oper == CLEAR_REMEDIATION) { + debug_print(ctx, 1, + "Clear remediation requirement for user '%s' in DB", + user); + if (clear_remediation(ctx, user, realm, dmacc) < 0) { + debug_print(ctx, 1, + "Failed to clear remediation requirement for user '%s' in DB", + user); + ret = build_spp_exchange_complete( + ctx, session_id, "Error occurred", + "Other"); + hs20_eventlog_node(ctx, user, realm, + session_id, + "Failed to update database", + ret); + db_remove_session(ctx, user, realm, session_id); + return ret; + } + hs20_eventlog(ctx, user, realm, + session_id, + "Cleared remediation requirement in database", + NULL); + } if (oper == SUBSCRIPTION_REGISTRATION) { if (add_subscription(ctx, session_id) < 0) { debug_print(ctx, 1, "Failed to add " @@ -2265,12 +2715,60 @@ static xml_node_t * hs20_spp_update_response(struct hs20_svc *ctx, if (oper == POLICY_UPDATE) db_update_val(ctx, user, realm, "polupd_done", "1", dmacc); + if (oper == CERT_REENROLL) { + char *new_user; + char event[200]; + + new_user = db_get_session_val(ctx, NULL, NULL, + session_id, "user"); + if (!new_user) { + debug_print(ctx, 1, + "Failed to find new user name (cert-serialnum)"); + ret = build_spp_exchange_complete( + ctx, session_id, "Error occurred", + "Other"); + hs20_eventlog_node(ctx, user, realm, + session_id, + "Failed to find new user name (cert reenroll)", + ret); + db_remove_session(ctx, NULL, NULL, session_id); + return ret; + } + + debug_print(ctx, 1, + "Update certificate user entry to use the new serial number (old=%s new=%s)", + user, new_user); + os_snprintf(event, sizeof(event), "renamed user to: %s", + new_user); + hs20_eventlog(ctx, user, realm, session_id, event, + NULL); + + if (db_update_val(ctx, user, realm, "identity", + new_user, 0) < 0 || + db_update_val(ctx, new_user, realm, "remediation", + "", 0) < 0) { + debug_print(ctx, 1, + "Failed to update user name (cert-serialnum)"); + ret = build_spp_exchange_complete( + ctx, session_id, "Error occurred", + "Other"); + hs20_eventlog_node(ctx, user, realm, + session_id, + "Failed to update user name (cert reenroll)", + ret); + db_remove_session(ctx, NULL, NULL, session_id); + os_free(new_user); + return ret; + } + + os_free(new_user); + } ret = build_spp_exchange_complete( ctx, session_id, "Exchange complete, release TLS connection", NULL); hs20_eventlog_node(ctx, user, realm, session_id, "Exchange completed", ret); - db_remove_session(ctx, user, realm, session_id); + db_remove_session(ctx, NULL, NULL, session_id); return ret; } diff --git a/hs20/server/spp_server.h b/hs20/server/spp_server.h index 3556f5c9..421974c6 100644 --- a/hs20/server/spp_server.h +++ b/hs20/server/spp_server.h @@ -17,6 +17,9 @@ struct hs20_svc { sqlite3 *db; const char *addr; const char *test; + const char *imsi; + const char *eap_method; + const char *id_hash; }; diff --git a/hs20/server/sql.txt b/hs20/server/sql.txt index 666ef131..2cc6edea 100644 --- a/hs20/server/sql.txt +++ b/hs20/server/sql.txt @@ -24,6 +24,10 @@ CREATE TABLE sessions( cert TEXT, cert_pem TEXT, mac_addr TEXT, + osu_user TEXT, + osu_password TEXT, + eap_method TEXT, + mobile_identifier_hash TEXT, test TEXT ); @@ -57,6 +61,7 @@ CREATE TABLE users( mac_addr TEXT, last_msk TEXT, polupd_done TEXT, + subrem TEXT ); CREATE TABLE wildcards( @@ -93,3 +98,11 @@ CREATE TABLE cert_enroll( realm TEXT, serialnum TEXT ); + +CREATE TABLE sim_provisioning( + mobile_identifier_hash TEXT PRIMARY KEY, + imsi TEXT, + mac_addr TEXT, + eap_method TEXT, + timestamp TEXT +); diff --git a/hs20/server/www/est.php b/hs20/server/www/est.php index 6983ec9e..b7fb260d 100644 --- a/hs20/server/www/est.php +++ b/hs20/server/www/est.php @@ -10,6 +10,12 @@ $method = $_SERVER["REQUEST_METHOD"]; unset($user); unset($rowid); +$db = new PDO($osu_db); +if (!$db) { + error_log("EST: Could not access database"); + die("Could not access database"); +} + if (!empty($_SERVER['PHP_AUTH_DIGEST'])) { $needed = array('nonce'=>1, 'nc'=>1, 'cnonce'=>1, 'qop'=>1, 'username'=>1, 'uri'=>1, 'response'=>1); @@ -31,12 +37,6 @@ if (!empty($_SERVER['PHP_AUTH_DIGEST'])) { die('Authentication failed'); } - $db = new PDO($osu_db); - if (!$db) { - error_log("EST: Could not access database"); - die("Could not access database"); - } - $sql = "SELECT rowid,password,operation FROM sessions " . "WHERE user='$user' AND realm='$realm'"; $q = $db->query($sql); @@ -70,6 +70,29 @@ if (!empty($_SERVER['PHP_AUTH_DIGEST'])) { error_log("EST: Incorrect authentication response for user=$user realm=$realm"); die('Authentication failed'); } +} else if (isset($_SERVER["SSL_CLIENT_VERIFY"]) && + $_SERVER["SSL_CLIENT_VERIFY"] == "SUCCESS" && + isset($_SERVER["SSL_CLIENT_M_SERIAL"])) { + $user = "cert-" . $_SERVER["SSL_CLIENT_M_SERIAL"]; + $sql = "SELECT rowid,password,operation FROM sessions " . + "WHERE user='$user' AND realm='$realm'"; + $q = $db->query($sql); + if (!$q) { + error_log("EST: Session not found for user=$user realm=$realm"); + die("Session not found"); + } + $row = $q->fetch(); + if (!$row) { + error_log("EST: Session fetch failed for user=$user realm=$realm"); + die('Session not found'); + } + $rowid = $row['rowid']; + + $oper = $row['operation']; + if ($oper != '10') { + error_log("EST: Unexpected operation $oper for user=$user realm=$realm"); + die("Session not found"); + } } @@ -92,14 +115,24 @@ if ($method == "GET" && $cmd == "cacerts") { header("Content-Type: application/csrattrs"); readfile("$osu_root/est/est-attrs.b64"); error_log("EST: csrattrs"); -} else if ($method == "POST" && $cmd == "simpleenroll") { - if (!isset($user) || strlen($user) == 0) { +} else if ($method == "POST" && + ($cmd == "simpleenroll" || $cmd == "simplereenroll")) { + $reenroll = $cmd == "simplereenroll"; + if (!$reenroll && (!isset($user) || strlen($user) == 0)) { header('HTTP/1.1 401 Unauthorized'); header('WWW-Authenticate: Digest realm="'.$realm. '",qop="auth",nonce="'.uniqid().'",opaque="'.md5($realm).'"'); error_log("EST: simpleenroll - require authentication"); die('Authentication required'); } + if ($reenroll && + (!isset($user) || + !isset($_SERVER["SSL_CLIENT_VERIFY"]) || + $_SERVER["SSL_CLIENT_VERIFY"] != "SUCCESS")) { + header('HTTP/1.1 403 Forbidden'); + error_log("EST: simplereenroll - require certificate authentication"); + die('Authentication required'); + } if (!isset($_SERVER["CONTENT_TYPE"])) { error_log("EST: simpleenroll without Content-Type"); die("Missing Content-Type"); @@ -167,6 +200,7 @@ if ($method == "GET" && $cmd == "cacerts") { } $der = file_get_contents($cert_der); $fingerprint = hash("sha256", $der); + error_log("EST: sha256(DER cert): $fingerprint"); $pkcs7 = "$cadir/tmp/est-client.pkcs7"; if (file_exists($pkcs7)) diff --git a/hs20/server/www/spp.php b/hs20/server/www/spp.php index f10e5ab8..c56d3d69 100644 --- a/hs20/server/www/spp.php +++ b/hs20/server/www/spp.php @@ -85,6 +85,40 @@ if (!empty($_SERVER['PHP_AUTH_DIGEST'])) { isset($_SERVER["SSL_CLIENT_M_SERIAL"])) { $user = "cert-" . $_SERVER["SSL_CLIENT_M_SERIAL"]; putenv("HS20CERT=yes"); +} else if (isset($_GET["hotspot2dot0-mobile-identifier-hash"])) { + $id_hash = $_GET["hotspot2dot0-mobile-identifier-hash"]; + $id_hash = PREG_REPLACE("/[^0-9a-h]/i", '', $id_hash); + + $db = new PDO($osu_db); + if (!$db) { + error_log("spp.php - Could not access database"); + die("Could not access database"); + } + + $row = $db->query("SELECT * FROM sim_provisioning " . + "WHERE mobile_identifier_hash='$id_hash'")->fetch(); + if (!$row) { + error_log("spp.php - SIM provisioning failed - mobile_identifier_hash not found"); + die('SIM provisioning failed - mobile_identifier_hash not found'); + } + + $imsi = $row['imsi']; + $mac_addr = $row['mac_addr']; + $eap_method = $row['eap_method']; + + $row = $db->query("SELECT COUNT(*) FROM osu_config " . + "WHERE realm='$realm'")->fetch(); + if (!$row || intval($row[0]) < 1) { + error_log("spp.php - SIM provisioning failed - realm $realm not found"); + die('SIM provisioning failed'); + } + + error_log("spp.php - SIM provisioning for IMSI $imsi"); + putenv("HS20SIMPROV=yes"); + putenv("HS20IMSI=$imsi"); + putenv("HS20MACADDR=$mac_addr"); + putenv("HS20EAPMETHOD=$eap_method"); + putenv("HS20IDHASH=$id_hash"); } else if (!isset($_SERVER["PATH_INFO"]) || $_SERVER["PATH_INFO"] != "/signup") { header('HTTP/1.1 401 Unauthorized'); diff --git a/hs20/server/www/users.php b/hs20/server/www/users.php index f546de3a..2bd55527 100644 --- a/hs20/server/www/users.php +++ b/hs20/server/www/users.php @@ -69,6 +69,9 @@ if ($cmd == 'subrem-add-user' && $id > 0) { if ($cmd == 'subrem-add-machine' && $id > 0) { $db->exec("UPDATE users SET remediation='machine' WHERE rowid=$id"); } +if ($cmd == 'subrem-add-reenroll' && $id > 0) { + $db->exec("UPDATE users SET remediation='reenroll' WHERE rowid=$id"); +} if ($cmd == 'subrem-add-policy' && $id > 0) { $db->exec("UPDATE users SET remediation='policy' WHERE rowid=$id"); } @@ -172,6 +175,10 @@ if ($rem == "") { $row['rowid'] . "\">add:user</a>]"; echo " [<a href=\"users.php?cmd=subrem-add-machine&id=" . $row['rowid'] . "\">add:machine</a>]"; + if ($row['methods'] == 'TLS') { + echo " [<a href=\"users.php?cmd=subrem-add-reenroll&id=" . + $row['rowid'] . "\">add:reenroll</a>]"; + } echo " [<a href=\"users.php?cmd=subrem-add-policy&id=" . $row['rowid'] . "\">add:policy</a>]"; echo " [<a href=\"users.php?cmd=subrem-add-free&id=" . @@ -185,6 +192,9 @@ if ($rem == "") { } else if ($rem == "free") { echo "Free [<a href=\"users.php?cmd=subrem-clear&id=" . $row['rowid'] . "\">clear</a>]"; +} else if ($rem == "reenroll") { + echo "Reenroll [<a href=\"users.php?cmd=subrem-clear&id=" . + $row['rowid'] . "\">clear</a>]"; } else { echo "Machine [<a href=\"users.php?cmd=subrem-clear&id=" . $row['rowid'] . "\">clear</a>]"; @@ -319,7 +329,7 @@ echo "<br>\n"; echo "<table border=1 cellspacing=0 cellpadding=0>\n"; echo "<tr><th>User<th>Realm<th><small>Remediation</small><th>Policy<th><small>Account type</small><th><small>Phase 2 method(s)</small><th>DevId<th>MAC Address<th>T&C\n"; -$res = $db->query('SELECT rowid,* FROM users WHERE phase2=1 ORDER BY identity'); +$res = $db->query('SELECT rowid,* FROM users WHERE (phase2=1 OR methods=\'TLS\') ORDER BY identity'); foreach ($res as $row) { echo "<tr><td><a href=\"users.php?id=" . $row['rowid'] . "\"> " . $row['identity'] . " </a>"; @@ -334,6 +344,8 @@ foreach ($res as $row) { echo "Policy"; } else if ($rem == "free") { echo "Free"; + } else if ($rem == "reenroll") { + echo "Reenroll"; } else { echo "Machine"; } |