cd648c696c7d13c76e644a10ba849a5d5449fc59
[samba.git] / source3 / nsswitch / winbindd_cache.c
1 /* 
2    Unix SMB/Netbios implementation.
3    Version 2.0
4
5    Winbind daemon - caching related functions
6
7    Copyright (C) Tim Potter 2000
8    
9    This program is free software; you can redistribute it and/or modify
10    it under the terms of the GNU General Public License as published by
11    the Free Software Foundation; either version 2 of the License, or
12    (at your option) any later version.
13    
14    This program is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License for more details.
18    
19    You should have received a copy of the GNU General Public License
20    along with this program; if not, write to the Free Software
21    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 */
23
24 #include "winbindd.h"
25
26 #define CACHE_TYPE_USER "USR"
27 #define CACHE_TYPE_GROUP "GRP"
28
29 /* Initialise caching system */
30
31 static TDB_CONTEXT *cache_tdb;
32
33 struct cache_rec {
34         uint32 seq_num;
35         time_t mod_time;
36 };
37
38 void winbindd_cache_init(void)
39 {
40         /* Open tdb cache */
41         unlink(lock_path("winbindd_cache.tdb"));
42         if (!(cache_tdb = tdb_open(lock_path("winbindd_cache.tdb"), 0, 
43                                    TDB_NOLOCK,
44                                    O_RDWR | O_CREAT, 0600))) {
45                 DEBUG(0, ("Unable to open tdb cache - user and group caching "
46                           "disabled\n"));
47         }
48 }
49
50 /* get the domain sequence number, possibly re-fetching */
51 static uint32 cached_sequence_number(char *domain_name)
52 {
53         fstring keystr;
54         TDB_DATA dbuf;
55         struct cache_rec rec;
56         time_t t = time(NULL);
57
58         slprintf(keystr, sizeof(keystr), "CACHESEQ/%s", domain_name);
59         dbuf = tdb_fetch_by_string(cache_tdb, keystr);
60         if (!dbuf.dptr || dbuf.dsize != sizeof(rec)) {
61                 goto refetch;
62         }
63         memcpy(&rec, dbuf.dptr, sizeof(rec));
64         free(dbuf.dptr);
65
66         if (t < (rec.mod_time + lp_winbind_cache_time())) {
67                 DEBUG(4,("cached sequence number for %s is %u\n",
68                          domain_name, (unsigned)rec.seq_num));
69                 return rec.seq_num;
70         }
71
72  refetch:       
73         rec.seq_num = domain_sequence_number(domain_name);
74         rec.mod_time = t;
75         tdb_store_by_string(cache_tdb, keystr, &rec, sizeof(rec));
76
77         return rec.seq_num;
78 }
79
80 /* Check whether a seq_num for a cached item has expired */
81 static BOOL cache_domain_expired(char *domain_name, uint32 seq_num)
82 {
83         if (cached_sequence_number(domain_name) != seq_num) {
84                 DEBUG(4,("seq %u for %s has expired\n", (unsigned)seq_num, domain_name));
85                 return True;
86         }
87         return False;
88 }
89
90 static void set_cache_sequence_number(char *domain_name, char *cache_type, char *subkey)
91 {
92         fstring keystr;
93         slprintf(keystr,sizeof(keystr),"CACHESEQ %s/%s/%s",
94                  domain_name, cache_type, subkey?subkey:"");
95         tdb_store_int(cache_tdb, keystr, cached_sequence_number(domain_name));
96 }
97
98 static uint32 get_cache_sequence_number(char *domain_name, char *cache_type, char *subkey)
99 {
100         fstring keystr;
101         uint32 seq_num;
102         slprintf(keystr,sizeof(keystr),"CACHESEQ %s/%s/%s",
103                  domain_name, cache_type, subkey?subkey:"");
104         seq_num = (uint32)tdb_fetch_int(cache_tdb, keystr);
105         DEBUG(4,("%s is %u\n", keystr, (unsigned)seq_num));
106         return seq_num;
107 }
108
109 /* Fill the user or group cache with supplied data */
110 static void fill_cache(char *domain_name, char *cache_type,
111                        struct acct_info *sam_entries,
112                        int num_sam_entries)
113 {
114         fstring keystr;
115
116         if (lp_winbind_cache_time() == 0) return;
117
118         /* Error check */
119         if (!sam_entries || (num_sam_entries == 0)) return;
120
121         DEBUG(4, ("filling %s cache for domain %s with %d entries\n",
122                   cache_type, domain_name, num_sam_entries));
123
124         /* Store data as a mega-huge chunk in the tdb */
125         slprintf(keystr, sizeof(keystr), "%s CACHE DATA/%s", cache_type,
126                  domain_name);
127
128         tdb_store_by_string(cache_tdb, keystr, 
129                             sam_entries, sizeof(struct acct_info) * num_sam_entries);
130
131         /* Stamp cache with current seq number */
132         set_cache_sequence_number(domain_name, cache_type, NULL);
133 }
134
135 /* Fill the user cache with supplied data */
136
137 void winbindd_fill_user_cache(char *domain_name, 
138                               struct acct_info *sam_entries,
139                               int num_sam_entries)
140 {
141         fill_cache(domain_name, CACHE_TYPE_USER, sam_entries, num_sam_entries);
142 }
143
144 /* Fill the group cache with supplied data */
145
146 void winbindd_fill_group_cache(char *domain_name,
147                                struct acct_info *sam_entries,
148                                int num_sam_entries)
149 {
150         fill_cache(domain_name, CACHE_TYPE_GROUP, sam_entries, num_sam_entries);
151 }
152
153 static void fill_cache_entry(char *domain, char *cache_type, char *name, void *buf, int len)
154 {
155         fstring keystr;
156
157         /* Create key for store */
158         slprintf(keystr, sizeof(keystr), "%s/%s/%s", cache_type, domain, name);
159
160         DEBUG(4, ("filling cache entry %s\n", keystr));
161
162         /* Store it */
163         tdb_store_by_string(cache_tdb, keystr, buf, len);
164 }
165
166 /* Fill a user info cache entry */
167 void winbindd_fill_user_cache_entry(char *domain, char *user_name, 
168                                     struct winbindd_pw *pw)
169 {
170         if (lp_winbind_cache_time() == 0) return;
171
172         fill_cache_entry(domain, CACHE_TYPE_USER, user_name, pw, sizeof(struct winbindd_pw));
173         set_cache_sequence_number(domain, CACHE_TYPE_USER, user_name);
174 }
175
176 /* Fill a user uid cache entry */
177 void winbindd_fill_uid_cache_entry(char *domain, uid_t uid, 
178                                     struct winbindd_pw *pw)
179 {
180         fstring uidstr;
181
182         if (lp_winbind_cache_time() == 0) return;
183
184         slprintf(uidstr, sizeof(uidstr), "#%u", (unsigned)uid);
185         fill_cache_entry(domain, CACHE_TYPE_USER, uidstr, pw, sizeof(struct winbindd_pw));
186         set_cache_sequence_number(domain, CACHE_TYPE_USER, uidstr);
187 }
188
189 /* Fill a group info cache entry */
190 void winbindd_fill_group_cache_entry(char *domain, char *group_name, 
191                                      struct winbindd_gr *gr, void *extra_data,
192                                      int extra_data_len)
193 {
194         fstring keystr;
195
196         if (lp_winbind_cache_time() == 0) return;
197
198         /* Fill group data */
199         fill_cache_entry(domain, CACHE_TYPE_GROUP, group_name, gr, sizeof(struct winbindd_gr));
200
201         /* Fill extra data */
202         slprintf(keystr, sizeof(keystr), "%s/%s/%s DATA", CACHE_TYPE_GROUP, domain, group_name);
203         tdb_store_by_string(cache_tdb, keystr, extra_data, extra_data_len);
204
205         set_cache_sequence_number(domain, CACHE_TYPE_GROUP, group_name);
206 }
207
208 /* Fill a group info cache entry */
209 void winbindd_fill_gid_cache_entry(char *domain, gid_t gid, 
210                                      struct winbindd_gr *gr, void *extra_data,
211                                      int extra_data_len)
212 {
213         fstring keystr;
214         fstring gidstr;
215
216         slprintf(gidstr, sizeof(gidstr), "#%u", (unsigned)gid);
217
218         if (lp_winbind_cache_time() == 0) return;
219
220         /* Fill group data */
221         fill_cache_entry(domain, CACHE_TYPE_GROUP, gidstr, gr, sizeof(struct winbindd_gr));
222
223         /* Fill extra data */
224         slprintf(keystr, sizeof(keystr), "%s/%s/%s DATA", CACHE_TYPE_GROUP, domain, gidstr);
225         tdb_store_by_string(cache_tdb, keystr, extra_data, extra_data_len);
226
227         set_cache_sequence_number(domain, CACHE_TYPE_GROUP, gidstr);
228 }
229
230 /* Fetch some cached user or group data */
231 static BOOL fetch_cache(char *domain_name, char *cache_type,
232                         struct acct_info **sam_entries, int *num_sam_entries)
233 {
234         TDB_DATA data;
235         fstring keystr;
236
237         if (lp_winbind_cache_time() == 0) return False;
238
239         /* Parameter check */
240         if (!sam_entries || !num_sam_entries) {
241                 return False;
242         }
243
244         /* Check cache data is current */
245         if (cache_domain_expired(domain_name, 
246                                  get_cache_sequence_number(domain_name, cache_type, NULL))) {
247                 return False;
248         }
249         
250         /* Create key */        
251         slprintf(keystr, sizeof(keystr), "%s CACHE DATA/%s", cache_type,
252                  domain_name);
253         
254         /* Fetch cache information */
255         data = tdb_fetch_by_string(cache_tdb, keystr);
256         
257         if (!data.dptr) return False;
258
259         /* Copy across cached data.  We can save a memcpy() by directly
260            assigning the data.dptr to the sam_entries pointer.  It will
261            be freed by the end{pw,gr}ent() function. */
262         
263         *sam_entries = (struct acct_info *)data.dptr;
264         *num_sam_entries = data.dsize / sizeof(struct acct_info);
265         
266         DEBUG(4, ("fetched %d cached %s entries for domain %s\n",
267                   *num_sam_entries, cache_type, domain_name));
268         
269         return True;
270 }
271
272 /* Return cached entries for a domain.  Return false if there are no cached
273    entries, or the cached information has expired for the domain. */
274
275 BOOL winbindd_fetch_user_cache(char *domain_name,
276                                struct acct_info **sam_entries,
277                                int *num_entries)
278 {
279         return fetch_cache(domain_name, CACHE_TYPE_USER, sam_entries,
280                            num_entries);
281 }
282
283 /* Return cached entries for a domain.  Return false if there are no cached
284    entries, or the cached information has expired for the domain. */
285
286 BOOL winbindd_fetch_group_cache(char *domain_name,
287                                 struct acct_info **sam_entries,
288                                 int *num_entries)
289 {
290         return fetch_cache(domain_name, CACHE_TYPE_GROUP, sam_entries,
291                            num_entries);
292 }
293
294 static BOOL fetch_cache_entry(char *domain, char *cache_type, char *name, void *buf, int len)
295 {
296         TDB_DATA data;
297         fstring keystr;
298     
299         /* Create key for lookup */
300         slprintf(keystr, sizeof(keystr), "%s/%s/%s", cache_type, domain, name);
301     
302         /* Look up cache entry */
303         data = tdb_fetch_by_string(cache_tdb, keystr);
304         if (!data.dptr) return False;
305         
306         DEBUG(4, ("returning cached entry for %s\\%s\n", domain, name));
307
308         /* Copy found entry into buffer */        
309         memcpy((char *)buf, data.dptr, len < data.dsize ? len : data.dsize);
310         free(data.dptr);
311         return True;
312 }
313
314 /* Fetch an individual user cache entry */
315 BOOL winbindd_fetch_user_cache_entry(char *domain_name, char *user, 
316                                      struct winbindd_pw *pw)
317 {
318         uint32 seq_num;
319
320         if (lp_winbind_cache_time() == 0) return False;
321
322         seq_num = get_cache_sequence_number(domain_name, CACHE_TYPE_USER, user);
323         if (cache_domain_expired(domain_name, seq_num)) return False;
324
325         return fetch_cache_entry(domain_name, CACHE_TYPE_USER, user, pw, sizeof(struct winbindd_pw));
326 }
327
328 /* Fetch an individual uid cache entry */
329 BOOL winbindd_fetch_uid_cache_entry(char *domain_name, uid_t uid, 
330                                     struct winbindd_pw *pw)
331 {
332         fstring uidstr;
333         uint32 seq_num;
334
335         if (lp_winbind_cache_time() == 0) return False;
336
337         slprintf(uidstr, sizeof(uidstr), "#%u", (unsigned)uid);
338         seq_num = get_cache_sequence_number(domain_name, CACHE_TYPE_USER, uidstr);
339         if (cache_domain_expired(domain_name, seq_num)) return False;
340
341         return fetch_cache_entry(domain_name, CACHE_TYPE_USER, uidstr, pw, sizeof(struct winbindd_pw));
342 }
343
344 /* Fetch an individual group cache entry.  This function differs from the
345    user cache code as we need to store the group membership data. */
346
347 BOOL winbindd_fetch_group_cache_entry(char *domain_name, char *group, 
348                                       struct winbindd_gr *gr,
349                                       void **extra_data, int *extra_data_len)
350 {
351         TDB_DATA data;
352         fstring keystr;
353         uint32 seq_num;
354
355         if (lp_winbind_cache_time() == 0) return False;
356
357         seq_num = get_cache_sequence_number(domain_name, CACHE_TYPE_GROUP, group);
358         if (cache_domain_expired(domain_name, seq_num)) return False;
359
360         /* Fetch group data */
361         if (!fetch_cache_entry(domain_name, CACHE_TYPE_GROUP, group, gr, sizeof(struct winbindd_gr))) return False;
362         
363         /* Fetch extra data */
364         slprintf(keystr, sizeof(keystr), "%s/%s/%s DATA", CACHE_TYPE_GROUP, domain_name, group);
365         data = tdb_fetch_by_string(cache_tdb, keystr);
366
367         if (!data.dptr) return False;
368
369         /* Extra data freed when data has been sent */
370         if (extra_data) *extra_data = data.dptr;
371         if (extra_data_len) *extra_data_len = data.dsize;
372         
373         return True;
374 }
375
376
377 /* Fetch an individual gid cache entry.  This function differs from the
378    user cache code as we need to store the group membership data. */
379
380 BOOL winbindd_fetch_gid_cache_entry(char *domain_name, gid_t gid,
381                                     struct winbindd_gr *gr,
382                                     void **extra_data, int *extra_data_len)
383 {
384         TDB_DATA data;
385         fstring keystr;
386         fstring gidstr;
387         uint32 seq_num;
388
389         slprintf(gidstr, sizeof(gidstr), "#%u", (unsigned)gid);
390         
391         if (lp_winbind_cache_time() == 0) return False;
392
393         seq_num = get_cache_sequence_number(domain_name, CACHE_TYPE_GROUP, gidstr);
394         if (cache_domain_expired(domain_name, seq_num)) return False;
395
396         /* Fetch group data */
397         if (!fetch_cache_entry(domain_name, CACHE_TYPE_GROUP, 
398                                gidstr, gr, sizeof(struct winbindd_gr))) return False;
399
400         /* Fetch extra data */
401         slprintf(keystr, sizeof(keystr), "%s/%s/%s DATA", CACHE_TYPE_GROUP, domain_name, gidstr);
402         data = tdb_fetch_by_string(cache_tdb, keystr);
403         if (!data.dptr) return False;
404
405         /* Extra data freed when data has been sent */
406         if (extra_data) *extra_data = data.dptr;
407         if (extra_data_len) *extra_data_len = data.dsize;
408
409         return True;
410 }
411
412 /* Flush cache data - easiest to just reopen the tdb */
413 void winbindd_flush_cache(void)
414 {
415         tdb_close(cache_tdb);
416         winbindd_cache_init();
417 }