Preliminary merge of winbind into HEAD. Note that this compiles and links
[ira/wip.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
42         if (!(cache_tdb = tdb_open(lock_path("winbindd_cache.tdb"), 0, 
43                                    TDB_NOLOCK, O_RDWR | O_CREAT | O_TRUNC, 
44                                    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         snprintf(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(3,("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(3,("seq %u for %s has expired\n", (unsigned)seq_num, 
85                          domain_name));
86                 return True;
87         }
88         return False;
89 }
90
91 static void set_cache_sequence_number(char *domain_name, char *cache_type, 
92                                       char *subkey)
93 {
94         fstring keystr;
95
96         snprintf(keystr, sizeof(keystr),"CACHESEQ %s/%s/%s",
97                  domain_name, cache_type, subkey?subkey:"");
98
99         tdb_store_int(cache_tdb, keystr, cached_sequence_number(domain_name));
100 }
101
102 static uint32 get_cache_sequence_number(char *domain_name, char *cache_type, 
103                                         char *subkey)
104 {
105         fstring keystr;
106         uint32 seq_num;
107
108         snprintf(keystr, sizeof(keystr), "CACHESEQ %s/%s/%s",
109                  domain_name, cache_type, subkey?subkey:"");
110         seq_num = (uint32)tdb_fetch_int(cache_tdb, keystr);
111
112         DEBUG(3,("%s is %u\n", keystr, (unsigned)seq_num));
113
114         return seq_num;
115 }
116
117 /* Fill the user or group cache with supplied data */
118
119 static void store_cache(char *domain_name, char *cache_type,
120                         void *sam_entries, int buflen)
121 {
122         fstring keystr;
123
124         if (lp_winbind_cache_time() == 0) return;
125
126         /* Error check */
127         if (!sam_entries || buflen == 0) return;
128
129         /* Store data as a mega-huge chunk in the tdb */
130         snprintf(keystr, sizeof(keystr), "%s CACHE DATA/%s", cache_type,
131                  domain_name);
132
133         tdb_store_by_string(cache_tdb, keystr, sam_entries, buflen);
134
135         /* Stamp cache with current seq number */
136         set_cache_sequence_number(domain_name, cache_type, NULL);
137 }
138
139 /* Fill the user cache with supplied data */
140
141 void winbindd_store_user_cache(char *domain, 
142                                struct getpwent_user *sam_entries,
143                                int num_sam_entries)
144 {
145         DEBUG(3, ("storing user cache %s/%d entries\n", domain,
146                   num_sam_entries));
147
148         store_cache(domain, CACHE_TYPE_USER, sam_entries,
149                     num_sam_entries * sizeof(struct getpwent_user));
150 }
151
152 /* Fill the group cache with supplied data */
153
154 void winbindd_store_group_cache(char *domain,
155                                 struct acct_info *sam_entries,
156                                 int num_sam_entries)
157 {
158         DEBUG(0, ("storing group cache %s/%d entries\n", domain,
159                   num_sam_entries));              
160
161         store_cache(domain, CACHE_TYPE_GROUP, sam_entries, 
162                     num_sam_entries * sizeof(struct acct_info));
163 }
164
165 static void store_cache_entry(char *domain, char *cache_type, char *name, 
166                               void *buf, int len)
167 {
168         fstring keystr;
169
170         /* Create key for store */
171         snprintf(keystr, sizeof(keystr), "%s/%s/%s", cache_type, domain, name);
172
173         /* Store it */
174         tdb_store_by_string(cache_tdb, keystr, buf, len);
175 }
176
177 /* Fill a user info cache entry */
178
179 void winbindd_store_user_cache_entry(char *domain, char *user_name, 
180                                      struct winbindd_pw *pw)
181 {
182         if (lp_winbind_cache_time() == 0) return;
183
184         store_cache_entry(domain, CACHE_TYPE_USER, user_name, pw, 
185                           sizeof(struct winbindd_pw));
186
187         set_cache_sequence_number(domain, CACHE_TYPE_USER, user_name);
188 }
189
190 /* Fill a user uid cache entry */
191
192 void winbindd_store_uid_cache_entry(char *domain, uid_t uid, 
193                                     struct winbindd_pw *pw)
194 {
195         fstring uidstr;
196
197         if (lp_winbind_cache_time() == 0) return;
198
199         snprintf(uidstr, sizeof(uidstr), "#%u", (unsigned)uid);
200
201         DEBUG(3, ("storing uid cache entry %s/%s\n", domain, uidstr));
202
203         store_cache_entry(domain, CACHE_TYPE_USER, uidstr, pw, 
204                           sizeof(struct winbindd_pw));
205
206         set_cache_sequence_number(domain, CACHE_TYPE_USER, uidstr);
207 }
208
209 /* Fill a group info cache entry */
210 void winbindd_store_group_cache_entry(char *domain, char *group_name, 
211                                       struct winbindd_gr *gr, void *extra_data,
212                                       int extra_data_len)
213 {
214         fstring keystr;
215
216         if (lp_winbind_cache_time() == 0) return;
217
218         DEBUG(3, ("storing group cache entry %s/%s\n", domain, group_name));
219
220         /* Fill group data */
221
222         store_cache_entry(domain, CACHE_TYPE_GROUP, group_name, gr, 
223                           sizeof(struct winbindd_gr));
224
225         /* Fill extra data */
226
227         snprintf(keystr, sizeof(keystr), "%s/%s/%s DATA", CACHE_TYPE_GROUP, 
228                  domain, group_name);
229         tdb_store_by_string(cache_tdb, keystr, extra_data, extra_data_len);
230
231         set_cache_sequence_number(domain, CACHE_TYPE_GROUP, group_name);
232 }
233
234 /* Fill a group info cache entry */
235
236 void winbindd_store_gid_cache_entry(char *domain, gid_t gid, 
237                                     struct winbindd_gr *gr, void *extra_data,
238                                     int extra_data_len)
239 {
240         fstring keystr;
241         fstring gidstr;
242
243         snprintf(gidstr, sizeof(gidstr), "#%u", (unsigned)gid);
244
245         if (lp_winbind_cache_time() == 0) return;
246
247         DEBUG(3, ("storing gid cache entry %s/%s\n", domain, gidstr));
248
249         /* Fill group data */
250
251         store_cache_entry(domain, CACHE_TYPE_GROUP, gidstr, gr, 
252                           sizeof(struct winbindd_gr));
253
254         /* Fill extra data */
255
256         snprintf(keystr, sizeof(keystr), "%s/%s/%s DATA", CACHE_TYPE_GROUP, 
257                  domain, gidstr);
258
259         tdb_store_by_string(cache_tdb, keystr, extra_data, extra_data_len);
260
261         set_cache_sequence_number(domain, CACHE_TYPE_GROUP, gidstr);
262 }
263
264 /* Fetch some cached user or group data */
265 static BOOL fetch_cache(char *domain_name, char *cache_type,
266                         void **sam_entries, int *buflen)
267 {
268         TDB_DATA data;
269         fstring keystr;
270
271         if (lp_winbind_cache_time() == 0) return False;
272
273         /* Parameter check */
274         if (!sam_entries || !buflen) {
275                 return False;
276         }
277
278         /* Check cache data is current */
279         if (cache_domain_expired(domain_name, 
280                                  get_cache_sequence_number(domain_name, 
281                                                            cache_type, 
282                                                            NULL))) {
283                 return False;
284         }
285         
286         /* Create key */        
287         snprintf(keystr, sizeof(keystr), "%s CACHE DATA/%s", cache_type,
288                  domain_name);
289         
290         /* Fetch cache information */
291         data = tdb_fetch_by_string(cache_tdb, keystr);
292         
293         if (!data.dptr) return False;
294
295         /* Copy across cached data.  We can save a memcpy() by directly
296            assigning the data.dptr to the sam_entries pointer.  It will
297            be freed by the end{pw,gr}ent() function. */
298         
299         *sam_entries = (struct acct_info *)data.dptr;
300         *buflen = data.dsize;
301         
302         return True;
303 }
304
305 /* Return cached entries for a domain.  Return false if there are no cached
306    entries, or the cached information has expired for the domain. */
307
308 BOOL winbindd_fetch_user_cache(char *domain_name, 
309                                struct getpwent_user **sam_entries,
310                                int *num_entries)
311 {
312         BOOL result;
313         int buflen;
314
315         result = fetch_cache(domain_name, CACHE_TYPE_USER, 
316                              (void **)sam_entries, &buflen);
317
318         *num_entries = buflen / sizeof(struct getpwent_user);
319
320         DEBUG(3, ("fetched %d cache entries for %s\n", *num_entries,
321                   domain_name));
322
323         return result;
324 }
325
326 /* Return cached entries for a domain.  Return false if there are no cached
327    entries, or the cached information has expired for the domain. */
328
329 BOOL winbindd_fetch_group_cache(char *domain_name, 
330                                 struct acct_info **sam_entries,
331                                 int *num_entries)
332 {
333         BOOL result;
334         int buflen;
335
336         result = fetch_cache(domain_name, CACHE_TYPE_GROUP, 
337                              (void **)sam_entries, &buflen);
338
339         *num_entries = buflen / sizeof(struct acct_info);
340
341         DEBUG(3, ("fetched %d cache entries for %s\n", *num_entries,
342                   domain_name));
343
344         return result;
345 }
346
347 static BOOL fetch_cache_entry(char *domain, char *cache_type, char *name, 
348                               void *buf, int len)
349 {
350         TDB_DATA data;
351         fstring keystr;
352     
353         /* Create key for lookup */
354         snprintf(keystr, sizeof(keystr), "%s/%s/%s", cache_type, domain, name);
355     
356         /* Look up cache entry */
357         data = tdb_fetch_by_string(cache_tdb, keystr);
358         if (!data.dptr) return False;
359         
360         /* Copy found entry into buffer */        
361         memcpy((char *)buf, data.dptr, len < data.dsize ? len : data.dsize);
362         free(data.dptr);
363         return True;
364 }
365
366 /* Fetch an individual user cache entry */
367 BOOL winbindd_fetch_user_cache_entry(char *domain_name, char *user, 
368                                      struct winbindd_pw *pw)
369 {
370         uint32 seq_num;
371
372         if (lp_winbind_cache_time() == 0) return False;
373
374         seq_num = get_cache_sequence_number(domain_name, CACHE_TYPE_USER,
375                                             user);
376         if (cache_domain_expired(domain_name, seq_num)) return False;
377
378         return fetch_cache_entry(domain_name, CACHE_TYPE_USER, user, pw, 
379                                  sizeof(struct winbindd_pw));
380 }
381
382 /* Fetch an individual uid cache entry */
383 BOOL winbindd_fetch_uid_cache_entry(char *domain_name, uid_t uid, 
384                                     struct winbindd_pw *pw)
385 {
386         fstring uidstr;
387         uint32 seq_num;
388
389         if (lp_winbind_cache_time() == 0) return False;
390
391         snprintf(uidstr, sizeof(uidstr), "#%u", (unsigned)uid);
392         seq_num = get_cache_sequence_number(domain_name, CACHE_TYPE_USER, 
393                                             uidstr);
394         if (cache_domain_expired(domain_name, seq_num)) return False;
395
396         return fetch_cache_entry(domain_name, CACHE_TYPE_USER, uidstr, pw, 
397                                  sizeof(struct winbindd_pw));
398 }
399
400 /* Fetch an individual group cache entry.  This function differs from the
401    user cache code as we need to store the group membership data. */
402
403 BOOL winbindd_fetch_group_cache_entry(char *domain_name, char *group, 
404                                       struct winbindd_gr *gr,
405                                       void **extra_data, int *extra_data_len)
406 {
407         TDB_DATA data;
408         fstring keystr;
409         uint32 seq_num;
410
411         if (lp_winbind_cache_time() == 0) return False;
412
413         seq_num = get_cache_sequence_number(domain_name, CACHE_TYPE_GROUP, 
414                                             group);
415
416         if (cache_domain_expired(domain_name, seq_num)) return False;
417
418         /* Fetch group data */
419         if (!fetch_cache_entry(domain_name, CACHE_TYPE_GROUP, group, gr, 
420                                sizeof(struct winbindd_gr))) {
421                 return False;
422         }
423         
424         /* Fetch extra data */
425         snprintf(keystr, sizeof(keystr), "%s/%s/%s DATA", CACHE_TYPE_GROUP, 
426                  domain_name, group);
427
428         data = tdb_fetch_by_string(cache_tdb, keystr);
429
430         if (!data.dptr) return False;
431
432         /* Extra data freed when data has been sent */
433         if (extra_data) *extra_data = data.dptr;
434         if (extra_data_len) *extra_data_len = data.dsize;
435         
436         return True;
437 }
438
439
440 /* Fetch an individual gid cache entry.  This function differs from the
441    user cache code as we need to store the group membership data. */
442
443 BOOL winbindd_fetch_gid_cache_entry(char *domain_name, gid_t gid,
444                                     struct winbindd_gr *gr,
445                                     void **extra_data, int *extra_data_len)
446 {
447         TDB_DATA data;
448         fstring keystr;
449         fstring gidstr;
450         uint32 seq_num;
451
452         snprintf(gidstr, sizeof(gidstr), "#%u", (unsigned)gid);
453         
454         if (lp_winbind_cache_time() == 0) return False;
455
456         seq_num = get_cache_sequence_number(domain_name, CACHE_TYPE_GROUP, 
457                                             gidstr);
458
459         if (cache_domain_expired(domain_name, seq_num)) return False;
460
461         /* Fetch group data */
462         if (!fetch_cache_entry(domain_name, CACHE_TYPE_GROUP, 
463                                gidstr, gr, sizeof(struct winbindd_gr))) {
464                 return False;
465         }
466
467         /* Fetch extra data */
468         snprintf(keystr, sizeof(keystr), "%s/%s/%s DATA", CACHE_TYPE_GROUP, 
469                  domain_name, gidstr);
470         data = tdb_fetch_by_string(cache_tdb, keystr);
471         if (!data.dptr) return False;
472
473         /* Extra data freed when data has been sent */
474         if (extra_data) *extra_data = data.dptr;
475         if (extra_data_len) *extra_data_len = data.dsize;
476
477         return True;
478 }
479
480 /* Flush cache data - easiest to just reopen the tdb */
481 void winbindd_flush_cache(void)
482 {
483         tdb_close(cache_tdb);
484         winbindd_cache_init();
485 }
486
487 /* Print cache status information */
488 void winbindd_cache_dump_status(void)
489 {
490 }