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