2 * Hotspot 2.0 - OMA DM client
3 * Copyright (c) 2013-2014, Qualcomm Atheros, Inc.
5 * This software may be distributed under the terms of the BSD license.
6 * See README for more details.
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"
19 #define DM_SERVER_INITIATED_MGMT 1200
20 #define DM_CLIENT_INITIATED_MGMT 1201
21 #define DM_GENERIC_ALERT 1226
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
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"
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"
66 static void add_item(struct hs20_osu_client *ctx, xml_node_t *parent,
67 const char *locuri, const char *data);
70 static const char * int2str(int val)
73 snprintf(buf, sizeof(buf), "%d", val);
78 static char * oma_dm_get_target_locuri(struct hs20_osu_client *ctx,
82 char *uri, *ret = NULL;
84 locuri = get_node(ctx->xml, node, "Item/Target/LocURI");
88 uri = xml_node_get_text(ctx->xml, locuri);
91 xml_node_get_text_free(ctx->xml, uri);
96 static void oma_dm_add_locuri(struct hs20_osu_client *ctx, xml_node_t *parent,
97 const char *element, const char *uri)
101 node = xml_node_create(ctx->xml, parent, NULL, element);
104 xml_node_create_text(ctx->xml, node, NULL, "LocURI", uri);
108 static xml_node_t * oma_dm_build_hdr(struct hs20_osu_client *ctx,
109 const char *url, int msgid)
111 xml_node_t *syncml, *synchdr;
114 syncml = xml_node_create_root(ctx->xml, "SYNCML:SYNCML1.2", NULL, &ns,
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));
123 oma_dm_add_locuri(ctx, synchdr, "Target", url);
124 oma_dm_add_locuri(ctx, synchdr, "Source", ctx->devid);
130 static void oma_dm_add_cmdid(struct hs20_osu_client *ctx, xml_node_t *parent,
133 xml_node_create_text(ctx->xml, parent, NULL, "CmdID", int2str(cmdid));
137 static xml_node_t * add_alert(struct hs20_osu_client *ctx, xml_node_t *parent,
142 node = xml_node_create(ctx->xml, parent, NULL, "Alert");
145 oma_dm_add_cmdid(ctx, node, cmdid);
146 xml_node_create_text(ctx->xml, node, NULL, "Data", int2str(data));
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)
158 node = xml_node_create(ctx->xml, parent, NULL, "Status");
161 oma_dm_add_cmdid(ctx, node, cmdid);
162 xml_node_create_text(ctx->xml, node, NULL, "MsgRef", int2str(msgref));
164 xml_node_create_text(ctx->xml, node, NULL, "CmdRef",
166 xml_node_create_text(ctx->xml, node, NULL, "Cmd", cmd);
167 xml_node_create_text(ctx->xml, node, NULL, "Data", int2str(data));
169 xml_node_create_text(ctx->xml, node, NULL, "TargetRef",
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)
183 node = xml_node_create(ctx->xml, parent, NULL, "Results");
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);
196 static char * mo_str(struct hs20_osu_client *ctx, const char *urn,
199 xml_node_t *fnode, *tnds;
202 fnode = node_from_file(ctx->xml, fname);
205 tnds = mo_to_tnds(ctx->xml, fnode, 0, urn, "syncml:dmddf1.2");
206 xml_node_free(ctx->xml, fnode);
210 str = xml_node_to_str(ctx->xml, tnds);
211 xml_node_free(ctx->xml, tnds);
214 wpa_printf(MSG_INFO, "MgmtTree: %s", str);
220 static void add_item(struct hs20_osu_client *ctx, xml_node_t *parent,
221 const char *locuri, const char *data)
223 xml_node_t *item, *node;
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",
230 xml_node_create_text_ns(ctx->xml, node, "syncml:metinf", "Type",
232 xml_node_create_text(ctx->xml, item, NULL, "Data", data);
236 static void add_replace_devinfo(struct hs20_osu_client *ctx, xml_node_t *parent,
239 xml_node_t *info, *child, *replace;
241 char locuri[200], *txt;
243 info = node_from_file(ctx->xml, "devinfo.xml");
245 wpa_printf(MSG_INFO, "Could not read devinfo.xml");
249 replace = xml_node_create(ctx->xml, parent, NULL, "Replace");
250 if (replace == NULL) {
251 xml_node_free(ctx->xml, info);
254 oma_dm_add_cmdid(ctx, replace, cmdid);
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);
262 add_item(ctx, replace, locuri, txt);
263 xml_node_get_text_free(ctx->xml, txt);
267 xml_node_free(ctx->xml, info);
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,
276 xml_node_t *node, *item;
279 node = add_alert(ctx, syncbody, cmdid, DM_GENERIC_ALERT);
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",
288 xml_node_create_text(ctx->xml, item, NULL, "Data", data);
292 static xml_node_t * build_oma_dm_1(struct hs20_osu_client *ctx,
293 const char *url, int msgid, const char *oper)
295 xml_node_t *syncml, *syncbody;
299 syncml = oma_dm_build_hdr(ctx, url, msgid);
303 syncbody = xml_node_create(ctx->xml, syncml, NULL, "SyncBody");
304 if (syncbody == NULL) {
305 xml_node_free(ctx->xml, syncml);
310 add_alert(ctx, syncbody, cmdid, DM_CLIENT_INITIATED_MGMT);
312 str = mo_str(ctx, NULL, "devdetail.xml");
314 xml_node_free(ctx->xml, syncml);
318 oma_dm_add_hs20_generic_alert(ctx, syncbody, cmdid, oper, str);
322 add_replace_devinfo(ctx, syncbody, cmdid);
324 xml_node_create(ctx->xml, syncbody, NULL, "Final");
330 static xml_node_t * build_oma_dm_1_sub_reg(struct hs20_osu_client *ctx,
331 const char *url, int msgid)
335 syncml = build_oma_dm_1(ctx, url, msgid, DM_HS20_SUBSCRIPTION_CREATION);
337 debug_dump_node(ctx, "OMA-DM Package 1 (sub reg)", syncml);
343 static xml_node_t * build_oma_dm_1_sub_prov(struct hs20_osu_client *ctx,
344 const char *url, int msgid)
348 syncml = build_oma_dm_1(ctx, url, msgid,
349 DM_HS20_SUBSCRIPTION_PROVISIONING);
351 debug_dump_node(ctx, "OMA-DM Package 1 (sub prov)", syncml);
357 static xml_node_t * build_oma_dm_1_pol_upd(struct hs20_osu_client *ctx,
358 const char *url, int msgid)
362 syncml = build_oma_dm_1(ctx, url, msgid, DM_HS20_POLICY_UPDATE);
364 debug_dump_node(ctx, "OMA-DM Package 1 (pol upd)", syncml);
370 static xml_node_t * build_oma_dm_1_sub_rem(struct hs20_osu_client *ctx,
371 const char *url, int msgid)
375 syncml = build_oma_dm_1(ctx, url, msgid,
376 DM_HS20_SUBSCRIPTION_REMEDIATION);
378 debug_dump_node(ctx, "OMA-DM Package 1 (sub rem)", syncml);
384 static int oma_dm_exec_browser(struct hs20_osu_client *ctx, xml_node_t *exec)
390 node = get_node(ctx->xml, exec, "Item/Data");
392 wpa_printf(MSG_INFO, "No Data node found");
393 return DM_RESP_BAD_REQUEST;
396 data = xml_node_get_text(ctx->xml, node);
398 wpa_printf(MSG_INFO, "Invalid data");
399 return DM_RESP_BAD_REQUEST;
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);
407 wpa_printf(MSG_INFO, "User response in browser completed successfully");
408 write_summary(ctx, "User response in browser completed successfully");
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;
418 static int oma_dm_exec_get_cert(struct hs20_osu_client *ctx, xml_node_t *exec)
420 xml_node_t *node, *getcert;
425 wpa_printf(MSG_INFO, "Client certificate enrollment");
426 write_summary(ctx, "Client certificate enrollment");
428 node = get_node(ctx->xml, exec, "Item/Data");
430 wpa_printf(MSG_INFO, "No Data node found");
431 return DM_RESP_BAD_REQUEST;
434 data = xml_node_get_text(ctx->xml, node);
436 wpa_printf(MSG_INFO, "Invalid data");
437 return DM_RESP_BAD_REQUEST;
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);
443 if (getcert == NULL) {
444 wpa_printf(MSG_INFO, "Could not parse Item/Data node contents");
445 return DM_RESP_BAD_REQUEST;
448 debug_dump_node(ctx, "OMA-DM getCertificate", getcert);
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'",
454 return DM_RESP_BAD_REQUEST;
457 res = osu_get_certificate(ctx, getcert);
459 xml_node_free(ctx->xml, getcert);
461 return res == 0 ? DM_RESP_OK : DM_RESP_COMMAND_FAILED;
465 static int oma_dm_exec(struct hs20_osu_client *ctx, xml_node_t *exec)
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;
476 wpa_printf(MSG_INFO, "Target LocURI: %s", locuri);
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);
485 wpa_printf(MSG_INFO, "Unsupported exec Target LocURI");
486 ret = DM_RESP_NOT_FOUND;
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)
500 xml_node_t *node, *tnds, *unode, *pps_node;
501 char *data, *uri, *upos, *end;
505 wpa_printf(MSG_INFO, "Add command target LocURI: %s", locuri);
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;
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",
520 return DM_RESP_PERMISSION_DENIED;
524 if (os_strncasecmp(pos, "PerProviderSubscription/", 24) != 0) {
526 "Do not allow Add outside ./Wi-Fi/%s/PerProviderSubscription",
528 return DM_RESP_PERMISSION_DENIED;
532 wpa_printf(MSG_INFO, "Add command for PPS node %s", pos);
534 pps_node = get_node(ctx->xml, pps, pos);
536 wpa_printf(MSG_INFO, "Specified PPS node exists already");
537 return DM_RESP_ALREADY_EXISTS;
540 uri = os_strdup(pos);
542 return DM_RESP_COMMAND_FAILED;
544 upos = os_strrchr(uri, '/');
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");
553 wpa_printf(MSG_INFO, "Parent URI: %s", uri);
556 /* Add at root of PPS MO */
560 uri_len = os_strlen(uri);
561 os_strlcpy(uri, pos + uri_len, os_strlen(pos));
565 wpa_printf(MSG_INFO, "Nodes to add: %s", upos);
568 end = os_strchr(upos, '/');
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) {
576 return DM_RESP_COMMAND_FAILED;
581 wpa_printf(MSG_INFO, "Adding node %s", upos);
583 node = get_node(ctx->xml, add, "Item/Meta/Type");
586 type = xml_node_get_text(ctx->xml, node);
588 wpa_printf(MSG_ERROR, "Could not find type text");
590 return DM_RESP_BAD_REQUEST;
593 os_strstr(type, "application/vnd.syncml.dmtnds+xml");
596 node = get_node(ctx->xml, add, "Item/Data");
598 wpa_printf(MSG_INFO, "No Add/Item/Data found");
600 return DM_RESP_BAD_REQUEST;
603 data = xml_node_get_text(ctx->xml, node);
605 wpa_printf(MSG_INFO, "Could not get Add/Item/Data text");
607 return DM_RESP_BAD_REQUEST;
610 wpa_printf(MSG_DEBUG, "Add/Item/Data: %s", data);
613 tnds = xml_node_from_buf(ctx->xml, data);
614 xml_node_get_text_free(ctx->xml, data);
617 "Could not parse Add/Item/Data text");
619 return DM_RESP_BAD_REQUEST;
622 unode = tnds_to_mo(ctx->xml, tnds);
623 xml_node_free(ctx->xml, tnds);
625 wpa_printf(MSG_INFO, "Could not parse TNDS text");
627 return DM_RESP_BAD_REQUEST;
630 debug_dump_node(ctx, "Parsed TNDS", unode);
632 xml_node_add_child(ctx->xml, pps_node, unode);
634 /* TODO: What to do here? */
636 return DM_RESP_BAD_REQUEST;
641 if (update_pps_file(ctx, pps_fname, pps) < 0)
642 return DM_RESP_COMMAND_FAILED;
644 ctx->pps_updated = 1;
650 static int oma_dm_add(struct hs20_osu_client *ctx, xml_node_t *add,
651 xml_node_t *pps, const char *pps_fname)
658 node = get_node(ctx->xml, add, "Item/Target/LocURI");
660 wpa_printf(MSG_INFO, "No Target LocURI node found");
661 return DM_RESP_BAD_REQUEST;
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;
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;
675 node = get_node(ctx->xml, add, "Item/Data");
677 wpa_printf(MSG_INFO, "No Data node found");
678 xml_node_get_text_free(ctx->xml, locuri);
679 return DM_RESP_BAD_REQUEST;
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);
689 os_strlcpy(fname, pps_fname, sizeof(fname));
691 ret = hs20_add_pps_mo(ctx, locuri, node, fname, sizeof(fname));
692 xml_node_get_text_free(ctx->xml, locuri);
694 return ret == -2 ? DM_RESP_ALREADY_EXISTS :
695 DM_RESP_COMMAND_FAILED;
697 if (ctx->no_reconnect == 2) {
698 os_snprintf(ctx->pps_fname, sizeof(ctx->pps_fname), "%s",
700 ctx->pps_cred_set = 1;
704 wpa_printf(MSG_INFO, "Updating wpa_supplicant credentials");
705 cmd_set_pps(ctx, fname);
707 if (ctx->no_reconnect)
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");
718 static int oma_dm_replace(struct hs20_osu_client *ctx, xml_node_t *replace,
719 xml_node_t *pps, const char *pps_fname)
723 xml_node_t *node, *tnds, *unode, *pps_node, *parent;
727 locuri = oma_dm_get_target_locuri(ctx, replace);
729 return DM_RESP_BAD_REQUEST;
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");
735 return DM_RESP_PERMISSION_DENIED;
739 if (ctx->fqdn == NULL) {
741 return DM_RESP_COMMAND_FAILED;
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",
749 return DM_RESP_PERMISSION_DENIED;
753 if (os_strncasecmp(pos, "PerProviderSubscription/", 24) != 0) {
755 "Do not allow Replace outside ./Wi-Fi/%s/PerProviderSubscription",
758 return DM_RESP_PERMISSION_DENIED;
762 wpa_printf(MSG_INFO, "Replace command for PPS node %s", pos);
764 pps_node = get_node(ctx->xml, pps, pos);
765 if (pps_node == NULL) {
766 wpa_printf(MSG_INFO, "Specified PPS node not found");
768 return DM_RESP_NOT_FOUND;
771 node = get_node(ctx->xml, replace, "Item/Meta/Type");
774 type = xml_node_get_text(ctx->xml, node);
776 wpa_printf(MSG_INFO, "Could not find type text");
778 return DM_RESP_BAD_REQUEST;
781 os_strstr(type, "application/vnd.syncml.dmtnds+xml");
784 node = get_node(ctx->xml, replace, "Item/Data");
786 wpa_printf(MSG_INFO, "No Replace/Item/Data found");
788 return DM_RESP_BAD_REQUEST;
791 data = xml_node_get_text(ctx->xml, node);
793 wpa_printf(MSG_INFO, "Could not get Replace/Item/Data text");
795 return DM_RESP_BAD_REQUEST;
798 wpa_printf(MSG_DEBUG, "Replace/Item/Data: %s", data);
801 tnds = xml_node_from_buf(ctx->xml, data);
802 xml_node_get_text_free(ctx->xml, data);
805 "Could not parse Replace/Item/Data text");
807 return DM_RESP_BAD_REQUEST;
810 unode = tnds_to_mo(ctx->xml, tnds);
811 xml_node_free(ctx->xml, tnds);
813 wpa_printf(MSG_INFO, "Could not parse TNDS text");
815 return DM_RESP_BAD_REQUEST;
818 debug_dump_node(ctx, "Parsed TNDS", unode);
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);
824 xml_node_set_text(ctx->xml, pps_node, data);
825 xml_node_get_text_free(ctx->xml, data);
830 if (update_pps_file(ctx, pps_fname, pps) < 0)
831 return DM_RESP_COMMAND_FAILED;
833 ctx->pps_updated = 1;
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)
844 xml_node_t *pps_node;
849 locuri = oma_dm_get_target_locuri(ctx, get);
851 return DM_RESP_BAD_REQUEST;
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");
857 return DM_RESP_PERMISSION_DENIED;
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",
869 return DM_RESP_PERMISSION_DENIED;
873 if (os_strncasecmp(pos, "PerProviderSubscription/", 24) != 0) {
875 "Do not allow Get outside ./Wi-Fi/%s/PerProviderSubscription",
878 return DM_RESP_PERMISSION_DENIED;
882 wpa_printf(MSG_INFO, "Get command for PPS node %s", pos);
884 pps_node = get_node(ctx->xml, pps, pos);
885 if (pps_node == NULL) {
886 wpa_printf(MSG_INFO, "Specified PPS node not found");
888 return DM_RESP_NOT_FOUND;
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");
896 return DM_RESP_PERMISSION_DENIED;
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].
905 *value = xml_node_get_text(ctx->xml, pps_node);
907 return DM_RESP_COMMAND_FAILED;
913 static int oma_dm_get_cmdid(struct hs20_osu_client *ctx, xml_node_t *node)
919 cnode = get_node(ctx->xml, node, "CmdID");
923 str = xml_node_get_text(ctx->xml, cnode);
927 xml_node_get_text_free(ctx->xml, str);
932 static xml_node_t * oma_dm_send_recv(struct hs20_osu_client *ctx,
933 const char *url, xml_node_t *syncml,
935 const char *username, const char *password,
936 const char *client_cert,
937 const char *client_key)
941 char *resp_uri = NULL;
943 str = xml_node_to_str(ctx->xml, syncml);
944 xml_node_free(ctx->xml, syncml);
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);
960 const char *err = http_get_err(ctx->http);
962 wpa_printf(MSG_INFO, "HTTP error: %s", err);
963 write_result(ctx, "HTTP error: %s", err);
965 write_summary(ctx, "Failed to send OMA DM Package");
969 wpa_printf(MSG_DEBUG, "Server response: %s", res);
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);
976 wpa_printf(MSG_INFO, "Failed to parse OMA DM response");
980 debug_dump_node(ctx, "OMA DM Package", resp);
986 static xml_node_t * oma_dm_process(struct hs20_osu_client *ctx, const char *url,
987 xml_node_t *resp, int msgid,
989 xml_node_t *pps, const char *pps_fname)
991 xml_node_t *syncml, *syncbody, *hdr, *body, *child;
993 char *resp_uri = NULL;
994 int server_msgid = 0;
1002 *ret_resp_uri = NULL;
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");
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");
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);
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);
1029 server_msgid = atoi(tmp);
1030 xml_node_get_text_free(ctx->xml, tmp);
1034 wpa_printf(MSG_INFO, "Server MsgID: %d", server_msgid);
1036 wpa_printf(MSG_INFO, "RespURI: %s", resp_uri);
1038 syncml = oma_dm_build_hdr(ctx, resp_uri ? resp_uri : url, msgid);
1039 if (syncml == NULL) {
1044 syncbody = xml_node_create(ctx->xml, syncml, NULL, "SyncBody");
1046 add_status(ctx, syncbody, server_msgid, 0, cmdid, "SyncHdr",
1047 DM_RESP_AUTH_ACCEPTED, NULL);
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);
1058 locuri = oma_dm_get_target_locuri(ctx, child);
1060 res = DM_RESP_BAD_REQUEST;
1061 add_status(ctx, syncbody, server_msgid, server_cmdid,
1062 cmdid, name, res, locuri);
1065 } else if (os_strcasecmp(name, "Add") == 0) {
1066 int res = oma_dm_add(ctx, child, pps, pps_fname);
1068 locuri = oma_dm_get_target_locuri(ctx, child);
1070 res = DM_RESP_BAD_REQUEST;
1071 add_status(ctx, syncbody, server_msgid, server_cmdid,
1072 cmdid, name, res, locuri);
1075 } else if (os_strcasecmp(name, "Replace") == 0) {
1077 res = oma_dm_replace(ctx, child, pps, pps_fname);
1079 locuri = oma_dm_get_target_locuri(ctx, child);
1081 res = DM_RESP_BAD_REQUEST;
1082 add_status(ctx, syncbody, server_msgid, server_cmdid,
1083 cmdid, name, res, locuri);
1086 } else if (os_strcasecmp(name, "Status") == 0) {
1087 /* TODO: Verify success */
1088 } else if (os_strcasecmp(name, "Get") == 0) {
1091 res = oma_dm_get(ctx, child, pps, pps_fname, &value);
1093 locuri = oma_dm_get_target_locuri(ctx, child);
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) {
1100 add_results(ctx, syncbody, server_msgid,
1101 server_cmdid, cmdid, locuri, value);
1104 xml_node_get_text_free(ctx->xml, value);
1106 #if 0 /* TODO: MUST support */
1107 } else if (os_strcasecmp(name, "Delete") == 0) {
1109 #if 0 /* TODO: MUST support */
1110 } else if (os_strcasecmp(name, "Sequence") == 0) {
1112 } else if (os_strcasecmp(name, "Final") == 0) {
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,
1126 wpa_printf(MSG_INFO, "Final node not found");
1127 xml_node_free(ctx->xml, syncml);
1133 wpa_printf(MSG_INFO, "Exchange completed - no response needed");
1134 xml_node_free(ctx->xml, syncml);
1139 xml_node_create(ctx->xml, syncbody, NULL, "Final");
1141 debug_dump_node(ctx, "OMA-DM Package 3", syncml);
1143 *ret_resp_uri = resp_uri;
1148 int cmd_oma_dm_prov(struct hs20_osu_client *ctx, const char *url)
1150 xml_node_t *syncml, *resp;
1151 char *resp_uri = NULL;
1155 wpa_printf(MSG_INFO, "Invalid prov command (missing URL)");
1159 wpa_printf(MSG_INFO, "OMA-DM credential provisioning requested");
1160 write_summary(ctx, "OMA-DM credential provisioning");
1163 syncml = build_oma_dm_1_sub_reg(ctx, url, msgid);
1168 resp = oma_dm_send_recv(ctx, resp_uri ? resp_uri : url,
1169 syncml, NULL, NULL, NULL, NULL, NULL);
1174 syncml = oma_dm_process(ctx, url, resp, msgid, &resp_uri,
1176 xml_node_free(ctx->xml, resp);
1181 return ctx->pps_cred_set ? 0 : -1;
1185 int cmd_oma_dm_sim_prov(struct hs20_osu_client *ctx, const char *url)
1187 xml_node_t *syncml, *resp;
1188 char *resp_uri = NULL;
1192 wpa_printf(MSG_INFO, "Invalid prov command (missing URL)");
1196 wpa_printf(MSG_INFO, "OMA-DM SIM provisioning requested");
1197 ctx->no_reconnect = 2;
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");
1202 if (wait_ip_addr(ctx->ifname, 15) < 0) {
1203 wpa_printf(MSG_INFO, "Could not get IP address for WLAN - try connection anyway");
1205 write_summary(ctx, "OMA-DM SIM provisioning");
1208 syncml = build_oma_dm_1_sub_prov(ctx, url, msgid);
1213 resp = oma_dm_send_recv(ctx, resp_uri ? resp_uri : url,
1214 syncml, NULL, NULL, NULL, NULL, NULL);
1219 syncml = oma_dm_process(ctx, url, resp, msgid, &resp_uri,
1221 xml_node_free(ctx->xml, resp);
1226 if (ctx->pps_cred_set) {
1227 wpa_printf(MSG_INFO, "Updating wpa_supplicant credentials");
1228 cmd_set_pps(ctx, ctx->pps_fname);
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");
1239 return ctx->pps_cred_set ? 0 : -1;
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,
1249 xml_node_t *syncml, *resp;
1250 char *resp_uri = NULL;
1253 wpa_printf(MSG_INFO, "OMA-DM policy update");
1254 write_summary(ctx, "OMA-DM policy update");
1257 syncml = build_oma_dm_1_pol_upd(ctx, address, msgid);
1262 resp = oma_dm_send_recv(ctx, resp_uri ? resp_uri : address,
1263 syncml, NULL, cred_username,
1264 cred_password, client_cert, client_key);
1269 syncml = oma_dm_process(ctx, address, resp, msgid, &resp_uri,
1271 xml_node_free(ctx->xml, resp);
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");
1284 "Failed to request wpa_supplicant to reconnect");
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,
1296 xml_node_t *syncml, *resp;
1297 char *resp_uri = NULL;
1300 wpa_printf(MSG_INFO, "OMA-DM subscription remediation");
1301 write_summary(ctx, "OMA-DM subscription remediation");
1304 syncml = build_oma_dm_1_sub_rem(ctx, address, msgid);
1309 resp = oma_dm_send_recv(ctx, resp_uri ? resp_uri : address,
1310 syncml, NULL, cred_username,
1311 cred_password, client_cert, client_key);
1316 syncml = oma_dm_process(ctx, address, resp, msgid, &resp_uri,
1318 xml_node_free(ctx->xml, resp);
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");
1333 void cmd_oma_dm_add(struct hs20_osu_client *ctx, const char *pps_fname,
1334 const char *add_fname)
1336 xml_node_t *pps, *add;
1339 ctx->fqdn = os_strdup("wi-fi.org");
1341 pps = node_from_file(ctx->xml, pps_fname);
1343 wpa_printf(MSG_INFO, "PPS file %s could not be parsed",
1348 add = node_from_file(ctx->xml, add_fname);
1350 wpa_printf(MSG_INFO, "Add file %s could not be parsed",
1352 xml_node_free(ctx->xml, pps);
1356 res = oma_dm_add(ctx, add, pps, pps_fname);
1357 wpa_printf(MSG_INFO, "oma_dm_add --> %d", res);
1359 xml_node_free(ctx->xml, pps);
1360 xml_node_free(ctx->xml, add);
1364 void cmd_oma_dm_replace(struct hs20_osu_client *ctx, const char *pps_fname,
1365 const char *replace_fname)
1367 xml_node_t *pps, *replace;
1370 ctx->fqdn = os_strdup("wi-fi.org");
1372 pps = node_from_file(ctx->xml, pps_fname);
1374 wpa_printf(MSG_INFO, "PPS file %s could not be parsed",
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",
1383 xml_node_free(ctx->xml, pps);
1387 res = oma_dm_replace(ctx, replace, pps, pps_fname);
1388 wpa_printf(MSG_INFO, "oma_dm_replace --> %d", res);
1390 xml_node_free(ctx->xml, pps);
1391 xml_node_free(ctx->xml, replace);