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