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