1 1.1 christos /* 2 1.1 christos * Hotspot 2.0 OSU client 3 1.1 christos * Copyright (c) 2012-2014, Qualcomm Atheros, Inc. 4 1.1 christos * 5 1.1 christos * This software may be distributed under the terms of the BSD license. 6 1.1 christos * See README for more details. 7 1.1 christos */ 8 1.1 christos 9 1.1 christos #include "includes.h" 10 1.1 christos #include <time.h> 11 1.1 christos #include <sys/stat.h> 12 1.1.1.2 christos #ifdef ANDROID 13 1.1.1.2 christos #include "private/android_filesystem_config.h" 14 1.1.1.2 christos #endif /* ANDROID */ 15 1.1 christos 16 1.1 christos #include "common.h" 17 1.1 christos #include "utils/browser.h" 18 1.1 christos #include "utils/base64.h" 19 1.1 christos #include "utils/xml-utils.h" 20 1.1 christos #include "utils/http-utils.h" 21 1.1 christos #include "common/wpa_ctrl.h" 22 1.1 christos #include "common/wpa_helpers.h" 23 1.1 christos #include "eap_common/eap_defs.h" 24 1.1 christos #include "crypto/crypto.h" 25 1.1 christos #include "crypto/sha256.h" 26 1.1 christos #include "osu_client.h" 27 1.1 christos 28 1.1.1.3 christos const char *spp_xsd_fname = "spp.xsd"; 29 1.1.1.3 christos 30 1.1 christos 31 1.1 christos void write_result(struct hs20_osu_client *ctx, const char *fmt, ...) 32 1.1 christos { 33 1.1 christos va_list ap; 34 1.1 christos FILE *f; 35 1.1 christos char buf[500]; 36 1.1 christos 37 1.1 christos va_start(ap, fmt); 38 1.1 christos vsnprintf(buf, sizeof(buf), fmt, ap); 39 1.1 christos va_end(ap); 40 1.1 christos write_summary(ctx, "%s", buf); 41 1.1 christos 42 1.1 christos if (!ctx->result_file) 43 1.1 christos return; 44 1.1 christos 45 1.1 christos f = fopen(ctx->result_file, "w"); 46 1.1 christos if (f == NULL) 47 1.1 christos return; 48 1.1 christos 49 1.1 christos va_start(ap, fmt); 50 1.1 christos vfprintf(f, fmt, ap); 51 1.1 christos va_end(ap); 52 1.1 christos fprintf(f, "\n"); 53 1.1 christos fclose(f); 54 1.1 christos } 55 1.1 christos 56 1.1 christos 57 1.1 christos void write_summary(struct hs20_osu_client *ctx, const char *fmt, ...) 58 1.1 christos { 59 1.1 christos va_list ap; 60 1.1 christos FILE *f; 61 1.1 christos 62 1.1 christos if (!ctx->summary_file) 63 1.1 christos return; 64 1.1 christos 65 1.1 christos f = fopen(ctx->summary_file, "a"); 66 1.1 christos if (f == NULL) 67 1.1 christos return; 68 1.1 christos 69 1.1 christos va_start(ap, fmt); 70 1.1 christos vfprintf(f, fmt, ap); 71 1.1 christos va_end(ap); 72 1.1 christos fprintf(f, "\n"); 73 1.1 christos fclose(f); 74 1.1 christos } 75 1.1 christos 76 1.1 christos 77 1.1 christos void debug_dump_node(struct hs20_osu_client *ctx, const char *title, 78 1.1 christos xml_node_t *node) 79 1.1 christos { 80 1.1 christos char *str = xml_node_to_str(ctx->xml, node); 81 1.1 christos wpa_printf(MSG_DEBUG, "[hs20] %s: '%s'", title, str); 82 1.1 christos free(str); 83 1.1 christos } 84 1.1 christos 85 1.1 christos 86 1.1 christos static int valid_fqdn(const char *fqdn) 87 1.1 christos { 88 1.1 christos const char *pos; 89 1.1 christos 90 1.1 christos /* TODO: could make this more complete.. */ 91 1.1 christos if (strchr(fqdn, '.') == 0 || strlen(fqdn) > 255) 92 1.1 christos return 0; 93 1.1 christos for (pos = fqdn; *pos; pos++) { 94 1.1 christos if (*pos >= 'a' && *pos <= 'z') 95 1.1 christos continue; 96 1.1 christos if (*pos >= 'A' && *pos <= 'Z') 97 1.1 christos continue; 98 1.1 christos if (*pos >= '0' && *pos <= '9') 99 1.1 christos continue; 100 1.1 christos if (*pos == '-' || *pos == '.' || *pos == '_') 101 1.1 christos continue; 102 1.1 christos return 0; 103 1.1 christos } 104 1.1 christos return 1; 105 1.1 christos } 106 1.1 christos 107 1.1 christos 108 1.1.1.4 christos static int android_update_permission(const char *path, mode_t mode) 109 1.1.1.4 christos { 110 1.1.1.4 christos #ifdef ANDROID 111 1.1.1.4 christos /* we need to change file/folder permission for Android */ 112 1.1.1.4 christos 113 1.1.1.4 christos if (!path) { 114 1.1.1.4 christos wpa_printf(MSG_ERROR, "file path null"); 115 1.1.1.4 christos return -1; 116 1.1.1.4 christos } 117 1.1.1.4 christos 118 1.1.1.4 christos /* Allow processes running with Group ID as AID_WIFI, 119 1.1.1.4 christos * to read files from SP, SP/<fqdn>, Cert and osu-info directories */ 120 1.1.1.5 christos if (lchown(path, -1, AID_WIFI)) { 121 1.1.1.5 christos wpa_printf(MSG_INFO, "CTRL: Could not lchown directory: %s", 122 1.1.1.4 christos strerror(errno)); 123 1.1.1.4 christos return -1; 124 1.1.1.4 christos } 125 1.1.1.4 christos 126 1.1.1.4 christos if (chmod(path, mode) < 0) { 127 1.1.1.4 christos wpa_printf(MSG_INFO, "CTRL: Could not chmod directory: %s", 128 1.1.1.4 christos strerror(errno)); 129 1.1.1.4 christos return -1; 130 1.1.1.4 christos } 131 1.1.1.4 christos #endif /* ANDROID */ 132 1.1.1.4 christos 133 1.1.1.4 christos return 0; 134 1.1.1.4 christos } 135 1.1.1.4 christos 136 1.1.1.4 christos 137 1.1 christos int osu_get_certificate(struct hs20_osu_client *ctx, xml_node_t *getcert) 138 1.1 christos { 139 1.1 christos xml_node_t *node; 140 1.1 christos char *url, *user = NULL, *pw = NULL; 141 1.1 christos char *proto; 142 1.1 christos int ret = -1; 143 1.1 christos 144 1.1 christos proto = xml_node_get_attr_value(ctx->xml, getcert, 145 1.1 christos "enrollmentProtocol"); 146 1.1 christos if (!proto) 147 1.1 christos return -1; 148 1.1 christos wpa_printf(MSG_INFO, "getCertificate - enrollmentProtocol=%s", proto); 149 1.1 christos write_summary(ctx, "getCertificate - enrollmentProtocol=%s", proto); 150 1.1 christos if (os_strcasecmp(proto, "EST") != 0) { 151 1.1 christos wpa_printf(MSG_INFO, "Unsupported enrollmentProtocol"); 152 1.1 christos xml_node_get_attr_value_free(ctx->xml, proto); 153 1.1 christos return -1; 154 1.1 christos } 155 1.1 christos xml_node_get_attr_value_free(ctx->xml, proto); 156 1.1 christos 157 1.1 christos node = get_node(ctx->xml, getcert, "enrollmentServerURI"); 158 1.1 christos if (node == NULL) { 159 1.1 christos wpa_printf(MSG_INFO, "Could not find enrollmentServerURI node"); 160 1.1 christos xml_node_get_attr_value_free(ctx->xml, proto); 161 1.1 christos return -1; 162 1.1 christos } 163 1.1 christos url = xml_node_get_text(ctx->xml, node); 164 1.1 christos if (url == NULL) { 165 1.1 christos wpa_printf(MSG_INFO, "Could not get URL text"); 166 1.1 christos return -1; 167 1.1 christos } 168 1.1 christos wpa_printf(MSG_INFO, "enrollmentServerURI: %s", url); 169 1.1 christos write_summary(ctx, "enrollmentServerURI: %s", url); 170 1.1 christos 171 1.1 christos node = get_node(ctx->xml, getcert, "estUserID"); 172 1.1 christos if (node == NULL && !ctx->client_cert_present) { 173 1.1 christos wpa_printf(MSG_INFO, "Could not find estUserID node"); 174 1.1 christos goto fail; 175 1.1 christos } 176 1.1 christos if (node) { 177 1.1 christos user = xml_node_get_text(ctx->xml, node); 178 1.1 christos if (user == NULL) { 179 1.1 christos wpa_printf(MSG_INFO, "Could not get estUserID text"); 180 1.1 christos goto fail; 181 1.1 christos } 182 1.1 christos wpa_printf(MSG_INFO, "estUserID: %s", user); 183 1.1 christos write_summary(ctx, "estUserID: %s", user); 184 1.1 christos } 185 1.1 christos 186 1.1 christos node = get_node(ctx->xml, getcert, "estPassword"); 187 1.1 christos if (node == NULL && !ctx->client_cert_present) { 188 1.1 christos wpa_printf(MSG_INFO, "Could not find estPassword node"); 189 1.1 christos goto fail; 190 1.1 christos } 191 1.1 christos if (node) { 192 1.1 christos pw = xml_node_get_base64_text(ctx->xml, node, NULL); 193 1.1 christos if (pw == NULL) { 194 1.1 christos wpa_printf(MSG_INFO, "Could not get estPassword text"); 195 1.1 christos goto fail; 196 1.1 christos } 197 1.1 christos wpa_printf(MSG_INFO, "estPassword: %s", pw); 198 1.1 christos } 199 1.1 christos 200 1.1 christos mkdir("Cert", S_IRWXU); 201 1.1.1.4 christos android_update_permission("Cert", S_IRWXU | S_IRWXG); 202 1.1.1.4 christos 203 1.1 christos if (est_load_cacerts(ctx, url) < 0 || 204 1.1 christos est_build_csr(ctx, url) < 0 || 205 1.1 christos est_simple_enroll(ctx, url, user, pw) < 0) 206 1.1 christos goto fail; 207 1.1 christos 208 1.1 christos ret = 0; 209 1.1 christos fail: 210 1.1 christos xml_node_get_text_free(ctx->xml, url); 211 1.1 christos xml_node_get_text_free(ctx->xml, user); 212 1.1 christos xml_node_get_text_free(ctx->xml, pw); 213 1.1 christos 214 1.1 christos return ret; 215 1.1 christos } 216 1.1 christos 217 1.1 christos 218 1.1 christos static int process_est_cert(struct hs20_osu_client *ctx, xml_node_t *cert, 219 1.1 christos const char *fqdn) 220 1.1 christos { 221 1.1 christos u8 digest1[SHA256_MAC_LEN], digest2[SHA256_MAC_LEN]; 222 1.1 christos char *der, *pem; 223 1.1 christos size_t der_len, pem_len; 224 1.1 christos char *fingerprint; 225 1.1 christos char buf[200]; 226 1.1 christos 227 1.1 christos wpa_printf(MSG_INFO, "PPS for certificate credential - fqdn=%s", fqdn); 228 1.1 christos 229 1.1 christos fingerprint = xml_node_get_text(ctx->xml, cert); 230 1.1 christos if (fingerprint == NULL) 231 1.1 christos return -1; 232 1.1 christos if (hexstr2bin(fingerprint, digest1, SHA256_MAC_LEN) < 0) { 233 1.1 christos wpa_printf(MSG_INFO, "Invalid SHA256 hash value"); 234 1.1 christos write_result(ctx, "Invalid client certificate SHA256 hash value in PPS"); 235 1.1 christos xml_node_get_text_free(ctx->xml, fingerprint); 236 1.1 christos return -1; 237 1.1 christos } 238 1.1 christos xml_node_get_text_free(ctx->xml, fingerprint); 239 1.1 christos 240 1.1 christos der = os_readfile("Cert/est_cert.der", &der_len); 241 1.1 christos if (der == NULL) { 242 1.1 christos wpa_printf(MSG_INFO, "Could not find client certificate from EST"); 243 1.1 christos write_result(ctx, "Could not find client certificate from EST"); 244 1.1 christos return -1; 245 1.1 christos } 246 1.1 christos 247 1.1 christos if (sha256_vector(1, (const u8 **) &der, &der_len, digest2) < 0) { 248 1.1 christos os_free(der); 249 1.1 christos return -1; 250 1.1 christos } 251 1.1 christos os_free(der); 252 1.1 christos 253 1.1 christos if (os_memcmp(digest1, digest2, sizeof(digest1)) != 0) { 254 1.1 christos wpa_printf(MSG_INFO, "Client certificate from EST does not match fingerprint from PPS MO"); 255 1.1 christos write_result(ctx, "Client certificate from EST does not match fingerprint from PPS MO"); 256 1.1 christos return -1; 257 1.1 christos } 258 1.1 christos 259 1.1 christos wpa_printf(MSG_INFO, "Client certificate from EST matches PPS MO"); 260 1.1 christos unlink("Cert/est_cert.der"); 261 1.1 christos 262 1.1 christos os_snprintf(buf, sizeof(buf), "SP/%s/client-ca.pem", fqdn); 263 1.1 christos if (rename("Cert/est-cacerts.pem", buf) < 0) { 264 1.1 christos wpa_printf(MSG_INFO, "Could not move est-cacerts.pem to client-ca.pem: %s", 265 1.1 christos strerror(errno)); 266 1.1 christos return -1; 267 1.1 christos } 268 1.1 christos pem = os_readfile(buf, &pem_len); 269 1.1 christos 270 1.1 christos os_snprintf(buf, sizeof(buf), "SP/%s/client-cert.pem", fqdn); 271 1.1 christos if (rename("Cert/est_cert.pem", buf) < 0) { 272 1.1 christos wpa_printf(MSG_INFO, "Could not move est_cert.pem to client-cert.pem: %s", 273 1.1 christos strerror(errno)); 274 1.1 christos os_free(pem); 275 1.1 christos return -1; 276 1.1 christos } 277 1.1 christos 278 1.1 christos if (pem) { 279 1.1 christos FILE *f = fopen(buf, "a"); 280 1.1 christos if (f) { 281 1.1 christos fwrite(pem, pem_len, 1, f); 282 1.1 christos fclose(f); 283 1.1 christos } 284 1.1 christos os_free(pem); 285 1.1 christos } 286 1.1 christos 287 1.1 christos os_snprintf(buf, sizeof(buf), "SP/%s/client-key.pem", fqdn); 288 1.1 christos if (rename("Cert/privkey-plain.pem", buf) < 0) { 289 1.1 christos wpa_printf(MSG_INFO, "Could not move privkey-plain.pem to client-key.pem: %s", 290 1.1 christos strerror(errno)); 291 1.1 christos return -1; 292 1.1 christos } 293 1.1 christos 294 1.1 christos unlink("Cert/est-req.b64"); 295 1.1 christos unlink("Cert/est-req.pem"); 296 1.1 christos rmdir("Cert"); 297 1.1 christos 298 1.1 christos return 0; 299 1.1 christos } 300 1.1 christos 301 1.1 christos 302 1.1 christos #define TMP_CERT_DL_FILE "tmp-cert-download" 303 1.1 christos 304 1.1 christos static int download_cert(struct hs20_osu_client *ctx, xml_node_t *params, 305 1.1 christos const char *fname) 306 1.1 christos { 307 1.1 christos xml_node_t *url_node, *hash_node; 308 1.1 christos char *url, *hash; 309 1.1 christos char *cert; 310 1.1 christos size_t len; 311 1.1 christos u8 digest1[SHA256_MAC_LEN], digest2[SHA256_MAC_LEN]; 312 1.1 christos int res; 313 1.1.1.6 christos char *b64; 314 1.1 christos FILE *f; 315 1.1 christos 316 1.1 christos url_node = get_node(ctx->xml, params, "CertURL"); 317 1.1 christos hash_node = get_node(ctx->xml, params, "CertSHA256Fingerprint"); 318 1.1 christos if (url_node == NULL || hash_node == NULL) 319 1.1 christos return -1; 320 1.1 christos url = xml_node_get_text(ctx->xml, url_node); 321 1.1 christos hash = xml_node_get_text(ctx->xml, hash_node); 322 1.1 christos if (url == NULL || hash == NULL) { 323 1.1 christos xml_node_get_text_free(ctx->xml, url); 324 1.1 christos xml_node_get_text_free(ctx->xml, hash); 325 1.1 christos return -1; 326 1.1 christos } 327 1.1 christos 328 1.1 christos wpa_printf(MSG_INFO, "CertURL: %s", url); 329 1.1 christos wpa_printf(MSG_INFO, "SHA256 hash: %s", hash); 330 1.1 christos 331 1.1 christos if (hexstr2bin(hash, digest1, SHA256_MAC_LEN) < 0) { 332 1.1 christos wpa_printf(MSG_INFO, "Invalid SHA256 hash value"); 333 1.1 christos write_result(ctx, "Invalid SHA256 hash value for downloaded certificate"); 334 1.1 christos xml_node_get_text_free(ctx->xml, hash); 335 1.1 christos return -1; 336 1.1 christos } 337 1.1 christos xml_node_get_text_free(ctx->xml, hash); 338 1.1 christos 339 1.1 christos write_summary(ctx, "Download certificate from %s", url); 340 1.1 christos ctx->no_osu_cert_validation = 1; 341 1.1 christos http_ocsp_set(ctx->http, 1); 342 1.1 christos res = http_download_file(ctx->http, url, TMP_CERT_DL_FILE, NULL); 343 1.1 christos http_ocsp_set(ctx->http, 344 1.1 christos (ctx->workarounds & WORKAROUND_OCSP_OPTIONAL) ? 1 : 2); 345 1.1 christos ctx->no_osu_cert_validation = 0; 346 1.1 christos xml_node_get_text_free(ctx->xml, url); 347 1.1 christos if (res < 0) 348 1.1 christos return -1; 349 1.1 christos 350 1.1 christos cert = os_readfile(TMP_CERT_DL_FILE, &len); 351 1.1 christos remove(TMP_CERT_DL_FILE); 352 1.1 christos if (cert == NULL) 353 1.1 christos return -1; 354 1.1 christos 355 1.1 christos if (sha256_vector(1, (const u8 **) &cert, &len, digest2) < 0) { 356 1.1 christos os_free(cert); 357 1.1 christos return -1; 358 1.1 christos } 359 1.1 christos 360 1.1 christos if (os_memcmp(digest1, digest2, sizeof(digest1)) != 0) { 361 1.1 christos wpa_printf(MSG_INFO, "Downloaded certificate fingerprint did not match"); 362 1.1 christos write_result(ctx, "Downloaded certificate fingerprint did not match"); 363 1.1 christos os_free(cert); 364 1.1 christos return -1; 365 1.1 christos } 366 1.1 christos 367 1.1.1.6 christos b64 = base64_encode(cert, len, NULL); 368 1.1 christos os_free(cert); 369 1.1 christos if (b64 == NULL) 370 1.1 christos return -1; 371 1.1 christos 372 1.1 christos f = fopen(fname, "wb"); 373 1.1 christos if (f == NULL) { 374 1.1 christos os_free(b64); 375 1.1 christos return -1; 376 1.1 christos } 377 1.1 christos 378 1.1 christos fprintf(f, "-----BEGIN CERTIFICATE-----\n" 379 1.1 christos "%s" 380 1.1 christos "-----END CERTIFICATE-----\n", 381 1.1 christos b64); 382 1.1 christos 383 1.1 christos os_free(b64); 384 1.1 christos fclose(f); 385 1.1 christos 386 1.1 christos wpa_printf(MSG_INFO, "Downloaded certificate into %s and validated fingerprint", 387 1.1 christos fname); 388 1.1 christos write_summary(ctx, "Downloaded certificate into %s and validated fingerprint", 389 1.1 christos fname); 390 1.1 christos 391 1.1 christos return 0; 392 1.1 christos } 393 1.1 christos 394 1.1 christos 395 1.1 christos static int cmd_dl_osu_ca(struct hs20_osu_client *ctx, const char *pps_fname, 396 1.1 christos const char *ca_fname) 397 1.1 christos { 398 1.1 christos xml_node_t *pps, *node; 399 1.1 christos int ret; 400 1.1 christos 401 1.1 christos pps = node_from_file(ctx->xml, pps_fname); 402 1.1 christos if (pps == NULL) { 403 1.1 christos wpa_printf(MSG_INFO, "Could not read or parse '%s'", pps_fname); 404 1.1 christos return -1; 405 1.1 christos } 406 1.1 christos 407 1.1 christos node = get_child_node(ctx->xml, pps, 408 1.1 christos "SubscriptionUpdate/TrustRoot"); 409 1.1 christos if (node == NULL) { 410 1.1 christos wpa_printf(MSG_INFO, "No SubscriptionUpdate/TrustRoot/CertURL found from PPS"); 411 1.1 christos xml_node_free(ctx->xml, pps); 412 1.1 christos return -1; 413 1.1 christos } 414 1.1 christos 415 1.1 christos ret = download_cert(ctx, node, ca_fname); 416 1.1 christos xml_node_free(ctx->xml, pps); 417 1.1 christos 418 1.1 christos return ret; 419 1.1 christos } 420 1.1 christos 421 1.1 christos 422 1.1 christos static int cmd_dl_polupd_ca(struct hs20_osu_client *ctx, const char *pps_fname, 423 1.1 christos const char *ca_fname) 424 1.1 christos { 425 1.1 christos xml_node_t *pps, *node; 426 1.1 christos int ret; 427 1.1 christos 428 1.1 christos pps = node_from_file(ctx->xml, pps_fname); 429 1.1 christos if (pps == NULL) { 430 1.1 christos wpa_printf(MSG_INFO, "Could not read or parse '%s'", pps_fname); 431 1.1 christos return -1; 432 1.1 christos } 433 1.1 christos 434 1.1 christos node = get_child_node(ctx->xml, pps, 435 1.1.1.2 christos "Policy/PolicyUpdate/TrustRoot"); 436 1.1 christos if (node == NULL) { 437 1.1.1.2 christos wpa_printf(MSG_INFO, "No Policy/PolicyUpdate/TrustRoot/CertURL found from PPS"); 438 1.1 christos xml_node_free(ctx->xml, pps); 439 1.1.1.4 christos return -2; 440 1.1 christos } 441 1.1 christos 442 1.1 christos ret = download_cert(ctx, node, ca_fname); 443 1.1 christos xml_node_free(ctx->xml, pps); 444 1.1 christos 445 1.1 christos return ret; 446 1.1 christos } 447 1.1 christos 448 1.1 christos 449 1.1 christos static int cmd_dl_aaa_ca(struct hs20_osu_client *ctx, const char *pps_fname, 450 1.1 christos const char *ca_fname) 451 1.1 christos { 452 1.1 christos xml_node_t *pps, *node, *aaa; 453 1.1 christos int ret; 454 1.1 christos 455 1.1 christos pps = node_from_file(ctx->xml, pps_fname); 456 1.1 christos if (pps == NULL) { 457 1.1 christos wpa_printf(MSG_INFO, "Could not read or parse '%s'", pps_fname); 458 1.1 christos return -1; 459 1.1 christos } 460 1.1 christos 461 1.1 christos node = get_child_node(ctx->xml, pps, 462 1.1 christos "AAAServerTrustRoot"); 463 1.1 christos if (node == NULL) { 464 1.1 christos wpa_printf(MSG_INFO, "No AAAServerTrustRoot/CertURL found from PPS"); 465 1.1 christos xml_node_free(ctx->xml, pps); 466 1.1.1.4 christos return -2; 467 1.1 christos } 468 1.1 christos 469 1.1 christos aaa = xml_node_first_child(ctx->xml, node); 470 1.1 christos if (aaa == NULL) { 471 1.1 christos wpa_printf(MSG_INFO, "No AAAServerTrustRoot/CertURL found from PPS"); 472 1.1 christos xml_node_free(ctx->xml, pps); 473 1.1 christos return -1; 474 1.1 christos } 475 1.1 christos 476 1.1 christos ret = download_cert(ctx, aaa, ca_fname); 477 1.1 christos xml_node_free(ctx->xml, pps); 478 1.1 christos 479 1.1 christos return ret; 480 1.1 christos } 481 1.1 christos 482 1.1 christos 483 1.1 christos static int download_trust_roots(struct hs20_osu_client *ctx, 484 1.1 christos const char *pps_fname) 485 1.1 christos { 486 1.1 christos char *dir, *pos; 487 1.1 christos char fname[300]; 488 1.1.1.4 christos int ret, ret1; 489 1.1 christos 490 1.1 christos dir = os_strdup(pps_fname); 491 1.1 christos if (dir == NULL) 492 1.1 christos return -1; 493 1.1 christos pos = os_strrchr(dir, '/'); 494 1.1 christos if (pos == NULL) { 495 1.1 christos os_free(dir); 496 1.1 christos return -1; 497 1.1 christos } 498 1.1 christos *pos = '\0'; 499 1.1 christos 500 1.1 christos snprintf(fname, sizeof(fname), "%s/ca.pem", dir); 501 1.1 christos ret = cmd_dl_osu_ca(ctx, pps_fname, fname); 502 1.1 christos snprintf(fname, sizeof(fname), "%s/polupd-ca.pem", dir); 503 1.1.1.4 christos ret1 = cmd_dl_polupd_ca(ctx, pps_fname, fname); 504 1.1.1.4 christos if (ret == 0 && ret1 == -1) 505 1.1.1.4 christos ret = -1; 506 1.1 christos snprintf(fname, sizeof(fname), "%s/aaa-ca.pem", dir); 507 1.1.1.4 christos ret1 = cmd_dl_aaa_ca(ctx, pps_fname, fname); 508 1.1.1.4 christos if (ret == 0 && ret1 == -1) 509 1.1.1.4 christos ret = -1; 510 1.1 christos 511 1.1 christos os_free(dir); 512 1.1 christos 513 1.1 christos return ret; 514 1.1 christos } 515 1.1 christos 516 1.1 christos 517 1.1 christos static int server_dnsname_suffix_match(struct hs20_osu_client *ctx, 518 1.1 christos const char *fqdn) 519 1.1 christos { 520 1.1 christos size_t match_len, len, i; 521 1.1 christos const char *val; 522 1.1 christos 523 1.1 christos match_len = os_strlen(fqdn); 524 1.1 christos 525 1.1 christos for (i = 0; i < ctx->server_dnsname_count; i++) { 526 1.1 christos wpa_printf(MSG_INFO, 527 1.1 christos "Checking suffix match against server dNSName %s", 528 1.1 christos ctx->server_dnsname[i]); 529 1.1 christos val = ctx->server_dnsname[i]; 530 1.1 christos len = os_strlen(val); 531 1.1 christos 532 1.1 christos if (match_len > len) 533 1.1 christos continue; 534 1.1 christos 535 1.1 christos if (os_strncasecmp(val + len - match_len, fqdn, match_len) != 0) 536 1.1 christos continue; /* no match */ 537 1.1 christos 538 1.1 christos if (match_len == len) 539 1.1 christos return 1; /* exact match */ 540 1.1 christos 541 1.1 christos if (val[len - match_len - 1] == '.') 542 1.1 christos return 1; /* full label match completes suffix match */ 543 1.1 christos 544 1.1 christos /* Reject due to incomplete label match */ 545 1.1 christos } 546 1.1 christos 547 1.1 christos /* None of the dNSName(s) matched */ 548 1.1 christos return 0; 549 1.1 christos } 550 1.1 christos 551 1.1 christos 552 1.1 christos int hs20_add_pps_mo(struct hs20_osu_client *ctx, const char *uri, 553 1.1 christos xml_node_t *add_mo, char *fname, size_t fname_len) 554 1.1 christos { 555 1.1 christos char *str; 556 1.1 christos char *fqdn, *pos; 557 1.1 christos xml_node_t *tnds, *mo, *cert; 558 1.1 christos const char *name; 559 1.1 christos int ret; 560 1.1 christos 561 1.1 christos if (strncmp(uri, "./Wi-Fi/", 8) != 0) { 562 1.1 christos wpa_printf(MSG_INFO, "Unsupported location for addMO to add PPS MO: '%s'", 563 1.1 christos uri); 564 1.1 christos write_result(ctx, "Unsupported location for addMO to add PPS MO: '%s'", 565 1.1 christos uri); 566 1.1 christos return -1; 567 1.1 christos } 568 1.1 christos 569 1.1 christos fqdn = strdup(uri + 8); 570 1.1 christos if (fqdn == NULL) 571 1.1 christos return -1; 572 1.1 christos pos = strchr(fqdn, '/'); 573 1.1 christos if (pos) { 574 1.1 christos if (os_strcasecmp(pos, "/PerProviderSubscription") != 0) { 575 1.1 christos wpa_printf(MSG_INFO, "Unsupported location for addMO to add PPS MO (extra directory): '%s'", 576 1.1 christos uri); 577 1.1 christos write_result(ctx, "Unsupported location for addMO to " 578 1.1 christos "add PPS MO (extra directory): '%s'", uri); 579 1.1.1.3 christos free(fqdn); 580 1.1 christos return -1; 581 1.1 christos } 582 1.1 christos *pos = '\0'; /* remove trailing slash and PPS node name */ 583 1.1 christos } 584 1.1 christos wpa_printf(MSG_INFO, "SP FQDN: %s", fqdn); 585 1.1 christos 586 1.1 christos if (!server_dnsname_suffix_match(ctx, fqdn)) { 587 1.1.1.3 christos wpa_printf(MSG_INFO, 588 1.1.1.3 christos "FQDN '%s' for new PPS MO did not have suffix match with server's dNSName values, count: %d", 589 1.1.1.3 christos fqdn, (int) ctx->server_dnsname_count); 590 1.1 christos write_result(ctx, "FQDN '%s' for new PPS MO did not have suffix match with server's dNSName values", 591 1.1 christos fqdn); 592 1.1 christos free(fqdn); 593 1.1 christos return -1; 594 1.1 christos } 595 1.1 christos 596 1.1 christos if (!valid_fqdn(fqdn)) { 597 1.1 christos wpa_printf(MSG_INFO, "Invalid FQDN '%s'", fqdn); 598 1.1 christos write_result(ctx, "Invalid FQDN '%s'", fqdn); 599 1.1 christos free(fqdn); 600 1.1 christos return -1; 601 1.1 christos } 602 1.1 christos 603 1.1 christos mkdir("SP", S_IRWXU); 604 1.1 christos snprintf(fname, fname_len, "SP/%s", fqdn); 605 1.1 christos if (mkdir(fname, S_IRWXU) < 0) { 606 1.1 christos if (errno != EEXIST) { 607 1.1 christos int err = errno; 608 1.1 christos wpa_printf(MSG_INFO, "mkdir(%s) failed: %s", 609 1.1 christos fname, strerror(err)); 610 1.1 christos free(fqdn); 611 1.1 christos return -1; 612 1.1 christos } 613 1.1 christos } 614 1.1 christos 615 1.1.1.5 christos android_update_permission("SP", S_IRWXU | S_IRWXG); 616 1.1.1.5 christos android_update_permission(fname, S_IRWXU | S_IRWXG); 617 1.1.1.2 christos 618 1.1 christos snprintf(fname, fname_len, "SP/%s/pps.xml", fqdn); 619 1.1 christos 620 1.1 christos if (os_file_exists(fname)) { 621 1.1 christos wpa_printf(MSG_INFO, "PPS file '%s' exists - reject addMO", 622 1.1 christos fname); 623 1.1 christos write_result(ctx, "PPS file '%s' exists - reject addMO", 624 1.1 christos fname); 625 1.1 christos free(fqdn); 626 1.1 christos return -2; 627 1.1 christos } 628 1.1 christos wpa_printf(MSG_INFO, "Using PPS file: %s", fname); 629 1.1 christos 630 1.1 christos str = xml_node_get_text(ctx->xml, add_mo); 631 1.1 christos if (str == NULL) { 632 1.1 christos wpa_printf(MSG_INFO, "Could not extract MO text"); 633 1.1 christos free(fqdn); 634 1.1 christos return -1; 635 1.1 christos } 636 1.1 christos wpa_printf(MSG_DEBUG, "[hs20] addMO text: '%s'", str); 637 1.1 christos 638 1.1 christos tnds = xml_node_from_buf(ctx->xml, str); 639 1.1 christos xml_node_get_text_free(ctx->xml, str); 640 1.1 christos if (tnds == NULL) { 641 1.1 christos wpa_printf(MSG_INFO, "[hs20] Could not parse addMO text"); 642 1.1 christos free(fqdn); 643 1.1 christos return -1; 644 1.1 christos } 645 1.1 christos 646 1.1 christos mo = tnds_to_mo(ctx->xml, tnds); 647 1.1 christos if (mo == NULL) { 648 1.1 christos wpa_printf(MSG_INFO, "[hs20] Could not parse addMO TNDS text"); 649 1.1 christos free(fqdn); 650 1.1 christos return -1; 651 1.1 christos } 652 1.1 christos 653 1.1 christos debug_dump_node(ctx, "Parsed TNDS", mo); 654 1.1 christos 655 1.1 christos name = xml_node_get_localname(ctx->xml, mo); 656 1.1 christos if (os_strcasecmp(name, "PerProviderSubscription") != 0) { 657 1.1 christos wpa_printf(MSG_INFO, "[hs20] Unexpected PPS MO root node name '%s'", 658 1.1 christos name); 659 1.1 christos free(fqdn); 660 1.1 christos return -1; 661 1.1 christos } 662 1.1 christos 663 1.1 christos cert = get_child_node(ctx->xml, mo, 664 1.1 christos "Credential/DigitalCertificate/" 665 1.1 christos "CertSHA256Fingerprint"); 666 1.1 christos if (cert && process_est_cert(ctx, cert, fqdn) < 0) { 667 1.1 christos xml_node_free(ctx->xml, mo); 668 1.1 christos free(fqdn); 669 1.1 christos return -1; 670 1.1 christos } 671 1.1 christos free(fqdn); 672 1.1 christos 673 1.1 christos if (node_to_file(ctx->xml, fname, mo) < 0) { 674 1.1 christos wpa_printf(MSG_INFO, "Could not write MO to file"); 675 1.1 christos xml_node_free(ctx->xml, mo); 676 1.1 christos return -1; 677 1.1 christos } 678 1.1 christos xml_node_free(ctx->xml, mo); 679 1.1 christos 680 1.1 christos wpa_printf(MSG_INFO, "A new PPS MO added as '%s'", fname); 681 1.1 christos write_summary(ctx, "A new PPS MO added as '%s'", fname); 682 1.1 christos 683 1.1 christos ret = download_trust_roots(ctx, fname); 684 1.1 christos if (ret < 0) { 685 1.1 christos wpa_printf(MSG_INFO, "Remove invalid PPS MO file"); 686 1.1 christos write_summary(ctx, "Remove invalid PPS MO file"); 687 1.1 christos unlink(fname); 688 1.1 christos } 689 1.1 christos 690 1.1 christos return ret; 691 1.1 christos } 692 1.1 christos 693 1.1 christos 694 1.1 christos int update_pps_file(struct hs20_osu_client *ctx, const char *pps_fname, 695 1.1 christos xml_node_t *pps) 696 1.1 christos { 697 1.1 christos char *str; 698 1.1 christos FILE *f; 699 1.1 christos char backup[300]; 700 1.1 christos 701 1.1 christos if (ctx->client_cert_present) { 702 1.1 christos xml_node_t *cert; 703 1.1 christos cert = get_child_node(ctx->xml, pps, 704 1.1 christos "Credential/DigitalCertificate/" 705 1.1 christos "CertSHA256Fingerprint"); 706 1.1 christos if (cert && os_file_exists("Cert/est_cert.der") && 707 1.1 christos process_est_cert(ctx, cert, ctx->fqdn) < 0) { 708 1.1 christos wpa_printf(MSG_INFO, "EST certificate update processing failed on PPS MO update"); 709 1.1 christos return -1; 710 1.1 christos } 711 1.1 christos } 712 1.1 christos 713 1.1 christos wpa_printf(MSG_INFO, "Updating PPS MO %s", pps_fname); 714 1.1 christos 715 1.1 christos str = xml_node_to_str(ctx->xml, pps); 716 1.1.1.2 christos if (str == NULL) { 717 1.1.1.2 christos wpa_printf(MSG_ERROR, "No node found"); 718 1.1.1.2 christos return -1; 719 1.1.1.2 christos } 720 1.1 christos wpa_printf(MSG_MSGDUMP, "[hs20] Updated PPS: '%s'", str); 721 1.1 christos 722 1.1 christos snprintf(backup, sizeof(backup), "%s.bak", pps_fname); 723 1.1 christos rename(pps_fname, backup); 724 1.1 christos f = fopen(pps_fname, "w"); 725 1.1 christos if (f == NULL) { 726 1.1 christos wpa_printf(MSG_INFO, "Could not write PPS"); 727 1.1 christos rename(backup, pps_fname); 728 1.1 christos free(str); 729 1.1 christos return -1; 730 1.1 christos } 731 1.1 christos fprintf(f, "%s\n", str); 732 1.1 christos fclose(f); 733 1.1 christos 734 1.1 christos free(str); 735 1.1 christos 736 1.1 christos return 0; 737 1.1 christos } 738 1.1 christos 739 1.1 christos 740 1.1 christos void get_user_pw(struct hs20_osu_client *ctx, xml_node_t *pps, 741 1.1 christos const char *alt_loc, char **user, char **pw) 742 1.1 christos { 743 1.1 christos xml_node_t *node; 744 1.1 christos 745 1.1 christos node = get_child_node(ctx->xml, pps, 746 1.1 christos "Credential/UsernamePassword/Username"); 747 1.1 christos if (node) 748 1.1 christos *user = xml_node_get_text(ctx->xml, node); 749 1.1 christos 750 1.1 christos node = get_child_node(ctx->xml, pps, 751 1.1 christos "Credential/UsernamePassword/Password"); 752 1.1 christos if (node) 753 1.1 christos *pw = xml_node_get_base64_text(ctx->xml, node, NULL); 754 1.1 christos 755 1.1 christos node = get_child_node(ctx->xml, pps, alt_loc); 756 1.1 christos if (node) { 757 1.1 christos xml_node_t *a; 758 1.1 christos a = get_node(ctx->xml, node, "Username"); 759 1.1 christos if (a) { 760 1.1 christos xml_node_get_text_free(ctx->xml, *user); 761 1.1 christos *user = xml_node_get_text(ctx->xml, a); 762 1.1 christos wpa_printf(MSG_INFO, "Use OSU username '%s'", *user); 763 1.1 christos } 764 1.1 christos 765 1.1 christos a = get_node(ctx->xml, node, "Password"); 766 1.1 christos if (a) { 767 1.1 christos free(*pw); 768 1.1 christos *pw = xml_node_get_base64_text(ctx->xml, a, NULL); 769 1.1 christos wpa_printf(MSG_INFO, "Use OSU password"); 770 1.1 christos } 771 1.1 christos } 772 1.1 christos } 773 1.1 christos 774 1.1 christos 775 1.1 christos /* Remove old credentials based on HomeSP/FQDN */ 776 1.1 christos static void remove_sp_creds(struct hs20_osu_client *ctx, const char *fqdn) 777 1.1 christos { 778 1.1 christos char cmd[300]; 779 1.1 christos os_snprintf(cmd, sizeof(cmd), "REMOVE_CRED provisioning_sp=%s", fqdn); 780 1.1 christos if (wpa_command(ctx->ifname, cmd) < 0) 781 1.1 christos wpa_printf(MSG_INFO, "Failed to remove old credential(s)"); 782 1.1 christos } 783 1.1 christos 784 1.1 christos 785 1.1 christos static void set_pps_cred_policy_spe(struct hs20_osu_client *ctx, int id, 786 1.1 christos xml_node_t *spe) 787 1.1 christos { 788 1.1 christos xml_node_t *ssid; 789 1.1 christos char *txt; 790 1.1 christos 791 1.1 christos ssid = get_node(ctx->xml, spe, "SSID"); 792 1.1 christos if (ssid == NULL) 793 1.1 christos return; 794 1.1 christos txt = xml_node_get_text(ctx->xml, ssid); 795 1.1 christos if (txt == NULL) 796 1.1 christos return; 797 1.1 christos wpa_printf(MSG_DEBUG, "- Policy/SPExclusionList/<X+>/SSID = %s", txt); 798 1.1 christos if (set_cred_quoted(ctx->ifname, id, "excluded_ssid", txt) < 0) 799 1.1 christos wpa_printf(MSG_INFO, "Failed to set cred excluded_ssid"); 800 1.1 christos xml_node_get_text_free(ctx->xml, txt); 801 1.1 christos } 802 1.1 christos 803 1.1 christos 804 1.1 christos static void set_pps_cred_policy_spel(struct hs20_osu_client *ctx, int id, 805 1.1 christos xml_node_t *spel) 806 1.1 christos { 807 1.1 christos xml_node_t *child; 808 1.1 christos 809 1.1 christos xml_node_for_each_child(ctx->xml, child, spel) { 810 1.1 christos xml_node_for_each_check(ctx->xml, child); 811 1.1 christos set_pps_cred_policy_spe(ctx, id, child); 812 1.1 christos } 813 1.1 christos } 814 1.1 christos 815 1.1 christos 816 1.1 christos static void set_pps_cred_policy_prp(struct hs20_osu_client *ctx, int id, 817 1.1 christos xml_node_t *prp) 818 1.1 christos { 819 1.1 christos xml_node_t *node; 820 1.1 christos char *txt = NULL, *pos; 821 1.1 christos char *prio, *country_buf = NULL; 822 1.1 christos const char *country; 823 1.1 christos char val[200]; 824 1.1 christos int priority; 825 1.1 christos 826 1.1 christos node = get_node(ctx->xml, prp, "Priority"); 827 1.1 christos if (node == NULL) 828 1.1 christos return; 829 1.1 christos prio = xml_node_get_text(ctx->xml, node); 830 1.1 christos if (prio == NULL) 831 1.1 christos return; 832 1.1 christos wpa_printf(MSG_INFO, "- Policy/PreferredRoamingPartnerList/<X+>/Priority = %s", 833 1.1 christos prio); 834 1.1 christos priority = atoi(prio); 835 1.1 christos xml_node_get_text_free(ctx->xml, prio); 836 1.1 christos 837 1.1 christos node = get_node(ctx->xml, prp, "Country"); 838 1.1 christos if (node) { 839 1.1 christos country_buf = xml_node_get_text(ctx->xml, node); 840 1.1 christos if (country_buf == NULL) 841 1.1 christos return; 842 1.1 christos country = country_buf; 843 1.1 christos wpa_printf(MSG_INFO, "- Policy/PreferredRoamingPartnerList/<X+>/Country = %s", 844 1.1 christos country); 845 1.1 christos } else { 846 1.1 christos country = "*"; 847 1.1 christos } 848 1.1 christos 849 1.1 christos node = get_node(ctx->xml, prp, "FQDN_Match"); 850 1.1 christos if (node == NULL) 851 1.1 christos goto out; 852 1.1 christos txt = xml_node_get_text(ctx->xml, node); 853 1.1 christos if (txt == NULL) 854 1.1 christos goto out; 855 1.1 christos wpa_printf(MSG_INFO, "- Policy/PreferredRoamingPartnerList/<X+>/FQDN_Match = %s", 856 1.1 christos txt); 857 1.1 christos pos = strrchr(txt, ','); 858 1.1 christos if (pos == NULL) 859 1.1 christos goto out; 860 1.1 christos *pos++ = '\0'; 861 1.1 christos 862 1.1 christos snprintf(val, sizeof(val), "%s,%d,%d,%s", txt, 863 1.1 christos strcmp(pos, "includeSubdomains") != 0, priority, country); 864 1.1 christos if (set_cred_quoted(ctx->ifname, id, "roaming_partner", val) < 0) 865 1.1 christos wpa_printf(MSG_INFO, "Failed to set cred roaming_partner"); 866 1.1 christos out: 867 1.1 christos xml_node_get_text_free(ctx->xml, country_buf); 868 1.1 christos xml_node_get_text_free(ctx->xml, txt); 869 1.1 christos } 870 1.1 christos 871 1.1 christos 872 1.1 christos static void set_pps_cred_policy_prpl(struct hs20_osu_client *ctx, int id, 873 1.1 christos xml_node_t *prpl) 874 1.1 christos { 875 1.1 christos xml_node_t *child; 876 1.1 christos 877 1.1 christos xml_node_for_each_child(ctx->xml, child, prpl) { 878 1.1 christos xml_node_for_each_check(ctx->xml, child); 879 1.1 christos set_pps_cred_policy_prp(ctx, id, child); 880 1.1 christos } 881 1.1 christos } 882 1.1 christos 883 1.1 christos 884 1.1 christos static void set_pps_cred_policy_min_backhaul(struct hs20_osu_client *ctx, int id, 885 1.1 christos xml_node_t *min_backhaul) 886 1.1 christos { 887 1.1 christos xml_node_t *node; 888 1.1 christos char *type, *dl = NULL, *ul = NULL; 889 1.1 christos int home; 890 1.1 christos 891 1.1 christos node = get_node(ctx->xml, min_backhaul, "NetworkType"); 892 1.1 christos if (node == NULL) { 893 1.1 christos wpa_printf(MSG_INFO, "Ignore MinBackhaulThreshold without mandatory NetworkType node"); 894 1.1 christos return; 895 1.1 christos } 896 1.1 christos 897 1.1 christos type = xml_node_get_text(ctx->xml, node); 898 1.1 christos if (type == NULL) 899 1.1 christos return; 900 1.1 christos wpa_printf(MSG_INFO, "- Policy/MinBackhaulThreshold/<X+>/NetworkType = %s", 901 1.1 christos type); 902 1.1 christos if (os_strcasecmp(type, "home") == 0) 903 1.1 christos home = 1; 904 1.1 christos else if (os_strcasecmp(type, "roaming") == 0) 905 1.1 christos home = 0; 906 1.1 christos else { 907 1.1 christos wpa_printf(MSG_INFO, "Ignore MinBackhaulThreshold with invalid NetworkType"); 908 1.1 christos xml_node_get_text_free(ctx->xml, type); 909 1.1 christos return; 910 1.1 christos } 911 1.1 christos xml_node_get_text_free(ctx->xml, type); 912 1.1 christos 913 1.1 christos node = get_node(ctx->xml, min_backhaul, "DLBandwidth"); 914 1.1 christos if (node) 915 1.1 christos dl = xml_node_get_text(ctx->xml, node); 916 1.1 christos 917 1.1 christos node = get_node(ctx->xml, min_backhaul, "ULBandwidth"); 918 1.1 christos if (node) 919 1.1 christos ul = xml_node_get_text(ctx->xml, node); 920 1.1 christos 921 1.1 christos if (dl == NULL && ul == NULL) { 922 1.1 christos wpa_printf(MSG_INFO, "Ignore MinBackhaulThreshold without either DLBandwidth or ULBandwidth nodes"); 923 1.1 christos return; 924 1.1 christos } 925 1.1 christos 926 1.1 christos if (dl) 927 1.1 christos wpa_printf(MSG_INFO, "- Policy/MinBackhaulThreshold/<X+>/DLBandwidth = %s", 928 1.1 christos dl); 929 1.1 christos if (ul) 930 1.1 christos wpa_printf(MSG_INFO, "- Policy/MinBackhaulThreshold/<X+>/ULBandwidth = %s", 931 1.1 christos ul); 932 1.1 christos 933 1.1 christos if (home) { 934 1.1 christos if (dl && 935 1.1 christos set_cred(ctx->ifname, id, "min_dl_bandwidth_home", dl) < 0) 936 1.1 christos wpa_printf(MSG_INFO, "Failed to set cred bandwidth limit"); 937 1.1 christos if (ul && 938 1.1 christos set_cred(ctx->ifname, id, "min_ul_bandwidth_home", ul) < 0) 939 1.1 christos wpa_printf(MSG_INFO, "Failed to set cred bandwidth limit"); 940 1.1 christos } else { 941 1.1 christos if (dl && 942 1.1 christos set_cred(ctx->ifname, id, "min_dl_bandwidth_roaming", dl) < 943 1.1 christos 0) 944 1.1 christos wpa_printf(MSG_INFO, "Failed to set cred bandwidth limit"); 945 1.1 christos if (ul && 946 1.1 christos set_cred(ctx->ifname, id, "min_ul_bandwidth_roaming", ul) < 947 1.1 christos 0) 948 1.1 christos wpa_printf(MSG_INFO, "Failed to set cred bandwidth limit"); 949 1.1 christos } 950 1.1 christos 951 1.1 christos xml_node_get_text_free(ctx->xml, dl); 952 1.1 christos xml_node_get_text_free(ctx->xml, ul); 953 1.1 christos } 954 1.1 christos 955 1.1 christos 956 1.1 christos static void set_pps_cred_policy_min_backhaul_list(struct hs20_osu_client *ctx, 957 1.1 christos int id, xml_node_t *node) 958 1.1 christos { 959 1.1 christos xml_node_t *child; 960 1.1 christos 961 1.1 christos wpa_printf(MSG_INFO, "- Policy/MinBackhaulThreshold"); 962 1.1 christos 963 1.1 christos xml_node_for_each_child(ctx->xml, child, node) { 964 1.1 christos xml_node_for_each_check(ctx->xml, child); 965 1.1 christos set_pps_cred_policy_min_backhaul(ctx, id, child); 966 1.1 christos } 967 1.1 christos } 968 1.1 christos 969 1.1 christos 970 1.1 christos static void set_pps_cred_policy_update(struct hs20_osu_client *ctx, int id, 971 1.1 christos xml_node_t *node) 972 1.1 christos { 973 1.1 christos wpa_printf(MSG_INFO, "- Policy/PolicyUpdate"); 974 1.1 christos /* Not used in wpa_supplicant */ 975 1.1 christos } 976 1.1 christos 977 1.1 christos 978 1.1 christos static void set_pps_cred_policy_required_proto_port(struct hs20_osu_client *ctx, 979 1.1 christos int id, xml_node_t *tuple) 980 1.1 christos { 981 1.1 christos xml_node_t *node; 982 1.1 christos char *proto, *port; 983 1.1 christos char *buf; 984 1.1 christos size_t buflen; 985 1.1 christos 986 1.1 christos node = get_node(ctx->xml, tuple, "IPProtocol"); 987 1.1 christos if (node == NULL) { 988 1.1 christos wpa_printf(MSG_INFO, "Ignore RequiredProtoPortTuple without mandatory IPProtocol node"); 989 1.1 christos return; 990 1.1 christos } 991 1.1 christos 992 1.1 christos proto = xml_node_get_text(ctx->xml, node); 993 1.1 christos if (proto == NULL) 994 1.1 christos return; 995 1.1 christos 996 1.1 christos wpa_printf(MSG_INFO, "- Policy/RequiredProtoPortTuple/<X+>/IPProtocol = %s", 997 1.1 christos proto); 998 1.1 christos 999 1.1 christos node = get_node(ctx->xml, tuple, "PortNumber"); 1000 1.1 christos port = node ? xml_node_get_text(ctx->xml, node) : NULL; 1001 1.1 christos if (port) { 1002 1.1 christos wpa_printf(MSG_INFO, "- Policy/RequiredProtoPortTuple/<X+>/PortNumber = %s", 1003 1.1 christos port); 1004 1.1 christos buflen = os_strlen(proto) + os_strlen(port) + 10; 1005 1.1 christos buf = os_malloc(buflen); 1006 1.1 christos if (buf) 1007 1.1 christos os_snprintf(buf, buflen, "%s:%s", proto, port); 1008 1.1 christos xml_node_get_text_free(ctx->xml, port); 1009 1.1 christos } else { 1010 1.1 christos buflen = os_strlen(proto) + 10; 1011 1.1 christos buf = os_malloc(buflen); 1012 1.1 christos if (buf) 1013 1.1 christos os_snprintf(buf, buflen, "%s", proto); 1014 1.1 christos } 1015 1.1 christos 1016 1.1 christos xml_node_get_text_free(ctx->xml, proto); 1017 1.1 christos 1018 1.1 christos if (buf == NULL) 1019 1.1 christos return; 1020 1.1 christos 1021 1.1 christos if (set_cred(ctx->ifname, id, "req_conn_capab", buf) < 0) 1022 1.1 christos wpa_printf(MSG_INFO, "Could not set req_conn_capab"); 1023 1.1 christos 1024 1.1 christos os_free(buf); 1025 1.1 christos } 1026 1.1 christos 1027 1.1 christos 1028 1.1 christos static void set_pps_cred_policy_required_proto_ports(struct hs20_osu_client *ctx, 1029 1.1 christos int id, xml_node_t *node) 1030 1.1 christos { 1031 1.1 christos xml_node_t *child; 1032 1.1 christos 1033 1.1 christos wpa_printf(MSG_INFO, "- Policy/RequiredProtoPortTuple"); 1034 1.1 christos 1035 1.1 christos xml_node_for_each_child(ctx->xml, child, node) { 1036 1.1 christos xml_node_for_each_check(ctx->xml, child); 1037 1.1 christos set_pps_cred_policy_required_proto_port(ctx, id, child); 1038 1.1 christos } 1039 1.1 christos } 1040 1.1 christos 1041 1.1 christos 1042 1.1 christos static void set_pps_cred_policy_max_bss_load(struct hs20_osu_client *ctx, int id, 1043 1.1 christos xml_node_t *node) 1044 1.1 christos { 1045 1.1 christos char *str = xml_node_get_text(ctx->xml, node); 1046 1.1 christos if (str == NULL) 1047 1.1 christos return; 1048 1.1 christos wpa_printf(MSG_INFO, "- Policy/MaximumBSSLoadValue - %s", str); 1049 1.1 christos if (set_cred(ctx->ifname, id, "max_bss_load", str) < 0) 1050 1.1 christos wpa_printf(MSG_INFO, "Failed to set cred max_bss_load limit"); 1051 1.1 christos xml_node_get_text_free(ctx->xml, str); 1052 1.1 christos } 1053 1.1 christos 1054 1.1 christos 1055 1.1 christos static void set_pps_cred_policy(struct hs20_osu_client *ctx, int id, 1056 1.1 christos xml_node_t *node) 1057 1.1 christos { 1058 1.1 christos xml_node_t *child; 1059 1.1 christos const char *name; 1060 1.1 christos 1061 1.1 christos wpa_printf(MSG_INFO, "- Policy"); 1062 1.1 christos 1063 1.1 christos xml_node_for_each_child(ctx->xml, child, node) { 1064 1.1 christos xml_node_for_each_check(ctx->xml, child); 1065 1.1 christos name = xml_node_get_localname(ctx->xml, child); 1066 1.1 christos if (os_strcasecmp(name, "PreferredRoamingPartnerList") == 0) 1067 1.1 christos set_pps_cred_policy_prpl(ctx, id, child); 1068 1.1 christos else if (os_strcasecmp(name, "MinBackhaulThreshold") == 0) 1069 1.1 christos set_pps_cred_policy_min_backhaul_list(ctx, id, child); 1070 1.1 christos else if (os_strcasecmp(name, "PolicyUpdate") == 0) 1071 1.1 christos set_pps_cred_policy_update(ctx, id, child); 1072 1.1 christos else if (os_strcasecmp(name, "SPExclusionList") == 0) 1073 1.1 christos set_pps_cred_policy_spel(ctx, id, child); 1074 1.1 christos else if (os_strcasecmp(name, "RequiredProtoPortTuple") == 0) 1075 1.1 christos set_pps_cred_policy_required_proto_ports(ctx, id, child); 1076 1.1 christos else if (os_strcasecmp(name, "MaximumBSSLoadValue") == 0) 1077 1.1 christos set_pps_cred_policy_max_bss_load(ctx, id, child); 1078 1.1 christos else 1079 1.1 christos wpa_printf(MSG_INFO, "Unknown Policy node '%s'", name); 1080 1.1 christos } 1081 1.1 christos } 1082 1.1 christos 1083 1.1 christos 1084 1.1 christos static void set_pps_cred_priority(struct hs20_osu_client *ctx, int id, 1085 1.1 christos xml_node_t *node) 1086 1.1 christos { 1087 1.1 christos char *str = xml_node_get_text(ctx->xml, node); 1088 1.1 christos if (str == NULL) 1089 1.1 christos return; 1090 1.1 christos wpa_printf(MSG_INFO, "- CredentialPriority = %s", str); 1091 1.1 christos if (set_cred(ctx->ifname, id, "sp_priority", str) < 0) 1092 1.1 christos wpa_printf(MSG_INFO, "Failed to set cred sp_priority"); 1093 1.1 christos xml_node_get_text_free(ctx->xml, str); 1094 1.1 christos } 1095 1.1 christos 1096 1.1 christos 1097 1.1 christos static void set_pps_cred_aaa_server_trust_root(struct hs20_osu_client *ctx, 1098 1.1 christos int id, xml_node_t *node) 1099 1.1 christos { 1100 1.1 christos wpa_printf(MSG_INFO, "- AAAServerTrustRoot - TODO"); 1101 1.1 christos } 1102 1.1 christos 1103 1.1 christos 1104 1.1 christos static void set_pps_cred_sub_update(struct hs20_osu_client *ctx, int id, 1105 1.1 christos xml_node_t *node) 1106 1.1 christos { 1107 1.1 christos wpa_printf(MSG_INFO, "- SubscriptionUpdate"); 1108 1.1 christos /* not used within wpa_supplicant */ 1109 1.1 christos } 1110 1.1 christos 1111 1.1 christos 1112 1.1 christos static void set_pps_cred_home_sp_network_id(struct hs20_osu_client *ctx, 1113 1.1 christos int id, xml_node_t *node) 1114 1.1 christos { 1115 1.1 christos xml_node_t *ssid_node, *hessid_node; 1116 1.1 christos char *ssid, *hessid; 1117 1.1 christos 1118 1.1 christos ssid_node = get_node(ctx->xml, node, "SSID"); 1119 1.1 christos if (ssid_node == NULL) { 1120 1.1 christos wpa_printf(MSG_INFO, "Ignore HomeSP/NetworkID without mandatory SSID node"); 1121 1.1 christos return; 1122 1.1 christos } 1123 1.1 christos 1124 1.1 christos hessid_node = get_node(ctx->xml, node, "HESSID"); 1125 1.1 christos 1126 1.1 christos ssid = xml_node_get_text(ctx->xml, ssid_node); 1127 1.1 christos if (ssid == NULL) 1128 1.1 christos return; 1129 1.1 christos hessid = hessid_node ? xml_node_get_text(ctx->xml, hessid_node) : NULL; 1130 1.1 christos 1131 1.1 christos wpa_printf(MSG_INFO, "- HomeSP/NetworkID/<X+>/SSID = %s", ssid); 1132 1.1 christos if (hessid) 1133 1.1 christos wpa_printf(MSG_INFO, "- HomeSP/NetworkID/<X+>/HESSID = %s", 1134 1.1 christos hessid); 1135 1.1 christos 1136 1.1 christos /* TODO: Configure to wpa_supplicant */ 1137 1.1 christos 1138 1.1 christos xml_node_get_text_free(ctx->xml, ssid); 1139 1.1 christos xml_node_get_text_free(ctx->xml, hessid); 1140 1.1 christos } 1141 1.1 christos 1142 1.1 christos 1143 1.1 christos static void set_pps_cred_home_sp_network_ids(struct hs20_osu_client *ctx, 1144 1.1 christos int id, xml_node_t *node) 1145 1.1 christos { 1146 1.1 christos xml_node_t *child; 1147 1.1 christos 1148 1.1 christos wpa_printf(MSG_INFO, "- HomeSP/NetworkID"); 1149 1.1 christos 1150 1.1 christos xml_node_for_each_child(ctx->xml, child, node) { 1151 1.1 christos xml_node_for_each_check(ctx->xml, child); 1152 1.1 christos set_pps_cred_home_sp_network_id(ctx, id, child); 1153 1.1 christos } 1154 1.1 christos } 1155 1.1 christos 1156 1.1 christos 1157 1.1 christos static void set_pps_cred_home_sp_friendly_name(struct hs20_osu_client *ctx, 1158 1.1 christos int id, xml_node_t *node) 1159 1.1 christos { 1160 1.1 christos char *str = xml_node_get_text(ctx->xml, node); 1161 1.1 christos if (str == NULL) 1162 1.1 christos return; 1163 1.1 christos wpa_printf(MSG_INFO, "- HomeSP/FriendlyName = %s", str); 1164 1.1 christos /* not used within wpa_supplicant(?) */ 1165 1.1 christos xml_node_get_text_free(ctx->xml, str); 1166 1.1 christos } 1167 1.1 christos 1168 1.1 christos 1169 1.1 christos static void set_pps_cred_home_sp_icon_url(struct hs20_osu_client *ctx, 1170 1.1 christos int id, xml_node_t *node) 1171 1.1 christos { 1172 1.1 christos char *str = xml_node_get_text(ctx->xml, node); 1173 1.1 christos if (str == NULL) 1174 1.1 christos return; 1175 1.1 christos wpa_printf(MSG_INFO, "- HomeSP/IconURL = %s", str); 1176 1.1 christos /* not used within wpa_supplicant */ 1177 1.1 christos xml_node_get_text_free(ctx->xml, str); 1178 1.1 christos } 1179 1.1 christos 1180 1.1 christos 1181 1.1 christos static void set_pps_cred_home_sp_fqdn(struct hs20_osu_client *ctx, int id, 1182 1.1 christos xml_node_t *node) 1183 1.1 christos { 1184 1.1 christos char *str = xml_node_get_text(ctx->xml, node); 1185 1.1 christos if (str == NULL) 1186 1.1 christos return; 1187 1.1 christos wpa_printf(MSG_INFO, "- HomeSP/FQDN = %s", str); 1188 1.1 christos if (set_cred_quoted(ctx->ifname, id, "domain", str) < 0) 1189 1.1 christos wpa_printf(MSG_INFO, "Failed to set cred domain"); 1190 1.1 christos if (set_cred_quoted(ctx->ifname, id, "domain_suffix_match", str) < 0) 1191 1.1 christos wpa_printf(MSG_INFO, "Failed to set cred domain_suffix_match"); 1192 1.1 christos xml_node_get_text_free(ctx->xml, str); 1193 1.1 christos } 1194 1.1 christos 1195 1.1 christos 1196 1.1 christos static void set_pps_cred_home_sp_oi(struct hs20_osu_client *ctx, int id, 1197 1.1 christos xml_node_t *node) 1198 1.1 christos { 1199 1.1 christos xml_node_t *child; 1200 1.1 christos const char *name; 1201 1.1 christos char *homeoi = NULL; 1202 1.1 christos int required = 0; 1203 1.1 christos char *str; 1204 1.1 christos 1205 1.1 christos xml_node_for_each_child(ctx->xml, child, node) { 1206 1.1 christos xml_node_for_each_check(ctx->xml, child); 1207 1.1 christos name = xml_node_get_localname(ctx->xml, child); 1208 1.1 christos if (strcasecmp(name, "HomeOI") == 0 && !homeoi) { 1209 1.1 christos homeoi = xml_node_get_text(ctx->xml, child); 1210 1.1 christos wpa_printf(MSG_INFO, "- HomeSP/HomeOIList/<X+>/HomeOI = %s", 1211 1.1 christos homeoi); 1212 1.1 christos } else if (strcasecmp(name, "HomeOIRequired") == 0) { 1213 1.1 christos str = xml_node_get_text(ctx->xml, child); 1214 1.1 christos wpa_printf(MSG_INFO, "- HomeSP/HomeOIList/<X+>/HomeOIRequired = '%s'", 1215 1.1 christos str); 1216 1.1 christos if (str == NULL) 1217 1.1 christos continue; 1218 1.1 christos required = strcasecmp(str, "true") == 0; 1219 1.1 christos xml_node_get_text_free(ctx->xml, str); 1220 1.1 christos } else 1221 1.1 christos wpa_printf(MSG_INFO, "Unknown HomeOIList node '%s'", 1222 1.1 christos name); 1223 1.1 christos } 1224 1.1 christos 1225 1.1 christos if (homeoi == NULL) { 1226 1.1 christos wpa_printf(MSG_INFO, "- HomeSP/HomeOIList/<X+> without HomeOI ignored"); 1227 1.1 christos return; 1228 1.1 christos } 1229 1.1 christos 1230 1.1 christos wpa_printf(MSG_INFO, "- HomeSP/HomeOIList/<X+> '%s' required=%d", 1231 1.1 christos homeoi, required); 1232 1.1 christos 1233 1.1 christos if (required) { 1234 1.1.1.6 christos if (set_cred_quoted(ctx->ifname, id, "required_home_ois", 1235 1.1.1.6 christos homeoi) < 0) 1236 1.1.1.6 christos wpa_printf(MSG_INFO, 1237 1.1.1.6 christos "Failed to set cred required_home_ois"); 1238 1.1 christos } else { 1239 1.1.1.6 christos if (set_cred_quoted(ctx->ifname, id, "home_ois", homeoi) < 0) 1240 1.1.1.6 christos wpa_printf(MSG_INFO, "Failed to set cred home_ois"); 1241 1.1 christos } 1242 1.1 christos 1243 1.1 christos xml_node_get_text_free(ctx->xml, homeoi); 1244 1.1 christos } 1245 1.1 christos 1246 1.1 christos 1247 1.1 christos static void set_pps_cred_home_sp_oi_list(struct hs20_osu_client *ctx, int id, 1248 1.1 christos xml_node_t *node) 1249 1.1 christos { 1250 1.1 christos xml_node_t *child; 1251 1.1 christos 1252 1.1 christos wpa_printf(MSG_INFO, "- HomeSP/HomeOIList"); 1253 1.1 christos 1254 1.1 christos xml_node_for_each_child(ctx->xml, child, node) { 1255 1.1 christos xml_node_for_each_check(ctx->xml, child); 1256 1.1 christos set_pps_cred_home_sp_oi(ctx, id, child); 1257 1.1 christos } 1258 1.1 christos } 1259 1.1 christos 1260 1.1 christos 1261 1.1 christos static void set_pps_cred_home_sp_other_partner(struct hs20_osu_client *ctx, 1262 1.1 christos int id, xml_node_t *node) 1263 1.1 christos { 1264 1.1 christos xml_node_t *child; 1265 1.1 christos const char *name; 1266 1.1 christos char *fqdn = NULL; 1267 1.1 christos 1268 1.1 christos xml_node_for_each_child(ctx->xml, child, node) { 1269 1.1 christos xml_node_for_each_check(ctx->xml, child); 1270 1.1 christos name = xml_node_get_localname(ctx->xml, child); 1271 1.1 christos if (os_strcasecmp(name, "FQDN") == 0 && !fqdn) { 1272 1.1 christos fqdn = xml_node_get_text(ctx->xml, child); 1273 1.1 christos wpa_printf(MSG_INFO, "- HomeSP/OtherHomePartners/<X+>/FQDN = %s", 1274 1.1 christos fqdn); 1275 1.1 christos } else 1276 1.1 christos wpa_printf(MSG_INFO, "Unknown OtherHomePartners node '%s'", 1277 1.1 christos name); 1278 1.1 christos } 1279 1.1 christos 1280 1.1 christos if (fqdn == NULL) { 1281 1.1 christos wpa_printf(MSG_INFO, "- HomeSP/OtherHomePartners/<X+> without FQDN ignored"); 1282 1.1 christos return; 1283 1.1 christos } 1284 1.1 christos 1285 1.1 christos if (set_cred_quoted(ctx->ifname, id, "domain", fqdn) < 0) 1286 1.1 christos wpa_printf(MSG_INFO, "Failed to set cred domain for OtherHomePartners node"); 1287 1.1 christos 1288 1.1 christos xml_node_get_text_free(ctx->xml, fqdn); 1289 1.1 christos } 1290 1.1 christos 1291 1.1 christos 1292 1.1 christos static void set_pps_cred_home_sp_other_partners(struct hs20_osu_client *ctx, 1293 1.1 christos int id, 1294 1.1 christos xml_node_t *node) 1295 1.1 christos { 1296 1.1 christos xml_node_t *child; 1297 1.1 christos 1298 1.1 christos wpa_printf(MSG_INFO, "- HomeSP/OtherHomePartners"); 1299 1.1 christos 1300 1.1 christos xml_node_for_each_child(ctx->xml, child, node) { 1301 1.1 christos xml_node_for_each_check(ctx->xml, child); 1302 1.1 christos set_pps_cred_home_sp_other_partner(ctx, id, child); 1303 1.1 christos } 1304 1.1 christos } 1305 1.1 christos 1306 1.1 christos 1307 1.1 christos static void set_pps_cred_home_sp_roaming_consortium_oi( 1308 1.1 christos struct hs20_osu_client *ctx, int id, xml_node_t *node) 1309 1.1 christos { 1310 1.1 christos char *str = xml_node_get_text(ctx->xml, node); 1311 1.1 christos if (str == NULL) 1312 1.1 christos return; 1313 1.1 christos wpa_printf(MSG_INFO, "- HomeSP/RoamingConsortiumOI = %s", str); 1314 1.1.1.4 christos if (set_cred_quoted(ctx->ifname, id, "roaming_consortiums", 1315 1.1.1.4 christos str) < 0) 1316 1.1.1.4 christos wpa_printf(MSG_INFO, "Failed to set cred roaming_consortiums"); 1317 1.1 christos xml_node_get_text_free(ctx->xml, str); 1318 1.1 christos } 1319 1.1 christos 1320 1.1 christos 1321 1.1 christos static void set_pps_cred_home_sp(struct hs20_osu_client *ctx, int id, 1322 1.1 christos xml_node_t *node) 1323 1.1 christos { 1324 1.1 christos xml_node_t *child; 1325 1.1 christos const char *name; 1326 1.1 christos 1327 1.1 christos wpa_printf(MSG_INFO, "- HomeSP"); 1328 1.1 christos 1329 1.1 christos xml_node_for_each_child(ctx->xml, child, node) { 1330 1.1 christos xml_node_for_each_check(ctx->xml, child); 1331 1.1 christos name = xml_node_get_localname(ctx->xml, child); 1332 1.1 christos if (os_strcasecmp(name, "NetworkID") == 0) 1333 1.1 christos set_pps_cred_home_sp_network_ids(ctx, id, child); 1334 1.1 christos else if (os_strcasecmp(name, "FriendlyName") == 0) 1335 1.1 christos set_pps_cred_home_sp_friendly_name(ctx, id, child); 1336 1.1 christos else if (os_strcasecmp(name, "IconURL") == 0) 1337 1.1 christos set_pps_cred_home_sp_icon_url(ctx, id, child); 1338 1.1 christos else if (os_strcasecmp(name, "FQDN") == 0) 1339 1.1 christos set_pps_cred_home_sp_fqdn(ctx, id, child); 1340 1.1 christos else if (os_strcasecmp(name, "HomeOIList") == 0) 1341 1.1 christos set_pps_cred_home_sp_oi_list(ctx, id, child); 1342 1.1 christos else if (os_strcasecmp(name, "OtherHomePartners") == 0) 1343 1.1 christos set_pps_cred_home_sp_other_partners(ctx, id, child); 1344 1.1 christos else if (os_strcasecmp(name, "RoamingConsortiumOI") == 0) 1345 1.1 christos set_pps_cred_home_sp_roaming_consortium_oi(ctx, id, 1346 1.1 christos child); 1347 1.1 christos else 1348 1.1 christos wpa_printf(MSG_INFO, "Unknown HomeSP node '%s'", name); 1349 1.1 christos } 1350 1.1 christos } 1351 1.1 christos 1352 1.1 christos 1353 1.1 christos static void set_pps_cred_sub_params(struct hs20_osu_client *ctx, int id, 1354 1.1 christos xml_node_t *node) 1355 1.1 christos { 1356 1.1 christos wpa_printf(MSG_INFO, "- SubscriptionParameters"); 1357 1.1 christos /* not used within wpa_supplicant */ 1358 1.1 christos } 1359 1.1 christos 1360 1.1 christos 1361 1.1 christos static void set_pps_cred_creation_date(struct hs20_osu_client *ctx, int id, 1362 1.1 christos xml_node_t *node) 1363 1.1 christos { 1364 1.1 christos char *str = xml_node_get_text(ctx->xml, node); 1365 1.1 christos if (str == NULL) 1366 1.1 christos return; 1367 1.1 christos wpa_printf(MSG_INFO, "- Credential/CreationDate = %s", str); 1368 1.1 christos /* not used within wpa_supplicant */ 1369 1.1 christos xml_node_get_text_free(ctx->xml, str); 1370 1.1 christos } 1371 1.1 christos 1372 1.1 christos 1373 1.1 christos static void set_pps_cred_expiration_date(struct hs20_osu_client *ctx, int id, 1374 1.1 christos xml_node_t *node) 1375 1.1 christos { 1376 1.1 christos char *str = xml_node_get_text(ctx->xml, node); 1377 1.1 christos if (str == NULL) 1378 1.1 christos return; 1379 1.1 christos wpa_printf(MSG_INFO, "- Credential/ExpirationDate = %s", str); 1380 1.1 christos /* not used within wpa_supplicant */ 1381 1.1 christos xml_node_get_text_free(ctx->xml, str); 1382 1.1 christos } 1383 1.1 christos 1384 1.1 christos 1385 1.1 christos static void set_pps_cred_username(struct hs20_osu_client *ctx, int id, 1386 1.1 christos xml_node_t *node) 1387 1.1 christos { 1388 1.1 christos char *str = xml_node_get_text(ctx->xml, node); 1389 1.1 christos if (str == NULL) 1390 1.1 christos return; 1391 1.1 christos wpa_printf(MSG_INFO, "- Credential/UsernamePassword/Username = %s", 1392 1.1 christos str); 1393 1.1 christos if (set_cred_quoted(ctx->ifname, id, "username", str) < 0) 1394 1.1 christos wpa_printf(MSG_INFO, "Failed to set cred username"); 1395 1.1 christos xml_node_get_text_free(ctx->xml, str); 1396 1.1 christos } 1397 1.1 christos 1398 1.1 christos 1399 1.1 christos static void set_pps_cred_password(struct hs20_osu_client *ctx, int id, 1400 1.1 christos xml_node_t *node) 1401 1.1 christos { 1402 1.1 christos int len, i; 1403 1.1 christos char *pw, *hex, *pos, *end; 1404 1.1 christos 1405 1.1 christos pw = xml_node_get_base64_text(ctx->xml, node, &len); 1406 1.1 christos if (pw == NULL) 1407 1.1 christos return; 1408 1.1 christos 1409 1.1 christos wpa_printf(MSG_INFO, "- Credential/UsernamePassword/Password = %s", pw); 1410 1.1 christos 1411 1.1 christos hex = malloc(len * 2 + 1); 1412 1.1 christos if (hex == NULL) { 1413 1.1 christos free(pw); 1414 1.1 christos return; 1415 1.1 christos } 1416 1.1 christos end = hex + len * 2 + 1; 1417 1.1 christos pos = hex; 1418 1.1 christos for (i = 0; i < len; i++) { 1419 1.1 christos snprintf(pos, end - pos, "%02x", pw[i]); 1420 1.1 christos pos += 2; 1421 1.1 christos } 1422 1.1 christos free(pw); 1423 1.1 christos 1424 1.1 christos if (set_cred(ctx->ifname, id, "password", hex) < 0) 1425 1.1 christos wpa_printf(MSG_INFO, "Failed to set cred password"); 1426 1.1 christos free(hex); 1427 1.1 christos } 1428 1.1 christos 1429 1.1 christos 1430 1.1 christos static void set_pps_cred_machine_managed(struct hs20_osu_client *ctx, int id, 1431 1.1 christos xml_node_t *node) 1432 1.1 christos { 1433 1.1 christos char *str = xml_node_get_text(ctx->xml, node); 1434 1.1 christos if (str == NULL) 1435 1.1 christos return; 1436 1.1 christos wpa_printf(MSG_INFO, "- Credential/UsernamePassword/MachineManaged = %s", 1437 1.1 christos str); 1438 1.1 christos /* not used within wpa_supplicant */ 1439 1.1 christos xml_node_get_text_free(ctx->xml, str); 1440 1.1 christos } 1441 1.1 christos 1442 1.1 christos 1443 1.1 christos static void set_pps_cred_soft_token_app(struct hs20_osu_client *ctx, int id, 1444 1.1 christos xml_node_t *node) 1445 1.1 christos { 1446 1.1 christos char *str = xml_node_get_text(ctx->xml, node); 1447 1.1 christos if (str == NULL) 1448 1.1 christos return; 1449 1.1 christos wpa_printf(MSG_INFO, "- Credential/UsernamePassword/SoftTokenApp = %s", 1450 1.1 christos str); 1451 1.1 christos /* not used within wpa_supplicant */ 1452 1.1 christos xml_node_get_text_free(ctx->xml, str); 1453 1.1 christos } 1454 1.1 christos 1455 1.1 christos 1456 1.1 christos static void set_pps_cred_able_to_share(struct hs20_osu_client *ctx, int id, 1457 1.1 christos xml_node_t *node) 1458 1.1 christos { 1459 1.1 christos char *str = xml_node_get_text(ctx->xml, node); 1460 1.1 christos if (str == NULL) 1461 1.1 christos return; 1462 1.1 christos wpa_printf(MSG_INFO, "- Credential/UsernamePassword/AbleToShare = %s", 1463 1.1 christos str); 1464 1.1 christos /* not used within wpa_supplicant */ 1465 1.1 christos xml_node_get_text_free(ctx->xml, str); 1466 1.1 christos } 1467 1.1 christos 1468 1.1 christos 1469 1.1.1.4 christos static void set_pps_cred_eap_method_eap_type(struct hs20_osu_client *ctx, 1470 1.1.1.4 christos int id, xml_node_t *node) 1471 1.1.1.4 christos { 1472 1.1.1.4 christos char *str = xml_node_get_text(ctx->xml, node); 1473 1.1.1.4 christos int type; 1474 1.1.1.4 christos const char *eap_method = NULL; 1475 1.1.1.4 christos 1476 1.1.1.4 christos if (!str) 1477 1.1.1.4 christos return; 1478 1.1.1.4 christos wpa_printf(MSG_INFO, 1479 1.1.1.4 christos "- Credential/UsernamePassword/EAPMethod/EAPType = %s", str); 1480 1.1.1.4 christos type = atoi(str); 1481 1.1.1.4 christos switch (type) { 1482 1.1.1.4 christos case EAP_TYPE_TLS: 1483 1.1.1.4 christos eap_method = "TLS"; 1484 1.1.1.4 christos break; 1485 1.1.1.4 christos case EAP_TYPE_TTLS: 1486 1.1.1.4 christos eap_method = "TTLS"; 1487 1.1.1.4 christos break; 1488 1.1.1.4 christos case EAP_TYPE_PEAP: 1489 1.1.1.4 christos eap_method = "PEAP"; 1490 1.1.1.4 christos break; 1491 1.1.1.4 christos case EAP_TYPE_PWD: 1492 1.1.1.4 christos eap_method = "PWD"; 1493 1.1.1.4 christos break; 1494 1.1.1.4 christos } 1495 1.1.1.4 christos xml_node_get_text_free(ctx->xml, str); 1496 1.1.1.4 christos if (!eap_method) { 1497 1.1.1.4 christos wpa_printf(MSG_INFO, "Unknown EAPType value"); 1498 1.1.1.4 christos return; 1499 1.1.1.4 christos } 1500 1.1.1.4 christos 1501 1.1.1.4 christos if (set_cred(ctx->ifname, id, "eap", eap_method) < 0) 1502 1.1.1.4 christos wpa_printf(MSG_INFO, "Failed to set cred eap"); 1503 1.1.1.4 christos } 1504 1.1.1.4 christos 1505 1.1.1.4 christos 1506 1.1.1.4 christos static void set_pps_cred_eap_method_inner_method(struct hs20_osu_client *ctx, 1507 1.1.1.4 christos int id, xml_node_t *node) 1508 1.1.1.4 christos { 1509 1.1.1.4 christos char *str = xml_node_get_text(ctx->xml, node); 1510 1.1.1.4 christos const char *phase2 = NULL; 1511 1.1.1.4 christos 1512 1.1.1.4 christos if (!str) 1513 1.1.1.4 christos return; 1514 1.1.1.4 christos wpa_printf(MSG_INFO, 1515 1.1.1.4 christos "- Credential/UsernamePassword/EAPMethod/InnerMethod = %s", 1516 1.1.1.4 christos str); 1517 1.1.1.4 christos if (os_strcmp(str, "PAP") == 0) 1518 1.1.1.4 christos phase2 = "auth=PAP"; 1519 1.1.1.4 christos else if (os_strcmp(str, "CHAP") == 0) 1520 1.1.1.4 christos phase2 = "auth=CHAP"; 1521 1.1.1.4 christos else if (os_strcmp(str, "MS-CHAP") == 0) 1522 1.1.1.4 christos phase2 = "auth=MSCHAP"; 1523 1.1.1.4 christos else if (os_strcmp(str, "MS-CHAP-V2") == 0) 1524 1.1.1.4 christos phase2 = "auth=MSCHAPV2"; 1525 1.1.1.4 christos xml_node_get_text_free(ctx->xml, str); 1526 1.1.1.4 christos if (!phase2) { 1527 1.1.1.4 christos wpa_printf(MSG_INFO, "Unknown InnerMethod value"); 1528 1.1.1.4 christos return; 1529 1.1.1.4 christos } 1530 1.1.1.4 christos 1531 1.1.1.4 christos if (set_cred_quoted(ctx->ifname, id, "phase2", phase2) < 0) 1532 1.1.1.4 christos wpa_printf(MSG_INFO, "Failed to set cred phase2"); 1533 1.1.1.4 christos } 1534 1.1.1.4 christos 1535 1.1.1.4 christos 1536 1.1 christos static void set_pps_cred_eap_method(struct hs20_osu_client *ctx, int id, 1537 1.1 christos xml_node_t *node) 1538 1.1 christos { 1539 1.1.1.4 christos xml_node_t *child; 1540 1.1.1.4 christos const char *name; 1541 1.1.1.4 christos 1542 1.1.1.4 christos wpa_printf(MSG_INFO, "- Credential/UsernamePassword/EAPMethod"); 1543 1.1.1.4 christos 1544 1.1.1.4 christos xml_node_for_each_child(ctx->xml, child, node) { 1545 1.1.1.4 christos xml_node_for_each_check(ctx->xml, child); 1546 1.1.1.4 christos name = xml_node_get_localname(ctx->xml, child); 1547 1.1.1.4 christos if (os_strcasecmp(name, "EAPType") == 0) 1548 1.1.1.4 christos set_pps_cred_eap_method_eap_type(ctx, id, child); 1549 1.1.1.4 christos else if (os_strcasecmp(name, "InnerMethod") == 0) 1550 1.1.1.4 christos set_pps_cred_eap_method_inner_method(ctx, id, child); 1551 1.1.1.4 christos else 1552 1.1.1.4 christos wpa_printf(MSG_INFO, "Unknown Credential/UsernamePassword/EAPMethod node '%s'", 1553 1.1.1.4 christos name); 1554 1.1.1.4 christos } 1555 1.1 christos } 1556 1.1 christos 1557 1.1 christos 1558 1.1 christos static void set_pps_cred_username_password(struct hs20_osu_client *ctx, int id, 1559 1.1 christos xml_node_t *node) 1560 1.1 christos { 1561 1.1 christos xml_node_t *child; 1562 1.1 christos const char *name; 1563 1.1 christos 1564 1.1 christos wpa_printf(MSG_INFO, "- Credential/UsernamePassword"); 1565 1.1 christos 1566 1.1 christos xml_node_for_each_child(ctx->xml, child, node) { 1567 1.1 christos xml_node_for_each_check(ctx->xml, child); 1568 1.1 christos name = xml_node_get_localname(ctx->xml, child); 1569 1.1 christos if (os_strcasecmp(name, "Username") == 0) 1570 1.1 christos set_pps_cred_username(ctx, id, child); 1571 1.1 christos else if (os_strcasecmp(name, "Password") == 0) 1572 1.1 christos set_pps_cred_password(ctx, id, child); 1573 1.1 christos else if (os_strcasecmp(name, "MachineManaged") == 0) 1574 1.1 christos set_pps_cred_machine_managed(ctx, id, child); 1575 1.1 christos else if (os_strcasecmp(name, "SoftTokenApp") == 0) 1576 1.1 christos set_pps_cred_soft_token_app(ctx, id, child); 1577 1.1 christos else if (os_strcasecmp(name, "AbleToShare") == 0) 1578 1.1 christos set_pps_cred_able_to_share(ctx, id, child); 1579 1.1 christos else if (os_strcasecmp(name, "EAPMethod") == 0) 1580 1.1 christos set_pps_cred_eap_method(ctx, id, child); 1581 1.1 christos else 1582 1.1 christos wpa_printf(MSG_INFO, "Unknown Credential/UsernamePassword node '%s'", 1583 1.1 christos name); 1584 1.1 christos } 1585 1.1 christos } 1586 1.1 christos 1587 1.1 christos 1588 1.1 christos static void set_pps_cred_digital_cert(struct hs20_osu_client *ctx, int id, 1589 1.1 christos xml_node_t *node, const char *fqdn) 1590 1.1 christos { 1591 1.1 christos char buf[200], dir[200]; 1592 1.1.1.5 christos int res; 1593 1.1 christos 1594 1.1 christos wpa_printf(MSG_INFO, "- Credential/DigitalCertificate"); 1595 1.1 christos 1596 1.1 christos if (getcwd(dir, sizeof(dir)) == NULL) 1597 1.1 christos return; 1598 1.1 christos 1599 1.1 christos /* TODO: could build username from Subject of Subject AltName */ 1600 1.1 christos if (set_cred_quoted(ctx->ifname, id, "username", "cert") < 0) { 1601 1.1 christos wpa_printf(MSG_INFO, "Failed to set username"); 1602 1.1 christos } 1603 1.1 christos 1604 1.1.1.5 christos res = os_snprintf(buf, sizeof(buf), "%s/SP/%s/client-cert.pem", dir, 1605 1.1.1.5 christos fqdn); 1606 1.1.1.5 christos if (os_snprintf_error(sizeof(buf), res)) 1607 1.1.1.5 christos return; 1608 1.1 christos if (os_file_exists(buf)) { 1609 1.1 christos if (set_cred_quoted(ctx->ifname, id, "client_cert", buf) < 0) { 1610 1.1 christos wpa_printf(MSG_INFO, "Failed to set client_cert"); 1611 1.1 christos } 1612 1.1 christos } 1613 1.1 christos 1614 1.1.1.5 christos res = os_snprintf(buf, sizeof(buf), "%s/SP/%s/client-key.pem", dir, 1615 1.1.1.5 christos fqdn); 1616 1.1.1.5 christos if (os_snprintf_error(sizeof(buf), res)) 1617 1.1.1.5 christos return; 1618 1.1 christos if (os_file_exists(buf)) { 1619 1.1 christos if (set_cred_quoted(ctx->ifname, id, "private_key", buf) < 0) { 1620 1.1 christos wpa_printf(MSG_INFO, "Failed to set private_key"); 1621 1.1 christos } 1622 1.1 christos } 1623 1.1 christos } 1624 1.1 christos 1625 1.1 christos 1626 1.1 christos static void set_pps_cred_realm(struct hs20_osu_client *ctx, int id, 1627 1.1 christos xml_node_t *node, const char *fqdn, int sim) 1628 1.1 christos { 1629 1.1 christos char *str = xml_node_get_text(ctx->xml, node); 1630 1.1 christos char buf[200], dir[200]; 1631 1.1.1.5 christos int res; 1632 1.1 christos 1633 1.1 christos if (str == NULL) 1634 1.1 christos return; 1635 1.1 christos 1636 1.1 christos wpa_printf(MSG_INFO, "- Credential/Realm = %s", str); 1637 1.1 christos if (set_cred_quoted(ctx->ifname, id, "realm", str) < 0) 1638 1.1 christos wpa_printf(MSG_INFO, "Failed to set cred realm"); 1639 1.1 christos xml_node_get_text_free(ctx->xml, str); 1640 1.1 christos 1641 1.1 christos if (sim) 1642 1.1 christos return; 1643 1.1 christos 1644 1.1 christos if (getcwd(dir, sizeof(dir)) == NULL) 1645 1.1 christos return; 1646 1.1.1.5 christos res = os_snprintf(buf, sizeof(buf), "%s/SP/%s/aaa-ca.pem", dir, fqdn); 1647 1.1.1.5 christos if (os_snprintf_error(sizeof(buf), res)) 1648 1.1.1.5 christos return; 1649 1.1 christos if (os_file_exists(buf)) { 1650 1.1 christos if (set_cred_quoted(ctx->ifname, id, "ca_cert", buf) < 0) { 1651 1.1 christos wpa_printf(MSG_INFO, "Failed to set CA cert"); 1652 1.1 christos } 1653 1.1 christos } 1654 1.1 christos } 1655 1.1 christos 1656 1.1 christos 1657 1.1 christos static void set_pps_cred_check_aaa_cert_status(struct hs20_osu_client *ctx, 1658 1.1 christos int id, xml_node_t *node) 1659 1.1 christos { 1660 1.1 christos char *str = xml_node_get_text(ctx->xml, node); 1661 1.1 christos 1662 1.1 christos if (str == NULL) 1663 1.1 christos return; 1664 1.1 christos 1665 1.1 christos wpa_printf(MSG_INFO, "- Credential/CheckAAAServerCertStatus = %s", str); 1666 1.1 christos if (os_strcasecmp(str, "true") == 0 && 1667 1.1 christos set_cred(ctx->ifname, id, "ocsp", "2") < 0) 1668 1.1 christos wpa_printf(MSG_INFO, "Failed to set cred ocsp"); 1669 1.1 christos xml_node_get_text_free(ctx->xml, str); 1670 1.1 christos } 1671 1.1 christos 1672 1.1 christos 1673 1.1 christos static void set_pps_cred_sim(struct hs20_osu_client *ctx, int id, 1674 1.1 christos xml_node_t *sim, xml_node_t *realm) 1675 1.1 christos { 1676 1.1 christos xml_node_t *node; 1677 1.1 christos char *imsi, *eaptype, *str, buf[20]; 1678 1.1 christos int type; 1679 1.1 christos int mnc_len = 3; 1680 1.1 christos size_t imsi_len; 1681 1.1 christos 1682 1.1 christos node = get_node(ctx->xml, sim, "EAPType"); 1683 1.1 christos if (node == NULL) { 1684 1.1 christos wpa_printf(MSG_INFO, "No SIM/EAPType node in credential"); 1685 1.1 christos return; 1686 1.1 christos } 1687 1.1 christos eaptype = xml_node_get_text(ctx->xml, node); 1688 1.1 christos if (eaptype == NULL) { 1689 1.1 christos wpa_printf(MSG_INFO, "Could not extract SIM/EAPType"); 1690 1.1 christos return; 1691 1.1 christos } 1692 1.1 christos wpa_printf(MSG_INFO, " - Credential/SIM/EAPType = %s", eaptype); 1693 1.1 christos type = atoi(eaptype); 1694 1.1 christos xml_node_get_text_free(ctx->xml, eaptype); 1695 1.1 christos 1696 1.1 christos switch (type) { 1697 1.1 christos case EAP_TYPE_SIM: 1698 1.1 christos if (set_cred(ctx->ifname, id, "eap", "SIM") < 0) 1699 1.1 christos wpa_printf(MSG_INFO, "Could not set eap=SIM"); 1700 1.1 christos break; 1701 1.1 christos case EAP_TYPE_AKA: 1702 1.1 christos if (set_cred(ctx->ifname, id, "eap", "AKA") < 0) 1703 1.1 christos wpa_printf(MSG_INFO, "Could not set eap=SIM"); 1704 1.1 christos break; 1705 1.1 christos case EAP_TYPE_AKA_PRIME: 1706 1.1 christos if (set_cred(ctx->ifname, id, "eap", "AKA'") < 0) 1707 1.1 christos wpa_printf(MSG_INFO, "Could not set eap=SIM"); 1708 1.1 christos break; 1709 1.1 christos default: 1710 1.1 christos wpa_printf(MSG_INFO, "Unsupported SIM/EAPType %d", type); 1711 1.1 christos return; 1712 1.1 christos } 1713 1.1 christos 1714 1.1 christos node = get_node(ctx->xml, sim, "IMSI"); 1715 1.1 christos if (node == NULL) { 1716 1.1 christos wpa_printf(MSG_INFO, "No SIM/IMSI node in credential"); 1717 1.1 christos return; 1718 1.1 christos } 1719 1.1 christos imsi = xml_node_get_text(ctx->xml, node); 1720 1.1 christos if (imsi == NULL) { 1721 1.1 christos wpa_printf(MSG_INFO, "Could not extract SIM/IMSI"); 1722 1.1 christos return; 1723 1.1 christos } 1724 1.1 christos wpa_printf(MSG_INFO, " - Credential/SIM/IMSI = %s", imsi); 1725 1.1 christos imsi_len = os_strlen(imsi); 1726 1.1 christos if (imsi_len < 7 || imsi_len + 2 > sizeof(buf)) { 1727 1.1 christos wpa_printf(MSG_INFO, "Invalid IMSI length"); 1728 1.1 christos xml_node_get_text_free(ctx->xml, imsi); 1729 1.1 christos return; 1730 1.1 christos } 1731 1.1 christos 1732 1.1 christos str = xml_node_get_text(ctx->xml, node); 1733 1.1 christos if (str) { 1734 1.1 christos char *pos; 1735 1.1 christos pos = os_strstr(str, "mnc"); 1736 1.1 christos if (pos && os_strlen(pos) >= 6) { 1737 1.1 christos if (os_strncmp(imsi + 3, pos + 3, 3) == 0) 1738 1.1 christos mnc_len = 3; 1739 1.1 christos else if (os_strncmp(imsi + 3, pos + 4, 2) == 0) 1740 1.1 christos mnc_len = 2; 1741 1.1 christos } 1742 1.1 christos xml_node_get_text_free(ctx->xml, str); 1743 1.1 christos } 1744 1.1 christos 1745 1.1 christos os_memcpy(buf, imsi, 3 + mnc_len); 1746 1.1 christos buf[3 + mnc_len] = '-'; 1747 1.1 christos os_strlcpy(buf + 3 + mnc_len + 1, imsi + 3 + mnc_len, 1748 1.1 christos sizeof(buf) - 3 - mnc_len - 1); 1749 1.1 christos 1750 1.1 christos xml_node_get_text_free(ctx->xml, imsi); 1751 1.1 christos 1752 1.1 christos if (set_cred_quoted(ctx->ifname, id, "imsi", buf) < 0) 1753 1.1 christos wpa_printf(MSG_INFO, "Could not set IMSI"); 1754 1.1 christos 1755 1.1 christos if (set_cred_quoted(ctx->ifname, id, "milenage", 1756 1.1 christos "90dca4eda45b53cf0f12d7c9c3bc6a89:" 1757 1.1 christos "cb9cccc4b9258e6dca4760379fb82581:000000000123") < 1758 1.1 christos 0) 1759 1.1 christos wpa_printf(MSG_INFO, "Could not set Milenage parameters"); 1760 1.1 christos } 1761 1.1 christos 1762 1.1 christos 1763 1.1 christos static void set_pps_cred_credential(struct hs20_osu_client *ctx, int id, 1764 1.1 christos xml_node_t *node, const char *fqdn) 1765 1.1 christos { 1766 1.1 christos xml_node_t *child, *sim, *realm; 1767 1.1 christos const char *name; 1768 1.1 christos 1769 1.1 christos wpa_printf(MSG_INFO, "- Credential"); 1770 1.1 christos 1771 1.1 christos sim = get_node(ctx->xml, node, "SIM"); 1772 1.1 christos realm = get_node(ctx->xml, node, "Realm"); 1773 1.1 christos 1774 1.1 christos xml_node_for_each_child(ctx->xml, child, node) { 1775 1.1 christos xml_node_for_each_check(ctx->xml, child); 1776 1.1 christos name = xml_node_get_localname(ctx->xml, child); 1777 1.1 christos if (os_strcasecmp(name, "CreationDate") == 0) 1778 1.1 christos set_pps_cred_creation_date(ctx, id, child); 1779 1.1 christos else if (os_strcasecmp(name, "ExpirationDate") == 0) 1780 1.1 christos set_pps_cred_expiration_date(ctx, id, child); 1781 1.1 christos else if (os_strcasecmp(name, "UsernamePassword") == 0) 1782 1.1 christos set_pps_cred_username_password(ctx, id, child); 1783 1.1 christos else if (os_strcasecmp(name, "DigitalCertificate") == 0) 1784 1.1 christos set_pps_cred_digital_cert(ctx, id, child, fqdn); 1785 1.1 christos else if (os_strcasecmp(name, "Realm") == 0) 1786 1.1 christos set_pps_cred_realm(ctx, id, child, fqdn, sim != NULL); 1787 1.1 christos else if (os_strcasecmp(name, "CheckAAAServerCertStatus") == 0) 1788 1.1 christos set_pps_cred_check_aaa_cert_status(ctx, id, child); 1789 1.1 christos else if (os_strcasecmp(name, "SIM") == 0) 1790 1.1 christos set_pps_cred_sim(ctx, id, child, realm); 1791 1.1 christos else 1792 1.1 christos wpa_printf(MSG_INFO, "Unknown Credential node '%s'", 1793 1.1 christos name); 1794 1.1 christos } 1795 1.1 christos } 1796 1.1 christos 1797 1.1 christos 1798 1.1 christos static void set_pps_credential(struct hs20_osu_client *ctx, int id, 1799 1.1 christos xml_node_t *cred, const char *fqdn) 1800 1.1 christos { 1801 1.1 christos xml_node_t *child; 1802 1.1 christos const char *name; 1803 1.1 christos 1804 1.1 christos xml_node_for_each_child(ctx->xml, child, cred) { 1805 1.1 christos xml_node_for_each_check(ctx->xml, child); 1806 1.1 christos name = xml_node_get_localname(ctx->xml, child); 1807 1.1 christos if (os_strcasecmp(name, "Policy") == 0) 1808 1.1 christos set_pps_cred_policy(ctx, id, child); 1809 1.1 christos else if (os_strcasecmp(name, "CredentialPriority") == 0) 1810 1.1 christos set_pps_cred_priority(ctx, id, child); 1811 1.1 christos else if (os_strcasecmp(name, "AAAServerTrustRoot") == 0) 1812 1.1 christos set_pps_cred_aaa_server_trust_root(ctx, id, child); 1813 1.1 christos else if (os_strcasecmp(name, "SubscriptionUpdate") == 0) 1814 1.1 christos set_pps_cred_sub_update(ctx, id, child); 1815 1.1 christos else if (os_strcasecmp(name, "HomeSP") == 0) 1816 1.1 christos set_pps_cred_home_sp(ctx, id, child); 1817 1.1 christos else if (os_strcasecmp(name, "SubscriptionParameters") == 0) 1818 1.1 christos set_pps_cred_sub_params(ctx, id, child); 1819 1.1 christos else if (os_strcasecmp(name, "Credential") == 0) 1820 1.1 christos set_pps_cred_credential(ctx, id, child, fqdn); 1821 1.1 christos else 1822 1.1 christos wpa_printf(MSG_INFO, "Unknown credential node '%s'", 1823 1.1 christos name); 1824 1.1 christos } 1825 1.1 christos } 1826 1.1 christos 1827 1.1 christos 1828 1.1 christos static void set_pps(struct hs20_osu_client *ctx, xml_node_t *pps, 1829 1.1 christos const char *fqdn) 1830 1.1 christos { 1831 1.1 christos xml_node_t *child; 1832 1.1 christos const char *name; 1833 1.1 christos int id; 1834 1.1 christos char *update_identifier = NULL; 1835 1.1 christos 1836 1.1 christos /* 1837 1.1 christos * TODO: Could consider more complex mechanism that would remove 1838 1.1 christos * credentials only if there are changes in the information sent to 1839 1.1 christos * wpa_supplicant. 1840 1.1 christos */ 1841 1.1 christos remove_sp_creds(ctx, fqdn); 1842 1.1 christos 1843 1.1 christos xml_node_for_each_child(ctx->xml, child, pps) { 1844 1.1 christos xml_node_for_each_check(ctx->xml, child); 1845 1.1 christos name = xml_node_get_localname(ctx->xml, child); 1846 1.1 christos if (os_strcasecmp(name, "UpdateIdentifier") == 0) { 1847 1.1 christos update_identifier = xml_node_get_text(ctx->xml, child); 1848 1.1 christos if (update_identifier) { 1849 1.1 christos wpa_printf(MSG_INFO, "- UpdateIdentifier = %s", 1850 1.1 christos update_identifier); 1851 1.1 christos break; 1852 1.1 christos } 1853 1.1 christos } 1854 1.1 christos } 1855 1.1 christos 1856 1.1 christos xml_node_for_each_child(ctx->xml, child, pps) { 1857 1.1 christos xml_node_for_each_check(ctx->xml, child); 1858 1.1 christos name = xml_node_get_localname(ctx->xml, child); 1859 1.1 christos if (os_strcasecmp(name, "UpdateIdentifier") == 0) 1860 1.1 christos continue; 1861 1.1 christos id = add_cred(ctx->ifname); 1862 1.1 christos if (id < 0) { 1863 1.1 christos wpa_printf(MSG_INFO, "Failed to add credential to wpa_supplicant"); 1864 1.1 christos write_summary(ctx, "Failed to add credential to wpa_supplicant"); 1865 1.1 christos break; 1866 1.1 christos } 1867 1.1 christos write_summary(ctx, "Add a credential to wpa_supplicant"); 1868 1.1 christos if (update_identifier && 1869 1.1 christos set_cred(ctx->ifname, id, "update_identifier", 1870 1.1 christos update_identifier) < 0) 1871 1.1 christos wpa_printf(MSG_INFO, "Failed to set update_identifier"); 1872 1.1 christos if (set_cred_quoted(ctx->ifname, id, "provisioning_sp", fqdn) < 1873 1.1 christos 0) 1874 1.1 christos wpa_printf(MSG_INFO, "Failed to set provisioning_sp"); 1875 1.1 christos wpa_printf(MSG_INFO, "credential localname: '%s'", name); 1876 1.1 christos set_pps_credential(ctx, id, child, fqdn); 1877 1.1 christos ctx->pps_cred_set = 1; 1878 1.1 christos } 1879 1.1 christos 1880 1.1 christos xml_node_get_text_free(ctx->xml, update_identifier); 1881 1.1 christos } 1882 1.1 christos 1883 1.1 christos 1884 1.1 christos void cmd_set_pps(struct hs20_osu_client *ctx, const char *pps_fname) 1885 1.1 christos { 1886 1.1 christos xml_node_t *pps; 1887 1.1 christos const char *fqdn; 1888 1.1 christos char *fqdn_buf = NULL, *pos; 1889 1.1 christos 1890 1.1 christos pps = node_from_file(ctx->xml, pps_fname); 1891 1.1 christos if (pps == NULL) { 1892 1.1 christos wpa_printf(MSG_INFO, "Could not read or parse '%s'", pps_fname); 1893 1.1 christos return; 1894 1.1 christos } 1895 1.1 christos 1896 1.1 christos fqdn = os_strstr(pps_fname, "SP/"); 1897 1.1 christos if (fqdn) { 1898 1.1 christos fqdn_buf = os_strdup(fqdn + 3); 1899 1.1 christos if (fqdn_buf == NULL) 1900 1.1 christos return; 1901 1.1 christos pos = os_strchr(fqdn_buf, '/'); 1902 1.1 christos if (pos) 1903 1.1 christos *pos = '\0'; 1904 1.1 christos fqdn = fqdn_buf; 1905 1.1 christos } else 1906 1.1 christos fqdn = "wi-fi.org"; 1907 1.1 christos 1908 1.1 christos wpa_printf(MSG_INFO, "Set PPS MO info to wpa_supplicant - SP FQDN %s", 1909 1.1 christos fqdn); 1910 1.1 christos set_pps(ctx, pps, fqdn); 1911 1.1 christos 1912 1.1 christos os_free(fqdn_buf); 1913 1.1 christos xml_node_free(ctx->xml, pps); 1914 1.1 christos } 1915 1.1 christos 1916 1.1 christos 1917 1.1 christos static int cmd_get_fqdn(struct hs20_osu_client *ctx, const char *pps_fname) 1918 1.1 christos { 1919 1.1 christos xml_node_t *pps, *node; 1920 1.1 christos char *fqdn = NULL; 1921 1.1 christos 1922 1.1 christos pps = node_from_file(ctx->xml, pps_fname); 1923 1.1 christos if (pps == NULL) { 1924 1.1 christos wpa_printf(MSG_INFO, "Could not read or parse '%s'", pps_fname); 1925 1.1 christos return -1; 1926 1.1 christos } 1927 1.1 christos 1928 1.1 christos node = get_child_node(ctx->xml, pps, "HomeSP/FQDN"); 1929 1.1 christos if (node) 1930 1.1 christos fqdn = xml_node_get_text(ctx->xml, node); 1931 1.1 christos 1932 1.1 christos xml_node_free(ctx->xml, pps); 1933 1.1 christos 1934 1.1 christos if (fqdn) { 1935 1.1 christos FILE *f = fopen("pps-fqdn", "w"); 1936 1.1 christos if (f) { 1937 1.1 christos fprintf(f, "%s", fqdn); 1938 1.1 christos fclose(f); 1939 1.1 christos } 1940 1.1 christos xml_node_get_text_free(ctx->xml, fqdn); 1941 1.1 christos return 0; 1942 1.1 christos } 1943 1.1 christos 1944 1.1 christos xml_node_get_text_free(ctx->xml, fqdn); 1945 1.1 christos return -1; 1946 1.1 christos } 1947 1.1 christos 1948 1.1 christos 1949 1.1 christos static void cmd_to_tnds(struct hs20_osu_client *ctx, const char *in_fname, 1950 1.1 christos const char *out_fname, const char *urn, int use_path) 1951 1.1 christos { 1952 1.1 christos xml_node_t *mo, *node; 1953 1.1 christos 1954 1.1 christos mo = node_from_file(ctx->xml, in_fname); 1955 1.1 christos if (mo == NULL) { 1956 1.1 christos wpa_printf(MSG_INFO, "Could not read or parse '%s'", in_fname); 1957 1.1 christos return; 1958 1.1 christos } 1959 1.1 christos 1960 1.1 christos node = mo_to_tnds(ctx->xml, mo, use_path, urn, NULL); 1961 1.1 christos if (node) { 1962 1.1 christos node_to_file(ctx->xml, out_fname, node); 1963 1.1 christos xml_node_free(ctx->xml, node); 1964 1.1 christos } 1965 1.1 christos 1966 1.1 christos xml_node_free(ctx->xml, mo); 1967 1.1 christos } 1968 1.1 christos 1969 1.1 christos 1970 1.1 christos static void cmd_from_tnds(struct hs20_osu_client *ctx, const char *in_fname, 1971 1.1 christos const char *out_fname) 1972 1.1 christos { 1973 1.1 christos xml_node_t *tnds, *mo; 1974 1.1 christos 1975 1.1 christos tnds = node_from_file(ctx->xml, in_fname); 1976 1.1 christos if (tnds == NULL) { 1977 1.1 christos wpa_printf(MSG_INFO, "Could not read or parse '%s'", in_fname); 1978 1.1 christos return; 1979 1.1 christos } 1980 1.1 christos 1981 1.1 christos mo = tnds_to_mo(ctx->xml, tnds); 1982 1.1 christos if (mo) { 1983 1.1 christos node_to_file(ctx->xml, out_fname, mo); 1984 1.1 christos xml_node_free(ctx->xml, mo); 1985 1.1 christos } 1986 1.1 christos 1987 1.1 christos xml_node_free(ctx->xml, tnds); 1988 1.1 christos } 1989 1.1 christos 1990 1.1 christos 1991 1.1 christos struct osu_icon { 1992 1.1 christos int id; 1993 1.1 christos char lang[4]; 1994 1.1 christos char mime_type[256]; 1995 1.1 christos char filename[256]; 1996 1.1 christos }; 1997 1.1 christos 1998 1.1 christos struct osu_data { 1999 1.1 christos char bssid[20]; 2000 1.1 christos char url[256]; 2001 1.1 christos unsigned int methods; 2002 1.1 christos char osu_ssid[33]; 2003 1.1.1.4 christos char osu_ssid2[33]; 2004 1.1 christos char osu_nai[256]; 2005 1.1.1.4 christos char osu_nai2[256]; 2006 1.1 christos struct osu_lang_text friendly_name[MAX_OSU_VALS]; 2007 1.1 christos size_t friendly_name_count; 2008 1.1 christos struct osu_lang_text serv_desc[MAX_OSU_VALS]; 2009 1.1 christos size_t serv_desc_count; 2010 1.1 christos struct osu_icon icon[MAX_OSU_VALS]; 2011 1.1 christos size_t icon_count; 2012 1.1 christos }; 2013 1.1 christos 2014 1.1 christos 2015 1.1 christos static struct osu_data * parse_osu_providers(const char *fname, size_t *count) 2016 1.1 christos { 2017 1.1 christos FILE *f; 2018 1.1 christos char buf[1000]; 2019 1.1 christos struct osu_data *osu = NULL, *last = NULL; 2020 1.1 christos size_t osu_count = 0; 2021 1.1 christos char *pos, *end; 2022 1.1.1.6 christos int res; 2023 1.1 christos 2024 1.1 christos f = fopen(fname, "r"); 2025 1.1 christos if (f == NULL) { 2026 1.1 christos wpa_printf(MSG_ERROR, "Could not open %s", fname); 2027 1.1 christos return NULL; 2028 1.1 christos } 2029 1.1 christos 2030 1.1 christos while (fgets(buf, sizeof(buf), f)) { 2031 1.1 christos pos = strchr(buf, '\n'); 2032 1.1 christos if (pos) 2033 1.1 christos *pos = '\0'; 2034 1.1 christos 2035 1.1 christos if (strncmp(buf, "OSU-PROVIDER ", 13) == 0) { 2036 1.1 christos last = realloc(osu, (osu_count + 1) * sizeof(*osu)); 2037 1.1 christos if (last == NULL) 2038 1.1 christos break; 2039 1.1 christos osu = last; 2040 1.1 christos last = &osu[osu_count++]; 2041 1.1 christos memset(last, 0, sizeof(*last)); 2042 1.1.1.6 christos res = os_snprintf(last->bssid, sizeof(last->bssid), 2043 1.1.1.6 christos "%s", buf + 13); 2044 1.1.1.6 christos if (os_snprintf_error(sizeof(last->bssid), res)) 2045 1.1.1.6 christos break; 2046 1.1 christos continue; 2047 1.1 christos } 2048 1.1 christos if (!last) 2049 1.1 christos continue; 2050 1.1 christos 2051 1.1 christos if (strncmp(buf, "uri=", 4) == 0) { 2052 1.1.1.6 christos res = os_snprintf(last->url, sizeof(last->url), 2053 1.1.1.6 christos "%s", buf + 4); 2054 1.1.1.6 christos if (os_snprintf_error(sizeof(last->url), res)) 2055 1.1.1.6 christos break; 2056 1.1 christos continue; 2057 1.1 christos } 2058 1.1 christos 2059 1.1 christos if (strncmp(buf, "methods=", 8) == 0) { 2060 1.1 christos last->methods = strtol(buf + 8, NULL, 16); 2061 1.1 christos continue; 2062 1.1 christos } 2063 1.1 christos 2064 1.1 christos if (strncmp(buf, "osu_ssid=", 9) == 0) { 2065 1.1.1.6 christos res = os_snprintf(last->osu_ssid, 2066 1.1.1.6 christos sizeof(last->osu_ssid), 2067 1.1.1.6 christos "%s", buf + 9); 2068 1.1.1.6 christos if (os_snprintf_error(sizeof(last->osu_ssid), res)) 2069 1.1.1.6 christos break; 2070 1.1 christos continue; 2071 1.1 christos } 2072 1.1 christos 2073 1.1.1.4 christos if (strncmp(buf, "osu_ssid2=", 10) == 0) { 2074 1.1.1.6 christos res = os_snprintf(last->osu_ssid2, 2075 1.1.1.6 christos sizeof(last->osu_ssid2), 2076 1.1.1.6 christos "%s", buf + 10); 2077 1.1.1.6 christos if (os_snprintf_error(sizeof(last->osu_ssid2), res)) 2078 1.1.1.6 christos break; 2079 1.1.1.4 christos continue; 2080 1.1.1.4 christos } 2081 1.1.1.4 christos 2082 1.1 christos if (os_strncmp(buf, "osu_nai=", 8) == 0) { 2083 1.1.1.6 christos res = os_snprintf(last->osu_nai, sizeof(last->osu_nai), 2084 1.1.1.6 christos "%s", buf + 8); 2085 1.1.1.6 christos if (os_snprintf_error(sizeof(last->osu_nai), res)) 2086 1.1.1.6 christos break; 2087 1.1 christos continue; 2088 1.1 christos } 2089 1.1 christos 2090 1.1.1.4 christos if (os_strncmp(buf, "osu_nai2=", 9) == 0) { 2091 1.1.1.6 christos res = os_snprintf(last->osu_nai2, 2092 1.1.1.6 christos sizeof(last->osu_nai2), 2093 1.1.1.6 christos "%s", buf + 9); 2094 1.1.1.6 christos if (os_snprintf_error(sizeof(last->osu_nai2), res)) 2095 1.1.1.6 christos break; 2096 1.1.1.4 christos continue; 2097 1.1.1.4 christos } 2098 1.1.1.4 christos 2099 1.1 christos if (strncmp(buf, "friendly_name=", 14) == 0) { 2100 1.1 christos struct osu_lang_text *txt; 2101 1.1 christos if (last->friendly_name_count == MAX_OSU_VALS) 2102 1.1 christos continue; 2103 1.1 christos pos = strchr(buf + 14, ':'); 2104 1.1 christos if (pos == NULL) 2105 1.1 christos continue; 2106 1.1 christos *pos++ = '\0'; 2107 1.1 christos txt = &last->friendly_name[last->friendly_name_count++]; 2108 1.1.1.6 christos res = os_snprintf(txt->lang, sizeof(txt->lang), 2109 1.1.1.6 christos "%s", buf + 14); 2110 1.1.1.6 christos if (os_snprintf_error(sizeof(txt->lang), res)) 2111 1.1.1.6 christos break; 2112 1.1.1.6 christos res = os_snprintf(txt->text, sizeof(txt->text), 2113 1.1.1.6 christos "%s", pos); 2114 1.1.1.6 christos if (os_snprintf_error(sizeof(txt->text), res)) 2115 1.1.1.6 christos break; 2116 1.1 christos } 2117 1.1 christos 2118 1.1 christos if (strncmp(buf, "desc=", 5) == 0) { 2119 1.1 christos struct osu_lang_text *txt; 2120 1.1 christos if (last->serv_desc_count == MAX_OSU_VALS) 2121 1.1 christos continue; 2122 1.1 christos pos = strchr(buf + 5, ':'); 2123 1.1 christos if (pos == NULL) 2124 1.1 christos continue; 2125 1.1 christos *pos++ = '\0'; 2126 1.1 christos txt = &last->serv_desc[last->serv_desc_count++]; 2127 1.1.1.6 christos res = os_snprintf(txt->lang, sizeof(txt->lang), 2128 1.1.1.6 christos "%s", buf + 5); 2129 1.1.1.6 christos if (os_snprintf_error(sizeof(txt->lang), res)) 2130 1.1.1.6 christos break; 2131 1.1.1.6 christos res = os_snprintf(txt->text, sizeof(txt->text), 2132 1.1.1.6 christos "%s", pos); 2133 1.1.1.6 christos if (os_snprintf_error(sizeof(txt->text), res)) 2134 1.1.1.6 christos break; 2135 1.1 christos } 2136 1.1 christos 2137 1.1 christos if (strncmp(buf, "icon=", 5) == 0) { 2138 1.1 christos struct osu_icon *icon; 2139 1.1 christos if (last->icon_count == MAX_OSU_VALS) 2140 1.1 christos continue; 2141 1.1 christos icon = &last->icon[last->icon_count++]; 2142 1.1 christos icon->id = atoi(buf + 5); 2143 1.1 christos pos = strchr(buf, ':'); 2144 1.1 christos if (pos == NULL) 2145 1.1 christos continue; 2146 1.1 christos pos = strchr(pos + 1, ':'); 2147 1.1 christos if (pos == NULL) 2148 1.1 christos continue; 2149 1.1 christos pos = strchr(pos + 1, ':'); 2150 1.1 christos if (pos == NULL) 2151 1.1 christos continue; 2152 1.1 christos pos++; 2153 1.1 christos end = strchr(pos, ':'); 2154 1.1 christos if (!end) 2155 1.1 christos continue; 2156 1.1 christos *end = '\0'; 2157 1.1.1.6 christos res = os_snprintf(icon->lang, sizeof(icon->lang), 2158 1.1.1.6 christos "%s", pos); 2159 1.1.1.6 christos if (os_snprintf_error(sizeof(icon->lang), res)) 2160 1.1.1.6 christos break; 2161 1.1 christos pos = end + 1; 2162 1.1 christos 2163 1.1 christos end = strchr(pos, ':'); 2164 1.1 christos if (end) 2165 1.1 christos *end = '\0'; 2166 1.1.1.6 christos res = os_snprintf(icon->mime_type, 2167 1.1.1.6 christos sizeof(icon->mime_type), "%s", pos); 2168 1.1.1.6 christos if (os_snprintf_error(sizeof(icon->mime_type), res)) 2169 1.1.1.6 christos break; 2170 1.1.1.6 christos if (!end) 2171 1.1 christos continue; 2172 1.1 christos pos = end + 1; 2173 1.1 christos 2174 1.1 christos end = strchr(pos, ':'); 2175 1.1 christos if (end) 2176 1.1 christos *end = '\0'; 2177 1.1.1.6 christos res = os_snprintf(icon->filename, 2178 1.1.1.6 christos sizeof(icon->filename), "%s", pos); 2179 1.1.1.6 christos if (os_snprintf_error(sizeof(icon->filename), res)) 2180 1.1.1.6 christos break; 2181 1.1 christos continue; 2182 1.1 christos } 2183 1.1 christos } 2184 1.1 christos 2185 1.1 christos fclose(f); 2186 1.1 christos 2187 1.1 christos *count = osu_count; 2188 1.1 christos return osu; 2189 1.1 christos } 2190 1.1 christos 2191 1.1 christos 2192 1.1 christos static int osu_connect(struct hs20_osu_client *ctx, const char *bssid, 2193 1.1.1.4 christos const char *ssid, const char *ssid2, const char *url, 2194 1.1 christos unsigned int methods, int no_prod_assoc, 2195 1.1.1.4 christos const char *osu_nai, const char *osu_nai2) 2196 1.1 christos { 2197 1.1 christos int id; 2198 1.1 christos const char *ifname = ctx->ifname; 2199 1.1 christos char buf[200]; 2200 1.1 christos struct wpa_ctrl *mon; 2201 1.1 christos int res; 2202 1.1 christos 2203 1.1.1.4 christos if (ssid2 && ssid2[0] == '\0') 2204 1.1.1.4 christos ssid2 = NULL; 2205 1.1.1.4 christos 2206 1.1.1.4 christos if (ctx->osu_ssid) { 2207 1.1.1.4 christos if (os_strcmp(ssid, ctx->osu_ssid) == 0) { 2208 1.1.1.4 christos wpa_printf(MSG_DEBUG, 2209 1.1.1.4 christos "Enforced OSU SSID matches ANQP info"); 2210 1.1.1.4 christos ssid2 = NULL; 2211 1.1.1.4 christos } else if (ssid2 && os_strcmp(ssid2, ctx->osu_ssid) == 0) { 2212 1.1.1.4 christos wpa_printf(MSG_DEBUG, 2213 1.1.1.4 christos "Enforced OSU SSID matches RSN[OSEN] info"); 2214 1.1.1.4 christos ssid = ssid2; 2215 1.1.1.4 christos } else { 2216 1.1.1.4 christos wpa_printf(MSG_INFO, "Enforced OSU SSID did not match"); 2217 1.1.1.4 christos write_summary(ctx, "Enforced OSU SSID did not match"); 2218 1.1.1.4 christos return -1; 2219 1.1.1.4 christos } 2220 1.1.1.4 christos } 2221 1.1.1.4 christos 2222 1.1 christos id = add_network(ifname); 2223 1.1 christos if (id < 0) 2224 1.1 christos return -1; 2225 1.1 christos if (set_network_quoted(ifname, id, "ssid", ssid) < 0) 2226 1.1 christos return -1; 2227 1.1.1.4 christos if (ssid2) 2228 1.1.1.4 christos osu_nai = osu_nai2; 2229 1.1 christos if (osu_nai && os_strlen(osu_nai) > 0) { 2230 1.1 christos char dir[255], fname[300]; 2231 1.1 christos if (getcwd(dir, sizeof(dir)) == NULL) 2232 1.1 christos return -1; 2233 1.1 christos os_snprintf(fname, sizeof(fname), "%s/osu-ca.pem", dir); 2234 1.1 christos 2235 1.1.1.4 christos if (ssid2 && set_network_quoted(ifname, id, "ssid", ssid2) < 0) 2236 1.1.1.4 christos return -1; 2237 1.1.1.4 christos 2238 1.1 christos if (set_network(ifname, id, "proto", "OSEN") < 0 || 2239 1.1 christos set_network(ifname, id, "key_mgmt", "OSEN") < 0 || 2240 1.1 christos set_network(ifname, id, "pairwise", "CCMP") < 0 || 2241 1.1.1.4 christos set_network(ifname, id, "group", "GTK_NOT_USED CCMP") < 0 || 2242 1.1 christos set_network(ifname, id, "eap", "WFA-UNAUTH-TLS") < 0 || 2243 1.1 christos set_network(ifname, id, "ocsp", "2") < 0 || 2244 1.1 christos set_network_quoted(ifname, id, "identity", osu_nai) < 0 || 2245 1.1 christos set_network_quoted(ifname, id, "ca_cert", fname) < 0) 2246 1.1 christos return -1; 2247 1.1.1.4 christos } else if (ssid2) { 2248 1.1.1.4 christos wpa_printf(MSG_INFO, "No OSU_NAI set for RSN[OSEN]"); 2249 1.1.1.4 christos write_summary(ctx, "No OSU_NAI set for RSN[OSEN]"); 2250 1.1.1.4 christos return -1; 2251 1.1 christos } else { 2252 1.1 christos if (set_network(ifname, id, "key_mgmt", "NONE") < 0) 2253 1.1 christos return -1; 2254 1.1 christos } 2255 1.1 christos 2256 1.1 christos mon = open_wpa_mon(ifname); 2257 1.1 christos if (mon == NULL) 2258 1.1 christos return -1; 2259 1.1 christos 2260 1.1 christos wpa_printf(MSG_INFO, "Associate with OSU SSID"); 2261 1.1 christos write_summary(ctx, "Associate with OSU SSID"); 2262 1.1 christos snprintf(buf, sizeof(buf), "SELECT_NETWORK %d", id); 2263 1.1 christos if (wpa_command(ifname, buf) < 0) 2264 1.1 christos return -1; 2265 1.1 christos 2266 1.1 christos res = get_wpa_cli_event(mon, "CTRL-EVENT-CONNECTED", 2267 1.1 christos buf, sizeof(buf)); 2268 1.1 christos 2269 1.1 christos wpa_ctrl_detach(mon); 2270 1.1 christos wpa_ctrl_close(mon); 2271 1.1 christos 2272 1.1 christos if (res < 0) { 2273 1.1.1.6 christos wpa_printf(MSG_INFO, "Could not connect to OSU network"); 2274 1.1 christos write_summary(ctx, "Could not connect to OSU network"); 2275 1.1 christos wpa_printf(MSG_INFO, "Remove OSU network connection"); 2276 1.1 christos snprintf(buf, sizeof(buf), "REMOVE_NETWORK %d", id); 2277 1.1 christos wpa_command(ifname, buf); 2278 1.1 christos return -1; 2279 1.1 christos } 2280 1.1 christos 2281 1.1 christos write_summary(ctx, "Waiting for IP address for subscription registration"); 2282 1.1 christos if (wait_ip_addr(ifname, 15) < 0) { 2283 1.1 christos wpa_printf(MSG_INFO, "Could not get IP address for WLAN - try connection anyway"); 2284 1.1 christos } 2285 1.1 christos 2286 1.1 christos if (no_prod_assoc) { 2287 1.1 christos if (res < 0) 2288 1.1 christos return -1; 2289 1.1 christos wpa_printf(MSG_INFO, "No production connection used for testing purposes"); 2290 1.1 christos write_summary(ctx, "No production connection used for testing purposes"); 2291 1.1 christos return 0; 2292 1.1 christos } 2293 1.1 christos 2294 1.1 christos ctx->no_reconnect = 1; 2295 1.1.1.3 christos if (methods & 0x02) { 2296 1.1.1.3 christos wpa_printf(MSG_DEBUG, "Calling cmd_prov from osu_connect"); 2297 1.1 christos res = cmd_prov(ctx, url); 2298 1.1.1.3 christos } else if (methods & 0x01) { 2299 1.1.1.3 christos wpa_printf(MSG_DEBUG, 2300 1.1.1.3 christos "Calling cmd_oma_dm_prov from osu_connect"); 2301 1.1 christos res = cmd_oma_dm_prov(ctx, url); 2302 1.1.1.3 christos } 2303 1.1 christos 2304 1.1 christos wpa_printf(MSG_INFO, "Remove OSU network connection"); 2305 1.1 christos write_summary(ctx, "Remove OSU network connection"); 2306 1.1 christos snprintf(buf, sizeof(buf), "REMOVE_NETWORK %d", id); 2307 1.1 christos wpa_command(ifname, buf); 2308 1.1 christos 2309 1.1 christos if (res < 0) 2310 1.1 christos return -1; 2311 1.1 christos 2312 1.1 christos wpa_printf(MSG_INFO, "Requesting reconnection with updated configuration"); 2313 1.1 christos write_summary(ctx, "Requesting reconnection with updated configuration"); 2314 1.1 christos if (wpa_command(ctx->ifname, "INTERWORKING_SELECT auto") < 0) { 2315 1.1 christos wpa_printf(MSG_INFO, "Failed to request wpa_supplicant to reconnect"); 2316 1.1 christos write_summary(ctx, "Failed to request wpa_supplicant to reconnect"); 2317 1.1 christos return -1; 2318 1.1 christos } 2319 1.1 christos 2320 1.1 christos return 0; 2321 1.1 christos } 2322 1.1 christos 2323 1.1 christos 2324 1.1 christos static int cmd_osu_select(struct hs20_osu_client *ctx, const char *dir, 2325 1.1 christos int connect, int no_prod_assoc, 2326 1.1 christos const char *friendly_name) 2327 1.1 christos { 2328 1.1 christos char fname[255]; 2329 1.1 christos FILE *f; 2330 1.1 christos struct osu_data *osu = NULL, *last = NULL; 2331 1.1.1.4 christos size_t osu_count = 0, i, j; 2332 1.1 christos int ret; 2333 1.1 christos 2334 1.1 christos write_summary(ctx, "OSU provider selection"); 2335 1.1 christos 2336 1.1 christos if (dir == NULL) { 2337 1.1 christos wpa_printf(MSG_INFO, "Missing dir parameter to osu_select"); 2338 1.1 christos return -1; 2339 1.1 christos } 2340 1.1 christos 2341 1.1 christos snprintf(fname, sizeof(fname), "%s/osu-providers.txt", dir); 2342 1.1 christos osu = parse_osu_providers(fname, &osu_count); 2343 1.1 christos if (osu == NULL) { 2344 1.1.1.3 christos wpa_printf(MSG_INFO, "Could not find any OSU providers from %s", 2345 1.1 christos fname); 2346 1.1 christos write_result(ctx, "No OSU providers available"); 2347 1.1 christos return -1; 2348 1.1 christos } 2349 1.1 christos 2350 1.1 christos if (friendly_name) { 2351 1.1 christos for (i = 0; i < osu_count; i++) { 2352 1.1 christos last = &osu[i]; 2353 1.1 christos for (j = 0; j < last->friendly_name_count; j++) { 2354 1.1 christos if (os_strcmp(last->friendly_name[j].text, 2355 1.1 christos friendly_name) == 0) 2356 1.1 christos break; 2357 1.1 christos } 2358 1.1 christos if (j < last->friendly_name_count) 2359 1.1 christos break; 2360 1.1 christos } 2361 1.1 christos if (i == osu_count) { 2362 1.1 christos wpa_printf(MSG_INFO, "Requested operator friendly name '%s' not found in the list of available providers", 2363 1.1 christos friendly_name); 2364 1.1 christos write_summary(ctx, "Requested operator friendly name '%s' not found in the list of available providers", 2365 1.1 christos friendly_name); 2366 1.1 christos free(osu); 2367 1.1 christos return -1; 2368 1.1 christos } 2369 1.1 christos 2370 1.1 christos wpa_printf(MSG_INFO, "OSU Provider selected based on requested operator friendly name '%s'", 2371 1.1 christos friendly_name); 2372 1.1 christos write_summary(ctx, "OSU Provider selected based on requested operator friendly name '%s'", 2373 1.1 christos friendly_name); 2374 1.1 christos ret = i + 1; 2375 1.1 christos goto selected; 2376 1.1 christos } 2377 1.1 christos 2378 1.1 christos snprintf(fname, sizeof(fname), "%s/osu-providers.html", dir); 2379 1.1 christos f = fopen(fname, "w"); 2380 1.1 christos if (f == NULL) { 2381 1.1 christos wpa_printf(MSG_INFO, "Could not open %s", fname); 2382 1.1 christos free(osu); 2383 1.1 christos return -1; 2384 1.1 christos } 2385 1.1 christos 2386 1.1 christos fprintf(f, "<html><head>" 2387 1.1 christos "<meta http-equiv=\"Content-type\" content=\"text/html; " 2388 1.1 christos "charset=utf-8\"<title>Select service operator</title>" 2389 1.1 christos "</head><body><h1>Select service operator</h1>\n"); 2390 1.1 christos 2391 1.1 christos if (osu_count == 0) 2392 1.1 christos fprintf(f, "No online signup available\n"); 2393 1.1 christos 2394 1.1 christos for (i = 0; i < osu_count; i++) { 2395 1.1 christos last = &osu[i]; 2396 1.1 christos #ifdef ANDROID 2397 1.1 christos fprintf(f, "<p>\n" 2398 1.1 christos "<a href=\"http://localhost:12345/osu/%d\">" 2399 1.1 christos "<table><tr><td>", (int) i + 1); 2400 1.1 christos #else /* ANDROID */ 2401 1.1 christos fprintf(f, "<p>\n" 2402 1.1 christos "<a href=\"osu://%d\">" 2403 1.1 christos "<table><tr><td>", (int) i + 1); 2404 1.1 christos #endif /* ANDROID */ 2405 1.1 christos for (j = 0; j < last->icon_count; j++) { 2406 1.1 christos fprintf(f, "<img src=\"osu-icon-%d.%s\">\n", 2407 1.1 christos last->icon[j].id, 2408 1.1 christos strcasecmp(last->icon[j].mime_type, 2409 1.1 christos "image/png") == 0 ? "png" : "icon"); 2410 1.1 christos } 2411 1.1 christos fprintf(f, "<td>"); 2412 1.1 christos for (j = 0; j < last->friendly_name_count; j++) { 2413 1.1 christos fprintf(f, "<small>[%s]</small> %s<br>\n", 2414 1.1 christos last->friendly_name[j].lang, 2415 1.1 christos last->friendly_name[j].text); 2416 1.1 christos } 2417 1.1 christos fprintf(f, "<tr><td colspan=2>"); 2418 1.1 christos for (j = 0; j < last->serv_desc_count; j++) { 2419 1.1 christos fprintf(f, "<small>[%s]</small> %s<br>\n", 2420 1.1 christos last->serv_desc[j].lang, 2421 1.1 christos last->serv_desc[j].text); 2422 1.1 christos } 2423 1.1 christos fprintf(f, "</table></a><br><small>BSSID: %s<br>\n" 2424 1.1 christos "SSID: %s<br>\n", 2425 1.1 christos last->bssid, last->osu_ssid); 2426 1.1.1.4 christos if (last->osu_ssid2[0]) 2427 1.1.1.4 christos fprintf(f, "SSID2: %s<br>\n", last->osu_ssid2); 2428 1.1.1.3 christos if (last->osu_nai[0]) 2429 1.1 christos fprintf(f, "NAI: %s<br>\n", last->osu_nai); 2430 1.1.1.4 christos if (last->osu_nai2[0]) 2431 1.1.1.4 christos fprintf(f, "NAI2: %s<br>\n", last->osu_nai2); 2432 1.1 christos fprintf(f, "URL: %s<br>\n" 2433 1.1 christos "methods:%s%s<br>\n" 2434 1.1 christos "</small></p>\n", 2435 1.1 christos last->url, 2436 1.1 christos last->methods & 0x01 ? " OMA-DM" : "", 2437 1.1 christos last->methods & 0x02 ? " SOAP-XML-SPP" : ""); 2438 1.1 christos } 2439 1.1 christos 2440 1.1 christos fprintf(f, "</body></html>\n"); 2441 1.1 christos 2442 1.1 christos fclose(f); 2443 1.1 christos 2444 1.1 christos snprintf(fname, sizeof(fname), "file://%s/osu-providers.html", dir); 2445 1.1 christos write_summary(ctx, "Start web browser with OSU provider selection page"); 2446 1.1.1.6 christos ret = hs20_web_browser(fname, 0); 2447 1.1 christos 2448 1.1 christos selected: 2449 1.1 christos if (ret > 0 && (size_t) ret <= osu_count) { 2450 1.1 christos char *data; 2451 1.1 christos size_t data_len; 2452 1.1 christos 2453 1.1 christos wpa_printf(MSG_INFO, "Selected OSU id=%d", ret); 2454 1.1 christos last = &osu[ret - 1]; 2455 1.1 christos ret = 0; 2456 1.1 christos wpa_printf(MSG_INFO, "BSSID: %s", last->bssid); 2457 1.1 christos wpa_printf(MSG_INFO, "SSID: %s", last->osu_ssid); 2458 1.1.1.4 christos if (last->osu_ssid2[0]) 2459 1.1.1.4 christos wpa_printf(MSG_INFO, "SSID2: %s", last->osu_ssid2); 2460 1.1 christos wpa_printf(MSG_INFO, "URL: %s", last->url); 2461 1.1 christos write_summary(ctx, "Selected OSU provider id=%d BSSID=%s SSID=%s URL=%s", 2462 1.1 christos ret, last->bssid, last->osu_ssid, last->url); 2463 1.1 christos 2464 1.1 christos ctx->friendly_name_count = last->friendly_name_count; 2465 1.1 christos for (j = 0; j < last->friendly_name_count; j++) { 2466 1.1 christos wpa_printf(MSG_INFO, "FRIENDLY_NAME: [%s]%s", 2467 1.1 christos last->friendly_name[j].lang, 2468 1.1 christos last->friendly_name[j].text); 2469 1.1 christos os_strlcpy(ctx->friendly_name[j].lang, 2470 1.1 christos last->friendly_name[j].lang, 2471 1.1 christos sizeof(ctx->friendly_name[j].lang)); 2472 1.1 christos os_strlcpy(ctx->friendly_name[j].text, 2473 1.1 christos last->friendly_name[j].text, 2474 1.1 christos sizeof(ctx->friendly_name[j].text)); 2475 1.1 christos } 2476 1.1 christos 2477 1.1 christos ctx->icon_count = last->icon_count; 2478 1.1 christos for (j = 0; j < last->icon_count; j++) { 2479 1.1 christos char fname[256]; 2480 1.1 christos 2481 1.1 christos os_snprintf(fname, sizeof(fname), "%s/osu-icon-%d.%s", 2482 1.1 christos dir, last->icon[j].id, 2483 1.1 christos strcasecmp(last->icon[j].mime_type, 2484 1.1 christos "image/png") == 0 ? 2485 1.1 christos "png" : "icon"); 2486 1.1 christos wpa_printf(MSG_INFO, "ICON: %s (%s)", 2487 1.1 christos fname, last->icon[j].filename); 2488 1.1 christos os_strlcpy(ctx->icon_filename[j], 2489 1.1 christos last->icon[j].filename, 2490 1.1 christos sizeof(ctx->icon_filename[j])); 2491 1.1 christos 2492 1.1 christos data = os_readfile(fname, &data_len); 2493 1.1 christos if (data) { 2494 1.1 christos sha256_vector(1, (const u8 **) &data, &data_len, 2495 1.1 christos ctx->icon_hash[j]); 2496 1.1 christos os_free(data); 2497 1.1 christos } 2498 1.1 christos } 2499 1.1 christos 2500 1.1 christos if (connect == 2) { 2501 1.1.1.3 christos if (last->methods & 0x02) { 2502 1.1.1.3 christos wpa_printf(MSG_DEBUG, 2503 1.1.1.3 christos "Calling cmd_prov from cmd_osu_select"); 2504 1.1 christos ret = cmd_prov(ctx, last->url); 2505 1.1.1.3 christos } else if (last->methods & 0x01) { 2506 1.1.1.3 christos wpa_printf(MSG_DEBUG, 2507 1.1.1.3 christos "Calling cmd_oma_dm_prov from cmd_osu_select"); 2508 1.1 christos ret = cmd_oma_dm_prov(ctx, last->url); 2509 1.1.1.3 christos } else { 2510 1.1.1.3 christos wpa_printf(MSG_DEBUG, 2511 1.1.1.3 christos "No supported OSU provisioning method"); 2512 1.1 christos ret = -1; 2513 1.1.1.3 christos } 2514 1.1.1.4 christos } else if (connect) { 2515 1.1 christos ret = osu_connect(ctx, last->bssid, last->osu_ssid, 2516 1.1.1.4 christos last->osu_ssid2, 2517 1.1 christos last->url, last->methods, 2518 1.1.1.4 christos no_prod_assoc, last->osu_nai, 2519 1.1.1.4 christos last->osu_nai2); 2520 1.1.1.4 christos } 2521 1.1 christos } else 2522 1.1 christos ret = -1; 2523 1.1 christos 2524 1.1 christos free(osu); 2525 1.1 christos 2526 1.1 christos return ret; 2527 1.1 christos } 2528 1.1 christos 2529 1.1 christos 2530 1.1 christos static int cmd_signup(struct hs20_osu_client *ctx, int no_prod_assoc, 2531 1.1 christos const char *friendly_name) 2532 1.1 christos { 2533 1.1 christos char dir[255]; 2534 1.1 christos char fname[300], buf[400]; 2535 1.1 christos struct wpa_ctrl *mon; 2536 1.1 christos const char *ifname; 2537 1.1 christos int res; 2538 1.1 christos 2539 1.1 christos ifname = ctx->ifname; 2540 1.1 christos 2541 1.1 christos if (getcwd(dir, sizeof(dir)) == NULL) 2542 1.1 christos return -1; 2543 1.1 christos 2544 1.1 christos snprintf(fname, sizeof(fname), "%s/osu-info", dir); 2545 1.1.1.3 christos if (mkdir(fname, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) < 0 && 2546 1.1.1.3 christos errno != EEXIST) { 2547 1.1 christos wpa_printf(MSG_INFO, "mkdir(%s) failed: %s", 2548 1.1 christos fname, strerror(errno)); 2549 1.1 christos return -1; 2550 1.1 christos } 2551 1.1 christos 2552 1.1.1.4 christos android_update_permission(fname, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); 2553 1.1.1.3 christos 2554 1.1 christos snprintf(buf, sizeof(buf), "SET osu_dir %s", fname); 2555 1.1 christos if (wpa_command(ifname, buf) < 0) { 2556 1.1 christos wpa_printf(MSG_INFO, "Failed to configure osu_dir to wpa_supplicant"); 2557 1.1 christos return -1; 2558 1.1 christos } 2559 1.1 christos 2560 1.1 christos mon = open_wpa_mon(ifname); 2561 1.1 christos if (mon == NULL) 2562 1.1 christos return -1; 2563 1.1 christos 2564 1.1 christos wpa_printf(MSG_INFO, "Starting OSU fetch"); 2565 1.1 christos write_summary(ctx, "Starting OSU provider information fetch"); 2566 1.1 christos if (wpa_command(ifname, "FETCH_OSU") < 0) { 2567 1.1 christos wpa_printf(MSG_INFO, "Could not start OSU fetch"); 2568 1.1 christos wpa_ctrl_detach(mon); 2569 1.1 christos wpa_ctrl_close(mon); 2570 1.1 christos return -1; 2571 1.1 christos } 2572 1.1 christos res = get_wpa_cli_event(mon, "OSU provider fetch completed", 2573 1.1 christos buf, sizeof(buf)); 2574 1.1 christos 2575 1.1 christos wpa_ctrl_detach(mon); 2576 1.1 christos wpa_ctrl_close(mon); 2577 1.1 christos 2578 1.1 christos if (res < 0) { 2579 1.1 christos wpa_printf(MSG_INFO, "OSU fetch did not complete"); 2580 1.1 christos write_summary(ctx, "OSU fetch did not complete"); 2581 1.1 christos return -1; 2582 1.1 christos } 2583 1.1 christos wpa_printf(MSG_INFO, "OSU provider fetch completed"); 2584 1.1 christos 2585 1.1 christos return cmd_osu_select(ctx, fname, 1, no_prod_assoc, friendly_name); 2586 1.1 christos } 2587 1.1 christos 2588 1.1 christos 2589 1.1.1.2 christos static int cmd_sub_rem(struct hs20_osu_client *ctx, const char *address, 2590 1.1.1.2 christos const char *pps_fname, const char *ca_fname) 2591 1.1 christos { 2592 1.1 christos xml_node_t *pps, *node; 2593 1.1 christos char pps_fname_buf[300]; 2594 1.1 christos char ca_fname_buf[200]; 2595 1.1 christos char *cred_username = NULL; 2596 1.1 christos char *cred_password = NULL; 2597 1.1 christos char *sub_rem_uri = NULL; 2598 1.1 christos char client_cert_buf[200]; 2599 1.1 christos char *client_cert = NULL; 2600 1.1 christos char client_key_buf[200]; 2601 1.1 christos char *client_key = NULL; 2602 1.1 christos int spp; 2603 1.1 christos 2604 1.1 christos wpa_printf(MSG_INFO, "Subscription remediation requested with Server URL: %s", 2605 1.1 christos address); 2606 1.1 christos 2607 1.1 christos if (!pps_fname) { 2608 1.1 christos char buf[256]; 2609 1.1 christos wpa_printf(MSG_INFO, "Determining PPS file based on Home SP information"); 2610 1.1 christos if (os_strncmp(address, "fqdn=", 5) == 0) { 2611 1.1 christos wpa_printf(MSG_INFO, "Use requested FQDN from command line"); 2612 1.1 christos os_snprintf(buf, sizeof(buf), "%s", address + 5); 2613 1.1 christos address = NULL; 2614 1.1 christos } else if (get_wpa_status(ctx->ifname, "provisioning_sp", buf, 2615 1.1 christos sizeof(buf)) < 0) { 2616 1.1 christos wpa_printf(MSG_INFO, "Could not get provisioning Home SP FQDN from wpa_supplicant"); 2617 1.1.1.2 christos return -1; 2618 1.1 christos } 2619 1.1 christos os_free(ctx->fqdn); 2620 1.1 christos ctx->fqdn = os_strdup(buf); 2621 1.1 christos if (ctx->fqdn == NULL) 2622 1.1.1.2 christos return -1; 2623 1.1 christos wpa_printf(MSG_INFO, "Home SP FQDN for current credential: %s", 2624 1.1 christos buf); 2625 1.1 christos os_snprintf(pps_fname_buf, sizeof(pps_fname_buf), 2626 1.1 christos "SP/%s/pps.xml", ctx->fqdn); 2627 1.1 christos pps_fname = pps_fname_buf; 2628 1.1 christos 2629 1.1 christos os_snprintf(ca_fname_buf, sizeof(ca_fname_buf), "SP/%s/ca.pem", 2630 1.1 christos ctx->fqdn); 2631 1.1 christos ca_fname = ca_fname_buf; 2632 1.1 christos } 2633 1.1 christos 2634 1.1 christos if (!os_file_exists(pps_fname)) { 2635 1.1 christos wpa_printf(MSG_INFO, "PPS file '%s' does not exist or is not accessible", 2636 1.1 christos pps_fname); 2637 1.1.1.2 christos return -1; 2638 1.1 christos } 2639 1.1 christos wpa_printf(MSG_INFO, "Using PPS file: %s", pps_fname); 2640 1.1 christos 2641 1.1 christos if (ca_fname && !os_file_exists(ca_fname)) { 2642 1.1 christos wpa_printf(MSG_INFO, "CA file '%s' does not exist or is not accessible", 2643 1.1 christos ca_fname); 2644 1.1.1.2 christos return -1; 2645 1.1 christos } 2646 1.1 christos wpa_printf(MSG_INFO, "Using server trust root: %s", ca_fname); 2647 1.1 christos ctx->ca_fname = ca_fname; 2648 1.1 christos 2649 1.1 christos pps = node_from_file(ctx->xml, pps_fname); 2650 1.1 christos if (pps == NULL) { 2651 1.1 christos wpa_printf(MSG_INFO, "Could not read PPS MO"); 2652 1.1.1.2 christos return -1; 2653 1.1 christos } 2654 1.1 christos 2655 1.1 christos if (!ctx->fqdn) { 2656 1.1 christos char *tmp; 2657 1.1 christos node = get_child_node(ctx->xml, pps, "HomeSP/FQDN"); 2658 1.1 christos if (node == NULL) { 2659 1.1 christos wpa_printf(MSG_INFO, "No HomeSP/FQDN found from PPS"); 2660 1.1.1.2 christos return -1; 2661 1.1 christos } 2662 1.1 christos tmp = xml_node_get_text(ctx->xml, node); 2663 1.1 christos if (tmp == NULL) { 2664 1.1 christos wpa_printf(MSG_INFO, "No HomeSP/FQDN text found from PPS"); 2665 1.1.1.2 christos return -1; 2666 1.1 christos } 2667 1.1 christos ctx->fqdn = os_strdup(tmp); 2668 1.1 christos xml_node_get_text_free(ctx->xml, tmp); 2669 1.1 christos if (!ctx->fqdn) { 2670 1.1 christos wpa_printf(MSG_INFO, "No FQDN known"); 2671 1.1.1.2 christos return -1; 2672 1.1 christos } 2673 1.1 christos } 2674 1.1 christos 2675 1.1 christos node = get_child_node(ctx->xml, pps, 2676 1.1 christos "SubscriptionUpdate/UpdateMethod"); 2677 1.1 christos if (node) { 2678 1.1 christos char *tmp; 2679 1.1 christos tmp = xml_node_get_text(ctx->xml, node); 2680 1.1 christos if (tmp && os_strcasecmp(tmp, "OMA-DM-ClientInitiated") == 0) 2681 1.1 christos spp = 0; 2682 1.1 christos else 2683 1.1 christos spp = 1; 2684 1.1 christos } else { 2685 1.1 christos wpa_printf(MSG_INFO, "No UpdateMethod specified - assume SPP"); 2686 1.1 christos spp = 1; 2687 1.1 christos } 2688 1.1 christos 2689 1.1 christos get_user_pw(ctx, pps, "SubscriptionUpdate/UsernamePassword", 2690 1.1 christos &cred_username, &cred_password); 2691 1.1 christos if (cred_username) 2692 1.1 christos wpa_printf(MSG_INFO, "Using username: %s", cred_username); 2693 1.1 christos if (cred_password) 2694 1.1 christos wpa_printf(MSG_DEBUG, "Using password: %s", cred_password); 2695 1.1 christos 2696 1.1 christos if (cred_username == NULL && cred_password == NULL && 2697 1.1 christos get_child_node(ctx->xml, pps, "Credential/DigitalCertificate")) { 2698 1.1 christos wpa_printf(MSG_INFO, "Using client certificate"); 2699 1.1 christos os_snprintf(client_cert_buf, sizeof(client_cert_buf), 2700 1.1 christos "SP/%s/client-cert.pem", ctx->fqdn); 2701 1.1 christos client_cert = client_cert_buf; 2702 1.1 christos os_snprintf(client_key_buf, sizeof(client_key_buf), 2703 1.1 christos "SP/%s/client-key.pem", ctx->fqdn); 2704 1.1 christos client_key = client_key_buf; 2705 1.1 christos ctx->client_cert_present = 1; 2706 1.1 christos } 2707 1.1 christos 2708 1.1 christos node = get_child_node(ctx->xml, pps, "SubscriptionUpdate/URI"); 2709 1.1 christos if (node) { 2710 1.1 christos sub_rem_uri = xml_node_get_text(ctx->xml, node); 2711 1.1 christos if (sub_rem_uri && 2712 1.1 christos (!address || os_strcmp(address, sub_rem_uri) != 0)) { 2713 1.1 christos wpa_printf(MSG_INFO, "Override sub rem URI based on PPS: %s", 2714 1.1 christos sub_rem_uri); 2715 1.1 christos address = sub_rem_uri; 2716 1.1 christos } 2717 1.1 christos } 2718 1.1 christos if (!address) { 2719 1.1 christos wpa_printf(MSG_INFO, "Server URL not known"); 2720 1.1.1.2 christos return -1; 2721 1.1 christos } 2722 1.1 christos 2723 1.1 christos write_summary(ctx, "Wait for IP address for subscriptiom remediation"); 2724 1.1 christos wpa_printf(MSG_INFO, "Wait for IP address before starting subscription remediation"); 2725 1.1 christos 2726 1.1 christos if (wait_ip_addr(ctx->ifname, 15) < 0) { 2727 1.1 christos wpa_printf(MSG_INFO, "Could not get IP address for WLAN - try connection anyway"); 2728 1.1 christos } 2729 1.1 christos 2730 1.1 christos if (spp) 2731 1.1 christos spp_sub_rem(ctx, address, pps_fname, 2732 1.1 christos client_cert, client_key, 2733 1.1 christos cred_username, cred_password, pps); 2734 1.1 christos else 2735 1.1 christos oma_dm_sub_rem(ctx, address, pps_fname, 2736 1.1 christos client_cert, client_key, 2737 1.1 christos cred_username, cred_password, pps); 2738 1.1 christos 2739 1.1 christos xml_node_get_text_free(ctx->xml, sub_rem_uri); 2740 1.1 christos xml_node_get_text_free(ctx->xml, cred_username); 2741 1.1 christos str_clear_free(cred_password); 2742 1.1 christos xml_node_free(ctx->xml, pps); 2743 1.1.1.2 christos return 0; 2744 1.1 christos } 2745 1.1 christos 2746 1.1 christos 2747 1.1 christos static int cmd_pol_upd(struct hs20_osu_client *ctx, const char *address, 2748 1.1 christos const char *pps_fname, const char *ca_fname) 2749 1.1 christos { 2750 1.1 christos xml_node_t *pps; 2751 1.1 christos xml_node_t *node; 2752 1.1 christos char pps_fname_buf[300]; 2753 1.1 christos char ca_fname_buf[200]; 2754 1.1 christos char *uri = NULL; 2755 1.1 christos char *cred_username = NULL; 2756 1.1 christos char *cred_password = NULL; 2757 1.1 christos char client_cert_buf[200]; 2758 1.1 christos char *client_cert = NULL; 2759 1.1 christos char client_key_buf[200]; 2760 1.1 christos char *client_key = NULL; 2761 1.1 christos int spp; 2762 1.1 christos 2763 1.1 christos wpa_printf(MSG_INFO, "Policy update requested"); 2764 1.1 christos 2765 1.1 christos if (!pps_fname) { 2766 1.1 christos char buf[256]; 2767 1.1.1.5 christos int res; 2768 1.1.1.5 christos 2769 1.1 christos wpa_printf(MSG_INFO, "Determining PPS file based on Home SP information"); 2770 1.1.1.3 christos if (address && os_strncmp(address, "fqdn=", 5) == 0) { 2771 1.1 christos wpa_printf(MSG_INFO, "Use requested FQDN from command line"); 2772 1.1 christos os_snprintf(buf, sizeof(buf), "%s", address + 5); 2773 1.1 christos address = NULL; 2774 1.1 christos } else if (get_wpa_status(ctx->ifname, "provisioning_sp", buf, 2775 1.1 christos sizeof(buf)) < 0) { 2776 1.1 christos wpa_printf(MSG_INFO, "Could not get provisioning Home SP FQDN from wpa_supplicant"); 2777 1.1 christos return -1; 2778 1.1 christos } 2779 1.1 christos os_free(ctx->fqdn); 2780 1.1 christos ctx->fqdn = os_strdup(buf); 2781 1.1 christos if (ctx->fqdn == NULL) 2782 1.1 christos return -1; 2783 1.1 christos wpa_printf(MSG_INFO, "Home SP FQDN for current credential: %s", 2784 1.1 christos buf); 2785 1.1 christos os_snprintf(pps_fname_buf, sizeof(pps_fname_buf), 2786 1.1 christos "SP/%s/pps.xml", ctx->fqdn); 2787 1.1 christos pps_fname = pps_fname_buf; 2788 1.1 christos 2789 1.1.1.5 christos res = os_snprintf(ca_fname_buf, sizeof(ca_fname_buf), 2790 1.1.1.5 christos "SP/%s/ca.pem", buf); 2791 1.1.1.5 christos if (os_snprintf_error(sizeof(ca_fname_buf), res)) { 2792 1.1.1.5 christos os_free(ctx->fqdn); 2793 1.1.1.5 christos ctx->fqdn = NULL; 2794 1.1.1.5 christos return -1; 2795 1.1.1.5 christos } 2796 1.1 christos ca_fname = ca_fname_buf; 2797 1.1 christos } 2798 1.1 christos 2799 1.1 christos if (!os_file_exists(pps_fname)) { 2800 1.1 christos wpa_printf(MSG_INFO, "PPS file '%s' does not exist or is not accessible", 2801 1.1 christos pps_fname); 2802 1.1 christos return -1; 2803 1.1 christos } 2804 1.1 christos wpa_printf(MSG_INFO, "Using PPS file: %s", pps_fname); 2805 1.1 christos 2806 1.1 christos if (ca_fname && !os_file_exists(ca_fname)) { 2807 1.1 christos wpa_printf(MSG_INFO, "CA file '%s' does not exist or is not accessible", 2808 1.1 christos ca_fname); 2809 1.1 christos return -1; 2810 1.1 christos } 2811 1.1 christos wpa_printf(MSG_INFO, "Using server trust root: %s", ca_fname); 2812 1.1 christos ctx->ca_fname = ca_fname; 2813 1.1 christos 2814 1.1 christos pps = node_from_file(ctx->xml, pps_fname); 2815 1.1 christos if (pps == NULL) { 2816 1.1 christos wpa_printf(MSG_INFO, "Could not read PPS MO"); 2817 1.1 christos return -1; 2818 1.1 christos } 2819 1.1 christos 2820 1.1 christos if (!ctx->fqdn) { 2821 1.1 christos char *tmp; 2822 1.1 christos node = get_child_node(ctx->xml, pps, "HomeSP/FQDN"); 2823 1.1 christos if (node == NULL) { 2824 1.1 christos wpa_printf(MSG_INFO, "No HomeSP/FQDN found from PPS"); 2825 1.1 christos return -1; 2826 1.1 christos } 2827 1.1 christos tmp = xml_node_get_text(ctx->xml, node); 2828 1.1 christos if (tmp == NULL) { 2829 1.1 christos wpa_printf(MSG_INFO, "No HomeSP/FQDN text found from PPS"); 2830 1.1 christos return -1; 2831 1.1 christos } 2832 1.1 christos ctx->fqdn = os_strdup(tmp); 2833 1.1 christos xml_node_get_text_free(ctx->xml, tmp); 2834 1.1 christos if (!ctx->fqdn) { 2835 1.1 christos wpa_printf(MSG_INFO, "No FQDN known"); 2836 1.1 christos return -1; 2837 1.1 christos } 2838 1.1 christos } 2839 1.1 christos 2840 1.1 christos node = get_child_node(ctx->xml, pps, 2841 1.1 christos "Policy/PolicyUpdate/UpdateMethod"); 2842 1.1 christos if (node) { 2843 1.1 christos char *tmp; 2844 1.1 christos tmp = xml_node_get_text(ctx->xml, node); 2845 1.1 christos if (tmp && os_strcasecmp(tmp, "OMA-DM-ClientInitiated") == 0) 2846 1.1 christos spp = 0; 2847 1.1 christos else 2848 1.1 christos spp = 1; 2849 1.1 christos } else { 2850 1.1 christos wpa_printf(MSG_INFO, "No UpdateMethod specified - assume SPP"); 2851 1.1 christos spp = 1; 2852 1.1 christos } 2853 1.1 christos 2854 1.1 christos get_user_pw(ctx, pps, "Policy/PolicyUpdate/UsernamePassword", 2855 1.1 christos &cred_username, &cred_password); 2856 1.1 christos if (cred_username) 2857 1.1 christos wpa_printf(MSG_INFO, "Using username: %s", cred_username); 2858 1.1 christos if (cred_password) 2859 1.1 christos wpa_printf(MSG_DEBUG, "Using password: %s", cred_password); 2860 1.1 christos 2861 1.1 christos if (cred_username == NULL && cred_password == NULL && 2862 1.1 christos get_child_node(ctx->xml, pps, "Credential/DigitalCertificate")) { 2863 1.1 christos wpa_printf(MSG_INFO, "Using client certificate"); 2864 1.1 christos os_snprintf(client_cert_buf, sizeof(client_cert_buf), 2865 1.1 christos "SP/%s/client-cert.pem", ctx->fqdn); 2866 1.1 christos client_cert = client_cert_buf; 2867 1.1 christos os_snprintf(client_key_buf, sizeof(client_key_buf), 2868 1.1 christos "SP/%s/client-key.pem", ctx->fqdn); 2869 1.1 christos client_key = client_key_buf; 2870 1.1 christos } 2871 1.1 christos 2872 1.1 christos if (!address) { 2873 1.1 christos node = get_child_node(ctx->xml, pps, "Policy/PolicyUpdate/URI"); 2874 1.1 christos if (node) { 2875 1.1 christos uri = xml_node_get_text(ctx->xml, node); 2876 1.1 christos wpa_printf(MSG_INFO, "URI based on PPS: %s", uri); 2877 1.1 christos address = uri; 2878 1.1 christos } 2879 1.1 christos } 2880 1.1 christos if (!address) { 2881 1.1 christos wpa_printf(MSG_INFO, "Server URL not known"); 2882 1.1 christos return -1; 2883 1.1 christos } 2884 1.1 christos 2885 1.1 christos if (spp) 2886 1.1 christos spp_pol_upd(ctx, address, pps_fname, 2887 1.1 christos client_cert, client_key, 2888 1.1 christos cred_username, cred_password, pps); 2889 1.1 christos else 2890 1.1 christos oma_dm_pol_upd(ctx, address, pps_fname, 2891 1.1 christos client_cert, client_key, 2892 1.1 christos cred_username, cred_password, pps); 2893 1.1 christos 2894 1.1 christos xml_node_get_text_free(ctx->xml, uri); 2895 1.1 christos xml_node_get_text_free(ctx->xml, cred_username); 2896 1.1 christos str_clear_free(cred_password); 2897 1.1 christos xml_node_free(ctx->xml, pps); 2898 1.1 christos 2899 1.1 christos return 0; 2900 1.1 christos } 2901 1.1 christos 2902 1.1 christos 2903 1.1 christos static char * get_hostname(const char *url) 2904 1.1 christos { 2905 1.1 christos const char *pos, *end, *end2; 2906 1.1 christos char *ret; 2907 1.1 christos 2908 1.1 christos if (url == NULL) 2909 1.1 christos return NULL; 2910 1.1 christos 2911 1.1 christos pos = os_strchr(url, '/'); 2912 1.1 christos if (pos == NULL) 2913 1.1 christos return NULL; 2914 1.1 christos pos++; 2915 1.1 christos if (*pos != '/') 2916 1.1 christos return NULL; 2917 1.1 christos pos++; 2918 1.1 christos 2919 1.1 christos end = os_strchr(pos, '/'); 2920 1.1 christos end2 = os_strchr(pos, ':'); 2921 1.1.1.3 christos if ((end && end2 && end2 < end) || (!end && end2)) 2922 1.1 christos end = end2; 2923 1.1 christos if (end) 2924 1.1 christos end--; 2925 1.1 christos else { 2926 1.1 christos end = pos; 2927 1.1 christos while (*end) 2928 1.1 christos end++; 2929 1.1 christos if (end > pos) 2930 1.1 christos end--; 2931 1.1 christos } 2932 1.1 christos 2933 1.1 christos ret = os_malloc(end - pos + 2); 2934 1.1 christos if (ret == NULL) 2935 1.1 christos return NULL; 2936 1.1 christos 2937 1.1 christos os_memcpy(ret, pos, end - pos + 1); 2938 1.1 christos ret[end - pos + 1] = '\0'; 2939 1.1 christos 2940 1.1 christos return ret; 2941 1.1 christos } 2942 1.1 christos 2943 1.1 christos 2944 1.1 christos static int osu_cert_cb(void *_ctx, struct http_cert *cert) 2945 1.1 christos { 2946 1.1 christos struct hs20_osu_client *ctx = _ctx; 2947 1.1.1.6 christos size_t i, j; 2948 1.1 christos int found; 2949 1.1 christos char *host = NULL; 2950 1.1 christos 2951 1.1.1.6 christos wpa_printf(MSG_INFO, "osu_cert_cb(osu_cert_validation=%d, url=%s server_url=%s)", 2952 1.1.1.6 christos !ctx->no_osu_cert_validation, cert->url ? cert->url : "N/A", 2953 1.1.1.6 christos ctx->server_url); 2954 1.1 christos 2955 1.1.1.6 christos if (ctx->no_osu_cert_validation && cert->url) 2956 1.1.1.6 christos host = get_hostname(cert->url); 2957 1.1.1.6 christos else 2958 1.1.1.6 christos host = get_hostname(ctx->server_url); 2959 1.1 christos 2960 1.1.1.6 christos if (!ctx->no_osu_cert_validation) { 2961 1.1.1.6 christos for (i = 0; i < ctx->server_dnsname_count; i++) 2962 1.1.1.6 christos os_free(ctx->server_dnsname[i]); 2963 1.1.1.6 christos os_free(ctx->server_dnsname); 2964 1.1.1.6 christos ctx->server_dnsname = os_calloc(cert->num_dnsname, 2965 1.1.1.6 christos sizeof(char *)); 2966 1.1.1.6 christos ctx->server_dnsname_count = 0; 2967 1.1.1.6 christos } 2968 1.1 christos 2969 1.1 christos found = 0; 2970 1.1 christos for (i = 0; i < cert->num_dnsname; i++) { 2971 1.1.1.6 christos if (!ctx->no_osu_cert_validation && ctx->server_dnsname) { 2972 1.1 christos ctx->server_dnsname[ctx->server_dnsname_count] = 2973 1.1 christos os_strdup(cert->dnsname[i]); 2974 1.1 christos if (ctx->server_dnsname[ctx->server_dnsname_count]) 2975 1.1 christos ctx->server_dnsname_count++; 2976 1.1 christos } 2977 1.1 christos if (host && os_strcasecmp(host, cert->dnsname[i]) == 0) 2978 1.1 christos found = 1; 2979 1.1 christos wpa_printf(MSG_INFO, "dNSName '%s'", cert->dnsname[i]); 2980 1.1 christos } 2981 1.1 christos 2982 1.1 christos if (host && !found) { 2983 1.1 christos wpa_printf(MSG_INFO, "Server name from URL (%s) did not match any dNSName - abort connection", 2984 1.1 christos host); 2985 1.1 christos write_result(ctx, "Server name from URL (%s) did not match any dNSName - abort connection", 2986 1.1 christos host); 2987 1.1 christos os_free(host); 2988 1.1 christos return -1; 2989 1.1 christos } 2990 1.1 christos 2991 1.1 christos os_free(host); 2992 1.1 christos 2993 1.1 christos for (i = 0; i < cert->num_othername; i++) { 2994 1.1 christos if (os_strcmp(cert->othername[i].oid, 2995 1.1 christos "1.3.6.1.4.1.40808.1.1.1") == 0) { 2996 1.1 christos wpa_hexdump_ascii(MSG_INFO, 2997 1.1 christos "id-wfa-hotspot-friendlyName", 2998 1.1 christos cert->othername[i].data, 2999 1.1 christos cert->othername[i].len); 3000 1.1 christos } 3001 1.1 christos } 3002 1.1 christos 3003 1.1 christos for (j = 0; !ctx->no_osu_cert_validation && 3004 1.1 christos j < ctx->friendly_name_count; j++) { 3005 1.1 christos int found = 0; 3006 1.1 christos for (i = 0; i < cert->num_othername; i++) { 3007 1.1 christos if (os_strcmp(cert->othername[i].oid, 3008 1.1 christos "1.3.6.1.4.1.40808.1.1.1") != 0) 3009 1.1 christos continue; 3010 1.1 christos if (cert->othername[i].len < 3) 3011 1.1 christos continue; 3012 1.1 christos if (os_strncasecmp((char *) cert->othername[i].data, 3013 1.1 christos ctx->friendly_name[j].lang, 3) != 0) 3014 1.1 christos continue; 3015 1.1 christos if (os_strncmp((char *) cert->othername[i].data + 3, 3016 1.1 christos ctx->friendly_name[j].text, 3017 1.1 christos cert->othername[i].len - 3) == 0) { 3018 1.1 christos found = 1; 3019 1.1 christos break; 3020 1.1 christos } 3021 1.1 christos } 3022 1.1 christos 3023 1.1 christos if (!found) { 3024 1.1 christos wpa_printf(MSG_INFO, "No friendly name match found for '[%s]%s'", 3025 1.1 christos ctx->friendly_name[j].lang, 3026 1.1 christos ctx->friendly_name[j].text); 3027 1.1 christos write_result(ctx, "No friendly name match found for '[%s]%s'", 3028 1.1 christos ctx->friendly_name[j].lang, 3029 1.1 christos ctx->friendly_name[j].text); 3030 1.1 christos return -1; 3031 1.1 christos } 3032 1.1 christos } 3033 1.1 christos 3034 1.1 christos for (i = 0; i < cert->num_logo; i++) { 3035 1.1 christos struct http_logo *logo = &cert->logo[i]; 3036 1.1 christos 3037 1.1 christos wpa_printf(MSG_INFO, "logo hash alg %s uri '%s'", 3038 1.1 christos logo->alg_oid, logo->uri); 3039 1.1 christos wpa_hexdump_ascii(MSG_INFO, "hashValue", 3040 1.1 christos logo->hash, logo->hash_len); 3041 1.1 christos } 3042 1.1 christos 3043 1.1 christos for (j = 0; !ctx->no_osu_cert_validation && j < ctx->icon_count; j++) { 3044 1.1 christos int found = 0; 3045 1.1 christos char *name = ctx->icon_filename[j]; 3046 1.1 christos size_t name_len = os_strlen(name); 3047 1.1 christos 3048 1.1.1.3 christos wpa_printf(MSG_INFO, 3049 1.1.1.6 christos "[%zu] Looking for icon file name '%s' match", 3050 1.1.1.3 christos j, name); 3051 1.1 christos for (i = 0; i < cert->num_logo; i++) { 3052 1.1 christos struct http_logo *logo = &cert->logo[i]; 3053 1.1 christos size_t uri_len = os_strlen(logo->uri); 3054 1.1 christos char *pos; 3055 1.1 christos 3056 1.1.1.3 christos wpa_printf(MSG_INFO, 3057 1.1.1.6 christos "[%zu] Comparing to '%s' uri_len=%d name_len=%d", 3058 1.1.1.3 christos i, logo->uri, (int) uri_len, (int) name_len); 3059 1.1.1.3 christos if (uri_len < 1 + name_len) { 3060 1.1.1.3 christos wpa_printf(MSG_INFO, "URI Length is too short"); 3061 1.1 christos continue; 3062 1.1.1.3 christos } 3063 1.1 christos pos = &logo->uri[uri_len - name_len - 1]; 3064 1.1 christos if (*pos != '/') 3065 1.1 christos continue; 3066 1.1 christos pos++; 3067 1.1 christos if (os_strcmp(pos, name) == 0) { 3068 1.1 christos found = 1; 3069 1.1 christos break; 3070 1.1 christos } 3071 1.1 christos } 3072 1.1 christos 3073 1.1 christos if (!found) { 3074 1.1 christos wpa_printf(MSG_INFO, "No icon filename match found for '%s'", 3075 1.1 christos name); 3076 1.1 christos write_result(ctx, 3077 1.1 christos "No icon filename match found for '%s'", 3078 1.1 christos name); 3079 1.1 christos return -1; 3080 1.1 christos } 3081 1.1 christos } 3082 1.1 christos 3083 1.1 christos for (j = 0; !ctx->no_osu_cert_validation && j < ctx->icon_count; j++) { 3084 1.1 christos int found = 0; 3085 1.1 christos 3086 1.1 christos for (i = 0; i < cert->num_logo; i++) { 3087 1.1 christos struct http_logo *logo = &cert->logo[i]; 3088 1.1 christos 3089 1.1.1.3 christos if (logo->hash_len != 32) { 3090 1.1.1.3 christos wpa_printf(MSG_INFO, 3091 1.1.1.6 christos "[%zu][%zu] Icon hash length invalid (should be 32): %d", 3092 1.1.1.3 christos j, i, (int) logo->hash_len); 3093 1.1 christos continue; 3094 1.1.1.3 christos } 3095 1.1 christos if (os_memcmp(logo->hash, ctx->icon_hash[j], 32) == 0) { 3096 1.1 christos found = 1; 3097 1.1 christos break; 3098 1.1 christos } 3099 1.1.1.3 christos 3100 1.1.1.3 christos wpa_printf(MSG_DEBUG, 3101 1.1.1.6 christos "[%zu][%zu] Icon hash did not match", j, i); 3102 1.1.1.3 christos wpa_hexdump_ascii(MSG_DEBUG, "logo->hash", 3103 1.1.1.3 christos logo->hash, 32); 3104 1.1.1.3 christos wpa_hexdump_ascii(MSG_DEBUG, "ctx->icon_hash[j]", 3105 1.1.1.3 christos ctx->icon_hash[j], 32); 3106 1.1 christos } 3107 1.1 christos 3108 1.1 christos if (!found) { 3109 1.1.1.3 christos wpa_printf(MSG_INFO, 3110 1.1.1.3 christos "No icon hash match (by hash) found"); 3111 1.1.1.3 christos write_result(ctx, 3112 1.1.1.3 christos "No icon hash match (by hash) found"); 3113 1.1 christos return -1; 3114 1.1 christos } 3115 1.1 christos } 3116 1.1 christos 3117 1.1 christos return 0; 3118 1.1 christos } 3119 1.1 christos 3120 1.1 christos 3121 1.1 christos static int init_ctx(struct hs20_osu_client *ctx) 3122 1.1 christos { 3123 1.1 christos xml_node_t *devinfo, *devid; 3124 1.1 christos 3125 1.1 christos os_memset(ctx, 0, sizeof(*ctx)); 3126 1.1 christos ctx->ifname = "wlan0"; 3127 1.1 christos ctx->xml = xml_node_init_ctx(ctx, NULL); 3128 1.1 christos if (ctx->xml == NULL) 3129 1.1 christos return -1; 3130 1.1 christos 3131 1.1 christos devinfo = node_from_file(ctx->xml, "devinfo.xml"); 3132 1.1.1.4 christos if (devinfo) { 3133 1.1.1.4 christos devid = get_node(ctx->xml, devinfo, "DevId"); 3134 1.1.1.4 christos if (devid) { 3135 1.1.1.4 christos char *tmp = xml_node_get_text(ctx->xml, devid); 3136 1.1.1.4 christos 3137 1.1.1.4 christos if (tmp) { 3138 1.1.1.4 christos ctx->devid = os_strdup(tmp); 3139 1.1.1.4 christos xml_node_get_text_free(ctx->xml, tmp); 3140 1.1.1.4 christos } 3141 1.1 christos } 3142 1.1.1.4 christos xml_node_free(ctx->xml, devinfo); 3143 1.1 christos } 3144 1.1 christos 3145 1.1 christos ctx->http = http_init_ctx(ctx, ctx->xml); 3146 1.1 christos if (ctx->http == NULL) { 3147 1.1 christos xml_node_deinit_ctx(ctx->xml); 3148 1.1 christos return -1; 3149 1.1 christos } 3150 1.1 christos http_ocsp_set(ctx->http, 2); 3151 1.1 christos http_set_cert_cb(ctx->http, osu_cert_cb, ctx); 3152 1.1 christos 3153 1.1 christos return 0; 3154 1.1 christos } 3155 1.1 christos 3156 1.1 christos 3157 1.1 christos static void deinit_ctx(struct hs20_osu_client *ctx) 3158 1.1 christos { 3159 1.1 christos size_t i; 3160 1.1 christos 3161 1.1 christos http_deinit_ctx(ctx->http); 3162 1.1 christos xml_node_deinit_ctx(ctx->xml); 3163 1.1 christos os_free(ctx->fqdn); 3164 1.1 christos os_free(ctx->server_url); 3165 1.1 christos os_free(ctx->devid); 3166 1.1 christos 3167 1.1 christos for (i = 0; i < ctx->server_dnsname_count; i++) 3168 1.1 christos os_free(ctx->server_dnsname[i]); 3169 1.1 christos os_free(ctx->server_dnsname); 3170 1.1 christos } 3171 1.1 christos 3172 1.1 christos 3173 1.1 christos static void check_workarounds(struct hs20_osu_client *ctx) 3174 1.1 christos { 3175 1.1 christos FILE *f; 3176 1.1 christos char buf[100]; 3177 1.1 christos unsigned long int val = 0; 3178 1.1 christos 3179 1.1 christos f = fopen("hs20-osu-client.workarounds", "r"); 3180 1.1 christos if (f == NULL) 3181 1.1 christos return; 3182 1.1 christos 3183 1.1 christos if (fgets(buf, sizeof(buf), f)) 3184 1.1 christos val = strtoul(buf, NULL, 16); 3185 1.1 christos 3186 1.1 christos fclose(f); 3187 1.1 christos 3188 1.1 christos if (val) { 3189 1.1 christos wpa_printf(MSG_INFO, "Workarounds enabled: 0x%lx", val); 3190 1.1 christos ctx->workarounds = val; 3191 1.1 christos if (ctx->workarounds & WORKAROUND_OCSP_OPTIONAL) 3192 1.1 christos http_ocsp_set(ctx->http, 1); 3193 1.1 christos } 3194 1.1 christos } 3195 1.1 christos 3196 1.1 christos 3197 1.1 christos static void usage(void) 3198 1.1 christos { 3199 1.1.1.6 christos printf("usage: hs20-osu-client [-dddqqKtT] [-S<station ifname>] \\\n" 3200 1.1 christos " [-w<wpa_supplicant ctrl_iface dir>] " 3201 1.1 christos "[-r<result file>] [-f<debug file>] \\\n" 3202 1.1 christos " [-s<summary file>] \\\n" 3203 1.1.1.3 christos " [-x<spp.xsd file name>] \\\n" 3204 1.1 christos " <command> [arguments..]\n" 3205 1.1 christos "commands:\n" 3206 1.1 christos "- to_tnds <XML MO> <XML MO in TNDS format> [URN]\n" 3207 1.1 christos "- to_tnds2 <XML MO> <XML MO in TNDS format (Path) " 3208 1.1 christos "[URN]>\n" 3209 1.1 christos "- from_tnds <XML MO in TNDS format> <XML MO>\n" 3210 1.1 christos "- set_pps <PerProviderSubscription XML file name>\n" 3211 1.1 christos "- get_fqdn <PerProviderSubscription XML file name>\n" 3212 1.1 christos "- pol_upd [Server URL] [PPS] [CA cert]\n" 3213 1.1 christos "- sub_rem <Server URL> [PPS] [CA cert]\n" 3214 1.1 christos "- prov <Server URL> [CA cert]\n" 3215 1.1 christos "- oma_dm_prov <Server URL> [CA cert]\n" 3216 1.1 christos "- sim_prov <Server URL> [CA cert]\n" 3217 1.1 christos "- oma_dm_sim_prov <Server URL> [CA cert]\n" 3218 1.1 christos "- signup [CA cert]\n" 3219 1.1 christos "- dl_osu_ca <PPS> <CA file>\n" 3220 1.1 christos "- dl_polupd_ca <PPS> <CA file>\n" 3221 1.1 christos "- dl_aaa_ca <PPS> <CA file>\n" 3222 1.1 christos "- browser <URL>\n" 3223 1.1 christos "- parse_cert <X.509 certificate (DER)>\n" 3224 1.1 christos "- osu_select <OSU info directory> [CA cert]\n"); 3225 1.1 christos } 3226 1.1 christos 3227 1.1 christos 3228 1.1 christos int main(int argc, char *argv[]) 3229 1.1 christos { 3230 1.1 christos struct hs20_osu_client ctx; 3231 1.1 christos int c; 3232 1.1 christos int ret = 0; 3233 1.1 christos int no_prod_assoc = 0; 3234 1.1 christos const char *friendly_name = NULL; 3235 1.1 christos const char *wpa_debug_file_path = NULL; 3236 1.1 christos extern char *wpas_ctrl_path; 3237 1.1 christos extern int wpa_debug_level; 3238 1.1 christos extern int wpa_debug_show_keys; 3239 1.1 christos extern int wpa_debug_timestamp; 3240 1.1 christos 3241 1.1 christos if (init_ctx(&ctx) < 0) 3242 1.1 christos return -1; 3243 1.1 christos 3244 1.1 christos for (;;) { 3245 1.1.1.6 christos c = getopt(argc, argv, "df:hKNo:O:qr:s:S:tTw:x:"); 3246 1.1 christos if (c < 0) 3247 1.1 christos break; 3248 1.1 christos switch (c) { 3249 1.1 christos case 'd': 3250 1.1 christos if (wpa_debug_level > 0) 3251 1.1 christos wpa_debug_level--; 3252 1.1 christos break; 3253 1.1 christos case 'f': 3254 1.1 christos wpa_debug_file_path = optarg; 3255 1.1 christos break; 3256 1.1 christos case 'K': 3257 1.1 christos wpa_debug_show_keys++; 3258 1.1 christos break; 3259 1.1 christos case 'N': 3260 1.1 christos no_prod_assoc = 1; 3261 1.1 christos break; 3262 1.1.1.4 christos case 'o': 3263 1.1.1.4 christos ctx.osu_ssid = optarg; 3264 1.1.1.4 christos break; 3265 1.1 christos case 'O': 3266 1.1 christos friendly_name = optarg; 3267 1.1 christos break; 3268 1.1 christos case 'q': 3269 1.1 christos wpa_debug_level++; 3270 1.1 christos break; 3271 1.1 christos case 'r': 3272 1.1 christos ctx.result_file = optarg; 3273 1.1 christos break; 3274 1.1 christos case 's': 3275 1.1 christos ctx.summary_file = optarg; 3276 1.1 christos break; 3277 1.1 christos case 'S': 3278 1.1 christos ctx.ifname = optarg; 3279 1.1 christos break; 3280 1.1 christos case 't': 3281 1.1 christos wpa_debug_timestamp++; 3282 1.1 christos break; 3283 1.1.1.6 christos case 'T': 3284 1.1.1.6 christos ctx.ignore_tls = 1; 3285 1.1.1.6 christos break; 3286 1.1 christos case 'w': 3287 1.1 christos wpas_ctrl_path = optarg; 3288 1.1 christos break; 3289 1.1.1.3 christos case 'x': 3290 1.1.1.3 christos spp_xsd_fname = optarg; 3291 1.1.1.3 christos break; 3292 1.1 christos case 'h': 3293 1.1 christos default: 3294 1.1 christos usage(); 3295 1.1 christos exit(0); 3296 1.1 christos } 3297 1.1 christos } 3298 1.1 christos 3299 1.1 christos if (argc - optind < 1) { 3300 1.1 christos usage(); 3301 1.1 christos exit(0); 3302 1.1 christos } 3303 1.1 christos 3304 1.1 christos wpa_debug_open_file(wpa_debug_file_path); 3305 1.1 christos 3306 1.1 christos #ifdef __linux__ 3307 1.1 christos setlinebuf(stdout); 3308 1.1 christos #endif /* __linux__ */ 3309 1.1 christos 3310 1.1 christos if (ctx.result_file) 3311 1.1 christos unlink(ctx.result_file); 3312 1.1 christos wpa_printf(MSG_DEBUG, "===[hs20-osu-client START - command: %s ]======" 3313 1.1 christos "================", argv[optind]); 3314 1.1 christos check_workarounds(&ctx); 3315 1.1 christos 3316 1.1 christos if (strcmp(argv[optind], "to_tnds") == 0) { 3317 1.1 christos if (argc - optind < 2) { 3318 1.1 christos usage(); 3319 1.1 christos exit(0); 3320 1.1 christos } 3321 1.1 christos cmd_to_tnds(&ctx, argv[optind + 1], argv[optind + 2], 3322 1.1 christos argc > optind + 3 ? argv[optind + 3] : NULL, 3323 1.1 christos 0); 3324 1.1 christos } else if (strcmp(argv[optind], "to_tnds2") == 0) { 3325 1.1 christos if (argc - optind < 2) { 3326 1.1 christos usage(); 3327 1.1 christos exit(0); 3328 1.1 christos } 3329 1.1 christos cmd_to_tnds(&ctx, argv[optind + 1], argv[optind + 2], 3330 1.1 christos argc > optind + 3 ? argv[optind + 3] : NULL, 3331 1.1 christos 1); 3332 1.1 christos } else if (strcmp(argv[optind], "from_tnds") == 0) { 3333 1.1 christos if (argc - optind < 2) { 3334 1.1 christos usage(); 3335 1.1 christos exit(0); 3336 1.1 christos } 3337 1.1 christos cmd_from_tnds(&ctx, argv[optind + 1], argv[optind + 2]); 3338 1.1 christos } else if (strcmp(argv[optind], "sub_rem") == 0) { 3339 1.1 christos if (argc - optind < 2) { 3340 1.1 christos usage(); 3341 1.1 christos exit(0); 3342 1.1 christos } 3343 1.1.1.3 christos ret = cmd_sub_rem(&ctx, argv[optind + 1], 3344 1.1.1.3 christos argc > optind + 2 ? argv[optind + 2] : NULL, 3345 1.1.1.3 christos argc > optind + 3 ? argv[optind + 3] : NULL); 3346 1.1 christos } else if (strcmp(argv[optind], "pol_upd") == 0) { 3347 1.1.1.3 christos ret = cmd_pol_upd(&ctx, 3348 1.1.1.3 christos argc > optind + 1 ? argv[optind + 1] : NULL, 3349 1.1 christos argc > optind + 2 ? argv[optind + 2] : NULL, 3350 1.1 christos argc > optind + 3 ? argv[optind + 3] : NULL); 3351 1.1 christos } else if (strcmp(argv[optind], "prov") == 0) { 3352 1.1 christos if (argc - optind < 2) { 3353 1.1 christos usage(); 3354 1.1 christos exit(0); 3355 1.1 christos } 3356 1.1 christos ctx.ca_fname = argv[optind + 2]; 3357 1.1.1.3 christos wpa_printf(MSG_DEBUG, "Calling cmd_prov from main"); 3358 1.1 christos cmd_prov(&ctx, argv[optind + 1]); 3359 1.1 christos } else if (strcmp(argv[optind], "sim_prov") == 0) { 3360 1.1 christos if (argc - optind < 2) { 3361 1.1 christos usage(); 3362 1.1 christos exit(0); 3363 1.1 christos } 3364 1.1 christos ctx.ca_fname = argv[optind + 2]; 3365 1.1 christos cmd_sim_prov(&ctx, argv[optind + 1]); 3366 1.1 christos } else if (strcmp(argv[optind], "dl_osu_ca") == 0) { 3367 1.1 christos if (argc - optind < 2) { 3368 1.1 christos usage(); 3369 1.1 christos exit(0); 3370 1.1 christos } 3371 1.1 christos cmd_dl_osu_ca(&ctx, argv[optind + 1], argv[optind + 2]); 3372 1.1 christos } else if (strcmp(argv[optind], "dl_polupd_ca") == 0) { 3373 1.1 christos if (argc - optind < 2) { 3374 1.1 christos usage(); 3375 1.1 christos exit(0); 3376 1.1 christos } 3377 1.1 christos cmd_dl_polupd_ca(&ctx, argv[optind + 1], argv[optind + 2]); 3378 1.1 christos } else if (strcmp(argv[optind], "dl_aaa_ca") == 0) { 3379 1.1 christos if (argc - optind < 2) { 3380 1.1 christos usage(); 3381 1.1 christos exit(0); 3382 1.1 christos } 3383 1.1 christos cmd_dl_aaa_ca(&ctx, argv[optind + 1], argv[optind + 2]); 3384 1.1 christos } else if (strcmp(argv[optind], "osu_select") == 0) { 3385 1.1 christos if (argc - optind < 2) { 3386 1.1 christos usage(); 3387 1.1 christos exit(0); 3388 1.1 christos } 3389 1.1 christos ctx.ca_fname = argc > optind + 2 ? argv[optind + 2] : NULL; 3390 1.1 christos cmd_osu_select(&ctx, argv[optind + 1], 2, 1, NULL); 3391 1.1 christos } else if (strcmp(argv[optind], "signup") == 0) { 3392 1.1 christos ctx.ca_fname = argc > optind + 1 ? argv[optind + 1] : NULL; 3393 1.1 christos ret = cmd_signup(&ctx, no_prod_assoc, friendly_name); 3394 1.1 christos } else if (strcmp(argv[optind], "set_pps") == 0) { 3395 1.1 christos if (argc - optind < 2) { 3396 1.1 christos usage(); 3397 1.1 christos exit(0); 3398 1.1 christos } 3399 1.1 christos cmd_set_pps(&ctx, argv[optind + 1]); 3400 1.1 christos } else if (strcmp(argv[optind], "get_fqdn") == 0) { 3401 1.1 christos if (argc - optind < 1) { 3402 1.1 christos usage(); 3403 1.1 christos exit(0); 3404 1.1 christos } 3405 1.1 christos ret = cmd_get_fqdn(&ctx, argv[optind + 1]); 3406 1.1 christos } else if (strcmp(argv[optind], "oma_dm_prov") == 0) { 3407 1.1 christos if (argc - optind < 2) { 3408 1.1 christos usage(); 3409 1.1 christos exit(0); 3410 1.1 christos } 3411 1.1 christos ctx.ca_fname = argv[optind + 2]; 3412 1.1 christos cmd_oma_dm_prov(&ctx, argv[optind + 1]); 3413 1.1 christos } else if (strcmp(argv[optind], "oma_dm_sim_prov") == 0) { 3414 1.1 christos if (argc - optind < 2) { 3415 1.1 christos usage(); 3416 1.1 christos exit(0); 3417 1.1 christos } 3418 1.1 christos ctx.ca_fname = argv[optind + 2]; 3419 1.1 christos if (cmd_oma_dm_sim_prov(&ctx, argv[optind + 1]) < 0) { 3420 1.1 christos write_summary(&ctx, "Failed to complete OMA DM SIM provisioning"); 3421 1.1 christos return -1; 3422 1.1 christos } 3423 1.1 christos } else if (strcmp(argv[optind], "oma_dm_add") == 0) { 3424 1.1 christos if (argc - optind < 2) { 3425 1.1 christos usage(); 3426 1.1 christos exit(0); 3427 1.1 christos } 3428 1.1 christos cmd_oma_dm_add(&ctx, argv[optind + 1], argv[optind + 2]); 3429 1.1 christos } else if (strcmp(argv[optind], "oma_dm_replace") == 0) { 3430 1.1 christos if (argc - optind < 2) { 3431 1.1 christos usage(); 3432 1.1 christos exit(0); 3433 1.1 christos } 3434 1.1 christos cmd_oma_dm_replace(&ctx, argv[optind + 1], argv[optind + 2]); 3435 1.1 christos } else if (strcmp(argv[optind], "est_csr") == 0) { 3436 1.1 christos if (argc - optind < 2) { 3437 1.1 christos usage(); 3438 1.1 christos exit(0); 3439 1.1 christos } 3440 1.1 christos mkdir("Cert", S_IRWXU); 3441 1.1 christos est_build_csr(&ctx, argv[optind + 1]); 3442 1.1 christos } else if (strcmp(argv[optind], "browser") == 0) { 3443 1.1 christos int ret; 3444 1.1 christos 3445 1.1 christos if (argc - optind < 2) { 3446 1.1 christos usage(); 3447 1.1 christos exit(0); 3448 1.1 christos } 3449 1.1 christos 3450 1.1 christos wpa_printf(MSG_INFO, "Launch web browser to URL %s", 3451 1.1 christos argv[optind + 1]); 3452 1.1.1.6 christos ret = hs20_web_browser(argv[optind + 1], ctx.ignore_tls); 3453 1.1 christos wpa_printf(MSG_INFO, "Web browser result: %d", ret); 3454 1.1 christos } else if (strcmp(argv[optind], "parse_cert") == 0) { 3455 1.1 christos if (argc - optind < 2) { 3456 1.1 christos usage(); 3457 1.1 christos exit(0); 3458 1.1 christos } 3459 1.1 christos 3460 1.1 christos wpa_debug_level = MSG_MSGDUMP; 3461 1.1 christos http_parse_x509_certificate(ctx.http, argv[optind + 1]); 3462 1.1 christos wpa_debug_level = MSG_INFO; 3463 1.1 christos } else { 3464 1.1 christos wpa_printf(MSG_INFO, "Unknown command '%s'", argv[optind]); 3465 1.1 christos } 3466 1.1 christos 3467 1.1 christos deinit_ctx(&ctx); 3468 1.1 christos wpa_printf(MSG_DEBUG, 3469 1.1 christos "===[hs20-osu-client END ]======================"); 3470 1.1 christos 3471 1.1 christos wpa_debug_close_file(); 3472 1.1 christos 3473 1.1 christos return ret; 3474 1.1 christos } 3475