summaryrefslogtreecommitdiff
path: root/tests
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:42 +0100
commit7385610d0c74c6a254fea5e4cd6e1d559d848c8c (patch)
tree3b572bcf972062b7cc1315ac23fdb547e7216463 /tests
parent9f43b28f783cc8f7464492a0b5b9dd35c1625fde (diff)
downloadcurl-7385610d0c74c6a254fea5e4cd6e1d559d848c8c.tar.gz
hsts: add support for Strict-Transport-Security
- enable in the build (configure) - header parsing - host name lookup - unit tests for the above - CI build - CURL_VERSION_HSTS bit - curl_version_info support - curl -V output - curl-config --features - CURLOPT_HSTS_CTRL - man page for CURLOPT_HSTS_CTRL - curl --hsts (sets CURLOPT_HSTS_CTRL and works with --libcurl) - man page for --hsts - save cache to disk - load cache from disk - CURLOPT_HSTS - man page for CURLOPT_HSTS - added docs/HSTS.md - fixed --version docs - adjusted curl_easy_duphandle Closes #5896
Diffstat (limited to 'tests')
-rw-r--r--tests/data/Makefile.inc1
-rw-r--r--tests/data/test166081
-rwxr-xr-xtests/runtests.pl5
-rw-r--r--tests/unit/Makefile.inc5
-rw-r--r--tests/unit/unit1660.c172
5 files changed, 263 insertions, 1 deletions
diff --git a/tests/data/Makefile.inc b/tests/data/Makefile.inc
index 2e0c092ad..04e23c6fc 100644
--- a/tests/data/Makefile.inc
+++ b/tests/data/Makefile.inc
@@ -197,6 +197,7 @@ test1620 test1621 \
test1630 test1631 test1632 test1633 \
\
test1650 test1651 test1652 test1653 test1654 test1655 \
+test1660 \
\
test1700 test1701 test1702 \
\
diff --git a/tests/data/test1660 b/tests/data/test1660
new file mode 100644
index 000000000..f64765c3f
--- /dev/null
+++ b/tests/data/test1660
@@ -0,0 +1,81 @@
+<testcase>
+<info>
+<keywords>
+unittest
+HSTS
+</keywords>
+</info>
+
+<client>
+<server>
+none
+</server>
+<features>
+unittest
+HSTS
+</features>
+
+<file name="log/input1660">
+# Your HSTS cache. https://curl.haxx.se/docs/hsts.html
+# This file was generated by libcurl! Edit at your own risk.
+.readfrom.example "20211001 04:47:41"
+.old.example "20161001 04:47:41"
+</file>
+
+# This date is exactly "20190124 22:34:21" UTC
+<setenv>
+CURL_TIME=1548369261
+</setenv>
+<name>
+HSTS
+</name>
+<command>
+-
+</command>
+</client>
+
+<verify>
+<stdout>
+readfrom.example [readfrom.example]: 1633063661 includeSubDomains
+'old.example' is not HSTS
+'readfrom.example' is not HSTS
+example.com [example.com]: 1579905261
+example.com [example.com]: 1569905261
+example.com [example.com]: 1569905261
+example.com [example.com]: 1569905261 includeSubDomains
+example.org [example.org]: 1579905261
+Input 8: error 43
+Input 9: error 43
+this.example [this.example]: 1548400797
+'this.example' is not HSTS
+Input 12: error 43
+Input 13: error 43
+Input 14: error 43
+3.example.com [example.com]: 1569905261 includeSubDomains
+3.example.com [example.com]: 1569905261 includeSubDomains
+foo.example.com [example.com]: 1569905261 includeSubDomains
+'foo.xample.com' is not HSTS
+'forexample.net' is not HSTS
+'forexample.net' is not HSTS
+'example.net' is not HSTS
+expire.example [expire.example]: 1548369268
+Number of entries: 3
+expire.example [expire.example]: 1548369268
+expire.example [expire.example]: 1548369268
+expire.example [expire.example]: 1548369268
+expire.example [expire.example]: 1548369268
+expire.example [expire.example]: 1548369268
+expire.example [expire.example]: 1548369268
+expire.example [expire.example]: 1548369268
+'expire.example' is not HSTS
+'expire.example' is not HSTS
+'expire.example' is not HSTS
+</stdout>
+<file name="log/hsts1660">
+# Your HSTS cache. https://curl.haxx.se/docs/hsts.html
+# This file was generated by libcurl! Edit at your own risk.
+.example.com "20191001 04:47:41"
+example.org "20200124 22:34:21"
+</file>
+</verify>
+</testcase>
diff --git a/tests/runtests.pl b/tests/runtests.pl
index 4bcf61bbf..a4e330661 100755
--- a/tests/runtests.pl
+++ b/tests/runtests.pl
@@ -256,6 +256,7 @@ my $has_cares; # set if built with c-ares
my $has_threadedres;# set if built with threaded resolver
my $has_psl; # set if libcurl is built with PSL support
my $has_altsvc; # set if libcurl is built with alt-svc support
+my $has_hsts; # set if libcurl is built with HSTS support
my $has_ldpreload; # set if curl is built for systems supporting LD_PRELOAD
my $has_multissl; # set if curl is build with MultiSSL support
my $has_manual; # set if curl is built with built-in manual
@@ -2762,6 +2763,7 @@ sub compare {
sub setupfeatures {
$feature{"alt-svc"} = $has_altsvc;
+ $feature{"HSTS"} = $has_hsts;
$feature{"brotli"} = $has_brotli;
$feature{"crypto"} = $has_crypto;
$feature{"debug"} = $debug_build;
@@ -3035,6 +3037,9 @@ sub checksystem {
# alt-svc enabled
$has_altsvc=1;
}
+ if($feat =~ /HSTS/i) {
+ $has_hsts=1;
+ }
if($feat =~ /AsynchDNS/i) {
if(!$has_cares) {
# this means threaded resolver
diff --git a/tests/unit/Makefile.inc b/tests/unit/Makefile.inc
index f63724f91..ee6816823 100644
--- a/tests/unit/Makefile.inc
+++ b/tests/unit/Makefile.inc
@@ -34,7 +34,8 @@ UNITPROGS = unit1300 unit1301 unit1302 unit1303 unit1304 unit1305 unit1307 \
unit1600 unit1601 unit1602 unit1603 unit1604 unit1605 unit1606 unit1607 \
unit1608 unit1609 unit1610 unit1611 unit1612 \
unit1620 unit1621 \
- unit1650 unit1651 unit1652 unit1653 unit1654 unit1655
+ unit1650 unit1651 unit1652 unit1653 unit1654 unit1655 \
+ unit1660
unit1300_SOURCES = unit1300.c $(UNITFILES)
unit1300_CPPFLAGS = $(AM_CPPFLAGS)
@@ -154,3 +155,5 @@ unit1654_CPPFLAGS = $(AM_CPPFLAGS)
unit1655_SOURCES = unit1655.c $(UNITFILES)
unit1655_CPPFLAGS = $(AM_CPPFLAGS)
+unit1660_SOURCES = unit1660.c $(UNITFILES)
+unit1660_CPPFLAGS = $(AM_CPPFLAGS)
diff --git a/tests/unit/unit1660.c b/tests/unit/unit1660.c
new file mode 100644
index 000000000..1687cafa1
--- /dev/null
+++ b/tests/unit/unit1660.c
@@ -0,0 +1,172 @@
+/***************************************************************************
+ * _ _ ____ _
+ * 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 "curlcheck.h"
+
+#include "urldata.h"
+#include "hsts.h"
+
+static CURLcode
+unit_setup(void)
+{
+ return CURLE_OK;
+}
+
+static void
+unit_stop(void)
+{
+ curl_global_cleanup();
+}
+
+#if defined(CURL_DISABLE_HTTP) || !defined(USE_HSTS)
+UNITTEST_START
+{
+ return 0; /* nothing to do when HTTP or HSTS are disabled */
+}
+UNITTEST_STOP
+#else
+
+struct testit {
+ const char *host;
+ const char *chost; /* if non-NULL, use to lookup with */
+ const char *hdr; /* if NULL, just do the lookup */
+ const CURLcode result; /* parse result */
+};
+
+static const struct testit headers[] = {
+ /* two entries read from disk cache, verify first */
+ { "-", "readfrom.example", NULL, CURLE_OK},
+ { "-", "old.example", NULL, CURLE_OK},
+ /* delete the remaining one read from disk */
+ { "readfrom.example", NULL, "max-age=\"0\"", CURLE_OK},
+
+ { "example.com", NULL, "max-age=\"31536000\"\r\n", CURLE_OK },
+ { "example.com", NULL, "max-age=\"21536000\"\r\n", CURLE_OK },
+ { "example.com", NULL, "max-age=\"21536000\"; \r\n", CURLE_OK },
+ { "example.com", NULL, "max-age=\"21536000\"; includeSubDomains\r\n",
+ CURLE_OK },
+ { "example.org", NULL, "max-age=\"31536000\"\r\n", CURLE_OK },
+ { "this.example", NULL, "max=\"31536\";", CURLE_BAD_FUNCTION_ARGUMENT },
+ { "this.example", NULL, "max-age=\"31536", CURLE_BAD_FUNCTION_ARGUMENT },
+ { "this.example", NULL, "max-age=31536\"", CURLE_OK },
+ /* max-age=0 removes the entry */
+ { "this.example", NULL, "max-age=0", CURLE_OK },
+ { "another.example", NULL, "includeSubDomains; ",
+ CURLE_BAD_FUNCTION_ARGUMENT },
+
+ /* Two max-age is illegal */
+ { "example.com", NULL,
+ "max-age=\"21536000\"; includeSubDomains; max-age=\"3\";",
+ CURLE_BAD_FUNCTION_ARGUMENT },
+ /* Two includeSubDomains is illegal */
+ { "2.example.com", NULL,
+ "max-age=\"21536000\"; includeSubDomains; includeSubDomains;",
+ CURLE_BAD_FUNCTION_ARGUMENT },
+ /* use a unknown directive "include" that should be ignored */
+ { "3.example.com", NULL, "max-age=\"21536000\"; include; includeSubDomains;",
+ CURLE_OK },
+ /* remove the "3.example.com" one, should still match the example.com */
+ { "3.example.com", NULL, "max-age=\"0\"; includeSubDomains;",
+ CURLE_OK },
+ { "-", "foo.example.com", NULL, CURLE_OK},
+ { "-", "foo.xample.com", NULL, CURLE_OK},
+
+ /* should not match */
+ { "example.net", "forexample.net", "max-age=\"31536000\"\r\n", CURLE_OK },
+
+ /* should not match either, since forexample.net is not in the example.net
+ domain */
+ { "example.net", "forexample.net",
+ "max-age=\"31536000\"; includeSubDomains\r\n", CURLE_OK },
+ /* remove example.net again */
+ { "example.net", NULL, "max-age=\"0\"; includeSubDomains\r\n", CURLE_OK },
+
+ /* make this live for 7 seconds */
+ { "expire.example", NULL, "max-age=\"7\"\r\n", CURLE_OK },
+ { NULL, NULL, NULL, 0 }
+};
+
+static void showsts(struct stsentry *e, const char *chost)
+{
+ if(!e)
+ printf("'%s' is not HSTS\n", chost);
+ else {
+ printf("%s [%s]: %" CURL_FORMAT_CURL_OFF_T "%s\n",
+ chost, e->host, e->expires,
+ e->includeSubDomains ? " includeSubDomains" : "");
+ }
+}
+
+UNITTEST_START
+{
+ CURLcode result;
+ struct stsentry *e;
+ struct hsts *h = Curl_hsts_init();
+ int i;
+ const char *chost;
+ CURL *easy;
+ if(!h)
+ return 1;
+
+ Curl_hsts_load(h, "log/input1660");
+
+ for(i = 0; headers[i].host ; i++) {
+ if(headers[i].hdr) {
+ result = Curl_hsts_parse(h, headers[i].host, headers[i].hdr);
+
+ if(result != headers[i].result) {
+ fprintf(stderr, "Curl_hsts_parse(%s) failed: %d\n",
+ headers[i].hdr, result);
+ unitfail++;
+ continue;
+ }
+ else if(result) {
+ printf("Input %u: error %d\n", i, (int) result);
+ continue;
+ }
+ }
+
+ chost = headers[i].chost ? headers[i].chost : headers[i].host;
+ e = Curl_hsts(h, chost, TRUE);
+ showsts(e, chost);
+ }
+
+ printf("Number of entries: %d\n", h->list.size);
+
+ /* verify that it is exists for 7 seconds */
+ chost = "expire.example";
+ for(i = 100; i < 110; i++) {
+ e = Curl_hsts(h, chost, TRUE);
+ showsts(e, chost);
+ deltatime++; /* another second passed */
+ }
+
+ easy = curl_easy_init();
+ if(easy) {
+ (void)Curl_hsts_save(easy, h, "log/hsts1660");
+ curl_easy_cleanup(easy);
+ }
+
+ Curl_hsts_cleanup(&h);
+ return unitfail;
+}
+UNITTEST_STOP
+#endif