VirtualBox

source: vbox/trunk/src/libs/curl-7.83.1/lib/hsts.c@ 97138

最後變更 在這個檔案從97138是 95312,由 vboxsync 提交於 3 年 前

libs/{curl,libxml2}: OSE export fixes, bugref:8515

  • 屬性 svn:eol-style 設為 native
檔案大小: 14.2 KB
 
1/***************************************************************************
2 * _ _ ____ _
3 * Project ___| | | | _ \| |
4 * / __| | | | |_) | |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
7 *
8 * Copyright (C) 2020 - 2022, Daniel Stenberg, <[email protected]>, 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/*
23 * The Strict-Transport-Security header is defined in RFC 6797:
24 * https://datatracker.ietf.org/doc/html/rfc6797
25 */
26#include "curl_setup.h"
27
28#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_HSTS)
29#include <curl/curl.h>
30#include "urldata.h"
31#include "llist.h"
32#include "hsts.h"
33#include "curl_get_line.h"
34#include "strcase.h"
35#include "sendf.h"
36#include "strtoofft.h"
37#include "parsedate.h"
38#include "rand.h"
39#include "rename.h"
40#include "strtoofft.h"
41
42/* The last 3 #include files should be in this order */
43#include "curl_printf.h"
44#include "curl_memory.h"
45#include "memdebug.h"
46
47#define MAX_HSTS_LINE 4095
48#define MAX_HSTS_HOSTLEN 256
49#define MAX_HSTS_HOSTLENSTR "256"
50#define MAX_HSTS_DATELEN 64
51#define MAX_HSTS_DATELENSTR "64"
52#define UNLIMITED "unlimited"
53
54#ifdef DEBUGBUILD
55/* to play well with debug builds, we can *set* a fixed time this will
56 return */
57time_t deltatime; /* allow for "adjustments" for unit test purposes */
58static time_t debugtime(void *unused)
59{
60 char *timestr = getenv("CURL_TIME");
61 (void)unused;
62 if(timestr) {
63 curl_off_t val;
64 (void)curlx_strtoofft(timestr, NULL, 10, &val);
65
66 val += (curl_off_t)deltatime;
67 return (time_t)val;
68 }
69 return time(NULL);
70}
71#define time(x) debugtime(x)
72#endif
73
74struct hsts *Curl_hsts_init(void)
75{
76 struct hsts *h = calloc(sizeof(struct hsts), 1);
77 if(h) {
78 Curl_llist_init(&h->list, NULL);
79 }
80 return h;
81}
82
83static void hsts_free(struct stsentry *e)
84{
85 free((char *)e->host);
86 free(e);
87}
88
89void Curl_hsts_cleanup(struct hsts **hp)
90{
91 struct hsts *h = *hp;
92 if(h) {
93 struct Curl_llist_element *e;
94 struct Curl_llist_element *n;
95 for(e = h->list.head; e; e = n) {
96 struct stsentry *sts = e->ptr;
97 n = e->next;
98 hsts_free(sts);
99 }
100 free(h->filename);
101 free(h);
102 *hp = NULL;
103 }
104}
105
106static struct stsentry *hsts_entry(void)
107{
108 return calloc(sizeof(struct stsentry), 1);
109}
110
111static CURLcode hsts_create(struct hsts *h,
112 const char *hostname,
113 bool subdomains,
114 curl_off_t expires)
115{
116 struct stsentry *sts = hsts_entry();
117 char *duphost;
118 size_t hlen;
119 if(!sts)
120 return CURLE_OUT_OF_MEMORY;
121
122 duphost = strdup(hostname);
123 if(!duphost) {
124 free(sts);
125 return CURLE_OUT_OF_MEMORY;
126 }
127
128 hlen = strlen(duphost);
129 if(duphost[hlen - 1] == '.')
130 /* strip off trailing any dot */
131 duphost[--hlen] = 0;
132
133 sts->host = duphost;
134 sts->expires = expires;
135 sts->includeSubDomains = subdomains;
136 Curl_llist_insert_next(&h->list, h->list.tail, sts, &sts->node);
137 return CURLE_OK;
138}
139
140CURLcode Curl_hsts_parse(struct hsts *h, const char *hostname,
141 const char *header)
142{
143 const char *p = header;
144 curl_off_t expires = 0;
145 bool gotma = FALSE;
146 bool gotinc = FALSE;
147 bool subdomains = FALSE;
148 struct stsentry *sts;
149 time_t now = time(NULL);
150
151 if(Curl_host_is_ipnum(hostname))
152 /* "explicit IP address identification of all forms is excluded."
153 / RFC 6797 */
154 return CURLE_OK;
155
156 do {
157 while(*p && ISSPACE(*p))
158 p++;
159 if(Curl_strncasecompare("max-age=", p, 8)) {
160 bool quoted = FALSE;
161 CURLofft offt;
162 char *endp;
163
164 if(gotma)
165 return CURLE_BAD_FUNCTION_ARGUMENT;
166
167 p += 8;
168 while(*p && ISSPACE(*p))
169 p++;
170 if(*p == '\"') {
171 p++;
172 quoted = TRUE;
173 }
174 offt = curlx_strtoofft(p, &endp, 10, &expires);
175 if(offt == CURL_OFFT_FLOW)
176 expires = CURL_OFF_T_MAX;
177 else if(offt)
178 /* invalid max-age */
179 return CURLE_BAD_FUNCTION_ARGUMENT;
180 p = endp;
181 if(quoted) {
182 if(*p != '\"')
183 return CURLE_BAD_FUNCTION_ARGUMENT;
184 p++;
185 }
186 gotma = TRUE;
187 }
188 else if(Curl_strncasecompare("includesubdomains", p, 17)) {
189 if(gotinc)
190 return CURLE_BAD_FUNCTION_ARGUMENT;
191 subdomains = TRUE;
192 p += 17;
193 gotinc = TRUE;
194 }
195 else {
196 /* unknown directive, do a lame attempt to skip */
197 while(*p && (*p != ';'))
198 p++;
199 }
200
201 while(*p && ISSPACE(*p))
202 p++;
203 if(*p == ';')
204 p++;
205 } while (*p);
206
207 if(!gotma)
208 /* max-age is mandatory */
209 return CURLE_BAD_FUNCTION_ARGUMENT;
210
211 if(!expires) {
212 /* remove the entry if present verbatim (without subdomain match) */
213 sts = Curl_hsts(h, hostname, FALSE);
214 if(sts) {
215 Curl_llist_remove(&h->list, &sts->node, NULL);
216 hsts_free(sts);
217 }
218 return CURLE_OK;
219 }
220
221 if(CURL_OFF_T_MAX - now < expires)
222 /* would overflow, use maximum value */
223 expires = CURL_OFF_T_MAX;
224 else
225 expires += now;
226
227 /* check if it already exists */
228 sts = Curl_hsts(h, hostname, FALSE);
229 if(sts) {
230 /* just update these fields */
231 sts->expires = expires;
232 sts->includeSubDomains = subdomains;
233 }
234 else
235 return hsts_create(h, hostname, subdomains, expires);
236
237 return CURLE_OK;
238}
239
240/*
241 * Return TRUE if the given host name is currently an HSTS one.
242 *
243 * The 'subdomain' argument tells the function if subdomain matching should be
244 * attempted.
245 */
246struct stsentry *Curl_hsts(struct hsts *h, const char *hostname,
247 bool subdomain)
248{
249 if(h) {
250 char buffer[MAX_HSTS_HOSTLEN + 1];
251 time_t now = time(NULL);
252 size_t hlen = strlen(hostname);
253 struct Curl_llist_element *e;
254 struct Curl_llist_element *n;
255
256 if((hlen > MAX_HSTS_HOSTLEN) || !hlen)
257 return NULL;
258 memcpy(buffer, hostname, hlen);
259 if(hostname[hlen-1] == '.')
260 /* remove the trailing dot */
261 --hlen;
262 buffer[hlen] = 0;
263 hostname = buffer;
264
265 for(e = h->list.head; e; e = n) {
266 struct stsentry *sts = e->ptr;
267 n = e->next;
268 if(sts->expires <= now) {
269 /* remove expired entries */
270 Curl_llist_remove(&h->list, &sts->node, NULL);
271 hsts_free(sts);
272 continue;
273 }
274 if(subdomain && sts->includeSubDomains) {
275 size_t ntail = strlen(sts->host);
276 if(ntail < hlen) {
277 size_t offs = hlen - ntail;
278 if((hostname[offs-1] == '.') &&
279 Curl_strncasecompare(&hostname[offs], sts->host, ntail))
280 return sts;
281 }
282 }
283 if(Curl_strcasecompare(hostname, sts->host))
284 return sts;
285 }
286 }
287 return NULL; /* no match */
288}
289
290/*
291 * Send this HSTS entry to the write callback.
292 */
293static CURLcode hsts_push(struct Curl_easy *data,
294 struct curl_index *i,
295 struct stsentry *sts,
296 bool *stop)
297{
298 struct curl_hstsentry e;
299 CURLSTScode sc;
300 struct tm stamp;
301 CURLcode result;
302
303 e.name = (char *)sts->host;
304 e.namelen = strlen(sts->host);
305 e.includeSubDomains = sts->includeSubDomains;
306
307 if(sts->expires != TIME_T_MAX) {
308 result = Curl_gmtime((time_t)sts->expires, &stamp);
309 if(result)
310 return result;
311
312 msnprintf(e.expire, sizeof(e.expire), "%d%02d%02d %02d:%02d:%02d",
313 stamp.tm_year + 1900, stamp.tm_mon + 1, stamp.tm_mday,
314 stamp.tm_hour, stamp.tm_min, stamp.tm_sec);
315 }
316 else
317 strcpy(e.expire, UNLIMITED);
318
319 sc = data->set.hsts_write(data, &e, i,
320 data->set.hsts_write_userp);
321 *stop = (sc != CURLSTS_OK);
322 return sc == CURLSTS_FAIL ? CURLE_BAD_FUNCTION_ARGUMENT : CURLE_OK;
323}
324
325/*
326 * Write this single hsts entry to a single output line
327 */
328static CURLcode hsts_out(struct stsentry *sts, FILE *fp)
329{
330 struct tm stamp;
331 if(sts->expires != TIME_T_MAX) {
332 CURLcode result = Curl_gmtime((time_t)sts->expires, &stamp);
333 if(result)
334 return result;
335 fprintf(fp, "%s%s \"%d%02d%02d %02d:%02d:%02d\"\n",
336 sts->includeSubDomains ? ".": "", sts->host,
337 stamp.tm_year + 1900, stamp.tm_mon + 1, stamp.tm_mday,
338 stamp.tm_hour, stamp.tm_min, stamp.tm_sec);
339 }
340 else
341 fprintf(fp, "%s%s \"%s\"\n",
342 sts->includeSubDomains ? ".": "", sts->host, UNLIMITED);
343 return CURLE_OK;
344}
345
346
347/*
348 * Curl_https_save() writes the HSTS cache to file and callback.
349 */
350CURLcode Curl_hsts_save(struct Curl_easy *data, struct hsts *h,
351 const char *file)
352{
353 struct Curl_llist_element *e;
354 struct Curl_llist_element *n;
355 CURLcode result = CURLE_OK;
356 FILE *out;
357 char *tempstore;
358 unsigned char randsuffix[9];
359
360 if(!h)
361 /* no cache activated */
362 return CURLE_OK;
363
364 /* if no new name is given, use the one we stored from the load */
365 if(!file && h->filename)
366 file = h->filename;
367
368 if((h->flags & CURLHSTS_READONLYFILE) || !file || !file[0])
369 /* marked as read-only, no file or zero length file name */
370 goto skipsave;
371
372 if(Curl_rand_hex(data, randsuffix, sizeof(randsuffix)))
373 return CURLE_FAILED_INIT;
374
375 tempstore = aprintf("%s.%s.tmp", file, randsuffix);
376 if(!tempstore)
377 return CURLE_OUT_OF_MEMORY;
378
379 out = fopen(tempstore, FOPEN_WRITETEXT);
380 if(!out)
381 result = CURLE_WRITE_ERROR;
382 else {
383 fputs("# Your HSTS cache. https://curl.se/docs/hsts.html\n"
384 "# This file was generated by libcurl! Edit at your own risk.\n",
385 out);
386 for(e = h->list.head; e; e = n) {
387 struct stsentry *sts = e->ptr;
388 n = e->next;
389 result = hsts_out(sts, out);
390 if(result)
391 break;
392 }
393 fclose(out);
394 if(!result && Curl_rename(tempstore, file))
395 result = CURLE_WRITE_ERROR;
396
397 if(result)
398 unlink(tempstore);
399 }
400 free(tempstore);
401 skipsave:
402 if(data->set.hsts_write) {
403 /* if there's a write callback */
404 struct curl_index i; /* count */
405 i.total = h->list.size;
406 i.index = 0;
407 for(e = h->list.head; e; e = n) {
408 struct stsentry *sts = e->ptr;
409 bool stop;
410 n = e->next;
411 result = hsts_push(data, &i, sts, &stop);
412 if(result || stop)
413 break;
414 i.index++;
415 }
416 }
417 return result;
418}
419
420/* only returns SERIOUS errors */
421static CURLcode hsts_add(struct hsts *h, char *line)
422{
423 /* Example lines:
424 example.com "20191231 10:00:00"
425 .example.net "20191231 10:00:00"
426 */
427 char host[MAX_HSTS_HOSTLEN + 1];
428 char date[MAX_HSTS_DATELEN + 1];
429 int rc;
430
431 rc = sscanf(line,
432 "%" MAX_HSTS_HOSTLENSTR "s \"%" MAX_HSTS_DATELENSTR "[^\"]\"",
433 host, date);
434 if(2 == rc) {
435 time_t expires = strcmp(date, UNLIMITED) ? Curl_getdate_capped(date) :
436 TIME_T_MAX;
437 CURLcode result;
438 char *p = host;
439 bool subdomain = FALSE;
440 if(p[0] == '.') {
441 p++;
442 subdomain = TRUE;
443 }
444 result = hsts_create(h, p, subdomain, expires);
445 if(result)
446 return result;
447 }
448
449 return CURLE_OK;
450}
451
452/*
453 * Load HSTS data from callback.
454 *
455 */
456static CURLcode hsts_pull(struct Curl_easy *data, struct hsts *h)
457{
458 /* if the HSTS read callback is set, use it */
459 if(data->set.hsts_read) {
460 CURLSTScode sc;
461 DEBUGASSERT(h);
462 do {
463 char buffer[MAX_HSTS_HOSTLEN + 1];
464 struct curl_hstsentry e;
465 e.name = buffer;
466 e.namelen = sizeof(buffer)-1;
467 e.includeSubDomains = FALSE; /* default */
468 e.expire[0] = 0;
469 e.name[0] = 0; /* just to make it clean */
470 sc = data->set.hsts_read(data, &e, data->set.hsts_read_userp);
471 if(sc == CURLSTS_OK) {
472 time_t expires;
473 CURLcode result;
474 if(!e.name[0])
475 /* bail out if no name was stored */
476 return CURLE_BAD_FUNCTION_ARGUMENT;
477 if(e.expire[0])
478 expires = Curl_getdate_capped(e.expire);
479 else
480 expires = TIME_T_MAX; /* the end of time */
481 result = hsts_create(h, e.name,
482 /* bitfield to bool conversion: */
483 e.includeSubDomains ? TRUE : FALSE,
484 expires);
485 if(result)
486 return result;
487 }
488 else if(sc == CURLSTS_FAIL)
489 return CURLE_ABORTED_BY_CALLBACK;
490 } while(sc == CURLSTS_OK);
491 }
492 return CURLE_OK;
493}
494
495/*
496 * Load the HSTS cache from the given file. The text based line-oriented file
497 * format is documented here:
498 * https://github.com/curl/curl/wiki/HSTS
499 *
500 * This function only returns error on major problems that prevent hsts
501 * handling to work completely. It will ignore individual syntactical errors
502 * etc.
503 */
504static CURLcode hsts_load(struct hsts *h, const char *file)
505{
506 CURLcode result = CURLE_OK;
507 char *line = NULL;
508 FILE *fp;
509
510 /* we need a private copy of the file name so that the hsts cache file
511 name survives an easy handle reset */
512 free(h->filename);
513 h->filename = strdup(file);
514 if(!h->filename)
515 return CURLE_OUT_OF_MEMORY;
516
517 fp = fopen(file, FOPEN_READTEXT);
518 if(fp) {
519 line = malloc(MAX_HSTS_LINE);
520 if(!line)
521 goto fail;
522 while(Curl_get_line(line, MAX_HSTS_LINE, fp)) {
523 char *lineptr = line;
524 while(*lineptr && ISBLANK(*lineptr))
525 lineptr++;
526 if(*lineptr == '#')
527 /* skip commented lines */
528 continue;
529
530 hsts_add(h, lineptr);
531 }
532 free(line); /* free the line buffer */
533 fclose(fp);
534 }
535 return result;
536
537 fail:
538 Curl_safefree(h->filename);
539 fclose(fp);
540 return CURLE_OUT_OF_MEMORY;
541}
542
543/*
544 * Curl_hsts_loadfile() loads HSTS from file
545 */
546CURLcode Curl_hsts_loadfile(struct Curl_easy *data,
547 struct hsts *h, const char *file)
548{
549 DEBUGASSERT(h);
550 (void)data;
551 return hsts_load(h, file);
552}
553
554/*
555 * Curl_hsts_loadcb() loads HSTS from callback
556 */
557CURLcode Curl_hsts_loadcb(struct Curl_easy *data, struct hsts *h)
558{
559 if(h)
560 return hsts_pull(data, h);
561 return CURLE_OK;
562}
563
564#endif /* CURL_DISABLE_HTTP || CURL_DISABLE_HSTS */
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette