Make gpgconf aware of --p12-charset.
[gnupg.git] / agent / cache.c
1 /* cache.c - keep a cache of passphrases
2  *      Copyright (C) 2002 Free Software Foundation, Inc.
3  *
4  * This file is part of GnuPG.
5  *
6  * GnuPG is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * GnuPG 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 General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
19  * USA.
20  */
21
22 #include <config.h>
23
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <time.h>
28 #include <assert.h>
29
30 #include "agent.h"
31
32 struct secret_data_s {
33   int  totallen; /* this includes the padding */
34   int  datalen;  /* actual data length */
35   char data[1];
36 };
37
38 typedef struct cache_item_s *ITEM;
39 struct cache_item_s {
40   ITEM next;
41   time_t created;
42   time_t accessed;
43   int ttl;  /* max. lifetime given in seconds, -1 one means infinite */
44   int lockcount;
45   struct secret_data_s *pw;
46   cache_mode_t cache_mode;
47   char key[1];
48 };
49
50
51 static ITEM thecache;
52
53
54 static void
55 release_data (struct secret_data_s *data)
56 {
57    xfree (data);
58 }
59
60 static struct secret_data_s *
61 new_data (const void *data, size_t length)
62 {
63   struct secret_data_s *d;
64   int total;
65
66   /* we pad the data to 32 bytes so that it get more complicated
67      finding something out by watching allocation patterns.  This is
68      usally not possible but we better assume nothing about our
69      secure storage provider*/
70   total = length + 32 - (length % 32);
71
72   d = gcry_malloc_secure (sizeof *d + total - 1);
73   if (d)
74     {
75       d->totallen = total;
76       d->datalen  = length;
77       memcpy (d->data, data, length);
78     }
79   return d;
80 }
81
82
83
84 /* check whether there are items to expire */
85 static void
86 housekeeping (void)
87 {
88   ITEM r, rprev;
89   time_t current = gnupg_get_time ();
90
91   /* First expire the actual data */
92   for (r=thecache; r; r = r->next)
93     {
94       if (!r->lockcount && r->pw
95           && r->ttl >= 0 && r->accessed + r->ttl < current)
96         {
97           if (DBG_CACHE)
98             log_debug ("  expired `%s' (%ds after last access)\n",
99                        r->key, r->ttl);
100           release_data (r->pw);
101           r->pw = NULL;
102           r->accessed = current;
103         }
104     }
105
106   /* Second, make sure that we also remove them based on the created stamp so
107      that the user has to enter it from time to time. */
108   for (r=thecache; r; r = r->next)
109     {
110       unsigned long maxttl;
111       
112       switch (r->cache_mode)
113         {
114         case CACHE_MODE_SSH: maxttl = opt.max_cache_ttl_ssh; break;
115         default: maxttl = opt.max_cache_ttl; break;
116         }
117       if (!r->lockcount && r->pw && r->created + maxttl < current)
118         {
119           if (DBG_CACHE)
120             log_debug ("  expired `%s' (%lus after creation)\n",
121                        r->key, opt.max_cache_ttl);
122           release_data (r->pw);
123           r->pw = NULL;
124           r->accessed = current;
125         }
126     }
127
128   /* Third, make sure that we don't have too many items in the list.
129      Expire old and unused entries after 30 minutes */
130   for (rprev=NULL, r=thecache; r; )
131     {
132       if (!r->pw && r->ttl >= 0 && r->accessed + 60*30 < current)
133         {
134           if (r->lockcount)
135             {
136               log_error ("can't remove unused cache entry `%s' due to"
137                          " lockcount=%d\n",
138                          r->key, r->lockcount);
139               r->accessed += 60*10; /* next error message in 10 minutes */
140               rprev = r;
141               r = r->next;
142             }
143           else
144             {
145               ITEM r2 = r->next;
146               if (DBG_CACHE)
147                 log_debug ("  removed `%s' (slot not used for 30m)\n", r->key);
148               xfree (r);
149               if (!rprev)
150                 thecache = r2;
151               else
152                 rprev->next = r2;
153               r = r2;
154             }
155         }
156       else
157         {
158           rprev = r;
159           r = r->next;
160         }
161     }
162 }
163
164
165 void
166 agent_flush_cache (void)
167 {
168   ITEM r;
169
170   if (DBG_CACHE)
171     log_debug ("agent_flush_cache\n");
172
173   for (r=thecache; r; r = r->next)
174     {
175       if (!r->lockcount && r->pw)
176         {
177           if (DBG_CACHE)
178             log_debug ("  flushing `%s'\n", r->key);
179           release_data (r->pw);
180           r->pw = NULL;
181           r->accessed = 0;
182         }
183       else if (r->lockcount && r->pw)
184         {
185           if (DBG_CACHE)
186             log_debug ("    marked `%s' for flushing\n", r->key);
187           r->accessed = 0;
188           r->ttl = 0;
189         }
190     }
191 }
192
193
194
195 /* Store DATA of length DATALEN in the cache under KEY and mark it
196    with a maximum lifetime of TTL seconds.  If there is already data
197    under this key, it will be replaced.  Using a DATA of NULL deletes
198    the entry.  A TTL of 0 is replaced by the default TTL and a TTL of
199    -1 set infinite timeout. CACHE_MODE is stored with the cache entry
200    and used t select different timeouts. */
201 int
202 agent_put_cache (const char *key, cache_mode_t cache_mode,
203                  const char *data, int ttl)
204 {
205   ITEM r;
206
207   if (DBG_CACHE)
208     log_debug ("agent_put_cache `%s' requested ttl=%d mode=%d\n",
209                key, ttl, cache_mode);
210   housekeeping ();
211
212   if (!ttl)
213     {
214       switch(cache_mode)
215         {
216         case CACHE_MODE_SSH: ttl = opt.def_cache_ttl_ssh; break;
217         default: ttl = opt.def_cache_ttl; break;
218         }
219     }
220   if (!ttl || cache_mode == CACHE_MODE_IGNORE)
221     return 0;
222
223   for (r=thecache; r; r = r->next)
224     {
225       if (!r->lockcount && !strcmp (r->key, key))
226         break;
227     }
228   if (r)
229     { /* replace */
230       if (r->pw)
231         {
232           release_data (r->pw);
233           r->pw = NULL;
234         }
235       if (data)
236         {
237           r->created = r->accessed = gnupg_get_time (); 
238           r->ttl = ttl;
239           r->cache_mode = cache_mode;
240           r->pw = new_data (data, strlen (data)+1);
241           if (!r->pw)
242             log_error ("out of core while allocating new cache item\n");
243         }
244     }
245   else if (data)
246     { /* simply insert */
247       r = xtrycalloc (1, sizeof *r + strlen (key));
248       if (!r)
249         log_error ("out of core while allocating new cache control\n");
250       else
251         {
252           strcpy (r->key, key);
253           r->created = r->accessed = gnupg_get_time (); 
254           r->ttl = ttl;
255           r->cache_mode = cache_mode;
256           r->pw = new_data (data, strlen (data)+1);
257           if (!r->pw)
258             {
259               log_error ("out of core while allocating new cache item\n");
260               xfree (r);
261             }
262           else
263             {
264               r->next = thecache;
265               thecache = r;
266             }
267         }
268     }
269   return 0;
270 }
271
272
273 /* Try to find an item in the cache.  Note that we currently don't
274    make use of CACHE_MODE.  */
275 const char *
276 agent_get_cache (const char *key, cache_mode_t cache_mode, void **cache_id)
277 {
278   ITEM r;
279
280   if (cache_mode == CACHE_MODE_IGNORE)
281     return NULL;
282
283   if (DBG_CACHE)
284     log_debug ("agent_get_cache `%s'...\n", key);
285   housekeeping ();
286
287   /* first try to find one with no locks - this is an updated cache
288      entry: We might have entries with a lockcount and without a
289      lockcount. */
290   for (r=thecache; r; r = r->next)
291     {
292       if (!r->lockcount && r->pw && !strcmp (r->key, key))
293         {
294           /* put_cache does only put strings into the cache, so we
295              don't need the lengths */
296           r->accessed = gnupg_get_time ();
297           if (DBG_CACHE)
298             log_debug ("... hit\n");
299           r->lockcount++;
300           *cache_id = r;
301           return r->pw->data;
302         }
303     }
304   /* again, but this time get even one with a lockcount set */
305   for (r=thecache; r; r = r->next)
306     {
307       if (r->pw && !strcmp (r->key, key))
308         {
309           r->accessed = gnupg_get_time ();
310           if (DBG_CACHE)
311             log_debug ("... hit (locked)\n");
312           r->lockcount++;
313           *cache_id = r;
314           return r->pw->data;
315         }
316     }
317   if (DBG_CACHE)
318     log_debug ("... miss\n");
319
320   *cache_id = NULL;
321   return NULL;
322 }
323
324
325 void
326 agent_unlock_cache_entry (void **cache_id)
327 {
328   ITEM r;
329
330   for (r=thecache; r; r = r->next)
331     {
332       if (r == *cache_id)
333         {
334           if (!r->lockcount)
335             log_error ("trying to unlock non-locked cache entry `%s'\n",
336                        r->key);
337           else
338             r->lockcount--;
339           return;
340         }
341     }
342 }