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, 1);
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