summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorDaniel Stenberg <daniel@haxx.se>2020-11-02 23:17:01 +0100
committerDaniel Stenberg <daniel@haxx.se>2020-11-03 16:08:48 +0100
commit2cfc4ed98347047249b8f7f91ad513a4b0b84e45 (patch)
tree6e69f05aeeb76db0a7f15f9c8314f1e983ed42e8 /lib
parent7385610d0c74c6a254fea5e4cd6e1d559d848c8c (diff)
downloadcurl-2cfc4ed98347047249b8f7f91ad513a4b0b84e45.tar.gz
hsts: add read/write callbacks
- read/write callback options - man pages for the 4 new setopts - test 1915 verifies the callbacks Closes #5896
Diffstat (limited to 'lib')
-rw-r--r--lib/curl_get_line.c2
-rw-r--r--lib/easy.c4
-rw-r--r--lib/easyoptions.c6
-rw-r--r--lib/hsts.c109
-rw-r--r--lib/hsts.h7
-rw-r--r--lib/setopt.c14
-rw-r--r--lib/transfer.c2
-rw-r--r--lib/url.c1
-rw-r--r--lib/urldata.h7
9 files changed, 139 insertions, 13 deletions
diff --git a/lib/curl_get_line.c b/lib/curl_get_line.c
index aa524d8fe..135217c57 100644
--- a/lib/curl_get_line.c
+++ b/lib/curl_get_line.c
@@ -22,7 +22,7 @@
#include "curl_setup.h"
-#if !defined(CURL_DISABLE_COOKIES) || !defined(CURL_DISABLE_ALTSVC)) || \
+#if !defined(CURL_DISABLE_COOKIES) || !defined(CURL_DISABLE_ALTSVC) || \
defined(USE_HSTS)
#include "curl_get_line.h"
diff --git a/lib/easy.c b/lib/easy.c
index ca1117a46..4dc094603 100644
--- a/lib/easy.c
+++ b/lib/easy.c
@@ -888,7 +888,9 @@ struct Curl_easy *curl_easy_duphandle(struct Curl_easy *data)
if(!outcurl->hsts)
goto fail;
if(outcurl->set.str[STRING_HSTS])
- (void)Curl_hsts_load(outcurl->hsts, outcurl->set.str[STRING_HSTS]);
+ (void)Curl_hsts_loadfile(outcurl,
+ outcurl->hsts, outcurl->set.str[STRING_HSTS]);
+ (void)Curl_hsts_loadcb(outcurl, outcurl->hsts);
}
#endif
/* Clone the resolver handle, if present, for the new handle */
diff --git a/lib/easyoptions.c b/lib/easyoptions.c
index e5b9ffb70..8a4aaee26 100644
--- a/lib/easyoptions.c
+++ b/lib/easyoptions.c
@@ -116,6 +116,10 @@ struct curl_easyoption Curl_easyopts[] = {
{"HEADERFUNCTION", CURLOPT_HEADERFUNCTION, CURLOT_FUNCTION, 0},
{"HEADEROPT", CURLOPT_HEADEROPT, CURLOT_VALUES, 0},
{"HSTS", CURLOPT_HSTS, CURLOT_STRING, 0},
+ {"HSTSREADDATA", CURLOPT_HSTSREADDATA, CURLOT_CBPTR, 0},
+ {"HSTSREADFUNCTION", CURLOPT_HSTSREADFUNCTION, CURLOT_FUNCTION, 0},
+ {"HSTSWRITEDATA", CURLOPT_HSTSWRITEDATA, CURLOT_CBPTR, 0},
+ {"HSTSWRITEFUNCTION", CURLOPT_HSTSWRITEFUNCTION, CURLOT_FUNCTION, 0},
{"HSTS_CTRL", CURLOPT_HSTS_CTRL, CURLOT_LONG, 0},
{"HTTP09_ALLOWED", CURLOPT_HTTP09_ALLOWED, CURLOT_LONG, 0},
{"HTTP200ALIASES", CURLOPT_HTTP200ALIASES, CURLOT_SLIST, 0},
@@ -344,6 +348,6 @@ struct curl_easyoption Curl_easyopts[] = {
*/
int Curl_easyopts_check(void)
{
- return (CURLOPT_LASTENTRY != (300 + 1));
+ return (CURLOPT_LASTENTRY != (304 + 1));
}
#endif
diff --git a/lib/hsts.c b/lib/hsts.c
index 7eb3cda03..53b6d202b 100644
--- a/lib/hsts.c
+++ b/lib/hsts.c
@@ -262,6 +262,37 @@ struct stsentry *Curl_hsts(struct hsts *h, const char *hostname,
}
/*
+ * Send this HSTS entry to the write callback.
+ */
+static CURLcode hsts_push(struct Curl_easy *data,
+ struct curl_index *i,
+ struct stsentry *sts,
+ bool *stop)
+{
+ struct curl_hstsentry e;
+ CURLSTScode sc;
+ struct tm stamp;
+ CURLcode result;
+
+ e.name = (char *)sts->host;
+ e.namelen = strlen(sts->host);
+ e.includeSubDomains = sts->includeSubDomains;
+
+ result = Curl_gmtime(sts->expires, &stamp);
+ if(result)
+ return result;
+
+ msnprintf(e.expire, sizeof(e.expire), "%d%02d%02d %02d:%02d:%02d",
+ stamp.tm_year + 1900, stamp.tm_mon + 1, stamp.tm_mday,
+ stamp.tm_hour, stamp.tm_min, stamp.tm_sec);
+
+ sc = data->set.hsts_write(data, &e, i,
+ data->set.hsts_write_userp);
+ *stop = (sc != CURLSTS_OK);
+ return sc == CURLSTS_FAIL ? CURLE_BAD_FUNCTION_ARGUMENT : CURLE_OK;
+}
+
+/*
* Write this single hsts entry to a single output line
*/
static CURLcode hsts_out(struct stsentry *sts, FILE *fp)
@@ -280,7 +311,7 @@ static CURLcode hsts_out(struct stsentry *sts, FILE *fp)
/*
- * Curl_https_save() writes the HSTS cache to a file.
+ * Curl_https_save() writes the HSTS cache to file and callback.
*/
CURLcode Curl_hsts_save(struct Curl_easy *data, struct hsts *h,
const char *file)
@@ -302,7 +333,7 @@ CURLcode Curl_hsts_save(struct Curl_easy *data, struct hsts *h,
if((h->flags & CURLHSTS_READONLYFILE) || !file || !file[0])
/* marked as read-only, no file or zero length file name */
- return CURLE_OK;
+ goto skipsave;
if(Curl_rand_hex(data, randsuffix, sizeof(randsuffix)))
return CURLE_FAILED_INIT;
@@ -333,6 +364,22 @@ CURLcode Curl_hsts_save(struct Curl_easy *data, struct hsts *h,
unlink(tempstore);
}
free(tempstore);
+ skipsave:
+ if(data->set.hsts_write) {
+ /* if there's a write callback */
+ struct curl_index i; /* count */
+ i.total = h->list.size;
+ i.index = 0;
+ for(e = h->list.head; e; e = n) {
+ struct stsentry *sts = e->ptr;
+ bool stop;
+ n = e->next;
+ result = hsts_push(data, &i, sts, &stop);
+ if(result || stop)
+ break;
+ i.index++;
+ }
+ }
return result;
}
@@ -368,6 +415,46 @@ static CURLcode hsts_add(struct hsts *h, char *line)
}
/*
+ * Load HSTS data from callback.
+ *
+ */
+static CURLcode hsts_pull(struct Curl_easy *data, struct hsts *h)
+{
+ /* if the HSTS read callback is set, use it */
+ if(data->set.hsts_read) {
+ CURLSTScode sc;
+ DEBUGASSERT(h);
+ do {
+ char buffer[257];
+ struct curl_hstsentry e;
+ e.name = buffer;
+ e.namelen = sizeof(buffer)-1;
+ e.includeSubDomains = FALSE; /* default */
+ e.expire[0] = 0;
+ e.name[0] = 0; /* just to make it clean */
+ sc = data->set.hsts_read(data, &e, data->set.hsts_read_userp);
+ if(sc == CURLSTS_OK) {
+ time_t expires;
+ CURLcode result;
+ if(!e.name[0])
+ /* bail out if no name was stored */
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+ if(e.expire[0])
+ expires = Curl_getdate_capped(e.expire);
+ else
+ expires = TIME_T_MAX; /* the end of time */
+ result = hsts_create(h, e.name, e.includeSubDomains, expires);
+ if(result)
+ return result;
+ }
+ else if(sc == CURLSTS_FAIL)
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+ } while(sc == CURLSTS_OK);
+ }
+ return CURLE_OK;
+}
+
+/*
* Load the HSTS cache from the given file. The text based line-oriented file
* format is documented here:
* https://github.com/curl/curl/wiki/HSTS
@@ -417,14 +504,22 @@ static CURLcode hsts_load(struct hsts *h, const char *file)
}
/*
- * Curl_hsts_load() loads HSTS from file.
+ * Curl_hsts_loadfile() loads HSTS from file
*/
-CURLcode Curl_hsts_load(struct hsts *h, const char *file)
+CURLcode Curl_hsts_loadfile(struct Curl_easy *data,
+ struct hsts *h, const char *file)
{
- CURLcode result;
DEBUGASSERT(h);
- result = hsts_load(h, file);
- return result;
+ (void)data;
+ return hsts_load(h, file);
+}
+
+/*
+ * Curl_hsts_loadcb() loads HSTS from callback
+ */
+CURLcode Curl_hsts_loadcb(struct Curl_easy *data, struct hsts *h)
+{
+ return hsts_pull(data, h);
}
#endif /* CURL_DISABLE_HTTP || USE_HSTS */
diff --git a/lib/hsts.h b/lib/hsts.h
index 60b3c2df7..0028d8ee4 100644
--- a/lib/hsts.h
+++ b/lib/hsts.h
@@ -53,8 +53,13 @@ struct stsentry *Curl_hsts(struct hsts *h, const char *hostname,
bool subdomain);
CURLcode Curl_hsts_save(struct Curl_easy *data, struct hsts *h,
const char *file);
-CURLcode Curl_hsts_load(struct hsts *h, const char *file);
+CURLcode Curl_hsts_loadfile(struct Curl_easy *data,
+ struct hsts *h, const char *file);
+CURLcode Curl_hsts_loadcb(struct Curl_easy *data,
+ struct hsts *h);
#else
#define Curl_hsts_cleanup(x)
+#define Curl_hsts_loadcb(x,y)
+#define Curl_hsts_save(x,y,z)
#endif /* CURL_DISABLE_HTTP || USE_HSTS */
#endif /* HEADER_CURL_HSTS_H */
diff --git a/lib/setopt.c b/lib/setopt.c
index 4aa31bb39..7627557f5 100644
--- a/lib/setopt.c
+++ b/lib/setopt.c
@@ -2841,6 +2841,18 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
#endif
break;
#ifdef USE_HSTS
+ case CURLOPT_HSTSREADFUNCTION:
+ data->set.hsts_read = va_arg(param, curl_hstsread_callback);
+ break;
+ case CURLOPT_HSTSREADDATA:
+ data->set.hsts_read_userp = va_arg(param, void *);
+ break;
+ case CURLOPT_HSTSWRITEFUNCTION:
+ data->set.hsts_write = va_arg(param, curl_hstswrite_callback);
+ break;
+ case CURLOPT_HSTSWRITEDATA:
+ data->set.hsts_write_userp = va_arg(param, void *);
+ break;
case CURLOPT_HSTS:
if(!data->hsts) {
data->hsts = Curl_hsts_init();
@@ -2852,7 +2864,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
if(result)
return result;
if(argptr)
- (void)Curl_hsts_load(data->hsts, argptr);
+ (void)Curl_hsts_loadfile(data, data->hsts, argptr);
break;
case CURLOPT_HSTS_CTRL:
arg = va_arg(param, long);
diff --git a/lib/transfer.c b/lib/transfer.c
index 05a9e8ff6..267058d45 100644
--- a/lib/transfer.c
+++ b/lib/transfer.c
@@ -78,6 +78,7 @@
#include "mime.h"
#include "strcase.h"
#include "urlapi-int.h"
+#include "hsts.h"
/* The last 3 #include files should be in this order */
#include "curl_printf.h"
@@ -1528,6 +1529,7 @@ CURLcode Curl_pretransfer(struct Curl_easy *data)
}
#endif
Curl_http2_init_state(&data->state);
+ Curl_hsts_loadcb(data, data->hsts);
}
return result;
diff --git a/lib/url.c b/lib/url.c
index 1e3f025ad..3109b9637 100644
--- a/lib/url.c
+++ b/lib/url.c
@@ -412,6 +412,7 @@ CURLcode Curl_close(struct Curl_easy **datap)
Curl_flush_cookies(data, TRUE);
Curl_altsvc_save(data, data->asi, data->set.str[STRING_ALTSVC]);
Curl_altsvc_cleanup(&data->asi);
+ Curl_hsts_save(data, data->hsts, data->set.str[STRING_HSTS]);
Curl_hsts_cleanup(&data->hsts);
#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_CRYPTO_AUTH)
Curl_http_auth_cleanup_digest(data);
diff --git a/lib/urldata.h b/lib/urldata.h
index ea7060ec5..a0238c5aa 100644
--- a/lib/urldata.h
+++ b/lib/urldata.h
@@ -1639,7 +1639,12 @@ struct UserDefined {
curl_conv_callback convtonetwork;
/* function to convert from UTF-8 encoding: */
curl_conv_callback convfromutf8;
-
+#ifdef USE_HSTS
+ curl_hstsread_callback hsts_read;
+ void *hsts_read_userp;
+ curl_hstswrite_callback hsts_write;
+ void *hsts_write_userp;
+#endif
void *progress_client; /* pointer to pass to the progress callback */
void *ioctl_client; /* pointer to pass to the ioctl callback */
long timeout; /* in milliseconds, 0 means no timeout */