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