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