Debug message cleanups.
[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->next = 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 there 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 = opt.def_cache_ttl;
156   if (!ttl)
157     return 0;
158
159   for (r=thecache; r; r = r->next)
160     {
161       if ( !strcmp (r->key, key))
162         break;
163     }
164   if (r)
165     { /* replace */
166       if (r->pw)
167         {
168           release_data (r->pw);
169           r->pw = NULL;
170         }
171       if (data)
172         {
173           r->created = r->accessed = time (NULL); 
174           r->ttl = ttl;
175           r->pw = new_data (data, strlen (data)+1);
176           if (!r->pw)
177             log_error ("out of core while allocating new cache item\n");
178         }
179     }
180   else if (data)
181     { /* simply insert */
182       r = xtrycalloc (1, sizeof *r + strlen (key));
183       if (!r)
184         log_error ("out of core while allocating new cache control\n");
185       else
186         {
187           strcpy (r->key, key);
188           r->created = r->accessed = time (NULL); 
189           r->ttl = ttl;
190           r->pw = new_data (data, strlen (data)+1);
191           if (!r->pw)
192             {
193               log_error ("out of core while allocating new cache item\n");
194               xfree (r);
195             }
196           else
197             {
198               r->next = thecache;
199               thecache = r;
200             }
201         }
202     }
203   return 0;
204 }
205
206
207 /* Try to find an item in the cache */
208 const char *
209 agent_get_cache (const char *key)
210 {
211   ITEM r;
212   int count = 0;
213
214   if (DBG_CACHE)
215     log_debug ("agent_get_cache `%s'...\n", key);
216   housekeeping ();
217
218   /* FIXME: Returning pointers is not thread safe - add a reference
219      counter */
220   for (r=thecache; r; r = r->next, count++)
221     {
222       if (r->pw && !strcmp (r->key, key))
223         {
224           /* put_cache does only put strings into the cache, so we
225              don't need the lengths */
226           r->accessed = time (NULL);
227           if (DBG_CACHE)
228             log_debug ("... hit\n");
229           return r->pw->data;
230         }
231     }
232   if (DBG_CACHE)
233     log_debug ("... miss\n");
234
235   return NULL;
236 }
237
238
239