1 /***************************************************************************
2 * _ _ ____ _
3 * Project ___| | | | _ \| |
4 * / __| | | | |_) | |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
7 *
8 * Copyright (C) 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 * SPDX-License-Identifier: curl
22 *
23 ***************************************************************************/
24 #include "curlcheck.h"
25
26 #include "urldata.h"
27 #include "hsts.h"
28
29 static CURLcode
unit_setup(void)30 unit_setup(void)
31 {
32 return CURLE_OK;
33 }
34
35 static void
unit_stop(void)36 unit_stop(void)
37 {
38 curl_global_cleanup();
39 }
40
41 #if defined(CURL_DISABLE_HTTP) || defined(CURL_DISABLE_HSTS)
42 UNITTEST_START
43 {
44 puts("nothing to do when HTTP or HSTS are disabled");
45 }
46 UNITTEST_STOP
47 #else
48
49 struct testit {
50 const char *host;
51 const char *chost; /* if non-NULL, use to lookup with */
52 const char *hdr; /* if NULL, just do the lookup */
53 const CURLcode result; /* parse result */
54 };
55
56 static const struct testit headers[] = {
57 /* two entries read from disk cache, verify first */
58 { "-", "readfrom.example", NULL, CURLE_OK},
59 { "-", "old.example", NULL, CURLE_OK},
60 /* delete the remaining one read from disk */
61 { "readfrom.example", NULL, "max-age=\"0\"", CURLE_OK},
62
63 { "example.com", NULL, "max-age=\"31536000\"\r\n", CURLE_OK },
64 { "example.com", NULL, "max-age=\"21536000\"\r\n", CURLE_OK },
65 { "example.com", NULL, "max-age=\"21536000\"; \r\n", CURLE_OK },
66 { "example.com", NULL, "max-age=\"21536000\"; includeSubDomains\r\n",
67 CURLE_OK },
68 { "example.org", NULL, "max-age=\"31536000\"\r\n", CURLE_OK },
69 { "this.example", NULL, "max=\"31536\";", CURLE_BAD_FUNCTION_ARGUMENT },
70 { "this.example", NULL, "max-age=\"31536", CURLE_BAD_FUNCTION_ARGUMENT },
71 { "this.example", NULL, "max-age=31536\"", CURLE_OK },
72 /* max-age=0 removes the entry */
73 { "this.example", NULL, "max-age=0", CURLE_OK },
74 { "another.example", NULL, "includeSubDomains; ",
75 CURLE_BAD_FUNCTION_ARGUMENT },
76
77 /* Two max-age is illegal */
78 { "example.com", NULL,
79 "max-age=\"21536000\"; includeSubDomains; max-age=\"3\";",
80 CURLE_BAD_FUNCTION_ARGUMENT },
81 /* Two includeSubDomains is illegal */
82 { "2.example.com", NULL,
83 "max-age=\"21536000\"; includeSubDomains; includeSubDomains;",
84 CURLE_BAD_FUNCTION_ARGUMENT },
85 /* use a unknown directive "include" that should be ignored */
86 { "3.example.com", NULL, "max-age=\"21536000\"; include; includeSubDomains;",
87 CURLE_OK },
88 /* remove the "3.example.com" one, should still match the example.com */
89 { "3.example.com", NULL, "max-age=\"0\"; includeSubDomains;",
90 CURLE_OK },
91 { "-", "foo.example.com", NULL, CURLE_OK},
92 { "-", "foo.xample.com", NULL, CURLE_OK},
93
94 /* should not match */
95 { "example.net", "forexample.net", "max-age=\"31536000\"\r\n", CURLE_OK },
96
97 /* should not match either, since forexample.net is not in the example.net
98 domain */
99 { "example.net", "forexample.net",
100 "max-age=\"31536000\"; includeSubDomains\r\n", CURLE_OK },
101 /* remove example.net again */
102 { "example.net", NULL, "max-age=\"0\"; includeSubDomains\r\n", CURLE_OK },
103
104 /* make this live for 7 seconds */
105 { "expire.example", NULL, "max-age=\"7\"\r\n", CURLE_OK },
106 { NULL, NULL, NULL, CURLE_OK }
107 };
108
109 static void showsts(struct stsentry *e, const char *chost)
110 {
111 if(!e)
112 printf("'%s' is not HSTS\n", chost);
113 else {
114 printf("%s [%s]: %" CURL_FORMAT_CURL_OFF_T "%s\n",
115 chost, e->host, e->expires,
116 e->includeSubDomains ? " includeSubDomains" : "");
117 }
118 }
119
120 UNITTEST_START
121 {
122 CURLcode result;
123 struct stsentry *e;
124 struct hsts *h = Curl_hsts_init();
125 int i;
126 const char *chost;
127 CURL *easy;
128 char savename[256];
129
130 abort_unless(h, "Curl_hsts_init()");
131
132 curl_global_init(CURL_GLOBAL_ALL);
133 easy = curl_easy_init();
134 if(!easy) {
135 Curl_hsts_cleanup(&h);
136 curl_global_cleanup();
137 abort_unless(easy, "curl_easy_init()");
138 }
139
140 Curl_hsts_loadfile(easy, h, arg);
141
142 for(i = 0; headers[i].host ; i++) {
143 if(headers[i].hdr) {
144 result = Curl_hsts_parse(h, headers[i].host, headers[i].hdr);
145
146 if(result != headers[i].result) {
147 fprintf(stderr, "Curl_hsts_parse(%s) failed: %d\n",
148 headers[i].hdr, result);
149 unitfail++;
150 continue;
151 }
152 else if(result) {
153 printf("Input %u: error %d\n", i, (int) result);
154 continue;
155 }
156 }
157
158 chost = headers[i].chost ? headers[i].chost : headers[i].host;
159 e = Curl_hsts(h, chost, TRUE);
160 showsts(e, chost);
161 }
162
163 printf("Number of entries: %zu\n", Curl_llist_count(&h->list));
164
165 /* verify that it is exists for 7 seconds */
166 chost = "expire.example";
167 for(i = 100; i < 110; i++) {
168 e = Curl_hsts(h, chost, TRUE);
169 showsts(e, chost);
170 deltatime++; /* another second passed */
171 }
172
173 msnprintf(savename, sizeof(savename), "%s.save", arg);
174 (void)Curl_hsts_save(easy, h, savename);
175 Curl_hsts_cleanup(&h);
176 curl_easy_cleanup(easy);
177 curl_global_cleanup();
178 }
179 UNITTEST_STOP
180 #endif
181