hsts: add read/write callbacks

- read/write callback options
- man pages for the 4 new setopts
- test 1915 verifies the callbacks

Closes #5896
This commit is contained in:
Daniel Stenberg 2020-11-02 23:17:01 +01:00
parent 7385610d0c
commit 2cfc4ed983
No known key found for this signature in database
GPG Key ID: 5CC908FDB71E12C2
27 changed files with 652 additions and 22 deletions

View File

@ -192,7 +192,7 @@ jobs:
- libbrotli-dev
- libzstd-dev
- env:
- T=debug C="--disable-alt-svc"
- T=debug C="--enable-hsts --disable-alt-svc"
- *clang
compiler: clang
addons:

View File

@ -323,6 +323,14 @@ Enable and configure Alt-Svc: treatment. See \fICURLOPT_ALTSVC_CTRL(3)\fP
Set HSTS cache file. See \fICURLOPT_HSTS(3)\fP
.IP CURLOPT_HSTS_CTRL
Enable HSTS. See \fICURLOPT_HSTS_CTRL(3)\fP
.IP CURLOPT_HSTSREADFUNCTION
Set HSTS read callback. See \fICURLOPT_HSTSREADFUNCTION(3)\fP
.IP CURLOPT_HSTSREADDATA
Pass pointer to the HSTS read callback. See \fICURLOPT_HSTSREADDATA(3)\fP
.IP CURLOPT_HSTSWRITEFUNCTION
Set HSTS write callback. See \fICURLOPT_HSTSWRITEFUNCTION(3)\fP
.IP CURLOPT_HSTSWRITEDATA
Pass pointer to the HSTS write callback. See \fICURLOPT_HSTSWRITEDATA(3)\fP
.IP CURLOPT_HTTPGET
Do an HTTP GET request. See \fICURLOPT_HTTPGET(3)\fP
.IP CURLOPT_REQUEST_TARGET

View File

@ -0,0 +1,66 @@
.\" **************************************************************************
.\" * _ _ ____ _
.\" * Project ___| | | | _ \| |
.\" * / __| | | | |_) | |
.\" * | (__| |_| | _ <| |___
.\" * \___|\___/|_| \_\_____|
.\" *
.\" * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
.\" *
.\" * This software is licensed as described in the file COPYING, which
.\" * you should have received as part of this distribution. The terms
.\" * are also available at https://curl.haxx.se/docs/copyright.html.
.\" *
.\" * You may opt to use, copy, modify, merge, publish, distribute and/or sell
.\" * copies of the Software, and permit persons to whom the Software is
.\" * furnished to do so, under the terms of the COPYING file.
.\" *
.\" * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
.\" * KIND, either express or implied.
.\" *
.\" **************************************************************************
.\"
.TH CURLOPT_HSTSREADDATA 3 "14 Sep 2020" "libcurl 7.74.0" "curl_easy_setopt options"
.SH NAME
CURLOPT_HSTSREADDATA \- pointer passed to the HSTS read callback
.SH SYNOPSIS
#include <curl/curl.h>
CURLcode curl_easy_setopt(CURL *handle, CURLOPT_HSTSREADDATA, void *pointer);
.SH EXPERIMENTAL
Warning: this feature is early code and is marked as experimental. It can only
be enabled by explicitly telling configure with \fB--enable-hsts\fP. You are
advised to not ship this in production before the experimental label is
removed.
.SH DESCRIPTION
Data \fIpointer\fP to pass to the HSTS read function. If you use the
\fICURLOPT_HSTSREADFUNCTION(3)\fP option, this is the pointer you'll get as
input in the 3rd argument to the callback.
This option doesn't enable HSTS, you need to use \fICURLOPT_HSTS_CTRL(3)\fP to
do that.
.SH DEFAULT
NULL
.SH PROTOCOLS
This feature is only used for HTTP(S) transfer.
.SH EXAMPLE
.nf
CURL *curl = curl_easy_init();
struct MyData this;
if(curl) {
curl_easy_setopt(curl, CURLOPT_URL, "http://example.com");
/* pass pointer that gets passed in to the
CURLOPT_HSTSREADFUNCTION callback */
curl_easy_setopt(curl, CURLOPT_HSTSREADDATA, &this);
curl_easy_perform(curl);
}
.fi
.SH AVAILABILITY
Added in 7.74.0
.SH RETURN VALUE
This will return CURLE_OK.
.SH "SEE ALSO"
.BR CURLOPT_HSTSREADFUNCTION "(3), " CURLOPT_HSTSWRITEDATA "(3), "
.BR CURLOPT_HSTSWRITEFUNCTION "(3), "

