s3: separate tdb validation code out into its own source file
[sfrench/samba-autobuild/.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 extern bool opt_nocache;
38 #ifdef HAVE_ADS
39 extern struct winbindd_methods ads_methods;
40 #endif
41 extern struct winbindd_methods builtin_passdb_methods;
42
43 /*
44  * JRA. KEEP THIS LIST UP TO DATE IF YOU ADD CACHE ENTRIES.
45  * Here are the list of entry types that are *not* stored
46  * as form struct cache_entry in the cache.
47  */
48
49 static const char *non_centry_keys[] = {
50         "SEQNUM/",
51         "DR/",
52         "DE/",
53         "WINBINDD_OFFLINE",
54         WINBINDD_CACHE_VERSION_KEYSTR,
55         NULL
56 };
57
58 /************************************************************************
59  Is this key a non-centry type ?
60 ************************************************************************/
61
62 static bool is_non_centry_key(TDB_DATA kbuf)
63 {
64         int i;
65
66         if (kbuf.dptr == NULL || kbuf.dsize == 0) {
67                 return false;
68         }
69         for (i = 0; non_centry_keys[i] != NULL; i++) {
70                 size_t namelen = strlen(non_centry_keys[i]);
71                 if (kbuf.dsize < namelen) {
72                         continue;
73                 }
74                 if (strncmp(non_centry_keys[i], (const char *)kbuf.dptr, namelen) == 0) {
75                         return true;
76                 }
77         }
78         return false;
79 }
80
81 /* Global online/offline state - False when online. winbindd starts up online
82    and sets this to true if the first query fails and there's an entry in
83    the cache tdb telling us to stay offline. */
84
85 static bool global_winbindd_offline_state;
86
87 struct winbind_cache {
88         TDB_CONTEXT *tdb;
89 };
90
91 struct cache_entry {
92         NTSTATUS status;
93         uint32 sequence_number;
94         uint8 *data;
95         uint32 len, ofs;
96 };
97
98 void (*smb_panic_fn)(const char *const why) = smb_panic;
99
100 #define WINBINDD_MAX_CACHE_SIZE (50*1024*1024)
101
102 static struct winbind_cache *wcache;
103
104 void winbindd_check_cache_size(time_t t)
105 {
106         static time_t last_check_time;
107         struct stat st;
108
109         if (last_check_time == (time_t)0)
110                 last_check_time = t;
111
112         if (t - last_check_time < 60 && t - last_check_time > 0)
113                 return;
114
115         if (wcache == NULL || wcache->tdb == NULL) {
116                 DEBUG(0, ("Unable to check size of tdb cache - cache not open !\n"));
117                 return;
118         }
119
120         if (fstat(tdb_fd(wcache->tdb), &st) == -1) {
121                 DEBUG(0, ("Unable to check size of tdb cache %s!\n", strerror(errno) ));
122                 return;
123         }
124
125         if (st.st_size > WINBINDD_MAX_CACHE_SIZE) {
126                 DEBUG(10,("flushing cache due to size (%lu) > (%lu)\n",
127                         (unsigned long)st.st_size,
128                         (unsigned long)WINBINDD_MAX_CACHE_SIZE));
129                 wcache_flush_cache();
130         }
131 }
132
133 /* get the winbind_cache structure */
134 static struct winbind_cache *get_cache(struct winbindd_domain *domain)
135 {
136         struct winbind_cache *ret = wcache;
137
138         /* We have to know what type of domain we are dealing with first. */
139
140         if (domain->internal) {
141                 domain->backend = &builtin_passdb_methods;
142                 domain->initialized = True;
143         }
144         if ( !domain->initialized ) {
145                 init_dc_connection( domain );
146         }
147
148         /* 
149            OK.  listen up becasue I'm only going to say this once.
150            We have the following scenarios to consider
151            (a) trusted AD domains on a Samba DC,
152            (b) trusted AD domains and we are joined to a non-kerberos domain
153            (c) trusted AD domains and we are joined to a kerberos (AD) domain
154
155            For (a) we can always contact the trusted domain using krb5 
156            since we have the domain trust account password
157
158            For (b) we can only use RPC since we have no way of 
159            getting a krb5 ticket in our own domain
160
161            For (c) we can always use krb5 since we have a kerberos trust
162
163            --jerry
164          */
165
166         if (!domain->backend) {
167 #ifdef HAVE_ADS
168                 struct winbindd_domain *our_domain = domain;
169
170                 /* find our domain first so we can figure out if we 
171                    are joined to a kerberized domain */
172
173                 if ( !domain->primary )
174                         our_domain = find_our_domain();
175
176                 if ((our_domain->active_directory || IS_DC)
177                     && domain->active_directory
178                     && !lp_winbind_rpc_only()) {
179                         DEBUG(5,("get_cache: Setting ADS methods for domain %s\n", domain->name));
180                         domain->backend = &ads_methods;
181                 } else {
182 #endif  /* HAVE_ADS */
183                         DEBUG(5,("get_cache: Setting MS-RPC methods for domain %s\n", domain->name));
184                         domain->backend = &reconnect_methods;
185 #ifdef HAVE_ADS
186                 }
187 #endif  /* HAVE_ADS */
188         }
189
190         if (ret)
191                 return ret;
192         
193         ret = SMB_XMALLOC_P(struct winbind_cache);
194         ZERO_STRUCTP(ret);
195
196         wcache = ret;
197         wcache_flush_cache();
198
199         return ret;
200 }
201
202 /*
203   free a centry structure
204 */
205 static void centry_free(struct cache_entry *centry)
206 {
207         if (!centry)
208                 return;
209         SAFE_FREE(centry->data);
210         free(centry);
211 }
212
213 static bool centry_check_bytes(struct cache_entry *centry, size_t nbytes)
214 {
215         if (centry->len - centry->ofs < nbytes) {
216                 DEBUG(0,("centry corruption? needed %u bytes, have %d\n", 
217                          (unsigned int)nbytes,
218                          centry->len - centry->ofs));
219                 return false;
220         }
221         return true;
222 }
223
224 /*
225   pull a uint32 from a cache entry 
226 */
227 static uint32 centry_uint32(struct cache_entry *centry)
228 {
229         uint32 ret;
230
231         if (!centry_check_bytes(centry, 4)) {
232                 smb_panic_fn("centry_uint32");
233         }
234         ret = IVAL(centry->data, centry->ofs);
235         centry->ofs += 4;
236         return ret;
237 }
238
239 /*
240   pull a uint16 from a cache entry 
241 */
242 static uint16 centry_uint16(struct cache_entry *centry)
243 {
244         uint16 ret;
245         if (!centry_check_bytes(centry, 2)) {
246                 smb_panic_fn("centry_uint16");
247         }
248         ret = CVAL(centry->data, centry->ofs);
249         centry->ofs += 2;
250         return ret;
251 }
252
253 /*
254   pull a uint8 from a cache entry 
255 */
256 static uint8 centry_uint8(struct cache_entry *centry)
257 {
258         uint8 ret;
259         if (!centry_check_bytes(centry, 1)) {
260                 smb_panic_fn("centry_uint8");
261         }
262         ret = CVAL(centry->data, centry->ofs);
263         centry->ofs += 1;
264         return ret;
265 }
266
267 /*
268   pull a NTTIME from a cache entry 
269 */
270 static NTTIME centry_nttime(struct cache_entry *centry)
271 {
272         NTTIME ret;
273         if (!centry_check_bytes(centry, 8)) {
274                 smb_panic_fn("centry_nttime");
275         }
276         ret = IVAL(centry->data, centry->ofs);
277         centry->ofs += 4;
278         ret += (uint64_t)IVAL(centry->data, centry->ofs) << 32;
279         centry->ofs += 4;
280         return ret;
281 }
282
283 /*
284   pull a time_t from a cache entry. time_t stored portably as a 64-bit time.
285 */
286 static time_t centry_time(struct cache_entry *centry)
287 {
288         return (time_t)centry_nttime(centry);
289 }
290
291 /* pull a string from a cache entry, using the supplied
292    talloc context 
293 */
294 static char *centry_string(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
295 {
296         uint32 len;
297         char *ret;
298
299         len = centry_uint8(centry);
300
301         if (len == 0xFF) {
302                 /* a deliberate NULL string */
303                 return NULL;
304         }
305
306         if (!centry_check_bytes(centry, (size_t)len)) {
307                 smb_panic_fn("centry_string");
308         }
309
310         ret = TALLOC_ARRAY(mem_ctx, char, len+1);
311         if (!ret) {
312                 smb_panic_fn("centry_string out of memory\n");
313         }
314         memcpy(ret,centry->data + centry->ofs, len);
315         ret[len] = 0;
316         centry->ofs += len;
317         return ret;
318 }
319
320 /* pull a hash16 from a cache entry, using the supplied
321    talloc context 
322 */
323 static char *centry_hash16(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
324 {
325         uint32 len;
326         char *ret;
327
328         len = centry_uint8(centry);
329
330         if (len != 16) {
331                 DEBUG(0,("centry corruption? hash len (%u) != 16\n", 
332                         len ));
333                 return NULL;
334         }
335
336         if (!centry_check_bytes(centry, 16)) {
337                 return NULL;
338         }
339
340         ret = TALLOC_ARRAY(mem_ctx, char, 16);
341         if (!ret) {
342                 smb_panic_fn("centry_hash out of memory\n");
343         }
344         memcpy(ret,centry->data + centry->ofs, 16);
345         centry->ofs += 16;
346         return ret;
347 }
348
349 /* pull a sid from a cache entry, using the supplied
350    talloc context 
351 */
352 static bool centry_sid(struct cache_entry *centry, TALLOC_CTX *mem_ctx, DOM_SID *sid)
353 {
354         char *sid_string;
355         sid_string = centry_string(centry, mem_ctx);
356         if ((sid_string == NULL) || (!string_to_sid(sid, sid_string))) {
357                 return false;
358         }
359         return true;
360 }
361
362
363 /*
364   pull a NTSTATUS from a cache entry
365 */
366 static NTSTATUS centry_ntstatus(struct cache_entry *centry)
367 {
368         NTSTATUS status;
369
370         status = NT_STATUS(centry_uint32(centry));
371         return status;
372 }
373
374
375 /* the server is considered down if it can't give us a sequence number */
376 static bool wcache_server_down(struct winbindd_domain *domain)
377 {
378         bool ret;
379
380         if (!wcache->tdb)
381                 return false;
382
383         ret = (domain->sequence_number == DOM_SEQUENCE_NONE);
384
385         if (ret)
386                 DEBUG(10,("wcache_server_down: server for Domain %s down\n", 
387                         domain->name ));
388         return ret;
389 }
390
391 static NTSTATUS fetch_cache_seqnum( struct winbindd_domain *domain, time_t now )
392 {
393         TDB_DATA data;
394         fstring key;
395         uint32 time_diff;
396         
397         if (!wcache->tdb) {
398                 DEBUG(10,("fetch_cache_seqnum: tdb == NULL\n"));
399                 return NT_STATUS_UNSUCCESSFUL;
400         }
401                 
402         fstr_sprintf( key, "SEQNUM/%s", domain->name );
403         
404         data = tdb_fetch_bystring( wcache->tdb, key );
405         if ( !data.dptr || data.dsize!=8 ) {
406                 DEBUG(10,("fetch_cache_seqnum: invalid data size key [%s]\n", key ));
407                 return NT_STATUS_UNSUCCESSFUL;
408         }
409         
410         domain->sequence_number = IVAL(data.dptr, 0);
411         domain->last_seq_check  = IVAL(data.dptr, 4);
412         
413         SAFE_FREE(data.dptr);
414
415         /* have we expired? */
416         
417         time_diff = now - domain->last_seq_check;
418         if ( time_diff > lp_winbind_cache_time() ) {
419                 DEBUG(10,("fetch_cache_seqnum: timeout [%s][%u @ %u]\n",
420                         domain->name, domain->sequence_number,
421                         (uint32)domain->last_seq_check));
422                 return NT_STATUS_UNSUCCESSFUL;
423         }
424
425         DEBUG(10,("fetch_cache_seqnum: success [%s][%u @ %u]\n", 
426                 domain->name, domain->sequence_number, 
427                 (uint32)domain->last_seq_check));
428
429         return NT_STATUS_OK;
430 }
431
432 static NTSTATUS store_cache_seqnum( struct winbindd_domain *domain )
433 {
434         TDB_DATA data;
435         fstring key_str;
436         uint8 buf[8];
437         
438         if (!wcache->tdb) {
439                 DEBUG(10,("store_cache_seqnum: tdb == NULL\n"));
440                 return NT_STATUS_UNSUCCESSFUL;
441         }
442                 
443         fstr_sprintf( key_str, "SEQNUM/%s", domain->name );
444         
445         SIVAL(buf, 0, domain->sequence_number);
446         SIVAL(buf, 4, domain->last_seq_check);
447         data.dptr = buf;
448         data.dsize = 8;
449         
450         if ( tdb_store_bystring( wcache->tdb, key_str, data, TDB_REPLACE) == -1 ) {
451                 DEBUG(10,("store_cache_seqnum: tdb_store fail key [%s]\n", key_str ));
452                 return NT_STATUS_UNSUCCESSFUL;
453         }
454
455         DEBUG(10,("store_cache_seqnum: success [%s][%u @ %u]\n", 
456                 domain->name, domain->sequence_number, 
457                 (uint32)domain->last_seq_check));
458         
459         return NT_STATUS_OK;
460 }
461
462 /*
463   refresh the domain sequence number. If force is true
464   then always refresh it, no matter how recently we fetched it
465 */
466
467 static void refresh_sequence_number(struct winbindd_domain *domain, bool force)
468 {
469         NTSTATUS status;
470         unsigned time_diff;
471         time_t t = time(NULL);
472         unsigned cache_time = lp_winbind_cache_time();
473
474         if ( IS_DOMAIN_OFFLINE(domain) ) {
475                 return;
476         }
477         
478         get_cache( domain );
479
480 #if 0   /* JERRY -- disable as the default cache time is now 5 minutes */
481         /* trying to reconnect is expensive, don't do it too often */
482         if (domain->sequence_number == DOM_SEQUENCE_NONE) {
483                 cache_time *= 8;
484         }
485 #endif
486
487         time_diff = t - domain->last_seq_check;
488
489         /* see if we have to refetch the domain sequence number */
490         if (!force && (time_diff < cache_time) &&
491                         (domain->sequence_number != DOM_SEQUENCE_NONE) &&
492                         NT_STATUS_IS_OK(domain->last_status)) {
493                 DEBUG(10, ("refresh_sequence_number: %s time ok\n", domain->name));
494                 goto done;
495         }
496         
497         /* try to get the sequence number from the tdb cache first */
498         /* this will update the timestamp as well */
499         
500         status = fetch_cache_seqnum( domain, t );
501         if (NT_STATUS_IS_OK(status) &&
502                         (domain->sequence_number != DOM_SEQUENCE_NONE) &&
503                         NT_STATUS_IS_OK(domain->last_status)) {
504                 goto done;
505         }
506
507         /* important! make sure that we know if this is a native 
508            mode domain or not.  And that we can contact it. */
509
510         if ( winbindd_can_contact_domain( domain ) ) {          
511                 status = domain->backend->sequence_number(domain, 
512                                                           &domain->sequence_number);
513         } else {
514                 /* just use the current time */
515                 status = NT_STATUS_OK;
516                 domain->sequence_number = time(NULL);
517         }
518
519
520         /* the above call could have set our domain->backend to NULL when
521          * coming from offline to online mode, make sure to reinitialize the
522          * backend - Guenther */
523         get_cache( domain );
524
525         if (!NT_STATUS_IS_OK(status)) {
526                 DEBUG(10,("refresh_sequence_number: failed with %s\n", nt_errstr(status)));
527                 domain->sequence_number = DOM_SEQUENCE_NONE;
528         }
529         
530         domain->last_status = status;
531         domain->last_seq_check = time(NULL);
532         
533         /* save the new sequence number in the cache */
534         store_cache_seqnum( domain );
535
536 done:
537         DEBUG(10, ("refresh_sequence_number: %s seq number is now %d\n", 
538                    domain->name, domain->sequence_number));
539
540         return;
541 }
542
543 /*
544   decide if a cache entry has expired
545 */
546 static bool centry_expired(struct winbindd_domain *domain, const char *keystr, struct cache_entry *centry)
547 {
548         /* If we've been told to be offline - stay in that state... */
549         if (lp_winbind_offline_logon() && global_winbindd_offline_state) {
550                 DEBUG(10,("centry_expired: Key %s for domain %s valid as winbindd is globally offline.\n",
551                         keystr, domain->name ));
552                 return false;
553         }
554
555         /* when the domain is offline return the cached entry.
556          * This deals with transient offline states... */
557
558         if (!domain->online) {
559                 DEBUG(10,("centry_expired: Key %s for domain %s valid as domain is offline.\n",
560                         keystr, domain->name ));
561                 return false;
562         }
563
564         /* if the server is OK and our cache entry came from when it was down then
565            the entry is invalid */
566         if ((domain->sequence_number != DOM_SEQUENCE_NONE) &&  
567             (centry->sequence_number == DOM_SEQUENCE_NONE)) {
568                 DEBUG(10,("centry_expired: Key %s for domain %s invalid sequence.\n",
569                         keystr, domain->name ));
570                 return true;
571         }
572
573         /* if the server is down or the cache entry is not older than the
574            current sequence number then it is OK */
575         if (wcache_server_down(domain) || 
576             centry->sequence_number == domain->sequence_number) {
577                 DEBUG(10,("centry_expired: Key %s for domain %s is good.\n",
578                         keystr, domain->name ));
579                 return false;
580         }
581
582         DEBUG(10,("centry_expired: Key %s for domain %s expired\n",
583                 keystr, domain->name ));
584
585         /* it's expired */
586         return true;
587 }
588
589 static struct cache_entry *wcache_fetch_raw(char *kstr)
590 {
591         TDB_DATA data;
592         struct cache_entry *centry;
593         TDB_DATA key;
594
595         key = string_tdb_data(kstr);
596         data = tdb_fetch(wcache->tdb, key);
597         if (!data.dptr) {
598                 /* a cache miss */
599                 return NULL;
600         }
601
602         centry = SMB_XMALLOC_P(struct cache_entry);
603         centry->data = (unsigned char *)data.dptr;
604         centry->len = data.dsize;
605         centry->ofs = 0;
606
607         if (centry->len < 8) {
608                 /* huh? corrupt cache? */
609                 DEBUG(10,("wcache_fetch_raw: Corrupt cache for key %s (len < 8) ?\n", kstr));
610                 centry_free(centry);
611                 return NULL;
612         }
613         
614         centry->status = centry_ntstatus(centry);
615         centry->sequence_number = centry_uint32(centry);
616
617         return centry;
618 }
619
620 /*
621   fetch an entry from the cache, with a varargs key. auto-fetch the sequence
622   number and return status
623 */
624 static struct cache_entry *wcache_fetch(struct winbind_cache *cache, 
625                                         struct winbindd_domain *domain,
626                                         const char *format, ...) PRINTF_ATTRIBUTE(3,4);
627 static struct cache_entry *wcache_fetch(struct winbind_cache *cache, 
628                                         struct winbindd_domain *domain,
629                                         const char *format, ...)
630 {
631         va_list ap;
632         char *kstr;
633         struct cache_entry *centry;
634
635         if (opt_nocache) {
636                 return NULL;
637         }
638
639         refresh_sequence_number(domain, false);
640
641         va_start(ap, format);
642         smb_xvasprintf(&kstr, format, ap);
643         va_end(ap);
644
645         centry = wcache_fetch_raw(kstr);
646         if (centry == NULL) {
647                 free(kstr);
648                 return NULL;
649         }
650
651         if (centry_expired(domain, kstr, centry)) {
652
653                 DEBUG(10,("wcache_fetch: entry %s expired for domain %s\n",
654                          kstr, domain->name ));
655
656                 centry_free(centry);
657                 free(kstr);
658                 return NULL;
659         }
660
661         DEBUG(10,("wcache_fetch: returning entry %s for domain %s\n",
662                  kstr, domain->name ));
663
664         free(kstr);
665         return centry;
666 }
667
668 static void wcache_delete(const char *format, ...) PRINTF_ATTRIBUTE(1,2);
669 static void wcache_delete(const char *format, ...)
670 {
671         va_list ap;
672         char *kstr;
673         TDB_DATA key;
674
675         va_start(ap, format);
676         smb_xvasprintf(&kstr, format, ap);
677         va_end(ap);
678
679         key = string_tdb_data(kstr);
680
681         tdb_delete(wcache->tdb, key);
682         free(kstr);
683 }
684
685 /*
686   make sure we have at least len bytes available in a centry 
687 */
688 static void centry_expand(struct cache_entry *centry, uint32 len)
689 {
690         if (centry->len - centry->ofs >= len)
691                 return;
692         centry->len *= 2;
693         centry->data = SMB_REALLOC_ARRAY(centry->data, unsigned char,
694                                          centry->len);
695         if (!centry->data) {
696                 DEBUG(0,("out of memory: needed %d bytes in centry_expand\n", centry->len));
697                 smb_panic_fn("out of memory in centry_expand");
698         }
699 }
700
701 /*
702   push a uint32 into a centry 
703 */
704 static void centry_put_uint32(struct cache_entry *centry, uint32 v)
705 {
706         centry_expand(centry, 4);
707         SIVAL(centry->data, centry->ofs, v);
708         centry->ofs += 4;
709 }
710
711 /*
712   push a uint16 into a centry 
713 */
714 static void centry_put_uint16(struct cache_entry *centry, uint16 v)
715 {
716         centry_expand(centry, 2);
717         SIVAL(centry->data, centry->ofs, v);
718         centry->ofs += 2;
719 }
720
721 /*
722   push a uint8 into a centry 
723 */
724 static void centry_put_uint8(struct cache_entry *centry, uint8 v)
725 {
726         centry_expand(centry, 1);
727         SCVAL(centry->data, centry->ofs, v);
728         centry->ofs += 1;
729 }
730
731 /* 
732    push a string into a centry 
733  */
734 static void centry_put_string(struct cache_entry *centry, const char *s)
735 {
736         int len;
737
738         if (!s) {
739                 /* null strings are marked as len 0xFFFF */
740                 centry_put_uint8(centry, 0xFF);
741                 return;
742         }
743
744         len = strlen(s);
745         /* can't handle more than 254 char strings. Truncating is probably best */
746         if (len > 254) {
747                 DEBUG(10,("centry_put_string: truncating len (%d) to: 254\n", len));
748                 len = 254;
749         }
750         centry_put_uint8(centry, len);
751         centry_expand(centry, len);
752         memcpy(centry->data + centry->ofs, s, len);
753         centry->ofs += len;
754 }
755
756 /* 
757    push a 16 byte hash into a centry - treat as 16 byte string.
758  */
759 static void centry_put_hash16(struct cache_entry *centry, const uint8 val[16])
760 {
761         centry_put_uint8(centry, 16);
762         centry_expand(centry, 16);
763         memcpy(centry->data + centry->ofs, val, 16);
764         centry->ofs += 16;
765 }
766
767 static void centry_put_sid(struct cache_entry *centry, const DOM_SID *sid) 
768 {
769         fstring sid_string;
770         centry_put_string(centry, sid_to_fstring(sid_string, sid));
771 }
772
773
774 /*
775   put NTSTATUS into a centry
776 */
777 static void centry_put_ntstatus(struct cache_entry *centry, NTSTATUS status)
778 {
779         uint32 status_value = NT_STATUS_V(status);
780         centry_put_uint32(centry, status_value);
781 }
782
783
784 /*
785   push a NTTIME into a centry 
786 */
787 static void centry_put_nttime(struct cache_entry *centry, NTTIME nt)
788 {
789         centry_expand(centry, 8);
790         SIVAL(centry->data, centry->ofs, nt & 0xFFFFFFFF);
791         centry->ofs += 4;
792         SIVAL(centry->data, centry->ofs, nt >> 32);
793         centry->ofs += 4;
794 }
795
796 /*
797   push a time_t into a centry - use a 64 bit size.
798   NTTIME here is being used as a convenient 64-bit size.
799 */
800 static void centry_put_time(struct cache_entry *centry, time_t t)
801 {
802         NTTIME nt = (NTTIME)t;
803         centry_put_nttime(centry, nt);
804 }
805
806 /*
807   start a centry for output. When finished, call centry_end()
808 */
809 struct cache_entry *centry_start(struct winbindd_domain *domain, NTSTATUS status)
810 {
811         struct cache_entry *centry;
812
813         if (!wcache->tdb)
814                 return NULL;
815
816         centry = SMB_XMALLOC_P(struct cache_entry);
817
818         centry->len = 8192; /* reasonable default */
819         centry->data = SMB_XMALLOC_ARRAY(uint8, centry->len);
820         centry->ofs = 0;
821         centry->sequence_number = domain->sequence_number;
822         centry_put_ntstatus(centry, status);
823         centry_put_uint32(centry, centry->sequence_number);
824         return centry;
825 }
826
827 /*
828   finish a centry and write it to the tdb
829 */
830 static void centry_end(struct cache_entry *centry, const char *format, ...) PRINTF_ATTRIBUTE(2,3);
831 static void centry_end(struct cache_entry *centry, const char *format, ...)
832 {
833         va_list ap;
834         char *kstr;
835         TDB_DATA key, data;
836
837         if (opt_nocache) {
838                 return;
839         }
840
841         va_start(ap, format);
842         smb_xvasprintf(&kstr, format, ap);
843         va_end(ap);
844
845         key = string_tdb_data(kstr);
846         data.dptr = centry->data;
847         data.dsize = centry->ofs;
848
849         tdb_store(wcache->tdb, key, data, TDB_REPLACE);
850         free(kstr);
851 }
852
853 static void wcache_save_name_to_sid(struct winbindd_domain *domain, 
854                                     NTSTATUS status, const char *domain_name,
855                                     const char *name, const DOM_SID *sid, 
856                                     enum lsa_SidType type)
857 {
858         struct cache_entry *centry;
859         fstring uname;
860
861         centry = centry_start(domain, status);
862         if (!centry)
863                 return;
864         centry_put_uint32(centry, type);
865         centry_put_sid(centry, sid);
866         fstrcpy(uname, name);
867         strupper_m(uname);
868         centry_end(centry, "NS/%s/%s", domain_name, uname);
869         DEBUG(10,("wcache_save_name_to_sid: %s\\%s -> %s (%s)\n", domain_name,
870                   uname, sid_string_dbg(sid), nt_errstr(status)));
871         centry_free(centry);
872 }
873
874 static void wcache_save_sid_to_name(struct winbindd_domain *domain, NTSTATUS status, 
875                                     const DOM_SID *sid, const char *domain_name, const char *name, enum lsa_SidType type)
876 {
877         struct cache_entry *centry;
878         fstring sid_string;
879
880         centry = centry_start(domain, status);
881         if (!centry)
882                 return;
883
884         if (NT_STATUS_IS_OK(status)) {
885                 centry_put_uint32(centry, type);
886                 centry_put_string(centry, domain_name);
887                 centry_put_string(centry, name);
888         }
889
890         centry_end(centry, "SN/%s", sid_to_fstring(sid_string, sid));
891         DEBUG(10,("wcache_save_sid_to_name: %s -> %s (%s)\n", sid_string, 
892                   name, nt_errstr(status)));
893         centry_free(centry);
894 }
895
896
897 static void wcache_save_user(struct winbindd_domain *domain, NTSTATUS status, WINBIND_USERINFO *info)
898 {
899         struct cache_entry *centry;
900         fstring sid_string;
901
902         if (is_null_sid(&info->user_sid)) {
903                 return;
904         }
905
906         centry = centry_start(domain, status);
907         if (!centry)
908                 return;
909         centry_put_string(centry, info->acct_name);
910         centry_put_string(centry, info->full_name);
911         centry_put_string(centry, info->homedir);
912         centry_put_string(centry, info->shell);
913         centry_put_uint32(centry, info->primary_gid);
914         centry_put_sid(centry, &info->user_sid);
915         centry_put_sid(centry, &info->group_sid);
916         centry_end(centry, "U/%s", sid_to_fstring(sid_string,
917                                                   &info->user_sid));
918         DEBUG(10,("wcache_save_user: %s (acct_name %s)\n", sid_string, info->acct_name));
919         centry_free(centry);
920 }
921
922 static void wcache_save_lockout_policy(struct winbindd_domain *domain,
923                                        NTSTATUS status,
924                                        struct samr_DomInfo12 *lockout_policy)
925 {
926         struct cache_entry *centry;
927
928         centry = centry_start(domain, status);
929         if (!centry)
930                 return;
931
932         centry_put_nttime(centry, lockout_policy->lockout_duration);
933         centry_put_nttime(centry, lockout_policy->lockout_window);
934         centry_put_uint16(centry, lockout_policy->lockout_threshold);
935
936         centry_end(centry, "LOC_POL/%s", domain->name);
937
938         DEBUG(10,("wcache_save_lockout_policy: %s\n", domain->name));
939
940         centry_free(centry);
941 }
942
943
944
945 static void wcache_save_password_policy(struct winbindd_domain *domain,
946                                         NTSTATUS status,
947                                         struct samr_DomInfo1 *policy)
948 {
949         struct cache_entry *centry;
950
951         centry = centry_start(domain, status);
952         if (!centry)
953                 return;
954
955         centry_put_uint16(centry, policy->min_password_length);
956         centry_put_uint16(centry, policy->password_history_length);
957         centry_put_uint32(centry, policy->password_properties);
958         centry_put_nttime(centry, policy->max_password_age);
959         centry_put_nttime(centry, policy->min_password_age);
960
961         centry_end(centry, "PWD_POL/%s", domain->name);
962
963         DEBUG(10,("wcache_save_password_policy: %s\n", domain->name));
964
965         centry_free(centry);
966 }
967
968 /***************************************************************************
969  ***************************************************************************/
970
971 static void wcache_save_username_alias(struct winbindd_domain *domain,
972                                        NTSTATUS status,
973                                        const char *name, const char *alias)
974 {
975         struct cache_entry *centry;
976         fstring uname;
977
978         if ( (centry = centry_start(domain, status)) == NULL )
979                 return;
980
981         centry_put_string( centry, alias );
982
983         fstrcpy(uname, name);
984         strupper_m(uname);
985         centry_end(centry, "NSS/NA/%s", uname);
986
987         DEBUG(10,("wcache_save_username_alias: %s -> %s\n", name, alias ));
988
989         centry_free(centry);
990 }
991
992 static void wcache_save_alias_username(struct winbindd_domain *domain,
993                                        NTSTATUS status,
994                                        const char *alias, const char *name)
995 {
996         struct cache_entry *centry;
997         fstring uname;
998
999         if ( (centry = centry_start(domain, status)) == NULL )
1000                 return;
1001
1002         centry_put_string( centry, name );
1003
1004         fstrcpy(uname, alias);
1005         strupper_m(uname);
1006         centry_end(centry, "NSS/AN/%s", uname);
1007
1008         DEBUG(10,("wcache_save_alias_username: %s -> %s\n", alias, name ));
1009
1010         centry_free(centry);
1011 }
1012
1013 /***************************************************************************
1014  ***************************************************************************/
1015
1016 NTSTATUS resolve_username_to_alias( TALLOC_CTX *mem_ctx,
1017                                     struct winbindd_domain *domain,
1018                                     const char *name, char **alias )
1019 {
1020         struct winbind_cache *cache = get_cache(domain);
1021         struct cache_entry *centry = NULL;
1022         NTSTATUS status;
1023         char *upper_name;
1024
1025         if ( domain->internal )
1026                 return NT_STATUS_NOT_SUPPORTED;
1027
1028         if (!cache->tdb)
1029                 goto do_query;
1030
1031         if ( (upper_name = SMB_STRDUP(name)) == NULL )
1032                 return NT_STATUS_NO_MEMORY;
1033         strupper_m(upper_name);
1034
1035         centry = wcache_fetch(cache, domain, "NSS/NA/%s", upper_name);
1036
1037         SAFE_FREE( upper_name );
1038
1039         if (!centry)
1040                 goto do_query;
1041
1042         status = centry->status;
1043
1044         if (!NT_STATUS_IS_OK(status)) {
1045                 centry_free(centry);
1046                 return status;
1047         }
1048
1049         *alias = centry_string( centry, mem_ctx );
1050
1051         centry_free(centry);
1052
1053         DEBUG(10,("resolve_username_to_alias: [Cached] - mapped %s to %s\n",
1054                   name, *alias ? *alias : "(none)"));
1055
1056         return (*alias) ? NT_STATUS_OK : NT_STATUS_OBJECT_NAME_NOT_FOUND;
1057
1058 do_query:
1059
1060         /* If its not in cache and we are offline, then fail */
1061
1062         if ( get_global_winbindd_state_offline() || !domain->online ) {
1063                 DEBUG(8,("resolve_username_to_alias: rejecting query "
1064                          "in offline mode\n"));
1065                 return NT_STATUS_NOT_FOUND;
1066         }
1067
1068         status = nss_map_to_alias( mem_ctx, domain->name, name, alias );
1069
1070         if ( NT_STATUS_IS_OK( status ) ) {
1071                 wcache_save_username_alias(domain, status, name, *alias);
1072         }
1073
1074         if ( NT_STATUS_EQUAL( status, NT_STATUS_NONE_MAPPED ) ) {
1075                 wcache_save_username_alias(domain, status, name, "(NULL)");
1076         }
1077
1078         DEBUG(5,("resolve_username_to_alias: backend query returned %s\n",
1079                  nt_errstr(status)));
1080
1081         if ( NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ) {
1082                 set_domain_offline( domain );
1083         }
1084
1085         return status;
1086 }
1087
1088 /***************************************************************************
1089  ***************************************************************************/
1090
1091 NTSTATUS resolve_alias_to_username( TALLOC_CTX *mem_ctx,
1092                                     struct winbindd_domain *domain,
1093                                     const char *alias, char **name )
1094 {
1095         struct winbind_cache *cache = get_cache(domain);
1096         struct cache_entry *centry = NULL;
1097         NTSTATUS status;
1098         char *upper_name;
1099
1100         if ( domain->internal )
1101                 return  NT_STATUS_NOT_SUPPORTED;
1102
1103         if (!cache->tdb)
1104                 goto do_query;
1105
1106         if ( (upper_name = SMB_STRDUP(alias)) == NULL )
1107                 return NT_STATUS_NO_MEMORY;
1108         strupper_m(upper_name);
1109
1110         centry = wcache_fetch(cache, domain, "NSS/AN/%s", upper_name);
1111
1112         SAFE_FREE( upper_name );
1113
1114         if (!centry)
1115                 goto do_query;
1116
1117         status = centry->status;
1118
1119         if (!NT_STATUS_IS_OK(status)) {
1120                 centry_free(centry);
1121                 return status;
1122         }
1123
1124         *name = centry_string( centry, mem_ctx );
1125
1126         centry_free(centry);
1127
1128         DEBUG(10,("resolve_alias_to_username: [Cached] - mapped %s to %s\n",
1129                   alias, *name ? *name : "(none)"));
1130
1131         return (*name) ? NT_STATUS_OK : NT_STATUS_OBJECT_NAME_NOT_FOUND;
1132
1133 do_query:
1134
1135         /* If its not in cache and we are offline, then fail */
1136
1137         if ( get_global_winbindd_state_offline() || !domain->online ) {
1138                 DEBUG(8,("resolve_alias_to_username: rejecting query "
1139                          "in offline mode\n"));
1140                 return NT_STATUS_NOT_FOUND;
1141         }
1142
1143         /* an alias cannot contain a domain prefix or '@' */
1144
1145         if (strchr(alias, '\\') || strchr(alias, '@')) {
1146                 DEBUG(10,("resolve_alias_to_username: skipping fully "
1147                           "qualified name %s\n", alias));
1148                 return NT_STATUS_OBJECT_NAME_INVALID;
1149         }
1150
1151         status = nss_map_from_alias( mem_ctx, domain->name, alias, name );
1152
1153         if ( NT_STATUS_IS_OK( status ) ) {
1154                 wcache_save_alias_username( domain, status, alias, *name );
1155         }
1156
1157         if (NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) {
1158                 wcache_save_alias_username(domain, status, alias, "(NULL)");
1159         }
1160
1161         DEBUG(5,("resolve_alias_to_username: backend query returned %s\n",
1162                  nt_errstr(status)));
1163
1164         if ( NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ) {
1165                 set_domain_offline( domain );
1166         }
1167
1168         return status;
1169 }
1170
1171 NTSTATUS wcache_cached_creds_exist(struct winbindd_domain *domain, const DOM_SID *sid)
1172 {
1173         struct winbind_cache *cache = get_cache(domain);
1174         TDB_DATA data;
1175         fstring key_str, tmp;
1176         uint32 rid;
1177
1178         if (!cache->tdb) {
1179                 return NT_STATUS_INTERNAL_DB_ERROR;
1180         }
1181
1182         if (is_null_sid(sid)) {
1183                 return NT_STATUS_INVALID_SID;
1184         }
1185
1186         if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1187                 return NT_STATUS_INVALID_SID;
1188         }
1189
1190         fstr_sprintf(key_str, "CRED/%s", sid_to_fstring(tmp, sid));
1191
1192         data = tdb_fetch(cache->tdb, string_tdb_data(key_str));
1193         if (!data.dptr) {
1194                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1195         }
1196
1197         SAFE_FREE(data.dptr);
1198         return NT_STATUS_OK;
1199 }
1200
1201 /* Lookup creds for a SID - copes with old (unsalted) creds as well
1202    as new salted ones. */
1203
1204 NTSTATUS wcache_get_creds(struct winbindd_domain *domain, 
1205                           TALLOC_CTX *mem_ctx, 
1206                           const DOM_SID *sid,
1207                           const uint8 **cached_nt_pass,
1208                           const uint8 **cached_salt)
1209 {
1210         struct winbind_cache *cache = get_cache(domain);
1211         struct cache_entry *centry = NULL;
1212         NTSTATUS status;
1213         time_t t;
1214         uint32 rid;
1215         fstring tmp;
1216
1217         if (!cache->tdb) {
1218                 return NT_STATUS_INTERNAL_DB_ERROR;
1219         }
1220
1221         if (is_null_sid(sid)) {
1222                 return NT_STATUS_INVALID_SID;
1223         }
1224
1225         if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1226                 return NT_STATUS_INVALID_SID;
1227         }
1228
1229         /* Try and get a salted cred first. If we can't
1230            fall back to an unsalted cred. */
1231
1232         centry = wcache_fetch(cache, domain, "CRED/%s",
1233                               sid_to_fstring(tmp, sid));
1234         if (!centry) {
1235                 DEBUG(10,("wcache_get_creds: entry for [CRED/%s] not found\n", 
1236                           sid_string_dbg(sid)));
1237                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1238         }
1239
1240         t = centry_time(centry);
1241
1242         /* In the salted case this isn't actually the nt_hash itself,
1243            but the MD5 of the salt + nt_hash. Let the caller
1244            sort this out. It can tell as we only return the cached_salt
1245            if we are returning a salted cred. */
1246
1247         *cached_nt_pass = (const uint8 *)centry_hash16(centry, mem_ctx);
1248         if (*cached_nt_pass == NULL) {
1249                 fstring sidstr;
1250
1251                 sid_to_fstring(sidstr, sid);
1252
1253                 /* Bad (old) cred cache. Delete and pretend we
1254                    don't have it. */
1255                 DEBUG(0,("wcache_get_creds: bad entry for [CRED/%s] - deleting\n", 
1256                                 sidstr));
1257                 wcache_delete("CRED/%s", sidstr);
1258                 centry_free(centry);
1259                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1260         }
1261
1262         /* We only have 17 bytes more data in the salted cred case. */
1263         if (centry->len - centry->ofs == 17) {
1264                 *cached_salt = (const uint8 *)centry_hash16(centry, mem_ctx);
1265         } else {
1266                 *cached_salt = NULL;
1267         }
1268
1269         dump_data_pw("cached_nt_pass", *cached_nt_pass, NT_HASH_LEN);
1270         if (*cached_salt) {
1271                 dump_data_pw("cached_salt", *cached_salt, NT_HASH_LEN);
1272         }
1273
1274         status = centry->status;
1275
1276         DEBUG(10,("wcache_get_creds: [Cached] - cached creds for user %s status: %s\n",
1277                   sid_string_dbg(sid), nt_errstr(status) ));
1278
1279         centry_free(centry);
1280         return status;
1281 }
1282
1283 /* Store creds for a SID - only writes out new salted ones. */
1284
1285 NTSTATUS wcache_save_creds(struct winbindd_domain *domain, 
1286                            TALLOC_CTX *mem_ctx, 
1287                            const DOM_SID *sid, 
1288                            const uint8 nt_pass[NT_HASH_LEN])
1289 {
1290         struct cache_entry *centry;
1291         fstring sid_string;
1292         uint32 rid;
1293         uint8 cred_salt[NT_HASH_LEN];
1294         uint8 salted_hash[NT_HASH_LEN];
1295
1296         if (is_null_sid(sid)) {
1297                 return NT_STATUS_INVALID_SID;
1298         }
1299
1300         if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1301                 return NT_STATUS_INVALID_SID;
1302         }
1303
1304         centry = centry_start(domain, NT_STATUS_OK);
1305         if (!centry) {
1306                 return NT_STATUS_INTERNAL_DB_ERROR;
1307         }
1308
1309         dump_data_pw("nt_pass", nt_pass, NT_HASH_LEN);
1310
1311         centry_put_time(centry, time(NULL));
1312
1313         /* Create a salt and then salt the hash. */
1314         generate_random_buffer(cred_salt, NT_HASH_LEN);
1315         E_md5hash(cred_salt, nt_pass, salted_hash);
1316
1317         centry_put_hash16(centry, salted_hash);
1318         centry_put_hash16(centry, cred_salt);
1319         centry_end(centry, "CRED/%s", sid_to_fstring(sid_string, sid));
1320
1321         DEBUG(10,("wcache_save_creds: %s\n", sid_string));
1322
1323         centry_free(centry);
1324
1325         return NT_STATUS_OK;
1326 }
1327
1328
1329 /* Query display info. This is the basic user list fn */
1330 static NTSTATUS query_user_list(struct winbindd_domain *domain,
1331                                 TALLOC_CTX *mem_ctx,
1332                                 uint32 *num_entries, 
1333                                 WINBIND_USERINFO **info)
1334 {
1335         struct winbind_cache *cache = get_cache(domain);
1336         struct cache_entry *centry = NULL;
1337         NTSTATUS status;
1338         unsigned int i, retry;
1339
1340         if (!cache->tdb)
1341                 goto do_query;
1342
1343         centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
1344         if (!centry)
1345                 goto do_query;
1346
1347         *num_entries = centry_uint32(centry);
1348         
1349         if (*num_entries == 0)
1350                 goto do_cached;
1351
1352         (*info) = TALLOC_ARRAY(mem_ctx, WINBIND_USERINFO, *num_entries);
1353         if (! (*info)) {
1354                 smb_panic_fn("query_user_list out of memory");
1355         }
1356         for (i=0; i<(*num_entries); i++) {
1357                 (*info)[i].acct_name = centry_string(centry, mem_ctx);
1358                 (*info)[i].full_name = centry_string(centry, mem_ctx);
1359                 (*info)[i].homedir = centry_string(centry, mem_ctx);
1360                 (*info)[i].shell = centry_string(centry, mem_ctx);
1361                 centry_sid(centry, mem_ctx, &(*info)[i].user_sid);
1362                 centry_sid(centry, mem_ctx, &(*info)[i].group_sid);
1363         }
1364
1365 do_cached:      
1366         status = centry->status;
1367
1368         DEBUG(10,("query_user_list: [Cached] - cached list for domain %s status: %s\n",
1369                 domain->name, nt_errstr(status) ));
1370
1371         centry_free(centry);
1372         return status;
1373
1374 do_query:
1375         *num_entries = 0;
1376         *info = NULL;
1377
1378         /* Return status value returned by seq number check */
1379
1380         if (!NT_STATUS_IS_OK(domain->last_status))
1381                 return domain->last_status;
1382
1383         /* Put the query_user_list() in a retry loop.  There appears to be
1384          * some bug either with Windows 2000 or Samba's handling of large
1385          * rpc replies.  This manifests itself as sudden disconnection
1386          * at a random point in the enumeration of a large (60k) user list.
1387          * The retry loop simply tries the operation again. )-:  It's not
1388          * pretty but an acceptable workaround until we work out what the
1389          * real problem is. */
1390
1391         retry = 0;
1392         do {
1393
1394                 DEBUG(10,("query_user_list: [Cached] - doing backend query for list for domain %s\n",
1395                         domain->name ));
1396
1397                 status = domain->backend->query_user_list(domain, mem_ctx, num_entries, info);
1398                 if (!NT_STATUS_IS_OK(status)) {
1399                         DEBUG(3, ("query_user_list: returned 0x%08x, "
1400                                   "retrying\n", NT_STATUS_V(status)));
1401                 }
1402                 if (NT_STATUS_EQUAL(status, NT_STATUS_UNSUCCESSFUL)) {
1403                         DEBUG(3, ("query_user_list: flushing "
1404                                   "connection cache\n"));
1405                         invalidate_cm_connection(&domain->conn);
1406                 }
1407
1408         } while (NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_UNSUCCESSFUL) && 
1409                  (retry++ < 5));
1410
1411         /* and save it */
1412         refresh_sequence_number(domain, false);
1413         centry = centry_start(domain, status);
1414         if (!centry)
1415                 goto skip_save;
1416         centry_put_uint32(centry, *num_entries);
1417         for (i=0; i<(*num_entries); i++) {
1418                 centry_put_string(centry, (*info)[i].acct_name);
1419                 centry_put_string(centry, (*info)[i].full_name);
1420                 centry_put_string(centry, (*info)[i].homedir);
1421                 centry_put_string(centry, (*info)[i].shell);
1422                 centry_put_sid(centry, &(*info)[i].user_sid);
1423                 centry_put_sid(centry, &(*info)[i].group_sid);
1424                 if (domain->backend && domain->backend->consistent) {
1425                         /* when the backend is consistent we can pre-prime some mappings */
1426                         wcache_save_name_to_sid(domain, NT_STATUS_OK, 
1427                                                 domain->name,
1428                                                 (*info)[i].acct_name, 
1429                                                 &(*info)[i].user_sid,
1430                                                 SID_NAME_USER);
1431                         wcache_save_sid_to_name(domain, NT_STATUS_OK, 
1432                                                 &(*info)[i].user_sid,
1433                                                 domain->name,
1434                                                 (*info)[i].acct_name, 
1435                                                 SID_NAME_USER);
1436                         wcache_save_user(domain, NT_STATUS_OK, &(*info)[i]);
1437                 }
1438         }       
1439         centry_end(centry, "UL/%s", domain->name);
1440         centry_free(centry);
1441
1442 skip_save:
1443         return status;
1444 }
1445
1446 /* list all domain groups */
1447 static NTSTATUS enum_dom_groups(struct winbindd_domain *domain,
1448                                 TALLOC_CTX *mem_ctx,
1449                                 uint32 *num_entries, 
1450                                 struct acct_info **info)
1451 {
1452         struct winbind_cache *cache = get_cache(domain);
1453         struct cache_entry *centry = NULL;
1454         NTSTATUS status;
1455         unsigned int i;
1456
1457         if (!cache->tdb)
1458                 goto do_query;
1459
1460         centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
1461         if (!centry)
1462                 goto do_query;
1463
1464         *num_entries = centry_uint32(centry);
1465         
1466         if (*num_entries == 0)
1467                 goto do_cached;
1468
1469         (*info) = TALLOC_ARRAY(mem_ctx, struct acct_info, *num_entries);
1470         if (! (*info)) {
1471                 smb_panic_fn("enum_dom_groups out of memory");
1472         }
1473         for (i=0; i<(*num_entries); i++) {
1474                 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1475                 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1476                 (*info)[i].rid = centry_uint32(centry);
1477         }
1478
1479 do_cached:      
1480         status = centry->status;
1481
1482         DEBUG(10,("enum_dom_groups: [Cached] - cached list for domain %s status: %s\n",
1483                 domain->name, nt_errstr(status) ));
1484
1485         centry_free(centry);
1486         return status;
1487
1488 do_query:
1489         *num_entries = 0;
1490         *info = NULL;
1491
1492         /* Return status value returned by seq number check */
1493
1494         if (!NT_STATUS_IS_OK(domain->last_status))
1495                 return domain->last_status;
1496
1497         DEBUG(10,("enum_dom_groups: [Cached] - doing backend query for list for domain %s\n",
1498                 domain->name ));
1499
1500         status = domain->backend->enum_dom_groups(domain, mem_ctx, num_entries, info);
1501
1502         /* and save it */
1503         refresh_sequence_number(domain, false);
1504         centry = centry_start(domain, status);
1505         if (!centry)
1506                 goto skip_save;
1507         centry_put_uint32(centry, *num_entries);
1508         for (i=0; i<(*num_entries); i++) {
1509                 centry_put_string(centry, (*info)[i].acct_name);
1510                 centry_put_string(centry, (*info)[i].acct_desc);
1511                 centry_put_uint32(centry, (*info)[i].rid);
1512         }       
1513         centry_end(centry, "GL/%s/domain", domain->name);
1514         centry_free(centry);
1515
1516 skip_save:
1517         return status;
1518 }
1519
1520 /* list all domain groups */
1521 static NTSTATUS enum_local_groups(struct winbindd_domain *domain,
1522                                 TALLOC_CTX *mem_ctx,
1523                                 uint32 *num_entries, 
1524                                 struct acct_info **info)
1525 {
1526         struct winbind_cache *cache = get_cache(domain);
1527         struct cache_entry *centry = NULL;
1528         NTSTATUS status;
1529         unsigned int i;
1530
1531         if (!cache->tdb)
1532                 goto do_query;
1533
1534         centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
1535         if (!centry)
1536                 goto do_query;
1537
1538         *num_entries = centry_uint32(centry);
1539         
1540         if (*num_entries == 0)
1541                 goto do_cached;
1542
1543         (*info) = TALLOC_ARRAY(mem_ctx, struct acct_info, *num_entries);
1544         if (! (*info)) {
1545                 smb_panic_fn("enum_dom_groups out of memory");
1546         }
1547         for (i=0; i<(*num_entries); i++) {
1548                 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1549                 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1550                 (*info)[i].rid = centry_uint32(centry);
1551         }
1552
1553 do_cached:      
1554
1555         /* If we are returning cached data and the domain controller
1556            is down then we don't know whether the data is up to date
1557            or not.  Return NT_STATUS_MORE_PROCESSING_REQUIRED to
1558            indicate this. */
1559
1560         if (wcache_server_down(domain)) {
1561                 DEBUG(10, ("enum_local_groups: returning cached user list and server was down\n"));
1562                 status = NT_STATUS_MORE_PROCESSING_REQUIRED;
1563         } else
1564                 status = centry->status;
1565
1566         DEBUG(10,("enum_local_groups: [Cached] - cached list for domain %s status: %s\n",
1567                 domain->name, nt_errstr(status) ));
1568
1569         centry_free(centry);
1570         return status;
1571
1572 do_query:
1573         *num_entries = 0;
1574         *info = NULL;
1575
1576         /* Return status value returned by seq number check */
1577
1578         if (!NT_STATUS_IS_OK(domain->last_status))
1579                 return domain->last_status;
1580
1581         DEBUG(10,("enum_local_groups: [Cached] - doing backend query for list for domain %s\n",
1582                 domain->name ));
1583
1584         status = domain->backend->enum_local_groups(domain, mem_ctx, num_entries, info);
1585
1586         /* and save it */
1587         refresh_sequence_number(domain, false);
1588         centry = centry_start(domain, status);
1589         if (!centry)
1590                 goto skip_save;
1591         centry_put_uint32(centry, *num_entries);
1592         for (i=0; i<(*num_entries); i++) {
1593                 centry_put_string(centry, (*info)[i].acct_name);
1594                 centry_put_string(centry, (*info)[i].acct_desc);
1595                 centry_put_uint32(centry, (*info)[i].rid);
1596         }
1597         centry_end(centry, "GL/%s/local", domain->name);
1598         centry_free(centry);
1599
1600 skip_save:
1601         return status;
1602 }
1603
1604 /* convert a single name to a sid in a domain */
1605 static NTSTATUS name_to_sid(struct winbindd_domain *domain,
1606                             TALLOC_CTX *mem_ctx,
1607                             enum winbindd_cmd orig_cmd,
1608                             const char *domain_name,
1609                             const char *name,
1610                             DOM_SID *sid,
1611                             enum lsa_SidType *type)
1612 {
1613         struct winbind_cache *cache = get_cache(domain);
1614         struct cache_entry *centry = NULL;
1615         NTSTATUS status;
1616         fstring uname;
1617
1618         if (!cache->tdb)
1619                 goto do_query;
1620
1621         fstrcpy(uname, name);
1622         strupper_m(uname);
1623         centry = wcache_fetch(cache, domain, "NS/%s/%s", domain_name, uname);
1624         if (!centry)
1625                 goto do_query;
1626
1627         status = centry->status;
1628         if (NT_STATUS_IS_OK(status)) {
1629                 *type = (enum lsa_SidType)centry_uint32(centry);
1630                 centry_sid(centry, mem_ctx, sid);
1631         }
1632
1633         DEBUG(10,("name_to_sid: [Cached] - cached name for domain %s status: %s\n",
1634                 domain->name, nt_errstr(status) ));
1635
1636         centry_free(centry);
1637         return status;
1638
1639 do_query:
1640         ZERO_STRUCTP(sid);
1641
1642         /* If the seq number check indicated that there is a problem
1643          * with this DC, then return that status... except for
1644          * access_denied.  This is special because the dc may be in
1645          * "restrict anonymous = 1" mode, in which case it will deny
1646          * most unauthenticated operations, but *will* allow the LSA
1647          * name-to-sid that we try as a fallback. */
1648
1649         if (!(NT_STATUS_IS_OK(domain->last_status)
1650               || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1651                 return domain->last_status;
1652
1653         DEBUG(10,("name_to_sid: [Cached] - doing backend query for name for domain %s\n",
1654                 domain->name ));
1655
1656         status = domain->backend->name_to_sid(domain, mem_ctx, orig_cmd, 
1657                                               domain_name, name, sid, type);
1658
1659         /* and save it */
1660         refresh_sequence_number(domain, false);
1661
1662         if (domain->online &&
1663             (NT_STATUS_IS_OK(status) || NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED))) {
1664                 wcache_save_name_to_sid(domain, status, domain_name, name, sid, *type);
1665
1666                 /* Only save the reverse mapping if this was not a UPN */
1667                 if (!strchr(name, '@')) {
1668                         strupper_m(CONST_DISCARD(char *,domain_name));
1669                         strlower_m(CONST_DISCARD(char *,name));
1670                         wcache_save_sid_to_name(domain, status, sid, domain_name, name, *type);
1671                 }
1672         }
1673         
1674         return status;
1675 }
1676
1677 /* convert a sid to a user or group name. The sid is guaranteed to be in the domain
1678    given */
1679 static NTSTATUS sid_to_name(struct winbindd_domain *domain,
1680                             TALLOC_CTX *mem_ctx,
1681                             const DOM_SID *sid,
1682                             char **domain_name,
1683                             char **name,
1684                             enum lsa_SidType *type)
1685 {
1686         struct winbind_cache *cache = get_cache(domain);
1687         struct cache_entry *centry = NULL;
1688         NTSTATUS status;
1689         fstring sid_string;
1690
1691         if (!cache->tdb)
1692                 goto do_query;
1693
1694         centry = wcache_fetch(cache, domain, "SN/%s",
1695                               sid_to_fstring(sid_string, sid));
1696         if (!centry)
1697                 goto do_query;
1698
1699         status = centry->status;
1700         if (NT_STATUS_IS_OK(status)) {
1701                 *type = (enum lsa_SidType)centry_uint32(centry);
1702                 *domain_name = centry_string(centry, mem_ctx);
1703                 *name = centry_string(centry, mem_ctx);
1704         }
1705
1706         DEBUG(10,("sid_to_name: [Cached] - cached name for domain %s status: %s\n",
1707                 domain->name, nt_errstr(status) ));
1708
1709         centry_free(centry);
1710         return status;
1711
1712 do_query:
1713         *name = NULL;
1714         *domain_name = NULL;
1715
1716         /* If the seq number check indicated that there is a problem
1717          * with this DC, then return that status... except for
1718          * access_denied.  This is special because the dc may be in
1719          * "restrict anonymous = 1" mode, in which case it will deny
1720          * most unauthenticated operations, but *will* allow the LSA
1721          * sid-to-name that we try as a fallback. */
1722
1723         if (!(NT_STATUS_IS_OK(domain->last_status)
1724               || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1725                 return domain->last_status;
1726
1727         DEBUG(10,("sid_to_name: [Cached] - doing backend query for name for domain %s\n",
1728                 domain->name ));
1729
1730         status = domain->backend->sid_to_name(domain, mem_ctx, sid, domain_name, name, type);
1731
1732         /* and save it */
1733         refresh_sequence_number(domain, false);
1734         wcache_save_sid_to_name(domain, status, sid, *domain_name, *name, *type);
1735
1736         /* We can't save the name to sid mapping here, as with sid history a
1737          * later name2sid would give the wrong sid. */
1738
1739         return status;
1740 }
1741
1742 static NTSTATUS rids_to_names(struct winbindd_domain *domain,
1743                               TALLOC_CTX *mem_ctx,
1744                               const DOM_SID *domain_sid,
1745                               uint32 *rids,
1746                               size_t num_rids,
1747                               char **domain_name,
1748                               char ***names,
1749                               enum lsa_SidType **types)
1750 {
1751         struct winbind_cache *cache = get_cache(domain);
1752         size_t i;
1753         NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
1754         bool have_mapped;
1755         bool have_unmapped;
1756
1757         *domain_name = NULL;
1758         *names = NULL;
1759         *types = NULL;
1760
1761         if (!cache->tdb) {
1762                 goto do_query;
1763         }
1764
1765         if (num_rids == 0) {
1766                 return NT_STATUS_OK;
1767         }
1768
1769         *names = TALLOC_ARRAY(mem_ctx, char *, num_rids);
1770         *types = TALLOC_ARRAY(mem_ctx, enum lsa_SidType, num_rids);
1771
1772         if ((*names == NULL) || (*types == NULL)) {
1773                 result = NT_STATUS_NO_MEMORY;
1774                 goto error;
1775         }
1776
1777         have_mapped = have_unmapped = false;
1778
1779         for (i=0; i<num_rids; i++) {
1780                 DOM_SID sid;
1781                 struct cache_entry *centry;
1782                 fstring tmp;
1783
1784                 if (!sid_compose(&sid, domain_sid, rids[i])) {
1785                         result = NT_STATUS_INTERNAL_ERROR;
1786                         goto error;
1787                 }
1788
1789                 centry = wcache_fetch(cache, domain, "SN/%s",
1790                                       sid_to_fstring(tmp, &sid));
1791                 if (!centry) {
1792                         goto do_query;
1793                 }
1794
1795                 (*types)[i] = SID_NAME_UNKNOWN;
1796                 (*names)[i] = talloc_strdup(*names, "");
1797
1798                 if (NT_STATUS_IS_OK(centry->status)) {
1799                         char *dom;
1800                         have_mapped = true;
1801                         (*types)[i] = (enum lsa_SidType)centry_uint32(centry);
1802
1803                         dom = centry_string(centry, mem_ctx);
1804                         if (*domain_name == NULL) {
1805                                 *domain_name = dom;
1806                         } else {
1807                                 talloc_free(dom);
1808                         }
1809
1810                         (*names)[i] = centry_string(centry, *names);
1811
1812                 } else if (NT_STATUS_EQUAL(centry->status, NT_STATUS_NONE_MAPPED)) {
1813                         have_unmapped = true;
1814
1815                 } else {
1816                         /* something's definitely wrong */
1817                         result = centry->status;
1818                         goto error;
1819                 }
1820
1821                 centry_free(centry);
1822         }
1823
1824         if (!have_mapped) {
1825                 return NT_STATUS_NONE_MAPPED;
1826         }
1827         if (!have_unmapped) {
1828                 return NT_STATUS_OK;
1829         }
1830         return STATUS_SOME_UNMAPPED;
1831
1832  do_query:
1833
1834         TALLOC_FREE(*names);
1835         TALLOC_FREE(*types);
1836
1837         result = domain->backend->rids_to_names(domain, mem_ctx, domain_sid,
1838                                                 rids, num_rids, domain_name,
1839                                                 names, types);
1840
1841         /*
1842           None of the queried rids has been found so save all negative entries
1843         */
1844         if (NT_STATUS_EQUAL(result, NT_STATUS_NONE_MAPPED)) {
1845                 for (i = 0; i < num_rids; i++) {
1846                         DOM_SID sid;
1847                         const char *name = "";
1848                         const enum lsa_SidType type = SID_NAME_UNKNOWN;
1849                         NTSTATUS status = NT_STATUS_NONE_MAPPED;
1850                         
1851                         if (!sid_compose(&sid, domain_sid, rids[i])) {
1852                                 return NT_STATUS_INTERNAL_ERROR;
1853                         }
1854
1855                         wcache_save_sid_to_name(domain, status, &sid, *domain_name,
1856                                                 name, type);
1857                 }
1858
1859                 return result;
1860         }
1861
1862         /*
1863           Some or all of the queried rids have been found.
1864         */
1865         if (!NT_STATUS_IS_OK(result) &&
1866             !NT_STATUS_EQUAL(result, STATUS_SOME_UNMAPPED)) {
1867                 return result;
1868         }
1869
1870         refresh_sequence_number(domain, false);
1871
1872         for (i=0; i<num_rids; i++) {
1873                 DOM_SID sid;
1874                 NTSTATUS status;
1875
1876                 if (!sid_compose(&sid, domain_sid, rids[i])) {
1877                         result = NT_STATUS_INTERNAL_ERROR;
1878                         goto error;
1879                 }
1880
1881                 status = (*types)[i] == SID_NAME_UNKNOWN ?
1882                         NT_STATUS_NONE_MAPPED : NT_STATUS_OK;
1883
1884                 wcache_save_sid_to_name(domain, status, &sid, *domain_name,
1885                                         (*names)[i], (*types)[i]);
1886         }
1887
1888         return result;
1889
1890  error:
1891         
1892         TALLOC_FREE(*names);
1893         TALLOC_FREE(*types);
1894         return result;
1895 }
1896
1897 /* Lookup user information from a rid */
1898 static NTSTATUS query_user(struct winbindd_domain *domain, 
1899                            TALLOC_CTX *mem_ctx, 
1900                            const DOM_SID *user_sid, 
1901                            WINBIND_USERINFO *info)
1902 {
1903         struct winbind_cache *cache = get_cache(domain);
1904         struct cache_entry *centry = NULL;
1905         NTSTATUS status;
1906         fstring tmp;
1907
1908         if (!cache->tdb)
1909                 goto do_query;
1910
1911         centry = wcache_fetch(cache, domain, "U/%s",
1912                               sid_to_fstring(tmp, user_sid));
1913         
1914         /* If we have an access denied cache entry and a cached info3 in the
1915            samlogon cache then do a query.  This will force the rpc back end
1916            to return the info3 data. */
1917
1918         if (NT_STATUS_V(domain->last_status) == NT_STATUS_V(NT_STATUS_ACCESS_DENIED) &&
1919             netsamlogon_cache_have(user_sid)) {
1920                 DEBUG(10, ("query_user: cached access denied and have cached info3\n"));
1921                 domain->last_status = NT_STATUS_OK;
1922                 centry_free(centry);
1923                 goto do_query;
1924         }
1925         
1926         if (!centry)
1927                 goto do_query;
1928         
1929         /* if status is not ok then this is a negative hit
1930            and the rest of the data doesn't matter */
1931         status = centry->status;
1932         if (NT_STATUS_IS_OK(status)) {
1933                 info->acct_name = centry_string(centry, mem_ctx);
1934                 info->full_name = centry_string(centry, mem_ctx);
1935                 info->homedir = centry_string(centry, mem_ctx);
1936                 info->shell = centry_string(centry, mem_ctx);
1937                 info->primary_gid = centry_uint32(centry);
1938                 centry_sid(centry, mem_ctx, &info->user_sid);
1939                 centry_sid(centry, mem_ctx, &info->group_sid);
1940         }
1941
1942         DEBUG(10,("query_user: [Cached] - cached info for domain %s status: %s\n",
1943                 domain->name, nt_errstr(status) ));
1944
1945         centry_free(centry);
1946         return status;
1947
1948 do_query:
1949         ZERO_STRUCTP(info);
1950
1951         /* Return status value returned by seq number check */
1952
1953         if (!NT_STATUS_IS_OK(domain->last_status))
1954                 return domain->last_status;
1955         
1956         DEBUG(10,("query_user: [Cached] - doing backend query for info for domain %s\n",
1957                 domain->name ));
1958
1959         status = domain->backend->query_user(domain, mem_ctx, user_sid, info);
1960
1961         /* and save it */
1962         refresh_sequence_number(domain, false);
1963         wcache_save_user(domain, status, info);
1964
1965         return status;
1966 }
1967
1968
1969 /* Lookup groups a user is a member of. */
1970 static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
1971                                   TALLOC_CTX *mem_ctx,
1972                                   const DOM_SID *user_sid, 
1973                                   uint32 *num_groups, DOM_SID **user_gids)
1974 {
1975         struct winbind_cache *cache = get_cache(domain);
1976         struct cache_entry *centry = NULL;
1977         NTSTATUS status;
1978         unsigned int i;
1979         fstring sid_string;
1980
1981         if (!cache->tdb)
1982                 goto do_query;
1983
1984         centry = wcache_fetch(cache, domain, "UG/%s",
1985                               sid_to_fstring(sid_string, user_sid));
1986         
1987         /* If we have an access denied cache entry and a cached info3 in the
1988            samlogon cache then do a query.  This will force the rpc back end
1989            to return the info3 data. */
1990
1991         if (NT_STATUS_V(domain->last_status) == NT_STATUS_V(NT_STATUS_ACCESS_DENIED) &&
1992             netsamlogon_cache_have(user_sid)) {
1993                 DEBUG(10, ("lookup_usergroups: cached access denied and have cached info3\n"));
1994                 domain->last_status = NT_STATUS_OK;
1995                 centry_free(centry);
1996                 goto do_query;
1997         }
1998         
1999         if (!centry)
2000                 goto do_query;
2001
2002         *num_groups = centry_uint32(centry);
2003         
2004         if (*num_groups == 0)
2005                 goto do_cached;
2006
2007         (*user_gids) = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_groups);
2008         if (! (*user_gids)) {
2009                 smb_panic_fn("lookup_usergroups out of memory");
2010         }
2011         for (i=0; i<(*num_groups); i++) {
2012                 centry_sid(centry, mem_ctx, &(*user_gids)[i]);
2013         }
2014
2015 do_cached:      
2016         status = centry->status;
2017
2018         DEBUG(10,("lookup_usergroups: [Cached] - cached info for domain %s status: %s\n",
2019                 domain->name, nt_errstr(status) ));
2020
2021         centry_free(centry);
2022         return status;
2023
2024 do_query:
2025         (*num_groups) = 0;
2026         (*user_gids) = NULL;
2027
2028         /* Return status value returned by seq number check */
2029
2030         if (!NT_STATUS_IS_OK(domain->last_status))
2031                 return domain->last_status;
2032
2033         DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info for domain %s\n",
2034                 domain->name ));
2035
2036         status = domain->backend->lookup_usergroups(domain, mem_ctx, user_sid, num_groups, user_gids);
2037
2038         if ( NT_STATUS_EQUAL(status, NT_STATUS_SYNCHRONIZATION_REQUIRED) )
2039                 goto skip_save;
2040         
2041         /* and save it */
2042         refresh_sequence_number(domain, false);
2043         centry = centry_start(domain, status);
2044         if (!centry)
2045                 goto skip_save;
2046
2047         centry_put_uint32(centry, *num_groups);
2048         for (i=0; i<(*num_groups); i++) {
2049                 centry_put_sid(centry, &(*user_gids)[i]);
2050         }       
2051
2052         centry_end(centry, "UG/%s", sid_to_fstring(sid_string, user_sid));
2053         centry_free(centry);
2054
2055 skip_save:
2056         return status;
2057 }
2058
2059 static NTSTATUS lookup_useraliases(struct winbindd_domain *domain,
2060                                    TALLOC_CTX *mem_ctx,
2061                                    uint32 num_sids, const DOM_SID *sids,
2062                                    uint32 *num_aliases, uint32 **alias_rids)
2063 {
2064         struct winbind_cache *cache = get_cache(domain);
2065         struct cache_entry *centry = NULL;
2066         NTSTATUS status;
2067         char *sidlist = talloc_strdup(mem_ctx, "");
2068         int i;
2069
2070         if (!cache->tdb)
2071                 goto do_query;
2072
2073         if (num_sids == 0) {
2074                 *num_aliases = 0;
2075                 *alias_rids = NULL;
2076                 return NT_STATUS_OK;
2077         }
2078
2079         /* We need to cache indexed by the whole list of SIDs, the aliases
2080          * resulting might come from any of the SIDs. */
2081
2082         for (i=0; i<num_sids; i++) {
2083                 fstring tmp;
2084                 sidlist = talloc_asprintf(mem_ctx, "%s/%s", sidlist,
2085                                           sid_to_fstring(tmp, &sids[i]));
2086                 if (sidlist == NULL)
2087                         return NT_STATUS_NO_MEMORY;
2088         }
2089
2090         centry = wcache_fetch(cache, domain, "UA%s", sidlist);
2091
2092         if (!centry)
2093                 goto do_query;
2094
2095         *num_aliases = centry_uint32(centry);
2096         *alias_rids = NULL;
2097
2098         if (*num_aliases) {
2099                 (*alias_rids) = TALLOC_ARRAY(mem_ctx, uint32, *num_aliases);
2100
2101                 if ((*alias_rids) == NULL) {
2102                         centry_free(centry);
2103                         return NT_STATUS_NO_MEMORY;
2104                 }
2105         } else {
2106                 (*alias_rids) = NULL;
2107         }
2108
2109         for (i=0; i<(*num_aliases); i++)
2110                 (*alias_rids)[i] = centry_uint32(centry);
2111
2112         status = centry->status;
2113
2114         DEBUG(10,("lookup_useraliases: [Cached] - cached info for domain: %s "
2115                   "status %s\n", domain->name, nt_errstr(status)));
2116
2117         centry_free(centry);
2118         return status;
2119
2120  do_query:
2121         (*num_aliases) = 0;
2122         (*alias_rids) = NULL;
2123
2124         if (!NT_STATUS_IS_OK(domain->last_status))
2125                 return domain->last_status;
2126
2127         DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info "
2128                   "for domain %s\n", domain->name ));
2129
2130         status = domain->backend->lookup_useraliases(domain, mem_ctx,
2131                                                      num_sids, sids,
2132                                                      num_aliases, alias_rids);
2133
2134         /* and save it */
2135         refresh_sequence_number(domain, false);
2136         centry = centry_start(domain, status);
2137         if (!centry)
2138                 goto skip_save;
2139         centry_put_uint32(centry, *num_aliases);
2140         for (i=0; i<(*num_aliases); i++)
2141                 centry_put_uint32(centry, (*alias_rids)[i]);
2142         centry_end(centry, "UA%s", sidlist);
2143         centry_free(centry);
2144
2145  skip_save:
2146         return status;
2147 }
2148
2149
2150 static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
2151                                 TALLOC_CTX *mem_ctx,
2152                                 const DOM_SID *group_sid, uint32 *num_names, 
2153                                 DOM_SID **sid_mem, char ***names, 
2154                                 uint32 **name_types)
2155 {
2156         struct winbind_cache *cache = get_cache(domain);
2157         struct cache_entry *centry = NULL;
2158         NTSTATUS status;
2159         unsigned int i;
2160         fstring sid_string;
2161
2162         if (!cache->tdb)
2163                 goto do_query;
2164
2165         centry = wcache_fetch(cache, domain, "GM/%s",
2166                               sid_to_fstring(sid_string, group_sid));
2167         if (!centry)
2168                 goto do_query;
2169
2170         *num_names = centry_uint32(centry);
2171         
2172         if (*num_names == 0)
2173                 goto do_cached;
2174
2175         (*sid_mem) = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_names);
2176         (*names) = TALLOC_ARRAY(mem_ctx, char *, *num_names);
2177         (*name_types) = TALLOC_ARRAY(mem_ctx, uint32, *num_names);
2178
2179         if (! (*sid_mem) || ! (*names) || ! (*name_types)) {
2180                 smb_panic_fn("lookup_groupmem out of memory");
2181         }
2182
2183         for (i=0; i<(*num_names); i++) {
2184                 centry_sid(centry, mem_ctx, &(*sid_mem)[i]);
2185                 (*names)[i] = centry_string(centry, mem_ctx);
2186                 (*name_types)[i] = centry_uint32(centry);
2187         }
2188
2189 do_cached:      
2190         status = centry->status;
2191
2192         DEBUG(10,("lookup_groupmem: [Cached] - cached info for domain %s status: %s\n",
2193                 domain->name, nt_errstr(status)));
2194
2195         centry_free(centry);
2196         return status;
2197
2198 do_query:
2199         (*num_names) = 0;
2200         (*sid_mem) = NULL;
2201         (*names) = NULL;
2202         (*name_types) = NULL;
2203         
2204         /* Return status value returned by seq number check */
2205
2206         if (!NT_STATUS_IS_OK(domain->last_status))
2207                 return domain->last_status;
2208
2209         DEBUG(10,("lookup_groupmem: [Cached] - doing backend query for info for domain %s\n",
2210                 domain->name ));
2211
2212         status = domain->backend->lookup_groupmem(domain, mem_ctx, group_sid, num_names, 
2213                                                   sid_mem, names, name_types);
2214
2215         /* and save it */
2216         refresh_sequence_number(domain, false);
2217         centry = centry_start(domain, status);
2218         if (!centry)
2219                 goto skip_save;
2220         centry_put_uint32(centry, *num_names);
2221         for (i=0; i<(*num_names); i++) {
2222                 centry_put_sid(centry, &(*sid_mem)[i]);
2223                 centry_put_string(centry, (*names)[i]);
2224                 centry_put_uint32(centry, (*name_types)[i]);
2225         }       
2226         centry_end(centry, "GM/%s", sid_to_fstring(sid_string, group_sid));
2227         centry_free(centry);
2228
2229 skip_save:
2230         return status;
2231 }
2232
2233 /* find the sequence number for a domain */
2234 static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
2235 {
2236         refresh_sequence_number(domain, false);
2237
2238         *seq = domain->sequence_number;
2239
2240         return NT_STATUS_OK;
2241 }
2242
2243 /* enumerate trusted domains 
2244  * (we need to have the list of trustdoms in the cache when we go offline) -
2245  * Guenther */
2246 static NTSTATUS trusted_domains(struct winbindd_domain *domain,
2247                                 TALLOC_CTX *mem_ctx,
2248                                 uint32 *num_domains,
2249                                 char ***names,
2250                                 char ***alt_names,
2251                                 DOM_SID **dom_sids)
2252 {
2253         struct winbind_cache *cache = get_cache(domain);
2254         struct cache_entry *centry = NULL;
2255         NTSTATUS status;
2256         int i;
2257  
2258         if (!cache->tdb)
2259                 goto do_query;
2260  
2261         centry = wcache_fetch(cache, domain, "TRUSTDOMS/%s", domain->name);
2262         
2263         if (!centry) {
2264                 goto do_query;
2265         }
2266  
2267         *num_domains = centry_uint32(centry);
2268         
2269         if (*num_domains) {
2270                 (*names)        = TALLOC_ARRAY(mem_ctx, char *, *num_domains);
2271                 (*alt_names)    = TALLOC_ARRAY(mem_ctx, char *, *num_domains);
2272                 (*dom_sids)     = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_domains);
2273  
2274                 if (! (*dom_sids) || ! (*names) || ! (*alt_names)) {
2275                         smb_panic_fn("trusted_domains out of memory");
2276                 }
2277         } else {
2278                 (*names) = NULL;
2279                 (*alt_names) = NULL;
2280                 (*dom_sids) = NULL;
2281         }
2282  
2283         for (i=0; i<(*num_domains); i++) {
2284                 (*names)[i] = centry_string(centry, mem_ctx);
2285                 (*alt_names)[i] = centry_string(centry, mem_ctx);
2286                 if (!centry_sid(centry, mem_ctx, &(*dom_sids)[i])) {
2287                         sid_copy(&(*dom_sids)[i], &global_sid_NULL);
2288                 }
2289         }
2290
2291         status = centry->status;
2292  
2293         DEBUG(10,("trusted_domains: [Cached] - cached info for domain %s (%d trusts) status: %s\n",
2294                 domain->name, *num_domains, nt_errstr(status) ));
2295  
2296         centry_free(centry);
2297         return status;
2298  
2299 do_query:
2300         (*num_domains) = 0;
2301         (*dom_sids) = NULL;
2302         (*names) = NULL;
2303         (*alt_names) = NULL;
2304  
2305         /* Return status value returned by seq number check */
2306
2307         if (!NT_STATUS_IS_OK(domain->last_status))
2308                 return domain->last_status;
2309         
2310         DEBUG(10,("trusted_domains: [Cached] - doing backend query for info for domain %s\n",
2311                 domain->name ));
2312  
2313         status = domain->backend->trusted_domains(domain, mem_ctx, num_domains,
2314                                                 names, alt_names, dom_sids);
2315
2316         /* no trusts gives NT_STATUS_NO_MORE_ENTRIES resetting to NT_STATUS_OK
2317          * so that the generic centry handling still applies correctly -
2318          * Guenther*/
2319
2320         if (!NT_STATUS_IS_ERR(status)) {
2321                 status = NT_STATUS_OK;
2322         }
2323
2324
2325 #if 0    /* Disabled as we want the trust dom list to be managed by
2326             the main parent and always to make the query.  --jerry */
2327
2328         /* and save it */
2329         refresh_sequence_number(domain, false);
2330  
2331         centry = centry_start(domain, status);
2332         if (!centry)
2333                 goto skip_save;
2334
2335         centry_put_uint32(centry, *num_domains);
2336
2337         for (i=0; i<(*num_domains); i++) {
2338                 centry_put_string(centry, (*names)[i]);
2339                 centry_put_string(centry, (*alt_names)[i]);
2340                 centry_put_sid(centry, &(*dom_sids)[i]);
2341         }
2342         
2343         centry_end(centry, "TRUSTDOMS/%s", domain->name);
2344  
2345         centry_free(centry);
2346  
2347 skip_save:
2348 #endif
2349
2350         return status;
2351 }       
2352
2353 /* get lockout policy */
2354 static NTSTATUS lockout_policy(struct winbindd_domain *domain,
2355                                TALLOC_CTX *mem_ctx,
2356                                struct samr_DomInfo12 *policy)
2357 {
2358         struct winbind_cache *cache = get_cache(domain);
2359         struct cache_entry *centry = NULL;
2360         NTSTATUS status;
2361
2362         if (!cache->tdb)
2363                 goto do_query;
2364
2365         centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
2366
2367         if (!centry)
2368                 goto do_query;
2369
2370         policy->lockout_duration = centry_nttime(centry);
2371         policy->lockout_window = centry_nttime(centry);
2372         policy->lockout_threshold = centry_uint16(centry);
2373
2374         status = centry->status;
2375
2376         DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2377                 domain->name, nt_errstr(status) ));
2378
2379         centry_free(centry);
2380         return status;
2381
2382 do_query:
2383         ZERO_STRUCTP(policy);
2384
2385         /* Return status value returned by seq number check */
2386
2387         if (!NT_STATUS_IS_OK(domain->last_status))
2388                 return domain->last_status;
2389
2390         DEBUG(10,("lockout_policy: [Cached] - doing backend query for info for domain %s\n",
2391                 domain->name ));
2392
2393         status = domain->backend->lockout_policy(domain, mem_ctx, policy);
2394
2395         /* and save it */
2396         refresh_sequence_number(domain, false);
2397         wcache_save_lockout_policy(domain, status, policy);
2398
2399         return status;
2400 }
2401
2402 /* get password policy */
2403 static NTSTATUS password_policy(struct winbindd_domain *domain,
2404                                 TALLOC_CTX *mem_ctx,
2405                                 struct samr_DomInfo1 *policy)
2406 {
2407         struct winbind_cache *cache = get_cache(domain);
2408         struct cache_entry *centry = NULL;
2409         NTSTATUS status;
2410
2411         if (!cache->tdb)
2412                 goto do_query;
2413
2414         centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
2415
2416         if (!centry)
2417                 goto do_query;
2418
2419         policy->min_password_length = centry_uint16(centry);
2420         policy->password_history_length = centry_uint16(centry);
2421         policy->password_properties = centry_uint32(centry);
2422         policy->max_password_age = centry_nttime(centry);
2423         policy->min_password_age = centry_nttime(centry);
2424
2425         status = centry->status;
2426
2427         DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2428                 domain->name, nt_errstr(status) ));
2429
2430         centry_free(centry);
2431         return status;
2432
2433 do_query:
2434         ZERO_STRUCTP(policy);
2435
2436         /* Return status value returned by seq number check */
2437
2438         if (!NT_STATUS_IS_OK(domain->last_status))
2439                 return domain->last_status;
2440
2441         DEBUG(10,("password_policy: [Cached] - doing backend query for info for domain %s\n",
2442                 domain->name ));
2443
2444         status = domain->backend->password_policy(domain, mem_ctx, policy);
2445
2446         /* and save it */
2447         refresh_sequence_number(domain, false);
2448         if (NT_STATUS_IS_OK(status)) {
2449                 wcache_save_password_policy(domain, status, policy);
2450         }
2451
2452         return status;
2453 }
2454
2455
2456 /* Invalidate cached user and group lists coherently */
2457
2458 static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, 
2459                        void *state)
2460 {
2461         if (strncmp((const char *)kbuf.dptr, "UL/", 3) == 0 ||
2462             strncmp((const char *)kbuf.dptr, "GL/", 3) == 0)
2463                 tdb_delete(the_tdb, kbuf);
2464
2465         return 0;
2466 }
2467
2468 /* Invalidate the getpwnam and getgroups entries for a winbindd domain */
2469
2470 void wcache_invalidate_samlogon(struct winbindd_domain *domain, 
2471                                 struct netr_SamInfo3 *info3)
2472 {
2473         DOM_SID sid;
2474         fstring key_str, sid_string;
2475         struct winbind_cache *cache;
2476
2477         /* dont clear cached U/SID and UG/SID entries when we want to logon
2478          * offline - gd */
2479
2480         if (lp_winbind_offline_logon()) {
2481                 return;
2482         }
2483
2484         if (!domain)
2485                 return;
2486
2487         cache = get_cache(domain);
2488
2489         if (!cache->tdb) {
2490                 return;
2491         }
2492
2493         sid_copy(&sid, info3->base.domain_sid);
2494         sid_append_rid(&sid, info3->base.rid);
2495
2496         /* Clear U/SID cache entry */
2497         fstr_sprintf(key_str, "U/%s", sid_to_fstring(sid_string, &sid));
2498         DEBUG(10, ("wcache_invalidate_samlogon: clearing %s\n", key_str));
2499         tdb_delete(cache->tdb, string_tdb_data(key_str));
2500
2501         /* Clear UG/SID cache entry */
2502         fstr_sprintf(key_str, "UG/%s", sid_to_fstring(sid_string, &sid));
2503         DEBUG(10, ("wcache_invalidate_samlogon: clearing %s\n", key_str));
2504         tdb_delete(cache->tdb, string_tdb_data(key_str));
2505
2506         /* Samba/winbindd never needs this. */
2507         netsamlogon_clear_cached_user(info3);
2508 }
2509
2510 bool wcache_invalidate_cache(void)
2511 {
2512         struct winbindd_domain *domain;
2513
2514         for (domain = domain_list(); domain; domain = domain->next) {
2515                 struct winbind_cache *cache = get_cache(domain);
2516
2517                 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
2518                            "entries for %s\n", domain->name));
2519                 if (cache) {
2520                         if (cache->tdb) {
2521                                 tdb_traverse(cache->tdb, traverse_fn, NULL);
2522                         } else {
2523                                 return false;
2524                         }
2525                 }
2526         }
2527         return true;
2528 }
2529
2530 bool init_wcache(void)
2531 {
2532         if (wcache == NULL) {
2533                 wcache = SMB_XMALLOC_P(struct winbind_cache);
2534                 ZERO_STRUCTP(wcache);
2535         }
2536
2537         if (wcache->tdb != NULL)
2538                 return true;
2539
2540         /* when working offline we must not clear the cache on restart */
2541         wcache->tdb = tdb_open_log(cache_path("winbindd_cache.tdb"),
2542                                 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE, 
2543                                 lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST), 
2544                                 O_RDWR|O_CREAT, 0600);
2545
2546         if (wcache->tdb == NULL) {
2547                 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
2548                 return false;
2549         }
2550
2551         return true;
2552 }
2553
2554 /************************************************************************
2555  This is called by the parent to initialize the cache file.
2556  We don't need sophisticated locking here as we know we're the
2557  only opener.
2558 ************************************************************************/
2559
2560 bool initialize_winbindd_cache(void)
2561 {
2562         bool cache_bad = true;
2563         uint32 vers;
2564
2565         if (!init_wcache()) {
2566                 DEBUG(0,("initialize_winbindd_cache: init_wcache failed.\n"));
2567                 return false;
2568         }
2569
2570         /* Check version number. */
2571         if (tdb_fetch_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, &vers) &&
2572                         vers == WINBINDD_CACHE_VERSION) {
2573                 cache_bad = false;
2574         }
2575
2576         if (cache_bad) {
2577                 DEBUG(0,("initialize_winbindd_cache: clearing cache "
2578                         "and re-creating with version number %d\n",
2579                         WINBINDD_CACHE_VERSION ));
2580
2581                 tdb_close(wcache->tdb);
2582                 wcache->tdb = NULL;
2583
2584                 if (unlink(cache_path("winbindd_cache.tdb")) == -1) {
2585                         DEBUG(0,("initialize_winbindd_cache: unlink %s failed %s ",
2586                                 cache_path("winbindd_cache.tdb"),
2587                                 strerror(errno) ));
2588                         return false;
2589                 }
2590                 if (!init_wcache()) {
2591                         DEBUG(0,("initialize_winbindd_cache: re-initialization "
2592                                         "init_wcache failed.\n"));
2593                         return false;
2594                 }
2595
2596                 /* Write the version. */
2597                 if (!tdb_store_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, WINBINDD_CACHE_VERSION)) {
2598                         DEBUG(0,("initialize_winbindd_cache: version number store failed %s\n",
2599                                 tdb_errorstr(wcache->tdb) ));
2600                         return false;
2601                 }
2602         }
2603
2604         tdb_close(wcache->tdb);
2605         wcache->tdb = NULL;
2606         return true;
2607 }
2608
2609 void close_winbindd_cache(void)
2610 {
2611         if (!wcache) {
2612                 return;
2613         }
2614         if (wcache->tdb) {
2615                 tdb_close(wcache->tdb);
2616                 wcache->tdb = NULL;
2617         }
2618 }
2619
2620 void cache_store_response(pid_t pid, struct winbindd_response *response)
2621 {
2622         fstring key_str;
2623
2624         if (!init_wcache())
2625                 return;
2626
2627         DEBUG(10, ("Storing response for pid %d, len %d\n",
2628                    pid, response->length));
2629
2630         fstr_sprintf(key_str, "DR/%d", pid);
2631         if (tdb_store(wcache->tdb, string_tdb_data(key_str), 
2632                       make_tdb_data((uint8 *)response, sizeof(*response)),
2633                       TDB_REPLACE) == -1)
2634                 return;
2635
2636         if (response->length == sizeof(*response))
2637                 return;
2638
2639         /* There's extra data */
2640
2641         DEBUG(10, ("Storing extra data: len=%d\n",
2642                    (int)(response->length - sizeof(*response))));
2643
2644         fstr_sprintf(key_str, "DE/%d", pid);
2645         if (tdb_store(wcache->tdb, string_tdb_data(key_str),
2646                       make_tdb_data((uint8 *)response->extra_data.data,
2647                                     response->length - sizeof(*response)),
2648                       TDB_REPLACE) == 0)
2649                 return;
2650
2651         /* We could not store the extra data, make sure the tdb does not
2652          * contain a main record with wrong dangling extra data */
2653
2654         fstr_sprintf(key_str, "DR/%d", pid);
2655         tdb_delete(wcache->tdb, string_tdb_data(key_str));
2656
2657         return;
2658 }
2659
2660 bool cache_retrieve_response(pid_t pid, struct winbindd_response * response)
2661 {
2662         TDB_DATA data;
2663         fstring key_str;
2664
2665         if (!init_wcache())
2666                 return false;
2667
2668         DEBUG(10, ("Retrieving response for pid %d\n", pid));
2669
2670         fstr_sprintf(key_str, "DR/%d", pid);
2671         data = tdb_fetch(wcache->tdb, string_tdb_data(key_str));
2672
2673         if (data.dptr == NULL)
2674                 return false;
2675
2676         if (data.dsize != sizeof(*response))
2677                 return false;
2678
2679         memcpy(response, data.dptr, data.dsize);
2680         SAFE_FREE(data.dptr);
2681
2682         if (response->length == sizeof(*response)) {
2683                 response->extra_data.data = NULL;
2684                 return true;
2685         }
2686
2687         /* There's extra data */
2688
2689         DEBUG(10, ("Retrieving extra data length=%d\n",
2690                    (int)(response->length - sizeof(*response))));
2691
2692         fstr_sprintf(key_str, "DE/%d", pid);
2693         data = tdb_fetch(wcache->tdb, string_tdb_data(key_str));
2694
2695         if (data.dptr == NULL) {
2696                 DEBUG(0, ("Did not find extra data\n"));
2697                 return false;
2698         }
2699
2700         if (data.dsize != (response->length - sizeof(*response))) {
2701                 DEBUG(0, ("Invalid extra data length: %d\n", (int)data.dsize));
2702                 SAFE_FREE(data.dptr);
2703                 return false;
2704         }
2705
2706         dump_data(11, (uint8 *)data.dptr, data.dsize);
2707
2708         response->extra_data.data = data.dptr;
2709         return true;
2710 }
2711
2712 void cache_cleanup_response(pid_t pid)
2713 {
2714         fstring key_str;
2715
2716         if (!init_wcache())
2717                 return;
2718
2719         fstr_sprintf(key_str, "DR/%d", pid);
2720         tdb_delete(wcache->tdb, string_tdb_data(key_str));
2721
2722         fstr_sprintf(key_str, "DE/%d", pid);
2723         tdb_delete(wcache->tdb, string_tdb_data(key_str));
2724
2725         return;
2726 }
2727
2728
2729 bool lookup_cached_sid(TALLOC_CTX *mem_ctx, const DOM_SID *sid,
2730                        char **domain_name, char **name,
2731                        enum lsa_SidType *type)
2732 {
2733         struct winbindd_domain *domain;
2734         struct winbind_cache *cache;
2735         struct cache_entry *centry = NULL;
2736         NTSTATUS status;
2737         fstring tmp;
2738
2739         domain = find_lookup_domain_from_sid(sid);
2740         if (domain == NULL) {
2741                 return false;
2742         }
2743
2744         cache = get_cache(domain);
2745
2746         if (cache->tdb == NULL) {
2747                 return false;
2748         }
2749
2750         centry = wcache_fetch(cache, domain, "SN/%s",
2751                               sid_to_fstring(tmp, sid));
2752         if (centry == NULL) {
2753                 return false;
2754         }
2755
2756         if (NT_STATUS_IS_OK(centry->status)) {
2757                 *type = (enum lsa_SidType)centry_uint32(centry);
2758                 *domain_name = centry_string(centry, mem_ctx);
2759                 *name = centry_string(centry, mem_ctx);
2760         }
2761
2762         status = centry->status;
2763         centry_free(centry);
2764         return NT_STATUS_IS_OK(status);
2765 }
2766
2767 bool lookup_cached_name(TALLOC_CTX *mem_ctx,
2768                         const char *domain_name,
2769                         const char *name,
2770                         DOM_SID *sid,
2771                         enum lsa_SidType *type)
2772 {
2773         struct winbindd_domain *domain;
2774         struct winbind_cache *cache;
2775         struct cache_entry *centry = NULL;
2776         NTSTATUS status;
2777         fstring uname;
2778         bool original_online_state;     
2779
2780         domain = find_lookup_domain_from_name(domain_name);
2781         if (domain == NULL) {
2782                 return false;
2783         }
2784
2785         cache = get_cache(domain);
2786
2787         if (cache->tdb == NULL) {
2788                 return false;
2789         }
2790
2791         fstrcpy(uname, name);
2792         strupper_m(uname);
2793         
2794         /* If we are doing a cached logon, temporarily set the domain
2795            offline so the cache won't expire the entry */
2796         
2797         original_online_state = domain->online;
2798         domain->online = false;
2799         centry = wcache_fetch(cache, domain, "NS/%s/%s", domain_name, uname);
2800         domain->online = original_online_state;
2801         
2802         if (centry == NULL) {
2803                 return false;
2804         }
2805
2806         if (NT_STATUS_IS_OK(centry->status)) {
2807                 *type = (enum lsa_SidType)centry_uint32(centry);
2808                 centry_sid(centry, mem_ctx, sid);
2809         }
2810
2811         status = centry->status;
2812         centry_free(centry);
2813         
2814         return NT_STATUS_IS_OK(status);
2815 }
2816
2817 void cache_name2sid(struct winbindd_domain *domain, 
2818                     const char *domain_name, const char *name,
2819                     enum lsa_SidType type, const DOM_SID *sid)
2820 {
2821         refresh_sequence_number(domain, false);
2822         wcache_save_name_to_sid(domain, NT_STATUS_OK, domain_name, name,
2823                                 sid, type);
2824 }
2825
2826 /*
2827  * The original idea that this cache only contains centries has
2828  * been blurred - now other stuff gets put in here. Ensure we
2829  * ignore these things on cleanup.
2830  */
2831
2832 static int traverse_fn_cleanup(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, 
2833                                TDB_DATA dbuf, void *state)
2834 {
2835         struct cache_entry *centry;
2836
2837         if (is_non_centry_key(kbuf)) {
2838                 return 0;
2839         }
2840
2841         centry = wcache_fetch_raw((char *)kbuf.dptr);
2842         if (!centry) {
2843                 return 0;
2844         }
2845
2846         if (!NT_STATUS_IS_OK(centry->status)) {
2847                 DEBUG(10,("deleting centry %s\n", (const char *)kbuf.dptr));
2848                 tdb_delete(the_tdb, kbuf);
2849         }
2850
2851         centry_free(centry);
2852         return 0;
2853 }
2854
2855 /* flush the cache */
2856 void wcache_flush_cache(void)
2857 {
2858         if (!wcache)
2859                 return;
2860         if (wcache->tdb) {
2861                 tdb_close(wcache->tdb);
2862                 wcache->tdb = NULL;
2863         }
2864         if (opt_nocache)
2865                 return;
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 };