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
get_spp_attr_value(struct xml_node_ctx * ctx,xml_node_t * node,char * attr_name)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
hs20_spp_validate(struct hs20_osu_client * ctx,xml_node_t * node,const char * expected_name)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
add_mo_container(struct xml_node_ctx * ctx,xml_namespace_t * ns,xml_node_t * parent,const char * urn,const char * fname)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
build_spp_post_dev_data(struct hs20_osu_client * ctx,xml_namespace_t ** ret_ns,const char * session_id,const char * reason)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
process_update_node(struct hs20_osu_client * ctx,xml_node_t * pps,xml_node_t * update)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
update_pps(struct hs20_osu_client * ctx,xml_node_t * update,const char * pps_fname,xml_node_t * pps)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
hs20_sub_rem_complete(struct hs20_osu_client * ctx,const char * pps_fname)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
hs20_spp_upload_mo(struct hs20_osu_client * ctx,xml_node_t * cmd,const char * session_id,const char * pps_fname)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
hs20_add_mo(struct hs20_osu_client * ctx,xml_node_t * add_mo,char * fname,size_t fname_len)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
process_spp_user_input_response(struct hs20_osu_client * ctx,const char * session_id,xml_node_t * add_mo)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
hs20_spp_user_input_completed(struct hs20_osu_client * ctx,const char * session_id)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
hs20_spp_get_certificate(struct hs20_osu_client * ctx,xml_node_t * cmd,const char * session_id,const char * pps_fname)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
hs20_spp_exec(struct hs20_osu_client * ctx,xml_node_t * exec,const char * session_id,const char * pps_fname,xml_node_t * pps,xml_node_t ** ret_node)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, 1);
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 }
568
569 if (strcasecmp(name, "uploadMO") == 0) {
570 if (pps_fname == NULL)
571 return -1;
572 *ret_node = hs20_spp_upload_mo(ctx, cmd, id,
573 pps_fname);
574 free(id);
575 return *ret_node ? 0 : -1;
576 }
577
578 if (strcasecmp(name, "getCertificate") == 0) {
579 *ret_node = hs20_spp_get_certificate(ctx, cmd, id,
580 pps_fname);
581 free(id);
582 return *ret_node ? 0 : -1;
583 }
584
585 wpa_printf(MSG_INFO, "Unsupported exec command: '%s'", name);
586 free(id);
587 return -1;
588 }
589
590
591 enum spp_post_dev_data_use {
592 SPP_SUBSCRIPTION_REMEDIATION,
593 SPP_POLICY_UPDATE,
594 SPP_SUBSCRIPTION_REGISTRATION,
595 };
596
process_spp_post_dev_data_response(struct hs20_osu_client * ctx,enum spp_post_dev_data_use use,xml_node_t * node,const char * pps_fname,xml_node_t * pps)597 static void process_spp_post_dev_data_response(
598 struct hs20_osu_client *ctx,
599 enum spp_post_dev_data_use use, xml_node_t *node,
600 const char *pps_fname, xml_node_t *pps)
601 {
602 xml_node_t *child;
603 char *status = NULL;
604 xml_node_t *update = NULL, *exec = NULL, *add_mo = NULL, *no_mo = NULL;
605 char *session_id = NULL;
606
607 debug_dump_node(ctx, "sppPostDevDataResponse node", node);
608
609 status = get_spp_attr_value(ctx->xml, node, "sppStatus");
610 if (status == NULL) {
611 wpa_printf(MSG_INFO, "No sppStatus attribute");
612 goto out;
613 }
614 write_summary(ctx, "Received sppPostDevDataResponse sppStatus='%s'",
615 status);
616
617 session_id = get_spp_attr_value(ctx->xml, node, "sessionID");
618 if (session_id == NULL) {
619 wpa_printf(MSG_INFO, "No sessionID attribute");
620 goto out;
621 }
622
623 wpa_printf(MSG_INFO, "[hs20] sppPostDevDataResponse - sppStatus: '%s' sessionID: '%s'",
624 status, session_id);
625
626 xml_node_for_each_child(ctx->xml, child, node) {
627 const char *name;
628 xml_node_for_each_check(ctx->xml, child);
629 debug_dump_node(ctx, "child", child);
630 name = xml_node_get_localname(ctx->xml, child);
631 wpa_printf(MSG_INFO, "localname: '%s'", name);
632 if (!update && strcasecmp(name, "updateNode") == 0)
633 update = child;
634 if (!exec && strcasecmp(name, "exec") == 0)
635 exec = child;
636 if (!add_mo && strcasecmp(name, "addMO") == 0)
637 add_mo = child;
638 if (!no_mo && strcasecmp(name, "noMOUpdate") == 0)
639 no_mo = child;
640 }
641
642 if (use == SPP_SUBSCRIPTION_REMEDIATION &&
643 strcasecmp(status,
644 "Remediation complete, request sppUpdateResponse") == 0)
645 {
646 int res, ret;
647 if (!update && !no_mo) {
648 wpa_printf(MSG_INFO, "No updateNode or noMOUpdate element");
649 goto out;
650 }
651 wpa_printf(MSG_INFO, "Subscription remediation completed");
652 res = update_pps(ctx, update, pps_fname, pps);
653 if (res < 0)
654 wpa_printf(MSG_INFO, "Failed to update PPS MO");
655 ret = hs20_spp_update_response(
656 ctx, session_id,
657 res < 0 ? "Error occurred" : "OK",
658 res < 0 ? "MO addition or update failed" : NULL);
659 if (res == 0 && ret == 0)
660 hs20_sub_rem_complete(ctx, pps_fname);
661 goto out;
662 }
663
664 if (use == SPP_SUBSCRIPTION_REMEDIATION &&
665 strcasecmp(status, "Exchange complete, release TLS connection") ==
666 0) {
667 if (!no_mo) {
668 wpa_printf(MSG_INFO, "No noMOUpdate element");
669 goto out;
670 }
671 wpa_printf(MSG_INFO, "Subscription remediation completed (no MO update)");
672 goto out;
673 }
674
675 if (use == SPP_POLICY_UPDATE &&
676 strcasecmp(status, "Update complete, request sppUpdateResponse") ==
677 0) {
678 int res, ret;
679 wpa_printf(MSG_INFO, "Policy update received - update PPS");
680 res = update_pps(ctx, update, pps_fname, pps);
681 ret = hs20_spp_update_response(
682 ctx, session_id,
683 res < 0 ? "Error occurred" : "OK",
684 res < 0 ? "MO addition or update failed" : NULL);
685 if (res == 0 && ret == 0)
686 hs20_policy_update_complete(ctx, pps_fname);
687 goto out;
688 }
689
690 if (use == SPP_SUBSCRIPTION_REGISTRATION &&
691 strcasecmp(status, "Provisioning complete, request "
692 "sppUpdateResponse") == 0) {
693 if (!add_mo) {
694 wpa_printf(MSG_INFO, "No addMO element - not sure what to do next");
695 goto out;
696 }
697 process_spp_user_input_response(ctx, session_id, add_mo);
698 node = NULL;
699 goto out;
700 }
701
702 if (strcasecmp(status, "No update available at this time") == 0) {
703 wpa_printf(MSG_INFO, "No update available at this time");
704 goto out;
705 }
706
707 if (strcasecmp(status, "OK") == 0) {
708 int res;
709 xml_node_t *ret;
710
711 if (!exec) {
712 wpa_printf(MSG_INFO, "No exec element - not sure what to do next");
713 goto out;
714 }
715 res = hs20_spp_exec(ctx, exec, session_id,
716 pps_fname, pps, &ret);
717 /* xml_node_free(ctx->xml, node); */
718 node = NULL;
719 if (res == 0 && ret)
720 process_spp_post_dev_data_response(ctx, use,
721 ret, pps_fname, pps);
722 goto out;
723 }
724
725 if (strcasecmp(status, "Error occurred") == 0) {
726 xml_node_t *err;
727 char *code = NULL;
728 err = get_node(ctx->xml, node, "sppError");
729 if (err)
730 code = xml_node_get_attr_value(ctx->xml, err,
731 "errorCode");
732 wpa_printf(MSG_INFO, "Error occurred - errorCode=%s",
733 code ? code : "N/A");
734 xml_node_get_attr_value_free(ctx->xml, code);
735 goto out;
736 }
737
738 wpa_printf(MSG_INFO,
739 "[hs20] Unsupported sppPostDevDataResponse sppStatus '%s'",
740 status);
741 out:
742 xml_node_get_attr_value_free(ctx->xml, status);
743 xml_node_get_attr_value_free(ctx->xml, session_id);
744 xml_node_free(ctx->xml, node);
745 }
746
747
spp_post_dev_data(struct hs20_osu_client * ctx,enum spp_post_dev_data_use use,const char * reason,const char * pps_fname,xml_node_t * pps)748 static int spp_post_dev_data(struct hs20_osu_client *ctx,
749 enum spp_post_dev_data_use use,
750 const char *reason,
751 const char *pps_fname, xml_node_t *pps)
752 {
753 xml_node_t *payload;
754 xml_node_t *ret_node;
755
756 payload = build_spp_post_dev_data(ctx, NULL, NULL, reason);
757 if (payload == NULL)
758 return -1;
759
760 ret_node = soap_send_receive(ctx->http, payload);
761 if (!ret_node) {
762 const char *err = http_get_err(ctx->http);
763 if (err) {
764 wpa_printf(MSG_INFO, "HTTP error: %s", err);
765 write_result(ctx, "HTTP error: %s", err);
766 } else {
767 write_summary(ctx, "Failed to send SOAP message");
768 }
769 return -1;
770 }
771
772 if (hs20_spp_validate(ctx, ret_node, "sppPostDevDataResponse") < 0) {
773 wpa_printf(MSG_INFO, "SPP validation failed");
774 xml_node_free(ctx->xml, ret_node);
775 return -1;
776 }
777
778 process_spp_post_dev_data_response(ctx, use, ret_node,
779 pps_fname, pps);
780 return 0;
781 }
782
783
spp_sub_rem(struct hs20_osu_client * ctx,const char * address,const char * pps_fname,const char * client_cert,const char * client_key,const char * cred_username,const char * cred_password,xml_node_t * pps)784 void spp_sub_rem(struct hs20_osu_client *ctx, const char *address,
785 const char *pps_fname,
786 const char *client_cert, const char *client_key,
787 const char *cred_username, const char *cred_password,
788 xml_node_t *pps)
789 {
790 wpa_printf(MSG_INFO, "SPP subscription remediation");
791 write_summary(ctx, "SPP subscription remediation");
792
793 os_free(ctx->server_url);
794 ctx->server_url = os_strdup(address);
795
796 if (soap_init_client(ctx->http, address, ctx->ca_fname,
797 cred_username, cred_password, client_cert,
798 client_key) == 0) {
799 spp_post_dev_data(ctx, SPP_SUBSCRIPTION_REMEDIATION,
800 "Subscription remediation", pps_fname, pps);
801 }
802 }
803
804
hs20_policy_update_complete(struct hs20_osu_client * ctx,const char * pps_fname)805 static void hs20_policy_update_complete(struct hs20_osu_client *ctx,
806 const char *pps_fname)
807 {
808 wpa_printf(MSG_INFO, "Policy update completed");
809
810 /*
811 * Update wpa_supplicant credentials and reconnect using updated
812 * information.
813 */
814 wpa_printf(MSG_INFO, "Updating wpa_supplicant credentials");
815 cmd_set_pps(ctx, pps_fname);
816
817 wpa_printf(MSG_INFO, "Requesting reconnection with updated configuration");
818 if (wpa_command(ctx->ifname, "INTERWORKING_SELECT auto") < 0)
819 wpa_printf(MSG_ERROR, "Failed to request wpa_supplicant to reconnect");
820 }
821
822
process_spp_exchange_complete(struct hs20_osu_client * ctx,xml_node_t * node)823 static int process_spp_exchange_complete(struct hs20_osu_client *ctx,
824 xml_node_t *node)
825 {
826 char *status, *session_id;
827
828 debug_dump_node(ctx, "sppExchangeComplete", node);
829
830 status = get_spp_attr_value(ctx->xml, node, "sppStatus");
831 if (status == NULL) {
832 wpa_printf(MSG_INFO, "No sppStatus attribute");
833 return -1;
834 }
835 write_summary(ctx, "Received sppExchangeComplete sppStatus='%s'",
836 status);
837
838 session_id = get_spp_attr_value(ctx->xml, node, "sessionID");
839 if (session_id == NULL) {
840 wpa_printf(MSG_INFO, "No sessionID attribute");
841 xml_node_get_attr_value_free(ctx->xml, status);
842 return -1;
843 }
844
845 wpa_printf(MSG_INFO, "[hs20] sppStatus: '%s' sessionID: '%s'",
846 status, session_id);
847 xml_node_get_attr_value_free(ctx->xml, session_id);
848
849 if (strcasecmp(status, "Exchange complete, release TLS connection") ==
850 0) {
851 xml_node_get_attr_value_free(ctx->xml, status);
852 return 0;
853 }
854
855 wpa_printf(MSG_INFO, "Unexpected sppStatus '%s'", status);
856 write_summary(ctx, "Unexpected sppStatus '%s'", status);
857 xml_node_get_attr_value_free(ctx->xml, status);
858 return -1;
859 }
860
861
build_spp_update_response(struct hs20_osu_client * ctx,const char * session_id,const char * spp_status,const char * error_code)862 static xml_node_t * build_spp_update_response(struct hs20_osu_client *ctx,
863 const char *session_id,
864 const char *spp_status,
865 const char *error_code)
866 {
867 xml_namespace_t *ns;
868 xml_node_t *spp_node, *node;
869
870 spp_node = xml_node_create_root(ctx->xml, SPP_NS_URI, "spp", &ns,
871 "sppUpdateResponse");
872 if (spp_node == NULL)
873 return NULL;
874
875 xml_node_add_attr(ctx->xml, spp_node, ns, "sppVersion", "1.0");
876 xml_node_add_attr(ctx->xml, spp_node, ns, "sessionID", session_id);
877 xml_node_add_attr(ctx->xml, spp_node, ns, "sppStatus", spp_status);
878
879 if (error_code) {
880 node = xml_node_create(ctx->xml, spp_node, ns, "sppError");
881 if (node)
882 xml_node_add_attr(ctx->xml, node, NULL, "errorCode",
883 error_code);
884 }
885
886 return spp_node;
887 }
888
889
hs20_spp_update_response(struct hs20_osu_client * ctx,const char * session_id,const char * spp_status,const char * error_code)890 static int hs20_spp_update_response(struct hs20_osu_client *ctx,
891 const char *session_id,
892 const char *spp_status,
893 const char *error_code)
894 {
895 xml_node_t *node, *ret_node;
896 int ret;
897
898 write_summary(ctx, "Building sppUpdateResponse sppStatus='%s' error_code='%s'",
899 spp_status, error_code);
900 node = build_spp_update_response(ctx, session_id, spp_status,
901 error_code);
902 if (node == NULL)
903 return -1;
904 ret_node = soap_send_receive(ctx->http, node);
905 if (!ret_node) {
906 if (soap_reinit_client(ctx->http) < 0)
907 return -1;
908 wpa_printf(MSG_INFO, "Try to finish with re-opened connection");
909 node = build_spp_update_response(ctx, session_id, spp_status,
910 error_code);
911 if (node == NULL)
912 return -1;
913 ret_node = soap_send_receive(ctx->http, node);
914 if (ret_node == NULL)
915 return -1;
916 wpa_printf(MSG_INFO, "Continue with new connection");
917 }
918
919 if (hs20_spp_validate(ctx, ret_node, "sppExchangeComplete") < 0) {
920 wpa_printf(MSG_INFO, "SPP validation failed");
921 xml_node_free(ctx->xml, ret_node);
922 return -1;
923 }
924
925 ret = process_spp_exchange_complete(ctx, ret_node);
926 xml_node_free(ctx->xml, ret_node);
927 return ret;
928 }
929
930
spp_pol_upd(struct hs20_osu_client * ctx,const char * address,const char * pps_fname,const char * client_cert,const char * client_key,const char * cred_username,const char * cred_password,xml_node_t * pps)931 void spp_pol_upd(struct hs20_osu_client *ctx, const char *address,
932 const char *pps_fname,
933 const char *client_cert, const char *client_key,
934 const char *cred_username, const char *cred_password,
935 xml_node_t *pps)
936 {
937 wpa_printf(MSG_INFO, "SPP policy update");
938 write_summary(ctx, "SPP policy update");
939
940 os_free(ctx->server_url);
941 ctx->server_url = os_strdup(address);
942
943 if (soap_init_client(ctx->http, address, ctx->ca_fname, cred_username,
944 cred_password, client_cert, client_key) == 0) {
945 spp_post_dev_data(ctx, SPP_POLICY_UPDATE, "Policy update",
946 pps_fname, pps);
947 }
948 }
949
950
cmd_prov(struct hs20_osu_client * ctx,const char * url)951 int cmd_prov(struct hs20_osu_client *ctx, const char *url)
952 {
953 unlink("Cert/est_cert.der");
954 unlink("Cert/est_cert.pem");
955
956 if (url == NULL) {
957 wpa_printf(MSG_INFO, "Invalid prov command (missing URL)");
958 return -1;
959 }
960
961 wpa_printf(MSG_INFO,
962 "Credential provisioning requested - URL: %s ca_fname: %s",
963 url, ctx->ca_fname ? ctx->ca_fname : "N/A");
964
965 os_free(ctx->server_url);
966 ctx->server_url = os_strdup(url);
967
968 if (soap_init_client(ctx->http, url, ctx->ca_fname, NULL, NULL, NULL,
969 NULL) < 0)
970 return -1;
971 spp_post_dev_data(ctx, SPP_SUBSCRIPTION_REGISTRATION,
972 "Subscription registration", NULL, NULL);
973
974 return ctx->pps_cred_set ? 0 : -1;
975 }
976
977
cmd_sim_prov(struct hs20_osu_client * ctx,const char * url)978 int cmd_sim_prov(struct hs20_osu_client *ctx, const char *url)
979 {
980 if (url == NULL) {
981 wpa_printf(MSG_INFO, "Invalid prov command (missing URL)");
982 return -1;
983 }
984
985 wpa_printf(MSG_INFO, "SIM provisioning requested");
986
987 os_free(ctx->server_url);
988 ctx->server_url = os_strdup(url);
989
990 wpa_printf(MSG_INFO, "Wait for IP address before starting SIM provisioning");
991
992 if (wait_ip_addr(ctx->ifname, 15) < 0) {
993 wpa_printf(MSG_INFO, "Could not get IP address for WLAN - try connection anyway");
994 }
995
996 if (soap_init_client(ctx->http, url, ctx->ca_fname, NULL, NULL, NULL,
997 NULL) < 0)
998 return -1;
999 spp_post_dev_data(ctx, SPP_SUBSCRIPTION_REGISTRATION,
1000 "Subscription provisioning", NULL, NULL);
1001
1002 return ctx->pps_cred_set ? 0 : -1;
1003 }
1004