[PATCH 6/9] hs20: Bind curl, including dns, to wlan interface.
[email protected] Fri, 7 Feb 2020 01:10:14 -0800
Newsgroups
gmane.linux.drivers.hostap
Message-ID
<[email protected] >
From: Ben Greear <[email protected] >
This lets things work better on multi-homed networks.
Signed-off-by: Ben Greear <[email protected] >
---
hs20/client/est.c | 15 ++++--
hs20/client/oma_dm_client.c | 7 ++-
hs20/client/osu_client.c | 43 +++++++++++++--
hs20/client/osu_client.h | 4 ++
hs20/client/spp_client.c | 18 +++++--
src/utils/http-utils.h | 12 +++--
src/utils/http_curl.c | 103 ++++++++++++++++++++++++++++++++----
7 files changed, 179 insertions(+), 23 deletions(-)
diff --git a/hs20/client/est.c b/hs20/client/est.c
index 97f913210..f93a15696 100644
--- a/hs20/client/est.c
+++ b/hs20/client/est.c
@@ -137,7 +137,10 @@ int est_load_cacerts(struct hs20_osu_client *ctx, const char *url)
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);
+ ctx->ca_fname,
+ ctx->do_bind_iface ? ctx->ifname : NULL,
+ ctx->dns);
+
http_ocsp_set(ctx->http,
(ctx->workarounds & WORKAROUND_OCSP_OPTIONAL) ? 1 : 2);
ctx->no_osu_cert_validation = 0;
@@ -619,7 +622,10 @@ int est_build_csr(struct hs20_osu_client *ctx, const char *url)
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);
+ ctx->ca_fname,
+ ctx->do_bind_iface ? ctx->ifname : NULL,
+ ctx->dns);
+
http_ocsp_set(ctx->http,
(ctx->workarounds & WORKAROUND_OCSP_OPTIONAL) ? 1 : 2);
ctx->no_osu_cert_validation = 0;
@@ -721,7 +727,10 @@ int est_simple_enroll(struct hs20_osu_client *ctx, const char *url,
resp = http_post(ctx->http, buf, req, "application/pkcs10",
"Content-Transfer-Encoding: base64",
ctx->ca_fname, user, pw, client_cert, client_key,
- &resp_len);
+ &resp_len,
+ ctx->do_bind_iface ? ctx->ifname : NULL,
+ ctx->dns);
+
http_ocsp_set(ctx->http,
(ctx->workarounds & WORKAROUND_OCSP_OPTIONAL) ? 1 : 2);
ctx->no_osu_cert_validation = 0;
diff --git a/hs20/client/oma_dm_client.c b/hs20/client/oma_dm_client.c
index d75c84562..78b46341a 100644
--- a/hs20/client/oma_dm_client.c
+++ b/hs20/client/oma_dm_client.c
@@ -957,7 +957,10 @@ static xml_node_t * oma_dm_send_recv(struct hs20_osu_client *ctx,
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);
+ client_cert, client_key, NULL,
+ ctx->do_bind_iface ? ctx->ifname : NULL,
+ ctx->dns);
+
os_free(str);
os_free(resp_uri);
resp_uri = NULL;
@@ -1210,6 +1213,8 @@ int cmd_oma_dm_sim_prov(struct hs20_osu_client *ctx, const char *url)
}
write_summary(ctx, "OMA-DM SIM provisioning");
+ check_dns_file(ctx);
+
msgid++;
syncml = build_oma_dm_1_sub_prov(ctx, url, msgid);
if (syncml == NULL)
diff --git a/hs20/client/osu_client.c b/hs20/client/osu_client.c
index 9f9c307b6..ff6e5b1c0 100644
--- a/hs20/client/osu_client.c
+++ b/hs20/client/osu_client.c
@@ -339,7 +339,10 @@ static int download_cert(struct hs20_osu_client *ctx, xml_node_t *params,
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);
+ res = http_download_file(ctx->http, url, TMP_CERT_DL_FILE, NULL,
+ ctx->do_bind_iface ? ctx->ifname : NULL,
+ ctx->dns);
+
http_ocsp_set(ctx->http,
(ctx->workarounds & WORKAROUND_OCSP_OPTIONAL) ? 1 : 2);
ctx->no_osu_cert_validation = 0;
@@ -2151,6 +2154,33 @@ static struct osu_data * parse_osu_providers(const char *fname, size_t *count)
return osu;
}
+void check_dns_file(struct hs20_osu_client* ctx)
+{
+ /* Look for DNS servers in case user specified a place to look. */
+ if (ctx->dns_file) {
+ FILE *f;
+ char buf[100];
+
+ f = fopen(ctx->dns_file, "r");
+ if (f) {
+ if (fgets(buf, sizeof(buf), f)) {
+ wpa_printf(MSG_DEBUG, "Checking DNS file: %s contents: %s",
+ ctx->dns_file, buf);
+ if (strncmp(buf, "DNS:", 4) == 0) {
+ /* remove ending whitespace */
+ int len = strlen(buf);
+ if ((buf[len - 2] == '\n') || (buf[len - 2] == '\r'))
+ buf[len - 2] = 0;
+ else if ((buf[len - 1] == '\n') || (buf[len - 1] == '\r'))
+ buf[len - 1] = 0;
+ http_bind_dns(ctx->http, NULL, buf + 4);
+ ctx->dns = strdup(buf + 4);
+ }
+ }
+ fclose(f);
+ }
+ }
+}
static int osu_connect(struct hs20_osu_client *ctx, const char *bssid,
const char *ssid, const char *ssid2, const char *url,
@@ -2255,6 +2285,8 @@ static int osu_connect(struct hs20_osu_client *ctx, const char *bssid,
wpa_printf(MSG_INFO, "Could not get IP address for WLAN - try connection anyway");
}
+ check_dns_file(ctx);
+
if (no_prod_assoc) {
if (res < 0)
return -1;
@@ -2698,6 +2730,7 @@ static int cmd_sub_rem(struct hs20_osu_client *ctx, const char *address,
if (wait_ip_addr(ctx->ifname, 15) < 0) {
wpa_printf(MSG_INFO, "Could not get IP address for WLAN - try connection anyway");
}
+ check_dns_file(ctx);
if (spp)
spp_sub_rem(ctx, address, pps_fname,
@@ -3162,7 +3195,7 @@ static void check_workarounds(struct hs20_osu_client *ctx)
static void usage(void)
{
printf("usage: hs20-osu-client [-dddqqKt] [-S<station ifname>] \\\n"
- " [-w<wpa_supplicant ctrl_iface dir>] "
+ " [-w<wpa_supplicant ctrl_iface dir>] [-D<dns-file-name>] "
"[-r<result file>] [-f<debug file>] \\\n"
" [-s<summary file>] \\\n"
" [-x<spp.xsd file name>] \\\n"
@@ -3207,7 +3240,7 @@ int main(int argc, char *argv[])
return -1;
for (;;) {
- c = getopt(argc, argv, "df:hKNo:O:qr:s:S:tw:x:");
+ c = getopt(argc, argv, "dD:f:hKNo:O:qr:s:S:tw:x:");
if (c < 0)
break;
switch (c) {
@@ -3215,6 +3248,9 @@ int main(int argc, char *argv[])
if (wpa_debug_level > 0)
wpa_debug_level--;
break;
+ case 'D':
+ ctx.dns_file = optarg;
+ break;
case 'f':
wpa_debug_file_path = optarg;
break;
@@ -3241,6 +3277,7 @@ int main(int argc, char *argv[])
break;
case 'S':
ctx.ifname = optarg;
+ ctx.do_bind_iface = 1;
break;
case 't':
wpa_debug_timestamp++;
diff --git a/hs20/client/osu_client.h b/hs20/client/osu_client.h
index 5c8e6d00b..9b1082278 100644
--- a/hs20/client/osu_client.h
+++ b/hs20/client/osu_client.h
@@ -34,6 +34,9 @@ struct hs20_osu_client {
const char *summary_file;
const char *ifname;
const char *ca_fname;
+ const char *dns_file;
+ const char* dns;
+ int do_bind_iface;
int no_osu_cert_validation; /* for EST operations */
char *fqdn;
char *server_url;
@@ -71,6 +74,7 @@ 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);
+void check_dns_file(struct hs20_osu_client* ctx);
/* spp_client.c */
diff --git a/hs20/client/spp_client.c b/hs20/client/spp_client.c
index c619541ae..4144189b0 100644
--- a/hs20/client/spp_client.c
+++ b/hs20/client/spp_client.c
@@ -796,7 +796,9 @@ void spp_sub_rem(struct hs20_osu_client *ctx, const char *address,
if (soap_init_client(ctx->http, address, ctx->ca_fname,
cred_username, cred_password, client_cert,
- client_key) == 0) {
+ client_key,
+ ctx->do_bind_iface ? ctx->ifname : NULL,
+ ctx->dns) == 0) {
spp_post_dev_data(ctx, SPP_SUBSCRIPTION_REMEDIATION,
"Subscription remediation", pps_fname, pps);
}
@@ -942,7 +944,9 @@ void spp_pol_upd(struct hs20_osu_client *ctx, const char *address,
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) {
+ cred_password, client_cert, client_key,
+ ctx->do_bind_iface ? ctx->ifname : NULL,
+ ctx->dns) == 0) {
spp_post_dev_data(ctx, SPP_POLICY_UPDATE, "Policy update",
pps_fname, pps);
}
@@ -967,7 +971,9 @@ int cmd_prov(struct hs20_osu_client *ctx, const char *url)
ctx->server_url = os_strdup(url);
if (soap_init_client(ctx->http, url, ctx->ca_fname, NULL, NULL, NULL,
- NULL) < 0)
+ NULL,
+ ctx->do_bind_iface ? ctx->ifname : NULL,
+ ctx->dns) < 0)
return -1;
spp_post_dev_data(ctx, SPP_SUBSCRIPTION_REGISTRATION,
"Subscription registration", NULL, NULL);
@@ -994,8 +1000,12 @@ int cmd_sim_prov(struct hs20_osu_client *ctx, const char *url)
wpa_printf(MSG_INFO, "Could not get IP address for WLAN - try connection anyway");
}
+ check_dns_file(ctx);
+
if (soap_init_client(ctx->http, url, ctx->ca_fname, NULL, NULL, NULL,
- NULL) < 0)
+ NULL,
+ ctx->do_bind_iface ? ctx->ifname : NULL,
+ ctx->dns) < 0)
return -1;
spp_post_dev_data(ctx, SPP_SUBSCRIPTION_REGISTRATION,
"Subscription provisioning", NULL, NULL);
diff --git a/src/utils/http-utils.h b/src/utils/http-utils.h
index 8d4399a37..277ac3485 100644
--- a/src/utils/http-utils.h
+++ b/src/utils/http-utils.h
@@ -38,22 +38,28 @@ struct http_cert {
int soap_init_client(struct http_ctx *ctx, const char *address,
const char *ca_fname, const char *username,
const char *password, const char *client_cert,
- const char *client_key);
+ const char *client_key, const char* ifname, const char* dns);
int soap_reinit_client(struct http_ctx *ctx);
xml_node_t * soap_send_receive(struct http_ctx *ctx, xml_node_t *node);
+/* Bind curl to an interface. */
+int http_bind_iface(struct http_ctx *ctx, void* curl, const char* ifname);
+/* Tell curl's resolver (in case it is using one) to use a specific DNS server. */
+int http_bind_dns(struct http_ctx *ctx, void* curl, const char* dns);
+
struct http_ctx * http_init_ctx(void *upper_ctx, struct xml_node_ctx *xml_ctx);
void http_ocsp_set(struct http_ctx *ctx, int val);
void http_deinit_ctx(struct http_ctx *ctx);
int http_download_file(struct http_ctx *ctx, const char *url,
- const char *fname, const char *ca_fname);
+ const char *fname, const char *ca_fname,
+ const char* ifname, const char* dns);
char * http_post(struct http_ctx *ctx, const char *url, const char *data,
const char *content_type, const char *ext_hdr,
const char *ca_fname,
const char *username, const char *password,
const char *client_cert, const char *client_key,
- size_t *resp_len);
+ size_t *resp_len, const char* ifname, const char* dns);
void http_set_cert_cb(struct http_ctx *ctx,
int (*cb)(void *ctx, struct http_cert *cert),
void *cb_ctx);
diff --git a/src/utils/http_curl.c b/src/utils/http_curl.c
index e62fbf96b..967eace09 100644
--- a/src/utils/http_curl.c
+++ b/src/utils/http_curl.c
@@ -50,8 +50,11 @@ struct http_ctx {
char *svc_password;
char *svc_client_cert;
char *svc_client_key;
+ char *ifname;
+ char *dns;
char *curl_buf;
size_t curl_buf_len;
+ char curl_err_buffer[CURL_ERROR_SIZE + 1];
int (*cert_cb)(void *ctx, struct http_cert *cert);
void *cert_cb_ctx;
@@ -1348,7 +1351,7 @@ static CURLcode curl_cb_ssl(CURL *curl, void *sslctx, void *parm)
static CURL * setup_curl_post(struct http_ctx *ctx, const char *address,
const char *ca_fname, const char *username,
const char *password, const char *client_cert,
- const char *client_key)
+ const char *client_key, const char* ifname, const char* dns)
{
CURL *curl;
#ifdef EAP_TLS_OPENSSL
@@ -1364,6 +1367,15 @@ static CURL * setup_curl_post(struct http_ctx *ctx, const char *address,
if (curl == NULL)
return NULL;
+ ctx->curl_err_buffer[0] = 0;
+ curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, ctx->curl_err_buffer);
+
+ if (ifname)
+ http_bind_iface(ctx, curl, ifname);
+
+ if (dns)
+ http_bind_dns(ctx, curl, dns);
+
curl_easy_setopt(curl, CURLOPT_URL, address);
curl_easy_setopt(curl, CURLOPT_POST, 1L);
if (ca_fname) {
@@ -1398,6 +1410,7 @@ static CURL * setup_curl_post(struct http_ctx *ctx, const char *address,
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curl_cb_write);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, ctx);
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
+
if (username) {
curl_easy_setopt(curl, CURLOPT_HTTPAUTH, CURLAUTH_ANYSAFE);
curl_easy_setopt(curl, CURLOPT_USERNAME, username);
@@ -1407,11 +1420,67 @@ static CURL * setup_curl_post(struct http_ctx *ctx, const char *address,
return curl;
}
+int http_bind_iface(struct http_ctx *ctx, void* _curl, const char* ifname)
+{
+ int rv;
+ CURL* curl = _curl;
+
+ if (!curl)
+ curl = ctx->curl;
+
+ if (ctx->ifname)
+ os_free(ctx->ifname);
+ ctx->ifname = NULL;
+ clone_str(&ctx->ifname, ifname);
+
+ /* Bind DNS resolver to the local interface, if curl is using such. */
+ rv = curl_easy_setopt(curl, CURLOPT_DNS_INTERFACE, ifname);
+ if (rv != CURLE_OK) {
+ wpa_printf(MSG_ERROR, "Failed CURLOPT_DNS_INTERFACE, curl: %p ifname: %s error: %s rv: %d (%s)\n",
+ curl, ifname, ctx->curl_err_buffer, rv, curl_easy_strerror(rv));
+ }
+ else {
+ wpa_printf(MSG_DEBUG, "Bound curl DNS to interface: %s\n", ifname);
+ }
+ rv = curl_easy_setopt(curl, CURLOPT_INTERFACE, ifname);
+ if (rv != CURLE_OK) {
+ wpa_printf(MSG_ERROR, "Failed CURLOPT_INTERFACE, curl: %p ifname: %s error: %s, rv: %d(%s)\n",
+ curl, ifname, ctx->curl_err_buffer, rv, curl_easy_strerror(rv));
+ }
+ else {
+ wpa_printf(MSG_DEBUG, "Bound curl to interface: %s\n", ifname);
+ }
+ return rv;
+}
+
+int http_bind_dns(struct http_ctx *ctx, void* _curl, const char* dns)
+{
+ CURL* curl = _curl;
+ int rv;
+
+ if (!curl)
+ curl = ctx->curl;
+
+ if (ctx->dns)
+ os_free(ctx->dns);
+ ctx->dns = NULL;
+ clone_str(&ctx->dns, dns);
+
+ rv = curl_easy_setopt(curl, CURLOPT_DNS_SERVERS, dns);
+ if (rv != CURLE_OK) {
+ wpa_printf(MSG_ERROR, "Failed CURLOPT_DNS_SERVERS, curl: %p dns: %s error: %s rv: %d(%s)\n",
+ curl, dns, ctx->curl_err_buffer, rv, curl_easy_strerror(rv));
+ }
+ else {
+ wpa_printf(MSG_DEBUG, "Bound curl DNS servers: %s\n", dns);
+ }
+ return rv;
+}
static int post_init_client(struct http_ctx *ctx, const char *address,
const char *ca_fname, const char *username,
const char *password, const char *client_cert,
- const char *client_key)
+ const char *client_key, const char* ifname, const char* dns)
{
char *pos;
int count;
@@ -1422,6 +1491,8 @@ static int post_init_client(struct http_ctx *ctx, const char *address,
clone_str(&ctx->svc_password, password);
clone_str(&ctx->svc_client_cert, client_cert);
clone_str(&ctx->svc_client_key, client_key);
+ clone_str(&ctx->ifname, ifname);
+ clone_str(&ctx->dns, dns);
/*
* Workaround for Apache "Hostname 'FOO' provided via SNI and hostname
@@ -1435,7 +1506,7 @@ static int post_init_client(struct http_ctx *ctx, const char *address,
}
ctx->curl = setup_curl_post(ctx, ctx->svc_address, ca_fname, username,
- password, client_cert, client_key);
+ password, client_cert, client_key, ifname, dns);
if (ctx->curl == NULL)
return -1;
@@ -1446,10 +1517,10 @@ static int post_init_client(struct http_ctx *ctx, const char *address,
int soap_init_client(struct http_ctx *ctx, const char *address,
const char *ca_fname, const char *username,
const char *password, const char *client_cert,
- const char *client_key)
+ const char *client_key, const char* ifname, const char* dns)
{
if (post_init_client(ctx, address, ca_fname, username, password,
- client_cert, client_key) < 0)
+ client_cert, client_key, ifname, dns) < 0)
return -1;
ctx->curl_hdr = curl_slist_append(ctx->curl_hdr,
@@ -1470,6 +1541,8 @@ int soap_reinit_client(struct http_ctx *ctx)
char *password = NULL;
char *client_cert = NULL;
char *client_key = NULL;
+ char *ifname = NULL;
+ char *dns = NULL;
int ret;
clear_curl(ctx);
@@ -1480,9 +1553,11 @@ int soap_reinit_client(struct http_ctx *ctx)
clone_str(&password, ctx->svc_password);
clone_str(&client_cert, ctx->svc_client_cert);
clone_str(&client_key, ctx->svc_client_key);
+ clone_str(&ifname, ctx->ifname);
+ clone_str(&dns, ctx->dns);
ret = soap_init_client(ctx, address, ca_fname, username, password,
- client_cert, client_key);
+ client_cert, client_key, ifname, dns);
os_free(address);
os_free(ca_fname);
str_clear_free(username);
@@ -1614,7 +1689,8 @@ void http_deinit_ctx(struct http_ctx *ctx)
int http_download_file(struct http_ctx *ctx, const char *url,
- const char *fname, const char *ca_fname)
+ const char *fname, const char *ca_fname,
+ const char* ifname, const char* dns)
{
CURL *curl;
FILE *f;
@@ -1629,6 +1705,15 @@ int http_download_file(struct http_ctx *ctx, const char *url,
if (curl == NULL)
return -1;
+ ctx->curl_err_buffer[0] = 0;
+ curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, ctx->curl_err_buffer);
+
+ if (ifname)
+ http_bind_iface(ctx, curl, ifname);
+
+ if (dns)
+ http_bind_dns(ctx, curl, dns);
+
f = fopen(fname, "wb");
if (f == NULL) {
curl_easy_cleanup(curl);
@@ -1682,7 +1767,7 @@ char * http_post(struct http_ctx *ctx, const char *url, const char *data,
const char *ca_fname,
const char *username, const char *password,
const char *client_cert, const char *client_key,
- size_t *resp_len)
+ size_t *resp_len, const char* ifname, const char* dns)
{
long http = 0;
CURLcode res;
@@ -1693,7 +1778,7 @@ char * http_post(struct http_ctx *ctx, const char *url, const char *data,
ctx->last_err = NULL;
wpa_printf(MSG_DEBUG, "curl: HTTP POST to %s", url);
curl = setup_curl_post(ctx, url, ca_fname, username, password,
- client_cert, client_key);
+ client_cert, client_key, ifname, dns);
if (curl == NULL)
return NULL;
--
2.20.1