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
;
115 wpa_printf(MSG_ERROR
,
116 "DevId from devinfo.xml is not available - cannot use OMA DM");
120 syncml
= xml_node_create_root(ctx
->xml
, "SYNCML:SYNCML1.2", NULL
, &ns
,
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
));
129 oma_dm_add_locuri(ctx
, synchdr
, "Target", url
);
130 oma_dm_add_locuri(ctx
, synchdr
, "Source", ctx
->devid
);
136 static void oma_dm_add_cmdid(struct hs20_osu_client
*ctx
, xml_node_t
*parent
,
139 xml_node_create_text(ctx
->xml
, parent
, NULL
, "CmdID", int2str(cmdid
));
143 static xml_node_t
* add_alert(struct hs20_osu_client
*ctx
, xml_node_t
*parent
,
148 node
= xml_node_create(ctx
->xml
, parent
, NULL
, "Alert");
151 oma_dm_add_cmdid(ctx
, node
, cmdid
);
152 xml_node_create_text(ctx
->xml
, node
, NULL
, "Data", int2str(data
));
158 static xml_node_t
* add_status(struct hs20_osu_client
*ctx
, xml_node_t
*parent
,
159 int msgref
, int cmdref
, int cmdid
,
160 const char *cmd
, int data
, const char *targetref
)
164 node
= xml_node_create(ctx
->xml
, parent
, NULL
, "Status");
167 oma_dm_add_cmdid(ctx
, node
, cmdid
);
168 xml_node_create_text(ctx
->xml
, node
, NULL
, "MsgRef", int2str(msgref
));
170 xml_node_create_text(ctx
->xml
, node
, NULL
, "CmdRef",
172 xml_node_create_text(ctx
->xml
, node
, NULL
, "Cmd", cmd
);
173 xml_node_create_text(ctx
->xml
, node
, NULL
, "Data", int2str(data
));
175 xml_node_create_text(ctx
->xml
, node
, NULL
, "TargetRef",
183 static xml_node_t
* add_results(struct hs20_osu_client
*ctx
, xml_node_t
*parent
,
184 int msgref
, int cmdref
, int cmdid
,
185 const char *locuri
, const char *data
)
189 node
= xml_node_create(ctx
->xml
, parent
, NULL
, "Results");
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
);
202 static char * mo_str(struct hs20_osu_client
*ctx
, const char *urn
,
205 xml_node_t
*fnode
, *tnds
;
208 fnode
= node_from_file(ctx
->xml
, fname
);
211 tnds
= mo_to_tnds(ctx
->xml
, fnode
, 0, urn
, "syncml:dmddf1.2");
212 xml_node_free(ctx
->xml
, fnode
);
216 str
= xml_node_to_str(ctx
->xml
, tnds
);
217 xml_node_free(ctx
->xml
, tnds
);
220 wpa_printf(MSG_INFO
, "MgmtTree: %s", str
);
226 static void add_item(struct hs20_osu_client
*ctx
, xml_node_t
*parent
,
227 const char *locuri
, const char *data
)
229 xml_node_t
*item
, *node
;
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",
236 xml_node_create_text_ns(ctx
->xml
, node
, "syncml:metinf", "Type",
238 xml_node_create_text(ctx
->xml
, item
, NULL
, "Data", data
);
242 static void add_replace_devinfo(struct hs20_osu_client
*ctx
, xml_node_t
*parent
,
245 xml_node_t
*info
, *child
, *replace
;
247 char locuri
[200], *txt
;
249 info
= node_from_file(ctx
->xml
, "devinfo.xml");
251 wpa_printf(MSG_INFO
, "Could not read devinfo.xml");
255 replace
= xml_node_create(ctx
->xml
, parent
, NULL
, "Replace");
256 if (replace
== NULL
) {
257 xml_node_free(ctx
->xml
, info
);
260 oma_dm_add_cmdid(ctx
, replace
, cmdid
);
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
);
268 add_item(ctx
, replace
, locuri
, txt
);
269 xml_node_get_text_free(ctx
->xml
, txt
);
273 xml_node_free(ctx
->xml
, info
);
277 static void oma_dm_add_hs20_generic_alert(struct hs20_osu_client
*ctx
,
278 xml_node_t
*syncbody
,
279 int cmdid
, const char *oper
,
282 xml_node_t
*node
, *item
;
285 node
= add_alert(ctx
, syncbody
, cmdid
, DM_GENERIC_ALERT
);
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",
294 xml_node_create_text(ctx
->xml
, item
, NULL
, "Data", data
);
298 static xml_node_t
* build_oma_dm_1(struct hs20_osu_client
*ctx
,
299 const char *url
, int msgid
, const char *oper
)
301 xml_node_t
*syncml
, *syncbody
;
305 syncml
= oma_dm_build_hdr(ctx
, url
, msgid
);
309 syncbody
= xml_node_create(ctx
->xml
, syncml
, NULL
, "SyncBody");
310 if (syncbody
== NULL
) {
311 xml_node_free(ctx
->xml
, syncml
);
316 add_alert(ctx
, syncbody
, cmdid
, DM_CLIENT_INITIATED_MGMT
);
318 str
= mo_str(ctx
, NULL
, "devdetail.xml");
320 xml_node_free(ctx
->xml
, syncml
);
324 oma_dm_add_hs20_generic_alert(ctx
, syncbody
, cmdid
, oper
, str
);
328 add_replace_devinfo(ctx
, syncbody
, cmdid
);
330 xml_node_create(ctx
->xml
, syncbody
, NULL
, "Final");
336 static xml_node_t
* build_oma_dm_1_sub_reg(struct hs20_osu_client
*ctx
,
337 const char *url
, int msgid
)
341 syncml
= build_oma_dm_1(ctx
, url
, msgid
, DM_HS20_SUBSCRIPTION_CREATION
);
343 debug_dump_node(ctx
, "OMA-DM Package 1 (sub reg)", syncml
);
349 static xml_node_t
* build_oma_dm_1_sub_prov(struct hs20_osu_client
*ctx
,
350 const char *url
, int msgid
)
354 syncml
= build_oma_dm_1(ctx
, url
, msgid
,
355 DM_HS20_SUBSCRIPTION_PROVISIONING
);
357 debug_dump_node(ctx
, "OMA-DM Package 1 (sub prov)", syncml
);
363 static xml_node_t
* build_oma_dm_1_pol_upd(struct hs20_osu_client
*ctx
,
364 const char *url
, int msgid
)
368 syncml
= build_oma_dm_1(ctx
, url
, msgid
, DM_HS20_POLICY_UPDATE
);
370 debug_dump_node(ctx
, "OMA-DM Package 1 (pol upd)", syncml
);
376 static xml_node_t
* build_oma_dm_1_sub_rem(struct hs20_osu_client
*ctx
,
377 const char *url
, int msgid
)
381 syncml
= build_oma_dm_1(ctx
, url
, msgid
,
382 DM_HS20_SUBSCRIPTION_REMEDIATION
);
384 debug_dump_node(ctx
, "OMA-DM Package 1 (sub rem)", syncml
);
390 static int oma_dm_exec_browser(struct hs20_osu_client
*ctx
, xml_node_t
*exec
)
396 node
= get_node(ctx
->xml
, exec
, "Item/Data");
398 wpa_printf(MSG_INFO
, "No Data node found");
399 return DM_RESP_BAD_REQUEST
;
402 data
= xml_node_get_text(ctx
->xml
, node
);
404 wpa_printf(MSG_INFO
, "Invalid data");
405 return DM_RESP_BAD_REQUEST
;
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
);
413 wpa_printf(MSG_INFO
, "User response in browser completed successfully");
414 write_summary(ctx
, "User response in browser completed successfully");
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
;
424 static int oma_dm_exec_get_cert(struct hs20_osu_client
*ctx
, xml_node_t
*exec
)
426 xml_node_t
*node
, *getcert
;
431 wpa_printf(MSG_INFO
, "Client certificate enrollment");
432 write_summary(ctx
, "Client certificate enrollment");
434 node
= get_node(ctx
->xml
, exec
, "Item/Data");
436 wpa_printf(MSG_INFO
, "No Data node found");
437 return DM_RESP_BAD_REQUEST
;
440 data
= xml_node_get_text(ctx
->xml
, node
);
442 wpa_printf(MSG_INFO
, "Invalid data");
443 return DM_RESP_BAD_REQUEST
;
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
);
449 if (getcert
== NULL
) {
450 wpa_printf(MSG_INFO
, "Could not parse Item/Data node contents");
451 return DM_RESP_BAD_REQUEST
;
454 debug_dump_node(ctx
, "OMA-DM getCertificate", getcert
);
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'",
460 return DM_RESP_BAD_REQUEST
;
463 res
= osu_get_certificate(ctx
, getcert
);
465 xml_node_free(ctx
->xml
, getcert
);
467 return res
== 0 ? DM_RESP_OK
: DM_RESP_COMMAND_FAILED
;
471 static int oma_dm_exec(struct hs20_osu_client
*ctx
, xml_node_t
*exec
)
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
;
482 wpa_printf(MSG_INFO
, "Target LocURI: %s", locuri
);
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
);
491 wpa_printf(MSG_INFO
, "Unsupported exec Target LocURI");
492 ret
= DM_RESP_NOT_FOUND
;
500 static int oma_dm_run_add(struct hs20_osu_client
*ctx
, const char *locuri
,
501 xml_node_t
*add
, xml_node_t
*pps
,
502 const char *pps_fname
)
506 xml_node_t
*node
, *tnds
, *unode
, *pps_node
;
507 char *data
, *uri
, *upos
, *end
;
511 wpa_printf(MSG_INFO
, "Add command target LocURI: %s", locuri
);
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
;
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",
526 return DM_RESP_PERMISSION_DENIED
;
530 if (os_strncasecmp(pos
, "PerProviderSubscription/", 24) != 0) {
532 "Do not allow Add outside ./Wi-Fi/%s/PerProviderSubscription",
534 return DM_RESP_PERMISSION_DENIED
;
538 wpa_printf(MSG_INFO
, "Add command for PPS node %s", pos
);
540 pps_node
= get_node(ctx
->xml
, pps
, pos
);
542 wpa_printf(MSG_INFO
, "Specified PPS node exists already");
543 return DM_RESP_ALREADY_EXISTS
;
546 uri
= os_strdup(pos
);
548 return DM_RESP_COMMAND_FAILED
;
550 upos
= os_strrchr(uri
, '/');
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");
559 wpa_printf(MSG_INFO
, "Parent URI: %s", uri
);
562 /* Add at root of PPS MO */
566 uri_len
= os_strlen(uri
);
567 os_strlcpy(uri
, pos
+ uri_len
, os_strlen(pos
));
571 wpa_printf(MSG_INFO
, "Nodes to add: %s", upos
);
574 end
= os_strchr(upos
, '/');
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
) {
582 return DM_RESP_COMMAND_FAILED
;
587 wpa_printf(MSG_INFO
, "Adding node %s", upos
);
589 node
= get_node(ctx
->xml
, add
, "Item/Meta/Type");
592 type
= xml_node_get_text(ctx
->xml
, node
);
594 wpa_printf(MSG_ERROR
, "Could not find type text");
596 return DM_RESP_BAD_REQUEST
;
599 os_strstr(type
, "application/vnd.syncml.dmtnds+xml");
602 node
= get_node(ctx
->xml
, add
, "Item/Data");
604 wpa_printf(MSG_INFO
, "No Add/Item/Data found");
606 return DM_RESP_BAD_REQUEST
;
609 data
= xml_node_get_text(ctx
->xml
, node
);
611 wpa_printf(MSG_INFO
, "Could not get Add/Item/Data text");
613 return DM_RESP_BAD_REQUEST
;
616 wpa_printf(MSG_DEBUG
, "Add/Item/Data: %s", data
);
619 tnds
= xml_node_from_buf(ctx
->xml
, data
);
620 xml_node_get_text_free(ctx
->xml
, data
);
623 "Could not parse Add/Item/Data text");
625 return DM_RESP_BAD_REQUEST
;
628 unode
= tnds_to_mo(ctx
->xml
, tnds
);
629 xml_node_free(ctx
->xml
, tnds
);
631 wpa_printf(MSG_INFO
, "Could not parse TNDS text");
633 return DM_RESP_BAD_REQUEST
;
636 debug_dump_node(ctx
, "Parsed TNDS", unode
);
638 xml_node_add_child(ctx
->xml
, pps_node
, unode
);
640 /* TODO: What to do here? */
642 return DM_RESP_BAD_REQUEST
;
647 if (update_pps_file(ctx
, pps_fname
, pps
) < 0)
648 return DM_RESP_COMMAND_FAILED
;
650 ctx
->pps_updated
= 1;
656 static int oma_dm_add(struct hs20_osu_client
*ctx
, xml_node_t
*add
,
657 xml_node_t
*pps
, const char *pps_fname
)
664 node
= get_node(ctx
->xml
, add
, "Item/Target/LocURI");
666 wpa_printf(MSG_INFO
, "No Target LocURI node found");
667 return DM_RESP_BAD_REQUEST
;
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
;
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
;
681 node
= get_node(ctx
->xml
, add
, "Item/Data");
683 wpa_printf(MSG_INFO
, "No Data node found");
684 xml_node_get_text_free(ctx
->xml
, locuri
);
685 return DM_RESP_BAD_REQUEST
;
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
);
695 os_strlcpy(fname
, pps_fname
, sizeof(fname
));
697 ret
= hs20_add_pps_mo(ctx
, locuri
, node
, fname
, sizeof(fname
));
698 xml_node_get_text_free(ctx
->xml
, locuri
);
700 return ret
== -2 ? DM_RESP_ALREADY_EXISTS
:
701 DM_RESP_COMMAND_FAILED
;
703 if (ctx
->no_reconnect
== 2) {
704 os_snprintf(ctx
->pps_fname
, sizeof(ctx
->pps_fname
), "%s",
706 ctx
->pps_cred_set
= 1;
710 wpa_printf(MSG_INFO
, "Updating wpa_supplicant credentials");
711 cmd_set_pps(ctx
, fname
);
713 if (ctx
->no_reconnect
)
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");
724 static int oma_dm_replace(struct hs20_osu_client
*ctx
, xml_node_t
*replace
,
725 xml_node_t
*pps
, const char *pps_fname
)
729 xml_node_t
*node
, *tnds
, *unode
, *pps_node
, *parent
;
733 locuri
= oma_dm_get_target_locuri(ctx
, replace
);
735 return DM_RESP_BAD_REQUEST
;
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");
741 return DM_RESP_PERMISSION_DENIED
;
745 if (ctx
->fqdn
== NULL
) {
747 return DM_RESP_COMMAND_FAILED
;
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",
755 return DM_RESP_PERMISSION_DENIED
;
759 if (os_strncasecmp(pos
, "PerProviderSubscription/", 24) != 0) {
761 "Do not allow Replace outside ./Wi-Fi/%s/PerProviderSubscription",
764 return DM_RESP_PERMISSION_DENIED
;
768 wpa_printf(MSG_INFO
, "Replace command for PPS node %s", pos
);
770 pps_node
= get_node(ctx
->xml
, pps
, pos
);
771 if (pps_node
== NULL
) {
772 wpa_printf(MSG_INFO
, "Specified PPS node not found");
774 return DM_RESP_NOT_FOUND
;
777 node
= get_node(ctx
->xml
, replace
, "Item/Meta/Type");
780 type
= xml_node_get_text(ctx
->xml
, node
);
782 wpa_printf(MSG_INFO
, "Could not find type text");
784 return DM_RESP_BAD_REQUEST
;
787 os_strstr(type
, "application/vnd.syncml.dmtnds+xml");
790 node
= get_node(ctx
->xml
, replace
, "Item/Data");
792 wpa_printf(MSG_INFO
, "No Replace/Item/Data found");
794 return DM_RESP_BAD_REQUEST
;
797 data
= xml_node_get_text(ctx
->xml
, node
);
799 wpa_printf(MSG_INFO
, "Could not get Replace/Item/Data text");
801 return DM_RESP_BAD_REQUEST
;
804 wpa_printf(MSG_DEBUG
, "Replace/Item/Data: %s", data
);
807 tnds
= xml_node_from_buf(ctx
->xml
, data
);
808 xml_node_get_text_free(ctx
->xml
, data
);
811 "Could not parse Replace/Item/Data text");
813 return DM_RESP_BAD_REQUEST
;
816 unode
= tnds_to_mo(ctx
->xml
, tnds
);
817 xml_node_free(ctx
->xml
, tnds
);
819 wpa_printf(MSG_INFO
, "Could not parse TNDS text");
821 return DM_RESP_BAD_REQUEST
;
824 debug_dump_node(ctx
, "Parsed TNDS", unode
);
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
);
830 xml_node_set_text(ctx
->xml
, pps_node
, data
);
831 xml_node_get_text_free(ctx
->xml
, data
);
836 if (update_pps_file(ctx
, pps_fname
, pps
) < 0)
837 return DM_RESP_COMMAND_FAILED
;
839 ctx
->pps_updated
= 1;
845 static int oma_dm_get(struct hs20_osu_client
*ctx
, xml_node_t
*get
,
846 xml_node_t
*pps
, const char *pps_fname
, char **value
)
850 xml_node_t
*pps_node
;
855 locuri
= oma_dm_get_target_locuri(ctx
, get
);
857 return DM_RESP_BAD_REQUEST
;
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");
863 return DM_RESP_PERMISSION_DENIED
;
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",
875 return DM_RESP_PERMISSION_DENIED
;
879 if (os_strncasecmp(pos
, "PerProviderSubscription/", 24) != 0) {
881 "Do not allow Get outside ./Wi-Fi/%s/PerProviderSubscription",
884 return DM_RESP_PERMISSION_DENIED
;
888 wpa_printf(MSG_INFO
, "Get command for PPS node %s", pos
);
890 pps_node
= get_node(ctx
->xml
, pps
, pos
);
891 if (pps_node
== NULL
) {
892 wpa_printf(MSG_INFO
, "Specified PPS node not found");
894 return DM_RESP_NOT_FOUND
;
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");
902 return DM_RESP_PERMISSION_DENIED
;
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].
911 *value
= xml_node_get_text(ctx
->xml
, pps_node
);
913 return DM_RESP_COMMAND_FAILED
;
919 static int oma_dm_get_cmdid(struct hs20_osu_client
*ctx
, xml_node_t
*node
)
925 cnode
= get_node(ctx
->xml
, node
, "CmdID");
929 str
= xml_node_get_text(ctx
->xml
, cnode
);
933 xml_node_get_text_free(ctx
->xml
, str
);
938 static xml_node_t
* oma_dm_send_recv(struct hs20_osu_client
*ctx
,
939 const char *url
, xml_node_t
*syncml
,
941 const char *username
, const char *password
,
942 const char *client_cert
,
943 const char *client_key
)
947 char *resp_uri
= NULL
;
949 str
= xml_node_to_str(ctx
->xml
, syncml
);
950 xml_node_free(ctx
->xml
, syncml
);
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
);
966 const char *err
= http_get_err(ctx
->http
);
968 wpa_printf(MSG_INFO
, "HTTP error: %s", err
);
969 write_result(ctx
, "HTTP error: %s", err
);
971 write_summary(ctx
, "Failed to send OMA DM Package");
975 wpa_printf(MSG_DEBUG
, "Server response: %s", res
);
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
);
982 wpa_printf(MSG_INFO
, "Failed to parse OMA DM response");
986 debug_dump_node(ctx
, "OMA DM Package", resp
);
992 static xml_node_t
* oma_dm_process(struct hs20_osu_client
*ctx
, const char *url
,
993 xml_node_t
*resp
, int msgid
,
995 xml_node_t
*pps
, const char *pps_fname
)
997 xml_node_t
*syncml
, *syncbody
, *hdr
, *body
, *child
;
999 char *resp_uri
= NULL
;
1000 int server_msgid
= 0;
1003 int resp_needed
= 0;
1008 *ret_resp_uri
= NULL
;
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");
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");
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
);
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
);
1035 server_msgid
= atoi(tmp
);
1036 xml_node_get_text_free(ctx
->xml
, tmp
);
1040 wpa_printf(MSG_INFO
, "Server MsgID: %d", server_msgid
);
1042 wpa_printf(MSG_INFO
, "RespURI: %s", resp_uri
);
1044 syncml
= oma_dm_build_hdr(ctx
, resp_uri
? resp_uri
: url
, msgid
);
1045 if (syncml
== NULL
) {
1050 syncbody
= xml_node_create(ctx
->xml
, syncml
, NULL
, "SyncBody");
1052 add_status(ctx
, syncbody
, server_msgid
, 0, cmdid
, "SyncHdr",
1053 DM_RESP_AUTH_ACCEPTED
, NULL
);
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
);
1064 locuri
= oma_dm_get_target_locuri(ctx
, child
);
1066 res
= DM_RESP_BAD_REQUEST
;
1067 add_status(ctx
, syncbody
, server_msgid
, server_cmdid
,
1068 cmdid
, name
, res
, locuri
);
1071 } else if (os_strcasecmp(name
, "Add") == 0) {
1072 int res
= oma_dm_add(ctx
, child
, pps
, pps_fname
);
1074 locuri
= oma_dm_get_target_locuri(ctx
, child
);
1076 res
= DM_RESP_BAD_REQUEST
;
1077 add_status(ctx
, syncbody
, server_msgid
, server_cmdid
,
1078 cmdid
, name
, res
, locuri
);
1081 } else if (os_strcasecmp(name
, "Replace") == 0) {
1083 res
= oma_dm_replace(ctx
, child
, pps
, pps_fname
);
1085 locuri
= oma_dm_get_target_locuri(ctx
, child
);
1087 res
= DM_RESP_BAD_REQUEST
;
1088 add_status(ctx
, syncbody
, server_msgid
, server_cmdid
,
1089 cmdid
, name
, res
, locuri
);
1092 } else if (os_strcasecmp(name
, "Status") == 0) {
1093 /* TODO: Verify success */
1094 } else if (os_strcasecmp(name
, "Get") == 0) {
1097 res
= oma_dm_get(ctx
, child
, pps
, pps_fname
, &value
);
1099 locuri
= oma_dm_get_target_locuri(ctx
, child
);
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
) {
1106 add_results(ctx
, syncbody
, server_msgid
,
1107 server_cmdid
, cmdid
, locuri
, value
);
1110 xml_node_get_text_free(ctx
->xml
, value
);
1112 #if 0 /* TODO: MUST support */
1113 } else if (os_strcasecmp(name
, "Delete") == 0) {
1115 #if 0 /* TODO: MUST support */
1116 } else if (os_strcasecmp(name
, "Sequence") == 0) {
1118 } else if (os_strcasecmp(name
, "Final") == 0) {
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
,
1132 wpa_printf(MSG_INFO
, "Final node not found");
1133 xml_node_free(ctx
->xml
, syncml
);
1139 wpa_printf(MSG_INFO
, "Exchange completed - no response needed");
1140 xml_node_free(ctx
->xml
, syncml
);
1145 xml_node_create(ctx
->xml
, syncbody
, NULL
, "Final");
1147 debug_dump_node(ctx
, "OMA-DM Package 3", syncml
);
1149 *ret_resp_uri
= resp_uri
;
1154 int cmd_oma_dm_prov(struct hs20_osu_client
*ctx
, const char *url
)
1156 xml_node_t
*syncml
, *resp
;
1157 char *resp_uri
= NULL
;
1161 wpa_printf(MSG_INFO
, "Invalid prov command (missing URL)");
1165 wpa_printf(MSG_INFO
, "OMA-DM credential provisioning requested");
1166 write_summary(ctx
, "OMA-DM credential provisioning");
1169 syncml
= build_oma_dm_1_sub_reg(ctx
, url
, msgid
);
1174 resp
= oma_dm_send_recv(ctx
, resp_uri
? resp_uri
: url
,
1175 syncml
, NULL
, NULL
, NULL
, NULL
, NULL
);
1180 syncml
= oma_dm_process(ctx
, url
, resp
, msgid
, &resp_uri
,
1182 xml_node_free(ctx
->xml
, resp
);
1187 return ctx
->pps_cred_set
? 0 : -1;
1191 int cmd_oma_dm_sim_prov(struct hs20_osu_client
*ctx
, const char *url
)
1193 xml_node_t
*syncml
, *resp
;
1194 char *resp_uri
= NULL
;
1198 wpa_printf(MSG_INFO
, "Invalid prov command (missing URL)");
1202 wpa_printf(MSG_INFO
, "OMA-DM SIM provisioning requested");
1203 ctx
->no_reconnect
= 2;
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");
1208 if (wait_ip_addr(ctx
->ifname
, 15) < 0) {
1209 wpa_printf(MSG_INFO
, "Could not get IP address for WLAN - try connection anyway");
1211 write_summary(ctx
, "OMA-DM SIM provisioning");
1214 syncml
= build_oma_dm_1_sub_prov(ctx
, url
, msgid
);
1219 resp
= oma_dm_send_recv(ctx
, resp_uri
? resp_uri
: url
,
1220 syncml
, NULL
, NULL
, NULL
, NULL
, NULL
);
1225 syncml
= oma_dm_process(ctx
, url
, resp
, msgid
, &resp_uri
,
1227 xml_node_free(ctx
->xml
, resp
);
1232 if (ctx
->pps_cred_set
) {
1233 wpa_printf(MSG_INFO
, "Updating wpa_supplicant credentials");
1234 cmd_set_pps(ctx
, ctx
->pps_fname
);
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");
1245 return ctx
->pps_cred_set
? 0 : -1;
1249 void oma_dm_pol_upd(struct hs20_osu_client
*ctx
, const char *address
,
1250 const char *pps_fname
,
1251 const char *client_cert
, const char *client_key
,
1252 const char *cred_username
, const char *cred_password
,
1255 xml_node_t
*syncml
, *resp
;
1256 char *resp_uri
= NULL
;
1259 wpa_printf(MSG_INFO
, "OMA-DM policy update");
1260 write_summary(ctx
, "OMA-DM policy update");
1263 syncml
= build_oma_dm_1_pol_upd(ctx
, address
, msgid
);
1268 resp
= oma_dm_send_recv(ctx
, resp_uri
? resp_uri
: address
,
1269 syncml
, NULL
, cred_username
,
1270 cred_password
, client_cert
, client_key
);
1275 syncml
= oma_dm_process(ctx
, address
, resp
, msgid
, &resp_uri
,
1277 xml_node_free(ctx
->xml
, resp
);
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");
1290 "Failed to request wpa_supplicant to reconnect");
1296 void oma_dm_sub_rem(struct hs20_osu_client
*ctx
, const char *address
,
1297 const char *pps_fname
,
1298 const char *client_cert
, const char *client_key
,
1299 const char *cred_username
, const char *cred_password
,
1302 xml_node_t
*syncml
, *resp
;
1303 char *resp_uri
= NULL
;
1306 wpa_printf(MSG_INFO
, "OMA-DM subscription remediation");
1307 write_summary(ctx
, "OMA-DM subscription remediation");
1310 syncml
= build_oma_dm_1_sub_rem(ctx
, address
, msgid
);
1315 resp
= oma_dm_send_recv(ctx
, resp_uri
? resp_uri
: address
,
1316 syncml
, NULL
, cred_username
,
1317 cred_password
, client_cert
, client_key
);
1322 syncml
= oma_dm_process(ctx
, address
, resp
, msgid
, &resp_uri
,
1324 xml_node_free(ctx
->xml
, resp
);
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");
1339 void cmd_oma_dm_add(struct hs20_osu_client
*ctx
, const char *pps_fname
,
1340 const char *add_fname
)
1342 xml_node_t
*pps
, *add
;
1345 ctx
->fqdn
= os_strdup("wi-fi.org");
1347 pps
= node_from_file(ctx
->xml
, pps_fname
);
1349 wpa_printf(MSG_INFO
, "PPS file %s could not be parsed",
1354 add
= node_from_file(ctx
->xml
, add_fname
);
1356 wpa_printf(MSG_INFO
, "Add file %s could not be parsed",
1358 xml_node_free(ctx
->xml
, pps
);
1362 res
= oma_dm_add(ctx
, add
, pps
, pps_fname
);
1363 wpa_printf(MSG_INFO
, "oma_dm_add --> %d", res
);
1365 xml_node_free(ctx
->xml
, pps
);
1366 xml_node_free(ctx
->xml
, add
);
1370 void cmd_oma_dm_replace(struct hs20_osu_client
*ctx
, const char *pps_fname
,
1371 const char *replace_fname
)
1373 xml_node_t
*pps
, *replace
;
1376 ctx
->fqdn
= os_strdup("wi-fi.org");
1378 pps
= node_from_file(ctx
->xml
, pps_fname
);
1380 wpa_printf(MSG_INFO
, "PPS file %s could not be parsed",
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",
1389 xml_node_free(ctx
->xml
, pps
);
1393 res
= oma_dm_replace(ctx
, replace
, pps
, pps_fname
);
1394 wpa_printf(MSG_INFO
, "oma_dm_replace --> %d", res
);
1396 xml_node_free(ctx
->xml
, pps
);
1397 xml_node_free(ctx
->xml
, replace
);