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 	if (data == NULL) {
    398 		wpa_printf(MSG_INFO, "Invalid data");
    399 		return DM_RESP_BAD_REQUEST;
    400 	}
    401 	wpa_printf(MSG_INFO, "Data: %s", data);
    402 	wpa_printf(MSG_INFO, "Launch browser to URI '%s'", data);
    403 	write_summary(ctx, "Launch browser to URI '%s'", data);
    404 	res = hs20_web_browser(data);
    405 	xml_node_get_text_free(ctx->xml, data);
    406 	if (res > 0) {
    407 		wpa_printf(MSG_INFO, "User response in browser completed successfully");
    408 		write_summary(ctx, "User response in browser completed successfully");
    409 		return DM_RESP_OK;
    410 	} else {
    411 		wpa_printf(MSG_INFO, "Failed to receive user response");
    412 		write_summary(ctx, "Failed to receive user response");
    413 		return DM_RESP_COMMAND_FAILED;
    414 	}
    415 }
    416 
    417 
    418 static int oma_dm_exec_get_cert(struct hs20_osu_client *ctx, xml_node_t *exec)
    419 {
    420 	xml_node_t *node, *getcert;
    421 	char *data;
    422 	const char *name;
    423 	int res;
    424 
    425 	wpa_printf(MSG_INFO, "Client certificate enrollment");
    426 	write_summary(ctx, "Client certificate enrollment");
    427 
    428 	node = get_node(ctx->xml, exec, "Item/Data");
    429 	if (node == NULL) {
    430 		wpa_printf(MSG_INFO, "No Data node found");
    431 		return DM_RESP_BAD_REQUEST;
    432 	}
    433 
    434 	data = xml_node_get_text(ctx->xml, node);
    435 	if (data == NULL) {
    436 		wpa_printf(MSG_INFO, "Invalid data");
    437 		return DM_RESP_BAD_REQUEST;
    438 	}
    439 	wpa_printf(MSG_INFO, "Data: %s", data);
    440 	getcert = xml_node_from_buf(ctx->xml, data);
    441 	xml_node_get_text_free(ctx->xml, data);
    442 
    443 	if (getcert == NULL) {
    444 		wpa_printf(MSG_INFO, "Could not parse Item/Data node contents");
    445 		return DM_RESP_BAD_REQUEST;
    446 	}
    447 
    448 	debug_dump_node(ctx, "OMA-DM getCertificate", getcert);
    449 
    450 	name = xml_node_get_localname(ctx->xml, getcert);
    451 	if (name == NULL || os_strcasecmp(name, "getCertificate") != 0) {
    452 		wpa_printf(MSG_INFO, "Unexpected getCertificate node name '%s'",
    453 			   name);
    454 		return DM_RESP_BAD_REQUEST;
    455 	}
    456 
    457 	res = osu_get_certificate(ctx, getcert);
    458 
    459 	xml_node_free(ctx->xml, getcert);
    460 
    461 	return res == 0 ? DM_RESP_OK : DM_RESP_COMMAND_FAILED;
    462 }
    463 
    464 
    465 static int oma_dm_exec(struct hs20_osu_client *ctx, xml_node_t *exec)
    466 {
    467 	char *locuri;
    468 	int ret;
    469 
    470 	locuri = oma_dm_get_target_locuri(ctx, exec);
    471 	if (locuri == NULL) {
    472 		wpa_printf(MSG_INFO, "No Target LocURI node found");
    473 		return DM_RESP_BAD_REQUEST;
    474 	}
    475 
    476 	wpa_printf(MSG_INFO, "Target LocURI: %s", locuri);
    477 
    478 	if (os_strcasecmp(locuri, "./DevDetail/Ext/org.wi-fi/Wi-Fi/Ops/"
    479 			  "launchBrowserToURI") == 0) {
    480 		ret = oma_dm_exec_browser(ctx, exec);
    481 	} else if (os_strcasecmp(locuri, "./DevDetail/Ext/org.wi-fi/Wi-Fi/Ops/"
    482 			  "getCertificate") == 0) {
    483 		ret = oma_dm_exec_get_cert(ctx, exec);
    484 	} else {
    485 		wpa_printf(MSG_INFO, "Unsupported exec Target LocURI");
    486 		ret = DM_RESP_NOT_FOUND;
    487 	}
    488 	os_free(locuri);
    489 
    490 	return ret;
    491 }
    492 
    493 
    494 static int oma_dm_run_add(struct hs20_osu_client *ctx, const char *locuri,
    495 			  xml_node_t *add, xml_node_t *pps,
    496 			  const char *pps_fname)
    497 {
    498 	const char *pos;
    499 	size_t fqdn_len;
    500 	xml_node_t *node, *tnds, *unode, *pps_node;
    501 	char *data, *uri, *upos, *end;
    502 	int use_tnds = 0;
    503 	size_t uri_len;
    504 
    505 	wpa_printf(MSG_INFO, "Add command target LocURI: %s", locuri);
    506 
    507 	if (os_strncasecmp(locuri, "./Wi-Fi/", 8) != 0) {
    508 		wpa_printf(MSG_INFO, "Do not allow Add outside ./Wi-Fi");
    509 		return DM_RESP_PERMISSION_DENIED;
    510 	}
    511 	pos = locuri + 8;
    512 
    513 	if (ctx->fqdn == NULL)
    514 		return DM_RESP_COMMAND_FAILED;
    515 	fqdn_len = os_strlen(ctx->fqdn);
    516 	if (os_strncasecmp(pos, ctx->fqdn, fqdn_len) != 0 ||
    517 	    pos[fqdn_len] != '/') {
    518 		wpa_printf(MSG_INFO, "Do not allow Add outside ./Wi-Fi/%s",
    519 			   ctx->fqdn);
    520 		return DM_RESP_PERMISSION_DENIED;
    521 	}
    522 	pos += fqdn_len + 1;
    523 
    524 	if (os_strncasecmp(pos, "PerProviderSubscription/", 24) != 0) {
    525 		wpa_printf(MSG_INFO,
    526 			   "Do not allow Add outside ./Wi-Fi/%s/PerProviderSubscription",
    527 			   ctx->fqdn);
    528 		return DM_RESP_PERMISSION_DENIED;
    529 	}
    530 	pos += 24;
    531 
    532 	wpa_printf(MSG_INFO, "Add command for PPS node %s", pos);
    533 
    534 	pps_node = get_node(ctx->xml, pps, pos);
    535 	if (pps_node) {
    536 		wpa_printf(MSG_INFO, "Specified PPS node exists already");
    537 		return DM_RESP_ALREADY_EXISTS;
    538 	}
    539 
    540 	uri = os_strdup(pos);
    541 	if (uri == NULL)
    542 		return DM_RESP_COMMAND_FAILED;
    543 	while (!pps_node) {
    544 		upos = os_strrchr(uri, '/');
    545 		if (!upos)
    546 			break;
    547 		upos[0] = '\0';
    548 		pps_node = get_node(ctx->xml, pps, uri);
    549 		wpa_printf(MSG_INFO, "Node %s %s", uri,
    550 			   pps_node ? "exists" : "does not exist");
    551 	}
    552 
    553 	wpa_printf(MSG_INFO, "Parent URI: %s", uri);
    554 
    555 	if (!pps_node) {
    556 		/* Add at root of PPS MO */
    557 		pps_node = pps;
    558 	}
    559 
    560 	uri_len = os_strlen(uri);
    561 	os_strlcpy(uri, pos + uri_len, os_strlen(pos));
    562 	upos = uri;
    563 	while (*upos == '/')
    564 		upos++;
    565 	wpa_printf(MSG_INFO, "Nodes to add: %s", upos);
    566 
    567 	for (;;) {
    568 		end = os_strchr(upos, '/');
    569 		if (!end)
    570 			break;
    571 		*end = '\0';
    572 		wpa_printf(MSG_INFO, "Adding interim node %s", upos);
    573 		pps_node = xml_node_create(ctx->xml, pps_node, NULL, upos);
    574 		if (pps_node == NULL) {
    575 			os_free(uri);
    576 			return DM_RESP_COMMAND_FAILED;
    577 		}
    578 		upos = end + 1;
    579 	}
    580 
    581 	wpa_printf(MSG_INFO, "Adding node %s", upos);
    582 
    583 	node = get_node(ctx->xml, add, "Item/Meta/Type");
    584 	if (node) {
    585 		char *type;
    586 		type = xml_node_get_text(ctx->xml, node);
    587 		if (type == NULL) {
    588 			wpa_printf(MSG_ERROR, "Could not find type text");
    589 			os_free(uri);
    590 			return DM_RESP_BAD_REQUEST;
    591 		}
    592 		use_tnds = node &&
    593 			os_strstr(type, "application/vnd.syncml.dmtnds+xml");
    594 	}
    595 
    596 	node = get_node(ctx->xml, add, "Item/Data");
    597 	if (node == NULL) {
    598 		wpa_printf(MSG_INFO, "No Add/Item/Data found");
    599 		os_free(uri);
    600 		return DM_RESP_BAD_REQUEST;
    601 	}
    602 
    603 	data = xml_node_get_text(ctx->xml, node);
    604 	if (data == NULL) {
    605 		wpa_printf(MSG_INFO, "Could not get Add/Item/Data text");
    606 		os_free(uri);
    607 		return DM_RESP_BAD_REQUEST;
    608 	}
    609 
    610 	wpa_printf(MSG_DEBUG, "Add/Item/Data: %s", data);
    611 
    612 	if (use_tnds) {
    613 		tnds = xml_node_from_buf(ctx->xml, data);
    614 		xml_node_get_text_free(ctx->xml, data);
    615 		if (tnds == NULL) {
    616 			wpa_printf(MSG_INFO,
    617 				   "Could not parse Add/Item/Data text");
    618 			os_free(uri);
    619 			return DM_RESP_BAD_REQUEST;
    620 		}
    621 
    622 		unode = tnds_to_mo(ctx->xml, tnds);
    623 		xml_node_free(ctx->xml, tnds);
    624 		if (unode == NULL) {
    625 			wpa_printf(MSG_INFO, "Could not parse TNDS text");
    626 			os_free(uri);
    627 			return DM_RESP_BAD_REQUEST;
    628 		}
    629 
    630 		debug_dump_node(ctx, "Parsed TNDS", unode);
    631 
    632 		xml_node_add_child(ctx->xml, pps_node, unode);
    633 	} else {
    634 		/* TODO: What to do here? */
    635 		os_free(uri);
    636 		return DM_RESP_BAD_REQUEST;
    637 	}
    638 
    639 	os_free(uri);
    640 
    641 	if (update_pps_file(ctx, pps_fname, pps) < 0)
    642 		return DM_RESP_COMMAND_FAILED;
    643 
    644 	ctx->pps_updated = 1;
    645 
    646 	return DM_RESP_OK;
    647 }
    648 
    649 
    650 static int oma_dm_add(struct hs20_osu_client *ctx, xml_node_t *add,
    651 		      xml_node_t *pps, const char *pps_fname)
    652 {
    653 	xml_node_t *node;
    654 	char *locuri;
    655 	char fname[300];
    656 	int ret;
    657 
    658 	node = get_node(ctx->xml, add, "Item/Target/LocURI");
    659 	if (node == NULL) {
    660 		wpa_printf(MSG_INFO, "No Target LocURI node found");
    661 		return DM_RESP_BAD_REQUEST;
    662 	}
    663 	locuri = xml_node_get_text(ctx->xml, node);
    664 	if (locuri == NULL) {
    665 		wpa_printf(MSG_ERROR, "No LocURI node text found");
    666 		return DM_RESP_BAD_REQUEST;
    667 	}
    668 	wpa_printf(MSG_INFO, "Target LocURI: %s", locuri);
    669 	if (os_strncasecmp(locuri, "./Wi-Fi/", 8) != 0) {
    670 		wpa_printf(MSG_INFO, "Unsupported Add Target LocURI");
    671 		xml_node_get_text_free(ctx->xml, locuri);
    672 		return DM_RESP_PERMISSION_DENIED;
    673 	}
    674 
    675 	node = get_node(ctx->xml, add, "Item/Data");
    676 	if (node == NULL) {
    677 		wpa_printf(MSG_INFO, "No Data node found");
    678 		xml_node_get_text_free(ctx->xml, locuri);
    679 		return DM_RESP_BAD_REQUEST;
    680 	}
    681 
    682 	if (pps_fname && os_file_exists(pps_fname)) {
    683 		ret = oma_dm_run_add(ctx, locuri, add, pps, pps_fname);
    684 		if (ret != DM_RESP_OK) {
    685 			xml_node_get_text_free(ctx->xml, locuri);
    686 			return ret;
    687 		}
    688 		ret = 0;
    689 		os_strlcpy(fname, pps_fname, sizeof(fname));
    690 	} else
    691 		ret = hs20_add_pps_mo(ctx, locuri, node, fname, sizeof(fname));
    692 	xml_node_get_text_free(ctx->xml, locuri);
    693 	if (ret < 0)
    694 		return ret == -2 ? DM_RESP_ALREADY_EXISTS :
    695 			DM_RESP_COMMAND_FAILED;
    696 
    697 	if (ctx->no_reconnect == 2) {
    698 		os_snprintf(ctx->pps_fname, sizeof(ctx->pps_fname), "%s",
    699 			    fname);
    700 		ctx->pps_cred_set = 1;
    701 		return DM_RESP_OK;
    702 	}
    703 
    704 	wpa_printf(MSG_INFO, "Updating wpa_supplicant credentials");
    705 	cmd_set_pps(ctx, fname);
    706 
    707 	if (ctx->no_reconnect)
    708 		return DM_RESP_OK;
    709 
    710 	wpa_printf(MSG_INFO, "Requesting reconnection with updated configuration");
    711 	if (wpa_command(ctx->ifname, "INTERWORKING_SELECT auto") < 0)
    712 		wpa_printf(MSG_INFO, "Failed to request wpa_supplicant to reconnect");
    713 
    714 	return DM_RESP_OK;
    715 }
    716 
    717 
    718 static int oma_dm_replace(struct hs20_osu_client *ctx, xml_node_t *replace,
    719 			  xml_node_t *pps, const char *pps_fname)
    720 {
    721 	char *locuri, *pos;
    722 	size_t fqdn_len;
    723 	xml_node_t *node, *tnds, *unode, *pps_node, *parent;
    724 	char *data;
    725 	int use_tnds = 0;
    726 
    727 	locuri = oma_dm_get_target_locuri(ctx, replace);
    728 	if (locuri == NULL)
    729 		return DM_RESP_BAD_REQUEST;
    730 
    731 	wpa_printf(MSG_INFO, "Replace command target LocURI: %s", locuri);
    732 	if (os_strncasecmp(locuri, "./Wi-Fi/", 8) != 0) {
    733 		wpa_printf(MSG_INFO, "Do not allow Replace outside ./Wi-Fi");
    734 		os_free(locuri);
    735 		return DM_RESP_PERMISSION_DENIED;
    736 	}
    737 	pos = locuri + 8;
    738 
    739 	if (ctx->fqdn == NULL) {
    740 		os_free(locuri);
    741 		return DM_RESP_COMMAND_FAILED;
    742 	}
    743 	fqdn_len = os_strlen(ctx->fqdn);
    744 	if (os_strncasecmp(pos, ctx->fqdn, fqdn_len) != 0 ||
    745 	    pos[fqdn_len] != '/') {
    746 		wpa_printf(MSG_INFO, "Do not allow Replace outside ./Wi-Fi/%s",
    747 			   ctx->fqdn);
    748 		os_free(locuri);
    749 		return DM_RESP_PERMISSION_DENIED;
    750 	}
    751 	pos += fqdn_len + 1;
    752 
    753 	if (os_strncasecmp(pos, "PerProviderSubscription/", 24) != 0) {
    754 		wpa_printf(MSG_INFO,
    755 			   "Do not allow Replace outside ./Wi-Fi/%s/PerProviderSubscription",
    756 			   ctx->fqdn);
    757 		os_free(locuri);
    758 		return DM_RESP_PERMISSION_DENIED;
    759 	}
    760 	pos += 24;
    761 
    762 	wpa_printf(MSG_INFO, "Replace command for PPS node %s", pos);
    763 
    764 	pps_node = get_node(ctx->xml, pps, pos);
    765 	if (pps_node == NULL) {
    766 		wpa_printf(MSG_INFO, "Specified PPS node not found");
    767 		os_free(locuri);
    768 		return DM_RESP_NOT_FOUND;
    769 	}
    770 
    771 	node = get_node(ctx->xml, replace, "Item/Meta/Type");
    772 	if (node) {
    773 		char *type;
    774 		type = xml_node_get_text(ctx->xml, node);
    775 		if (type == NULL) {
    776 			wpa_printf(MSG_INFO, "Could not find type text");
    777 			os_free(locuri);
    778 			return DM_RESP_BAD_REQUEST;
    779 		}
    780 		use_tnds = node &&
    781 			os_strstr(type, "application/vnd.syncml.dmtnds+xml");
    782 	}
    783 
    784 	node = get_node(ctx->xml, replace, "Item/Data");
    785 	if (node == NULL) {
    786 		wpa_printf(MSG_INFO, "No Replace/Item/Data found");
    787 		os_free(locuri);
    788 		return DM_RESP_BAD_REQUEST;
    789 	}
    790 
    791 	data = xml_node_get_text(ctx->xml, node);
    792 	if (data == NULL) {
    793 		wpa_printf(MSG_INFO, "Could not get Replace/Item/Data text");
    794 		os_free(locuri);
    795 		return DM_RESP_BAD_REQUEST;
    796 	}
    797 
    798 	wpa_printf(MSG_DEBUG, "Replace/Item/Data: %s", data);
    799 
    800 	if (use_tnds) {
    801 		tnds = xml_node_from_buf(ctx->xml, data);
    802 		xml_node_get_text_free(ctx->xml, data);
    803 		if (tnds == NULL) {
    804 			wpa_printf(MSG_INFO,
    805 				   "Could not parse Replace/Item/Data text");
    806 			os_free(locuri);
    807 			return DM_RESP_BAD_REQUEST;
    808 		}
    809 
    810 		unode = tnds_to_mo(ctx->xml, tnds);
    811 		xml_node_free(ctx->xml, tnds);
    812 		if (unode == NULL) {
    813 			wpa_printf(MSG_INFO, "Could not parse TNDS text");
    814 			os_free(locuri);
    815 			return DM_RESP_BAD_REQUEST;
    816 		}
    817 
    818 		debug_dump_node(ctx, "Parsed TNDS", unode);
    819 
    820 		parent = xml_node_get_parent(ctx->xml, pps_node);
    821 		xml_node_detach(ctx->xml, pps_node);
    822 		xml_node_add_child(ctx->xml, parent, unode);
    823 	} else {
    824 		xml_node_set_text(ctx->xml, pps_node, data);
    825 		xml_node_get_text_free(ctx->xml, data);
    826 	}
    827 
    828 	os_free(locuri);
    829 
    830 	if (update_pps_file(ctx, pps_fname, pps) < 0)
    831 		return DM_RESP_COMMAND_FAILED;
    832 
    833 	ctx->pps_updated = 1;
    834 
    835 	return DM_RESP_OK;
    836 }
    837 
    838 
    839 static int oma_dm_get(struct hs20_osu_client *ctx, xml_node_t *get,
    840 		      xml_node_t *pps, const char *pps_fname, char **value)
    841 {
    842 	char *locuri, *pos;
    843 	size_t fqdn_len;
    844 	xml_node_t *pps_node;
    845 	const char *name;
    846 
    847 	*value = NULL;
    848 
    849 	locuri = oma_dm_get_target_locuri(ctx, get);
    850 	if (locuri == NULL)
    851 		return DM_RESP_BAD_REQUEST;
    852 
    853 	wpa_printf(MSG_INFO, "Get command target LocURI: %s", locuri);
    854 	if (os_strncasecmp(locuri, "./Wi-Fi/", 8) != 0) {
    855 		wpa_printf(MSG_INFO, "Do not allow Get outside ./Wi-Fi");
    856 		os_free(locuri);
    857 		return DM_RESP_PERMISSION_DENIED;
    858 	}
    859 	pos = locuri + 8;
    860 
    861 	if (ctx->fqdn == NULL)
    862 		return DM_RESP_COMMAND_FAILED;
    863 	fqdn_len = os_strlen(ctx->fqdn);
    864 	if (os_strncasecmp(pos, ctx->fqdn, fqdn_len) != 0 ||
    865 	    pos[fqdn_len] != '/') {
    866 		wpa_printf(MSG_INFO, "Do not allow Get outside ./Wi-Fi/%s",
    867 			   ctx->fqdn);
    868 		os_free(locuri);
    869 		return DM_RESP_PERMISSION_DENIED;
    870 	}
    871 	pos += fqdn_len + 1;
    872 
    873 	if (os_strncasecmp(pos, "PerProviderSubscription/", 24) != 0) {
    874 		wpa_printf(MSG_INFO,
    875 			   "Do not allow Get outside ./Wi-Fi/%s/PerProviderSubscription",
    876 			   ctx->fqdn);
    877 		os_free(locuri);
    878 		return DM_RESP_PERMISSION_DENIED;
    879 	}
    880 	pos += 24;
    881 
    882 	wpa_printf(MSG_INFO, "Get command for PPS node %s", pos);
    883 
    884 	pps_node = get_node(ctx->xml, pps, pos);
    885 	if (pps_node == NULL) {
    886 		wpa_printf(MSG_INFO, "Specified PPS node not found");
    887 		os_free(locuri);
    888 		return DM_RESP_NOT_FOUND;
    889 	}
    890 
    891 	name = xml_node_get_localname(ctx->xml, pps_node);
    892 	wpa_printf(MSG_INFO, "Get command returned node with name '%s'", name);
    893 	if (os_strcasecmp(name, "Password") == 0) {
    894 		wpa_printf(MSG_INFO, "Do not allow Get for Password node");
    895 		os_free(locuri);
    896 		return DM_RESP_PERMISSION_DENIED;
    897 	}
    898 
    899 	/*
    900 	 * TODO: No support for DMTNDS, so if interior node, reply with a
    901 	 * list of children node names in Results element. The child list type is
    902 	 * defined in [DMTND].
    903 	 */
    904 
    905 	*value = xml_node_get_text(ctx->xml, pps_node);
    906 	if (*value == NULL)
    907 		return DM_RESP_COMMAND_FAILED;
    908 
    909 	return DM_RESP_OK;
    910 }
    911 
    912 
    913 static int oma_dm_get_cmdid(struct hs20_osu_client *ctx, xml_node_t *node)
    914 {
    915 	xml_node_t *cnode;
    916 	char *str;
    917 	int ret;
    918 
    919 	cnode = get_node(ctx->xml, node, "CmdID");
    920 	if (cnode == NULL)
    921 		return 0;
    922 
    923 	str = xml_node_get_text(ctx->xml, cnode);
    924 	if (str == NULL)
    925 		return 0;
    926 	ret = atoi(str);
    927 	xml_node_get_text_free(ctx->xml, str);
    928 	return ret;
    929 }
    930 
    931 
    932 static xml_node_t * oma_dm_send_recv(struct hs20_osu_client *ctx,
    933 				     const char *url, xml_node_t *syncml,
    934 				     const char *ext_hdr,
    935 				     const char *username, const char *password,
    936 				     const char *client_cert,
    937 				     const char *client_key)
    938 {
    939 	xml_node_t *resp;
    940 	char *str, *res;
    941 	char *resp_uri = NULL;
    942 
    943 	str = xml_node_to_str(ctx->xml, syncml);
    944 	xml_node_free(ctx->xml, syncml);
    945 	if (str == NULL)
    946 		return NULL;
    947 
    948 	wpa_printf(MSG_INFO, "Send OMA DM Package");
    949 	write_summary(ctx, "Send OMA DM Package");
    950 	os_free(ctx->server_url);
    951 	ctx->server_url = os_strdup(url);
    952 	res = http_post(ctx->http, url, str, "application/vnd.syncml.dm+xml",
    953 			ext_hdr, ctx->ca_fname, username, password,
    954 			client_cert, client_key, NULL);
    955 	os_free(str);
    956 	os_free(resp_uri);
    957 	resp_uri = NULL;
    958 
    959 	if (res == NULL) {
    960 		const char *err = http_get_err(ctx->http);
    961 		if (err) {
    962 			wpa_printf(MSG_INFO, "HTTP error: %s", err);
    963 			write_result(ctx, "HTTP error: %s", err);
    964 		} else {
    965 			write_summary(ctx, "Failed to send OMA DM Package");
    966 		}
    967 		return NULL;
    968 	}
    969 	wpa_printf(MSG_DEBUG, "Server response: %s", res);
    970 
    971 	wpa_printf(MSG_INFO, "Process OMA DM Package");
    972 	write_summary(ctx, "Process received OMA DM Package");
    973 	resp = xml_node_from_buf(ctx->xml, res);
    974 	os_free(res);
    975 	if (resp == NULL) {
    976 		wpa_printf(MSG_INFO, "Failed to parse OMA DM response");
    977 		return NULL;
    978 	}
    979 
    980 	debug_dump_node(ctx, "OMA DM Package", resp);
    981 
    982 	return resp;
    983 }
    984 
    985 
    986 static xml_node_t * oma_dm_process(struct hs20_osu_client *ctx, const char *url,
    987 				   xml_node_t *resp, int msgid,
    988 				   char **ret_resp_uri,
    989 				   xml_node_t *pps, const char *pps_fname)
    990 {
    991 	xml_node_t *syncml, *syncbody, *hdr, *body, *child;
    992 	const char *name;
    993 	char *resp_uri = NULL;
    994 	int server_msgid = 0;
    995 	int cmdid = 0;
    996 	int server_cmdid;
    997 	int resp_needed = 0;
    998 	char *tmp;
    999 	int final = 0;
   1000 	char *locuri;
   1001 
   1002 	*ret_resp_uri = NULL;
   1003 
   1004 	name = xml_node_get_localname(ctx->xml, resp);
   1005 	if (name == NULL || os_strcasecmp(name, "SyncML") != 0) {
   1006 		wpa_printf(MSG_INFO, "SyncML node not found");
   1007 		return NULL;
   1008 	}
   1009 
   1010 	hdr = get_node(ctx->xml, resp, "SyncHdr");
   1011 	body = get_node(ctx->xml, resp, "SyncBody");
   1012 	if (hdr == NULL || body == NULL) {
   1013 		wpa_printf(MSG_INFO, "Could not find SyncHdr or SyncBody");
   1014 		return NULL;
   1015 	}
   1016 
   1017 	xml_node_for_each_child(ctx->xml, child, hdr) {
   1018 		xml_node_for_each_check(ctx->xml, child);
   1019 		name = xml_node_get_localname(ctx->xml, child);
   1020 		wpa_printf(MSG_INFO, "SyncHdr %s", name);
   1021 		if (os_strcasecmp(name, "RespURI") == 0) {
   1022 			tmp = xml_node_get_text(ctx->xml, child);
   1023 			if (tmp)
   1024 				resp_uri = os_strdup(tmp);
   1025 			xml_node_get_text_free(ctx->xml, tmp);
   1026 		} else if (os_strcasecmp(name, "MsgID") == 0) {
   1027 			tmp = xml_node_get_text(ctx->xml, child);
   1028 			if (tmp)
   1029 				server_msgid = atoi(tmp);
   1030 			xml_node_get_text_free(ctx->xml, tmp);
   1031 		}
   1032 	}
   1033 
   1034 	wpa_printf(MSG_INFO, "Server MsgID: %d", server_msgid);
   1035 	if (resp_uri)
   1036 		wpa_printf(MSG_INFO, "RespURI: %s", resp_uri);
   1037 
   1038 	syncml = oma_dm_build_hdr(ctx, resp_uri ? resp_uri : url, msgid);
   1039 	if (syncml == NULL) {
   1040 		os_free(resp_uri);
   1041 		return NULL;
   1042 	}
   1043 
   1044 	syncbody = xml_node_create(ctx->xml, syncml, NULL, "SyncBody");
   1045 	cmdid++;
   1046 	add_status(ctx, syncbody, server_msgid, 0, cmdid, "SyncHdr",
   1047 		   DM_RESP_AUTH_ACCEPTED, NULL);
   1048 
   1049 	xml_node_for_each_child(ctx->xml, child, body) {
   1050 		xml_node_for_each_check(ctx->xml, child);
   1051 		server_cmdid = oma_dm_get_cmdid(ctx, child);
   1052 		name = xml_node_get_localname(ctx->xml, child);
   1053 		wpa_printf(MSG_INFO, "SyncBody CmdID=%d - %s",
   1054 			   server_cmdid, name);
   1055 		if (os_strcasecmp(name, "Exec") == 0) {
   1056 			int res = oma_dm_exec(ctx, child);
   1057 			cmdid++;
   1058 			locuri = oma_dm_get_target_locuri(ctx, child);
   1059 			if (locuri == NULL)
   1060 				res = DM_RESP_BAD_REQUEST;
   1061 			add_status(ctx, syncbody, server_msgid, server_cmdid,
   1062 				   cmdid, name, res, locuri);
   1063 			os_free(locuri);
   1064 			resp_needed = 1;
   1065 		} else if (os_strcasecmp(name, "Add") == 0) {
   1066 			int res = oma_dm_add(ctx, child, pps, pps_fname);
   1067 			cmdid++;
   1068 			locuri = oma_dm_get_target_locuri(ctx, child);
   1069 			if (locuri == NULL)
   1070 				res = DM_RESP_BAD_REQUEST;
   1071 			add_status(ctx, syncbody, server_msgid, server_cmdid,
   1072 				   cmdid, name, res, locuri);
   1073 			os_free(locuri);
   1074 			resp_needed = 1;
   1075 		} else if (os_strcasecmp(name, "Replace") == 0) {
   1076 			int res;
   1077 			res = oma_dm_replace(ctx, child, pps, pps_fname);
   1078 			cmdid++;
   1079 			locuri = oma_dm_get_target_locuri(ctx, child);
   1080 			if (locuri == NULL)
   1081 				res = DM_RESP_BAD_REQUEST;
   1082 			add_status(ctx, syncbody, server_msgid, server_cmdid,
   1083 				   cmdid, name, res, locuri);
   1084 			os_free(locuri);
   1085 			resp_needed = 1;
   1086 		} else if (os_strcasecmp(name, "Status") == 0) {
   1087 			/* TODO: Verify success */
   1088 		} else if (os_strcasecmp(name, "Get") == 0) {
   1089 			int res;
   1090 			char *value;
   1091 			res = oma_dm_get(ctx, child, pps, pps_fname, &value);
   1092 			cmdid++;
   1093 			locuri = oma_dm_get_target_locuri(ctx, child);
   1094 			if (locuri == NULL)
   1095 				res = DM_RESP_BAD_REQUEST;
   1096 			add_status(ctx, syncbody, server_msgid, server_cmdid,
   1097 				   cmdid, name, res, locuri);
   1098 			if (res == DM_RESP_OK && value) {
   1099 				cmdid++;
   1100 				add_results(ctx, syncbody, server_msgid,
   1101 					    server_cmdid, cmdid, locuri, value);
   1102 			}
   1103 			os_free(locuri);
   1104 			xml_node_get_text_free(ctx->xml, value);
   1105 			resp_needed = 1;
   1106 #if 0 /* TODO: MUST support */
   1107 		} else if (os_strcasecmp(name, "Delete") == 0) {
   1108 #endif
   1109 #if 0 /* TODO: MUST support */
   1110 		} else if (os_strcasecmp(name, "Sequence") == 0) {
   1111 #endif
   1112 		} else if (os_strcasecmp(name, "Final") == 0) {
   1113 			final = 1;
   1114 			break;
   1115 		} else {
   1116 			locuri = oma_dm_get_target_locuri(ctx, child);
   1117 			add_status(ctx, syncbody, server_msgid, server_cmdid,
   1118 				   cmdid, name, DM_RESP_COMMAND_NOT_IMPLEMENTED,
   1119 				   locuri);
   1120 			os_free(locuri);
   1121 			resp_needed = 1;
   1122 		}
   1123 	}
   1124 
   1125 	if (!final) {
   1126 		wpa_printf(MSG_INFO, "Final node not found");
   1127 		xml_node_free(ctx->xml, syncml);
   1128 		os_free(resp_uri);
   1129 		return NULL;
   1130 	}
   1131 
   1132 	if (!resp_needed) {
   1133 		wpa_printf(MSG_INFO, "Exchange completed - no response needed");
   1134 		xml_node_free(ctx->xml, syncml);
   1135 		os_free(resp_uri);
   1136 		return NULL;
   1137 	}
   1138 
   1139 	xml_node_create(ctx->xml, syncbody, NULL, "Final");
   1140 
   1141 	debug_dump_node(ctx, "OMA-DM Package 3", syncml);
   1142 
   1143 	*ret_resp_uri = resp_uri;
   1144 	return syncml;
   1145 }
   1146 
   1147 
   1148 int cmd_oma_dm_prov(struct hs20_osu_client *ctx, const char *url)
   1149 {
   1150 	xml_node_t *syncml, *resp;
   1151 	char *resp_uri = NULL;
   1152 	int msgid = 0;
   1153 
   1154 	if (url == NULL) {
   1155 		wpa_printf(MSG_INFO, "Invalid prov command (missing URL)");
   1156 		return -1;
   1157 	}
   1158 
   1159 	wpa_printf(MSG_INFO, "OMA-DM credential provisioning requested");
   1160 	write_summary(ctx, "OMA-DM credential provisioning");
   1161 
   1162 	msgid++;
   1163 	syncml = build_oma_dm_1_sub_reg(ctx, url, msgid);
   1164 	if (syncml == NULL)
   1165 		return -1;
   1166 
   1167 	while (syncml) {
   1168 		resp = oma_dm_send_recv(ctx, resp_uri ? resp_uri : url,
   1169 					syncml, NULL, NULL, NULL, NULL, NULL);
   1170 		if (resp == NULL)
   1171 			return -1;
   1172 
   1173 		msgid++;
   1174 		syncml = oma_dm_process(ctx, url, resp, msgid, &resp_uri,
   1175 					NULL, NULL);
   1176 		xml_node_free(ctx->xml, resp);
   1177 	}
   1178 
   1179 	os_free(resp_uri);
   1180 
   1181 	return ctx->pps_cred_set ? 0 : -1;
   1182 }
   1183 
   1184 
   1185 int cmd_oma_dm_sim_prov(struct hs20_osu_client *ctx, const char *url)
   1186 {
   1187 	xml_node_t *syncml, *resp;
   1188 	char *resp_uri = NULL;
   1189 	int msgid = 0;
   1190 
   1191 	if (url == NULL) {
   1192 		wpa_printf(MSG_INFO, "Invalid prov command (missing URL)");
   1193 		return -1;
   1194 	}
   1195 
   1196 	wpa_printf(MSG_INFO, "OMA-DM SIM provisioning requested");
   1197 	ctx->no_reconnect = 2;
   1198 
   1199 	wpa_printf(MSG_INFO, "Wait for IP address before starting SIM provisioning");
   1200 	write_summary(ctx, "Wait for IP address before starting SIM provisioning");
   1201 
   1202 	if (wait_ip_addr(ctx->ifname, 15) < 0) {
   1203 		wpa_printf(MSG_INFO, "Could not get IP address for WLAN - try connection anyway");
   1204 	}
   1205 	write_summary(ctx, "OMA-DM SIM provisioning");
   1206 
   1207 	msgid++;
   1208 	syncml = build_oma_dm_1_sub_prov(ctx, url, msgid);
   1209 	if (syncml == NULL)
   1210 		return -1;
   1211 
   1212 	while (syncml) {
   1213 		resp = oma_dm_send_recv(ctx, resp_uri ? resp_uri : url,
   1214 					syncml, NULL, NULL, NULL, NULL, NULL);
   1215 		if (resp == NULL)
   1216 			return -1;
   1217 
   1218 		msgid++;
   1219 		syncml = oma_dm_process(ctx, url, resp, msgid, &resp_uri,
   1220 					NULL, NULL);
   1221 		xml_node_free(ctx->xml, resp);
   1222 	}
   1223 
   1224 	os_free(resp_uri);
   1225 
   1226 	if (ctx->pps_cred_set) {
   1227 		wpa_printf(MSG_INFO, "Updating wpa_supplicant credentials");
   1228 		cmd_set_pps(ctx, ctx->pps_fname);
   1229 
   1230 		wpa_printf(MSG_INFO, "Requesting reconnection with updated configuration");
   1231 		write_summary(ctx, "Requesting reconnection with updated configuration");
   1232 		if (wpa_command(ctx->ifname, "INTERWORKING_SELECT auto") < 0) {
   1233 			wpa_printf(MSG_INFO, "Failed to request wpa_supplicant to reconnect");
   1234 			write_summary(ctx, "Failed to request wpa_supplicant to reconnect");
   1235 			return -1;
   1236 		}
   1237 	}
   1238 
   1239 	return ctx->pps_cred_set ? 0 : -1;
   1240 }
   1241 
   1242 
   1243 void oma_dm_pol_upd(struct hs20_osu_client *ctx, const char *address,
   1244 		    const char *pps_fname,
   1245 		    const char *client_cert, const char *client_key,
   1246 		    const char *cred_username, const char *cred_password,
   1247 		    xml_node_t *pps)
   1248 {
   1249 	xml_node_t *syncml, *resp;
   1250 	char *resp_uri = NULL;
   1251 	int msgid = 0;
   1252 
   1253 	wpa_printf(MSG_INFO, "OMA-DM policy update");
   1254 	write_summary(ctx, "OMA-DM policy update");
   1255 
   1256 	msgid++;
   1257 	syncml = build_oma_dm_1_pol_upd(ctx, address, msgid);
   1258 	if (syncml == NULL)
   1259 		return;
   1260 
   1261 	while (syncml) {
   1262 		resp = oma_dm_send_recv(ctx, resp_uri ? resp_uri : address,
   1263 					syncml, NULL, cred_username,
   1264 					cred_password, client_cert, client_key);
   1265 		if (resp == NULL)
   1266 			return;
   1267 
   1268 		msgid++;
   1269 		syncml = oma_dm_process(ctx, address, resp, msgid, &resp_uri,
   1270 					pps, pps_fname);
   1271 		xml_node_free(ctx->xml, resp);
   1272 	}
   1273 
   1274 	os_free(resp_uri);
   1275 
   1276 	if (ctx->pps_updated) {
   1277 		wpa_printf(MSG_INFO, "Update wpa_supplicant credential based on updated PPS MO");
   1278 		write_summary(ctx, "Update wpa_supplicant credential based on updated PPS MO and request connection");
   1279 		cmd_set_pps(ctx, pps_fname);
   1280 		if (wpa_command(ctx->ifname, "INTERWORKING_SELECT auto") < 0) {
   1281 			wpa_printf(MSG_INFO,
   1282 				   "Failed to request wpa_supplicant to reconnect");
   1283 			write_summary(ctx,
   1284 				      "Failed to request wpa_supplicant to reconnect");
   1285 		}
   1286 	}
   1287 }
   1288 
   1289 
   1290 void oma_dm_sub_rem(struct hs20_osu_client *ctx, const char *address,
   1291 		    const char *pps_fname,
   1292 		    const char *client_cert, const char *client_key,
   1293 		    const char *cred_username, const char *cred_password,
   1294 		    xml_node_t *pps)
   1295 {
   1296 	xml_node_t *syncml, *resp;
   1297 	char *resp_uri = NULL;
   1298 	int msgid = 0;
   1299 
   1300 	wpa_printf(MSG_INFO, "OMA-DM subscription remediation");
   1301 	write_summary(ctx, "OMA-DM subscription remediation");
   1302 
   1303 	msgid++;
   1304 	syncml = build_oma_dm_1_sub_rem(ctx, address, msgid);
   1305 	if (syncml == NULL)
   1306 		return;
   1307 
   1308 	while (syncml) {
   1309 		resp = oma_dm_send_recv(ctx, resp_uri ? resp_uri : address,
   1310 					syncml, NULL, cred_username,
   1311 					cred_password, client_cert, client_key);
   1312 		if (resp == NULL)
   1313 			return;
   1314 
   1315 		msgid++;
   1316 		syncml = oma_dm_process(ctx, address, resp, msgid, &resp_uri,
   1317 					pps, pps_fname);
   1318 		xml_node_free(ctx->xml, resp);
   1319 	}
   1320 
   1321 	os_free(resp_uri);
   1322 
   1323 	wpa_printf(MSG_INFO, "Update wpa_supplicant credential based on updated PPS MO and request reconnection");
   1324 	write_summary(ctx, "Update wpa_supplicant credential based on updated PPS MO and request reconnection");
   1325 	cmd_set_pps(ctx, pps_fname);
   1326 	if (wpa_command(ctx->ifname, "INTERWORKING_SELECT auto") < 0) {
   1327 		wpa_printf(MSG_INFO, "Failed to request wpa_supplicant to reconnect");
   1328 		write_summary(ctx, "Failed to request wpa_supplicant to reconnect");
   1329 	}
   1330 }
   1331 
   1332 
   1333 void cmd_oma_dm_add(struct hs20_osu_client *ctx, const char *pps_fname,
   1334 		    const char *add_fname)
   1335 {
   1336 	xml_node_t *pps, *add;
   1337 	int res;
   1338 
   1339 	ctx->fqdn = os_strdup("wi-fi.org");
   1340 
   1341 	pps = node_from_file(ctx->xml, pps_fname);
   1342 	if (pps == NULL) {
   1343 		wpa_printf(MSG_INFO, "PPS file %s could not be parsed",
   1344 			   pps_fname);
   1345 		return;
   1346 	}
   1347 
   1348 	add = node_from_file(ctx->xml, add_fname);
   1349 	if (add == NULL) {
   1350 		wpa_printf(MSG_INFO, "Add file %s could not be parsed",
   1351 			   add_fname);
   1352 		xml_node_free(ctx->xml, pps);
   1353 		return;
   1354 	}
   1355 
   1356 	res = oma_dm_add(ctx, add, pps, pps_fname);
   1357 	wpa_printf(MSG_INFO, "oma_dm_add --> %d", res);
   1358 
   1359 	xml_node_free(ctx->xml, pps);
   1360 	xml_node_free(ctx->xml, add);
   1361 }
   1362 
   1363 
   1364 void cmd_oma_dm_replace(struct hs20_osu_client *ctx, const char *pps_fname,
   1365 			const char *replace_fname)
   1366 {
   1367 	xml_node_t *pps, *replace;
   1368 	int res;
   1369 
   1370 	ctx->fqdn = os_strdup("wi-fi.org");
   1371 
   1372 	pps = node_from_file(ctx->xml, pps_fname);
   1373 	if (pps == NULL) {
   1374 		wpa_printf(MSG_INFO, "PPS file %s could not be parsed",
   1375 			   pps_fname);
   1376 		return;
   1377 	}
   1378 
   1379 	replace = node_from_file(ctx->xml, replace_fname);
   1380 	if (replace == NULL) {
   1381 		wpa_printf(MSG_INFO, "Replace file %s could not be parsed",
   1382 			   replace_fname);
   1383 		xml_node_free(ctx->xml, pps);
   1384 		return;
   1385 	}
   1386 
   1387 	res = oma_dm_replace(ctx, replace, pps, pps_fname);
   1388 	wpa_printf(MSG_INFO, "oma_dm_replace --> %d", res);
   1389 
   1390 	xml_node_free(ctx->xml, pps);
   1391 	xml_node_free(ctx->xml, replace);
   1392 }
   1393