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