/src/wget2/libwget/hpkp_db.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright (c) 2015-2024 Free Software Foundation, Inc. |
3 | | * |
4 | | * This file is part of libwget. |
5 | | * |
6 | | * Libwget is free software: you can redistribute it and/or modify |
7 | | * it under the terms of the GNU Lesser General Public License as published by |
8 | | * the Free Software Foundation, either version 3 of the License, or |
9 | | * (at your option) any later version. |
10 | | * |
11 | | * Libwget is distributed in the hope that it will be useful, |
12 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
14 | | * GNU Lesser General Public License for more details. |
15 | | * |
16 | | * You should have received a copy of the GNU Lesser General Public License |
17 | | * along with libwget. If not, see <https://www.gnu.org/licenses/>. |
18 | | * |
19 | | * |
20 | | * HTTP Public Key Pinning database |
21 | | */ |
22 | | |
23 | | #include <config.h> |
24 | | |
25 | | #include <wget.h> |
26 | | #include <string.h> |
27 | | #include <stddef.h> |
28 | | #include <ctype.h> |
29 | | #include <sys/stat.h> |
30 | | #include <limits.h> |
31 | | #include "private.h" |
32 | | #include "hpkp.h" |
33 | | |
34 | | /** |
35 | | * \ingroup libwget-hpkp |
36 | | * |
37 | | * HTTP Public Key Pinning (RFC 7469) database implementation |
38 | | * |
39 | | * @{ |
40 | | */ |
41 | | |
42 | | struct wget_hpkp_db_st { |
43 | | char * |
44 | | fname; |
45 | | wget_hashmap * |
46 | | entries; |
47 | | wget_thread_mutex |
48 | | mutex; |
49 | | int64_t |
50 | | load_time; |
51 | | }; |
52 | | |
53 | | /// Pointer to the function table |
54 | | static const wget_hpkp_db_vtable |
55 | | *plugin_vtable; |
56 | | |
57 | | void wget_hpkp_set_plugin(const wget_hpkp_db_vtable *vtable) |
58 | 0 | { |
59 | 0 | plugin_vtable = vtable; |
60 | 0 | } |
61 | | |
62 | | #ifdef __clang__ |
63 | | __attribute__((no_sanitize("integer"))) |
64 | | #endif |
65 | | WGET_GCC_PURE |
66 | | static unsigned int hash_hpkp(const wget_hpkp *hpkp) |
67 | 6.87k | { |
68 | 6.87k | unsigned int hash = 0; |
69 | 6.87k | const unsigned char *p; |
70 | | |
71 | 27.8k | for (p = (unsigned char *)hpkp->host; *p; p++) |
72 | 20.9k | hash = hash * 101 + *p; // possible integer overflow, suppression above |
73 | | |
74 | 6.87k | return hash; |
75 | 6.87k | } |
76 | | |
77 | | WGET_GCC_NONNULL_ALL WGET_GCC_PURE |
78 | | static int compare_hpkp(const wget_hpkp *h1, const wget_hpkp *h2) |
79 | 1.28k | { |
80 | 1.28k | return strcmp(h1->host, h2->host); |
81 | 1.28k | } |
82 | | |
83 | | /** |
84 | | * \param[in] hpkp_db Pointer to the pointer of an HPKP database, provided by wget_hpkp_db_init() |
85 | | * |
86 | | * Frees all resources allocated for the HPKP database, except for the structure. |
87 | | * |
88 | | * Works only for databases created by wget_hpkp_db_init(). |
89 | | * The parameter \p hpkp_db can then be passed to \ref wget_hpkp_db_init "wget_hpkp_db_init()". |
90 | | * |
91 | | * If \p hpkp_db is NULL then this function does nothing. |
92 | | */ |
93 | | void wget_hpkp_db_deinit(wget_hpkp_db *hpkp_db) |
94 | 1.72k | { |
95 | 1.72k | if (plugin_vtable) { |
96 | 0 | plugin_vtable->deinit(hpkp_db); |
97 | 0 | return; |
98 | 0 | } |
99 | | |
100 | 1.72k | if (hpkp_db) { |
101 | 1.72k | xfree(hpkp_db->fname); |
102 | 1.72k | wget_thread_mutex_lock(hpkp_db->mutex); |
103 | 1.72k | wget_hashmap_free(&hpkp_db->entries); |
104 | 1.72k | wget_thread_mutex_unlock(hpkp_db->mutex); |
105 | | |
106 | 1.72k | wget_thread_mutex_destroy(&hpkp_db->mutex); |
107 | 1.72k | } |
108 | 1.72k | } |
109 | | |
110 | | /** |
111 | | * \param[in] hpkp_db Pointer to the pointer of an HPKP database |
112 | | * |
113 | | * Closes and frees the HPKP database. A double pointer is required because this function will |
114 | | * set the handle (pointer) to the HPKP database to NULL to prevent potential use-after-free conditions. |
115 | | * |
116 | | * Newly added entries will be lost unless committed to persistent storage using wget_hsts_db_save(). |
117 | | * |
118 | | * If \p hpkp_db or the pointer it points to is NULL then this function does nothing. |
119 | | */ |
120 | | void wget_hpkp_db_free(wget_hpkp_db **hpkp_db) |
121 | 11.9k | { |
122 | 11.9k | if (plugin_vtable) { |
123 | 0 | plugin_vtable->free(hpkp_db); |
124 | 0 | return; |
125 | 0 | } |
126 | | |
127 | 11.9k | if (hpkp_db && *hpkp_db) { |
128 | 1.72k | wget_hpkp_db_deinit(*hpkp_db); |
129 | 1.72k | xfree(*hpkp_db); |
130 | 1.72k | } |
131 | 11.9k | } |
132 | | |
133 | | /** |
134 | | * \param[in] hpkp_db An HPKP database |
135 | | * \param[in] host The hostname in question. |
136 | | * \param[in] pubkey The public key in DER format |
137 | | * \param[in] pubkeysize Size of `pubkey` |
138 | | * \return 1 if both host and public key was found in the database, |
139 | | * -2 if host was found and public key was not found, |
140 | | * 0 if host was not found, |
141 | | * -1 for any other error condition. |
142 | | * |
143 | | * Checks the validity of the given hostname and public key combination. |
144 | | * |
145 | | * This function is thread-safe and can be called from multiple threads concurrently. |
146 | | * Any implementation for this function must be thread-safe as well. |
147 | | */ |
148 | | int wget_hpkp_db_check_pubkey(wget_hpkp_db *hpkp_db, const char *host, const void *pubkey, size_t pubkeysize) |
149 | 1.72k | { |
150 | 1.72k | if (plugin_vtable) |
151 | 0 | return plugin_vtable->check_pubkey(hpkp_db, host, pubkey, pubkeysize); |
152 | | |
153 | 1.72k | wget_hpkp *hpkp = NULL; |
154 | 1.72k | int subdomain = 0; |
155 | 1.72k | char digest[32]; |
156 | 1.72k | size_t digestlen = wget_hash_get_len(WGET_DIGTYPE_SHA256); |
157 | | |
158 | 1.72k | if (digestlen > sizeof(digest)) { |
159 | 0 | error_printf(_("%s: Unexpected hash len %zu > %zu\n"), __func__, digestlen, sizeof(digest)); |
160 | 0 | return -1; |
161 | 0 | } |
162 | | |
163 | 5.14k | for (const char *domain = host; *domain && !hpkp; domain = strchrnul(domain, '.')) { |
164 | 5.12k | while (*domain == '.') |
165 | 1.70k | domain++; |
166 | | |
167 | 3.42k | wget_hpkp key = { .host = domain }; |
168 | | |
169 | 3.42k | if (!wget_hashmap_get(hpkp_db->entries, &key, &hpkp)) |
170 | 3.07k | subdomain = 1; |
171 | 3.42k | } |
172 | | |
173 | 1.72k | if (!hpkp) |
174 | 1.37k | return 0; // OK, host is not in database |
175 | | |
176 | 350 | if (subdomain && !hpkp->include_subdomains) |
177 | 24 | return 0; // OK, found a matching super domain which isn't responsible for <host> |
178 | | |
179 | 326 | if (wget_hash_fast(WGET_DIGTYPE_SHA256, pubkey, pubkeysize, digest)) |
180 | 0 | return -1; |
181 | | |
182 | 326 | wget_hpkp_pin pinkey = { .pin = digest, .pinsize = digestlen, .hash_type = "sha256" }; |
183 | | |
184 | 326 | if (wget_vector_find(hpkp->pins, &pinkey) != -1) |
185 | 0 | return 1; // OK, pinned pubkey found |
186 | | |
187 | 326 | return -2; |
188 | 326 | } |
189 | | |
190 | | /* We 'consume' _hpkp and thus set *_hpkp to NULL, so that the calling function |
191 | | * can't access it any more */ |
192 | | /** |
193 | | * \param[in] hpkp_db An HPKP database |
194 | | * \param[in] hpkp pointer to HPKP database entry (will be set to NULL) |
195 | | * |
196 | | * Adds an entry to given HPKP database. The entry will replace any entry with same `host` (see wget_hpkp_set_host()). |
197 | | * If `maxage` property of `hpkp` is zero, any existing entry with same `host` property will be removed. |
198 | | * |
199 | | * The database takes the ownership of the HPKP entry and the calling function must not access the entry afterwards. |
200 | | * |
201 | | * This function is thread-safe and can be called from multiple threads concurrently. |
202 | | * Any implementation for this function must be thread-safe as well. |
203 | | */ |
204 | | void wget_hpkp_db_add(wget_hpkp_db *hpkp_db, wget_hpkp **_hpkp) |
205 | 4.77k | { |
206 | 4.77k | if (plugin_vtable) { |
207 | 0 | plugin_vtable->add(hpkp_db, _hpkp); |
208 | 0 | *_hpkp = NULL; |
209 | 0 | return; |
210 | 0 | } |
211 | | |
212 | 4.77k | if (!_hpkp || !*_hpkp) |
213 | 2.70k | return; |
214 | | |
215 | 2.07k | wget_hpkp *hpkp = *_hpkp; |
216 | | |
217 | 2.07k | wget_thread_mutex_lock(hpkp_db->mutex); |
218 | | |
219 | 2.07k | if (hpkp->maxage == 0 || wget_vector_size(hpkp->pins) == 0) { |
220 | 650 | if (wget_hashmap_remove(hpkp_db->entries, hpkp)) |
221 | 28 | debug_printf("removed HPKP %s\n", hpkp->host); |
222 | 650 | wget_hpkp_free(hpkp); |
223 | 1.42k | } else { |
224 | 1.42k | wget_hpkp *old; |
225 | | |
226 | 1.42k | if (wget_hashmap_get(hpkp_db->entries, hpkp, &old)) { |
227 | 51 | old->created = hpkp->created; |
228 | 51 | old->maxage = hpkp->maxage; |
229 | 51 | old->expires = hpkp->expires; |
230 | 51 | old->include_subdomains = hpkp->include_subdomains; |
231 | 51 | wget_vector_free(&old->pins); |
232 | 51 | old->pins = hpkp->pins; |
233 | 51 | hpkp->pins = NULL; |
234 | 51 | debug_printf("update HPKP %s (maxage=%lld, includeSubDomains=%d)\n", old->host, (long long)old->maxage, old->include_subdomains); |
235 | 51 | wget_hpkp_free(hpkp); |
236 | 1.37k | } else { |
237 | | // key and value are the same to make wget_hashmap_get() return old 'hpkp' |
238 | | /* debug_printf("add HPKP %s (maxage=%lld, includeSubDomains=%d)\n", hpkp->host, (long long)hpkp->maxage, hpkp->include_subdomains); */ |
239 | 1.37k | wget_hashmap_put(hpkp_db->entries, hpkp, hpkp); |
240 | | // no need to free anything here |
241 | 1.37k | } |
242 | 1.42k | } |
243 | | |
244 | 2.07k | wget_thread_mutex_unlock(hpkp_db->mutex); |
245 | | |
246 | 2.07k | *_hpkp = NULL; |
247 | 2.07k | } |
248 | | |
249 | | static int hpkp_db_load(wget_hpkp_db *hpkp_db, FILE *fp) |
250 | 1.72k | { |
251 | 1.72k | int64_t created, max_age; |
252 | 1.72k | long long _created, _max_age; |
253 | 1.72k | int include_subdomains; |
254 | | |
255 | 1.72k | wget_hpkp *hpkp = NULL; |
256 | 1.72k | struct stat st; |
257 | 1.72k | char *buf = NULL; |
258 | 1.72k | size_t bufsize = 0; |
259 | 1.72k | ssize_t buflen; |
260 | 1.72k | char hash_type[32], host[256], pin_b64[256]; |
261 | 1.72k | int64_t now = time(NULL); |
262 | | |
263 | | // if the database file hasn't changed since the last read |
264 | | // there's no need to reload |
265 | | |
266 | 1.72k | if (fstat(fileno(fp), &st) == 0) { |
267 | 0 | if (st.st_mtime != hpkp_db->load_time) |
268 | 0 | hpkp_db->load_time = st.st_mtime; |
269 | 0 | else |
270 | 0 | return 0; |
271 | 0 | } |
272 | | |
273 | 9.47k | while ((buflen = wget_getline(&buf, &bufsize, fp)) >= 0) { |
274 | 7.75k | char *linep = buf; |
275 | | |
276 | 7.75k | while (isspace(*linep)) linep++; // ignore leading whitespace |
277 | 7.75k | if (!*linep) continue; // skip empty lines |
278 | | |
279 | 6.58k | if (*linep == '#') |
280 | 197 | continue; // skip comments |
281 | | |
282 | | // strip off \r\n |
283 | 6.39k | while (buflen > 0 && (buf[buflen] == '\n' || buf[buflen] == '\r')) |
284 | 0 | buf[--buflen] = 0; |
285 | | |
286 | 6.39k | if (*linep != '*') { |
287 | 3.05k | wget_hpkp_db_add(hpkp_db, &hpkp); |
288 | | |
289 | 3.05k | if (sscanf(linep, "%255s %d %lld %lld", host, &include_subdomains, &_created, &_max_age) == 4) { |
290 | 2.60k | created = _created; |
291 | 2.60k | max_age = _max_age; |
292 | 2.60k | if (created < 0 || max_age < 0 || created >= INT64_MAX / 2 || max_age >= INT64_MAX / 2) { |
293 | 346 | max_age = 0; // avoid integer overflow here |
294 | 346 | } |
295 | 2.60k | int64_t expires = created + max_age; |
296 | 2.60k | if (max_age && expires >= now) { |
297 | 2.07k | hpkp = wget_hpkp_new(); |
298 | 2.07k | if (hpkp) { |
299 | 2.07k | if (!(hpkp->host = wget_strdup(host))) |
300 | 0 | xfree(hpkp); |
301 | 2.07k | else { |
302 | 2.07k | hpkp->maxage = max_age; |
303 | 2.07k | hpkp->created = created; |
304 | 2.07k | hpkp->expires = expires; |
305 | 2.07k | hpkp->include_subdomains = include_subdomains != 0; |
306 | 2.07k | } |
307 | 2.07k | } |
308 | 2.07k | } else |
309 | 527 | debug_printf("HPKP: entry '%s' is expired\n", host); |
310 | 2.60k | } else { |
311 | 455 | error_printf(_("HPKP: could not parse host line '%s'\n"), buf); |
312 | 455 | } |
313 | 3.33k | } else if (hpkp) { |
314 | 2.94k | if (sscanf(linep, "*%31s %255s", hash_type, pin_b64) == 2) { |
315 | 2.87k | wget_hpkp_pin_add(hpkp, hash_type, pin_b64); |
316 | 2.87k | } else { |
317 | 73 | error_printf(_("HPKP: could not parse pin line '%s'\n"), buf); |
318 | 73 | } |
319 | 2.94k | } else { |
320 | 392 | debug_printf("HPKP: skipping PIN entry: '%s'\n", buf); |
321 | 392 | } |
322 | 6.39k | } |
323 | | |
324 | 1.72k | wget_hpkp_db_add(hpkp_db, &hpkp); |
325 | | |
326 | 1.72k | xfree(buf); |
327 | | |
328 | 1.72k | if (ferror(fp)) { |
329 | 0 | hpkp_db->load_time = 0; // reload on next call to this function |
330 | 0 | return -1; |
331 | 0 | } |
332 | | |
333 | 1.72k | return 0; |
334 | 1.72k | } |
335 | | |
336 | | /** |
337 | | * \param[in] hpkp_db Handle to an HPKP database, obtained with wget_hpkp_db_init() |
338 | | * \return 0 on success, or a negative number on error |
339 | | * |
340 | | * Performs all operations necessary to access the HPKP database entries from persistent storage |
341 | | * using wget_hpkp_db_check_pubkey() for example. |
342 | | * |
343 | | * For databases created by wget_hpkp_db_init() data is loaded from `fname` parameter of wget_hpkp_db_init(). |
344 | | * If this function cannot correctly parse the whole file, -1 is returned. |
345 | | * |
346 | | * If `hpkp_db` is NULL then this function returns 0 and does nothing else. |
347 | | */ |
348 | | int wget_hpkp_db_load(wget_hpkp_db *hpkp_db) |
349 | 1.72k | { |
350 | 1.72k | if (plugin_vtable) |
351 | 0 | return plugin_vtable->load(hpkp_db); |
352 | | |
353 | 1.72k | if (!hpkp_db) |
354 | 0 | return 0; |
355 | | |
356 | 1.72k | if (!hpkp_db->fname || !*hpkp_db->fname) |
357 | 0 | return 0; |
358 | | |
359 | 1.72k | if (wget_update_file(hpkp_db->fname, (wget_update_load_fn *) hpkp_db_load, NULL, hpkp_db)) { |
360 | 0 | error_printf(_("Failed to read HPKP data\n")); |
361 | 0 | return -1; |
362 | 1.72k | } else { |
363 | 1.72k | debug_printf("Fetched HPKP data from '%s'\n", hpkp_db->fname); |
364 | 1.72k | return 0; |
365 | 1.72k | } |
366 | 1.72k | } |
367 | | |
368 | | static int hpkp_save_pin(void *_fp, void *_pin) |
369 | 0 | { |
370 | 0 | FILE *fp = _fp; |
371 | 0 | wget_hpkp_pin *pin = _pin; |
372 | |
|
373 | 0 | wget_fprintf(fp, "*%s %s\n", pin->hash_type, pin->pin_b64); |
374 | |
|
375 | 0 | if (ferror(fp)) |
376 | 0 | return -1; |
377 | | |
378 | 0 | return 0; |
379 | 0 | } |
380 | | |
381 | | WGET_GCC_NONNULL_ALL |
382 | | static int hpkp_save(void *_fp, const void *_hpkp, WGET_GCC_UNUSED void *v) |
383 | 0 | { |
384 | 0 | FILE *fp = _fp; |
385 | 0 | const wget_hpkp *hpkp = _hpkp; |
386 | |
|
387 | 0 | if (wget_vector_size(hpkp->pins) == 0) |
388 | 0 | debug_printf("HPKP: drop '%s', no PIN entries\n", hpkp->host); |
389 | 0 | else if (hpkp->expires < time(NULL)) |
390 | 0 | debug_printf("HPKP: drop '%s', expired\n", hpkp->host); |
391 | 0 | else { |
392 | 0 | wget_fprintf(fp, "%s %d %lld %lld\n", hpkp->host, hpkp->include_subdomains, (long long) hpkp->created, (long long) hpkp->maxage); |
393 | |
|
394 | 0 | if (ferror(fp)) |
395 | 0 | return -1; |
396 | | |
397 | 0 | return wget_vector_browse(hpkp->pins, hpkp_save_pin, fp); |
398 | 0 | } |
399 | | |
400 | 0 | return 0; |
401 | 0 | } |
402 | | |
403 | | static int hpkp_db_save(wget_hpkp_db *hpkp_db, FILE *fp) |
404 | 0 | { |
405 | 0 | wget_hashmap *entries = hpkp_db->entries; |
406 | |
|
407 | 0 | if (wget_hashmap_size(entries) > 0) { |
408 | 0 | fputs("# HPKP 1.0 file\n", fp); |
409 | 0 | fputs("#Generated by libwget " PACKAGE_VERSION ". Edit at your own risk.\n", fp); |
410 | 0 | fputs("#<hostname> <incl. subdomains> <created> <max-age>\n\n", fp); |
411 | |
|
412 | 0 | if (ferror(fp)) |
413 | 0 | return -1; |
414 | | |
415 | 0 | return wget_hashmap_browse(entries, hpkp_save, fp); |
416 | 0 | } |
417 | | |
418 | 0 | return 0; |
419 | 0 | } |
420 | | |
421 | | /** |
422 | | * \param[in] hpkp_db Handle to an HPKP database |
423 | | * \return 0 if the operation was successful, negative number in case of error. |
424 | | * |
425 | | * Saves the current HPKP database to persistent storage |
426 | | * |
427 | | * In case of databases created by wget_hpkp_db_init(), HPKP entries will be saved into file specified by |
428 | | * \p fname parameter of wget_hpkp_db_init(). In case of failure -1 will be returned with errno set. |
429 | | * |
430 | | * If \p fname is NULL then this function returns -1 and does nothing else. |
431 | | */ |
432 | | int wget_hpkp_db_save(wget_hpkp_db *hpkp_db) |
433 | 0 | { |
434 | 0 | if (plugin_vtable) |
435 | 0 | return plugin_vtable->save(hpkp_db); |
436 | | |
437 | 0 | if (!hpkp_db) |
438 | 0 | return -1; |
439 | | |
440 | 0 | int size; |
441 | |
|
442 | 0 | if (!hpkp_db->fname || !*hpkp_db->fname) |
443 | 0 | return -1; |
444 | | |
445 | 0 | if (wget_update_file(hpkp_db->fname, |
446 | 0 | (wget_update_load_fn *) hpkp_db_load, |
447 | 0 | (wget_update_load_fn *) hpkp_db_save, |
448 | 0 | hpkp_db)) |
449 | 0 | { |
450 | 0 | error_printf(_("Failed to write HPKP file '%s'\n"), hpkp_db->fname); |
451 | 0 | return -1; |
452 | 0 | } |
453 | | |
454 | 0 | if ((size = wget_hashmap_size(hpkp_db->entries))) |
455 | 0 | debug_printf("Saved %d HPKP entr%s into '%s'\n", size, size != 1 ? "ies" : "y", hpkp_db->fname); |
456 | 0 | else |
457 | 0 | debug_printf("No HPKP entries to save. Table is empty.\n"); |
458 | |
|
459 | 0 | return 0; |
460 | 0 | } |
461 | | |
462 | | /** |
463 | | * \param[in] hpkp_db Older HPKP database already passed to wget_hpkp_db_deinit(), or NULL |
464 | | * \param[in] fname Name of the file where the data should be stored, or NULL |
465 | | * \return Handle (pointer) to an HPKP database |
466 | | * |
467 | | * Constructor for the default implementation of HSTS database. |
468 | | * |
469 | | * This function does no file IO, data is loaded from file specified by `fname` when wget_hpkp_db_load() is called. |
470 | | * The entries in the file are subject to sanity checks as if they were added to the HPKP database |
471 | | * via wget_hpkp_db_add(). In particular, if an entry is expired due to `creation_time + max_age > cur_time` |
472 | | * it will not be added to the database, and a subsequent call to wget_hpkp_db_save() with the same `hpkp_db_priv` |
473 | | * handle and file name will overwrite the file without all the expired entries. |
474 | | * |
475 | | * Since the format of the file might change without notice, hand-crafted files are discouraged. |
476 | | * To create an HPKP database file that is guaranteed to be correctly parsed by this function, |
477 | | * wget_hpkp_db_save() should be used. |
478 | | * |
479 | | */ |
480 | | wget_hpkp_db *wget_hpkp_db_init(wget_hpkp_db *hpkp_db, const char *fname) |
481 | 1.72k | { |
482 | 1.72k | if (plugin_vtable) |
483 | 0 | return plugin_vtable->init(hpkp_db, fname); |
484 | | |
485 | 1.72k | if (!hpkp_db) { |
486 | 1.72k | hpkp_db = wget_calloc(1, sizeof(struct wget_hpkp_db_st)); |
487 | 1.72k | if (!hpkp_db) |
488 | 0 | return NULL; |
489 | 1.72k | } else |
490 | 0 | memset(hpkp_db, 0, sizeof(*hpkp_db)); |
491 | | |
492 | 1.72k | if (fname) |
493 | 1.72k | hpkp_db->fname = wget_strdup(fname); |
494 | 1.72k | hpkp_db->entries = wget_hashmap_create(16, (wget_hashmap_hash_fn *) hash_hpkp, (wget_hashmap_compare_fn *) compare_hpkp); |
495 | 1.72k | wget_hashmap_set_key_destructor(hpkp_db->entries, (wget_hashmap_key_destructor *) wget_hpkp_free); |
496 | | |
497 | | /* |
498 | | * Keys and values for the hashmap are 'hpkp' entries, so value == key. |
499 | | * The hash function hashes hostname. |
500 | | * The compare function compares hostname. |
501 | | * |
502 | | * Since the value == key, we just need the value destructor for freeing hashmap entries. |
503 | | */ |
504 | | |
505 | 1.72k | wget_thread_mutex_init(&hpkp_db->mutex); |
506 | | |
507 | 1.72k | return hpkp_db; |
508 | 1.72k | } |
509 | | |
510 | | /** |
511 | | * \param[in] hpkp_db HPKP database created using wget_hpkp_db_init() |
512 | | * \param[in] fname Name of the file where the data should be stored, or NULL |
513 | | * |
514 | | * Changes the file where data should be stored. Works only for databases created by wget_hpkp_db_init(). |
515 | | * This function does no file IO, data is loaded when wget_hpkp_db_load() is called. |
516 | | */ |
517 | | void wget_hpkp_db_set_fname(wget_hpkp_db *hpkp_db, const char *fname) |
518 | 0 | { |
519 | 0 | xfree(hpkp_db->fname); |
520 | 0 | hpkp_db->fname = wget_strdup(fname); |
521 | 0 | } |
522 | | |
523 | | /**@}*/ |