r14674: Further cleanup for cached logins, only dump hashes with DEBUG_PASSWORD.
[jra/samba/.git] / source / nsswitch / winbindd_cache.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    Winbind cache backend functions
5
6    Copyright (C) Andrew Tridgell 2001
7    Copyright (C) Gerald Carter   2003
8    Copyright (C) Volker Lendecke 2005
9    Copyright (C) Guenther Deschner 2005
10    
11    This program is free software; you can redistribute it and/or modify
12    it under the terms of the GNU General Public License as published by
13    the Free Software Foundation; either version 2 of the License, or
14    (at your option) any later version.
15    
16    This program is distributed in the hope that it will be useful,
17    but WITHOUT ANY WARRANTY; without even the implied warranty of
18    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19    GNU General Public License for more details.
20    
21    You should have received a copy of the GNU General Public License
22    along with this program; if not, write to the Free Software
23    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24 */
25
26 #include "includes.h"
27 #include "winbindd.h"
28
29 #undef DBGC_CLASS
30 #define DBGC_CLASS DBGC_WINBIND
31
32 /* Global online/offline state - False when online. winbindd starts up online
33    and sets this to true if the first query fails and there's an entry in
34    the cache tdb telling us to stay offline. */
35
36 static BOOL global_winbindd_offline_state;
37
38 struct winbind_cache {
39         TDB_CONTEXT *tdb;
40 };
41
42 struct cache_entry {
43         NTSTATUS status;
44         uint32 sequence_number;
45         uint8 *data;
46         uint32 len, ofs;
47 };
48
49 #define WINBINDD_MAX_CACHE_SIZE (50*1024*1024)
50
51 static struct winbind_cache *wcache;
52
53 void winbindd_check_cache_size(time_t t)
54 {
55         static time_t last_check_time;
56         struct stat st;
57
58         if (last_check_time == (time_t)0)
59                 last_check_time = t;
60
61         if (t - last_check_time < 60 && t - last_check_time > 0)
62                 return;
63
64         if (wcache == NULL || wcache->tdb == NULL) {
65                 DEBUG(0, ("Unable to check size of tdb cache - cache not open !\n"));
66                 return;
67         }
68
69         if (fstat(wcache->tdb->fd, &st) == -1) {
70                 DEBUG(0, ("Unable to check size of tdb cache %s!\n", strerror(errno) ));
71                 return;
72         }
73
74         if (st.st_size > WINBINDD_MAX_CACHE_SIZE) {
75                 DEBUG(10,("flushing cache due to size (%lu) > (%lu)\n",
76                         (unsigned long)st.st_size,
77                         (unsigned long)WINBINDD_MAX_CACHE_SIZE));
78                 wcache_flush_cache();
79         }
80 }
81
82 /* get the winbind_cache structure */
83 static struct winbind_cache *get_cache(struct winbindd_domain *domain)
84 {
85         struct winbind_cache *ret = wcache;
86 #ifdef HAVE_ADS
87         struct winbindd_domain *our_domain = domain;
88 #endif
89
90         /* we have to know what type of domain we are dealing with first */
91
92         if ( !domain->initialized )
93                 set_dc_type_and_flags( domain );
94
95         /* 
96            OK.  listen up becasue I'm only going to say this once.
97            We have the following scenarios to consider
98            (a) trusted AD domains on a Samba DC,
99            (b) trusted AD domains and we are joined to a non-kerberos domain
100            (c) trusted AD domains and we are joined to a kerberos (AD) domain
101
102            For (a) we can always contact the trusted domain using krb5 
103            since we have the domain trust account password
104
105            For (b) we can only use RPC since we have no way of 
106            getting a krb5 ticket in our own domain
107
108            For (c) we can always use krb5 since we have a kerberos trust
109
110            --jerry
111          */
112
113         if (!domain->backend) {
114                 extern struct winbindd_methods reconnect_methods;
115 #ifdef HAVE_ADS
116                 extern struct winbindd_methods ads_methods;
117
118                 /* find our domain first so we can figure out if we 
119                    are joined to a kerberized domain */
120
121                 if ( !domain->primary )
122                         our_domain = find_our_domain();
123
124                 if ( (our_domain->active_directory || IS_DC) && domain->active_directory ) {
125                         DEBUG(5,("get_cache: Setting ADS methods for domain %s\n", domain->name));
126                         domain->backend = &ads_methods;
127                 } else {
128 #endif  /* HAVE_ADS */
129                         DEBUG(5,("get_cache: Setting MS-RPC methods for domain %s\n", domain->name));
130                         domain->backend = &reconnect_methods;
131 #ifdef HAVE_ADS
132                 }
133 #endif  /* HAVE_ADS */
134         }
135
136         if (ret)
137                 return ret;
138         
139         ret = SMB_XMALLOC_P(struct winbind_cache);
140         ZERO_STRUCTP(ret);
141
142         wcache = ret;
143         wcache_flush_cache();
144
145         return ret;
146 }
147
148 /*
149   free a centry structure
150 */
151 static void centry_free(struct cache_entry *centry)
152 {
153         if (!centry)
154                 return;
155         SAFE_FREE(centry->data);
156         free(centry);
157 }
158
159 /*
160   pull a uint32 from a cache entry 
161 */
162 static uint32 centry_uint32(struct cache_entry *centry)
163 {
164         uint32 ret;
165         if (centry->len - centry->ofs < 4) {
166                 DEBUG(0,("centry corruption? needed 4 bytes, have %d\n", 
167                          centry->len - centry->ofs));
168                 smb_panic("centry_uint32");
169         }
170         ret = IVAL(centry->data, centry->ofs);
171         centry->ofs += 4;
172         return ret;
173 }
174
175 /*
176   pull a uint16 from a cache entry 
177 */
178 static uint16 centry_uint16(struct cache_entry *centry)
179 {
180         uint16 ret;
181         if (centry->len - centry->ofs < 2) {
182                 DEBUG(0,("centry corruption? needed 2 bytes, have %d\n", 
183                          centry->len - centry->ofs));
184                 smb_panic("centry_uint16");
185         }
186         ret = CVAL(centry->data, centry->ofs);
187         centry->ofs += 2;
188         return ret;
189 }
190
191 /*
192   pull a uint8 from a cache entry 
193 */
194 static uint8 centry_uint8(struct cache_entry *centry)
195 {
196         uint8 ret;
197         if (centry->len - centry->ofs < 1) {
198                 DEBUG(0,("centry corruption? needed 1 bytes, have %d\n", 
199                          centry->len - centry->ofs));
200                 smb_panic("centry_uint32");
201         }
202         ret = CVAL(centry->data, centry->ofs);
203         centry->ofs += 1;
204         return ret;
205 }
206
207 /*
208   pull a NTTIME from a cache entry 
209 */
210 static NTTIME centry_nttime(struct cache_entry *centry)
211 {
212         NTTIME ret;
213         if (centry->len - centry->ofs < 8) {
214                 DEBUG(0,("centry corruption? needed 8 bytes, have %d\n", 
215                          centry->len - centry->ofs));
216                 smb_panic("centry_nttime");
217         }
218         ret.low = IVAL(centry->data, centry->ofs);
219         centry->ofs += 4;
220         ret.high = IVAL(centry->data, centry->ofs);
221         centry->ofs += 4;
222         return ret;
223 }
224
225 /*
226   pull a time_t from a cache entry 
227 */
228 static time_t centry_time(struct cache_entry *centry)
229 {
230         time_t ret;
231         if (centry->len - centry->ofs < sizeof(time_t)) {
232                 DEBUG(0,("centry corruption? needed %d bytes, have %d\n", 
233                          sizeof(time_t), centry->len - centry->ofs));
234                 smb_panic("centry_time");
235         }
236         ret = IVAL(centry->data, centry->ofs); /* FIXME: correct ? */
237         centry->ofs += sizeof(time_t);
238         return ret;
239 }
240
241 /* pull a string from a cache entry, using the supplied
242    talloc context 
243 */
244 static char *centry_string(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
245 {
246         uint32 len;
247         char *ret;
248
249         len = centry_uint8(centry);
250
251         if (len == 0xFF) {
252                 /* a deliberate NULL string */
253                 return NULL;
254         }
255
256         if (centry->len - centry->ofs < len) {
257                 DEBUG(0,("centry corruption? needed %d bytes, have %d\n", 
258                          len, centry->len - centry->ofs));
259                 smb_panic("centry_string");
260         }
261
262         ret = TALLOC(mem_ctx, len+1);
263         if (!ret) {
264                 smb_panic("centry_string out of memory\n");
265         }
266         memcpy(ret,centry->data + centry->ofs, len);
267         ret[len] = 0;
268         centry->ofs += len;
269         return ret;
270 }
271
272 /* pull a string from a cache entry, using the supplied
273    talloc context 
274 */
275 static BOOL centry_sid(struct cache_entry *centry, TALLOC_CTX *mem_ctx, DOM_SID *sid)
276 {
277         char *sid_string;
278         sid_string = centry_string(centry, mem_ctx);
279         if (!string_to_sid(sid, sid_string)) {
280                 return False;
281         }
282         return True;
283 }
284
285 /* the server is considered down if it can't give us a sequence number */
286 static BOOL wcache_server_down(struct winbindd_domain *domain)
287 {
288         BOOL ret;
289
290         if (!wcache->tdb)
291                 return False;
292
293         ret = (domain->sequence_number == DOM_SEQUENCE_NONE);
294
295         if (ret)
296                 DEBUG(10,("wcache_server_down: server for Domain %s down\n", 
297                         domain->name ));
298         return ret;
299 }
300
301 static NTSTATUS fetch_cache_seqnum( struct winbindd_domain *domain, time_t now )
302 {
303         TDB_DATA data;
304         fstring key;
305         uint32 time_diff;
306         
307         if (!wcache->tdb) {
308                 DEBUG(10,("fetch_cache_seqnum: tdb == NULL\n"));
309                 return NT_STATUS_UNSUCCESSFUL;
310         }
311                 
312         fstr_sprintf( key, "SEQNUM/%s", domain->name );
313         
314         data = tdb_fetch_bystring( wcache->tdb, key );
315         if ( !data.dptr || data.dsize!=8 ) {
316                 DEBUG(10,("fetch_cache_seqnum: invalid data size key [%s]\n", key ));
317                 return NT_STATUS_UNSUCCESSFUL;
318         }
319         
320         domain->sequence_number = IVAL(data.dptr, 0);
321         domain->last_seq_check  = IVAL(data.dptr, 4);
322         
323         SAFE_FREE(data.dptr);
324
325         /* have we expired? */
326         
327         time_diff = now - domain->last_seq_check;
328         if ( time_diff > lp_winbind_cache_time() ) {
329                 DEBUG(10,("fetch_cache_seqnum: timeout [%s][%u @ %u]\n",
330                         domain->name, domain->sequence_number,
331                         (uint32)domain->last_seq_check));
332                 return NT_STATUS_UNSUCCESSFUL;
333         }
334
335         DEBUG(10,("fetch_cache_seqnum: success [%s][%u @ %u]\n", 
336                 domain->name, domain->sequence_number, 
337                 (uint32)domain->last_seq_check));
338
339         return NT_STATUS_OK;
340 }
341
342 static NTSTATUS store_cache_seqnum( struct winbindd_domain *domain )
343 {
344         TDB_DATA data, key;
345         fstring key_str;
346         char buf[8];
347         
348         if (!wcache->tdb) {
349                 DEBUG(10,("store_cache_seqnum: tdb == NULL\n"));
350                 return NT_STATUS_UNSUCCESSFUL;
351         }
352                 
353         fstr_sprintf( key_str, "SEQNUM/%s", domain->name );
354         key.dptr = key_str;
355         key.dsize = strlen(key_str)+1;
356         
357         SIVAL(buf, 0, domain->sequence_number);
358         SIVAL(buf, 4, domain->last_seq_check);
359         data.dptr = buf;
360         data.dsize = 8;
361         
362         if ( tdb_store( wcache->tdb, key, data, TDB_REPLACE) == -1 ) {
363                 DEBUG(10,("store_cache_seqnum: tdb_store fail key [%s]\n", key_str ));
364                 return NT_STATUS_UNSUCCESSFUL;
365         }
366
367         DEBUG(10,("store_cache_seqnum: success [%s][%u @ %u]\n", 
368                 domain->name, domain->sequence_number, 
369                 (uint32)domain->last_seq_check));
370         
371         return NT_STATUS_OK;
372 }
373
374 /*
375   refresh the domain sequence number. If force is True
376   then always refresh it, no matter how recently we fetched it
377 */
378
379 static void refresh_sequence_number(struct winbindd_domain *domain, BOOL force)
380 {
381         NTSTATUS status;
382         unsigned time_diff;
383         time_t t = time(NULL);
384         unsigned cache_time = lp_winbind_cache_time();
385
386         get_cache( domain );
387
388 #if 0   /* JERRY -- disable as the default cache time is now 5 minutes */
389         /* trying to reconnect is expensive, don't do it too often */
390         if (domain->sequence_number == DOM_SEQUENCE_NONE) {
391                 cache_time *= 8;
392         }
393 #endif
394
395         time_diff = t - domain->last_seq_check;
396
397         /* see if we have to refetch the domain sequence number */
398         if (!force && (time_diff < cache_time)) {
399                 DEBUG(10, ("refresh_sequence_number: %s time ok\n", domain->name));
400                 goto done;
401         }
402         
403         /* try to get the sequence number from the tdb cache first */
404         /* this will update the timestamp as well */
405         
406         status = fetch_cache_seqnum( domain, t );
407         if ( NT_STATUS_IS_OK(status) )
408                 goto done;      
409
410         /* important! make sure that we know if this is a native 
411            mode domain or not */
412
413         status = domain->backend->sequence_number(domain, &domain->sequence_number);
414
415         if (!NT_STATUS_IS_OK(status)) {
416                 domain->sequence_number = DOM_SEQUENCE_NONE;
417         }
418         
419         domain->last_status = status;
420         domain->last_seq_check = time(NULL);
421         
422         /* save the new sequence number ni the cache */
423         store_cache_seqnum( domain );
424
425 done:
426         DEBUG(10, ("refresh_sequence_number: %s seq number is now %d\n", 
427                    domain->name, domain->sequence_number));
428
429         return;
430 }
431
432 /*
433   decide if a cache entry has expired
434 */
435 static BOOL centry_expired(struct winbindd_domain *domain, const char *keystr, struct cache_entry *centry)
436 {
437         /* If we've been told to be offline - stay in that state... */
438         if (lp_winbind_offline_logon() && global_winbindd_offline_state) {
439                 DEBUG(10,("centry_expired: Key %s for domain %s valid as winbindd is globally offline.\n",
440                         keystr, domain->name ));
441                 return False;
442         }
443
444         /* when the domain is offline and we havent checked in the last 30
445          * seconds if it has become online again, return the cached entry.
446          * This deals with transient offline states... */
447
448         if (!domain->online && 
449             !NT_STATUS_IS_OK(check_negative_conn_cache(domain->name, domain->dcname))) {
450                 DEBUG(10,("centry_expired: Key %s for domain %s valid as domain is offline.\n",
451                         keystr, domain->name ));
452                 return False;
453         }
454
455         /* if the server is OK and our cache entry came from when it was down then
456            the entry is invalid */
457         if ((domain->sequence_number != DOM_SEQUENCE_NONE) &&  
458             (centry->sequence_number == DOM_SEQUENCE_NONE)) {
459                 DEBUG(10,("centry_expired: Key %s for domain %s invalid sequence.\n",
460                         keystr, domain->name ));
461                 return True;
462         }
463
464         /* if the server is down or the cache entry is not older than the
465            current sequence number then it is OK */
466         if (wcache_server_down(domain) || 
467             centry->sequence_number == domain->sequence_number) {
468                 DEBUG(10,("centry_expired: Key %s for domain %s is good.\n",
469                         keystr, domain->name ));
470                 return False;
471         }
472
473         DEBUG(10,("centry_expired: Key %s for domain %s expired\n",
474                 keystr, domain->name ));
475
476         /* it's expired */
477         return True;
478 }
479
480 static struct cache_entry *wcache_fetch_raw(char *kstr)
481 {
482         TDB_DATA data;
483         struct cache_entry *centry;
484         TDB_DATA key;
485
486         key.dptr = kstr;
487         key.dsize = strlen(kstr);
488         data = tdb_fetch(wcache->tdb, key);
489         if (!data.dptr) {
490                 /* a cache miss */
491                 return NULL;
492         }
493
494         centry = SMB_XMALLOC_P(struct cache_entry);
495         centry->data = (unsigned char *)data.dptr;
496         centry->len = data.dsize;
497         centry->ofs = 0;
498
499         if (centry->len < 8) {
500                 /* huh? corrupt cache? */
501                 DEBUG(10,("wcache_fetch_raw: Corrupt cache for key %s (len < 8) ?\n", kstr));
502                 centry_free(centry);
503                 return NULL;
504         }
505         
506         centry->status = NT_STATUS(centry_uint32(centry));
507         centry->sequence_number = centry_uint32(centry);
508
509         return centry;
510 }
511
512 /*
513   fetch an entry from the cache, with a varargs key. auto-fetch the sequence
514   number and return status
515 */
516 static struct cache_entry *wcache_fetch(struct winbind_cache *cache, 
517                                         struct winbindd_domain *domain,
518                                         const char *format, ...) PRINTF_ATTRIBUTE(3,4);
519 static struct cache_entry *wcache_fetch(struct winbind_cache *cache, 
520                                         struct winbindd_domain *domain,
521                                         const char *format, ...)
522 {
523         va_list ap;
524         char *kstr;
525         struct cache_entry *centry;
526
527         refresh_sequence_number(domain, False);
528
529         va_start(ap, format);
530         smb_xvasprintf(&kstr, format, ap);
531         va_end(ap);
532
533         centry = wcache_fetch_raw(kstr);
534         if (centry == NULL) {
535                 free(kstr);
536                 return NULL;
537         }
538
539         if (centry_expired(domain, kstr, centry)) {
540
541                 DEBUG(10,("wcache_fetch: entry %s expired for domain %s\n",
542                          kstr, domain->name ));
543
544                 centry_free(centry);
545                 free(kstr);
546                 return NULL;
547         }
548
549         DEBUG(10,("wcache_fetch: returning entry %s for domain %s\n",
550                  kstr, domain->name ));
551
552         free(kstr);
553         return centry;
554 }
555
556 /*
557   make sure we have at least len bytes available in a centry 
558 */
559 static void centry_expand(struct cache_entry *centry, uint32 len)
560 {
561         if (centry->len - centry->ofs >= len)
562                 return;
563         centry->len *= 2;
564         centry->data = SMB_REALLOC(centry->data, centry->len);
565         if (!centry->data) {
566                 DEBUG(0,("out of memory: needed %d bytes in centry_expand\n", centry->len));
567                 smb_panic("out of memory in centry_expand");
568         }
569 }
570
571 /*
572   push a uint32 into a centry 
573 */
574 static void centry_put_uint32(struct cache_entry *centry, uint32 v)
575 {
576         centry_expand(centry, 4);
577         SIVAL(centry->data, centry->ofs, v);
578         centry->ofs += 4;
579 }
580
581 /*
582   push a uint16 into a centry 
583 */
584 static void centry_put_uint16(struct cache_entry *centry, uint16 v)
585 {
586         centry_expand(centry, 2);
587         SIVAL(centry->data, centry->ofs, v);
588         centry->ofs += 2;
589 }
590
591 /*
592   push a uint8 into a centry 
593 */
594 static void centry_put_uint8(struct cache_entry *centry, uint8 v)
595 {
596         centry_expand(centry, 1);
597         SCVAL(centry->data, centry->ofs, v);
598         centry->ofs += 1;
599 }
600
601 /* 
602    push a string into a centry 
603  */
604 static void centry_put_string(struct cache_entry *centry, const char *s)
605 {
606         int len;
607
608         if (!s) {
609                 /* null strings are marked as len 0xFFFF */
610                 centry_put_uint8(centry, 0xFF);
611                 return;
612         }
613
614         len = strlen(s);
615         /* can't handle more than 254 char strings. Truncating is probably best */
616         if (len > 254) {
617                 DEBUG(10,("centry_put_string: truncating len (%d) to: 254\n", len));
618                 len = 254;
619         }
620         centry_put_uint8(centry, len);
621         centry_expand(centry, len);
622         memcpy(centry->data + centry->ofs, s, len);
623         centry->ofs += len;
624 }
625
626 static void centry_put_sid(struct cache_entry *centry, const DOM_SID *sid) 
627 {
628         fstring sid_string;
629         centry_put_string(centry, sid_to_string(sid_string, sid));
630 }
631
632 /*
633   push a NTTIME into a centry 
634 */
635 static void centry_put_nttime(struct cache_entry *centry, NTTIME nt)
636 {
637         centry_expand(centry, 8);
638         SIVAL(centry->data, centry->ofs, nt.low);
639         centry->ofs += 4;
640         SIVAL(centry->data, centry->ofs, nt.high);
641         centry->ofs += 4;
642 }
643
644 /*
645   push a time_t into a centry 
646 */
647 static void centry_put_time(struct cache_entry *centry, time_t t)
648 {
649         centry_expand(centry, sizeof(time_t));
650         SIVAL(centry->data, centry->ofs, t); /* FIXME: is this correct ?? */
651         centry->ofs += sizeof(time_t);
652 }
653
654 /*
655   start a centry for output. When finished, call centry_end()
656 */
657 struct cache_entry *centry_start(struct winbindd_domain *domain, NTSTATUS status)
658 {
659         struct cache_entry *centry;
660
661         if (!wcache->tdb)
662                 return NULL;
663
664         centry = SMB_XMALLOC_P(struct cache_entry);
665
666         centry->len = 8192; /* reasonable default */
667         centry->data = SMB_XMALLOC_ARRAY(uint8, centry->len);
668         centry->ofs = 0;
669         centry->sequence_number = domain->sequence_number;
670         centry_put_uint32(centry, NT_STATUS_V(status));
671         centry_put_uint32(centry, centry->sequence_number);
672         return centry;
673 }
674
675 /*
676   finish a centry and write it to the tdb
677 */
678 static void centry_end(struct cache_entry *centry, const char *format, ...) PRINTF_ATTRIBUTE(2,3);
679 static void centry_end(struct cache_entry *centry, const char *format, ...)
680 {
681         va_list ap;
682         char *kstr;
683         TDB_DATA key, data;
684
685         va_start(ap, format);
686         smb_xvasprintf(&kstr, format, ap);
687         va_end(ap);
688
689         key.dptr = kstr;
690         key.dsize = strlen(kstr);
691         data.dptr = (char *)centry->data;
692         data.dsize = centry->ofs;
693
694         tdb_store(wcache->tdb, key, data, TDB_REPLACE);
695         free(kstr);
696 }
697
698 static void wcache_save_name_to_sid(struct winbindd_domain *domain, 
699                                     NTSTATUS status, const char *domain_name,
700                                     const char *name, const DOM_SID *sid, 
701                                     enum SID_NAME_USE type)
702 {
703         struct cache_entry *centry;
704         fstring uname;
705
706         centry = centry_start(domain, status);
707         if (!centry)
708                 return;
709         centry_put_uint32(centry, type);
710         centry_put_sid(centry, sid);
711         fstrcpy(uname, name);
712         strupper_m(uname);
713         centry_end(centry, "NS/%s/%s", domain_name, uname);
714         DEBUG(10,("wcache_save_name_to_sid: %s -> %s\n", uname,
715                   sid_string_static(sid)));
716         centry_free(centry);
717 }
718
719 static void wcache_save_sid_to_name(struct winbindd_domain *domain, NTSTATUS status, 
720                                     const DOM_SID *sid, const char *domain_name, const char *name, enum SID_NAME_USE type)
721 {
722         struct cache_entry *centry;
723         fstring sid_string;
724
725         centry = centry_start(domain, status);
726         if (!centry)
727                 return;
728         if (NT_STATUS_IS_OK(status)) {
729                 centry_put_uint32(centry, type);
730                 centry_put_string(centry, domain_name);
731                 centry_put_string(centry, name);
732         }
733         centry_end(centry, "SN/%s", sid_to_string(sid_string, sid));
734         DEBUG(10,("wcache_save_sid_to_name: %s -> %s\n", sid_string, name));
735         centry_free(centry);
736 }
737
738
739 static void wcache_save_user(struct winbindd_domain *domain, NTSTATUS status, WINBIND_USERINFO *info)
740 {
741         struct cache_entry *centry;
742         fstring sid_string;
743
744         centry = centry_start(domain, status);
745         if (!centry)
746                 return;
747         centry_put_string(centry, info->acct_name);
748         centry_put_string(centry, info->full_name);
749         centry_put_string(centry, info->homedir);
750         centry_put_string(centry, info->shell);
751         centry_put_sid(centry, &info->user_sid);
752         centry_put_sid(centry, &info->group_sid);
753         centry_end(centry, "U/%s", sid_to_string(sid_string, &info->user_sid));
754         DEBUG(10,("wcache_save_user: %s (acct_name %s)\n", sid_string, info->acct_name));
755         centry_free(centry);
756 }
757
758 static void wcache_save_lockout_policy(struct winbindd_domain *domain, NTSTATUS status, SAM_UNK_INFO_12 *lockout_policy)
759 {
760         struct cache_entry *centry;
761
762         centry = centry_start(domain, status);
763         if (!centry)
764                 return;
765
766         centry_put_nttime(centry, lockout_policy->duration);
767         centry_put_nttime(centry, lockout_policy->reset_count);
768         centry_put_uint16(centry, lockout_policy->bad_attempt_lockout);
769
770         centry_end(centry, "LOC_POL/%s", domain->name);
771         
772         DEBUG(10,("wcache_save_lockout_policy: %s\n", domain->name));
773
774         centry_free(centry);
775 }
776
777 static void wcache_save_password_policy(struct winbindd_domain *domain, NTSTATUS status, SAM_UNK_INFO_1 *password_policy)
778 {
779         struct cache_entry *centry;
780
781         centry = centry_start(domain, status);
782         if (!centry)
783                 return;
784
785         centry_put_uint16(centry, password_policy->min_length_password);
786         centry_put_uint16(centry, password_policy->password_history);
787         centry_put_uint32(centry, password_policy->password_properties);
788         centry_put_nttime(centry, password_policy->expire);
789         centry_put_nttime(centry, password_policy->min_passwordage);
790
791         centry_end(centry, "PWD_POL/%s", domain->name);
792         
793         DEBUG(10,("wcache_save_password_policy: %s\n", domain->name));
794
795         centry_free(centry);
796 }
797
798 NTSTATUS wcache_cached_creds_exist(struct winbindd_domain *domain, const DOM_SID *sid)
799 {
800         struct winbind_cache *cache = get_cache(domain);
801         TDB_DATA data;
802         fstring key_str;
803
804         if (!cache->tdb) {
805                 return NT_STATUS_INTERNAL_DB_ERROR;
806         }
807
808         fstr_sprintf(key_str, "CRED/%s", sid_string_static(sid));
809
810         data = tdb_fetch(cache->tdb, make_tdb_data(key_str, strlen(key_str)));
811         if (!data.dptr) {
812                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
813         }
814
815         return NT_STATUS_OK;
816 }
817
818 /* Lookup creds for a SID */
819 NTSTATUS wcache_get_creds(struct winbindd_domain *domain, 
820                           TALLOC_CTX *mem_ctx, 
821                           const DOM_SID *sid,
822                           const uint8 **cached_nt_pass)
823 {
824         struct winbind_cache *cache = get_cache(domain);
825         struct cache_entry *centry = NULL;
826         NTSTATUS status;
827         time_t t;
828
829         if (!cache->tdb) {
830                 return NT_STATUS_INTERNAL_DB_ERROR;
831         }
832
833         centry = wcache_fetch(cache, domain, "CRED/%s", sid_string_static(sid));
834         
835         if (!centry) {
836                 DEBUG(10,("wcache_get_creds: entry for [CRED/%s] not found\n", 
837                         sid_string_static(sid)));
838                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
839         }
840
841         t = centry_time(centry);
842         *cached_nt_pass = (const uint8 *)centry_string(centry, mem_ctx);
843
844 #if DEBUG_PASSWORD
845         dump_data(100, (const char *)cached_nt_pass, NT_HASH_LEN);
846 #endif
847         status = centry->status;
848
849         DEBUG(10,("wcache_get_creds: [Cached] - cached creds for user %s status %s\n",
850                 sid_string_static(sid), get_friendly_nt_error_msg(status) ));
851
852         centry_free(centry);
853         return status;
854 }
855
856 NTSTATUS wcache_save_creds(struct winbindd_domain *domain, 
857                            TALLOC_CTX *mem_ctx, 
858                            const DOM_SID *sid, 
859                            const uint8 nt_pass[NT_HASH_LEN])
860 {
861         struct cache_entry *centry;
862         fstring sid_string;
863         NTSTATUS status = NT_STATUS_OK; /* ??? */
864
865         centry = centry_start(domain, status);
866         if (!centry) {
867                 return NT_STATUS_INTERNAL_DB_ERROR;
868         }
869
870 #if DEBUG_PASSWORD
871         dump_data(100, (const char *)nt_pass, NT_HASH_LEN);
872 #endif
873
874         centry_put_time(centry, time(NULL));
875         centry_put_string(centry, (const char *)nt_pass);
876         centry_end(centry, "CRED/%s", sid_to_string(sid_string, sid));
877
878         DEBUG(10,("wcache_save_creds: %s\n", sid_string));
879
880         centry_free(centry);
881
882         return NT_STATUS_OK;
883 }
884
885
886 /* Query display info. This is the basic user list fn */
887 static NTSTATUS query_user_list(struct winbindd_domain *domain,
888                                 TALLOC_CTX *mem_ctx,
889                                 uint32 *num_entries, 
890                                 WINBIND_USERINFO **info)
891 {
892         struct winbind_cache *cache = get_cache(domain);
893         struct cache_entry *centry = NULL;
894         NTSTATUS status;
895         unsigned int i, retry;
896
897         if (!cache->tdb)
898                 goto do_query;
899
900         centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
901         if (!centry)
902                 goto do_query;
903
904         *num_entries = centry_uint32(centry);
905         
906         if (*num_entries == 0)
907                 goto do_cached;
908
909         (*info) = TALLOC_ARRAY(mem_ctx, WINBIND_USERINFO, *num_entries);
910         if (! (*info))
911                 smb_panic("query_user_list out of memory");
912         for (i=0; i<(*num_entries); i++) {
913                 (*info)[i].acct_name = centry_string(centry, mem_ctx);
914                 (*info)[i].full_name = centry_string(centry, mem_ctx);
915                 (*info)[i].homedir = centry_string(centry, mem_ctx);
916                 (*info)[i].shell = centry_string(centry, mem_ctx);
917                 centry_sid(centry, mem_ctx, &(*info)[i].user_sid);
918                 centry_sid(centry, mem_ctx, &(*info)[i].group_sid);
919         }
920
921 do_cached:      
922         status = centry->status;
923
924         DEBUG(10,("query_user_list: [Cached] - cached list for domain %s status %s\n",
925                 domain->name, get_friendly_nt_error_msg(status) ));
926
927         centry_free(centry);
928         return status;
929
930 do_query:
931         *num_entries = 0;
932         *info = NULL;
933
934         /* Return status value returned by seq number check */
935
936         if (!NT_STATUS_IS_OK(domain->last_status))
937                 return domain->last_status;
938
939         /* Put the query_user_list() in a retry loop.  There appears to be
940          * some bug either with Windows 2000 or Samba's handling of large
941          * rpc replies.  This manifests itself as sudden disconnection
942          * at a random point in the enumeration of a large (60k) user list.
943          * The retry loop simply tries the operation again. )-:  It's not
944          * pretty but an acceptable workaround until we work out what the
945          * real problem is. */
946
947         retry = 0;
948         do {
949
950                 DEBUG(10,("query_user_list: [Cached] - doing backend query for list for domain %s\n",
951                         domain->name ));
952
953                 status = domain->backend->query_user_list(domain, mem_ctx, num_entries, info);
954                 if (!NT_STATUS_IS_OK(status))
955                         DEBUG(3, ("query_user_list: returned 0x%08x, "
956                                   "retrying\n", NT_STATUS_V(status)));
957                         if (NT_STATUS_EQUAL(status, NT_STATUS_UNSUCCESSFUL)) {
958                                 DEBUG(3, ("query_user_list: flushing "
959                                           "connection cache\n"));
960                                 invalidate_cm_connection(&domain->conn);
961                         }
962
963         } while (NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_UNSUCCESSFUL) && 
964                  (retry++ < 5));
965
966         /* and save it */
967         refresh_sequence_number(domain, False);
968         centry = centry_start(domain, status);
969         if (!centry)
970                 goto skip_save;
971         centry_put_uint32(centry, *num_entries);
972         for (i=0; i<(*num_entries); i++) {
973                 centry_put_string(centry, (*info)[i].acct_name);
974                 centry_put_string(centry, (*info)[i].full_name);
975                 centry_put_string(centry, (*info)[i].homedir);
976                 centry_put_string(centry, (*info)[i].shell);
977                 centry_put_sid(centry, &(*info)[i].user_sid);
978                 centry_put_sid(centry, &(*info)[i].group_sid);
979                 if (domain->backend->consistent) {
980                         /* when the backend is consistent we can pre-prime some mappings */
981                         wcache_save_name_to_sid(domain, NT_STATUS_OK, 
982                                                 domain->name,
983                                                 (*info)[i].acct_name, 
984                                                 &(*info)[i].user_sid,
985                                                 SID_NAME_USER);
986                         wcache_save_sid_to_name(domain, NT_STATUS_OK, 
987                                                 &(*info)[i].user_sid,
988                                                 domain->name,
989                                                 (*info)[i].acct_name, 
990                                                 SID_NAME_USER);
991                         wcache_save_user(domain, NT_STATUS_OK, &(*info)[i]);
992                 }
993         }       
994         centry_end(centry, "UL/%s", domain->name);
995         centry_free(centry);
996
997 skip_save:
998         return status;
999 }
1000
1001 /* list all domain groups */
1002 static NTSTATUS enum_dom_groups(struct winbindd_domain *domain,
1003                                 TALLOC_CTX *mem_ctx,
1004                                 uint32 *num_entries, 
1005                                 struct acct_info **info)
1006 {
1007         struct winbind_cache *cache = get_cache(domain);
1008         struct cache_entry *centry = NULL;
1009         NTSTATUS status;
1010         unsigned int i;
1011
1012         if (!cache->tdb)
1013                 goto do_query;
1014
1015         centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
1016         if (!centry)
1017                 goto do_query;
1018
1019         *num_entries = centry_uint32(centry);
1020         
1021         if (*num_entries == 0)
1022                 goto do_cached;
1023
1024         (*info) = TALLOC_ARRAY(mem_ctx, struct acct_info, *num_entries);
1025         if (! (*info))
1026                 smb_panic("enum_dom_groups out of memory");
1027         for (i=0; i<(*num_entries); i++) {
1028                 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1029                 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1030                 (*info)[i].rid = centry_uint32(centry);
1031         }
1032
1033 do_cached:      
1034         status = centry->status;
1035
1036         DEBUG(10,("enum_dom_groups: [Cached] - cached list for domain %s status %s\n",
1037                 domain->name, get_friendly_nt_error_msg(status) ));
1038
1039         centry_free(centry);
1040         return status;
1041
1042 do_query:
1043         *num_entries = 0;
1044         *info = NULL;
1045
1046         /* Return status value returned by seq number check */
1047
1048         if (!NT_STATUS_IS_OK(domain->last_status))
1049                 return domain->last_status;
1050
1051         DEBUG(10,("enum_dom_groups: [Cached] - doing backend query for list for domain %s\n",
1052                 domain->name ));
1053
1054         status = domain->backend->enum_dom_groups(domain, mem_ctx, num_entries, info);
1055
1056         /* and save it */
1057         refresh_sequence_number(domain, False);
1058         centry = centry_start(domain, status);
1059         if (!centry)
1060                 goto skip_save;
1061         centry_put_uint32(centry, *num_entries);
1062         for (i=0; i<(*num_entries); i++) {
1063                 centry_put_string(centry, (*info)[i].acct_name);
1064                 centry_put_string(centry, (*info)[i].acct_desc);
1065                 centry_put_uint32(centry, (*info)[i].rid);
1066         }       
1067         centry_end(centry, "GL/%s/domain", domain->name);
1068         centry_free(centry);
1069
1070 skip_save:
1071         return status;
1072 }
1073
1074 /* list all domain groups */
1075 static NTSTATUS enum_local_groups(struct winbindd_domain *domain,
1076                                 TALLOC_CTX *mem_ctx,
1077                                 uint32 *num_entries, 
1078                                 struct acct_info **info)
1079 {
1080         struct winbind_cache *cache = get_cache(domain);
1081         struct cache_entry *centry = NULL;
1082         NTSTATUS status;
1083         unsigned int i;
1084
1085         if (!cache->tdb)
1086                 goto do_query;
1087
1088         centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
1089         if (!centry)
1090                 goto do_query;
1091
1092         *num_entries = centry_uint32(centry);
1093         
1094         if (*num_entries == 0)
1095                 goto do_cached;
1096
1097         (*info) = TALLOC_ARRAY(mem_ctx, struct acct_info, *num_entries);
1098         if (! (*info))
1099                 smb_panic("enum_dom_groups out of memory");
1100         for (i=0; i<(*num_entries); i++) {
1101                 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1102                 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1103                 (*info)[i].rid = centry_uint32(centry);
1104         }
1105
1106 do_cached:      
1107
1108         /* If we are returning cached data and the domain controller
1109            is down then we don't know whether the data is up to date
1110            or not.  Return NT_STATUS_MORE_PROCESSING_REQUIRED to
1111            indicate this. */
1112
1113         if (wcache_server_down(domain)) {
1114                 DEBUG(10, ("enum_local_groups: returning cached user list and server was down\n"));
1115                 status = NT_STATUS_MORE_PROCESSING_REQUIRED;
1116         } else
1117                 status = centry->status;
1118
1119         DEBUG(10,("enum_local_groups: [Cached] - cached list for domain %s status %s\n",
1120                 domain->name, get_friendly_nt_error_msg(status) ));
1121
1122         centry_free(centry);
1123         return status;
1124
1125 do_query:
1126         *num_entries = 0;
1127         *info = NULL;
1128
1129         /* Return status value returned by seq number check */
1130
1131         if (!NT_STATUS_IS_OK(domain->last_status))
1132                 return domain->last_status;
1133
1134         DEBUG(10,("enum_local_groups: [Cached] - doing backend query for list for domain %s\n",
1135                 domain->name ));
1136
1137         status = domain->backend->enum_local_groups(domain, mem_ctx, num_entries, info);
1138
1139         /* and save it */
1140         refresh_sequence_number(domain, False);
1141         centry = centry_start(domain, status);
1142         if (!centry)
1143                 goto skip_save;
1144         centry_put_uint32(centry, *num_entries);
1145         for (i=0; i<(*num_entries); i++) {
1146                 centry_put_string(centry, (*info)[i].acct_name);
1147                 centry_put_string(centry, (*info)[i].acct_desc);
1148                 centry_put_uint32(centry, (*info)[i].rid);
1149         }
1150         centry_end(centry, "GL/%s/local", domain->name);
1151         centry_free(centry);
1152
1153 skip_save:
1154         return status;
1155 }
1156
1157 /* convert a single name to a sid in a domain */
1158 static NTSTATUS name_to_sid(struct winbindd_domain *domain,
1159                             TALLOC_CTX *mem_ctx,
1160                             const char *domain_name,
1161                             const char *name,
1162                             DOM_SID *sid,
1163                             enum SID_NAME_USE *type)
1164 {
1165         struct winbind_cache *cache = get_cache(domain);
1166         struct cache_entry *centry = NULL;
1167         NTSTATUS status;
1168         fstring uname;
1169
1170         if (!cache->tdb)
1171                 goto do_query;
1172
1173         fstrcpy(uname, name);
1174         strupper_m(uname);
1175         centry = wcache_fetch(cache, domain, "NS/%s/%s", domain_name, uname);
1176         if (!centry)
1177                 goto do_query;
1178         *type = (enum SID_NAME_USE)centry_uint32(centry);
1179         status = centry->status;
1180         if (NT_STATUS_IS_OK(status)) {
1181                 centry_sid(centry, mem_ctx, sid);
1182         }
1183
1184         DEBUG(10,("name_to_sid: [Cached] - cached name for domain %s status %s\n",
1185                 domain->name, get_friendly_nt_error_msg(status) ));
1186
1187         centry_free(centry);
1188         return status;
1189
1190 do_query:
1191         ZERO_STRUCTP(sid);
1192
1193         /* If the seq number check indicated that there is a problem
1194          * with this DC, then return that status... except for
1195          * access_denied.  This is special because the dc may be in
1196          * "restrict anonymous = 1" mode, in which case it will deny
1197          * most unauthenticated operations, but *will* allow the LSA
1198          * name-to-sid that we try as a fallback. */
1199
1200         if (!(NT_STATUS_IS_OK(domain->last_status)
1201               || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1202                 return domain->last_status;
1203
1204         DEBUG(10,("name_to_sid: [Cached] - doing backend query for name for domain %s\n",
1205                 domain->name ));
1206
1207         status = domain->backend->name_to_sid(domain, mem_ctx, domain_name, name, sid, type);
1208
1209         /* and save it */
1210         if (domain->online || !is_null_sid(sid)) {
1211                 wcache_save_name_to_sid(domain, status, domain_name, name, sid, *type);
1212         }
1213
1214         if (NT_STATUS_IS_OK(status)) {
1215                 strupper_m(CONST_DISCARD(char *,domain_name));
1216                 strlower_m(CONST_DISCARD(char *,name));
1217                 wcache_save_sid_to_name(domain, status, sid, domain_name, name, *type);
1218         }
1219
1220         return status;
1221 }
1222
1223 /* convert a sid to a user or group name. The sid is guaranteed to be in the domain
1224    given */
1225 static NTSTATUS sid_to_name(struct winbindd_domain *domain,
1226                             TALLOC_CTX *mem_ctx,
1227                             const DOM_SID *sid,
1228                             char **domain_name,
1229                             char **name,
1230                             enum SID_NAME_USE *type)
1231 {
1232         struct winbind_cache *cache = get_cache(domain);
1233         struct cache_entry *centry = NULL;
1234         NTSTATUS status;
1235         fstring sid_string;
1236
1237         if (!cache->tdb)
1238                 goto do_query;
1239
1240         centry = wcache_fetch(cache, domain, "SN/%s", sid_to_string(sid_string, sid));
1241         if (!centry)
1242                 goto do_query;
1243         if (NT_STATUS_IS_OK(centry->status)) {
1244                 *type = (enum SID_NAME_USE)centry_uint32(centry);
1245                 *domain_name = centry_string(centry, mem_ctx);
1246                 *name = centry_string(centry, mem_ctx);
1247         }
1248         status = centry->status;
1249
1250         DEBUG(10,("sid_to_name: [Cached] - cached name for domain %s status %s\n",
1251                 domain->name, get_friendly_nt_error_msg(status) ));
1252
1253         centry_free(centry);
1254         return status;
1255
1256 do_query:
1257         *name = NULL;
1258         *domain_name = NULL;
1259
1260         /* If the seq number check indicated that there is a problem
1261          * with this DC, then return that status... except for
1262          * access_denied.  This is special because the dc may be in
1263          * "restrict anonymous = 1" mode, in which case it will deny
1264          * most unauthenticated operations, but *will* allow the LSA
1265          * sid-to-name that we try as a fallback. */
1266
1267         if (!(NT_STATUS_IS_OK(domain->last_status)
1268               || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1269                 return domain->last_status;
1270
1271         DEBUG(10,("sid_to_name: [Cached] - doing backend query for name for domain %s\n",
1272                 domain->name ));
1273
1274         status = domain->backend->sid_to_name(domain, mem_ctx, sid, domain_name, name, type);
1275
1276         /* and save it */
1277         refresh_sequence_number(domain, False);
1278         wcache_save_sid_to_name(domain, status, sid, *domain_name, *name, *type);
1279
1280         /* We can't save the name to sid mapping here, as with sid history a
1281          * later name2sid would give the wrong sid. */
1282
1283         return status;
1284 }
1285
1286 /* Lookup user information from a rid */
1287 static NTSTATUS query_user(struct winbindd_domain *domain, 
1288                            TALLOC_CTX *mem_ctx, 
1289                            const DOM_SID *user_sid, 
1290                            WINBIND_USERINFO *info)
1291 {
1292         struct winbind_cache *cache = get_cache(domain);
1293         struct cache_entry *centry = NULL;
1294         NTSTATUS status;
1295
1296         if (!cache->tdb)
1297                 goto do_query;
1298
1299         centry = wcache_fetch(cache, domain, "U/%s", sid_string_static(user_sid));
1300         
1301         /* If we have an access denied cache entry and a cached info3 in the
1302            samlogon cache then do a query.  This will force the rpc back end
1303            to return the info3 data. */
1304
1305         if (NT_STATUS_V(domain->last_status) == NT_STATUS_V(NT_STATUS_ACCESS_DENIED) &&
1306             netsamlogon_cache_have(user_sid)) {
1307                 DEBUG(10, ("query_user: cached access denied and have cached info3\n"));
1308                 domain->last_status = NT_STATUS_OK;
1309                 centry_free(centry);
1310                 goto do_query;
1311         }
1312         
1313         if (!centry)
1314                 goto do_query;
1315
1316         info->acct_name = centry_string(centry, mem_ctx);
1317         info->full_name = centry_string(centry, mem_ctx);
1318         info->homedir = centry_string(centry, mem_ctx);
1319         info->shell = centry_string(centry, mem_ctx);
1320         centry_sid(centry, mem_ctx, &info->user_sid);
1321         centry_sid(centry, mem_ctx, &info->group_sid);
1322         status = centry->status;
1323
1324         DEBUG(10,("query_user: [Cached] - cached info for domain %s status %s\n",
1325                 domain->name, get_friendly_nt_error_msg(status) ));
1326
1327         centry_free(centry);
1328         return status;
1329
1330 do_query:
1331         ZERO_STRUCTP(info);
1332
1333         /* Return status value returned by seq number check */
1334
1335         if (!NT_STATUS_IS_OK(domain->last_status))
1336                 return domain->last_status;
1337         
1338         DEBUG(10,("sid_to_name: [Cached] - doing backend query for info for domain %s\n",
1339                 domain->name ));
1340
1341         status = domain->backend->query_user(domain, mem_ctx, user_sid, info);
1342
1343         /* and save it */
1344         refresh_sequence_number(domain, False);
1345         wcache_save_user(domain, status, info);
1346
1347         return status;
1348 }
1349
1350
1351 /* Lookup groups a user is a member of. */
1352 static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
1353                                   TALLOC_CTX *mem_ctx,
1354                                   const DOM_SID *user_sid, 
1355                                   uint32 *num_groups, DOM_SID **user_gids)
1356 {
1357         struct winbind_cache *cache = get_cache(domain);
1358         struct cache_entry *centry = NULL;
1359         NTSTATUS status;
1360         unsigned int i;
1361         fstring sid_string;
1362
1363         if (!cache->tdb)
1364                 goto do_query;
1365
1366         centry = wcache_fetch(cache, domain, "UG/%s", sid_to_string(sid_string, user_sid));
1367         
1368         /* If we have an access denied cache entry and a cached info3 in the
1369            samlogon cache then do a query.  This will force the rpc back end
1370            to return the info3 data. */
1371
1372         if (NT_STATUS_V(domain->last_status) == NT_STATUS_V(NT_STATUS_ACCESS_DENIED) &&
1373             netsamlogon_cache_have(user_sid)) {
1374                 DEBUG(10, ("lookup_usergroups: cached access denied and have cached info3\n"));
1375                 domain->last_status = NT_STATUS_OK;
1376                 centry_free(centry);
1377                 goto do_query;
1378         }
1379         
1380         if (!centry)
1381                 goto do_query;
1382
1383         *num_groups = centry_uint32(centry);
1384         
1385         if (*num_groups == 0)
1386                 goto do_cached;
1387
1388         (*user_gids) = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_groups);
1389         if (! (*user_gids))
1390                 smb_panic("lookup_usergroups out of memory");
1391         for (i=0; i<(*num_groups); i++) {
1392                 centry_sid(centry, mem_ctx, &(*user_gids)[i]);
1393         }
1394
1395 do_cached:      
1396         status = centry->status;
1397
1398         DEBUG(10,("lookup_usergroups: [Cached] - cached info for domain %s status %s\n",
1399                 domain->name, get_friendly_nt_error_msg(status) ));
1400
1401         centry_free(centry);
1402         return status;
1403
1404 do_query:
1405         (*num_groups) = 0;
1406         (*user_gids) = NULL;
1407
1408         /* Return status value returned by seq number check */
1409
1410         if (!NT_STATUS_IS_OK(domain->last_status))
1411                 return domain->last_status;
1412
1413         DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info for domain %s\n",
1414                 domain->name ));
1415
1416         status = domain->backend->lookup_usergroups(domain, mem_ctx, user_sid, num_groups, user_gids);
1417
1418         /* and save it */
1419         refresh_sequence_number(domain, False);
1420         centry = centry_start(domain, status);
1421         if (!centry)
1422                 goto skip_save;
1423         centry_put_uint32(centry, *num_groups);
1424         for (i=0; i<(*num_groups); i++) {
1425                 centry_put_sid(centry, &(*user_gids)[i]);
1426         }       
1427         centry_end(centry, "UG/%s", sid_to_string(sid_string, user_sid));
1428         centry_free(centry);
1429
1430 skip_save:
1431         return status;
1432 }
1433
1434 static NTSTATUS lookup_useraliases(struct winbindd_domain *domain,
1435                                    TALLOC_CTX *mem_ctx,
1436                                    uint32 num_sids, const DOM_SID *sids,
1437                                    uint32 *num_aliases, uint32 **alias_rids)
1438 {
1439         struct winbind_cache *cache = get_cache(domain);
1440         struct cache_entry *centry = NULL;
1441         NTSTATUS status;
1442         char *sidlist = talloc_strdup(mem_ctx, "");
1443         int i;
1444
1445         if (!cache->tdb)
1446                 goto do_query;
1447
1448         if (num_sids == 0) {
1449                 *num_aliases = 0;
1450                 *alias_rids = NULL;
1451                 return NT_STATUS_OK;
1452         }
1453
1454         /* We need to cache indexed by the whole list of SIDs, the aliases
1455          * resulting might come from any of the SIDs. */
1456
1457         for (i=0; i<num_sids; i++) {
1458                 sidlist = talloc_asprintf(mem_ctx, "%s/%s", sidlist,
1459                                           sid_string_static(&sids[i]));
1460                 if (sidlist == NULL)
1461                         return NT_STATUS_NO_MEMORY;
1462         }
1463
1464         centry = wcache_fetch(cache, domain, "UA%s", sidlist);
1465
1466         if (!centry)
1467                 goto do_query;
1468
1469         *num_aliases = centry_uint32(centry);
1470         *alias_rids = NULL;
1471
1472         (*alias_rids) = TALLOC_ARRAY(mem_ctx, uint32, *num_aliases);
1473
1474         if ((*num_aliases != 0) && ((*alias_rids) == NULL)) {
1475                 centry_free(centry);
1476                 return NT_STATUS_NO_MEMORY;
1477         }
1478
1479         for (i=0; i<(*num_aliases); i++)
1480                 (*alias_rids)[i] = centry_uint32(centry);
1481
1482         status = centry->status;
1483
1484         DEBUG(10,("lookup_useraliases: [Cached] - cached info for domain %s "
1485                   "status %s\n", domain->name,
1486                   get_friendly_nt_error_msg(status)));
1487
1488         centry_free(centry);
1489         return status;
1490
1491  do_query:
1492         (*num_aliases) = 0;
1493         (*alias_rids) = NULL;
1494
1495         if (!NT_STATUS_IS_OK(domain->last_status))
1496                 return domain->last_status;
1497
1498         DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info "
1499                   "for domain %s\n", domain->name ));
1500
1501         status = domain->backend->lookup_useraliases(domain, mem_ctx,
1502                                                      num_sids, sids,
1503                                                      num_aliases, alias_rids);
1504
1505         /* and save it */
1506         refresh_sequence_number(domain, False);
1507         centry = centry_start(domain, status);
1508         if (!centry)
1509                 goto skip_save;
1510         centry_put_uint32(centry, *num_aliases);
1511         for (i=0; i<(*num_aliases); i++)
1512                 centry_put_uint32(centry, (*alias_rids)[i]);
1513         centry_end(centry, "UA%s", sidlist);
1514         centry_free(centry);
1515
1516  skip_save:
1517         return status;
1518 }
1519
1520
1521 static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
1522                                 TALLOC_CTX *mem_ctx,
1523                                 const DOM_SID *group_sid, uint32 *num_names, 
1524                                 DOM_SID **sid_mem, char ***names, 
1525                                 uint32 **name_types)
1526 {
1527         struct winbind_cache *cache = get_cache(domain);
1528         struct cache_entry *centry = NULL;
1529         NTSTATUS status;
1530         unsigned int i;
1531         fstring sid_string;
1532
1533         if (!cache->tdb)
1534                 goto do_query;
1535
1536         centry = wcache_fetch(cache, domain, "GM/%s", sid_to_string(sid_string, group_sid));
1537         if (!centry)
1538                 goto do_query;
1539
1540         *num_names = centry_uint32(centry);
1541         
1542         if (*num_names == 0)
1543                 goto do_cached;
1544
1545         (*sid_mem) = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_names);
1546         (*names) = TALLOC_ARRAY(mem_ctx, char *, *num_names);
1547         (*name_types) = TALLOC_ARRAY(mem_ctx, uint32, *num_names);
1548
1549         if (! (*sid_mem) || ! (*names) || ! (*name_types)) {
1550                 smb_panic("lookup_groupmem out of memory");
1551         }
1552
1553         for (i=0; i<(*num_names); i++) {
1554                 centry_sid(centry, mem_ctx, &(*sid_mem)[i]);
1555                 (*names)[i] = centry_string(centry, mem_ctx);
1556                 (*name_types)[i] = centry_uint32(centry);
1557         }
1558
1559 do_cached:      
1560         status = centry->status;
1561
1562         DEBUG(10,("lookup_groupmem: [Cached] - cached info for domain %s status %s\n",
1563                 domain->name, get_friendly_nt_error_msg(status) ));
1564
1565         centry_free(centry);
1566         return status;
1567
1568 do_query:
1569         (*num_names) = 0;
1570         (*sid_mem) = NULL;
1571         (*names) = NULL;
1572         (*name_types) = NULL;
1573         
1574         /* Return status value returned by seq number check */
1575
1576         if (!NT_STATUS_IS_OK(domain->last_status))
1577                 return domain->last_status;
1578
1579         DEBUG(10,("lookup_groupmem: [Cached] - doing backend query for info for domain %s\n",
1580                 domain->name ));
1581
1582         status = domain->backend->lookup_groupmem(domain, mem_ctx, group_sid, num_names, 
1583                                                   sid_mem, names, name_types);
1584
1585         /* and save it */
1586         refresh_sequence_number(domain, False);
1587         centry = centry_start(domain, status);
1588         if (!centry)
1589                 goto skip_save;
1590         centry_put_uint32(centry, *num_names);
1591         for (i=0; i<(*num_names); i++) {
1592                 centry_put_sid(centry, &(*sid_mem)[i]);
1593                 centry_put_string(centry, (*names)[i]);
1594                 centry_put_uint32(centry, (*name_types)[i]);
1595         }       
1596         centry_end(centry, "GM/%s", sid_to_string(sid_string, group_sid));
1597         centry_free(centry);
1598
1599 skip_save:
1600         return status;
1601 }
1602
1603 /* find the sequence number for a domain */
1604 static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
1605 {
1606         refresh_sequence_number(domain, False);
1607
1608         *seq = domain->sequence_number;
1609
1610         return NT_STATUS_OK;
1611 }
1612
1613 /* enumerate trusted domains 
1614  * (we need to have the list of trustdoms in the cache when we go offline) -
1615  * Guenther */
1616 static NTSTATUS trusted_domains(struct winbindd_domain *domain,
1617                                 TALLOC_CTX *mem_ctx,
1618                                 uint32 *num_domains,
1619                                 char ***names,
1620                                 char ***alt_names,
1621                                 DOM_SID **dom_sids)
1622 {
1623         struct winbind_cache *cache = get_cache(domain);
1624         struct cache_entry *centry = NULL;
1625         NTSTATUS status;
1626         int i;
1627  
1628         if (!cache->tdb)
1629                 goto do_query;
1630  
1631         centry = wcache_fetch(cache, domain, "TRUSTDOMS/%s", domain->name);
1632         
1633         if (!centry) {
1634                 goto do_query;
1635         }
1636  
1637         *num_domains = centry_uint32(centry);
1638         
1639         (*names)        = TALLOC_ARRAY(mem_ctx, char *, *num_domains);
1640         (*alt_names)    = TALLOC_ARRAY(mem_ctx, char *, *num_domains);
1641         (*dom_sids)     = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_domains);
1642  
1643         if (! (*dom_sids) || ! (*names) || ! (*alt_names)) {
1644                 smb_panic("trusted_domains out of memory");
1645         }
1646  
1647         for (i=0; i<(*num_domains); i++) {
1648                 (*names)[i] = centry_string(centry, mem_ctx);
1649                 (*alt_names)[i] = centry_string(centry, mem_ctx);
1650                 centry_sid(centry, mem_ctx, &(*dom_sids)[i]);
1651         }
1652
1653         status = centry->status;
1654  
1655         DEBUG(10,("trusted_domains: [Cached] - cached info for domain %s (%d trusts) status %s\n",
1656                 domain->name, *num_domains, get_friendly_nt_error_msg(status) ));
1657  
1658         centry_free(centry);
1659         return status;
1660  
1661 do_query:
1662         (*num_domains) = 0;
1663         (*dom_sids) = NULL;
1664         (*names) = NULL;
1665         (*alt_names) = NULL;
1666  
1667         /* Return status value returned by seq number check */
1668
1669         if (!NT_STATUS_IS_OK(domain->last_status))
1670                 return domain->last_status;
1671         
1672         DEBUG(10,("trusted_domains: [Cached] - doing backend query for info for domain %s\n",
1673                 domain->name ));
1674  
1675         status = domain->backend->trusted_domains(domain, mem_ctx, num_domains,
1676                                                 names, alt_names, dom_sids);
1677
1678         /* no trusts gives NT_STATUS_NO_MORE_ENTRIES resetting to NT_STATUS_OK
1679          * so that the generic centry handling still applies correctly -
1680          * Guenther*/
1681
1682         if (!NT_STATUS_IS_ERR(status)) {
1683                 status = NT_STATUS_OK;
1684         }
1685
1686         /* and save it */
1687         refresh_sequence_number(domain, False);
1688  
1689         centry = centry_start(domain, status);
1690         if (!centry)
1691                 goto skip_save;
1692
1693         centry_put_uint32(centry, *num_domains);
1694
1695         for (i=0; i<(*num_domains); i++) {
1696                 centry_put_string(centry, (*names)[i]);
1697                 centry_put_string(centry, (*alt_names)[i]);
1698                 centry_put_sid(centry, &(*dom_sids)[i]);
1699         }
1700         
1701         centry_end(centry, "TRUSTDOMS/%s", domain->name);
1702  
1703         centry_free(centry);
1704  
1705 skip_save:
1706         return status;
1707 }       
1708
1709 /* get lockout policy */
1710 static NTSTATUS lockout_policy(struct winbindd_domain *domain,
1711                                TALLOC_CTX *mem_ctx,
1712                                SAM_UNK_INFO_12 *lockout_policy){
1713         struct winbind_cache *cache = get_cache(domain);
1714         struct cache_entry *centry = NULL;
1715         NTSTATUS status;
1716  
1717         if (!cache->tdb)
1718                 goto do_query;
1719  
1720         centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
1721         
1722         if (!centry)
1723                 goto do_query;
1724  
1725         lockout_policy->duration = centry_nttime(centry);
1726         lockout_policy->reset_count = centry_nttime(centry);
1727         lockout_policy->bad_attempt_lockout = centry_uint16(centry);
1728  
1729         status = centry->status;
1730  
1731         DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status %s\n",
1732                 domain->name, get_friendly_nt_error_msg(status) ));
1733  
1734         centry_free(centry);
1735         return status;
1736  
1737 do_query:
1738         ZERO_STRUCTP(lockout_policy);
1739  
1740         /* Return status value returned by seq number check */
1741
1742         if (!NT_STATUS_IS_OK(domain->last_status))
1743                 return domain->last_status;
1744         
1745         DEBUG(10,("lockout_policy: [Cached] - doing backend query for info for domain %s\n",
1746                 domain->name ));
1747  
1748         status = domain->backend->lockout_policy(domain, mem_ctx, lockout_policy); 
1749  
1750         /* and save it */
1751         refresh_sequence_number(domain, False);
1752         wcache_save_lockout_policy(domain, status, lockout_policy);
1753  
1754         return status;
1755 }
1756  
1757 /* get password policy */
1758 static NTSTATUS password_policy(struct winbindd_domain *domain,
1759                                 TALLOC_CTX *mem_ctx,
1760                                 SAM_UNK_INFO_1 *password_policy)
1761 {
1762         struct winbind_cache *cache = get_cache(domain);
1763         struct cache_entry *centry = NULL;
1764         NTSTATUS status;
1765
1766         if (!cache->tdb)
1767                 goto do_query;
1768  
1769         centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
1770         
1771         if (!centry)
1772                 goto do_query;
1773
1774         password_policy->min_length_password = centry_uint16(centry);
1775         password_policy->password_history = centry_uint16(centry);
1776         password_policy->password_properties = centry_uint32(centry);
1777         password_policy->expire = centry_nttime(centry);
1778         password_policy->min_passwordage = centry_nttime(centry);
1779
1780         status = centry->status;
1781
1782         DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status %s\n",
1783                 domain->name, get_friendly_nt_error_msg(status) ));
1784
1785         centry_free(centry);
1786         return status;
1787
1788 do_query:
1789         ZERO_STRUCTP(password_policy);
1790
1791         /* Return status value returned by seq number check */
1792
1793         if (!NT_STATUS_IS_OK(domain->last_status))
1794                 return domain->last_status;
1795         
1796         DEBUG(10,("password_policy: [Cached] - doing backend query for info for domain %s\n",
1797                 domain->name ));
1798
1799         status = domain->backend->password_policy(domain, mem_ctx, password_policy); 
1800
1801         /* and save it */
1802         refresh_sequence_number(domain, False);
1803         wcache_save_password_policy(domain, status, password_policy);
1804
1805         return status;
1806 }
1807
1808
1809 /* Invalidate cached user and group lists coherently */
1810
1811 static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, 
1812                        void *state)
1813 {
1814         if (strncmp(kbuf.dptr, "UL/", 3) == 0 ||
1815             strncmp(kbuf.dptr, "GL/", 3) == 0)
1816                 tdb_delete(the_tdb, kbuf);
1817
1818         return 0;
1819 }
1820
1821 /* Invalidate the getpwnam and getgroups entries for a winbindd domain */
1822
1823 void wcache_invalidate_samlogon(struct winbindd_domain *domain, 
1824                                 NET_USER_INFO_3 *info3)
1825 {
1826         struct winbind_cache *cache;
1827         
1828         if (!domain)
1829                 return;
1830
1831         cache = get_cache(domain);
1832         netsamlogon_clear_cached_user(cache->tdb, info3);
1833 }
1834
1835 void wcache_invalidate_cache(void)
1836 {
1837         struct winbindd_domain *domain;
1838
1839         for (domain = domain_list(); domain; domain = domain->next) {
1840                 struct winbind_cache *cache = get_cache(domain);
1841
1842                 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
1843                            "entries for %s\n", domain->name));
1844                 if (cache)
1845                         tdb_traverse(cache->tdb, traverse_fn, NULL);
1846         }
1847 }
1848
1849 static BOOL init_wcache(void)
1850 {
1851         if (wcache == NULL) {
1852                 wcache = SMB_XMALLOC_P(struct winbind_cache);
1853                 ZERO_STRUCTP(wcache);
1854         }
1855
1856         if (wcache->tdb != NULL)
1857                 return True;
1858
1859         /* when working offline we must not clear the cache on restart */
1860         wcache->tdb = tdb_open_log(lock_path("winbindd_cache.tdb"),
1861                                 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE, 
1862                                 TDB_DEFAULT /*TDB_CLEAR_IF_FIRST*/, O_RDWR|O_CREAT, 0600);
1863
1864         if (wcache->tdb == NULL) {
1865                 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
1866                 return False;
1867         }
1868
1869         return True;
1870 }
1871
1872 void cache_store_response(pid_t pid, struct winbindd_response *response)
1873 {
1874         fstring key_str;
1875
1876         if (!init_wcache())
1877                 return;
1878
1879         DEBUG(10, ("Storing response for pid %d, len %d\n",
1880                    pid, response->length));
1881
1882         fstr_sprintf(key_str, "DR/%d", pid);
1883         if (tdb_store(wcache->tdb, string_tdb_data(key_str), 
1884                       make_tdb_data((void *)response, sizeof(*response)),
1885                       TDB_REPLACE) == -1)
1886                 return;
1887
1888         if (response->length == sizeof(*response))
1889                 return;
1890
1891         /* There's extra data */
1892
1893         DEBUG(10, ("Storing extra data: len=%d\n",
1894                    (int)(response->length - sizeof(*response))));
1895
1896         fstr_sprintf(key_str, "DE/%d", pid);
1897         if (tdb_store(wcache->tdb, string_tdb_data(key_str),
1898                       make_tdb_data(response->extra_data,
1899                                     response->length - sizeof(*response)),
1900                       TDB_REPLACE) == 0)
1901                 return;
1902
1903         /* We could not store the extra data, make sure the tdb does not
1904          * contain a main record with wrong dangling extra data */
1905
1906         fstr_sprintf(key_str, "DR/%d", pid);
1907         tdb_delete(wcache->tdb, string_tdb_data(key_str));
1908
1909         return;
1910 }
1911
1912 BOOL cache_retrieve_response(pid_t pid, struct winbindd_response * response)
1913 {
1914         TDB_DATA data;
1915         fstring key_str;
1916
1917         if (!init_wcache())
1918                 return False;
1919
1920         DEBUG(10, ("Retrieving response for pid %d\n", pid));
1921
1922         fstr_sprintf(key_str, "DR/%d", pid);
1923         data = tdb_fetch(wcache->tdb, string_tdb_data(key_str));
1924
1925         if (data.dptr == NULL)
1926                 return False;
1927
1928         if (data.dsize != sizeof(*response))
1929                 return False;
1930
1931         memcpy(response, data.dptr, data.dsize);
1932         SAFE_FREE(data.dptr);
1933
1934         if (response->length == sizeof(*response)) {
1935                 response->extra_data = NULL;
1936                 return True;
1937         }
1938
1939         /* There's extra data */
1940
1941         DEBUG(10, ("Retrieving extra data length=%d\n",
1942                    (int)(response->length - sizeof(*response))));
1943
1944         fstr_sprintf(key_str, "DE/%d", pid);
1945         data = tdb_fetch(wcache->tdb, string_tdb_data(key_str));
1946
1947         if (data.dptr == NULL) {
1948                 DEBUG(0, ("Did not find extra data\n"));
1949                 return False;
1950         }
1951
1952         if (data.dsize != (response->length - sizeof(*response))) {
1953                 DEBUG(0, ("Invalid extra data length: %d\n", (int)data.dsize));
1954                 SAFE_FREE(data.dptr);
1955                 return False;
1956         }
1957
1958         dump_data(11, data.dptr, data.dsize);
1959
1960         response->extra_data = data.dptr;
1961         return True;
1962 }
1963
1964 void cache_cleanup_response(pid_t pid)
1965 {
1966         fstring key_str;
1967
1968         if (!init_wcache())
1969                 return;
1970
1971         fstr_sprintf(key_str, "DR/%d", pid);
1972         tdb_delete(wcache->tdb, string_tdb_data(key_str));
1973
1974         fstr_sprintf(key_str, "DE/%d", pid);
1975         tdb_delete(wcache->tdb, string_tdb_data(key_str));
1976
1977         return;
1978 }
1979
1980
1981 BOOL lookup_cached_sid(TALLOC_CTX *mem_ctx, const DOM_SID *sid,
1982                        const char **domain_name, const char **name,
1983                        enum SID_NAME_USE *type)
1984 {
1985         struct winbindd_domain *domain;
1986         struct winbind_cache *cache;
1987         struct cache_entry *centry = NULL;
1988         NTSTATUS status;
1989
1990         domain = find_lookup_domain_from_sid(sid);
1991         if (domain == NULL) {
1992                 return False;
1993         }
1994
1995         cache = get_cache(domain);
1996
1997         if (cache->tdb == NULL) {
1998                 return False;
1999         }
2000
2001         centry = wcache_fetch(cache, domain, "SN/%s", sid_string_static(sid));
2002         if (centry == NULL) {
2003                 return False;
2004         }
2005
2006         if (NT_STATUS_IS_OK(centry->status)) {
2007                 *type = (enum SID_NAME_USE)centry_uint32(centry);
2008                 *domain_name = centry_string(centry, mem_ctx);
2009                 *name = centry_string(centry, mem_ctx);
2010         }
2011
2012         status = centry->status;
2013         centry_free(centry);
2014         return NT_STATUS_IS_OK(status);
2015 }
2016
2017 BOOL lookup_cached_name(TALLOC_CTX *mem_ctx,
2018                         const char *domain_name,
2019                         const char *name,
2020                         DOM_SID *sid,
2021                         enum SID_NAME_USE *type)
2022 {
2023         struct winbindd_domain *domain;
2024         struct winbind_cache *cache;
2025         struct cache_entry *centry = NULL;
2026         NTSTATUS status;
2027         fstring uname;
2028
2029         domain = find_lookup_domain_from_name(domain_name);
2030         if (domain == NULL) {
2031                 return False;
2032         }
2033
2034         cache = get_cache(domain);
2035
2036         if (cache->tdb == NULL) {
2037                 return False;
2038         }
2039
2040         fstrcpy(uname, name);
2041         strupper_m(uname);
2042         
2043         centry = wcache_fetch(cache, domain, "NS/%s/%s", domain_name, uname);
2044         if (centry == NULL) {
2045                 return False;
2046         }
2047
2048         if (NT_STATUS_IS_OK(centry->status)) {
2049                 *type = (enum SID_NAME_USE)centry_uint32(centry);
2050                 centry_sid(centry, mem_ctx, sid);
2051         }
2052
2053         status = centry->status;
2054         centry_free(centry);
2055         
2056         return NT_STATUS_IS_OK(status);
2057 }
2058
2059 void cache_name2sid(struct winbindd_domain *domain, 
2060                     const char *domain_name, const char *name,
2061                     enum SID_NAME_USE type, const DOM_SID *sid)
2062 {
2063         wcache_save_name_to_sid(domain, NT_STATUS_OK, domain_name, name,
2064                                 sid, type);
2065 }
2066
2067 /* delete all centries that don't have NT_STATUS_OK set */
2068 static int traverse_fn_cleanup(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, 
2069                                TDB_DATA dbuf, void *state)
2070 {
2071         struct cache_entry *centry;
2072         char buf[1024];
2073
2074         if (!snprintf(buf, kbuf.dsize + 1, "%s", kbuf.dptr)) {
2075                 return 1;
2076         }
2077
2078         centry = wcache_fetch_raw(buf);
2079         if (!centry) {
2080                 return 0;
2081         }
2082
2083         if (!NT_STATUS_IS_OK(centry->status)) {
2084                 DEBUG(10,("deleting centry %s\n", buf));
2085                 tdb_delete(the_tdb, kbuf);
2086         }
2087
2088         centry_free(centry);
2089         return 0;
2090 }
2091
2092 /* flush the cache */
2093 void wcache_flush_cache(void)
2094 {
2095         extern BOOL opt_nocache;
2096
2097         if (!wcache)
2098                 return;
2099         if (wcache->tdb) {
2100                 tdb_close(wcache->tdb);
2101                 wcache->tdb = NULL;
2102         }
2103         if (opt_nocache)
2104                 return;
2105
2106         /* when working offline we must not clear the cache on restart */
2107         wcache->tdb = tdb_open_log(lock_path("winbindd_cache.tdb"),
2108                                 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE, 
2109                                 TDB_DEFAULT /* TDB_CLEAR_IF_FIRST */, O_RDWR|O_CREAT, 0600);
2110
2111         if (!wcache->tdb) {
2112                 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
2113         }
2114
2115         tdb_traverse(wcache->tdb, traverse_fn_cleanup, NULL);
2116
2117         DEBUG(10,("wcache_flush_cache success\n"));
2118 }
2119
2120 /* Count cached creds */
2121
2122 static int traverse_fn_cached_creds(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, 
2123                                     void *state)
2124 {
2125         int *cred_count = (int*)state;
2126  
2127         if (strncmp(kbuf.dptr, "CRED/", 5) == 0) {
2128                 (*cred_count)++;
2129         }
2130         return 0;
2131 }
2132
2133 NTSTATUS wcache_count_cached_creds(struct winbindd_domain *domain, int *count)
2134 {
2135         struct winbind_cache *cache = get_cache(domain);
2136
2137         *count = 0;
2138
2139         if (!cache->tdb) {
2140                 return NT_STATUS_INTERNAL_DB_ERROR;
2141         }
2142  
2143         tdb_traverse(cache->tdb, traverse_fn_cached_creds, (void *)count);
2144
2145         return NT_STATUS_OK;
2146 }
2147
2148 struct cred_list {
2149         struct cred_list *prev, *next;
2150         TDB_DATA key;
2151         fstring name;
2152         time_t created;
2153 };
2154 static struct cred_list *wcache_cred_list;
2155
2156 static int traverse_fn_get_credlist(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, 
2157                                     void *state)
2158 {
2159         struct cred_list *cred;
2160
2161         if (strncmp(kbuf.dptr, "CRED/", 5) == 0) {
2162
2163                 cred = SMB_MALLOC_P(struct cred_list);
2164                 if (cred == NULL) {
2165                         DEBUG(0,("traverse_fn_remove_first_creds: failed to malloc new entry for list\n"));
2166                         return -1;
2167                 }
2168
2169                 ZERO_STRUCTP(cred);
2170                 
2171                 /* save a copy of the key */
2172                 
2173                 fstrcpy(cred->name, kbuf.dptr);         
2174                 DLIST_ADD(wcache_cred_list, cred);
2175         }
2176         
2177         return 0;
2178 }
2179
2180 NTSTATUS wcache_remove_oldest_cached_creds(struct winbindd_domain *domain, const DOM_SID *sid) 
2181 {
2182         struct winbind_cache *cache = get_cache(domain);
2183         NTSTATUS status;
2184         int ret;
2185         struct cred_list *cred, *oldest = NULL;
2186
2187         if (!cache->tdb) {
2188                 return NT_STATUS_INTERNAL_DB_ERROR;
2189         }
2190
2191         /* we possibly already have an entry */
2192         if (sid && NT_STATUS_IS_OK(wcache_cached_creds_exist(domain, sid))) {
2193         
2194                 fstring key_str;
2195
2196                 DEBUG(11,("we already have an entry, deleting that\n"));
2197
2198                 fstr_sprintf(key_str, "CRED/%s", sid_string_static(sid));
2199
2200                 tdb_delete(cache->tdb, string_tdb_data(key_str));
2201
2202                 return NT_STATUS_OK;
2203         }
2204
2205         ret = tdb_traverse(cache->tdb, traverse_fn_get_credlist, NULL);
2206         if (ret == 0) {
2207                 return NT_STATUS_OK;
2208         } else if (ret == -1) {
2209                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
2210         }
2211
2212         ZERO_STRUCTP(oldest);
2213
2214         for (cred = wcache_cred_list; cred; cred = cred->next) {
2215
2216                 TDB_DATA data;
2217                 time_t t;
2218
2219                 data = tdb_fetch(cache->tdb, make_tdb_data(cred->name, strlen(cred->name)));
2220                 if (!data.dptr) {
2221                         DEBUG(10,("wcache_remove_oldest_cached_creds: entry for [%s] not found\n", 
2222                                 cred->name));
2223                         status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
2224                         goto done;
2225                 }
2226         
2227                 t = IVAL(data.dptr, 0);
2228                 SAFE_FREE(data.dptr);
2229
2230                 if (!oldest) {
2231                         oldest = SMB_MALLOC_P(struct cred_list);
2232                         if (oldest == NULL) {
2233                                 status = NT_STATUS_NO_MEMORY;
2234                                 goto done;
2235                         }
2236
2237                         fstrcpy(oldest->name, cred->name);
2238                         oldest->created = t;
2239                         continue;
2240                 }
2241
2242                 if (t < oldest->created) {
2243                         fstrcpy(oldest->name, cred->name);
2244                         oldest->created = t;
2245                 }
2246         }
2247
2248         if (tdb_delete(cache->tdb, string_tdb_data(oldest->name)) == 0) {
2249                 status = NT_STATUS_OK;
2250         } else {
2251                 status = NT_STATUS_UNSUCCESSFUL;
2252         }
2253 done:
2254         SAFE_FREE(wcache_cred_list);
2255         SAFE_FREE(oldest);
2256         
2257         return status;
2258 }
2259
2260 /* Change the global online/offline state. */
2261 BOOL set_global_winbindd_state_offline(void)
2262 {
2263         TDB_DATA data;
2264         int err;
2265
2266         DEBUG(10,("set_global_winbindd_state_offline: offline requested.\n"));
2267
2268         /* Only go offline if someone has created
2269            the key "WINBINDD_OFFLINE" in the cache tdb. */
2270
2271         if (wcache == NULL || wcache->tdb == NULL) {
2272                 DEBUG(10,("set_global_winbindd_state_offline: wcache not open yet.\n"));
2273                 return False;
2274         }
2275
2276         if (!lp_winbind_offline_logon()) {
2277                 DEBUG(10,("set_global_winbindd_state_offline: rejecting.\n"));
2278                 return False;
2279         }
2280
2281         if (global_winbindd_offline_state) {
2282                 /* Already offline. */
2283                 return True;
2284         }
2285
2286         wcache->tdb->ecode = 0;
2287
2288         data = tdb_fetch_bystring( wcache->tdb, "WINBINDD_OFFLINE" );
2289
2290         /* As this is a key with no data we don't need to free, we
2291            check for existence by looking at tdb_err. */
2292
2293         err = tdb_error(wcache->tdb);
2294
2295         if (err == TDB_ERR_NOEXIST) {
2296                 DEBUG(10,("set_global_winbindd_state_offline: offline state not set.\n"));
2297                 return False;
2298         } else {
2299                 DEBUG(10,("set_global_winbindd_state_offline: offline state set.\n"));
2300                 global_winbindd_offline_state = True;
2301                 return True;
2302         }
2303 }
2304
2305 void set_global_winbindd_state_online(void)
2306 {
2307         DEBUG(10,("set_global_winbindd_state_online: online requested.\n"));
2308
2309         if (!lp_winbind_offline_logon()) {
2310                 DEBUG(10,("set_global_winbindd_state_online: rejecting.\n"));
2311                 return;
2312         }
2313
2314         if (!global_winbindd_offline_state) {
2315                 /* Already online. */
2316                 return;
2317         }
2318         global_winbindd_offline_state = False;
2319
2320         if (!wcache->tdb) {
2321                 return;
2322         }
2323
2324         /* Ensure there is no key "WINBINDD_OFFLINE" in the cache tdb. */
2325         tdb_delete_bystring(wcache->tdb, "WINBINDD_OFFLINE");
2326 }
2327
2328 /* the cache backend methods are exposed via this structure */
2329 struct winbindd_methods cache_methods = {
2330         True,
2331         query_user_list,
2332         enum_dom_groups,
2333         enum_local_groups,
2334         name_to_sid,
2335         sid_to_name,
2336         query_user,
2337         lookup_usergroups,
2338         lookup_useraliases,
2339         lookup_groupmem,
2340         sequence_number,
2341         lockout_policy,
2342         password_policy,
2343         trusted_domains
2344 };