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	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
130static 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
137static 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
152static 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
177static 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
196static 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
220static 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
236static 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
271static 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
292static 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
330static 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
343static 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
357static 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
370static 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
384static 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
418static 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
465static 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
494static 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
650static 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
718static 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
839static 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
913static 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
932static 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
986static 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
1148int 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
1185int 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
1243void 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
1290void 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
1333void 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
1364void 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