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