hostap/hs20/client/spp_client.c
Jouni Malinen 40bdceac88 HS 2.0R2: Configure OSU client trust root more consistently
Some of the code paths could have ended up ignoring CA file name from
command line due to overly complex way of setting ctx->ca_fname.
Configure this more consistently in osu_client.c as soon as the CA file
name has been determined.

Signed-off-by: Jouni Malinen <jouni@qca.qualcomm.com>
2014-03-18 00:39:39 +02:00

996 lines
26 KiB
C

/*
* Hotspot 2.0 SPP client
* Copyright (c) 2012-2014, Qualcomm Atheros, Inc.
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#include "includes.h"
#include <sys/stat.h>
#include "common.h"
#include "browser.h"
#include "wpa_ctrl.h"
#include "wpa_helpers.h"
#include "xml-utils.h"
#include "http-utils.h"
#include "utils/base64.h"
#include "crypto/crypto.h"
#include "crypto/sha256.h"
#include "osu_client.h"
static int hs20_spp_update_response(struct hs20_osu_client *ctx,
const char *session_id,
const char *spp_status,
const char *error_code);
static void hs20_policy_update_complete(
struct hs20_osu_client *ctx, const char *pps_fname);
static char * get_spp_attr_value(struct xml_node_ctx *ctx, xml_node_t *node,
char *attr_name)
{
return xml_node_get_attr_value_ns(ctx, node, SPP_NS_URI, attr_name);
}
static int hs20_spp_validate(struct hs20_osu_client *ctx, xml_node_t *node,
const char *expected_name)
{
struct xml_node_ctx *xctx = ctx->xml;
const char *name;
char *err;
int ret;
if (!xml_node_is_element(xctx, node))
return -1;
name = xml_node_get_localname(xctx, node);
if (name == NULL)
return -1;
if (strcmp(expected_name, name) != 0) {
wpa_printf(MSG_INFO, "Unexpected SOAP method name '%s' (expected '%s')",
name, expected_name);
write_summary(ctx, "Unexpected SOAP method name '%s' (expected '%s')",
name, expected_name);
return -1;
}
ret = xml_validate(xctx, node, "spp.xsd", &err);
if (ret < 0) {
wpa_printf(MSG_INFO, "XML schema validation error(s)\n%s", err);
write_summary(ctx, "SPP XML schema validation failed");
os_free(err);
}
return ret;
}
static void add_mo_container(struct xml_node_ctx *ctx, xml_namespace_t *ns,
xml_node_t *parent, const char *urn,
const char *fname)
{
xml_node_t *node;
xml_node_t *fnode, *tnds;
char *str;
fnode = node_from_file(ctx, fname);
if (!fnode)
return;
tnds = mo_to_tnds(ctx, fnode, 0, urn, "syncml:dmddf1.2");
xml_node_free(ctx, fnode);
if (!tnds)
return;
str = xml_node_to_str(ctx, tnds);
xml_node_free(ctx, tnds);
if (str == NULL)
return;
node = xml_node_create_text(ctx, parent, ns, "moContainer", str);
if (node)
xml_node_add_attr(ctx, node, ns, "moURN", urn);
os_free(str);
}
static xml_node_t * build_spp_post_dev_data(struct hs20_osu_client *ctx,
xml_namespace_t **ret_ns,
const char *session_id,
const char *reason)
{
xml_namespace_t *ns;
xml_node_t *spp_node;
write_summary(ctx, "Building sppPostDevData requestReason='%s'",
reason);
spp_node = xml_node_create_root(ctx->xml, SPP_NS_URI, "spp", &ns,
"sppPostDevData");
if (spp_node == NULL)
return NULL;
if (ret_ns)
*ret_ns = ns;
xml_node_add_attr(ctx->xml, spp_node, ns, "sppVersion", "1.0");
xml_node_add_attr(ctx->xml, spp_node, NULL, "requestReason", reason);
if (session_id)
xml_node_add_attr(ctx->xml, spp_node, ns, "sessionID",
session_id);
xml_node_add_attr(ctx->xml, spp_node, NULL, "redirectURI",
"http://localhost:12345/");
xml_node_create_text(ctx->xml, spp_node, ns, "supportedSPPVersions",
"1.0");
xml_node_create_text(ctx->xml, spp_node, ns, "supportedMOList",
URN_HS20_PPS " " URN_OMA_DM_DEVINFO " "
URN_OMA_DM_DEVDETAIL " " URN_HS20_DEVDETAIL_EXT);
add_mo_container(ctx->xml, ns, spp_node, URN_OMA_DM_DEVINFO,
"devinfo.xml");
add_mo_container(ctx->xml, ns, spp_node, URN_OMA_DM_DEVDETAIL,
"devdetail.xml");
return spp_node;
}
static int process_update_node(struct hs20_osu_client *ctx, xml_node_t *pps,
xml_node_t *update)
{
xml_node_t *node, *parent, *tnds, *unode;
char *str;
const char *name;
char *uri, *pos;
char *cdata, *cdata_end;
size_t fqdn_len;
wpa_printf(MSG_INFO, "Processing updateNode");
debug_dump_node(ctx, "updateNode", update);
uri = get_spp_attr_value(ctx->xml, update, "managementTreeURI");
if (uri == NULL) {
wpa_printf(MSG_INFO, "No managementTreeURI present");
return -1;
}
wpa_printf(MSG_INFO, "managementTreeUri: '%s'", uri);
name = os_strrchr(uri, '/');
if (name == NULL) {
wpa_printf(MSG_INFO, "Unexpected URI");
xml_node_get_attr_value_free(ctx->xml, uri);
return -1;
}
name++;
wpa_printf(MSG_INFO, "Update interior node: '%s'", name);
str = xml_node_get_text(ctx->xml, update);
if (str == NULL) {
wpa_printf(MSG_INFO, "Could not extract MO text");
xml_node_get_attr_value_free(ctx->xml, uri);
return -1;
}
wpa_printf(MSG_DEBUG, "[hs20] nodeContainer text: '%s'", str);
cdata = strstr(str, "<![CDATA[");
cdata_end = strstr(str, "]]>");
if (cdata && cdata_end && cdata_end > cdata &&
cdata < strstr(str, "MgmtTree") &&
cdata_end > strstr(str, "/MgmtTree")) {
char *tmp;
wpa_printf(MSG_DEBUG, "[hs20] Removing extra CDATA container");
tmp = strdup(cdata + 9);
if (tmp) {
cdata_end = strstr(tmp, "]]>");
if (cdata_end)
*cdata_end = '\0';
wpa_printf(MSG_DEBUG, "[hs20] nodeContainer text with CDATA container removed: '%s'",
tmp);
tnds = xml_node_from_buf(ctx->xml, tmp);
free(tmp);
} else
tnds = NULL;
} else
tnds = xml_node_from_buf(ctx->xml, str);
xml_node_get_text_free(ctx->xml, str);
if (tnds == NULL) {
wpa_printf(MSG_INFO, "[hs20] Could not parse nodeContainer text");
xml_node_get_attr_value_free(ctx->xml, uri);
return -1;
}
unode = tnds_to_mo(ctx->xml, tnds);
xml_node_free(ctx->xml, tnds);
if (unode == NULL) {
wpa_printf(MSG_INFO, "[hs20] Could not parse nodeContainer TNDS text");
xml_node_get_attr_value_free(ctx->xml, uri);
return -1;
}
debug_dump_node(ctx, "Parsed TNDS", unode);
if (get_node_uri(ctx->xml, unode, name) == NULL) {
wpa_printf(MSG_INFO, "[hs20] %s node not found", name);
xml_node_free(ctx->xml, unode);
xml_node_get_attr_value_free(ctx->xml, uri);
return -1;
}
if (os_strncasecmp(uri, "./Wi-Fi/", 8) != 0) {
wpa_printf(MSG_INFO, "Do not allow update outside ./Wi-Fi");
xml_node_free(ctx->xml, unode);
xml_node_get_attr_value_free(ctx->xml, uri);
return -1;
}
pos = uri + 8;
if (ctx->fqdn == NULL) {
wpa_printf(MSG_INFO, "FQDN not known");
xml_node_free(ctx->xml, unode);
xml_node_get_attr_value_free(ctx->xml, uri);
return -1;
}
fqdn_len = os_strlen(ctx->fqdn);
if (os_strncasecmp(pos, ctx->fqdn, fqdn_len) != 0 ||
pos[fqdn_len] != '/') {
wpa_printf(MSG_INFO, "Do not allow update outside ./Wi-Fi/%s",
ctx->fqdn);
xml_node_free(ctx->xml, unode);
xml_node_get_attr_value_free(ctx->xml, uri);
return -1;
}
pos += fqdn_len + 1;
if (os_strncasecmp(pos, "PerProviderSubscription/", 24) != 0) {
wpa_printf(MSG_INFO, "Do not allow update outside ./Wi-Fi/%s/PerProviderSubscription",
ctx->fqdn);
xml_node_free(ctx->xml, unode);
xml_node_get_attr_value_free(ctx->xml, uri);
return -1;
}
pos += 24;
wpa_printf(MSG_INFO, "Update command for PPS node %s", pos);
node = get_node(ctx->xml, pps, pos);
if (node) {
parent = xml_node_get_parent(ctx->xml, node);
xml_node_detach(ctx->xml, node);
wpa_printf(MSG_INFO, "Replace '%s' node", name);
} else {
char *pos2;
pos2 = os_strrchr(pos, '/');
if (pos2 == NULL) {
parent = pps;
} else {
*pos2 = '\0';
parent = get_node(ctx->xml, pps, pos);
}
if (parent == NULL) {
wpa_printf(MSG_INFO, "Could not find parent %s", pos);
xml_node_free(ctx->xml, unode);
xml_node_get_attr_value_free(ctx->xml, uri);
return -1;
}
wpa_printf(MSG_INFO, "Add '%s' node", name);
}
xml_node_add_child(ctx->xml, parent, unode);
xml_node_get_attr_value_free(ctx->xml, uri);
return 0;
}
static int update_pps(struct hs20_osu_client *ctx, xml_node_t *update,
const char *pps_fname, xml_node_t *pps)
{
wpa_printf(MSG_INFO, "Updating PPS based on updateNode element(s)");
xml_node_for_each_sibling(ctx->xml, update) {
xml_node_for_each_check(ctx->xml, update);
if (process_update_node(ctx, pps, update) < 0)
return -1;
}
return update_pps_file(ctx, pps_fname, pps);
}
static void hs20_sub_rem_complete(struct hs20_osu_client *ctx,
const char *pps_fname)
{
/*
* Update wpa_supplicant credentials and reconnect using updated
* information.
*/
wpa_printf(MSG_INFO, "Updating wpa_supplicant credentials");
cmd_set_pps(ctx, pps_fname);
if (ctx->no_reconnect)
return;
wpa_printf(MSG_INFO, "Requesting reconnection with updated configuration");
if (wpa_command(ctx->ifname, "INTERWORKING_SELECT auto") < 0)
wpa_printf(MSG_ERROR, "Failed to request wpa_supplicant to reconnect");
}
static xml_node_t * hs20_spp_upload_mo(struct hs20_osu_client *ctx,
xml_node_t *cmd,
const char *session_id,
const char *pps_fname)
{
xml_namespace_t *ns;
xml_node_t *node, *ret_node;
char *urn;
urn = get_spp_attr_value(ctx->xml, cmd, "moURN");
if (!urn) {
wpa_printf(MSG_INFO, "No URN included");
return NULL;
}
wpa_printf(MSG_INFO, "Upload MO request - URN=%s", urn);
if (strcasecmp(urn, URN_HS20_PPS) != 0) {
wpa_printf(MSG_INFO, "Unsupported moURN");
xml_node_get_attr_value_free(ctx->xml, urn);
return NULL;
}
xml_node_get_attr_value_free(ctx->xml, urn);
if (!pps_fname) {
wpa_printf(MSG_INFO, "PPS file name no known");
return NULL;
}
node = build_spp_post_dev_data(ctx, &ns, session_id,
"MO upload");
if (node == NULL)
return NULL;
add_mo_container(ctx->xml, ns, node, URN_HS20_PPS, pps_fname);
ret_node = soap_send_receive(ctx->http, node);
if (ret_node == NULL)
return NULL;
debug_dump_node(ctx, "Received response to MO upload", ret_node);
if (hs20_spp_validate(ctx, ret_node, "sppPostDevDataResponse") < 0) {
wpa_printf(MSG_INFO, "SPP validation failed");
xml_node_free(ctx->xml, ret_node);
return NULL;
}
return ret_node;
}
static int hs20_add_mo(struct hs20_osu_client *ctx, xml_node_t *add_mo,
char *fname, size_t fname_len)
{
char *uri, *urn;
int ret;
debug_dump_node(ctx, "Received addMO", add_mo);
urn = get_spp_attr_value(ctx->xml, add_mo, "moURN");
if (urn == NULL) {
wpa_printf(MSG_INFO, "[hs20] No moURN in addMO");
return -1;
}
wpa_printf(MSG_INFO, "addMO - moURN: '%s'", urn);
if (strcasecmp(urn, URN_HS20_PPS) != 0) {
wpa_printf(MSG_INFO, "[hs20] Unsupported MO in addMO");
xml_node_get_attr_value_free(ctx->xml, urn);
return -1;
}
xml_node_get_attr_value_free(ctx->xml, urn);
uri = get_spp_attr_value(ctx->xml, add_mo, "managementTreeURI");
if (uri == NULL) {
wpa_printf(MSG_INFO, "[hs20] No managementTreeURI in addMO");
return -1;
}
wpa_printf(MSG_INFO, "addMO - managementTreeURI: '%s'", uri);
ret = hs20_add_pps_mo(ctx, uri, add_mo, fname, fname_len);
xml_node_get_attr_value_free(ctx->xml, uri);
return ret;
}
static int process_spp_user_input_response(struct hs20_osu_client *ctx,
const char *session_id,
xml_node_t *add_mo)
{
int ret;
char fname[300];
debug_dump_node(ctx, "addMO", add_mo);
wpa_printf(MSG_INFO, "Subscription registration completed");
if (hs20_add_mo(ctx, add_mo, fname, sizeof(fname)) < 0) {
wpa_printf(MSG_INFO, "Could not add MO");
ret = hs20_spp_update_response(
ctx, session_id,
"Error occurred",
"MO addition or update failed");
return 0;
}
ret = hs20_spp_update_response(ctx, session_id, "OK", NULL);
if (ret == 0)
hs20_sub_rem_complete(ctx, fname);
return 0;
}
static xml_node_t * hs20_spp_user_input_completed(struct hs20_osu_client *ctx,
const char *session_id)
{
xml_node_t *node, *ret_node;
node = build_spp_post_dev_data(ctx, NULL, session_id,
"User input completed");
if (node == NULL)
return NULL;
ret_node = soap_send_receive(ctx->http, node);
if (!ret_node) {
if (soap_reinit_client(ctx->http) < 0)
return NULL;
wpa_printf(MSG_INFO, "Try to finish with re-opened connection");
node = build_spp_post_dev_data(ctx, NULL, session_id,
"User input completed");
if (node == NULL)
return NULL;
ret_node = soap_send_receive(ctx->http, node);
if (ret_node == NULL)
return NULL;
wpa_printf(MSG_INFO, "Continue with new connection");
}
if (hs20_spp_validate(ctx, ret_node, "sppPostDevDataResponse") < 0) {
wpa_printf(MSG_INFO, "SPP validation failed");
xml_node_free(ctx->xml, ret_node);
return NULL;
}
return ret_node;
}
static xml_node_t * hs20_spp_get_certificate(struct hs20_osu_client *ctx,
xml_node_t *cmd,
const char *session_id,
const char *pps_fname)
{
xml_namespace_t *ns;
xml_node_t *node, *ret_node;
int res;
wpa_printf(MSG_INFO, "Client certificate enrollment");
res = osu_get_certificate(ctx, cmd);
if (res < 0)
wpa_printf(MSG_INFO, "EST simpleEnroll failed");
node = build_spp_post_dev_data(ctx, &ns, session_id,
res == 0 ?
"Certificate enrollment completed" :
"Certificate enrollment failed");
if (node == NULL)
return NULL;
ret_node = soap_send_receive(ctx->http, node);
if (ret_node == NULL)
return NULL;
debug_dump_node(ctx, "Received response to certificate enrollment "
"completed", ret_node);
if (hs20_spp_validate(ctx, ret_node, "sppPostDevDataResponse") < 0) {
wpa_printf(MSG_INFO, "SPP validation failed");
xml_node_free(ctx->xml, ret_node);
return NULL;
}
return ret_node;
}
static int hs20_spp_exec(struct hs20_osu_client *ctx, xml_node_t *exec,
const char *session_id, const char *pps_fname,
xml_node_t *pps, xml_node_t **ret_node)
{
xml_node_t *cmd;
const char *name;
char *uri;
char *id = strdup(session_id);
if (id == NULL)
return -1;
*ret_node = NULL;
debug_dump_node(ctx, "exec", exec);
xml_node_for_each_child(ctx->xml, cmd, exec) {
xml_node_for_each_check(ctx->xml, cmd);
break;
}
if (!cmd) {
wpa_printf(MSG_INFO, "exec command element not found (cmd=%p)",
cmd);
free(id);
return -1;
}
name = xml_node_get_localname(ctx->xml, cmd);
if (strcasecmp(name, "launchBrowserToURI") == 0) {
int res;
uri = xml_node_get_text(ctx->xml, cmd);
if (!uri) {
wpa_printf(MSG_INFO, "No URI found");
free(id);
return -1;
}
wpa_printf(MSG_INFO, "Launch browser to URI '%s'", uri);
write_summary(ctx, "Launch browser to URI '%s'", uri);
res = hs20_web_browser(uri);
xml_node_get_text_free(ctx->xml, uri);
if (res > 0) {
wpa_printf(MSG_INFO, "User response in browser completed successfully - sessionid='%s'",
id);
write_summary(ctx, "User response in browser completed successfully");
*ret_node = hs20_spp_user_input_completed(ctx, id);
free(id);
return *ret_node ? 0 : -1;
} else {
wpa_printf(MSG_INFO, "Failed to receive user response");
write_summary(ctx, "Failed to receive user response");
hs20_spp_update_response(
ctx, id, "Error occurred", "Other");
free(id);
return -1;
}
return 0;
}
if (strcasecmp(name, "uploadMO") == 0) {
if (pps_fname == NULL)
return -1;
*ret_node = hs20_spp_upload_mo(ctx, cmd, id,
pps_fname);
free(id);
return *ret_node ? 0 : -1;
}
if (strcasecmp(name, "getCertificate") == 0) {
*ret_node = hs20_spp_get_certificate(ctx, cmd, id,
pps_fname);
free(id);
return *ret_node ? 0 : -1;
}
wpa_printf(MSG_INFO, "Unsupported exec command: '%s'", name);
free(id);
return -1;
}
enum spp_post_dev_data_use {
SPP_SUBSCRIPTION_REMEDIATION,
SPP_POLICY_UPDATE,
SPP_SUBSCRIPTION_REGISTRATION,
};
static void process_spp_post_dev_data_response(
struct hs20_osu_client *ctx,
enum spp_post_dev_data_use use, xml_node_t *node,
const char *pps_fname, xml_node_t *pps)
{
xml_node_t *child;
char *status = NULL;
xml_node_t *update = NULL, *exec = NULL, *add_mo = NULL, *no_mo = NULL;
char *session_id = NULL;
debug_dump_node(ctx, "sppPostDevDataResponse node", node);
status = get_spp_attr_value(ctx->xml, node, "sppStatus");
if (status == NULL) {
wpa_printf(MSG_INFO, "No sppStatus attribute");
goto out;
}
write_summary(ctx, "Received sppPostDevDataResponse sppStatus='%s'",
status);
session_id = get_spp_attr_value(ctx->xml, node, "sessionID");
if (session_id == NULL) {
wpa_printf(MSG_INFO, "No sessionID attribute");
goto out;
}
wpa_printf(MSG_INFO, "[hs20] sppPostDevDataResponse - sppStatus: '%s' sessionID: '%s'",
status, session_id);
xml_node_for_each_child(ctx->xml, child, node) {
const char *name;
xml_node_for_each_check(ctx->xml, child);
debug_dump_node(ctx, "child", child);
name = xml_node_get_localname(ctx->xml, child);
wpa_printf(MSG_INFO, "localname: '%s'", name);
if (!update && strcasecmp(name, "updateNode") == 0)
update = child;
if (!exec && strcasecmp(name, "exec") == 0)
exec = child;
if (!add_mo && strcasecmp(name, "addMO") == 0)
add_mo = child;
if (!no_mo && strcasecmp(name, "noMOUpdate") == 0)
no_mo = child;
}
if (use == SPP_SUBSCRIPTION_REMEDIATION &&
strcasecmp(status,
"Remediation complete, request sppUpdateResponse") == 0)
{
int res, ret;
if (!update && !no_mo) {
wpa_printf(MSG_INFO, "No updateNode or noMOUpdate element");
goto out;
}
wpa_printf(MSG_INFO, "Subscription remediation completed");
res = update_pps(ctx, update, pps_fname, pps);
if (res < 0)
wpa_printf(MSG_INFO, "Failed to update PPS MO");
ret = hs20_spp_update_response(
ctx, session_id,
res < 0 ? "Error occurred" : "OK",
res < 0 ? "MO addition or update failed" : NULL);
if (res == 0 && ret == 0)
hs20_sub_rem_complete(ctx, pps_fname);
goto out;
}
if (use == SPP_SUBSCRIPTION_REMEDIATION &&
strcasecmp(status, "Exchange complete, release TLS connection") ==
0) {
if (!no_mo) {
wpa_printf(MSG_INFO, "No noMOUpdate element");
goto out;
}
wpa_printf(MSG_INFO, "Subscription remediation completed (no MO update)");
goto out;
}
if (use == SPP_POLICY_UPDATE &&
strcasecmp(status, "Update complete, request sppUpdateResponse") ==
0) {
int res, ret;
wpa_printf(MSG_INFO, "Policy update received - update PPS");
res = update_pps(ctx, update, pps_fname, pps);
ret = hs20_spp_update_response(
ctx, session_id,
res < 0 ? "Error occurred" : "OK",
res < 0 ? "MO addition or update failed" : NULL);
if (res == 0 && ret == 0)
hs20_policy_update_complete(ctx, pps_fname);
goto out;
}
if (use == SPP_SUBSCRIPTION_REGISTRATION &&
strcasecmp(status, "Provisioning complete, request "
"sppUpdateResponse") == 0) {
if (!add_mo) {
wpa_printf(MSG_INFO, "No addMO element - not sure what to do next");
goto out;
}
process_spp_user_input_response(ctx, session_id, add_mo);
node = NULL;
goto out;
}
if (strcasecmp(status, "No update available at this time") == 0) {
wpa_printf(MSG_INFO, "No update available at this time");
goto out;
}
if (strcasecmp(status, "OK") == 0) {
int res;
xml_node_t *ret;
if (!exec) {
wpa_printf(MSG_INFO, "No exec element - not sure what to do next");
goto out;
}
res = hs20_spp_exec(ctx, exec, session_id,
pps_fname, pps, &ret);
/* xml_node_free(ctx->xml, node); */
node = NULL;
if (res == 0 && ret)
process_spp_post_dev_data_response(ctx, use,
ret, pps_fname, pps);
goto out;
}
if (strcasecmp(status, "Error occurred") == 0) {
xml_node_t *err;
char *code = NULL;
err = get_node(ctx->xml, node, "sppError");
if (err)
code = xml_node_get_attr_value(ctx->xml, err,
"errorCode");
wpa_printf(MSG_INFO, "Error occurred - errorCode=%s",
code ? code : "N/A");
xml_node_get_attr_value_free(ctx->xml, code);
goto out;
}
wpa_printf(MSG_INFO,
"[hs20] Unsupported sppPostDevDataResponse sppStatus '%s'",
status);
out:
xml_node_get_attr_value_free(ctx->xml, status);
xml_node_get_attr_value_free(ctx->xml, session_id);
xml_node_free(ctx->xml, node);
}
static int spp_post_dev_data(struct hs20_osu_client *ctx,
enum spp_post_dev_data_use use,
const char *reason,
const char *pps_fname, xml_node_t *pps)
{
xml_node_t *payload;
xml_node_t *ret_node;
payload = build_spp_post_dev_data(ctx, NULL, NULL, reason);
if (payload == NULL)
return -1;
ret_node = soap_send_receive(ctx->http, payload);
if (!ret_node) {
const char *err = http_get_err(ctx->http);
if (err) {
wpa_printf(MSG_INFO, "HTTP error: %s", err);
write_result(ctx, "HTTP error: %s", err);
} else {
write_summary(ctx, "Failed to send SOAP message");
}
return -1;
}
if (hs20_spp_validate(ctx, ret_node, "sppPostDevDataResponse") < 0) {
wpa_printf(MSG_INFO, "SPP validation failed");
xml_node_free(ctx->xml, ret_node);
return -1;
}
process_spp_post_dev_data_response(ctx, use, ret_node,
pps_fname, pps);
return 0;
}
void spp_sub_rem(struct hs20_osu_client *ctx, const char *address,
const char *pps_fname,
const char *client_cert, const char *client_key,
const char *cred_username, const char *cred_password,
xml_node_t *pps)
{
wpa_printf(MSG_INFO, "SPP subscription remediation");
write_summary(ctx, "SPP subscription remediation");
os_free(ctx->server_url);
ctx->server_url = os_strdup(address);
if (soap_init_client(ctx->http, address, ctx->ca_fname,
cred_username, cred_password, client_cert,
client_key) == 0) {
spp_post_dev_data(ctx, SPP_SUBSCRIPTION_REMEDIATION,
"Subscription remediation", pps_fname, pps);
}
}
static void hs20_policy_update_complete(struct hs20_osu_client *ctx,
const char *pps_fname)
{
wpa_printf(MSG_INFO, "Policy update completed");
/*
* Update wpa_supplicant credentials and reconnect using updated
* information.
*/
wpa_printf(MSG_INFO, "Updating wpa_supplicant credentials");
cmd_set_pps(ctx, pps_fname);
wpa_printf(MSG_INFO, "Requesting reconnection with updated configuration");
if (wpa_command(ctx->ifname, "INTERWORKING_SELECT auto") < 0)
wpa_printf(MSG_ERROR, "Failed to request wpa_supplicant to reconnect");
}
static int process_spp_exchange_complete(struct hs20_osu_client *ctx,
xml_node_t *node)
{
char *status, *session_id;
debug_dump_node(ctx, "sppExchangeComplete", node);
status = get_spp_attr_value(ctx->xml, node, "sppStatus");
if (status == NULL) {
wpa_printf(MSG_INFO, "No sppStatus attribute");
return -1;
}
write_summary(ctx, "Received sppExchangeComplete sppStatus='%s'",
status);
session_id = get_spp_attr_value(ctx->xml, node, "sessionID");
if (session_id == NULL) {
wpa_printf(MSG_INFO, "No sessionID attribute");
xml_node_get_attr_value_free(ctx->xml, status);
return -1;
}
wpa_printf(MSG_INFO, "[hs20] sppStatus: '%s' sessionID: '%s'",
status, session_id);
xml_node_get_attr_value_free(ctx->xml, session_id);
if (strcasecmp(status, "Exchange complete, release TLS connection") ==
0) {
xml_node_get_attr_value_free(ctx->xml, status);
return 0;
}
wpa_printf(MSG_INFO, "Unexpected sppStatus '%s'", status);
write_summary(ctx, "Unexpected sppStatus '%s'", status);
xml_node_get_attr_value_free(ctx->xml, status);
return -1;
}
static xml_node_t * build_spp_update_response(struct hs20_osu_client *ctx,
const char *session_id,
const char *spp_status,
const char *error_code)
{
xml_namespace_t *ns;
xml_node_t *spp_node, *node;
spp_node = xml_node_create_root(ctx->xml, SPP_NS_URI, "spp", &ns,
"sppUpdateResponse");
if (spp_node == NULL)
return NULL;
xml_node_add_attr(ctx->xml, spp_node, ns, "sppVersion", "1.0");
xml_node_add_attr(ctx->xml, spp_node, ns, "sessionID", session_id);
xml_node_add_attr(ctx->xml, spp_node, ns, "sppStatus", spp_status);
if (error_code) {
node = xml_node_create(ctx->xml, spp_node, ns, "sppError");
if (node)
xml_node_add_attr(ctx->xml, node, NULL, "errorCode",
error_code);
}
return spp_node;
}
static int hs20_spp_update_response(struct hs20_osu_client *ctx,
const char *session_id,
const char *spp_status,
const char *error_code)
{
xml_node_t *node, *ret_node;
int ret;
write_summary(ctx, "Building sppUpdateResponse sppStatus='%s' error_code='%s'",
spp_status, error_code);
node = build_spp_update_response(ctx, session_id, spp_status,
error_code);
if (node == NULL)
return -1;
ret_node = soap_send_receive(ctx->http, node);
if (!ret_node) {
if (soap_reinit_client(ctx->http) < 0)
return -1;
wpa_printf(MSG_INFO, "Try to finish with re-opened connection");
node = build_spp_update_response(ctx, session_id, spp_status,
error_code);
if (node == NULL)
return -1;
ret_node = soap_send_receive(ctx->http, node);
if (ret_node == NULL)
return -1;
wpa_printf(MSG_INFO, "Continue with new connection");
}
if (hs20_spp_validate(ctx, ret_node, "sppExchangeComplete") < 0) {
wpa_printf(MSG_INFO, "SPP validation failed");
xml_node_free(ctx->xml, ret_node);
return -1;
}
ret = process_spp_exchange_complete(ctx, ret_node);
xml_node_free(ctx->xml, ret_node);
return ret;
}
void spp_pol_upd(struct hs20_osu_client *ctx, const char *address,
const char *pps_fname,
const char *client_cert, const char *client_key,
const char *cred_username, const char *cred_password,
xml_node_t *pps)
{
wpa_printf(MSG_INFO, "SPP policy update");
write_summary(ctx, "SPP policy update");
os_free(ctx->server_url);
ctx->server_url = os_strdup(address);
if (soap_init_client(ctx->http, address, ctx->ca_fname, cred_username,
cred_password, client_cert, client_key) == 0) {
spp_post_dev_data(ctx, SPP_POLICY_UPDATE, "Policy update",
pps_fname, pps);
}
}
int cmd_prov(struct hs20_osu_client *ctx, const char *url)
{
unlink("Cert/est_cert.der");
unlink("Cert/est_cert.pem");
if (url == NULL) {
wpa_printf(MSG_INFO, "Invalid prov command (missing URL)");
return -1;
}
wpa_printf(MSG_INFO, "Credential provisioning requested");
os_free(ctx->server_url);
ctx->server_url = os_strdup(url);
if (soap_init_client(ctx->http, url, ctx->ca_fname, NULL, NULL, NULL,
NULL) < 0)
return -1;
spp_post_dev_data(ctx, SPP_SUBSCRIPTION_REGISTRATION,
"Subscription registration", NULL, NULL);
return ctx->pps_cred_set ? 0 : -1;
}
int cmd_sim_prov(struct hs20_osu_client *ctx, const char *url)
{
if (url == NULL) {
wpa_printf(MSG_INFO, "Invalid prov command (missing URL)");
return -1;
}
wpa_printf(MSG_INFO, "SIM provisioning requested");
os_free(ctx->server_url);
ctx->server_url = os_strdup(url);
wpa_printf(MSG_INFO, "Wait for IP address before starting SIM provisioning");
if (wait_ip_addr(ctx->ifname, 15) < 0) {
wpa_printf(MSG_INFO, "Could not get IP address for WLAN - try connection anyway");
}
if (soap_init_client(ctx->http, url, ctx->ca_fname, NULL, NULL, NULL,
NULL) < 0)
return -1;
spp_post_dev_data(ctx, SPP_SUBSCRIPTION_REGISTRATION,
"Subscription provisioning", NULL, NULL);
return ctx->pps_cred_set ? 0 : -1;
}