aboutsummaryrefslogtreecommitdiff
path: root/hs20
diff options
context:
space:
mode:
authorHai Shalom <haishalom@google.com>2019-02-06 16:32:13 -0800
committerHai Shalom <haishalom@google.com>2019-02-11 18:36:10 +0000
commit39bc25d3a79c1375de430a7918d949c1a86f70c6 (patch)
tree28a9b8d123fef7d5e7cf2bfbd0c110f37756beda /hs20
parente6747a12928845398fe963c9b522acb4aedc075b (diff)
downloadwpa_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/.gitignore1
-rw-r--r--hs20/client/Makefile5
-rw-r--r--hs20/client/est.c13
-rw-r--r--hs20/client/oma_dm_client.c6
-rw-r--r--hs20/client/osu_client.c31
-rw-r--r--hs20/server/Makefile10
-rw-r--r--hs20/server/hs20-osu-server.txt5
-rw-r--r--hs20/server/hs20_spp_server.c18
-rw-r--r--hs20/server/spp_server.c630
-rw-r--r--hs20/server/spp_server.h3
-rw-r--r--hs20/server/sql.txt13
-rw-r--r--hs20/server/www/est.php50
-rw-r--r--hs20/server/www/spp.php34
-rw-r--r--hs20/server/www/users.php14
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";
}