Resurrected sam sequence number code.
[samba.git] / source / 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_log(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 /* find the sequence number for a domain */
50
51 static uint32 domain_sequence_number(struct winbindd_domain *domain)
52 {
53         TALLOC_CTX *mem_ctx;
54         CLI_POLICY_HND *hnd;
55         SAM_UNK_CTR ctr;
56         uint16 switch_value = 2;
57         NTSTATUS result;
58         uint32 seqnum = DOM_SEQUENCE_NONE;
59
60         if (!(mem_ctx = talloc_init()))
61                 return DOM_SEQUENCE_NONE;
62
63         if (!(hnd = cm_get_sam_dom_handle(domain->name, &domain->sid)))
64                 goto done;
65
66         result = cli_samr_query_dom_info(hnd->cli, mem_ctx, &hnd->pol,
67                                          switch_value, &ctr);
68
69         if (NT_STATUS_IS_OK(result))
70                 seqnum = ctr.info.inf2.seq_num;
71
72  done:
73         talloc_destroy(mem_ctx);
74
75         return seqnum;
76 }
77
78 /* get the domain sequence number, possibly re-fetching */
79
80 static uint32 cached_sequence_number(struct winbindd_domain *domain)
81 {
82         fstring keystr;
83         TDB_DATA dbuf;
84         struct cache_rec rec;
85         time_t t = time(NULL);
86
87         snprintf(keystr, sizeof(keystr), "CACHESEQ/%s", domain->name);
88         dbuf = tdb_fetch_by_string(cache_tdb, keystr);
89
90         if (!dbuf.dptr || dbuf.dsize != sizeof(rec))
91                 goto refetch;
92
93         memcpy(&rec, dbuf.dptr, sizeof(rec));
94         SAFE_FREE(dbuf.dptr);
95
96         if (t < (rec.mod_time + lp_winbind_cache_time())) {
97                 DEBUG(3,("cached sequence number for %s is %u\n",
98                          domain->name, (unsigned)rec.seq_num));
99                 return rec.seq_num;
100         }
101
102  refetch:       
103         rec.seq_num = domain_sequence_number(domain);
104         rec.mod_time = t;
105
106         tdb_store_by_string(cache_tdb, keystr, &rec, sizeof(rec));
107
108         return rec.seq_num;
109 }
110
111 /* Check whether a seq_num for a cached item has expired */
112 static BOOL cache_domain_expired(struct winbindd_domain *domain, 
113                                  uint32 seq_num)
114 {
115         if (cached_sequence_number(domain) != seq_num) {
116                 DEBUG(3,("seq %u for %s has expired\n", (unsigned)seq_num, 
117                          domain->name));
118                 return True;
119         }
120
121         return False;
122 }
123
124 static void set_cache_sequence_number(struct winbindd_domain *domain, 
125                                       char *cache_type, char *subkey)
126 {
127         fstring keystr;
128
129         snprintf(keystr, sizeof(keystr),"CACHESEQ %s/%s/%s",
130                  domain->name, cache_type, subkey?subkey:"");
131
132         tdb_store_int(cache_tdb, keystr, cached_sequence_number(domain));
133 }
134
135 static uint32 get_cache_sequence_number(struct winbindd_domain *domain, 
136                                         char *cache_type, char *subkey)
137 {
138         fstring keystr;
139         uint32 seq_num;
140
141         snprintf(keystr, sizeof(keystr), "CACHESEQ %s/%s/%s",
142                  domain->name, cache_type, subkey ? subkey : "");
143
144         seq_num = (uint32)tdb_fetch_int(cache_tdb, keystr);
145
146         DEBUG(3,("%s is %u\n", keystr, (unsigned)seq_num));
147
148         return seq_num;
149 }
150
151 /* Fill the user or group cache with supplied data */
152
153 static void store_cache(struct winbindd_domain *domain, char *cache_type,
154                         void *sam_entries, int buflen)
155 {
156         fstring keystr;
157
158         if (lp_winbind_cache_time() == 0) 
159                 return;
160
161         /* Error check */
162
163         if (!sam_entries || buflen == 0) 
164                 return;
165
166         /* Store data as a mega-huge chunk in the tdb */
167
168         snprintf(keystr, sizeof(keystr), "%s CACHE DATA/%s", cache_type,
169                  domain->name);
170
171         tdb_store_by_string(cache_tdb, keystr, sam_entries, buflen);
172
173         /* Stamp cache with current seq number */
174
175         set_cache_sequence_number(domain, cache_type, NULL);
176 }
177
178 /* Fill the user cache with supplied data */
179
180 void winbindd_store_user_cache(struct winbindd_domain *domain, 
181                                struct getpwent_user *sam_entries,
182                                int num_sam_entries)
183 {
184         DEBUG(3, ("storing user cache %s/%d entries\n", domain->name,
185                   num_sam_entries));
186
187         store_cache(domain, CACHE_TYPE_USER, sam_entries,
188                     num_sam_entries * sizeof(struct getpwent_user));
189 }
190
191 /* Fill the group cache with supplied data */
192
193 void winbindd_store_group_cache(struct winbindd_domain *domain,
194                                 struct acct_info *sam_entries,
195                                 int num_sam_entries)
196 {
197         DEBUG(0, ("storing group cache %s/%d entries\n", domain->name,
198                   num_sam_entries));              
199
200         store_cache(domain, CACHE_TYPE_GROUP, sam_entries, 
201                     num_sam_entries * sizeof(struct acct_info));
202 }
203
204 static void store_cache_entry(struct winbindd_domain *domain, char *cache_type,
205                               char *name, void *buf, int len)
206 {
207         fstring keystr;
208
209         /* Create key for store */
210
211         snprintf(keystr, sizeof(keystr), "%s/%s/%s", cache_type, 
212                  domain->name, name);
213
214         /* Store it */
215
216         tdb_store_by_string(cache_tdb, keystr, buf, len);
217 }
218
219 /* Fill a user info cache entry */
220
221 void winbindd_store_user_cache_entry(struct winbindd_domain *domain, 
222                                      char *user_name, struct winbindd_pw *pw)
223 {
224         if (lp_winbind_cache_time() == 0) 
225                 return;
226
227         store_cache_entry(domain, CACHE_TYPE_USER, user_name, pw, 
228                           sizeof(struct winbindd_pw));
229
230         set_cache_sequence_number(domain, CACHE_TYPE_USER, user_name);
231 }
232
233 /* Fill a user uid cache entry */
234
235 void winbindd_store_uid_cache_entry(struct winbindd_domain *domain, uid_t uid, 
236                                     struct winbindd_pw *pw)
237 {
238         fstring uidstr;
239
240         if (lp_winbind_cache_time() == 0) return;
241
242         snprintf(uidstr, sizeof(uidstr), "#%u", (unsigned)uid);
243
244         DEBUG(3, ("storing uid cache entry %s/%s\n", domain->name, uidstr));
245
246         store_cache_entry(domain, CACHE_TYPE_USER, uidstr, pw, 
247                           sizeof(struct winbindd_pw));
248
249         set_cache_sequence_number(domain, CACHE_TYPE_USER, uidstr);
250 }
251
252 /* Fill a group info cache entry */
253
254 void winbindd_store_group_cache_entry(struct winbindd_domain *domain, 
255                                       char *group_name, struct winbindd_gr *gr,
256                                       void *extra_data, int extra_data_len)
257 {
258         fstring keystr;
259
260         if (lp_winbind_cache_time() == 0) 
261                 return;
262
263         DEBUG(3, ("storing group cache entry %s/%s\n", domain->name, 
264                   group_name));
265
266         /* Fill group data */
267
268         store_cache_entry(domain, CACHE_TYPE_GROUP, group_name, gr, 
269                           sizeof(struct winbindd_gr));
270
271         /* Fill extra data */
272
273         snprintf(keystr, sizeof(keystr), "%s/%s/%s DATA", CACHE_TYPE_GROUP, 
274                  domain->name, group_name);
275
276         tdb_store_by_string(cache_tdb, keystr, extra_data, extra_data_len);
277
278         set_cache_sequence_number(domain, CACHE_TYPE_GROUP, group_name);
279 }
280
281 /* Fill a group info cache entry */
282
283 void winbindd_store_gid_cache_entry(struct winbindd_domain *domain, gid_t gid, 
284                                     struct winbindd_gr *gr, void *extra_data,
285                                     int extra_data_len)
286 {
287         fstring keystr;
288         fstring gidstr;
289
290         snprintf(gidstr, sizeof(gidstr), "#%u", (unsigned)gid);
291
292         if (lp_winbind_cache_time() == 0) 
293                 return;
294
295         DEBUG(3, ("storing gid cache entry %s/%s\n", domain->name, gidstr));
296
297         /* Fill group data */
298
299         store_cache_entry(domain, CACHE_TYPE_GROUP, gidstr, gr, 
300                           sizeof(struct winbindd_gr));
301
302         /* Fill extra data */
303
304         snprintf(keystr, sizeof(keystr), "%s/%s/%s DATA", CACHE_TYPE_GROUP, 
305                  domain->name, gidstr);
306
307         tdb_store_by_string(cache_tdb, keystr, extra_data, extra_data_len);
308
309         set_cache_sequence_number(domain, CACHE_TYPE_GROUP, gidstr);
310 }
311
312 /* Fetch some cached user or group data */
313
314 static BOOL fetch_cache(struct winbindd_domain *domain, char *cache_type,
315                         void **sam_entries, int *buflen)
316 {
317         TDB_DATA data;
318         fstring keystr;
319
320         if (lp_winbind_cache_time() == 0) 
321                 return False;
322
323         /* Parameter check */
324
325         if (!sam_entries || !buflen)
326                 return False;
327
328         /* Check cache data is current */
329
330         if (cache_domain_expired(
331                 domain, get_cache_sequence_number(domain, cache_type, NULL)))
332                 return False;
333         
334         /* Create key */        
335
336         snprintf(keystr, sizeof(keystr), "%s CACHE DATA/%s", cache_type,
337                  domain->name);
338         
339         /* Fetch cache information */
340
341         data = tdb_fetch_by_string(cache_tdb, keystr);
342         
343         if (!data.dptr) 
344                 return False;
345
346         /* Copy across cached data.  We can save a memcpy() by directly
347            assigning the data.dptr to the sam_entries pointer.  It will
348            be freed by the end{pw,gr}ent() function. */
349         
350         *sam_entries = (struct acct_info *)data.dptr;
351         *buflen = data.dsize;
352         
353         return True;
354 }
355
356 /* Return cached entries for a domain.  Return false if there are no cached
357    entries, or the cached information has expired for the domain. */
358
359 BOOL winbindd_fetch_user_cache(struct winbindd_domain *domain, 
360                                struct getpwent_user **sam_entries,
361                                int *num_entries)
362 {
363         BOOL result;
364         int buflen;
365
366         result = fetch_cache(domain, CACHE_TYPE_USER, 
367                              (void **)sam_entries, &buflen);
368
369         *num_entries = buflen / sizeof(struct getpwent_user);
370
371         DEBUG(3, ("fetched %d cache entries for %s\n", *num_entries,
372                   domain->name));
373
374         return result;
375 }
376
377 /* Return cached entries for a domain.  Return false if there are no cached
378    entries, or the cached information has expired for the domain. */
379
380 BOOL winbindd_fetch_group_cache(struct winbindd_domain *domain, 
381                                 struct acct_info **sam_entries,
382                                 int *num_entries)
383 {
384         BOOL result;
385         int buflen;
386
387         result = fetch_cache(domain, CACHE_TYPE_GROUP, 
388                              (void **)sam_entries, &buflen);
389
390         *num_entries = buflen / sizeof(struct acct_info);
391
392         DEBUG(3, ("fetched %d cache entries for %s\n", *num_entries,
393                   domain->name));
394
395         return result;
396 }
397
398 static BOOL fetch_cache_entry(struct winbindd_domain *domain, 
399                               char *cache_type, char *name, void *buf, int len)
400 {
401         TDB_DATA data;
402         fstring keystr;
403     
404         /* Create key for lookup */
405
406         snprintf(keystr, sizeof(keystr), "%s/%s/%s", cache_type, 
407                  domain->name, name);
408     
409         /* Look up cache entry */
410
411         data = tdb_fetch_by_string(cache_tdb, keystr);
412
413         if (!data.dptr) 
414                 return False;
415         
416         /* Copy found entry into buffer */        
417
418         memcpy((char *)buf, data.dptr, len < data.dsize ? len : data.dsize);
419         SAFE_FREE(data.dptr);
420
421         return True;
422 }
423
424 /* Fetch an individual user cache entry */
425
426 BOOL winbindd_fetch_user_cache_entry(struct winbindd_domain *domain, 
427                                      char *user, struct winbindd_pw *pw)
428 {
429         uint32 seq_num;
430
431         if (lp_winbind_cache_time() == 0) 
432                 return False;
433
434         seq_num = get_cache_sequence_number(domain, CACHE_TYPE_USER,
435                                             user);
436
437         if (cache_domain_expired(domain, seq_num)) 
438                 return False;
439
440         return fetch_cache_entry(domain, CACHE_TYPE_USER, user, pw, 
441                                  sizeof(struct winbindd_pw));
442 }
443
444 /* Fetch an individual uid cache entry */
445
446 BOOL winbindd_fetch_uid_cache_entry(struct winbindd_domain *domain, uid_t uid, 
447                                     struct winbindd_pw *pw)
448 {
449         fstring uidstr;
450         uint32 seq_num;
451
452         if (lp_winbind_cache_time() == 0) 
453                 return False;
454
455         snprintf(uidstr, sizeof(uidstr), "#%u", (unsigned)uid);
456
457         seq_num = get_cache_sequence_number(domain, CACHE_TYPE_USER, 
458                                             uidstr);
459
460         if (cache_domain_expired(domain, seq_num)) 
461                 return False;
462
463         return fetch_cache_entry(domain, CACHE_TYPE_USER, uidstr, pw, 
464                                  sizeof(struct winbindd_pw));
465 }
466
467 /* Fetch an individual group cache entry.  This function differs from the
468    user cache code as we need to store the group membership data. */
469
470 BOOL winbindd_fetch_group_cache_entry(struct winbindd_domain *domain, 
471                                       char *group, struct winbindd_gr *gr,
472                                       void **extra_data, int *extra_data_len)
473 {
474         TDB_DATA data;
475         fstring keystr;
476         uint32 seq_num;
477
478         if (lp_winbind_cache_time() == 0) 
479                 return False;
480
481         seq_num = get_cache_sequence_number(domain, CACHE_TYPE_GROUP, 
482                                             group);
483
484         if (cache_domain_expired(domain, seq_num)) 
485                 return False;
486
487         /* Fetch group data */
488
489         if (!fetch_cache_entry(domain, CACHE_TYPE_GROUP, group, gr, 
490                                sizeof(struct winbindd_gr)))
491                 return False;
492         
493         /* Fetch extra data */
494
495         snprintf(keystr, sizeof(keystr), "%s/%s/%s DATA", CACHE_TYPE_GROUP, 
496                  domain->name, group);
497
498         data = tdb_fetch_by_string(cache_tdb, keystr);
499
500         if (!data.dptr) 
501                 return False;
502
503         /* Extra data freed when data has been sent */
504
505         if (extra_data) 
506                 *extra_data = data.dptr;
507
508         if (extra_data_len) 
509                 *extra_data_len = data.dsize;
510         
511         return True;
512 }
513
514
515 /* Fetch an individual gid cache entry.  This function differs from the
516    user cache code as we need to store the group membership data. */
517
518 BOOL winbindd_fetch_gid_cache_entry(struct winbindd_domain *domain, gid_t gid,
519                                     struct winbindd_gr *gr,
520                                     void **extra_data, int *extra_data_len)
521 {
522         TDB_DATA data;
523         fstring keystr;
524         fstring gidstr;
525         uint32 seq_num;
526
527         snprintf(gidstr, sizeof(gidstr), "#%u", (unsigned)gid);
528         
529         if (lp_winbind_cache_time() == 0) 
530                 return False;
531
532         seq_num = get_cache_sequence_number(domain, CACHE_TYPE_GROUP, 
533                                             gidstr);
534
535         if (cache_domain_expired(domain, seq_num)) 
536                 return False;
537
538         /* Fetch group data */
539
540         if (!fetch_cache_entry(domain, CACHE_TYPE_GROUP, 
541                                gidstr, gr, sizeof(struct winbindd_gr)))
542                 return False;
543
544         /* Fetch extra data */
545
546         snprintf(keystr, sizeof(keystr), "%s/%s/%s DATA", CACHE_TYPE_GROUP, 
547                  domain->name, gidstr);
548
549         data = tdb_fetch_by_string(cache_tdb, keystr);
550
551         if (!data.dptr) 
552                 return False;
553
554         /* Extra data freed when data has been sent */
555
556         if (extra_data) 
557                 *extra_data = data.dptr;
558
559         if (extra_data_len) 
560                 *extra_data_len = data.dsize;
561
562         return True;
563 }
564
565 /* Flush cache data - easiest to just reopen the tdb */
566
567 void winbindd_flush_cache(void)
568 {
569         tdb_close(cache_tdb);
570         winbindd_cache_init();
571 }
572
573 /* Print cache status information */
574
575 void winbindd_cache_dump_status(void)
576 {
577 }