* gpg-agent.c (main): Disable core dumps.
[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           release_data (r->pw);
93           r->pw = NULL;
94           r->accessed = current;
95         }
96     }
97
98   /* second, make sure that we also remove them based on the created stamp so
99      that the used has to enter it from time to time.  We do this every hour */
100   for (r=thecache; r; r = r->next)
101     {
102       if (r->pw && r->created + 60*60 < current)
103         {
104           release_data (r->pw);
105           r->pw = NULL;
106           r->accessed = current;
107         }
108     }
109
110   /* third, make sure that we don't have too many items in the list.
111      Expire old and unused entries after 30 minutes */
112   for (rprev=NULL, r=thecache; r; )
113     {
114       if (!r->pw && r->accessed + 60*30 < current)
115         {
116           ITEM r2 = r->next;
117           xfree (r);
118           if (!rprev)
119             thecache = r2;
120           else
121             rprev = r2;
122           r = r2;
123         }
124       else
125         {
126           rprev = r;
127           r = r->next;
128         }
129     }
130 }
131
132
133
134 /* Store DATA of length DATALEN in the cache under KEY and mark it
135    with a maxiumum lifetime of TTL seconds.  If tehre is already data
136    under this key, it will be replaced.  Using a DATA of NULL deletes
137    the entry */
138 int
139 agent_put_cache (const char *key, const char *data, int ttl)
140 {
141   ITEM r;
142
143   housekeeping ();
144
145   if (ttl < 1)
146     ttl = 60*5; /* default is 5 minutes */
147
148   for (r=thecache; r; r = r->next)
149     {
150       if ( !strcmp (r->key, key))
151         break;
152     }
153   if (r)
154     { /* replace */
155       if (r->pw)
156         {
157           release_data (r->pw);
158           r->pw = NULL;
159         }
160       if (data)
161         {
162           r->pw = new_data (data, strlen (data)+1);
163           if (!r->pw)
164             log_error ("out of core while allocating new cache item\n");
165         }
166     }
167   else if (data)
168     { /* simply insert */
169       r = xtrycalloc (1, sizeof *r + strlen (key));
170       if (!r)
171         log_error ("out of core while allocating new cache control\n");
172       else
173         {
174           strcpy (r->key, key);
175           r->created = r->accessed = time (NULL); 
176           r->ttl = ttl;
177           r->pw = new_data (data, strlen (data)+1);
178           if (!r->pw)
179             {
180               log_error ("out of core while allocating new cache item\n");
181               xfree (r);
182             }
183           else
184             {
185               r->next = thecache;
186               thecache = r;
187             }
188         }
189     }
190   return 0;
191 }
192
193
194 /* Try to find an item in the cache */
195 const char *
196 agent_get_cache (const char *key)
197 {
198   ITEM r;
199   int count = 0;
200
201   housekeeping ();
202
203   /* FIXME: Returning pointers is not thread safe - add a referencense
204      counter */
205   for (r=thecache; r; r = r->next, count++)
206     {
207       if (r->pw && !strcmp (r->key, key))
208         {
209           /* put_cache does onlu put strings into the cache, so we
210              don't need the lengths */
211           r->accessed = time (NULL);
212           return r->pw->data;
213         }
214     }
215
216   return NULL;
217 }
218
219
220