Changes from APPLIANCE_HEAD:
[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         dos_to_unix(keystr, True);             /* Convert key to unix-codepage */
60         dbuf = tdb_fetch_by_string(cache_tdb, keystr);
61         if (!dbuf.dptr || dbuf.dsize != sizeof(rec)) {
62                 goto refetch;
63         }
64         memcpy(&rec, dbuf.dptr, sizeof(rec));
65         free(dbuf.dptr);
66
67         if (t < (rec.mod_time + lp_winbind_cache_time())) {
68                 DEBUG(4,("cached sequence number for %s is %u\n",
69                          domain_name, (unsigned)rec.seq_num));
70                 return rec.seq_num;
71         }
72
73  refetch:       
74         rec.seq_num = domain_sequence_number(domain_name);
75         rec.mod_time = t;
76         tdb_store_by_string(cache_tdb, keystr, &rec, sizeof(rec));
77
78         return rec.seq_num;
79 }
80
81 /* Check whether a seq_num for a cached item has expired */
82 static BOOL cache_domain_expired(char *domain_name, uint32 seq_num)
83 {
84         if (cached_sequence_number(domain_name) != seq_num) {
85                 DEBUG(4,("seq %u for %s has expired\n", (unsigned)seq_num, domain_name));
86                 return True;
87         }
88         return False;
89 }
90
91 static void set_cache_sequence_number(char *domain_name, char *cache_type, char *subkey)
92 {
93         fstring keystr;
94         slprintf(keystr,sizeof(keystr),"CACHESEQ %s/%s/%s",
95                  domain_name, cache_type, subkey?subkey:"");
96         dos_to_unix(keystr, True);             /* Convert key to unix-codepage */
97         tdb_store_int(cache_tdb, keystr, cached_sequence_number(domain_name));
98 }
99
100 static uint32 get_cache_sequence_number(char *domain_name, char *cache_type, char *subkey)
101 {
102         fstring keystr;
103         uint32 seq_num;
104         slprintf(keystr,sizeof(keystr),"CACHESEQ %s/%s/%s",
105                  domain_name, cache_type, subkey?subkey:"");
106         dos_to_unix(keystr, True);             /* Convert key to unix-codepage */
107         seq_num = (uint32)tdb_fetch_int(cache_tdb, keystr);
108         DEBUG(4,("%s is %u\n", keystr, (unsigned)seq_num));
109         return seq_num;
110 }
111
112 /* Fill the user or group cache with supplied data */
113 static void fill_cache(char *domain_name, char *cache_type,
114                        struct acct_info *sam_entries,
115                        int num_sam_entries)
116 {
117         fstring keystr;
118
119         if (lp_winbind_cache_time() == 0) return;
120
121         /* Error check */
122         if (!sam_entries || (num_sam_entries == 0)) return;
123
124         DEBUG(4, ("filling %s cache for domain %s with %d entries\n",
125                   cache_type, domain_name, num_sam_entries));
126
127         /* Store data as a mega-huge chunk in the tdb */
128         slprintf(keystr, sizeof(keystr), "%s CACHE DATA/%s", cache_type,
129                  domain_name);
130         dos_to_unix(keystr, True);             /* Convert key to unix-codepage */
131         tdb_store_by_string(cache_tdb, keystr, 
132                             sam_entries, sizeof(struct acct_info) * num_sam_entries);
133
134         /* Stamp cache with current seq number */
135         set_cache_sequence_number(domain_name, cache_type, NULL);
136 }
137
138 /* Fill the user cache with supplied data */
139
140 void winbindd_fill_user_cache(char *domain_name, 
141                               struct acct_info *sam_entries,
142                               int num_sam_entries)
143 {
144         fill_cache(domain_name, CACHE_TYPE_USER, sam_entries, num_sam_entries);
145 }
146
147 /* Fill the group cache with supplied data */
148
149 void winbindd_fill_group_cache(char *domain_name,
150                                struct acct_info *sam_entries,
151                                int num_sam_entries)
152 {
153         fill_cache(domain_name, CACHE_TYPE_GROUP, sam_entries, num_sam_entries);
154 }
155
156 static void fill_cache_entry(char *domain, char *cache_type, char *name, void *buf, int len)
157 {
158         fstring keystr;
159
160         /* Create key for store */
161         slprintf(keystr, sizeof(keystr), "%s/%s/%s", cache_type, domain, name);
162         dos_to_unix(keystr, True);             /* Convert key to unix-codepage */
163
164         DEBUG(4, ("filling cache entry %s\n", keystr));
165
166         /* Store it */
167         tdb_store_by_string(cache_tdb, keystr, buf, len);
168 }
169
170 /* Fill a user info cache entry */
171 void winbindd_fill_user_cache_entry(char *domain, char *user_name, 
172                                     struct winbindd_pw *pw)
173 {
174         if (lp_winbind_cache_time() == 0) return;
175
176         fill_cache_entry(domain, CACHE_TYPE_USER, user_name, pw, sizeof(struct winbindd_pw));
177         set_cache_sequence_number(domain, CACHE_TYPE_USER, user_name);
178 }
179
180 /* Fill a user uid cache entry */
181 void winbindd_fill_uid_cache_entry(char *domain, uid_t uid, 
182                                     struct winbindd_pw *pw)
183 {
184         fstring uidstr;
185
186         if (lp_winbind_cache_time() == 0) return;
187
188         slprintf(uidstr, sizeof(uidstr), "#%u", (unsigned)uid);
189         fill_cache_entry(domain, CACHE_TYPE_USER, uidstr, pw, sizeof(struct winbindd_pw));
190         set_cache_sequence_number(domain, CACHE_TYPE_USER, uidstr);
191 }
192
193 /* Fill a group info cache entry */
194 void winbindd_fill_group_cache_entry(char *domain, char *group_name, 
195                                      struct winbindd_gr *gr, void *extra_data,
196                                      int extra_data_len)
197 {
198         fstring keystr;
199
200         if (lp_winbind_cache_time() == 0) return;
201
202         /* Fill group data */
203         fill_cache_entry(domain, CACHE_TYPE_GROUP, group_name, gr, sizeof(struct winbindd_gr));
204
205         /* Fill extra data */
206         slprintf(keystr, sizeof(keystr), "%s/%s/%s DATA", CACHE_TYPE_GROUP, domain, group_name);
207         dos_to_unix(keystr, True);         /* Convert key to unix-codepage */
208         tdb_store_by_string(cache_tdb, keystr, extra_data, extra_data_len);
209
210         set_cache_sequence_number(domain, CACHE_TYPE_GROUP, group_name);
211 }
212
213 /* Fill a group info cache entry */
214 void winbindd_fill_gid_cache_entry(char *domain, gid_t gid, 
215                                      struct winbindd_gr *gr, void *extra_data,
216                                      int extra_data_len)
217 {
218         fstring keystr;
219         fstring gidstr;
220
221         slprintf(gidstr, sizeof(gidstr), "#%u", (unsigned)gid);
222
223         if (lp_winbind_cache_time() == 0) return;
224
225         /* Fill group data */
226         fill_cache_entry(domain, CACHE_TYPE_GROUP, gidstr, gr, sizeof(struct winbindd_gr));
227
228         /* Fill extra data */
229         slprintf(keystr, sizeof(keystr), "%s/%s/%s DATA", CACHE_TYPE_GROUP, domain, gidstr);
230         dos_to_unix(keystr, True);         /* Convert key to unix-codepage */
231         tdb_store_by_string(cache_tdb, keystr, extra_data, extra_data_len);
232
233         set_cache_sequence_number(domain, CACHE_TYPE_GROUP, gidstr);
234 }
235
236 /* Fetch some cached user or group data */
237 static BOOL fetch_cache(char *domain_name, char *cache_type,
238                         struct acct_info **sam_entries, int *num_sam_entries)
239 {
240         TDB_DATA data;
241         fstring keystr;
242
243         if (lp_winbind_cache_time() == 0) return False;
244
245         /* Parameter check */
246         if (!sam_entries || !num_sam_entries) {
247                 return False;
248         }
249
250         /* Check cache data is current */
251         if (cache_domain_expired(domain_name, 
252                                  get_cache_sequence_number(domain_name, cache_type, NULL))) {
253                 return False;
254         }
255         
256         /* Create key */        
257         slprintf(keystr, sizeof(keystr), "%s CACHE DATA/%s", cache_type,
258                  domain_name);
259         dos_to_unix(keystr, True);         /* Convert key to unix-codepage */
260         
261         /* Fetch cache information */
262         data = tdb_fetch_by_string(cache_tdb, keystr);
263         
264         if (!data.dptr) return False;
265
266         /* Copy across cached data.  We can save a memcpy() by directly
267            assigning the data.dptr to the sam_entries pointer.  It will
268            be freed by the end{pw,gr}ent() function. */
269         
270         *sam_entries = (struct acct_info *)data.dptr;
271         *num_sam_entries = data.dsize / sizeof(struct acct_info);
272         
273         DEBUG(4, ("fetched %d cached %s entries for domain %s\n",
274                   *num_sam_entries, cache_type, domain_name));
275         
276         return True;
277 }
278
279 /* Return cached entries for a domain.  Return false if there are no cached
280    entries, or the cached information has expired for the domain. */
281
282 BOOL winbindd_fetch_user_cache(char *domain_name,
283                                struct acct_info **sam_entries,
284                                int *num_entries)
285 {
286         return fetch_cache(domain_name, CACHE_TYPE_USER, sam_entries,
287                            num_entries);
288 }
289
290 /* Return cached entries for a domain.  Return false if there are no cached
291    entries, or the cached information has expired for the domain. */
292
293 BOOL winbindd_fetch_group_cache(char *domain_name,
294                                 struct acct_info **sam_entries,
295                                 int *num_entries)
296 {
297         return fetch_cache(domain_name, CACHE_TYPE_GROUP, sam_entries,
298                            num_entries);
299 }
300
301 static BOOL fetch_cache_entry(char *domain, char *cache_type, char *name, void *buf, int len)
302 {
303         TDB_DATA data;
304         fstring keystr;
305     
306         /* Create key for lookup */
307         slprintf(keystr, sizeof(keystr), "%s/%s/%s", cache_type, domain, name);
308         dos_to_unix(keystr, True);             /* Convert key to unix-codepage */
309     
310         /* Look up cache entry */
311         data = tdb_fetch_by_string(cache_tdb, keystr);
312         if (!data.dptr) return False;
313         
314         DEBUG(4, ("returning cached entry for %s\\%s\n", domain, name));
315
316         /* Copy found entry into buffer */        
317         memcpy((char *)buf, data.dptr, len < data.dsize ? len : data.dsize);
318         free(data.dptr);
319         return True;
320 }
321
322 /* Fetch an individual user cache entry */
323 BOOL winbindd_fetch_user_cache_entry(char *domain_name, char *user, 
324                                      struct winbindd_pw *pw)
325 {
326         uint32 seq_num;
327
328         if (lp_winbind_cache_time() == 0) return False;
329
330         seq_num = get_cache_sequence_number(domain_name, CACHE_TYPE_USER, user);
331         if (cache_domain_expired(domain_name, seq_num)) return False;
332
333         return fetch_cache_entry(domain_name, CACHE_TYPE_USER, user, pw, sizeof(struct winbindd_pw));
334 }
335
336 /* Fetch an individual uid cache entry */
337 BOOL winbindd_fetch_uid_cache_entry(char *domain_name, uid_t uid, 
338                                     struct winbindd_pw *pw)
339 {
340         fstring uidstr;
341         uint32 seq_num;
342
343         if (lp_winbind_cache_time() == 0) return False;
344
345         slprintf(uidstr, sizeof(uidstr), "#%u", (unsigned)uid);
346         seq_num = get_cache_sequence_number(domain_name, CACHE_TYPE_USER, uidstr);
347         if (cache_domain_expired(domain_name, seq_num)) return False;
348
349         return fetch_cache_entry(domain_name, CACHE_TYPE_USER, uidstr, pw, sizeof(struct winbindd_pw));
350 }
351
352 /* Fetch an individual group cache entry.  This function differs from the
353    user cache code as we need to store the group membership data. */
354
355 BOOL winbindd_fetch_group_cache_entry(char *domain_name, char *group, 
356                                       struct winbindd_gr *gr,
357                                       void **extra_data, int *extra_data_len)
358 {
359         TDB_DATA data;
360         fstring keystr;
361         uint32 seq_num;
362
363         if (lp_winbind_cache_time() == 0) return False;
364
365         seq_num = get_cache_sequence_number(domain_name, CACHE_TYPE_GROUP, group);
366         if (cache_domain_expired(domain_name, seq_num)) return False;
367
368         /* Fetch group data */
369         if (!fetch_cache_entry(domain_name, CACHE_TYPE_GROUP, group, gr, sizeof(struct winbindd_gr))) return False;
370         
371         /* Fetch extra data */
372         slprintf(keystr, sizeof(keystr), "%s/%s/%s DATA", CACHE_TYPE_GROUP, domain_name, group);
373         dos_to_unix(keystr, True);         /* Convert key to unix-codepage */
374         data = tdb_fetch_by_string(cache_tdb, keystr);
375
376         if (!data.dptr) return False;
377
378         /* Extra data freed when data has been sent */
379         if (extra_data) *extra_data = data.dptr;
380         if (extra_data_len) *extra_data_len = data.dsize;
381         
382         return True;
383 }
384
385
386 /* Fetch an individual gid cache entry.  This function differs from the
387    user cache code as we need to store the group membership data. */
388
389 BOOL winbindd_fetch_gid_cache_entry(char *domain_name, gid_t gid,
390                                     struct winbindd_gr *gr,
391                                     void **extra_data, int *extra_data_len)
392 {
393         TDB_DATA data;
394         fstring keystr;
395         fstring gidstr;
396         uint32 seq_num;
397
398         slprintf(gidstr, sizeof(gidstr), "#%u", (unsigned)gid);
399         
400         if (lp_winbind_cache_time() == 0) return False;
401
402         seq_num = get_cache_sequence_number(domain_name, CACHE_TYPE_GROUP, gidstr);
403         if (cache_domain_expired(domain_name, seq_num)) return False;
404
405         /* Fetch group data */
406         if (!fetch_cache_entry(domain_name, CACHE_TYPE_GROUP, 
407                                gidstr, gr, sizeof(struct winbindd_gr))) return False;
408
409         /* Fetch extra data */
410         slprintf(keystr, sizeof(keystr), "%s/%s/%s DATA", CACHE_TYPE_GROUP, domain_name, gidstr);
411         dos_to_unix(keystr, True);         /* Convert key to unix-codepage */
412         data = tdb_fetch_by_string(cache_tdb, keystr);
413         if (!data.dptr) return False;
414
415         /* Extra data freed when data has been sent */
416         if (extra_data) *extra_data = data.dptr;
417         if (extra_data_len) *extra_data_len = data.dsize;
418
419         return True;
420 }
421
422 /* Flush cache data - easiest to just reopen the tdb */
423 void winbindd_flush_cache(void)
424 {
425         tdb_close(cache_tdb);
426         winbindd_cache_init();
427 }