Updated through tag hostap_2_5 from git://w1.fi/hostap.git
[mech_eap.git] / libeap / hs20 / client / spp_client.c
1 /*
2  * Hotspot 2.0 SPP client
3  * Copyright (c) 2012-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 #include <sys/stat.h>
11
12 #include "common.h"
13 #include "browser.h"
14 #include "wpa_ctrl.h"
15 #include "wpa_helpers.h"
16 #include "xml-utils.h"
17 #include "http-utils.h"
18 #include "utils/base64.h"
19 #include "crypto/crypto.h"
20 #include "crypto/sha256.h"
21 #include "osu_client.h"
22
23
24 extern const char *spp_xsd_fname;
25
26 static int hs20_spp_update_response(struct hs20_osu_client *ctx,
27                                     const char *session_id,
28                                     const char *spp_status,
29                                     const char *error_code);
30 static void hs20_policy_update_complete(
31         struct hs20_osu_client *ctx, const char *pps_fname);
32
33
34 static char * get_spp_attr_value(struct xml_node_ctx *ctx, xml_node_t *node,
35                                  char *attr_name)
36 {
37         return xml_node_get_attr_value_ns(ctx, node, SPP_NS_URI, attr_name);
38 }
39
40
41 static int hs20_spp_validate(struct hs20_osu_client *ctx, xml_node_t *node,
42                              const char *expected_name)
43 {
44         struct xml_node_ctx *xctx = ctx->xml;
45         const char *name;
46         char *err;
47         int ret;
48
49         if (!xml_node_is_element(xctx, node))
50                 return -1;
51
52         name = xml_node_get_localname(xctx, node);
53         if (name == NULL)
54                 return -1;
55
56         if (strcmp(expected_name, name) != 0) {
57                 wpa_printf(MSG_INFO, "Unexpected SOAP method name '%s' (expected '%s')",
58                            name, expected_name);
59                 write_summary(ctx, "Unexpected SOAP method name '%s' (expected '%s')",
60                               name, expected_name);
61                 return -1;
62         }
63
64         ret = xml_validate(xctx, node, spp_xsd_fname, &err);
65         if (ret < 0) {
66                 wpa_printf(MSG_INFO, "XML schema validation error(s)\n%s", err);
67                 write_summary(ctx, "SPP XML schema validation failed");
68                 os_free(err);
69         }
70         return ret;
71 }
72
73
74 static void add_mo_container(struct xml_node_ctx *ctx, xml_namespace_t *ns,
75                              xml_node_t *parent, const char *urn,
76                              const char *fname)
77 {
78         xml_node_t *node;
79         xml_node_t *fnode, *tnds;
80         char *str;
81
82         errno = 0;
83         fnode = node_from_file(ctx, fname);
84         if (!fnode) {
85                 wpa_printf(MSG_ERROR,
86                            "Failed to create XML node from file: %s, possible error: %s",
87                            fname, strerror(errno));
88                 return;
89         }
90         tnds = mo_to_tnds(ctx, fnode, 0, urn, "syncml:dmddf1.2");
91         xml_node_free(ctx, fnode);
92         if (!tnds)
93                 return;
94
95         str = xml_node_to_str(ctx, tnds);
96         xml_node_free(ctx, tnds);
97         if (str == NULL)
98                 return;
99
100         node = xml_node_create_text(ctx, parent, ns, "moContainer", str);
101         if (node)
102                 xml_node_add_attr(ctx, node, ns, "moURN", urn);
103         os_free(str);
104 }
105
106
107 static xml_node_t * build_spp_post_dev_data(struct hs20_osu_client *ctx,
108                                             xml_namespace_t **ret_ns,
109                                             const char *session_id,
110                                             const char *reason)
111 {
112         xml_namespace_t *ns;
113         xml_node_t *spp_node;
114
115         write_summary(ctx, "Building sppPostDevData requestReason='%s'",
116                       reason);
117         spp_node = xml_node_create_root(ctx->xml, SPP_NS_URI, "spp", &ns,
118                                         "sppPostDevData");
119         if (spp_node == NULL)
120                 return NULL;
121         if (ret_ns)
122                 *ret_ns = ns;
123
124         xml_node_add_attr(ctx->xml, spp_node, ns, "sppVersion", "1.0");
125         xml_node_add_attr(ctx->xml, spp_node, NULL, "requestReason", reason);
126         if (session_id)
127                 xml_node_add_attr(ctx->xml, spp_node, ns, "sessionID",
128                                   session_id);
129         xml_node_add_attr(ctx->xml, spp_node, NULL, "redirectURI",
130                           "http://localhost:12345/");
131
132         xml_node_create_text(ctx->xml, spp_node, ns, "supportedSPPVersions",
133                              "1.0");
134         xml_node_create_text(ctx->xml, spp_node, ns, "supportedMOList",
135                              URN_HS20_PPS " " URN_OMA_DM_DEVINFO " "
136                              URN_OMA_DM_DEVDETAIL " " URN_HS20_DEVDETAIL_EXT);
137
138         add_mo_container(ctx->xml, ns, spp_node, URN_OMA_DM_DEVINFO,
139                          "devinfo.xml");
140         add_mo_container(ctx->xml, ns, spp_node, URN_OMA_DM_DEVDETAIL,
141                          "devdetail.xml");
142
143         return spp_node;
144 }
145
146
147 static int process_update_node(struct hs20_osu_client *ctx, xml_node_t *pps,
148                                xml_node_t *update)
149 {
150         xml_node_t *node, *parent, *tnds, *unode;
151         char *str;
152         const char *name;
153         char *uri, *pos;
154         char *cdata, *cdata_end;
155         size_t fqdn_len;
156
157         wpa_printf(MSG_INFO, "Processing updateNode");
158         debug_dump_node(ctx, "updateNode", update);
159
160         uri = get_spp_attr_value(ctx->xml, update, "managementTreeURI");
161         if (uri == NULL) {
162                 wpa_printf(MSG_INFO, "No managementTreeURI present");
163                 return -1;
164         }
165         wpa_printf(MSG_INFO, "managementTreeUri: '%s'", uri);
166
167         name = os_strrchr(uri, '/');
168         if (name == NULL) {
169                 wpa_printf(MSG_INFO, "Unexpected URI");
170                 xml_node_get_attr_value_free(ctx->xml, uri);
171                 return -1;
172         }
173         name++;
174         wpa_printf(MSG_INFO, "Update interior node: '%s'", name);
175
176         str = xml_node_get_text(ctx->xml, update);
177         if (str == NULL) {
178                 wpa_printf(MSG_INFO, "Could not extract MO text");
179                 xml_node_get_attr_value_free(ctx->xml, uri);
180                 return -1;
181         }
182         wpa_printf(MSG_DEBUG, "[hs20] nodeContainer text: '%s'", str);
183         cdata = strstr(str, "<![CDATA[");
184         cdata_end = strstr(str, "]]>");
185         if (cdata && cdata_end && cdata_end > cdata &&
186             cdata < strstr(str, "MgmtTree") &&
187             cdata_end > strstr(str, "/MgmtTree")) {
188                 char *tmp;
189                 wpa_printf(MSG_DEBUG, "[hs20] Removing extra CDATA container");
190                 tmp = strdup(cdata + 9);
191                 if (tmp) {
192                         cdata_end = strstr(tmp, "]]>");
193                         if (cdata_end)
194                                 *cdata_end = '\0';
195                         wpa_printf(MSG_DEBUG, "[hs20] nodeContainer text with CDATA container removed: '%s'",
196                                    tmp);
197                         tnds = xml_node_from_buf(ctx->xml, tmp);
198                         free(tmp);
199                 } else
200                         tnds = NULL;
201         } else
202                 tnds = xml_node_from_buf(ctx->xml, str);
203         xml_node_get_text_free(ctx->xml, str);
204         if (tnds == NULL) {
205                 wpa_printf(MSG_INFO, "[hs20] Could not parse nodeContainer text");
206                 xml_node_get_attr_value_free(ctx->xml, uri);
207                 return -1;
208         }
209
210         unode = tnds_to_mo(ctx->xml, tnds);
211         xml_node_free(ctx->xml, tnds);
212         if (unode == NULL) {
213                 wpa_printf(MSG_INFO, "[hs20] Could not parse nodeContainer TNDS text");
214                 xml_node_get_attr_value_free(ctx->xml, uri);
215                 return -1;
216         }
217
218         debug_dump_node(ctx, "Parsed TNDS", unode);
219
220         if (get_node_uri(ctx->xml, unode, name) == NULL) {
221                 wpa_printf(MSG_INFO, "[hs20] %s node not found", name);
222                 xml_node_free(ctx->xml, unode);
223                 xml_node_get_attr_value_free(ctx->xml, uri);
224                 return -1;
225         }
226
227         if (os_strncasecmp(uri, "./Wi-Fi/", 8) != 0) {
228                 wpa_printf(MSG_INFO, "Do not allow update outside ./Wi-Fi");
229                 xml_node_free(ctx->xml, unode);
230                 xml_node_get_attr_value_free(ctx->xml, uri);
231                 return -1;
232         }
233         pos = uri + 8;
234
235         if (ctx->fqdn == NULL) {
236                 wpa_printf(MSG_INFO, "FQDN not known");
237                 xml_node_free(ctx->xml, unode);
238                 xml_node_get_attr_value_free(ctx->xml, uri);
239                 return -1;
240         }
241         fqdn_len = os_strlen(ctx->fqdn);
242         if (os_strncasecmp(pos, ctx->fqdn, fqdn_len) != 0 ||
243             pos[fqdn_len] != '/') {
244                 wpa_printf(MSG_INFO, "Do not allow update outside ./Wi-Fi/%s",
245                            ctx->fqdn);
246                 xml_node_free(ctx->xml, unode);
247                 xml_node_get_attr_value_free(ctx->xml, uri);
248                 return -1;
249         }
250         pos += fqdn_len + 1;
251
252         if (os_strncasecmp(pos, "PerProviderSubscription/", 24) != 0) {
253                 wpa_printf(MSG_INFO, "Do not allow update outside ./Wi-Fi/%s/PerProviderSubscription",
254                            ctx->fqdn);
255                 xml_node_free(ctx->xml, unode);
256                 xml_node_get_attr_value_free(ctx->xml, uri);
257                 return -1;
258         }
259         pos += 24;
260
261         wpa_printf(MSG_INFO, "Update command for PPS node %s", pos);
262
263         node = get_node(ctx->xml, pps, pos);
264         if (node) {
265                 parent = xml_node_get_parent(ctx->xml, node);
266                 xml_node_detach(ctx->xml, node);
267                 wpa_printf(MSG_INFO, "Replace '%s' node", name);
268         } else {
269                 char *pos2;
270                 pos2 = os_strrchr(pos, '/');
271                 if (pos2 == NULL) {
272                         parent = pps;
273                 } else {
274                         *pos2 = '\0';
275                         parent = get_node(ctx->xml, pps, pos);
276                 }
277                 if (parent == NULL) {
278                         wpa_printf(MSG_INFO, "Could not find parent %s", pos);
279                         xml_node_free(ctx->xml, unode);
280                         xml_node_get_attr_value_free(ctx->xml, uri);
281                         return -1;
282                 }
283                 wpa_printf(MSG_INFO, "Add '%s' node", name);
284         }
285         xml_node_add_child(ctx->xml, parent, unode);
286
287         xml_node_get_attr_value_free(ctx->xml, uri);
288
289         return 0;
290 }
291
292
293 static int update_pps(struct hs20_osu_client *ctx, xml_node_t *update,
294                       const char *pps_fname, xml_node_t *pps)
295 {
296         wpa_printf(MSG_INFO, "Updating PPS based on updateNode element(s)");
297         xml_node_for_each_sibling(ctx->xml, update) {
298                 xml_node_for_each_check(ctx->xml, update);
299                 if (process_update_node(ctx, pps, update) < 0)
300                         return -1;
301         }
302
303         return update_pps_file(ctx, pps_fname, pps);
304 }
305
306
307 static void hs20_sub_rem_complete(struct hs20_osu_client *ctx,
308                                   const char *pps_fname)
309 {
310         /*
311          * Update wpa_supplicant credentials and reconnect using updated
312          * information.
313          */
314         wpa_printf(MSG_INFO, "Updating wpa_supplicant credentials");
315         cmd_set_pps(ctx, pps_fname);
316
317         if (ctx->no_reconnect)
318                 return;
319
320         wpa_printf(MSG_INFO, "Requesting reconnection with updated configuration");
321         if (wpa_command(ctx->ifname, "INTERWORKING_SELECT auto") < 0)
322                 wpa_printf(MSG_ERROR, "Failed to request wpa_supplicant to reconnect");
323 }
324
325
326 static xml_node_t * hs20_spp_upload_mo(struct hs20_osu_client *ctx,
327                                        xml_node_t *cmd,
328                                        const char *session_id,
329                                        const char *pps_fname)
330 {
331         xml_namespace_t *ns;
332         xml_node_t *node, *ret_node;
333         char *urn;
334
335         urn = get_spp_attr_value(ctx->xml, cmd, "moURN");
336         if (!urn) {
337                 wpa_printf(MSG_INFO, "No URN included");
338                 return NULL;
339         }
340         wpa_printf(MSG_INFO, "Upload MO request - URN=%s", urn);
341         if (strcasecmp(urn, URN_HS20_PPS) != 0) {
342                 wpa_printf(MSG_INFO, "Unsupported moURN");
343                 xml_node_get_attr_value_free(ctx->xml, urn);
344                 return NULL;
345         }
346         xml_node_get_attr_value_free(ctx->xml, urn);
347
348         if (!pps_fname) {
349                 wpa_printf(MSG_INFO, "PPS file name no known");
350                 return NULL;
351         }
352
353         node = build_spp_post_dev_data(ctx, &ns, session_id,
354                                        "MO upload");
355         if (node == NULL)
356                 return NULL;
357         add_mo_container(ctx->xml, ns, node, URN_HS20_PPS, pps_fname);
358
359         ret_node = soap_send_receive(ctx->http, node);
360         if (ret_node == NULL)
361                 return NULL;
362
363         debug_dump_node(ctx, "Received response to MO upload", ret_node);
364
365         if (hs20_spp_validate(ctx, ret_node, "sppPostDevDataResponse") < 0) {
366                 wpa_printf(MSG_INFO, "SPP validation failed");
367                 xml_node_free(ctx->xml, ret_node);
368                 return NULL;
369         }
370
371         return ret_node;
372 }
373
374
375 static int hs20_add_mo(struct hs20_osu_client *ctx, xml_node_t *add_mo,
376                        char *fname, size_t fname_len)
377 {
378         char *uri, *urn;
379         int ret;
380
381         debug_dump_node(ctx, "Received addMO", add_mo);
382
383         urn = get_spp_attr_value(ctx->xml, add_mo, "moURN");
384         if (urn == NULL) {
385                 wpa_printf(MSG_INFO, "[hs20] No moURN in addMO");
386                 return -1;
387         }
388         wpa_printf(MSG_INFO, "addMO - moURN: '%s'", urn);
389         if (strcasecmp(urn, URN_HS20_PPS) != 0) {
390                 wpa_printf(MSG_INFO, "[hs20] Unsupported MO in addMO");
391                 xml_node_get_attr_value_free(ctx->xml, urn);
392                 return -1;
393         }
394         xml_node_get_attr_value_free(ctx->xml, urn);
395
396         uri = get_spp_attr_value(ctx->xml, add_mo, "managementTreeURI");
397         if (uri == NULL) {
398                 wpa_printf(MSG_INFO, "[hs20] No managementTreeURI in addMO");
399                 return -1;
400         }
401         wpa_printf(MSG_INFO, "addMO - managementTreeURI: '%s'", uri);
402
403         ret = hs20_add_pps_mo(ctx, uri, add_mo, fname, fname_len);
404         xml_node_get_attr_value_free(ctx->xml, uri);
405         return ret;
406 }
407
408
409 static int process_spp_user_input_response(struct hs20_osu_client *ctx,
410                                            const char *session_id,
411                                            xml_node_t *add_mo)
412 {
413         int ret;
414         char fname[300];
415
416         debug_dump_node(ctx, "addMO", add_mo);
417
418         wpa_printf(MSG_INFO, "Subscription registration completed");
419
420         if (hs20_add_mo(ctx, add_mo, fname, sizeof(fname)) < 0) {
421                 wpa_printf(MSG_INFO, "Could not add MO");
422                 ret = hs20_spp_update_response(
423                         ctx, session_id,
424                         "Error occurred",
425                         "MO addition or update failed");
426                 return 0;
427         }
428
429         ret = hs20_spp_update_response(ctx, session_id, "OK", NULL);
430         if (ret == 0)
431                 hs20_sub_rem_complete(ctx, fname);
432
433         return 0;
434 }
435
436
437 static xml_node_t * hs20_spp_user_input_completed(struct hs20_osu_client *ctx,
438                                                     const char *session_id)
439 {
440         xml_node_t *node, *ret_node;
441
442         node = build_spp_post_dev_data(ctx, NULL, session_id,
443                                        "User input completed");
444         if (node == NULL)
445                 return NULL;
446
447         ret_node = soap_send_receive(ctx->http, node);
448         if (!ret_node) {
449                 if (soap_reinit_client(ctx->http) < 0)
450                         return NULL;
451                 wpa_printf(MSG_INFO, "Try to finish with re-opened connection");
452                 node = build_spp_post_dev_data(ctx, NULL, session_id,
453                                                "User input completed");
454                 if (node == NULL)
455                         return NULL;
456                 ret_node = soap_send_receive(ctx->http, node);
457                 if (ret_node == NULL)
458                         return NULL;
459                 wpa_printf(MSG_INFO, "Continue with new connection");
460         }
461
462         if (hs20_spp_validate(ctx, ret_node, "sppPostDevDataResponse") < 0) {
463                 wpa_printf(MSG_INFO, "SPP validation failed");
464                 xml_node_free(ctx->xml, ret_node);
465                 return NULL;
466         }
467
468         return ret_node;
469 }
470
471
472 static xml_node_t * hs20_spp_get_certificate(struct hs20_osu_client *ctx,
473                                              xml_node_t *cmd,
474                                              const char *session_id,
475                                              const char *pps_fname)
476 {
477         xml_namespace_t *ns;
478         xml_node_t *node, *ret_node;
479         int res;
480
481         wpa_printf(MSG_INFO, "Client certificate enrollment");
482
483         res = osu_get_certificate(ctx, cmd);
484         if (res < 0)
485                 wpa_printf(MSG_INFO, "EST simpleEnroll failed");
486
487         node = build_spp_post_dev_data(ctx, &ns, session_id,
488                                        res == 0 ?
489                                        "Certificate enrollment completed" :
490                                        "Certificate enrollment failed");
491         if (node == NULL)
492                 return NULL;
493
494         ret_node = soap_send_receive(ctx->http, node);
495         if (ret_node == NULL)
496                 return NULL;
497
498         debug_dump_node(ctx, "Received response to certificate enrollment "
499                         "completed", ret_node);
500
501         if (hs20_spp_validate(ctx, ret_node, "sppPostDevDataResponse") < 0) {
502                 wpa_printf(MSG_INFO, "SPP validation failed");
503                 xml_node_free(ctx->xml, ret_node);
504                 return NULL;
505         }
506
507         return ret_node;
508 }
509
510
511 static int hs20_spp_exec(struct hs20_osu_client *ctx, xml_node_t *exec,
512                          const char *session_id, const char *pps_fname,
513                          xml_node_t *pps, xml_node_t **ret_node)
514 {
515         xml_node_t *cmd;
516         const char *name;
517         char *uri;
518         char *id = strdup(session_id);
519
520         if (id == NULL)
521                 return -1;
522
523         *ret_node = NULL;
524
525         debug_dump_node(ctx, "exec", exec);
526
527         xml_node_for_each_child(ctx->xml, cmd, exec) {
528                 xml_node_for_each_check(ctx->xml, cmd);
529                 break;
530         }
531         if (!cmd) {
532                 wpa_printf(MSG_INFO, "exec command element not found (cmd=%p)",
533                            cmd);
534                 free(id);
535                 return -1;
536         }
537
538         name = xml_node_get_localname(ctx->xml, cmd);
539
540         if (strcasecmp(name, "launchBrowserToURI") == 0) {
541                 int res;
542                 uri = xml_node_get_text(ctx->xml, cmd);
543                 if (!uri) {
544                         wpa_printf(MSG_INFO, "No URI found");
545                         free(id);
546                         return -1;
547                 }
548                 wpa_printf(MSG_INFO, "Launch browser to URI '%s'", uri);
549                 write_summary(ctx, "Launch browser to URI '%s'", uri);
550                 res = hs20_web_browser(uri);
551                 xml_node_get_text_free(ctx->xml, uri);
552                 if (res > 0) {
553                         wpa_printf(MSG_INFO, "User response in browser completed successfully - sessionid='%s'",
554                                    id);
555                         write_summary(ctx, "User response in browser completed successfully");
556                         *ret_node = hs20_spp_user_input_completed(ctx, id);
557                         free(id);
558                         return *ret_node ? 0 : -1;
559                 } else {
560                         wpa_printf(MSG_INFO, "Failed to receive user response");
561                         write_summary(ctx, "Failed to receive user response");
562                         hs20_spp_update_response(
563                                 ctx, id, "Error occurred", "Other");
564                         free(id);
565                         return -1;
566                 }
567                 return 0;
568         }
569
570         if (strcasecmp(name, "uploadMO") == 0) {
571                 if (pps_fname == NULL)
572                         return -1;
573                 *ret_node = hs20_spp_upload_mo(ctx, cmd, id,
574                                                pps_fname);
575                 free(id);
576                 return *ret_node ? 0 : -1;
577         }
578
579         if (strcasecmp(name, "getCertificate") == 0) {
580                 *ret_node = hs20_spp_get_certificate(ctx, cmd, id,
581                                                      pps_fname);
582                 free(id);
583                 return *ret_node ? 0 : -1;
584         }
585
586         wpa_printf(MSG_INFO, "Unsupported exec command: '%s'", name);
587         free(id);
588         return -1;
589 }
590
591
592 enum spp_post_dev_data_use {
593         SPP_SUBSCRIPTION_REMEDIATION,
594         SPP_POLICY_UPDATE,
595         SPP_SUBSCRIPTION_REGISTRATION,
596 };
597
598 static void process_spp_post_dev_data_response(
599         struct hs20_osu_client *ctx,
600         enum spp_post_dev_data_use use, xml_node_t *node,
601         const char *pps_fname, xml_node_t *pps)
602 {
603         xml_node_t *child;
604         char *status = NULL;
605         xml_node_t *update = NULL, *exec = NULL, *add_mo = NULL, *no_mo = NULL;
606         char *session_id = NULL;
607
608         debug_dump_node(ctx, "sppPostDevDataResponse node", node);
609
610         status = get_spp_attr_value(ctx->xml, node, "sppStatus");
611         if (status == NULL) {
612                 wpa_printf(MSG_INFO, "No sppStatus attribute");
613                 goto out;
614         }
615         write_summary(ctx, "Received sppPostDevDataResponse sppStatus='%s'",
616                       status);
617
618         session_id = get_spp_attr_value(ctx->xml, node, "sessionID");
619         if (session_id == NULL) {
620                 wpa_printf(MSG_INFO, "No sessionID attribute");
621                 goto out;
622         }
623
624         wpa_printf(MSG_INFO, "[hs20] sppPostDevDataResponse - sppStatus: '%s'  sessionID: '%s'",
625                    status, session_id);
626
627         xml_node_for_each_child(ctx->xml, child, node) {
628                 const char *name;
629                 xml_node_for_each_check(ctx->xml, child);
630                 debug_dump_node(ctx, "child", child);
631                 name = xml_node_get_localname(ctx->xml, child);
632                 wpa_printf(MSG_INFO, "localname: '%s'", name);
633                 if (!update && strcasecmp(name, "updateNode") == 0)
634                         update = child;
635                 if (!exec && strcasecmp(name, "exec") == 0)
636                         exec = child;
637                 if (!add_mo && strcasecmp(name, "addMO") == 0)
638                         add_mo = child;
639                 if (!no_mo && strcasecmp(name, "noMOUpdate") == 0)
640                         no_mo = child;
641         }
642
643         if (use == SPP_SUBSCRIPTION_REMEDIATION &&
644             strcasecmp(status,
645                        "Remediation complete, request sppUpdateResponse") == 0)
646         {
647                 int res, ret;
648                 if (!update && !no_mo) {
649                         wpa_printf(MSG_INFO, "No updateNode or noMOUpdate element");
650                         goto out;
651                 }
652                 wpa_printf(MSG_INFO, "Subscription remediation completed");
653                 res = update_pps(ctx, update, pps_fname, pps);
654                 if (res < 0)
655                         wpa_printf(MSG_INFO, "Failed to update PPS MO");
656                 ret = hs20_spp_update_response(
657                         ctx, session_id,
658                         res < 0 ? "Error occurred" : "OK",
659                         res < 0 ? "MO addition or update failed" : NULL);
660                 if (res == 0 && ret == 0)
661                         hs20_sub_rem_complete(ctx, pps_fname);
662                 goto out;
663         }
664
665         if (use == SPP_SUBSCRIPTION_REMEDIATION &&
666             strcasecmp(status, "Exchange complete, release TLS connection") ==
667             0) {
668                 if (!no_mo) {
669                         wpa_printf(MSG_INFO, "No noMOUpdate element");
670                         goto out;
671                 }
672                 wpa_printf(MSG_INFO, "Subscription remediation completed (no MO update)");
673                 goto out;
674         }
675
676         if (use == SPP_POLICY_UPDATE &&
677             strcasecmp(status, "Update complete, request sppUpdateResponse") ==
678             0) {
679                 int res, ret;
680                 wpa_printf(MSG_INFO, "Policy update received - update PPS");
681                 res = update_pps(ctx, update, pps_fname, pps);
682                 ret = hs20_spp_update_response(
683                         ctx, session_id,
684                         res < 0 ? "Error occurred" : "OK",
685                         res < 0 ? "MO addition or update failed" : NULL);
686                 if (res == 0 && ret == 0)
687                         hs20_policy_update_complete(ctx, pps_fname);
688                 goto out;
689         }
690
691         if (use == SPP_SUBSCRIPTION_REGISTRATION &&
692             strcasecmp(status, "Provisioning complete, request "
693                        "sppUpdateResponse")  == 0) {
694                 if (!add_mo) {
695                         wpa_printf(MSG_INFO, "No addMO element - not sure what to do next");
696                         goto out;
697                 }
698                 process_spp_user_input_response(ctx, session_id, add_mo);
699                 node = NULL;
700                 goto out;
701         }
702
703         if (strcasecmp(status, "No update available at this time") == 0) {
704                 wpa_printf(MSG_INFO, "No update available at this time");
705                 goto out;
706         }
707
708         if (strcasecmp(status, "OK") == 0) {
709                 int res;
710                 xml_node_t *ret;
711
712                 if (!exec) {
713                         wpa_printf(MSG_INFO, "No exec element - not sure what to do next");
714                         goto out;
715                 }
716                 res = hs20_spp_exec(ctx, exec, session_id,
717                                     pps_fname, pps, &ret);
718                 /* xml_node_free(ctx->xml, node); */
719                 node = NULL;
720                 if (res == 0 && ret)
721                         process_spp_post_dev_data_response(ctx, use,
722                                                            ret, pps_fname, pps);
723                 goto out;
724         }
725
726         if (strcasecmp(status, "Error occurred") == 0) {
727                 xml_node_t *err;
728                 char *code = NULL;
729                 err = get_node(ctx->xml, node, "sppError");
730                 if (err)
731                         code = xml_node_get_attr_value(ctx->xml, err,
732                                                        "errorCode");
733                 wpa_printf(MSG_INFO, "Error occurred - errorCode=%s",
734                            code ? code : "N/A");
735                 xml_node_get_attr_value_free(ctx->xml, code);
736                 goto out;
737         }
738
739         wpa_printf(MSG_INFO,
740                    "[hs20] Unsupported sppPostDevDataResponse sppStatus '%s'",
741                    status);
742 out:
743         xml_node_get_attr_value_free(ctx->xml, status);
744         xml_node_get_attr_value_free(ctx->xml, session_id);
745         xml_node_free(ctx->xml, node);
746 }
747
748
749 static int spp_post_dev_data(struct hs20_osu_client *ctx,
750                              enum spp_post_dev_data_use use,
751                              const char *reason,
752                              const char *pps_fname, xml_node_t *pps)
753 {
754         xml_node_t *payload;
755         xml_node_t *ret_node;
756
757         payload = build_spp_post_dev_data(ctx, NULL, NULL, reason);
758         if (payload == NULL)
759                 return -1;
760
761         ret_node = soap_send_receive(ctx->http, payload);
762         if (!ret_node) {
763                 const char *err = http_get_err(ctx->http);
764                 if (err) {
765                         wpa_printf(MSG_INFO, "HTTP error: %s", err);
766                         write_result(ctx, "HTTP error: %s", err);
767                 } else {
768                         write_summary(ctx, "Failed to send SOAP message");
769                 }
770                 return -1;
771         }
772
773         if (hs20_spp_validate(ctx, ret_node, "sppPostDevDataResponse") < 0) {
774                 wpa_printf(MSG_INFO, "SPP validation failed");
775                 xml_node_free(ctx->xml, ret_node);
776                 return -1;
777         }
778
779         process_spp_post_dev_data_response(ctx, use, ret_node,
780                                            pps_fname, pps);
781         return 0;
782 }
783
784
785 void spp_sub_rem(struct hs20_osu_client *ctx, const char *address,
786                  const char *pps_fname,
787                  const char *client_cert, const char *client_key,
788                  const char *cred_username, const char *cred_password,
789                  xml_node_t *pps)
790 {
791         wpa_printf(MSG_INFO, "SPP subscription remediation");
792         write_summary(ctx, "SPP subscription remediation");
793
794         os_free(ctx->server_url);
795         ctx->server_url = os_strdup(address);
796
797         if (soap_init_client(ctx->http, address, ctx->ca_fname,
798                              cred_username, cred_password, client_cert,
799                              client_key) == 0) {
800                 spp_post_dev_data(ctx, SPP_SUBSCRIPTION_REMEDIATION,
801                                   "Subscription remediation", pps_fname, pps);
802         }
803 }
804
805
806 static void hs20_policy_update_complete(struct hs20_osu_client *ctx,
807                                         const char *pps_fname)
808 {
809         wpa_printf(MSG_INFO, "Policy update completed");
810
811         /*
812          * Update wpa_supplicant credentials and reconnect using updated
813          * information.
814          */
815         wpa_printf(MSG_INFO, "Updating wpa_supplicant credentials");
816         cmd_set_pps(ctx, pps_fname);
817
818         wpa_printf(MSG_INFO, "Requesting reconnection with updated configuration");
819         if (wpa_command(ctx->ifname, "INTERWORKING_SELECT auto") < 0)
820                 wpa_printf(MSG_ERROR, "Failed to request wpa_supplicant to reconnect");
821 }
822
823
824 static int process_spp_exchange_complete(struct hs20_osu_client *ctx,
825                                          xml_node_t *node)
826 {
827         char *status, *session_id;
828
829         debug_dump_node(ctx, "sppExchangeComplete", node);
830
831         status = get_spp_attr_value(ctx->xml, node, "sppStatus");
832         if (status == NULL) {
833                 wpa_printf(MSG_INFO, "No sppStatus attribute");
834                 return -1;
835         }
836         write_summary(ctx, "Received sppExchangeComplete sppStatus='%s'",
837                       status);
838
839         session_id = get_spp_attr_value(ctx->xml, node, "sessionID");
840         if (session_id == NULL) {
841                 wpa_printf(MSG_INFO, "No sessionID attribute");
842                 xml_node_get_attr_value_free(ctx->xml, status);
843                 return -1;
844         }
845
846         wpa_printf(MSG_INFO, "[hs20] sppStatus: '%s'  sessionID: '%s'",
847                    status, session_id);
848         xml_node_get_attr_value_free(ctx->xml, session_id);
849
850         if (strcasecmp(status, "Exchange complete, release TLS connection") ==
851             0) {
852                 xml_node_get_attr_value_free(ctx->xml, status);
853                 return 0;
854         }
855
856         wpa_printf(MSG_INFO, "Unexpected sppStatus '%s'", status);
857         write_summary(ctx, "Unexpected sppStatus '%s'", status);
858         xml_node_get_attr_value_free(ctx->xml, status);
859         return -1;
860 }
861
862
863 static xml_node_t * build_spp_update_response(struct hs20_osu_client *ctx,
864                                               const char *session_id,
865                                               const char *spp_status,
866                                               const char *error_code)
867 {
868         xml_namespace_t *ns;
869         xml_node_t *spp_node, *node;
870
871         spp_node = xml_node_create_root(ctx->xml, SPP_NS_URI, "spp", &ns,
872                                         "sppUpdateResponse");
873         if (spp_node == NULL)
874                 return NULL;
875
876         xml_node_add_attr(ctx->xml, spp_node, ns, "sppVersion", "1.0");
877         xml_node_add_attr(ctx->xml, spp_node, ns, "sessionID", session_id);
878         xml_node_add_attr(ctx->xml, spp_node, ns, "sppStatus", spp_status);
879
880         if (error_code) {
881                 node = xml_node_create(ctx->xml, spp_node, ns, "sppError");
882                 if (node)
883                         xml_node_add_attr(ctx->xml, node, NULL, "errorCode",
884                                           error_code);
885         }
886
887         return spp_node;
888 }
889
890
891 static int hs20_spp_update_response(struct hs20_osu_client *ctx,
892                                     const char *session_id,
893                                     const char *spp_status,
894                                     const char *error_code)
895 {
896         xml_node_t *node, *ret_node;
897         int ret;
898
899         write_summary(ctx, "Building sppUpdateResponse sppStatus='%s' error_code='%s'",
900                       spp_status, error_code);
901         node = build_spp_update_response(ctx, session_id, spp_status,
902                                          error_code);
903         if (node == NULL)
904                 return -1;
905         ret_node = soap_send_receive(ctx->http, node);
906         if (!ret_node) {
907                 if (soap_reinit_client(ctx->http) < 0)
908                         return -1;
909                 wpa_printf(MSG_INFO, "Try to finish with re-opened connection");
910                 node = build_spp_update_response(ctx, session_id, spp_status,
911                                                  error_code);
912                 if (node == NULL)
913                         return -1;
914                 ret_node = soap_send_receive(ctx->http, node);
915                 if (ret_node == NULL)
916                         return -1;
917                 wpa_printf(MSG_INFO, "Continue with new connection");
918         }
919
920         if (hs20_spp_validate(ctx, ret_node, "sppExchangeComplete") < 0) {
921                 wpa_printf(MSG_INFO, "SPP validation failed");
922                 xml_node_free(ctx->xml, ret_node);
923                 return -1;
924         }
925
926         ret = process_spp_exchange_complete(ctx, ret_node);
927         xml_node_free(ctx->xml, ret_node);
928         return ret;
929 }
930
931
932 void spp_pol_upd(struct hs20_osu_client *ctx, const char *address,
933                  const char *pps_fname,
934                  const char *client_cert, const char *client_key,
935                  const char *cred_username, const char *cred_password,
936                  xml_node_t *pps)
937 {
938         wpa_printf(MSG_INFO, "SPP policy update");
939         write_summary(ctx, "SPP policy update");
940
941         os_free(ctx->server_url);
942         ctx->server_url = os_strdup(address);
943
944         if (soap_init_client(ctx->http, address, ctx->ca_fname, cred_username,
945                              cred_password, client_cert, client_key) == 0) {
946                 spp_post_dev_data(ctx, SPP_POLICY_UPDATE, "Policy update",
947                                   pps_fname, pps);
948         }
949 }
950
951
952 int cmd_prov(struct hs20_osu_client *ctx, const char *url)
953 {
954         unlink("Cert/est_cert.der");
955         unlink("Cert/est_cert.pem");
956
957         if (url == NULL) {
958                 wpa_printf(MSG_INFO, "Invalid prov command (missing URL)");
959                 return -1;
960         }
961
962         wpa_printf(MSG_INFO,
963                    "Credential provisioning requested - URL: %s ca_fname: %s",
964                    url, ctx->ca_fname ? ctx->ca_fname : "N/A");
965
966         os_free(ctx->server_url);
967         ctx->server_url = os_strdup(url);
968
969         if (soap_init_client(ctx->http, url, ctx->ca_fname, NULL, NULL, NULL,
970                              NULL) < 0)
971                 return -1;
972         spp_post_dev_data(ctx, SPP_SUBSCRIPTION_REGISTRATION,
973                           "Subscription registration", NULL, NULL);
974
975         return ctx->pps_cred_set ? 0 : -1;
976 }
977
978
979 int cmd_sim_prov(struct hs20_osu_client *ctx, const char *url)
980 {
981         if (url == NULL) {
982                 wpa_printf(MSG_INFO, "Invalid prov command (missing URL)");
983                 return -1;
984         }
985
986         wpa_printf(MSG_INFO, "SIM provisioning requested");
987
988         os_free(ctx->server_url);
989         ctx->server_url = os_strdup(url);
990
991         wpa_printf(MSG_INFO, "Wait for IP address before starting SIM provisioning");
992
993         if (wait_ip_addr(ctx->ifname, 15) < 0) {
994                 wpa_printf(MSG_INFO, "Could not get IP address for WLAN - try connection anyway");
995         }
996
997         if (soap_init_client(ctx->http, url, ctx->ca_fname, NULL, NULL, NULL,
998                              NULL) < 0)
999                 return -1;
1000         spp_post_dev_data(ctx, SPP_SUBSCRIPTION_REGISTRATION,
1001                           "Subscription provisioning", NULL, NULL);
1002
1003         return ctx->pps_cred_set ? 0 : -1;
1004 }