r15053: fix portabilities issues between 32-bit winbind clients and a 64-bit winbindd...
[tprouty/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         uint32 rid;
804
805         if (!cache->tdb) {
806                 return NT_STATUS_INTERNAL_DB_ERROR;
807         }
808
809         if (is_null_sid(sid)) {
810                 return NT_STATUS_INVALID_SID;
811         }
812
813         if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
814                 return NT_STATUS_INVALID_SID;
815         }
816
817         fstr_sprintf(key_str, "CRED/%s", sid_string_static(sid));
818
819         data = tdb_fetch(cache->tdb, make_tdb_data(key_str, strlen(key_str)));
820         if (!data.dptr) {
821                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
822         }
823
824         return NT_STATUS_OK;
825 }
826
827 /* Lookup creds for a SID */
828 NTSTATUS wcache_get_creds(struct winbindd_domain *domain, 
829                           TALLOC_CTX *mem_ctx, 
830                           const DOM_SID *sid,
831                           const uint8 **cached_nt_pass)
832 {
833         struct winbind_cache *cache = get_cache(domain);
834         struct cache_entry *centry = NULL;
835         NTSTATUS status;
836         time_t t;
837         uint32 rid;
838
839         if (!cache->tdb) {
840                 return NT_STATUS_INTERNAL_DB_ERROR;
841         }
842
843         if (is_null_sid(sid)) {
844                 return NT_STATUS_INVALID_SID;
845         }
846
847         if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
848                 return NT_STATUS_INVALID_SID;
849         }
850
851         centry = wcache_fetch(cache, domain, "CRED/%s", sid_string_static(sid));
852         
853         if (!centry) {
854                 DEBUG(10,("wcache_get_creds: entry for [CRED/%s] not found\n", 
855                         sid_string_static(sid)));
856                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
857         }
858
859         t = centry_time(centry);
860         *cached_nt_pass = (const uint8 *)centry_string(centry, mem_ctx);
861
862 #if DEBUG_PASSWORD
863         dump_data(100, (const char *)cached_nt_pass, NT_HASH_LEN);
864 #endif
865         status = centry->status;
866
867         DEBUG(10,("wcache_get_creds: [Cached] - cached creds for user %s status %s\n",
868                 sid_string_static(sid), get_friendly_nt_error_msg(status) ));
869
870         centry_free(centry);
871         return status;
872 }
873
874 NTSTATUS wcache_save_creds(struct winbindd_domain *domain, 
875                            TALLOC_CTX *mem_ctx, 
876                            const DOM_SID *sid, 
877                            const uint8 nt_pass[NT_HASH_LEN])
878 {
879         struct cache_entry *centry;
880         fstring sid_string;
881         uint32 rid;
882
883         if (is_null_sid(sid)) {
884                 return NT_STATUS_INVALID_SID;
885         }
886
887         if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
888                 return NT_STATUS_INVALID_SID;
889         }
890
891         centry = centry_start(domain, NT_STATUS_OK);
892         if (!centry) {
893                 return NT_STATUS_INTERNAL_DB_ERROR;
894         }
895
896 #if DEBUG_PASSWORD
897         dump_data(100, (const char *)nt_pass, NT_HASH_LEN);
898 #endif
899
900         centry_put_time(centry, time(NULL));
901         centry_put_string(centry, (const char *)nt_pass);
902         centry_end(centry, "CRED/%s", sid_to_string(sid_string, sid));
903
904         DEBUG(10,("wcache_save_creds: %s\n", sid_string));
905
906         centry_free(centry);
907
908         return NT_STATUS_OK;
909 }
910
911
912 /* Query display info. This is the basic user list fn */
913 static NTSTATUS query_user_list(struct winbindd_domain *domain,
914                                 TALLOC_CTX *mem_ctx,
915                                 uint32 *num_entries, 
916                                 WINBIND_USERINFO **info)
917 {
918         struct winbind_cache *cache = get_cache(domain);
919         struct cache_entry *centry = NULL;
920         NTSTATUS status;
921         unsigned int i, retry;
922
923         if (!cache->tdb)
924                 goto do_query;
925
926         centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
927         if (!centry)
928                 goto do_query;
929
930         *num_entries = centry_uint32(centry);
931         
932         if (*num_entries == 0)
933                 goto do_cached;
934
935         (*info) = TALLOC_ARRAY(mem_ctx, WINBIND_USERINFO, *num_entries);
936         if (! (*info))
937                 smb_panic("query_user_list out of memory");
938         for (i=0; i<(*num_entries); i++) {
939                 (*info)[i].acct_name = centry_string(centry, mem_ctx);
940                 (*info)[i].full_name = centry_string(centry, mem_ctx);
941                 (*info)[i].homedir = centry_string(centry, mem_ctx);
942                 (*info)[i].shell = centry_string(centry, mem_ctx);
943                 centry_sid(centry, mem_ctx, &(*info)[i].user_sid);
944                 centry_sid(centry, mem_ctx, &(*info)[i].group_sid);
945         }
946
947 do_cached:      
948         status = centry->status;
949
950         DEBUG(10,("query_user_list: [Cached] - cached list for domain %s status %s\n",
951                 domain->name, get_friendly_nt_error_msg(status) ));
952
953         centry_free(centry);
954         return status;
955
956 do_query:
957         *num_entries = 0;
958         *info = NULL;
959
960         /* Return status value returned by seq number check */
961
962         if (!NT_STATUS_IS_OK(domain->last_status))
963                 return domain->last_status;
964
965         /* Put the query_user_list() in a retry loop.  There appears to be
966          * some bug either with Windows 2000 or Samba's handling of large
967          * rpc replies.  This manifests itself as sudden disconnection
968          * at a random point in the enumeration of a large (60k) user list.
969          * The retry loop simply tries the operation again. )-:  It's not
970          * pretty but an acceptable workaround until we work out what the
971          * real problem is. */
972
973         retry = 0;
974         do {
975
976                 DEBUG(10,("query_user_list: [Cached] - doing backend query for list for domain %s\n",
977                         domain->name ));
978
979                 status = domain->backend->query_user_list(domain, mem_ctx, num_entries, info);
980                 if (!NT_STATUS_IS_OK(status))
981                         DEBUG(3, ("query_user_list: returned 0x%08x, "
982                                   "retrying\n", NT_STATUS_V(status)));
983                         if (NT_STATUS_EQUAL(status, NT_STATUS_UNSUCCESSFUL)) {
984                                 DEBUG(3, ("query_user_list: flushing "
985                                           "connection cache\n"));
986                                 invalidate_cm_connection(&domain->conn);
987                         }
988
989         } while (NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_UNSUCCESSFUL) && 
990                  (retry++ < 5));
991
992         /* and save it */
993         refresh_sequence_number(domain, False);
994         centry = centry_start(domain, status);
995         if (!centry)
996                 goto skip_save;
997         centry_put_uint32(centry, *num_entries);
998         for (i=0; i<(*num_entries); i++) {
999                 centry_put_string(centry, (*info)[i].acct_name);
1000                 centry_put_string(centry, (*info)[i].full_name);
1001                 centry_put_string(centry, (*info)[i].homedir);
1002                 centry_put_string(centry, (*info)[i].shell);
1003                 centry_put_sid(centry, &(*info)[i].user_sid);
1004                 centry_put_sid(centry, &(*info)[i].group_sid);
1005                 if (domain->backend->consistent) {
1006                         /* when the backend is consistent we can pre-prime some mappings */
1007                         wcache_save_name_to_sid(domain, NT_STATUS_OK, 
1008                                                 domain->name,
1009                                                 (*info)[i].acct_name, 
1010                                                 &(*info)[i].user_sid,
1011                                                 SID_NAME_USER);
1012                         wcache_save_sid_to_name(domain, NT_STATUS_OK, 
1013                                                 &(*info)[i].user_sid,
1014                                                 domain->name,
1015                                                 (*info)[i].acct_name, 
1016                                                 SID_NAME_USER);
1017                         wcache_save_user(domain, NT_STATUS_OK, &(*info)[i]);
1018                 }
1019         }       
1020         centry_end(centry, "UL/%s", domain->name);
1021         centry_free(centry);
1022
1023 skip_save:
1024         return status;
1025 }
1026
1027 /* list all domain groups */
1028 static NTSTATUS enum_dom_groups(struct winbindd_domain *domain,
1029                                 TALLOC_CTX *mem_ctx,
1030                                 uint32 *num_entries, 
1031                                 struct acct_info **info)
1032 {
1033         struct winbind_cache *cache = get_cache(domain);
1034         struct cache_entry *centry = NULL;
1035         NTSTATUS status;
1036         unsigned int i;
1037
1038         if (!cache->tdb)
1039                 goto do_query;
1040
1041         centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
1042         if (!centry)
1043                 goto do_query;
1044
1045         *num_entries = centry_uint32(centry);
1046         
1047         if (*num_entries == 0)
1048                 goto do_cached;
1049
1050         (*info) = TALLOC_ARRAY(mem_ctx, struct acct_info, *num_entries);
1051         if (! (*info))
1052                 smb_panic("enum_dom_groups out of memory");
1053         for (i=0; i<(*num_entries); i++) {
1054                 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1055                 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1056                 (*info)[i].rid = centry_uint32(centry);
1057         }
1058
1059 do_cached:      
1060         status = centry->status;
1061
1062         DEBUG(10,("enum_dom_groups: [Cached] - cached list for domain %s status %s\n",
1063                 domain->name, get_friendly_nt_error_msg(status) ));
1064
1065         centry_free(centry);
1066         return status;
1067
1068 do_query:
1069         *num_entries = 0;
1070         *info = NULL;
1071
1072         /* Return status value returned by seq number check */
1073
1074         if (!NT_STATUS_IS_OK(domain->last_status))
1075                 return domain->last_status;
1076
1077         DEBUG(10,("enum_dom_groups: [Cached] - doing backend query for list for domain %s\n",
1078                 domain->name ));
1079
1080         status = domain->backend->enum_dom_groups(domain, mem_ctx, num_entries, info);
1081
1082         /* and save it */
1083         refresh_sequence_number(domain, False);
1084         centry = centry_start(domain, status);
1085         if (!centry)
1086                 goto skip_save;
1087         centry_put_uint32(centry, *num_entries);
1088         for (i=0; i<(*num_entries); i++) {
1089                 centry_put_string(centry, (*info)[i].acct_name);
1090                 centry_put_string(centry, (*info)[i].acct_desc);
1091                 centry_put_uint32(centry, (*info)[i].rid);
1092         }       
1093         centry_end(centry, "GL/%s/domain", domain->name);
1094         centry_free(centry);
1095
1096 skip_save:
1097         return status;
1098 }
1099
1100 /* list all domain groups */
1101 static NTSTATUS enum_local_groups(struct winbindd_domain *domain,
1102                                 TALLOC_CTX *mem_ctx,
1103                                 uint32 *num_entries, 
1104                                 struct acct_info **info)
1105 {
1106         struct winbind_cache *cache = get_cache(domain);
1107         struct cache_entry *centry = NULL;
1108         NTSTATUS status;
1109         unsigned int i;
1110
1111         if (!cache->tdb)
1112                 goto do_query;
1113
1114         centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
1115         if (!centry)
1116                 goto do_query;
1117
1118         *num_entries = centry_uint32(centry);
1119         
1120         if (*num_entries == 0)
1121                 goto do_cached;
1122
1123         (*info) = TALLOC_ARRAY(mem_ctx, struct acct_info, *num_entries);
1124         if (! (*info))
1125                 smb_panic("enum_dom_groups out of memory");
1126         for (i=0; i<(*num_entries); i++) {
1127                 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1128                 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1129                 (*info)[i].rid = centry_uint32(centry);
1130         }
1131
1132 do_cached:      
1133
1134         /* If we are returning cached data and the domain controller
1135            is down then we don't know whether the data is up to date
1136            or not.  Return NT_STATUS_MORE_PROCESSING_REQUIRED to
1137            indicate this. */
1138
1139         if (wcache_server_down(domain)) {
1140                 DEBUG(10, ("enum_local_groups: returning cached user list and server was down\n"));
1141                 status = NT_STATUS_MORE_PROCESSING_REQUIRED;
1142         } else
1143                 status = centry->status;
1144
1145         DEBUG(10,("enum_local_groups: [Cached] - cached list for domain %s status %s\n",
1146                 domain->name, get_friendly_nt_error_msg(status) ));
1147
1148         centry_free(centry);
1149         return status;
1150
1151 do_query:
1152         *num_entries = 0;
1153         *info = NULL;
1154
1155         /* Return status value returned by seq number check */
1156
1157         if (!NT_STATUS_IS_OK(domain->last_status))
1158                 return domain->last_status;
1159
1160         DEBUG(10,("enum_local_groups: [Cached] - doing backend query for list for domain %s\n",
1161                 domain->name ));
1162
1163         status = domain->backend->enum_local_groups(domain, mem_ctx, num_entries, info);
1164
1165         /* and save it */
1166         refresh_sequence_number(domain, False);
1167         centry = centry_start(domain, status);
1168         if (!centry)
1169                 goto skip_save;
1170         centry_put_uint32(centry, *num_entries);
1171         for (i=0; i<(*num_entries); i++) {
1172                 centry_put_string(centry, (*info)[i].acct_name);
1173                 centry_put_string(centry, (*info)[i].acct_desc);
1174                 centry_put_uint32(centry, (*info)[i].rid);
1175         }
1176         centry_end(centry, "GL/%s/local", domain->name);
1177         centry_free(centry);
1178
1179 skip_save:
1180         return status;
1181 }
1182
1183 /* convert a single name to a sid in a domain */
1184 static NTSTATUS name_to_sid(struct winbindd_domain *domain,
1185                             TALLOC_CTX *mem_ctx,
1186                             const char *domain_name,
1187                             const char *name,
1188                             DOM_SID *sid,
1189                             enum SID_NAME_USE *type)
1190 {
1191         struct winbind_cache *cache = get_cache(domain);
1192         struct cache_entry *centry = NULL;
1193         NTSTATUS status;
1194         fstring uname;
1195
1196         if (!cache->tdb)
1197                 goto do_query;
1198
1199         fstrcpy(uname, name);
1200         strupper_m(uname);
1201         centry = wcache_fetch(cache, domain, "NS/%s/%s", domain_name, uname);
1202         if (!centry)
1203                 goto do_query;
1204         *type = (enum SID_NAME_USE)centry_uint32(centry);
1205         status = centry->status;
1206         if (NT_STATUS_IS_OK(status)) {
1207                 centry_sid(centry, mem_ctx, sid);
1208         }
1209
1210         DEBUG(10,("name_to_sid: [Cached] - cached name for domain %s status %s\n",
1211                 domain->name, get_friendly_nt_error_msg(status) ));
1212
1213         centry_free(centry);
1214         return status;
1215
1216 do_query:
1217         ZERO_STRUCTP(sid);
1218
1219         /* If the seq number check indicated that there is a problem
1220          * with this DC, then return that status... except for
1221          * access_denied.  This is special because the dc may be in
1222          * "restrict anonymous = 1" mode, in which case it will deny
1223          * most unauthenticated operations, but *will* allow the LSA
1224          * name-to-sid that we try as a fallback. */
1225
1226         if (!(NT_STATUS_IS_OK(domain->last_status)
1227               || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1228                 return domain->last_status;
1229
1230         DEBUG(10,("name_to_sid: [Cached] - doing backend query for name for domain %s\n",
1231                 domain->name ));
1232
1233         status = domain->backend->name_to_sid(domain, mem_ctx, domain_name, name, sid, type);
1234
1235         /* and save it */
1236         if (domain->online || !is_null_sid(sid)) {
1237                 wcache_save_name_to_sid(domain, status, domain_name, name, sid, *type);
1238         }
1239
1240         if (NT_STATUS_IS_OK(status)) {
1241                 strupper_m(CONST_DISCARD(char *,domain_name));
1242                 strlower_m(CONST_DISCARD(char *,name));
1243                 wcache_save_sid_to_name(domain, status, sid, domain_name, name, *type);
1244         }
1245
1246         return status;
1247 }
1248
1249 /* convert a sid to a user or group name. The sid is guaranteed to be in the domain
1250    given */
1251 static NTSTATUS sid_to_name(struct winbindd_domain *domain,
1252                             TALLOC_CTX *mem_ctx,
1253                             const DOM_SID *sid,
1254                             char **domain_name,
1255                             char **name,
1256                             enum SID_NAME_USE *type)
1257 {
1258         struct winbind_cache *cache = get_cache(domain);
1259         struct cache_entry *centry = NULL;
1260         NTSTATUS status;
1261         fstring sid_string;
1262
1263         if (!cache->tdb)
1264                 goto do_query;
1265
1266         centry = wcache_fetch(cache, domain, "SN/%s", sid_to_string(sid_string, sid));
1267         if (!centry)
1268                 goto do_query;
1269         if (NT_STATUS_IS_OK(centry->status)) {
1270                 *type = (enum SID_NAME_USE)centry_uint32(centry);
1271                 *domain_name = centry_string(centry, mem_ctx);
1272                 *name = centry_string(centry, mem_ctx);
1273         }
1274         status = centry->status;
1275
1276         DEBUG(10,("sid_to_name: [Cached] - cached name for domain %s status %s\n",
1277                 domain->name, get_friendly_nt_error_msg(status) ));
1278
1279         centry_free(centry);
1280         return status;
1281
1282 do_query:
1283         *name = NULL;
1284         *domain_name = NULL;
1285
1286         /* If the seq number check indicated that there is a problem
1287          * with this DC, then return that status... except for
1288          * access_denied.  This is special because the dc may be in
1289          * "restrict anonymous = 1" mode, in which case it will deny
1290          * most unauthenticated operations, but *will* allow the LSA
1291          * sid-to-name that we try as a fallback. */
1292
1293         if (!(NT_STATUS_IS_OK(domain->last_status)
1294               || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1295                 return domain->last_status;
1296
1297         DEBUG(10,("sid_to_name: [Cached] - doing backend query for name for domain %s\n",
1298                 domain->name ));
1299
1300         status = domain->backend->sid_to_name(domain, mem_ctx, sid, domain_name, name, type);
1301
1302         /* and save it */
1303         refresh_sequence_number(domain, False);
1304         wcache_save_sid_to_name(domain, status, sid, *domain_name, *name, *type);
1305
1306         /* We can't save the name to sid mapping here, as with sid history a
1307          * later name2sid would give the wrong sid. */
1308
1309         return status;
1310 }
1311
1312 /* Lookup user information from a rid */
1313 static NTSTATUS query_user(struct winbindd_domain *domain, 
1314                            TALLOC_CTX *mem_ctx, 
1315                            const DOM_SID *user_sid, 
1316                            WINBIND_USERINFO *info)
1317 {
1318         struct winbind_cache *cache = get_cache(domain);
1319         struct cache_entry *centry = NULL;
1320         NTSTATUS status;
1321
1322         if (!cache->tdb)
1323                 goto do_query;
1324
1325         centry = wcache_fetch(cache, domain, "U/%s", sid_string_static(user_sid));
1326         
1327         /* If we have an access denied cache entry and a cached info3 in the
1328            samlogon cache then do a query.  This will force the rpc back end
1329            to return the info3 data. */
1330
1331         if (NT_STATUS_V(domain->last_status) == NT_STATUS_V(NT_STATUS_ACCESS_DENIED) &&
1332             netsamlogon_cache_have(user_sid)) {
1333                 DEBUG(10, ("query_user: cached access denied and have cached info3\n"));
1334                 domain->last_status = NT_STATUS_OK;
1335                 centry_free(centry);
1336                 goto do_query;
1337         }
1338         
1339         if (!centry)
1340                 goto do_query;
1341
1342         info->acct_name = centry_string(centry, mem_ctx);
1343         info->full_name = centry_string(centry, mem_ctx);
1344         info->homedir = centry_string(centry, mem_ctx);
1345         info->shell = centry_string(centry, mem_ctx);
1346         centry_sid(centry, mem_ctx, &info->user_sid);
1347         centry_sid(centry, mem_ctx, &info->group_sid);
1348         status = centry->status;
1349
1350         DEBUG(10,("query_user: [Cached] - cached info for domain %s status %s\n",
1351                 domain->name, get_friendly_nt_error_msg(status) ));
1352
1353         centry_free(centry);
1354         return status;
1355
1356 do_query:
1357         ZERO_STRUCTP(info);
1358
1359         /* Return status value returned by seq number check */
1360
1361         if (!NT_STATUS_IS_OK(domain->last_status))
1362                 return domain->last_status;
1363         
1364         DEBUG(10,("sid_to_name: [Cached] - doing backend query for info for domain %s\n",
1365                 domain->name ));
1366
1367         status = domain->backend->query_user(domain, mem_ctx, user_sid, info);
1368
1369         /* and save it */
1370         refresh_sequence_number(domain, False);
1371         wcache_save_user(domain, status, info);
1372
1373         return status;
1374 }
1375
1376
1377 /* Lookup groups a user is a member of. */
1378 static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
1379                                   TALLOC_CTX *mem_ctx,
1380                                   const DOM_SID *user_sid, 
1381                                   uint32 *num_groups, DOM_SID **user_gids)
1382 {
1383         struct winbind_cache *cache = get_cache(domain);
1384         struct cache_entry *centry = NULL;
1385         NTSTATUS status;
1386         unsigned int i;
1387         fstring sid_string;
1388
1389         if (!cache->tdb)
1390                 goto do_query;
1391
1392         centry = wcache_fetch(cache, domain, "UG/%s", sid_to_string(sid_string, user_sid));
1393         
1394         /* If we have an access denied cache entry and a cached info3 in the
1395            samlogon cache then do a query.  This will force the rpc back end
1396            to return the info3 data. */
1397
1398         if (NT_STATUS_V(domain->last_status) == NT_STATUS_V(NT_STATUS_ACCESS_DENIED) &&
1399             netsamlogon_cache_have(user_sid)) {
1400                 DEBUG(10, ("lookup_usergroups: cached access denied and have cached info3\n"));
1401                 domain->last_status = NT_STATUS_OK;
1402                 centry_free(centry);
1403                 goto do_query;
1404         }
1405         
1406         if (!centry)
1407                 goto do_query;
1408
1409         *num_groups = centry_uint32(centry);
1410         
1411         if (*num_groups == 0)
1412                 goto do_cached;
1413
1414         (*user_gids) = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_groups);
1415         if (! (*user_gids))
1416                 smb_panic("lookup_usergroups out of memory");
1417         for (i=0; i<(*num_groups); i++) {
1418                 centry_sid(centry, mem_ctx, &(*user_gids)[i]);
1419         }
1420
1421 do_cached:      
1422         status = centry->status;
1423
1424         DEBUG(10,("lookup_usergroups: [Cached] - cached info for domain %s status %s\n",
1425                 domain->name, get_friendly_nt_error_msg(status) ));
1426
1427         centry_free(centry);
1428         return status;
1429
1430 do_query:
1431         (*num_groups) = 0;
1432         (*user_gids) = NULL;
1433
1434         /* Return status value returned by seq number check */
1435
1436         if (!NT_STATUS_IS_OK(domain->last_status))
1437                 return domain->last_status;
1438
1439         DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info for domain %s\n",
1440                 domain->name ));
1441
1442         status = domain->backend->lookup_usergroups(domain, mem_ctx, user_sid, num_groups, user_gids);
1443
1444         /* and save it */
1445         refresh_sequence_number(domain, False);
1446         centry = centry_start(domain, status);
1447         if (!centry)
1448                 goto skip_save;
1449         centry_put_uint32(centry, *num_groups);
1450         for (i=0; i<(*num_groups); i++) {
1451                 centry_put_sid(centry, &(*user_gids)[i]);
1452         }       
1453         centry_end(centry, "UG/%s", sid_to_string(sid_string, user_sid));
1454         centry_free(centry);
1455
1456 skip_save:
1457         return status;
1458 }
1459
1460 static NTSTATUS lookup_useraliases(struct winbindd_domain *domain,
1461                                    TALLOC_CTX *mem_ctx,
1462                                    uint32 num_sids, const DOM_SID *sids,
1463                                    uint32 *num_aliases, uint32 **alias_rids)
1464 {
1465         struct winbind_cache *cache = get_cache(domain);
1466         struct cache_entry *centry = NULL;
1467         NTSTATUS status;
1468         char *sidlist = talloc_strdup(mem_ctx, "");
1469         int i;
1470
1471         if (!cache->tdb)
1472                 goto do_query;
1473
1474         if (num_sids == 0) {
1475                 *num_aliases = 0;
1476                 *alias_rids = NULL;
1477                 return NT_STATUS_OK;
1478         }
1479
1480         /* We need to cache indexed by the whole list of SIDs, the aliases
1481          * resulting might come from any of the SIDs. */
1482
1483         for (i=0; i<num_sids; i++) {
1484                 sidlist = talloc_asprintf(mem_ctx, "%s/%s", sidlist,
1485                                           sid_string_static(&sids[i]));
1486                 if (sidlist == NULL)
1487                         return NT_STATUS_NO_MEMORY;
1488         }
1489
1490         centry = wcache_fetch(cache, domain, "UA%s", sidlist);
1491
1492         if (!centry)
1493                 goto do_query;
1494
1495         *num_aliases = centry_uint32(centry);
1496         *alias_rids = NULL;
1497
1498         (*alias_rids) = TALLOC_ARRAY(mem_ctx, uint32, *num_aliases);
1499
1500         if ((*num_aliases != 0) && ((*alias_rids) == NULL)) {
1501                 centry_free(centry);
1502                 return NT_STATUS_NO_MEMORY;
1503         }
1504
1505         for (i=0; i<(*num_aliases); i++)
1506                 (*alias_rids)[i] = centry_uint32(centry);
1507
1508         status = centry->status;
1509
1510         DEBUG(10,("lookup_useraliases: [Cached] - cached info for domain %s "
1511                   "status %s\n", domain->name,
1512                   get_friendly_nt_error_msg(status)));
1513
1514         centry_free(centry);
1515         return status;
1516
1517  do_query:
1518         (*num_aliases) = 0;
1519         (*alias_rids) = NULL;
1520
1521         if (!NT_STATUS_IS_OK(domain->last_status))
1522                 return domain->last_status;
1523
1524         DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info "
1525                   "for domain %s\n", domain->name ));
1526
1527         status = domain->backend->lookup_useraliases(domain, mem_ctx,
1528                                                      num_sids, sids,
1529                                                      num_aliases, alias_rids);
1530
1531         /* and save it */
1532         refresh_sequence_number(domain, False);
1533         centry = centry_start(domain, status);
1534         if (!centry)
1535                 goto skip_save;
1536         centry_put_uint32(centry, *num_aliases);
1537         for (i=0; i<(*num_aliases); i++)
1538                 centry_put_uint32(centry, (*alias_rids)[i]);
1539         centry_end(centry, "UA%s", sidlist);
1540         centry_free(centry);
1541
1542  skip_save:
1543         return status;
1544 }
1545
1546
1547 static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
1548                                 TALLOC_CTX *mem_ctx,
1549                                 const DOM_SID *group_sid, uint32 *num_names, 
1550                                 DOM_SID **sid_mem, char ***names, 
1551                                 uint32 **name_types)
1552 {
1553         struct winbind_cache *cache = get_cache(domain);
1554         struct cache_entry *centry = NULL;
1555         NTSTATUS status;
1556         unsigned int i;
1557         fstring sid_string;
1558
1559         if (!cache->tdb)
1560                 goto do_query;
1561
1562         centry = wcache_fetch(cache, domain, "GM/%s", sid_to_string(sid_string, group_sid));
1563         if (!centry)
1564                 goto do_query;
1565
1566         *num_names = centry_uint32(centry);
1567         
1568         if (*num_names == 0)
1569                 goto do_cached;
1570
1571         (*sid_mem) = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_names);
1572         (*names) = TALLOC_ARRAY(mem_ctx, char *, *num_names);
1573         (*name_types) = TALLOC_ARRAY(mem_ctx, uint32, *num_names);
1574
1575         if (! (*sid_mem) || ! (*names) || ! (*name_types)) {
1576                 smb_panic("lookup_groupmem out of memory");
1577         }
1578
1579         for (i=0; i<(*num_names); i++) {
1580                 centry_sid(centry, mem_ctx, &(*sid_mem)[i]);
1581                 (*names)[i] = centry_string(centry, mem_ctx);
1582                 (*name_types)[i] = centry_uint32(centry);
1583         }
1584
1585 do_cached:      
1586         status = centry->status;
1587
1588         DEBUG(10,("lookup_groupmem: [Cached] - cached info for domain %s status %s\n",
1589                 domain->name, get_friendly_nt_error_msg(status) ));
1590
1591         centry_free(centry);
1592         return status;
1593
1594 do_query:
1595         (*num_names) = 0;
1596         (*sid_mem) = NULL;
1597         (*names) = NULL;
1598         (*name_types) = NULL;
1599         
1600         /* Return status value returned by seq number check */
1601
1602         if (!NT_STATUS_IS_OK(domain->last_status))
1603                 return domain->last_status;
1604
1605         DEBUG(10,("lookup_groupmem: [Cached] - doing backend query for info for domain %s\n",
1606                 domain->name ));
1607
1608         status = domain->backend->lookup_groupmem(domain, mem_ctx, group_sid, num_names, 
1609                                                   sid_mem, names, name_types);
1610
1611         /* and save it */
1612         refresh_sequence_number(domain, False);
1613         centry = centry_start(domain, status);
1614         if (!centry)
1615                 goto skip_save;
1616         centry_put_uint32(centry, *num_names);
1617         for (i=0; i<(*num_names); i++) {
1618                 centry_put_sid(centry, &(*sid_mem)[i]);
1619                 centry_put_string(centry, (*names)[i]);
1620                 centry_put_uint32(centry, (*name_types)[i]);
1621         }       
1622         centry_end(centry, "GM/%s", sid_to_string(sid_string, group_sid));
1623         centry_free(centry);
1624
1625 skip_save:
1626         return status;
1627 }
1628
1629 /* find the sequence number for a domain */
1630 static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
1631 {
1632         refresh_sequence_number(domain, False);
1633
1634         *seq = domain->sequence_number;
1635
1636         return NT_STATUS_OK;
1637 }
1638
1639 /* enumerate trusted domains 
1640  * (we need to have the list of trustdoms in the cache when we go offline) -
1641  * Guenther */
1642 static NTSTATUS trusted_domains(struct winbindd_domain *domain,
1643                                 TALLOC_CTX *mem_ctx,
1644                                 uint32 *num_domains,
1645                                 char ***names,
1646                                 char ***alt_names,
1647                                 DOM_SID **dom_sids)
1648 {
1649         struct winbind_cache *cache = get_cache(domain);
1650         struct cache_entry *centry = NULL;
1651         NTSTATUS status;
1652         int i;
1653  
1654         if (!cache->tdb)
1655                 goto do_query;
1656  
1657         centry = wcache_fetch(cache, domain, "TRUSTDOMS/%s", domain->name);
1658         
1659         if (!centry) {
1660                 goto do_query;
1661         }
1662  
1663         *num_domains = centry_uint32(centry);
1664         
1665         (*names)        = TALLOC_ARRAY(mem_ctx, char *, *num_domains);
1666         (*alt_names)    = TALLOC_ARRAY(mem_ctx, char *, *num_domains);
1667         (*dom_sids)     = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_domains);
1668  
1669         if (! (*dom_sids) || ! (*names) || ! (*alt_names)) {
1670                 smb_panic("trusted_domains out of memory");
1671         }
1672  
1673         for (i=0; i<(*num_domains); i++) {
1674                 (*names)[i] = centry_string(centry, mem_ctx);
1675                 (*alt_names)[i] = centry_string(centry, mem_ctx);
1676                 centry_sid(centry, mem_ctx, &(*dom_sids)[i]);
1677         }
1678
1679         status = centry->status;
1680  
1681         DEBUG(10,("trusted_domains: [Cached] - cached info for domain %s (%d trusts) status %s\n",
1682                 domain->name, *num_domains, get_friendly_nt_error_msg(status) ));
1683  
1684         centry_free(centry);
1685         return status;
1686  
1687 do_query:
1688         (*num_domains) = 0;
1689         (*dom_sids) = NULL;
1690         (*names) = NULL;
1691         (*alt_names) = NULL;
1692  
1693         /* Return status value returned by seq number check */
1694
1695         if (!NT_STATUS_IS_OK(domain->last_status))
1696                 return domain->last_status;
1697         
1698         DEBUG(10,("trusted_domains: [Cached] - doing backend query for info for domain %s\n",
1699                 domain->name ));
1700  
1701         status = domain->backend->trusted_domains(domain, mem_ctx, num_domains,
1702                                                 names, alt_names, dom_sids);
1703
1704         /* no trusts gives NT_STATUS_NO_MORE_ENTRIES resetting to NT_STATUS_OK
1705          * so that the generic centry handling still applies correctly -
1706          * Guenther*/
1707
1708         if (!NT_STATUS_IS_ERR(status)) {
1709                 status = NT_STATUS_OK;
1710         }
1711
1712         /* and save it */
1713         refresh_sequence_number(domain, False);
1714  
1715         centry = centry_start(domain, status);
1716         if (!centry)
1717                 goto skip_save;
1718
1719         centry_put_uint32(centry, *num_domains);
1720
1721         for (i=0; i<(*num_domains); i++) {
1722                 centry_put_string(centry, (*names)[i]);
1723                 centry_put_string(centry, (*alt_names)[i]);
1724                 centry_put_sid(centry, &(*dom_sids)[i]);
1725         }
1726         
1727         centry_end(centry, "TRUSTDOMS/%s", domain->name);
1728  
1729         centry_free(centry);
1730  
1731 skip_save:
1732         return status;
1733 }       
1734
1735 /* get lockout policy */
1736 static NTSTATUS lockout_policy(struct winbindd_domain *domain,
1737                                TALLOC_CTX *mem_ctx,
1738                                SAM_UNK_INFO_12 *lockout_policy){
1739         struct winbind_cache *cache = get_cache(domain);
1740         struct cache_entry *centry = NULL;
1741         NTSTATUS status;
1742  
1743         if (!cache->tdb)
1744                 goto do_query;
1745  
1746         centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
1747         
1748         if (!centry)
1749                 goto do_query;
1750  
1751         lockout_policy->duration = centry_nttime(centry);
1752         lockout_policy->reset_count = centry_nttime(centry);
1753         lockout_policy->bad_attempt_lockout = centry_uint16(centry);
1754  
1755         status = centry->status;
1756  
1757         DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status %s\n",
1758                 domain->name, get_friendly_nt_error_msg(status) ));
1759  
1760         centry_free(centry);
1761         return status;
1762  
1763 do_query:
1764         ZERO_STRUCTP(lockout_policy);
1765  
1766         /* Return status value returned by seq number check */
1767
1768         if (!NT_STATUS_IS_OK(domain->last_status))
1769                 return domain->last_status;
1770         
1771         DEBUG(10,("lockout_policy: [Cached] - doing backend query for info for domain %s\n",
1772                 domain->name ));
1773  
1774         status = domain->backend->lockout_policy(domain, mem_ctx, lockout_policy); 
1775  
1776         /* and save it */
1777         refresh_sequence_number(domain, False);
1778         wcache_save_lockout_policy(domain, status, lockout_policy);
1779  
1780         return status;
1781 }
1782  
1783 /* get password policy */
1784 static NTSTATUS password_policy(struct winbindd_domain *domain,
1785                                 TALLOC_CTX *mem_ctx,
1786                                 SAM_UNK_INFO_1 *password_policy)
1787 {
1788         struct winbind_cache *cache = get_cache(domain);
1789         struct cache_entry *centry = NULL;
1790         NTSTATUS status;
1791
1792         if (!cache->tdb)
1793                 goto do_query;
1794  
1795         centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
1796         
1797         if (!centry)
1798                 goto do_query;
1799
1800         password_policy->min_length_password = centry_uint16(centry);
1801         password_policy->password_history = centry_uint16(centry);
1802         password_policy->password_properties = centry_uint32(centry);
1803         password_policy->expire = centry_nttime(centry);
1804         password_policy->min_passwordage = centry_nttime(centry);
1805
1806         status = centry->status;
1807
1808         DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status %s\n",
1809                 domain->name, get_friendly_nt_error_msg(status) ));
1810
1811         centry_free(centry);
1812         return status;
1813
1814 do_query:
1815         ZERO_STRUCTP(password_policy);
1816
1817         /* Return status value returned by seq number check */
1818
1819         if (!NT_STATUS_IS_OK(domain->last_status))
1820                 return domain->last_status;
1821         
1822         DEBUG(10,("password_policy: [Cached] - doing backend query for info for domain %s\n",
1823                 domain->name ));
1824
1825         status = domain->backend->password_policy(domain, mem_ctx, password_policy); 
1826
1827         /* and save it */
1828         refresh_sequence_number(domain, False);
1829         wcache_save_password_policy(domain, status, password_policy);
1830
1831         return status;
1832 }
1833
1834
1835 /* Invalidate cached user and group lists coherently */
1836
1837 static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, 
1838                        void *state)
1839 {
1840         if (strncmp(kbuf.dptr, "UL/", 3) == 0 ||
1841             strncmp(kbuf.dptr, "GL/", 3) == 0)
1842                 tdb_delete(the_tdb, kbuf);
1843
1844         return 0;
1845 }
1846
1847 /* Invalidate the getpwnam and getgroups entries for a winbindd domain */
1848
1849 void wcache_invalidate_samlogon(struct winbindd_domain *domain, 
1850                                 NET_USER_INFO_3 *info3)
1851 {
1852         struct winbind_cache *cache;
1853         
1854         if (!domain)
1855                 return;
1856
1857         cache = get_cache(domain);
1858         netsamlogon_clear_cached_user(cache->tdb, info3);
1859 }
1860
1861 void wcache_invalidate_cache(void)
1862 {
1863         struct winbindd_domain *domain;
1864
1865         for (domain = domain_list(); domain; domain = domain->next) {
1866                 struct winbind_cache *cache = get_cache(domain);
1867
1868                 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
1869                            "entries for %s\n", domain->name));
1870                 if (cache)
1871                         tdb_traverse(cache->tdb, traverse_fn, NULL);
1872         }
1873 }
1874
1875 static BOOL init_wcache(void)
1876 {
1877         if (wcache == NULL) {
1878                 wcache = SMB_XMALLOC_P(struct winbind_cache);
1879                 ZERO_STRUCTP(wcache);
1880         }
1881
1882         if (wcache->tdb != NULL)
1883                 return True;
1884
1885         /* when working offline we must not clear the cache on restart */
1886         wcache->tdb = tdb_open_log(lock_path("winbindd_cache.tdb"),
1887                                 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE, 
1888                                 TDB_DEFAULT /*TDB_CLEAR_IF_FIRST*/, O_RDWR|O_CREAT, 0600);
1889
1890         if (wcache->tdb == NULL) {
1891                 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
1892                 return False;
1893         }
1894
1895         return True;
1896 }
1897
1898 void cache_store_response(pid_t pid, struct winbindd_response *response)
1899 {
1900         fstring key_str;
1901
1902         if (!init_wcache())
1903                 return;
1904
1905         DEBUG(10, ("Storing response for pid %d, len %d\n",
1906                    pid, response->length));
1907
1908         fstr_sprintf(key_str, "DR/%d", pid);
1909         if (tdb_store(wcache->tdb, string_tdb_data(key_str), 
1910                       make_tdb_data((void *)response, sizeof(*response)),
1911                       TDB_REPLACE) == -1)
1912                 return;
1913
1914         if (response->length == sizeof(*response))
1915                 return;
1916
1917         /* There's extra data */
1918
1919         DEBUG(10, ("Storing extra data: len=%d\n",
1920                    (int)(response->length - sizeof(*response))));
1921
1922         fstr_sprintf(key_str, "DE/%d", pid);
1923         if (tdb_store(wcache->tdb, string_tdb_data(key_str),
1924                       make_tdb_data(response->extra_data.data,
1925                                     response->length - sizeof(*response)),
1926                       TDB_REPLACE) == 0)
1927                 return;
1928
1929         /* We could not store the extra data, make sure the tdb does not
1930          * contain a main record with wrong dangling extra data */
1931
1932         fstr_sprintf(key_str, "DR/%d", pid);
1933         tdb_delete(wcache->tdb, string_tdb_data(key_str));
1934
1935         return;
1936 }
1937
1938 BOOL cache_retrieve_response(pid_t pid, struct winbindd_response * response)
1939 {
1940         TDB_DATA data;
1941         fstring key_str;
1942
1943         if (!init_wcache())
1944                 return False;
1945
1946         DEBUG(10, ("Retrieving response for pid %d\n", pid));
1947
1948         fstr_sprintf(key_str, "DR/%d", pid);
1949         data = tdb_fetch(wcache->tdb, string_tdb_data(key_str));
1950
1951         if (data.dptr == NULL)
1952                 return False;
1953
1954         if (data.dsize != sizeof(*response))
1955                 return False;
1956
1957         memcpy(response, data.dptr, data.dsize);
1958         SAFE_FREE(data.dptr);
1959
1960         if (response->length == sizeof(*response)) {
1961                 response->extra_data.data = NULL;
1962                 return True;
1963         }
1964
1965         /* There's extra data */
1966
1967         DEBUG(10, ("Retrieving extra data length=%d\n",
1968                    (int)(response->length - sizeof(*response))));
1969
1970         fstr_sprintf(key_str, "DE/%d", pid);
1971         data = tdb_fetch(wcache->tdb, string_tdb_data(key_str));
1972
1973         if (data.dptr == NULL) {
1974                 DEBUG(0, ("Did not find extra data\n"));
1975                 return False;
1976         }
1977
1978         if (data.dsize != (response->length - sizeof(*response))) {
1979                 DEBUG(0, ("Invalid extra data length: %d\n", (int)data.dsize));
1980                 SAFE_FREE(data.dptr);
1981                 return False;
1982         }
1983
1984         dump_data(11, data.dptr, data.dsize);
1985
1986         response->extra_data.data = data.dptr;
1987         return True;
1988 }
1989
1990 void cache_cleanup_response(pid_t pid)
1991 {
1992         fstring key_str;
1993
1994         if (!init_wcache())
1995                 return;
1996
1997         fstr_sprintf(key_str, "DR/%d", pid);
1998         tdb_delete(wcache->tdb, string_tdb_data(key_str));
1999
2000         fstr_sprintf(key_str, "DE/%d", pid);
2001         tdb_delete(wcache->tdb, string_tdb_data(key_str));
2002
2003         return;
2004 }
2005
2006
2007 BOOL lookup_cached_sid(TALLOC_CTX *mem_ctx, const DOM_SID *sid,
2008                        const char **domain_name, const char **name,
2009                        enum SID_NAME_USE *type)
2010 {
2011         struct winbindd_domain *domain;
2012         struct winbind_cache *cache;
2013         struct cache_entry *centry = NULL;
2014         NTSTATUS status;
2015
2016         domain = find_lookup_domain_from_sid(sid);
2017         if (domain == NULL) {
2018                 return False;
2019         }
2020
2021         cache = get_cache(domain);
2022
2023         if (cache->tdb == NULL) {
2024                 return False;
2025         }
2026
2027         centry = wcache_fetch(cache, domain, "SN/%s", sid_string_static(sid));
2028         if (centry == NULL) {
2029                 return False;
2030         }
2031
2032         if (NT_STATUS_IS_OK(centry->status)) {
2033                 *type = (enum SID_NAME_USE)centry_uint32(centry);
2034                 *domain_name = centry_string(centry, mem_ctx);
2035                 *name = centry_string(centry, mem_ctx);
2036         }
2037
2038         status = centry->status;
2039         centry_free(centry);
2040         return NT_STATUS_IS_OK(status);
2041 }
2042
2043 BOOL lookup_cached_name(TALLOC_CTX *mem_ctx,
2044                         const char *domain_name,
2045                         const char *name,
2046                         DOM_SID *sid,
2047                         enum SID_NAME_USE *type)
2048 {
2049         struct winbindd_domain *domain;
2050         struct winbind_cache *cache;
2051         struct cache_entry *centry = NULL;
2052         NTSTATUS status;
2053         fstring uname;
2054
2055         domain = find_lookup_domain_from_name(domain_name);
2056         if (domain == NULL) {
2057                 return False;
2058         }
2059
2060         cache = get_cache(domain);
2061
2062         if (cache->tdb == NULL) {
2063                 return False;
2064         }
2065
2066         fstrcpy(uname, name);
2067         strupper_m(uname);
2068         
2069         centry = wcache_fetch(cache, domain, "NS/%s/%s", domain_name, uname);
2070         if (centry == NULL) {
2071                 return False;
2072         }
2073
2074         if (NT_STATUS_IS_OK(centry->status)) {
2075                 *type = (enum SID_NAME_USE)centry_uint32(centry);
2076                 centry_sid(centry, mem_ctx, sid);
2077         }
2078
2079         status = centry->status;
2080         centry_free(centry);
2081         
2082         return NT_STATUS_IS_OK(status);
2083 }
2084
2085 void cache_name2sid(struct winbindd_domain *domain, 
2086                     const char *domain_name, const char *name,
2087                     enum SID_NAME_USE type, const DOM_SID *sid)
2088 {
2089         wcache_save_name_to_sid(domain, NT_STATUS_OK, domain_name, name,
2090                                 sid, type);
2091 }
2092
2093 /* delete all centries that don't have NT_STATUS_OK set */
2094 static int traverse_fn_cleanup(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, 
2095                                TDB_DATA dbuf, void *state)
2096 {
2097         struct cache_entry *centry;
2098         char buf[1024];
2099
2100         if (!snprintf(buf, kbuf.dsize + 1, "%s", kbuf.dptr)) {
2101                 return 1;
2102         }
2103
2104         centry = wcache_fetch_raw(buf);
2105         if (!centry) {
2106                 return 0;
2107         }
2108
2109         if (!NT_STATUS_IS_OK(centry->status)) {
2110                 DEBUG(10,("deleting centry %s\n", buf));
2111                 tdb_delete(the_tdb, kbuf);
2112         }
2113
2114         centry_free(centry);
2115         return 0;
2116 }
2117
2118 /* flush the cache */
2119 void wcache_flush_cache(void)
2120 {
2121         extern BOOL opt_nocache;
2122
2123         if (!wcache)
2124                 return;
2125         if (wcache->tdb) {
2126                 tdb_close(wcache->tdb);
2127                 wcache->tdb = NULL;
2128         }
2129         if (opt_nocache)
2130                 return;
2131
2132         /* when working offline we must not clear the cache on restart */
2133         wcache->tdb = tdb_open_log(lock_path("winbindd_cache.tdb"),
2134                                 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE, 
2135                                 TDB_DEFAULT /* TDB_CLEAR_IF_FIRST */, O_RDWR|O_CREAT, 0600);
2136
2137         if (!wcache->tdb) {
2138                 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
2139         }
2140
2141         tdb_traverse(wcache->tdb, traverse_fn_cleanup, NULL);
2142
2143         DEBUG(10,("wcache_flush_cache success\n"));
2144 }
2145
2146 /* Count cached creds */
2147
2148 static int traverse_fn_cached_creds(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, 
2149                                     void *state)
2150 {
2151         int *cred_count = (int*)state;
2152  
2153         if (strncmp(kbuf.dptr, "CRED/", 5) == 0) {
2154                 (*cred_count)++;
2155         }
2156         return 0;
2157 }
2158
2159 NTSTATUS wcache_count_cached_creds(struct winbindd_domain *domain, int *count)
2160 {
2161         struct winbind_cache *cache = get_cache(domain);
2162
2163         *count = 0;
2164
2165         if (!cache->tdb) {
2166                 return NT_STATUS_INTERNAL_DB_ERROR;
2167         }
2168  
2169         tdb_traverse(cache->tdb, traverse_fn_cached_creds, (void *)count);
2170
2171         return NT_STATUS_OK;
2172 }
2173
2174 struct cred_list {
2175         struct cred_list *prev, *next;
2176         TDB_DATA key;
2177         fstring name;
2178         time_t created;
2179 };
2180 static struct cred_list *wcache_cred_list;
2181
2182 static int traverse_fn_get_credlist(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, 
2183                                     void *state)
2184 {
2185         struct cred_list *cred;
2186
2187         if (strncmp(kbuf.dptr, "CRED/", 5) == 0) {
2188
2189                 cred = SMB_MALLOC_P(struct cred_list);
2190                 if (cred == NULL) {
2191                         DEBUG(0,("traverse_fn_remove_first_creds: failed to malloc new entry for list\n"));
2192                         return -1;
2193                 }
2194
2195                 ZERO_STRUCTP(cred);
2196                 
2197                 /* save a copy of the key */
2198                 
2199                 fstrcpy(cred->name, kbuf.dptr);         
2200                 DLIST_ADD(wcache_cred_list, cred);
2201         }
2202         
2203         return 0;
2204 }
2205
2206 NTSTATUS wcache_remove_oldest_cached_creds(struct winbindd_domain *domain, const DOM_SID *sid) 
2207 {
2208         struct winbind_cache *cache = get_cache(domain);
2209         NTSTATUS status;
2210         int ret;
2211         struct cred_list *cred, *oldest = NULL;
2212
2213         if (!cache->tdb) {
2214                 return NT_STATUS_INTERNAL_DB_ERROR;
2215         }
2216
2217         /* we possibly already have an entry */
2218         if (sid && NT_STATUS_IS_OK(wcache_cached_creds_exist(domain, sid))) {
2219         
2220                 fstring key_str;
2221
2222                 DEBUG(11,("we already have an entry, deleting that\n"));
2223
2224                 fstr_sprintf(key_str, "CRED/%s", sid_string_static(sid));
2225
2226                 tdb_delete(cache->tdb, string_tdb_data(key_str));
2227
2228                 return NT_STATUS_OK;
2229         }
2230
2231         ret = tdb_traverse(cache->tdb, traverse_fn_get_credlist, NULL);
2232         if (ret == 0) {
2233                 return NT_STATUS_OK;
2234         } else if (ret == -1) {
2235                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
2236         }
2237
2238         ZERO_STRUCTP(oldest);
2239
2240         for (cred = wcache_cred_list; cred; cred = cred->next) {
2241
2242                 TDB_DATA data;
2243                 time_t t;
2244
2245                 data = tdb_fetch(cache->tdb, make_tdb_data(cred->name, strlen(cred->name)));
2246                 if (!data.dptr) {
2247                         DEBUG(10,("wcache_remove_oldest_cached_creds: entry for [%s] not found\n", 
2248                                 cred->name));
2249                         status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
2250                         goto done;
2251                 }
2252         
2253                 t = IVAL(data.dptr, 0);
2254                 SAFE_FREE(data.dptr);
2255
2256                 if (!oldest) {
2257                         oldest = SMB_MALLOC_P(struct cred_list);
2258                         if (oldest == NULL) {
2259                                 status = NT_STATUS_NO_MEMORY;
2260                                 goto done;
2261                         }
2262
2263                         fstrcpy(oldest->name, cred->name);
2264                         oldest->created = t;
2265                         continue;
2266                 }
2267
2268                 if (t < oldest->created) {
2269                         fstrcpy(oldest->name, cred->name);
2270                         oldest->created = t;
2271                 }
2272         }
2273
2274         if (tdb_delete(cache->tdb, string_tdb_data(oldest->name)) == 0) {
2275                 status = NT_STATUS_OK;
2276         } else {
2277                 status = NT_STATUS_UNSUCCESSFUL;
2278         }
2279 done:
2280         SAFE_FREE(wcache_cred_list);
2281         SAFE_FREE(oldest);
2282         
2283         return status;
2284 }
2285
2286 /* Change the global online/offline state. */
2287 BOOL set_global_winbindd_state_offline(void)
2288 {
2289         TDB_DATA data;
2290         int err;
2291
2292         DEBUG(10,("set_global_winbindd_state_offline: offline requested.\n"));
2293
2294         /* Only go offline if someone has created
2295            the key "WINBINDD_OFFLINE" in the cache tdb. */
2296
2297         if (wcache == NULL || wcache->tdb == NULL) {
2298                 DEBUG(10,("set_global_winbindd_state_offline: wcache not open yet.\n"));
2299                 return False;
2300         }
2301
2302         if (!lp_winbind_offline_logon()) {
2303                 DEBUG(10,("set_global_winbindd_state_offline: rejecting.\n"));
2304                 return False;
2305         }
2306
2307         if (global_winbindd_offline_state) {
2308                 /* Already offline. */
2309                 return True;
2310         }
2311
2312         wcache->tdb->ecode = 0;
2313
2314         data = tdb_fetch_bystring( wcache->tdb, "WINBINDD_OFFLINE" );
2315
2316         /* As this is a key with no data we don't need to free, we
2317            check for existence by looking at tdb_err. */
2318
2319         err = tdb_error(wcache->tdb);
2320
2321         if (err == TDB_ERR_NOEXIST) {
2322                 DEBUG(10,("set_global_winbindd_state_offline: offline state not set.\n"));
2323                 return False;
2324         } else {
2325                 DEBUG(10,("set_global_winbindd_state_offline: offline state set.\n"));
2326                 global_winbindd_offline_state = True;
2327                 return True;
2328         }
2329 }
2330
2331 void set_global_winbindd_state_online(void)
2332 {
2333         DEBUG(10,("set_global_winbindd_state_online: online requested.\n"));
2334
2335         if (!lp_winbind_offline_logon()) {
2336                 DEBUG(10,("set_global_winbindd_state_online: rejecting.\n"));
2337                 return;
2338         }
2339
2340         if (!global_winbindd_offline_state) {
2341                 /* Already online. */
2342                 return;
2343         }
2344         global_winbindd_offline_state = False;
2345
2346         if (!wcache->tdb) {
2347                 return;
2348         }
2349
2350         /* Ensure there is no key "WINBINDD_OFFLINE" in the cache tdb. */
2351         tdb_delete_bystring(wcache->tdb, "WINBINDD_OFFLINE");
2352 }
2353
2354 /* the cache backend methods are exposed via this structure */
2355 struct winbindd_methods cache_methods = {
2356         True,
2357         query_user_list,
2358         enum_dom_groups,
2359         enum_local_groups,
2360         name_to_sid,
2361         sid_to_name,
2362         query_user,
2363         lookup_usergroups,
2364         lookup_useraliases,
2365         lookup_groupmem,
2366         sequence_number,
2367         lockout_policy,
2368         password_policy,
2369         trusted_domains
2370 };