L_CFLAGS += -DCONFIG_CTRL_IFACE_UNIX
L_CFLAGS += -DCONFIG_CTRL_IFACE_CLIENT_DIR=\"/data/misc/wifi/sockets\"
-OBJS = spp_client.c
-OBJS += oma_dm_client.c
-OBJS += osu_client.c
-OBJS += est.c
+OBJS = osu_client.c
OBJS += ../../src/common/wpa_ctrl.c
OBJS += ../../src/common/wpa_helpers.c
OBJS += ../../src/utils/xml-utils.c
endif
endif
-OBJS=spp_client.o
-OBJS += oma_dm_client.o
-OBJS += osu_client.o
-OBJS += est.o
+OBJS = osu_client.o
OBJS += ../../src/utils/xml-utils.o
CFLAGS += -DCONFIG_CTRL_IFACE
CFLAGS += -DCONFIG_CTRL_IFACE_UNIX
+++ /dev/null
-<DevDetail xmlns="urn:oma:mo:oma-dm-devdetail:1.0">
- <Ext>
- <org.wi-fi>
- <Wi-Fi>
- <EAPMethodList>
- <EAPMethod1>
- <EAPType>13</EAPType>
- </EAPMethod1>
- <EAPMethod2>
- <EAPType>21</EAPType>
- <InnerMethod>MS-CHAP-V2</InnerMethod>
- </EAPMethod2>
- <EAPMethod3>
- <EAPType>18</EAPType>
- </EAPMethod3>
- <EAPMethod4>
- <EAPType>23</EAPType>
- </EAPMethod4>
- <EAPMethod5>
- <EAPType>50</EAPType>
- </EAPMethod5>
- </EAPMethodList>
- <ManufacturingCertificate>false</ManufacturingCertificate>
- <Wi-FiMACAddress>020102030405</Wi-FiMACAddress>
- <IMSI>310026000000000</IMSI>
- <IMEI_MEID>imei:490123456789012</IMEI_MEID>
- <ClientTriggerRedirectURI>http://localhost:12345/</ClientTriggerRedirectURI>
- <Ops>
- <launchBrowserToURI></launchBrowserToURI>
- <negotiateClientCertTLS></negotiateClientCertTLS>
- <getCertificate></getCertificate>
- </Ops>
- </Wi-Fi>
- </org.wi-fi>
- </Ext>
- <URI>
- <MaxDepth>0</MaxDepth>
- <MaxTotLen>0</MaxTotLen>
- <MaxSegLen>0</MaxSegLen>
- </URI>
- <DevType>MobilePhone</DevType>
- <OEM>Manufacturer</OEM>
- <FwV>1.0</FwV>
- <SwV>1.0</SwV>
- <HwV>1.0</HwV>
- <LrgObj>false</LrgObj>
-</DevDetail>
+++ /dev/null
-<DevInfo xmlns="urn:oma:mo:oma-dm-devinfo:1.0">
- <DevId>urn:Example:HS20-station:123456</DevId>
- <Man>Manufacturer</Man>
- <Mod>HS20-station</Mod>
- <DmV>1.2</DmV>
- <Lang>en</Lang>
-</DevInfo>
+++ /dev/null
-/*
- * Hotspot 2.0 OSU client - EST client
- * Copyright (c) 2012-2014, Qualcomm Atheros, Inc.
- *
- * This software may be distributed under the terms of the BSD license.
- * See README for more details.
- */
-
-#include "includes.h"
-#include <openssl/err.h>
-#include <openssl/evp.h>
-#include <openssl/pem.h>
-#include <openssl/pkcs7.h>
-#include <openssl/asn1.h>
-#include <openssl/asn1t.h>
-#include <openssl/x509.h>
-#include <openssl/x509v3.h>
-#include <openssl/opensslv.h>
-#include <openssl/buffer.h>
-
-#include "common.h"
-#include "utils/base64.h"
-#include "utils/xml-utils.h"
-#include "utils/http-utils.h"
-#include "osu_client.h"
-
-
-static int pkcs7_to_cert(struct hs20_osu_client *ctx, const u8 *pkcs7,
- size_t len, char *pem_file, char *der_file)
-{
-#ifdef OPENSSL_IS_BORINGSSL
- CBS pkcs7_cbs;
-#else /* OPENSSL_IS_BORINGSSL */
- PKCS7 *p7 = NULL;
- const unsigned char *p = pkcs7;
-#endif /* OPENSSL_IS_BORINGSSL */
- STACK_OF(X509) *certs;
- int i, num, ret = -1;
- BIO *out = NULL;
-
-#ifdef OPENSSL_IS_BORINGSSL
- certs = sk_X509_new_null();
- if (!certs)
- goto fail;
- CBS_init(&pkcs7_cbs, pkcs7, len);
- if (!PKCS7_get_certificates(certs, &pkcs7_cbs)) {
- wpa_printf(MSG_INFO, "Could not parse PKCS#7 object: %s",
- ERR_error_string(ERR_get_error(), NULL));
- write_result(ctx, "Could not parse PKCS#7 object from EST");
- goto fail;
- }
-#else /* OPENSSL_IS_BORINGSSL */
- p7 = d2i_PKCS7(NULL, &p, len);
- if (p7 == NULL) {
- wpa_printf(MSG_INFO, "Could not parse PKCS#7 object: %s",
- ERR_error_string(ERR_get_error(), NULL));
- write_result(ctx, "Could not parse PKCS#7 object from EST");
- goto fail;
- }
-
- switch (OBJ_obj2nid(p7->type)) {
- case NID_pkcs7_signed:
- certs = p7->d.sign->cert;
- break;
- case NID_pkcs7_signedAndEnveloped:
- certs = p7->d.signed_and_enveloped->cert;
- break;
- default:
- certs = NULL;
- break;
- }
-#endif /* OPENSSL_IS_BORINGSSL */
-
- if (!certs || ((num = sk_X509_num(certs)) == 0)) {
- wpa_printf(MSG_INFO, "No certificates found in PKCS#7 object");
- write_result(ctx, "No certificates found in PKCS#7 object");
- goto fail;
- }
-
- if (der_file) {
- FILE *f = fopen(der_file, "wb");
- if (f == NULL)
- goto fail;
- i2d_X509_fp(f, sk_X509_value(certs, 0));
- fclose(f);
- }
-
- if (pem_file) {
- out = BIO_new(BIO_s_file());
- if (out == NULL ||
- BIO_write_filename(out, pem_file) <= 0)
- goto fail;
-
- for (i = 0; i < num; i++) {
- X509 *cert = sk_X509_value(certs, i);
- X509_print(out, cert);
- PEM_write_bio_X509(out, cert);
- BIO_puts(out, "\n");
- }
- }
-
- ret = 0;
-
-fail:
-#ifdef OPENSSL_IS_BORINGSSL
- if (certs)
- sk_X509_pop_free(certs, X509_free);
-#else /* OPENSSL_IS_BORINGSSL */
- PKCS7_free(p7);
-#endif /* OPENSSL_IS_BORINGSSL */
- if (out)
- BIO_free_all(out);
-
- return ret;
-}
-
-
-int est_load_cacerts(struct hs20_osu_client *ctx, const char *url)
-{
- char *buf, *resp;
- size_t buflen;
- unsigned char *pkcs7;
- size_t pkcs7_len, resp_len;
- int res;
-
- buflen = os_strlen(url) + 100;
- buf = os_malloc(buflen);
- if (buf == NULL)
- return -1;
-
- os_snprintf(buf, buflen, "%s/cacerts", url);
- wpa_printf(MSG_INFO, "Download EST cacerts from %s", buf);
- write_summary(ctx, "Download EST cacerts from %s", buf);
- ctx->no_osu_cert_validation = 1;
- http_ocsp_set(ctx->http, 1);
- res = http_download_file(ctx->http, buf, "Cert/est-cacerts.txt",
- ctx->ca_fname);
- http_ocsp_set(ctx->http,
- (ctx->workarounds & WORKAROUND_OCSP_OPTIONAL) ? 1 : 2);
- ctx->no_osu_cert_validation = 0;
- if (res < 0) {
- wpa_printf(MSG_INFO, "Failed to download EST cacerts from %s",
- buf);
- write_result(ctx, "Failed to download EST cacerts from %s",
- buf);
- os_free(buf);
- return -1;
- }
- os_free(buf);
-
- resp = os_readfile("Cert/est-cacerts.txt", &resp_len);
- if (resp == NULL) {
- wpa_printf(MSG_INFO, "Could not read Cert/est-cacerts.txt");
- write_result(ctx, "Could not read EST cacerts");
- return -1;
- }
-
- pkcs7 = base64_decode(resp, resp_len, &pkcs7_len);
- if (pkcs7 && pkcs7_len < resp_len / 2) {
- wpa_printf(MSG_INFO, "Too short base64 decode (%u bytes; downloaded %u bytes) - assume this was binary",
- (unsigned int) pkcs7_len, (unsigned int) resp_len);
- os_free(pkcs7);
- pkcs7 = NULL;
- }
- if (pkcs7 == NULL) {
- wpa_printf(MSG_INFO, "EST workaround - Could not decode base64, assume this is DER encoded PKCS7");
- pkcs7 = os_malloc(resp_len);
- if (pkcs7) {
- os_memcpy(pkcs7, resp, resp_len);
- pkcs7_len = resp_len;
- }
- }
- os_free(resp);
-
- if (pkcs7 == NULL) {
- wpa_printf(MSG_INFO, "Could not fetch PKCS7 cacerts");
- write_result(ctx, "Could not fetch EST PKCS#7 cacerts");
- return -1;
- }
-
- res = pkcs7_to_cert(ctx, pkcs7, pkcs7_len, "Cert/est-cacerts.pem",
- NULL);
- os_free(pkcs7);
- if (res < 0) {
- wpa_printf(MSG_INFO, "Could not parse CA certs from PKCS#7 cacerts response");
- write_result(ctx, "Could not parse CA certs from EST PKCS#7 cacerts response");
- return -1;
- }
- unlink("Cert/est-cacerts.txt");
-
- return 0;
-}
-
-
-/*
- * CsrAttrs ::= SEQUENCE SIZE (0..MAX) OF AttrOrOID
- *
- * AttrOrOID ::= CHOICE {
- * oid OBJECT IDENTIFIER,
- * attribute Attribute }
- *
- * Attribute ::= SEQUENCE {
- * type OBJECT IDENTIFIER,
- * values SET SIZE(1..MAX) OF OBJECT IDENTIFIER }
- */
-
-typedef struct {
- ASN1_OBJECT *type;
- STACK_OF(ASN1_OBJECT) *values;
-} Attribute;
-
-typedef struct {
- int type;
- union {
- ASN1_OBJECT *oid;
- Attribute *attribute;
- } d;
-} AttrOrOID;
-
-#if OPENSSL_VERSION_NUMBER >= 0x10100000L
-DEFINE_STACK_OF(AttrOrOID)
-#endif
-
-typedef struct {
- int type;
- STACK_OF(AttrOrOID) *attrs;
-} CsrAttrs;
-
-ASN1_SEQUENCE(Attribute) = {
- ASN1_SIMPLE(Attribute, type, ASN1_OBJECT),
- ASN1_SET_OF(Attribute, values, ASN1_OBJECT)
-} ASN1_SEQUENCE_END(Attribute);
-
-ASN1_CHOICE(AttrOrOID) = {
- ASN1_SIMPLE(AttrOrOID, d.oid, ASN1_OBJECT),
- ASN1_SIMPLE(AttrOrOID, d.attribute, Attribute)
-} ASN1_CHOICE_END(AttrOrOID);
-
-ASN1_CHOICE(CsrAttrs) = {
- ASN1_SEQUENCE_OF(CsrAttrs, attrs, AttrOrOID)
-} ASN1_CHOICE_END(CsrAttrs);
-
-IMPLEMENT_ASN1_FUNCTIONS(CsrAttrs);
-
-
-static void add_csrattrs_oid(struct hs20_osu_client *ctx, ASN1_OBJECT *oid,
- STACK_OF(X509_EXTENSION) *exts)
-{
- char txt[100];
- int res;
-
- if (!oid)
- return;
-
- res = OBJ_obj2txt(txt, sizeof(txt), oid, 1);
- if (res < 0 || res >= (int) sizeof(txt))
- return;
-
- if (os_strcmp(txt, "1.2.840.113549.1.9.7") == 0) {
- wpa_printf(MSG_INFO, "TODO: csrattr challengePassword");
- } else if (os_strcmp(txt, "1.2.840.113549.1.1.11") == 0) {
- wpa_printf(MSG_INFO, "csrattr sha256WithRSAEncryption");
- } else {
- wpa_printf(MSG_INFO, "Ignore unsupported csrattr oid %s", txt);
- }
-}
-
-
-static void add_csrattrs_ext_req(struct hs20_osu_client *ctx,
- STACK_OF(ASN1_OBJECT) *values,
- STACK_OF(X509_EXTENSION) *exts)
-{
- char txt[100];
- int i, num, res;
-
- num = sk_ASN1_OBJECT_num(values);
- for (i = 0; i < num; i++) {
- ASN1_OBJECT *oid = sk_ASN1_OBJECT_value(values, i);
-
- res = OBJ_obj2txt(txt, sizeof(txt), oid, 1);
- if (res < 0 || res >= (int) sizeof(txt))
- continue;
-
- if (os_strcmp(txt, "1.3.6.1.1.1.1.22") == 0) {
- wpa_printf(MSG_INFO, "TODO: extReq macAddress");
- } else if (os_strcmp(txt, "1.3.6.1.4.1.40808.1.1.3") == 0) {
- wpa_printf(MSG_INFO, "TODO: extReq imei");
- } else if (os_strcmp(txt, "1.3.6.1.4.1.40808.1.1.4") == 0) {
- wpa_printf(MSG_INFO, "TODO: extReq meid");
- } else if (os_strcmp(txt, "1.3.6.1.4.1.40808.1.1.5") == 0) {
- wpa_printf(MSG_INFO, "TODO: extReq DevId");
- } else {
- wpa_printf(MSG_INFO, "Ignore unsupported cstattr extensionsRequest %s",
- txt);
- }
- }
-}
-
-
-static void add_csrattrs_attr(struct hs20_osu_client *ctx, Attribute *attr,
- STACK_OF(X509_EXTENSION) *exts)
-{
- char txt[100], txt2[100];
- int i, num, res;
-
- if (!attr || !attr->type || !attr->values)
- return;
-
- res = OBJ_obj2txt(txt, sizeof(txt), attr->type, 1);
- if (res < 0 || res >= (int) sizeof(txt))
- return;
-
- if (os_strcmp(txt, "1.2.840.113549.1.9.14") == 0) {
- add_csrattrs_ext_req(ctx, attr->values, exts);
- return;
- }
-
- num = sk_ASN1_OBJECT_num(attr->values);
- for (i = 0; i < num; i++) {
- ASN1_OBJECT *oid = sk_ASN1_OBJECT_value(attr->values, i);
-
- res = OBJ_obj2txt(txt2, sizeof(txt2), oid, 1);
- if (res < 0 || res >= (int) sizeof(txt2))
- continue;
-
- wpa_printf(MSG_INFO, "Ignore unsupported cstattr::attr %s oid %s",
- txt, txt2);
- }
-}
-
-
-static void add_csrattrs(struct hs20_osu_client *ctx, CsrAttrs *csrattrs,
- STACK_OF(X509_EXTENSION) *exts)
-{
- int i, num;
-
- if (!csrattrs || ! csrattrs->attrs)
- return;
-
-#if OPENSSL_VERSION_NUMBER >= 0x10100000L
- num = sk_AttrOrOID_num(csrattrs->attrs);
-#else
- num = SKM_sk_num(AttrOrOID, csrattrs->attrs);
-#endif
- for (i = 0; i < num; i++) {
-#if OPENSSL_VERSION_NUMBER >= 0x10100000L
- AttrOrOID *ao = sk_AttrOrOID_value(csrattrs->attrs, i);
-#else
- AttrOrOID *ao = SKM_sk_value(AttrOrOID, csrattrs->attrs, i);
-#endif
- switch (ao->type) {
- case 0:
- add_csrattrs_oid(ctx, ao->d.oid, exts);
- break;
- case 1:
- add_csrattrs_attr(ctx, ao->d.attribute, exts);
- break;
- }
- }
-}
-
-
-static int generate_csr(struct hs20_osu_client *ctx, char *key_pem,
- char *csr_pem, char *est_req, char *old_cert,
- CsrAttrs *csrattrs)
-{
- EVP_PKEY_CTX *pctx = NULL;
- EVP_PKEY *pkey = NULL;
- X509_REQ *req = NULL;
- int ret = -1;
- unsigned int val;
- X509_NAME *subj = NULL;
- char name[100];
- STACK_OF(X509_EXTENSION) *exts = NULL;
- X509_EXTENSION *ex;
- BIO *out;
- CONF *ctmp = NULL;
-
- wpa_printf(MSG_INFO, "Generate RSA private key");
- write_summary(ctx, "Generate RSA private key");
- pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, NULL);
- if (!pctx)
- return -1;
-
- if (EVP_PKEY_keygen_init(pctx) <= 0)
- goto fail;
-
- if (EVP_PKEY_CTX_set_rsa_keygen_bits(pctx, 2048) <= 0)
- goto fail;
-
- if (EVP_PKEY_keygen(pctx, &pkey) <= 0)
- goto fail;
- EVP_PKEY_CTX_free(pctx);
- pctx = NULL;
-
- if (key_pem) {
- FILE *f = fopen(key_pem, "wb");
- if (f == NULL)
- goto fail;
- if (!PEM_write_PrivateKey(f, pkey, NULL, NULL, 0, NULL, NULL)) {
- wpa_printf(MSG_INFO, "Could not write private key: %s",
- ERR_error_string(ERR_get_error(), NULL));
- fclose(f);
- goto fail;
- }
- fclose(f);
- }
-
- wpa_printf(MSG_INFO, "Generate CSR");
- write_summary(ctx, "Generate CSR");
- req = X509_REQ_new();
- if (req == NULL)
- goto fail;
-
- if (old_cert) {
- FILE *f;
- X509 *cert;
- int res;
-
- f = fopen(old_cert, "r");
- if (f == NULL)
- goto fail;
- cert = PEM_read_X509(f, NULL, NULL, NULL);
- fclose(f);
-
- if (cert == NULL)
- goto fail;
- res = X509_REQ_set_subject_name(req,
- X509_get_subject_name(cert));
- X509_free(cert);
- if (!res)
- goto fail;
- } else {
- os_get_random((u8 *) &val, sizeof(val));
- os_snprintf(name, sizeof(name), "cert-user-%u", val);
- subj = X509_NAME_new();
- if (subj == NULL ||
- !X509_NAME_add_entry_by_txt(subj, "CN", MBSTRING_ASC,
- (unsigned char *) name,
- -1, -1, 0) ||
- !X509_REQ_set_subject_name(req, subj))
- goto fail;
- X509_NAME_free(subj);
- subj = NULL;
- }
-
- if (!X509_REQ_set_pubkey(req, pkey))
- goto fail;
-
- exts = sk_X509_EXTENSION_new_null();
- if (!exts)
- goto fail;
-
- ex = X509V3_EXT_nconf_nid(ctmp, NULL, NID_basic_constraints,
- "CA:FALSE");
- if (ex == NULL ||
- !sk_X509_EXTENSION_push(exts, ex))
- goto fail;
-
- ex = X509V3_EXT_nconf_nid(ctmp, NULL, NID_key_usage,
- "nonRepudiation,digitalSignature,keyEncipherment");
- if (ex == NULL ||
- !sk_X509_EXTENSION_push(exts, ex))
- goto fail;
-
- ex = X509V3_EXT_nconf_nid(ctmp, NULL, NID_ext_key_usage,
- "1.3.6.1.4.1.40808.1.1.2");
- if (ex == NULL ||
- !sk_X509_EXTENSION_push(exts, ex))
- goto fail;
-
- add_csrattrs(ctx, csrattrs, exts);
-
- if (!X509_REQ_add_extensions(req, exts))
- goto fail;
- sk_X509_EXTENSION_pop_free(exts, X509_EXTENSION_free);
- exts = NULL;
-
- if (!X509_REQ_sign(req, pkey, EVP_sha256()))
- goto fail;
-
- out = BIO_new(BIO_s_mem());
- if (out) {
- char *txt;
- size_t rlen;
-
-#if !defined(ANDROID) || !defined(OPENSSL_IS_BORINGSSL)
- X509_REQ_print(out, req);
-#endif
- rlen = BIO_ctrl_pending(out);
- txt = os_malloc(rlen + 1);
- if (txt) {
- int res = BIO_read(out, txt, rlen);
- if (res > 0) {
- txt[res] = '\0';
- wpa_printf(MSG_MSGDUMP, "OpenSSL: Certificate request:\n%s",
- txt);
- }
- os_free(txt);
- }
- BIO_free(out);
- }
-
- if (csr_pem) {
- FILE *f = fopen(csr_pem, "w");
- if (f == NULL)
- goto fail;
-#if !defined(ANDROID) || !defined(OPENSSL_IS_BORINGSSL)
- X509_REQ_print_fp(f, req);
-#endif
- if (!PEM_write_X509_REQ(f, req)) {
- fclose(f);
- goto fail;
- }
- fclose(f);
- }
-
- if (est_req) {
- BIO *mem = BIO_new(BIO_s_mem());
- BUF_MEM *ptr;
- char *pos, *end, *buf_end;
- FILE *f;
-
- if (mem == NULL)
- goto fail;
- if (!PEM_write_bio_X509_REQ(mem, req)) {
- BIO_free(mem);
- goto fail;
- }
-
- BIO_get_mem_ptr(mem, &ptr);
- pos = ptr->data;
- buf_end = pos + ptr->length;
-
- /* Remove START/END lines */
- while (pos < buf_end && *pos != '\n')
- pos++;
- if (pos == buf_end) {
- BIO_free(mem);
- goto fail;
- }
- pos++;
-
- end = pos;
- while (end < buf_end && *end != '-')
- end++;
-
- f = fopen(est_req, "w");
- if (f == NULL) {
- BIO_free(mem);
- goto fail;
- }
- fwrite(pos, end - pos, 1, f);
- fclose(f);
-
- BIO_free(mem);
- }
-
- ret = 0;
-fail:
- if (exts)
- sk_X509_EXTENSION_pop_free(exts, X509_EXTENSION_free);
- if (subj)
- X509_NAME_free(subj);
- if (req)
- X509_REQ_free(req);
- if (pkey)
- EVP_PKEY_free(pkey);
- if (pctx)
- EVP_PKEY_CTX_free(pctx);
- return ret;
-}
-
-
-int est_build_csr(struct hs20_osu_client *ctx, const char *url)
-{
- char *buf;
- size_t buflen;
- int res;
- char old_cert_buf[200];
- char *old_cert = NULL;
- CsrAttrs *csrattrs = NULL;
-
- buflen = os_strlen(url) + 100;
- buf = os_malloc(buflen);
- if (buf == NULL)
- return -1;
-
- os_snprintf(buf, buflen, "%s/csrattrs", url);
- wpa_printf(MSG_INFO, "Download csrattrs from %s", buf);
- write_summary(ctx, "Download EST csrattrs from %s", buf);
- ctx->no_osu_cert_validation = 1;
- http_ocsp_set(ctx->http, 1);
- res = http_download_file(ctx->http, buf, "Cert/est-csrattrs.txt",
- ctx->ca_fname);
- http_ocsp_set(ctx->http,
- (ctx->workarounds & WORKAROUND_OCSP_OPTIONAL) ? 1 : 2);
- ctx->no_osu_cert_validation = 0;
- os_free(buf);
- if (res < 0) {
- wpa_printf(MSG_INFO, "Failed to download EST csrattrs - assume no extra attributes are needed");
- } else {
- size_t resp_len;
- char *resp;
- unsigned char *attrs;
- const unsigned char *pos;
- size_t attrs_len;
-
- resp = os_readfile("Cert/est-csrattrs.txt", &resp_len);
- if (resp == NULL) {
- wpa_printf(MSG_INFO, "Could not read csrattrs");
- return -1;
- }
-
- attrs = base64_decode(resp, resp_len, &attrs_len);
- os_free(resp);
-
- if (attrs == NULL) {
- wpa_printf(MSG_INFO, "Could not base64 decode csrattrs");
- return -1;
- }
- unlink("Cert/est-csrattrs.txt");
-
- pos = attrs;
- csrattrs = d2i_CsrAttrs(NULL, &pos, attrs_len);
- os_free(attrs);
- if (csrattrs == NULL) {
- wpa_printf(MSG_INFO, "Failed to parse csrattrs ASN.1");
- /* Continue assuming no additional requirements */
- }
- }
-
- if (ctx->client_cert_present) {
- os_snprintf(old_cert_buf, sizeof(old_cert_buf),
- "SP/%s/client-cert.pem", ctx->fqdn);
- old_cert = old_cert_buf;
- }
-
- res = generate_csr(ctx, "Cert/privkey-plain.pem", "Cert/est-req.pem",
- "Cert/est-req.b64", old_cert, csrattrs);
- if (csrattrs)
- CsrAttrs_free(csrattrs);
-
- return res;
-}
-
-
-int est_simple_enroll(struct hs20_osu_client *ctx, const char *url,
- const char *user, const char *pw)
-{
- char *buf, *resp, *req, *req2;
- size_t buflen, resp_len, len, pkcs7_len;
- unsigned char *pkcs7;
- char client_cert_buf[200];
- char client_key_buf[200];
- const char *client_cert = NULL, *client_key = NULL;
- int res;
-
- req = os_readfile("Cert/est-req.b64", &len);
- if (req == NULL) {
- wpa_printf(MSG_INFO, "Could not read Cert/req.b64");
- return -1;
- }
- req2 = os_realloc(req, len + 1);
- if (req2 == NULL) {
- os_free(req);
- return -1;
- }
- req2[len] = '\0';
- req = req2;
- wpa_printf(MSG_DEBUG, "EST simpleenroll request: %s", req);
-
- buflen = os_strlen(url) + 100;
- buf = os_malloc(buflen);
- if (buf == NULL) {
- os_free(req);
- return -1;
- }
-
- if (ctx->client_cert_present) {
- os_snprintf(buf, buflen, "%s/simplereenroll", url);
- os_snprintf(client_cert_buf, sizeof(client_cert_buf),
- "SP/%s/client-cert.pem", ctx->fqdn);
- client_cert = client_cert_buf;
- os_snprintf(client_key_buf, sizeof(client_key_buf),
- "SP/%s/client-key.pem", ctx->fqdn);
- client_key = client_key_buf;
- } else
- os_snprintf(buf, buflen, "%s/simpleenroll", url);
- wpa_printf(MSG_INFO, "EST simpleenroll URL: %s", buf);
- write_summary(ctx, "EST simpleenroll URL: %s", buf);
- ctx->no_osu_cert_validation = 1;
- http_ocsp_set(ctx->http, 1);
- resp = http_post(ctx->http, buf, req, "application/pkcs10",
- "Content-Transfer-Encoding: base64",
- ctx->ca_fname, user, pw, client_cert, client_key,
- &resp_len);
- http_ocsp_set(ctx->http,
- (ctx->workarounds & WORKAROUND_OCSP_OPTIONAL) ? 1 : 2);
- ctx->no_osu_cert_validation = 0;
- os_free(buf);
- if (resp == NULL) {
- wpa_printf(MSG_INFO, "EST certificate enrollment failed");
- write_result(ctx, "EST certificate enrollment failed");
- return -1;
- }
- wpa_printf(MSG_DEBUG, "EST simpleenroll response: %s", resp);
-
- pkcs7 = base64_decode(resp, resp_len, &pkcs7_len);
- if (pkcs7 == NULL) {
- wpa_printf(MSG_INFO, "EST workaround - Could not decode base64, assume this is DER encoded PKCS7");
- pkcs7 = os_malloc(resp_len);
- if (pkcs7) {
- os_memcpy(pkcs7, resp, resp_len);
- pkcs7_len = resp_len;
- }
- }
- os_free(resp);
-
- if (pkcs7 == NULL) {
- wpa_printf(MSG_INFO, "Failed to parse simpleenroll base64 response");
- write_result(ctx, "Failed to parse EST simpleenroll base64 response");
- return -1;
- }
-
- res = pkcs7_to_cert(ctx, pkcs7, pkcs7_len, "Cert/est_cert.pem",
- "Cert/est_cert.der");
- os_free(pkcs7);
-
- if (res < 0) {
- wpa_printf(MSG_INFO, "EST: Failed to extract certificate from PKCS7 file");
- write_result(ctx, "EST: Failed to extract certificate from EST PKCS7 file");
- return -1;
- }
-
- wpa_printf(MSG_INFO, "EST simple%senroll completed successfully",
- ctx->client_cert_present ? "re" : "");
- write_summary(ctx, "EST simple%senroll completed successfully",
- ctx->client_cert_present ? "re" : "");
-
- return 0;
-}
+++ /dev/null
-/*
- * Hotspot 2.0 - OMA DM client
- * Copyright (c) 2013-2014, Qualcomm Atheros, Inc.
- *
- * This software may be distributed under the terms of the BSD license.
- * See README for more details.
- */
-
-#include "includes.h"
-
-#include "common.h"
-#include "wpa_helpers.h"
-#include "xml-utils.h"
-#include "http-utils.h"
-#include "utils/browser.h"
-#include "osu_client.h"
-
-
-#define DM_SERVER_INITIATED_MGMT 1200
-#define DM_CLIENT_INITIATED_MGMT 1201
-#define DM_GENERIC_ALERT 1226
-
-/* OMA-TS-SyncML-RepPro-V1_2_2 - 10. Response Status Codes */
-#define DM_RESP_OK 200
-#define DM_RESP_AUTH_ACCEPTED 212
-#define DM_RESP_CHUNKED_ITEM_ACCEPTED 213
-#define DM_RESP_NOT_EXECUTED 215
-#define DM_RESP_ATOMIC_ROLL_BACK_OK 216
-#define DM_RESP_NOT_MODIFIED 304
-#define DM_RESP_BAD_REQUEST 400
-#define DM_RESP_UNAUTHORIZED 401
-#define DM_RESP_FORBIDDEN 403
-#define DM_RESP_NOT_FOUND 404
-#define DM_RESP_COMMAND_NOT_ALLOWED 405
-#define DM_RESP_OPTIONAL_FEATURE_NOT_SUPPORTED 406
-#define DM_RESP_MISSING_CREDENTIALS 407
-#define DM_RESP_CONFLICT 409
-#define DM_RESP_GONE 410
-#define DM_RESP_INCOMPLETE_COMMAND 412
-#define DM_RESP_REQ_ENTITY_TOO_LARGE 413
-#define DM_RESP_URI_TOO_LONG 414
-#define DM_RESP_UNSUPPORTED_MEDIA_TYPE_OR_FORMAT 415
-#define DM_RESP_REQ_TOO_BIG 416
-#define DM_RESP_ALREADY_EXISTS 418
-#define DM_RESP_DEVICE_FULL 420
-#define DM_RESP_SIZE_MISMATCH 424
-#define DM_RESP_PERMISSION_DENIED 425
-#define DM_RESP_COMMAND_FAILED 500
-#define DM_RESP_COMMAND_NOT_IMPLEMENTED 501
-#define DM_RESP_ATOMIC_ROLL_BACK_FAILED 516
-
-#define DM_HS20_SUBSCRIPTION_CREATION \
- "org.wi-fi.hotspot2dot0.SubscriptionCreation"
-#define DM_HS20_SUBSCRIPTION_PROVISIONING \
- "org.wi-fi.hotspot2dot0.SubscriptionProvisioning"
-#define DM_HS20_SUBSCRIPTION_REMEDIATION \
- "org.wi-fi.hotspot2dot0.SubscriptionRemediation"
-#define DM_HS20_POLICY_UPDATE \
- "org.wi-fi.hotspot2dot0.PolicyUpdate"
-
-#define DM_URI_PPS "./Wi-Fi/org.wi-fi/PerProviderSubscription"
-#define DM_URI_LAUNCH_BROWSER \
- "./DevDetail/Ext/org.wi-fi/Wi-Fi/Ops/launchBrowserToURI"
-
-
-static void add_item(struct hs20_osu_client *ctx, xml_node_t *parent,
- const char *locuri, const char *data);
-
-
-static const char * int2str(int val)
-{
- static char buf[20];
- snprintf(buf, sizeof(buf), "%d", val);
- return buf;
-}
-
-
-static char * oma_dm_get_target_locuri(struct hs20_osu_client *ctx,
- xml_node_t *node)
-{
- xml_node_t *locuri;
- char *uri, *ret = NULL;
-
- locuri = get_node(ctx->xml, node, "Item/Target/LocURI");
- if (locuri == NULL)
- return NULL;
-
- uri = xml_node_get_text(ctx->xml, locuri);
- if (uri)
- ret = os_strdup(uri);
- xml_node_get_text_free(ctx->xml, uri);
- return ret;
-}
-
-
-static void oma_dm_add_locuri(struct hs20_osu_client *ctx, xml_node_t *parent,
- const char *element, const char *uri)
-{
- xml_node_t *node;
-
- node = xml_node_create(ctx->xml, parent, NULL, element);
- if (node == NULL)
- return;
- xml_node_create_text(ctx->xml, node, NULL, "LocURI", uri);
-}
-
-
-static xml_node_t * oma_dm_build_hdr(struct hs20_osu_client *ctx,
- const char *url, int msgid)
-{
- xml_node_t *syncml, *synchdr;
- xml_namespace_t *ns;
-
- if (!ctx->devid) {
- wpa_printf(MSG_ERROR,
- "DevId from devinfo.xml is not available - cannot use OMA DM");
- return NULL;
- }
-
- syncml = xml_node_create_root(ctx->xml, "SYNCML:SYNCML1.2", NULL, &ns,
- "SyncML");
-
- synchdr = xml_node_create(ctx->xml, syncml, NULL, "SyncHdr");
- xml_node_create_text(ctx->xml, synchdr, NULL, "VerDTD", "1.2");
- xml_node_create_text(ctx->xml, synchdr, NULL, "VerProto", "DM/1.2");
- xml_node_create_text(ctx->xml, synchdr, NULL, "SessionID", "1");
- xml_node_create_text(ctx->xml, synchdr, NULL, "MsgID", int2str(msgid));
-
- oma_dm_add_locuri(ctx, synchdr, "Target", url);
- oma_dm_add_locuri(ctx, synchdr, "Source", ctx->devid);
-
- return syncml;
-}
-
-
-static void oma_dm_add_cmdid(struct hs20_osu_client *ctx, xml_node_t *parent,
- int cmdid)
-{
- xml_node_create_text(ctx->xml, parent, NULL, "CmdID", int2str(cmdid));
-}
-
-
-static xml_node_t * add_alert(struct hs20_osu_client *ctx, xml_node_t *parent,
- int cmdid, int data)
-{
- xml_node_t *node;
-
- node = xml_node_create(ctx->xml, parent, NULL, "Alert");
- if (node == NULL)
- return NULL;
- oma_dm_add_cmdid(ctx, node, cmdid);
- xml_node_create_text(ctx->xml, node, NULL, "Data", int2str(data));
-
- return node;
-}
-
-
-static xml_node_t * add_status(struct hs20_osu_client *ctx, xml_node_t *parent,
- int msgref, int cmdref, int cmdid,
- const char *cmd, int data, const char *targetref)
-{
- xml_node_t *node;
-
- node = xml_node_create(ctx->xml, parent, NULL, "Status");
- if (node == NULL)
- return NULL;
- oma_dm_add_cmdid(ctx, node, cmdid);
- xml_node_create_text(ctx->xml, node, NULL, "MsgRef", int2str(msgref));
- if (cmdref)
- xml_node_create_text(ctx->xml, node, NULL, "CmdRef",
- int2str(cmdref));
- xml_node_create_text(ctx->xml, node, NULL, "Cmd", cmd);
- xml_node_create_text(ctx->xml, node, NULL, "Data", int2str(data));
- if (targetref) {
- xml_node_create_text(ctx->xml, node, NULL, "TargetRef",
- targetref);
- }
-
- return node;
-}
-
-
-static xml_node_t * add_results(struct hs20_osu_client *ctx, xml_node_t *parent,
- int msgref, int cmdref, int cmdid,
- const char *locuri, const char *data)
-{
- xml_node_t *node;
-
- node = xml_node_create(ctx->xml, parent, NULL, "Results");
- if (node == NULL)
- return NULL;
-
- oma_dm_add_cmdid(ctx, node, cmdid);
- xml_node_create_text(ctx->xml, node, NULL, "MsgRef", int2str(msgref));
- xml_node_create_text(ctx->xml, node, NULL, "CmdRef", int2str(cmdref));
- add_item(ctx, node, locuri, data);
-
- return node;
-}
-
-
-static char * mo_str(struct hs20_osu_client *ctx, const char *urn,
- const char *fname)
-{
- xml_node_t *fnode, *tnds;
- char *str;
-
- fnode = node_from_file(ctx->xml, fname);
- if (!fnode)
- return NULL;
- tnds = mo_to_tnds(ctx->xml, fnode, 0, urn, "syncml:dmddf1.2");
- xml_node_free(ctx->xml, fnode);
- if (!tnds)
- return NULL;
-
- str = xml_node_to_str(ctx->xml, tnds);
- xml_node_free(ctx->xml, tnds);
- if (str == NULL)
- return NULL;
- wpa_printf(MSG_INFO, "MgmtTree: %s", str);
-
- return str;
-}
-
-
-static void add_item(struct hs20_osu_client *ctx, xml_node_t *parent,
- const char *locuri, const char *data)
-{
- xml_node_t *item, *node;
-
- item = xml_node_create(ctx->xml, parent, NULL, "Item");
- oma_dm_add_locuri(ctx, item, "Source", locuri);
- node = xml_node_create(ctx->xml, item, NULL, "Meta");
- xml_node_create_text_ns(ctx->xml, node, "syncml:metinf", "Format",
- "Chr");
- xml_node_create_text_ns(ctx->xml, node, "syncml:metinf", "Type",
- "text/plain");
- xml_node_create_text(ctx->xml, item, NULL, "Data", data);
-}
-
-
-static void add_replace_devinfo(struct hs20_osu_client *ctx, xml_node_t *parent,
- int cmdid)
-{
- xml_node_t *info, *child, *replace;
- const char *name;
- char locuri[200], *txt;
-
- info = node_from_file(ctx->xml, "devinfo.xml");
- if (info == NULL) {
- wpa_printf(MSG_INFO, "Could not read devinfo.xml");
- return;
- }
-
- replace = xml_node_create(ctx->xml, parent, NULL, "Replace");
- if (replace == NULL) {
- xml_node_free(ctx->xml, info);
- return;
- }
- oma_dm_add_cmdid(ctx, replace, cmdid);
-
- xml_node_for_each_child(ctx->xml, child, info) {
- xml_node_for_each_check(ctx->xml, child);
- name = xml_node_get_localname(ctx->xml, child);
- os_snprintf(locuri, sizeof(locuri), "./DevInfo/%s", name);
- txt = xml_node_get_text(ctx->xml, child);
- if (txt) {
- add_item(ctx, replace, locuri, txt);
- xml_node_get_text_free(ctx->xml, txt);
- }
- }
-
- xml_node_free(ctx->xml, info);
-}
-
-
-static void oma_dm_add_hs20_generic_alert(struct hs20_osu_client *ctx,
- xml_node_t *syncbody,
- int cmdid, const char *oper,
- const char *data)
-{
- xml_node_t *node, *item;
- char buf[200];
-
- node = add_alert(ctx, syncbody, cmdid, DM_GENERIC_ALERT);
-
- item = xml_node_create(ctx->xml, node, NULL, "Item");
- oma_dm_add_locuri(ctx, item, "Source", DM_URI_PPS);
- node = xml_node_create(ctx->xml, item, NULL, "Meta");
- snprintf(buf, sizeof(buf), "Reversed-Domain-Name: %s", oper);
- xml_node_create_text_ns(ctx->xml, node, "syncml:metinf", "Type", buf);
- xml_node_create_text_ns(ctx->xml, node, "syncml:metinf", "Format",
- "xml");
- xml_node_create_text(ctx->xml, item, NULL, "Data", data);
-}
-
-
-static xml_node_t * build_oma_dm_1(struct hs20_osu_client *ctx,
- const char *url, int msgid, const char *oper)
-{
- xml_node_t *syncml, *syncbody;
- char *str;
- int cmdid = 0;
-
- syncml = oma_dm_build_hdr(ctx, url, msgid);
- if (syncml == NULL)
- return NULL;
-
- syncbody = xml_node_create(ctx->xml, syncml, NULL, "SyncBody");
- if (syncbody == NULL) {
- xml_node_free(ctx->xml, syncml);
- return NULL;
- }
-
- cmdid++;
- add_alert(ctx, syncbody, cmdid, DM_CLIENT_INITIATED_MGMT);
-
- str = mo_str(ctx, NULL, "devdetail.xml");
- if (str == NULL) {
- xml_node_free(ctx->xml, syncml);
- return NULL;
- }
- cmdid++;
- oma_dm_add_hs20_generic_alert(ctx, syncbody, cmdid, oper, str);
- os_free(str);
-
- cmdid++;
- add_replace_devinfo(ctx, syncbody, cmdid);
-
- xml_node_create(ctx->xml, syncbody, NULL, "Final");
-
- return syncml;
-}
-
-
-static xml_node_t * build_oma_dm_1_sub_reg(struct hs20_osu_client *ctx,
- const char *url, int msgid)
-{
- xml_node_t *syncml;
-
- syncml = build_oma_dm_1(ctx, url, msgid, DM_HS20_SUBSCRIPTION_CREATION);
- if (syncml)
- debug_dump_node(ctx, "OMA-DM Package 1 (sub reg)", syncml);
-
- return syncml;
-}
-
-
-static xml_node_t * build_oma_dm_1_sub_prov(struct hs20_osu_client *ctx,
- const char *url, int msgid)
-{
- xml_node_t *syncml;
-
- syncml = build_oma_dm_1(ctx, url, msgid,
- DM_HS20_SUBSCRIPTION_PROVISIONING);
- if (syncml)
- debug_dump_node(ctx, "OMA-DM Package 1 (sub prov)", syncml);
-
- return syncml;
-}
-
-
-static xml_node_t * build_oma_dm_1_pol_upd(struct hs20_osu_client *ctx,
- const char *url, int msgid)
-{
- xml_node_t *syncml;
-
- syncml = build_oma_dm_1(ctx, url, msgid, DM_HS20_POLICY_UPDATE);
- if (syncml)
- debug_dump_node(ctx, "OMA-DM Package 1 (pol upd)", syncml);
-
- return syncml;
-}
-
-
-static xml_node_t * build_oma_dm_1_sub_rem(struct hs20_osu_client *ctx,
- const char *url, int msgid)
-{
- xml_node_t *syncml;
-
- syncml = build_oma_dm_1(ctx, url, msgid,
- DM_HS20_SUBSCRIPTION_REMEDIATION);
- if (syncml)
- debug_dump_node(ctx, "OMA-DM Package 1 (sub rem)", syncml);
-
- return syncml;
-}
-
-
-static int oma_dm_exec_browser(struct hs20_osu_client *ctx, xml_node_t *exec)
-{
- xml_node_t *node;
- char *data;
- int res;
-
- node = get_node(ctx->xml, exec, "Item/Data");
- if (node == NULL) {
- wpa_printf(MSG_INFO, "No Data node found");
- return DM_RESP_BAD_REQUEST;
- }
-
- data = xml_node_get_text(ctx->xml, node);
- if (data == NULL) {
- wpa_printf(MSG_INFO, "Invalid data");
- return DM_RESP_BAD_REQUEST;
- }
- wpa_printf(MSG_INFO, "Data: %s", data);
- wpa_printf(MSG_INFO, "Launch browser to URI '%s'", data);
- write_summary(ctx, "Launch browser to URI '%s'", data);
- res = hs20_web_browser(data, 1);
- xml_node_get_text_free(ctx->xml, data);
- if (res > 0) {
- wpa_printf(MSG_INFO, "User response in browser completed successfully");
- write_summary(ctx, "User response in browser completed successfully");
- return DM_RESP_OK;
- } else {
- wpa_printf(MSG_INFO, "Failed to receive user response");
- write_summary(ctx, "Failed to receive user response");
- return DM_RESP_COMMAND_FAILED;
- }
-}
-
-
-static int oma_dm_exec_get_cert(struct hs20_osu_client *ctx, xml_node_t *exec)
-{
- xml_node_t *node, *getcert;
- char *data;
- const char *name;
- int res;
-
- wpa_printf(MSG_INFO, "Client certificate enrollment");
- write_summary(ctx, "Client certificate enrollment");
-
- node = get_node(ctx->xml, exec, "Item/Data");
- if (node == NULL) {
- wpa_printf(MSG_INFO, "No Data node found");
- return DM_RESP_BAD_REQUEST;
- }
-
- data = xml_node_get_text(ctx->xml, node);
- if (data == NULL) {
- wpa_printf(MSG_INFO, "Invalid data");
- return DM_RESP_BAD_REQUEST;
- }
- wpa_printf(MSG_INFO, "Data: %s", data);
- getcert = xml_node_from_buf(ctx->xml, data);
- xml_node_get_text_free(ctx->xml, data);
-
- if (getcert == NULL) {
- wpa_printf(MSG_INFO, "Could not parse Item/Data node contents");
- return DM_RESP_BAD_REQUEST;
- }
-
- debug_dump_node(ctx, "OMA-DM getCertificate", getcert);
-
- name = xml_node_get_localname(ctx->xml, getcert);
- if (name == NULL || os_strcasecmp(name, "getCertificate") != 0) {
- wpa_printf(MSG_INFO, "Unexpected getCertificate node name '%s'",
- name);
- return DM_RESP_BAD_REQUEST;
- }
-
- res = osu_get_certificate(ctx, getcert);
-
- xml_node_free(ctx->xml, getcert);
-
- return res == 0 ? DM_RESP_OK : DM_RESP_COMMAND_FAILED;
-}
-
-
-static int oma_dm_exec(struct hs20_osu_client *ctx, xml_node_t *exec)
-{
- char *locuri;
- int ret;
-
- locuri = oma_dm_get_target_locuri(ctx, exec);
- if (locuri == NULL) {
- wpa_printf(MSG_INFO, "No Target LocURI node found");
- return DM_RESP_BAD_REQUEST;
- }
-
- wpa_printf(MSG_INFO, "Target LocURI: %s", locuri);
-
- if (os_strcasecmp(locuri, "./DevDetail/Ext/org.wi-fi/Wi-Fi/Ops/"
- "launchBrowserToURI") == 0) {
- ret = oma_dm_exec_browser(ctx, exec);
- } else if (os_strcasecmp(locuri, "./DevDetail/Ext/org.wi-fi/Wi-Fi/Ops/"
- "getCertificate") == 0) {
- ret = oma_dm_exec_get_cert(ctx, exec);
- } else {
- wpa_printf(MSG_INFO, "Unsupported exec Target LocURI");
- ret = DM_RESP_NOT_FOUND;
- }
- os_free(locuri);
-
- return ret;
-}
-
-
-static int oma_dm_run_add(struct hs20_osu_client *ctx, const char *locuri,
- xml_node_t *add, xml_node_t *pps,
- const char *pps_fname)
-{
- const char *pos;
- size_t fqdn_len;
- xml_node_t *node, *tnds, *unode, *pps_node;
- char *data, *uri, *upos, *end;
- int use_tnds = 0;
- size_t uri_len;
-
- wpa_printf(MSG_INFO, "Add command target LocURI: %s", locuri);
-
- if (os_strncasecmp(locuri, "./Wi-Fi/", 8) != 0) {
- wpa_printf(MSG_INFO, "Do not allow Add outside ./Wi-Fi");
- return DM_RESP_PERMISSION_DENIED;
- }
- pos = locuri + 8;
-
- if (ctx->fqdn == NULL)
- return DM_RESP_COMMAND_FAILED;
- fqdn_len = os_strlen(ctx->fqdn);
- if (os_strncasecmp(pos, ctx->fqdn, fqdn_len) != 0 ||
- pos[fqdn_len] != '/') {
- wpa_printf(MSG_INFO, "Do not allow Add outside ./Wi-Fi/%s",
- ctx->fqdn);
- return DM_RESP_PERMISSION_DENIED;
- }
- pos += fqdn_len + 1;
-
- if (os_strncasecmp(pos, "PerProviderSubscription/", 24) != 0) {
- wpa_printf(MSG_INFO,
- "Do not allow Add outside ./Wi-Fi/%s/PerProviderSubscription",
- ctx->fqdn);
- return DM_RESP_PERMISSION_DENIED;
- }
- pos += 24;
-
- wpa_printf(MSG_INFO, "Add command for PPS node %s", pos);
-
- pps_node = get_node(ctx->xml, pps, pos);
- if (pps_node) {
- wpa_printf(MSG_INFO, "Specified PPS node exists already");
- return DM_RESP_ALREADY_EXISTS;
- }
-
- uri = os_strdup(pos);
- if (uri == NULL)
- return DM_RESP_COMMAND_FAILED;
- while (!pps_node) {
- upos = os_strrchr(uri, '/');
- if (!upos)
- break;
- upos[0] = '\0';
- pps_node = get_node(ctx->xml, pps, uri);
- wpa_printf(MSG_INFO, "Node %s %s", uri,
- pps_node ? "exists" : "does not exist");
- }
-
- wpa_printf(MSG_INFO, "Parent URI: %s", uri);
-
- if (!pps_node) {
- /* Add at root of PPS MO */
- pps_node = pps;
- }
-
- uri_len = os_strlen(uri);
- os_strlcpy(uri, pos + uri_len, os_strlen(pos));
- upos = uri;
- while (*upos == '/')
- upos++;
- wpa_printf(MSG_INFO, "Nodes to add: %s", upos);
-
- for (;;) {
- end = os_strchr(upos, '/');
- if (!end)
- break;
- *end = '\0';
- wpa_printf(MSG_INFO, "Adding interim node %s", upos);
- pps_node = xml_node_create(ctx->xml, pps_node, NULL, upos);
- if (pps_node == NULL) {
- os_free(uri);
- return DM_RESP_COMMAND_FAILED;
- }
- upos = end + 1;
- }
-
- wpa_printf(MSG_INFO, "Adding node %s", upos);
-
- node = get_node(ctx->xml, add, "Item/Meta/Type");
- if (node) {
- char *type;
- type = xml_node_get_text(ctx->xml, node);
- if (type == NULL) {
- wpa_printf(MSG_ERROR, "Could not find type text");
- os_free(uri);
- return DM_RESP_BAD_REQUEST;
- }
- use_tnds = node &&
- os_strstr(type, "application/vnd.syncml.dmtnds+xml");
- }
-
- node = get_node(ctx->xml, add, "Item/Data");
- if (node == NULL) {
- wpa_printf(MSG_INFO, "No Add/Item/Data found");
- os_free(uri);
- return DM_RESP_BAD_REQUEST;
- }
-
- data = xml_node_get_text(ctx->xml, node);
- if (data == NULL) {
- wpa_printf(MSG_INFO, "Could not get Add/Item/Data text");
- os_free(uri);
- return DM_RESP_BAD_REQUEST;
- }
-
- wpa_printf(MSG_DEBUG, "Add/Item/Data: %s", data);
-
- if (use_tnds) {
- tnds = xml_node_from_buf(ctx->xml, data);
- xml_node_get_text_free(ctx->xml, data);
- if (tnds == NULL) {
- wpa_printf(MSG_INFO,
- "Could not parse Add/Item/Data text");
- os_free(uri);
- return DM_RESP_BAD_REQUEST;
- }
-
- unode = tnds_to_mo(ctx->xml, tnds);
- xml_node_free(ctx->xml, tnds);
- if (unode == NULL) {
- wpa_printf(MSG_INFO, "Could not parse TNDS text");
- os_free(uri);
- return DM_RESP_BAD_REQUEST;
- }
-
- debug_dump_node(ctx, "Parsed TNDS", unode);
-
- xml_node_add_child(ctx->xml, pps_node, unode);
- } else {
- /* TODO: What to do here? */
- os_free(uri);
- return DM_RESP_BAD_REQUEST;
- }
-
- os_free(uri);
-
- if (update_pps_file(ctx, pps_fname, pps) < 0)
- return DM_RESP_COMMAND_FAILED;
-
- ctx->pps_updated = 1;
-
- return DM_RESP_OK;
-}
-
-
-static int oma_dm_add(struct hs20_osu_client *ctx, xml_node_t *add,
- xml_node_t *pps, const char *pps_fname)
-{
- xml_node_t *node;
- char *locuri;
- char fname[300];
- int ret;
-
- node = get_node(ctx->xml, add, "Item/Target/LocURI");
- if (node == NULL) {
- wpa_printf(MSG_INFO, "No Target LocURI node found");
- return DM_RESP_BAD_REQUEST;
- }
- locuri = xml_node_get_text(ctx->xml, node);
- if (locuri == NULL) {
- wpa_printf(MSG_ERROR, "No LocURI node text found");
- return DM_RESP_BAD_REQUEST;
- }
- wpa_printf(MSG_INFO, "Target LocURI: %s", locuri);
- if (os_strncasecmp(locuri, "./Wi-Fi/", 8) != 0) {
- wpa_printf(MSG_INFO, "Unsupported Add Target LocURI");
- xml_node_get_text_free(ctx->xml, locuri);
- return DM_RESP_PERMISSION_DENIED;
- }
-
- node = get_node(ctx->xml, add, "Item/Data");
- if (node == NULL) {
- wpa_printf(MSG_INFO, "No Data node found");
- xml_node_get_text_free(ctx->xml, locuri);
- return DM_RESP_BAD_REQUEST;
- }
-
- if (pps_fname && os_file_exists(pps_fname)) {
- ret = oma_dm_run_add(ctx, locuri, add, pps, pps_fname);
- if (ret != DM_RESP_OK) {
- xml_node_get_text_free(ctx->xml, locuri);
- return ret;
- }
- ret = 0;
- os_strlcpy(fname, pps_fname, sizeof(fname));
- } else
- ret = hs20_add_pps_mo(ctx, locuri, node, fname, sizeof(fname));
- xml_node_get_text_free(ctx->xml, locuri);
- if (ret < 0)
- return ret == -2 ? DM_RESP_ALREADY_EXISTS :
- DM_RESP_COMMAND_FAILED;
-
- if (ctx->no_reconnect == 2) {
- os_snprintf(ctx->pps_fname, sizeof(ctx->pps_fname), "%s",
- fname);
- ctx->pps_cred_set = 1;
- return DM_RESP_OK;
- }
-
- wpa_printf(MSG_INFO, "Updating wpa_supplicant credentials");
- cmd_set_pps(ctx, fname);
-
- if (ctx->no_reconnect)
- return DM_RESP_OK;
-
- wpa_printf(MSG_INFO, "Requesting reconnection with updated configuration");
- if (wpa_command(ctx->ifname, "INTERWORKING_SELECT auto") < 0)
- wpa_printf(MSG_INFO, "Failed to request wpa_supplicant to reconnect");
-
- return DM_RESP_OK;
-}
-
-
-static int oma_dm_replace(struct hs20_osu_client *ctx, xml_node_t *replace,
- xml_node_t *pps, const char *pps_fname)
-{
- char *locuri, *pos;
- size_t fqdn_len;
- xml_node_t *node, *tnds, *unode, *pps_node, *parent;
- char *data;
- int use_tnds = 0;
-
- locuri = oma_dm_get_target_locuri(ctx, replace);
- if (locuri == NULL)
- return DM_RESP_BAD_REQUEST;
-
- wpa_printf(MSG_INFO, "Replace command target LocURI: %s", locuri);
- if (os_strncasecmp(locuri, "./Wi-Fi/", 8) != 0) {
- wpa_printf(MSG_INFO, "Do not allow Replace outside ./Wi-Fi");
- os_free(locuri);
- return DM_RESP_PERMISSION_DENIED;
- }
- pos = locuri + 8;
-
- if (ctx->fqdn == NULL) {
- os_free(locuri);
- return DM_RESP_COMMAND_FAILED;
- }
- fqdn_len = os_strlen(ctx->fqdn);
- if (os_strncasecmp(pos, ctx->fqdn, fqdn_len) != 0 ||
- pos[fqdn_len] != '/') {
- wpa_printf(MSG_INFO, "Do not allow Replace outside ./Wi-Fi/%s",
- ctx->fqdn);
- os_free(locuri);
- return DM_RESP_PERMISSION_DENIED;
- }
- pos += fqdn_len + 1;
-
- if (os_strncasecmp(pos, "PerProviderSubscription/", 24) != 0) {
- wpa_printf(MSG_INFO,
- "Do not allow Replace outside ./Wi-Fi/%s/PerProviderSubscription",
- ctx->fqdn);
- os_free(locuri);
- return DM_RESP_PERMISSION_DENIED;
- }
- pos += 24;
-
- wpa_printf(MSG_INFO, "Replace command for PPS node %s", pos);
-
- pps_node = get_node(ctx->xml, pps, pos);
- if (pps_node == NULL) {
- wpa_printf(MSG_INFO, "Specified PPS node not found");
- os_free(locuri);
- return DM_RESP_NOT_FOUND;
- }
-
- node = get_node(ctx->xml, replace, "Item/Meta/Type");
- if (node) {
- char *type;
- type = xml_node_get_text(ctx->xml, node);
- if (type == NULL) {
- wpa_printf(MSG_INFO, "Could not find type text");
- os_free(locuri);
- return DM_RESP_BAD_REQUEST;
- }
- use_tnds = node &&
- os_strstr(type, "application/vnd.syncml.dmtnds+xml");
- }
-
- node = get_node(ctx->xml, replace, "Item/Data");
- if (node == NULL) {
- wpa_printf(MSG_INFO, "No Replace/Item/Data found");
- os_free(locuri);
- return DM_RESP_BAD_REQUEST;
- }
-
- data = xml_node_get_text(ctx->xml, node);
- if (data == NULL) {
- wpa_printf(MSG_INFO, "Could not get Replace/Item/Data text");
- os_free(locuri);
- return DM_RESP_BAD_REQUEST;
- }
-
- wpa_printf(MSG_DEBUG, "Replace/Item/Data: %s", data);
-
- if (use_tnds) {
- tnds = xml_node_from_buf(ctx->xml, data);
- xml_node_get_text_free(ctx->xml, data);
- if (tnds == NULL) {
- wpa_printf(MSG_INFO,
- "Could not parse Replace/Item/Data text");
- os_free(locuri);
- return DM_RESP_BAD_REQUEST;
- }
-
- unode = tnds_to_mo(ctx->xml, tnds);
- xml_node_free(ctx->xml, tnds);
- if (unode == NULL) {
- wpa_printf(MSG_INFO, "Could not parse TNDS text");
- os_free(locuri);
- return DM_RESP_BAD_REQUEST;
- }
-
- debug_dump_node(ctx, "Parsed TNDS", unode);
-
- parent = xml_node_get_parent(ctx->xml, pps_node);
- xml_node_detach(ctx->xml, pps_node);
- xml_node_add_child(ctx->xml, parent, unode);
- } else {
- xml_node_set_text(ctx->xml, pps_node, data);
- xml_node_get_text_free(ctx->xml, data);
- }
-
- os_free(locuri);
-
- if (update_pps_file(ctx, pps_fname, pps) < 0)
- return DM_RESP_COMMAND_FAILED;
-
- ctx->pps_updated = 1;
-
- return DM_RESP_OK;
-}
-
-
-static int oma_dm_get(struct hs20_osu_client *ctx, xml_node_t *get,
- xml_node_t *pps, const char *pps_fname, char **value)
-{
- char *locuri, *pos;
- size_t fqdn_len;
- xml_node_t *pps_node;
- const char *name;
-
- *value = NULL;
-
- locuri = oma_dm_get_target_locuri(ctx, get);
- if (locuri == NULL)
- return DM_RESP_BAD_REQUEST;
-
- wpa_printf(MSG_INFO, "Get command target LocURI: %s", locuri);
- if (os_strncasecmp(locuri, "./Wi-Fi/", 8) != 0) {
- wpa_printf(MSG_INFO, "Do not allow Get outside ./Wi-Fi");
- os_free(locuri);
- return DM_RESP_PERMISSION_DENIED;
- }
- pos = locuri + 8;
-
- if (ctx->fqdn == NULL)
- return DM_RESP_COMMAND_FAILED;
- fqdn_len = os_strlen(ctx->fqdn);
- if (os_strncasecmp(pos, ctx->fqdn, fqdn_len) != 0 ||
- pos[fqdn_len] != '/') {
- wpa_printf(MSG_INFO, "Do not allow Get outside ./Wi-Fi/%s",
- ctx->fqdn);
- os_free(locuri);
- return DM_RESP_PERMISSION_DENIED;
- }
- pos += fqdn_len + 1;
-
- if (os_strncasecmp(pos, "PerProviderSubscription/", 24) != 0) {
- wpa_printf(MSG_INFO,
- "Do not allow Get outside ./Wi-Fi/%s/PerProviderSubscription",
- ctx->fqdn);
- os_free(locuri);
- return DM_RESP_PERMISSION_DENIED;
- }
- pos += 24;
-
- wpa_printf(MSG_INFO, "Get command for PPS node %s", pos);
-
- pps_node = get_node(ctx->xml, pps, pos);
- if (pps_node == NULL) {
- wpa_printf(MSG_INFO, "Specified PPS node not found");
- os_free(locuri);
- return DM_RESP_NOT_FOUND;
- }
-
- name = xml_node_get_localname(ctx->xml, pps_node);
- wpa_printf(MSG_INFO, "Get command returned node with name '%s'", name);
- if (os_strcasecmp(name, "Password") == 0) {
- wpa_printf(MSG_INFO, "Do not allow Get for Password node");
- os_free(locuri);
- return DM_RESP_PERMISSION_DENIED;
- }
-
- /*
- * TODO: No support for DMTNDS, so if interior node, reply with a
- * list of children node names in Results element. The child list type is
- * defined in [DMTND].
- */
-
- *value = xml_node_get_text(ctx->xml, pps_node);
- if (*value == NULL)
- return DM_RESP_COMMAND_FAILED;
-
- return DM_RESP_OK;
-}
-
-
-static int oma_dm_get_cmdid(struct hs20_osu_client *ctx, xml_node_t *node)
-{
- xml_node_t *cnode;
- char *str;
- int ret;
-
- cnode = get_node(ctx->xml, node, "CmdID");
- if (cnode == NULL)
- return 0;
-
- str = xml_node_get_text(ctx->xml, cnode);
- if (str == NULL)
- return 0;
- ret = atoi(str);
- xml_node_get_text_free(ctx->xml, str);
- return ret;
-}
-
-
-static xml_node_t * oma_dm_send_recv(struct hs20_osu_client *ctx,
- const char *url, xml_node_t *syncml,
- const char *ext_hdr,
- const char *username, const char *password,
- const char *client_cert,
- const char *client_key)
-{
- xml_node_t *resp;
- char *str, *res;
- char *resp_uri = NULL;
-
- str = xml_node_to_str(ctx->xml, syncml);
- xml_node_free(ctx->xml, syncml);
- if (str == NULL)
- return NULL;
-
- wpa_printf(MSG_INFO, "Send OMA DM Package");
- write_summary(ctx, "Send OMA DM Package");
- os_free(ctx->server_url);
- ctx->server_url = os_strdup(url);
- res = http_post(ctx->http, url, str, "application/vnd.syncml.dm+xml",
- ext_hdr, ctx->ca_fname, username, password,
- client_cert, client_key, NULL);
- os_free(str);
- os_free(resp_uri);
- resp_uri = NULL;
-
- if (res == NULL) {
- const char *err = http_get_err(ctx->http);
- if (err) {
- wpa_printf(MSG_INFO, "HTTP error: %s", err);
- write_result(ctx, "HTTP error: %s", err);
- } else {
- write_summary(ctx, "Failed to send OMA DM Package");
- }
- return NULL;
- }
- wpa_printf(MSG_DEBUG, "Server response: %s", res);
-
- wpa_printf(MSG_INFO, "Process OMA DM Package");
- write_summary(ctx, "Process received OMA DM Package");
- resp = xml_node_from_buf(ctx->xml, res);
- os_free(res);
- if (resp == NULL) {
- wpa_printf(MSG_INFO, "Failed to parse OMA DM response");
- return NULL;
- }
-
- debug_dump_node(ctx, "OMA DM Package", resp);
-
- return resp;
-}
-
-
-static xml_node_t * oma_dm_process(struct hs20_osu_client *ctx, const char *url,
- xml_node_t *resp, int msgid,
- char **ret_resp_uri,
- xml_node_t *pps, const char *pps_fname)
-{
- xml_node_t *syncml, *syncbody, *hdr, *body, *child;
- const char *name;
- char *resp_uri = NULL;
- int server_msgid = 0;
- int cmdid = 0;
- int server_cmdid;
- int resp_needed = 0;
- char *tmp;
- int final = 0;
- char *locuri;
-
- *ret_resp_uri = NULL;
-
- name = xml_node_get_localname(ctx->xml, resp);
- if (name == NULL || os_strcasecmp(name, "SyncML") != 0) {
- wpa_printf(MSG_INFO, "SyncML node not found");
- return NULL;
- }
-
- hdr = get_node(ctx->xml, resp, "SyncHdr");
- body = get_node(ctx->xml, resp, "SyncBody");
- if (hdr == NULL || body == NULL) {
- wpa_printf(MSG_INFO, "Could not find SyncHdr or SyncBody");
- return NULL;
- }
-
- xml_node_for_each_child(ctx->xml, child, hdr) {
- xml_node_for_each_check(ctx->xml, child);
- name = xml_node_get_localname(ctx->xml, child);
- wpa_printf(MSG_INFO, "SyncHdr %s", name);
- if (os_strcasecmp(name, "RespURI") == 0) {
- tmp = xml_node_get_text(ctx->xml, child);
- if (tmp)
- resp_uri = os_strdup(tmp);
- xml_node_get_text_free(ctx->xml, tmp);
- } else if (os_strcasecmp(name, "MsgID") == 0) {
- tmp = xml_node_get_text(ctx->xml, child);
- if (tmp)
- server_msgid = atoi(tmp);
- xml_node_get_text_free(ctx->xml, tmp);
- }
- }
-
- wpa_printf(MSG_INFO, "Server MsgID: %d", server_msgid);
- if (resp_uri)
- wpa_printf(MSG_INFO, "RespURI: %s", resp_uri);
-
- syncml = oma_dm_build_hdr(ctx, resp_uri ? resp_uri : url, msgid);
- if (syncml == NULL) {
- os_free(resp_uri);
- return NULL;
- }
-
- syncbody = xml_node_create(ctx->xml, syncml, NULL, "SyncBody");
- cmdid++;
- add_status(ctx, syncbody, server_msgid, 0, cmdid, "SyncHdr",
- DM_RESP_AUTH_ACCEPTED, NULL);
-
- xml_node_for_each_child(ctx->xml, child, body) {
- xml_node_for_each_check(ctx->xml, child);
- server_cmdid = oma_dm_get_cmdid(ctx, child);
- name = xml_node_get_localname(ctx->xml, child);
- wpa_printf(MSG_INFO, "SyncBody CmdID=%d - %s",
- server_cmdid, name);
- if (os_strcasecmp(name, "Exec") == 0) {
- int res = oma_dm_exec(ctx, child);
- cmdid++;
- locuri = oma_dm_get_target_locuri(ctx, child);
- if (locuri == NULL)
- res = DM_RESP_BAD_REQUEST;
- add_status(ctx, syncbody, server_msgid, server_cmdid,
- cmdid, name, res, locuri);
- os_free(locuri);
- resp_needed = 1;
- } else if (os_strcasecmp(name, "Add") == 0) {
- int res = oma_dm_add(ctx, child, pps, pps_fname);
- cmdid++;
- locuri = oma_dm_get_target_locuri(ctx, child);
- if (locuri == NULL)
- res = DM_RESP_BAD_REQUEST;
- add_status(ctx, syncbody, server_msgid, server_cmdid,
- cmdid, name, res, locuri);
- os_free(locuri);
- resp_needed = 1;
- } else if (os_strcasecmp(name, "Replace") == 0) {
- int res;
- res = oma_dm_replace(ctx, child, pps, pps_fname);
- cmdid++;
- locuri = oma_dm_get_target_locuri(ctx, child);
- if (locuri == NULL)
- res = DM_RESP_BAD_REQUEST;
- add_status(ctx, syncbody, server_msgid, server_cmdid,
- cmdid, name, res, locuri);
- os_free(locuri);
- resp_needed = 1;
- } else if (os_strcasecmp(name, "Status") == 0) {
- /* TODO: Verify success */
- } else if (os_strcasecmp(name, "Get") == 0) {
- int res;
- char *value;
- res = oma_dm_get(ctx, child, pps, pps_fname, &value);
- cmdid++;
- locuri = oma_dm_get_target_locuri(ctx, child);
- if (locuri == NULL)
- res = DM_RESP_BAD_REQUEST;
- add_status(ctx, syncbody, server_msgid, server_cmdid,
- cmdid, name, res, locuri);
- if (res == DM_RESP_OK && value) {
- cmdid++;
- add_results(ctx, syncbody, server_msgid,
- server_cmdid, cmdid, locuri, value);
- }
- os_free(locuri);
- xml_node_get_text_free(ctx->xml, value);
- resp_needed = 1;
-#if 0 /* TODO: MUST support */
- } else if (os_strcasecmp(name, "Delete") == 0) {
-#endif
-#if 0 /* TODO: MUST support */
- } else if (os_strcasecmp(name, "Sequence") == 0) {
-#endif
- } else if (os_strcasecmp(name, "Final") == 0) {
- final = 1;
- break;
- } else {
- locuri = oma_dm_get_target_locuri(ctx, child);
- add_status(ctx, syncbody, server_msgid, server_cmdid,
- cmdid, name, DM_RESP_COMMAND_NOT_IMPLEMENTED,
- locuri);
- os_free(locuri);
- resp_needed = 1;
- }
- }
-
- if (!final) {
- wpa_printf(MSG_INFO, "Final node not found");
- xml_node_free(ctx->xml, syncml);
- os_free(resp_uri);
- return NULL;
- }
-
- if (!resp_needed) {
- wpa_printf(MSG_INFO, "Exchange completed - no response needed");
- xml_node_free(ctx->xml, syncml);
- os_free(resp_uri);
- return NULL;
- }
-
- xml_node_create(ctx->xml, syncbody, NULL, "Final");
-
- debug_dump_node(ctx, "OMA-DM Package 3", syncml);
-
- *ret_resp_uri = resp_uri;
- return syncml;
-}
-
-
-int cmd_oma_dm_prov(struct hs20_osu_client *ctx, const char *url)
-{
- xml_node_t *syncml, *resp;
- char *resp_uri = NULL;
- int msgid = 0;
-
- if (url == NULL) {
- wpa_printf(MSG_INFO, "Invalid prov command (missing URL)");
- return -1;
- }
-
- wpa_printf(MSG_INFO, "OMA-DM credential provisioning requested");
- write_summary(ctx, "OMA-DM credential provisioning");
-
- msgid++;
- syncml = build_oma_dm_1_sub_reg(ctx, url, msgid);
- if (syncml == NULL)
- return -1;
-
- while (syncml) {
- resp = oma_dm_send_recv(ctx, resp_uri ? resp_uri : url,
- syncml, NULL, NULL, NULL, NULL, NULL);
- if (resp == NULL)
- return -1;
-
- msgid++;
- syncml = oma_dm_process(ctx, url, resp, msgid, &resp_uri,
- NULL, NULL);
- xml_node_free(ctx->xml, resp);
- }
-
- os_free(resp_uri);
-
- return ctx->pps_cred_set ? 0 : -1;
-}
-
-
-int cmd_oma_dm_sim_prov(struct hs20_osu_client *ctx, const char *url)
-{
- xml_node_t *syncml, *resp;
- char *resp_uri = NULL;
- int msgid = 0;
-
- if (url == NULL) {
- wpa_printf(MSG_INFO, "Invalid prov command (missing URL)");
- return -1;
- }
-
- wpa_printf(MSG_INFO, "OMA-DM SIM provisioning requested");
- ctx->no_reconnect = 2;
-
- wpa_printf(MSG_INFO, "Wait for IP address before starting SIM provisioning");
- write_summary(ctx, "Wait for IP address before starting SIM provisioning");
-
- if (wait_ip_addr(ctx->ifname, 15) < 0) {
- wpa_printf(MSG_INFO, "Could not get IP address for WLAN - try connection anyway");
- }
- write_summary(ctx, "OMA-DM SIM provisioning");
-
- msgid++;
- syncml = build_oma_dm_1_sub_prov(ctx, url, msgid);
- if (syncml == NULL)
- return -1;
-
- while (syncml) {
- resp = oma_dm_send_recv(ctx, resp_uri ? resp_uri : url,
- syncml, NULL, NULL, NULL, NULL, NULL);
- if (resp == NULL)
- return -1;
-
- msgid++;
- syncml = oma_dm_process(ctx, url, resp, msgid, &resp_uri,
- NULL, NULL);
- xml_node_free(ctx->xml, resp);
- }
-
- os_free(resp_uri);
-
- if (ctx->pps_cred_set) {
- wpa_printf(MSG_INFO, "Updating wpa_supplicant credentials");
- cmd_set_pps(ctx, ctx->pps_fname);
-
- wpa_printf(MSG_INFO, "Requesting reconnection with updated configuration");
- write_summary(ctx, "Requesting reconnection with updated configuration");
- if (wpa_command(ctx->ifname, "INTERWORKING_SELECT auto") < 0) {
- wpa_printf(MSG_INFO, "Failed to request wpa_supplicant to reconnect");
- write_summary(ctx, "Failed to request wpa_supplicant to reconnect");
- return -1;
- }
- }
-
- return ctx->pps_cred_set ? 0 : -1;
-}
-
-
-void oma_dm_pol_upd(struct hs20_osu_client *ctx, const char *address,
- const char *pps_fname,
- const char *client_cert, const char *client_key,
- const char *cred_username, const char *cred_password,
- xml_node_t *pps)
-{
- xml_node_t *syncml, *resp;
- char *resp_uri = NULL;
- int msgid = 0;
-
- wpa_printf(MSG_INFO, "OMA-DM policy update");
- write_summary(ctx, "OMA-DM policy update");
-
- msgid++;
- syncml = build_oma_dm_1_pol_upd(ctx, address, msgid);
- if (syncml == NULL)
- return;
-
- while (syncml) {
- resp = oma_dm_send_recv(ctx, resp_uri ? resp_uri : address,
- syncml, NULL, cred_username,
- cred_password, client_cert, client_key);
- if (resp == NULL)
- return;
-
- msgid++;
- syncml = oma_dm_process(ctx, address, resp, msgid, &resp_uri,
- pps, pps_fname);
- xml_node_free(ctx->xml, resp);
- }
-
- os_free(resp_uri);
-
- if (ctx->pps_updated) {
- wpa_printf(MSG_INFO, "Update wpa_supplicant credential based on updated PPS MO");
- write_summary(ctx, "Update wpa_supplicant credential based on updated PPS MO and request connection");
- cmd_set_pps(ctx, pps_fname);
- if (wpa_command(ctx->ifname, "INTERWORKING_SELECT auto") < 0) {
- wpa_printf(MSG_INFO,
- "Failed to request wpa_supplicant to reconnect");
- write_summary(ctx,
- "Failed to request wpa_supplicant to reconnect");
- }
- }
-}
-
-
-void oma_dm_sub_rem(struct hs20_osu_client *ctx, const char *address,
- const char *pps_fname,
- const char *client_cert, const char *client_key,
- const char *cred_username, const char *cred_password,
- xml_node_t *pps)
-{
- xml_node_t *syncml, *resp;
- char *resp_uri = NULL;
- int msgid = 0;
-
- wpa_printf(MSG_INFO, "OMA-DM subscription remediation");
- write_summary(ctx, "OMA-DM subscription remediation");
-
- msgid++;
- syncml = build_oma_dm_1_sub_rem(ctx, address, msgid);
- if (syncml == NULL)
- return;
-
- while (syncml) {
- resp = oma_dm_send_recv(ctx, resp_uri ? resp_uri : address,
- syncml, NULL, cred_username,
- cred_password, client_cert, client_key);
- if (resp == NULL)
- return;
-
- msgid++;
- syncml = oma_dm_process(ctx, address, resp, msgid, &resp_uri,
- pps, pps_fname);
- xml_node_free(ctx->xml, resp);
- }
-
- os_free(resp_uri);
-
- wpa_printf(MSG_INFO, "Update wpa_supplicant credential based on updated PPS MO and request reconnection");
- write_summary(ctx, "Update wpa_supplicant credential based on updated PPS MO and request reconnection");
- cmd_set_pps(ctx, pps_fname);
- if (wpa_command(ctx->ifname, "INTERWORKING_SELECT auto") < 0) {
- wpa_printf(MSG_INFO, "Failed to request wpa_supplicant to reconnect");
- write_summary(ctx, "Failed to request wpa_supplicant to reconnect");
- }
-}
-
-
-void cmd_oma_dm_add(struct hs20_osu_client *ctx, const char *pps_fname,
- const char *add_fname)
-{
- xml_node_t *pps, *add;
- int res;
-
- ctx->fqdn = os_strdup("wi-fi.org");
-
- pps = node_from_file(ctx->xml, pps_fname);
- if (pps == NULL) {
- wpa_printf(MSG_INFO, "PPS file %s could not be parsed",
- pps_fname);
- return;
- }
-
- add = node_from_file(ctx->xml, add_fname);
- if (add == NULL) {
- wpa_printf(MSG_INFO, "Add file %s could not be parsed",
- add_fname);
- xml_node_free(ctx->xml, pps);
- return;
- }
-
- res = oma_dm_add(ctx, add, pps, pps_fname);
- wpa_printf(MSG_INFO, "oma_dm_add --> %d", res);
-
- xml_node_free(ctx->xml, pps);
- xml_node_free(ctx->xml, add);
-}
-
-
-void cmd_oma_dm_replace(struct hs20_osu_client *ctx, const char *pps_fname,
- const char *replace_fname)
-{
- xml_node_t *pps, *replace;
- int res;
-
- ctx->fqdn = os_strdup("wi-fi.org");
-
- pps = node_from_file(ctx->xml, pps_fname);
- if (pps == NULL) {
- wpa_printf(MSG_INFO, "PPS file %s could not be parsed",
- pps_fname);
- return;
- }
-
- replace = node_from_file(ctx->xml, replace_fname);
- if (replace == NULL) {
- wpa_printf(MSG_INFO, "Replace file %s could not be parsed",
- replace_fname);
- xml_node_free(ctx->xml, pps);
- return;
- }
-
- res = oma_dm_replace(ctx, replace, pps, pps_fname);
- wpa_printf(MSG_INFO, "oma_dm_replace --> %d", res);
-
- xml_node_free(ctx->xml, pps);
- xml_node_free(ctx->xml, replace);
-}
#include "includes.h"
#include <time.h>
#include <sys/stat.h>
-#ifdef ANDROID
-#include "private/android_filesystem_config.h"
-#endif /* ANDROID */
#include "common.h"
#include "utils/browser.h"
#include "crypto/sha256.h"
#include "osu_client.h"
-const char *spp_xsd_fname = "spp.xsd";
+static void write_summary(struct hs20_osu_client *ctx, const char *fmt, ...);
-
-void write_result(struct hs20_osu_client *ctx, const char *fmt, ...)
+static void write_result(struct hs20_osu_client *ctx, const char *fmt, ...)
{
va_list ap;
FILE *f;
}
-void write_summary(struct hs20_osu_client *ctx, const char *fmt, ...)
+static void write_summary(struct hs20_osu_client *ctx, const char *fmt, ...)
{
va_list ap;
FILE *f;
}
-void debug_dump_node(struct hs20_osu_client *ctx, const char *title,
- xml_node_t *node)
-{
- char *str = xml_node_to_str(ctx->xml, node);
- wpa_printf(MSG_DEBUG, "[hs20] %s: '%s'", title, str);
- free(str);
-}
-
-
-static int valid_fqdn(const char *fqdn)
-{
- const char *pos;
-
- /* TODO: could make this more complete.. */
- if (strchr(fqdn, '.') == 0 || strlen(fqdn) > 255)
- return 0;
- for (pos = fqdn; *pos; pos++) {
- if (*pos >= 'a' && *pos <= 'z')
- continue;
- if (*pos >= 'A' && *pos <= 'Z')
- continue;
- if (*pos >= '0' && *pos <= '9')
- continue;
- if (*pos == '-' || *pos == '.' || *pos == '_')
- continue;
- return 0;
- }
- return 1;
-}
-
-
-static int android_update_permission(const char *path, mode_t mode)
-{
-#ifdef ANDROID
- /* we need to change file/folder permission for Android */
-
- if (!path) {
- wpa_printf(MSG_ERROR, "file path null");
- return -1;
- }
-
- /* Allow processes running with Group ID as AID_WIFI,
- * to read files from SP, SP/<fqdn>, Cert and osu-info directories */
- if (lchown(path, -1, AID_WIFI)) {
- wpa_printf(MSG_INFO, "CTRL: Could not lchown directory: %s",
- strerror(errno));
- return -1;
- }
-
- if (chmod(path, mode) < 0) {
- wpa_printf(MSG_INFO, "CTRL: Could not chmod directory: %s",
- strerror(errno));
- return -1;
- }
-#endif /* ANDROID */
-
- return 0;
-}
-
-
-int osu_get_certificate(struct hs20_osu_client *ctx, xml_node_t *getcert)
-{
- xml_node_t *node;
- char *url, *user = NULL, *pw = NULL;
- char *proto;
- int ret = -1;
-
- proto = xml_node_get_attr_value(ctx->xml, getcert,
- "enrollmentProtocol");
- if (!proto)
- return -1;
- wpa_printf(MSG_INFO, "getCertificate - enrollmentProtocol=%s", proto);
- write_summary(ctx, "getCertificate - enrollmentProtocol=%s", proto);
- if (os_strcasecmp(proto, "EST") != 0) {
- wpa_printf(MSG_INFO, "Unsupported enrollmentProtocol");
- xml_node_get_attr_value_free(ctx->xml, proto);
- return -1;
- }
- xml_node_get_attr_value_free(ctx->xml, proto);
-
- node = get_node(ctx->xml, getcert, "enrollmentServerURI");
- if (node == NULL) {
- wpa_printf(MSG_INFO, "Could not find enrollmentServerURI node");
- xml_node_get_attr_value_free(ctx->xml, proto);
- return -1;
- }
- url = xml_node_get_text(ctx->xml, node);
- if (url == NULL) {
- wpa_printf(MSG_INFO, "Could not get URL text");
- return -1;
- }
- wpa_printf(MSG_INFO, "enrollmentServerURI: %s", url);
- write_summary(ctx, "enrollmentServerURI: %s", url);
-
- node = get_node(ctx->xml, getcert, "estUserID");
- if (node == NULL && !ctx->client_cert_present) {
- wpa_printf(MSG_INFO, "Could not find estUserID node");
- goto fail;
- }
- if (node) {
- user = xml_node_get_text(ctx->xml, node);
- if (user == NULL) {
- wpa_printf(MSG_INFO, "Could not get estUserID text");
- goto fail;
- }
- wpa_printf(MSG_INFO, "estUserID: %s", user);
- write_summary(ctx, "estUserID: %s", user);
- }
-
- node = get_node(ctx->xml, getcert, "estPassword");
- if (node == NULL && !ctx->client_cert_present) {
- wpa_printf(MSG_INFO, "Could not find estPassword node");
- goto fail;
- }
- if (node) {
- pw = xml_node_get_base64_text(ctx->xml, node, NULL);
- if (pw == NULL) {
- wpa_printf(MSG_INFO, "Could not get estPassword text");
- goto fail;
- }
- wpa_printf(MSG_INFO, "estPassword: %s", pw);
- }
-
- mkdir("Cert", S_IRWXU);
- android_update_permission("Cert", S_IRWXU | S_IRWXG);
-
- if (est_load_cacerts(ctx, url) < 0 ||
- est_build_csr(ctx, url) < 0 ||
- est_simple_enroll(ctx, url, user, pw) < 0)
- goto fail;
-
- ret = 0;
-fail:
- xml_node_get_text_free(ctx->xml, url);
- xml_node_get_text_free(ctx->xml, user);
- xml_node_get_text_free(ctx->xml, pw);
-
- return ret;
-}
-
-
-static int process_est_cert(struct hs20_osu_client *ctx, xml_node_t *cert,
- const char *fqdn)
-{
- u8 digest1[SHA256_MAC_LEN], digest2[SHA256_MAC_LEN];
- char *der, *pem;
- size_t der_len, pem_len;
- char *fingerprint;
- char buf[200];
-
- wpa_printf(MSG_INFO, "PPS for certificate credential - fqdn=%s", fqdn);
-
- fingerprint = xml_node_get_text(ctx->xml, cert);
- if (fingerprint == NULL)
- return -1;
- if (hexstr2bin(fingerprint, digest1, SHA256_MAC_LEN) < 0) {
- wpa_printf(MSG_INFO, "Invalid SHA256 hash value");
- write_result(ctx, "Invalid client certificate SHA256 hash value in PPS");
- xml_node_get_text_free(ctx->xml, fingerprint);
- return -1;
- }
- xml_node_get_text_free(ctx->xml, fingerprint);
-
- der = os_readfile("Cert/est_cert.der", &der_len);
- if (der == NULL) {
- wpa_printf(MSG_INFO, "Could not find client certificate from EST");
- write_result(ctx, "Could not find client certificate from EST");
- return -1;
- }
-
- if (sha256_vector(1, (const u8 **) &der, &der_len, digest2) < 0) {
- os_free(der);
- return -1;
- }
- os_free(der);
-
- if (os_memcmp(digest1, digest2, sizeof(digest1)) != 0) {
- wpa_printf(MSG_INFO, "Client certificate from EST does not match fingerprint from PPS MO");
- write_result(ctx, "Client certificate from EST does not match fingerprint from PPS MO");
- return -1;
- }
-
- wpa_printf(MSG_INFO, "Client certificate from EST matches PPS MO");
- unlink("Cert/est_cert.der");
-
- os_snprintf(buf, sizeof(buf), "SP/%s/client-ca.pem", fqdn);
- if (rename("Cert/est-cacerts.pem", buf) < 0) {
- wpa_printf(MSG_INFO, "Could not move est-cacerts.pem to client-ca.pem: %s",
- strerror(errno));
- return -1;
- }
- pem = os_readfile(buf, &pem_len);
-
- os_snprintf(buf, sizeof(buf), "SP/%s/client-cert.pem", fqdn);
- if (rename("Cert/est_cert.pem", buf) < 0) {
- wpa_printf(MSG_INFO, "Could not move est_cert.pem to client-cert.pem: %s",
- strerror(errno));
- os_free(pem);
- return -1;
- }
-
- if (pem) {
- FILE *f = fopen(buf, "a");
- if (f) {
- fwrite(pem, pem_len, 1, f);
- fclose(f);
- }
- os_free(pem);
- }
-
- os_snprintf(buf, sizeof(buf), "SP/%s/client-key.pem", fqdn);
- if (rename("Cert/privkey-plain.pem", buf) < 0) {
- wpa_printf(MSG_INFO, "Could not move privkey-plain.pem to client-key.pem: %s",
- strerror(errno));
- return -1;
- }
-
- unlink("Cert/est-req.b64");
- unlink("Cert/est-req.pem");
- rmdir("Cert");
-
- return 0;
-}
-
-
#define TMP_CERT_DL_FILE "tmp-cert-download"
static int download_cert(struct hs20_osu_client *ctx, xml_node_t *params,
xml_node_get_text_free(ctx->xml, hash);
write_summary(ctx, "Download certificate from %s", url);
- ctx->no_osu_cert_validation = 1;
http_ocsp_set(ctx->http, 1);
res = http_download_file(ctx->http, url, TMP_CERT_DL_FILE, NULL);
http_ocsp_set(ctx->http,
(ctx->workarounds & WORKAROUND_OCSP_OPTIONAL) ? 1 : 2);
- ctx->no_osu_cert_validation = 0;
xml_node_get_text_free(ctx->xml, url);
if (res < 0)
return -1;
}
-static int cmd_dl_osu_ca(struct hs20_osu_client *ctx, const char *pps_fname,
- const char *ca_fname)
-{
- xml_node_t *pps, *node;
- int ret;
-
- pps = node_from_file(ctx->xml, pps_fname);
- if (pps == NULL) {
- wpa_printf(MSG_INFO, "Could not read or parse '%s'", pps_fname);
- return -1;
- }
-
- node = get_child_node(ctx->xml, pps,
- "SubscriptionUpdate/TrustRoot");
- if (node == NULL) {
- wpa_printf(MSG_INFO, "No SubscriptionUpdate/TrustRoot/CertURL found from PPS");
- xml_node_free(ctx->xml, pps);
- return -1;
- }
-
- ret = download_cert(ctx, node, ca_fname);
- xml_node_free(ctx->xml, pps);
-
- return ret;
-}
-
-
-static int cmd_dl_polupd_ca(struct hs20_osu_client *ctx, const char *pps_fname,
- const char *ca_fname)
-{
- xml_node_t *pps, *node;
- int ret;
-
- pps = node_from_file(ctx->xml, pps_fname);
- if (pps == NULL) {
- wpa_printf(MSG_INFO, "Could not read or parse '%s'", pps_fname);
- return -1;
- }
-
- node = get_child_node(ctx->xml, pps,
- "Policy/PolicyUpdate/TrustRoot");
- if (node == NULL) {
- wpa_printf(MSG_INFO, "No Policy/PolicyUpdate/TrustRoot/CertURL found from PPS");
- xml_node_free(ctx->xml, pps);
- return -2;
- }
-
- ret = download_cert(ctx, node, ca_fname);
- xml_node_free(ctx->xml, pps);
-
- return ret;
-}
-
-
static int cmd_dl_aaa_ca(struct hs20_osu_client *ctx, const char *pps_fname,
const char *ca_fname)
{
}
-static int download_trust_roots(struct hs20_osu_client *ctx,
- const char *pps_fname)
+/* Remove old credentials based on HomeSP/FQDN */
+static void remove_sp_creds(struct hs20_osu_client *ctx, const char *fqdn)
{
- char *dir, *pos;
- char fname[300];
- int ret, ret1;
+ char cmd[300];
+ os_snprintf(cmd, sizeof(cmd), "REMOVE_CRED provisioning_sp=%s", fqdn);
+ if (wpa_command(ctx->ifname, cmd) < 0)
+ wpa_printf(MSG_INFO, "Failed to remove old credential(s)");
+}
- dir = os_strdup(pps_fname);
- if (dir == NULL)
- return -1;
- pos = os_strrchr(dir, '/');
- if (pos == NULL) {
- os_free(dir);
- return -1;
- }
- *pos = '\0';
- snprintf(fname, sizeof(fname), "%s/ca.pem", dir);
- ret = cmd_dl_osu_ca(ctx, pps_fname, fname);
- snprintf(fname, sizeof(fname), "%s/polupd-ca.pem", dir);
- ret1 = cmd_dl_polupd_ca(ctx, pps_fname, fname);
- if (ret == 0 && ret1 == -1)
- ret = -1;
- snprintf(fname, sizeof(fname), "%s/aaa-ca.pem", dir);
- ret1 = cmd_dl_aaa_ca(ctx, pps_fname, fname);
- if (ret == 0 && ret1 == -1)
- ret = -1;
+static void set_pps_cred_policy_spe(struct hs20_osu_client *ctx, int id,
+ xml_node_t *spe)
+{
+ xml_node_t *ssid;
+ char *txt;
+
+ ssid = get_node(ctx->xml, spe, "SSID");
+ if (ssid == NULL)
+ return;
+ txt = xml_node_get_text(ctx->xml, ssid);
+ if (txt == NULL)
+ return;
+ wpa_printf(MSG_DEBUG, "- Policy/SPExclusionList/<X+>/SSID = %s", txt);
+ if (set_cred_quoted(ctx->ifname, id, "excluded_ssid", txt) < 0)
+ wpa_printf(MSG_INFO, "Failed to set cred excluded_ssid");
+ xml_node_get_text_free(ctx->xml, txt);
+}
- os_free(dir);
- return ret;
+static void set_pps_cred_policy_spel(struct hs20_osu_client *ctx, int id,
+ xml_node_t *spel)
+{
+ xml_node_t *child;
+
+ xml_node_for_each_child(ctx->xml, child, spel) {
+ xml_node_for_each_check(ctx->xml, child);
+ set_pps_cred_policy_spe(ctx, id, child);
+ }
}
-static int server_dnsname_suffix_match(struct hs20_osu_client *ctx,
- const char *fqdn)
+static void set_pps_cred_policy_prp(struct hs20_osu_client *ctx, int id,
+ xml_node_t *prp)
{
- size_t match_len, len, i;
- const char *val;
+ xml_node_t *node;
+ char *txt = NULL, *pos;
+ char *prio, *country_buf = NULL;
+ const char *country;
+ char val[200];
+ int priority;
- match_len = os_strlen(fqdn);
+ node = get_node(ctx->xml, prp, "Priority");
+ if (node == NULL)
+ return;
+ prio = xml_node_get_text(ctx->xml, node);
+ if (prio == NULL)
+ return;
+ wpa_printf(MSG_INFO, "- Policy/PreferredRoamingPartnerList/<X+>/Priority = %s",
+ prio);
+ priority = atoi(prio);
+ xml_node_get_text_free(ctx->xml, prio);
- for (i = 0; i < ctx->server_dnsname_count; i++) {
- wpa_printf(MSG_INFO,
- "Checking suffix match against server dNSName %s",
- ctx->server_dnsname[i]);
- val = ctx->server_dnsname[i];
- len = os_strlen(val);
+ node = get_node(ctx->xml, prp, "Country");
+ if (node) {
+ country_buf = xml_node_get_text(ctx->xml, node);
+ if (country_buf == NULL)
+ return;
+ country = country_buf;
+ wpa_printf(MSG_INFO, "- Policy/PreferredRoamingPartnerList/<X+>/Country = %s",
+ country);
+ } else {
+ country = "*";
+ }
- if (match_len > len)
- continue;
+ node = get_node(ctx->xml, prp, "FQDN_Match");
+ if (node == NULL)
+ goto out;
+ txt = xml_node_get_text(ctx->xml, node);
+ if (txt == NULL)
+ goto out;
+ wpa_printf(MSG_INFO, "- Policy/PreferredRoamingPartnerList/<X+>/FQDN_Match = %s",
+ txt);
+ pos = strrchr(txt, ',');
+ if (pos == NULL)
+ goto out;
+ *pos++ = '\0';
- if (os_strncasecmp(val + len - match_len, fqdn, match_len) != 0)
- continue; /* no match */
+ snprintf(val, sizeof(val), "%s,%d,%d,%s", txt,
+ strcmp(pos, "includeSubdomains") != 0, priority, country);
+ if (set_cred_quoted(ctx->ifname, id, "roaming_partner", val) < 0)
+ wpa_printf(MSG_INFO, "Failed to set cred roaming_partner");
+out:
+ xml_node_get_text_free(ctx->xml, country_buf);
+ xml_node_get_text_free(ctx->xml, txt);
+}
- if (match_len == len)
- return 1; /* exact match */
- if (val[len - match_len - 1] == '.')
- return 1; /* full label match completes suffix match */
+static void set_pps_cred_policy_prpl(struct hs20_osu_client *ctx, int id,
+ xml_node_t *prpl)
+{
+ xml_node_t *child;
- /* Reject due to incomplete label match */
+ xml_node_for_each_child(ctx->xml, child, prpl) {
+ xml_node_for_each_check(ctx->xml, child);
+ set_pps_cred_policy_prp(ctx, id, child);
}
-
- /* None of the dNSName(s) matched */
- return 0;
}
-int hs20_add_pps_mo(struct hs20_osu_client *ctx, const char *uri,
- xml_node_t *add_mo, char *fname, size_t fname_len)
+static void set_pps_cred_policy_min_backhaul(struct hs20_osu_client *ctx, int id,
+ xml_node_t *min_backhaul)
{
- char *str;
- char *fqdn, *pos;
- xml_node_t *tnds, *mo, *cert;
- const char *name;
- int ret;
+ xml_node_t *node;
+ char *type, *dl = NULL, *ul = NULL;
+ int home;
- if (strncmp(uri, "./Wi-Fi/", 8) != 0) {
- wpa_printf(MSG_INFO, "Unsupported location for addMO to add PPS MO: '%s'",
- uri);
- write_result(ctx, "Unsupported location for addMO to add PPS MO: '%s'",
- uri);
- return -1;
- }
-
- fqdn = strdup(uri + 8);
- if (fqdn == NULL)
- return -1;
- pos = strchr(fqdn, '/');
- if (pos) {
- if (os_strcasecmp(pos, "/PerProviderSubscription") != 0) {
- wpa_printf(MSG_INFO, "Unsupported location for addMO to add PPS MO (extra directory): '%s'",
- uri);
- write_result(ctx, "Unsupported location for addMO to "
- "add PPS MO (extra directory): '%s'", uri);
- free(fqdn);
- return -1;
- }
- *pos = '\0'; /* remove trailing slash and PPS node name */
- }
- wpa_printf(MSG_INFO, "SP FQDN: %s", fqdn);
-
- if (!server_dnsname_suffix_match(ctx, fqdn)) {
- wpa_printf(MSG_INFO,
- "FQDN '%s' for new PPS MO did not have suffix match with server's dNSName values, count: %d",
- fqdn, (int) ctx->server_dnsname_count);
- write_result(ctx, "FQDN '%s' for new PPS MO did not have suffix match with server's dNSName values",
- fqdn);
- free(fqdn);
- return -1;
- }
-
- if (!valid_fqdn(fqdn)) {
- wpa_printf(MSG_INFO, "Invalid FQDN '%s'", fqdn);
- write_result(ctx, "Invalid FQDN '%s'", fqdn);
- free(fqdn);
- return -1;
- }
-
- mkdir("SP", S_IRWXU);
- snprintf(fname, fname_len, "SP/%s", fqdn);
- if (mkdir(fname, S_IRWXU) < 0) {
- if (errno != EEXIST) {
- int err = errno;
- wpa_printf(MSG_INFO, "mkdir(%s) failed: %s",
- fname, strerror(err));
- free(fqdn);
- return -1;
- }
- }
-
- android_update_permission("SP", S_IRWXU | S_IRWXG);
- android_update_permission(fname, S_IRWXU | S_IRWXG);
-
- snprintf(fname, fname_len, "SP/%s/pps.xml", fqdn);
-
- if (os_file_exists(fname)) {
- wpa_printf(MSG_INFO, "PPS file '%s' exists - reject addMO",
- fname);
- write_result(ctx, "PPS file '%s' exists - reject addMO",
- fname);
- free(fqdn);
- return -2;
- }
- wpa_printf(MSG_INFO, "Using PPS file: %s", fname);
-
- str = xml_node_get_text(ctx->xml, add_mo);
- if (str == NULL) {
- wpa_printf(MSG_INFO, "Could not extract MO text");
- free(fqdn);
- return -1;
- }
- wpa_printf(MSG_DEBUG, "[hs20] addMO text: '%s'", str);
-
- tnds = xml_node_from_buf(ctx->xml, str);
- xml_node_get_text_free(ctx->xml, str);
- if (tnds == NULL) {
- wpa_printf(MSG_INFO, "[hs20] Could not parse addMO text");
- free(fqdn);
- return -1;
- }
-
- mo = tnds_to_mo(ctx->xml, tnds);
- if (mo == NULL) {
- wpa_printf(MSG_INFO, "[hs20] Could not parse addMO TNDS text");
- free(fqdn);
- return -1;
- }
-
- debug_dump_node(ctx, "Parsed TNDS", mo);
-
- name = xml_node_get_localname(ctx->xml, mo);
- if (os_strcasecmp(name, "PerProviderSubscription") != 0) {
- wpa_printf(MSG_INFO, "[hs20] Unexpected PPS MO root node name '%s'",
- name);
- free(fqdn);
- return -1;
- }
-
- cert = get_child_node(ctx->xml, mo,
- "Credential/DigitalCertificate/"
- "CertSHA256Fingerprint");
- if (cert && process_est_cert(ctx, cert, fqdn) < 0) {
- xml_node_free(ctx->xml, mo);
- free(fqdn);
- return -1;
- }
- free(fqdn);
-
- if (node_to_file(ctx->xml, fname, mo) < 0) {
- wpa_printf(MSG_INFO, "Could not write MO to file");
- xml_node_free(ctx->xml, mo);
- return -1;
- }
- xml_node_free(ctx->xml, mo);
-
- wpa_printf(MSG_INFO, "A new PPS MO added as '%s'", fname);
- write_summary(ctx, "A new PPS MO added as '%s'", fname);
-
- ret = download_trust_roots(ctx, fname);
- if (ret < 0) {
- wpa_printf(MSG_INFO, "Remove invalid PPS MO file");
- write_summary(ctx, "Remove invalid PPS MO file");
- unlink(fname);
- }
-
- return ret;
-}
-
-
-int update_pps_file(struct hs20_osu_client *ctx, const char *pps_fname,
- xml_node_t *pps)
-{
- char *str;
- FILE *f;
- char backup[300];
-
- if (ctx->client_cert_present) {
- xml_node_t *cert;
- cert = get_child_node(ctx->xml, pps,
- "Credential/DigitalCertificate/"
- "CertSHA256Fingerprint");
- if (cert && os_file_exists("Cert/est_cert.der") &&
- process_est_cert(ctx, cert, ctx->fqdn) < 0) {
- wpa_printf(MSG_INFO, "EST certificate update processing failed on PPS MO update");
- return -1;
- }
- }
-
- wpa_printf(MSG_INFO, "Updating PPS MO %s", pps_fname);
-
- str = xml_node_to_str(ctx->xml, pps);
- if (str == NULL) {
- wpa_printf(MSG_ERROR, "No node found");
- return -1;
- }
- wpa_printf(MSG_MSGDUMP, "[hs20] Updated PPS: '%s'", str);
-
- snprintf(backup, sizeof(backup), "%s.bak", pps_fname);
- rename(pps_fname, backup);
- f = fopen(pps_fname, "w");
- if (f == NULL) {
- wpa_printf(MSG_INFO, "Could not write PPS");
- rename(backup, pps_fname);
- free(str);
- return -1;
- }
- fprintf(f, "%s\n", str);
- fclose(f);
-
- free(str);
-
- return 0;
-}
-
-
-void get_user_pw(struct hs20_osu_client *ctx, xml_node_t *pps,
- const char *alt_loc, char **user, char **pw)
-{
- xml_node_t *node;
-
- node = get_child_node(ctx->xml, pps,
- "Credential/UsernamePassword/Username");
- if (node)
- *user = xml_node_get_text(ctx->xml, node);
-
- node = get_child_node(ctx->xml, pps,
- "Credential/UsernamePassword/Password");
- if (node)
- *pw = xml_node_get_base64_text(ctx->xml, node, NULL);
-
- node = get_child_node(ctx->xml, pps, alt_loc);
- if (node) {
- xml_node_t *a;
- a = get_node(ctx->xml, node, "Username");
- if (a) {
- xml_node_get_text_free(ctx->xml, *user);
- *user = xml_node_get_text(ctx->xml, a);
- wpa_printf(MSG_INFO, "Use OSU username '%s'", *user);
- }
-
- a = get_node(ctx->xml, node, "Password");
- if (a) {
- free(*pw);
- *pw = xml_node_get_base64_text(ctx->xml, a, NULL);
- wpa_printf(MSG_INFO, "Use OSU password");
- }
- }
-}
-
-
-/* Remove old credentials based on HomeSP/FQDN */
-static void remove_sp_creds(struct hs20_osu_client *ctx, const char *fqdn)
-{
- char cmd[300];
- os_snprintf(cmd, sizeof(cmd), "REMOVE_CRED provisioning_sp=%s", fqdn);
- if (wpa_command(ctx->ifname, cmd) < 0)
- wpa_printf(MSG_INFO, "Failed to remove old credential(s)");
-}
-
-
-static void set_pps_cred_policy_spe(struct hs20_osu_client *ctx, int id,
- xml_node_t *spe)
-{
- xml_node_t *ssid;
- char *txt;
-
- ssid = get_node(ctx->xml, spe, "SSID");
- if (ssid == NULL)
- return;
- txt = xml_node_get_text(ctx->xml, ssid);
- if (txt == NULL)
- return;
- wpa_printf(MSG_DEBUG, "- Policy/SPExclusionList/<X+>/SSID = %s", txt);
- if (set_cred_quoted(ctx->ifname, id, "excluded_ssid", txt) < 0)
- wpa_printf(MSG_INFO, "Failed to set cred excluded_ssid");
- xml_node_get_text_free(ctx->xml, txt);
-}
-
-
-static void set_pps_cred_policy_spel(struct hs20_osu_client *ctx, int id,
- xml_node_t *spel)
-{
- xml_node_t *child;
-
- xml_node_for_each_child(ctx->xml, child, spel) {
- xml_node_for_each_check(ctx->xml, child);
- set_pps_cred_policy_spe(ctx, id, child);
- }
-}
-
-
-static void set_pps_cred_policy_prp(struct hs20_osu_client *ctx, int id,
- xml_node_t *prp)
-{
- xml_node_t *node;
- char *txt = NULL, *pos;
- char *prio, *country_buf = NULL;
- const char *country;
- char val[200];
- int priority;
-
- node = get_node(ctx->xml, prp, "Priority");
- if (node == NULL)
- return;
- prio = xml_node_get_text(ctx->xml, node);
- if (prio == NULL)
- return;
- wpa_printf(MSG_INFO, "- Policy/PreferredRoamingPartnerList/<X+>/Priority = %s",
- prio);
- priority = atoi(prio);
- xml_node_get_text_free(ctx->xml, prio);
-
- node = get_node(ctx->xml, prp, "Country");
- if (node) {
- country_buf = xml_node_get_text(ctx->xml, node);
- if (country_buf == NULL)
- return;
- country = country_buf;
- wpa_printf(MSG_INFO, "- Policy/PreferredRoamingPartnerList/<X+>/Country = %s",
- country);
- } else {
- country = "*";
- }
-
- node = get_node(ctx->xml, prp, "FQDN_Match");
- if (node == NULL)
- goto out;
- txt = xml_node_get_text(ctx->xml, node);
- if (txt == NULL)
- goto out;
- wpa_printf(MSG_INFO, "- Policy/PreferredRoamingPartnerList/<X+>/FQDN_Match = %s",
- txt);
- pos = strrchr(txt, ',');
- if (pos == NULL)
- goto out;
- *pos++ = '\0';
-
- snprintf(val, sizeof(val), "%s,%d,%d,%s", txt,
- strcmp(pos, "includeSubdomains") != 0, priority, country);
- if (set_cred_quoted(ctx->ifname, id, "roaming_partner", val) < 0)
- wpa_printf(MSG_INFO, "Failed to set cred roaming_partner");
-out:
- xml_node_get_text_free(ctx->xml, country_buf);
- xml_node_get_text_free(ctx->xml, txt);
-}
-
-
-static void set_pps_cred_policy_prpl(struct hs20_osu_client *ctx, int id,
- xml_node_t *prpl)
-{
- xml_node_t *child;
-
- xml_node_for_each_child(ctx->xml, child, prpl) {
- xml_node_for_each_check(ctx->xml, child);
- set_pps_cred_policy_prp(ctx, id, child);
- }
-}
-
-
-static void set_pps_cred_policy_min_backhaul(struct hs20_osu_client *ctx, int id,
- xml_node_t *min_backhaul)
-{
- xml_node_t *node;
- char *type, *dl = NULL, *ul = NULL;
- int home;
-
- node = get_node(ctx->xml, min_backhaul, "NetworkType");
- if (node == NULL) {
- wpa_printf(MSG_INFO, "Ignore MinBackhaulThreshold without mandatory NetworkType node");
- return;
+ node = get_node(ctx->xml, min_backhaul, "NetworkType");
+ if (node == NULL) {
+ wpa_printf(MSG_INFO, "Ignore MinBackhaulThreshold without mandatory NetworkType node");
+ return;
}
type = xml_node_get_text(ctx->xml, node);
set_pps_cred_sub_update(ctx, id, child);
else if (os_strcasecmp(name, "HomeSP") == 0)
set_pps_cred_home_sp(ctx, id, child);
- else if (os_strcasecmp(name, "SubscriptionParameters") == 0)
- set_pps_cred_sub_params(ctx, id, child);
- else if (os_strcasecmp(name, "Credential") == 0)
- set_pps_cred_credential(ctx, id, child, fqdn);
- else
- wpa_printf(MSG_INFO, "Unknown credential node '%s'",
- name);
- }
-}
-
-
-static void set_pps(struct hs20_osu_client *ctx, xml_node_t *pps,
- const char *fqdn)
-{
- xml_node_t *child;
- const char *name;
- int id;
- char *update_identifier = NULL;
-
- /*
- * TODO: Could consider more complex mechanism that would remove
- * credentials only if there are changes in the information sent to
- * wpa_supplicant.
- */
- remove_sp_creds(ctx, fqdn);
-
- xml_node_for_each_child(ctx->xml, child, pps) {
- xml_node_for_each_check(ctx->xml, child);
- name = xml_node_get_localname(ctx->xml, child);
- if (os_strcasecmp(name, "UpdateIdentifier") == 0) {
- update_identifier = xml_node_get_text(ctx->xml, child);
- if (update_identifier) {
- wpa_printf(MSG_INFO, "- UpdateIdentifier = %s",
- update_identifier);
- break;
- }
- }
- }
-
- xml_node_for_each_child(ctx->xml, child, pps) {
- xml_node_for_each_check(ctx->xml, child);
- name = xml_node_get_localname(ctx->xml, child);
- if (os_strcasecmp(name, "UpdateIdentifier") == 0)
- continue;
- id = add_cred(ctx->ifname);
- if (id < 0) {
- wpa_printf(MSG_INFO, "Failed to add credential to wpa_supplicant");
- write_summary(ctx, "Failed to add credential to wpa_supplicant");
- break;
- }
- write_summary(ctx, "Add a credential to wpa_supplicant");
- if (update_identifier &&
- set_cred(ctx->ifname, id, "update_identifier",
- update_identifier) < 0)
- wpa_printf(MSG_INFO, "Failed to set update_identifier");
- if (set_cred_quoted(ctx->ifname, id, "provisioning_sp", fqdn) <
- 0)
- wpa_printf(MSG_INFO, "Failed to set provisioning_sp");
- wpa_printf(MSG_INFO, "credential localname: '%s'", name);
- set_pps_credential(ctx, id, child, fqdn);
- ctx->pps_cred_set = 1;
- }
-
- xml_node_get_text_free(ctx->xml, update_identifier);
-}
-
-
-void cmd_set_pps(struct hs20_osu_client *ctx, const char *pps_fname)
-{
- xml_node_t *pps;
- const char *fqdn;
- char *fqdn_buf = NULL, *pos;
-
- pps = node_from_file(ctx->xml, pps_fname);
- if (pps == NULL) {
- wpa_printf(MSG_INFO, "Could not read or parse '%s'", pps_fname);
- return;
- }
-
- fqdn = os_strstr(pps_fname, "SP/");
- if (fqdn) {
- fqdn_buf = os_strdup(fqdn + 3);
- if (fqdn_buf == NULL)
- return;
- pos = os_strchr(fqdn_buf, '/');
- if (pos)
- *pos = '\0';
- fqdn = fqdn_buf;
- } else
- fqdn = "wi-fi.org";
-
- wpa_printf(MSG_INFO, "Set PPS MO info to wpa_supplicant - SP FQDN %s",
- fqdn);
- set_pps(ctx, pps, fqdn);
-
- os_free(fqdn_buf);
- xml_node_free(ctx->xml, pps);
-}
-
-
-static int cmd_get_fqdn(struct hs20_osu_client *ctx, const char *pps_fname)
-{
- xml_node_t *pps, *node;
- char *fqdn = NULL;
-
- pps = node_from_file(ctx->xml, pps_fname);
- if (pps == NULL) {
- wpa_printf(MSG_INFO, "Could not read or parse '%s'", pps_fname);
- return -1;
- }
-
- node = get_child_node(ctx->xml, pps, "HomeSP/FQDN");
- if (node)
- fqdn = xml_node_get_text(ctx->xml, node);
-
- xml_node_free(ctx->xml, pps);
-
- if (fqdn) {
- FILE *f = fopen("pps-fqdn", "w");
- if (f) {
- fprintf(f, "%s", fqdn);
- fclose(f);
- }
- xml_node_get_text_free(ctx->xml, fqdn);
- return 0;
- }
-
- xml_node_get_text_free(ctx->xml, fqdn);
- return -1;
-}
-
-
-static void cmd_to_tnds(struct hs20_osu_client *ctx, const char *in_fname,
- const char *out_fname, const char *urn, int use_path)
-{
- xml_node_t *mo, *node;
-
- mo = node_from_file(ctx->xml, in_fname);
- if (mo == NULL) {
- wpa_printf(MSG_INFO, "Could not read or parse '%s'", in_fname);
- return;
- }
-
- node = mo_to_tnds(ctx->xml, mo, use_path, urn, NULL);
- if (node) {
- node_to_file(ctx->xml, out_fname, node);
- xml_node_free(ctx->xml, node);
- }
-
- xml_node_free(ctx->xml, mo);
-}
-
-
-static void cmd_from_tnds(struct hs20_osu_client *ctx, const char *in_fname,
- const char *out_fname)
-{
- xml_node_t *tnds, *mo;
-
- tnds = node_from_file(ctx->xml, in_fname);
- if (tnds == NULL) {
- wpa_printf(MSG_INFO, "Could not read or parse '%s'", in_fname);
- return;
- }
-
- mo = tnds_to_mo(ctx->xml, tnds);
- if (mo) {
- node_to_file(ctx->xml, out_fname, mo);
- xml_node_free(ctx->xml, mo);
- }
-
- xml_node_free(ctx->xml, tnds);
-}
-
-
-struct osu_icon {
- int id;
- char lang[4];
- char mime_type[256];
- char filename[256];
-};
-
-struct osu_data {
- char bssid[20];
- char url[256];
- unsigned int methods;
- char osu_ssid[33];
- char osu_ssid2[33];
- char osu_nai[256];
- char osu_nai2[256];
- struct osu_lang_text friendly_name[MAX_OSU_VALS];
- size_t friendly_name_count;
- struct osu_lang_text serv_desc[MAX_OSU_VALS];
- size_t serv_desc_count;
- struct osu_icon icon[MAX_OSU_VALS];
- size_t icon_count;
-};
-
-
-static struct osu_data * parse_osu_providers(const char *fname, size_t *count)
-{
- FILE *f;
- char buf[1000];
- struct osu_data *osu = NULL, *last = NULL;
- size_t osu_count = 0;
- char *pos, *end;
- int res;
-
- f = fopen(fname, "r");
- if (f == NULL) {
- wpa_printf(MSG_ERROR, "Could not open %s", fname);
- return NULL;
- }
-
- while (fgets(buf, sizeof(buf), f)) {
- pos = strchr(buf, '\n');
- if (pos)
- *pos = '\0';
-
- if (strncmp(buf, "OSU-PROVIDER ", 13) == 0) {
- last = realloc(osu, (osu_count + 1) * sizeof(*osu));
- if (last == NULL)
- break;
- osu = last;
- last = &osu[osu_count++];
- memset(last, 0, sizeof(*last));
- res = os_snprintf(last->bssid, sizeof(last->bssid),
- "%s", buf + 13);
- if (os_snprintf_error(sizeof(last->bssid), res))
- break;
- continue;
- }
- if (!last)
- continue;
-
- if (strncmp(buf, "uri=", 4) == 0) {
- res = os_snprintf(last->url, sizeof(last->url),
- "%s", buf + 4);
- if (os_snprintf_error(sizeof(last->url), res))
- break;
- continue;
- }
-
- if (strncmp(buf, "methods=", 8) == 0) {
- last->methods = strtol(buf + 8, NULL, 16);
- continue;
- }
-
- if (strncmp(buf, "osu_ssid=", 9) == 0) {
- res = os_snprintf(last->osu_ssid,
- sizeof(last->osu_ssid),
- "%s", buf + 9);
- if (os_snprintf_error(sizeof(last->osu_ssid), res))
- break;
- continue;
- }
-
- if (strncmp(buf, "osu_ssid2=", 10) == 0) {
- res = os_snprintf(last->osu_ssid2,
- sizeof(last->osu_ssid2),
- "%s", buf + 10);
- if (os_snprintf_error(sizeof(last->osu_ssid2), res))
- break;
- continue;
- }
-
- if (os_strncmp(buf, "osu_nai=", 8) == 0) {
- res = os_snprintf(last->osu_nai, sizeof(last->osu_nai),
- "%s", buf + 8);
- if (os_snprintf_error(sizeof(last->osu_nai), res))
- break;
- continue;
- }
-
- if (os_strncmp(buf, "osu_nai2=", 9) == 0) {
- res = os_snprintf(last->osu_nai2,
- sizeof(last->osu_nai2),
- "%s", buf + 9);
- if (os_snprintf_error(sizeof(last->osu_nai2), res))
- break;
- continue;
- }
-
- if (strncmp(buf, "friendly_name=", 14) == 0) {
- struct osu_lang_text *txt;
- if (last->friendly_name_count == MAX_OSU_VALS)
- continue;
- pos = strchr(buf + 14, ':');
- if (pos == NULL)
- continue;
- *pos++ = '\0';
- txt = &last->friendly_name[last->friendly_name_count++];
- res = os_snprintf(txt->lang, sizeof(txt->lang),
- "%s", buf + 14);
- if (os_snprintf_error(sizeof(txt->lang), res))
- break;
- res = os_snprintf(txt->text, sizeof(txt->text),
- "%s", pos);
- if (os_snprintf_error(sizeof(txt->text), res))
- break;
- }
-
- if (strncmp(buf, "desc=", 5) == 0) {
- struct osu_lang_text *txt;
- if (last->serv_desc_count == MAX_OSU_VALS)
- continue;
- pos = strchr(buf + 5, ':');
- if (pos == NULL)
- continue;
- *pos++ = '\0';
- txt = &last->serv_desc[last->serv_desc_count++];
- res = os_snprintf(txt->lang, sizeof(txt->lang),
- "%s", buf + 5);
- if (os_snprintf_error(sizeof(txt->lang), res))
- break;
- res = os_snprintf(txt->text, sizeof(txt->text),
- "%s", pos);
- if (os_snprintf_error(sizeof(txt->text), res))
- break;
- }
-
- if (strncmp(buf, "icon=", 5) == 0) {
- struct osu_icon *icon;
- if (last->icon_count == MAX_OSU_VALS)
- continue;
- icon = &last->icon[last->icon_count++];
- icon->id = atoi(buf + 5);
- pos = strchr(buf, ':');
- if (pos == NULL)
- continue;
- pos = strchr(pos + 1, ':');
- if (pos == NULL)
- continue;
- pos = strchr(pos + 1, ':');
- if (pos == NULL)
- continue;
- pos++;
- end = strchr(pos, ':');
- if (!end)
- continue;
- *end = '\0';
- res = os_snprintf(icon->lang, sizeof(icon->lang),
- "%s", pos);
- if (os_snprintf_error(sizeof(icon->lang), res))
- break;
- pos = end + 1;
-
- end = strchr(pos, ':');
- if (end)
- *end = '\0';
- res = os_snprintf(icon->mime_type,
- sizeof(icon->mime_type), "%s", pos);
- if (os_snprintf_error(sizeof(icon->mime_type), res))
- break;
- if (!end)
- continue;
- pos = end + 1;
-
- end = strchr(pos, ':');
- if (end)
- *end = '\0';
- res = os_snprintf(icon->filename,
- sizeof(icon->filename), "%s", pos);
- if (os_snprintf_error(sizeof(icon->filename), res))
- break;
- continue;
- }
- }
-
- fclose(f);
-
- *count = osu_count;
- return osu;
-}
-
-
-static int osu_connect(struct hs20_osu_client *ctx, const char *bssid,
- const char *ssid, const char *ssid2, const char *url,
- unsigned int methods, int no_prod_assoc,
- const char *osu_nai, const char *osu_nai2)
-{
- int id;
- const char *ifname = ctx->ifname;
- char buf[200];
- struct wpa_ctrl *mon;
- int res;
-
- if (ssid2 && ssid2[0] == '\0')
- ssid2 = NULL;
-
- if (ctx->osu_ssid) {
- if (os_strcmp(ssid, ctx->osu_ssid) == 0) {
- wpa_printf(MSG_DEBUG,
- "Enforced OSU SSID matches ANQP info");
- ssid2 = NULL;
- } else if (ssid2 && os_strcmp(ssid2, ctx->osu_ssid) == 0) {
- wpa_printf(MSG_DEBUG,
- "Enforced OSU SSID matches RSN[OSEN] info");
- ssid = ssid2;
- } else {
- wpa_printf(MSG_INFO, "Enforced OSU SSID did not match");
- write_summary(ctx, "Enforced OSU SSID did not match");
- return -1;
- }
- }
-
- id = add_network(ifname);
- if (id < 0)
- return -1;
- if (set_network_quoted(ifname, id, "ssid", ssid) < 0)
- return -1;
- if (ssid2)
- osu_nai = osu_nai2;
- if (osu_nai && os_strlen(osu_nai) > 0) {
- char dir[255], fname[300];
- if (getcwd(dir, sizeof(dir)) == NULL)
- return -1;
- os_snprintf(fname, sizeof(fname), "%s/osu-ca.pem", dir);
-
- if (ssid2 && set_network_quoted(ifname, id, "ssid", ssid2) < 0)
- return -1;
-
- if (set_network(ifname, id, "proto", "OSEN") < 0 ||
- set_network(ifname, id, "key_mgmt", "OSEN") < 0 ||
- set_network(ifname, id, "pairwise", "CCMP") < 0 ||
- set_network(ifname, id, "group", "GTK_NOT_USED CCMP") < 0 ||
- set_network(ifname, id, "eap", "WFA-UNAUTH-TLS") < 0 ||
- set_network(ifname, id, "ocsp", "2") < 0 ||
- set_network_quoted(ifname, id, "identity", osu_nai) < 0 ||
- set_network_quoted(ifname, id, "ca_cert", fname) < 0)
- return -1;
- } else if (ssid2) {
- wpa_printf(MSG_INFO, "No OSU_NAI set for RSN[OSEN]");
- write_summary(ctx, "No OSU_NAI set for RSN[OSEN]");
- return -1;
- } else {
- if (set_network(ifname, id, "key_mgmt", "NONE") < 0)
- return -1;
- }
-
- mon = open_wpa_mon(ifname);
- if (mon == NULL)
- return -1;
-
- wpa_printf(MSG_INFO, "Associate with OSU SSID");
- write_summary(ctx, "Associate with OSU SSID");
- snprintf(buf, sizeof(buf), "SELECT_NETWORK %d", id);
- if (wpa_command(ifname, buf) < 0)
- return -1;
-
- res = get_wpa_cli_event(mon, "CTRL-EVENT-CONNECTED",
- buf, sizeof(buf));
-
- wpa_ctrl_detach(mon);
- wpa_ctrl_close(mon);
-
- if (res < 0) {
- wpa_printf(MSG_INFO, "Could not connect to OSU network");
- write_summary(ctx, "Could not connect to OSU network");
- wpa_printf(MSG_INFO, "Remove OSU network connection");
- snprintf(buf, sizeof(buf), "REMOVE_NETWORK %d", id);
- wpa_command(ifname, buf);
- return -1;
- }
-
- write_summary(ctx, "Waiting for IP address for subscription registration");
- if (wait_ip_addr(ifname, 15) < 0) {
- wpa_printf(MSG_INFO, "Could not get IP address for WLAN - try connection anyway");
- }
-
- if (no_prod_assoc) {
- if (res < 0)
- return -1;
- wpa_printf(MSG_INFO, "No production connection used for testing purposes");
- write_summary(ctx, "No production connection used for testing purposes");
- return 0;
- }
-
- ctx->no_reconnect = 1;
- if (methods & 0x02) {
- wpa_printf(MSG_DEBUG, "Calling cmd_prov from osu_connect");
- res = cmd_prov(ctx, url);
- } else if (methods & 0x01) {
- wpa_printf(MSG_DEBUG,
- "Calling cmd_oma_dm_prov from osu_connect");
- res = cmd_oma_dm_prov(ctx, url);
- }
-
- wpa_printf(MSG_INFO, "Remove OSU network connection");
- write_summary(ctx, "Remove OSU network connection");
- snprintf(buf, sizeof(buf), "REMOVE_NETWORK %d", id);
- wpa_command(ifname, buf);
-
- if (res < 0)
- return -1;
-
- wpa_printf(MSG_INFO, "Requesting reconnection with updated configuration");
- write_summary(ctx, "Requesting reconnection with updated configuration");
- if (wpa_command(ctx->ifname, "INTERWORKING_SELECT auto") < 0) {
- wpa_printf(MSG_INFO, "Failed to request wpa_supplicant to reconnect");
- write_summary(ctx, "Failed to request wpa_supplicant to reconnect");
- return -1;
- }
-
- return 0;
-}
-
-
-static int cmd_osu_select(struct hs20_osu_client *ctx, const char *dir,
- int connect, int no_prod_assoc,
- const char *friendly_name)
-{
- char fname[255];
- FILE *f;
- struct osu_data *osu = NULL, *last = NULL;
- size_t osu_count = 0, i, j;
- int ret;
-
- write_summary(ctx, "OSU provider selection");
-
- if (dir == NULL) {
- wpa_printf(MSG_INFO, "Missing dir parameter to osu_select");
- return -1;
- }
-
- snprintf(fname, sizeof(fname), "%s/osu-providers.txt", dir);
- osu = parse_osu_providers(fname, &osu_count);
- if (osu == NULL) {
- wpa_printf(MSG_INFO, "Could not find any OSU providers from %s",
- fname);
- write_result(ctx, "No OSU providers available");
- return -1;
- }
-
- if (friendly_name) {
- for (i = 0; i < osu_count; i++) {
- last = &osu[i];
- for (j = 0; j < last->friendly_name_count; j++) {
- if (os_strcmp(last->friendly_name[j].text,
- friendly_name) == 0)
- break;
- }
- if (j < last->friendly_name_count)
- break;
- }
- if (i == osu_count) {
- wpa_printf(MSG_INFO, "Requested operator friendly name '%s' not found in the list of available providers",
- friendly_name);
- write_summary(ctx, "Requested operator friendly name '%s' not found in the list of available providers",
- friendly_name);
- free(osu);
- return -1;
- }
-
- wpa_printf(MSG_INFO, "OSU Provider selected based on requested operator friendly name '%s'",
- friendly_name);
- write_summary(ctx, "OSU Provider selected based on requested operator friendly name '%s'",
- friendly_name);
- ret = i + 1;
- goto selected;
- }
-
- snprintf(fname, sizeof(fname), "%s/osu-providers.html", dir);
- f = fopen(fname, "w");
- if (f == NULL) {
- wpa_printf(MSG_INFO, "Could not open %s", fname);
- free(osu);
- return -1;
- }
-
- fprintf(f, "<html><head>"
- "<meta http-equiv=\"Content-type\" content=\"text/html; "
- "charset=utf-8\"<title>Select service operator</title>"
- "</head><body><h1>Select service operator</h1>\n");
-
- if (osu_count == 0)
- fprintf(f, "No online signup available\n");
-
- for (i = 0; i < osu_count; i++) {
- last = &osu[i];
-#ifdef ANDROID
- fprintf(f, "<p>\n"
- "<a href=\"http://localhost:12345/osu/%d\">"
- "<table><tr><td>", (int) i + 1);
-#else /* ANDROID */
- fprintf(f, "<p>\n"
- "<a href=\"osu://%d\">"
- "<table><tr><td>", (int) i + 1);
-#endif /* ANDROID */
- for (j = 0; j < last->icon_count; j++) {
- fprintf(f, "<img src=\"osu-icon-%d.%s\">\n",
- last->icon[j].id,
- strcasecmp(last->icon[j].mime_type,
- "image/png") == 0 ? "png" : "icon");
- }
- fprintf(f, "<td>");
- for (j = 0; j < last->friendly_name_count; j++) {
- fprintf(f, "<small>[%s]</small> %s<br>\n",
- last->friendly_name[j].lang,
- last->friendly_name[j].text);
- }
- fprintf(f, "<tr><td colspan=2>");
- for (j = 0; j < last->serv_desc_count; j++) {
- fprintf(f, "<small>[%s]</small> %s<br>\n",
- last->serv_desc[j].lang,
- last->serv_desc[j].text);
- }
- fprintf(f, "</table></a><br><small>BSSID: %s<br>\n"
- "SSID: %s<br>\n",
- last->bssid, last->osu_ssid);
- if (last->osu_ssid2[0])
- fprintf(f, "SSID2: %s<br>\n", last->osu_ssid2);
- if (last->osu_nai[0])
- fprintf(f, "NAI: %s<br>\n", last->osu_nai);
- if (last->osu_nai2[0])
- fprintf(f, "NAI2: %s<br>\n", last->osu_nai2);
- fprintf(f, "URL: %s<br>\n"
- "methods:%s%s<br>\n"
- "</small></p>\n",
- last->url,
- last->methods & 0x01 ? " OMA-DM" : "",
- last->methods & 0x02 ? " SOAP-XML-SPP" : "");
- }
-
- fprintf(f, "</body></html>\n");
-
- fclose(f);
-
- snprintf(fname, sizeof(fname), "file://%s/osu-providers.html", dir);
- write_summary(ctx, "Start web browser with OSU provider selection page");
- ret = hs20_web_browser(fname, 0);
-
-selected:
- if (ret > 0 && (size_t) ret <= osu_count) {
- char *data;
- size_t data_len;
-
- wpa_printf(MSG_INFO, "Selected OSU id=%d", ret);
- last = &osu[ret - 1];
- ret = 0;
- wpa_printf(MSG_INFO, "BSSID: %s", last->bssid);
- wpa_printf(MSG_INFO, "SSID: %s", last->osu_ssid);
- if (last->osu_ssid2[0])
- wpa_printf(MSG_INFO, "SSID2: %s", last->osu_ssid2);
- wpa_printf(MSG_INFO, "URL: %s", last->url);
- write_summary(ctx, "Selected OSU provider id=%d BSSID=%s SSID=%s URL=%s",
- ret, last->bssid, last->osu_ssid, last->url);
-
- ctx->friendly_name_count = last->friendly_name_count;
- for (j = 0; j < last->friendly_name_count; j++) {
- wpa_printf(MSG_INFO, "FRIENDLY_NAME: [%s]%s",
- last->friendly_name[j].lang,
- last->friendly_name[j].text);
- os_strlcpy(ctx->friendly_name[j].lang,
- last->friendly_name[j].lang,
- sizeof(ctx->friendly_name[j].lang));
- os_strlcpy(ctx->friendly_name[j].text,
- last->friendly_name[j].text,
- sizeof(ctx->friendly_name[j].text));
- }
-
- ctx->icon_count = last->icon_count;
- for (j = 0; j < last->icon_count; j++) {
- char fname[256];
-
- os_snprintf(fname, sizeof(fname), "%s/osu-icon-%d.%s",
- dir, last->icon[j].id,
- strcasecmp(last->icon[j].mime_type,
- "image/png") == 0 ?
- "png" : "icon");
- wpa_printf(MSG_INFO, "ICON: %s (%s)",
- fname, last->icon[j].filename);
- os_strlcpy(ctx->icon_filename[j],
- last->icon[j].filename,
- sizeof(ctx->icon_filename[j]));
-
- data = os_readfile(fname, &data_len);
- if (data) {
- sha256_vector(1, (const u8 **) &data, &data_len,
- ctx->icon_hash[j]);
- os_free(data);
- }
- }
-
- if (connect == 2) {
- if (last->methods & 0x02) {
- wpa_printf(MSG_DEBUG,
- "Calling cmd_prov from cmd_osu_select");
- ret = cmd_prov(ctx, last->url);
- } else if (last->methods & 0x01) {
- wpa_printf(MSG_DEBUG,
- "Calling cmd_oma_dm_prov from cmd_osu_select");
- ret = cmd_oma_dm_prov(ctx, last->url);
- } else {
- wpa_printf(MSG_DEBUG,
- "No supported OSU provisioning method");
- ret = -1;
- }
- } else if (connect) {
- ret = osu_connect(ctx, last->bssid, last->osu_ssid,
- last->osu_ssid2,
- last->url, last->methods,
- no_prod_assoc, last->osu_nai,
- last->osu_nai2);
- }
- } else
- ret = -1;
-
- free(osu);
-
- return ret;
-}
-
-
-static int cmd_signup(struct hs20_osu_client *ctx, int no_prod_assoc,
- const char *friendly_name)
-{
- char dir[255];
- char fname[300], buf[400];
- struct wpa_ctrl *mon;
- const char *ifname;
- int res;
-
- ifname = ctx->ifname;
-
- if (getcwd(dir, sizeof(dir)) == NULL)
- return -1;
-
- snprintf(fname, sizeof(fname), "%s/osu-info", dir);
- if (mkdir(fname, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) < 0 &&
- errno != EEXIST) {
- wpa_printf(MSG_INFO, "mkdir(%s) failed: %s",
- fname, strerror(errno));
- return -1;
- }
-
- android_update_permission(fname, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
-
- snprintf(buf, sizeof(buf), "SET osu_dir %s", fname);
- if (wpa_command(ifname, buf) < 0) {
- wpa_printf(MSG_INFO, "Failed to configure osu_dir to wpa_supplicant");
- return -1;
- }
-
- mon = open_wpa_mon(ifname);
- if (mon == NULL)
- return -1;
-
- wpa_printf(MSG_INFO, "Starting OSU fetch");
- write_summary(ctx, "Starting OSU provider information fetch");
- if (wpa_command(ifname, "FETCH_OSU") < 0) {
- wpa_printf(MSG_INFO, "Could not start OSU fetch");
- wpa_ctrl_detach(mon);
- wpa_ctrl_close(mon);
- return -1;
- }
- res = get_wpa_cli_event(mon, "OSU provider fetch completed",
- buf, sizeof(buf));
-
- wpa_ctrl_detach(mon);
- wpa_ctrl_close(mon);
-
- if (res < 0) {
- wpa_printf(MSG_INFO, "OSU fetch did not complete");
- write_summary(ctx, "OSU fetch did not complete");
- return -1;
- }
- wpa_printf(MSG_INFO, "OSU provider fetch completed");
-
- return cmd_osu_select(ctx, fname, 1, no_prod_assoc, friendly_name);
-}
-
-
-static int cmd_sub_rem(struct hs20_osu_client *ctx, const char *address,
- const char *pps_fname, const char *ca_fname)
-{
- xml_node_t *pps, *node;
- char pps_fname_buf[300];
- char ca_fname_buf[200];
- char *cred_username = NULL;
- char *cred_password = NULL;
- char *sub_rem_uri = NULL;
- char client_cert_buf[200];
- char *client_cert = NULL;
- char client_key_buf[200];
- char *client_key = NULL;
- int spp;
-
- wpa_printf(MSG_INFO, "Subscription remediation requested with Server URL: %s",
- address);
-
- if (!pps_fname) {
- char buf[256];
- wpa_printf(MSG_INFO, "Determining PPS file based on Home SP information");
- if (os_strncmp(address, "fqdn=", 5) == 0) {
- wpa_printf(MSG_INFO, "Use requested FQDN from command line");
- os_snprintf(buf, sizeof(buf), "%s", address + 5);
- address = NULL;
- } else if (get_wpa_status(ctx->ifname, "provisioning_sp", buf,
- sizeof(buf)) < 0) {
- wpa_printf(MSG_INFO, "Could not get provisioning Home SP FQDN from wpa_supplicant");
- return -1;
- }
- os_free(ctx->fqdn);
- ctx->fqdn = os_strdup(buf);
- if (ctx->fqdn == NULL)
- return -1;
- wpa_printf(MSG_INFO, "Home SP FQDN for current credential: %s",
- buf);
- os_snprintf(pps_fname_buf, sizeof(pps_fname_buf),
- "SP/%s/pps.xml", ctx->fqdn);
- pps_fname = pps_fname_buf;
-
- os_snprintf(ca_fname_buf, sizeof(ca_fname_buf), "SP/%s/ca.pem",
- ctx->fqdn);
- ca_fname = ca_fname_buf;
- }
-
- if (!os_file_exists(pps_fname)) {
- wpa_printf(MSG_INFO, "PPS file '%s' does not exist or is not accessible",
- pps_fname);
- return -1;
- }
- wpa_printf(MSG_INFO, "Using PPS file: %s", pps_fname);
-
- if (ca_fname && !os_file_exists(ca_fname)) {
- wpa_printf(MSG_INFO, "CA file '%s' does not exist or is not accessible",
- ca_fname);
- return -1;
- }
- wpa_printf(MSG_INFO, "Using server trust root: %s", ca_fname);
- ctx->ca_fname = ca_fname;
-
- pps = node_from_file(ctx->xml, pps_fname);
- if (pps == NULL) {
- wpa_printf(MSG_INFO, "Could not read PPS MO");
- return -1;
- }
-
- if (!ctx->fqdn) {
- char *tmp;
- node = get_child_node(ctx->xml, pps, "HomeSP/FQDN");
- if (node == NULL) {
- wpa_printf(MSG_INFO, "No HomeSP/FQDN found from PPS");
- return -1;
- }
- tmp = xml_node_get_text(ctx->xml, node);
- if (tmp == NULL) {
- wpa_printf(MSG_INFO, "No HomeSP/FQDN text found from PPS");
- return -1;
- }
- ctx->fqdn = os_strdup(tmp);
- xml_node_get_text_free(ctx->xml, tmp);
- if (!ctx->fqdn) {
- wpa_printf(MSG_INFO, "No FQDN known");
- return -1;
- }
- }
-
- node = get_child_node(ctx->xml, pps,
- "SubscriptionUpdate/UpdateMethod");
- if (node) {
- char *tmp;
- tmp = xml_node_get_text(ctx->xml, node);
- if (tmp && os_strcasecmp(tmp, "OMA-DM-ClientInitiated") == 0)
- spp = 0;
+ else if (os_strcasecmp(name, "SubscriptionParameters") == 0)
+ set_pps_cred_sub_params(ctx, id, child);
+ else if (os_strcasecmp(name, "Credential") == 0)
+ set_pps_cred_credential(ctx, id, child, fqdn);
else
- spp = 1;
- } else {
- wpa_printf(MSG_INFO, "No UpdateMethod specified - assume SPP");
- spp = 1;
- }
-
- get_user_pw(ctx, pps, "SubscriptionUpdate/UsernamePassword",
- &cred_username, &cred_password);
- if (cred_username)
- wpa_printf(MSG_INFO, "Using username: %s", cred_username);
- if (cred_password)
- wpa_printf(MSG_DEBUG, "Using password: %s", cred_password);
-
- if (cred_username == NULL && cred_password == NULL &&
- get_child_node(ctx->xml, pps, "Credential/DigitalCertificate")) {
- wpa_printf(MSG_INFO, "Using client certificate");
- os_snprintf(client_cert_buf, sizeof(client_cert_buf),
- "SP/%s/client-cert.pem", ctx->fqdn);
- client_cert = client_cert_buf;
- os_snprintf(client_key_buf, sizeof(client_key_buf),
- "SP/%s/client-key.pem", ctx->fqdn);
- client_key = client_key_buf;
- ctx->client_cert_present = 1;
- }
-
- node = get_child_node(ctx->xml, pps, "SubscriptionUpdate/URI");
- if (node) {
- sub_rem_uri = xml_node_get_text(ctx->xml, node);
- if (sub_rem_uri &&
- (!address || os_strcmp(address, sub_rem_uri) != 0)) {
- wpa_printf(MSG_INFO, "Override sub rem URI based on PPS: %s",
- sub_rem_uri);
- address = sub_rem_uri;
- }
- }
- if (!address) {
- wpa_printf(MSG_INFO, "Server URL not known");
- return -1;
- }
-
- write_summary(ctx, "Wait for IP address for subscriptiom remediation");
- wpa_printf(MSG_INFO, "Wait for IP address before starting subscription remediation");
-
- if (wait_ip_addr(ctx->ifname, 15) < 0) {
- wpa_printf(MSG_INFO, "Could not get IP address for WLAN - try connection anyway");
+ wpa_printf(MSG_INFO, "Unknown credential node '%s'",
+ name);
}
-
- if (spp)
- spp_sub_rem(ctx, address, pps_fname,
- client_cert, client_key,
- cred_username, cred_password, pps);
- else
- oma_dm_sub_rem(ctx, address, pps_fname,
- client_cert, client_key,
- cred_username, cred_password, pps);
-
- xml_node_get_text_free(ctx->xml, sub_rem_uri);
- xml_node_get_text_free(ctx->xml, cred_username);
- str_clear_free(cred_password);
- xml_node_free(ctx->xml, pps);
- return 0;
}
-static int cmd_pol_upd(struct hs20_osu_client *ctx, const char *address,
- const char *pps_fname, const char *ca_fname)
+static void set_pps(struct hs20_osu_client *ctx, xml_node_t *pps,
+ const char *fqdn)
{
- xml_node_t *pps;
- xml_node_t *node;
- char pps_fname_buf[300];
- char ca_fname_buf[200];
- char *uri = NULL;
- char *cred_username = NULL;
- char *cred_password = NULL;
- char client_cert_buf[200];
- char *client_cert = NULL;
- char client_key_buf[200];
- char *client_key = NULL;
- int spp;
-
- wpa_printf(MSG_INFO, "Policy update requested");
-
- if (!pps_fname) {
- char buf[256];
- int res;
-
- wpa_printf(MSG_INFO, "Determining PPS file based on Home SP information");
- if (address && os_strncmp(address, "fqdn=", 5) == 0) {
- wpa_printf(MSG_INFO, "Use requested FQDN from command line");
- os_snprintf(buf, sizeof(buf), "%s", address + 5);
- address = NULL;
- } else if (get_wpa_status(ctx->ifname, "provisioning_sp", buf,
- sizeof(buf)) < 0) {
- wpa_printf(MSG_INFO, "Could not get provisioning Home SP FQDN from wpa_supplicant");
- return -1;
- }
- os_free(ctx->fqdn);
- ctx->fqdn = os_strdup(buf);
- if (ctx->fqdn == NULL)
- return -1;
- wpa_printf(MSG_INFO, "Home SP FQDN for current credential: %s",
- buf);
- os_snprintf(pps_fname_buf, sizeof(pps_fname_buf),
- "SP/%s/pps.xml", ctx->fqdn);
- pps_fname = pps_fname_buf;
-
- res = os_snprintf(ca_fname_buf, sizeof(ca_fname_buf),
- "SP/%s/ca.pem", buf);
- if (os_snprintf_error(sizeof(ca_fname_buf), res)) {
- os_free(ctx->fqdn);
- ctx->fqdn = NULL;
- return -1;
- }
- ca_fname = ca_fname_buf;
- }
-
- if (!os_file_exists(pps_fname)) {
- wpa_printf(MSG_INFO, "PPS file '%s' does not exist or is not accessible",
- pps_fname);
- return -1;
- }
- wpa_printf(MSG_INFO, "Using PPS file: %s", pps_fname);
-
- if (ca_fname && !os_file_exists(ca_fname)) {
- wpa_printf(MSG_INFO, "CA file '%s' does not exist or is not accessible",
- ca_fname);
- return -1;
- }
- wpa_printf(MSG_INFO, "Using server trust root: %s", ca_fname);
- ctx->ca_fname = ca_fname;
+ xml_node_t *child;
+ const char *name;
+ int id;
+ char *update_identifier = NULL;
- pps = node_from_file(ctx->xml, pps_fname);
- if (pps == NULL) {
- wpa_printf(MSG_INFO, "Could not read PPS MO");
- return -1;
- }
+ /*
+ * TODO: Could consider more complex mechanism that would remove
+ * credentials only if there are changes in the information sent to
+ * wpa_supplicant.
+ */
+ remove_sp_creds(ctx, fqdn);
- if (!ctx->fqdn) {
- char *tmp;
- node = get_child_node(ctx->xml, pps, "HomeSP/FQDN");
- if (node == NULL) {
- wpa_printf(MSG_INFO, "No HomeSP/FQDN found from PPS");
- return -1;
- }
- tmp = xml_node_get_text(ctx->xml, node);
- if (tmp == NULL) {
- wpa_printf(MSG_INFO, "No HomeSP/FQDN text found from PPS");
- return -1;
- }
- ctx->fqdn = os_strdup(tmp);
- xml_node_get_text_free(ctx->xml, tmp);
- if (!ctx->fqdn) {
- wpa_printf(MSG_INFO, "No FQDN known");
- return -1;
+ xml_node_for_each_child(ctx->xml, child, pps) {
+ xml_node_for_each_check(ctx->xml, child);
+ name = xml_node_get_localname(ctx->xml, child);
+ if (os_strcasecmp(name, "UpdateIdentifier") == 0) {
+ update_identifier = xml_node_get_text(ctx->xml, child);
+ if (update_identifier) {
+ wpa_printf(MSG_INFO, "- UpdateIdentifier = %s",
+ update_identifier);
+ break;
+ }
}
}
- node = get_child_node(ctx->xml, pps,
- "Policy/PolicyUpdate/UpdateMethod");
- if (node) {
- char *tmp;
- tmp = xml_node_get_text(ctx->xml, node);
- if (tmp && os_strcasecmp(tmp, "OMA-DM-ClientInitiated") == 0)
- spp = 0;
- else
- spp = 1;
- } else {
- wpa_printf(MSG_INFO, "No UpdateMethod specified - assume SPP");
- spp = 1;
- }
-
- get_user_pw(ctx, pps, "Policy/PolicyUpdate/UsernamePassword",
- &cred_username, &cred_password);
- if (cred_username)
- wpa_printf(MSG_INFO, "Using username: %s", cred_username);
- if (cred_password)
- wpa_printf(MSG_DEBUG, "Using password: %s", cred_password);
-
- if (cred_username == NULL && cred_password == NULL &&
- get_child_node(ctx->xml, pps, "Credential/DigitalCertificate")) {
- wpa_printf(MSG_INFO, "Using client certificate");
- os_snprintf(client_cert_buf, sizeof(client_cert_buf),
- "SP/%s/client-cert.pem", ctx->fqdn);
- client_cert = client_cert_buf;
- os_snprintf(client_key_buf, sizeof(client_key_buf),
- "SP/%s/client-key.pem", ctx->fqdn);
- client_key = client_key_buf;
- }
-
- if (!address) {
- node = get_child_node(ctx->xml, pps, "Policy/PolicyUpdate/URI");
- if (node) {
- uri = xml_node_get_text(ctx->xml, node);
- wpa_printf(MSG_INFO, "URI based on PPS: %s", uri);
- address = uri;
+ xml_node_for_each_child(ctx->xml, child, pps) {
+ xml_node_for_each_check(ctx->xml, child);
+ name = xml_node_get_localname(ctx->xml, child);
+ if (os_strcasecmp(name, "UpdateIdentifier") == 0)
+ continue;
+ id = add_cred(ctx->ifname);
+ if (id < 0) {
+ wpa_printf(MSG_INFO, "Failed to add credential to wpa_supplicant");
+ write_summary(ctx, "Failed to add credential to wpa_supplicant");
+ break;
}
+ write_summary(ctx, "Add a credential to wpa_supplicant");
+ if (update_identifier &&
+ set_cred(ctx->ifname, id, "update_identifier",
+ update_identifier) < 0)
+ wpa_printf(MSG_INFO, "Failed to set update_identifier");
+ if (set_cred_quoted(ctx->ifname, id, "provisioning_sp", fqdn) <
+ 0)
+ wpa_printf(MSG_INFO, "Failed to set provisioning_sp");
+ wpa_printf(MSG_INFO, "credential localname: '%s'", name);
+ set_pps_credential(ctx, id, child, fqdn);
}
- if (!address) {
- wpa_printf(MSG_INFO, "Server URL not known");
- return -1;
- }
-
- if (spp)
- spp_pol_upd(ctx, address, pps_fname,
- client_cert, client_key,
- cred_username, cred_password, pps);
- else
- oma_dm_pol_upd(ctx, address, pps_fname,
- client_cert, client_key,
- cred_username, cred_password, pps);
- xml_node_get_text_free(ctx->xml, uri);
- xml_node_get_text_free(ctx->xml, cred_username);
- str_clear_free(cred_password);
- xml_node_free(ctx->xml, pps);
-
- return 0;
+ xml_node_get_text_free(ctx->xml, update_identifier);
}
-static char * get_hostname(const char *url)
+static void cmd_set_pps(struct hs20_osu_client *ctx, const char *pps_fname)
{
- const char *pos, *end, *end2;
- char *ret;
-
- if (url == NULL)
- return NULL;
+ xml_node_t *pps;
+ const char *fqdn;
+ char *fqdn_buf = NULL, *pos;
- pos = os_strchr(url, '/');
- if (pos == NULL)
- return NULL;
- pos++;
- if (*pos != '/')
- return NULL;
- pos++;
-
- end = os_strchr(pos, '/');
- end2 = os_strchr(pos, ':');
- if ((end && end2 && end2 < end) || (!end && end2))
- end = end2;
- if (end)
- end--;
- else {
- end = pos;
- while (*end)
- end++;
- if (end > pos)
- end--;
+ pps = node_from_file(ctx->xml, pps_fname);
+ if (pps == NULL) {
+ wpa_printf(MSG_INFO, "Could not read or parse '%s'", pps_fname);
+ return;
}
- ret = os_malloc(end - pos + 2);
- if (ret == NULL)
- return NULL;
+ fqdn = os_strstr(pps_fname, "SP/");
+ if (fqdn) {
+ fqdn_buf = os_strdup(fqdn + 3);
+ if (fqdn_buf == NULL)
+ return;
+ pos = os_strchr(fqdn_buf, '/');
+ if (pos)
+ *pos = '\0';
+ fqdn = fqdn_buf;
+ } else
+ fqdn = "wi-fi.org";
- os_memcpy(ret, pos, end - pos + 1);
- ret[end - pos + 1] = '\0';
+ wpa_printf(MSG_INFO, "Set PPS MO info to wpa_supplicant - SP FQDN %s",
+ fqdn);
+ set_pps(ctx, pps, fqdn);
- return ret;
+ os_free(fqdn_buf);
+ xml_node_free(ctx->xml, pps);
}
-static int osu_cert_cb(void *_ctx, struct http_cert *cert)
+static int cmd_get_fqdn(struct hs20_osu_client *ctx, const char *pps_fname)
{
- struct hs20_osu_client *ctx = _ctx;
- size_t i, j;
- int found;
- char *host = NULL;
-
- wpa_printf(MSG_INFO, "osu_cert_cb(osu_cert_validation=%d, url=%s server_url=%s)",
- !ctx->no_osu_cert_validation, cert->url ? cert->url : "N/A",
- ctx->server_url);
-
- if (ctx->no_osu_cert_validation && cert->url)
- host = get_hostname(cert->url);
- else
- host = get_hostname(ctx->server_url);
-
- if (!ctx->no_osu_cert_validation) {
- for (i = 0; i < ctx->server_dnsname_count; i++)
- os_free(ctx->server_dnsname[i]);
- os_free(ctx->server_dnsname);
- ctx->server_dnsname = os_calloc(cert->num_dnsname,
- sizeof(char *));
- ctx->server_dnsname_count = 0;
- }
-
- found = 0;
- for (i = 0; i < cert->num_dnsname; i++) {
- if (!ctx->no_osu_cert_validation && ctx->server_dnsname) {
- ctx->server_dnsname[ctx->server_dnsname_count] =
- os_strdup(cert->dnsname[i]);
- if (ctx->server_dnsname[ctx->server_dnsname_count])
- ctx->server_dnsname_count++;
- }
- if (host && os_strcasecmp(host, cert->dnsname[i]) == 0)
- found = 1;
- wpa_printf(MSG_INFO, "dNSName '%s'", cert->dnsname[i]);
- }
+ xml_node_t *pps, *node;
+ char *fqdn = NULL;
- if (host && !found) {
- wpa_printf(MSG_INFO, "Server name from URL (%s) did not match any dNSName - abort connection",
- host);
- write_result(ctx, "Server name from URL (%s) did not match any dNSName - abort connection",
- host);
- os_free(host);
+ pps = node_from_file(ctx->xml, pps_fname);
+ if (pps == NULL) {
+ wpa_printf(MSG_INFO, "Could not read or parse '%s'", pps_fname);
return -1;
}
- os_free(host);
-
- for (i = 0; i < cert->num_othername; i++) {
- if (os_strcmp(cert->othername[i].oid,
- "1.3.6.1.4.1.40808.1.1.1") == 0) {
- wpa_hexdump_ascii(MSG_INFO,
- "id-wfa-hotspot-friendlyName",
- cert->othername[i].data,
- cert->othername[i].len);
- }
- }
+ node = get_child_node(ctx->xml, pps, "HomeSP/FQDN");
+ if (node)
+ fqdn = xml_node_get_text(ctx->xml, node);
- for (j = 0; !ctx->no_osu_cert_validation &&
- j < ctx->friendly_name_count; j++) {
- int found = 0;
- for (i = 0; i < cert->num_othername; i++) {
- if (os_strcmp(cert->othername[i].oid,
- "1.3.6.1.4.1.40808.1.1.1") != 0)
- continue;
- if (cert->othername[i].len < 3)
- continue;
- if (os_strncasecmp((char *) cert->othername[i].data,
- ctx->friendly_name[j].lang, 3) != 0)
- continue;
- if (os_strncmp((char *) cert->othername[i].data + 3,
- ctx->friendly_name[j].text,
- cert->othername[i].len - 3) == 0) {
- found = 1;
- break;
- }
- }
+ xml_node_free(ctx->xml, pps);
- if (!found) {
- wpa_printf(MSG_INFO, "No friendly name match found for '[%s]%s'",
- ctx->friendly_name[j].lang,
- ctx->friendly_name[j].text);
- write_result(ctx, "No friendly name match found for '[%s]%s'",
- ctx->friendly_name[j].lang,
- ctx->friendly_name[j].text);
- return -1;
+ if (fqdn) {
+ FILE *f = fopen("pps-fqdn", "w");
+ if (f) {
+ fprintf(f, "%s", fqdn);
+ fclose(f);
}
+ xml_node_get_text_free(ctx->xml, fqdn);
+ return 0;
}
- for (i = 0; i < cert->num_logo; i++) {
- struct http_logo *logo = &cert->logo[i];
-
- wpa_printf(MSG_INFO, "logo hash alg %s uri '%s'",
- logo->alg_oid, logo->uri);
- wpa_hexdump_ascii(MSG_INFO, "hashValue",
- logo->hash, logo->hash_len);
- }
+ xml_node_get_text_free(ctx->xml, fqdn);
+ return -1;
+}
- for (j = 0; !ctx->no_osu_cert_validation && j < ctx->icon_count; j++) {
- int found = 0;
- char *name = ctx->icon_filename[j];
- size_t name_len = os_strlen(name);
- wpa_printf(MSG_INFO,
- "[%zu] Looking for icon file name '%s' match",
- j, name);
- for (i = 0; i < cert->num_logo; i++) {
- struct http_logo *logo = &cert->logo[i];
- size_t uri_len = os_strlen(logo->uri);
- char *pos;
+static void cmd_to_tnds(struct hs20_osu_client *ctx, const char *in_fname,
+ const char *out_fname, const char *urn, int use_path)
+{
+ xml_node_t *mo, *node;
- wpa_printf(MSG_INFO,
- "[%zu] Comparing to '%s' uri_len=%d name_len=%d",
- i, logo->uri, (int) uri_len, (int) name_len);
- if (uri_len < 1 + name_len) {
- wpa_printf(MSG_INFO, "URI Length is too short");
- continue;
- }
- pos = &logo->uri[uri_len - name_len - 1];
- if (*pos != '/')
- continue;
- pos++;
- if (os_strcmp(pos, name) == 0) {
- found = 1;
- break;
- }
- }
+ mo = node_from_file(ctx->xml, in_fname);
+ if (mo == NULL) {
+ wpa_printf(MSG_INFO, "Could not read or parse '%s'", in_fname);
+ return;
+ }
- if (!found) {
- wpa_printf(MSG_INFO, "No icon filename match found for '%s'",
- name);
- write_result(ctx,
- "No icon filename match found for '%s'",
- name);
- return -1;
- }
+ node = mo_to_tnds(ctx->xml, mo, use_path, urn, NULL);
+ if (node) {
+ node_to_file(ctx->xml, out_fname, node);
+ xml_node_free(ctx->xml, node);
}
- for (j = 0; !ctx->no_osu_cert_validation && j < ctx->icon_count; j++) {
- int found = 0;
+ xml_node_free(ctx->xml, mo);
+}
- for (i = 0; i < cert->num_logo; i++) {
- struct http_logo *logo = &cert->logo[i];
- if (logo->hash_len != 32) {
- wpa_printf(MSG_INFO,
- "[%zu][%zu] Icon hash length invalid (should be 32): %d",
- j, i, (int) logo->hash_len);
- continue;
- }
- if (os_memcmp(logo->hash, ctx->icon_hash[j], 32) == 0) {
- found = 1;
- break;
- }
+static void cmd_from_tnds(struct hs20_osu_client *ctx, const char *in_fname,
+ const char *out_fname)
+{
+ xml_node_t *tnds, *mo;
- wpa_printf(MSG_DEBUG,
- "[%zu][%zu] Icon hash did not match", j, i);
- wpa_hexdump_ascii(MSG_DEBUG, "logo->hash",
- logo->hash, 32);
- wpa_hexdump_ascii(MSG_DEBUG, "ctx->icon_hash[j]",
- ctx->icon_hash[j], 32);
- }
+ tnds = node_from_file(ctx->xml, in_fname);
+ if (tnds == NULL) {
+ wpa_printf(MSG_INFO, "Could not read or parse '%s'", in_fname);
+ return;
+ }
- if (!found) {
- wpa_printf(MSG_INFO,
- "No icon hash match (by hash) found");
- write_result(ctx,
- "No icon hash match (by hash) found");
- return -1;
- }
+ mo = tnds_to_mo(ctx->xml, tnds);
+ if (mo) {
+ node_to_file(ctx->xml, out_fname, mo);
+ xml_node_free(ctx->xml, mo);
}
- return 0;
+ xml_node_free(ctx->xml, tnds);
}
static int init_ctx(struct hs20_osu_client *ctx)
{
- xml_node_t *devinfo, *devid;
-
os_memset(ctx, 0, sizeof(*ctx));
ctx->ifname = "wlan0";
ctx->xml = xml_node_init_ctx(ctx, NULL);
if (ctx->xml == NULL)
return -1;
- devinfo = node_from_file(ctx->xml, "devinfo.xml");
- if (devinfo) {
- devid = get_node(ctx->xml, devinfo, "DevId");
- if (devid) {
- char *tmp = xml_node_get_text(ctx->xml, devid);
-
- if (tmp) {
- ctx->devid = os_strdup(tmp);
- xml_node_get_text_free(ctx->xml, tmp);
- }
- }
- xml_node_free(ctx->xml, devinfo);
- }
-
ctx->http = http_init_ctx(ctx, ctx->xml);
if (ctx->http == NULL) {
xml_node_deinit_ctx(ctx->xml);
return -1;
}
http_ocsp_set(ctx->http, 2);
- http_set_cert_cb(ctx->http, osu_cert_cb, ctx);
return 0;
}
static void deinit_ctx(struct hs20_osu_client *ctx)
{
- size_t i;
-
http_deinit_ctx(ctx->http);
xml_node_deinit_ctx(ctx->xml);
- os_free(ctx->fqdn);
- os_free(ctx->server_url);
- os_free(ctx->devid);
-
- for (i = 0; i < ctx->server_dnsname_count; i++)
- os_free(ctx->server_dnsname[i]);
- os_free(ctx->server_dnsname);
}
"- from_tnds <XML MO in TNDS format> <XML MO>\n"
"- set_pps <PerProviderSubscription XML file name>\n"
"- get_fqdn <PerProviderSubscription XML file name>\n"
- "- pol_upd [Server URL] [PPS] [CA cert]\n"
- "- sub_rem <Server URL> [PPS] [CA cert]\n"
- "- prov <Server URL> [CA cert]\n"
- "- oma_dm_prov <Server URL> [CA cert]\n"
- "- sim_prov <Server URL> [CA cert]\n"
- "- oma_dm_sim_prov <Server URL> [CA cert]\n"
- "- signup [CA cert]\n"
- "- dl_osu_ca <PPS> <CA file>\n"
- "- dl_polupd_ca <PPS> <CA file>\n"
"- dl_aaa_ca <PPS> <CA file>\n"
- "- browser <URL>\n"
- "- parse_cert <X.509 certificate (DER)>\n"
- "- osu_select <OSU info directory> [CA cert]\n");
+ "- browser <URL>\n");
}
struct hs20_osu_client ctx;
int c;
int ret = 0;
- int no_prod_assoc = 0;
- const char *friendly_name = NULL;
const char *wpa_debug_file_path = NULL;
extern char *wpas_ctrl_path;
extern int wpa_debug_level;
return -1;
for (;;) {
- c = getopt(argc, argv, "df:hKNo:O:qr:s:S:tTw:x:");
+ c = getopt(argc, argv, "df:hKqr:s:S:tTw:");
if (c < 0)
break;
switch (c) {
case 'K':
wpa_debug_show_keys++;
break;
- case 'N':
- no_prod_assoc = 1;
- break;
- case 'o':
- ctx.osu_ssid = optarg;
- break;
- case 'O':
- friendly_name = optarg;
- break;
case 'q':
wpa_debug_level++;
break;
case 'w':
wpas_ctrl_path = optarg;
break;
- case 'x':
- spp_xsd_fname = optarg;
- break;
case 'h':
default:
usage();
exit(0);
}
cmd_from_tnds(&ctx, argv[optind + 1], argv[optind + 2]);
- } else if (strcmp(argv[optind], "sub_rem") == 0) {
- if (argc - optind < 2) {
- usage();
- exit(0);
- }
- ret = cmd_sub_rem(&ctx, argv[optind + 1],
- argc > optind + 2 ? argv[optind + 2] : NULL,
- argc > optind + 3 ? argv[optind + 3] : NULL);
- } else if (strcmp(argv[optind], "pol_upd") == 0) {
- ret = cmd_pol_upd(&ctx,
- argc > optind + 1 ? argv[optind + 1] : NULL,
- argc > optind + 2 ? argv[optind + 2] : NULL,
- argc > optind + 3 ? argv[optind + 3] : NULL);
- } else if (strcmp(argv[optind], "prov") == 0) {
- if (argc - optind < 2) {
- usage();
- exit(0);
- }
- ctx.ca_fname = argv[optind + 2];
- wpa_printf(MSG_DEBUG, "Calling cmd_prov from main");
- cmd_prov(&ctx, argv[optind + 1]);
- } else if (strcmp(argv[optind], "sim_prov") == 0) {
- if (argc - optind < 2) {
- usage();
- exit(0);
- }
- ctx.ca_fname = argv[optind + 2];
- cmd_sim_prov(&ctx, argv[optind + 1]);
- } else if (strcmp(argv[optind], "dl_osu_ca") == 0) {
- if (argc - optind < 2) {
- usage();
- exit(0);
- }
- cmd_dl_osu_ca(&ctx, argv[optind + 1], argv[optind + 2]);
- } else if (strcmp(argv[optind], "dl_polupd_ca") == 0) {
- if (argc - optind < 2) {
- usage();
- exit(0);
- }
- cmd_dl_polupd_ca(&ctx, argv[optind + 1], argv[optind + 2]);
} else if (strcmp(argv[optind], "dl_aaa_ca") == 0) {
if (argc - optind < 2) {
usage();
exit(0);
}
cmd_dl_aaa_ca(&ctx, argv[optind + 1], argv[optind + 2]);
- } else if (strcmp(argv[optind], "osu_select") == 0) {
- if (argc - optind < 2) {
- usage();
- exit(0);
- }
- ctx.ca_fname = argc > optind + 2 ? argv[optind + 2] : NULL;
- cmd_osu_select(&ctx, argv[optind + 1], 2, 1, NULL);
- } else if (strcmp(argv[optind], "signup") == 0) {
- ctx.ca_fname = argc > optind + 1 ? argv[optind + 1] : NULL;
- ret = cmd_signup(&ctx, no_prod_assoc, friendly_name);
} else if (strcmp(argv[optind], "set_pps") == 0) {
if (argc - optind < 2) {
usage();
exit(0);
}
ret = cmd_get_fqdn(&ctx, argv[optind + 1]);
- } else if (strcmp(argv[optind], "oma_dm_prov") == 0) {
- if (argc - optind < 2) {
- usage();
- exit(0);
- }
- ctx.ca_fname = argv[optind + 2];
- cmd_oma_dm_prov(&ctx, argv[optind + 1]);
- } else if (strcmp(argv[optind], "oma_dm_sim_prov") == 0) {
- if (argc - optind < 2) {
- usage();
- exit(0);
- }
- ctx.ca_fname = argv[optind + 2];
- if (cmd_oma_dm_sim_prov(&ctx, argv[optind + 1]) < 0) {
- write_summary(&ctx, "Failed to complete OMA DM SIM provisioning");
- return -1;
- }
- } else if (strcmp(argv[optind], "oma_dm_add") == 0) {
- if (argc - optind < 2) {
- usage();
- exit(0);
- }
- cmd_oma_dm_add(&ctx, argv[optind + 1], argv[optind + 2]);
- } else if (strcmp(argv[optind], "oma_dm_replace") == 0) {
- if (argc - optind < 2) {
- usage();
- exit(0);
- }
- cmd_oma_dm_replace(&ctx, argv[optind + 1], argv[optind + 2]);
- } else if (strcmp(argv[optind], "est_csr") == 0) {
- if (argc - optind < 2) {
- usage();
- exit(0);
- }
- mkdir("Cert", S_IRWXU);
- est_build_csr(&ctx, argv[optind + 1]);
} else if (strcmp(argv[optind], "browser") == 0) {
int ret;
argv[optind + 1]);
ret = hs20_web_browser(argv[optind + 1], ctx.ignore_tls);
wpa_printf(MSG_INFO, "Web browser result: %d", ret);
- } else if (strcmp(argv[optind], "parse_cert") == 0) {
- if (argc - optind < 2) {
- usage();
- exit(0);
- }
-
- wpa_debug_level = MSG_MSGDUMP;
- http_parse_x509_certificate(ctx.http, argv[optind + 1]);
- wpa_debug_level = MSG_INFO;
} else {
wpa_printf(MSG_INFO, "Unknown command '%s'", argv[optind]);
}
#ifndef OSU_CLIENT_H
#define OSU_CLIENT_H
-#define SPP_NS_URI "http://www.wi-fi.org/specifications/hotspot2dot0/v1.0/spp"
-
-#define URN_OMA_DM_DEVINFO "urn:oma:mo:oma-dm-devinfo:1.0"
-#define URN_OMA_DM_DEVDETAIL "urn:oma:mo:oma-dm-devdetail:1.0"
-#define URN_HS20_DEVDETAIL_EXT "urn:wfa:mo-ext:hotspot2dot0-devdetail-ext:1.0"
-#define URN_HS20_PPS "urn:wfa:mo:hotspot2dot0-perprovidersubscription:1.0"
-
-
-#define MAX_OSU_VALS 10
-
-struct osu_lang_text {
- char lang[4];
- char text[253];
-};
-
struct hs20_osu_client {
struct xml_node_ctx *xml;
struct http_ctx *http;
- int no_reconnect;
- char pps_fname[300];
- char *devid;
const char *result_file;
const char *summary_file;
const char *ifname;
- const char *ca_fname;
- int no_osu_cert_validation; /* for EST operations */
- char *fqdn;
- char *server_url;
- struct osu_lang_text friendly_name[MAX_OSU_VALS];
- size_t friendly_name_count;
- size_t icon_count;
- char icon_filename[MAX_OSU_VALS][256];
- u8 icon_hash[MAX_OSU_VALS][32];
- int pps_cred_set;
- int pps_updated;
- int client_cert_present;
- char **server_dnsname;
- size_t server_dnsname_count;
- const char *osu_ssid; /* Enforced OSU_SSID for testing purposes */
#define WORKAROUND_OCSP_OPTIONAL 0x00000001
unsigned long int workarounds;
int ignore_tls; /* whether to ignore TLS validation issues with HTTPS
* server certificate */
};
-
-/* osu_client.c */
-
-void write_result(struct hs20_osu_client *ctx, const char *fmt, ...)
- __attribute__ ((format (printf, 2, 3)));
-void write_summary(struct hs20_osu_client *ctx, const char *fmt, ...)
- __attribute__ ((format (printf, 2, 3)));
-
-void debug_dump_node(struct hs20_osu_client *ctx, const char *title,
- xml_node_t *node);
-int osu_get_certificate(struct hs20_osu_client *ctx, xml_node_t *getcert);
-int hs20_add_pps_mo(struct hs20_osu_client *ctx, const char *uri,
- xml_node_t *add_mo, char *fname, size_t fname_len);
-void get_user_pw(struct hs20_osu_client *ctx, xml_node_t *pps,
- const char *alt_loc, char **user, char **pw);
-int update_pps_file(struct hs20_osu_client *ctx, const char *pps_fname,
- xml_node_t *pps);
-void cmd_set_pps(struct hs20_osu_client *ctx, const char *pps_fname);
-
-
-/* spp_client.c */
-
-void spp_sub_rem(struct hs20_osu_client *ctx, const char *address,
- const char *pps_fname,
- const char *client_cert, const char *client_key,
- const char *cred_username, const char *cred_password,
- xml_node_t *pps);
-void spp_pol_upd(struct hs20_osu_client *ctx, const char *address,
- const char *pps_fname,
- const char *client_cert, const char *client_key,
- const char *cred_username, const char *cred_password,
- xml_node_t *pps);
-int cmd_prov(struct hs20_osu_client *ctx, const char *url);
-int cmd_sim_prov(struct hs20_osu_client *ctx, const char *url);
-
-
-/* oma_dm_client.c */
-
-int cmd_oma_dm_prov(struct hs20_osu_client *ctx, const char *url);
-int cmd_oma_dm_sim_prov(struct hs20_osu_client *ctx, const char *url);
-void oma_dm_sub_rem(struct hs20_osu_client *ctx, const char *address,
- const char *pps_fname,
- const char *client_cert, const char *client_key,
- const char *cred_username, const char *cred_password,
- xml_node_t *pps);
-void oma_dm_pol_upd(struct hs20_osu_client *ctx, const char *address,
- const char *pps_fname,
- const char *client_cert, const char *client_key,
- const char *cred_username, const char *cred_password,
- xml_node_t *pps);
-void cmd_oma_dm_sub_rem(struct hs20_osu_client *ctx, const char *address,
- const char *pps_fname);
-void cmd_oma_dm_add(struct hs20_osu_client *ctx, const char *pps_fname,
- const char *add_fname);
-void cmd_oma_dm_replace(struct hs20_osu_client *ctx, const char *pps_fname,
- const char *replace_fname);
-
-/* est.c */
-
-int est_load_cacerts(struct hs20_osu_client *ctx, const char *url);
-int est_build_csr(struct hs20_osu_client *ctx, const char *url);
-int est_simple_enroll(struct hs20_osu_client *ctx, const char *url,
- const char *user, const char *pw);
-
#endif /* OSU_CLIENT_H */
+++ /dev/null
-/*
- * Hotspot 2.0 SPP client
- * Copyright (c) 2012-2014, Qualcomm Atheros, Inc.
- *
- * This software may be distributed under the terms of the BSD license.
- * See README for more details.
- */
-
-#include "includes.h"
-#include <sys/stat.h>
-
-#include "common.h"
-#include "browser.h"
-#include "wpa_ctrl.h"
-#include "wpa_helpers.h"
-#include "xml-utils.h"
-#include "http-utils.h"
-#include "utils/base64.h"
-#include "crypto/crypto.h"
-#include "crypto/sha256.h"
-#include "osu_client.h"
-
-
-extern const char *spp_xsd_fname;
-
-static int hs20_spp_update_response(struct hs20_osu_client *ctx,
- const char *session_id,
- const char *spp_status,
- const char *error_code);
-static void hs20_policy_update_complete(
- struct hs20_osu_client *ctx, const char *pps_fname);
-
-
-static char * get_spp_attr_value(struct xml_node_ctx *ctx, xml_node_t *node,
- char *attr_name)
-{
- return xml_node_get_attr_value_ns(ctx, node, SPP_NS_URI, attr_name);
-}
-
-
-static int hs20_spp_validate(struct hs20_osu_client *ctx, xml_node_t *node,
- const char *expected_name)
-{
- struct xml_node_ctx *xctx = ctx->xml;
- const char *name;
- char *err;
- int ret;
-
- if (!xml_node_is_element(xctx, node))
- return -1;
-
- name = xml_node_get_localname(xctx, node);
- if (name == NULL)
- return -1;
-
- if (strcmp(expected_name, name) != 0) {
- wpa_printf(MSG_INFO, "Unexpected SOAP method name '%s' (expected '%s')",
- name, expected_name);
- write_summary(ctx, "Unexpected SOAP method name '%s' (expected '%s')",
- name, expected_name);
- return -1;
- }
-
- ret = xml_validate(xctx, node, spp_xsd_fname, &err);
- if (ret < 0) {
- wpa_printf(MSG_INFO, "XML schema validation error(s)\n%s", err);
- write_summary(ctx, "SPP XML schema validation failed");
- os_free(err);
- }
- return ret;
-}
-
-
-static void add_mo_container(struct xml_node_ctx *ctx, xml_namespace_t *ns,
- xml_node_t *parent, const char *urn,
- const char *fname)
-{
- xml_node_t *node;
- xml_node_t *fnode, *tnds;
- char *str;
-
- errno = 0;
- fnode = node_from_file(ctx, fname);
- if (!fnode) {
- wpa_printf(MSG_ERROR,
- "Failed to create XML node from file: %s, possible error: %s",
- fname, strerror(errno));
- return;
- }
- tnds = mo_to_tnds(ctx, fnode, 0, urn, "syncml:dmddf1.2");
- xml_node_free(ctx, fnode);
- if (!tnds)
- return;
-
- str = xml_node_to_str(ctx, tnds);
- xml_node_free(ctx, tnds);
- if (str == NULL)
- return;
-
- node = xml_node_create_text(ctx, parent, ns, "moContainer", str);
- if (node)
- xml_node_add_attr(ctx, node, ns, "moURN", urn);
- os_free(str);
-}
-
-
-static xml_node_t * build_spp_post_dev_data(struct hs20_osu_client *ctx,
- xml_namespace_t **ret_ns,
- const char *session_id,
- const char *reason)
-{
- xml_namespace_t *ns;
- xml_node_t *spp_node;
-
- write_summary(ctx, "Building sppPostDevData requestReason='%s'",
- reason);
- spp_node = xml_node_create_root(ctx->xml, SPP_NS_URI, "spp", &ns,
- "sppPostDevData");
- if (spp_node == NULL)
- return NULL;
- if (ret_ns)
- *ret_ns = ns;
-
- xml_node_add_attr(ctx->xml, spp_node, ns, "sppVersion", "1.0");
- xml_node_add_attr(ctx->xml, spp_node, NULL, "requestReason", reason);
- if (session_id)
- xml_node_add_attr(ctx->xml, spp_node, ns, "sessionID",
- session_id);
- xml_node_add_attr(ctx->xml, spp_node, NULL, "redirectURI",
- "http://localhost:12345/");
-
- xml_node_create_text(ctx->xml, spp_node, ns, "supportedSPPVersions",
- "1.0");
- xml_node_create_text(ctx->xml, spp_node, ns, "supportedMOList",
- URN_HS20_PPS " " URN_OMA_DM_DEVINFO " "
- URN_OMA_DM_DEVDETAIL " " URN_HS20_DEVDETAIL_EXT);
-
- add_mo_container(ctx->xml, ns, spp_node, URN_OMA_DM_DEVINFO,
- "devinfo.xml");
- add_mo_container(ctx->xml, ns, spp_node, URN_OMA_DM_DEVDETAIL,
- "devdetail.xml");
-
- return spp_node;
-}
-
-
-static int process_update_node(struct hs20_osu_client *ctx, xml_node_t *pps,
- xml_node_t *update)
-{
- xml_node_t *node, *parent, *tnds, *unode;
- char *str;
- const char *name;
- char *uri, *pos;
- char *cdata, *cdata_end;
- size_t fqdn_len;
-
- wpa_printf(MSG_INFO, "Processing updateNode");
- debug_dump_node(ctx, "updateNode", update);
-
- uri = get_spp_attr_value(ctx->xml, update, "managementTreeURI");
- if (uri == NULL) {
- wpa_printf(MSG_INFO, "No managementTreeURI present");
- return -1;
- }
- wpa_printf(MSG_INFO, "managementTreeUri: '%s'", uri);
-
- name = os_strrchr(uri, '/');
- if (name == NULL) {
- wpa_printf(MSG_INFO, "Unexpected URI");
- xml_node_get_attr_value_free(ctx->xml, uri);
- return -1;
- }
- name++;
- wpa_printf(MSG_INFO, "Update interior node: '%s'", name);
-
- str = xml_node_get_text(ctx->xml, update);
- if (str == NULL) {
- wpa_printf(MSG_INFO, "Could not extract MO text");
- xml_node_get_attr_value_free(ctx->xml, uri);
- return -1;
- }
- wpa_printf(MSG_DEBUG, "[hs20] nodeContainer text: '%s'", str);
- cdata = strstr(str, "<![CDATA[");
- cdata_end = strstr(str, "]]>");
- if (cdata && cdata_end && cdata_end > cdata &&
- cdata < strstr(str, "MgmtTree") &&
- cdata_end > strstr(str, "/MgmtTree")) {
- char *tmp;
- wpa_printf(MSG_DEBUG, "[hs20] Removing extra CDATA container");
- tmp = strdup(cdata + 9);
- if (tmp) {
- cdata_end = strstr(tmp, "]]>");
- if (cdata_end)
- *cdata_end = '\0';
- wpa_printf(MSG_DEBUG, "[hs20] nodeContainer text with CDATA container removed: '%s'",
- tmp);
- tnds = xml_node_from_buf(ctx->xml, tmp);
- free(tmp);
- } else
- tnds = NULL;
- } else
- tnds = xml_node_from_buf(ctx->xml, str);
- xml_node_get_text_free(ctx->xml, str);
- if (tnds == NULL) {
- wpa_printf(MSG_INFO, "[hs20] Could not parse nodeContainer text");
- xml_node_get_attr_value_free(ctx->xml, uri);
- return -1;
- }
-
- unode = tnds_to_mo(ctx->xml, tnds);
- xml_node_free(ctx->xml, tnds);
- if (unode == NULL) {
- wpa_printf(MSG_INFO, "[hs20] Could not parse nodeContainer TNDS text");
- xml_node_get_attr_value_free(ctx->xml, uri);
- return -1;
- }
-
- debug_dump_node(ctx, "Parsed TNDS", unode);
-
- if (get_node_uri(ctx->xml, unode, name) == NULL) {
- wpa_printf(MSG_INFO, "[hs20] %s node not found", name);
- xml_node_free(ctx->xml, unode);
- xml_node_get_attr_value_free(ctx->xml, uri);
- return -1;
- }
-
- if (os_strncasecmp(uri, "./Wi-Fi/", 8) != 0) {
- wpa_printf(MSG_INFO, "Do not allow update outside ./Wi-Fi");
- xml_node_free(ctx->xml, unode);
- xml_node_get_attr_value_free(ctx->xml, uri);
- return -1;
- }
- pos = uri + 8;
-
- if (ctx->fqdn == NULL) {
- wpa_printf(MSG_INFO, "FQDN not known");
- xml_node_free(ctx->xml, unode);
- xml_node_get_attr_value_free(ctx->xml, uri);
- return -1;
- }
- fqdn_len = os_strlen(ctx->fqdn);
- if (os_strncasecmp(pos, ctx->fqdn, fqdn_len) != 0 ||
- pos[fqdn_len] != '/') {
- wpa_printf(MSG_INFO, "Do not allow update outside ./Wi-Fi/%s",
- ctx->fqdn);
- xml_node_free(ctx->xml, unode);
- xml_node_get_attr_value_free(ctx->xml, uri);
- return -1;
- }
- pos += fqdn_len + 1;
-
- if (os_strncasecmp(pos, "PerProviderSubscription/", 24) != 0) {
- wpa_printf(MSG_INFO, "Do not allow update outside ./Wi-Fi/%s/PerProviderSubscription",
- ctx->fqdn);
- xml_node_free(ctx->xml, unode);
- xml_node_get_attr_value_free(ctx->xml, uri);
- return -1;
- }
- pos += 24;
-
- wpa_printf(MSG_INFO, "Update command for PPS node %s", pos);
-
- node = get_node(ctx->xml, pps, pos);
- if (node) {
- parent = xml_node_get_parent(ctx->xml, node);
- xml_node_detach(ctx->xml, node);
- wpa_printf(MSG_INFO, "Replace '%s' node", name);
- } else {
- char *pos2;
- pos2 = os_strrchr(pos, '/');
- if (pos2 == NULL) {
- parent = pps;
- } else {
- *pos2 = '\0';
- parent = get_node(ctx->xml, pps, pos);
- }
- if (parent == NULL) {
- wpa_printf(MSG_INFO, "Could not find parent %s", pos);
- xml_node_free(ctx->xml, unode);
- xml_node_get_attr_value_free(ctx->xml, uri);
- return -1;
- }
- wpa_printf(MSG_INFO, "Add '%s' node", name);
- }
- xml_node_add_child(ctx->xml, parent, unode);
-
- xml_node_get_attr_value_free(ctx->xml, uri);
-
- return 0;
-}
-
-
-static int update_pps(struct hs20_osu_client *ctx, xml_node_t *update,
- const char *pps_fname, xml_node_t *pps)
-{
- wpa_printf(MSG_INFO, "Updating PPS based on updateNode element(s)");
- xml_node_for_each_sibling(ctx->xml, update) {
- xml_node_for_each_check(ctx->xml, update);
- if (process_update_node(ctx, pps, update) < 0)
- return -1;
- }
-
- return update_pps_file(ctx, pps_fname, pps);
-}
-
-
-static void hs20_sub_rem_complete(struct hs20_osu_client *ctx,
- const char *pps_fname)
-{
- /*
- * Update wpa_supplicant credentials and reconnect using updated
- * information.
- */
- wpa_printf(MSG_INFO, "Updating wpa_supplicant credentials");
- cmd_set_pps(ctx, pps_fname);
-
- if (ctx->no_reconnect)
- return;
-
- wpa_printf(MSG_INFO, "Requesting reconnection with updated configuration");
- if (wpa_command(ctx->ifname, "INTERWORKING_SELECT auto") < 0)
- wpa_printf(MSG_ERROR, "Failed to request wpa_supplicant to reconnect");
-}
-
-
-static xml_node_t * hs20_spp_upload_mo(struct hs20_osu_client *ctx,
- xml_node_t *cmd,
- const char *session_id,
- const char *pps_fname)
-{
- xml_namespace_t *ns;
- xml_node_t *node, *ret_node;
- char *urn;
-
- urn = get_spp_attr_value(ctx->xml, cmd, "moURN");
- if (!urn) {
- wpa_printf(MSG_INFO, "No URN included");
- return NULL;
- }
- wpa_printf(MSG_INFO, "Upload MO request - URN=%s", urn);
- if (strcasecmp(urn, URN_HS20_PPS) != 0) {
- wpa_printf(MSG_INFO, "Unsupported moURN");
- xml_node_get_attr_value_free(ctx->xml, urn);
- return NULL;
- }
- xml_node_get_attr_value_free(ctx->xml, urn);
-
- if (!pps_fname) {
- wpa_printf(MSG_INFO, "PPS file name no known");
- return NULL;
- }
-
- node = build_spp_post_dev_data(ctx, &ns, session_id,
- "MO upload");
- if (node == NULL)
- return NULL;
- add_mo_container(ctx->xml, ns, node, URN_HS20_PPS, pps_fname);
-
- ret_node = soap_send_receive(ctx->http, node);
- if (ret_node == NULL)
- return NULL;
-
- debug_dump_node(ctx, "Received response to MO upload", ret_node);
-
- if (hs20_spp_validate(ctx, ret_node, "sppPostDevDataResponse") < 0) {
- wpa_printf(MSG_INFO, "SPP validation failed");
- xml_node_free(ctx->xml, ret_node);
- return NULL;
- }
-
- return ret_node;
-}
-
-
-static int hs20_add_mo(struct hs20_osu_client *ctx, xml_node_t *add_mo,
- char *fname, size_t fname_len)
-{
- char *uri, *urn;
- int ret;
-
- debug_dump_node(ctx, "Received addMO", add_mo);
-
- urn = get_spp_attr_value(ctx->xml, add_mo, "moURN");
- if (urn == NULL) {
- wpa_printf(MSG_INFO, "[hs20] No moURN in addMO");
- return -1;
- }
- wpa_printf(MSG_INFO, "addMO - moURN: '%s'", urn);
- if (strcasecmp(urn, URN_HS20_PPS) != 0) {
- wpa_printf(MSG_INFO, "[hs20] Unsupported MO in addMO");
- xml_node_get_attr_value_free(ctx->xml, urn);
- return -1;
- }
- xml_node_get_attr_value_free(ctx->xml, urn);
-
- uri = get_spp_attr_value(ctx->xml, add_mo, "managementTreeURI");
- if (uri == NULL) {
- wpa_printf(MSG_INFO, "[hs20] No managementTreeURI in addMO");
- return -1;
- }
- wpa_printf(MSG_INFO, "addMO - managementTreeURI: '%s'", uri);
-
- ret = hs20_add_pps_mo(ctx, uri, add_mo, fname, fname_len);
- xml_node_get_attr_value_free(ctx->xml, uri);
- return ret;
-}
-
-
-static int process_spp_user_input_response(struct hs20_osu_client *ctx,
- const char *session_id,
- xml_node_t *add_mo)
-{
- int ret;
- char fname[300];
-
- debug_dump_node(ctx, "addMO", add_mo);
-
- wpa_printf(MSG_INFO, "Subscription registration completed");
-
- if (hs20_add_mo(ctx, add_mo, fname, sizeof(fname)) < 0) {
- wpa_printf(MSG_INFO, "Could not add MO");
- ret = hs20_spp_update_response(
- ctx, session_id,
- "Error occurred",
- "MO addition or update failed");
- return 0;
- }
-
- ret = hs20_spp_update_response(ctx, session_id, "OK", NULL);
- if (ret == 0)
- hs20_sub_rem_complete(ctx, fname);
-
- return 0;
-}
-
-
-static xml_node_t * hs20_spp_user_input_completed(struct hs20_osu_client *ctx,
- const char *session_id)
-{
- xml_node_t *node, *ret_node;
-
- node = build_spp_post_dev_data(ctx, NULL, session_id,
- "User input completed");
- if (node == NULL)
- return NULL;
-
- ret_node = soap_send_receive(ctx->http, node);
- if (!ret_node) {
- if (soap_reinit_client(ctx->http) < 0)
- return NULL;
- wpa_printf(MSG_INFO, "Try to finish with re-opened connection");
- node = build_spp_post_dev_data(ctx, NULL, session_id,
- "User input completed");
- if (node == NULL)
- return NULL;
- ret_node = soap_send_receive(ctx->http, node);
- if (ret_node == NULL)
- return NULL;
- wpa_printf(MSG_INFO, "Continue with new connection");
- }
-
- if (hs20_spp_validate(ctx, ret_node, "sppPostDevDataResponse") < 0) {
- wpa_printf(MSG_INFO, "SPP validation failed");
- xml_node_free(ctx->xml, ret_node);
- return NULL;
- }
-
- return ret_node;
-}
-
-
-static xml_node_t * hs20_spp_get_certificate(struct hs20_osu_client *ctx,
- xml_node_t *cmd,
- const char *session_id,
- const char *pps_fname)
-{
- xml_namespace_t *ns;
- xml_node_t *node, *ret_node;
- int res;
-
- wpa_printf(MSG_INFO, "Client certificate enrollment");
-
- res = osu_get_certificate(ctx, cmd);
- if (res < 0)
- wpa_printf(MSG_INFO, "EST simpleEnroll failed");
-
- node = build_spp_post_dev_data(ctx, &ns, session_id,
- res == 0 ?
- "Certificate enrollment completed" :
- "Certificate enrollment failed");
- if (node == NULL)
- return NULL;
-
- ret_node = soap_send_receive(ctx->http, node);
- if (ret_node == NULL)
- return NULL;
-
- debug_dump_node(ctx, "Received response to certificate enrollment "
- "completed", ret_node);
-
- if (hs20_spp_validate(ctx, ret_node, "sppPostDevDataResponse") < 0) {
- wpa_printf(MSG_INFO, "SPP validation failed");
- xml_node_free(ctx->xml, ret_node);
- return NULL;
- }
-
- return ret_node;
-}
-
-
-static int hs20_spp_exec(struct hs20_osu_client *ctx, xml_node_t *exec,
- const char *session_id, const char *pps_fname,
- xml_node_t *pps, xml_node_t **ret_node)
-{
- xml_node_t *cmd;
- const char *name;
- char *uri;
- char *id = strdup(session_id);
-
- if (id == NULL)
- return -1;
-
- *ret_node = NULL;
-
- debug_dump_node(ctx, "exec", exec);
-
- xml_node_for_each_child(ctx->xml, cmd, exec) {
- xml_node_for_each_check(ctx->xml, cmd);
- break;
- }
- if (!cmd) {
- wpa_printf(MSG_INFO, "exec command element not found (cmd=%p)",
- cmd);
- free(id);
- return -1;
- }
-
- name = xml_node_get_localname(ctx->xml, cmd);
-
- if (strcasecmp(name, "launchBrowserToURI") == 0) {
- int res;
- uri = xml_node_get_text(ctx->xml, cmd);
- if (!uri) {
- wpa_printf(MSG_INFO, "No URI found");
- free(id);
- return -1;
- }
- wpa_printf(MSG_INFO, "Launch browser to URI '%s'", uri);
- write_summary(ctx, "Launch browser to URI '%s'", uri);
- res = hs20_web_browser(uri, 1);
- xml_node_get_text_free(ctx->xml, uri);
- if (res > 0) {
- wpa_printf(MSG_INFO, "User response in browser completed successfully - sessionid='%s'",
- id);
- write_summary(ctx, "User response in browser completed successfully");
- *ret_node = hs20_spp_user_input_completed(ctx, id);
- free(id);
- return *ret_node ? 0 : -1;
- } else {
- wpa_printf(MSG_INFO, "Failed to receive user response");
- write_summary(ctx, "Failed to receive user response");
- hs20_spp_update_response(
- ctx, id, "Error occurred", "Other");
- free(id);
- return -1;
- }
- }
-
- if (strcasecmp(name, "uploadMO") == 0) {
- if (pps_fname == NULL)
- return -1;
- *ret_node = hs20_spp_upload_mo(ctx, cmd, id,
- pps_fname);
- free(id);
- return *ret_node ? 0 : -1;
- }
-
- if (strcasecmp(name, "getCertificate") == 0) {
- *ret_node = hs20_spp_get_certificate(ctx, cmd, id,
- pps_fname);
- free(id);
- return *ret_node ? 0 : -1;
- }
-
- wpa_printf(MSG_INFO, "Unsupported exec command: '%s'", name);
- free(id);
- return -1;
-}
-
-
-enum spp_post_dev_data_use {
- SPP_SUBSCRIPTION_REMEDIATION,
- SPP_POLICY_UPDATE,
- SPP_SUBSCRIPTION_REGISTRATION,
-};
-
-static void process_spp_post_dev_data_response(
- struct hs20_osu_client *ctx,
- enum spp_post_dev_data_use use, xml_node_t *node,
- const char *pps_fname, xml_node_t *pps)
-{
- xml_node_t *child;
- char *status = NULL;
- xml_node_t *update = NULL, *exec = NULL, *add_mo = NULL, *no_mo = NULL;
- char *session_id = NULL;
-
- debug_dump_node(ctx, "sppPostDevDataResponse node", node);
-
- status = get_spp_attr_value(ctx->xml, node, "sppStatus");
- if (status == NULL) {
- wpa_printf(MSG_INFO, "No sppStatus attribute");
- goto out;
- }
- write_summary(ctx, "Received sppPostDevDataResponse sppStatus='%s'",
- status);
-
- session_id = get_spp_attr_value(ctx->xml, node, "sessionID");
- if (session_id == NULL) {
- wpa_printf(MSG_INFO, "No sessionID attribute");
- goto out;
- }
-
- wpa_printf(MSG_INFO, "[hs20] sppPostDevDataResponse - sppStatus: '%s' sessionID: '%s'",
- status, session_id);
-
- xml_node_for_each_child(ctx->xml, child, node) {
- const char *name;
- xml_node_for_each_check(ctx->xml, child);
- debug_dump_node(ctx, "child", child);
- name = xml_node_get_localname(ctx->xml, child);
- wpa_printf(MSG_INFO, "localname: '%s'", name);
- if (!update && strcasecmp(name, "updateNode") == 0)
- update = child;
- if (!exec && strcasecmp(name, "exec") == 0)
- exec = child;
- if (!add_mo && strcasecmp(name, "addMO") == 0)
- add_mo = child;
- if (!no_mo && strcasecmp(name, "noMOUpdate") == 0)
- no_mo = child;
- }
-
- if (use == SPP_SUBSCRIPTION_REMEDIATION &&
- strcasecmp(status,
- "Remediation complete, request sppUpdateResponse") == 0)
- {
- int res, ret;
- if (!update && !no_mo) {
- wpa_printf(MSG_INFO, "No updateNode or noMOUpdate element");
- goto out;
- }
- wpa_printf(MSG_INFO, "Subscription remediation completed");
- res = update_pps(ctx, update, pps_fname, pps);
- if (res < 0)
- wpa_printf(MSG_INFO, "Failed to update PPS MO");
- ret = hs20_spp_update_response(
- ctx, session_id,
- res < 0 ? "Error occurred" : "OK",
- res < 0 ? "MO addition or update failed" : NULL);
- if (res == 0 && ret == 0)
- hs20_sub_rem_complete(ctx, pps_fname);
- goto out;
- }
-
- if (use == SPP_SUBSCRIPTION_REMEDIATION &&
- strcasecmp(status, "Exchange complete, release TLS connection") ==
- 0) {
- if (!no_mo) {
- wpa_printf(MSG_INFO, "No noMOUpdate element");
- goto out;
- }
- wpa_printf(MSG_INFO, "Subscription remediation completed (no MO update)");
- goto out;
- }
-
- if (use == SPP_POLICY_UPDATE &&
- strcasecmp(status, "Update complete, request sppUpdateResponse") ==
- 0) {
- int res, ret;
- wpa_printf(MSG_INFO, "Policy update received - update PPS");
- res = update_pps(ctx, update, pps_fname, pps);
- ret = hs20_spp_update_response(
- ctx, session_id,
- res < 0 ? "Error occurred" : "OK",
- res < 0 ? "MO addition or update failed" : NULL);
- if (res == 0 && ret == 0)
- hs20_policy_update_complete(ctx, pps_fname);
- goto out;
- }
-
- if (use == SPP_SUBSCRIPTION_REGISTRATION &&
- strcasecmp(status, "Provisioning complete, request "
- "sppUpdateResponse") == 0) {
- if (!add_mo) {
- wpa_printf(MSG_INFO, "No addMO element - not sure what to do next");
- goto out;
- }
- process_spp_user_input_response(ctx, session_id, add_mo);
- node = NULL;
- goto out;
- }
-
- if (strcasecmp(status, "No update available at this time") == 0) {
- wpa_printf(MSG_INFO, "No update available at this time");
- goto out;
- }
-
- if (strcasecmp(status, "OK") == 0) {
- int res;
- xml_node_t *ret;
-
- if (!exec) {
- wpa_printf(MSG_INFO, "No exec element - not sure what to do next");
- goto out;
- }
- res = hs20_spp_exec(ctx, exec, session_id,
- pps_fname, pps, &ret);
- /* xml_node_free(ctx->xml, node); */
- node = NULL;
- if (res == 0 && ret)
- process_spp_post_dev_data_response(ctx, use,
- ret, pps_fname, pps);
- goto out;
- }
-
- if (strcasecmp(status, "Error occurred") == 0) {
- xml_node_t *err;
- char *code = NULL;
- err = get_node(ctx->xml, node, "sppError");
- if (err)
- code = xml_node_get_attr_value(ctx->xml, err,
- "errorCode");
- wpa_printf(MSG_INFO, "Error occurred - errorCode=%s",
- code ? code : "N/A");
- xml_node_get_attr_value_free(ctx->xml, code);
- goto out;
- }
-
- wpa_printf(MSG_INFO,
- "[hs20] Unsupported sppPostDevDataResponse sppStatus '%s'",
- status);
-out:
- xml_node_get_attr_value_free(ctx->xml, status);
- xml_node_get_attr_value_free(ctx->xml, session_id);
- xml_node_free(ctx->xml, node);
-}
-
-
-static int spp_post_dev_data(struct hs20_osu_client *ctx,
- enum spp_post_dev_data_use use,
- const char *reason,
- const char *pps_fname, xml_node_t *pps)
-{
- xml_node_t *payload;
- xml_node_t *ret_node;
-
- payload = build_spp_post_dev_data(ctx, NULL, NULL, reason);
- if (payload == NULL)
- return -1;
-
- ret_node = soap_send_receive(ctx->http, payload);
- if (!ret_node) {
- const char *err = http_get_err(ctx->http);
- if (err) {
- wpa_printf(MSG_INFO, "HTTP error: %s", err);
- write_result(ctx, "HTTP error: %s", err);
- } else {
- write_summary(ctx, "Failed to send SOAP message");
- }
- return -1;
- }
-
- if (hs20_spp_validate(ctx, ret_node, "sppPostDevDataResponse") < 0) {
- wpa_printf(MSG_INFO, "SPP validation failed");
- xml_node_free(ctx->xml, ret_node);
- return -1;
- }
-
- process_spp_post_dev_data_response(ctx, use, ret_node,
- pps_fname, pps);
- return 0;
-}
-
-
-void spp_sub_rem(struct hs20_osu_client *ctx, const char *address,
- const char *pps_fname,
- const char *client_cert, const char *client_key,
- const char *cred_username, const char *cred_password,
- xml_node_t *pps)
-{
- wpa_printf(MSG_INFO, "SPP subscription remediation");
- write_summary(ctx, "SPP subscription remediation");
-
- os_free(ctx->server_url);
- ctx->server_url = os_strdup(address);
-
- if (soap_init_client(ctx->http, address, ctx->ca_fname,
- cred_username, cred_password, client_cert,
- client_key) == 0) {
- spp_post_dev_data(ctx, SPP_SUBSCRIPTION_REMEDIATION,
- "Subscription remediation", pps_fname, pps);
- }
-}
-
-
-static void hs20_policy_update_complete(struct hs20_osu_client *ctx,
- const char *pps_fname)
-{
- wpa_printf(MSG_INFO, "Policy update completed");
-
- /*
- * Update wpa_supplicant credentials and reconnect using updated
- * information.
- */
- wpa_printf(MSG_INFO, "Updating wpa_supplicant credentials");
- cmd_set_pps(ctx, pps_fname);
-
- wpa_printf(MSG_INFO, "Requesting reconnection with updated configuration");
- if (wpa_command(ctx->ifname, "INTERWORKING_SELECT auto") < 0)
- wpa_printf(MSG_ERROR, "Failed to request wpa_supplicant to reconnect");
-}
-
-
-static int process_spp_exchange_complete(struct hs20_osu_client *ctx,
- xml_node_t *node)
-{
- char *status, *session_id;
-
- debug_dump_node(ctx, "sppExchangeComplete", node);
-
- status = get_spp_attr_value(ctx->xml, node, "sppStatus");
- if (status == NULL) {
- wpa_printf(MSG_INFO, "No sppStatus attribute");
- return -1;
- }
- write_summary(ctx, "Received sppExchangeComplete sppStatus='%s'",
- status);
-
- session_id = get_spp_attr_value(ctx->xml, node, "sessionID");
- if (session_id == NULL) {
- wpa_printf(MSG_INFO, "No sessionID attribute");
- xml_node_get_attr_value_free(ctx->xml, status);
- return -1;
- }
-
- wpa_printf(MSG_INFO, "[hs20] sppStatus: '%s' sessionID: '%s'",
- status, session_id);
- xml_node_get_attr_value_free(ctx->xml, session_id);
-
- if (strcasecmp(status, "Exchange complete, release TLS connection") ==
- 0) {
- xml_node_get_attr_value_free(ctx->xml, status);
- return 0;
- }
-
- wpa_printf(MSG_INFO, "Unexpected sppStatus '%s'", status);
- write_summary(ctx, "Unexpected sppStatus '%s'", status);
- xml_node_get_attr_value_free(ctx->xml, status);
- return -1;
-}
-
-
-static xml_node_t * build_spp_update_response(struct hs20_osu_client *ctx,
- const char *session_id,
- const char *spp_status,
- const char *error_code)
-{
- xml_namespace_t *ns;
- xml_node_t *spp_node, *node;
-
- spp_node = xml_node_create_root(ctx->xml, SPP_NS_URI, "spp", &ns,
- "sppUpdateResponse");
- if (spp_node == NULL)
- return NULL;
-
- xml_node_add_attr(ctx->xml, spp_node, ns, "sppVersion", "1.0");
- xml_node_add_attr(ctx->xml, spp_node, ns, "sessionID", session_id);
- xml_node_add_attr(ctx->xml, spp_node, ns, "sppStatus", spp_status);
-
- if (error_code) {
- node = xml_node_create(ctx->xml, spp_node, ns, "sppError");
- if (node)
- xml_node_add_attr(ctx->xml, node, NULL, "errorCode",
- error_code);
- }
-
- return spp_node;
-}
-
-
-static int hs20_spp_update_response(struct hs20_osu_client *ctx,
- const char *session_id,
- const char *spp_status,
- const char *error_code)
-{
- xml_node_t *node, *ret_node;
- int ret;
-
- write_summary(ctx, "Building sppUpdateResponse sppStatus='%s' error_code='%s'",
- spp_status, error_code);
- node = build_spp_update_response(ctx, session_id, spp_status,
- error_code);
- if (node == NULL)
- return -1;
- ret_node = soap_send_receive(ctx->http, node);
- if (!ret_node) {
- if (soap_reinit_client(ctx->http) < 0)
- return -1;
- wpa_printf(MSG_INFO, "Try to finish with re-opened connection");
- node = build_spp_update_response(ctx, session_id, spp_status,
- error_code);
- if (node == NULL)
- return -1;
- ret_node = soap_send_receive(ctx->http, node);
- if (ret_node == NULL)
- return -1;
- wpa_printf(MSG_INFO, "Continue with new connection");
- }
-
- if (hs20_spp_validate(ctx, ret_node, "sppExchangeComplete") < 0) {
- wpa_printf(MSG_INFO, "SPP validation failed");
- xml_node_free(ctx->xml, ret_node);
- return -1;
- }
-
- ret = process_spp_exchange_complete(ctx, ret_node);
- xml_node_free(ctx->xml, ret_node);
- return ret;
-}
-
-
-void spp_pol_upd(struct hs20_osu_client *ctx, const char *address,
- const char *pps_fname,
- const char *client_cert, const char *client_key,
- const char *cred_username, const char *cred_password,
- xml_node_t *pps)
-{
- wpa_printf(MSG_INFO, "SPP policy update");
- write_summary(ctx, "SPP policy update");
-
- os_free(ctx->server_url);
- ctx->server_url = os_strdup(address);
-
- if (soap_init_client(ctx->http, address, ctx->ca_fname, cred_username,
- cred_password, client_cert, client_key) == 0) {
- spp_post_dev_data(ctx, SPP_POLICY_UPDATE, "Policy update",
- pps_fname, pps);
- }
-}
-
-
-int cmd_prov(struct hs20_osu_client *ctx, const char *url)
-{
- unlink("Cert/est_cert.der");
- unlink("Cert/est_cert.pem");
-
- if (url == NULL) {
- wpa_printf(MSG_INFO, "Invalid prov command (missing URL)");
- return -1;
- }
-
- wpa_printf(MSG_INFO,
- "Credential provisioning requested - URL: %s ca_fname: %s",
- url, ctx->ca_fname ? ctx->ca_fname : "N/A");
-
- os_free(ctx->server_url);
- ctx->server_url = os_strdup(url);
-
- if (soap_init_client(ctx->http, url, ctx->ca_fname, NULL, NULL, NULL,
- NULL) < 0)
- return -1;
- spp_post_dev_data(ctx, SPP_SUBSCRIPTION_REGISTRATION,
- "Subscription registration", NULL, NULL);
-
- return ctx->pps_cred_set ? 0 : -1;
-}
-
-
-int cmd_sim_prov(struct hs20_osu_client *ctx, const char *url)
-{
- if (url == NULL) {
- wpa_printf(MSG_INFO, "Invalid prov command (missing URL)");
- return -1;
- }
-
- wpa_printf(MSG_INFO, "SIM provisioning requested");
-
- os_free(ctx->server_url);
- ctx->server_url = os_strdup(url);
-
- wpa_printf(MSG_INFO, "Wait for IP address before starting SIM provisioning");
-
- if (wait_ip_addr(ctx->ifname, 15) < 0) {
- wpa_printf(MSG_INFO, "Could not get IP address for WLAN - try connection anyway");
- }
-
- if (soap_init_client(ctx->http, url, ctx->ca_fname, NULL, NULL, NULL,
- NULL) < 0)
- return -1;
- spp_post_dev_data(ctx, SPP_SUBSCRIPTION_REGISTRATION,
- "Subscription provisioning", NULL, NULL);
-
- return ctx->pps_cred_set ? 0 : -1;
-}