View File

@ -0,0 +1,81 @@
.\" **************************************************************************
.\" * _ _ ____ _
.\" * Project ___| | | | _ \| |
.\" * / __| | | | |_) | |
.\" * | (__| |_| | _ <| |___
.\" * \___|\___/|_| \_\_____|
.\" *
.\" * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
.\" *
.\" * This software is licensed as described in the file COPYING, which
.\" * you should have received as part of this distribution. The terms
.\" * are also available at https://curl.haxx.se/docs/copyright.html.
.\" *
.\" * You may opt to use, copy, modify, merge, publish, distribute and/or sell
.\" * copies of the Software, and permit persons to whom the Software is
.\" * furnished to do so, under the terms of the COPYING file.
.\" *
.\" * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
.\" * KIND, either express or implied.
.\" *
.\" **************************************************************************
.\"
.TH CURLOPT_HSTSREADFUNCTION 3 "14 Sep 2020" "libcurl 7.74.0" "curl_easy_setopt options"
.SH NAME
CURLOPT_HSTSREADFUNCTION \- read callback for HSTS hosts
.SH SYNOPSIS
#include <curl/curl.h>
CURLSTScode hstsread(CURL *easy, struct curl_hstsentry *sts, void *userp);
CURLcode curl_easy_setopt(CURL *handle, CURLOPT_HSTSREADFUNCTION, hstsread);
.SH EXPERIMENTAL
Warning: this feature is early code and is marked as experimental. It can only
be enabled by explicitly telling configure with \fB--enable-hsts\fP. You are
advised to not ship this in production before the experimental label is
removed.
.SH DESCRIPTION
Pass a pointer to your callback function, as the prototype shows above.
This callback function gets called by libcurl repeatedly when it populates the
in-memory HSTS cache.
Set the \fIuserp\fP argument with the \fICURLOPT_HSTSREADDATA(3)\fP option or
it will be NULL.
When this callback is invoked, the \fIsts\fP pointer points to a populated
struct: Copy the host name to 'name' (no longer than 'namelen' bytes). Make it
null-terminated. Set 'includeSubDomains' to TRUE or FALSE. Set 'expire' to a
date stamp or a zero length string for *forever* (wrong date stamp format
might cause the name to not get accepted)
The callback should return \fICURLSTS_OK\fP if it returns a name and is
prepared to be called again (for another host) or \fICURLSTS_DONE\fP if it has
no entry to return. It can also return \fICURLSTS_FAIL\fP to signal error.
This option doesn't enable HSTS, you need to use \fICURLOPT_HSTS_CTRL(3)\fP to
do that.
.SH DEFAULT
NULL - no callback.
.SH PROTOCOLS
This feature is only used for HTTP(S) transfer.
.SH EXAMPLE
.nf
{
/* set HSTS read callback */
curl_easy_setopt(curl, CURLOPT_HSTSREADFUNCTION, hstsread);
/* pass in suitable argument to the callback */
curl_easy_setopt(curl, CURLOPT_HSTSREADDATA, &hstspreload[0]);
result = curl_easy_perform(curl);
}
.fi
.SH AVAILABILITY
Added in 7.74.0
.SH RETURN VALUE
This will return CURLE_OK.
.SH "SEE ALSO"
.BR CURLOPT_HSTSREADDATA "(3), " CURLOPT_HSTSWRITEFUNCTION "(3), "
.BR CURLOPT_HSTS "(3), " CURLOPT_HSTS_CTRL "(3), "

View File

