Merge branch 'nodiscard' of /home/jelmer/samba4
[ira/wip.git] / source3 / winbindd / 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-2007
8    Copyright (C) Volker Lendecke 2005
9    Copyright (C) Guenther Deschner 2005
10    Copyright (C) Michael Adam    2007
11
12    This program is free software; you can redistribute it and/or modify
13    it under the terms of the GNU General Public License as published by
14    the Free Software Foundation; either version 3 of the License, or
15    (at your option) any later version.
16    
17    This program is distributed in the hope that it will be useful,
18    but WITHOUT ANY WARRANTY; without even the implied warranty of
19    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20    GNU General Public License for more details.
21    
22    You should have received a copy of the GNU General Public License
23    along with this program.  If not, see <http://www.gnu.org/licenses/>.
24 */
25
26 #include "includes.h"
27 #include "winbindd.h"
28 #include "tdb_validate.h"
29
30 #undef DBGC_CLASS
31 #define DBGC_CLASS DBGC_WINBIND
32
33 #define WINBINDD_CACHE_VERSION 1
34 #define WINBINDD_CACHE_VERSION_KEYSTR "WINBINDD_CACHE_VERSION"
35
36 extern struct winbindd_methods reconnect_methods;
37 #ifdef HAVE_ADS
38 extern struct winbindd_methods ads_methods;
39 #endif
40 extern struct winbindd_methods builtin_passdb_methods;
41
42 /*
43  * JRA. KEEP THIS LIST UP TO DATE IF YOU ADD CACHE ENTRIES.
44  * Here are the list of entry types that are *not* stored
45  * as form struct cache_entry in the cache.
46  */
47
48 static const char *non_centry_keys[] = {
49         "SEQNUM/",
50         "DR/",
51         "DE/",
52         "WINBINDD_OFFLINE",
53         WINBINDD_CACHE_VERSION_KEYSTR,
54         NULL
55 };
56
57 /************************************************************************
58  Is this key a non-centry type ?
59 ************************************************************************/
60
61 static bool is_non_centry_key(TDB_DATA kbuf)
62 {
63         int i;
64
65         if (kbuf.dptr == NULL || kbuf.dsize == 0) {
66                 return false;
67         }
68         for (i = 0; non_centry_keys[i] != NULL; i++) {
69                 size_t namelen = strlen(non_centry_keys[i]);
70                 if (kbuf.dsize < namelen) {
71                         continue;
72                 }
73                 if (strncmp(non_centry_keys[i], (const char *)kbuf.dptr, namelen) == 0) {
74                         return true;
75                 }
76         }
77         return false;
78 }
79
80 /* Global online/offline state - False when online. winbindd starts up online
81    and sets this to true if the first query fails and there's an entry in
82    the cache tdb telling us to stay offline. */
83
84 static bool global_winbindd_offline_state;
85
86 struct winbind_cache {
87         TDB_CONTEXT *tdb;
88 };
89
90 struct cache_entry {
91         NTSTATUS status;
92         uint32 sequence_number;
93         uint8 *data;
94         uint32 len, ofs;
95 };
96
97 void (*smb_panic_fn)(const char *const why) = smb_panic;
98
99 #define WINBINDD_MAX_CACHE_SIZE (50*1024*1024)
100
101 static struct winbind_cache *wcache;
102
103 void winbindd_check_cache_size(time_t t)
104 {
105         static time_t last_check_time;
106         struct stat st;
107
108         if (last_check_time == (time_t)0)
109                 last_check_time = t;
110
111         if (t - last_check_time < 60 && t - last_check_time > 0)
112                 return;
113
114         if (wcache == NULL || wcache->tdb == NULL) {
115                 DEBUG(0, ("Unable to check size of tdb cache - cache not open !\n"));
116                 return;
117         }
118
119         if (fstat(tdb_fd(wcache->tdb), &st) == -1) {
120                 DEBUG(0, ("Unable to check size of tdb cache %s!\n", strerror(errno) ));
121                 return;
122         }
123
124         if (st.st_size > WINBINDD_MAX_CACHE_SIZE) {
125                 DEBUG(10,("flushing cache due to size (%lu) > (%lu)\n",
126                         (unsigned long)st.st_size,
127                         (unsigned long)WINBINDD_MAX_CACHE_SIZE));
128                 wcache_flush_cache();
129         }
130 }
131
132 /* get the winbind_cache structure */
133 static struct winbind_cache *get_cache(struct winbindd_domain *domain)
134 {
135         struct winbind_cache *ret = wcache;
136
137         /* We have to know what type of domain we are dealing with first. */
138
139         if (domain->internal) {
140                 domain->backend = &builtin_passdb_methods;
141                 domain->initialized = True;
142         }
143         if ( !domain->initialized ) {
144                 init_dc_connection( domain );
145         }
146
147         /* 
148            OK.  listen up becasue I'm only going to say this once.
149            We have the following scenarios to consider
150            (a) trusted AD domains on a Samba DC,
151            (b) trusted AD domains and we are joined to a non-kerberos domain
152            (c) trusted AD domains and we are joined to a kerberos (AD) domain
153
154            For (a) we can always contact the trusted domain using krb5 
155            since we have the domain trust account password
156
157            For (b) we can only use RPC since we have no way of 
158            getting a krb5 ticket in our own domain
159
160            For (c) we can always use krb5 since we have a kerberos trust
161
162            --jerry
163          */
164
165         if (!domain->backend) {
166 #ifdef HAVE_ADS
167                 struct winbindd_domain *our_domain = domain;
168
169                 /* find our domain first so we can figure out if we 
170                    are joined to a kerberized domain */
171
172                 if ( !domain->primary )
173                         our_domain = find_our_domain();
174
175                 if ((our_domain->active_directory || IS_DC)
176                     && domain->active_directory
177                     && !lp_winbind_rpc_only()) {
178                         DEBUG(5,("get_cache: Setting ADS methods for domain %s\n", domain->name));
179                         domain->backend = &ads_methods;
180                 } else {
181 #endif  /* HAVE_ADS */
182                         DEBUG(5,("get_cache: Setting MS-RPC methods for domain %s\n", domain->name));
183                         domain->backend = &reconnect_methods;
184 #ifdef HAVE_ADS
185                 }
186 #endif  /* HAVE_ADS */
187         }
188
189         if (ret)
190                 return ret;
191         
192         ret = SMB_XMALLOC_P(struct winbind_cache);
193         ZERO_STRUCTP(ret);
194
195         wcache = ret;
196         wcache_flush_cache();
197
198         return ret;
199 }
200
201 /*
202   free a centry structure
203 */
204 static void centry_free(struct cache_entry *centry)
205 {
206         if (!centry)
207                 return;
208         SAFE_FREE(centry->data);
209         free(centry);
210 }
211
212 static bool centry_check_bytes(struct cache_entry *centry, size_t nbytes)
213 {
214         if (centry->len - centry->ofs < nbytes) {
215                 DEBUG(0,("centry corruption? needed %u bytes, have %d\n", 
216                          (unsigned int)nbytes,
217                          centry->len - centry->ofs));
218                 return false;
219         }
220         return true;
221 }
222
223 /*
224   pull a uint32 from a cache entry 
225 */
226 static uint32 centry_uint32(struct cache_entry *centry)
227 {
228         uint32 ret;
229
230         if (!centry_check_bytes(centry, 4)) {
231                 smb_panic_fn("centry_uint32");
232         }
233         ret = IVAL(centry->data, centry->ofs);
234         centry->ofs += 4;
235         return ret;
236 }
237
238 /*
239   pull a uint16 from a cache entry 
240 */
241 static uint16 centry_uint16(struct cache_entry *centry)
242 {
243         uint16 ret;
244         if (!centry_check_bytes(centry, 2)) {
245                 smb_panic_fn("centry_uint16");
246         }
247         ret = CVAL(centry->data, centry->ofs);
248         centry->ofs += 2;
249         return ret;
250 }
251
252 /*
253   pull a uint8 from a cache entry 
254 */
255 static uint8 centry_uint8(struct cache_entry *centry)
256 {
257         uint8 ret;
258         if (!centry_check_bytes(centry, 1)) {
259                 smb_panic_fn("centry_uint8");
260         }
261         ret = CVAL(centry->data, centry->ofs);
262         centry->ofs += 1;
263         return ret;
264 }
265
266 /*
267   pull a NTTIME from a cache entry 
268 */
269 static NTTIME centry_nttime(struct cache_entry *centry)
270 {
271         NTTIME ret;
272         if (!centry_check_bytes(centry, 8)) {
273                 smb_panic_fn("centry_nttime");
274         }
275         ret = IVAL(centry->data, centry->ofs);
276         centry->ofs += 4;
277         ret += (uint64_t)IVAL(centry->data, centry->ofs) << 32;
278         centry->ofs += 4;
279         return ret;
280 }
281
282 /*
283   pull a time_t from a cache entry. time_t stored portably as a 64-bit time.
284 */
285 static time_t centry_time(struct cache_entry *centry)
286 {
287         return (time_t)centry_nttime(centry);
288 }
289
290 /* pull a string from a cache entry, using the supplied
291    talloc context 
292 */
293 static char *centry_string(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
294 {
295         uint32 len;
296         char *ret;
297
298         len = centry_uint8(centry);
299
300         if (len == 0xFF) {
301                 /* a deliberate NULL string */
302                 return NULL;
303         }
304
305         if (!centry_check_bytes(centry, (size_t)len)) {
306                 smb_panic_fn("centry_string");
307         }
308
309         ret = TALLOC_ARRAY(mem_ctx, char, len+1);
310         if (!ret) {
311                 smb_panic_fn("centry_string out of memory\n");
312         }
313         memcpy(ret,centry->data + centry->ofs, len);
314         ret[len] = 0;
315         centry->ofs += len;
316         return ret;
317 }
318
319 /* pull a hash16 from a cache entry, using the supplied
320    talloc context 
321 */
322 static char *centry_hash16(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
323 {
324         uint32 len;
325         char *ret;
326
327         len = centry_uint8(centry);
328
329         if (len != 16) {
330                 DEBUG(0,("centry corruption? hash len (%u) != 16\n", 
331                         len ));
332                 return NULL;
333         }
334
335         if (!centry_check_bytes(centry, 16)) {
336                 return NULL;
337         }
338
339         ret = TALLOC_ARRAY(mem_ctx, char, 16);
340         if (!ret) {
341                 smb_panic_fn("centry_hash out of memory\n");
342         }
343         memcpy(ret,centry->data + centry->ofs, 16);
344         centry->ofs += 16;
345         return ret;
346 }
347
348 /* pull a sid from a cache entry, using the supplied
349    talloc context 
350 */
351 static bool centry_sid(struct cache_entry *centry, TALLOC_CTX *mem_ctx, DOM_SID *sid)
352 {
353         char *sid_string;
354         sid_string = centry_string(centry, mem_ctx);
355         if ((sid_string == NULL) || (!string_to_sid(sid, sid_string))) {
356                 return false;
357         }
358         return true;
359 }
360
361
362 /*
363   pull a NTSTATUS from a cache entry
364 */
365 static NTSTATUS centry_ntstatus(struct cache_entry *centry)
366 {
367         NTSTATUS status;
368
369         status = NT_STATUS(centry_uint32(centry));
370         return status;
371 }
372
373
374 /* the server is considered down if it can't give us a sequence number */
375 static bool wcache_server_down(struct winbindd_domain *domain)
376 {
377         bool ret;
378
379         if (!wcache->tdb)
380                 return false;
381
382         ret = (domain->sequence_number == DOM_SEQUENCE_NONE);
383
384         if (ret)
385                 DEBUG(10,("wcache_server_down: server for Domain %s down\n", 
386                         domain->name ));
387         return ret;
388 }
389
390 static NTSTATUS fetch_cache_seqnum( struct winbindd_domain *domain, time_t now )
391 {
392         TDB_DATA data;
393         fstring key;
394         uint32 time_diff;
395         
396         if (!wcache->tdb) {
397                 DEBUG(10,("fetch_cache_seqnum: tdb == NULL\n"));
398                 return NT_STATUS_UNSUCCESSFUL;
399         }
400                 
401         fstr_sprintf( key, "SEQNUM/%s", domain->name );
402         
403         data = tdb_fetch_bystring( wcache->tdb, key );
404         if ( !data.dptr || data.dsize!=8 ) {
405                 DEBUG(10,("fetch_cache_seqnum: invalid data size key [%s]\n", key ));
406                 return NT_STATUS_UNSUCCESSFUL;
407         }
408         
409         domain->sequence_number = IVAL(data.dptr, 0);
410         domain->last_seq_check  = IVAL(data.dptr, 4);
411         
412         SAFE_FREE(data.dptr);
413
414         /* have we expired? */
415         
416         time_diff = now - domain->last_seq_check;
417         if ( time_diff > lp_winbind_cache_time() ) {
418                 DEBUG(10,("fetch_cache_seqnum: timeout [%s][%u @ %u]\n",
419                         domain->name, domain->sequence_number,
420                         (uint32)domain->last_seq_check));
421                 return NT_STATUS_UNSUCCESSFUL;
422         }
423
424         DEBUG(10,("fetch_cache_seqnum: success [%s][%u @ %u]\n", 
425                 domain->name, domain->sequence_number, 
426                 (uint32)domain->last_seq_check));
427
428         return NT_STATUS_OK;
429 }
430
431 static NTSTATUS store_cache_seqnum( struct winbindd_domain *domain )
432 {
433         TDB_DATA data;
434         fstring key_str;
435         uint8 buf[8];
436         
437         if (!wcache->tdb) {
438                 DEBUG(10,("store_cache_seqnum: tdb == NULL\n"));
439                 return NT_STATUS_UNSUCCESSFUL;
440         }
441                 
442         fstr_sprintf( key_str, "SEQNUM/%s", domain->name );
443         
444         SIVAL(buf, 0, domain->sequence_number);
445         SIVAL(buf, 4, domain->last_seq_check);
446         data.dptr = buf;
447         data.dsize = 8;
448         
449         if ( tdb_store_bystring( wcache->tdb, key_str, data, TDB_REPLACE) == -1 ) {
450                 DEBUG(10,("store_cache_seqnum: tdb_store fail key [%s]\n", key_str ));
451                 return NT_STATUS_UNSUCCESSFUL;
452         }
453
454         DEBUG(10,("store_cache_seqnum: success [%s][%u @ %u]\n", 
455                 domain->name, domain->sequence_number, 
456                 (uint32)domain->last_seq_check));
457         
458         return NT_STATUS_OK;
459 }
460
461 /*
462   refresh the domain sequence number. If force is true
463   then always refresh it, no matter how recently we fetched it
464 */
465
466 static void refresh_sequence_number(struct winbindd_domain *domain, bool force)
467 {
468         NTSTATUS status;
469         unsigned time_diff;
470         time_t t = time(NULL);
471         unsigned cache_time = lp_winbind_cache_time();
472
473         if ( IS_DOMAIN_OFFLINE(domain) ) {
474                 return;
475         }
476         
477         get_cache( domain );
478
479 #if 0   /* JERRY -- disable as the default cache time is now 5 minutes */
480         /* trying to reconnect is expensive, don't do it too often */
481         if (domain->sequence_number == DOM_SEQUENCE_NONE) {
482                 cache_time *= 8;
483         }
484 #endif
485
486         time_diff = t - domain->last_seq_check;
487
488         /* see if we have to refetch the domain sequence number */
489         if (!force && (time_diff < cache_time) &&
490                         (domain->sequence_number != DOM_SEQUENCE_NONE) &&
491                         NT_STATUS_IS_OK(domain->last_status)) {
492                 DEBUG(10, ("refresh_sequence_number: %s time ok\n", domain->name));
493                 goto done;
494         }
495         
496         /* try to get the sequence number from the tdb cache first */
497         /* this will update the timestamp as well */
498         
499         status = fetch_cache_seqnum( domain, t );
500         if (NT_STATUS_IS_OK(status) &&
501                         (domain->sequence_number != DOM_SEQUENCE_NONE) &&
502                         NT_STATUS_IS_OK(domain->last_status)) {
503                 goto done;
504         }
505
506         /* important! make sure that we know if this is a native 
507            mode domain or not.  And that we can contact it. */
508
509         if ( winbindd_can_contact_domain( domain ) ) {          
510                 status = domain->backend->sequence_number(domain, 
511                                                           &domain->sequence_number);
512         } else {
513                 /* just use the current time */
514                 status = NT_STATUS_OK;
515                 domain->sequence_number = time(NULL);
516         }
517
518
519         /* the above call could have set our domain->backend to NULL when
520          * coming from offline to online mode, make sure to reinitialize the
521          * backend - Guenther */
522         get_cache( domain );
523
524         if (!NT_STATUS_IS_OK(status)) {
525                 DEBUG(10,("refresh_sequence_number: failed with %s\n", nt_errstr(status)));
526                 domain->sequence_number = DOM_SEQUENCE_NONE;
527         }
528         
529         domain->last_status = status;
530         domain->last_seq_check = time(NULL);
531         
532         /* save the new sequence number in the cache */
533         store_cache_seqnum( domain );
534
535 done:
536         DEBUG(10, ("refresh_sequence_number: %s seq number is now %d\n", 
537                    domain->name, domain->sequence_number));
538
539         return;
540 }
541
542 /*
543   decide if a cache entry has expired
544 */
545 static bool centry_expired(struct winbindd_domain *domain, const char *keystr, struct cache_entry *centry)
546 {
547         /* If we've been told to be offline - stay in that state... */
548         if (lp_winbind_offline_logon() && global_winbindd_offline_state) {
549                 DEBUG(10,("centry_expired: Key %s for domain %s valid as winbindd is globally offline.\n",
550                         keystr, domain->name ));
551                 return false;
552         }
553
554         /* when the domain is offline return the cached entry.
555          * This deals with transient offline states... */
556
557         if (!domain->online) {
558                 DEBUG(10,("centry_expired: Key %s for domain %s valid as domain is offline.\n",
559                         keystr, domain->name ));
560                 return false;
561         }
562
563         /* if the server is OK and our cache entry came from when it was down then
564            the entry is invalid */
565         if ((domain->sequence_number != DOM_SEQUENCE_NONE) &&  
566             (centry->sequence_number == DOM_SEQUENCE_NONE)) {
567                 DEBUG(10,("centry_expired: Key %s for domain %s invalid sequence.\n",
568                         keystr, domain->name ));
569                 return true;
570         }
571
572         /* if the server is down or the cache entry is not older than the
573            current sequence number then it is OK */
574         if (wcache_server_down(domain) || 
575             centry->sequence_number == domain->sequence_number) {
576                 DEBUG(10,("centry_expired: Key %s for domain %s is good.\n",
577                         keystr, domain->name ));
578                 return false;
579         }
580
581         DEBUG(10,("centry_expired: Key %s for domain %s expired\n",
582                 keystr, domain->name ));
583
584         /* it's expired */
585         return true;
586 }
587
588 static struct cache_entry *wcache_fetch_raw(char *kstr)
589 {
590         TDB_DATA data;
591         struct cache_entry *centry;
592         TDB_DATA key;
593
594         key = string_tdb_data(kstr);
595         data = tdb_fetch(wcache->tdb, key);
596         if (!data.dptr) {
597                 /* a cache miss */
598                 return NULL;
599         }
600
601         centry = SMB_XMALLOC_P(struct cache_entry);
602         centry->data = (unsigned char *)data.dptr;
603         centry->len = data.dsize;
604         centry->ofs = 0;
605
606         if (centry->len < 8) {
607                 /* huh? corrupt cache? */
608                 DEBUG(10,("wcache_fetch_raw: Corrupt cache for key %s (len < 8) ?\n", kstr));
609                 centry_free(centry);
610                 return NULL;
611         }
612         
613         centry->status = centry_ntstatus(centry);
614         centry->sequence_number = centry_uint32(centry);
615
616         return centry;
617 }
618
619 /*
620   fetch an entry from the cache, with a varargs key. auto-fetch the sequence
621   number and return status
622 */
623 static struct cache_entry *wcache_fetch(struct winbind_cache *cache, 
624                                         struct winbindd_domain *domain,
625                                         const char *format, ...) PRINTF_ATTRIBUTE(3,4);
626 static struct cache_entry *wcache_fetch(struct winbind_cache *cache, 
627                                         struct winbindd_domain *domain,
628                                         const char *format, ...)
629 {
630         va_list ap;
631         char *kstr;
632         struct cache_entry *centry;
633
634         if (!winbindd_use_cache()) {
635                 return NULL;
636         }
637
638         refresh_sequence_number(domain, false);
639
640         va_start(ap, format);
641         smb_xvasprintf(&kstr, format, ap);
642         va_end(ap);
643
644         centry = wcache_fetch_raw(kstr);
645         if (centry == NULL) {
646                 free(kstr);
647                 return NULL;
648         }
649
650         if (centry_expired(domain, kstr, centry)) {
651
652                 DEBUG(10,("wcache_fetch: entry %s expired for domain %s\n",
653                          kstr, domain->name ));
654
655                 centry_free(centry);
656                 free(kstr);
657                 return NULL;
658         }
659
660         DEBUG(10,("wcache_fetch: returning entry %s for domain %s\n",
661                  kstr, domain->name ));
662
663         free(kstr);
664         return centry;
665 }
666
667 static void wcache_delete(const char *format, ...) PRINTF_ATTRIBUTE(1,2);
668 static void wcache_delete(const char *format, ...)
669 {
670         va_list ap;
671         char *kstr;
672         TDB_DATA key;
673
674         va_start(ap, format);
675         smb_xvasprintf(&kstr, format, ap);
676         va_end(ap);
677
678         key = string_tdb_data(kstr);
679
680         tdb_delete(wcache->tdb, key);
681         free(kstr);
682 }
683
684 /*
685   make sure we have at least len bytes available in a centry 
686 */
687 static void centry_expand(struct cache_entry *centry, uint32 len)
688 {
689         if (centry->len - centry->ofs >= len)
690                 return;
691         centry->len *= 2;
692         centry->data = SMB_REALLOC_ARRAY(centry->data, unsigned char,
693                                          centry->len);
694         if (!centry->data) {
695                 DEBUG(0,("out of memory: needed %d bytes in centry_expand\n", centry->len));
696                 smb_panic_fn("out of memory in centry_expand");
697         }
698 }
699
700 /*
701   push a uint32 into a centry 
702 */
703 static void centry_put_uint32(struct cache_entry *centry, uint32 v)
704 {
705         centry_expand(centry, 4);
706         SIVAL(centry->data, centry->ofs, v);
707         centry->ofs += 4;
708 }
709
710 /*
711   push a uint16 into a centry 
712 */
713 static void centry_put_uint16(struct cache_entry *centry, uint16 v)
714 {
715         centry_expand(centry, 2);
716         SIVAL(centry->data, centry->ofs, v);
717         centry->ofs += 2;
718 }
719
720 /*
721   push a uint8 into a centry 
722 */
723 static void centry_put_uint8(struct cache_entry *centry, uint8 v)
724 {
725         centry_expand(centry, 1);
726         SCVAL(centry->data, centry->ofs, v);
727         centry->ofs += 1;
728 }
729
730 /* 
731    push a string into a centry 
732  */
733 static void centry_put_string(struct cache_entry *centry, const char *s)
734 {
735         int len;
736
737         if (!s) {
738                 /* null strings are marked as len 0xFFFF */
739                 centry_put_uint8(centry, 0xFF);
740                 return;
741         }
742
743         len = strlen(s);
744         /* can't handle more than 254 char strings. Truncating is probably best */
745         if (len > 254) {
746                 DEBUG(10,("centry_put_string: truncating len (%d) to: 254\n", len));
747                 len = 254;
748         }
749         centry_put_uint8(centry, len);
750         centry_expand(centry, len);
751         memcpy(centry->data + centry->ofs, s, len);
752         centry->ofs += len;
753 }
754
755 /* 
756    push a 16 byte hash into a centry - treat as 16 byte string.
757  */
758 static void centry_put_hash16(struct cache_entry *centry, const uint8 val[16])
759 {
760         centry_put_uint8(centry, 16);
761         centry_expand(centry, 16);
762         memcpy(centry->data + centry->ofs, val, 16);
763         centry->ofs += 16;
764 }
765
766 static void centry_put_sid(struct cache_entry *centry, const DOM_SID *sid) 
767 {
768         fstring sid_string;
769         centry_put_string(centry, sid_to_fstring(sid_string, sid));
770 }
771
772
773 /*
774   put NTSTATUS into a centry
775 */
776 static void centry_put_ntstatus(struct cache_entry *centry, NTSTATUS status)
777 {
778         uint32 status_value = NT_STATUS_V(status);
779         centry_put_uint32(centry, status_value);
780 }
781
782
783 /*
784   push a NTTIME into a centry 
785 */
786 static void centry_put_nttime(struct cache_entry *centry, NTTIME nt)
787 {
788         centry_expand(centry, 8);
789         SIVAL(centry->data, centry->ofs, nt & 0xFFFFFFFF);
790         centry->ofs += 4;
791         SIVAL(centry->data, centry->ofs, nt >> 32);
792         centry->ofs += 4;
793 }
794
795 /*
796   push a time_t into a centry - use a 64 bit size.
797   NTTIME here is being used as a convenient 64-bit size.
798 */
799 static void centry_put_time(struct cache_entry *centry, time_t t)
800 {
801         NTTIME nt = (NTTIME)t;
802         centry_put_nttime(centry, nt);
803 }
804
805 /*
806   start a centry for output. When finished, call centry_end()
807 */
808 struct cache_entry *centry_start(struct winbindd_domain *domain, NTSTATUS status)
809 {
810         struct cache_entry *centry;
811
812         if (!wcache->tdb)
813                 return NULL;
814
815         centry = SMB_XMALLOC_P(struct cache_entry);
816
817         centry->len = 8192; /* reasonable default */
818         centry->data = SMB_XMALLOC_ARRAY(uint8, centry->len);
819         centry->ofs = 0;
820         centry->sequence_number = domain->sequence_number;
821         centry_put_ntstatus(centry, status);
822         centry_put_uint32(centry, centry->sequence_number);
823         return centry;
824 }
825
826 /*
827   finish a centry and write it to the tdb
828 */
829 static void centry_end(struct cache_entry *centry, const char *format, ...) PRINTF_ATTRIBUTE(2,3);
830 static void centry_end(struct cache_entry *centry, const char *format, ...)
831 {
832         va_list ap;
833         char *kstr;
834         TDB_DATA key, data;
835
836         if (!winbindd_use_cache()) {
837                 return;
838         }
839
840         va_start(ap, format);
841         smb_xvasprintf(&kstr, format, ap);
842         va_end(ap);
843
844         key = string_tdb_data(kstr);
845         data.dptr = centry->data;
846         data.dsize = centry->ofs;
847
848         tdb_store(wcache->tdb, key, data, TDB_REPLACE);
849         free(kstr);
850 }
851
852 static void wcache_save_name_to_sid(struct winbindd_domain *domain, 
853                                     NTSTATUS status, const char *domain_name,
854                                     const char *name, const DOM_SID *sid, 
855                                     enum lsa_SidType type)
856 {
857         struct cache_entry *centry;
858         fstring uname;
859
860         centry = centry_start(domain, status);
861         if (!centry)
862                 return;
863         centry_put_uint32(centry, type);
864         centry_put_sid(centry, sid);
865         fstrcpy(uname, name);
866         strupper_m(uname);
867         centry_end(centry, "NS/%s/%s", domain_name, uname);
868         DEBUG(10,("wcache_save_name_to_sid: %s\\%s -> %s (%s)\n", domain_name,
869                   uname, sid_string_dbg(sid), nt_errstr(status)));
870         centry_free(centry);
871 }
872
873 static void wcache_save_sid_to_name(struct winbindd_domain *domain, NTSTATUS status, 
874                                     const DOM_SID *sid, const char *domain_name, const char *name, enum lsa_SidType type)
875 {
876         struct cache_entry *centry;
877         fstring sid_string;
878
879         centry = centry_start(domain, status);
880         if (!centry)
881                 return;
882
883         if (NT_STATUS_IS_OK(status)) {
884                 centry_put_uint32(centry, type);
885                 centry_put_string(centry, domain_name);
886                 centry_put_string(centry, name);
887         }
888
889         centry_end(centry, "SN/%s", sid_to_fstring(sid_string, sid));
890         DEBUG(10,("wcache_save_sid_to_name: %s -> %s (%s)\n", sid_string, 
891                   name, nt_errstr(status)));
892         centry_free(centry);
893 }
894
895
896 static void wcache_save_user(struct winbindd_domain *domain, NTSTATUS status, WINBIND_USERINFO *info)
897 {
898         struct cache_entry *centry;
899         fstring sid_string;
900
901         if (is_null_sid(&info->user_sid)) {
902                 return;
903         }
904
905         centry = centry_start(domain, status);
906         if (!centry)
907                 return;
908         centry_put_string(centry, info->acct_name);
909         centry_put_string(centry, info->full_name);
910         centry_put_string(centry, info->homedir);
911         centry_put_string(centry, info->shell);
912         centry_put_uint32(centry, info->primary_gid);
913         centry_put_sid(centry, &info->user_sid);
914         centry_put_sid(centry, &info->group_sid);
915         centry_end(centry, "U/%s", sid_to_fstring(sid_string,
916                                                   &info->user_sid));
917         DEBUG(10,("wcache_save_user: %s (acct_name %s)\n", sid_string, info->acct_name));
918         centry_free(centry);
919 }
920
921 static void wcache_save_lockout_policy(struct winbindd_domain *domain,
922                                        NTSTATUS status,
923                                        struct samr_DomInfo12 *lockout_policy)
924 {
925         struct cache_entry *centry;
926
927         centry = centry_start(domain, status);
928         if (!centry)
929                 return;
930
931         centry_put_nttime(centry, lockout_policy->lockout_duration);
932         centry_put_nttime(centry, lockout_policy->lockout_window);
933         centry_put_uint16(centry, lockout_policy->lockout_threshold);
934
935         centry_end(centry, "LOC_POL/%s", domain->name);
936
937         DEBUG(10,("wcache_save_lockout_policy: %s\n", domain->name));
938
939         centry_free(centry);
940 }
941
942
943
944 static void wcache_save_password_policy(struct winbindd_domain *domain,
945                                         NTSTATUS status,
946                                         struct samr_DomInfo1 *policy)
947 {
948         struct cache_entry *centry;
949
950         centry = centry_start(domain, status);
951         if (!centry)
952                 return;
953
954         centry_put_uint16(centry, policy->min_password_length);
955         centry_put_uint16(centry, policy->password_history_length);
956         centry_put_uint32(centry, policy->password_properties);
957         centry_put_nttime(centry, policy->max_password_age);
958         centry_put_nttime(centry, policy->min_password_age);
959
960         centry_end(centry, "PWD_POL/%s", domain->name);
961
962         DEBUG(10,("wcache_save_password_policy: %s\n", domain->name));
963
964         centry_free(centry);
965 }
966
967 /***************************************************************************
968  ***************************************************************************/
969
970 static void wcache_save_username_alias(struct winbindd_domain *domain,
971                                        NTSTATUS status,
972                                        const char *name, const char *alias)
973 {
974         struct cache_entry *centry;
975         fstring uname;
976
977         if ( (centry = centry_start(domain, status)) == NULL )
978                 return;
979
980         centry_put_string( centry, alias );
981
982         fstrcpy(uname, name);
983         strupper_m(uname);
984         centry_end(centry, "NSS/NA/%s", uname);
985
986         DEBUG(10,("wcache_save_username_alias: %s -> %s\n", name, alias ));
987
988         centry_free(centry);
989 }
990
991 static void wcache_save_alias_username(struct winbindd_domain *domain,
992                                        NTSTATUS status,
993                                        const char *alias, const char *name)
994 {
995         struct cache_entry *centry;
996         fstring uname;
997
998         if ( (centry = centry_start(domain, status)) == NULL )
999                 return;
1000
1001         centry_put_string( centry, name );
1002
1003         fstrcpy(uname, alias);
1004         strupper_m(uname);
1005         centry_end(centry, "NSS/AN/%s", uname);
1006
1007         DEBUG(10,("wcache_save_alias_username: %s -> %s\n", alias, name ));
1008
1009         centry_free(centry);
1010 }
1011
1012 /***************************************************************************
1013  ***************************************************************************/
1014
1015 NTSTATUS resolve_username_to_alias( TALLOC_CTX *mem_ctx,
1016                                     struct winbindd_domain *domain,
1017                                     const char *name, char **alias )
1018 {
1019         struct winbind_cache *cache = get_cache(domain);
1020         struct cache_entry *centry = NULL;
1021         NTSTATUS status;
1022         char *upper_name;
1023
1024         if ( domain->internal )
1025                 return NT_STATUS_NOT_SUPPORTED;
1026
1027         if (!cache->tdb)
1028                 goto do_query;
1029
1030         if ( (upper_name = SMB_STRDUP(name)) == NULL )
1031                 return NT_STATUS_NO_MEMORY;
1032         strupper_m(upper_name);
1033
1034         centry = wcache_fetch(cache, domain, "NSS/NA/%s", upper_name);
1035
1036         SAFE_FREE( upper_name );
1037
1038         if (!centry)
1039                 goto do_query;
1040
1041         status = centry->status;
1042
1043         if (!NT_STATUS_IS_OK(status)) {
1044                 centry_free(centry);
1045                 return status;
1046         }
1047
1048         *alias = centry_string( centry, mem_ctx );
1049
1050         centry_free(centry);
1051
1052         DEBUG(10,("resolve_username_to_alias: [Cached] - mapped %s to %s\n",
1053                   name, *alias ? *alias : "(none)"));
1054
1055         return (*alias) ? NT_STATUS_OK : NT_STATUS_OBJECT_NAME_NOT_FOUND;
1056
1057 do_query:
1058
1059         /* If its not in cache and we are offline, then fail */
1060
1061         if ( get_global_winbindd_state_offline() || !domain->online ) {
1062                 DEBUG(8,("resolve_username_to_alias: rejecting query "
1063                          "in offline mode\n"));
1064                 return NT_STATUS_NOT_FOUND;
1065         }
1066
1067         status = nss_map_to_alias( mem_ctx, domain->name, name, alias );
1068
1069         if ( NT_STATUS_IS_OK( status ) ) {
1070                 wcache_save_username_alias(domain, status, name, *alias);
1071         }
1072
1073         if ( NT_STATUS_EQUAL( status, NT_STATUS_NONE_MAPPED ) ) {
1074                 wcache_save_username_alias(domain, status, name, "(NULL)");
1075         }
1076
1077         DEBUG(5,("resolve_username_to_alias: backend query returned %s\n",
1078                  nt_errstr(status)));
1079
1080         if ( NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ) {
1081                 set_domain_offline( domain );
1082         }
1083
1084         return status;
1085 }
1086
1087 /***************************************************************************
1088  ***************************************************************************/
1089
1090 NTSTATUS resolve_alias_to_username( TALLOC_CTX *mem_ctx,
1091                                     struct winbindd_domain *domain,
1092                                     const char *alias, char **name )
1093 {
1094         struct winbind_cache *cache = get_cache(domain);
1095         struct cache_entry *centry = NULL;
1096         NTSTATUS status;
1097         char *upper_name;
1098
1099         if ( domain->internal )
1100                 return  NT_STATUS_NOT_SUPPORTED;
1101
1102         if (!cache->tdb)
1103                 goto do_query;
1104
1105         if ( (upper_name = SMB_STRDUP(alias)) == NULL )
1106                 return NT_STATUS_NO_MEMORY;
1107         strupper_m(upper_name);
1108
1109         centry = wcache_fetch(cache, domain, "NSS/AN/%s", upper_name);
1110
1111         SAFE_FREE( upper_name );
1112
1113         if (!centry)
1114                 goto do_query;
1115
1116         status = centry->status;
1117
1118         if (!NT_STATUS_IS_OK(status)) {
1119                 centry_free(centry);
1120                 return status;
1121         }
1122
1123         *name = centry_string( centry, mem_ctx );
1124
1125         centry_free(centry);
1126
1127         DEBUG(10,("resolve_alias_to_username: [Cached] - mapped %s to %s\n",
1128                   alias, *name ? *name : "(none)"));
1129
1130         return (*name) ? NT_STATUS_OK : NT_STATUS_OBJECT_NAME_NOT_FOUND;
1131
1132 do_query:
1133
1134         /* If its not in cache and we are offline, then fail */
1135
1136         if ( get_global_winbindd_state_offline() || !domain->online ) {
1137                 DEBUG(8,("resolve_alias_to_username: rejecting query "
1138                          "in offline mode\n"));
1139                 return NT_STATUS_NOT_FOUND;
1140         }
1141
1142         /* an alias cannot contain a domain prefix or '@' */
1143
1144         if (strchr(alias, '\\') || strchr(alias, '@')) {
1145                 DEBUG(10,("resolve_alias_to_username: skipping fully "
1146                           "qualified name %s\n", alias));
1147                 return NT_STATUS_OBJECT_NAME_INVALID;
1148         }
1149
1150         status = nss_map_from_alias( mem_ctx, domain->name, alias, name );
1151
1152         if ( NT_STATUS_IS_OK( status ) ) {
1153                 wcache_save_alias_username( domain, status, alias, *name );
1154         }
1155
1156         if (NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) {
1157                 wcache_save_alias_username(domain, status, alias, "(NULL)");
1158         }
1159
1160         DEBUG(5,("resolve_alias_to_username: backend query returned %s\n",
1161                  nt_errstr(status)));
1162
1163         if ( NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ) {
1164                 set_domain_offline( domain );
1165         }
1166
1167         return status;
1168 }
1169
1170 NTSTATUS wcache_cached_creds_exist(struct winbindd_domain *domain, const DOM_SID *sid)
1171 {
1172         struct winbind_cache *cache = get_cache(domain);
1173         TDB_DATA data;
1174         fstring key_str, tmp;
1175         uint32 rid;
1176
1177         if (!cache->tdb) {
1178                 return NT_STATUS_INTERNAL_DB_ERROR;
1179         }
1180
1181         if (is_null_sid(sid)) {
1182                 return NT_STATUS_INVALID_SID;
1183         }
1184
1185         if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1186                 return NT_STATUS_INVALID_SID;
1187         }
1188
1189         fstr_sprintf(key_str, "CRED/%s", sid_to_fstring(tmp, sid));
1190
1191         data = tdb_fetch(cache->tdb, string_tdb_data(key_str));
1192         if (!data.dptr) {
1193                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1194         }
1195
1196         SAFE_FREE(data.dptr);
1197         return NT_STATUS_OK;
1198 }
1199
1200 /* Lookup creds for a SID - copes with old (unsalted) creds as well
1201    as new salted ones. */
1202
1203 NTSTATUS wcache_get_creds(struct winbindd_domain *domain, 
1204                           TALLOC_CTX *mem_ctx, 
1205                           const DOM_SID *sid,
1206                           const uint8 **cached_nt_pass,
1207                           const uint8 **cached_salt)
1208 {
1209         struct winbind_cache *cache = get_cache(domain);
1210         struct cache_entry *centry = NULL;
1211         NTSTATUS status;
1212         time_t t;
1213         uint32 rid;
1214         fstring tmp;
1215
1216         if (!cache->tdb) {
1217                 return NT_STATUS_INTERNAL_DB_ERROR;
1218         }
1219
1220         if (is_null_sid(sid)) {
1221                 return NT_STATUS_INVALID_SID;
1222         }
1223
1224         if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1225                 return NT_STATUS_INVALID_SID;
1226         }
1227
1228         /* Try and get a salted cred first. If we can't
1229            fall back to an unsalted cred. */
1230
1231         centry = wcache_fetch(cache, domain, "CRED/%s",
1232                               sid_to_fstring(tmp, sid));
1233         if (!centry) {
1234                 DEBUG(10,("wcache_get_creds: entry for [CRED/%s] not found\n", 
1235                           sid_string_dbg(sid)));
1236                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1237         }
1238
1239         t = centry_time(centry);
1240
1241         /* In the salted case this isn't actually the nt_hash itself,
1242            but the MD5 of the salt + nt_hash. Let the caller
1243            sort this out. It can tell as we only return the cached_salt
1244            if we are returning a salted cred. */
1245
1246         *cached_nt_pass = (const uint8 *)centry_hash16(centry, mem_ctx);
1247         if (*cached_nt_pass == NULL) {
1248                 fstring sidstr;
1249
1250                 sid_to_fstring(sidstr, sid);
1251
1252                 /* Bad (old) cred cache. Delete and pretend we
1253                    don't have it. */
1254                 DEBUG(0,("wcache_get_creds: bad entry for [CRED/%s] - deleting\n", 
1255                                 sidstr));
1256                 wcache_delete("CRED/%s", sidstr);
1257                 centry_free(centry);
1258                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1259         }
1260
1261         /* We only have 17 bytes more data in the salted cred case. */
1262         if (centry->len - centry->ofs == 17) {
1263                 *cached_salt = (const uint8 *)centry_hash16(centry, mem_ctx);
1264         } else {
1265                 *cached_salt = NULL;
1266         }
1267
1268         dump_data_pw("cached_nt_pass", *cached_nt_pass, NT_HASH_LEN);
1269         if (*cached_salt) {
1270                 dump_data_pw("cached_salt", *cached_salt, NT_HASH_LEN);
1271         }
1272
1273         status = centry->status;
1274
1275         DEBUG(10,("wcache_get_creds: [Cached] - cached creds for user %s status: %s\n",
1276                   sid_string_dbg(sid), nt_errstr(status) ));
1277
1278         centry_free(centry);
1279         return status;
1280 }
1281
1282 /* Store creds for a SID - only writes out new salted ones. */
1283
1284 NTSTATUS wcache_save_creds(struct winbindd_domain *domain, 
1285                            TALLOC_CTX *mem_ctx, 
1286                            const DOM_SID *sid, 
1287                            const uint8 nt_pass[NT_HASH_LEN])
1288 {
1289         struct cache_entry *centry;
1290         fstring sid_string;
1291         uint32 rid;
1292         uint8 cred_salt[NT_HASH_LEN];
1293         uint8 salted_hash[NT_HASH_LEN];
1294
1295         if (is_null_sid(sid)) {
1296                 return NT_STATUS_INVALID_SID;
1297         }
1298
1299         if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1300                 return NT_STATUS_INVALID_SID;
1301         }
1302
1303         centry = centry_start(domain, NT_STATUS_OK);
1304         if (!centry) {
1305                 return NT_STATUS_INTERNAL_DB_ERROR;
1306         }
1307
1308         dump_data_pw("nt_pass", nt_pass, NT_HASH_LEN);
1309
1310         centry_put_time(centry, time(NULL));
1311
1312         /* Create a salt and then salt the hash. */
1313         generate_random_buffer(cred_salt, NT_HASH_LEN);
1314         E_md5hash(cred_salt, nt_pass, salted_hash);
1315
1316         centry_put_hash16(centry, salted_hash);
1317         centry_put_hash16(centry, cred_salt);
1318         centry_end(centry, "CRED/%s", sid_to_fstring(sid_string, sid));
1319
1320         DEBUG(10,("wcache_save_creds: %s\n", sid_string));
1321
1322         centry_free(centry);
1323
1324         return NT_STATUS_OK;
1325 }
1326
1327
1328 /* Query display info. This is the basic user list fn */
1329 static NTSTATUS query_user_list(struct winbindd_domain *domain,
1330                                 TALLOC_CTX *mem_ctx,
1331                                 uint32 *num_entries, 
1332                                 WINBIND_USERINFO **info)
1333 {
1334         struct winbind_cache *cache = get_cache(domain);
1335         struct cache_entry *centry = NULL;
1336         NTSTATUS status;
1337         unsigned int i, retry;
1338
1339         if (!cache->tdb)
1340                 goto do_query;
1341
1342         centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
1343         if (!centry)
1344                 goto do_query;
1345
1346         *num_entries = centry_uint32(centry);
1347         
1348         if (*num_entries == 0)
1349                 goto do_cached;
1350
1351         (*info) = TALLOC_ARRAY(mem_ctx, WINBIND_USERINFO, *num_entries);
1352         if (! (*info)) {
1353                 smb_panic_fn("query_user_list out of memory");
1354         }
1355         for (i=0; i<(*num_entries); i++) {
1356                 (*info)[i].acct_name = centry_string(centry, mem_ctx);
1357                 (*info)[i].full_name = centry_string(centry, mem_ctx);
1358                 (*info)[i].homedir = centry_string(centry, mem_ctx);
1359                 (*info)[i].shell = centry_string(centry, mem_ctx);
1360                 centry_sid(centry, mem_ctx, &(*info)[i].user_sid);
1361                 centry_sid(centry, mem_ctx, &(*info)[i].group_sid);
1362         }
1363
1364 do_cached:      
1365         status = centry->status;
1366
1367         DEBUG(10,("query_user_list: [Cached] - cached list for domain %s status: %s\n",
1368                 domain->name, nt_errstr(status) ));
1369
1370         centry_free(centry);
1371         return status;
1372
1373 do_query:
1374         *num_entries = 0;
1375         *info = NULL;
1376
1377         /* Return status value returned by seq number check */
1378
1379         if (!NT_STATUS_IS_OK(domain->last_status))
1380                 return domain->last_status;
1381
1382         /* Put the query_user_list() in a retry loop.  There appears to be
1383          * some bug either with Windows 2000 or Samba's handling of large
1384          * rpc replies.  This manifests itself as sudden disconnection
1385          * at a random point in the enumeration of a large (60k) user list.
1386          * The retry loop simply tries the operation again. )-:  It's not
1387          * pretty but an acceptable workaround until we work out what the
1388          * real problem is. */
1389
1390         retry = 0;
1391         do {
1392
1393                 DEBUG(10,("query_user_list: [Cached] - doing backend query for list for domain %s\n",
1394                         domain->name ));
1395
1396                 status = domain->backend->query_user_list(domain, mem_ctx, num_entries, info);
1397                 if (!NT_STATUS_IS_OK(status)) {
1398                         DEBUG(3, ("query_user_list: returned 0x%08x, "
1399                                   "retrying\n", NT_STATUS_V(status)));
1400                 }
1401                 if (NT_STATUS_EQUAL(status, NT_STATUS_UNSUCCESSFUL)) {
1402                         DEBUG(3, ("query_user_list: flushing "
1403                                   "connection cache\n"));
1404                         invalidate_cm_connection(&domain->conn);
1405                 }
1406
1407         } while (NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_UNSUCCESSFUL) && 
1408                  (retry++ < 5));
1409
1410         /* and save it */
1411         refresh_sequence_number(domain, false);
1412         centry = centry_start(domain, status);
1413         if (!centry)
1414                 goto skip_save;
1415         centry_put_uint32(centry, *num_entries);
1416         for (i=0; i<(*num_entries); i++) {
1417                 centry_put_string(centry, (*info)[i].acct_name);
1418                 centry_put_string(centry, (*info)[i].full_name);
1419                 centry_put_string(centry, (*info)[i].homedir);
1420                 centry_put_string(centry, (*info)[i].shell);
1421                 centry_put_sid(centry, &(*info)[i].user_sid);
1422                 centry_put_sid(centry, &(*info)[i].group_sid);
1423                 if (domain->backend && domain->backend->consistent) {
1424                         /* when the backend is consistent we can pre-prime some mappings */
1425                         wcache_save_name_to_sid(domain, NT_STATUS_OK, 
1426                                                 domain->name,
1427                                                 (*info)[i].acct_name, 
1428                                                 &(*info)[i].user_sid,
1429                                                 SID_NAME_USER);
1430                         wcache_save_sid_to_name(domain, NT_STATUS_OK, 
1431                                                 &(*info)[i].user_sid,
1432                                                 domain->name,
1433                                                 (*info)[i].acct_name, 
1434                                                 SID_NAME_USER);
1435                         wcache_save_user(domain, NT_STATUS_OK, &(*info)[i]);
1436                 }
1437         }       
1438         centry_end(centry, "UL/%s", domain->name);
1439         centry_free(centry);
1440
1441 skip_save:
1442         return status;
1443 }
1444
1445 /* list all domain groups */
1446 static NTSTATUS enum_dom_groups(struct winbindd_domain *domain,
1447                                 TALLOC_CTX *mem_ctx,
1448                                 uint32 *num_entries, 
1449                                 struct acct_info **info)
1450 {
1451         struct winbind_cache *cache = get_cache(domain);
1452         struct cache_entry *centry = NULL;
1453         NTSTATUS status;
1454         unsigned int i;
1455
1456         if (!cache->tdb)
1457                 goto do_query;
1458
1459         centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
1460         if (!centry)
1461                 goto do_query;
1462
1463         *num_entries = centry_uint32(centry);
1464         
1465         if (*num_entries == 0)
1466                 goto do_cached;
1467
1468         (*info) = TALLOC_ARRAY(mem_ctx, struct acct_info, *num_entries);
1469         if (! (*info)) {
1470                 smb_panic_fn("enum_dom_groups out of memory");
1471         }
1472         for (i=0; i<(*num_entries); i++) {
1473                 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1474                 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1475                 (*info)[i].rid = centry_uint32(centry);
1476         }
1477
1478 do_cached:      
1479         status = centry->status;
1480
1481         DEBUG(10,("enum_dom_groups: [Cached] - cached list for domain %s status: %s\n",
1482                 domain->name, nt_errstr(status) ));
1483
1484         centry_free(centry);
1485         return status;
1486
1487 do_query:
1488         *num_entries = 0;
1489         *info = NULL;
1490
1491         /* Return status value returned by seq number check */
1492
1493         if (!NT_STATUS_IS_OK(domain->last_status))
1494                 return domain->last_status;
1495
1496         DEBUG(10,("enum_dom_groups: [Cached] - doing backend query for list for domain %s\n",
1497                 domain->name ));
1498
1499         status = domain->backend->enum_dom_groups(domain, mem_ctx, num_entries, info);
1500
1501         /* and save it */
1502         refresh_sequence_number(domain, false);
1503         centry = centry_start(domain, status);
1504         if (!centry)
1505                 goto skip_save;
1506         centry_put_uint32(centry, *num_entries);
1507         for (i=0; i<(*num_entries); i++) {
1508                 centry_put_string(centry, (*info)[i].acct_name);
1509                 centry_put_string(centry, (*info)[i].acct_desc);
1510                 centry_put_uint32(centry, (*info)[i].rid);
1511         }       
1512         centry_end(centry, "GL/%s/domain", domain->name);
1513         centry_free(centry);
1514
1515 skip_save:
1516         return status;
1517 }
1518
1519 /* list all domain groups */
1520 static NTSTATUS enum_local_groups(struct winbindd_domain *domain,
1521                                 TALLOC_CTX *mem_ctx,
1522                                 uint32 *num_entries, 
1523                                 struct acct_info **info)
1524 {
1525         struct winbind_cache *cache = get_cache(domain);
1526         struct cache_entry *centry = NULL;
1527         NTSTATUS status;
1528         unsigned int i;
1529
1530         if (!cache->tdb)
1531                 goto do_query;
1532
1533         centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
1534         if (!centry)
1535                 goto do_query;
1536
1537         *num_entries = centry_uint32(centry);
1538         
1539         if (*num_entries == 0)
1540                 goto do_cached;
1541
1542         (*info) = TALLOC_ARRAY(mem_ctx, struct acct_info, *num_entries);
1543         if (! (*info)) {
1544                 smb_panic_fn("enum_dom_groups out of memory");
1545         }
1546         for (i=0; i<(*num_entries); i++) {
1547                 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1548                 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1549                 (*info)[i].rid = centry_uint32(centry);
1550         }
1551
1552 do_cached:      
1553
1554         /* If we are returning cached data and the domain controller
1555            is down then we don't know whether the data is up to date
1556            or not.  Return NT_STATUS_MORE_PROCESSING_REQUIRED to
1557            indicate this. */
1558
1559         if (wcache_server_down(domain)) {
1560                 DEBUG(10, ("enum_local_groups: returning cached user list and server was down\n"));
1561                 status = NT_STATUS_MORE_PROCESSING_REQUIRED;
1562         } else
1563                 status = centry->status;
1564
1565         DEBUG(10,("enum_local_groups: [Cached] - cached list for domain %s status: %s\n",
1566                 domain->name, nt_errstr(status) ));
1567
1568         centry_free(centry);
1569         return status;
1570
1571 do_query:
1572         *num_entries = 0;
1573         *info = NULL;
1574
1575         /* Return status value returned by seq number check */
1576
1577         if (!NT_STATUS_IS_OK(domain->last_status))
1578                 return domain->last_status;
1579
1580         DEBUG(10,("enum_local_groups: [Cached] - doing backend query for list for domain %s\n",
1581                 domain->name ));
1582
1583         status = domain->backend->enum_local_groups(domain, mem_ctx, num_entries, info);
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_entries);
1591         for (i=0; i<(*num_entries); i++) {
1592                 centry_put_string(centry, (*info)[i].acct_name);
1593                 centry_put_string(centry, (*info)[i].acct_desc);
1594                 centry_put_uint32(centry, (*info)[i].rid);
1595         }
1596         centry_end(centry, "GL/%s/local", domain->name);
1597         centry_free(centry);
1598
1599 skip_save:
1600         return status;
1601 }
1602
1603 /* convert a single name to a sid in a domain */
1604 static NTSTATUS name_to_sid(struct winbindd_domain *domain,
1605                             TALLOC_CTX *mem_ctx,
1606                             enum winbindd_cmd orig_cmd,
1607                             const char *domain_name,
1608                             const char *name,
1609                             DOM_SID *sid,
1610                             enum lsa_SidType *type)
1611 {
1612         struct winbind_cache *cache = get_cache(domain);
1613         struct cache_entry *centry = NULL;
1614         NTSTATUS status;
1615         fstring uname;
1616
1617         if (!cache->tdb)
1618                 goto do_query;
1619
1620         fstrcpy(uname, name);
1621         strupper_m(uname);
1622         centry = wcache_fetch(cache, domain, "NS/%s/%s", domain_name, uname);
1623         if (!centry)
1624                 goto do_query;
1625
1626         status = centry->status;
1627         if (NT_STATUS_IS_OK(status)) {
1628                 *type = (enum lsa_SidType)centry_uint32(centry);
1629                 centry_sid(centry, mem_ctx, sid);
1630         }
1631
1632         DEBUG(10,("name_to_sid: [Cached] - cached name for domain %s status: %s\n",
1633                 domain->name, nt_errstr(status) ));
1634
1635         centry_free(centry);
1636         return status;
1637
1638 do_query:
1639         ZERO_STRUCTP(sid);
1640
1641         /* If the seq number check indicated that there is a problem
1642          * with this DC, then return that status... except for
1643          * access_denied.  This is special because the dc may be in
1644          * "restrict anonymous = 1" mode, in which case it will deny
1645          * most unauthenticated operations, but *will* allow the LSA
1646          * name-to-sid that we try as a fallback. */
1647
1648         if (!(NT_STATUS_IS_OK(domain->last_status)
1649               || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1650                 return domain->last_status;
1651
1652         DEBUG(10,("name_to_sid: [Cached] - doing backend query for name for domain %s\n",
1653                 domain->name ));
1654
1655         status = domain->backend->name_to_sid(domain, mem_ctx, orig_cmd, 
1656                                               domain_name, name, sid, type);
1657
1658         /* and save it */
1659         refresh_sequence_number(domain, false);
1660
1661         if (domain->online &&
1662             (NT_STATUS_IS_OK(status) || NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED))) {
1663                 wcache_save_name_to_sid(domain, status, domain_name, name, sid, *type);
1664
1665                 /* Only save the reverse mapping if this was not a UPN */
1666                 if (!strchr(name, '@')) {
1667                         strupper_m(CONST_DISCARD(char *,domain_name));
1668                         strlower_m(CONST_DISCARD(char *,name));
1669                         wcache_save_sid_to_name(domain, status, sid, domain_name, name, *type);
1670                 }
1671         }
1672         
1673         return status;
1674 }
1675
1676 /* convert a sid to a user or group name. The sid is guaranteed to be in the domain
1677    given */
1678 static NTSTATUS sid_to_name(struct winbindd_domain *domain,
1679                             TALLOC_CTX *mem_ctx,
1680                             const DOM_SID *sid,
1681                             char **domain_name,
1682                             char **name,
1683                             enum lsa_SidType *type)
1684 {
1685         struct winbind_cache *cache = get_cache(domain);
1686         struct cache_entry *centry = NULL;
1687         NTSTATUS status;
1688         fstring sid_string;
1689
1690         if (!cache->tdb)
1691                 goto do_query;
1692
1693         centry = wcache_fetch(cache, domain, "SN/%s",
1694                               sid_to_fstring(sid_string, sid));
1695         if (!centry)
1696                 goto do_query;
1697
1698         status = centry->status;
1699         if (NT_STATUS_IS_OK(status)) {
1700                 *type = (enum lsa_SidType)centry_uint32(centry);
1701                 *domain_name = centry_string(centry, mem_ctx);
1702                 *name = centry_string(centry, mem_ctx);
1703         }
1704
1705         DEBUG(10,("sid_to_name: [Cached] - cached name for domain %s status: %s\n",
1706                 domain->name, nt_errstr(status) ));
1707
1708         centry_free(centry);
1709         return status;
1710
1711 do_query:
1712         *name = NULL;
1713         *domain_name = NULL;
1714
1715         /* If the seq number check indicated that there is a problem
1716          * with this DC, then return that status... except for
1717          * access_denied.  This is special because the dc may be in
1718          * "restrict anonymous = 1" mode, in which case it will deny
1719          * most unauthenticated operations, but *will* allow the LSA
1720          * sid-to-name that we try as a fallback. */
1721
1722         if (!(NT_STATUS_IS_OK(domain->last_status)
1723               || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1724                 return domain->last_status;
1725
1726         DEBUG(10,("sid_to_name: [Cached] - doing backend query for name for domain %s\n",
1727                 domain->name ));
1728
1729         status = domain->backend->sid_to_name(domain, mem_ctx, sid, domain_name, name, type);
1730
1731         /* and save it */
1732         refresh_sequence_number(domain, false);
1733         wcache_save_sid_to_name(domain, status, sid, *domain_name, *name, *type);
1734
1735         /* We can't save the name to sid mapping here, as with sid history a
1736          * later name2sid would give the wrong sid. */
1737
1738         return status;
1739 }
1740
1741 static NTSTATUS rids_to_names(struct winbindd_domain *domain,
1742                               TALLOC_CTX *mem_ctx,
1743                               const DOM_SID *domain_sid,
1744                               uint32 *rids,
1745                               size_t num_rids,
1746                               char **domain_name,
1747                               char ***names,
1748                               enum lsa_SidType **types)
1749 {
1750         struct winbind_cache *cache = get_cache(domain);
1751         size_t i;
1752         NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
1753         bool have_mapped;
1754         bool have_unmapped;
1755
1756         *domain_name = NULL;
1757         *names = NULL;
1758         *types = NULL;
1759
1760         if (!cache->tdb) {
1761                 goto do_query;
1762         }
1763
1764         if (num_rids == 0) {
1765                 return NT_STATUS_OK;
1766         }
1767
1768         *names = TALLOC_ARRAY(mem_ctx, char *, num_rids);
1769         *types = TALLOC_ARRAY(mem_ctx, enum lsa_SidType, num_rids);
1770
1771         if ((*names == NULL) || (*types == NULL)) {
1772                 result = NT_STATUS_NO_MEMORY;
1773                 goto error;
1774         }
1775
1776         have_mapped = have_unmapped = false;
1777
1778         for (i=0; i<num_rids; i++) {
1779                 DOM_SID sid;
1780                 struct cache_entry *centry;
1781                 fstring tmp;
1782
1783                 if (!sid_compose(&sid, domain_sid, rids[i])) {
1784                         result = NT_STATUS_INTERNAL_ERROR;
1785                         goto error;
1786                 }
1787
1788                 centry = wcache_fetch(cache, domain, "SN/%s",
1789                                       sid_to_fstring(tmp, &sid));
1790                 if (!centry) {
1791                         goto do_query;
1792                 }
1793
1794                 (*types)[i] = SID_NAME_UNKNOWN;
1795                 (*names)[i] = talloc_strdup(*names, "");
1796
1797                 if (NT_STATUS_IS_OK(centry->status)) {
1798                         char *dom;
1799                         have_mapped = true;
1800                         (*types)[i] = (enum lsa_SidType)centry_uint32(centry);
1801
1802                         dom = centry_string(centry, mem_ctx);
1803                         if (*domain_name == NULL) {
1804                                 *domain_name = dom;
1805                         } else {
1806                                 talloc_free(dom);
1807                         }
1808
1809                         (*names)[i] = centry_string(centry, *names);
1810
1811                 } else if (NT_STATUS_EQUAL(centry->status, NT_STATUS_NONE_MAPPED)) {
1812                         have_unmapped = true;
1813
1814                 } else {
1815                         /* something's definitely wrong */
1816                         result = centry->status;
1817                         goto error;
1818                 }
1819
1820                 centry_free(centry);
1821         }
1822
1823         if (!have_mapped) {
1824                 return NT_STATUS_NONE_MAPPED;
1825         }
1826         if (!have_unmapped) {
1827                 return NT_STATUS_OK;
1828         }
1829         return STATUS_SOME_UNMAPPED;
1830
1831  do_query:
1832
1833         TALLOC_FREE(*names);
1834         TALLOC_FREE(*types);
1835
1836         result = domain->backend->rids_to_names(domain, mem_ctx, domain_sid,
1837                                                 rids, num_rids, domain_name,
1838                                                 names, types);
1839
1840         /*
1841           None of the queried rids has been found so save all negative entries
1842         */
1843         if (NT_STATUS_EQUAL(result, NT_STATUS_NONE_MAPPED)) {
1844                 for (i = 0; i < num_rids; i++) {
1845                         DOM_SID sid;
1846                         const char *name = "";
1847                         const enum lsa_SidType type = SID_NAME_UNKNOWN;
1848                         NTSTATUS status = NT_STATUS_NONE_MAPPED;
1849                         
1850                         if (!sid_compose(&sid, domain_sid, rids[i])) {
1851                                 return NT_STATUS_INTERNAL_ERROR;
1852                         }
1853
1854                         wcache_save_sid_to_name(domain, status, &sid, *domain_name,
1855                                                 name, type);
1856                 }
1857
1858                 return result;
1859         }
1860
1861         /*
1862           Some or all of the queried rids have been found.
1863         */
1864         if (!NT_STATUS_IS_OK(result) &&
1865             !NT_STATUS_EQUAL(result, STATUS_SOME_UNMAPPED)) {
1866                 return result;
1867         }
1868
1869         refresh_sequence_number(domain, false);
1870
1871         for (i=0; i<num_rids; i++) {
1872                 DOM_SID sid;
1873                 NTSTATUS status;
1874
1875                 if (!sid_compose(&sid, domain_sid, rids[i])) {
1876                         result = NT_STATUS_INTERNAL_ERROR;
1877                         goto error;
1878                 }
1879
1880                 status = (*types)[i] == SID_NAME_UNKNOWN ?
1881                         NT_STATUS_NONE_MAPPED : NT_STATUS_OK;
1882
1883                 wcache_save_sid_to_name(domain, status, &sid, *domain_name,
1884                                         (*names)[i], (*types)[i]);
1885         }
1886
1887         return result;
1888
1889  error:
1890         
1891         TALLOC_FREE(*names);
1892         TALLOC_FREE(*types);
1893         return result;
1894 }
1895
1896 /* Lookup user information from a rid */
1897 static NTSTATUS query_user(struct winbindd_domain *domain, 
1898                            TALLOC_CTX *mem_ctx, 
1899                            const DOM_SID *user_sid, 
1900                            WINBIND_USERINFO *info)
1901 {
1902         struct winbind_cache *cache = get_cache(domain);
1903         struct cache_entry *centry = NULL;
1904         NTSTATUS status;
1905         fstring tmp;
1906
1907         if (!cache->tdb)
1908                 goto do_query;
1909
1910         centry = wcache_fetch(cache, domain, "U/%s",
1911                               sid_to_fstring(tmp, user_sid));
1912         
1913         /* If we have an access denied cache entry and a cached info3 in the
1914            samlogon cache then do a query.  This will force the rpc back end
1915            to return the info3 data. */
1916
1917         if (NT_STATUS_V(domain->last_status) == NT_STATUS_V(NT_STATUS_ACCESS_DENIED) &&
1918             netsamlogon_cache_have(user_sid)) {
1919                 DEBUG(10, ("query_user: cached access denied and have cached info3\n"));
1920                 domain->last_status = NT_STATUS_OK;
1921                 centry_free(centry);
1922                 goto do_query;
1923         }
1924         
1925         if (!centry)
1926                 goto do_query;
1927         
1928         /* if status is not ok then this is a negative hit
1929            and the rest of the data doesn't matter */
1930         status = centry->status;
1931         if (NT_STATUS_IS_OK(status)) {
1932                 info->acct_name = centry_string(centry, mem_ctx);
1933                 info->full_name = centry_string(centry, mem_ctx);
1934                 info->homedir = centry_string(centry, mem_ctx);
1935                 info->shell = centry_string(centry, mem_ctx);
1936                 info->primary_gid = centry_uint32(centry);
1937                 centry_sid(centry, mem_ctx, &info->user_sid);
1938                 centry_sid(centry, mem_ctx, &info->group_sid);
1939         }
1940
1941         DEBUG(10,("query_user: [Cached] - cached info for domain %s status: %s\n",
1942                 domain->name, nt_errstr(status) ));
1943
1944         centry_free(centry);
1945         return status;
1946
1947 do_query:
1948         ZERO_STRUCTP(info);
1949
1950         /* Return status value returned by seq number check */
1951
1952         if (!NT_STATUS_IS_OK(domain->last_status))
1953                 return domain->last_status;
1954         
1955         DEBUG(10,("query_user: [Cached] - doing backend query for info for domain %s\n",
1956                 domain->name ));
1957
1958         status = domain->backend->query_user(domain, mem_ctx, user_sid, info);
1959
1960         /* and save it */
1961         refresh_sequence_number(domain, false);
1962         wcache_save_user(domain, status, info);
1963
1964         return status;
1965 }
1966
1967
1968 /* Lookup groups a user is a member of. */
1969 static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
1970                                   TALLOC_CTX *mem_ctx,
1971                                   const DOM_SID *user_sid, 
1972                                   uint32 *num_groups, DOM_SID **user_gids)
1973 {
1974         struct winbind_cache *cache = get_cache(domain);
1975         struct cache_entry *centry = NULL;
1976         NTSTATUS status;
1977         unsigned int i;
1978         fstring sid_string;
1979
1980         if (!cache->tdb)
1981                 goto do_query;
1982
1983         centry = wcache_fetch(cache, domain, "UG/%s",
1984                               sid_to_fstring(sid_string, user_sid));
1985         
1986         /* If we have an access denied cache entry and a cached info3 in the
1987            samlogon cache then do a query.  This will force the rpc back end
1988            to return the info3 data. */
1989
1990         if (NT_STATUS_V(domain->last_status) == NT_STATUS_V(NT_STATUS_ACCESS_DENIED) &&
1991             netsamlogon_cache_have(user_sid)) {
1992                 DEBUG(10, ("lookup_usergroups: cached access denied and have cached info3\n"));
1993                 domain->last_status = NT_STATUS_OK;
1994                 centry_free(centry);
1995                 goto do_query;
1996         }
1997         
1998         if (!centry)
1999                 goto do_query;
2000
2001         *num_groups = centry_uint32(centry);
2002         
2003         if (*num_groups == 0)
2004                 goto do_cached;
2005
2006         (*user_gids) = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_groups);
2007         if (! (*user_gids)) {
2008                 smb_panic_fn("lookup_usergroups out of memory");
2009         }
2010         for (i=0; i<(*num_groups); i++) {
2011                 centry_sid(centry, mem_ctx, &(*user_gids)[i]);
2012         }
2013
2014 do_cached:      
2015         status = centry->status;
2016
2017         DEBUG(10,("lookup_usergroups: [Cached] - cached info for domain %s status: %s\n",
2018                 domain->name, nt_errstr(status) ));
2019
2020         centry_free(centry);
2021         return status;
2022
2023 do_query:
2024         (*num_groups) = 0;
2025         (*user_gids) = NULL;
2026
2027         /* Return status value returned by seq number check */
2028
2029         if (!NT_STATUS_IS_OK(domain->last_status))
2030                 return domain->last_status;
2031
2032         DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info for domain %s\n",
2033                 domain->name ));
2034
2035         status = domain->backend->lookup_usergroups(domain, mem_ctx, user_sid, num_groups, user_gids);
2036
2037         if ( NT_STATUS_EQUAL(status, NT_STATUS_SYNCHRONIZATION_REQUIRED) )
2038                 goto skip_save;
2039         
2040         /* and save it */
2041         refresh_sequence_number(domain, false);
2042         centry = centry_start(domain, status);
2043         if (!centry)
2044                 goto skip_save;
2045
2046         centry_put_uint32(centry, *num_groups);
2047         for (i=0; i<(*num_groups); i++) {
2048                 centry_put_sid(centry, &(*user_gids)[i]);
2049         }       
2050
2051         centry_end(centry, "UG/%s", sid_to_fstring(sid_string, user_sid));
2052         centry_free(centry);
2053
2054 skip_save:
2055         return status;
2056 }
2057
2058 static NTSTATUS lookup_useraliases(struct winbindd_domain *domain,
2059                                    TALLOC_CTX *mem_ctx,
2060                                    uint32 num_sids, const DOM_SID *sids,
2061                                    uint32 *num_aliases, uint32 **alias_rids)
2062 {
2063         struct winbind_cache *cache = get_cache(domain);
2064         struct cache_entry *centry = NULL;
2065         NTSTATUS status;
2066         char *sidlist = talloc_strdup(mem_ctx, "");
2067         int i;
2068
2069         if (!cache->tdb)
2070                 goto do_query;
2071
2072         if (num_sids == 0) {
2073                 *num_aliases = 0;
2074                 *alias_rids = NULL;
2075                 return NT_STATUS_OK;
2076         }
2077
2078         /* We need to cache indexed by the whole list of SIDs, the aliases
2079          * resulting might come from any of the SIDs. */
2080
2081         for (i=0; i<num_sids; i++) {
2082                 fstring tmp;
2083                 sidlist = talloc_asprintf(mem_ctx, "%s/%s", sidlist,
2084                                           sid_to_fstring(tmp, &sids[i]));
2085                 if (sidlist == NULL)
2086                         return NT_STATUS_NO_MEMORY;
2087         }
2088
2089         centry = wcache_fetch(cache, domain, "UA%s", sidlist);
2090
2091         if (!centry)
2092                 goto do_query;
2093
2094         *num_aliases = centry_uint32(centry);
2095         *alias_rids = NULL;
2096
2097         if (*num_aliases) {
2098                 (*alias_rids) = TALLOC_ARRAY(mem_ctx, uint32, *num_aliases);
2099
2100                 if ((*alias_rids) == NULL) {
2101                         centry_free(centry);
2102                         return NT_STATUS_NO_MEMORY;
2103                 }
2104         } else {
2105                 (*alias_rids) = NULL;
2106         }
2107
2108         for (i=0; i<(*num_aliases); i++)
2109                 (*alias_rids)[i] = centry_uint32(centry);
2110
2111         status = centry->status;
2112
2113         DEBUG(10,("lookup_useraliases: [Cached] - cached info for domain: %s "
2114                   "status %s\n", domain->name, nt_errstr(status)));
2115
2116         centry_free(centry);
2117         return status;
2118
2119  do_query:
2120         (*num_aliases) = 0;
2121         (*alias_rids) = NULL;
2122
2123         if (!NT_STATUS_IS_OK(domain->last_status))
2124                 return domain->last_status;
2125
2126         DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info "
2127                   "for domain %s\n", domain->name ));
2128
2129         status = domain->backend->lookup_useraliases(domain, mem_ctx,
2130                                                      num_sids, sids,
2131                                                      num_aliases, alias_rids);
2132
2133         /* and save it */
2134         refresh_sequence_number(domain, false);
2135         centry = centry_start(domain, status);
2136         if (!centry)
2137                 goto skip_save;
2138         centry_put_uint32(centry, *num_aliases);
2139         for (i=0; i<(*num_aliases); i++)
2140                 centry_put_uint32(centry, (*alias_rids)[i]);
2141         centry_end(centry, "UA%s", sidlist);
2142         centry_free(centry);
2143
2144  skip_save:
2145         return status;
2146 }
2147
2148
2149 static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
2150                                 TALLOC_CTX *mem_ctx,
2151                                 const DOM_SID *group_sid, uint32 *num_names, 
2152                                 DOM_SID **sid_mem, char ***names, 
2153                                 uint32 **name_types)
2154 {
2155         struct winbind_cache *cache = get_cache(domain);
2156         struct cache_entry *centry = NULL;
2157         NTSTATUS status;
2158         unsigned int i;
2159         fstring sid_string;
2160
2161         if (!cache->tdb)
2162                 goto do_query;
2163
2164         centry = wcache_fetch(cache, domain, "GM/%s",
2165                               sid_to_fstring(sid_string, group_sid));
2166         if (!centry)
2167                 goto do_query;
2168
2169         *num_names = centry_uint32(centry);
2170         
2171         if (*num_names == 0)
2172                 goto do_cached;
2173
2174         (*sid_mem) = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_names);
2175         (*names) = TALLOC_ARRAY(mem_ctx, char *, *num_names);
2176         (*name_types) = TALLOC_ARRAY(mem_ctx, uint32, *num_names);
2177
2178         if (! (*sid_mem) || ! (*names) || ! (*name_types)) {
2179                 smb_panic_fn("lookup_groupmem out of memory");
2180         }
2181
2182         for (i=0; i<(*num_names); i++) {
2183                 centry_sid(centry, mem_ctx, &(*sid_mem)[i]);
2184                 (*names)[i] = centry_string(centry, mem_ctx);
2185                 (*name_types)[i] = centry_uint32(centry);
2186         }
2187
2188 do_cached:      
2189         status = centry->status;
2190
2191         DEBUG(10,("lookup_groupmem: [Cached] - cached info for domain %s status: %s\n",
2192                 domain->name, nt_errstr(status)));
2193
2194         centry_free(centry);
2195         return status;
2196
2197 do_query:
2198         (*num_names) = 0;
2199         (*sid_mem) = NULL;
2200         (*names) = NULL;
2201         (*name_types) = NULL;
2202         
2203         /* Return status value returned by seq number check */
2204
2205         if (!NT_STATUS_IS_OK(domain->last_status))
2206                 return domain->last_status;
2207
2208         DEBUG(10,("lookup_groupmem: [Cached] - doing backend query for info for domain %s\n",
2209                 domain->name ));
2210
2211         status = domain->backend->lookup_groupmem(domain, mem_ctx, group_sid, num_names, 
2212                                                   sid_mem, names, name_types);
2213
2214         /* and save it */
2215         refresh_sequence_number(domain, false);
2216         centry = centry_start(domain, status);
2217         if (!centry)
2218                 goto skip_save;
2219         centry_put_uint32(centry, *num_names);
2220         for (i=0; i<(*num_names); i++) {
2221                 centry_put_sid(centry, &(*sid_mem)[i]);
2222                 centry_put_string(centry, (*names)[i]);
2223                 centry_put_uint32(centry, (*name_types)[i]);
2224         }       
2225         centry_end(centry, "GM/%s", sid_to_fstring(sid_string, group_sid));
2226         centry_free(centry);
2227
2228 skip_save:
2229         return status;
2230 }
2231
2232 /* find the sequence number for a domain */
2233 static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
2234 {
2235         refresh_sequence_number(domain, false);
2236
2237         *seq = domain->sequence_number;
2238
2239         return NT_STATUS_OK;
2240 }
2241
2242 /* enumerate trusted domains 
2243  * (we need to have the list of trustdoms in the cache when we go offline) -
2244  * Guenther */
2245 static NTSTATUS trusted_domains(struct winbindd_domain *domain,
2246                                 TALLOC_CTX *mem_ctx,
2247                                 uint32 *num_domains,
2248                                 char ***names,
2249                                 char ***alt_names,
2250                                 DOM_SID **dom_sids)
2251 {
2252         struct winbind_cache *cache = get_cache(domain);
2253         struct cache_entry *centry = NULL;
2254         NTSTATUS status;
2255         int i;
2256  
2257         if (!cache->tdb)
2258                 goto do_query;
2259  
2260         centry = wcache_fetch(cache, domain, "TRUSTDOMS/%s", domain->name);
2261         
2262         if (!centry) {
2263                 goto do_query;
2264         }
2265  
2266         *num_domains = centry_uint32(centry);
2267         
2268         if (*num_domains) {
2269                 (*names)        = TALLOC_ARRAY(mem_ctx, char *, *num_domains);
2270                 (*alt_names)    = TALLOC_ARRAY(mem_ctx, char *, *num_domains);
2271                 (*dom_sids)     = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_domains);
2272  
2273                 if (! (*dom_sids) || ! (*names) || ! (*alt_names)) {
2274                         smb_panic_fn("trusted_domains out of memory");
2275                 }
2276         } else {
2277                 (*names) = NULL;
2278                 (*alt_names) = NULL;
2279                 (*dom_sids) = NULL;
2280         }
2281  
2282         for (i=0; i<(*num_domains); i++) {
2283                 (*names)[i] = centry_string(centry, mem_ctx);
2284                 (*alt_names)[i] = centry_string(centry, mem_ctx);
2285                 if (!centry_sid(centry, mem_ctx, &(*dom_sids)[i])) {
2286                         sid_copy(&(*dom_sids)[i], &global_sid_NULL);
2287                 }
2288         }
2289
2290         status = centry->status;
2291  
2292         DEBUG(10,("trusted_domains: [Cached] - cached info for domain %s (%d trusts) status: %s\n",
2293                 domain->name, *num_domains, nt_errstr(status) ));
2294  
2295         centry_free(centry);
2296         return status;
2297  
2298 do_query:
2299         (*num_domains) = 0;
2300         (*dom_sids) = NULL;
2301         (*names) = NULL;
2302         (*alt_names) = NULL;
2303  
2304         /* Return status value returned by seq number check */
2305
2306         if (!NT_STATUS_IS_OK(domain->last_status))
2307                 return domain->last_status;
2308         
2309         DEBUG(10,("trusted_domains: [Cached] - doing backend query for info for domain %s\n",
2310                 domain->name ));
2311  
2312         status = domain->backend->trusted_domains(domain, mem_ctx, num_domains,
2313                                                 names, alt_names, dom_sids);
2314
2315         /* no trusts gives NT_STATUS_NO_MORE_ENTRIES resetting to NT_STATUS_OK
2316          * so that the generic centry handling still applies correctly -
2317          * Guenther*/
2318
2319         if (!NT_STATUS_IS_ERR(status)) {
2320                 status = NT_STATUS_OK;
2321         }
2322
2323
2324 #if 0    /* Disabled as we want the trust dom list to be managed by
2325             the main parent and always to make the query.  --jerry */
2326
2327         /* and save it */
2328         refresh_sequence_number(domain, false);
2329  
2330         centry = centry_start(domain, status);
2331         if (!centry)
2332                 goto skip_save;
2333
2334         centry_put_uint32(centry, *num_domains);
2335
2336         for (i=0; i<(*num_domains); i++) {
2337                 centry_put_string(centry, (*names)[i]);
2338                 centry_put_string(centry, (*alt_names)[i]);
2339                 centry_put_sid(centry, &(*dom_sids)[i]);
2340         }
2341         
2342         centry_end(centry, "TRUSTDOMS/%s", domain->name);
2343  
2344         centry_free(centry);
2345  
2346 skip_save:
2347 #endif
2348
2349         return status;
2350 }       
2351
2352 /* get lockout policy */
2353 static NTSTATUS lockout_policy(struct winbindd_domain *domain,
2354                                TALLOC_CTX *mem_ctx,
2355                                struct samr_DomInfo12 *policy)
2356 {
2357         struct winbind_cache *cache = get_cache(domain);
2358         struct cache_entry *centry = NULL;
2359         NTSTATUS status;
2360
2361         if (!cache->tdb)
2362                 goto do_query;
2363
2364         centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
2365
2366         if (!centry)
2367                 goto do_query;
2368
2369         policy->lockout_duration = centry_nttime(centry);
2370         policy->lockout_window = centry_nttime(centry);
2371         policy->lockout_threshold = centry_uint16(centry);
2372
2373         status = centry->status;
2374
2375         DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2376                 domain->name, nt_errstr(status) ));
2377
2378         centry_free(centry);
2379         return status;
2380
2381 do_query:
2382         ZERO_STRUCTP(policy);
2383
2384         /* Return status value returned by seq number check */
2385
2386         if (!NT_STATUS_IS_OK(domain->last_status))
2387                 return domain->last_status;
2388
2389         DEBUG(10,("lockout_policy: [Cached] - doing backend query for info for domain %s\n",
2390                 domain->name ));
2391
2392         status = domain->backend->lockout_policy(domain, mem_ctx, policy);
2393
2394         /* and save it */
2395         refresh_sequence_number(domain, false);
2396         wcache_save_lockout_policy(domain, status, policy);
2397
2398         return status;
2399 }
2400
2401 /* get password policy */
2402 static NTSTATUS password_policy(struct winbindd_domain *domain,
2403                                 TALLOC_CTX *mem_ctx,
2404                                 struct samr_DomInfo1 *policy)
2405 {
2406         struct winbind_cache *cache = get_cache(domain);
2407         struct cache_entry *centry = NULL;
2408         NTSTATUS status;
2409
2410         if (!cache->tdb)
2411                 goto do_query;
2412
2413         centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
2414
2415         if (!centry)
2416                 goto do_query;
2417
2418         policy->min_password_length = centry_uint16(centry);
2419         policy->password_history_length = centry_uint16(centry);
2420         policy->password_properties = centry_uint32(centry);
2421         policy->max_password_age = centry_nttime(centry);
2422         policy->min_password_age = centry_nttime(centry);
2423
2424         status = centry->status;
2425
2426         DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2427                 domain->name, nt_errstr(status) ));
2428
2429         centry_free(centry);
2430         return status;
2431
2432 do_query:
2433         ZERO_STRUCTP(policy);
2434
2435         /* Return status value returned by seq number check */
2436
2437         if (!NT_STATUS_IS_OK(domain->last_status))
2438                 return domain->last_status;
2439
2440         DEBUG(10,("password_policy: [Cached] - doing backend query for info for domain %s\n",
2441                 domain->name ));
2442
2443         status = domain->backend->password_policy(domain, mem_ctx, policy);
2444
2445         /* and save it */
2446         refresh_sequence_number(domain, false);
2447         if (NT_STATUS_IS_OK(status)) {
2448                 wcache_save_password_policy(domain, status, policy);
2449         }
2450
2451         return status;
2452 }
2453
2454
2455 /* Invalidate cached user and group lists coherently */
2456
2457 static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, 
2458                        void *state)
2459 {
2460         if (strncmp((const char *)kbuf.dptr, "UL/", 3) == 0 ||
2461             strncmp((const char *)kbuf.dptr, "GL/", 3) == 0)
2462                 tdb_delete(the_tdb, kbuf);
2463
2464         return 0;
2465 }
2466
2467 /* Invalidate the getpwnam and getgroups entries for a winbindd domain */
2468
2469 void wcache_invalidate_samlogon(struct winbindd_domain *domain, 
2470                                 struct netr_SamInfo3 *info3)
2471 {
2472         DOM_SID sid;
2473         fstring key_str, sid_string;
2474         struct winbind_cache *cache;
2475
2476         /* dont clear cached U/SID and UG/SID entries when we want to logon
2477          * offline - gd */
2478
2479         if (lp_winbind_offline_logon()) {
2480                 return;
2481         }
2482
2483         if (!domain)
2484                 return;
2485
2486         cache = get_cache(domain);
2487
2488         if (!cache->tdb) {
2489                 return;
2490         }
2491
2492         sid_copy(&sid, info3->base.domain_sid);
2493         sid_append_rid(&sid, info3->base.rid);
2494
2495         /* Clear U/SID cache entry */
2496         fstr_sprintf(key_str, "U/%s", sid_to_fstring(sid_string, &sid));
2497         DEBUG(10, ("wcache_invalidate_samlogon: clearing %s\n", key_str));
2498         tdb_delete(cache->tdb, string_tdb_data(key_str));
2499
2500         /* Clear UG/SID cache entry */
2501         fstr_sprintf(key_str, "UG/%s", sid_to_fstring(sid_string, &sid));
2502         DEBUG(10, ("wcache_invalidate_samlogon: clearing %s\n", key_str));
2503         tdb_delete(cache->tdb, string_tdb_data(key_str));
2504
2505         /* Samba/winbindd never needs this. */
2506         netsamlogon_clear_cached_user(info3);
2507 }
2508
2509 bool wcache_invalidate_cache(void)
2510 {
2511         struct winbindd_domain *domain;
2512
2513         for (domain = domain_list(); domain; domain = domain->next) {
2514                 struct winbind_cache *cache = get_cache(domain);
2515
2516                 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
2517                            "entries for %s\n", domain->name));
2518                 if (cache) {
2519                         if (cache->tdb) {
2520                                 tdb_traverse(cache->tdb, traverse_fn, NULL);
2521                         } else {
2522                                 return false;
2523                         }
2524                 }
2525         }
2526         return true;
2527 }
2528
2529 bool init_wcache(void)
2530 {
2531         if (wcache == NULL) {
2532                 wcache = SMB_XMALLOC_P(struct winbind_cache);
2533                 ZERO_STRUCTP(wcache);
2534         }
2535
2536         if (wcache->tdb != NULL)
2537                 return true;
2538
2539         /* when working offline we must not clear the cache on restart */
2540         wcache->tdb = tdb_open_log(cache_path("winbindd_cache.tdb"),
2541                                 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE, 
2542                                 lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST), 
2543                                 O_RDWR|O_CREAT, 0600);
2544
2545         if (wcache->tdb == NULL) {
2546                 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
2547                 return false;
2548         }
2549
2550         return true;
2551 }
2552
2553 /************************************************************************
2554  This is called by the parent to initialize the cache file.
2555  We don't need sophisticated locking here as we know we're the
2556  only opener.
2557 ************************************************************************/
2558
2559 bool initialize_winbindd_cache(void)
2560 {
2561         bool cache_bad = true;
2562         uint32 vers;
2563
2564         if (!init_wcache()) {
2565                 DEBUG(0,("initialize_winbindd_cache: init_wcache failed.\n"));
2566                 return false;
2567         }
2568
2569         /* Check version number. */
2570         if (tdb_fetch_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, &vers) &&
2571                         vers == WINBINDD_CACHE_VERSION) {
2572                 cache_bad = false;
2573         }
2574
2575         if (cache_bad) {
2576                 DEBUG(0,("initialize_winbindd_cache: clearing cache "
2577                         "and re-creating with version number %d\n",
2578                         WINBINDD_CACHE_VERSION ));
2579
2580                 tdb_close(wcache->tdb);
2581                 wcache->tdb = NULL;
2582
2583                 if (unlink(cache_path("winbindd_cache.tdb")) == -1) {
2584                         DEBUG(0,("initialize_winbindd_cache: unlink %s failed %s ",
2585                                 cache_path("winbindd_cache.tdb"),
2586                                 strerror(errno) ));
2587                         return false;
2588                 }
2589                 if (!init_wcache()) {
2590                         DEBUG(0,("initialize_winbindd_cache: re-initialization "
2591                                         "init_wcache failed.\n"));
2592                         return false;
2593                 }
2594
2595                 /* Write the version. */
2596                 if (!tdb_store_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, WINBINDD_CACHE_VERSION)) {
2597                         DEBUG(0,("initialize_winbindd_cache: version number store failed %s\n",
2598                                 tdb_errorstr(wcache->tdb) ));
2599                         return false;
2600                 }
2601         }
2602
2603         tdb_close(wcache->tdb);
2604         wcache->tdb = NULL;
2605         return true;
2606 }
2607
2608 void close_winbindd_cache(void)
2609 {
2610         if (!wcache) {
2611                 return;
2612         }
2613         if (wcache->tdb) {
2614                 tdb_close(wcache->tdb);
2615                 wcache->tdb = NULL;
2616         }
2617 }
2618
2619 void cache_store_response(pid_t pid, struct winbindd_response *response)
2620 {
2621         fstring key_str;
2622
2623         if (!init_wcache())
2624                 return;
2625
2626         DEBUG(10, ("Storing response for pid %d, len %d\n",
2627                    (int)pid, response->length));
2628
2629         fstr_sprintf(key_str, "DR/%d", (int)pid);
2630         if (tdb_store(wcache->tdb, string_tdb_data(key_str), 
2631                       make_tdb_data((uint8 *)response, sizeof(*response)),
2632                       TDB_REPLACE) == -1)
2633                 return;
2634
2635         if (response->length == sizeof(*response))
2636                 return;
2637
2638         /* There's extra data */
2639
2640         DEBUG(10, ("Storing extra data: len=%d\n",
2641                    (int)(response->length - sizeof(*response))));
2642
2643         fstr_sprintf(key_str, "DE/%d", (int)pid);
2644         if (tdb_store(wcache->tdb, string_tdb_data(key_str),
2645                       make_tdb_data((uint8 *)response->extra_data.data,
2646                                     response->length - sizeof(*response)),
2647                       TDB_REPLACE) == 0)
2648                 return;
2649
2650         /* We could not store the extra data, make sure the tdb does not
2651          * contain a main record with wrong dangling extra data */
2652
2653         fstr_sprintf(key_str, "DR/%d", (int)pid);
2654         tdb_delete(wcache->tdb, string_tdb_data(key_str));
2655
2656         return;
2657 }
2658
2659 bool cache_retrieve_response(pid_t pid, struct winbindd_response * response)
2660 {
2661         TDB_DATA data;
2662         fstring key_str;
2663
2664         if (!init_wcache())
2665                 return false;
2666
2667         DEBUG(10, ("Retrieving response for pid %d\n", (int)pid));
2668
2669         fstr_sprintf(key_str, "DR/%d", (int)pid);
2670         data = tdb_fetch(wcache->tdb, string_tdb_data(key_str));
2671
2672         if (data.dptr == NULL)
2673                 return false;
2674
2675         if (data.dsize != sizeof(*response))
2676                 return false;
2677
2678         memcpy(response, data.dptr, data.dsize);
2679         SAFE_FREE(data.dptr);
2680
2681         if (response->length == sizeof(*response)) {
2682                 response->extra_data.data = NULL;
2683                 return true;
2684         }
2685
2686         /* There's extra data */
2687
2688         DEBUG(10, ("Retrieving extra data length=%d\n",
2689                    (int)(response->length - sizeof(*response))));
2690
2691         fstr_sprintf(key_str, "DE/%d", (int)pid);
2692         data = tdb_fetch(wcache->tdb, string_tdb_data(key_str));
2693
2694         if (data.dptr == NULL) {
2695                 DEBUG(0, ("Did not find extra data\n"));
2696                 return false;
2697         }
2698
2699         if (data.dsize != (response->length - sizeof(*response))) {
2700                 DEBUG(0, ("Invalid extra data length: %d\n", (int)data.dsize));
2701                 SAFE_FREE(data.dptr);
2702                 return false;
2703         }
2704
2705         dump_data(11, (uint8 *)data.dptr, data.dsize);
2706
2707         response->extra_data.data = data.dptr;
2708         return true;
2709 }
2710
2711 void cache_cleanup_response(pid_t pid)
2712 {
2713         fstring key_str;
2714
2715         if (!init_wcache())
2716                 return;
2717
2718         fstr_sprintf(key_str, "DR/%d", (int)pid);
2719         tdb_delete(wcache->tdb, string_tdb_data(key_str));
2720
2721         fstr_sprintf(key_str, "DE/%d", (int)pid);
2722         tdb_delete(wcache->tdb, string_tdb_data(key_str));
2723
2724         return;
2725 }
2726
2727
2728 bool lookup_cached_sid(TALLOC_CTX *mem_ctx, const DOM_SID *sid,
2729                        char **domain_name, char **name,
2730                        enum lsa_SidType *type)
2731 {
2732         struct winbindd_domain *domain;
2733         struct winbind_cache *cache;
2734         struct cache_entry *centry = NULL;
2735         NTSTATUS status;
2736         fstring tmp;
2737
2738         domain = find_lookup_domain_from_sid(sid);
2739         if (domain == NULL) {
2740                 return false;
2741         }
2742
2743         cache = get_cache(domain);
2744
2745         if (cache->tdb == NULL) {
2746                 return false;
2747         }
2748
2749         centry = wcache_fetch(cache, domain, "SN/%s",
2750                               sid_to_fstring(tmp, sid));
2751         if (centry == NULL) {
2752                 return false;
2753         }
2754
2755         if (NT_STATUS_IS_OK(centry->status)) {
2756                 *type = (enum lsa_SidType)centry_uint32(centry);
2757                 *domain_name = centry_string(centry, mem_ctx);
2758                 *name = centry_string(centry, mem_ctx);
2759         }
2760
2761         status = centry->status;
2762         centry_free(centry);
2763         return NT_STATUS_IS_OK(status);
2764 }
2765
2766 bool lookup_cached_name(TALLOC_CTX *mem_ctx,
2767                         const char *domain_name,
2768                         const char *name,
2769                         DOM_SID *sid,
2770                         enum lsa_SidType *type)
2771 {
2772         struct winbindd_domain *domain;
2773         struct winbind_cache *cache;
2774         struct cache_entry *centry = NULL;
2775         NTSTATUS status;
2776         fstring uname;
2777         bool original_online_state;     
2778
2779         domain = find_lookup_domain_from_name(domain_name);
2780         if (domain == NULL) {
2781                 return false;
2782         }
2783
2784         cache = get_cache(domain);
2785
2786         if (cache->tdb == NULL) {
2787                 return false;
2788         }
2789
2790         fstrcpy(uname, name);
2791         strupper_m(uname);
2792         
2793         /* If we are doing a cached logon, temporarily set the domain
2794            offline so the cache won't expire the entry */
2795         
2796         original_online_state = domain->online;
2797         domain->online = false;
2798         centry = wcache_fetch(cache, domain, "NS/%s/%s", domain_name, uname);
2799         domain->online = original_online_state;
2800         
2801         if (centry == NULL) {
2802                 return false;
2803         }
2804
2805         if (NT_STATUS_IS_OK(centry->status)) {
2806                 *type = (enum lsa_SidType)centry_uint32(centry);
2807                 centry_sid(centry, mem_ctx, sid);
2808         }
2809
2810         status = centry->status;
2811         centry_free(centry);
2812         
2813         return NT_STATUS_IS_OK(status);
2814 }
2815
2816 void cache_name2sid(struct winbindd_domain *domain, 
2817                     const char *domain_name, const char *name,
2818                     enum lsa_SidType type, const DOM_SID *sid)
2819 {
2820         refresh_sequence_number(domain, false);
2821         wcache_save_name_to_sid(domain, NT_STATUS_OK, domain_name, name,
2822                                 sid, type);
2823 }
2824
2825 /*
2826  * The original idea that this cache only contains centries has
2827  * been blurred - now other stuff gets put in here. Ensure we
2828  * ignore these things on cleanup.
2829  */
2830
2831 static int traverse_fn_cleanup(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, 
2832                                TDB_DATA dbuf, void *state)
2833 {
2834         struct cache_entry *centry;
2835
2836         if (is_non_centry_key(kbuf)) {
2837                 return 0;
2838         }
2839
2840         centry = wcache_fetch_raw((char *)kbuf.dptr);
2841         if (!centry) {
2842                 return 0;
2843         }
2844
2845         if (!NT_STATUS_IS_OK(centry->status)) {
2846                 DEBUG(10,("deleting centry %s\n", (const char *)kbuf.dptr));
2847                 tdb_delete(the_tdb, kbuf);
2848         }
2849
2850         centry_free(centry);
2851         return 0;
2852 }
2853
2854 /* flush the cache */
2855 void wcache_flush_cache(void)
2856 {
2857         if (!wcache)
2858                 return;
2859         if (wcache->tdb) {
2860                 tdb_close(wcache->tdb);
2861                 wcache->tdb = NULL;
2862         }
2863         if (!winbindd_use_cache()) {
2864                 return;
2865         }
2866
2867         /* when working offline we must not clear the cache on restart */
2868         wcache->tdb = tdb_open_log(cache_path("winbindd_cache.tdb"),
2869                                 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE, 
2870                                 lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST), 
2871                                 O_RDWR|O_CREAT, 0600);
2872
2873         if (!wcache->tdb) {
2874                 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
2875                 return;
2876         }
2877
2878         tdb_traverse(wcache->tdb, traverse_fn_cleanup, NULL);
2879
2880         DEBUG(10,("wcache_flush_cache success\n"));
2881 }
2882
2883 /* Count cached creds */
2884
2885 static int traverse_fn_cached_creds(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, 
2886                                     void *state)
2887 {
2888         int *cred_count = (int*)state;
2889  
2890         if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
2891                 (*cred_count)++;
2892         }
2893         return 0;
2894 }
2895
2896 NTSTATUS wcache_count_cached_creds(struct winbindd_domain *domain, int *count)
2897 {
2898         struct winbind_cache *cache = get_cache(domain);
2899
2900         *count = 0;
2901
2902         if (!cache->tdb) {
2903                 return NT_STATUS_INTERNAL_DB_ERROR;
2904         }
2905  
2906         tdb_traverse(cache->tdb, traverse_fn_cached_creds, (void *)count);
2907
2908         return NT_STATUS_OK;
2909 }
2910
2911 struct cred_list {
2912         struct cred_list *prev, *next;
2913         TDB_DATA key;
2914         fstring name;
2915         time_t created;
2916 };
2917 static struct cred_list *wcache_cred_list;
2918
2919 static int traverse_fn_get_credlist(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, 
2920                                     void *state)
2921 {
2922         struct cred_list *cred;
2923
2924         if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
2925
2926                 cred = SMB_MALLOC_P(struct cred_list);
2927                 if (cred == NULL) {
2928                         DEBUG(0,("traverse_fn_remove_first_creds: failed to malloc new entry for list\n"));
2929                         return -1;
2930                 }
2931
2932                 ZERO_STRUCTP(cred);
2933                 
2934                 /* save a copy of the key */
2935                 
2936                 fstrcpy(cred->name, (const char *)kbuf.dptr);           
2937                 DLIST_ADD(wcache_cred_list, cred);
2938         }
2939         
2940         return 0;
2941 }
2942
2943 NTSTATUS wcache_remove_oldest_cached_creds(struct winbindd_domain *domain, const DOM_SID *sid) 
2944 {
2945         struct winbind_cache *cache = get_cache(domain);
2946         NTSTATUS status;
2947         int ret;
2948         struct cred_list *cred, *oldest = NULL;
2949
2950         if (!cache->tdb) {
2951                 return NT_STATUS_INTERNAL_DB_ERROR;
2952         }
2953
2954         /* we possibly already have an entry */
2955         if (sid && NT_STATUS_IS_OK(wcache_cached_creds_exist(domain, sid))) {
2956         
2957                 fstring key_str, tmp;
2958
2959                 DEBUG(11,("we already have an entry, deleting that\n"));
2960
2961                 fstr_sprintf(key_str, "CRED/%s", sid_to_fstring(tmp, sid));
2962
2963                 tdb_delete(cache->tdb, string_tdb_data(key_str));
2964
2965                 return NT_STATUS_OK;
2966         }
2967
2968         ret = tdb_traverse(cache->tdb, traverse_fn_get_credlist, NULL);
2969         if (ret == 0) {
2970                 return NT_STATUS_OK;
2971         } else if ((ret == -1) || (wcache_cred_list == NULL)) {
2972                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
2973         }
2974
2975         ZERO_STRUCTP(oldest);
2976
2977         for (cred = wcache_cred_list; cred; cred = cred->next) {
2978
2979                 TDB_DATA data;
2980                 time_t t;
2981
2982                 data = tdb_fetch(cache->tdb, string_tdb_data(cred->name));
2983                 if (!data.dptr) {
2984                         DEBUG(10,("wcache_remove_oldest_cached_creds: entry for [%s] not found\n", 
2985                                 cred->name));
2986                         status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
2987                         goto done;
2988                 }
2989         
2990                 t = IVAL(data.dptr, 0);
2991                 SAFE_FREE(data.dptr);
2992
2993                 if (!oldest) {
2994                         oldest = SMB_MALLOC_P(struct cred_list);
2995                         if (oldest == NULL) {
2996                                 status = NT_STATUS_NO_MEMORY;
2997                                 goto done;
2998                         }
2999
3000                         fstrcpy(oldest->name, cred->name);
3001                         oldest->created = t;
3002                         continue;
3003                 }
3004
3005                 if (t < oldest->created) {
3006                         fstrcpy(oldest->name, cred->name);
3007                         oldest->created = t;
3008                 }
3009         }
3010
3011         if (tdb_delete(cache->tdb, string_tdb_data(oldest->name)) == 0) {
3012                 status = NT_STATUS_OK;
3013         } else {
3014                 status = NT_STATUS_UNSUCCESSFUL;
3015         }
3016 done:
3017         SAFE_FREE(wcache_cred_list);
3018         SAFE_FREE(oldest);
3019         
3020         return status;
3021 }
3022
3023 /* Change the global online/offline state. */
3024 bool set_global_winbindd_state_offline(void)
3025 {
3026         TDB_DATA data;
3027
3028         DEBUG(10,("set_global_winbindd_state_offline: offline requested.\n"));
3029
3030         /* Only go offline if someone has created
3031            the key "WINBINDD_OFFLINE" in the cache tdb. */
3032
3033         if (wcache == NULL || wcache->tdb == NULL) {
3034                 DEBUG(10,("set_global_winbindd_state_offline: wcache not open yet.\n"));
3035                 return false;
3036         }
3037
3038         if (!lp_winbind_offline_logon()) {
3039                 DEBUG(10,("set_global_winbindd_state_offline: rejecting.\n"));
3040                 return false;
3041         }
3042
3043         if (global_winbindd_offline_state) {
3044                 /* Already offline. */
3045                 return true;
3046         }
3047
3048         data = tdb_fetch_bystring( wcache->tdb, "WINBINDD_OFFLINE" );
3049
3050         if (!data.dptr || data.dsize != 4) {
3051                 DEBUG(10,("set_global_winbindd_state_offline: offline state not set.\n"));
3052                 SAFE_FREE(data.dptr);
3053                 return false;
3054         } else {
3055                 DEBUG(10,("set_global_winbindd_state_offline: offline state set.\n"));
3056                 global_winbindd_offline_state = true;
3057                 SAFE_FREE(data.dptr);
3058                 return true;
3059         }
3060 }
3061
3062 void set_global_winbindd_state_online(void)
3063 {
3064         DEBUG(10,("set_global_winbindd_state_online: online requested.\n"));
3065
3066         if (!lp_winbind_offline_logon()) {
3067                 DEBUG(10,("set_global_winbindd_state_online: rejecting.\n"));
3068                 return;
3069         }
3070
3071         if (!global_winbindd_offline_state) {
3072                 /* Already online. */
3073                 return;
3074         }
3075         global_winbindd_offline_state = false;
3076
3077         if (!wcache->tdb) {
3078                 return;
3079         }
3080
3081         /* Ensure there is no key "WINBINDD_OFFLINE" in the cache tdb. */
3082         tdb_delete_bystring(wcache->tdb, "WINBINDD_OFFLINE");
3083 }
3084
3085 bool get_global_winbindd_state_offline(void)
3086 {
3087         return global_winbindd_offline_state;
3088 }
3089
3090 /***********************************************************************
3091  Validate functions for all possible cache tdb keys.
3092 ***********************************************************************/
3093
3094 static struct cache_entry *create_centry_validate(const char *kstr, TDB_DATA data, 
3095                                                   struct tdb_validation_status *state)
3096 {
3097         struct cache_entry *centry;
3098
3099         centry = SMB_XMALLOC_P(struct cache_entry);
3100         centry->data = (unsigned char *)memdup(data.dptr, data.dsize);
3101         if (!centry->data) {
3102                 SAFE_FREE(centry);
3103                 return NULL;
3104         }
3105         centry->len = data.dsize;
3106         centry->ofs = 0;
3107
3108         if (centry->len < 8) {
3109                 /* huh? corrupt cache? */
3110                 DEBUG(0,("create_centry_validate: Corrupt cache for key %s (len < 8) ?\n", kstr));
3111                 centry_free(centry);
3112                 state->bad_entry = true;
3113                 state->success = false;
3114                 return NULL;
3115         }
3116
3117         centry->status = NT_STATUS(centry_uint32(centry));
3118         centry->sequence_number = centry_uint32(centry);
3119         return centry;
3120 }
3121
3122 static int validate_seqnum(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3123                            struct tdb_validation_status *state)
3124 {
3125         if (dbuf.dsize != 8) {
3126                 DEBUG(0,("validate_seqnum: Corrupt cache for key %s (len %u != 8) ?\n",
3127                                 keystr, (unsigned int)dbuf.dsize ));
3128                 state->bad_entry = true;
3129                 return 1;
3130         }
3131         return 0;
3132 }
3133
3134 static int validate_ns(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3135                        struct tdb_validation_status *state)
3136 {
3137         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3138         if (!centry) {
3139                 return 1;
3140         }
3141
3142         (void)centry_uint32(centry);
3143         if (NT_STATUS_IS_OK(centry->status)) {
3144                 DOM_SID sid;
3145                 (void)centry_sid(centry, mem_ctx, &sid);
3146         }
3147
3148         centry_free(centry);
3149
3150         if (!(state->success)) {
3151                 return 1;
3152         }
3153         DEBUG(10,("validate_ns: %s ok\n", keystr));
3154         return 0;
3155 }
3156
3157 static int validate_sn(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3158                        struct tdb_validation_status *state)
3159 {
3160         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3161         if (!centry) {
3162                 return 1;
3163         }
3164
3165         if (NT_STATUS_IS_OK(centry->status)) {
3166                 (void)centry_uint32(centry);
3167                 (void)centry_string(centry, mem_ctx);
3168                 (void)centry_string(centry, mem_ctx);
3169         }
3170
3171         centry_free(centry);
3172
3173         if (!(state->success)) {
3174                 return 1;
3175         }
3176         DEBUG(10,("validate_sn: %s ok\n", keystr));
3177         return 0;
3178 }
3179
3180 static int validate_u(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3181                       struct tdb_validation_status *state)
3182 {
3183         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3184         DOM_SID sid;
3185
3186         if (!centry) {
3187                 return 1;
3188         }
3189
3190         (void)centry_string(centry, mem_ctx);
3191         (void)centry_string(centry, mem_ctx);
3192         (void)centry_string(centry, mem_ctx);
3193         (void)centry_string(centry, mem_ctx);
3194         (void)centry_uint32(centry);
3195         (void)centry_sid(centry, mem_ctx, &sid);
3196         (void)centry_sid(centry, mem_ctx, &sid);
3197
3198         centry_free(centry);
3199
3200         if (!(state->success)) {
3201                 return 1;
3202         }
3203         DEBUG(10,("validate_u: %s ok\n", keystr));
3204         return 0;
3205 }
3206
3207 static int validate_loc_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3208                             struct tdb_validation_status *state)
3209 {
3210         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3211
3212         if (!centry) {
3213                 return 1;
3214         }
3215
3216         (void)centry_nttime(centry);
3217         (void)centry_nttime(centry);
3218         (void)centry_uint16(centry);
3219
3220         centry_free(centry);
3221
3222         if (!(state->success)) {
3223                 return 1;
3224         }
3225         DEBUG(10,("validate_loc_pol: %s ok\n", keystr));
3226         return 0;
3227 }
3228
3229 static int validate_pwd_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3230                             struct tdb_validation_status *state)
3231 {
3232         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3233
3234         if (!centry) {
3235                 return 1;
3236         }
3237
3238         (void)centry_uint16(centry);
3239         (void)centry_uint16(centry);
3240         (void)centry_uint32(centry);
3241         (void)centry_nttime(centry);
3242         (void)centry_nttime(centry);
3243
3244         centry_free(centry);
3245
3246         if (!(state->success)) {
3247                 return 1;
3248         }
3249         DEBUG(10,("validate_pwd_pol: %s ok\n", keystr));
3250         return 0;
3251 }
3252
3253 static int validate_cred(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3254                          struct tdb_validation_status *state)
3255 {
3256         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3257
3258         if (!centry) {
3259                 return 1;
3260         }
3261
3262         (void)centry_time(centry);
3263         (void)centry_hash16(centry, mem_ctx);
3264
3265         /* We only have 17 bytes more data in the salted cred case. */
3266         if (centry->len - centry->ofs == 17) {
3267                 (void)centry_hash16(centry, mem_ctx);
3268         }
3269
3270         centry_free(centry);
3271
3272         if (!(state->success)) {
3273                 return 1;
3274         }
3275         DEBUG(10,("validate_cred: %s ok\n", keystr));
3276         return 0;
3277 }
3278
3279 static int validate_ul(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3280                        struct tdb_validation_status *state)
3281 {
3282         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3283         int32 num_entries, i;
3284
3285         if (!centry) {
3286                 return 1;
3287         }
3288
3289         num_entries = (int32)centry_uint32(centry);
3290
3291         for (i=0; i< num_entries; i++) {
3292                 DOM_SID sid;
3293                 (void)centry_string(centry, mem_ctx);
3294                 (void)centry_string(centry, mem_ctx);
3295                 (void)centry_string(centry, mem_ctx);
3296                 (void)centry_string(centry, mem_ctx);
3297                 (void)centry_sid(centry, mem_ctx, &sid);
3298                 (void)centry_sid(centry, mem_ctx, &sid);
3299         }
3300
3301         centry_free(centry);
3302
3303         if (!(state->success)) {
3304                 return 1;
3305         }
3306         DEBUG(10,("validate_ul: %s ok\n", keystr));
3307         return 0;
3308 }
3309
3310 static int validate_gl(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3311                        struct tdb_validation_status *state)
3312 {
3313         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3314         int32 num_entries, i;
3315
3316         if (!centry) {
3317                 return 1;
3318         }
3319
3320         num_entries = centry_uint32(centry);
3321         
3322         for (i=0; i< num_entries; i++) {
3323                 (void)centry_string(centry, mem_ctx);
3324                 (void)centry_string(centry, mem_ctx);
3325                 (void)centry_uint32(centry);
3326         }
3327
3328         centry_free(centry);
3329
3330         if (!(state->success)) {
3331                 return 1;
3332         }
3333         DEBUG(10,("validate_gl: %s ok\n", keystr));
3334         return 0;
3335 }
3336
3337 static int validate_ug(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3338                        struct tdb_validation_status *state)
3339 {
3340         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3341         int32 num_groups, i;
3342
3343         if (!centry) {
3344                 return 1;
3345         }
3346
3347         num_groups = centry_uint32(centry);
3348
3349         for (i=0; i< num_groups; i++) {
3350                 DOM_SID sid;
3351                 centry_sid(centry, mem_ctx, &sid);
3352         }
3353
3354         centry_free(centry);
3355
3356         if (!(state->success)) {
3357                 return 1;
3358         }
3359         DEBUG(10,("validate_ug: %s ok\n", keystr));
3360         return 0;
3361 }
3362
3363 static int validate_ua(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3364                        struct tdb_validation_status *state)
3365 {
3366         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3367         int32 num_aliases, i;
3368
3369         if (!centry) {
3370                 return 1;
3371         }
3372
3373         num_aliases = centry_uint32(centry);
3374
3375         for (i=0; i < num_aliases; i++) {
3376                 (void)centry_uint32(centry);
3377         }
3378
3379         centry_free(centry);
3380
3381         if (!(state->success)) {
3382                 return 1;
3383         }
3384         DEBUG(10,("validate_ua: %s ok\n", keystr));
3385         return 0;
3386 }
3387
3388 static int validate_gm(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3389                        struct tdb_validation_status *state)
3390 {
3391         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3392         int32 num_names, i;
3393
3394         if (!centry) {
3395                 return 1;
3396         }
3397
3398         num_names = centry_uint32(centry);
3399
3400         for (i=0; i< num_names; i++) {
3401                 DOM_SID sid;
3402                 centry_sid(centry, mem_ctx, &sid);
3403                 (void)centry_string(centry, mem_ctx);
3404                 (void)centry_uint32(centry);
3405         }
3406
3407         centry_free(centry);
3408
3409         if (!(state->success)) {
3410                 return 1;
3411         }
3412         DEBUG(10,("validate_gm: %s ok\n", keystr));
3413         return 0;
3414 }
3415
3416 static int validate_dr(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3417                        struct tdb_validation_status *state)
3418 {
3419         /* Can't say anything about this other than must be nonzero. */
3420         if (dbuf.dsize == 0) {
3421                 DEBUG(0,("validate_dr: Corrupt cache for key %s (len == 0) ?\n",
3422                                 keystr));
3423                 state->bad_entry = true;
3424                 state->success = false;
3425                 return 1;
3426         }
3427
3428         DEBUG(10,("validate_dr: %s ok\n", keystr));
3429         return 0;
3430 }
3431
3432 static int validate_de(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3433                        struct tdb_validation_status *state)
3434 {
3435         /* Can't say anything about this other than must be nonzero. */
3436         if (dbuf.dsize == 0) {
3437                 DEBUG(0,("validate_de: Corrupt cache for key %s (len == 0) ?\n",
3438                                 keystr));
3439                 state->bad_entry = true;
3440                 state->success = false;
3441                 return 1;
3442         }
3443
3444         DEBUG(10,("validate_de: %s ok\n", keystr));
3445         return 0;
3446 }
3447
3448 static int validate_pwinfo(TALLOC_CTX *mem_ctx, const char *keystr,
3449                            TDB_DATA dbuf, struct tdb_validation_status *state)
3450 {
3451         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3452
3453         if (!centry) {
3454                 return 1;
3455         }
3456
3457         (void)centry_string(centry, mem_ctx);
3458         (void)centry_string(centry, mem_ctx);
3459         (void)centry_string(centry, mem_ctx);
3460         (void)centry_uint32(centry);
3461
3462         centry_free(centry);
3463
3464         if (!(state->success)) {
3465                 return 1;
3466         }
3467         DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3468         return 0;
3469 }
3470
3471 static int validate_nss_an(TALLOC_CTX *mem_ctx, const char *keystr,
3472                            TDB_DATA dbuf,
3473                            struct tdb_validation_status *state)
3474 {
3475         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3476
3477         if (!centry) {
3478                 return 1;
3479         }
3480
3481         (void)centry_string( centry, mem_ctx );
3482
3483         centry_free(centry);
3484
3485         if (!(state->success)) {
3486                 return 1;
3487         }
3488         DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3489         return 0;
3490 }
3491
3492 static int validate_nss_na(TALLOC_CTX *mem_ctx, const char *keystr,
3493                            TDB_DATA dbuf,
3494                            struct tdb_validation_status *state)
3495 {
3496         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3497
3498         if (!centry) {
3499                 return 1;
3500         }
3501
3502         (void)centry_string( centry, mem_ctx );
3503
3504         centry_free(centry);
3505
3506         if (!(state->success)) {
3507                 return 1;
3508         }
3509         DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3510         return 0;
3511 }
3512
3513 static int validate_trustdoms(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3514                               struct tdb_validation_status *state)
3515 {
3516         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3517         int32 num_domains, i;
3518
3519         if (!centry) {
3520                 return 1;
3521         }
3522
3523         num_domains = centry_uint32(centry);
3524         
3525         for (i=0; i< num_domains; i++) {
3526                 DOM_SID sid;
3527                 (void)centry_string(centry, mem_ctx);
3528                 (void)centry_string(centry, mem_ctx);
3529                 (void)centry_sid(centry, mem_ctx, &sid);
3530         }
3531
3532         centry_free(centry);
3533
3534         if (!(state->success)) {
3535                 return 1;
3536         }
3537         DEBUG(10,("validate_trustdoms: %s ok\n", keystr));
3538         return 0;
3539 }
3540
3541 static int validate_trustdomcache(TALLOC_CTX *mem_ctx, const char *keystr, 
3542                                   TDB_DATA dbuf,
3543                                   struct tdb_validation_status *state)
3544 {
3545         if (dbuf.dsize == 0) {
3546                 DEBUG(0, ("validate_trustdomcache: Corrupt cache for "
3547                           "key %s (len ==0) ?\n", keystr));
3548                 state->bad_entry = true;
3549                 state->success = false;
3550                 return 1;
3551         }
3552
3553         DEBUG(10,    ("validate_trustdomcache: %s ok\n", keystr));
3554         DEBUGADD(10, ("  Don't trust me, I am a DUMMY!\n"));
3555         return 0;
3556 }
3557
3558 static int validate_offline(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3559                             struct tdb_validation_status *state)
3560 {
3561         if (dbuf.dsize != 4) {
3562                 DEBUG(0,("validate_offline: Corrupt cache for key %s (len %u != 4) ?\n",
3563                                 keystr, (unsigned int)dbuf.dsize ));
3564                 state->bad_entry = true;
3565                 state->success = false;
3566                 return 1;
3567         }
3568         DEBUG(10,("validate_offline: %s ok\n", keystr));
3569         return 0;
3570 }
3571
3572 static int validate_cache_version(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3573                                   struct tdb_validation_status *state)
3574 {
3575         if (dbuf.dsize != 4) {
3576                 DEBUG(0, ("validate_cache_version: Corrupt cache for "
3577                           "key %s (len %u != 4) ?\n", 
3578                           keystr, (unsigned int)dbuf.dsize));
3579                 state->bad_entry = true;
3580                 state->success = false;
3581                 return 1;
3582         }
3583
3584         DEBUG(10, ("validate_cache_version: %s ok\n", keystr));
3585         return 0;
3586 }
3587
3588 /***********************************************************************
3589  A list of all possible cache tdb keys with associated validation
3590  functions.
3591 ***********************************************************************/
3592
3593 struct key_val_struct {
3594         const char *keyname;
3595         int (*validate_data_fn)(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, struct tdb_validation_status* state);
3596 } key_val[] = {
3597         {"SEQNUM/", validate_seqnum},
3598         {"NS/", validate_ns},
3599         {"SN/", validate_sn},
3600         {"U/", validate_u},
3601         {"LOC_POL/", validate_loc_pol},
3602         {"PWD_POL/", validate_pwd_pol},
3603         {"CRED/", validate_cred},
3604         {"UL/", validate_ul},
3605         {"GL/", validate_gl},
3606         {"UG/", validate_ug},
3607         {"UA", validate_ua},
3608         {"GM/", validate_gm},
3609         {"DR/", validate_dr},
3610         {"DE/", validate_de},
3611         {"NSS/PWINFO/", validate_pwinfo},
3612         {"TRUSTDOMS/", validate_trustdoms},
3613         {"TRUSTDOMCACHE/", validate_trustdomcache},
3614         {"NSS/NA/", validate_nss_na},
3615         {"NSS/AN/", validate_nss_an},
3616         {"WINBINDD_OFFLINE", validate_offline},
3617         {WINBINDD_CACHE_VERSION_KEYSTR, validate_cache_version},
3618         {NULL, NULL}
3619 };
3620
3621 /***********************************************************************
3622  Function to look at every entry in the tdb and validate it as far as
3623  possible.
3624 ***********************************************************************/
3625
3626 static int cache_traverse_validate_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, void *state)
3627 {
3628         int i;
3629         unsigned int max_key_len = 1024;
3630         struct tdb_validation_status *v_state = (struct tdb_validation_status *)state;
3631
3632         /* Paranoia check. */
3633         if (strncmp("UA/", (const char *)kbuf.dptr, 3) == 0) {
3634                 max_key_len = 1024 * 1024;
3635         }
3636         if (kbuf.dsize > max_key_len) {
3637                 DEBUG(0, ("cache_traverse_validate_fn: key length too large: "
3638                           "(%u) > (%u)\n\n",
3639                           (unsigned int)kbuf.dsize, (unsigned int)max_key_len));
3640                 return 1;
3641         }
3642
3643         for (i = 0; key_val[i].keyname; i++) {
3644                 size_t namelen = strlen(key_val[i].keyname);
3645                 if (kbuf.dsize >= namelen && (
3646                                 strncmp(key_val[i].keyname, (const char *)kbuf.dptr, namelen)) == 0) {
3647                         TALLOC_CTX *mem_ctx;
3648                         char *keystr;
3649                         int ret;
3650
3651                         keystr = SMB_MALLOC_ARRAY(char, kbuf.dsize+1);
3652                         if (!keystr) {
3653                                 return 1;
3654                         }
3655                         memcpy(keystr, kbuf.dptr, kbuf.dsize);
3656                         keystr[kbuf.dsize] = '\0';
3657
3658                         mem_ctx = talloc_init("validate_ctx");
3659                         if (!mem_ctx) {
3660                                 SAFE_FREE(keystr);
3661                                 return 1;
3662                         }
3663
3664                         ret = key_val[i].validate_data_fn(mem_ctx, keystr, dbuf, 
3665                                                           v_state);
3666
3667                         SAFE_FREE(keystr);
3668                         talloc_destroy(mem_ctx);
3669                         return ret;
3670                 }
3671         }
3672
3673         DEBUG(0,("cache_traverse_validate_fn: unknown cache entry\nkey :\n"));
3674         dump_data(0, (uint8 *)kbuf.dptr, kbuf.dsize);
3675         DEBUG(0,("data :\n"));
3676         dump_data(0, (uint8 *)dbuf.dptr, dbuf.dsize);
3677         v_state->unknown_key = true;
3678         v_state->success = false;
3679         return 1; /* terminate. */
3680 }
3681
3682 static void validate_panic(const char *const why)
3683 {
3684         DEBUG(0,("validating cache: would panic %s\n", why ));
3685         DEBUGADD(0, ("exiting instead (cache validation mode)\n"));
3686         exit(47);
3687 }
3688
3689 /***********************************************************************
3690  Try and validate every entry in the winbindd cache. If we fail here,
3691  delete the cache tdb and return non-zero.
3692 ***********************************************************************/
3693
3694 int winbindd_validate_cache(void)
3695 {
3696         int ret = -1;
3697         const char *tdb_path = cache_path("winbindd_cache.tdb");
3698         TDB_CONTEXT *tdb = NULL;
3699
3700         DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
3701         smb_panic_fn = validate_panic;
3702
3703
3704         tdb = tdb_open_log(tdb_path, 
3705                            WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
3706                            ( lp_winbind_offline_logon() 
3707                              ? TDB_DEFAULT 
3708                              : TDB_DEFAULT | TDB_CLEAR_IF_FIRST ),
3709                            O_RDWR|O_CREAT, 
3710                            0600);
3711         if (!tdb) {
3712                 DEBUG(0, ("winbindd_validate_cache: "
3713                           "error opening/initializing tdb\n"));
3714                 goto done;
3715         }
3716         tdb_close(tdb);
3717
3718         ret = tdb_validate_and_backup(tdb_path, cache_traverse_validate_fn);
3719
3720         if (ret != 0) {
3721                 DEBUG(10, ("winbindd_validate_cache: validation not successful.\n"));
3722                 DEBUGADD(10, ("removing tdb %s.\n", tdb_path));
3723                 unlink(tdb_path);
3724         }
3725
3726 done:
3727         DEBUG(10, ("winbindd_validate_cache: restoring panic function\n"));
3728         smb_panic_fn = smb_panic;
3729         return ret;
3730 }
3731
3732 /***********************************************************************
3733  Try and validate every entry in the winbindd cache.
3734 ***********************************************************************/
3735
3736 int winbindd_validate_cache_nobackup(void)
3737 {
3738         int ret = -1;
3739         const char *tdb_path = cache_path("winbindd_cache.tdb");
3740
3741         DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
3742         smb_panic_fn = validate_panic;
3743
3744
3745         if (wcache == NULL || wcache->tdb == NULL) {
3746                 ret = tdb_validate_open(tdb_path, cache_traverse_validate_fn);
3747         } else {
3748                 ret = tdb_validate(wcache->tdb, cache_traverse_validate_fn);
3749         }
3750
3751         if (ret != 0) {
3752                 DEBUG(10, ("winbindd_validate_cache_nobackup: validation not "
3753                            "successful.\n"));
3754         }
3755
3756         DEBUG(10, ("winbindd_validate_cache_nobackup: restoring panic "
3757                    "function\n"));
3758         smb_panic_fn = smb_panic;
3759         return ret;
3760 }
3761
3762 bool winbindd_cache_validate_and_initialize(void)
3763 {
3764         close_winbindd_cache();
3765
3766         if (lp_winbind_offline_logon()) {
3767                 if (winbindd_validate_cache() < 0) {
3768                         DEBUG(0, ("winbindd cache tdb corrupt and no backup "
3769                                   "could be restored.\n"));
3770                 }
3771         }
3772
3773         return initialize_winbindd_cache();
3774 }
3775
3776 /*********************************************************************
3777  ********************************************************************/
3778
3779 static bool add_wbdomain_to_tdc_array( struct winbindd_domain *new_dom,
3780                                        struct winbindd_tdc_domain **domains, 
3781                                        size_t *num_domains )
3782 {
3783         struct winbindd_tdc_domain *list = NULL;
3784         size_t idx;
3785         int i;
3786         bool set_only = false;
3787         
3788         /* don't allow duplicates */
3789
3790         idx = *num_domains;
3791         list = *domains;
3792         
3793         for ( i=0; i< (*num_domains); i++ ) {
3794                 if ( strequal( new_dom->name, list[i].domain_name ) ) {
3795                         DEBUG(10,("add_wbdomain_to_tdc_array: Found existing record for %s\n",
3796                                   new_dom->name));
3797                         idx = i;
3798                         set_only = true;
3799                         
3800                         break;
3801                 }
3802         }
3803
3804         if ( !set_only ) {
3805                 if ( !*domains ) {
3806                         list = TALLOC_ARRAY( NULL, struct winbindd_tdc_domain, 1 );
3807                         idx = 0;
3808                 } else {
3809                         list = TALLOC_REALLOC_ARRAY( *domains, *domains, 
3810                                                      struct winbindd_tdc_domain,  
3811                                                      (*num_domains)+1);
3812                         idx = *num_domains;             
3813                 }
3814
3815                 ZERO_STRUCT( list[idx] );
3816         }
3817
3818         if ( !list )
3819                 return false;
3820
3821         list[idx].domain_name = talloc_strdup( list, new_dom->name );
3822         list[idx].dns_name = talloc_strdup( list, new_dom->alt_name );
3823
3824         if ( !is_null_sid( &new_dom->sid ) ) {
3825                 sid_copy( &list[idx].sid, &new_dom->sid );
3826         } else {
3827                 sid_copy(&list[idx].sid, &global_sid_NULL);
3828         }
3829
3830         if ( new_dom->domain_flags != 0x0 )
3831                 list[idx].trust_flags = new_dom->domain_flags;  
3832
3833         if ( new_dom->domain_type != 0x0 )
3834                 list[idx].trust_type = new_dom->domain_type;
3835
3836         if ( new_dom->domain_trust_attribs != 0x0 )
3837                 list[idx].trust_attribs = new_dom->domain_trust_attribs;
3838         
3839         if ( !set_only ) {
3840                 *domains = list;
3841                 *num_domains = idx + 1; 
3842         }
3843
3844         return true;
3845 }
3846
3847 /*********************************************************************
3848  ********************************************************************/
3849
3850 static TDB_DATA make_tdc_key( const char *domain_name )
3851 {
3852         char *keystr = NULL;
3853         TDB_DATA key = { NULL, 0 };
3854         
3855         if ( !domain_name ) {
3856                 DEBUG(5,("make_tdc_key: Keyname workgroup is NULL!\n"));
3857                 return key;
3858         }
3859                
3860                 
3861         if (asprintf( &keystr, "TRUSTDOMCACHE/%s", domain_name ) == -1) {
3862                 return key;
3863         }
3864         key = string_term_tdb_data(keystr);
3865         
3866         return key;     
3867 }
3868
3869 /*********************************************************************
3870  ********************************************************************/
3871
3872 static int pack_tdc_domains( struct winbindd_tdc_domain *domains, 
3873                              size_t num_domains,
3874                              unsigned char **buf )
3875 {
3876         unsigned char *buffer = NULL;
3877         int len = 0;
3878         int buflen = 0;
3879         int i = 0;
3880
3881         DEBUG(10,("pack_tdc_domains: Packing %d trusted domains\n",
3882                   (int)num_domains));
3883         
3884         buflen = 0;
3885         
3886  again: 
3887         len = 0;
3888         
3889         /* Store the number of array items first */
3890         len += tdb_pack( buffer+len, buflen-len, "d", 
3891                          num_domains );
3892
3893         /* now pack each domain trust record */
3894         for ( i=0; i<num_domains; i++ ) {
3895
3896                 fstring tmp;
3897
3898                 if ( buflen > 0 ) {
3899                         DEBUG(10,("pack_tdc_domains: Packing domain %s (%s)\n",
3900                                   domains[i].domain_name,
3901                                   domains[i].dns_name ? domains[i].dns_name : "UNKNOWN" ));
3902                 }
3903                 
3904                 len += tdb_pack( buffer+len, buflen-len, "fffddd",
3905                                  domains[i].domain_name,
3906                                  domains[i].dns_name,
3907                                  sid_to_fstring(tmp, &domains[i].sid),
3908                                  domains[i].trust_flags,
3909                                  domains[i].trust_attribs,
3910                                  domains[i].trust_type );
3911         }
3912
3913         if ( buflen < len ) {
3914                 SAFE_FREE(buffer);
3915                 if ( (buffer = SMB_MALLOC_ARRAY(unsigned char, len)) == NULL ) {
3916                         DEBUG(0,("pack_tdc_domains: failed to alloc buffer!\n"));
3917                         buflen = -1;
3918                         goto done;
3919                 }
3920                 buflen = len;
3921                 goto again;
3922         }
3923
3924         *buf = buffer;  
3925         
3926  done:  
3927         return buflen;  
3928 }
3929
3930 /*********************************************************************
3931  ********************************************************************/
3932
3933 static size_t unpack_tdc_domains( unsigned char *buf, int buflen, 
3934                                   struct winbindd_tdc_domain **domains )
3935 {
3936         fstring domain_name, dns_name, sid_string;      
3937         uint32 type, attribs, flags;
3938         int num_domains;
3939         int len = 0;
3940         int i;
3941         struct winbindd_tdc_domain *list = NULL;
3942
3943         /* get the number of domains */
3944         len += tdb_unpack( buf+len, buflen-len, "d", &num_domains);
3945         if ( len == -1 ) {
3946                 DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));               
3947                 return 0;
3948         }
3949
3950         list = TALLOC_ARRAY( NULL, struct winbindd_tdc_domain, num_domains );
3951         if ( !list ) {
3952                 DEBUG(0,("unpack_tdc_domains: Failed to talloc() domain list!\n"));
3953                 return 0;               
3954         }
3955         
3956         for ( i=0; i<num_domains; i++ ) {
3957                 len += tdb_unpack( buf+len, buflen-len, "fffddd",
3958                                    domain_name,
3959                                    dns_name,
3960                                    sid_string,
3961                                    &flags,
3962                                    &attribs,
3963                                    &type );
3964
3965                 if ( len == -1 ) {
3966                         DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
3967                         TALLOC_FREE( list );                    
3968                         return 0;
3969                 }
3970
3971                 DEBUG(11,("unpack_tdc_domains: Unpacking domain %s (%s) "
3972                           "SID %s, flags = 0x%x, attribs = 0x%x, type = 0x%x\n",
3973                           domain_name, dns_name, sid_string,
3974                           flags, attribs, type));
3975                 
3976                 list[i].domain_name = talloc_strdup( list, domain_name );
3977                 list[i].dns_name = talloc_strdup( list, dns_name );
3978                 if ( !string_to_sid( &(list[i].sid), sid_string ) ) {                   
3979                         DEBUG(10,("unpack_tdc_domains: no SID for domain %s\n",
3980                                   domain_name));
3981                 }
3982                 list[i].trust_flags = flags;
3983                 list[i].trust_attribs = attribs;
3984                 list[i].trust_type = type;
3985         }
3986
3987         *domains = list;
3988         
3989         return num_domains;
3990 }
3991
3992 /*********************************************************************
3993  ********************************************************************/
3994
3995 static bool wcache_tdc_store_list( struct winbindd_tdc_domain *domains, size_t num_domains )
3996 {
3997         TDB_DATA key = make_tdc_key( lp_workgroup() );   
3998         TDB_DATA data = { NULL, 0 };
3999         int ret;
4000         
4001         if ( !key.dptr )
4002                 return false;
4003         
4004         /* See if we were asked to delete the cache entry */
4005
4006         if ( !domains ) {
4007                 ret = tdb_delete( wcache->tdb, key );
4008                 goto done;
4009         }
4010         
4011         data.dsize = pack_tdc_domains( domains, num_domains, &data.dptr );
4012         
4013         if ( !data.dptr ) {
4014                 ret = -1;
4015                 goto done;
4016         }
4017                 
4018         ret = tdb_store( wcache->tdb, key, data, 0 );
4019
4020  done:
4021         SAFE_FREE( data.dptr );
4022         SAFE_FREE( key.dptr );
4023         
4024         return ( ret != -1 );   
4025 }
4026
4027 /*********************************************************************
4028  ********************************************************************/
4029
4030 bool wcache_tdc_fetch_list( struct winbindd_tdc_domain **domains, size_t *num_domains )
4031 {
4032         TDB_DATA key = make_tdc_key( lp_workgroup() );
4033         TDB_DATA data = { NULL, 0 };
4034
4035         *domains = NULL;        
4036         *num_domains = 0;       
4037
4038         if ( !key.dptr )
4039                 return false;
4040         
4041         data = tdb_fetch( wcache->tdb, key );
4042
4043         SAFE_FREE( key.dptr );
4044         
4045         if ( !data.dptr ) 
4046                 return false;
4047         
4048         *num_domains = unpack_tdc_domains( data.dptr, data.dsize, domains );
4049
4050         SAFE_FREE( data.dptr );
4051         
4052         if ( !*domains )
4053                 return false;
4054
4055         return true;
4056 }
4057
4058 /*********************************************************************
4059  ********************************************************************/
4060
4061 bool wcache_tdc_add_domain( struct winbindd_domain *domain )
4062 {
4063         struct winbindd_tdc_domain *dom_list = NULL;
4064         size_t num_domains = 0;
4065         bool ret = false;
4066
4067         DEBUG(10,("wcache_tdc_add_domain: Adding domain %s (%s), SID %s, "
4068                   "flags = 0x%x, attributes = 0x%x, type = 0x%x\n",
4069                   domain->name, domain->alt_name, 
4070                   sid_string_dbg(&domain->sid),
4071                   domain->domain_flags,
4072                   domain->domain_trust_attribs,
4073                   domain->domain_type));        
4074         
4075         if ( !init_wcache() ) {
4076                 return false;
4077         }
4078         
4079         /* fetch the list */
4080
4081         wcache_tdc_fetch_list( &dom_list, &num_domains );
4082         
4083         /* add the new domain */
4084
4085         if ( !add_wbdomain_to_tdc_array( domain, &dom_list, &num_domains ) ) {
4086                 goto done;              
4087         }       
4088
4089         /* pack the domain */
4090
4091         if ( !wcache_tdc_store_list( dom_list, num_domains ) ) {
4092                 goto done;              
4093         }
4094         
4095         /* Success */
4096
4097         ret = true;
4098  done:
4099         TALLOC_FREE( dom_list );
4100         
4101         return ret;     
4102 }
4103
4104 /*********************************************************************
4105  ********************************************************************/
4106
4107 struct winbindd_tdc_domain * wcache_tdc_fetch_domain( TALLOC_CTX *ctx, const char *name )
4108 {
4109         struct winbindd_tdc_domain *dom_list = NULL;
4110         size_t num_domains = 0;
4111         int i;
4112         struct winbindd_tdc_domain *d = NULL;   
4113
4114         DEBUG(10,("wcache_tdc_fetch_domain: Searching for domain %s\n", name));
4115
4116         if ( !init_wcache() ) {
4117                 return false;
4118         }
4119         
4120         /* fetch the list */
4121
4122         wcache_tdc_fetch_list( &dom_list, &num_domains );
4123         
4124         for ( i=0; i<num_domains; i++ ) {
4125                 if ( strequal(name, dom_list[i].domain_name) ||
4126                      strequal(name, dom_list[i].dns_name) )
4127                 {
4128                         DEBUG(10,("wcache_tdc_fetch_domain: Found domain %s\n",
4129                                   name));
4130                         
4131                         d = TALLOC_P( ctx, struct winbindd_tdc_domain );
4132                         if ( !d )
4133                                 break;                  
4134                         
4135                         d->domain_name = talloc_strdup( d, dom_list[i].domain_name );
4136                         d->dns_name = talloc_strdup( d, dom_list[i].dns_name );
4137                         sid_copy( &d->sid, &dom_list[i].sid );
4138                         d->trust_flags   = dom_list[i].trust_flags;
4139                         d->trust_type    = dom_list[i].trust_type;
4140                         d->trust_attribs = dom_list[i].trust_attribs;
4141
4142                         break;
4143                 }
4144         }
4145
4146         TALLOC_FREE( dom_list );
4147         
4148         return d;       
4149 }
4150
4151
4152 /*********************************************************************
4153  ********************************************************************/
4154
4155 void wcache_tdc_clear( void )
4156 {
4157         if ( !init_wcache() )
4158                 return;
4159
4160         wcache_tdc_store_list( NULL, 0 );
4161         
4162         return; 
4163 }
4164
4165
4166 /*********************************************************************
4167  ********************************************************************/
4168
4169 static void wcache_save_user_pwinfo(struct winbindd_domain *domain, 
4170                                     NTSTATUS status,
4171                                     const DOM_SID *user_sid,
4172                                     const char *homedir,
4173                                     const char *shell,
4174                                     const char *gecos,
4175                                     uint32 gid)
4176 {
4177         struct cache_entry *centry;
4178         fstring tmp;
4179
4180         if ( (centry = centry_start(domain, status)) == NULL )
4181                 return;
4182
4183         centry_put_string( centry, homedir );
4184         centry_put_string( centry, shell );
4185         centry_put_string( centry, gecos );
4186         centry_put_uint32( centry, gid );
4187         
4188         centry_end(centry, "NSS/PWINFO/%s", sid_to_fstring(tmp, user_sid) );
4189
4190         DEBUG(10,("wcache_save_user_pwinfo: %s\n", sid_string_dbg(user_sid) ));
4191
4192         centry_free(centry);
4193 }
4194
4195 NTSTATUS nss_get_info_cached( struct winbindd_domain *domain, 
4196                               const DOM_SID *user_sid,
4197                               TALLOC_CTX *ctx,
4198                               ADS_STRUCT *ads, LDAPMessage *msg,
4199                               char **homedir, char **shell, char **gecos,
4200                               gid_t *p_gid)
4201 {
4202         struct winbind_cache *cache = get_cache(domain);
4203         struct cache_entry *centry = NULL;
4204         NTSTATUS nt_status;
4205         fstring tmp;
4206
4207         if (!cache->tdb)
4208                 goto do_query;
4209
4210         centry = wcache_fetch(cache, domain, "NSS/PWINFO/%s",
4211                               sid_to_fstring(tmp, user_sid));
4212         
4213         if (!centry)
4214                 goto do_query;
4215
4216         *homedir = centry_string( centry, ctx );
4217         *shell   = centry_string( centry, ctx );
4218         *gecos   = centry_string( centry, ctx );
4219         *p_gid   = centry_uint32( centry );     
4220
4221         centry_free(centry);
4222
4223         DEBUG(10,("nss_get_info_cached: [Cached] - user_sid %s\n",
4224                   sid_string_dbg(user_sid)));
4225
4226         return NT_STATUS_OK;
4227
4228 do_query:
4229         
4230         nt_status = nss_get_info( domain->name, user_sid, ctx, ads, msg, 
4231                                   homedir, shell, gecos, p_gid );
4232
4233         DEBUG(10, ("nss_get_info returned %s\n", nt_errstr(nt_status)));
4234
4235         if ( NT_STATUS_IS_OK(nt_status) ) {
4236                 DEBUG(10, ("result:\n\thomedir = '%s'\n", *homedir));
4237                 DEBUGADD(10, ("\tshell = '%s'\n", *shell));
4238                 DEBUGADD(10, ("\tgecos = '%s'\n", *gecos));
4239                 DEBUGADD(10, ("\tgid = '%u'\n", *p_gid));
4240
4241                 wcache_save_user_pwinfo( domain, nt_status, user_sid,
4242                                          *homedir, *shell, *gecos, *p_gid );
4243         }       
4244
4245         if ( NT_STATUS_EQUAL( nt_status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND ) ) {
4246                 DEBUG(5,("nss_get_info_cached: Setting domain %s offline\n",
4247                          domain->name ));
4248                 set_domain_offline( domain );
4249         }
4250
4251         return nt_status;       
4252 }
4253
4254
4255 /* the cache backend methods are exposed via this structure */
4256 struct winbindd_methods cache_methods = {
4257         true,
4258         query_user_list,
4259         enum_dom_groups,
4260         enum_local_groups,
4261         name_to_sid,
4262         sid_to_name,
4263         rids_to_names,
4264         query_user,
4265         lookup_usergroups,
4266         lookup_useraliases,
4267         lookup_groupmem,
4268         sequence_number,
4269         lockout_policy,
4270         password_policy,
4271         trusted_domains
4272 };