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