* gpg-agent.c (handle_signal): Flush cache on SIGHUP.
[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 seonds */
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 && r->accessed + r->ttl < current)
92         {
93           if (DBG_CACHE)
94             log_debug ("  expired `%s' (%ds after last access)\n",
95                        r->key, r->ttl);
96           release_data (r->pw);
97           r->pw = NULL;
98           r->accessed = current;
99         }
100     }
101
102   /* second, make sure that we also remove them based on the created stamp so
103      that the user has to enter it from time to time.  We do this every hour */
104   for (r=thecache; r; r = r->next)
105     {
106       if (!r->lockcount && r->pw && r->created + 60*60 < current)
107         {
108           if (DBG_CACHE)
109             log_debug ("  expired `%s' (1h after creation)\n", r->key);
110           release_data (r->pw);
111           r->pw = NULL;
112           r->accessed = current;
113         }
114     }
115
116   /* third, make sure that we don't have too many items in the list.
117      Expire old and unused entries after 30 minutes */
118   for (rprev=NULL, r=thecache; r; )
119     {
120       if (!r->pw && r->accessed + 60*30 < current)
121         {
122           if (r->lockcount)
123             {
124               log_error ("can't remove unused cache entry `%s' due to"
125                          " lockcount=%d\n",
126                          r->key, r->lockcount);
127               r->accessed += 60*10; /* next error message in 10 minutes */
128               rprev = r;
129               r = r->next;
130             }
131           else
132             {
133               ITEM r2 = r->next;
134               if (DBG_CACHE)
135                 log_debug ("  removed `%s' (slot not used for 30m)\n", r->key);
136               xfree (r);
137               if (!rprev)
138                 thecache = r2;
139               else
140                 rprev->next = r2;
141               r = r2;
142             }
143         }
144       else
145         {
146           rprev = r;
147           r = r->next;
148         }
149     }
150 }
151
152
153 void
154 agent_flush_cache (void)
155 {
156   ITEM r;
157
158   if (DBG_CACHE)
159     log_debug ("agent_flush_cache\n");
160
161   for (r=thecache; r; r = r->next)
162     {
163       if (!r->lockcount && r->pw)
164         {
165           if (DBG_CACHE)
166             log_debug ("  flushing `%s'\n", r->key);
167           release_data (r->pw);
168           r->pw = NULL;
169           r->accessed = 0;
170         }
171       else if (r->lockcount && r->pw)
172         {
173           if (DBG_CACHE)
174             log_debug ("    marked `%s' for flushing\n", r->key);
175           r->accessed = 0;
176           r->ttl = 0;
177         }
178     }
179 }
180
181
182
183 /* Store DATA of length DATALEN in the cache under KEY and mark it
184    with a maximum lifetime of TTL seconds.  If there is already data
185    under this key, it will be replaced.  Using a DATA of NULL deletes
186    the entry */
187 int
188 agent_put_cache (const char *key, const char *data, int ttl)
189 {
190   ITEM r;
191
192   if (DBG_CACHE)
193     log_debug ("agent_put_cache `%s'\n", key);
194   housekeeping ();
195
196   if (ttl < 1)
197     ttl = opt.def_cache_ttl;
198   if (!ttl)
199     return 0;
200
201   for (r=thecache; r; r = r->next)
202     {
203       if (!r->lockcount && !strcmp (r->key, key))
204         break;
205     }
206   if (r)
207     { /* replace */
208       if (r->pw)
209         {
210           release_data (r->pw);
211           r->pw = NULL;
212         }
213       if (data)
214         {
215           r->created = r->accessed = gnupg_get_time (); 
216           r->ttl = ttl;
217           r->pw = new_data (data, strlen (data)+1);
218           if (!r->pw)
219             log_error ("out of core while allocating new cache item\n");
220         }
221     }
222   else if (data)
223     { /* simply insert */
224       r = xtrycalloc (1, sizeof *r + strlen (key));
225       if (!r)
226         log_error ("out of core while allocating new cache control\n");
227       else
228         {
229           strcpy (r->key, key);
230           r->created = r->accessed = gnupg_get_time (); 
231           r->ttl = ttl;
232           r->pw = new_data (data, strlen (data)+1);
233           if (!r->pw)
234             {
235               log_error ("out of core while allocating new cache item\n");
236               xfree (r);
237             }
238           else
239             {
240               r->next = thecache;
241               thecache = r;
242             }
243         }
244     }
245   return 0;
246 }
247
248
249 /* Try to find an item in the cache */
250 const char *
251 agent_get_cache (const char *key, void **cache_id)
252 {
253   ITEM r;
254
255   if (DBG_CACHE)
256     log_debug ("agent_get_cache `%s'...\n", key);
257   housekeeping ();
258
259   /* first try to find one with no locks - this is an updated cache
260      entry: We might have entries with a lockcount and without a
261      lockcount. */
262   for (r=thecache; r; r = r->next)
263     {
264       if (!r->lockcount && r->pw && !strcmp (r->key, key))
265         {
266           /* put_cache does only put strings into the cache, so we
267              don't need the lengths */
268           r->accessed = gnupg_get_time ();
269           if (DBG_CACHE)
270             log_debug ("... hit\n");
271           r->lockcount++;
272           *cache_id = r;
273           return r->pw->data;
274         }
275     }
276   /* again, but this time get even one with a lockcount set */
277   for (r=thecache; r; r = r->next)
278     {
279       if (r->pw && !strcmp (r->key, key))
280         {
281           r->accessed = gnupg_get_time ();
282           if (DBG_CACHE)
283             log_debug ("... hit (locked)\n");
284           r->lockcount++;
285           *cache_id = r;
286           return r->pw->data;
287         }
288     }
289   if (DBG_CACHE)
290     log_debug ("... miss\n");
291
292   *cache_id = NULL;
293   return NULL;
294 }
295
296
297 void
298 agent_unlock_cache_entry (void **cache_id)
299 {
300   ITEM r;
301
302   for (r=thecache; r; r = r->next)
303     {
304       if (r == *cache_id)
305         {
306           if (!r->lockcount)
307             log_error ("trying to unlock non-locked cache entry `%s'\n",
308                        r->key);
309           else
310             r->lockcount--;
311           return;
312         }
313     }
314 }