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