@ -0,0 +1,66 @@
.\" **************************************************************************
.\" * _ _ ____ _
.\" * Project ___| | | | _ \| |
.\" * / __| | | | |_) | |
.\" * | (__| |_| | _ <| |___
.\" * \___|\___/|_| \_\_____|
.\" *
.\" * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
.\" *
.\" * This software is licensed as described in the file COPYING, which
.\" * you should have received as part of this distribution. The terms
.\" * are also available at https://curl.haxx.se/docs/copyright.html.
.\" *
.\" * You may opt to use, copy, modify, merge, publish, distribute and/or sell
.\" * copies of the Software, and permit persons to whom the Software is
.\" * furnished to do so, under the terms of the COPYING file.
.\" *
.\" * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
.\" * KIND, either express or implied.
.\" *
.\" **************************************************************************
.\"
.TH CURLOPT_HSTSWRITEDATA 3 "14 Sep 2020" "libcurl 7.74.0" "curl_easy_setopt options"
.SH NAME
CURLOPT_HSTSWRITEDATA \- pointer passed to the HSTS write callback
.SH SYNOPSIS
#include <curl/curl.h>
CURLcode curl_easy_setopt(CURL *handle, CURLOPT_HSTSWRITEDATA, void *pointer);
.SH EXPERIMENTAL
Warning: this feature is early code and is marked as experimental. It can only
be enabled by explicitly telling configure with \fB--enable-hsts\fP. You are
advised to not ship this in production before the experimental label is
removed.
.SH DESCRIPTION
Data \fIpointer\fP to pass to the HSTS write function. If you use the
\fICURLOPT_HSTSWRITEFUNCTION(3)\fP option, this is the pointer you'll get as
input in the 4th argument to the callback.
This option doesn't enable HSTS, you need to use \fICURLOPT_HSTS_CTRL(3)\fP to
do that.
.SH DEFAULT
NULL
.SH PROTOCOLS
This feature is only used for HTTP(S) transfer.
.SH EXAMPLE
.nf
CURL *curl = curl_easy_init();
struct MyData this;
if(curl) {
curl_easy_setopt(curl, CURLOPT_URL, "http://example.com");
/* pass pointer that gets passed in to the
CURLOPT_HSTSWRITEFUNCTION callback */
curl_easy_setopt(curl, CURLOPT_HSTSWRITEDATA, &this);
curl_easy_perform(curl);
}
.fi
.SH AVAILABILITY
Added in 7.74.0
.SH RETURN VALUE
This will return CURLE_OK.
.SH "SEE ALSO"
.BR CURLOPT_HSTSWRITEFUNCTION "(3), " CURLOPT_HSTSREADDATA "(3), "
.BR CURLOPT_HSTSREADFUNCTION "(3), "

View File

