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