Fix compile time warning.
[pwmd.git] / src / cache.c
blob1d3e070e6da27b986e4215da41be7622f2beedb8
1 /*
2 Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013
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 <ctype.h>
29 #include "pwmd-error.h"
30 #include <gcrypt.h>
31 #include "mutex.h"
32 #include "cache.h"
33 #include "status.h"
34 #include "mem.h"
35 #include "util-misc.h"
36 #include "util-string.h"
37 #include "crypto.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 #ifdef WITH_AGENT
46 static struct agent_s *cache_agent;
47 #endif
49 extern void log_write (const char *fmt, ...);
51 typedef gpg_error_t (*free_data_fn_t) (file_cache_t *);
52 static free_data_fn_t free_data_fn;
53 static gpg_error_t clear_once (file_cache_t * p);
54 static int remove_entry (const unsigned char *md5file);
55 static void free_entry (file_cache_t * p);
57 static file_cache_t *
58 get_entry (const unsigned char *md5file)
60 int t = slist_length (key_cache);
61 int i;
63 if (!md5file)
64 return NULL;
66 for (i = 0; i < t; i++)
68 file_cache_t *p = slist_nth_data (key_cache, i);
70 if (!memcmp (p->filename, md5file, sizeof (p->filename)))
71 return p;
74 return NULL;
77 gpg_error_t
78 cache_lock_mutex (void *ctx, const unsigned char *md5file,
79 long lock_timeout, int add, int timeout)
81 MUTEX_LOCK (&cache_mutex);
82 gpg_error_t rc = 0;
83 file_cache_t *p = get_entry (md5file);
85 if (p)
87 MUTEX_UNLOCK (&cache_mutex);
88 MUTEX_TRYLOCK (ctx, p->mutex, rc, lock_timeout);
90 else if (add)
92 if (cache_add_file (md5file, NULL, NULL, timeout))
94 p = get_entry (md5file);
95 MUTEX_UNLOCK (&cache_mutex);
96 MUTEX_TRYLOCK (ctx, p->mutex, rc, lock_timeout);
98 else
100 MUTEX_UNLOCK (&cache_mutex);
101 rc = GPG_ERR_ENOMEM;
104 else
106 MUTEX_UNLOCK (&cache_mutex);
109 return !p && !rc ? GPG_ERR_NO_DATA : rc;
112 static int
113 remove_entry (const unsigned char *md5file)
115 MUTEX_LOCK (&cache_mutex);
116 file_cache_t *p = get_entry (md5file);
118 pthread_cleanup_push (cleanup_mutex_cb, &cache_mutex);
119 if (p)
121 /* Keep a refcount because another client maybe editing this same new
122 * file. The entry needs to be kept in case the other client SAVE's.
124 p->refcount--;
125 if (!p->refcount)
126 free_entry (p);
129 pthread_cleanup_pop (1);
130 return p ? 1 : 0;
133 gpg_error_t
134 cache_unlock_mutex (const unsigned char *md5file, int remove)
136 MUTEX_LOCK (&cache_mutex);
137 file_cache_t *p = get_entry (md5file);
139 if (p)
141 MUTEX_UNLOCK (p->mutex);
142 if (remove)
143 remove_entry (md5file);
146 MUTEX_UNLOCK (&cache_mutex);
147 return p ? 0 : GPG_ERR_NO_DATA;
150 #ifdef WITH_AGENT
151 static int valid_agent_grip (file_cache_t *e)
153 size_t c;
155 for (c = 0; c < sizeof (e->grip); c++)
157 if (e->grip[c] && e->grip[c] != '0')
158 return 1;
161 return 0;
163 #endif
165 static int
166 valid_grip (file_cache_t * e)
168 if (e->data && e->data->key)
169 return 1;
171 #ifdef WITH_AGENT
172 return valid_agent_grip (e);
173 #else
174 return 0;
175 #endif
178 static gpg_error_t
179 iscached (const unsigned char *md5file, int *defer)
181 gpg_error_t rc = 0;
182 file_cache_t *p = get_entry (md5file);
184 if (!p || !p->data || !valid_grip (p))
185 return GPG_ERR_NO_DATA;
187 if (defer)
188 *defer = p->defer_clear;
190 if (p->data && p->data->key)
191 return 0;
193 #ifdef WITH_AGENT
194 char *line;
196 rc = send_to_agent (cache_agent, &line, NULL, "KEYINFO --data %s", p->grip);
197 if (!rc)
199 char **fields = str_split (line, " ", 0);
201 /* Smartcard with a cached document. */
202 if (*fields[1] == 'T' && p->data->doc)
203 rc = 0;
204 else
205 rc = isdigit (*fields[4]) || *fields[5] == 'C' ? 0 : GPG_ERR_NO_DATA;
207 strv_free (fields);
208 xfree (line);
210 #endif
212 return rc;
215 unsigned
216 cache_file_count ()
218 MUTEX_LOCK (&cache_mutex);
219 unsigned total = 0, i, n;
221 pthread_cleanup_push (cleanup_mutex_cb, &cache_mutex);
222 n = slist_length (key_cache);
223 for (i = 0; i < n; i++)
225 file_cache_t *p = slist_nth_data (key_cache, i);
227 if (!iscached (p->filename, NULL))
228 total++;
231 pthread_cleanup_pop (1);
232 return total;
235 void
236 cache_adjust_timeout ()
238 MUTEX_LOCK (&cache_mutex);
239 int t = slist_length (key_cache);
240 int i;
242 pthread_cleanup_push (cleanup_mutex_cb, &cache_mutex);
244 for (i = 0; i < t; i++)
246 file_cache_t *p = slist_nth_data (key_cache, i);
248 if (p->timeout > 0)
249 p->timeout--;
251 if (!p->timeout)
253 /* The file associated with this cache entry may be locked by a
254 * client. Defer freeing this cache entry until the next timeout
255 * check. */
256 if (!clear_once (p))
257 send_status_all (STATUS_CACHE, NULL);
261 pthread_cleanup_pop (1);
264 static void
265 setgrip (file_cache_t * p, const unsigned char *grip)
267 if (grip)
269 char *tmp = bin2hex (grip, 20);
271 memcpy (p->grip, tmp, sizeof (p->grip));
272 xfree (tmp);
274 else
275 memset (p->grip, 0, sizeof(p->grip));
278 static gpg_error_t
279 set_timeout (const unsigned char *md5file, int timeout, int defer)
281 MUTEX_LOCK (&cache_mutex);
282 file_cache_t *p = get_entry (md5file);
283 gpg_error_t rc = 0;
285 if (!p)
287 MUTEX_UNLOCK (&cache_mutex);
288 return GPG_ERR_NOT_FOUND;
291 p->reset = timeout;
292 if (p->timeout == -1 || timeout == -1)
293 p->timeout = timeout;
295 if (!timeout || defer)
297 rc = clear_once (p);
298 if (!rc)
299 send_status_all (STATUS_CACHE, NULL);
302 MUTEX_UNLOCK (&cache_mutex);
303 return rc;
306 gpg_error_t
307 cache_set_data (const unsigned char *md5file, struct cache_data_s * data,
308 const unsigned char *grip)
310 MUTEX_LOCK (&cache_mutex);
311 file_cache_t *p = get_entry (md5file);
313 if (p)
315 setgrip (p, grip);
316 p->data = data;
317 set_timeout (md5file, p->reset, p->defer_clear);
318 if (!p->reset)
320 clear_once (p);
321 send_status_all (STATUS_CACHE, NULL);
325 MUTEX_UNLOCK (&cache_mutex);
326 return p ? 0 : GPG_ERR_NO_DATA;
329 struct cache_data_s *
330 cache_get_data (const unsigned char *md5file)
332 MUTEX_LOCK (&cache_mutex);
333 file_cache_t *p = get_entry (md5file);
335 MUTEX_UNLOCK (&cache_mutex);
336 return p ? p->data : NULL;
340 struct cache_data_s *
341 cache_get_data_filename (const char *filename)
343 unsigned char md5file[16];
345 gcry_md_hash_buffer (GCRY_MD_MD5, md5file, filename, strlen (filename));
346 return cache_get_data (md5file);
350 cache_add_file (const unsigned char *md5file, const unsigned char *grip,
351 struct cache_data_s *data, int timeout)
353 MUTEX_LOCK (&cache_mutex);
354 file_cache_t *p = get_entry (md5file);
355 struct slist_s *new;
356 int b;
358 if (p)
360 setgrip (p, grip);
361 p->data = data;
362 p->refcount++;
363 b = set_timeout (md5file, timeout, p->defer_clear) == 0 ? 1 : 0;
364 MUTEX_UNLOCK (&cache_mutex);
365 send_status_all (STATUS_CACHE, NULL);
366 return b;
369 p = xcalloc (1, sizeof (file_cache_t));
370 if (!p)
372 MUTEX_UNLOCK (&cache_mutex);
373 return 0;
376 p->mutex = (pthread_mutex_t *) xmalloc (sizeof (pthread_mutex_t));
377 if (!p->mutex)
379 xfree (p);
380 MUTEX_UNLOCK (&cache_mutex);
381 return 0;
384 pthread_mutexattr_t attr;
385 pthread_mutexattr_init (&attr);
386 pthread_mutexattr_settype (&attr, PTHREAD_MUTEX_RECURSIVE);
387 pthread_mutex_init (p->mutex, &attr);
388 pthread_mutexattr_destroy (&attr);
389 memcpy (p->filename, md5file, sizeof (p->filename));
390 setgrip (p, grip);
391 p->reset = p->timeout = timeout;
392 p->data = data;
393 p->refcount++;
394 new = slist_append (key_cache, p);
395 if (!new)
397 pthread_mutex_destroy (p->mutex);
398 xfree (p->mutex);
399 xfree (p);
400 MUTEX_UNLOCK (&cache_mutex);
401 return 0;
404 key_cache = new;
405 MUTEX_UNLOCK (&cache_mutex);
406 send_status_all (STATUS_CACHE, NULL);
407 return 1;
410 static gpg_error_t
411 clear_once (file_cache_t * p)
413 gpg_error_t rc = free_data_fn (p);
415 if (!rc && valid_grip (p))
417 #ifdef WITH_AGENT
418 if (use_agent && valid_agent_grip (p))
420 rc = send_to_agent (cache_agent, NULL, NULL,
421 "CLEAR_PASSPHRASE --mode=normal %s", p->grip);
422 if (rc)
423 log_write ("%s(): %s", __FUNCTION__, pwmd_strerror (rc));
425 #endif
426 memset (p->grip, 0, sizeof (p->grip));
429 return rc;
432 static void
433 free_entry (file_cache_t * p)
435 gpg_error_t rc;
437 if (!p)
438 return;
440 rc = clear_once (p);
441 if (rc)
442 log_write ("%s(): %s", __FUNCTION__, pwmd_strerror (rc));
444 if (p->mutex)
446 pthread_mutex_destroy (p->mutex);
447 xfree (p->mutex);
450 key_cache = slist_remove (key_cache, p);
451 xfree (p);
454 gpg_error_t
455 cache_clear (const unsigned char *md5file)
457 file_cache_t *p;
458 gpg_error_t rc = 0;
459 int i, t;
461 MUTEX_LOCK (&cache_mutex);
463 if (md5file)
465 p = get_entry (md5file);
466 if (!p)
468 MUTEX_UNLOCK (&cache_mutex);
469 return 0;
472 pthread_cleanup_push (cleanup_mutex_cb, &cache_mutex);
473 rc = clear_once (p);
474 pthread_cleanup_pop (1);
475 if (rc)
476 log_write ("%s(): %s", __FUNCTION__, pwmd_strerror (rc));
478 return rc;
481 pthread_cleanup_push (cleanup_mutex_cb, &cache_mutex);
482 t = slist_length (key_cache);
483 for (i = 0; i < t; i++)
485 p = slist_nth_data (key_cache, i);
486 clear_once (p);
489 pthread_cleanup_pop (1);
490 return 0;
493 gpg_error_t
494 cache_iscached (const char *filename, int *defer)
496 gpg_error_t rc;
497 unsigned char md5file[16];
499 if (access (filename, R_OK) == -1)
500 return gpg_error_from_syserror ();
502 MUTEX_LOCK (&cache_mutex);
503 pthread_cleanup_push (cleanup_mutex_cb, &cache_mutex);
504 gcry_md_hash_buffer (GCRY_MD_MD5, md5file, filename, strlen (filename));
505 rc = iscached (md5file, defer);
506 pthread_cleanup_pop (1);
507 return rc;
510 gpg_error_t
511 cache_defer_clear (const unsigned char *md5file)
513 MUTEX_LOCK (&cache_mutex);
514 file_cache_t *p = get_entry (md5file);
515 gpg_error_t rc = 0;
517 if (!p)
518 rc = GPG_ERR_NOT_FOUND;
519 else
520 p->defer_clear = 1;
522 MUTEX_UNLOCK (&cache_mutex);
523 return rc;
526 gpg_error_t
527 cache_set_timeout (const unsigned char *md5file, int timeout)
529 return set_timeout (md5file, timeout, 0);
533 cache_get_grip (const unsigned char *md5file, unsigned char *grip)
535 MUTEX_LOCK (&cache_mutex);
536 file_cache_t *p = get_entry (md5file);
538 if (!p || !valid_grip (p))
540 MUTEX_UNLOCK (&cache_mutex);
541 return 0;
544 memcpy (grip, p->grip, sizeof (p->grip));
545 MUTEX_UNLOCK (&cache_mutex);
546 return 1;
549 /* The with_pkcs parameter is needed to prevent clearing gpg-agent
550 cached keys during an atfork callback since the agent is still
551 shared between the two processes. */
552 void
553 cache_deinit (int atfork)
555 MUTEX_LOCK (&cache_mutex);
556 int i, t = slist_length (key_cache);
558 pthread_cleanup_push (cleanup_mutex_cb, &cache_mutex);
559 for (i = 0; i < t; i++)
561 file_cache_t *p = slist_nth_data (key_cache, i);
563 #ifdef WITH_AGENT
564 if (atfork && valid_agent_grip (p))
565 continue;
566 #endif
568 free_entry (p);
569 t = slist_length (key_cache);
570 i = -1;
573 #ifdef WITH_AGENT
574 if (use_agent && cache_agent)
575 cleanup_agent (cache_agent);
577 cache_agent = NULL;
578 #endif
579 gcry_free (cache_key);
580 xfree (cache_iv);
581 cache_key = NULL;
582 cache_iv = NULL;
583 pthread_cleanup_pop (1);
584 pthread_mutex_destroy (&cache_mutex);
587 void
588 cache_mutex_init ()
590 pthread_mutexattr_t attr;
592 pthread_mutexattr_init (&attr);
593 pthread_mutexattr_settype (&attr, PTHREAD_MUTEX_RECURSIVE);
594 pthread_mutex_init (&cache_mutex, &attr);
595 pthread_mutexattr_destroy (&attr);
598 gpg_error_t
599 cache_init (free_data_fn_t fn)
601 gpg_error_t rc = 0;
603 if (!cache_key)
605 rc = gcry_cipher_algo_info (GCRY_CIPHER_AES, GCRYCTL_GET_BLKLEN, NULL,
606 &cache_blocksize);
607 if (rc)
608 return rc;
610 rc = gcry_cipher_algo_info (GCRY_CIPHER_AES, GCRYCTL_GET_KEYLEN, NULL,
611 &cache_keysize);
612 if (rc)
613 return rc;
615 cache_key = gcry_malloc (cache_keysize);
616 if (!cache_key)
617 return GPG_ERR_ENOMEM;
619 cache_iv = xmalloc (cache_blocksize);
620 if (!cache_iv)
622 gcry_free (cache_key);
623 cache_key = NULL;
624 return GPG_ERR_ENOMEM;
627 gcry_create_nonce (cache_key, cache_keysize);
628 gcry_create_nonce (cache_iv, cache_blocksize);
631 #ifdef WITH_AGENT
632 if (use_agent && !cache_agent)
634 rc = agent_init (&cache_agent);
635 if (!rc)
637 char *line;
639 rc = send_to_agent (cache_agent, &line, NULL, "GETINFO version");
640 if (!rc)
642 char **fields = str_split (line, ".", 0);
643 int major, minor;
645 major = atoi (fields[0]);
646 minor = atoi (fields[1]);
647 if (major < 2 || minor < 1)
648 rc = GPG_ERR_UNKNOWN_VERSION;
650 strv_free (fields);
651 xfree (line);
655 #endif
657 cache_mutex_init ();
658 free_data_fn = fn;
659 return rc;
662 #ifdef WITH_AGENT
663 gpg_error_t
664 cache_is_shadowed (const char *grip)
666 MUTEX_LOCK (&cache_mutex);
667 char *line;
668 size_t len;
669 gpg_error_t rc;
671 pthread_cleanup_push (cleanup_mutex_cb, &cache_mutex);
672 rc = send_to_agent (cache_agent, &line, &len, "KEYINFO --data %s", grip);
673 pthread_cleanup_pop (1);
675 if (!rc)
677 char **fields = str_split (line, " ", 0);
679 rc = (*fields[1] == 'T') ? 0 : GPG_ERR_NO_DATA;
680 xfree (line);
681 strv_free (fields);
684 return rc;
686 #endif
688 void
689 cache_lock ()
691 MUTEX_LOCK (&cache_mutex);
694 void
695 cache_unlock ()
697 MUTEX_UNLOCK (&cache_mutex);
700 void
701 free_cache_data_once (struct cache_data_s *data)
703 if (!data)
704 return;
706 #ifdef WITH_AGENT
707 if (data->pubkey)
708 gcry_sexp_release (data->pubkey);
709 #endif
711 gcry_free (data->doc);
712 gcry_free (data->key);
713 xfree (data->crc);
714 xfree (data);