@ -0,0 +1,79 @@
.\" **************************************************************************
.\" * _ _ ____ _
.\" * Project ___| | | | _ \| |
.\" * / __| | | | |_) | |
.\" * | (__| |_| | _ <| |___
.\" * \___|\___/|_| \_\_____|
.\" *
.\" * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
.\" *
.\" * This software is licensed as described in the file COPYING, which
.\" * you should have received as part of this distribution. The terms
.\" * are also available at https://curl.haxx.se/docs/copyright.html.
.\" *
.\" * You may opt to use, copy, modify, merge, publish, distribute and/or sell
.\" * copies of the Software, and permit persons to whom the Software is
.\" * furnished to do so, under the terms of the COPYING file.
.\" *
.\" * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
.\" * KIND, either express or implied.
.\" *
.\" **************************************************************************
.\"
.TH CURLOPT_HSTSWRITEFUNCTION 3 "14 Sep 2020" "libcurl 7.74.0" "curl_easy_setopt options"
.SH NAME
CURLOPT_HSTSWRITEFUNCTION \- write callback for HSTS hosts
.SH SYNOPSIS
#include <curl/curl.h>
CURLSTScode hstswrite(CURL *easy, struct curl_hstsentry *sts,
struct curl_index *count, void *userp);
CURLcode curl_easy_setopt(CURL *handle, CURLOPT_HSTSWRITEFUNCTION, hstswrite);
.SH EXPERIMENTAL
Warning: this feature is early code and is marked as experimental. It can only
be enabled by explicitly telling configure with \fB--enable-hsts\fP. You are
advised to not ship this in production before the experimental label is
removed.
.SH DESCRIPTION
Pass a pointer to your callback function, as the prototype shows above.
This callback function gets called by libcurl repeatedly to allow the
application to store the in-memory HSTS cache when libcurl is about to discard
it.
Set the \fIuserp\fP argument with the \fICURLOPT_HSTSWRITEDATA(3)\fP option or
it will be NULL.
When the callback is invoked, the \fIsts\fP pointer points to a populated
struct: Read the host name to 'name' (it is 'namelen' bytes long and null
terminated. The 'includeSubDomains' field is non-zero if the entry matches
subdomains. The 'expire' string is a date stamp null-terminated string using
the syntax YYYYMMDD HH:MM:SS.
The callback should return \fICURLSTS_OK\fP if it succeeded and is prepared to
be called again (for another host) or \fICURLSTS_DONE\fP if there's nothing
more to do. It can also return \fICURLSTS_FAIL\fP to signal error.
.SH DEFAULT
NULL - no callback.
.SH PROTOCOLS
This feature is only used for HTTP(S) transfer.
.SH EXAMPLE
.nf
{
/* set HSTS read callback */
curl_easy_setopt(curl, CURLOPT_HSTSWRITEFUNCTION, hstswrite);
/* pass in suitable argument to the callback */
curl_easy_setopt(curl, CURLOPT_HSTSWRITEDATA, &hstspreload[0]);
result = curl_easy_perform(curl);
}
.fi
.SH AVAILABILITY
Added in 7.74.0
.SH RETURN VALUE
This will return CURLE_OK.
.SH "SEE ALSO"
.BR CURLOPT_HSTSWRITEDATA "(3), " CURLOPT_HSTSWRITEFUNCTION "(3), "
.BR CURLOPT_HSTS "(3), " CURLOPT_HSTS_CTRL "(3), "

View File

@ -67,6 +67,9 @@ transfer to become paused. See \fIcurl_easy_pause(3)\fP for further details.
Set this option to NULL to get the internal default function used instead of
your callback. The internal default function will write the data to the FILE *
given with \fICURLOPT_WRITEDATA(3)\fP.
This option doesn't enable HSTS, you need to use \fICURLOPT_HSTS_CTRL(3)\fP to
do that.
.SH DEFAULT
libcurl will use 'fwrite' as a callback by default.
.SH PROTOCOLS

View File

@ -181,6 +181,10 @@ man_MANS = \
CURLOPT_HEADERFUNCTION.3 \
CURLOPT_HEADEROPT.3 \
CURLOPT_HSTS.3 \
CURLOPT_HSTSREADDATA.3 \
CURLOPT_HSTSREADFUNCTION.3 \
CURLOPT_HSTSWRITEDATA.3 \
CURLOPT_HSTSWRITEFUNCTION.3 \
CURLOPT_HSTS_CTRL.3 \
CURLOPT_HTTP09_ALLOWED.3 \
CURLOPT_HTTP200ALIASES.3 \

View File

@ -447,6 +447,10 @@ CURLOPT_HEADERFUNCTION 7.7.2
CURLOPT_HEADEROPT 7.37.0
CURLOPT_HSTS 7.74.0
CURLOPT_HSTS_CTRL 7.74.0
CURLOPT_HSTSREADDATA 7.74.0
CURLOPT_HSTSREADFUNCTION 7.74.0
CURLOPT_HSTSWRITEDATA 7.74.0
CURLOPT_HSTSWRITEFUNCTION 7.74.0
CURLOPT_HTTP09_ALLOWED 7.64.0
CURLOPT_HTTP200ALIASES 7.10.3
CURLOPT_HTTPAUTH 7.10.6
@ -812,6 +816,9 @@ CURLSSLSET_NO_BACKENDS 7.56.0
CURLSSLSET_OK 7.56.0
CURLSSLSET_TOO_LATE 7.56.0
CURLSSLSET_UNKNOWN_BACKEND 7.56.0
CURLSTS_DONE 7.74.0
CURLSTS_FAIL 7.74.0
CURLSTS_OK 7.74.0
CURLUE_BAD_HANDLE 7.62.0
CURLUE_BAD_PARTPOINTER 7.62.0
CURLUE_BAD_PORT_NUMBER 7.62.0

View File

@ -954,6 +954,33 @@ typedef enum {
#define CURLALTSVC_H2 (1<<4)
#define CURLALTSVC_H3 (1<<5)
struct curl_hstsentry {
char *name;
size_t namelen;
unsigned int includeSubDomains:1;
char expire[18]; /* YYYYMMDD HH:MM:SS [null-terminated] */
};
struct curl_index {
size_t index; /* the provided entry's "index" or count */
size_t total; /* total number of entries to save */
};
typedef enum {
CURLSTS_OK,
CURLSTS_DONE,
CURLSTS_FAIL
} CURLSTScode;
typedef CURLSTScode (*curl_hstsread_callback)(CURL *easy,
struct curl_hstsentry *e,
void *userp);
typedef CURLSTScode (*curl_hstswrite_callback)(CURL *easy,
struct curl_hstsentry *e,
struct curl_index *i,
void *userp);
/* CURLHSTS_* are bits for the CURLOPT_HSTS option */
#define CURLHSTS_ENABLE (long)(1<<0)
#define CURLHSTS_READONLYFILE (long)(1<<1)
@ -2038,6 +2065,14 @@ typedef enum {
/* HSTS file name */
CURLOPT(CURLOPT_HSTS, CURLOPTTYPE_STRINGPOINT, 300),
/* HSTS read callback */
CURLOPT(CURLOPT_HSTSREADFUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 301),
CURLOPT(CURLOPT_HSTSREADDATA, CURLOPTTYPE_CBPOINT, 302),
/* HSTS write callback */
CURLOPT(CURLOPT_HSTSWRITEFUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 303),
CURLOPT(CURLOPT_HSTSWRITEDATA, CURLOPTTYPE_CBPOINT, 304),
CURLOPT_LASTENTRY /* the last unused */
} CURLoption;

View File

@ -273,6 +273,7 @@ CURLWARNING(_curl_easy_getinfo_err_curl_off_t,
(option) == CURLOPT_FTPPORT || \
(option) == CURLOPT_FTP_ACCOUNT || \
(option) == CURLOPT_FTP_ALTERNATIVE_TO_USER || \
(option) == CURLOPT_HSTS || \
(option) == CURLOPT_INTERFACE || \
(option) == CURLOPT_ISSUERCERT || \
(option) == CURLOPT_KEYPASSWD || \
@ -356,6 +357,8 @@ CURLWARNING(_curl_easy_getinfo_err_curl_off_t,
(option) == CURLOPT_DEBUGDATA || \
(option) == CURLOPT_FNMATCH_DATA || \
(option) == CURLOPT_HEADERDATA || \
(option) == CURLOPT_HSTSREADDATA || \
(option) == CURLOPT_HSTSWRITEDATA || \
(option) == CURLOPT_INTERLEAVEDATA || \
(option) == CURLOPT_IOCTLDATA || \
(option) == CURLOPT_OPENSOCKETDATA || \

View File

@ -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"

View File

@ -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 */

View File

@ -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

View File

@ -261,6 +261,37 @@ struct stsentry *Curl_hsts(struct hsts *h, const char *hostname,
return NULL; /* no match */
}
/*
* 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
*/
@ -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;
}
@ -367,6 +414,46 @@ static CURLcode hsts_add(struct hsts *h, char *line)
return CURLE_OK;
}
/*
* 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:
@ -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 */

View File

@ -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 */

View File

@ -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);

View File

@ -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;

View File

@ -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);

