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/>.
27 #include "pwmd-error.h"
36 #include "util-misc.h"
37 #include "util-string.h"
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
);
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
)))
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
);
78 file_cache_t
*p
= get_entry(md5file
);
81 MUTEX_UNLOCK(&cache_mutex
);
82 MUTEX_TRYLOCK(ctx
, p
->mutex
, rc
, lock_timeout
);
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
);
91 MUTEX_UNLOCK(&cache_mutex
);
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
);
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.
117 pthread_cleanup_pop(1);
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
);
127 MUTEX_UNLOCK(p
->mutex
);
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
)
141 for (c
= len
= 0; len
< sizeof(e
->grip
); len
++, c
++) {
149 static gpg_error_t
iscached(const unsigned char *md5file
, int *defer
)
152 file_cache_t
*p
= get_entry(md5file
);
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
);
160 char **fields
= str_split(line
, " ", 0);
162 /* Smartcard with a cached document. */
163 if (*fields
[1] == 'T' && p
->data
->doc
)
166 rc
= isdigit(*fields
[4]) || *fields
[5] == 'C' ? 0 : GPG_ERR_NO_DATA
;
172 *defer
= p
->defer_clear
;
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
))
191 pthread_cleanup_pop(1);
195 void cache_adjust_timeout()
197 MUTEX_LOCK(&cache_mutex
);
198 int t
= slist_length(key_cache
);
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
);
210 /* The file associated with this cache entry may be locked by a
211 * client. Defer freeing this cache entry until the next timeout
214 send_status_all(STATUS_CACHE
, NULL
);
218 pthread_cleanup_pop(1);
221 static void setgrip(file_cache_t
*p
, const unsigned char *grip
)
224 char *tmp
= bin2hex(grip
, 20);
226 memcpy(p
->grip
, tmp
, sizeof(p
->grip
));
231 static gpg_error_t
set_timeout(const unsigned char *md5file
, int timeout
,
234 MUTEX_LOCK(&cache_mutex
);
235 file_cache_t
*p
= get_entry(md5file
);
239 MUTEX_UNLOCK(&cache_mutex
);
240 return GPG_ERR_NOT_FOUND
;
244 if (p
->timeout
== -1 || timeout
== -1)
245 p
->timeout
= timeout
;
247 if (!timeout
|| defer
) {
250 send_status_all(STATUS_CACHE
, NULL
);
253 MUTEX_UNLOCK(&cache_mutex
);
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
);
266 set_timeout(md5file
, p
->reset
, p
->defer_clear
);
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
);
298 b
= set_timeout(md5file
, timeout
, p
->defer_clear
) == 0 ? 1 : 0;
299 MUTEX_UNLOCK(&cache_mutex
);
300 send_status_all(STATUS_CACHE
, NULL
);
304 p
= xcalloc(1, sizeof(file_cache_t
));
306 MUTEX_UNLOCK(&cache_mutex
);
310 p
->mutex
= (pthread_mutex_t
*)xmalloc(sizeof(pthread_mutex_t
));
313 MUTEX_UNLOCK(&cache_mutex
);
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
));
324 p
->reset
= p
->timeout
= timeout
;
327 new = slist_append(key_cache
, p
);
329 pthread_mutex_destroy(p
->mutex
);
332 MUTEX_UNLOCK(&cache_mutex
);
337 MUTEX_UNLOCK(&cache_mutex
);
338 send_status_all(STATUS_CACHE
, NULL
);
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
);
350 log_write("%s(): %s", __FUNCTION__
, pwmd_strerror(rc
));
352 memset(p
->grip
, 0, sizeof(p
->grip
));
358 static void free_entry(file_cache_t
*p
)
367 log_write("%s(): %s", __FUNCTION__
, pwmd_strerror(rc
));
370 pthread_mutex_destroy(p
->mutex
);
374 key_cache
= slist_remove(key_cache
, p
);
378 gpg_error_t
cache_clear(const unsigned char *md5file
)
384 MUTEX_LOCK(&cache_mutex
);
387 p
= get_entry(md5file
);
389 MUTEX_UNLOCK(&cache_mutex
);
393 pthread_cleanup_push(cleanup_mutex_cb
, &cache_mutex
);
395 pthread_cleanup_pop(1);
397 log_write("%s(): %s", __FUNCTION__
, pwmd_strerror(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
);
409 pthread_cleanup_pop(1);
413 gpg_error_t
cache_iscached(const char *filename
, int *defer
)
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);
429 gpg_error_t
cache_defer_clear(const unsigned char *md5file
)
431 MUTEX_LOCK(&cache_mutex
);
432 file_cache_t
*p
= get_entry(md5file
);
436 rc
= GPG_ERR_NOT_FOUND
;
440 MUTEX_UNLOCK(&cache_mutex
);
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
);
459 memcpy(grip
, p
->grip
, sizeof(p
->grip
));
460 MUTEX_UNLOCK(&cache_mutex
);
466 MUTEX_LOCK(&cache_mutex
);
469 pthread_cleanup_push(cleanup_mutex_cb
, &cache_mutex
);
470 while ((p
= slist_nth_data(key_cache
, 0)))
474 cleanup_agent(cache_agent
);
477 gcry_free(cache_key
);
481 pthread_cleanup_pop(1);
482 pthread_mutex_destroy(&cache_mutex
);
485 gpg_error_t
cache_init(free_data_fn_t fn
)
488 pthread_mutexattr_t attr
;
491 rc
= gcry_cipher_algo_info(GCRY_CIPHER_AES
, GCRYCTL_GET_BLKLEN
, NULL
,
496 rc
= gcry_cipher_algo_info(GCRY_CIPHER_AES
, GCRYCTL_GET_KEYLEN
, NULL
,
501 cache_key
= gcry_malloc(cache_keysize
);
503 return GPG_ERR_ENOMEM
;
505 cache_iv
= xmalloc(cache_blocksize
);
507 gcry_free(cache_key
);
509 return GPG_ERR_ENOMEM
;
512 gcry_create_nonce(cache_key
, cache_keysize
);
513 gcry_create_nonce(cache_iv
, cache_blocksize
);
518 rc
= agent_init(&cache_agent
);
522 rc
= send_to_agent(cache_agent
, &line
, NULL
, "GETINFO version");
524 char **fields
= str_split(line
, ".", 0);
527 major
= atoi(fields
[0]);
528 minor
= atoi(fields
[1]);
529 if (major
< 2 || minor
< 1)
530 rc
= GPG_ERR_UNKNOWN_VERSION
;
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
);
546 gpg_error_t
cache_is_shadowed(const char *grip
)
548 MUTEX_LOCK(&cache_mutex
);
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);
559 char **fields
= str_split(line
, " ", 0);
561 rc
= (*fields
[1] == 'T') ? 0 : GPG_ERR_NO_DATA
;
571 MUTEX_LOCK(&cache_mutex
);
576 MUTEX_UNLOCK(&cache_mutex
);
579 void free_cache_data_once(struct cache_data_s
*data
)
585 gcry_free(data
->doc
);
588 gcry_sexp_release(data
->pubkey
);