Updated through tag hostap_2_5 from git://w1.fi/hostap.git
[mech_eap.git] / libeap / hs20 / client / oma_dm_client.c
1 /*
2  * Hotspot 2.0 - OMA DM client
3  * Copyright (c) 2013-2014, Qualcomm Atheros, Inc.
4  *
5  * This software may be distributed under the terms of the BSD license.
6  * See README for more details.
7  */
8
9 #include "includes.h"
10
11 #include "common.h"
12 #include "wpa_helpers.h"
13 #include "xml-utils.h"
14 #include "http-utils.h"
15 #include "utils/browser.h"
16 #include "osu_client.h"
17
18
19 #define DM_SERVER_INITIATED_MGMT 1200
20 #define DM_CLIENT_INITIATED_MGMT 1201
21 #define DM_GENERIC_ALERT 1226
22
23 /* OMA-TS-SyncML-RepPro-V1_2_2 - 10. Response Status Codes */
24 #define DM_RESP_OK 200
25 #define DM_RESP_AUTH_ACCEPTED 212
26 #define DM_RESP_CHUNKED_ITEM_ACCEPTED 213
27 #define DM_RESP_NOT_EXECUTED 215
28 #define DM_RESP_ATOMIC_ROLL_BACK_OK 216
29 #define DM_RESP_NOT_MODIFIED 304
30 #define DM_RESP_BAD_REQUEST 400
31 #define DM_RESP_UNAUTHORIZED 401
32 #define DM_RESP_FORBIDDEN 403
33 #define DM_RESP_NOT_FOUND 404
34 #define DM_RESP_COMMAND_NOT_ALLOWED 405
35 #define DM_RESP_OPTIONAL_FEATURE_NOT_SUPPORTED 406
36 #define DM_RESP_MISSING_CREDENTIALS 407
37 #define DM_RESP_CONFLICT 409
38 #define DM_RESP_GONE 410
39 #define DM_RESP_INCOMPLETE_COMMAND 412
40 #define DM_RESP_REQ_ENTITY_TOO_LARGE 413
41 #define DM_RESP_URI_TOO_LONG 414
42 #define DM_RESP_UNSUPPORTED_MEDIA_TYPE_OR_FORMAT 415
43 #define DM_RESP_REQ_TOO_BIG 416
44 #define DM_RESP_ALREADY_EXISTS 418
45 #define DM_RESP_DEVICE_FULL 420
46 #define DM_RESP_SIZE_MISMATCH 424
47 #define DM_RESP_PERMISSION_DENIED 425
48 #define DM_RESP_COMMAND_FAILED 500
49 #define DM_RESP_COMMAND_NOT_IMPLEMENTED 501
50 #define DM_RESP_ATOMIC_ROLL_BACK_FAILED 516
51
52 #define DM_HS20_SUBSCRIPTION_CREATION \
53         "org.wi-fi.hotspot2dot0.SubscriptionCreation"
54 #define DM_HS20_SUBSCRIPTION_PROVISIONING \
55         "org.wi-fi.hotspot2dot0.SubscriptionProvisioning"
56 #define DM_HS20_SUBSCRIPTION_REMEDIATION \
57         "org.wi-fi.hotspot2dot0.SubscriptionRemediation"
58 #define DM_HS20_POLICY_UPDATE \
59         "org.wi-fi.hotspot2dot0.PolicyUpdate"
60
61 #define DM_URI_PPS "./Wi-Fi/org.wi-fi/PerProviderSubscription"
62 #define DM_URI_LAUNCH_BROWSER \
63         "./DevDetail/Ext/org.wi-fi/Wi-Fi/Ops/launchBrowserToURI"
64
65
66 static void add_item(struct hs20_osu_client *ctx, xml_node_t *parent,
67                      const char *locuri, const char *data);
68
69
70 static const char * int2str(int val)
71 {
72         static char buf[20];
73         snprintf(buf, sizeof(buf), "%d", val);
74         return buf;
75 }
76
77
78 static char * oma_dm_get_target_locuri(struct hs20_osu_client *ctx,
79                                        xml_node_t *node)
80 {
81         xml_node_t *locuri;
82         char *uri, *ret = NULL;
83
84         locuri = get_node(ctx->xml, node, "Item/Target/LocURI");
85         if (locuri == NULL)
86                 return NULL;
87
88         uri = xml_node_get_text(ctx->xml, locuri);
89         if (uri)
90                 ret = os_strdup(uri);
91         xml_node_get_text_free(ctx->xml, uri);
92         return ret;
93 }
94
95
96 static void oma_dm_add_locuri(struct hs20_osu_client *ctx, xml_node_t *parent,
97                               const char *element, const char *uri)
98 {
99         xml_node_t *node;
100
101         node = xml_node_create(ctx->xml, parent, NULL, element);
102         if (node == NULL)
103                 return;
104         xml_node_create_text(ctx->xml, node, NULL, "LocURI", uri);
105 }
106
107
108 static xml_node_t * oma_dm_build_hdr(struct hs20_osu_client *ctx,
109                                      const char *url, int msgid)
110 {
111         xml_node_t *syncml, *synchdr;
112         xml_namespace_t *ns;
113
114         syncml = xml_node_create_root(ctx->xml, "SYNCML:SYNCML1.2", NULL, &ns,
115                                       "SyncML");
116
117         synchdr = xml_node_create(ctx->xml, syncml, NULL, "SyncHdr");
118         xml_node_create_text(ctx->xml, synchdr, NULL, "VerDTD", "1.2");
119         xml_node_create_text(ctx->xml, synchdr, NULL, "VerProto", "DM/1.2");
120         xml_node_create_text(ctx->xml, synchdr, NULL, "SessionID", "1");
121         xml_node_create_text(ctx->xml, synchdr, NULL, "MsgID", int2str(msgid));
122
123         oma_dm_add_locuri(ctx, synchdr, "Target", url);
124         oma_dm_add_locuri(ctx, synchdr, "Source", ctx->devid);
125
126         return syncml;
127 }
128
129
130 static void oma_dm_add_cmdid(struct hs20_osu_client *ctx, xml_node_t *parent,
131                              int cmdid)
132 {
133         xml_node_create_text(ctx->xml, parent, NULL, "CmdID", int2str(cmdid));
134 }
135
136
137 static xml_node_t * add_alert(struct hs20_osu_client *ctx, xml_node_t *parent,
138                               int cmdid, int data)
139 {
140         xml_node_t *node;
141
142         node = xml_node_create(ctx->xml, parent, NULL, "Alert");
143         if (node == NULL)
144                 return NULL;
145         oma_dm_add_cmdid(ctx, node, cmdid);
146         xml_node_create_text(ctx->xml, node, NULL, "Data", int2str(data));
147
148         return node;
149 }
150
151
152 static xml_node_t * add_status(struct hs20_osu_client *ctx, xml_node_t *parent,
153                                int msgref, int cmdref, int cmdid,
154                                const char *cmd, int data, const char *targetref)
155 {
156         xml_node_t *node;
157
158         node = xml_node_create(ctx->xml, parent, NULL, "Status");
159         if (node == NULL)
160                 return NULL;
161         oma_dm_add_cmdid(ctx, node, cmdid);
162         xml_node_create_text(ctx->xml, node, NULL, "MsgRef", int2str(msgref));
163         if (cmdref)
164                 xml_node_create_text(ctx->xml, node, NULL, "CmdRef",
165                                      int2str(cmdref));
166         xml_node_create_text(ctx->xml, node, NULL, "Cmd", cmd);
167         xml_node_create_text(ctx->xml, node, NULL, "Data", int2str(data));
168         if (targetref) {
169                 xml_node_create_text(ctx->xml, node, NULL, "TargetRef",
170                                      targetref);
171         }
172
173         return node;
174 }
175
176
177 static xml_node_t * add_results(struct hs20_osu_client *ctx, xml_node_t *parent,
178                                 int msgref, int cmdref, int cmdid,
179                                 const char *locuri, const char *data)
180 {
181         xml_node_t *node;
182
183         node = xml_node_create(ctx->xml, parent, NULL, "Results");
184         if (node == NULL)
185                 return NULL;
186
187         oma_dm_add_cmdid(ctx, node, cmdid);
188         xml_node_create_text(ctx->xml, node, NULL, "MsgRef", int2str(msgref));
189         xml_node_create_text(ctx->xml, node, NULL, "CmdRef", int2str(cmdref));
190         add_item(ctx, node, locuri, data);
191
192         return node;
193 }
194
195
196 static char * mo_str(struct hs20_osu_client *ctx, const char *urn,
197                      const char *fname)
198 {
199         xml_node_t *fnode, *tnds;
200         char *str;
201
202         fnode = node_from_file(ctx->xml, fname);
203         if (!fnode)
204                 return NULL;
205         tnds = mo_to_tnds(ctx->xml, fnode, 0, urn, "syncml:dmddf1.2");
206         xml_node_free(ctx->xml, fnode);
207         if (!tnds)
208                 return NULL;
209
210         str = xml_node_to_str(ctx->xml, tnds);
211         xml_node_free(ctx->xml, tnds);
212         if (str == NULL)
213                 return NULL;
214         wpa_printf(MSG_INFO, "MgmtTree: %s", str);
215
216         return str;
217 }
218
219
220 static void add_item(struct hs20_osu_client *ctx, xml_node_t *parent,
221                      const char *locuri, const char *data)
222 {
223         xml_node_t *item, *node;
224
225         item = xml_node_create(ctx->xml, parent, NULL, "Item");
226         oma_dm_add_locuri(ctx, item, "Source", locuri);
227         node = xml_node_create(ctx->xml, item, NULL, "Meta");
228         xml_node_create_text_ns(ctx->xml, node, "syncml:metinf", "Format",
229                                 "Chr");
230         xml_node_create_text_ns(ctx->xml, node, "syncml:metinf", "Type",
231                                 "text/plain");
232         xml_node_create_text(ctx->xml, item, NULL, "Data", data);
233 }
234
235
236 static void add_replace_devinfo(struct hs20_osu_client *ctx, xml_node_t *parent,
237                                 int cmdid)
238 {
239         xml_node_t *info, *child, *replace;
240         const char *name;
241         char locuri[200], *txt;
242
243         info = node_from_file(ctx->xml, "devinfo.xml");
244         if (info == NULL) {
245                 wpa_printf(MSG_INFO, "Could not read devinfo.xml");
246                 return;
247         }
248
249         replace = xml_node_create(ctx->xml, parent, NULL, "Replace");
250         if (replace == NULL) {
251                 xml_node_free(ctx->xml, info);
252                 return;
253         }
254         oma_dm_add_cmdid(ctx, replace, cmdid);
255
256         xml_node_for_each_child(ctx->xml, child, info) {
257                 xml_node_for_each_check(ctx->xml, child);
258                 name = xml_node_get_localname(ctx->xml, child);
259                 os_snprintf(locuri, sizeof(locuri), "./DevInfo/%s", name);
260                 txt = xml_node_get_text(ctx->xml, child);
261                 if (txt) {
262                         add_item(ctx, replace, locuri, txt);
263                         xml_node_get_text_free(ctx->xml, txt);
264                 }
265         }
266
267         xml_node_free(ctx->xml, info);
268 }
269
270
271 static void oma_dm_add_hs20_generic_alert(struct hs20_osu_client *ctx,
272                                           xml_node_t *syncbody,
273                                           int cmdid, const char *oper,
274                                           const char *data)
275 {
276         xml_node_t *node, *item;
277         char buf[200];
278
279         node = add_alert(ctx, syncbody, cmdid, DM_GENERIC_ALERT);
280
281         item = xml_node_create(ctx->xml, node, NULL, "Item");
282         oma_dm_add_locuri(ctx, item, "Source", DM_URI_PPS);
283         node = xml_node_create(ctx->xml, item, NULL, "Meta");
284         snprintf(buf, sizeof(buf), "Reversed-Domain-Name: %s", oper);
285         xml_node_create_text_ns(ctx->xml, node, "syncml:metinf", "Type", buf);
286         xml_node_create_text_ns(ctx->xml, node, "syncml:metinf", "Format",
287                                 "xml");
288         xml_node_create_text(ctx->xml, item, NULL, "Data", data);
289 }
290
291
292 static xml_node_t * build_oma_dm_1(struct hs20_osu_client *ctx,
293                                    const char *url, int msgid, const char *oper)
294 {
295         xml_node_t *syncml, *syncbody;
296         char *str;
297         int cmdid = 0;
298
299         syncml = oma_dm_build_hdr(ctx, url, msgid);
300         if (syncml == NULL)
301                 return NULL;
302
303         syncbody = xml_node_create(ctx->xml, syncml, NULL, "SyncBody");
304         if (syncbody == NULL) {
305                 xml_node_free(ctx->xml, syncml);
306                 return NULL;
307         }
308
309         cmdid++;
310         add_alert(ctx, syncbody, cmdid, DM_CLIENT_INITIATED_MGMT);
311
312         str = mo_str(ctx, NULL, "devdetail.xml");
313         if (str == NULL) {
314                 xml_node_free(ctx->xml, syncml);
315                 return NULL;
316         }
317         cmdid++;
318         oma_dm_add_hs20_generic_alert(ctx, syncbody, cmdid, oper, str);
319         os_free(str);
320
321         cmdid++;
322         add_replace_devinfo(ctx, syncbody, cmdid);
323
324         xml_node_create(ctx->xml, syncbody, NULL, "Final");
325
326         return syncml;
327 }
328
329
330 static xml_node_t * build_oma_dm_1_sub_reg(struct hs20_osu_client *ctx,
331                                            const char *url, int msgid)
332 {
333         xml_node_t *syncml;
334
335         syncml = build_oma_dm_1(ctx, url, msgid, DM_HS20_SUBSCRIPTION_CREATION);
336         if (syncml)
337                 debug_dump_node(ctx, "OMA-DM Package 1 (sub reg)", syncml);
338
339         return syncml;
340 }
341
342
343 static xml_node_t * build_oma_dm_1_sub_prov(struct hs20_osu_client *ctx,
344                                             const char *url, int msgid)
345 {
346         xml_node_t *syncml;
347
348         syncml = build_oma_dm_1(ctx, url, msgid,
349                                 DM_HS20_SUBSCRIPTION_PROVISIONING);
350         if (syncml)
351                 debug_dump_node(ctx, "OMA-DM Package 1 (sub prov)", syncml);
352
353         return syncml;
354 }
355
356
357 static xml_node_t * build_oma_dm_1_pol_upd(struct hs20_osu_client *ctx,
358                                            const char *url, int msgid)
359 {
360         xml_node_t *syncml;
361
362         syncml = build_oma_dm_1(ctx, url, msgid, DM_HS20_POLICY_UPDATE);
363         if (syncml)
364                 debug_dump_node(ctx, "OMA-DM Package 1 (pol upd)", syncml);
365
366         return syncml;
367 }
368
369
370 static xml_node_t * build_oma_dm_1_sub_rem(struct hs20_osu_client *ctx,
371                                            const char *url, int msgid)
372 {
373         xml_node_t *syncml;
374
375         syncml = build_oma_dm_1(ctx, url, msgid,
376                                 DM_HS20_SUBSCRIPTION_REMEDIATION);
377         if (syncml)
378                 debug_dump_node(ctx, "OMA-DM Package 1 (sub rem)", syncml);
379
380         return syncml;
381 }
382
383
384 static int oma_dm_exec_browser(struct hs20_osu_client *ctx, xml_node_t *exec)
385 {
386         xml_node_t *node;
387         char *data;
388         int res;
389
390         node = get_node(ctx->xml, exec, "Item/Data");
391         if (node == NULL) {
392                 wpa_printf(MSG_INFO, "No Data node found");
393                 return DM_RESP_BAD_REQUEST;
394         }
395
396         data = xml_node_get_text(ctx->xml, node);
397         if (data == NULL) {
398                 wpa_printf(MSG_INFO, "Invalid data");
399                 return DM_RESP_BAD_REQUEST;
400         }
401         wpa_printf(MSG_INFO, "Data: %s", data);
402         wpa_printf(MSG_INFO, "Launch browser to URI '%s'", data);
403         write_summary(ctx, "Launch browser to URI '%s'", data);
404         res = hs20_web_browser(data);
405         xml_node_get_text_free(ctx->xml, data);
406         if (res > 0) {
407                 wpa_printf(MSG_INFO, "User response in browser completed successfully");
408                 write_summary(ctx, "User response in browser completed successfully");
409                 return DM_RESP_OK;
410         } else {
411                 wpa_printf(MSG_INFO, "Failed to receive user response");
412                 write_summary(ctx, "Failed to receive user response");
413                 return DM_RESP_COMMAND_FAILED;
414         }
415 }
416
417
418 static int oma_dm_exec_get_cert(struct hs20_osu_client *ctx, xml_node_t *exec)
419 {
420         xml_node_t *node, *getcert;
421         char *data;
422         const char *name;
423         int res;
424
425         wpa_printf(MSG_INFO, "Client certificate enrollment");
426         write_summary(ctx, "Client certificate enrollment");
427
428         node = get_node(ctx->xml, exec, "Item/Data");
429         if (node == NULL) {
430                 wpa_printf(MSG_INFO, "No Data node found");
431                 return DM_RESP_BAD_REQUEST;
432         }
433
434         data = xml_node_get_text(ctx->xml, node);
435         if (data == NULL) {
436                 wpa_printf(MSG_INFO, "Invalid data");
437                 return DM_RESP_BAD_REQUEST;
438         }
439         wpa_printf(MSG_INFO, "Data: %s", data);
440         getcert = xml_node_from_buf(ctx->xml, data);
441         xml_node_get_text_free(ctx->xml, data);
442
443         if (getcert == NULL) {
444                 wpa_printf(MSG_INFO, "Could not parse Item/Data node contents");
445                 return DM_RESP_BAD_REQUEST;
446         }
447
448         debug_dump_node(ctx, "OMA-DM getCertificate", getcert);
449
450         name = xml_node_get_localname(ctx->xml, getcert);
451         if (name == NULL || os_strcasecmp(name, "getCertificate") != 0) {
452                 wpa_printf(MSG_INFO, "Unexpected getCertificate node name '%s'",
453                            name);
454                 return DM_RESP_BAD_REQUEST;
455         }
456
457         res = osu_get_certificate(ctx, getcert);
458
459         xml_node_free(ctx->xml, getcert);
460
461         return res == 0 ? DM_RESP_OK : DM_RESP_COMMAND_FAILED;
462 }
463
464
465 static int oma_dm_exec(struct hs20_osu_client *ctx, xml_node_t *exec)
466 {
467         char *locuri;
468         int ret;
469
470         locuri = oma_dm_get_target_locuri(ctx, exec);
471         if (locuri == NULL) {
472                 wpa_printf(MSG_INFO, "No Target LocURI node found");
473                 return DM_RESP_BAD_REQUEST;
474         }
475
476         wpa_printf(MSG_INFO, "Target LocURI: %s", locuri);
477
478         if (os_strcasecmp(locuri, "./DevDetail/Ext/org.wi-fi/Wi-Fi/Ops/"
479                           "launchBrowserToURI") == 0) {
480                 ret = oma_dm_exec_browser(ctx, exec);
481         } else if (os_strcasecmp(locuri, "./DevDetail/Ext/org.wi-fi/Wi-Fi/Ops/"
482                           "getCertificate") == 0) {
483                 ret = oma_dm_exec_get_cert(ctx, exec);
484         } else {
485                 wpa_printf(MSG_INFO, "Unsupported exec Target LocURI");
486                 ret = DM_RESP_NOT_FOUND;
487         }
488         os_free(locuri);
489
490         return ret;
491 }
492
493
494 static int oma_dm_run_add(struct hs20_osu_client *ctx, const char *locuri,
495                           xml_node_t *add, xml_node_t *pps,
496                           const char *pps_fname)
497 {
498         const char *pos;
499         size_t fqdn_len;
500         xml_node_t *node, *tnds, *unode, *pps_node;
501         char *data, *uri, *upos, *end;
502         int use_tnds = 0;
503         size_t uri_len;
504
505         wpa_printf(MSG_INFO, "Add command target LocURI: %s", locuri);
506
507         if (os_strncasecmp(locuri, "./Wi-Fi/", 8) != 0) {
508                 wpa_printf(MSG_INFO, "Do not allow Add outside ./Wi-Fi");
509                 return DM_RESP_PERMISSION_DENIED;
510         }
511         pos = locuri + 8;
512
513         if (ctx->fqdn == NULL)
514                 return DM_RESP_COMMAND_FAILED;
515         fqdn_len = os_strlen(ctx->fqdn);
516         if (os_strncasecmp(pos, ctx->fqdn, fqdn_len) != 0 ||
517             pos[fqdn_len] != '/') {
518                 wpa_printf(MSG_INFO, "Do not allow Add outside ./Wi-Fi/%s",
519                            ctx->fqdn);
520                 return DM_RESP_PERMISSION_DENIED;
521         }
522         pos += fqdn_len + 1;
523
524         if (os_strncasecmp(pos, "PerProviderSubscription/", 24) != 0) {
525                 wpa_printf(MSG_INFO,
526                            "Do not allow Add outside ./Wi-Fi/%s/PerProviderSubscription",
527                            ctx->fqdn);
528                 return DM_RESP_PERMISSION_DENIED;
529         }
530         pos += 24;
531
532         wpa_printf(MSG_INFO, "Add command for PPS node %s", pos);
533
534         pps_node = get_node(ctx->xml, pps, pos);
535         if (pps_node) {
536                 wpa_printf(MSG_INFO, "Specified PPS node exists already");
537                 return DM_RESP_ALREADY_EXISTS;
538         }
539
540         uri = os_strdup(pos);
541         if (uri == NULL)
542                 return DM_RESP_COMMAND_FAILED;
543         while (!pps_node) {
544                 upos = os_strrchr(uri, '/');
545                 if (!upos)
546                         break;
547                 upos[0] = '\0';
548                 pps_node = get_node(ctx->xml, pps, uri);
549                 wpa_printf(MSG_INFO, "Node %s %s", uri,
550                            pps_node ? "exists" : "does not exist");
551         }
552
553         wpa_printf(MSG_INFO, "Parent URI: %s", uri);
554
555         if (!pps_node) {
556                 /* Add at root of PPS MO */
557                 pps_node = pps;
558         }
559
560         uri_len = os_strlen(uri);
561         os_strlcpy(uri, pos + uri_len, os_strlen(pos));
562         upos = uri;
563         while (*upos == '/')
564                 upos++;
565         wpa_printf(MSG_INFO, "Nodes to add: %s", upos);
566
567         for (;;) {
568                 end = os_strchr(upos, '/');
569                 if (!end)
570                         break;
571                 *end = '\0';
572                 wpa_printf(MSG_INFO, "Adding interim node %s", upos);
573                 pps_node = xml_node_create(ctx->xml, pps_node, NULL, upos);
574                 if (pps_node == NULL) {
575                         os_free(uri);
576                         return DM_RESP_COMMAND_FAILED;
577                 }
578                 upos = end + 1;
579         }
580
581         wpa_printf(MSG_INFO, "Adding node %s", upos);
582
583         node = get_node(ctx->xml, add, "Item/Meta/Type");
584         if (node) {
585                 char *type;
586                 type = xml_node_get_text(ctx->xml, node);
587                 if (type == NULL) {
588                         wpa_printf(MSG_ERROR, "Could not find type text");
589                         os_free(uri);
590                         return DM_RESP_BAD_REQUEST;
591                 }
592                 use_tnds = node &&
593                         os_strstr(type, "application/vnd.syncml.dmtnds+xml");
594         }
595
596         node = get_node(ctx->xml, add, "Item/Data");
597         if (node == NULL) {
598                 wpa_printf(MSG_INFO, "No Add/Item/Data found");
599                 os_free(uri);
600                 return DM_RESP_BAD_REQUEST;
601         }
602
603         data = xml_node_get_text(ctx->xml, node);
604         if (data == NULL) {
605                 wpa_printf(MSG_INFO, "Could not get Add/Item/Data text");
606                 os_free(uri);
607                 return DM_RESP_BAD_REQUEST;
608         }
609
610         wpa_printf(MSG_DEBUG, "Add/Item/Data: %s", data);
611
612         if (use_tnds) {
613                 tnds = xml_node_from_buf(ctx->xml, data);
614                 xml_node_get_text_free(ctx->xml, data);
615                 if (tnds == NULL) {
616                         wpa_printf(MSG_INFO,
617                                    "Could not parse Add/Item/Data text");
618                         os_free(uri);
619                         return DM_RESP_BAD_REQUEST;
620                 }
621
622                 unode = tnds_to_mo(ctx->xml, tnds);
623                 xml_node_free(ctx->xml, tnds);
624                 if (unode == NULL) {
625                         wpa_printf(MSG_INFO, "Could not parse TNDS text");
626                         os_free(uri);
627                         return DM_RESP_BAD_REQUEST;
628                 }
629
630                 debug_dump_node(ctx, "Parsed TNDS", unode);
631
632                 xml_node_add_child(ctx->xml, pps_node, unode);
633         } else {
634                 /* TODO: What to do here? */
635                 os_free(uri);
636                 return DM_RESP_BAD_REQUEST;
637         }
638
639         os_free(uri);
640
641         if (update_pps_file(ctx, pps_fname, pps) < 0)
642                 return DM_RESP_COMMAND_FAILED;
643
644         ctx->pps_updated = 1;
645
646         return DM_RESP_OK;
647 }
648
649
650 static int oma_dm_add(struct hs20_osu_client *ctx, xml_node_t *add,
651                       xml_node_t *pps, const char *pps_fname)
652 {
653         xml_node_t *node;
654         char *locuri;
655         char fname[300];
656         int ret;
657
658         node = get_node(ctx->xml, add, "Item/Target/LocURI");
659         if (node == NULL) {
660                 wpa_printf(MSG_INFO, "No Target LocURI node found");
661                 return DM_RESP_BAD_REQUEST;
662         }
663         locuri = xml_node_get_text(ctx->xml, node);
664         if (locuri == NULL) {
665                 wpa_printf(MSG_ERROR, "No LocURI node text found");
666                 return DM_RESP_BAD_REQUEST;
667         }
668         wpa_printf(MSG_INFO, "Target LocURI: %s", locuri);
669         if (os_strncasecmp(locuri, "./Wi-Fi/", 8) != 0) {
670                 wpa_printf(MSG_INFO, "Unsupported Add Target LocURI");
671                 xml_node_get_text_free(ctx->xml, locuri);
672                 return DM_RESP_PERMISSION_DENIED;
673         }
674
675         node = get_node(ctx->xml, add, "Item/Data");
676         if (node == NULL) {
677                 wpa_printf(MSG_INFO, "No Data node found");
678                 xml_node_get_text_free(ctx->xml, locuri);
679                 return DM_RESP_BAD_REQUEST;
680         }
681
682         if (pps_fname && os_file_exists(pps_fname)) {
683                 ret = oma_dm_run_add(ctx, locuri, add, pps, pps_fname);
684                 if (ret != DM_RESP_OK) {
685                         xml_node_get_text_free(ctx->xml, locuri);
686                         return ret;
687                 }
688                 ret = 0;
689                 os_strlcpy(fname, pps_fname, sizeof(fname));
690         } else
691                 ret = hs20_add_pps_mo(ctx, locuri, node, fname, sizeof(fname));
692         xml_node_get_text_free(ctx->xml, locuri);
693         if (ret < 0)
694                 return ret == -2 ? DM_RESP_ALREADY_EXISTS :
695                         DM_RESP_COMMAND_FAILED;
696
697         if (ctx->no_reconnect == 2) {
698                 os_snprintf(ctx->pps_fname, sizeof(ctx->pps_fname), "%s",
699                             fname);
700                 ctx->pps_cred_set = 1;
701                 return DM_RESP_OK;
702         }
703
704         wpa_printf(MSG_INFO, "Updating wpa_supplicant credentials");
705         cmd_set_pps(ctx, fname);
706
707         if (ctx->no_reconnect)
708                 return DM_RESP_OK;
709
710         wpa_printf(MSG_INFO, "Requesting reconnection with updated configuration");
711         if (wpa_command(ctx->ifname, "INTERWORKING_SELECT auto") < 0)
712                 wpa_printf(MSG_INFO, "Failed to request wpa_supplicant to reconnect");
713
714         return DM_RESP_OK;
715 }
716
717
718 static int oma_dm_replace(struct hs20_osu_client *ctx, xml_node_t *replace,
719                           xml_node_t *pps, const char *pps_fname)
720 {
721         char *locuri, *pos;
722         size_t fqdn_len;
723         xml_node_t *node, *tnds, *unode, *pps_node, *parent;
724         char *data;
725         int use_tnds = 0;
726
727         locuri = oma_dm_get_target_locuri(ctx, replace);
728         if (locuri == NULL)
729                 return DM_RESP_BAD_REQUEST;
730
731         wpa_printf(MSG_INFO, "Replace command target LocURI: %s", locuri);
732         if (os_strncasecmp(locuri, "./Wi-Fi/", 8) != 0) {
733                 wpa_printf(MSG_INFO, "Do not allow Replace outside ./Wi-Fi");
734                 os_free(locuri);
735                 return DM_RESP_PERMISSION_DENIED;
736         }
737         pos = locuri + 8;
738
739         if (ctx->fqdn == NULL) {
740                 os_free(locuri);
741                 return DM_RESP_COMMAND_FAILED;
742         }
743         fqdn_len = os_strlen(ctx->fqdn);
744         if (os_strncasecmp(pos, ctx->fqdn, fqdn_len) != 0 ||
745             pos[fqdn_len] != '/') {
746                 wpa_printf(MSG_INFO, "Do not allow Replace outside ./Wi-Fi/%s",
747                            ctx->fqdn);
748                 os_free(locuri);
749                 return DM_RESP_PERMISSION_DENIED;
750         }
751         pos += fqdn_len + 1;
752
753         if (os_strncasecmp(pos, "PerProviderSubscription/", 24) != 0) {
754                 wpa_printf(MSG_INFO,
755                            "Do not allow Replace outside ./Wi-Fi/%s/PerProviderSubscription",
756                            ctx->fqdn);
757                 os_free(locuri);
758                 return DM_RESP_PERMISSION_DENIED;
759         }
760         pos += 24;
761
762         wpa_printf(MSG_INFO, "Replace command for PPS node %s", pos);
763
764         pps_node = get_node(ctx->xml, pps, pos);
765         if (pps_node == NULL) {
766                 wpa_printf(MSG_INFO, "Specified PPS node not found");
767                 os_free(locuri);
768                 return DM_RESP_NOT_FOUND;
769         }
770
771         node = get_node(ctx->xml, replace, "Item/Meta/Type");
772         if (node) {
773                 char *type;
774                 type = xml_node_get_text(ctx->xml, node);
775                 if (type == NULL) {
776                         wpa_printf(MSG_INFO, "Could not find type text");
777                         os_free(locuri);
778                         return DM_RESP_BAD_REQUEST;
779                 }
780                 use_tnds = node &&
781                         os_strstr(type, "application/vnd.syncml.dmtnds+xml");
782         }
783
784         node = get_node(ctx->xml, replace, "Item/Data");
785         if (node == NULL) {
786                 wpa_printf(MSG_INFO, "No Replace/Item/Data found");
787                 os_free(locuri);
788                 return DM_RESP_BAD_REQUEST;
789         }
790
791         data = xml_node_get_text(ctx->xml, node);
792         if (data == NULL) {
793                 wpa_printf(MSG_INFO, "Could not get Replace/Item/Data text");
794                 os_free(locuri);
795                 return DM_RESP_BAD_REQUEST;
796         }
797
798         wpa_printf(MSG_DEBUG, "Replace/Item/Data: %s", data);
799
800         if (use_tnds) {
801                 tnds = xml_node_from_buf(ctx->xml, data);
802                 xml_node_get_text_free(ctx->xml, data);
803                 if (tnds == NULL) {
804                         wpa_printf(MSG_INFO,
805                                    "Could not parse Replace/Item/Data text");
806                         os_free(locuri);
807                         return DM_RESP_BAD_REQUEST;
808                 }
809
810                 unode = tnds_to_mo(ctx->xml, tnds);
811                 xml_node_free(ctx->xml, tnds);
812                 if (unode == NULL) {
813                         wpa_printf(MSG_INFO, "Could not parse TNDS text");
814                         os_free(locuri);
815                         return DM_RESP_BAD_REQUEST;
816                 }
817
818                 debug_dump_node(ctx, "Parsed TNDS", unode);
819
820                 parent = xml_node_get_parent(ctx->xml, pps_node);
821                 xml_node_detach(ctx->xml, pps_node);
822                 xml_node_add_child(ctx->xml, parent, unode);
823         } else {
824                 xml_node_set_text(ctx->xml, pps_node, data);
825                 xml_node_get_text_free(ctx->xml, data);
826         }
827
828         os_free(locuri);
829
830         if (update_pps_file(ctx, pps_fname, pps) < 0)
831                 return DM_RESP_COMMAND_FAILED;
832
833         ctx->pps_updated = 1;
834
835         return DM_RESP_OK;
836 }
837
838
839 static int oma_dm_get(struct hs20_osu_client *ctx, xml_node_t *get,
840                       xml_node_t *pps, const char *pps_fname, char **value)
841 {
842         char *locuri, *pos;
843         size_t fqdn_len;
844         xml_node_t *pps_node;
845         const char *name;
846
847         *value = NULL;
848
849         locuri = oma_dm_get_target_locuri(ctx, get);
850         if (locuri == NULL)
851                 return DM_RESP_BAD_REQUEST;
852
853         wpa_printf(MSG_INFO, "Get command target LocURI: %s", locuri);
854         if (os_strncasecmp(locuri, "./Wi-Fi/", 8) != 0) {
855                 wpa_printf(MSG_INFO, "Do not allow Get outside ./Wi-Fi");
856                 os_free(locuri);
857                 return DM_RESP_PERMISSION_DENIED;
858         }
859         pos = locuri + 8;
860
861         if (ctx->fqdn == NULL)
862                 return DM_RESP_COMMAND_FAILED;
863         fqdn_len = os_strlen(ctx->fqdn);
864         if (os_strncasecmp(pos, ctx->fqdn, fqdn_len) != 0 ||
865             pos[fqdn_len] != '/') {
866                 wpa_printf(MSG_INFO, "Do not allow Get outside ./Wi-Fi/%s",
867                            ctx->fqdn);
868                 os_free(locuri);
869                 return DM_RESP_PERMISSION_DENIED;
870         }
871         pos += fqdn_len + 1;
872
873         if (os_strncasecmp(pos, "PerProviderSubscription/", 24) != 0) {
874                 wpa_printf(MSG_INFO,
875                            "Do not allow Get outside ./Wi-Fi/%s/PerProviderSubscription",
876                            ctx->fqdn);
877                 os_free(locuri);
878                 return DM_RESP_PERMISSION_DENIED;
879         }
880         pos += 24;
881
882         wpa_printf(MSG_INFO, "Get command for PPS node %s", pos);
883
884         pps_node = get_node(ctx->xml, pps, pos);
885         if (pps_node == NULL) {
886                 wpa_printf(MSG_INFO, "Specified PPS node not found");
887                 os_free(locuri);
888                 return DM_RESP_NOT_FOUND;
889         }
890
891         name = xml_node_get_localname(ctx->xml, pps_node);
892         wpa_printf(MSG_INFO, "Get command returned node with name '%s'", name);
893         if (os_strcasecmp(name, "Password") == 0) {
894                 wpa_printf(MSG_INFO, "Do not allow Get for Password node");
895                 os_free(locuri);
896                 return DM_RESP_PERMISSION_DENIED;
897         }
898
899         /*
900          * TODO: No support for DMTNDS, so if interior node, reply with a
901          * list of children node names in Results element. The child list type is
902          * defined in [DMTND].
903          */
904
905         *value = xml_node_get_text(ctx->xml, pps_node);
906         if (*value == NULL)
907                 return DM_RESP_COMMAND_FAILED;
908
909         return DM_RESP_OK;
910 }
911
912
913 static int oma_dm_get_cmdid(struct hs20_osu_client *ctx, xml_node_t *node)
914 {
915         xml_node_t *cnode;
916         char *str;
917         int ret;
918
919         cnode = get_node(ctx->xml, node, "CmdID");
920         if (cnode == NULL)
921                 return 0;
922
923         str = xml_node_get_text(ctx->xml, cnode);
924         if (str == NULL)
925                 return 0;
926         ret = atoi(str);
927         xml_node_get_text_free(ctx->xml, str);
928         return ret;
929 }
930
931
932 static xml_node_t * oma_dm_send_recv(struct hs20_osu_client *ctx,
933                                      const char *url, xml_node_t *syncml,
934                                      const char *ext_hdr,
935                                      const char *username, const char *password,
936                                      const char *client_cert,
937                                      const char *client_key)
938 {
939         xml_node_t *resp;
940         char *str, *res;
941         char *resp_uri = NULL;
942
943         str = xml_node_to_str(ctx->xml, syncml);
944         xml_node_free(ctx->xml, syncml);
945         if (str == NULL)
946                 return NULL;
947
948         wpa_printf(MSG_INFO, "Send OMA DM Package");
949         write_summary(ctx, "Send OMA DM Package");
950         os_free(ctx->server_url);
951         ctx->server_url = os_strdup(url);
952         res = http_post(ctx->http, url, str, "application/vnd.syncml.dm+xml",
953                         ext_hdr, ctx->ca_fname, username, password,
954                         client_cert, client_key, NULL);
955         os_free(str);
956         os_free(resp_uri);
957         resp_uri = NULL;
958
959         if (res == NULL) {
960                 const char *err = http_get_err(ctx->http);
961                 if (err) {
962                         wpa_printf(MSG_INFO, "HTTP error: %s", err);
963                         write_result(ctx, "HTTP error: %s", err);
964                 } else {
965                         write_summary(ctx, "Failed to send OMA DM Package");
966                 }
967                 return NULL;
968         }
969         wpa_printf(MSG_DEBUG, "Server response: %s", res);
970
971         wpa_printf(MSG_INFO, "Process OMA DM Package");
972         write_summary(ctx, "Process received OMA DM Package");
973         resp = xml_node_from_buf(ctx->xml, res);
974         os_free(res);
975         if (resp == NULL) {
976                 wpa_printf(MSG_INFO, "Failed to parse OMA DM response");
977                 return NULL;
978         }
979
980         debug_dump_node(ctx, "OMA DM Package", resp);
981
982         return resp;
983 }
984
985
986 static xml_node_t * oma_dm_process(struct hs20_osu_client *ctx, const char *url,
987                                    xml_node_t *resp, int msgid,
988                                    char **ret_resp_uri,
989                                    xml_node_t *pps, const char *pps_fname)
990 {
991         xml_node_t *syncml, *syncbody, *hdr, *body, *child;
992         const char *name;
993         char *resp_uri = NULL;
994         int server_msgid = 0;
995         int cmdid = 0;
996         int server_cmdid;
997         int resp_needed = 0;
998         char *tmp;
999         int final = 0;
1000         char *locuri;
1001
1002         *ret_resp_uri = NULL;
1003
1004         name = xml_node_get_localname(ctx->xml, resp);
1005         if (name == NULL || os_strcasecmp(name, "SyncML") != 0) {
1006                 wpa_printf(MSG_INFO, "SyncML node not found");
1007                 return NULL;
1008         }
1009
1010         hdr = get_node(ctx->xml, resp, "SyncHdr");
1011         body = get_node(ctx->xml, resp, "SyncBody");
1012         if (hdr == NULL || body == NULL) {
1013                 wpa_printf(MSG_INFO, "Could not find SyncHdr or SyncBody");
1014                 return NULL;
1015         }
1016
1017         xml_node_for_each_child(ctx->xml, child, hdr) {
1018                 xml_node_for_each_check(ctx->xml, child);
1019                 name = xml_node_get_localname(ctx->xml, child);
1020                 wpa_printf(MSG_INFO, "SyncHdr %s", name);
1021                 if (os_strcasecmp(name, "RespURI") == 0) {
1022                         tmp = xml_node_get_text(ctx->xml, child);
1023                         if (tmp)
1024                                 resp_uri = os_strdup(tmp);
1025                         xml_node_get_text_free(ctx->xml, tmp);
1026                 } else if (os_strcasecmp(name, "MsgID") == 0) {
1027                         tmp = xml_node_get_text(ctx->xml, child);
1028                         if (tmp)
1029                                 server_msgid = atoi(tmp);
1030                         xml_node_get_text_free(ctx->xml, tmp);
1031                 }
1032         }
1033
1034         wpa_printf(MSG_INFO, "Server MsgID: %d", server_msgid);
1035         if (resp_uri)
1036                 wpa_printf(MSG_INFO, "RespURI: %s", resp_uri);
1037
1038         syncml = oma_dm_build_hdr(ctx, resp_uri ? resp_uri : url, msgid);
1039         if (syncml == NULL) {
1040                 os_free(resp_uri);
1041                 return NULL;
1042         }
1043
1044         syncbody = xml_node_create(ctx->xml, syncml, NULL, "SyncBody");
1045         cmdid++;
1046         add_status(ctx, syncbody, server_msgid, 0, cmdid, "SyncHdr",
1047                    DM_RESP_AUTH_ACCEPTED, NULL);
1048
1049         xml_node_for_each_child(ctx->xml, child, body) {
1050                 xml_node_for_each_check(ctx->xml, child);
1051                 server_cmdid = oma_dm_get_cmdid(ctx, child);
1052                 name = xml_node_get_localname(ctx->xml, child);
1053                 wpa_printf(MSG_INFO, "SyncBody CmdID=%d - %s",
1054                            server_cmdid, name);
1055                 if (os_strcasecmp(name, "Exec") == 0) {
1056                         int res = oma_dm_exec(ctx, child);
1057                         cmdid++;
1058                         locuri = oma_dm_get_target_locuri(ctx, child);
1059                         if (locuri == NULL)
1060                                 res = DM_RESP_BAD_REQUEST;
1061                         add_status(ctx, syncbody, server_msgid, server_cmdid,
1062                                    cmdid, name, res, locuri);
1063                         os_free(locuri);
1064                         resp_needed = 1;
1065                 } else if (os_strcasecmp(name, "Add") == 0) {
1066                         int res = oma_dm_add(ctx, child, pps, pps_fname);
1067                         cmdid++;
1068                         locuri = oma_dm_get_target_locuri(ctx, child);
1069                         if (locuri == NULL)
1070                                 res = DM_RESP_BAD_REQUEST;
1071                         add_status(ctx, syncbody, server_msgid, server_cmdid,
1072                                    cmdid, name, res, locuri);
1073                         os_free(locuri);
1074                         resp_needed = 1;
1075                 } else if (os_strcasecmp(name, "Replace") == 0) {
1076                         int res;
1077                         res = oma_dm_replace(ctx, child, pps, pps_fname);
1078                         cmdid++;
1079                         locuri = oma_dm_get_target_locuri(ctx, child);
1080                         if (locuri == NULL)
1081                                 res = DM_RESP_BAD_REQUEST;
1082                         add_status(ctx, syncbody, server_msgid, server_cmdid,
1083                                    cmdid, name, res, locuri);
1084                         os_free(locuri);
1085                         resp_needed = 1;
1086                 } else if (os_strcasecmp(name, "Status") == 0) {
1087                         /* TODO: Verify success */
1088                 } else if (os_strcasecmp(name, "Get") == 0) {
1089                         int res;
1090                         char *value;
1091                         res = oma_dm_get(ctx, child, pps, pps_fname, &value);
1092                         cmdid++;
1093                         locuri = oma_dm_get_target_locuri(ctx, child);
1094                         if (locuri == NULL)
1095                                 res = DM_RESP_BAD_REQUEST;
1096                         add_status(ctx, syncbody, server_msgid, server_cmdid,
1097                                    cmdid, name, res, locuri);
1098                         if (res == DM_RESP_OK && value) {
1099                                 cmdid++;
1100                                 add_results(ctx, syncbody, server_msgid,
1101                                             server_cmdid, cmdid, locuri, value);
1102                         }
1103                         os_free(locuri);
1104                         xml_node_get_text_free(ctx->xml, value);
1105                         resp_needed = 1;
1106 #if 0 /* TODO: MUST support */
1107                 } else if (os_strcasecmp(name, "Delete") == 0) {
1108 #endif
1109 #if 0 /* TODO: MUST support */
1110                 } else if (os_strcasecmp(name, "Sequence") == 0) {
1111 #endif
1112                 } else if (os_strcasecmp(name, "Final") == 0) {
1113                         final = 1;
1114                         break;
1115                 } else {
1116                         locuri = oma_dm_get_target_locuri(ctx, child);
1117                         add_status(ctx, syncbody, server_msgid, server_cmdid,
1118                                    cmdid, name, DM_RESP_COMMAND_NOT_IMPLEMENTED,
1119                                    locuri);
1120                         os_free(locuri);
1121                         resp_needed = 1;
1122                 }
1123         }
1124
1125         if (!final) {
1126                 wpa_printf(MSG_INFO, "Final node not found");
1127                 xml_node_free(ctx->xml, syncml);
1128                 os_free(resp_uri);
1129                 return NULL;
1130         }
1131
1132         if (!resp_needed) {
1133                 wpa_printf(MSG_INFO, "Exchange completed - no response needed");
1134                 xml_node_free(ctx->xml, syncml);
1135                 os_free(resp_uri);
1136                 return NULL;
1137         }
1138
1139         xml_node_create(ctx->xml, syncbody, NULL, "Final");
1140
1141         debug_dump_node(ctx, "OMA-DM Package 3", syncml);
1142
1143         *ret_resp_uri = resp_uri;
1144         return syncml;
1145 }
1146
1147
1148 int cmd_oma_dm_prov(struct hs20_osu_client *ctx, const char *url)
1149 {
1150         xml_node_t *syncml, *resp;
1151         char *resp_uri = NULL;
1152         int msgid = 0;
1153
1154         if (url == NULL) {
1155                 wpa_printf(MSG_INFO, "Invalid prov command (missing URL)");
1156                 return -1;
1157         }
1158
1159         wpa_printf(MSG_INFO, "OMA-DM credential provisioning requested");
1160         write_summary(ctx, "OMA-DM credential provisioning");
1161
1162         msgid++;
1163         syncml = build_oma_dm_1_sub_reg(ctx, url, msgid);
1164         if (syncml == NULL)
1165                 return -1;
1166
1167         while (syncml) {
1168                 resp = oma_dm_send_recv(ctx, resp_uri ? resp_uri : url,
1169                                         syncml, NULL, NULL, NULL, NULL, NULL);
1170                 if (resp == NULL)
1171                         return -1;
1172
1173                 msgid++;
1174                 syncml = oma_dm_process(ctx, url, resp, msgid, &resp_uri,
1175                                         NULL, NULL);
1176                 xml_node_free(ctx->xml, resp);
1177         }
1178
1179         os_free(resp_uri);
1180
1181         return ctx->pps_cred_set ? 0 : -1;
1182 }
1183
1184
1185 int cmd_oma_dm_sim_prov(struct hs20_osu_client *ctx, const char *url)
1186 {
1187         xml_node_t *syncml, *resp;
1188         char *resp_uri = NULL;
1189         int msgid = 0;
1190
1191         if (url == NULL) {
1192                 wpa_printf(MSG_INFO, "Invalid prov command (missing URL)");
1193                 return -1;
1194         }
1195
1196         wpa_printf(MSG_INFO, "OMA-DM SIM provisioning requested");
1197         ctx->no_reconnect = 2;
1198
1199         wpa_printf(MSG_INFO, "Wait for IP address before starting SIM provisioning");
1200         write_summary(ctx, "Wait for IP address before starting SIM provisioning");
1201
1202         if (wait_ip_addr(ctx->ifname, 15) < 0) {
1203                 wpa_printf(MSG_INFO, "Could not get IP address for WLAN - try connection anyway");
1204         }
1205         write_summary(ctx, "OMA-DM SIM provisioning");
1206
1207         msgid++;
1208         syncml = build_oma_dm_1_sub_prov(ctx, url, msgid);
1209         if (syncml == NULL)
1210                 return -1;
1211
1212         while (syncml) {
1213                 resp = oma_dm_send_recv(ctx, resp_uri ? resp_uri : url,
1214                                         syncml, NULL, NULL, NULL, NULL, NULL);
1215                 if (resp == NULL)
1216                         return -1;
1217
1218                 msgid++;
1219                 syncml = oma_dm_process(ctx, url, resp, msgid, &resp_uri,
1220                                         NULL, NULL);
1221                 xml_node_free(ctx->xml, resp);
1222         }
1223
1224         os_free(resp_uri);
1225
1226         if (ctx->pps_cred_set) {
1227                 wpa_printf(MSG_INFO, "Updating wpa_supplicant credentials");
1228                 cmd_set_pps(ctx, ctx->pps_fname);
1229
1230                 wpa_printf(MSG_INFO, "Requesting reconnection with updated configuration");
1231                 write_summary(ctx, "Requesting reconnection with updated configuration");
1232                 if (wpa_command(ctx->ifname, "INTERWORKING_SELECT auto") < 0) {
1233                         wpa_printf(MSG_INFO, "Failed to request wpa_supplicant to reconnect");
1234                         write_summary(ctx, "Failed to request wpa_supplicant to reconnect");
1235                         return -1;
1236                 }
1237         }
1238
1239         return ctx->pps_cred_set ? 0 : -1;
1240 }
1241
1242
1243 void oma_dm_pol_upd(struct hs20_osu_client *ctx, const char *address,
1244                     const char *pps_fname,
1245                     const char *client_cert, const char *client_key,
1246                     const char *cred_username, const char *cred_password,
1247                     xml_node_t *pps)
1248 {
1249         xml_node_t *syncml, *resp;
1250         char *resp_uri = NULL;
1251         int msgid = 0;
1252
1253         wpa_printf(MSG_INFO, "OMA-DM policy update");
1254         write_summary(ctx, "OMA-DM policy update");
1255
1256         msgid++;
1257         syncml = build_oma_dm_1_pol_upd(ctx, address, msgid);
1258         if (syncml == NULL)
1259                 return;
1260
1261         while (syncml) {
1262                 resp = oma_dm_send_recv(ctx, resp_uri ? resp_uri : address,
1263                                         syncml, NULL, cred_username,
1264                                         cred_password, client_cert, client_key);
1265                 if (resp == NULL)
1266                         return;
1267
1268                 msgid++;
1269                 syncml = oma_dm_process(ctx, address, resp, msgid, &resp_uri,
1270                                         pps, pps_fname);
1271                 xml_node_free(ctx->xml, resp);
1272         }
1273
1274         os_free(resp_uri);
1275
1276         if (ctx->pps_updated) {
1277                 wpa_printf(MSG_INFO, "Update wpa_supplicant credential based on updated PPS MO");
1278                 write_summary(ctx, "Update wpa_supplicant credential based on updated PPS MO and request connection");
1279                 cmd_set_pps(ctx, pps_fname);
1280                 if (wpa_command(ctx->ifname, "INTERWORKING_SELECT auto") < 0) {
1281                         wpa_printf(MSG_INFO,
1282                                    "Failed to request wpa_supplicant to reconnect");
1283                         write_summary(ctx,
1284                                       "Failed to request wpa_supplicant to reconnect");
1285                 }
1286         }
1287 }
1288
1289
1290 void oma_dm_sub_rem(struct hs20_osu_client *ctx, const char *address,
1291                     const char *pps_fname,
1292                     const char *client_cert, const char *client_key,
1293                     const char *cred_username, const char *cred_password,
1294                     xml_node_t *pps)
1295 {
1296         xml_node_t *syncml, *resp;
1297         char *resp_uri = NULL;
1298         int msgid = 0;
1299
1300         wpa_printf(MSG_INFO, "OMA-DM subscription remediation");
1301         write_summary(ctx, "OMA-DM subscription remediation");
1302
1303         msgid++;
1304         syncml = build_oma_dm_1_sub_rem(ctx, address, msgid);
1305         if (syncml == NULL)
1306                 return;
1307
1308         while (syncml) {
1309                 resp = oma_dm_send_recv(ctx, resp_uri ? resp_uri : address,
1310                                         syncml, NULL, cred_username,
1311                                         cred_password, client_cert, client_key);
1312                 if (resp == NULL)
1313                         return;
1314
1315                 msgid++;
1316                 syncml = oma_dm_process(ctx, address, resp, msgid, &resp_uri,
1317                                         pps, pps_fname);
1318                 xml_node_free(ctx->xml, resp);
1319         }
1320
1321         os_free(resp_uri);
1322
1323         wpa_printf(MSG_INFO, "Update wpa_supplicant credential based on updated PPS MO and request reconnection");
1324         write_summary(ctx, "Update wpa_supplicant credential based on updated PPS MO and request reconnection");
1325         cmd_set_pps(ctx, pps_fname);
1326         if (wpa_command(ctx->ifname, "INTERWORKING_SELECT auto") < 0) {
1327                 wpa_printf(MSG_INFO, "Failed to request wpa_supplicant to reconnect");
1328                 write_summary(ctx, "Failed to request wpa_supplicant to reconnect");
1329         }
1330 }
1331
1332
1333 void cmd_oma_dm_add(struct hs20_osu_client *ctx, const char *pps_fname,
1334                     const char *add_fname)
1335 {
1336         xml_node_t *pps, *add;
1337         int res;
1338
1339         ctx->fqdn = os_strdup("wi-fi.org");
1340
1341         pps = node_from_file(ctx->xml, pps_fname);
1342         if (pps == NULL) {
1343                 wpa_printf(MSG_INFO, "PPS file %s could not be parsed",
1344                            pps_fname);
1345                 return;
1346         }
1347
1348         add = node_from_file(ctx->xml, add_fname);
1349         if (add == NULL) {
1350                 wpa_printf(MSG_INFO, "Add file %s could not be parsed",
1351                            add_fname);
1352                 xml_node_free(ctx->xml, pps);
1353                 return;
1354         }
1355
1356         res = oma_dm_add(ctx, add, pps, pps_fname);
1357         wpa_printf(MSG_INFO, "oma_dm_add --> %d", res);
1358
1359         xml_node_free(ctx->xml, pps);
1360         xml_node_free(ctx->xml, add);
1361 }
1362
1363
1364 void cmd_oma_dm_replace(struct hs20_osu_client *ctx, const char *pps_fname,
1365                         const char *replace_fname)
1366 {
1367         xml_node_t *pps, *replace;
1368         int res;
1369
1370         ctx->fqdn = os_strdup("wi-fi.org");
1371
1372         pps = node_from_file(ctx->xml, pps_fname);
1373         if (pps == NULL) {
1374                 wpa_printf(MSG_INFO, "PPS file %s could not be parsed",
1375                            pps_fname);
1376                 return;
1377         }
1378
1379         replace = node_from_file(ctx->xml, replace_fname);
1380         if (replace == NULL) {
1381                 wpa_printf(MSG_INFO, "Replace file %s could not be parsed",
1382                            replace_fname);
1383                 xml_node_free(ctx->xml, pps);
1384                 return;
1385         }
1386
1387         res = oma_dm_replace(ctx, replace, pps, pps_fname);
1388         wpa_printf(MSG_INFO, "oma_dm_replace --> %d", res);
1389
1390         xml_node_free(ctx->xml, pps);
1391         xml_node_free(ctx->xml, replace);
1392 }