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
66static void add_item(struct hs20_osu_client *ctx, xml_node_t *parent,
67		     const char *locuri, const char *data);
68
69
70static const char * int2str(int val)
71{
72	static char buf[20];
73	snprintf(buf, sizeof(buf), "%d", val);
74	return buf;
75}
76
77
78static 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
96static 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
108static 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
136static 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
143static 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
158static 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
183static 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
202static 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
226static 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
242static 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
277static 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
298static 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
336static 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
349static 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
363static 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
376static 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
390static int oma_dm_exec_browser(struct hs20_osu_client *ctx, xml_node_t *exec)
391{
392	xml_node_t *node;
393	char *data;
394	int res;
395
396	node = get_node(ctx->xml, exec, "Item/Data");
397	if (node == NULL) {
398		wpa_printf(MSG_INFO, "No Data node found");
399		return DM_RESP_BAD_REQUEST;
400	}
401
402	data = xml_node_get_text(ctx->xml, node);
403	if (data == NULL) {
404		wpa_printf(MSG_INFO, "Invalid data");
405		return DM_RESP_BAD_REQUEST;
406	}
407	wpa_printf(MSG_INFO, "Data: %s", data);
408	wpa_printf(MSG_INFO, "Launch browser to URI '%s'", data);
409	write_summary(ctx, "Launch browser to URI '%s'", data);
410	res = hs20_web_browser(data);
411	xml_node_get_text_free(ctx->xml, data);
412	if (res > 0) {
413		wpa_printf(MSG_INFO, "User response in browser completed successfully");
414		write_summary(ctx, "User response in browser completed successfully");
415		return DM_RESP_OK;
416	} else {
417		wpa_printf(MSG_INFO, "Failed to receive user response");
418		write_summary(ctx, "Failed to receive user response");
419		return DM_RESP_COMMAND_FAILED;
420	}
421}
422
423
424static 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
471static 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
500static 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
656static 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
724static 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
845static 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
919static 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
938static 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
992static 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
1154int 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
1191int 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
1249void 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
1296void 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
1339void 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
1370void 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