s3:winbind: Make wcache_sid_to_name externally visible
[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, WINBIND_USERINFO *info)
902 {
903         struct cache_entry *centry;
904         fstring sid_string;
905
906         if (is_null_sid(&info->user_sid)) {
907                 return;
908         }
909
910         centry = centry_start(domain, status);
911         if (!centry)
912                 return;
913         centry_put_string(centry, info->acct_name);
914         centry_put_string(centry, info->full_name);
915         centry_put_string(centry, info->homedir);
916         centry_put_string(centry, info->shell);
917         centry_put_uint32(centry, info->primary_gid);
918         centry_put_sid(centry, &info->user_sid);
919         centry_put_sid(centry, &info->group_sid);
920         centry_end(centry, "U/%s", sid_to_fstring(sid_string,
921                                                   &info->user_sid));
922         DEBUG(10,("wcache_save_user: %s (acct_name %s)\n", sid_string, info->acct_name));
923         centry_free(centry);
924 }
925
926 static void wcache_save_lockout_policy(struct winbindd_domain *domain,
927                                        NTSTATUS status,
928                                        struct samr_DomInfo12 *lockout_policy)
929 {
930         struct cache_entry *centry;
931
932         centry = centry_start(domain, status);
933         if (!centry)
934                 return;
935
936         centry_put_nttime(centry, lockout_policy->lockout_duration);
937         centry_put_nttime(centry, lockout_policy->lockout_window);
938         centry_put_uint16(centry, lockout_policy->lockout_threshold);
939
940         centry_end(centry, "LOC_POL/%s", domain->name);
941
942         DEBUG(10,("wcache_save_lockout_policy: %s\n", domain->name));
943
944         centry_free(centry);
945 }
946
947
948
949 static void wcache_save_password_policy(struct winbindd_domain *domain,
950                                         NTSTATUS status,
951                                         struct samr_DomInfo1 *policy)
952 {
953         struct cache_entry *centry;
954
955         centry = centry_start(domain, status);
956         if (!centry)
957                 return;
958
959         centry_put_uint16(centry, policy->min_password_length);
960         centry_put_uint16(centry, policy->password_history_length);
961         centry_put_uint32(centry, policy->password_properties);
962         centry_put_nttime(centry, policy->max_password_age);
963         centry_put_nttime(centry, policy->min_password_age);
964
965         centry_end(centry, "PWD_POL/%s", domain->name);
966
967         DEBUG(10,("wcache_save_password_policy: %s\n", domain->name));
968
969         centry_free(centry);
970 }
971
972 /***************************************************************************
973  ***************************************************************************/
974
975 static void wcache_save_username_alias(struct winbindd_domain *domain,
976                                        NTSTATUS status,
977                                        const char *name, const char *alias)
978 {
979         struct cache_entry *centry;
980         fstring uname;
981
982         if ( (centry = centry_start(domain, status)) == NULL )
983                 return;
984
985         centry_put_string( centry, alias );
986
987         fstrcpy(uname, name);
988         strupper_m(uname);
989         centry_end(centry, "NSS/NA/%s", uname);
990
991         DEBUG(10,("wcache_save_username_alias: %s -> %s\n", name, alias ));
992
993         centry_free(centry);
994 }
995
996 static void wcache_save_alias_username(struct winbindd_domain *domain,
997                                        NTSTATUS status,
998                                        const char *alias, const char *name)
999 {
1000         struct cache_entry *centry;
1001         fstring uname;
1002
1003         if ( (centry = centry_start(domain, status)) == NULL )
1004                 return;
1005
1006         centry_put_string( centry, name );
1007
1008         fstrcpy(uname, alias);
1009         strupper_m(uname);
1010         centry_end(centry, "NSS/AN/%s", uname);
1011
1012         DEBUG(10,("wcache_save_alias_username: %s -> %s\n", alias, name ));
1013
1014         centry_free(centry);
1015 }
1016
1017 /***************************************************************************
1018  ***************************************************************************/
1019
1020 NTSTATUS resolve_username_to_alias( TALLOC_CTX *mem_ctx,
1021                                     struct winbindd_domain *domain,
1022                                     const char *name, char **alias )
1023 {
1024         struct winbind_cache *cache = get_cache(domain);
1025         struct cache_entry *centry = NULL;
1026         NTSTATUS status;
1027         char *upper_name;
1028
1029         if ( domain->internal )
1030                 return NT_STATUS_NOT_SUPPORTED;
1031
1032         if (!cache->tdb)
1033                 goto do_query;
1034
1035         if ( (upper_name = SMB_STRDUP(name)) == NULL )
1036                 return NT_STATUS_NO_MEMORY;
1037         strupper_m(upper_name);
1038
1039         centry = wcache_fetch(cache, domain, "NSS/NA/%s", upper_name);
1040
1041         SAFE_FREE( upper_name );
1042
1043         if (!centry)
1044                 goto do_query;
1045
1046         status = centry->status;
1047
1048         if (!NT_STATUS_IS_OK(status)) {
1049                 centry_free(centry);
1050                 return status;
1051         }
1052
1053         *alias = centry_string( centry, mem_ctx );
1054
1055         centry_free(centry);
1056
1057         DEBUG(10,("resolve_username_to_alias: [Cached] - mapped %s to %s\n",
1058                   name, *alias ? *alias : "(none)"));
1059
1060         return (*alias) ? NT_STATUS_OK : NT_STATUS_OBJECT_NAME_NOT_FOUND;
1061
1062 do_query:
1063
1064         /* If its not in cache and we are offline, then fail */
1065
1066         if ( get_global_winbindd_state_offline() || !domain->online ) {
1067                 DEBUG(8,("resolve_username_to_alias: rejecting query "
1068                          "in offline mode\n"));
1069                 return NT_STATUS_NOT_FOUND;
1070         }
1071
1072         status = nss_map_to_alias( mem_ctx, domain->name, name, alias );
1073
1074         if ( NT_STATUS_IS_OK( status ) ) {
1075                 wcache_save_username_alias(domain, status, name, *alias);
1076         }
1077
1078         if ( NT_STATUS_EQUAL( status, NT_STATUS_NONE_MAPPED ) ) {
1079                 wcache_save_username_alias(domain, status, name, "(NULL)");
1080         }
1081
1082         DEBUG(5,("resolve_username_to_alias: backend query returned %s\n",
1083                  nt_errstr(status)));
1084
1085         if ( NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ) {
1086                 set_domain_offline( domain );
1087         }
1088
1089         return status;
1090 }
1091
1092 /***************************************************************************
1093  ***************************************************************************/
1094
1095 NTSTATUS resolve_alias_to_username( TALLOC_CTX *mem_ctx,
1096                                     struct winbindd_domain *domain,
1097                                     const char *alias, char **name )
1098 {
1099         struct winbind_cache *cache = get_cache(domain);
1100         struct cache_entry *centry = NULL;
1101         NTSTATUS status;
1102         char *upper_name;
1103
1104         if ( domain->internal )
1105                 return  NT_STATUS_NOT_SUPPORTED;
1106
1107         if (!cache->tdb)
1108                 goto do_query;
1109
1110         if ( (upper_name = SMB_STRDUP(alias)) == NULL )
1111                 return NT_STATUS_NO_MEMORY;
1112         strupper_m(upper_name);
1113
1114         centry = wcache_fetch(cache, domain, "NSS/AN/%s", upper_name);
1115
1116         SAFE_FREE( upper_name );
1117
1118         if (!centry)
1119                 goto do_query;
1120
1121         status = centry->status;
1122
1123         if (!NT_STATUS_IS_OK(status)) {
1124                 centry_free(centry);
1125                 return status;
1126         }
1127
1128         *name = centry_string( centry, mem_ctx );
1129
1130         centry_free(centry);
1131
1132         DEBUG(10,("resolve_alias_to_username: [Cached] - mapped %s to %s\n",
1133                   alias, *name ? *name : "(none)"));
1134
1135         return (*name) ? NT_STATUS_OK : NT_STATUS_OBJECT_NAME_NOT_FOUND;
1136
1137 do_query:
1138
1139         /* If its not in cache and we are offline, then fail */
1140
1141         if ( get_global_winbindd_state_offline() || !domain->online ) {
1142                 DEBUG(8,("resolve_alias_to_username: rejecting query "
1143                          "in offline mode\n"));
1144                 return NT_STATUS_NOT_FOUND;
1145         }
1146
1147         /* an alias cannot contain a domain prefix or '@' */
1148
1149         if (strchr(alias, '\\') || strchr(alias, '@')) {
1150                 DEBUG(10,("resolve_alias_to_username: skipping fully "
1151                           "qualified name %s\n", alias));
1152                 return NT_STATUS_OBJECT_NAME_INVALID;
1153         }
1154
1155         status = nss_map_from_alias( mem_ctx, domain->name, alias, name );
1156
1157         if ( NT_STATUS_IS_OK( status ) ) {
1158                 wcache_save_alias_username( domain, status, alias, *name );
1159         }
1160
1161         if (NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) {
1162                 wcache_save_alias_username(domain, status, alias, "(NULL)");
1163         }
1164
1165         DEBUG(5,("resolve_alias_to_username: backend query returned %s\n",
1166                  nt_errstr(status)));
1167
1168         if ( NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ) {
1169                 set_domain_offline( domain );
1170         }
1171
1172         return status;
1173 }
1174
1175 NTSTATUS wcache_cached_creds_exist(struct winbindd_domain *domain, const DOM_SID *sid)
1176 {
1177         struct winbind_cache *cache = get_cache(domain);
1178         TDB_DATA data;
1179         fstring key_str, tmp;
1180         uint32 rid;
1181
1182         if (!cache->tdb) {
1183                 return NT_STATUS_INTERNAL_DB_ERROR;
1184         }
1185
1186         if (is_null_sid(sid)) {
1187                 return NT_STATUS_INVALID_SID;
1188         }
1189
1190         if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1191                 return NT_STATUS_INVALID_SID;
1192         }
1193
1194         fstr_sprintf(key_str, "CRED/%s", sid_to_fstring(tmp, sid));
1195
1196         data = tdb_fetch(cache->tdb, string_tdb_data(key_str));
1197         if (!data.dptr) {
1198                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1199         }
1200
1201         SAFE_FREE(data.dptr);
1202         return NT_STATUS_OK;
1203 }
1204
1205 /* Lookup creds for a SID - copes with old (unsalted) creds as well
1206    as new salted ones. */
1207
1208 NTSTATUS wcache_get_creds(struct winbindd_domain *domain, 
1209                           TALLOC_CTX *mem_ctx, 
1210                           const DOM_SID *sid,
1211                           const uint8 **cached_nt_pass,
1212                           const uint8 **cached_salt)
1213 {
1214         struct winbind_cache *cache = get_cache(domain);
1215         struct cache_entry *centry = NULL;
1216         NTSTATUS status;
1217         time_t t;
1218         uint32 rid;
1219         fstring tmp;
1220
1221         if (!cache->tdb) {
1222                 return NT_STATUS_INTERNAL_DB_ERROR;
1223         }
1224
1225         if (is_null_sid(sid)) {
1226                 return NT_STATUS_INVALID_SID;
1227         }
1228
1229         if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1230                 return NT_STATUS_INVALID_SID;
1231         }
1232
1233         /* Try and get a salted cred first. If we can't
1234            fall back to an unsalted cred. */
1235
1236         centry = wcache_fetch(cache, domain, "CRED/%s",
1237                               sid_to_fstring(tmp, sid));
1238         if (!centry) {
1239                 DEBUG(10,("wcache_get_creds: entry for [CRED/%s] not found\n", 
1240                           sid_string_dbg(sid)));
1241                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1242         }
1243
1244         t = centry_time(centry);
1245
1246         /* In the salted case this isn't actually the nt_hash itself,
1247            but the MD5 of the salt + nt_hash. Let the caller
1248            sort this out. It can tell as we only return the cached_salt
1249            if we are returning a salted cred. */
1250
1251         *cached_nt_pass = (const uint8 *)centry_hash16(centry, mem_ctx);
1252         if (*cached_nt_pass == NULL) {
1253                 fstring sidstr;
1254
1255                 sid_to_fstring(sidstr, sid);
1256
1257                 /* Bad (old) cred cache. Delete and pretend we
1258                    don't have it. */
1259                 DEBUG(0,("wcache_get_creds: bad entry for [CRED/%s] - deleting\n", 
1260                                 sidstr));
1261                 wcache_delete("CRED/%s", sidstr);
1262                 centry_free(centry);
1263                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1264         }
1265
1266         /* We only have 17 bytes more data in the salted cred case. */
1267         if (centry->len - centry->ofs == 17) {
1268                 *cached_salt = (const uint8 *)centry_hash16(centry, mem_ctx);
1269         } else {
1270                 *cached_salt = NULL;
1271         }
1272
1273         dump_data_pw("cached_nt_pass", *cached_nt_pass, NT_HASH_LEN);
1274         if (*cached_salt) {
1275                 dump_data_pw("cached_salt", *cached_salt, NT_HASH_LEN);
1276         }
1277
1278         status = centry->status;
1279
1280         DEBUG(10,("wcache_get_creds: [Cached] - cached creds for user %s status: %s\n",
1281                   sid_string_dbg(sid), nt_errstr(status) ));
1282
1283         centry_free(centry);
1284         return status;
1285 }
1286
1287 /* Store creds for a SID - only writes out new salted ones. */
1288
1289 NTSTATUS wcache_save_creds(struct winbindd_domain *domain, 
1290                            TALLOC_CTX *mem_ctx, 
1291                            const DOM_SID *sid, 
1292                            const uint8 nt_pass[NT_HASH_LEN])
1293 {
1294         struct cache_entry *centry;
1295         fstring sid_string;
1296         uint32 rid;
1297         uint8 cred_salt[NT_HASH_LEN];
1298         uint8 salted_hash[NT_HASH_LEN];
1299
1300         if (is_null_sid(sid)) {
1301                 return NT_STATUS_INVALID_SID;
1302         }
1303
1304         if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1305                 return NT_STATUS_INVALID_SID;
1306         }
1307
1308         centry = centry_start(domain, NT_STATUS_OK);
1309         if (!centry) {
1310                 return NT_STATUS_INTERNAL_DB_ERROR;
1311         }
1312
1313         dump_data_pw("nt_pass", nt_pass, NT_HASH_LEN);
1314
1315         centry_put_time(centry, time(NULL));
1316
1317         /* Create a salt and then salt the hash. */
1318         generate_random_buffer(cred_salt, NT_HASH_LEN);
1319         E_md5hash(cred_salt, nt_pass, salted_hash);
1320
1321         centry_put_hash16(centry, salted_hash);
1322         centry_put_hash16(centry, cred_salt);
1323         centry_end(centry, "CRED/%s", sid_to_fstring(sid_string, sid));
1324
1325         DEBUG(10,("wcache_save_creds: %s\n", sid_string));
1326
1327         centry_free(centry);
1328
1329         return NT_STATUS_OK;
1330 }
1331
1332
1333 /* Query display info. This is the basic user list fn */
1334 static NTSTATUS query_user_list(struct winbindd_domain *domain,
1335                                 TALLOC_CTX *mem_ctx,
1336                                 uint32 *num_entries, 
1337                                 WINBIND_USERINFO **info)
1338 {
1339         struct winbind_cache *cache = get_cache(domain);
1340         struct cache_entry *centry = NULL;
1341         NTSTATUS status;
1342         unsigned int i, retry;
1343
1344         if (!cache->tdb)
1345                 goto do_query;
1346
1347         centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
1348         if (!centry)
1349                 goto do_query;
1350
1351         *num_entries = centry_uint32(centry);
1352
1353         if (*num_entries == 0)
1354                 goto do_cached;
1355
1356         (*info) = TALLOC_ARRAY(mem_ctx, WINBIND_USERINFO, *num_entries);
1357         if (! (*info)) {
1358                 smb_panic_fn("query_user_list out of memory");
1359         }
1360         for (i=0; i<(*num_entries); i++) {
1361                 (*info)[i].acct_name = centry_string(centry, mem_ctx);
1362                 (*info)[i].full_name = centry_string(centry, mem_ctx);
1363                 (*info)[i].homedir = centry_string(centry, mem_ctx);
1364                 (*info)[i].shell = centry_string(centry, mem_ctx);
1365                 centry_sid(centry, &(*info)[i].user_sid);
1366                 centry_sid(centry, &(*info)[i].group_sid);
1367         }
1368
1369 do_cached:      
1370         status = centry->status;
1371
1372         DEBUG(10,("query_user_list: [Cached] - cached list for domain %s status: %s\n",
1373                 domain->name, nt_errstr(status) ));
1374
1375         centry_free(centry);
1376         return status;
1377
1378 do_query:
1379         *num_entries = 0;
1380         *info = NULL;
1381
1382         /* Return status value returned by seq number check */
1383
1384         if (!NT_STATUS_IS_OK(domain->last_status))
1385                 return domain->last_status;
1386
1387         /* Put the query_user_list() in a retry loop.  There appears to be
1388          * some bug either with Windows 2000 or Samba's handling of large
1389          * rpc replies.  This manifests itself as sudden disconnection
1390          * at a random point in the enumeration of a large (60k) user list.
1391          * The retry loop simply tries the operation again. )-:  It's not
1392          * pretty but an acceptable workaround until we work out what the
1393          * real problem is. */
1394
1395         retry = 0;
1396         do {
1397
1398                 DEBUG(10,("query_user_list: [Cached] - doing backend query for list for domain %s\n",
1399                         domain->name ));
1400
1401                 status = domain->backend->query_user_list(domain, mem_ctx, num_entries, info);
1402                 if (!NT_STATUS_IS_OK(status)) {
1403                         DEBUG(3, ("query_user_list: returned 0x%08x, "
1404                                   "retrying\n", NT_STATUS_V(status)));
1405                 }
1406                 if (NT_STATUS_EQUAL(status, NT_STATUS_UNSUCCESSFUL)) {
1407                         DEBUG(3, ("query_user_list: flushing "
1408                                   "connection cache\n"));
1409                         invalidate_cm_connection(&domain->conn);
1410                 }
1411
1412         } while (NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_UNSUCCESSFUL) && 
1413                  (retry++ < 5));
1414
1415         /* and save it */
1416         refresh_sequence_number(domain, false);
1417         centry = centry_start(domain, status);
1418         if (!centry)
1419                 goto skip_save;
1420         centry_put_uint32(centry, *num_entries);
1421         for (i=0; i<(*num_entries); i++) {
1422                 centry_put_string(centry, (*info)[i].acct_name);
1423                 centry_put_string(centry, (*info)[i].full_name);
1424                 centry_put_string(centry, (*info)[i].homedir);
1425                 centry_put_string(centry, (*info)[i].shell);
1426                 centry_put_sid(centry, &(*info)[i].user_sid);
1427                 centry_put_sid(centry, &(*info)[i].group_sid);
1428                 if (domain->backend && domain->backend->consistent) {
1429                         /* when the backend is consistent we can pre-prime some mappings */
1430                         wcache_save_name_to_sid(domain, NT_STATUS_OK, 
1431                                                 domain->name,
1432                                                 (*info)[i].acct_name, 
1433                                                 &(*info)[i].user_sid,
1434                                                 SID_NAME_USER);
1435                         wcache_save_sid_to_name(domain, NT_STATUS_OK, 
1436                                                 &(*info)[i].user_sid,
1437                                                 domain->name,
1438                                                 (*info)[i].acct_name, 
1439                                                 SID_NAME_USER);
1440                         wcache_save_user(domain, NT_STATUS_OK, &(*info)[i]);
1441                 }
1442         }       
1443         centry_end(centry, "UL/%s", domain->name);
1444         centry_free(centry);
1445
1446 skip_save:
1447         return status;
1448 }
1449
1450 /* list all domain groups */
1451 static NTSTATUS enum_dom_groups(struct winbindd_domain *domain,
1452                                 TALLOC_CTX *mem_ctx,
1453                                 uint32 *num_entries, 
1454                                 struct acct_info **info)
1455 {
1456         struct winbind_cache *cache = get_cache(domain);
1457         struct cache_entry *centry = NULL;
1458         NTSTATUS status;
1459         unsigned int i;
1460
1461         if (!cache->tdb)
1462                 goto do_query;
1463
1464         centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
1465         if (!centry)
1466                 goto do_query;
1467
1468         *num_entries = centry_uint32(centry);
1469
1470         if (*num_entries == 0)
1471                 goto do_cached;
1472
1473         (*info) = TALLOC_ARRAY(mem_ctx, struct acct_info, *num_entries);
1474         if (! (*info)) {
1475                 smb_panic_fn("enum_dom_groups out of memory");
1476         }
1477         for (i=0; i<(*num_entries); i++) {
1478                 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1479                 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1480                 (*info)[i].rid = centry_uint32(centry);
1481         }
1482
1483 do_cached:      
1484         status = centry->status;
1485
1486         DEBUG(10,("enum_dom_groups: [Cached] - cached list for domain %s status: %s\n",
1487                 domain->name, nt_errstr(status) ));
1488
1489         centry_free(centry);
1490         return status;
1491
1492 do_query:
1493         *num_entries = 0;
1494         *info = NULL;
1495
1496         /* Return status value returned by seq number check */
1497
1498         if (!NT_STATUS_IS_OK(domain->last_status))
1499                 return domain->last_status;
1500
1501         DEBUG(10,("enum_dom_groups: [Cached] - doing backend query for list for domain %s\n",
1502                 domain->name ));
1503
1504         status = domain->backend->enum_dom_groups(domain, mem_ctx, num_entries, info);
1505
1506         /* and save it */
1507         refresh_sequence_number(domain, false);
1508         centry = centry_start(domain, status);
1509         if (!centry)
1510                 goto skip_save;
1511         centry_put_uint32(centry, *num_entries);
1512         for (i=0; i<(*num_entries); i++) {
1513                 centry_put_string(centry, (*info)[i].acct_name);
1514                 centry_put_string(centry, (*info)[i].acct_desc);
1515                 centry_put_uint32(centry, (*info)[i].rid);
1516         }       
1517         centry_end(centry, "GL/%s/domain", domain->name);
1518         centry_free(centry);
1519
1520 skip_save:
1521         return status;
1522 }
1523
1524 /* list all domain groups */
1525 static NTSTATUS enum_local_groups(struct winbindd_domain *domain,
1526                                 TALLOC_CTX *mem_ctx,
1527                                 uint32 *num_entries, 
1528                                 struct acct_info **info)
1529 {
1530         struct winbind_cache *cache = get_cache(domain);
1531         struct cache_entry *centry = NULL;
1532         NTSTATUS status;
1533         unsigned int i;
1534
1535         if (!cache->tdb)
1536                 goto do_query;
1537
1538         centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
1539         if (!centry)
1540                 goto do_query;
1541
1542         *num_entries = centry_uint32(centry);
1543
1544         if (*num_entries == 0)
1545                 goto do_cached;
1546
1547         (*info) = TALLOC_ARRAY(mem_ctx, struct acct_info, *num_entries);
1548         if (! (*info)) {
1549                 smb_panic_fn("enum_dom_groups out of memory");
1550         }
1551         for (i=0; i<(*num_entries); i++) {
1552                 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1553                 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1554                 (*info)[i].rid = centry_uint32(centry);
1555         }
1556
1557 do_cached:      
1558
1559         /* If we are returning cached data and the domain controller
1560            is down then we don't know whether the data is up to date
1561            or not.  Return NT_STATUS_MORE_PROCESSING_REQUIRED to
1562            indicate this. */
1563
1564         if (wcache_server_down(domain)) {
1565                 DEBUG(10, ("enum_local_groups: returning cached user list and server was down\n"));
1566                 status = NT_STATUS_MORE_PROCESSING_REQUIRED;
1567         } else
1568                 status = centry->status;
1569
1570         DEBUG(10,("enum_local_groups: [Cached] - cached list for domain %s status: %s\n",
1571                 domain->name, nt_errstr(status) ));
1572
1573         centry_free(centry);
1574         return status;
1575
1576 do_query:
1577         *num_entries = 0;
1578         *info = NULL;
1579
1580         /* Return status value returned by seq number check */
1581
1582         if (!NT_STATUS_IS_OK(domain->last_status))
1583                 return domain->last_status;
1584
1585         DEBUG(10,("enum_local_groups: [Cached] - doing backend query for list for domain %s\n",
1586                 domain->name ));
1587
1588         status = domain->backend->enum_local_groups(domain, mem_ctx, num_entries, info);
1589
1590         /* and save it */
1591         refresh_sequence_number(domain, false);
1592         centry = centry_start(domain, status);
1593         if (!centry)
1594                 goto skip_save;
1595         centry_put_uint32(centry, *num_entries);
1596         for (i=0; i<(*num_entries); i++) {
1597                 centry_put_string(centry, (*info)[i].acct_name);
1598                 centry_put_string(centry, (*info)[i].acct_desc);
1599                 centry_put_uint32(centry, (*info)[i].rid);
1600         }
1601         centry_end(centry, "GL/%s/local", domain->name);
1602         centry_free(centry);
1603
1604 skip_save:
1605         return status;
1606 }
1607
1608 /* convert a single name to a sid in a domain */
1609 static NTSTATUS name_to_sid(struct winbindd_domain *domain,
1610                             TALLOC_CTX *mem_ctx,
1611                             const char *domain_name,
1612                             const char *name,
1613                             uint32_t flags,
1614                             DOM_SID *sid,
1615                             enum lsa_SidType *type)
1616 {
1617         struct winbind_cache *cache = get_cache(domain);
1618         struct cache_entry *centry = NULL;
1619         NTSTATUS status;
1620         fstring uname;
1621
1622         if (!cache->tdb)
1623                 goto do_query;
1624
1625         fstrcpy(uname, name);
1626         strupper_m(uname);
1627         centry = wcache_fetch(cache, domain, "NS/%s/%s", domain_name, uname);
1628         if (!centry)
1629                 goto do_query;
1630
1631         status = centry->status;
1632         if (NT_STATUS_IS_OK(status)) {
1633                 *type = (enum lsa_SidType)centry_uint32(centry);
1634                 centry_sid(centry, sid);
1635         }
1636
1637         DEBUG(10,("name_to_sid: [Cached] - cached name for domain %s status: %s\n",
1638                 domain->name, nt_errstr(status) ));
1639
1640         centry_free(centry);
1641         return status;
1642
1643 do_query:
1644         ZERO_STRUCTP(sid);
1645
1646         /* If the seq number check indicated that there is a problem
1647          * with this DC, then return that status... except for
1648          * access_denied.  This is special because the dc may be in
1649          * "restrict anonymous = 1" mode, in which case it will deny
1650          * most unauthenticated operations, but *will* allow the LSA
1651          * name-to-sid that we try as a fallback. */
1652
1653         if (!(NT_STATUS_IS_OK(domain->last_status)
1654               || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1655                 return domain->last_status;
1656
1657         DEBUG(10,("name_to_sid: [Cached] - doing backend query for name for domain %s\n",
1658                 domain->name ));
1659
1660         status = domain->backend->name_to_sid(domain, mem_ctx, domain_name,
1661                                               name, flags, sid, type);
1662
1663         /* and save it */
1664         refresh_sequence_number(domain, false);
1665
1666         if (domain->online &&
1667             (NT_STATUS_IS_OK(status) || NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED))) {
1668                 wcache_save_name_to_sid(domain, status, domain_name, name, sid, *type);
1669
1670                 /* Only save the reverse mapping if this was not a UPN */
1671                 if (!strchr(name, '@')) {
1672                         strupper_m(CONST_DISCARD(char *,domain_name));
1673                         strlower_m(CONST_DISCARD(char *,name));
1674                         wcache_save_sid_to_name(domain, status, sid, domain_name, name, *type);
1675                 }
1676         }
1677
1678         return status;
1679 }
1680
1681 NTSTATUS wcache_sid_to_name(struct winbindd_domain *domain,
1682                             const struct dom_sid *sid,
1683                             TALLOC_CTX *mem_ctx,
1684                             char **domain_name,
1685                             char **name,
1686                             enum lsa_SidType *type)
1687 {
1688         struct winbind_cache *cache = get_cache(domain);
1689         struct cache_entry *centry;
1690         char *sid_string;
1691         NTSTATUS status;
1692
1693         if (cache->tdb == NULL) {
1694                 return NT_STATUS_NOT_FOUND;
1695         }
1696
1697         sid_string = sid_string_tos(sid);
1698         if (sid_string == NULL) {
1699                 return NT_STATUS_NO_MEMORY;
1700         }
1701
1702         centry = wcache_fetch(cache, domain, "SN/%s", sid_string);
1703         TALLOC_FREE(sid_string);
1704         if (centry == NULL) {
1705                 return NT_STATUS_NOT_FOUND;
1706         }
1707
1708         if (NT_STATUS_IS_OK(centry->status)) {
1709                 *type = (enum lsa_SidType)centry_uint32(centry);
1710                 *domain_name = centry_string(centry, mem_ctx);
1711                 *name = centry_string(centry, mem_ctx);
1712         }
1713
1714         status = centry->status;
1715         centry_free(centry);
1716
1717         DEBUG(10,("sid_to_name: [Cached] - cached name for domain %s status: "
1718                   "%s\n", domain->name, nt_errstr(status) ));
1719
1720         return status;
1721 }
1722
1723 /* convert a sid to a user or group name. The sid is guaranteed to be in the domain
1724    given */
1725 static NTSTATUS sid_to_name(struct winbindd_domain *domain,
1726                             TALLOC_CTX *mem_ctx,
1727                             const DOM_SID *sid,
1728                             char **domain_name,
1729                             char **name,
1730                             enum lsa_SidType *type)
1731 {
1732         NTSTATUS status;
1733
1734         status = wcache_sid_to_name(domain, sid, mem_ctx, domain_name, name,
1735                                     type);
1736         if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
1737                 return status;
1738         }
1739
1740         *name = NULL;
1741         *domain_name = NULL;
1742
1743         /* If the seq number check indicated that there is a problem
1744          * with this DC, then return that status... except for
1745          * access_denied.  This is special because the dc may be in
1746          * "restrict anonymous = 1" mode, in which case it will deny
1747          * most unauthenticated operations, but *will* allow the LSA
1748          * sid-to-name that we try as a fallback. */
1749
1750         if (!(NT_STATUS_IS_OK(domain->last_status)
1751               || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1752                 return domain->last_status;
1753
1754         DEBUG(10,("sid_to_name: [Cached] - doing backend query for name for domain %s\n",
1755                 domain->name ));
1756
1757         status = domain->backend->sid_to_name(domain, mem_ctx, sid, domain_name, name, type);
1758
1759         /* and save it */
1760         refresh_sequence_number(domain, false);
1761         wcache_save_sid_to_name(domain, status, sid, *domain_name, *name, *type);
1762
1763         /* We can't save the name to sid mapping here, as with sid history a
1764          * later name2sid would give the wrong sid. */
1765
1766         return status;
1767 }
1768
1769 static NTSTATUS rids_to_names(struct winbindd_domain *domain,
1770                               TALLOC_CTX *mem_ctx,
1771                               const DOM_SID *domain_sid,
1772                               uint32 *rids,
1773                               size_t num_rids,
1774                               char **domain_name,
1775                               char ***names,
1776                               enum lsa_SidType **types)
1777 {
1778         struct winbind_cache *cache = get_cache(domain);
1779         size_t i;
1780         NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
1781         bool have_mapped;
1782         bool have_unmapped;
1783
1784         *domain_name = NULL;
1785         *names = NULL;
1786         *types = NULL;
1787
1788         if (!cache->tdb) {
1789                 goto do_query;
1790         }
1791
1792         if (num_rids == 0) {
1793                 return NT_STATUS_OK;
1794         }
1795
1796         *names = TALLOC_ARRAY(mem_ctx, char *, num_rids);
1797         *types = TALLOC_ARRAY(mem_ctx, enum lsa_SidType, num_rids);
1798
1799         if ((*names == NULL) || (*types == NULL)) {
1800                 result = NT_STATUS_NO_MEMORY;
1801                 goto error;
1802         }
1803
1804         have_mapped = have_unmapped = false;
1805
1806         for (i=0; i<num_rids; i++) {
1807                 DOM_SID sid;
1808                 struct cache_entry *centry;
1809                 fstring tmp;
1810
1811                 if (!sid_compose(&sid, domain_sid, rids[i])) {
1812                         result = NT_STATUS_INTERNAL_ERROR;
1813                         goto error;
1814                 }
1815
1816                 centry = wcache_fetch(cache, domain, "SN/%s",
1817                                       sid_to_fstring(tmp, &sid));
1818                 if (!centry) {
1819                         goto do_query;
1820                 }
1821
1822                 (*types)[i] = SID_NAME_UNKNOWN;
1823                 (*names)[i] = talloc_strdup(*names, "");
1824
1825                 if (NT_STATUS_IS_OK(centry->status)) {
1826                         char *dom;
1827                         have_mapped = true;
1828                         (*types)[i] = (enum lsa_SidType)centry_uint32(centry);
1829
1830                         dom = centry_string(centry, mem_ctx);
1831                         if (*domain_name == NULL) {
1832                                 *domain_name = dom;
1833                         } else {
1834                                 talloc_free(dom);
1835                         }
1836
1837                         (*names)[i] = centry_string(centry, *names);
1838
1839                 } else if (NT_STATUS_EQUAL(centry->status, NT_STATUS_NONE_MAPPED)) {
1840                         have_unmapped = true;
1841
1842                 } else {
1843                         /* something's definitely wrong */
1844                         result = centry->status;
1845                         goto error;
1846                 }
1847
1848                 centry_free(centry);
1849         }
1850
1851         if (!have_mapped) {
1852                 return NT_STATUS_NONE_MAPPED;
1853         }
1854         if (!have_unmapped) {
1855                 return NT_STATUS_OK;
1856         }
1857         return STATUS_SOME_UNMAPPED;
1858
1859  do_query:
1860
1861         TALLOC_FREE(*names);
1862         TALLOC_FREE(*types);
1863
1864         result = domain->backend->rids_to_names(domain, mem_ctx, domain_sid,
1865                                                 rids, num_rids, domain_name,
1866                                                 names, types);
1867
1868         /*
1869           None of the queried rids has been found so save all negative entries
1870         */
1871         if (NT_STATUS_EQUAL(result, NT_STATUS_NONE_MAPPED)) {
1872                 for (i = 0; i < num_rids; i++) {
1873                         DOM_SID sid;
1874                         const char *name = "";
1875                         const enum lsa_SidType type = SID_NAME_UNKNOWN;
1876                         NTSTATUS status = NT_STATUS_NONE_MAPPED;
1877
1878                         if (!sid_compose(&sid, domain_sid, rids[i])) {
1879                                 return NT_STATUS_INTERNAL_ERROR;
1880                         }
1881
1882                         wcache_save_sid_to_name(domain, status, &sid, *domain_name,
1883                                                 name, type);
1884                 }
1885
1886                 return result;
1887         }
1888
1889         /*
1890           Some or all of the queried rids have been found.
1891         */
1892         if (!NT_STATUS_IS_OK(result) &&
1893             !NT_STATUS_EQUAL(result, STATUS_SOME_UNMAPPED)) {
1894                 return result;
1895         }
1896
1897         refresh_sequence_number(domain, false);
1898
1899         for (i=0; i<num_rids; i++) {
1900                 DOM_SID sid;
1901                 NTSTATUS status;
1902
1903                 if (!sid_compose(&sid, domain_sid, rids[i])) {
1904                         result = NT_STATUS_INTERNAL_ERROR;
1905                         goto error;
1906                 }
1907
1908                 status = (*types)[i] == SID_NAME_UNKNOWN ?
1909                         NT_STATUS_NONE_MAPPED : NT_STATUS_OK;
1910
1911                 wcache_save_sid_to_name(domain, status, &sid, *domain_name,
1912                                         (*names)[i], (*types)[i]);
1913         }
1914
1915         return result;
1916
1917  error:
1918         TALLOC_FREE(*names);
1919         TALLOC_FREE(*types);
1920         return result;
1921 }
1922
1923 /* Lookup user information from a rid */
1924 static NTSTATUS query_user(struct winbindd_domain *domain, 
1925                            TALLOC_CTX *mem_ctx, 
1926                            const DOM_SID *user_sid, 
1927                            WINBIND_USERINFO *info)
1928 {
1929         struct winbind_cache *cache = get_cache(domain);
1930         struct cache_entry *centry = NULL;
1931         NTSTATUS status;
1932         fstring tmp;
1933
1934         if (!cache->tdb)
1935                 goto do_query;
1936
1937         centry = wcache_fetch(cache, domain, "U/%s",
1938                               sid_to_fstring(tmp, user_sid));
1939
1940         /* If we have an access denied cache entry and a cached info3 in the
1941            samlogon cache then do a query.  This will force the rpc back end
1942            to return the info3 data. */
1943
1944         if (NT_STATUS_V(domain->last_status) == NT_STATUS_V(NT_STATUS_ACCESS_DENIED) &&
1945             netsamlogon_cache_have(user_sid)) {
1946                 DEBUG(10, ("query_user: cached access denied and have cached info3\n"));
1947                 domain->last_status = NT_STATUS_OK;
1948                 centry_free(centry);
1949                 goto do_query;
1950         }
1951
1952         if (!centry)
1953                 goto do_query;
1954
1955         /* if status is not ok then this is a negative hit
1956            and the rest of the data doesn't matter */
1957         status = centry->status;
1958         if (NT_STATUS_IS_OK(status)) {
1959                 info->acct_name = centry_string(centry, mem_ctx);
1960                 info->full_name = centry_string(centry, mem_ctx);
1961                 info->homedir = centry_string(centry, mem_ctx);
1962                 info->shell = centry_string(centry, mem_ctx);
1963                 info->primary_gid = centry_uint32(centry);
1964                 centry_sid(centry, &info->user_sid);
1965                 centry_sid(centry, &info->group_sid);
1966         }
1967
1968         DEBUG(10,("query_user: [Cached] - cached info for domain %s status: %s\n",
1969                 domain->name, nt_errstr(status) ));
1970
1971         centry_free(centry);
1972         return status;
1973
1974 do_query:
1975         ZERO_STRUCTP(info);
1976
1977         /* Return status value returned by seq number check */
1978
1979         if (!NT_STATUS_IS_OK(domain->last_status))
1980                 return domain->last_status;
1981
1982         DEBUG(10,("query_user: [Cached] - doing backend query for info for domain %s\n",
1983                 domain->name ));
1984
1985         status = domain->backend->query_user(domain, mem_ctx, user_sid, info);
1986
1987         /* and save it */
1988         refresh_sequence_number(domain, false);
1989         wcache_save_user(domain, status, info);
1990
1991         return status;
1992 }
1993
1994
1995 /* Lookup groups a user is a member of. */
1996 static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
1997                                   TALLOC_CTX *mem_ctx,
1998                                   const DOM_SID *user_sid, 
1999                                   uint32 *num_groups, DOM_SID **user_gids)
2000 {
2001         struct winbind_cache *cache = get_cache(domain);
2002         struct cache_entry *centry = NULL;
2003         NTSTATUS status;
2004         unsigned int i;
2005         fstring sid_string;
2006
2007         if (!cache->tdb)
2008                 goto do_query;
2009
2010         centry = wcache_fetch(cache, domain, "UG/%s",
2011                               sid_to_fstring(sid_string, user_sid));
2012
2013         /* If we have an access denied cache entry and a cached info3 in the
2014            samlogon cache then do a query.  This will force the rpc back end
2015            to return the info3 data. */
2016
2017         if (NT_STATUS_V(domain->last_status) == NT_STATUS_V(NT_STATUS_ACCESS_DENIED) &&
2018             netsamlogon_cache_have(user_sid)) {
2019                 DEBUG(10, ("lookup_usergroups: cached access denied and have cached info3\n"));
2020                 domain->last_status = NT_STATUS_OK;
2021                 centry_free(centry);
2022                 goto do_query;
2023         }
2024
2025         if (!centry)
2026                 goto do_query;
2027
2028         *num_groups = centry_uint32(centry);
2029
2030         if (*num_groups == 0)
2031                 goto do_cached;
2032
2033         (*user_gids) = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_groups);
2034         if (! (*user_gids)) {
2035                 smb_panic_fn("lookup_usergroups out of memory");
2036         }
2037         for (i=0; i<(*num_groups); i++) {
2038                 centry_sid(centry, &(*user_gids)[i]);
2039         }
2040
2041 do_cached:      
2042         status = centry->status;
2043
2044         DEBUG(10,("lookup_usergroups: [Cached] - cached info for domain %s status: %s\n",
2045                 domain->name, nt_errstr(status) ));
2046
2047         centry_free(centry);
2048         return status;
2049
2050 do_query:
2051         (*num_groups) = 0;
2052         (*user_gids) = NULL;
2053
2054         /* Return status value returned by seq number check */
2055
2056         if (!NT_STATUS_IS_OK(domain->last_status))
2057                 return domain->last_status;
2058
2059         DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info for domain %s\n",
2060                 domain->name ));
2061
2062         status = domain->backend->lookup_usergroups(domain, mem_ctx, user_sid, num_groups, user_gids);
2063
2064         if ( NT_STATUS_EQUAL(status, NT_STATUS_SYNCHRONIZATION_REQUIRED) )
2065                 goto skip_save;
2066
2067         /* and save it */
2068         refresh_sequence_number(domain, false);
2069         centry = centry_start(domain, status);
2070         if (!centry)
2071                 goto skip_save;
2072
2073         centry_put_uint32(centry, *num_groups);
2074         for (i=0; i<(*num_groups); i++) {
2075                 centry_put_sid(centry, &(*user_gids)[i]);
2076         }       
2077
2078         centry_end(centry, "UG/%s", sid_to_fstring(sid_string, user_sid));
2079         centry_free(centry);
2080
2081 skip_save:
2082         return status;
2083 }
2084
2085 static NTSTATUS lookup_useraliases(struct winbindd_domain *domain,
2086                                    TALLOC_CTX *mem_ctx,
2087                                    uint32 num_sids, const DOM_SID *sids,
2088                                    uint32 *num_aliases, uint32 **alias_rids)
2089 {
2090         struct winbind_cache *cache = get_cache(domain);
2091         struct cache_entry *centry = NULL;
2092         NTSTATUS status;
2093         char *sidlist = talloc_strdup(mem_ctx, "");
2094         int i;
2095
2096         if (!cache->tdb)
2097                 goto do_query;
2098
2099         if (num_sids == 0) {
2100                 *num_aliases = 0;
2101                 *alias_rids = NULL;
2102                 return NT_STATUS_OK;
2103         }
2104
2105         /* We need to cache indexed by the whole list of SIDs, the aliases
2106          * resulting might come from any of the SIDs. */
2107
2108         for (i=0; i<num_sids; i++) {
2109                 fstring tmp;
2110                 sidlist = talloc_asprintf(mem_ctx, "%s/%s", sidlist,
2111                                           sid_to_fstring(tmp, &sids[i]));
2112                 if (sidlist == NULL)
2113                         return NT_STATUS_NO_MEMORY;
2114         }
2115
2116         centry = wcache_fetch(cache, domain, "UA%s", sidlist);
2117
2118         if (!centry)
2119                 goto do_query;
2120
2121         *num_aliases = centry_uint32(centry);
2122         *alias_rids = NULL;
2123
2124         if (*num_aliases) {
2125                 (*alias_rids) = TALLOC_ARRAY(mem_ctx, uint32, *num_aliases);
2126
2127                 if ((*alias_rids) == NULL) {
2128                         centry_free(centry);
2129                         return NT_STATUS_NO_MEMORY;
2130                 }
2131         } else {
2132                 (*alias_rids) = NULL;
2133         }
2134
2135         for (i=0; i<(*num_aliases); i++)
2136                 (*alias_rids)[i] = centry_uint32(centry);
2137
2138         status = centry->status;
2139
2140         DEBUG(10,("lookup_useraliases: [Cached] - cached info for domain: %s "
2141                   "status %s\n", domain->name, nt_errstr(status)));
2142
2143         centry_free(centry);
2144         return status;
2145
2146  do_query:
2147         (*num_aliases) = 0;
2148         (*alias_rids) = NULL;
2149
2150         if (!NT_STATUS_IS_OK(domain->last_status))
2151                 return domain->last_status;
2152
2153         DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info "
2154                   "for domain %s\n", domain->name ));
2155
2156         status = domain->backend->lookup_useraliases(domain, mem_ctx,
2157                                                      num_sids, sids,
2158                                                      num_aliases, alias_rids);
2159
2160         /* and save it */
2161         refresh_sequence_number(domain, false);
2162         centry = centry_start(domain, status);
2163         if (!centry)
2164                 goto skip_save;
2165         centry_put_uint32(centry, *num_aliases);
2166         for (i=0; i<(*num_aliases); i++)
2167                 centry_put_uint32(centry, (*alias_rids)[i]);
2168         centry_end(centry, "UA%s", sidlist);
2169         centry_free(centry);
2170
2171  skip_save:
2172         return status;
2173 }
2174
2175
2176 static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
2177                                 TALLOC_CTX *mem_ctx,
2178                                 const DOM_SID *group_sid, uint32 *num_names, 
2179                                 DOM_SID **sid_mem, char ***names, 
2180                                 uint32 **name_types)
2181 {
2182         struct winbind_cache *cache = get_cache(domain);
2183         struct cache_entry *centry = NULL;
2184         NTSTATUS status;
2185         unsigned int i;
2186         fstring sid_string;
2187
2188         if (!cache->tdb)
2189                 goto do_query;
2190
2191         centry = wcache_fetch(cache, domain, "GM/%s",
2192                               sid_to_fstring(sid_string, group_sid));
2193         if (!centry)
2194                 goto do_query;
2195
2196         *num_names = centry_uint32(centry);
2197
2198         if (*num_names == 0)
2199                 goto do_cached;
2200
2201         (*sid_mem) = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_names);
2202         (*names) = TALLOC_ARRAY(mem_ctx, char *, *num_names);
2203         (*name_types) = TALLOC_ARRAY(mem_ctx, uint32, *num_names);
2204
2205         if (! (*sid_mem) || ! (*names) || ! (*name_types)) {
2206                 smb_panic_fn("lookup_groupmem out of memory");
2207         }
2208
2209         for (i=0; i<(*num_names); i++) {
2210                 centry_sid(centry, &(*sid_mem)[i]);
2211                 (*names)[i] = centry_string(centry, mem_ctx);
2212                 (*name_types)[i] = centry_uint32(centry);
2213         }
2214
2215 do_cached:      
2216         status = centry->status;
2217
2218         DEBUG(10,("lookup_groupmem: [Cached] - cached info for domain %s status: %s\n",
2219                 domain->name, nt_errstr(status)));
2220
2221         centry_free(centry);
2222         return status;
2223
2224 do_query:
2225         (*num_names) = 0;
2226         (*sid_mem) = NULL;
2227         (*names) = NULL;
2228         (*name_types) = NULL;
2229
2230         /* Return status value returned by seq number check */
2231
2232         if (!NT_STATUS_IS_OK(domain->last_status))
2233                 return domain->last_status;
2234
2235         DEBUG(10,("lookup_groupmem: [Cached] - doing backend query for info for domain %s\n",
2236                 domain->name ));
2237
2238         status = domain->backend->lookup_groupmem(domain, mem_ctx, group_sid, num_names, 
2239                                                   sid_mem, names, name_types);
2240
2241         /* and save it */
2242         refresh_sequence_number(domain, false);
2243         centry = centry_start(domain, status);
2244         if (!centry)
2245                 goto skip_save;
2246         centry_put_uint32(centry, *num_names);
2247         for (i=0; i<(*num_names); i++) {
2248                 centry_put_sid(centry, &(*sid_mem)[i]);
2249                 centry_put_string(centry, (*names)[i]);
2250                 centry_put_uint32(centry, (*name_types)[i]);
2251         }       
2252         centry_end(centry, "GM/%s", sid_to_fstring(sid_string, group_sid));
2253         centry_free(centry);
2254
2255 skip_save:
2256         return status;
2257 }
2258
2259 /* find the sequence number for a domain */
2260 static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
2261 {
2262         refresh_sequence_number(domain, false);
2263
2264         *seq = domain->sequence_number;
2265
2266         return NT_STATUS_OK;
2267 }
2268
2269 /* enumerate trusted domains 
2270  * (we need to have the list of trustdoms in the cache when we go offline) -
2271  * Guenther */
2272 static NTSTATUS trusted_domains(struct winbindd_domain *domain,
2273                                 TALLOC_CTX *mem_ctx,
2274                                 uint32 *num_domains,
2275                                 char ***names,
2276                                 char ***alt_names,
2277                                 DOM_SID **dom_sids)
2278 {
2279         struct winbind_cache *cache = get_cache(domain);
2280         struct cache_entry *centry = NULL;
2281         NTSTATUS status;
2282         int i;
2283
2284         if (!cache->tdb)
2285                 goto do_query;
2286
2287         centry = wcache_fetch(cache, domain, "TRUSTDOMS/%s", domain->name);
2288
2289         if (!centry) {
2290                 goto do_query;
2291         }
2292
2293         *num_domains = centry_uint32(centry);
2294
2295         if (*num_domains) {
2296                 (*names)        = TALLOC_ARRAY(mem_ctx, char *, *num_domains);
2297                 (*alt_names)    = TALLOC_ARRAY(mem_ctx, char *, *num_domains);
2298                 (*dom_sids)     = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_domains);
2299
2300                 if (! (*dom_sids) || ! (*names) || ! (*alt_names)) {
2301                         smb_panic_fn("trusted_domains out of memory");
2302                 }
2303         } else {
2304                 (*names) = NULL;
2305                 (*alt_names) = NULL;
2306                 (*dom_sids) = NULL;
2307         }
2308
2309         for (i=0; i<(*num_domains); i++) {
2310                 (*names)[i] = centry_string(centry, mem_ctx);
2311                 (*alt_names)[i] = centry_string(centry, mem_ctx);
2312                 if (!centry_sid(centry, &(*dom_sids)[i])) {
2313                         sid_copy(&(*dom_sids)[i], &global_sid_NULL);
2314                 }
2315         }
2316
2317         status = centry->status;
2318
2319         DEBUG(10,("trusted_domains: [Cached] - cached info for domain %s (%d trusts) status: %s\n",
2320                 domain->name, *num_domains, nt_errstr(status) ));
2321
2322         centry_free(centry);
2323         return status;
2324
2325 do_query:
2326         (*num_domains) = 0;
2327         (*dom_sids) = NULL;
2328         (*names) = NULL;
2329         (*alt_names) = NULL;
2330
2331         /* Return status value returned by seq number check */
2332
2333         if (!NT_STATUS_IS_OK(domain->last_status))
2334                 return domain->last_status;
2335
2336         DEBUG(10,("trusted_domains: [Cached] - doing backend query for info for domain %s\n",
2337                 domain->name ));
2338
2339         status = domain->backend->trusted_domains(domain, mem_ctx, num_domains,
2340                                                 names, alt_names, dom_sids);
2341
2342         /* no trusts gives NT_STATUS_NO_MORE_ENTRIES resetting to NT_STATUS_OK
2343          * so that the generic centry handling still applies correctly -
2344          * Guenther*/
2345
2346         if (!NT_STATUS_IS_ERR(status)) {
2347                 status = NT_STATUS_OK;
2348         }
2349
2350
2351 #if 0    /* Disabled as we want the trust dom list to be managed by
2352             the main parent and always to make the query.  --jerry */
2353
2354         /* and save it */
2355         refresh_sequence_number(domain, false);
2356
2357         centry = centry_start(domain, status);
2358         if (!centry)
2359                 goto skip_save;
2360
2361         centry_put_uint32(centry, *num_domains);
2362
2363         for (i=0; i<(*num_domains); i++) {
2364                 centry_put_string(centry, (*names)[i]);
2365                 centry_put_string(centry, (*alt_names)[i]);
2366                 centry_put_sid(centry, &(*dom_sids)[i]);
2367         }
2368
2369         centry_end(centry, "TRUSTDOMS/%s", domain->name);
2370
2371         centry_free(centry);
2372
2373 skip_save:
2374 #endif
2375
2376         return status;
2377 }       
2378
2379 /* get lockout policy */
2380 static NTSTATUS lockout_policy(struct winbindd_domain *domain,
2381                                TALLOC_CTX *mem_ctx,
2382                                struct samr_DomInfo12 *policy)
2383 {
2384         struct winbind_cache *cache = get_cache(domain);
2385         struct cache_entry *centry = NULL;
2386         NTSTATUS status;
2387
2388         if (!cache->tdb)
2389                 goto do_query;
2390
2391         centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
2392
2393         if (!centry)
2394                 goto do_query;
2395
2396         policy->lockout_duration = centry_nttime(centry);
2397         policy->lockout_window = centry_nttime(centry);
2398         policy->lockout_threshold = centry_uint16(centry);
2399
2400         status = centry->status;
2401
2402         DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2403                 domain->name, nt_errstr(status) ));
2404
2405         centry_free(centry);
2406         return status;
2407
2408 do_query:
2409         ZERO_STRUCTP(policy);
2410
2411         /* Return status value returned by seq number check */
2412
2413         if (!NT_STATUS_IS_OK(domain->last_status))
2414                 return domain->last_status;
2415
2416         DEBUG(10,("lockout_policy: [Cached] - doing backend query for info for domain %s\n",
2417                 domain->name ));
2418
2419         status = domain->backend->lockout_policy(domain, mem_ctx, policy);
2420
2421         /* and save it */
2422         refresh_sequence_number(domain, false);
2423         wcache_save_lockout_policy(domain, status, policy);
2424
2425         return status;
2426 }
2427
2428 /* get password policy */
2429 static NTSTATUS password_policy(struct winbindd_domain *domain,
2430                                 TALLOC_CTX *mem_ctx,
2431                                 struct samr_DomInfo1 *policy)
2432 {
2433         struct winbind_cache *cache = get_cache(domain);
2434         struct cache_entry *centry = NULL;
2435         NTSTATUS status;
2436
2437         if (!cache->tdb)
2438                 goto do_query;
2439
2440         centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
2441
2442         if (!centry)
2443                 goto do_query;
2444
2445         policy->min_password_length = centry_uint16(centry);
2446         policy->password_history_length = centry_uint16(centry);
2447         policy->password_properties = centry_uint32(centry);
2448         policy->max_password_age = centry_nttime(centry);
2449         policy->min_password_age = centry_nttime(centry);
2450
2451         status = centry->status;
2452
2453         DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2454                 domain->name, nt_errstr(status) ));
2455
2456         centry_free(centry);
2457         return status;
2458
2459 do_query:
2460         ZERO_STRUCTP(policy);
2461
2462         /* Return status value returned by seq number check */
2463
2464         if (!NT_STATUS_IS_OK(domain->last_status))
2465                 return domain->last_status;
2466
2467         DEBUG(10,("password_policy: [Cached] - doing backend query for info for domain %s\n",
2468                 domain->name ));
2469
2470         status = domain->backend->password_policy(domain, mem_ctx, policy);
2471
2472         /* and save it */
2473         refresh_sequence_number(domain, false);
2474         if (NT_STATUS_IS_OK(status)) {
2475                 wcache_save_password_policy(domain, status, policy);
2476         }
2477
2478         return status;
2479 }
2480
2481
2482 /* Invalidate cached user and group lists coherently */
2483
2484 static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, 
2485                        void *state)
2486 {
2487         if (strncmp((const char *)kbuf.dptr, "UL/", 3) == 0 ||
2488             strncmp((const char *)kbuf.dptr, "GL/", 3) == 0)
2489                 tdb_delete(the_tdb, kbuf);
2490
2491         return 0;
2492 }
2493
2494 /* Invalidate the getpwnam and getgroups entries for a winbindd domain */
2495
2496 void wcache_invalidate_samlogon(struct winbindd_domain *domain, 
2497                                 struct netr_SamInfo3 *info3)
2498 {
2499         DOM_SID sid;
2500         fstring key_str, sid_string;
2501         struct winbind_cache *cache;
2502
2503         /* dont clear cached U/SID and UG/SID entries when we want to logon
2504          * offline - gd */
2505
2506         if (lp_winbind_offline_logon()) {
2507                 return;
2508         }
2509
2510         if (!domain)
2511                 return;
2512
2513         cache = get_cache(domain);
2514
2515         if (!cache->tdb) {
2516                 return;
2517         }
2518
2519         sid_copy(&sid, info3->base.domain_sid);
2520         sid_append_rid(&sid, info3->base.rid);
2521
2522         /* Clear U/SID cache entry */
2523         fstr_sprintf(key_str, "U/%s", sid_to_fstring(sid_string, &sid));
2524         DEBUG(10, ("wcache_invalidate_samlogon: clearing %s\n", key_str));
2525         tdb_delete(cache->tdb, string_tdb_data(key_str));
2526
2527         /* Clear UG/SID cache entry */
2528         fstr_sprintf(key_str, "UG/%s", sid_to_fstring(sid_string, &sid));
2529         DEBUG(10, ("wcache_invalidate_samlogon: clearing %s\n", key_str));
2530         tdb_delete(cache->tdb, string_tdb_data(key_str));
2531
2532         /* Samba/winbindd never needs this. */
2533         netsamlogon_clear_cached_user(info3);
2534 }
2535
2536 bool wcache_invalidate_cache(void)
2537 {
2538         struct winbindd_domain *domain;
2539
2540         for (domain = domain_list(); domain; domain = domain->next) {
2541                 struct winbind_cache *cache = get_cache(domain);
2542
2543                 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
2544                            "entries for %s\n", domain->name));
2545                 if (cache) {
2546                         if (cache->tdb) {
2547                                 tdb_traverse(cache->tdb, traverse_fn, NULL);
2548                         } else {
2549                                 return false;
2550                         }
2551                 }
2552         }
2553         return true;
2554 }
2555
2556 bool init_wcache(void)
2557 {
2558         if (wcache == NULL) {
2559                 wcache = SMB_XMALLOC_P(struct winbind_cache);
2560                 ZERO_STRUCTP(wcache);
2561         }
2562
2563         if (wcache->tdb != NULL)
2564                 return true;
2565
2566         /* when working offline we must not clear the cache on restart */
2567         wcache->tdb = tdb_open_log(cache_path("winbindd_cache.tdb"),
2568                                 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE, 
2569                                 lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST), 
2570                                 O_RDWR|O_CREAT, 0600);
2571
2572         if (wcache->tdb == NULL) {
2573                 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
2574                 return false;
2575         }
2576
2577         return true;
2578 }
2579
2580 /************************************************************************
2581  This is called by the parent to initialize the cache file.
2582  We don't need sophisticated locking here as we know we're the
2583  only opener.
2584 ************************************************************************/
2585
2586 bool initialize_winbindd_cache(void)
2587 {
2588         bool cache_bad = true;
2589         uint32 vers;
2590
2591         if (!init_wcache()) {
2592                 DEBUG(0,("initialize_winbindd_cache: init_wcache failed.\n"));
2593                 return false;
2594         }
2595
2596         /* Check version number. */
2597         if (tdb_fetch_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, &vers) &&
2598                         vers == WINBINDD_CACHE_VERSION) {
2599                 cache_bad = false;
2600         }
2601
2602         if (cache_bad) {
2603                 DEBUG(0,("initialize_winbindd_cache: clearing cache "
2604                         "and re-creating with version number %d\n",
2605                         WINBINDD_CACHE_VERSION ));
2606
2607                 tdb_close(wcache->tdb);
2608                 wcache->tdb = NULL;
2609
2610                 if (unlink(cache_path("winbindd_cache.tdb")) == -1) {
2611                         DEBUG(0,("initialize_winbindd_cache: unlink %s failed %s ",
2612                                 cache_path("winbindd_cache.tdb"),
2613                                 strerror(errno) ));
2614                         return false;
2615                 }
2616                 if (!init_wcache()) {
2617                         DEBUG(0,("initialize_winbindd_cache: re-initialization "
2618                                         "init_wcache failed.\n"));
2619                         return false;
2620                 }
2621
2622                 /* Write the version. */
2623                 if (!tdb_store_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, WINBINDD_CACHE_VERSION)) {
2624                         DEBUG(0,("initialize_winbindd_cache: version number store failed %s\n",
2625                                 tdb_errorstr(wcache->tdb) ));
2626                         return false;
2627                 }
2628         }
2629
2630         tdb_close(wcache->tdb);
2631         wcache->tdb = NULL;
2632         return true;
2633 }
2634
2635 void close_winbindd_cache(void)
2636 {
2637         if (!wcache) {
2638                 return;
2639         }
2640         if (wcache->tdb) {
2641                 tdb_close(wcache->tdb);
2642                 wcache->tdb = NULL;
2643         }
2644 }
2645
2646 bool lookup_cached_sid(TALLOC_CTX *mem_ctx, const DOM_SID *sid,
2647                        char **domain_name, char **name,
2648                        enum lsa_SidType *type)
2649 {
2650         struct winbindd_domain *domain;
2651         NTSTATUS status;
2652
2653         domain = find_lookup_domain_from_sid(sid);
2654         if (domain == NULL) {
2655                 return false;
2656         }
2657         status = wcache_sid_to_name(domain, sid, mem_ctx, domain_name, name,
2658                                     type);
2659         return NT_STATUS_IS_OK(status);
2660 }
2661
2662 bool lookup_cached_name(TALLOC_CTX *mem_ctx,
2663                         const char *domain_name,
2664                         const char *name,
2665                         DOM_SID *sid,
2666                         enum lsa_SidType *type)
2667 {
2668         struct winbindd_domain *domain;
2669         struct winbind_cache *cache;
2670         struct cache_entry *centry = NULL;
2671         NTSTATUS status;
2672         fstring uname;
2673         bool original_online_state;     
2674
2675         domain = find_lookup_domain_from_name(domain_name);
2676         if (domain == NULL) {
2677                 return false;
2678         }
2679
2680         cache = get_cache(domain);
2681
2682         if (cache->tdb == NULL) {
2683                 return false;
2684         }
2685
2686         fstrcpy(uname, name);
2687         strupper_m(uname);
2688
2689         /* If we are doing a cached logon, temporarily set the domain
2690            offline so the cache won't expire the entry */
2691
2692         original_online_state = domain->online;
2693         domain->online = false;
2694         centry = wcache_fetch(cache, domain, "NS/%s/%s", domain_name, uname);
2695         domain->online = original_online_state;
2696
2697         if (centry == NULL) {
2698                 return false;
2699         }
2700
2701         if (NT_STATUS_IS_OK(centry->status)) {
2702                 *type = (enum lsa_SidType)centry_uint32(centry);
2703                 centry_sid(centry, sid);
2704         }
2705
2706         status = centry->status;
2707         centry_free(centry);
2708
2709         return NT_STATUS_IS_OK(status);
2710 }
2711
2712 void cache_name2sid(struct winbindd_domain *domain, 
2713                     const char *domain_name, const char *name,
2714                     enum lsa_SidType type, const DOM_SID *sid)
2715 {
2716         refresh_sequence_number(domain, false);
2717         wcache_save_name_to_sid(domain, NT_STATUS_OK, domain_name, name,
2718                                 sid, type);
2719 }
2720
2721 /*
2722  * The original idea that this cache only contains centries has
2723  * been blurred - now other stuff gets put in here. Ensure we
2724  * ignore these things on cleanup.
2725  */
2726
2727 static int traverse_fn_cleanup(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, 
2728                                TDB_DATA dbuf, void *state)
2729 {
2730         struct cache_entry *centry;
2731
2732         if (is_non_centry_key(kbuf)) {
2733                 return 0;
2734         }
2735
2736         centry = wcache_fetch_raw((char *)kbuf.dptr);
2737         if (!centry) {
2738                 return 0;
2739         }
2740
2741         if (!NT_STATUS_IS_OK(centry->status)) {
2742                 DEBUG(10,("deleting centry %s\n", (const char *)kbuf.dptr));
2743                 tdb_delete(the_tdb, kbuf);
2744         }
2745
2746         centry_free(centry);
2747         return 0;
2748 }
2749
2750 /* flush the cache */
2751 void wcache_flush_cache(void)
2752 {
2753         if (!wcache)
2754                 return;
2755         if (wcache->tdb) {
2756                 tdb_close(wcache->tdb);
2757                 wcache->tdb = NULL;
2758         }
2759         if (!winbindd_use_cache()) {
2760                 return;
2761         }
2762
2763         /* when working offline we must not clear the cache on restart */
2764         wcache->tdb = tdb_open_log(cache_path("winbindd_cache.tdb"),
2765                                 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE, 
2766                                 lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST), 
2767                                 O_RDWR|O_CREAT, 0600);
2768
2769         if (!wcache->tdb) {
2770                 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
2771                 return;
2772         }
2773
2774         tdb_traverse(wcache->tdb, traverse_fn_cleanup, NULL);
2775
2776         DEBUG(10,("wcache_flush_cache success\n"));
2777 }
2778
2779 /* Count cached creds */
2780
2781 static int traverse_fn_cached_creds(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, 
2782                                     void *state)
2783 {
2784         int *cred_count = (int*)state;
2785  
2786         if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
2787                 (*cred_count)++;
2788         }
2789         return 0;
2790 }
2791
2792 NTSTATUS wcache_count_cached_creds(struct winbindd_domain *domain, int *count)
2793 {
2794         struct winbind_cache *cache = get_cache(domain);
2795
2796         *count = 0;
2797
2798         if (!cache->tdb) {
2799                 return NT_STATUS_INTERNAL_DB_ERROR;
2800         }
2801  
2802         tdb_traverse(cache->tdb, traverse_fn_cached_creds, (void *)count);
2803
2804         return NT_STATUS_OK;
2805 }
2806
2807 struct cred_list {
2808         struct cred_list *prev, *next;
2809         TDB_DATA key;
2810         fstring name;
2811         time_t created;
2812 };
2813 static struct cred_list *wcache_cred_list;
2814
2815 static int traverse_fn_get_credlist(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, 
2816                                     void *state)
2817 {
2818         struct cred_list *cred;
2819
2820         if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
2821
2822                 cred = SMB_MALLOC_P(struct cred_list);
2823                 if (cred == NULL) {
2824                         DEBUG(0,("traverse_fn_remove_first_creds: failed to malloc new entry for list\n"));
2825                         return -1;
2826                 }
2827
2828                 ZERO_STRUCTP(cred);
2829
2830                 /* save a copy of the key */
2831
2832                 fstrcpy(cred->name, (const char *)kbuf.dptr);           
2833                 DLIST_ADD(wcache_cred_list, cred);
2834         }
2835
2836         return 0;
2837 }
2838
2839 NTSTATUS wcache_remove_oldest_cached_creds(struct winbindd_domain *domain, const DOM_SID *sid) 
2840 {
2841         struct winbind_cache *cache = get_cache(domain);
2842         NTSTATUS status;
2843         int ret;
2844         struct cred_list *cred, *oldest = NULL;
2845
2846         if (!cache->tdb) {
2847                 return NT_STATUS_INTERNAL_DB_ERROR;
2848         }
2849
2850         /* we possibly already have an entry */
2851         if (sid && NT_STATUS_IS_OK(wcache_cached_creds_exist(domain, sid))) {
2852
2853                 fstring key_str, tmp;
2854
2855                 DEBUG(11,("we already have an entry, deleting that\n"));
2856
2857                 fstr_sprintf(key_str, "CRED/%s", sid_to_fstring(tmp, sid));
2858
2859                 tdb_delete(cache->tdb, string_tdb_data(key_str));
2860
2861                 return NT_STATUS_OK;
2862         }
2863
2864         ret = tdb_traverse(cache->tdb, traverse_fn_get_credlist, NULL);
2865         if (ret == 0) {
2866                 return NT_STATUS_OK;
2867         } else if ((ret == -1) || (wcache_cred_list == NULL)) {
2868                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
2869         }
2870
2871         ZERO_STRUCTP(oldest);
2872
2873         for (cred = wcache_cred_list; cred; cred = cred->next) {
2874
2875                 TDB_DATA data;
2876                 time_t t;
2877
2878                 data = tdb_fetch(cache->tdb, string_tdb_data(cred->name));
2879                 if (!data.dptr) {
2880                         DEBUG(10,("wcache_remove_oldest_cached_creds: entry for [%s] not found\n", 
2881                                 cred->name));
2882                         status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
2883                         goto done;
2884                 }
2885
2886                 t = IVAL(data.dptr, 0);
2887                 SAFE_FREE(data.dptr);
2888
2889                 if (!oldest) {
2890                         oldest = SMB_MALLOC_P(struct cred_list);
2891                         if (oldest == NULL) {
2892                                 status = NT_STATUS_NO_MEMORY;
2893                                 goto done;
2894                         }
2895
2896                         fstrcpy(oldest->name, cred->name);
2897                         oldest->created = t;
2898                         continue;
2899                 }
2900
2901                 if (t < oldest->created) {
2902                         fstrcpy(oldest->name, cred->name);
2903                         oldest->created = t;
2904                 }
2905         }
2906
2907         if (tdb_delete(cache->tdb, string_tdb_data(oldest->name)) == 0) {
2908                 status = NT_STATUS_OK;
2909         } else {
2910                 status = NT_STATUS_UNSUCCESSFUL;
2911         }
2912 done:
2913         SAFE_FREE(wcache_cred_list);
2914         SAFE_FREE(oldest);
2915
2916         return status;
2917 }
2918
2919 /* Change the global online/offline state. */
2920 bool set_global_winbindd_state_offline(void)
2921 {
2922         TDB_DATA data;
2923
2924         DEBUG(10,("set_global_winbindd_state_offline: offline requested.\n"));
2925
2926         /* Only go offline if someone has created
2927            the key "WINBINDD_OFFLINE" in the cache tdb. */
2928
2929         if (wcache == NULL || wcache->tdb == NULL) {
2930                 DEBUG(10,("set_global_winbindd_state_offline: wcache not open yet.\n"));
2931                 return false;
2932         }
2933
2934         if (!lp_winbind_offline_logon()) {
2935                 DEBUG(10,("set_global_winbindd_state_offline: rejecting.\n"));
2936                 return false;
2937         }
2938
2939         if (global_winbindd_offline_state) {
2940                 /* Already offline. */
2941                 return true;
2942         }
2943
2944         data = tdb_fetch_bystring( wcache->tdb, "WINBINDD_OFFLINE" );
2945
2946         if (!data.dptr || data.dsize != 4) {
2947                 DEBUG(10,("set_global_winbindd_state_offline: offline state not set.\n"));
2948                 SAFE_FREE(data.dptr);
2949                 return false;
2950         } else {
2951                 DEBUG(10,("set_global_winbindd_state_offline: offline state set.\n"));
2952                 global_winbindd_offline_state = true;
2953                 SAFE_FREE(data.dptr);
2954                 return true;
2955         }
2956 }
2957
2958 void set_global_winbindd_state_online(void)
2959 {
2960         DEBUG(10,("set_global_winbindd_state_online: online requested.\n"));
2961
2962         if (!lp_winbind_offline_logon()) {
2963                 DEBUG(10,("set_global_winbindd_state_online: rejecting.\n"));
2964                 return;
2965         }
2966
2967         if (!global_winbindd_offline_state) {
2968                 /* Already online. */
2969                 return;
2970         }
2971         global_winbindd_offline_state = false;
2972
2973         if (!wcache->tdb) {
2974                 return;
2975         }
2976
2977         /* Ensure there is no key "WINBINDD_OFFLINE" in the cache tdb. */
2978         tdb_delete_bystring(wcache->tdb, "WINBINDD_OFFLINE");
2979 }
2980
2981 bool get_global_winbindd_state_offline(void)
2982 {
2983         return global_winbindd_offline_state;
2984 }
2985
2986 /***********************************************************************
2987  Validate functions for all possible cache tdb keys.
2988 ***********************************************************************/
2989
2990 static struct cache_entry *create_centry_validate(const char *kstr, TDB_DATA data, 
2991                                                   struct tdb_validation_status *state)
2992 {
2993         struct cache_entry *centry;
2994
2995         centry = SMB_XMALLOC_P(struct cache_entry);
2996         centry->data = (unsigned char *)memdup(data.dptr, data.dsize);
2997         if (!centry->data) {
2998                 SAFE_FREE(centry);
2999                 return NULL;
3000         }
3001         centry->len = data.dsize;
3002         centry->ofs = 0;
3003
3004         if (centry->len < 8) {
3005                 /* huh? corrupt cache? */
3006                 DEBUG(0,("create_centry_validate: Corrupt cache for key %s (len < 8) ?\n", kstr));
3007                 centry_free(centry);
3008                 state->bad_entry = true;
3009                 state->success = false;
3010                 return NULL;
3011         }
3012
3013         centry->status = NT_STATUS(centry_uint32(centry));
3014         centry->sequence_number = centry_uint32(centry);
3015         return centry;
3016 }
3017
3018 static int validate_seqnum(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3019                            struct tdb_validation_status *state)
3020 {
3021         if (dbuf.dsize != 8) {
3022                 DEBUG(0,("validate_seqnum: Corrupt cache for key %s (len %u != 8) ?\n",
3023                                 keystr, (unsigned int)dbuf.dsize ));
3024                 state->bad_entry = true;
3025                 return 1;
3026         }
3027         return 0;
3028 }
3029
3030 static int validate_ns(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3031                        struct tdb_validation_status *state)
3032 {
3033         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3034         if (!centry) {
3035                 return 1;
3036         }
3037
3038         (void)centry_uint32(centry);
3039         if (NT_STATUS_IS_OK(centry->status)) {
3040                 DOM_SID sid;
3041                 (void)centry_sid(centry, &sid);
3042         }
3043
3044         centry_free(centry);
3045
3046         if (!(state->success)) {
3047                 return 1;
3048         }
3049         DEBUG(10,("validate_ns: %s ok\n", keystr));
3050         return 0;
3051 }
3052
3053 static int validate_sn(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3054                        struct tdb_validation_status *state)
3055 {
3056         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3057         if (!centry) {
3058                 return 1;
3059         }
3060
3061         if (NT_STATUS_IS_OK(centry->status)) {
3062                 (void)centry_uint32(centry);
3063                 (void)centry_string(centry, mem_ctx);
3064                 (void)centry_string(centry, mem_ctx);
3065         }
3066
3067         centry_free(centry);
3068
3069         if (!(state->success)) {
3070                 return 1;
3071         }
3072         DEBUG(10,("validate_sn: %s ok\n", keystr));
3073         return 0;
3074 }
3075
3076 static int validate_u(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3077                       struct tdb_validation_status *state)
3078 {
3079         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3080         DOM_SID sid;
3081
3082         if (!centry) {
3083                 return 1;
3084         }
3085
3086         (void)centry_string(centry, mem_ctx);
3087         (void)centry_string(centry, mem_ctx);
3088         (void)centry_string(centry, mem_ctx);
3089         (void)centry_string(centry, mem_ctx);
3090         (void)centry_uint32(centry);
3091         (void)centry_sid(centry, &sid);
3092         (void)centry_sid(centry, &sid);
3093
3094         centry_free(centry);
3095
3096         if (!(state->success)) {
3097                 return 1;
3098         }
3099         DEBUG(10,("validate_u: %s ok\n", keystr));
3100         return 0;
3101 }
3102
3103 static int validate_loc_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3104                             struct tdb_validation_status *state)
3105 {
3106         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3107
3108         if (!centry) {
3109                 return 1;
3110         }
3111
3112         (void)centry_nttime(centry);
3113         (void)centry_nttime(centry);
3114         (void)centry_uint16(centry);
3115
3116         centry_free(centry);
3117
3118         if (!(state->success)) {
3119                 return 1;
3120         }
3121         DEBUG(10,("validate_loc_pol: %s ok\n", keystr));
3122         return 0;
3123 }
3124
3125 static int validate_pwd_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3126                             struct tdb_validation_status *state)
3127 {
3128         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3129
3130         if (!centry) {
3131                 return 1;
3132         }
3133
3134         (void)centry_uint16(centry);
3135         (void)centry_uint16(centry);
3136         (void)centry_uint32(centry);
3137         (void)centry_nttime(centry);
3138         (void)centry_nttime(centry);
3139
3140         centry_free(centry);
3141
3142         if (!(state->success)) {
3143                 return 1;
3144         }
3145         DEBUG(10,("validate_pwd_pol: %s ok\n", keystr));
3146         return 0;
3147 }
3148
3149 static int validate_cred(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3150                          struct tdb_validation_status *state)
3151 {
3152         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3153
3154         if (!centry) {
3155                 return 1;
3156         }
3157
3158         (void)centry_time(centry);
3159         (void)centry_hash16(centry, mem_ctx);
3160
3161         /* We only have 17 bytes more data in the salted cred case. */
3162         if (centry->len - centry->ofs == 17) {
3163                 (void)centry_hash16(centry, mem_ctx);
3164         }
3165
3166         centry_free(centry);
3167
3168         if (!(state->success)) {
3169                 return 1;
3170         }
3171         DEBUG(10,("validate_cred: %s ok\n", keystr));
3172         return 0;
3173 }
3174
3175 static int validate_ul(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3176                        struct tdb_validation_status *state)
3177 {
3178         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3179         int32 num_entries, i;
3180
3181         if (!centry) {
3182                 return 1;
3183         }
3184
3185         num_entries = (int32)centry_uint32(centry);
3186
3187         for (i=0; i< num_entries; i++) {
3188                 DOM_SID sid;
3189                 (void)centry_string(centry, mem_ctx);
3190                 (void)centry_string(centry, mem_ctx);
3191                 (void)centry_string(centry, mem_ctx);
3192                 (void)centry_string(centry, mem_ctx);
3193                 (void)centry_sid(centry, &sid);
3194                 (void)centry_sid(centry, &sid);
3195         }
3196
3197         centry_free(centry);
3198
3199         if (!(state->success)) {
3200                 return 1;
3201         }
3202         DEBUG(10,("validate_ul: %s ok\n", keystr));
3203         return 0;
3204 }
3205
3206 static int validate_gl(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3207                        struct tdb_validation_status *state)
3208 {
3209         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3210         int32 num_entries, i;
3211
3212         if (!centry) {
3213                 return 1;
3214         }
3215
3216         num_entries = centry_uint32(centry);
3217
3218         for (i=0; i< num_entries; i++) {
3219                 (void)centry_string(centry, mem_ctx);
3220                 (void)centry_string(centry, mem_ctx);
3221                 (void)centry_uint32(centry);
3222         }
3223
3224         centry_free(centry);
3225
3226         if (!(state->success)) {
3227                 return 1;
3228         }
3229         DEBUG(10,("validate_gl: %s ok\n", keystr));
3230         return 0;
3231 }
3232
3233 static int validate_ug(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3234                        struct tdb_validation_status *state)
3235 {
3236         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3237         int32 num_groups, i;
3238
3239         if (!centry) {
3240                 return 1;
3241         }
3242
3243         num_groups = centry_uint32(centry);
3244
3245         for (i=0; i< num_groups; i++) {
3246                 DOM_SID sid;
3247                 centry_sid(centry, &sid);
3248         }
3249
3250         centry_free(centry);
3251
3252         if (!(state->success)) {
3253                 return 1;
3254         }
3255         DEBUG(10,("validate_ug: %s ok\n", keystr));
3256         return 0;
3257 }
3258
3259 static int validate_ua(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3260                        struct tdb_validation_status *state)
3261 {
3262         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3263         int32 num_aliases, i;
3264
3265         if (!centry) {
3266                 return 1;
3267         }
3268
3269         num_aliases = centry_uint32(centry);
3270
3271         for (i=0; i < num_aliases; i++) {
3272                 (void)centry_uint32(centry);
3273         }
3274
3275         centry_free(centry);
3276
3277         if (!(state->success)) {
3278                 return 1;
3279         }
3280         DEBUG(10,("validate_ua: %s ok\n", keystr));
3281         return 0;
3282 }
3283
3284 static int validate_gm(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3285                        struct tdb_validation_status *state)
3286 {
3287         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3288         int32 num_names, i;
3289
3290         if (!centry) {
3291                 return 1;
3292         }
3293
3294         num_names = centry_uint32(centry);
3295
3296         for (i=0; i< num_names; i++) {
3297                 DOM_SID sid;
3298                 centry_sid(centry, &sid);
3299                 (void)centry_string(centry, mem_ctx);
3300                 (void)centry_uint32(centry);
3301         }
3302
3303         centry_free(centry);
3304
3305         if (!(state->success)) {
3306                 return 1;
3307         }
3308         DEBUG(10,("validate_gm: %s ok\n", keystr));
3309         return 0;
3310 }
3311
3312 static int validate_dr(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3313                        struct tdb_validation_status *state)
3314 {
3315         /* Can't say anything about this other than must be nonzero. */
3316         if (dbuf.dsize == 0) {
3317                 DEBUG(0,("validate_dr: Corrupt cache for key %s (len == 0) ?\n",
3318                                 keystr));
3319                 state->bad_entry = true;
3320                 state->success = false;
3321                 return 1;
3322         }
3323
3324         DEBUG(10,("validate_dr: %s ok\n", keystr));
3325         return 0;
3326 }
3327
3328 static int validate_de(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3329                        struct tdb_validation_status *state)
3330 {
3331         /* Can't say anything about this other than must be nonzero. */
3332         if (dbuf.dsize == 0) {
3333                 DEBUG(0,("validate_de: Corrupt cache for key %s (len == 0) ?\n",
3334                                 keystr));
3335                 state->bad_entry = true;
3336                 state->success = false;
3337                 return 1;
3338         }
3339
3340         DEBUG(10,("validate_de: %s ok\n", keystr));
3341         return 0;
3342 }
3343
3344 static int validate_pwinfo(TALLOC_CTX *mem_ctx, const char *keystr,
3345                            TDB_DATA dbuf, struct tdb_validation_status *state)
3346 {
3347         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3348
3349         if (!centry) {
3350                 return 1;
3351         }
3352
3353         (void)centry_string(centry, mem_ctx);
3354         (void)centry_string(centry, mem_ctx);
3355         (void)centry_string(centry, mem_ctx);
3356         (void)centry_uint32(centry);
3357
3358         centry_free(centry);
3359
3360         if (!(state->success)) {
3361                 return 1;
3362         }
3363         DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3364         return 0;
3365 }
3366
3367 static int validate_nss_an(TALLOC_CTX *mem_ctx, const char *keystr,
3368                            TDB_DATA dbuf,
3369                            struct tdb_validation_status *state)
3370 {
3371         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3372
3373         if (!centry) {
3374                 return 1;
3375         }
3376
3377         (void)centry_string( centry, mem_ctx );
3378
3379         centry_free(centry);
3380
3381         if (!(state->success)) {
3382                 return 1;
3383         }
3384         DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3385         return 0;
3386 }
3387
3388 static int validate_nss_na(TALLOC_CTX *mem_ctx, const char *keystr,
3389                            TDB_DATA dbuf,
3390                            struct tdb_validation_status *state)
3391 {
3392         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3393
3394         if (!centry) {
3395                 return 1;
3396         }
3397
3398         (void)centry_string( centry, mem_ctx );
3399
3400         centry_free(centry);
3401
3402         if (!(state->success)) {
3403                 return 1;
3404         }
3405         DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3406         return 0;
3407 }
3408
3409 static int validate_trustdoms(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3410                               struct tdb_validation_status *state)
3411 {
3412         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3413         int32 num_domains, i;
3414
3415         if (!centry) {
3416                 return 1;
3417         }
3418
3419         num_domains = centry_uint32(centry);
3420
3421         for (i=0; i< num_domains; i++) {
3422                 DOM_SID sid;
3423                 (void)centry_string(centry, mem_ctx);
3424                 (void)centry_string(centry, mem_ctx);
3425                 (void)centry_sid(centry, &sid);
3426         }
3427
3428         centry_free(centry);
3429
3430         if (!(state->success)) {
3431                 return 1;
3432         }
3433         DEBUG(10,("validate_trustdoms: %s ok\n", keystr));
3434         return 0;
3435 }
3436
3437 static int validate_trustdomcache(TALLOC_CTX *mem_ctx, const char *keystr, 
3438                                   TDB_DATA dbuf,
3439                                   struct tdb_validation_status *state)
3440 {
3441         if (dbuf.dsize == 0) {
3442                 DEBUG(0, ("validate_trustdomcache: Corrupt cache for "
3443                           "key %s (len ==0) ?\n", keystr));
3444                 state->bad_entry = true;
3445                 state->success = false;
3446                 return 1;
3447         }
3448
3449         DEBUG(10,    ("validate_trustdomcache: %s ok\n", keystr));
3450         DEBUGADD(10, ("  Don't trust me, I am a DUMMY!\n"));
3451         return 0;
3452 }
3453
3454 static int validate_offline(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3455                             struct tdb_validation_status *state)
3456 {
3457         if (dbuf.dsize != 4) {
3458                 DEBUG(0,("validate_offline: Corrupt cache for key %s (len %u != 4) ?\n",
3459                                 keystr, (unsigned int)dbuf.dsize ));
3460                 state->bad_entry = true;
3461                 state->success = false;
3462                 return 1;
3463         }
3464         DEBUG(10,("validate_offline: %s ok\n", keystr));
3465         return 0;
3466 }
3467
3468 static int validate_cache_version(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3469                                   struct tdb_validation_status *state)
3470 {
3471         if (dbuf.dsize != 4) {
3472                 DEBUG(0, ("validate_cache_version: Corrupt cache for "
3473                           "key %s (len %u != 4) ?\n", 
3474                           keystr, (unsigned int)dbuf.dsize));
3475                 state->bad_entry = true;
3476                 state->success = false;
3477                 return 1;
3478         }
3479
3480         DEBUG(10, ("validate_cache_version: %s ok\n", keystr));
3481         return 0;
3482 }
3483
3484 /***********************************************************************
3485  A list of all possible cache tdb keys with associated validation
3486  functions.
3487 ***********************************************************************/
3488
3489 struct key_val_struct {
3490         const char *keyname;
3491         int (*validate_data_fn)(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, struct tdb_validation_status* state);
3492 } key_val[] = {
3493         {"SEQNUM/", validate_seqnum},
3494         {"NS/", validate_ns},
3495         {"SN/", validate_sn},
3496         {"U/", validate_u},
3497         {"LOC_POL/", validate_loc_pol},
3498         {"PWD_POL/", validate_pwd_pol},
3499         {"CRED/", validate_cred},
3500         {"UL/", validate_ul},
3501         {"GL/", validate_gl},
3502         {"UG/", validate_ug},
3503         {"UA", validate_ua},
3504         {"GM/", validate_gm},
3505         {"DR/", validate_dr},
3506         {"DE/", validate_de},
3507         {"NSS/PWINFO/", validate_pwinfo},
3508         {"TRUSTDOMS/", validate_trustdoms},
3509         {"TRUSTDOMCACHE/", validate_trustdomcache},
3510         {"NSS/NA/", validate_nss_na},
3511         {"NSS/AN/", validate_nss_an},
3512         {"WINBINDD_OFFLINE", validate_offline},
3513         {WINBINDD_CACHE_VERSION_KEYSTR, validate_cache_version},
3514         {NULL, NULL}
3515 };
3516
3517 /***********************************************************************
3518  Function to look at every entry in the tdb and validate it as far as
3519  possible.
3520 ***********************************************************************/
3521
3522 static int cache_traverse_validate_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, void *state)
3523 {
3524         int i;
3525         unsigned int max_key_len = 1024;
3526         struct tdb_validation_status *v_state = (struct tdb_validation_status *)state;
3527
3528         /* Paranoia check. */
3529         if (strncmp("UA/", (const char *)kbuf.dptr, 3) == 0) {
3530                 max_key_len = 1024 * 1024;
3531         }
3532         if (kbuf.dsize > max_key_len) {
3533                 DEBUG(0, ("cache_traverse_validate_fn: key length too large: "
3534                           "(%u) > (%u)\n\n",
3535                           (unsigned int)kbuf.dsize, (unsigned int)max_key_len));
3536                 return 1;
3537         }
3538
3539         for (i = 0; key_val[i].keyname; i++) {
3540                 size_t namelen = strlen(key_val[i].keyname);
3541                 if (kbuf.dsize >= namelen && (
3542                                 strncmp(key_val[i].keyname, (const char *)kbuf.dptr, namelen)) == 0) {
3543                         TALLOC_CTX *mem_ctx;
3544                         char *keystr;
3545                         int ret;
3546
3547                         keystr = SMB_MALLOC_ARRAY(char, kbuf.dsize+1);
3548                         if (!keystr) {
3549                                 return 1;
3550                         }
3551                         memcpy(keystr, kbuf.dptr, kbuf.dsize);
3552                         keystr[kbuf.dsize] = '\0';
3553
3554                         mem_ctx = talloc_init("validate_ctx");
3555                         if (!mem_ctx) {
3556                                 SAFE_FREE(keystr);
3557                                 return 1;
3558                         }
3559
3560                         ret = key_val[i].validate_data_fn(mem_ctx, keystr, dbuf, 
3561                                                           v_state);
3562
3563                         SAFE_FREE(keystr);
3564                         talloc_destroy(mem_ctx);
3565                         return ret;
3566                 }
3567         }
3568
3569         DEBUG(0,("cache_traverse_validate_fn: unknown cache entry\nkey :\n"));
3570         dump_data(0, (uint8 *)kbuf.dptr, kbuf.dsize);
3571         DEBUG(0,("data :\n"));
3572         dump_data(0, (uint8 *)dbuf.dptr, dbuf.dsize);
3573         v_state->unknown_key = true;
3574         v_state->success = false;
3575         return 1; /* terminate. */
3576 }
3577
3578 static void validate_panic(const char *const why)
3579 {
3580         DEBUG(0,("validating cache: would panic %s\n", why ));
3581         DEBUGADD(0, ("exiting instead (cache validation mode)\n"));
3582         exit(47);
3583 }
3584
3585 /***********************************************************************
3586  Try and validate every entry in the winbindd cache. If we fail here,
3587  delete the cache tdb and return non-zero.
3588 ***********************************************************************/
3589
3590 int winbindd_validate_cache(void)
3591 {
3592         int ret = -1;
3593         const char *tdb_path = cache_path("winbindd_cache.tdb");
3594         TDB_CONTEXT *tdb = NULL;
3595
3596         DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
3597         smb_panic_fn = validate_panic;
3598
3599
3600         tdb = tdb_open_log(tdb_path, 
3601                            WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
3602                            ( lp_winbind_offline_logon() 
3603                              ? TDB_DEFAULT 
3604                              : TDB_DEFAULT | TDB_CLEAR_IF_FIRST ),
3605                            O_RDWR|O_CREAT, 
3606                            0600);
3607         if (!tdb) {
3608                 DEBUG(0, ("winbindd_validate_cache: "
3609                           "error opening/initializing tdb\n"));
3610                 goto done;
3611         }
3612         tdb_close(tdb);
3613
3614         ret = tdb_validate_and_backup(tdb_path, cache_traverse_validate_fn);
3615
3616         if (ret != 0) {
3617                 DEBUG(10, ("winbindd_validate_cache: validation not successful.\n"));
3618                 DEBUGADD(10, ("removing tdb %s.\n", tdb_path));
3619                 unlink(tdb_path);
3620         }
3621
3622 done:
3623         DEBUG(10, ("winbindd_validate_cache: restoring panic function\n"));
3624         smb_panic_fn = smb_panic;
3625         return ret;
3626 }
3627
3628 /***********************************************************************
3629  Try and validate every entry in the winbindd cache.
3630 ***********************************************************************/
3631
3632 int winbindd_validate_cache_nobackup(void)
3633 {
3634         int ret = -1;
3635         const char *tdb_path = cache_path("winbindd_cache.tdb");
3636
3637         DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
3638         smb_panic_fn = validate_panic;
3639
3640
3641         if (wcache == NULL || wcache->tdb == NULL) {
3642                 ret = tdb_validate_open(tdb_path, cache_traverse_validate_fn);
3643         } else {
3644                 ret = tdb_validate(wcache->tdb, cache_traverse_validate_fn);
3645         }
3646
3647         if (ret != 0) {
3648                 DEBUG(10, ("winbindd_validate_cache_nobackup: validation not "
3649                            "successful.\n"));
3650         }
3651
3652         DEBUG(10, ("winbindd_validate_cache_nobackup: restoring panic "
3653                    "function\n"));
3654         smb_panic_fn = smb_panic;
3655         return ret;
3656 }
3657
3658 bool winbindd_cache_validate_and_initialize(void)
3659 {
3660         close_winbindd_cache();
3661
3662         if (lp_winbind_offline_logon()) {
3663                 if (winbindd_validate_cache() < 0) {
3664                         DEBUG(0, ("winbindd cache tdb corrupt and no backup "
3665                                   "could be restored.\n"));
3666                 }
3667         }
3668
3669         return initialize_winbindd_cache();
3670 }
3671
3672 /*********************************************************************
3673  ********************************************************************/
3674
3675 static bool add_wbdomain_to_tdc_array( struct winbindd_domain *new_dom,
3676                                        struct winbindd_tdc_domain **domains, 
3677                                        size_t *num_domains )
3678 {
3679         struct winbindd_tdc_domain *list = NULL;
3680         size_t idx;
3681         int i;
3682         bool set_only = false;
3683
3684         /* don't allow duplicates */
3685
3686         idx = *num_domains;
3687         list = *domains;
3688
3689         for ( i=0; i< (*num_domains); i++ ) {
3690                 if ( strequal( new_dom->name, list[i].domain_name ) ) {
3691                         DEBUG(10,("add_wbdomain_to_tdc_array: Found existing record for %s\n",
3692                                   new_dom->name));
3693                         idx = i;
3694                         set_only = true;
3695
3696                         break;
3697                 }
3698         }
3699
3700         if ( !set_only ) {
3701                 if ( !*domains ) {
3702                         list = TALLOC_ARRAY( NULL, struct winbindd_tdc_domain, 1 );
3703                         idx = 0;
3704                 } else {
3705                         list = TALLOC_REALLOC_ARRAY( *domains, *domains, 
3706                                                      struct winbindd_tdc_domain,  
3707                                                      (*num_domains)+1);
3708                         idx = *num_domains;             
3709                 }
3710
3711                 ZERO_STRUCT( list[idx] );
3712         }
3713
3714         if ( !list )
3715                 return false;
3716
3717         list[idx].domain_name = talloc_strdup( list, new_dom->name );
3718         list[idx].dns_name = talloc_strdup( list, new_dom->alt_name );
3719
3720         if ( !is_null_sid( &new_dom->sid ) ) {
3721                 sid_copy( &list[idx].sid, &new_dom->sid );
3722         } else {
3723                 sid_copy(&list[idx].sid, &global_sid_NULL);
3724         }
3725
3726         if ( new_dom->domain_flags != 0x0 )
3727                 list[idx].trust_flags = new_dom->domain_flags;  
3728
3729         if ( new_dom->domain_type != 0x0 )
3730                 list[idx].trust_type = new_dom->domain_type;
3731
3732         if ( new_dom->domain_trust_attribs != 0x0 )
3733                 list[idx].trust_attribs = new_dom->domain_trust_attribs;
3734
3735         if ( !set_only ) {
3736                 *domains = list;
3737                 *num_domains = idx + 1; 
3738         }
3739
3740         return true;
3741 }
3742
3743 /*********************************************************************
3744  ********************************************************************/
3745
3746 static TDB_DATA make_tdc_key( const char *domain_name )
3747 {
3748         char *keystr = NULL;
3749         TDB_DATA key = { NULL, 0 };
3750
3751         if ( !domain_name ) {
3752                 DEBUG(5,("make_tdc_key: Keyname workgroup is NULL!\n"));
3753                 return key;
3754         }
3755
3756         if (asprintf( &keystr, "TRUSTDOMCACHE/%s", domain_name ) == -1) {
3757                 return key;
3758         }
3759         key = string_term_tdb_data(keystr);
3760
3761         return key;     
3762 }
3763
3764 /*********************************************************************
3765  ********************************************************************/
3766
3767 static int pack_tdc_domains( struct winbindd_tdc_domain *domains, 
3768                              size_t num_domains,
3769                              unsigned char **buf )
3770 {
3771         unsigned char *buffer = NULL;
3772         int len = 0;
3773         int buflen = 0;
3774         int i = 0;
3775
3776         DEBUG(10,("pack_tdc_domains: Packing %d trusted domains\n",
3777                   (int)num_domains));
3778
3779         buflen = 0;
3780
3781  again: 
3782         len = 0;
3783
3784         /* Store the number of array items first */
3785         len += tdb_pack( buffer+len, buflen-len, "d", 
3786                          num_domains );
3787
3788         /* now pack each domain trust record */
3789         for ( i=0; i<num_domains; i++ ) {
3790
3791                 fstring tmp;
3792
3793                 if ( buflen > 0 ) {
3794                         DEBUG(10,("pack_tdc_domains: Packing domain %s (%s)\n",
3795                                   domains[i].domain_name,
3796                                   domains[i].dns_name ? domains[i].dns_name : "UNKNOWN" ));
3797                 }
3798
3799                 len += tdb_pack( buffer+len, buflen-len, "fffddd",
3800                                  domains[i].domain_name,
3801                                  domains[i].dns_name,
3802                                  sid_to_fstring(tmp, &domains[i].sid),
3803                                  domains[i].trust_flags,
3804                                  domains[i].trust_attribs,
3805                                  domains[i].trust_type );
3806         }
3807
3808         if ( buflen < len ) {
3809                 SAFE_FREE(buffer);
3810                 if ( (buffer = SMB_MALLOC_ARRAY(unsigned char, len)) == NULL ) {
3811                         DEBUG(0,("pack_tdc_domains: failed to alloc buffer!\n"));
3812                         buflen = -1;
3813                         goto done;
3814                 }
3815                 buflen = len;
3816                 goto again;
3817         }
3818
3819         *buf = buffer;  
3820
3821  done:  
3822         return buflen;  
3823 }
3824
3825 /*********************************************************************
3826  ********************************************************************/
3827
3828 static size_t unpack_tdc_domains( unsigned char *buf, int buflen, 
3829                                   struct winbindd_tdc_domain **domains )
3830 {
3831         fstring domain_name, dns_name, sid_string;      
3832         uint32 type, attribs, flags;
3833         int num_domains;
3834         int len = 0;
3835         int i;
3836         struct winbindd_tdc_domain *list = NULL;
3837
3838         /* get the number of domains */
3839         len += tdb_unpack( buf+len, buflen-len, "d", &num_domains);
3840         if ( len == -1 ) {
3841                 DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));               
3842                 return 0;
3843         }
3844
3845         list = TALLOC_ARRAY( NULL, struct winbindd_tdc_domain, num_domains );
3846         if ( !list ) {
3847                 DEBUG(0,("unpack_tdc_domains: Failed to talloc() domain list!\n"));
3848                 return 0;               
3849         }
3850
3851         for ( i=0; i<num_domains; i++ ) {
3852                 len += tdb_unpack( buf+len, buflen-len, "fffddd",
3853                                    domain_name,
3854                                    dns_name,
3855                                    sid_string,
3856                                    &flags,
3857                                    &attribs,
3858                                    &type );
3859
3860                 if ( len == -1 ) {
3861                         DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
3862                         TALLOC_FREE( list );                    
3863                         return 0;
3864                 }
3865
3866                 DEBUG(11,("unpack_tdc_domains: Unpacking domain %s (%s) "
3867                           "SID %s, flags = 0x%x, attribs = 0x%x, type = 0x%x\n",
3868                           domain_name, dns_name, sid_string,
3869                           flags, attribs, type));
3870
3871                 list[i].domain_name = talloc_strdup( list, domain_name );
3872                 list[i].dns_name = talloc_strdup( list, dns_name );
3873                 if ( !string_to_sid( &(list[i].sid), sid_string ) ) {                   
3874                         DEBUG(10,("unpack_tdc_domains: no SID for domain %s\n",
3875                                   domain_name));
3876                 }
3877                 list[i].trust_flags = flags;
3878                 list[i].trust_attribs = attribs;
3879                 list[i].trust_type = type;
3880         }
3881
3882         *domains = list;
3883
3884         return num_domains;
3885 }
3886
3887 /*********************************************************************
3888  ********************************************************************/
3889
3890 static bool wcache_tdc_store_list( struct winbindd_tdc_domain *domains, size_t num_domains )
3891 {
3892         TDB_DATA key = make_tdc_key( lp_workgroup() );   
3893         TDB_DATA data = { NULL, 0 };
3894         int ret;
3895
3896         if ( !key.dptr )
3897                 return false;
3898
3899         /* See if we were asked to delete the cache entry */
3900
3901         if ( !domains ) {
3902                 ret = tdb_delete( wcache->tdb, key );
3903                 goto done;
3904         }
3905
3906         data.dsize = pack_tdc_domains( domains, num_domains, &data.dptr );
3907
3908         if ( !data.dptr ) {
3909                 ret = -1;
3910                 goto done;
3911         }
3912
3913         ret = tdb_store( wcache->tdb, key, data, 0 );
3914
3915  done:
3916         SAFE_FREE( data.dptr );
3917         SAFE_FREE( key.dptr );
3918
3919         return ( ret != -1 );   
3920 }
3921
3922 /*********************************************************************
3923  ********************************************************************/
3924
3925 bool wcache_tdc_fetch_list( struct winbindd_tdc_domain **domains, size_t *num_domains )
3926 {
3927         TDB_DATA key = make_tdc_key( lp_workgroup() );
3928         TDB_DATA data = { NULL, 0 };
3929
3930         *domains = NULL;        
3931         *num_domains = 0;       
3932
3933         if ( !key.dptr )
3934                 return false;
3935
3936         data = tdb_fetch( wcache->tdb, key );
3937
3938         SAFE_FREE( key.dptr );
3939
3940         if ( !data.dptr ) 
3941                 return false;
3942
3943         *num_domains = unpack_tdc_domains( data.dptr, data.dsize, domains );
3944
3945         SAFE_FREE( data.dptr );
3946
3947         if ( !*domains )
3948                 return false;
3949
3950         return true;
3951 }
3952
3953 /*********************************************************************
3954  ********************************************************************/
3955
3956 bool wcache_tdc_add_domain( struct winbindd_domain *domain )
3957 {
3958         struct winbindd_tdc_domain *dom_list = NULL;
3959         size_t num_domains = 0;
3960         bool ret = false;
3961
3962         DEBUG(10,("wcache_tdc_add_domain: Adding domain %s (%s), SID %s, "
3963                   "flags = 0x%x, attributes = 0x%x, type = 0x%x\n",
3964                   domain->name, domain->alt_name, 
3965                   sid_string_dbg(&domain->sid),
3966                   domain->domain_flags,
3967                   domain->domain_trust_attribs,
3968                   domain->domain_type));        
3969
3970         if ( !init_wcache() ) {
3971                 return false;
3972         }
3973
3974         /* fetch the list */
3975
3976         wcache_tdc_fetch_list( &dom_list, &num_domains );
3977
3978         /* add the new domain */
3979
3980         if ( !add_wbdomain_to_tdc_array( domain, &dom_list, &num_domains ) ) {
3981                 goto done;              
3982         }       
3983
3984         /* pack the domain */
3985
3986         if ( !wcache_tdc_store_list( dom_list, num_domains ) ) {
3987                 goto done;              
3988         }
3989
3990         /* Success */
3991
3992         ret = true;
3993  done:
3994         TALLOC_FREE( dom_list );
3995
3996         return ret;     
3997 }
3998
3999 /*********************************************************************
4000  ********************************************************************/
4001
4002 struct winbindd_tdc_domain * wcache_tdc_fetch_domain( TALLOC_CTX *ctx, const char *name )
4003 {
4004         struct winbindd_tdc_domain *dom_list = NULL;
4005         size_t num_domains = 0;
4006         int i;
4007         struct winbindd_tdc_domain *d = NULL;   
4008
4009         DEBUG(10,("wcache_tdc_fetch_domain: Searching for domain %s\n", name));
4010
4011         if ( !init_wcache() ) {
4012                 return false;
4013         }
4014
4015         /* fetch the list */
4016
4017         wcache_tdc_fetch_list( &dom_list, &num_domains );
4018
4019         for ( i=0; i<num_domains; i++ ) {
4020                 if ( strequal(name, dom_list[i].domain_name) ||
4021                      strequal(name, dom_list[i].dns_name) )
4022                 {
4023                         DEBUG(10,("wcache_tdc_fetch_domain: Found domain %s\n",
4024                                   name));
4025
4026                         d = TALLOC_P( ctx, struct winbindd_tdc_domain );
4027                         if ( !d )
4028                                 break;                  
4029
4030                         d->domain_name = talloc_strdup( d, dom_list[i].domain_name );
4031                         d->dns_name = talloc_strdup( d, dom_list[i].dns_name );
4032                         sid_copy( &d->sid, &dom_list[i].sid );
4033                         d->trust_flags   = dom_list[i].trust_flags;
4034                         d->trust_type    = dom_list[i].trust_type;
4035                         d->trust_attribs = dom_list[i].trust_attribs;
4036
4037                         break;
4038                 }
4039         }
4040
4041         TALLOC_FREE( dom_list );
4042
4043         return d;       
4044 }
4045
4046
4047 /*********************************************************************
4048  ********************************************************************/
4049
4050 void wcache_tdc_clear( void )
4051 {
4052         if ( !init_wcache() )
4053                 return;
4054
4055         wcache_tdc_store_list( NULL, 0 );
4056
4057         return; 
4058 }
4059
4060
4061 /*********************************************************************
4062  ********************************************************************/
4063
4064 static void wcache_save_user_pwinfo(struct winbindd_domain *domain, 
4065                                     NTSTATUS status,
4066                                     const DOM_SID *user_sid,
4067                                     const char *homedir,
4068                                     const char *shell,
4069                                     const char *gecos,
4070                                     uint32 gid)
4071 {
4072         struct cache_entry *centry;
4073         fstring tmp;
4074
4075         if ( (centry = centry_start(domain, status)) == NULL )
4076                 return;
4077
4078         centry_put_string( centry, homedir );
4079         centry_put_string( centry, shell );
4080         centry_put_string( centry, gecos );
4081         centry_put_uint32( centry, gid );
4082
4083         centry_end(centry, "NSS/PWINFO/%s", sid_to_fstring(tmp, user_sid) );
4084
4085         DEBUG(10,("wcache_save_user_pwinfo: %s\n", sid_string_dbg(user_sid) ));
4086
4087         centry_free(centry);
4088 }
4089
4090 NTSTATUS nss_get_info_cached( struct winbindd_domain *domain, 
4091                               const DOM_SID *user_sid,
4092                               TALLOC_CTX *ctx,
4093                               ADS_STRUCT *ads, LDAPMessage *msg,
4094                               const char **homedir, const char **shell,
4095                               const char **gecos, gid_t *p_gid)
4096 {
4097         struct winbind_cache *cache = get_cache(domain);
4098         struct cache_entry *centry = NULL;
4099         NTSTATUS nt_status;
4100         fstring tmp;
4101
4102         if (!cache->tdb)
4103                 goto do_query;
4104
4105         centry = wcache_fetch(cache, domain, "NSS/PWINFO/%s",
4106                               sid_to_fstring(tmp, user_sid));
4107
4108         if (!centry)
4109                 goto do_query;
4110
4111         *homedir = centry_string( centry, ctx );
4112         *shell   = centry_string( centry, ctx );
4113         *gecos   = centry_string( centry, ctx );
4114         *p_gid   = centry_uint32( centry );     
4115
4116         centry_free(centry);
4117
4118         DEBUG(10,("nss_get_info_cached: [Cached] - user_sid %s\n",
4119                   sid_string_dbg(user_sid)));
4120
4121         return NT_STATUS_OK;
4122
4123 do_query:
4124
4125         nt_status = nss_get_info( domain->name, user_sid, ctx, ads, msg, 
4126                                   homedir, shell, gecos, p_gid );
4127
4128         DEBUG(10, ("nss_get_info returned %s\n", nt_errstr(nt_status)));
4129
4130         if ( NT_STATUS_IS_OK(nt_status) ) {
4131                 DEBUG(10, ("result:\n\thomedir = '%s'\n", *homedir));
4132                 DEBUGADD(10, ("\tshell = '%s'\n", *shell));
4133                 DEBUGADD(10, ("\tgecos = '%s'\n", *gecos));
4134                 DEBUGADD(10, ("\tgid = '%u'\n", (unsigned int)*p_gid));
4135
4136                 wcache_save_user_pwinfo( domain, nt_status, user_sid,
4137                                          *homedir, *shell, *gecos, *p_gid );
4138         }       
4139
4140         if ( NT_STATUS_EQUAL( nt_status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND ) ) {
4141                 DEBUG(5,("nss_get_info_cached: Setting domain %s offline\n",
4142                          domain->name ));
4143                 set_domain_offline( domain );
4144         }
4145
4146         return nt_status;       
4147 }
4148
4149
4150 /* the cache backend methods are exposed via this structure */
4151 struct winbindd_methods cache_methods = {
4152         true,
4153         query_user_list,
4154         enum_dom_groups,
4155         enum_local_groups,
4156         name_to_sid,
4157         sid_to_name,
4158         rids_to_names,
4159         query_user,
4160         lookup_usergroups,
4161         lookup_useraliases,
4162         lookup_groupmem,
4163         sequence_number,
4164         lockout_policy,
4165         password_policy,
4166         trusted_domains
4167 };