1 /***************************************************************************
2 * _ _ ____ _
3 * Project ___| | | | _ \| |
4 * / __| | | | |_) | |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
7 *
8 * Copyright (C) 2020 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
9 *
10 * This software is licensed as described in the file COPYING, which
11 * you should have received as part of this distribution. The terms
12 * are also available at https://curl.se/docs/copyright.html.
13 *
14 * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15 * copies of the Software, and permit persons to whom the Software is
16 * furnished to do so, under the terms of the COPYING file.
17 *
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
20 *
21 ***************************************************************************/
22 #include "curlcheck.h"
23
24 #include "urldata.h"
25 #include "hsts.h"
26
27 static CURLcode
unit_setup(void)28 unit_setup(void)
29 {
30 return CURLE_OK;
31 }
32
33 static void
unit_stop(void)34 unit_stop(void)
35 {
36 curl_global_cleanup();
37 }
38
39 #if defined(CURL_DISABLE_HTTP) || defined(CURL_DISABLE_HSTS)
40 UNITTEST_START
41 {
42 return 0; /* nothing to do when HTTP or HSTS are disabled */
43 }
44 UNITTEST_STOP
45 #else
46
47 struct testit {
48 const char *host;
49 const char *chost; /* if non-NULL, use to lookup with */
50 const char *hdr; /* if NULL, just do the lookup */
51 const CURLcode result; /* parse result */
52 };
53
54 static const struct testit headers[] = {
55 /* two entries read from disk cache, verify first */
56 { "-", "readfrom.example", NULL, CURLE_OK},
57 { "-", "old.example", NULL, CURLE_OK},
58 /* delete the remaining one read from disk */
59 { "readfrom.example", NULL, "max-age=\"0\"", CURLE_OK},
60
61 { "example.com", NULL, "max-age=\"31536000\"\r\n", CURLE_OK },
62 { "example.com", NULL, "max-age=\"21536000\"\r\n", CURLE_OK },
63 { "example.com", NULL, "max-age=\"21536000\"; \r\n", CURLE_OK },
64 { "example.com", NULL, "max-age=\"21536000\"; includeSubDomains\r\n",
65 CURLE_OK },
66 { "example.org", NULL, "max-age=\"31536000\"\r\n", CURLE_OK },
67 { "this.example", NULL, "max=\"31536\";", CURLE_BAD_FUNCTION_ARGUMENT },
68 { "this.example", NULL, "max-age=\"31536", CURLE_BAD_FUNCTION_ARGUMENT },
69 { "this.example", NULL, "max-age=31536\"", CURLE_OK },
70 /* max-age=0 removes the entry */
71 { "this.example", NULL, "max-age=0", CURLE_OK },
72 { "another.example", NULL, "includeSubDomains; ",
73 CURLE_BAD_FUNCTION_ARGUMENT },
74
75 /* Two max-age is illegal */
76 { "example.com", NULL,
77 "max-age=\"21536000\"; includeSubDomains; max-age=\"3\";",
78 CURLE_BAD_FUNCTION_ARGUMENT },
79 /* Two includeSubDomains is illegal */
80 { "2.example.com", NULL,
81 "max-age=\"21536000\"; includeSubDomains; includeSubDomains;",
82 CURLE_BAD_FUNCTION_ARGUMENT },
83 /* use a unknown directive "include" that should be ignored */
84 { "3.example.com", NULL, "max-age=\"21536000\"; include; includeSubDomains;",
85 CURLE_OK },
86 /* remove the "3.example.com" one, should still match the example.com */
87 { "3.example.com", NULL, "max-age=\"0\"; includeSubDomains;",
88 CURLE_OK },
89 { "-", "foo.example.com", NULL, CURLE_OK},
90 { "-", "foo.xample.com", NULL, CURLE_OK},
91
92 /* should not match */
93 { "example.net", "forexample.net", "max-age=\"31536000\"\r\n", CURLE_OK },
94
95 /* should not match either, since forexample.net is not in the example.net
96 domain */
97 { "example.net", "forexample.net",
98 "max-age=\"31536000\"; includeSubDomains\r\n", CURLE_OK },
99 /* remove example.net again */
100 { "example.net", NULL, "max-age=\"0\"; includeSubDomains\r\n", CURLE_OK },
101
102 /* make this live for 7 seconds */
103 { "expire.example", NULL, "max-age=\"7\"\r\n", CURLE_OK },
104 { NULL, NULL, NULL, 0 }
105 };
106
107 static void showsts(struct stsentry *e, const char *chost)
108 {
109 if(!e)
110 printf("'%s' is not HSTS\n", chost);
111 else {
112 printf("%s [%s]: %" CURL_FORMAT_CURL_OFF_T "%s\n",
113 chost, e->host, e->expires,
114 e->includeSubDomains ? " includeSubDomains" : "");
115 }
116 }
117
118 UNITTEST_START
119 {
120 CURLcode result;
121 struct stsentry *e;
122 struct hsts *h = Curl_hsts_init();
123 int i;
124 const char *chost;
125 CURL *easy;
126 if(!h)
127 return 1;
128
129 curl_global_init(CURL_GLOBAL_ALL);
130 easy = curl_easy_init();
131 if(!easy) {
132 Curl_hsts_cleanup(&h);
133 curl_global_cleanup();
134 return 1;
135 }
136
137 Curl_hsts_loadfile(easy, h, "log/input1660");
138
139 for(i = 0; headers[i].host ; i++) {
140 if(headers[i].hdr) {
141 result = Curl_hsts_parse(h, headers[i].host, headers[i].hdr);
142
143 if(result != headers[i].result) {
144 fprintf(stderr, "Curl_hsts_parse(%s) failed: %d\n",
145 headers[i].hdr, result);
146 unitfail++;
147 continue;
148 }
149 else if(result) {
150 printf("Input %u: error %d\n", i, (int) result);
151 continue;
152 }
153 }
154
155 chost = headers[i].chost ? headers[i].chost : headers[i].host;
156 e = Curl_hsts(h, chost, TRUE);
157 showsts(e, chost);
158 }
159
160 printf("Number of entries: %zu\n", h->list.size);
161
162 /* verify that it is exists for 7 seconds */
163 chost = "expire.example";
164 for(i = 100; i < 110; i++) {
165 e = Curl_hsts(h, chost, TRUE);
166 showsts(e, chost);
167 deltatime++; /* another second passed */
168 }
169
170 (void)Curl_hsts_save(easy, h, "log/hsts1660");
171 Curl_hsts_cleanup(&h);
172 curl_easy_cleanup(easy);
173 curl_global_cleanup();
174 return unitfail;
175 }
176 UNITTEST_STOP
177 #endif
178