View File

@ -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 */

View File

@ -340,6 +340,7 @@ Features testable here are:
- `getrlimit`
- `GnuTLS`
- `GSS-API`
- `HSTS`
- `HTTP-auth`
- `http/2`
- `idn`

View File

@ -204,7 +204,7 @@ test1700 test1701 test1702 \
test1800 test1801 \
\
test1904 test1905 test1906 test1907 \
test1908 test1909 test1910 test1911 test1912 test1913 test1914 \
test1908 test1909 test1910 test1911 test1912 test1913 test1914 test1915 \
\
test2000 test2001 test2002 test2003 test2004 test2005 test2006 test2007 \
test2008 test2009 test2010 test2011 test2012 test2013 test2014 test2015 \

50
tests/data/test1915 Normal file
View File

@ -0,0 +1,50 @@
<testcase>
<info>
<keywords>
HTTP
HSTS
CURLOPT_HSTSREADFUNCTION
</keywords>
</info>
# Server-side
<reply>
</reply>
# Client-side
<client>
<features>
HSTS
http
</features>
<server>
none
</server>
# require debug so that alt-svc can work over plain old HTTP
<name>
HSTS read/write callbacks
</name>
<tool>
lib1915
</tool>
<command>
http://%HOSTIP:%NOLISTENPORT/not-there/1915
</command>
</client>
# Verify data after the test has been "shot"
<verify>
# it fails because there's nothing on that port
<errorcode>
7
</errorcode>
<stdout>
[0/4] 1.example.com 20300320 01:02:03
[1/4] 2.example.com 20300320 01:02:03
[2/4] 3.example.com 20300320 01:02:03
[3/4] 4.example.com 20300320 01:02:03
</stdout>
</verify>
</testcase>

View File

