Move libxml2 linker flags to the end of LDFLAGS.
[pwmd.git] / src / cache.c
blobf8f82ccd97e9c2a15db3e130ba378a8a5aba9f1f
1 /*
2 Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012
3 Ben Kibbey <bjk@luxsci.net>
5 This file is part of pwmd.
7 Pwmd is free software: you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation, either version 2 of the License, or
10 (at your option) any later version.
12 Pwmd is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with Pwmd. If not, see <http://www.gnu.org/licenses/>.
20 #ifdef HAVE_CONFIG_H
21 #include <config.h>
22 #endif
24 #include <stdio.h>
25 #include <unistd.h>
26 #include <pthread.h>
27 #include "pwmd-error.h"
28 #include <gcrypt.h>
29 #include <ctype.h>
31 #include "agent.h"
32 #include "mutex.h"
33 #include "cache.h"
34 #include "status.h"
35 #include "mem.h"
36 #include "util-misc.h"
37 #include "util-string.h"
39 #ifdef HAVE_TIME_H
40 #include <time.h>
41 #endif
43 static pthread_mutex_t cache_mutex;
44 static struct slist_s *key_cache;
45 static struct agent_s *cache_agent;
47 extern void log_write(const char *fmt, ...);
49 typedef gpg_error_t (*free_data_fn_t)(file_cache_t *);
50 static free_data_fn_t free_data_fn;
51 static gpg_error_t clear_once(file_cache_t *p);
52 static int remove_entry(const unsigned char *md5file);
53 static void free_entry(file_cache_t *p);
55 static file_cache_t *get_entry(const unsigned char *md5file)
57 int t = slist_length(key_cache);
58 int i;
60 if (!md5file)
61 return NULL;
63 for (i = 0; i < t; i++) {
64 file_cache_t *p = slist_nth_data(key_cache, i);
66 if (!memcmp(p->filename, md5file, sizeof(p->filename)))
67 return p;
70 return NULL;
73 gpg_error_t cache_lock_mutex(void * ctx, const unsigned char *md5file,
74 long lock_timeout, int add, int timeout)
76 MUTEX_LOCK(&cache_mutex);
77 gpg_error_t rc = 0;
78 file_cache_t *p = get_entry(md5file);
80 if (p) {
81 MUTEX_UNLOCK(&cache_mutex);
82 MUTEX_TRYLOCK(ctx, p->mutex, rc, lock_timeout);
84 else if (add) {
85 if (cache_add_file(md5file, NULL, NULL, timeout)) {
86 p = get_entry(md5file);
87 MUTEX_UNLOCK(&cache_mutex);
88 MUTEX_TRYLOCK(ctx, p->mutex, rc, lock_timeout);
90 else {
91 MUTEX_UNLOCK(&cache_mutex);
92 rc = GPG_ERR_ENOMEM;
95 else {
96 MUTEX_UNLOCK(&cache_mutex);
99 return !p && !rc ? GPG_ERR_NO_DATA : rc;
102 static int remove_entry(const unsigned char *md5file)
104 MUTEX_LOCK(&cache_mutex);
105 file_cache_t *p = get_entry(md5file);
107 pthread_cleanup_push(cleanup_mutex_cb, &cache_mutex);
108 if (p) {
109 /* Keep a refcount because another client maybe editing this same new
110 * file. The entry needs to be kept in case the other client SAVE's.
112 p->refcount--;
113 if (!p->refcount)
114 free_entry(p);
117 pthread_cleanup_pop(1);
118 return p ? 1 : 0;
121 gpg_error_t cache_unlock_mutex(const unsigned char *md5file, int remove)
123 MUTEX_LOCK(&cache_mutex);
124 file_cache_t *p = get_entry(md5file);
126 if (p) {
127 MUTEX_UNLOCK(p->mutex);
128 if (remove)
129 remove_entry(md5file);
132 MUTEX_UNLOCK(&cache_mutex);
133 return p ? 0 : GPG_ERR_NO_DATA;
136 static int valid_grip(file_cache_t *e)
138 int c;
139 size_t len;
141 for (c = len = 0; len < sizeof(e->grip); len++, c++) {
142 if (e->grip[c])
143 return 1;
146 return 0;
149 static gpg_error_t iscached(const unsigned char *md5file, int *defer)
151 gpg_error_t rc;
152 file_cache_t *p = get_entry(md5file);
153 char *line;
155 if (!p || !valid_grip(p) || !p->data)
156 return GPG_ERR_NO_DATA;
158 rc = send_to_agent(cache_agent, &line, NULL, "KEYINFO --data %s", p->grip);
159 if (!rc) {
160 char **fields = str_split(line, " ", 0);
162 /* Smartcard with a cached document. */
163 if (*fields[1] == 'T' && p->data->doc)
164 rc = 0;
165 else
166 rc = isdigit(*fields[4]) || *fields[5] == 'C' ? 0 : GPG_ERR_NO_DATA;
167 strv_free(fields);
168 xfree(line);
171 if (!rc && defer)
172 *defer = p->defer_clear;
174 return rc;
177 unsigned cache_file_count()
179 MUTEX_LOCK(&cache_mutex);
180 unsigned total = 0, i, n;
182 pthread_cleanup_push(cleanup_mutex_cb, &cache_mutex);
183 n = slist_length(key_cache);
184 for (i = 0; i < n; i++) {
185 file_cache_t *p = slist_nth_data(key_cache, i);
187 if (!iscached(p->filename, NULL))
188 total++;
191 pthread_cleanup_pop(1);
192 return total;
195 void cache_adjust_timeout()
197 MUTEX_LOCK(&cache_mutex);
198 int t = slist_length(key_cache);
199 int i;
201 pthread_cleanup_push(cleanup_mutex_cb, &cache_mutex);
203 for (i = 0; i < t; i++) {
204 file_cache_t *p = slist_nth_data(key_cache, i);
206 if (p->timeout > 0)
207 p->timeout--;
209 if (!p->timeout) {
210 /* The file associated with this cache entry may be locked by a
211 * client. Defer freeing this cache entry until the next timeout
212 * check. */
213 if (!clear_once(p))
214 send_status_all(STATUS_CACHE, NULL);
218 pthread_cleanup_pop(1);
221 static void setgrip(file_cache_t *p, const unsigned char *grip)
223 if (grip) {
224 char *tmp = bin2hex(grip, 20);
226 memcpy(p->grip, tmp, sizeof(p->grip));
227 xfree(tmp);
231 static gpg_error_t set_timeout(const unsigned char *md5file, int timeout,
232 int defer)
234 MUTEX_LOCK(&cache_mutex);
235 file_cache_t *p = get_entry(md5file);
236 gpg_error_t rc = 0;
238 if (!p) {
239 MUTEX_UNLOCK(&cache_mutex);
240 return GPG_ERR_NOT_FOUND;
243 p->reset = timeout;
244 if (p->timeout == -1 || timeout == -1)
245 p->timeout = timeout;
247 if (!timeout || defer) {
248 rc = clear_once(p);
249 if (!rc)
250 send_status_all(STATUS_CACHE, NULL);
253 MUTEX_UNLOCK(&cache_mutex);
254 return rc;
257 gpg_error_t cache_set_data(const unsigned char *md5file, struct cache_data_s *data,
258 const unsigned char *grip)
260 MUTEX_LOCK(&cache_mutex);
261 file_cache_t *p = get_entry(md5file);
263 if (p) {
264 setgrip(p, grip);
265 p->data = data;
266 set_timeout(md5file, p->reset, p->defer_clear);
267 if (!p->reset) {
268 clear_once(p);
269 send_status_all(STATUS_CACHE, NULL);
273 MUTEX_UNLOCK(&cache_mutex);
274 return p ? 0 : GPG_ERR_NO_DATA;
277 struct cache_data_s *cache_get_data(const unsigned char *md5file)
279 MUTEX_LOCK(&cache_mutex);
280 file_cache_t *p = get_entry(md5file);
282 MUTEX_UNLOCK(&cache_mutex);
283 return p ? p->data : NULL;
286 int cache_add_file(const unsigned char *md5file, const unsigned char *grip,
287 struct cache_data_s *data, int timeout)
289 MUTEX_LOCK(&cache_mutex);
290 file_cache_t *p = get_entry(md5file);
291 struct slist_s *new;
292 int b;
294 if (p) {
295 setgrip(p, grip);
296 p->data = data;
297 p->refcount++;
298 b = set_timeout(md5file, timeout, p->defer_clear) == 0 ? 1 : 0;
299 MUTEX_UNLOCK(&cache_mutex);
300 send_status_all(STATUS_CACHE, NULL);
301 return b;
304 p = xcalloc(1, sizeof(file_cache_t));
305 if (!p) {
306 MUTEX_UNLOCK(&cache_mutex);
307 return 0;
310 p->mutex = (pthread_mutex_t *)xmalloc(sizeof(pthread_mutex_t));
311 if (!p->mutex) {
312 xfree(p);
313 MUTEX_UNLOCK(&cache_mutex);
314 return 0;
317 pthread_mutexattr_t attr;
318 pthread_mutexattr_init(&attr);
319 pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
320 pthread_mutex_init(p->mutex, &attr);
321 pthread_mutexattr_destroy(&attr);
322 memcpy(p->filename, md5file, sizeof(p->filename));
323 setgrip(p, grip);
324 p->reset = p->timeout = timeout;
325 p->data = data;
326 p->refcount++;
327 new = slist_append(key_cache, p);
328 if (!new) {
329 pthread_mutex_destroy(p->mutex);
330 xfree(p->mutex);
331 xfree(p);
332 MUTEX_UNLOCK(&cache_mutex);
333 return 0;
336 key_cache = new;
337 MUTEX_UNLOCK(&cache_mutex);
338 send_status_all(STATUS_CACHE, NULL);
339 return 1;
342 static gpg_error_t clear_once(file_cache_t *p)
344 gpg_error_t rc = free_data_fn(p);
346 if (!rc && valid_grip(p)) {
347 rc = send_to_agent(cache_agent, NULL, NULL,
348 "CLEAR_PASSPHRASE --mode=normal %s", p->grip);
349 if (rc)
350 log_write("%s(): %s", __FUNCTION__, pwmd_strerror(rc));
352 memset(p->grip, 0, sizeof(p->grip));
355 return rc;
358 static void free_entry(file_cache_t *p)
360 gpg_error_t rc;
362 if (!p)
363 return;
365 rc = clear_once(p);
366 if (rc)
367 log_write("%s(): %s", __FUNCTION__, pwmd_strerror(rc));
369 if (p->mutex) {
370 pthread_mutex_destroy(p->mutex);
371 xfree(p->mutex);
374 key_cache = slist_remove(key_cache, p);
375 xfree(p);
378 gpg_error_t cache_clear(const unsigned char *md5file)
380 file_cache_t *p;
381 gpg_error_t rc = 0;
382 int i, t;
384 MUTEX_LOCK(&cache_mutex);
386 if (md5file) {
387 p = get_entry(md5file);
388 if (!p) {
389 MUTEX_UNLOCK(&cache_mutex);
390 return 0;
393 pthread_cleanup_push(cleanup_mutex_cb, &cache_mutex);
394 rc = clear_once(p);
395 pthread_cleanup_pop(1);
396 if (rc)
397 log_write("%s(): %s", __FUNCTION__, pwmd_strerror(rc));
399 return rc;
402 pthread_cleanup_push(cleanup_mutex_cb, &cache_mutex);
403 t = slist_length(key_cache);
404 for (i = 0; i < t; i++) {
405 p = slist_nth_data(key_cache, i);
406 clear_once(p);
409 pthread_cleanup_pop(1);
410 return 0;
413 gpg_error_t cache_iscached(const char *filename, int *defer)
415 gpg_error_t rc;
416 unsigned char md5file[16];
418 if (access(filename, R_OK) == -1)
419 return gpg_error_from_syserror();
421 MUTEX_LOCK(&cache_mutex);
422 pthread_cleanup_push(cleanup_mutex_cb, &cache_mutex);
423 gcry_md_hash_buffer(GCRY_MD_MD5, md5file, filename, strlen(filename));
424 rc = iscached(md5file, defer);
425 pthread_cleanup_pop(1);
426 return rc;
429 gpg_error_t cache_defer_clear(const unsigned char *md5file)
431 MUTEX_LOCK(&cache_mutex);
432 file_cache_t *p = get_entry(md5file);
433 gpg_error_t rc = 0;
435 if (!p)
436 rc = GPG_ERR_NOT_FOUND;
437 else
438 p->defer_clear = 1;
440 MUTEX_UNLOCK(&cache_mutex);
441 return rc;
444 gpg_error_t cache_set_timeout(const unsigned char *md5file, int timeout)
446 return set_timeout(md5file, timeout, 0);
449 int cache_get_grip(const unsigned char *md5file, unsigned char *grip)
451 MUTEX_LOCK(&cache_mutex);
452 file_cache_t *p = get_entry(md5file);
454 if (!p || !valid_grip(p)) {
455 MUTEX_UNLOCK(&cache_mutex);
456 return 0;
459 memcpy(grip, p->grip, sizeof(p->grip));
460 MUTEX_UNLOCK(&cache_mutex);
461 return 1;
464 void cache_deinit()
466 MUTEX_LOCK(&cache_mutex);
467 file_cache_t *p;
469 pthread_cleanup_push(cleanup_mutex_cb, &cache_mutex);
470 while ((p = slist_nth_data(key_cache, 0)))
471 free_entry(p);
473 if (cache_agent)
474 cleanup_agent(cache_agent);
476 cache_agent = NULL;
477 gcry_free(cache_key);
478 xfree(cache_iv);
479 cache_key = NULL;
480 cache_iv = NULL;
481 pthread_cleanup_pop(1);
482 pthread_mutex_destroy(&cache_mutex);
485 gpg_error_t cache_init(free_data_fn_t fn)
487 gpg_error_t rc = 0;
488 pthread_mutexattr_t attr;
490 if (!cache_key) {
491 rc = gcry_cipher_algo_info(GCRY_CIPHER_AES, GCRYCTL_GET_BLKLEN, NULL,
492 &cache_blocksize);
493 if (rc)
494 return rc;
496 rc = gcry_cipher_algo_info(GCRY_CIPHER_AES, GCRYCTL_GET_KEYLEN, NULL,
497 &cache_keysize);
498 if (rc)
499 return rc;
501 cache_key = gcry_malloc(cache_keysize);
502 if (!cache_key)
503 return GPG_ERR_ENOMEM;
505 cache_iv = xmalloc(cache_blocksize);
506 if (!cache_iv) {
507 gcry_free(cache_key);
508 cache_key = NULL;
509 return GPG_ERR_ENOMEM;
512 gcry_create_nonce(cache_key, cache_keysize);
513 gcry_create_nonce(cache_iv, cache_blocksize);
517 if (!cache_agent) {
518 rc = agent_init(&cache_agent);
519 if (!rc) {
520 char *line;
522 rc = send_to_agent(cache_agent, &line, NULL, "GETINFO version");
523 if (!rc) {
524 char **fields = str_split(line, ".", 0);
525 int major, minor;
527 major = atoi(fields[0]);
528 minor = atoi(fields[1]);
529 if (major < 2 || minor < 1)
530 rc = GPG_ERR_UNKNOWN_VERSION;
532 strv_free(fields);
533 xfree(line);
538 pthread_mutexattr_init(&attr);
539 pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
540 pthread_mutex_init(&cache_mutex, &attr);
541 pthread_mutexattr_destroy(&attr);
542 free_data_fn = fn;
543 return rc;
546 gpg_error_t cache_is_shadowed(const char *grip)
548 MUTEX_LOCK(&cache_mutex);
549 char *line;
550 size_t len;
551 gpg_error_t rc;
553 pthread_cleanup_push(cleanup_mutex_cb, &cache_mutex);
554 rc = send_to_agent(cache_agent, &line, &len,
555 "KEYINFO --data %s", grip);
556 pthread_cleanup_pop(1);
558 if (!rc) {
559 char **fields = str_split(line, " ", 0);
561 rc = (*fields[1] == 'T') ? 0 : GPG_ERR_NO_DATA;
562 xfree(line);
563 strv_free(fields);
566 return rc;
569 void cache_lock()
571 MUTEX_LOCK(&cache_mutex);
574 void cache_unlock()
576 MUTEX_UNLOCK(&cache_mutex);
579 void free_cache_data_once(struct cache_data_s *data)
581 if (!data)
582 return;
584 if (data->doc)
585 gcry_free(data->doc);
587 if (data->pubkey)
588 gcry_sexp_release(data->pubkey);
590 xfree(data->crc);
591 xfree(data);