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