@ -59,6 +59,7 @@ noinst_PROGRAMS = chkhostname libauthretry libntlmconnect \
lib1558 lib1559 lib1560 lib1564 lib1565 lib1567 \
lib1591 lib1592 lib1593 lib1594 lib1596 \
lib1905 lib1906 lib1907 lib1908 lib1910 lib1911 lib1912 lib1913 \
lib1915 \
lib3010
chkdecimalpoint_SOURCES = chkdecimalpoint.c ../../lib/mprintf.c \
@ -649,6 +650,10 @@ lib1913_SOURCES = lib1913.c $(SUPPORTFILES) $(TESTUTIL) $(WARNLESS)
lib1913_LDADD = $(TESTUTIL_LIBS)
lib1913_CPPFLAGS = $(AM_CPPFLAGS)
lib1915_SOURCES = lib1915.c $(SUPPORTFILES) $(TESTUTIL) $(WARNLESS)
lib1915_LDADD = $(TESTUTIL_LIBS)
lib1915_CPPFLAGS = $(AM_CPPFLAGS)
lib3010_SOURCES = lib3010.c $(SUPPORTFILES) $(TESTUTIL) $(WARNLESS)
lib3010_LDADD = $(TESTUTIL_LIBS)
lib3010_CPPFLAGS = $(AM_CPPFLAGS)

95
tests/libtest/lib1915.c Normal file
View File

@ -0,0 +1,95 @@
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at https://curl.haxx.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
***************************************************************************/
#include "test.h"
#include "testutil.h"
#include "warnless.h"
#include "memdebug.h"
static const char *preload_hosts[] = {
"1.example.com",
"2.example.com",
"3.example.com",
"4.example.com",
NULL /* end of list marker */
};
struct state {
int index;
};
/* "read" is from the point of the library, it wants data from us */
static CURLSTScode hstsread(CURL *easy, struct curl_hstsentry *e,
void *userp)
{
const char *host;
struct state *s = (struct state *)userp;
(void)easy;
host = preload_hosts[s->index++];
if(host && (strlen(host) < e->namelen)) {
strcpy(e->name, host);
e->includeSubDomains = FALSE;
strcpy(e->expire, "20300320 01:02:03"); /* curl turns 32 that day */
fprintf(stderr, "add '%s'\n", host);
}
else
return CURLSTS_DONE;
return CURLSTS_OK;
}
/* check that we get the hosts back in the save */
static CURLSTScode hstswrite(CURL *easy, struct curl_hstsentry *e,
struct curl_index *i, void *userp)
{
(void)easy;
(void)userp;
printf("[%u/%u] %s %s\n", i->index, i->total, e->name, e->expire);
return CURLSTS_OK;
}
/*
* Read/write HSTS cache entries via callback.
*/
int test(char *URL)
{
CURLcode ret = CURLE_OK;
CURL *hnd;
struct state st = {0};
curl_global_init(CURL_GLOBAL_ALL);
hnd = curl_easy_init();
if(hnd) {
curl_easy_setopt(hnd, CURLOPT_URL, URL);
curl_easy_setopt(hnd, CURLOPT_HSTSREADFUNCTION, hstsread);
curl_easy_setopt(hnd, CURLOPT_HSTSREADDATA, &st);
curl_easy_setopt(hnd, CURLOPT_HSTSWRITEFUNCTION, hstswrite);
curl_easy_setopt(hnd, CURLOPT_HSTSWRITEDATA, &st);
curl_easy_setopt(hnd, CURLOPT_HSTS_CTRL, CURLHSTS_ENABLE);
ret = curl_easy_perform(hnd);
curl_easy_cleanup(hnd);
}
curl_global_cleanup();
return (int)ret;
}

View File

@ -133,6 +133,8 @@ static curl_chunk_end_callback chunk_end_cb;
static curl_fnmatch_callback fnmatch_cb;
static curl_closesocket_callback closesocketcb;
static curl_xferinfo_callback xferinfocb;
static curl_hstsread_callback hstsreadcb;
static curl_hstswrite_callback hstswritecb;
static curl_resolver_start_callback resolver_start_cb;
int test(char *URL)

View File

@ -125,8 +125,11 @@ UNITTEST_START
CURL *easy;
if(!h)
return 1;
easy = curl_easy_init();
if(!easy)
return 1;
Curl_hsts_load(h, "log/input1660");
Curl_hsts_loadfile(easy, h, "log/input1660");
for(i = 0; headers[i].host ; i++) {
if(headers[i].hdr) {
@ -159,13 +162,9 @@ UNITTEST_START
deltatime++; /* another second passed */
}
easy = curl_easy_init();
if(easy) {
(void)Curl_hsts_save(easy, h, "log/hsts1660");
curl_easy_cleanup(easy);
}
(void)Curl_hsts_save(easy, h, "log/hsts1660");
Curl_hsts_cleanup(&h);
curl_easy_cleanup(easy);
return unitfail;
}
UNITTEST_STOP