* cache.c: Add a few debug outputs.
[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   struct secret_data_s *pw;
44   char key[1];
45 };
46
47
48 static ITEM thecache;
49
50
51 static void
52 release_data (struct secret_data_s *data)
53 {
54    xfree (data);
55 }
56
57 static struct secret_data_s *
58 new_data (const void *data, size_t length)
59 {
60   struct secret_data_s *d;
61   int total;
62
63   /* we pad the data to 32 bytes so that it get more complicated
64      finding something out by watching allocation patterns.  This is
65      usally not possible but we better assume nothing about our
66      secure storage provider*/
67   total = length + 32 - (length % 32);
68
69   d = gcry_malloc_secure (sizeof d + total - 1);
70   if (d)
71     {
72       d->totallen = total;
73       d->datalen  = length;
74       memcpy (d->data, data, length);
75     }
76   return d;
77 }
78
79
80 /* check whether there are items to expire */
81 static void
82 housekeeping (void)
83 {
84   ITEM r, rprev;
85   time_t current = time (NULL);
86
87   /* first expire the actual data */
88   for (r=thecache; r; r = r->next)
89     {
90       if (r->pw && r->accessed + r->ttl < current)
91         {
92           if (DBG_CACHE)
93             log_debug ("  expired `%s' (%ds after last access)\n",
94                        r->key, r->ttl);
95           release_data (r->pw);
96           r->pw = NULL;
97           r->accessed = current;
98         }
99     }
100
101   /* second, make sure that we also remove them based on the created stamp so
102      that the used has to enter it from time to time.  We do this every hour */
103   for (r=thecache; r; r = r->next)
104     {
105       if (r->pw && r->created + 60*60 < current)
106         {
107           if (DBG_CACHE)
108             log_debug ("  expired `%s' (1h after creation)\n", r->key);
109           release_data (r->pw);
110           r->pw = NULL;
111           r->accessed = current;
112         }
113     }
114
115   /* third, make sure that we don't have too many items in the list.
116      Expire old and unused entries after 30 minutes */
117   for (rprev=NULL, r=thecache; r; )
118     {
119       if (!r->pw && r->accessed + 60*30 < current)
120         {
121           ITEM r2 = r->next;
122           if (DBG_CACHE)
123             log_debug ("  removed `%s' (slot not used for 30m)\n", r->key);
124           xfree (r);
125           if (!rprev)
126             thecache = r2;
127           else
128             rprev = r2;
129           r = r2;
130         }
131       else
132         {
133           rprev = r;
134           r = r->next;
135         }
136     }
137 }
138
139
140
141 /* Store DATA of length DATALEN in the cache under KEY and mark it
142    with a maximum lifetime of TTL seconds.  If tehre is already data
143    under this key, it will be replaced.  Using a DATA of NULL deletes
144    the entry */
145 int
146 agent_put_cache (const char *key, const char *data, int ttl)
147 {
148   ITEM r;
149
150   if (DBG_CACHE)
151     log_debug ("agent_put_cache `%s'\n", key);
152   housekeeping ();
153
154   if (ttl < 1)
155     ttl = 60*5; /* default is 5 minutes */
156
157   for (r=thecache; r; r = r->next)
158     {
159       if ( !strcmp (r->key, key))
160         break;
161     }
162   if (r)
163     { /* replace */
164       if (r->pw)
165         {
166           release_data (r->pw);
167           r->pw = NULL;
168         }
169       if (data)
170         {
171           r->pw = new_data (data, strlen (data)+1);
172           if (!r->pw)
173             log_error ("out of core while allocating new cache item\n");
174         }
175     }
176   else if (data)
177     { /* simply insert */
178       r = xtrycalloc (1, sizeof *r + strlen (key));
179       if (!r)
180         log_error ("out of core while allocating new cache control\n");
181       else
182         {
183           strcpy (r->key, key);
184           r->created = r->accessed = time (NULL); 
185           r->ttl = ttl;
186           r->pw = new_data (data, strlen (data)+1);
187           if (!r->pw)
188             {
189               log_error ("out of core while allocating new cache item\n");
190               xfree (r);
191             }
192           else
193             {
194               r->next = thecache;
195               thecache = r;
196             }
197         }
198     }
199   return 0;
200 }
201
202
203 /* Try to find an item in the cache */
204 const char *
205 agent_get_cache (const char *key)
206 {
207   ITEM r;
208   int count = 0;
209
210   if (DBG_CACHE)
211     log_debug ("agent_get_cache `%s'...\n", key);
212   housekeeping ();
213
214   /* FIXME: Returning pointers is not thread safe - add a referencense
215      counter */
216   for (r=thecache; r; r = r->next, count++)
217     {
218       if (r->pw && !strcmp (r->key, key))
219         {
220           /* put_cache does only put strings into the cache, so we
221              don't need the lengths */
222           r->accessed = time (NULL);
223           if (DBG_CACHE)
224             log_debug ("... hit\n");
225           return r->pw->data;
226         }
227     }
228   if (DBG_CACHE)
229     log_debug ("... miss\n");
230
231   return NULL;
232 }
233
234
235