Home | History | Annotate | Download | only in client
      1 /*
      2  * Hotspot 2.0 - OMA DM client
      3  * Copyright (c) 2013-2014, Qualcomm Atheros, Inc.
      4  *
      5  * This software may be distributed under the terms of the BSD license.
      6  * See README for more details.
      7  */
      8 
      9 #include "includes.h"
     10 
     11 #include "common.h"
     12 #include "wpa_helpers.h"
     13 #include "xml-utils.h"
     14 #include "http-utils.h"
     15 #include "utils/browser.h"
     16 #include "osu_client.h"
     17 
     18 
     19 #define DM_SERVER_INITIATED_MGMT 1200
     20 #define DM_CLIENT_INITIATED_MGMT 1201
     21 #define DM_GENERIC_ALERT 1226
     22 
     23 /* OMA-TS-SyncML-RepPro-V1_2_2 - 10. Response Status Codes */
     24 #define DM_RESP_OK 200
     25 #define DM_RESP_AUTH_ACCEPTED 212
     26 #define DM_RESP_CHUNKED_ITEM_ACCEPTED 213
     27 #define DM_RESP_NOT_EXECUTED 215
     28 #define DM_RESP_ATOMIC_ROLL_BACK_OK 216
     29 #define DM_RESP_NOT_MODIFIED 304
     30 #define DM_RESP_BAD_REQUEST 400
     31 #define DM_RESP_UNAUTHORIZED 401
     32 #define DM_RESP_FORBIDDEN 403
     33 #define DM_RESP_NOT_FOUND 404
     34 #define DM_RESP_COMMAND_NOT_ALLOWED 405
     35 #define DM_RESP_OPTIONAL_FEATURE_NOT_SUPPORTED 406
     36 #define DM_RESP_MISSING_CREDENTIALS 407
     37 #define DM_RESP_CONFLICT 409
     38 #define DM_RESP_GONE 410
     39 #define DM_RESP_INCOMPLETE_COMMAND 412
     40 #define DM_RESP_REQ_ENTITY_TOO_LARGE 413
     41 #define DM_RESP_URI_TOO_LONG 414
     42 #define DM_RESP_UNSUPPORTED_MEDIA_TYPE_OR_FORMAT 415
     43 #define DM_RESP_REQ_TOO_BIG 416
     44 #define DM_RESP_ALREADY_EXISTS 418
     45 #define DM_RESP_DEVICE_FULL 420
     46 #define DM_RESP_SIZE_MISMATCH 424
     47 #define DM_RESP_PERMISSION_DENIED 425
     48 #define DM_RESP_COMMAND_FAILED 500
     49 #define DM_RESP_COMMAND_NOT_IMPLEMENTED 501
     50 #define DM_RESP_ATOMIC_ROLL_BACK_FAILED 516
     51 
     52 #define DM_HS20_SUBSCRIPTION_CREATION \
     53 	"org.wi-fi.hotspot2dot0.SubscriptionCreation"
     54 #define DM_HS20_SUBSCRIPTION_PROVISIONING \
     55 	"org.wi-fi.hotspot2dot0.SubscriptionProvisioning"
     56 #define DM_HS20_SUBSCRIPTION_REMEDIATION \
     57 	"org.wi-fi.hotspot2dot0.SubscriptionRemediation"
     58 #define DM_HS20_POLICY_UPDATE \
     59 	"org.wi-fi.hotspot2dot0.PolicyUpdate"
     60 
     61 #define DM_URI_PPS "./Wi-Fi/org.wi-fi/PerProviderSubscription"
     62 #define DM_URI_LAUNCH_BROWSER \
     63 	"./DevDetail/Ext/org.wi-fi/Wi-Fi/Ops/launchBrowserToURI"
     64 
     65 
     66 static void add_item(struct hs20_osu_client *ctx, xml_node_t *parent,
     67 		     const char *locuri, const char *data);
     68 
     69 
     70 static const char * int2str(int val)
     71 {
     72 	static char buf[20];
     73 	snprintf(buf, sizeof(buf), "%d", val);
     74 	return buf;
     75 }
     76 
     77 
     78 static char * oma_dm_get_target_locuri(struct hs20_osu_client *ctx,
     79 				       xml_node_t *node)
     80 {
     81 	xml_node_t *locuri;
     82 	char *uri, *ret = NULL;
     83 
     84 	locuri = get_node(ctx->xml, node, "Item/Target/LocURI");
     85 	if (locuri == NULL)
     86 		return NULL;
     87 
     88 	uri = xml_node_get_text(ctx->xml, locuri);
     89 	if (uri)
     90 		ret = os_strdup(uri);
     91 	xml_node_get_text_free(ctx->xml, uri);
     92 	return ret;
     93 }
     94 
     95 
     96 static void oma_dm_add_locuri(struct hs20_osu_client *ctx, xml_node_t *parent,
     97 			      const char *element, const char *uri)
     98 {
     99 	xml_node_t *node;
    100 
    101 	node = xml_node_create(ctx->xml, parent, NULL, element);
    102 	if (node == NULL)
    103 		return;
    104 	xml_node_create_text(ctx->xml, node, NULL, "LocURI", uri);
    105 }
    106 
    107 
    108 static xml_node_t * oma_dm_build_hdr(struct hs20_osu_client *ctx,
    109 				     const char *url, int msgid)
    110 {
    111 	xml_node_t *syncml, *synchdr;
    112 	xml_namespace_t *ns;
    113 
    114 	syncml = xml_node_create_root(ctx->xml, "SYNCML:SYNCML1.2", NULL, &ns,
    115 				      "SyncML");
    116 
    117 	synchdr = xml_node_create(ctx->xml, syncml, NULL, "SyncHdr");
    118 	xml_node_create_text(ctx->xml, synchdr, NULL, "VerDTD", "1.2");
    119 	xml_node_create_text(ctx->xml, synchdr, NULL, "VerProto", "DM/1.2");
    120 	xml_node_create_text(ctx->xml, synchdr, NULL, "SessionID", "1");
    121 	xml_node_create_text(ctx->xml, synchdr, NULL, "MsgID", int2str(msgid));
    122 
    123 	oma_dm_add_locuri(ctx, synchdr, "Target", url);
    124 	oma_dm_add_locuri(ctx, synchdr, "Source", ctx->devid);
    125 
    126 	return syncml;
    127 }
    128 
    129 
    130 static void oma_dm_add_cmdid(struct hs20_osu_client *ctx, xml_node_t *parent,
    131 			     int cmdid)
    132 {
    133 	xml_node_create_text(ctx->xml, parent, NULL, "CmdID", int2str(cmdid));
    134 }
    135 
    136 
    137 static xml_node_t * add_alert(struct hs20_osu_client *ctx, xml_node_t *parent,
    138 			      int cmdid, int data)
    139 {
    140 	xml_node_t *node;
    141 
    142 	node = xml_node_create(ctx->xml, parent, NULL, "Alert");
    143 	if (node == NULL)
    144 		return NULL;
    145 	oma_dm_add_cmdid(ctx, node, cmdid);
    146 	xml_node_create_text(ctx->xml, node, NULL, "Data", int2str(data));
    147 
    148 	return node;
    149 }
    150 
    151 
    152 static xml_node_t * add_status(struct hs20_osu_client *ctx, xml_node_t *parent,
    153 			       int msgref, int cmdref, int cmdid,
    154 			       const char *cmd, int data, const char *targetref)
    155 {
    156 	xml_node_t *node;
    157 
    158 	node = xml_node_create(ctx->xml, parent, NULL, "Status");
    159 	if (node == NULL)
    160 		return NULL;
    161 	oma_dm_add_cmdid(ctx, node, cmdid);
    162 	xml_node_create_text(ctx->xml, node, NULL, "MsgRef", int2str(msgref));
    163 	if (cmdref)
    164 		xml_node_create_text(ctx->xml, node, NULL, "CmdRef",
    165 				     int2str(cmdref));
    166 	xml_node_create_text(ctx->xml, node, NULL, "Cmd", cmd);
    167 	xml_node_create_text(ctx->xml, node, NULL, "Data", int2str(data));
    168 	if (targetref) {
    169 		xml_node_create_text(ctx->xml, node, NULL, "TargetRef",
    170 				     targetref);
    171 	}
    172 
    173 	return node;
    174 }
    175 
    176 
    177 static xml_node_t * add_results(struct hs20_osu_client *ctx, xml_node_t *parent,
    178 				int msgref, int cmdref, int cmdid,
    179 				const char *locuri, const char *data)
    180 {
    181 	xml_node_t *node;
    182 
    183 	node = xml_node_create(ctx->xml, parent, NULL, "Results");
    184 	if (node == NULL)
    185 		return NULL;
    186 
    187 	oma_dm_add_cmdid(ctx, node, cmdid);
    188 	xml_node_create_text(ctx->xml, node, NULL, "MsgRef", int2str(msgref));
    189 	xml_node_create_text(ctx->xml, node, NULL, "CmdRef", int2str(cmdref));
    190 	add_item(ctx, node, locuri, data);
    191 
    192 	return node;
    193 }
    194 
    195 
    196 static char * mo_str(struct hs20_osu_client *ctx, const char *urn,
    197 		     const char *fname)
    198 {
    199 	xml_node_t *fnode, *tnds;
    200 	char *str;
    201 
    202 	fnode = node_from_file(ctx->xml, fname);
    203 	if (!fnode)
    204 		return NULL;
    205 	tnds = mo_to_tnds(ctx->xml, fnode, 0, urn, "syncml:dmddf1.2");
    206 	xml_node_free(ctx->xml, fnode);
    207 	if (!tnds)
    208 		return NULL;
    209 
    210 	str = xml_node_to_str(ctx->xml, tnds);
    211 	xml_node_free(ctx->xml, tnds);
    212 	if (str == NULL)
    213 		return NULL;
    214 	wpa_printf(MSG_INFO, "MgmtTree: %s", str);
    215 
    216 	return str;
    217 }
    218 
    219 
    220 static void add_item(struct hs20_osu_client *ctx, xml_node_t *parent,
    221 		     const char *locuri, const char *data)
    222 {
    223 	xml_node_t *item, *node;
    224 
    225 	item = xml_node_create(ctx->xml, parent, NULL, "Item");
    226 	oma_dm_add_locuri(ctx, item, "Source", locuri);
    227 	node = xml_node_create(ctx->xml, item, NULL, "Meta");
    228 	xml_node_create_text_ns(ctx->xml, node, "syncml:metinf", "Format",
    229 				"Chr");
    230 	xml_node_create_text_ns(ctx->xml, node, "syncml:metinf", "Type",
    231 				"text/plain");
    232 	xml_node_create_text(ctx->xml, item, NULL, "Data", data);
    233 }
    234 
    235 
    236 static void add_replace_devinfo(struct hs20_osu_client *ctx, xml_node_t *parent,
    237 				int cmdid)
    238 {
    239 	xml_node_t *info, *child, *replace;
    240 	const char *name;
    241 	char locuri[200], *txt;
    242 
    243 	info = node_from_file(ctx->xml, "devinfo.xml");
    244 	if (info == NULL) {
    245 		wpa_printf(MSG_INFO, "Could not read devinfo.xml");
    246 		return;
    247 	}
    248 
    249 	replace = xml_node_create(ctx->xml, parent, NULL, "Replace");
    250 	if (replace == NULL) {
    251 		xml_node_free(ctx->xml, info);
    252 		return;
    253 	}
    254 	oma_dm_add_cmdid(ctx, replace, cmdid);
    255 
    256 	xml_node_for_each_child(ctx->xml, child, info) {
    257 		xml_node_for_each_check(ctx->xml, child);
    258 		name = xml_node_get_localname(ctx->xml, child);
    259 		os_snprintf(locuri, sizeof(locuri), "./DevInfo/%s", name);
    260 		txt = xml_node_get_text(ctx->xml, child);
    261 		if (txt) {
    262 			add_item(ctx, replace, locuri, txt);
    263 			xml_node_get_text_free(ctx->xml, txt);
    264 		}
    265 	}
    266 
    267 	xml_node_free(ctx->xml, info);
    268 }
    269 
    270 
    271 static void oma_dm_add_hs20_generic_alert(struct hs20_osu_client *ctx,
    272 					  xml_node_t *syncbody,
    273 					  int cmdid, const char *oper,
    274 					  const char *data)
    275 {
    276 	xml_node_t *node, *item;
    277 	char buf[200];
    278 
    279 	node = add_alert(ctx, syncbody, cmdid, DM_GENERIC_ALERT);
    280 
    281 	item = xml_node_create(ctx->xml, node, NULL, "Item");
    282 	oma_dm_add_locuri(ctx, item, "Source", DM_URI_PPS);
    283 	node = xml_node_create(ctx->xml, item, NULL, "Meta");
    284 	snprintf(buf, sizeof(buf), "Reversed-Domain-Name: %s", oper);
    285 	xml_node_create_text_ns(ctx->xml, node, "syncml:metinf", "Type", buf);
    286 	xml_node_create_text_ns(ctx->xml, node, "syncml:metinf", "Format",
    287 				"xml");
    288 	xml_node_create_text(ctx->xml, item, NULL, "Data", data);
    289 }
    290 
    291 
    292 static xml_node_t * build_oma_dm_1(struct hs20_osu_client *ctx,
    293 				   const char *url, int msgid, const char *oper)
    294 {
    295 	xml_node_t *syncml, *syncbody;
    296 	char *str;
    297 	int cmdid = 0;
    298 
    299 	syncml = oma_dm_build_hdr(ctx, url, msgid);
    300 	if (syncml == NULL)
    301 		return NULL;
    302 
    303 	syncbody = xml_node_create(ctx->xml, syncml, NULL, "SyncBody");
    304 	if (syncbody == NULL) {
    305 		xml_node_free(ctx->xml, syncml);
    306 		return NULL;
    307 	}
    308 
    309 	cmdid++;
    310 	add_alert(ctx, syncbody, cmdid, DM_CLIENT_INITIATED_MGMT);
    311 
    312 	str = mo_str(ctx, NULL, "devdetail.xml");
    313 	if (str == NULL) {
    314 		xml_node_free(ctx->xml, syncml);
    315 		return NULL;
    316 	}
    317 	cmdid++;
    318 	oma_dm_add_hs20_generic_alert(ctx, syncbody, cmdid, oper, str);
    319 	os_free(str);
    320 
    321 	cmdid++;
    322 	add_replace_devinfo(ctx, syncbody, cmdid);
    323 
    324 	xml_node_create(ctx->xml, syncbody, NULL, "Final");
    325 
    326 	return syncml;
    327 }
    328 
    329 
    330 static xml_node_t * build_oma_dm_1_sub_reg(struct hs20_osu_client *ctx,
    331 					   const char *url, int msgid)
    332 {
    333 	xml_node_t *syncml;
    334 
    335 	syncml = build_oma_dm_1(ctx, url, msgid, DM_HS20_SUBSCRIPTION_CREATION);
    336 	if (syncml)
    337 		debug_dump_node(ctx, "OMA-DM Package 1 (sub reg)", syncml);
    338 
    339 	return syncml;
    340 }
    341 
    342 
    343 static xml_node_t * build_oma_dm_1_sub_prov(struct hs20_osu_client *ctx,
    344 					    const char *url, int msgid)
    345 {
    346 	xml_node_t *syncml;
    347 
    348 	syncml = build_oma_dm_1(ctx, url, msgid,
    349 				DM_HS20_SUBSCRIPTION_PROVISIONING);
    350 	if (syncml)
    351 		debug_dump_node(ctx, "OMA-DM Package 1 (sub prov)", syncml);
    352 
    353 	return syncml;
    354 }
    355 
    356 
    357 static xml_node_t * build_oma_dm_1_pol_upd(struct hs20_osu_client *ctx,
    358 					   const char *url, int msgid)
    359 {
    360 	xml_node_t *syncml;
    361 
    362 	syncml = build_oma_dm_1(ctx, url, msgid, DM_HS20_POLICY_UPDATE);
    363 	if (syncml)
    364 		debug_dump_node(ctx, "OMA-DM Package 1 (pol upd)", syncml);
    365 
    366 	return syncml;
    367 }
    368 
    369 
    370 static xml_node_t * build_oma_dm_1_sub_rem(struct hs20_osu_client *ctx,
    371 					   const char *url, int msgid)
    372 {
    373 	xml_node_t *syncml;
    374 
    375 	syncml = build_oma_dm_1(ctx, url, msgid,
    376 				DM_HS20_SUBSCRIPTION_REMEDIATION);
    377 	if (syncml)
    378 		debug_dump_node(ctx, "OMA-DM Package 1 (sub rem)", syncml);
    379 
    380 	return syncml;
    381 }
    382 
    383 
    384 static int oma_dm_exec_browser(struct hs20_osu_client *ctx, xml_node_t *exec)
    385 {
    386 	xml_node_t *node;
    387 	char *data;
    388 	int res;
    389 
    390 	node = get_node(ctx->xml, exec, "Item/Data");
    391 	if (node == NULL) {
    392 		wpa_printf(MSG_INFO, "No Data node found");
    393 		return DM_RESP_BAD_REQUEST;
    394 	}
    395 
    396 	data = xml_node_get_text(ctx->xml, node);
    397 	wpa_printf(MSG_INFO, "Data: %s", data);
    398 	wpa_printf(MSG_INFO, "Launch browser to URI '%s'", data);
    399 	write_summary(ctx, "Launch browser to URI '%s'", data);
    400 	res = hs20_web_browser(data);
    401 	xml_node_get_text_free(ctx->xml, data);
    402 	if (res > 0) {
    403 		wpa_printf(MSG_INFO, "User response in browser completed successfully");
    404 		write_summary(ctx, "User response in browser completed successfully");
    405 		return DM_RESP_OK;
    406 	} else {
    407 		wpa_printf(MSG_INFO, "Failed to receive user response");
    408 		write_summary(ctx, "Failed to receive user response");
    409 		return DM_RESP_COMMAND_FAILED;
    410 	}
    411 }
    412 
    413 
    414 static int oma_dm_exec_get_cert(struct hs20_osu_client *ctx, xml_node_t *exec)
    415 {
    416 	xml_node_t *node, *getcert;
    417 	char *data;
    418 	const char *name;
    419 	int res;
    420 
    421 	wpa_printf(MSG_INFO, "Client certificate enrollment");
    422 	write_summary(ctx, "Client certificate enrollment");
    423 
    424 	node = get_node(ctx->xml, exec, "Item/Data");
    425 	if (node == NULL) {
    426 		wpa_printf(MSG_INFO, "No Data node found");
    427 		return DM_RESP_BAD_REQUEST;
    428 	}
    429 
    430 	data = xml_node_get_text(ctx->xml, node);
    431 	wpa_printf(MSG_INFO, "Data: %s", data);
    432 	getcert = xml_node_from_buf(ctx->xml, data);
    433 	xml_node_get_text_free(ctx->xml, data);
    434 
    435 	if (getcert == NULL) {
    436 		wpa_printf(MSG_INFO, "Could not parse Item/Data node contents");
    437 		return DM_RESP_BAD_REQUEST;
    438 	}
    439 
    440 	debug_dump_node(ctx, "OMA-DM getCertificate", getcert);
    441 
    442 	name = xml_node_get_localname(ctx->xml, getcert);
    443 	if (name == NULL || os_strcasecmp(name, "getCertificate") != 0) {
    444 		wpa_printf(MSG_INFO, "Unexpected getCertificate node name '%s'",
    445 			   name);
    446 		return DM_RESP_BAD_REQUEST;
    447 	}
    448 
    449 	res = osu_get_certificate(ctx, getcert);
    450 
    451 	xml_node_free(ctx->xml, getcert);
    452 
    453 	return res == 0 ? DM_RESP_OK : DM_RESP_COMMAND_FAILED;
    454 }
    455 
    456 
    457 static int oma_dm_exec(struct hs20_osu_client *ctx, xml_node_t *exec)
    458 {
    459 	char *locuri;
    460 	int ret;
    461 
    462 	locuri = oma_dm_get_target_locuri(ctx, exec);
    463 	if (locuri == NULL) {
    464 		wpa_printf(MSG_INFO, "No Target LocURI node found");
    465 		return DM_RESP_BAD_REQUEST;
    466 	}
    467 
    468 	wpa_printf(MSG_INFO, "Target LocURI: %s", locuri);
    469 
    470 	if (os_strcasecmp(locuri, "./DevDetail/Ext/org.wi-fi/Wi-Fi/Ops/"
    471 			  "launchBrowserToURI") == 0) {
    472 		ret = oma_dm_exec_browser(ctx, exec);
    473 	} else if (os_strcasecmp(locuri, "./DevDetail/Ext/org.wi-fi/Wi-Fi/Ops/"
    474 			  "getCertificate") == 0) {
    475 		ret = oma_dm_exec_get_cert(ctx, exec);
    476 	} else {
    477 		wpa_printf(MSG_INFO, "Unsupported exec Target LocURI");
    478 		ret = DM_RESP_NOT_FOUND;
    479 	}
    480 	os_free(locuri);
    481 
    482 	return ret;
    483 }
    484 
    485 
    486 static int oma_dm_run_add(struct hs20_osu_client *ctx, const char *locuri,
    487 			  xml_node_t *add, xml_node_t *pps,
    488 			  const char *pps_fname)
    489 {
    490 	const char *pos;
    491 	size_t fqdn_len;
    492 	xml_node_t *node, *tnds, *unode, *pps_node;
    493 	char *data, *uri, *upos, *end;
    494 	int use_tnds = 0;
    495 	size_t uri_len;
    496 
    497 	wpa_printf(MSG_INFO, "Add command target LocURI: %s", locuri);
    498 
    499 	if (os_strncasecmp(locuri, "./Wi-Fi/", 8) != 0) {
    500 		wpa_printf(MSG_INFO, "Do not allow Add outside ./Wi-Fi");
    501 		return DM_RESP_PERMISSION_DENIED;
    502 	}
    503 	pos = locuri + 8;
    504 
    505 	if (ctx->fqdn == NULL)
    506 		return DM_RESP_COMMAND_FAILED;
    507 	fqdn_len = os_strlen(ctx->fqdn);
    508 	if (os_strncasecmp(pos, ctx->fqdn, fqdn_len) != 0 ||
    509 	    pos[fqdn_len] != '/') {
    510 		wpa_printf(MSG_INFO, "Do not allow Add outside ./Wi-Fi/%s",
    511 			   ctx->fqdn);
    512 		return DM_RESP_PERMISSION_DENIED;
    513 	}
    514 	pos += fqdn_len + 1;
    515 
    516 	if (os_strncasecmp(pos, "PerProviderSubscription/", 24) != 0) {
    517 		wpa_printf(MSG_INFO,
    518 			   "Do not allow Add outside ./Wi-Fi/%s/PerProviderSubscription",
    519 			   ctx->fqdn);
    520 		return DM_RESP_PERMISSION_DENIED;
    521 	}
    522 	pos += 24;
    523 
    524 	wpa_printf(MSG_INFO, "Add command for PPS node %s", pos);
    525 
    526 	pps_node = get_node(ctx->xml, pps, pos);
    527 	if (pps_node) {
    528 		wpa_printf(MSG_INFO, "Specified PPS node exists already");
    529 		return DM_RESP_ALREADY_EXISTS;
    530 	}
    531 
    532 	uri = os_strdup(pos);
    533 	if (uri == NULL)
    534 		return DM_RESP_COMMAND_FAILED;
    535 	while (!pps_node) {
    536 		upos = os_strrchr(uri, '/');
    537 		if (!upos)
    538 			break;
    539 		upos[0] = '\0';
    540 		pps_node = get_node(ctx->xml, pps, uri);
    541 		wpa_printf(MSG_INFO, "Node %s %s", uri,
    542 			   pps_node ? "exists" : "does not exist");
    543 	}
    544 
    545 	wpa_printf(MSG_INFO, "Parent URI: %s", uri);
    546 
    547 	if (!pps_node) {
    548 		/* Add at root of PPS MO */
    549 		pps_node = pps;
    550 	}
    551 
    552 	uri_len = os_strlen(uri);
    553 	os_strlcpy(uri, pos + uri_len, os_strlen(pos));
    554 	upos = uri;
    555 	while (*upos == '/')
    556 		upos++;
    557 	wpa_printf(MSG_INFO, "Nodes to add: %s", upos);
    558 
    559 	for (;;) {
    560 		end = os_strchr(upos, '/');
    561 		if (!end)
    562 			break;
    563 		*end = '\0';
    564 		wpa_printf(MSG_INFO, "Adding interim node %s", upos);
    565 		pps_node = xml_node_create(ctx->xml, pps_node, NULL, upos);
    566 		if (pps_node == NULL) {
    567 			os_free(uri);
    568 			return DM_RESP_COMMAND_FAILED;
    569 		}
    570 		upos = end + 1;
    571 	}
    572 
    573 	wpa_printf(MSG_INFO, "Adding node %s", upos);
    574 
    575 	node = get_node(ctx->xml, add, "Item/Meta/Type");
    576 	if (node) {
    577 		char *type;
    578 		type = xml_node_get_text(ctx->xml, node);
    579 		use_tnds = node &&
    580 			os_strstr(type, "application/vnd.syncml.dmtnds+xml");
    581 	}
    582 
    583 	node = get_node(ctx->xml, add, "Item/Data");
    584 	if (node == NULL) {
    585 		wpa_printf(MSG_INFO, "No Add/Item/Data found");
    586 		os_free(uri);
    587 		return DM_RESP_BAD_REQUEST;
    588 	}
    589 
    590 	data = xml_node_get_text(ctx->xml, node);
    591 	if (data == NULL) {
    592 		wpa_printf(MSG_INFO, "Could not get Add/Item/Data text");
    593 		os_free(uri);
    594 		return DM_RESP_BAD_REQUEST;
    595 	}
    596 
    597 	wpa_printf(MSG_DEBUG, "Add/Item/Data: %s", data);
    598 
    599 	if (use_tnds) {
    600 		tnds = xml_node_from_buf(ctx->xml, data);
    601 		xml_node_get_text_free(ctx->xml, data);
    602 		if (tnds == NULL) {
    603 			wpa_printf(MSG_INFO,
    604 				   "Could not parse Add/Item/Data text");
    605 			os_free(uri);
    606 			return DM_RESP_BAD_REQUEST;
    607 		}
    608 
    609 		unode = tnds_to_mo(ctx->xml, tnds);
    610 		xml_node_free(ctx->xml, tnds);
    611 		if (unode == NULL) {
    612 			wpa_printf(MSG_INFO, "Could not parse TNDS text");
    613 			os_free(uri);
    614 			return DM_RESP_BAD_REQUEST;
    615 		}
    616 
    617 		debug_dump_node(ctx, "Parsed TNDS", unode);
    618 
    619 		xml_node_add_child(ctx->xml, pps_node, unode);
    620 	} else {
    621 		/* TODO: What to do here? */
    622 		os_free(uri);
    623 		return DM_RESP_BAD_REQUEST;
    624 	}
    625 
    626 	os_free(uri);
    627 
    628 	if (update_pps_file(ctx, pps_fname, pps) < 0)
    629 		return DM_RESP_COMMAND_FAILED;
    630 
    631 	ctx->pps_updated = 1;
    632 
    633 	return DM_RESP_OK;
    634 }
    635 
    636 
    637 static int oma_dm_add(struct hs20_osu_client *ctx, xml_node_t *add,
    638 		      xml_node_t *pps, const char *pps_fname)
    639 {
    640 	xml_node_t *node;
    641 	char *locuri;
    642 	char fname[300];
    643 	int ret;
    644 
    645 	node = get_node(ctx->xml, add, "Item/Target/LocURI");
    646 	if (node == NULL) {
    647 		wpa_printf(MSG_INFO, "No Target LocURI node found");
    648 		return DM_RESP_BAD_REQUEST;
    649 	}
    650 	locuri = xml_node_get_text(ctx->xml, node);
    651 	wpa_printf(MSG_INFO, "Target LocURI: %s", locuri);
    652 	if (os_strncasecmp(locuri, "./Wi-Fi/", 8) != 0) {
    653 		wpa_printf(MSG_INFO, "Unsupported Add Target LocURI");
    654 		xml_node_get_text_free(ctx->xml, locuri);
    655 		return DM_RESP_PERMISSION_DENIED;
    656 	}
    657 
    658 	node = get_node(ctx->xml, add, "Item/Data");
    659 	if (node == NULL) {
    660 		wpa_printf(MSG_INFO, "No Data node found");
    661 		xml_node_get_text_free(ctx->xml, locuri);
    662 		return DM_RESP_BAD_REQUEST;
    663 	}
    664 
    665 	if (pps_fname && os_file_exists(pps_fname)) {
    666 		ret = oma_dm_run_add(ctx, locuri, add, pps, pps_fname);
    667 		if (ret != DM_RESP_OK) {
    668 			xml_node_get_text_free(ctx->xml, locuri);
    669 			return ret;
    670 		}
    671 		ret = 0;
    672 		os_strlcpy(fname, pps_fname, sizeof(fname));
    673 	} else
    674 		ret = hs20_add_pps_mo(ctx, locuri, node, fname, sizeof(fname));
    675 	xml_node_get_text_free(ctx->xml, locuri);
    676 	if (ret < 0)
    677 		return ret == -2 ? DM_RESP_ALREADY_EXISTS :
    678 			DM_RESP_COMMAND_FAILED;
    679 
    680 	if (ctx->no_reconnect == 2) {
    681 		os_snprintf(ctx->pps_fname, sizeof(ctx->pps_fname), "%s",
    682 			    fname);
    683 		ctx->pps_cred_set = 1;
    684 		return DM_RESP_OK;
    685 	}
    686 
    687 	wpa_printf(MSG_INFO, "Updating wpa_supplicant credentials");
    688 	cmd_set_pps(ctx, fname);
    689 
    690 	if (ctx->no_reconnect)
    691 		return DM_RESP_OK;
    692 
    693 	wpa_printf(MSG_INFO, "Requesting reconnection with updated configuration");
    694 	if (wpa_command(ctx->ifname, "INTERWORKING_SELECT auto") < 0)
    695 		wpa_printf(MSG_INFO, "Failed to request wpa_supplicant to reconnect");
    696 
    697 	return DM_RESP_OK;
    698 }
    699 
    700 
    701 static int oma_dm_replace(struct hs20_osu_client *ctx, xml_node_t *replace,
    702 			  xml_node_t *pps, const char *pps_fname)
    703 {
    704 	char *locuri, *pos;
    705 	size_t fqdn_len;
    706 	xml_node_t *node, *tnds, *unode, *pps_node, *parent;
    707 	char *data;
    708 	int use_tnds = 0;
    709 
    710 	locuri = oma_dm_get_target_locuri(ctx, replace);
    711 	if (locuri == NULL)
    712 		return DM_RESP_BAD_REQUEST;
    713 
    714 	wpa_printf(MSG_INFO, "Replace command target LocURI: %s", locuri);
    715 	if (os_strncasecmp(locuri, "./Wi-Fi/", 8) != 0) {
    716 		wpa_printf(MSG_INFO, "Do not allow Replace outside ./Wi-Fi");
    717 		os_free(locuri);
    718 		return DM_RESP_PERMISSION_DENIED;
    719 	}
    720 	pos = locuri + 8;
    721 
    722 	if (ctx->fqdn == NULL) {
    723 		os_free(locuri);
    724 		return DM_RESP_COMMAND_FAILED;
    725 	}
    726 	fqdn_len = os_strlen(ctx->fqdn);
    727 	if (os_strncasecmp(pos, ctx->fqdn, fqdn_len) != 0 ||
    728 	    pos[fqdn_len] != '/') {
    729 		wpa_printf(MSG_INFO, "Do not allow Replace outside ./Wi-Fi/%s",
    730 			   ctx->fqdn);
    731 		os_free(locuri);
    732 		return DM_RESP_PERMISSION_DENIED;
    733 	}
    734 	pos += fqdn_len + 1;
    735 
    736 	if (os_strncasecmp(pos, "PerProviderSubscription/", 24) != 0) {
    737 		wpa_printf(MSG_INFO,
    738 			   "Do not allow Replace outside ./Wi-Fi/%s/PerProviderSubscription",
    739 			   ctx->fqdn);
    740 		os_free(locuri);
    741 		return DM_RESP_PERMISSION_DENIED;
    742 	}
    743 	pos += 24;
    744 
    745 	wpa_printf(MSG_INFO, "Replace command for PPS node %s", pos);
    746 
    747 	pps_node = get_node(ctx->xml, pps, pos);
    748 	if (pps_node == NULL) {
    749 		wpa_printf(MSG_INFO, "Specified PPS node not found");
    750 		os_free(locuri);
    751 		return DM_RESP_NOT_FOUND;
    752 	}
    753 
    754 	node = get_node(ctx->xml, replace, "Item/Meta/Type");
    755 	if (node) {
    756 		char *type;
    757 		type = xml_node_get_text(ctx->xml, node);
    758 		use_tnds = node &&
    759 			os_strstr(type, "application/vnd.syncml.dmtnds+xml");
    760 	}
    761 
    762 	node = get_node(ctx->xml, replace, "Item/Data");
    763 	if (node == NULL) {
    764 		wpa_printf(MSG_INFO, "No Replace/Item/Data found");
    765 		os_free(locuri);
    766 		return DM_RESP_BAD_REQUEST;
    767 	}
    768 
    769 	data = xml_node_get_text(ctx->xml, node);
    770 	if (data == NULL) {
    771 		wpa_printf(MSG_INFO, "Could not get Replace/Item/Data text");
    772 		os_free(locuri);
    773 		return DM_RESP_BAD_REQUEST;
    774 	}
    775 
    776 	wpa_printf(MSG_DEBUG, "Replace/Item/Data: %s", data);
    777 
    778 	if (use_tnds) {
    779 		tnds = xml_node_from_buf(ctx->xml, data);
    780 		xml_node_get_text_free(ctx->xml, data);
    781 		if (tnds == NULL) {
    782 			wpa_printf(MSG_INFO,
    783 				   "Could not parse Replace/Item/Data text");
    784 			os_free(locuri);
    785 			return DM_RESP_BAD_REQUEST;
    786 		}
    787 
    788 		unode = tnds_to_mo(ctx->xml, tnds);
    789 		xml_node_free(ctx->xml, tnds);
    790 		if (unode == NULL) {
    791 			wpa_printf(MSG_INFO, "Could not parse TNDS text");
    792 			os_free(locuri);
    793 			return DM_RESP_BAD_REQUEST;
    794 		}
    795 
    796 		debug_dump_node(ctx, "Parsed TNDS", unode);
    797 
    798 		parent = xml_node_get_parent(ctx->xml, pps_node);
    799 		xml_node_detach(ctx->xml, pps_node);
    800 		xml_node_add_child(ctx->xml, parent, unode);
    801 	} else {
    802 		xml_node_set_text(ctx->xml, pps_node, data);
    803 		xml_node_get_text_free(ctx->xml, data);
    804 	}
    805 
    806 	os_free(locuri);
    807 
    808 	if (update_pps_file(ctx, pps_fname, pps) < 0)
    809 		return DM_RESP_COMMAND_FAILED;
    810 
    811 	ctx->pps_updated = 1;
    812 
    813 	return DM_RESP_OK;
    814 }
    815 
    816 
    817 static int oma_dm_get(struct hs20_osu_client *ctx, xml_node_t *get,
    818 		      xml_node_t *pps, const char *pps_fname, char **value)
    819 {
    820 	char *locuri, *pos;
    821 	size_t fqdn_len;
    822 	xml_node_t *pps_node;
    823 	const char *name;
    824 
    825 	*value = NULL;
    826 
    827 	locuri = oma_dm_get_target_locuri(ctx, get);
    828 	if (locuri == NULL)
    829 		return DM_RESP_BAD_REQUEST;
    830 
    831 	wpa_printf(MSG_INFO, "Get command target LocURI: %s", locuri);
    832 	if (os_strncasecmp(locuri, "./Wi-Fi/", 8) != 0) {
    833 		wpa_printf(MSG_INFO, "Do not allow Get outside ./Wi-Fi");
    834 		os_free(locuri);
    835 		return DM_RESP_PERMISSION_DENIED;
    836 	}
    837 	pos = locuri + 8;
    838 
    839 	if (ctx->fqdn == NULL)
    840 		return DM_RESP_COMMAND_FAILED;
    841 	fqdn_len = os_strlen(ctx->fqdn);
    842 	if (os_strncasecmp(pos, ctx->fqdn, fqdn_len) != 0 ||
    843 	    pos[fqdn_len] != '/') {
    844 		wpa_printf(MSG_INFO, "Do not allow Get outside ./Wi-Fi/%s",
    845 			   ctx->fqdn);
    846 		os_free(locuri);
    847 		return DM_RESP_PERMISSION_DENIED;
    848 	}
    849 	pos += fqdn_len + 1;
    850 
    851 	if (os_strncasecmp(pos, "PerProviderSubscription/", 24) != 0) {
    852 		wpa_printf(MSG_INFO,
    853 			   "Do not allow Get outside ./Wi-Fi/%s/PerProviderSubscription",
    854 			   ctx->fqdn);
    855 		os_free(locuri);
    856 		return DM_RESP_PERMISSION_DENIED;
    857 	}
    858 	pos += 24;
    859 
    860 	wpa_printf(MSG_INFO, "Get command for PPS node %s", pos);
    861 
    862 	pps_node = get_node(ctx->xml, pps, pos);
    863 	if (pps_node == NULL) {
    864 		wpa_printf(MSG_INFO, "Specified PPS node not found");
    865 		os_free(locuri);
    866 		return DM_RESP_NOT_FOUND;
    867 	}
    868 
    869 	name = xml_node_get_localname(ctx->xml, pps_node);
    870 	wpa_printf(MSG_INFO, "Get command returned node with name '%s'", name);
    871 	if (os_strcasecmp(name, "Password") == 0) {
    872 		wpa_printf(MSG_INFO, "Do not allow Get for Password node");
    873 		os_free(locuri);
    874 		return DM_RESP_PERMISSION_DENIED;
    875 	}
    876 
    877 	/*
    878 	 * TODO: No support for DMTNDS, so if interior node, reply with a
    879 	 * list of children node names in Results element. The child list type is
    880 	 * defined in [DMTND].
    881 	 */
    882 
    883 	*value = xml_node_get_text(ctx->xml, pps_node);
    884 	if (*value == NULL)
    885 		return DM_RESP_COMMAND_FAILED;
    886 
    887 	return DM_RESP_OK;
    888 }
    889 
    890 
    891 static int oma_dm_get_cmdid(struct hs20_osu_client *ctx, xml_node_t *node)
    892 {
    893 	xml_node_t *cnode;
    894 	char *str;
    895 	int ret;
    896 
    897 	cnode = get_node(ctx->xml, node, "CmdID");
    898 	if (cnode == NULL)
    899 		return 0;
    900 
    901 	str = xml_node_get_text(ctx->xml, cnode);
    902 	if (str == NULL)
    903 		return 0;
    904 	ret = atoi(str);
    905 	xml_node_get_text_free(ctx->xml, str);
    906 	return ret;
    907 }
    908 
    909 
    910 static xml_node_t * oma_dm_send_recv(struct hs20_osu_client *ctx,
    911 				     const char *url, xml_node_t *syncml,
    912 				     const char *ext_hdr,
    913 				     const char *username, const char *password,
    914 				     const char *client_cert,
    915 				     const char *client_key)
    916 {
    917 	xml_node_t *resp;
    918 	char *str, *res;
    919 	char *resp_uri = NULL;
    920 
    921 	str = xml_node_to_str(ctx->xml, syncml);
    922 	xml_node_free(ctx->xml, syncml);
    923 	if (str == NULL)
    924 		return NULL;
    925 
    926 	wpa_printf(MSG_INFO, "Send OMA DM Package");
    927 	write_summary(ctx, "Send OMA DM Package");
    928 	os_free(ctx->server_url);
    929 	ctx->server_url = os_strdup(url);
    930 	res = http_post(ctx->http, url, str, "application/vnd.syncml.dm+xml",
    931 			ext_hdr, ctx->ca_fname, username, password,
    932 			client_cert, client_key, NULL);
    933 	os_free(str);
    934 	os_free(resp_uri);
    935 	resp_uri = NULL;
    936 
    937 	if (res == NULL) {
    938 		const char *err = http_get_err(ctx->http);
    939 		if (err) {
    940 			wpa_printf(MSG_INFO, "HTTP error: %s", err);
    941 			write_result(ctx, "HTTP error: %s", err);
    942 		} else {
    943 			write_summary(ctx, "Failed to send OMA DM Package");
    944 		}
    945 		return NULL;
    946 	}
    947 	wpa_printf(MSG_DEBUG, "Server response: %s", res);
    948 
    949 	wpa_printf(MSG_INFO, "Process OMA DM Package");
    950 	write_summary(ctx, "Process received OMA DM Package");
    951 	resp = xml_node_from_buf(ctx->xml, res);
    952 	os_free(res);
    953 	if (resp == NULL) {
    954 		wpa_printf(MSG_INFO, "Failed to parse OMA DM response");
    955 		return NULL;
    956 	}
    957 
    958 	debug_dump_node(ctx, "OMA DM Package", resp);
    959 
    960 	return resp;
    961 }
    962 
    963 
    964 static xml_node_t * oma_dm_process(struct hs20_osu_client *ctx, const char *url,
    965 				   xml_node_t *resp, int msgid,
    966 				   char **ret_resp_uri,
    967 				   xml_node_t *pps, const char *pps_fname)
    968 {
    969 	xml_node_t *syncml, *syncbody, *hdr, *body, *child;
    970 	const char *name;
    971 	char *resp_uri = NULL;
    972 	int server_msgid = 0;
    973 	int cmdid = 0;
    974 	int server_cmdid;
    975 	int resp_needed = 0;
    976 	char *tmp;
    977 	int final = 0;
    978 	char *locuri;
    979 
    980 	*ret_resp_uri = NULL;
    981 
    982 	name = xml_node_get_localname(ctx->xml, resp);
    983 	if (name == NULL || os_strcasecmp(name, "SyncML") != 0) {
    984 		wpa_printf(MSG_INFO, "SyncML node not found");
    985 		return NULL;
    986 	}
    987 
    988 	hdr = get_node(ctx->xml, resp, "SyncHdr");
    989 	body = get_node(ctx->xml, resp, "SyncBody");
    990 	if (hdr == NULL || body == NULL) {
    991 		wpa_printf(MSG_INFO, "Could not find SyncHdr or SyncBody");
    992 		return NULL;
    993 	}
    994 
    995 	xml_node_for_each_child(ctx->xml, child, hdr) {
    996 		xml_node_for_each_check(ctx->xml, child);
    997 		name = xml_node_get_localname(ctx->xml, child);
    998 		wpa_printf(MSG_INFO, "SyncHdr %s", name);
    999 		if (os_strcasecmp(name, "RespURI") == 0) {
   1000 			tmp = xml_node_get_text(ctx->xml, child);
   1001 			if (tmp)
   1002 				resp_uri = os_strdup(tmp);
   1003 			xml_node_get_text_free(ctx->xml, tmp);
   1004 		} else if (os_strcasecmp(name, "MsgID") == 0) {
   1005 			tmp = xml_node_get_text(ctx->xml, child);
   1006 			if (tmp)
   1007 				server_msgid = atoi(tmp);
   1008 			xml_node_get_text_free(ctx->xml, tmp);
   1009 		}
   1010 	}
   1011 
   1012 	wpa_printf(MSG_INFO, "Server MsgID: %d", server_msgid);
   1013 	if (resp_uri)
   1014 		wpa_printf(MSG_INFO, "RespURI: %s", resp_uri);
   1015 
   1016 	syncml = oma_dm_build_hdr(ctx, resp_uri ? resp_uri : url, msgid);
   1017 	if (syncml == NULL) {
   1018 		os_free(resp_uri);
   1019 		return NULL;
   1020 	}
   1021 
   1022 	syncbody = xml_node_create(ctx->xml, syncml, NULL, "SyncBody");
   1023 	cmdid++;
   1024 	add_status(ctx, syncbody, server_msgid, 0, cmdid, "SyncHdr",
   1025 		   DM_RESP_AUTH_ACCEPTED, NULL);
   1026 
   1027 	xml_node_for_each_child(ctx->xml, child, body) {
   1028 		xml_node_for_each_check(ctx->xml, child);
   1029 		server_cmdid = oma_dm_get_cmdid(ctx, child);
   1030 		name = xml_node_get_localname(ctx->xml, child);
   1031 		wpa_printf(MSG_INFO, "SyncBody CmdID=%d - %s",
   1032 			   server_cmdid, name);
   1033 		if (os_strcasecmp(name, "Exec") == 0) {
   1034 			int res = oma_dm_exec(ctx, child);
   1035 			cmdid++;
   1036 			locuri = oma_dm_get_target_locuri(ctx, child);
   1037 			if (locuri == NULL)
   1038 				res = DM_RESP_BAD_REQUEST;
   1039 			add_status(ctx, syncbody, server_msgid, server_cmdid,
   1040 				   cmdid, name, res, locuri);
   1041 			os_free(locuri);
   1042 			resp_needed = 1;
   1043 		} else if (os_strcasecmp(name, "Add") == 0) {
   1044 			int res = oma_dm_add(ctx, child, pps, pps_fname);
   1045 			cmdid++;
   1046 			locuri = oma_dm_get_target_locuri(ctx, child);
   1047 			if (locuri == NULL)
   1048 				res = DM_RESP_BAD_REQUEST;
   1049 			add_status(ctx, syncbody, server_msgid, server_cmdid,
   1050 				   cmdid, name, res, locuri);
   1051 			os_free(locuri);
   1052 			resp_needed = 1;
   1053 		} else if (os_strcasecmp(name, "Replace") == 0) {
   1054 			int res;
   1055 			res = oma_dm_replace(ctx, child, pps, pps_fname);
   1056 			cmdid++;
   1057 			locuri = oma_dm_get_target_locuri(ctx, child);
   1058 			if (locuri == NULL)
   1059 				res = DM_RESP_BAD_REQUEST;
   1060 			add_status(ctx, syncbody, server_msgid, server_cmdid,
   1061 				   cmdid, name, res, locuri);
   1062 			os_free(locuri);
   1063 			resp_needed = 1;
   1064 		} else if (os_strcasecmp(name, "Status") == 0) {
   1065 			/* TODO: Verify success */
   1066 		} else if (os_strcasecmp(name, "Get") == 0) {
   1067 			int res;
   1068 			char *value;
   1069 			res = oma_dm_get(ctx, child, pps, pps_fname, &value);
   1070 			cmdid++;
   1071 			locuri = oma_dm_get_target_locuri(ctx, child);
   1072 			if (locuri == NULL)
   1073 				res = DM_RESP_BAD_REQUEST;
   1074 			add_status(ctx, syncbody, server_msgid, server_cmdid,
   1075 				   cmdid, name, res, locuri);
   1076 			if (res == DM_RESP_OK && value) {
   1077 				cmdid++;
   1078 				add_results(ctx, syncbody, server_msgid,
   1079 					    server_cmdid, cmdid, locuri, value);
   1080 			}
   1081 			os_free(locuri);
   1082 			xml_node_get_text_free(ctx->xml, value);
   1083 			resp_needed = 1;
   1084 #if 0 /* TODO: MUST support */
   1085 		} else if (os_strcasecmp(name, "Delete") == 0) {
   1086 #endif
   1087 #if 0 /* TODO: MUST support */
   1088 		} else if (os_strcasecmp(name, "Sequence") == 0) {
   1089 #endif
   1090 		} else if (os_strcasecmp(name, "Final") == 0) {
   1091 			final = 1;
   1092 			break;
   1093 		} else {
   1094 			locuri = oma_dm_get_target_locuri(ctx, child);
   1095 			add_status(ctx, syncbody, server_msgid, server_cmdid,
   1096 				   cmdid, name, DM_RESP_COMMAND_NOT_IMPLEMENTED,
   1097 				   locuri);
   1098 			os_free(locuri);
   1099 			resp_needed = 1;
   1100 		}
   1101 	}
   1102 
   1103 	if (!final) {
   1104 		wpa_printf(MSG_INFO, "Final node not found");
   1105 		xml_node_free(ctx->xml, syncml);
   1106 		os_free(resp_uri);
   1107 		return NULL;
   1108 	}
   1109 
   1110 	if (!resp_needed) {
   1111 		wpa_printf(MSG_INFO, "Exchange completed - no response needed");
   1112 		xml_node_free(ctx->xml, syncml);
   1113 		os_free(resp_uri);
   1114 		return NULL;
   1115 	}
   1116 
   1117 	xml_node_create(ctx->xml, syncbody, NULL, "Final");
   1118 
   1119 	debug_dump_node(ctx, "OMA-DM Package 3", syncml);
   1120 
   1121 	*ret_resp_uri = resp_uri;
   1122 	return syncml;
   1123 }
   1124 
   1125 
   1126 int cmd_oma_dm_prov(struct hs20_osu_client *ctx, const char *url)
   1127 {
   1128 	xml_node_t *syncml, *resp;
   1129 	char *resp_uri = NULL;
   1130 	int msgid = 0;
   1131 
   1132 	if (url == NULL) {
   1133 		wpa_printf(MSG_INFO, "Invalid prov command (missing URL)");
   1134 		return -1;
   1135 	}
   1136 
   1137 	wpa_printf(MSG_INFO, "OMA-DM credential provisioning requested");
   1138 	write_summary(ctx, "OMA-DM credential provisioning");
   1139 
   1140 	msgid++;
   1141 	syncml = build_oma_dm_1_sub_reg(ctx, url, msgid);
   1142 	if (syncml == NULL)
   1143 		return -1;
   1144 
   1145 	while (syncml) {
   1146 		resp = oma_dm_send_recv(ctx, resp_uri ? resp_uri : url,
   1147 					syncml, NULL, NULL, NULL, NULL, NULL);
   1148 		if (resp == NULL)
   1149 			return -1;
   1150 
   1151 		msgid++;
   1152 		syncml = oma_dm_process(ctx, url, resp, msgid, &resp_uri,
   1153 					NULL, NULL);
   1154 		xml_node_free(ctx->xml, resp);
   1155 	}
   1156 
   1157 	os_free(resp_uri);
   1158 
   1159 	return ctx->pps_cred_set ? 0 : -1;
   1160 }
   1161 
   1162 
   1163 int cmd_oma_dm_sim_prov(struct hs20_osu_client *ctx, const char *url)
   1164 {
   1165 	xml_node_t *syncml, *resp;
   1166 	char *resp_uri = NULL;
   1167 	int msgid = 0;
   1168 
   1169 	if (url == NULL) {
   1170 		wpa_printf(MSG_INFO, "Invalid prov command (missing URL)");
   1171 		return -1;
   1172 	}
   1173 
   1174 	wpa_printf(MSG_INFO, "OMA-DM SIM provisioning requested");
   1175 	ctx->no_reconnect = 2;
   1176 
   1177 	wpa_printf(MSG_INFO, "Wait for IP address before starting SIM provisioning");
   1178 	write_summary(ctx, "Wait for IP address before starting SIM provisioning");
   1179 
   1180 	if (wait_ip_addr(ctx->ifname, 15) < 0) {
   1181 		wpa_printf(MSG_INFO, "Could not get IP address for WLAN - try connection anyway");
   1182 	}
   1183 	write_summary(ctx, "OMA-DM SIM provisioning");
   1184 
   1185 	msgid++;
   1186 	syncml = build_oma_dm_1_sub_prov(ctx, url, msgid);
   1187 	if (syncml == NULL)
   1188 		return -1;
   1189 
   1190 	while (syncml) {
   1191 		resp = oma_dm_send_recv(ctx, resp_uri ? resp_uri : url,
   1192 					syncml, NULL, NULL, NULL, NULL, NULL);
   1193 		if (resp == NULL)
   1194 			return -1;
   1195 
   1196 		msgid++;
   1197 		syncml = oma_dm_process(ctx, url, resp, msgid, &resp_uri,
   1198 					NULL, NULL);
   1199 		xml_node_free(ctx->xml, resp);
   1200 	}
   1201 
   1202 	os_free(resp_uri);
   1203 
   1204 	if (ctx->pps_cred_set) {
   1205 		wpa_printf(MSG_INFO, "Updating wpa_supplicant credentials");
   1206 		cmd_set_pps(ctx, ctx->pps_fname);
   1207 
   1208 		wpa_printf(MSG_INFO, "Requesting reconnection with updated configuration");
   1209 		write_summary(ctx, "Requesting reconnection with updated configuration");
   1210 		if (wpa_command(ctx->ifname, "INTERWORKING_SELECT auto") < 0) {
   1211 			wpa_printf(MSG_INFO, "Failed to request wpa_supplicant to reconnect");
   1212 			write_summary(ctx, "Failed to request wpa_supplicant to reconnect");
   1213 			return -1;
   1214 		}
   1215 	}
   1216 
   1217 	return ctx->pps_cred_set ? 0 : -1;
   1218 }
   1219 
   1220 
   1221 void oma_dm_pol_upd(struct hs20_osu_client *ctx, const char *address,
   1222 		    const char *pps_fname,
   1223 		    const char *client_cert, const char *client_key,
   1224 		    const char *cred_username, const char *cred_password,
   1225 		    xml_node_t *pps)
   1226 {
   1227 	xml_node_t *syncml, *resp;
   1228 	char *resp_uri = NULL;
   1229 	int msgid = 0;
   1230 
   1231 	wpa_printf(MSG_INFO, "OMA-DM policy update");
   1232 	write_summary(ctx, "OMA-DM policy update");
   1233 
   1234 	msgid++;
   1235 	syncml = build_oma_dm_1_pol_upd(ctx, address, msgid);
   1236 	if (syncml == NULL)
   1237 		return;
   1238 
   1239 	while (syncml) {
   1240 		resp = oma_dm_send_recv(ctx, resp_uri ? resp_uri : address,
   1241 					syncml, NULL, cred_username,
   1242 					cred_password, client_cert, client_key);
   1243 		if (resp == NULL)
   1244 			return;
   1245 
   1246 		msgid++;
   1247 		syncml = oma_dm_process(ctx, address, resp, msgid, &resp_uri,
   1248 					pps, pps_fname);
   1249 		xml_node_free(ctx->xml, resp);
   1250 	}
   1251 
   1252 	os_free(resp_uri);
   1253 
   1254 	if (ctx->pps_updated) {
   1255 		wpa_printf(MSG_INFO, "Update wpa_supplicant credential based on updated PPS MO");
   1256 		write_summary(ctx, "Update wpa_supplicant credential based on updated PPS MO and request connection");
   1257 		cmd_set_pps(ctx, pps_fname);
   1258 		if (wpa_command(ctx->ifname, "INTERWORKING_SELECT auto") < 0) {
   1259 			wpa_printf(MSG_INFO,
   1260 				   "Failed to request wpa_supplicant to reconnect");
   1261 			write_summary(ctx,
   1262 				      "Failed to request wpa_supplicant to reconnect");
   1263 		}
   1264 	}
   1265 }
   1266 
   1267 
   1268 void oma_dm_sub_rem(struct hs20_osu_client *ctx, const char *address,
   1269 		    const char *pps_fname,
   1270 		    const char *client_cert, const char *client_key,
   1271 		    const char *cred_username, const char *cred_password,
   1272 		    xml_node_t *pps)
   1273 {
   1274 	xml_node_t *syncml, *resp;
   1275 	char *resp_uri = NULL;
   1276 	int msgid = 0;
   1277 
   1278 	wpa_printf(MSG_INFO, "OMA-DM subscription remediation");
   1279 	write_summary(ctx, "OMA-DM subscription remediation");
   1280 
   1281 	msgid++;
   1282 	syncml = build_oma_dm_1_sub_rem(ctx, address, msgid);
   1283 	if (syncml == NULL)
   1284 		return;
   1285 
   1286 	while (syncml) {
   1287 		resp = oma_dm_send_recv(ctx, resp_uri ? resp_uri : address,
   1288 					syncml, NULL, cred_username,
   1289 					cred_password, client_cert, client_key);
   1290 		if (resp == NULL)
   1291 			return;
   1292 
   1293 		msgid++;
   1294 		syncml = oma_dm_process(ctx, address, resp, msgid, &resp_uri,
   1295 					pps, pps_fname);
   1296 		xml_node_free(ctx->xml, resp);
   1297 	}
   1298 
   1299 	os_free(resp_uri);
   1300 
   1301 	wpa_printf(MSG_INFO, "Update wpa_supplicant credential based on updated PPS MO and request reconnection");
   1302 	write_summary(ctx, "Update wpa_supplicant credential based on updated PPS MO and request reconnection");
   1303 	cmd_set_pps(ctx, pps_fname);
   1304 	if (wpa_command(ctx->ifname, "INTERWORKING_SELECT auto") < 0) {
   1305 		wpa_printf(MSG_INFO, "Failed to request wpa_supplicant to reconnect");
   1306 		write_summary(ctx, "Failed to request wpa_supplicant to reconnect");
   1307 	}
   1308 }
   1309 
   1310 
   1311 void cmd_oma_dm_add(struct hs20_osu_client *ctx, const char *pps_fname,
   1312 		    const char *add_fname)
   1313 {
   1314 	xml_node_t *pps, *add;
   1315 	int res;
   1316 
   1317 	ctx->fqdn = os_strdup("wi-fi.org");
   1318 
   1319 	pps = node_from_file(ctx->xml, pps_fname);
   1320 	if (pps == NULL) {
   1321 		wpa_printf(MSG_INFO, "PPS file %s could not be parsed",
   1322 			   pps_fname);
   1323 		return;
   1324 	}
   1325 
   1326 	add = node_from_file(ctx->xml, add_fname);
   1327 	if (add == NULL) {
   1328 		wpa_printf(MSG_INFO, "Add file %s could not be parsed",
   1329 			   add_fname);
   1330 		xml_node_free(ctx->xml, pps);
   1331 		return;
   1332 	}
   1333 
   1334 	res = oma_dm_add(ctx, add, pps, pps_fname);
   1335 	wpa_printf(MSG_INFO, "oma_dm_add --> %d", res);
   1336 
   1337 	xml_node_free(ctx->xml, pps);
   1338 	xml_node_free(ctx->xml, add);
   1339 }
   1340 
   1341 
   1342 void cmd_oma_dm_replace(struct hs20_osu_client *ctx, const char *pps_fname,
   1343 			const char *replace_fname)
   1344 {
   1345 	xml_node_t *pps, *replace;
   1346 	int res;
   1347 
   1348 	ctx->fqdn = os_strdup("wi-fi.org");
   1349 
   1350 	pps = node_from_file(ctx->xml, pps_fname);
   1351 	if (pps == NULL) {
   1352 		wpa_printf(MSG_INFO, "PPS file %s could not be parsed",
   1353 			   pps_fname);
   1354 		return;
   1355 	}
   1356 
   1357 	replace = node_from_file(ctx->xml, replace_fname);
   1358 	if (replace == NULL) {
   1359 		wpa_printf(MSG_INFO, "Replace file %s could not be parsed",
   1360 			   replace_fname);
   1361 		xml_node_free(ctx->xml, pps);
   1362 		return;
   1363 	}
   1364 
   1365 	res = oma_dm_replace(ctx, replace, pps, pps_fname);
   1366 	wpa_printf(MSG_INFO, "oma_dm_replace --> %d", res);
   1367 
   1368 	xml_node_free(ctx->xml, pps);
   1369 	xml_node_free(ctx->xml, replace);
   1370 }
   1371