Refactoring: mem_ctx is not needed as an argument to centry_sid
[kai/samba.git] / source3 / winbindd / winbindd_cache.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    Winbind cache backend functions
5
6    Copyright (C) Andrew Tridgell 2001
7    Copyright (C) Gerald Carter   2003-2007
8    Copyright (C) Volker Lendecke 2005
9    Copyright (C) Guenther Deschner 2005
10    Copyright (C) Michael Adam    2007
11
12    This program is free software; you can redistribute it and/or modify
13    it under the terms of the GNU General Public License as published by
14    the Free Software Foundation; either version 3 of the License, or
15    (at your option) any later version.
16
17    This program is distributed in the hope that it will be useful,
18    but WITHOUT ANY WARRANTY; without even the implied warranty of
19    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20    GNU General Public License for more details.
21
22    You should have received a copy of the GNU General Public License
23    along with this program.  If not, see <http://www.gnu.org/licenses/>.
24 */
25
26 #include "includes.h"
27 #include "winbindd.h"
28 #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                             enum winbindd_cmd orig_cmd,
1612                             const char *domain_name,
1613                             const char *name,
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, orig_cmd, 
1661                                               domain_name, name, 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 /* convert a sid to a user or group name. The sid is guaranteed to be in the domain
1682    given */
1683 static NTSTATUS sid_to_name(struct winbindd_domain *domain,
1684                             TALLOC_CTX *mem_ctx,
1685                             const DOM_SID *sid,
1686                             char **domain_name,
1687                             char **name,
1688                             enum lsa_SidType *type)
1689 {
1690         struct winbind_cache *cache = get_cache(domain);
1691         struct cache_entry *centry = NULL;
1692         NTSTATUS status;
1693         fstring sid_string;
1694
1695         if (!cache->tdb)
1696                 goto do_query;
1697
1698         centry = wcache_fetch(cache, domain, "SN/%s",
1699                               sid_to_fstring(sid_string, sid));
1700         if (!centry)
1701                 goto do_query;
1702
1703         status = centry->status;
1704         if (NT_STATUS_IS_OK(status)) {
1705                 *type = (enum lsa_SidType)centry_uint32(centry);
1706                 *domain_name = centry_string(centry, mem_ctx);
1707                 *name = centry_string(centry, mem_ctx);
1708         }
1709
1710         DEBUG(10,("sid_to_name: [Cached] - cached name for domain %s status: %s\n",
1711                 domain->name, nt_errstr(status) ));
1712
1713         centry_free(centry);
1714         return status;
1715
1716 do_query:
1717         *name = NULL;
1718         *domain_name = NULL;
1719
1720         /* If the seq number check indicated that there is a problem
1721          * with this DC, then return that status... except for
1722          * access_denied.  This is special because the dc may be in
1723          * "restrict anonymous = 1" mode, in which case it will deny
1724          * most unauthenticated operations, but *will* allow the LSA
1725          * sid-to-name that we try as a fallback. */
1726
1727         if (!(NT_STATUS_IS_OK(domain->last_status)
1728               || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1729                 return domain->last_status;
1730
1731         DEBUG(10,("sid_to_name: [Cached] - doing backend query for name for domain %s\n",
1732                 domain->name ));
1733
1734         status = domain->backend->sid_to_name(domain, mem_ctx, sid, domain_name, name, type);
1735
1736         /* and save it */
1737         refresh_sequence_number(domain, false);
1738         wcache_save_sid_to_name(domain, status, sid, *domain_name, *name, *type);
1739
1740         /* We can't save the name to sid mapping here, as with sid history a
1741          * later name2sid would give the wrong sid. */
1742
1743         return status;
1744 }
1745
1746 static NTSTATUS rids_to_names(struct winbindd_domain *domain,
1747                               TALLOC_CTX *mem_ctx,
1748                               const DOM_SID *domain_sid,
1749                               uint32 *rids,
1750                               size_t num_rids,
1751                               char **domain_name,
1752                               char ***names,
1753                               enum lsa_SidType **types)
1754 {
1755         struct winbind_cache *cache = get_cache(domain);
1756         size_t i;
1757         NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
1758         bool have_mapped;
1759         bool have_unmapped;
1760
1761         *domain_name = NULL;
1762         *names = NULL;
1763         *types = NULL;
1764
1765         if (!cache->tdb) {
1766                 goto do_query;
1767         }
1768
1769         if (num_rids == 0) {
1770                 return NT_STATUS_OK;
1771         }
1772
1773         *names = TALLOC_ARRAY(mem_ctx, char *, num_rids);
1774         *types = TALLOC_ARRAY(mem_ctx, enum lsa_SidType, num_rids);
1775
1776         if ((*names == NULL) || (*types == NULL)) {
1777                 result = NT_STATUS_NO_MEMORY;
1778                 goto error;
1779         }
1780
1781         have_mapped = have_unmapped = false;
1782
1783         for (i=0; i<num_rids; i++) {
1784                 DOM_SID sid;
1785                 struct cache_entry *centry;
1786                 fstring tmp;
1787
1788                 if (!sid_compose(&sid, domain_sid, rids[i])) {
1789                         result = NT_STATUS_INTERNAL_ERROR;
1790                         goto error;
1791                 }
1792
1793                 centry = wcache_fetch(cache, domain, "SN/%s",
1794                                       sid_to_fstring(tmp, &sid));
1795                 if (!centry) {
1796                         goto do_query;
1797                 }
1798
1799                 (*types)[i] = SID_NAME_UNKNOWN;
1800                 (*names)[i] = talloc_strdup(*names, "");
1801
1802                 if (NT_STATUS_IS_OK(centry->status)) {
1803                         char *dom;
1804                         have_mapped = true;
1805                         (*types)[i] = (enum lsa_SidType)centry_uint32(centry);
1806
1807                         dom = centry_string(centry, mem_ctx);
1808                         if (*domain_name == NULL) {
1809                                 *domain_name = dom;
1810                         } else {
1811                                 talloc_free(dom);
1812                         }
1813
1814                         (*names)[i] = centry_string(centry, *names);
1815
1816                 } else if (NT_STATUS_EQUAL(centry->status, NT_STATUS_NONE_MAPPED)) {
1817                         have_unmapped = true;
1818
1819                 } else {
1820                         /* something's definitely wrong */
1821                         result = centry->status;
1822                         goto error;
1823                 }
1824
1825                 centry_free(centry);
1826         }
1827
1828         if (!have_mapped) {
1829                 return NT_STATUS_NONE_MAPPED;
1830         }
1831         if (!have_unmapped) {
1832                 return NT_STATUS_OK;
1833         }
1834         return STATUS_SOME_UNMAPPED;
1835
1836  do_query:
1837
1838         TALLOC_FREE(*names);
1839         TALLOC_FREE(*types);
1840
1841         result = domain->backend->rids_to_names(domain, mem_ctx, domain_sid,
1842                                                 rids, num_rids, domain_name,
1843                                                 names, types);
1844
1845         /*
1846           None of the queried rids has been found so save all negative entries
1847         */
1848         if (NT_STATUS_EQUAL(result, NT_STATUS_NONE_MAPPED)) {
1849                 for (i = 0; i < num_rids; i++) {
1850                         DOM_SID sid;
1851                         const char *name = "";
1852                         const enum lsa_SidType type = SID_NAME_UNKNOWN;
1853                         NTSTATUS status = NT_STATUS_NONE_MAPPED;
1854
1855                         if (!sid_compose(&sid, domain_sid, rids[i])) {
1856                                 return NT_STATUS_INTERNAL_ERROR;
1857                         }
1858
1859                         wcache_save_sid_to_name(domain, status, &sid, *domain_name,
1860                                                 name, type);
1861                 }
1862
1863                 return result;
1864         }
1865
1866         /*
1867           Some or all of the queried rids have been found.
1868         */
1869         if (!NT_STATUS_IS_OK(result) &&
1870             !NT_STATUS_EQUAL(result, STATUS_SOME_UNMAPPED)) {
1871                 return result;
1872         }
1873
1874         refresh_sequence_number(domain, false);
1875
1876         for (i=0; i<num_rids; i++) {
1877                 DOM_SID sid;
1878                 NTSTATUS status;
1879
1880                 if (!sid_compose(&sid, domain_sid, rids[i])) {
1881                         result = NT_STATUS_INTERNAL_ERROR;
1882                         goto error;
1883                 }
1884
1885                 status = (*types)[i] == SID_NAME_UNKNOWN ?
1886                         NT_STATUS_NONE_MAPPED : NT_STATUS_OK;
1887
1888                 wcache_save_sid_to_name(domain, status, &sid, *domain_name,
1889                                         (*names)[i], (*types)[i]);
1890         }
1891
1892         return result;
1893
1894  error:
1895         TALLOC_FREE(*names);
1896         TALLOC_FREE(*types);
1897         return result;
1898 }
1899
1900 /* Lookup user information from a rid */
1901 static NTSTATUS query_user(struct winbindd_domain *domain, 
1902                            TALLOC_CTX *mem_ctx, 
1903                            const DOM_SID *user_sid, 
1904                            WINBIND_USERINFO *info)
1905 {
1906         struct winbind_cache *cache = get_cache(domain);
1907         struct cache_entry *centry = NULL;
1908         NTSTATUS status;
1909         fstring tmp;
1910
1911         if (!cache->tdb)
1912                 goto do_query;
1913
1914         centry = wcache_fetch(cache, domain, "U/%s",
1915                               sid_to_fstring(tmp, user_sid));
1916
1917         /* If we have an access denied cache entry and a cached info3 in the
1918            samlogon cache then do a query.  This will force the rpc back end
1919            to return the info3 data. */
1920
1921         if (NT_STATUS_V(domain->last_status) == NT_STATUS_V(NT_STATUS_ACCESS_DENIED) &&
1922             netsamlogon_cache_have(user_sid)) {
1923                 DEBUG(10, ("query_user: cached access denied and have cached info3\n"));
1924                 domain->last_status = NT_STATUS_OK;
1925                 centry_free(centry);
1926                 goto do_query;
1927         }
1928
1929         if (!centry)
1930                 goto do_query;
1931
1932         /* if status is not ok then this is a negative hit
1933            and the rest of the data doesn't matter */
1934         status = centry->status;
1935         if (NT_STATUS_IS_OK(status)) {
1936                 info->acct_name = centry_string(centry, mem_ctx);
1937                 info->full_name = centry_string(centry, mem_ctx);
1938                 info->homedir = centry_string(centry, mem_ctx);
1939                 info->shell = centry_string(centry, mem_ctx);
1940                 info->primary_gid = centry_uint32(centry);
1941                 centry_sid(centry, &info->user_sid);
1942                 centry_sid(centry, &info->group_sid);
1943         }
1944
1945         DEBUG(10,("query_user: [Cached] - cached info for domain %s status: %s\n",
1946                 domain->name, nt_errstr(status) ));
1947
1948         centry_free(centry);
1949         return status;
1950
1951 do_query:
1952         ZERO_STRUCTP(info);
1953
1954         /* Return status value returned by seq number check */
1955
1956         if (!NT_STATUS_IS_OK(domain->last_status))
1957                 return domain->last_status;
1958
1959         DEBUG(10,("query_user: [Cached] - doing backend query for info for domain %s\n",
1960                 domain->name ));
1961
1962         status = domain->backend->query_user(domain, mem_ctx, user_sid, info);
1963
1964         /* and save it */
1965         refresh_sequence_number(domain, false);
1966         wcache_save_user(domain, status, info);
1967
1968         return status;
1969 }
1970
1971
1972 /* Lookup groups a user is a member of. */
1973 static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
1974                                   TALLOC_CTX *mem_ctx,
1975                                   const DOM_SID *user_sid, 
1976                                   uint32 *num_groups, DOM_SID **user_gids)
1977 {
1978         struct winbind_cache *cache = get_cache(domain);
1979         struct cache_entry *centry = NULL;
1980         NTSTATUS status;
1981         unsigned int i;
1982         fstring sid_string;
1983
1984         if (!cache->tdb)
1985                 goto do_query;
1986
1987         centry = wcache_fetch(cache, domain, "UG/%s",
1988                               sid_to_fstring(sid_string, user_sid));
1989
1990         /* If we have an access denied cache entry and a cached info3 in the
1991            samlogon cache then do a query.  This will force the rpc back end
1992            to return the info3 data. */
1993
1994         if (NT_STATUS_V(domain->last_status) == NT_STATUS_V(NT_STATUS_ACCESS_DENIED) &&
1995             netsamlogon_cache_have(user_sid)) {
1996                 DEBUG(10, ("lookup_usergroups: cached access denied and have cached info3\n"));
1997                 domain->last_status = NT_STATUS_OK;
1998                 centry_free(centry);
1999                 goto do_query;
2000         }
2001
2002         if (!centry)
2003                 goto do_query;
2004
2005         *num_groups = centry_uint32(centry);
2006
2007         if (*num_groups == 0)
2008                 goto do_cached;
2009
2010         (*user_gids) = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_groups);
2011         if (! (*user_gids)) {
2012                 smb_panic_fn("lookup_usergroups out of memory");
2013         }
2014         for (i=0; i<(*num_groups); i++) {
2015                 centry_sid(centry, &(*user_gids)[i]);
2016         }
2017
2018 do_cached:      
2019         status = centry->status;
2020
2021         DEBUG(10,("lookup_usergroups: [Cached] - cached info for domain %s status: %s\n",
2022                 domain->name, nt_errstr(status) ));
2023
2024         centry_free(centry);
2025         return status;
2026
2027 do_query:
2028         (*num_groups) = 0;
2029         (*user_gids) = NULL;
2030
2031         /* Return status value returned by seq number check */
2032
2033         if (!NT_STATUS_IS_OK(domain->last_status))
2034                 return domain->last_status;
2035
2036         DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info for domain %s\n",
2037                 domain->name ));
2038
2039         status = domain->backend->lookup_usergroups(domain, mem_ctx, user_sid, num_groups, user_gids);
2040
2041         if ( NT_STATUS_EQUAL(status, NT_STATUS_SYNCHRONIZATION_REQUIRED) )
2042                 goto skip_save;
2043
2044         /* and save it */
2045         refresh_sequence_number(domain, false);
2046         centry = centry_start(domain, status);
2047         if (!centry)
2048                 goto skip_save;
2049
2050         centry_put_uint32(centry, *num_groups);
2051         for (i=0; i<(*num_groups); i++) {
2052                 centry_put_sid(centry, &(*user_gids)[i]);
2053         }       
2054
2055         centry_end(centry, "UG/%s", sid_to_fstring(sid_string, user_sid));
2056         centry_free(centry);
2057
2058 skip_save:
2059         return status;
2060 }
2061
2062 static NTSTATUS lookup_useraliases(struct winbindd_domain *domain,
2063                                    TALLOC_CTX *mem_ctx,
2064                                    uint32 num_sids, const DOM_SID *sids,
2065                                    uint32 *num_aliases, uint32 **alias_rids)
2066 {
2067         struct winbind_cache *cache = get_cache(domain);
2068         struct cache_entry *centry = NULL;
2069         NTSTATUS status;
2070         char *sidlist = talloc_strdup(mem_ctx, "");
2071         int i;
2072
2073         if (!cache->tdb)
2074                 goto do_query;
2075
2076         if (num_sids == 0) {
2077                 *num_aliases = 0;
2078                 *alias_rids = NULL;
2079                 return NT_STATUS_OK;
2080         }
2081
2082         /* We need to cache indexed by the whole list of SIDs, the aliases
2083          * resulting might come from any of the SIDs. */
2084
2085         for (i=0; i<num_sids; i++) {
2086                 fstring tmp;
2087                 sidlist = talloc_asprintf(mem_ctx, "%s/%s", sidlist,
2088                                           sid_to_fstring(tmp, &sids[i]));
2089                 if (sidlist == NULL)
2090                         return NT_STATUS_NO_MEMORY;
2091         }
2092
2093         centry = wcache_fetch(cache, domain, "UA%s", sidlist);
2094
2095         if (!centry)
2096                 goto do_query;
2097
2098         *num_aliases = centry_uint32(centry);
2099         *alias_rids = NULL;
2100
2101         if (*num_aliases) {
2102                 (*alias_rids) = TALLOC_ARRAY(mem_ctx, uint32, *num_aliases);
2103
2104                 if ((*alias_rids) == NULL) {
2105                         centry_free(centry);
2106                         return NT_STATUS_NO_MEMORY;
2107                 }
2108         } else {
2109                 (*alias_rids) = NULL;
2110         }
2111
2112         for (i=0; i<(*num_aliases); i++)
2113                 (*alias_rids)[i] = centry_uint32(centry);
2114
2115         status = centry->status;
2116
2117         DEBUG(10,("lookup_useraliases: [Cached] - cached info for domain: %s "
2118                   "status %s\n", domain->name, nt_errstr(status)));
2119
2120         centry_free(centry);
2121         return status;
2122
2123  do_query:
2124         (*num_aliases) = 0;
2125         (*alias_rids) = NULL;
2126
2127         if (!NT_STATUS_IS_OK(domain->last_status))
2128                 return domain->last_status;
2129
2130         DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info "
2131                   "for domain %s\n", domain->name ));
2132
2133         status = domain->backend->lookup_useraliases(domain, mem_ctx,
2134                                                      num_sids, sids,
2135                                                      num_aliases, alias_rids);
2136
2137         /* and save it */
2138         refresh_sequence_number(domain, false);
2139         centry = centry_start(domain, status);
2140         if (!centry)
2141                 goto skip_save;
2142         centry_put_uint32(centry, *num_aliases);
2143         for (i=0; i<(*num_aliases); i++)
2144                 centry_put_uint32(centry, (*alias_rids)[i]);
2145         centry_end(centry, "UA%s", sidlist);
2146         centry_free(centry);
2147
2148  skip_save:
2149         return status;
2150 }
2151
2152
2153 static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
2154                                 TALLOC_CTX *mem_ctx,
2155                                 const DOM_SID *group_sid, uint32 *num_names, 
2156                                 DOM_SID **sid_mem, char ***names, 
2157                                 uint32 **name_types)
2158 {
2159         struct winbind_cache *cache = get_cache(domain);
2160         struct cache_entry *centry = NULL;
2161         NTSTATUS status;
2162         unsigned int i;
2163         fstring sid_string;
2164
2165         if (!cache->tdb)
2166                 goto do_query;
2167
2168         centry = wcache_fetch(cache, domain, "GM/%s",
2169                               sid_to_fstring(sid_string, group_sid));
2170         if (!centry)
2171                 goto do_query;
2172
2173         *num_names = centry_uint32(centry);
2174
2175         if (*num_names == 0)
2176                 goto do_cached;
2177
2178         (*sid_mem) = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_names);
2179         (*names) = TALLOC_ARRAY(mem_ctx, char *, *num_names);
2180         (*name_types) = TALLOC_ARRAY(mem_ctx, uint32, *num_names);
2181
2182         if (! (*sid_mem) || ! (*names) || ! (*name_types)) {
2183                 smb_panic_fn("lookup_groupmem out of memory");
2184         }
2185
2186         for (i=0; i<(*num_names); i++) {
2187                 centry_sid(centry, &(*sid_mem)[i]);
2188                 (*names)[i] = centry_string(centry, mem_ctx);
2189                 (*name_types)[i] = centry_uint32(centry);
2190         }
2191
2192 do_cached:      
2193         status = centry->status;
2194
2195         DEBUG(10,("lookup_groupmem: [Cached] - cached info for domain %s status: %s\n",
2196                 domain->name, nt_errstr(status)));
2197
2198         centry_free(centry);
2199         return status;
2200
2201 do_query:
2202         (*num_names) = 0;
2203         (*sid_mem) = NULL;
2204         (*names) = NULL;
2205         (*name_types) = NULL;
2206
2207         /* Return status value returned by seq number check */
2208
2209         if (!NT_STATUS_IS_OK(domain->last_status))
2210                 return domain->last_status;
2211
2212         DEBUG(10,("lookup_groupmem: [Cached] - doing backend query for info for domain %s\n",
2213                 domain->name ));
2214
2215         status = domain->backend->lookup_groupmem(domain, mem_ctx, group_sid, num_names, 
2216                                                   sid_mem, names, name_types);
2217
2218         /* and save it */
2219         refresh_sequence_number(domain, false);
2220         centry = centry_start(domain, status);
2221         if (!centry)
2222                 goto skip_save;
2223         centry_put_uint32(centry, *num_names);
2224         for (i=0; i<(*num_names); i++) {
2225                 centry_put_sid(centry, &(*sid_mem)[i]);
2226                 centry_put_string(centry, (*names)[i]);
2227                 centry_put_uint32(centry, (*name_types)[i]);
2228         }       
2229         centry_end(centry, "GM/%s", sid_to_fstring(sid_string, group_sid));
2230         centry_free(centry);
2231
2232 skip_save:
2233         return status;
2234 }
2235
2236 /* find the sequence number for a domain */
2237 static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
2238 {
2239         refresh_sequence_number(domain, false);
2240
2241         *seq = domain->sequence_number;
2242
2243         return NT_STATUS_OK;
2244 }
2245
2246 /* enumerate trusted domains 
2247  * (we need to have the list of trustdoms in the cache when we go offline) -
2248  * Guenther */
2249 static NTSTATUS trusted_domains(struct winbindd_domain *domain,
2250                                 TALLOC_CTX *mem_ctx,
2251                                 uint32 *num_domains,
2252                                 char ***names,
2253                                 char ***alt_names,
2254                                 DOM_SID **dom_sids)
2255 {
2256         struct winbind_cache *cache = get_cache(domain);
2257         struct cache_entry *centry = NULL;
2258         NTSTATUS status;
2259         int i;
2260
2261         if (!cache->tdb)
2262                 goto do_query;
2263
2264         centry = wcache_fetch(cache, domain, "TRUSTDOMS/%s", domain->name);
2265
2266         if (!centry) {
2267                 goto do_query;
2268         }
2269
2270         *num_domains = centry_uint32(centry);
2271
2272         if (*num_domains) {
2273                 (*names)        = TALLOC_ARRAY(mem_ctx, char *, *num_domains);
2274                 (*alt_names)    = TALLOC_ARRAY(mem_ctx, char *, *num_domains);
2275                 (*dom_sids)     = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_domains);
2276
2277                 if (! (*dom_sids) || ! (*names) || ! (*alt_names)) {
2278                         smb_panic_fn("trusted_domains out of memory");
2279                 }
2280         } else {
2281                 (*names) = NULL;
2282                 (*alt_names) = NULL;
2283                 (*dom_sids) = NULL;
2284         }
2285
2286         for (i=0; i<(*num_domains); i++) {
2287                 (*names)[i] = centry_string(centry, mem_ctx);
2288                 (*alt_names)[i] = centry_string(centry, mem_ctx);
2289                 if (!centry_sid(centry, &(*dom_sids)[i])) {
2290                         sid_copy(&(*dom_sids)[i], &global_sid_NULL);
2291                 }
2292         }
2293
2294         status = centry->status;
2295
2296         DEBUG(10,("trusted_domains: [Cached] - cached info for domain %s (%d trusts) status: %s\n",
2297                 domain->name, *num_domains, nt_errstr(status) ));
2298
2299         centry_free(centry);
2300         return status;
2301
2302 do_query:
2303         (*num_domains) = 0;
2304         (*dom_sids) = NULL;
2305         (*names) = NULL;
2306         (*alt_names) = NULL;
2307
2308         /* Return status value returned by seq number check */
2309
2310         if (!NT_STATUS_IS_OK(domain->last_status))
2311                 return domain->last_status;
2312
2313         DEBUG(10,("trusted_domains: [Cached] - doing backend query for info for domain %s\n",
2314                 domain->name ));
2315
2316         status = domain->backend->trusted_domains(domain, mem_ctx, num_domains,
2317                                                 names, alt_names, dom_sids);
2318
2319         /* no trusts gives NT_STATUS_NO_MORE_ENTRIES resetting to NT_STATUS_OK
2320          * so that the generic centry handling still applies correctly -
2321          * Guenther*/
2322
2323         if (!NT_STATUS_IS_ERR(status)) {
2324                 status = NT_STATUS_OK;
2325         }
2326
2327
2328 #if 0    /* Disabled as we want the trust dom list to be managed by
2329             the main parent and always to make the query.  --jerry */
2330
2331         /* and save it */
2332         refresh_sequence_number(domain, false);
2333
2334         centry = centry_start(domain, status);
2335         if (!centry)
2336                 goto skip_save;
2337
2338         centry_put_uint32(centry, *num_domains);
2339
2340         for (i=0; i<(*num_domains); i++) {
2341                 centry_put_string(centry, (*names)[i]);
2342                 centry_put_string(centry, (*alt_names)[i]);
2343                 centry_put_sid(centry, &(*dom_sids)[i]);
2344         }
2345
2346         centry_end(centry, "TRUSTDOMS/%s", domain->name);
2347
2348         centry_free(centry);
2349
2350 skip_save:
2351 #endif
2352
2353         return status;
2354 }       
2355
2356 /* get lockout policy */
2357 static NTSTATUS lockout_policy(struct winbindd_domain *domain,
2358                                TALLOC_CTX *mem_ctx,
2359                                struct samr_DomInfo12 *policy)
2360 {
2361         struct winbind_cache *cache = get_cache(domain);
2362         struct cache_entry *centry = NULL;
2363         NTSTATUS status;
2364
2365         if (!cache->tdb)
2366                 goto do_query;
2367
2368         centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
2369
2370         if (!centry)
2371                 goto do_query;
2372
2373         policy->lockout_duration = centry_nttime(centry);
2374         policy->lockout_window = centry_nttime(centry);
2375         policy->lockout_threshold = centry_uint16(centry);
2376
2377         status = centry->status;
2378
2379         DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2380                 domain->name, nt_errstr(status) ));
2381
2382         centry_free(centry);
2383         return status;
2384
2385 do_query:
2386         ZERO_STRUCTP(policy);
2387
2388         /* Return status value returned by seq number check */
2389
2390         if (!NT_STATUS_IS_OK(domain->last_status))
2391                 return domain->last_status;
2392
2393         DEBUG(10,("lockout_policy: [Cached] - doing backend query for info for domain %s\n",
2394                 domain->name ));
2395
2396         status = domain->backend->lockout_policy(domain, mem_ctx, policy);
2397
2398         /* and save it */
2399         refresh_sequence_number(domain, false);
2400         wcache_save_lockout_policy(domain, status, policy);
2401
2402         return status;
2403 }
2404
2405 /* get password policy */
2406 static NTSTATUS password_policy(struct winbindd_domain *domain,
2407                                 TALLOC_CTX *mem_ctx,
2408                                 struct samr_DomInfo1 *policy)
2409 {
2410         struct winbind_cache *cache = get_cache(domain);
2411         struct cache_entry *centry = NULL;
2412         NTSTATUS status;
2413
2414         if (!cache->tdb)
2415                 goto do_query;
2416
2417         centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
2418
2419         if (!centry)
2420                 goto do_query;
2421
2422         policy->min_password_length = centry_uint16(centry);
2423         policy->password_history_length = centry_uint16(centry);
2424         policy->password_properties = centry_uint32(centry);
2425         policy->max_password_age = centry_nttime(centry);
2426         policy->min_password_age = centry_nttime(centry);
2427
2428         status = centry->status;
2429
2430         DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2431                 domain->name, nt_errstr(status) ));
2432
2433         centry_free(centry);
2434         return status;
2435
2436 do_query:
2437         ZERO_STRUCTP(policy);
2438
2439         /* Return status value returned by seq number check */
2440
2441         if (!NT_STATUS_IS_OK(domain->last_status))
2442                 return domain->last_status;
2443
2444         DEBUG(10,("password_policy: [Cached] - doing backend query for info for domain %s\n",
2445                 domain->name ));
2446
2447         status = domain->backend->password_policy(domain, mem_ctx, policy);
2448
2449         /* and save it */
2450         refresh_sequence_number(domain, false);
2451         if (NT_STATUS_IS_OK(status)) {
2452                 wcache_save_password_policy(domain, status, policy);
2453         }
2454
2455         return status;
2456 }
2457
2458
2459 /* Invalidate cached user and group lists coherently */
2460
2461 static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, 
2462                        void *state)
2463 {
2464         if (strncmp((const char *)kbuf.dptr, "UL/", 3) == 0 ||
2465             strncmp((const char *)kbuf.dptr, "GL/", 3) == 0)
2466                 tdb_delete(the_tdb, kbuf);
2467
2468         return 0;
2469 }
2470
2471 /* Invalidate the getpwnam and getgroups entries for a winbindd domain */
2472
2473 void wcache_invalidate_samlogon(struct winbindd_domain *domain, 
2474                                 struct netr_SamInfo3 *info3)
2475 {
2476         DOM_SID sid;
2477         fstring key_str, sid_string;
2478         struct winbind_cache *cache;
2479
2480         /* dont clear cached U/SID and UG/SID entries when we want to logon
2481          * offline - gd */
2482
2483         if (lp_winbind_offline_logon()) {
2484                 return;
2485         }
2486
2487         if (!domain)
2488                 return;
2489
2490         cache = get_cache(domain);
2491
2492         if (!cache->tdb) {
2493                 return;
2494         }
2495
2496         sid_copy(&sid, info3->base.domain_sid);
2497         sid_append_rid(&sid, info3->base.rid);
2498
2499         /* Clear U/SID cache entry */
2500         fstr_sprintf(key_str, "U/%s", sid_to_fstring(sid_string, &sid));
2501         DEBUG(10, ("wcache_invalidate_samlogon: clearing %s\n", key_str));
2502         tdb_delete(cache->tdb, string_tdb_data(key_str));
2503
2504         /* Clear UG/SID cache entry */
2505         fstr_sprintf(key_str, "UG/%s", sid_to_fstring(sid_string, &sid));
2506         DEBUG(10, ("wcache_invalidate_samlogon: clearing %s\n", key_str));
2507         tdb_delete(cache->tdb, string_tdb_data(key_str));
2508
2509         /* Samba/winbindd never needs this. */
2510         netsamlogon_clear_cached_user(info3);
2511 }
2512
2513 bool wcache_invalidate_cache(void)
2514 {
2515         struct winbindd_domain *domain;
2516
2517         for (domain = domain_list(); domain; domain = domain->next) {
2518                 struct winbind_cache *cache = get_cache(domain);
2519
2520                 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
2521                            "entries for %s\n", domain->name));
2522                 if (cache) {
2523                         if (cache->tdb) {
2524                                 tdb_traverse(cache->tdb, traverse_fn, NULL);
2525                         } else {
2526                                 return false;
2527                         }
2528                 }
2529         }
2530         return true;
2531 }
2532
2533 bool init_wcache(void)
2534 {
2535         if (wcache == NULL) {
2536                 wcache = SMB_XMALLOC_P(struct winbind_cache);
2537                 ZERO_STRUCTP(wcache);
2538         }
2539
2540         if (wcache->tdb != NULL)
2541                 return true;
2542
2543         /* when working offline we must not clear the cache on restart */
2544         wcache->tdb = tdb_open_log(cache_path("winbindd_cache.tdb"),
2545                                 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE, 
2546                                 lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST), 
2547                                 O_RDWR|O_CREAT, 0600);
2548
2549         if (wcache->tdb == NULL) {
2550                 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
2551                 return false;
2552         }
2553
2554         return true;
2555 }
2556
2557 /************************************************************************
2558  This is called by the parent to initialize the cache file.
2559  We don't need sophisticated locking here as we know we're the
2560  only opener.
2561 ************************************************************************/
2562
2563 bool initialize_winbindd_cache(void)
2564 {
2565         bool cache_bad = true;
2566         uint32 vers;
2567
2568         if (!init_wcache()) {
2569                 DEBUG(0,("initialize_winbindd_cache: init_wcache failed.\n"));
2570                 return false;
2571         }
2572
2573         /* Check version number. */
2574         if (tdb_fetch_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, &vers) &&
2575                         vers == WINBINDD_CACHE_VERSION) {
2576                 cache_bad = false;
2577         }
2578
2579         if (cache_bad) {
2580                 DEBUG(0,("initialize_winbindd_cache: clearing cache "
2581                         "and re-creating with version number %d\n",
2582                         WINBINDD_CACHE_VERSION ));
2583
2584                 tdb_close(wcache->tdb);
2585                 wcache->tdb = NULL;
2586
2587                 if (unlink(cache_path("winbindd_cache.tdb")) == -1) {
2588                         DEBUG(0,("initialize_winbindd_cache: unlink %s failed %s ",
2589                                 cache_path("winbindd_cache.tdb"),
2590                                 strerror(errno) ));
2591                         return false;
2592                 }
2593                 if (!init_wcache()) {
2594                         DEBUG(0,("initialize_winbindd_cache: re-initialization "
2595                                         "init_wcache failed.\n"));
2596                         return false;
2597                 }
2598
2599                 /* Write the version. */
2600                 if (!tdb_store_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, WINBINDD_CACHE_VERSION)) {
2601                         DEBUG(0,("initialize_winbindd_cache: version number store failed %s\n",
2602                                 tdb_errorstr(wcache->tdb) ));
2603                         return false;
2604                 }
2605         }
2606
2607         tdb_close(wcache->tdb);
2608         wcache->tdb = NULL;
2609         return true;
2610 }
2611
2612 void close_winbindd_cache(void)
2613 {
2614         if (!wcache) {
2615                 return;
2616         }
2617         if (wcache->tdb) {
2618                 tdb_close(wcache->tdb);
2619                 wcache->tdb = NULL;
2620         }
2621 }
2622
2623 bool lookup_cached_sid(TALLOC_CTX *mem_ctx, const DOM_SID *sid,
2624                        char **domain_name, char **name,
2625                        enum lsa_SidType *type)
2626 {
2627         struct winbindd_domain *domain;
2628         struct winbind_cache *cache;
2629         struct cache_entry *centry = NULL;
2630         NTSTATUS status;
2631         fstring tmp;
2632
2633         domain = find_lookup_domain_from_sid(sid);
2634         if (domain == NULL) {
2635                 return false;
2636         }
2637
2638         cache = get_cache(domain);
2639
2640         if (cache->tdb == NULL) {
2641                 return false;
2642         }
2643
2644         centry = wcache_fetch(cache, domain, "SN/%s",
2645                               sid_to_fstring(tmp, sid));
2646         if (centry == NULL) {
2647                 return false;
2648         }
2649
2650         if (NT_STATUS_IS_OK(centry->status)) {
2651                 *type = (enum lsa_SidType)centry_uint32(centry);
2652                 *domain_name = centry_string(centry, mem_ctx);
2653                 *name = centry_string(centry, mem_ctx);
2654         }
2655
2656         status = centry->status;
2657         centry_free(centry);
2658         return NT_STATUS_IS_OK(status);
2659 }
2660
2661 bool lookup_cached_name(TALLOC_CTX *mem_ctx,
2662                         const char *domain_name,
2663                         const char *name,
2664                         DOM_SID *sid,
2665                         enum lsa_SidType *type)
2666 {
2667         struct winbindd_domain *domain;
2668         struct winbind_cache *cache;
2669         struct cache_entry *centry = NULL;
2670         NTSTATUS status;
2671         fstring uname;
2672         bool original_online_state;     
2673
2674         domain = find_lookup_domain_from_name(domain_name);
2675         if (domain == NULL) {
2676                 return false;
2677         }
2678
2679         cache = get_cache(domain);
2680
2681         if (cache->tdb == NULL) {
2682                 return false;
2683         }
2684
2685         fstrcpy(uname, name);
2686         strupper_m(uname);
2687
2688         /* If we are doing a cached logon, temporarily set the domain
2689            offline so the cache won't expire the entry */
2690
2691         original_online_state = domain->online;
2692         domain->online = false;
2693         centry = wcache_fetch(cache, domain, "NS/%s/%s", domain_name, uname);
2694         domain->online = original_online_state;
2695
2696         if (centry == NULL) {
2697                 return false;
2698         }
2699
2700         if (NT_STATUS_IS_OK(centry->status)) {
2701                 *type = (enum lsa_SidType)centry_uint32(centry);
2702                 centry_sid(centry, sid);
2703         }
2704
2705         status = centry->status;
2706         centry_free(centry);
2707
2708         return NT_STATUS_IS_OK(status);
2709 }
2710
2711 void cache_name2sid(struct winbindd_domain *domain, 
2712                     const char *domain_name, const char *name,
2713                     enum lsa_SidType type, const DOM_SID *sid)
2714 {
2715         refresh_sequence_number(domain, false);
2716         wcache_save_name_to_sid(domain, NT_STATUS_OK, domain_name, name,
2717                                 sid, type);
2718 }
2719
2720 /*
2721  * The original idea that this cache only contains centries has
2722  * been blurred - now other stuff gets put in here. Ensure we
2723  * ignore these things on cleanup.
2724  */
2725
2726 static int traverse_fn_cleanup(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, 
2727                                TDB_DATA dbuf, void *state)
2728 {
2729         struct cache_entry *centry;
2730
2731         if (is_non_centry_key(kbuf)) {
2732                 return 0;
2733         }
2734
2735         centry = wcache_fetch_raw((char *)kbuf.dptr);
2736         if (!centry) {
2737                 return 0;
2738         }
2739
2740         if (!NT_STATUS_IS_OK(centry->status)) {
2741                 DEBUG(10,("deleting centry %s\n", (const char *)kbuf.dptr));
2742                 tdb_delete(the_tdb, kbuf);
2743         }
2744
2745         centry_free(centry);
2746         return 0;
2747 }
2748
2749 /* flush the cache */
2750 void wcache_flush_cache(void)
2751 {
2752         if (!wcache)
2753                 return;
2754         if (wcache->tdb) {
2755                 tdb_close(wcache->tdb);
2756                 wcache->tdb = NULL;
2757         }
2758         if (!winbindd_use_cache()) {
2759                 return;
2760         }
2761
2762         /* when working offline we must not clear the cache on restart */
2763         wcache->tdb = tdb_open_log(cache_path("winbindd_cache.tdb"),
2764                                 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE, 
2765                                 lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST), 
2766                                 O_RDWR|O_CREAT, 0600);
2767
2768         if (!wcache->tdb) {
2769                 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
2770                 return;
2771         }
2772
2773         tdb_traverse(wcache->tdb, traverse_fn_cleanup, NULL);
2774
2775         DEBUG(10,("wcache_flush_cache success\n"));
2776 }
2777
2778 /* Count cached creds */
2779
2780 static int traverse_fn_cached_creds(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, 
2781                                     void *state)
2782 {
2783         int *cred_count = (int*)state;
2784  
2785         if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
2786                 (*cred_count)++;
2787         }
2788         return 0;
2789 }
2790
2791 NTSTATUS wcache_count_cached_creds(struct winbindd_domain *domain, int *count)
2792 {
2793         struct winbind_cache *cache = get_cache(domain);
2794
2795         *count = 0;
2796
2797         if (!cache->tdb) {
2798                 return NT_STATUS_INTERNAL_DB_ERROR;
2799         }
2800  
2801         tdb_traverse(cache->tdb, traverse_fn_cached_creds, (void *)count);
2802
2803         return NT_STATUS_OK;
2804 }
2805
2806 struct cred_list {
2807         struct cred_list *prev, *next;
2808         TDB_DATA key;
2809         fstring name;
2810         time_t created;
2811 };
2812 static struct cred_list *wcache_cred_list;
2813
2814 static int traverse_fn_get_credlist(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, 
2815                                     void *state)
2816 {
2817         struct cred_list *cred;
2818
2819         if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
2820
2821                 cred = SMB_MALLOC_P(struct cred_list);
2822                 if (cred == NULL) {
2823                         DEBUG(0,("traverse_fn_remove_first_creds: failed to malloc new entry for list\n"));
2824                         return -1;
2825                 }
2826
2827                 ZERO_STRUCTP(cred);
2828
2829                 /* save a copy of the key */
2830
2831                 fstrcpy(cred->name, (const char *)kbuf.dptr);           
2832                 DLIST_ADD(wcache_cred_list, cred);
2833         }
2834
2835         return 0;
2836 }
2837
2838 NTSTATUS wcache_remove_oldest_cached_creds(struct winbindd_domain *domain, const DOM_SID *sid) 
2839 {
2840         struct winbind_cache *cache = get_cache(domain);
2841         NTSTATUS status;
2842         int ret;
2843         struct cred_list *cred, *oldest = NULL;
2844
2845         if (!cache->tdb) {
2846                 return NT_STATUS_INTERNAL_DB_ERROR;
2847         }
2848
2849         /* we possibly already have an entry */
2850         if (sid && NT_STATUS_IS_OK(wcache_cached_creds_exist(domain, sid))) {
2851
2852                 fstring key_str, tmp;
2853
2854                 DEBUG(11,("we already have an entry, deleting that\n"));
2855
2856                 fstr_sprintf(key_str, "CRED/%s", sid_to_fstring(tmp, sid));
2857
2858                 tdb_delete(cache->tdb, string_tdb_data(key_str));
2859
2860                 return NT_STATUS_OK;
2861         }
2862
2863         ret = tdb_traverse(cache->tdb, traverse_fn_get_credlist, NULL);
2864         if (ret == 0) {
2865                 return NT_STATUS_OK;
2866         } else if ((ret == -1) || (wcache_cred_list == NULL)) {
2867                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
2868         }
2869
2870         ZERO_STRUCTP(oldest);
2871
2872         for (cred = wcache_cred_list; cred; cred = cred->next) {
2873
2874                 TDB_DATA data;
2875                 time_t t;
2876
2877                 data = tdb_fetch(cache->tdb, string_tdb_data(cred->name));
2878                 if (!data.dptr) {
2879                         DEBUG(10,("wcache_remove_oldest_cached_creds: entry for [%s] not found\n", 
2880                                 cred->name));
2881                         status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
2882                         goto done;
2883                 }
2884
2885                 t = IVAL(data.dptr, 0);
2886                 SAFE_FREE(data.dptr);
2887
2888                 if (!oldest) {
2889                         oldest = SMB_MALLOC_P(struct cred_list);
2890                         if (oldest == NULL) {
2891                                 status = NT_STATUS_NO_MEMORY;
2892                                 goto done;
2893                         }
2894
2895                         fstrcpy(oldest->name, cred->name);
2896                         oldest->created = t;
2897                         continue;
2898                 }
2899
2900                 if (t < oldest->created) {
2901                         fstrcpy(oldest->name, cred->name);
2902                         oldest->created = t;
2903                 }
2904         }
2905
2906         if (tdb_delete(cache->tdb, string_tdb_data(oldest->name)) == 0) {
2907                 status = NT_STATUS_OK;
2908         } else {
2909                 status = NT_STATUS_UNSUCCESSFUL;
2910         }
2911 done:
2912         SAFE_FREE(wcache_cred_list);
2913         SAFE_FREE(oldest);
2914
2915         return status;
2916 }
2917
2918 /* Change the global online/offline state. */
2919 bool set_global_winbindd_state_offline(void)
2920 {
2921         TDB_DATA data;
2922
2923         DEBUG(10,("set_global_winbindd_state_offline: offline requested.\n"));
2924
2925         /* Only go offline if someone has created
2926            the key "WINBINDD_OFFLINE" in the cache tdb. */
2927
2928         if (wcache == NULL || wcache->tdb == NULL) {
2929                 DEBUG(10,("set_global_winbindd_state_offline: wcache not open yet.\n"));
2930                 return false;
2931         }
2932
2933         if (!lp_winbind_offline_logon()) {
2934                 DEBUG(10,("set_global_winbindd_state_offline: rejecting.\n"));
2935                 return false;
2936         }
2937
2938         if (global_winbindd_offline_state) {
2939                 /* Already offline. */
2940                 return true;
2941         }
2942
2943         data = tdb_fetch_bystring( wcache->tdb, "WINBINDD_OFFLINE" );
2944
2945         if (!data.dptr || data.dsize != 4) {
2946                 DEBUG(10,("set_global_winbindd_state_offline: offline state not set.\n"));
2947                 SAFE_FREE(data.dptr);
2948                 return false;
2949         } else {
2950                 DEBUG(10,("set_global_winbindd_state_offline: offline state set.\n"));
2951                 global_winbindd_offline_state = true;
2952                 SAFE_FREE(data.dptr);
2953                 return true;
2954         }
2955 }
2956
2957 void set_global_winbindd_state_online(void)
2958 {
2959         DEBUG(10,("set_global_winbindd_state_online: online requested.\n"));
2960
2961         if (!lp_winbind_offline_logon()) {
2962                 DEBUG(10,("set_global_winbindd_state_online: rejecting.\n"));
2963                 return;
2964         }
2965
2966         if (!global_winbindd_offline_state) {
2967                 /* Already online. */
2968                 return;
2969         }
2970         global_winbindd_offline_state = false;
2971
2972         if (!wcache->tdb) {
2973                 return;
2974         }
2975
2976         /* Ensure there is no key "WINBINDD_OFFLINE" in the cache tdb. */
2977         tdb_delete_bystring(wcache->tdb, "WINBINDD_OFFLINE");
2978 }
2979
2980 bool get_global_winbindd_state_offline(void)
2981 {
2982         return global_winbindd_offline_state;
2983 }
2984
2985 /***********************************************************************
2986  Validate functions for all possible cache tdb keys.
2987 ***********************************************************************/
2988
2989 static struct cache_entry *create_centry_validate(const char *kstr, TDB_DATA data, 
2990                                                   struct tdb_validation_status *state)
2991 {
2992         struct cache_entry *centry;
2993
2994         centry = SMB_XMALLOC_P(struct cache_entry);
2995         centry->data = (unsigned char *)memdup(data.dptr, data.dsize);
2996         if (!centry->data) {
2997                 SAFE_FREE(centry);
2998                 return NULL;
2999         }
3000         centry->len = data.dsize;
3001         centry->ofs = 0;
3002
3003         if (centry->len < 8) {
3004                 /* huh? corrupt cache? */
3005                 DEBUG(0,("create_centry_validate: Corrupt cache for key %s (len < 8) ?\n", kstr));
3006                 centry_free(centry);
3007                 state->bad_entry = true;
3008                 state->success = false;
3009                 return NULL;
3010         }
3011
3012         centry->status = NT_STATUS(centry_uint32(centry));
3013         centry->sequence_number = centry_uint32(centry);
3014         return centry;
3015 }
3016
3017 static int validate_seqnum(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3018                            struct tdb_validation_status *state)
3019 {
3020         if (dbuf.dsize != 8) {
3021                 DEBUG(0,("validate_seqnum: Corrupt cache for key %s (len %u != 8) ?\n",
3022                                 keystr, (unsigned int)dbuf.dsize ));
3023                 state->bad_entry = true;
3024                 return 1;
3025         }
3026         return 0;
3027 }
3028
3029 static int validate_ns(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3030                        struct tdb_validation_status *state)
3031 {
3032         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3033         if (!centry) {
3034                 return 1;
3035         }
3036
3037         (void)centry_uint32(centry);
3038         if (NT_STATUS_IS_OK(centry->status)) {
3039                 DOM_SID sid;
3040                 (void)centry_sid(centry, &sid);
3041         }
3042
3043         centry_free(centry);
3044
3045         if (!(state->success)) {
3046                 return 1;
3047         }
3048         DEBUG(10,("validate_ns: %s ok\n", keystr));
3049         return 0;
3050 }
3051
3052 static int validate_sn(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3053                        struct tdb_validation_status *state)
3054 {
3055         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3056         if (!centry) {
3057                 return 1;
3058         }
3059
3060         if (NT_STATUS_IS_OK(centry->status)) {
3061                 (void)centry_uint32(centry);
3062                 (void)centry_string(centry, mem_ctx);
3063                 (void)centry_string(centry, mem_ctx);
3064         }
3065
3066         centry_free(centry);
3067
3068         if (!(state->success)) {
3069                 return 1;
3070         }
3071         DEBUG(10,("validate_sn: %s ok\n", keystr));
3072         return 0;
3073 }
3074
3075 static int validate_u(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3076                       struct tdb_validation_status *state)
3077 {
3078         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3079         DOM_SID sid;
3080
3081         if (!centry) {
3082                 return 1;
3083         }
3084
3085         (void)centry_string(centry, mem_ctx);
3086         (void)centry_string(centry, mem_ctx);
3087         (void)centry_string(centry, mem_ctx);
3088         (void)centry_string(centry, mem_ctx);
3089         (void)centry_uint32(centry);
3090         (void)centry_sid(centry, &sid);
3091         (void)centry_sid(centry, &sid);
3092
3093         centry_free(centry);
3094
3095         if (!(state->success)) {
3096                 return 1;
3097         }
3098         DEBUG(10,("validate_u: %s ok\n", keystr));
3099         return 0;
3100 }
3101
3102 static int validate_loc_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3103                             struct tdb_validation_status *state)
3104 {
3105         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3106
3107         if (!centry) {
3108                 return 1;
3109         }
3110
3111         (void)centry_nttime(centry);
3112         (void)centry_nttime(centry);
3113         (void)centry_uint16(centry);
3114
3115         centry_free(centry);
3116
3117         if (!(state->success)) {
3118                 return 1;
3119         }
3120         DEBUG(10,("validate_loc_pol: %s ok\n", keystr));
3121         return 0;
3122 }
3123
3124 static int validate_pwd_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3125                             struct tdb_validation_status *state)
3126 {
3127         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3128
3129         if (!centry) {
3130                 return 1;
3131         }
3132
3133         (void)centry_uint16(centry);
3134         (void)centry_uint16(centry);
3135         (void)centry_uint32(centry);
3136         (void)centry_nttime(centry);
3137         (void)centry_nttime(centry);
3138
3139         centry_free(centry);
3140
3141         if (!(state->success)) {
3142                 return 1;
3143         }
3144         DEBUG(10,("validate_pwd_pol: %s ok\n", keystr));
3145         return 0;
3146 }
3147
3148 static int validate_cred(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3149                          struct tdb_validation_status *state)
3150 {
3151         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3152
3153         if (!centry) {
3154                 return 1;
3155         }
3156
3157         (void)centry_time(centry);
3158         (void)centry_hash16(centry, mem_ctx);
3159
3160         /* We only have 17 bytes more data in the salted cred case. */
3161         if (centry->len - centry->ofs == 17) {
3162                 (void)centry_hash16(centry, mem_ctx);
3163         }
3164
3165         centry_free(centry);
3166
3167         if (!(state->success)) {
3168                 return 1;
3169         }
3170         DEBUG(10,("validate_cred: %s ok\n", keystr));
3171         return 0;
3172 }
3173
3174 static int validate_ul(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3175                        struct tdb_validation_status *state)
3176 {
3177         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3178         int32 num_entries, i;
3179
3180         if (!centry) {
3181                 return 1;
3182         }
3183
3184         num_entries = (int32)centry_uint32(centry);
3185
3186         for (i=0; i< num_entries; i++) {
3187                 DOM_SID sid;
3188                 (void)centry_string(centry, mem_ctx);
3189                 (void)centry_string(centry, mem_ctx);
3190                 (void)centry_string(centry, mem_ctx);
3191                 (void)centry_string(centry, mem_ctx);
3192                 (void)centry_sid(centry, &sid);
3193                 (void)centry_sid(centry, &sid);
3194         }
3195
3196         centry_free(centry);
3197
3198         if (!(state->success)) {
3199                 return 1;
3200         }
3201         DEBUG(10,("validate_ul: %s ok\n", keystr));
3202         return 0;
3203 }
3204
3205 static int validate_gl(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3206                        struct tdb_validation_status *state)
3207 {
3208         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3209         int32 num_entries, i;
3210
3211         if (!centry) {
3212                 return 1;
3213         }
3214
3215         num_entries = centry_uint32(centry);
3216
3217         for (i=0; i< num_entries; i++) {
3218                 (void)centry_string(centry, mem_ctx);
3219                 (void)centry_string(centry, mem_ctx);
3220                 (void)centry_uint32(centry);
3221         }
3222
3223         centry_free(centry);
3224
3225         if (!(state->success)) {
3226                 return 1;
3227         }
3228         DEBUG(10,("validate_gl: %s ok\n", keystr));
3229         return 0;
3230 }
3231
3232 static int validate_ug(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3233                        struct tdb_validation_status *state)
3234 {
3235         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3236         int32 num_groups, i;
3237
3238         if (!centry) {
3239                 return 1;
3240         }
3241
3242         num_groups = centry_uint32(centry);
3243
3244         for (i=0; i< num_groups; i++) {
3245                 DOM_SID sid;
3246                 centry_sid(centry, &sid);
3247         }
3248
3249         centry_free(centry);
3250
3251         if (!(state->success)) {
3252                 return 1;
3253         }
3254         DEBUG(10,("validate_ug: %s ok\n", keystr));
3255         return 0;
3256 }
3257
3258 static int validate_ua(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3259                        struct tdb_validation_status *state)
3260 {
3261         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3262         int32 num_aliases, i;
3263
3264         if (!centry) {
3265                 return 1;
3266         }
3267
3268         num_aliases = centry_uint32(centry);
3269
3270         for (i=0; i < num_aliases; i++) {
3271                 (void)centry_uint32(centry);
3272         }
3273
3274         centry_free(centry);
3275
3276         if (!(state->success)) {
3277                 return 1;
3278         }
3279         DEBUG(10,("validate_ua: %s ok\n", keystr));
3280         return 0;
3281 }
3282
3283 static int validate_gm(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3284                        struct tdb_validation_status *state)
3285 {
3286         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3287         int32 num_names, i;
3288
3289         if (!centry) {
3290                 return 1;
3291         }
3292
3293         num_names = centry_uint32(centry);
3294
3295         for (i=0; i< num_names; i++) {
3296                 DOM_SID sid;
3297                 centry_sid(centry, &sid);
3298                 (void)centry_string(centry, mem_ctx);
3299                 (void)centry_uint32(centry);
3300         }
3301
3302         centry_free(centry);
3303
3304         if (!(state->success)) {
3305                 return 1;
3306         }
3307         DEBUG(10,("validate_gm: %s ok\n", keystr));
3308         return 0;
3309 }
3310
3311 static int validate_dr(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3312                        struct tdb_validation_status *state)
3313 {
3314         /* Can't say anything about this other than must be nonzero. */
3315         if (dbuf.dsize == 0) {
3316                 DEBUG(0,("validate_dr: Corrupt cache for key %s (len == 0) ?\n",
3317                                 keystr));
3318                 state->bad_entry = true;
3319                 state->success = false;
3320                 return 1;
3321         }
3322
3323         DEBUG(10,("validate_dr: %s ok\n", keystr));
3324         return 0;
3325 }
3326
3327 static int validate_de(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3328                        struct tdb_validation_status *state)
3329 {
3330         /* Can't say anything about this other than must be nonzero. */
3331         if (dbuf.dsize == 0) {
3332                 DEBUG(0,("validate_de: Corrupt cache for key %s (len == 0) ?\n",
3333                                 keystr));
3334                 state->bad_entry = true;
3335                 state->success = false;
3336                 return 1;
3337         }
3338
3339         DEBUG(10,("validate_de: %s ok\n", keystr));
3340         return 0;
3341 }
3342
3343 static int validate_pwinfo(TALLOC_CTX *mem_ctx, const char *keystr,
3344                            TDB_DATA dbuf, struct tdb_validation_status *state)
3345 {
3346         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3347
3348         if (!centry) {
3349                 return 1;
3350         }
3351
3352         (void)centry_string(centry, mem_ctx);
3353         (void)centry_string(centry, mem_ctx);
3354         (void)centry_string(centry, mem_ctx);
3355         (void)centry_uint32(centry);
3356
3357         centry_free(centry);
3358
3359         if (!(state->success)) {
3360                 return 1;
3361         }
3362         DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3363         return 0;
3364 }
3365
3366 static int validate_nss_an(TALLOC_CTX *mem_ctx, const char *keystr,
3367                            TDB_DATA dbuf,
3368                            struct tdb_validation_status *state)
3369 {
3370         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3371
3372         if (!centry) {
3373                 return 1;
3374         }
3375
3376         (void)centry_string( centry, mem_ctx );
3377
3378         centry_free(centry);
3379
3380         if (!(state->success)) {
3381                 return 1;
3382         }
3383         DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3384         return 0;
3385 }
3386
3387 static int validate_nss_na(TALLOC_CTX *mem_ctx, const char *keystr,
3388                            TDB_DATA dbuf,
3389                            struct tdb_validation_status *state)
3390 {
3391         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3392
3393         if (!centry) {
3394                 return 1;
3395         }
3396
3397         (void)centry_string( centry, mem_ctx );
3398
3399         centry_free(centry);
3400
3401         if (!(state->success)) {
3402                 return 1;
3403         }
3404         DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3405         return 0;
3406 }
3407
3408 static int validate_trustdoms(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3409                               struct tdb_validation_status *state)
3410 {
3411         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3412         int32 num_domains, i;
3413
3414         if (!centry) {
3415                 return 1;
3416         }
3417
3418         num_domains = centry_uint32(centry);
3419
3420         for (i=0; i< num_domains; i++) {
3421                 DOM_SID sid;
3422                 (void)centry_string(centry, mem_ctx);
3423                 (void)centry_string(centry, mem_ctx);
3424                 (void)centry_sid(centry, &sid);
3425         }
3426
3427         centry_free(centry);
3428
3429         if (!(state->success)) {
3430                 return 1;
3431         }
3432         DEBUG(10,("validate_trustdoms: %s ok\n", keystr));
3433         return 0;
3434 }
3435
3436 static int validate_trustdomcache(TALLOC_CTX *mem_ctx, const char *keystr, 
3437                                   TDB_DATA dbuf,
3438                                   struct tdb_validation_status *state)
3439 {
3440         if (dbuf.dsize == 0) {
3441                 DEBUG(0, ("validate_trustdomcache: Corrupt cache for "
3442                           "key %s (len ==0) ?\n", keystr));
3443                 state->bad_entry = true;
3444                 state->success = false;
3445                 return 1;
3446         }
3447
3448         DEBUG(10,    ("validate_trustdomcache: %s ok\n", keystr));
3449         DEBUGADD(10, ("  Don't trust me, I am a DUMMY!\n"));
3450         return 0;
3451 }
3452
3453 static int validate_offline(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3454                             struct tdb_validation_status *state)
3455 {
3456         if (dbuf.dsize != 4) {
3457                 DEBUG(0,("validate_offline: Corrupt cache for key %s (len %u != 4) ?\n",
3458                                 keystr, (unsigned int)dbuf.dsize ));
3459                 state->bad_entry = true;
3460                 state->success = false;
3461                 return 1;
3462         }
3463         DEBUG(10,("validate_offline: %s ok\n", keystr));
3464         return 0;
3465 }
3466
3467 static int validate_cache_version(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3468                                   struct tdb_validation_status *state)
3469 {
3470         if (dbuf.dsize != 4) {
3471                 DEBUG(0, ("validate_cache_version: Corrupt cache for "
3472                           "key %s (len %u != 4) ?\n", 
3473                           keystr, (unsigned int)dbuf.dsize));
3474                 state->bad_entry = true;
3475                 state->success = false;
3476                 return 1;
3477         }
3478
3479         DEBUG(10, ("validate_cache_version: %s ok\n", keystr));
3480         return 0;
3481 }
3482
3483 /***********************************************************************
3484  A list of all possible cache tdb keys with associated validation
3485  functions.
3486 ***********************************************************************/
3487
3488 struct key_val_struct {
3489         const char *keyname;
3490         int (*validate_data_fn)(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, struct tdb_validation_status* state);
3491 } key_val[] = {
3492         {"SEQNUM/", validate_seqnum},
3493         {"NS/", validate_ns},
3494         {"SN/", validate_sn},
3495         {"U/", validate_u},
3496         {"LOC_POL/", validate_loc_pol},
3497         {"PWD_POL/", validate_pwd_pol},
3498         {"CRED/", validate_cred},
3499         {"UL/", validate_ul},
3500         {"GL/", validate_gl},
3501         {"UG/", validate_ug},
3502         {"UA", validate_ua},
3503         {"GM/", validate_gm},
3504         {"DR/", validate_dr},
3505         {"DE/", validate_de},
3506         {"NSS/PWINFO/", validate_pwinfo},
3507         {"TRUSTDOMS/", validate_trustdoms},
3508         {"TRUSTDOMCACHE/", validate_trustdomcache},
3509         {"NSS/NA/", validate_nss_na},
3510         {"NSS/AN/", validate_nss_an},
3511         {"WINBINDD_OFFLINE", validate_offline},
3512         {WINBINDD_CACHE_VERSION_KEYSTR, validate_cache_version},
3513         {NULL, NULL}
3514 };
3515
3516 /***********************************************************************
3517  Function to look at every entry in the tdb and validate it as far as
3518  possible.
3519 ***********************************************************************/
3520
3521 static int cache_traverse_validate_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, void *state)
3522 {
3523         int i;
3524         unsigned int max_key_len = 1024;
3525         struct tdb_validation_status *v_state = (struct tdb_validation_status *)state;
3526
3527         /* Paranoia check. */
3528         if (strncmp("UA/", (const char *)kbuf.dptr, 3) == 0) {
3529                 max_key_len = 1024 * 1024;
3530         }
3531         if (kbuf.dsize > max_key_len) {
3532                 DEBUG(0, ("cache_traverse_validate_fn: key length too large: "
3533                           "(%u) > (%u)\n\n",
3534                           (unsigned int)kbuf.dsize, (unsigned int)max_key_len));
3535                 return 1;
3536         }
3537
3538         for (i = 0; key_val[i].keyname; i++) {
3539                 size_t namelen = strlen(key_val[i].keyname);
3540                 if (kbuf.dsize >= namelen && (
3541                                 strncmp(key_val[i].keyname, (const char *)kbuf.dptr, namelen)) == 0) {
3542                         TALLOC_CTX *mem_ctx;
3543                         char *keystr;
3544                         int ret;
3545
3546                         keystr = SMB_MALLOC_ARRAY(char, kbuf.dsize+1);
3547                         if (!keystr) {
3548                                 return 1;
3549                         }
3550                         memcpy(keystr, kbuf.dptr, kbuf.dsize);
3551                         keystr[kbuf.dsize] = '\0';
3552
3553                         mem_ctx = talloc_init("validate_ctx");
3554                         if (!mem_ctx) {
3555                                 SAFE_FREE(keystr);
3556                                 return 1;
3557                         }
3558
3559                         ret = key_val[i].validate_data_fn(mem_ctx, keystr, dbuf, 
3560                                                           v_state);
3561
3562                         SAFE_FREE(keystr);
3563                         talloc_destroy(mem_ctx);
3564                         return ret;
3565                 }
3566         }
3567
3568         DEBUG(0,("cache_traverse_validate_fn: unknown cache entry\nkey :\n"));
3569         dump_data(0, (uint8 *)kbuf.dptr, kbuf.dsize);
3570         DEBUG(0,("data :\n"));
3571         dump_data(0, (uint8 *)dbuf.dptr, dbuf.dsize);
3572         v_state->unknown_key = true;
3573         v_state->success = false;
3574         return 1; /* terminate. */
3575 }
3576
3577 static void validate_panic(const char *const why)
3578 {
3579         DEBUG(0,("validating cache: would panic %s\n", why ));
3580         DEBUGADD(0, ("exiting instead (cache validation mode)\n"));
3581         exit(47);
3582 }
3583
3584 /***********************************************************************
3585  Try and validate every entry in the winbindd cache. If we fail here,
3586  delete the cache tdb and return non-zero.
3587 ***********************************************************************/
3588
3589 int winbindd_validate_cache(void)
3590 {
3591         int ret = -1;
3592         const char *tdb_path = cache_path("winbindd_cache.tdb");
3593         TDB_CONTEXT *tdb = NULL;
3594
3595         DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
3596         smb_panic_fn = validate_panic;
3597
3598
3599         tdb = tdb_open_log(tdb_path, 
3600                            WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
3601                            ( lp_winbind_offline_logon() 
3602                              ? TDB_DEFAULT 
3603                              : TDB_DEFAULT | TDB_CLEAR_IF_FIRST ),
3604                            O_RDWR|O_CREAT, 
3605                            0600);
3606         if (!tdb) {
3607                 DEBUG(0, ("winbindd_validate_cache: "
3608                           "error opening/initializing tdb\n"));
3609                 goto done;
3610         }
3611         tdb_close(tdb);
3612
3613         ret = tdb_validate_and_backup(tdb_path, cache_traverse_validate_fn);
3614
3615         if (ret != 0) {
3616                 DEBUG(10, ("winbindd_validate_cache: validation not successful.\n"));
3617                 DEBUGADD(10, ("removing tdb %s.\n", tdb_path));
3618                 unlink(tdb_path);
3619         }
3620
3621 done:
3622         DEBUG(10, ("winbindd_validate_cache: restoring panic function\n"));
3623         smb_panic_fn = smb_panic;
3624         return ret;
3625 }
3626
3627 /***********************************************************************
3628  Try and validate every entry in the winbindd cache.
3629 ***********************************************************************/
3630
3631 int winbindd_validate_cache_nobackup(void)
3632 {
3633         int ret = -1;
3634         const char *tdb_path = cache_path("winbindd_cache.tdb");
3635
3636         DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
3637         smb_panic_fn = validate_panic;
3638
3639
3640         if (wcache == NULL || wcache->tdb == NULL) {
3641                 ret = tdb_validate_open(tdb_path, cache_traverse_validate_fn);
3642         } else {
3643                 ret = tdb_validate(wcache->tdb, cache_traverse_validate_fn);
3644         }
3645
3646         if (ret != 0) {
3647                 DEBUG(10, ("winbindd_validate_cache_nobackup: validation not "
3648                            "successful.\n"));
3649         }
3650
3651         DEBUG(10, ("winbindd_validate_cache_nobackup: restoring panic "
3652                    "function\n"));
3653         smb_panic_fn = smb_panic;
3654         return ret;
3655 }
3656
3657 bool winbindd_cache_validate_and_initialize(void)
3658 {
3659         close_winbindd_cache();
3660
3661         if (lp_winbind_offline_logon()) {
3662                 if (winbindd_validate_cache() < 0) {
3663                         DEBUG(0, ("winbindd cache tdb corrupt and no backup "
3664                                   "could be restored.\n"));
3665                 }
3666         }
3667
3668         return initialize_winbindd_cache();
3669 }
3670
3671 /*********************************************************************
3672  ********************************************************************/
3673
3674 static bool add_wbdomain_to_tdc_array( struct winbindd_domain *new_dom,
3675                                        struct winbindd_tdc_domain **domains, 
3676                                        size_t *num_domains )
3677 {
3678         struct winbindd_tdc_domain *list = NULL;
3679         size_t idx;
3680         int i;
3681         bool set_only = false;
3682
3683         /* don't allow duplicates */
3684
3685         idx = *num_domains;
3686         list = *domains;
3687
3688         for ( i=0; i< (*num_domains); i++ ) {
3689                 if ( strequal( new_dom->name, list[i].domain_name ) ) {
3690                         DEBUG(10,("add_wbdomain_to_tdc_array: Found existing record for %s\n",
3691                                   new_dom->name));
3692                         idx = i;
3693                         set_only = true;
3694
3695                         break;
3696                 }
3697         }
3698
3699         if ( !set_only ) {
3700                 if ( !*domains ) {
3701                         list = TALLOC_ARRAY( NULL, struct winbindd_tdc_domain, 1 );
3702                         idx = 0;
3703                 } else {
3704                         list = TALLOC_REALLOC_ARRAY( *domains, *domains, 
3705                                                      struct winbindd_tdc_domain,  
3706                                                      (*num_domains)+1);
3707                         idx = *num_domains;             
3708                 }
3709
3710                 ZERO_STRUCT( list[idx] );
3711         }
3712
3713         if ( !list )
3714                 return false;
3715
3716         list[idx].domain_name = talloc_strdup( list, new_dom->name );
3717         list[idx].dns_name = talloc_strdup( list, new_dom->alt_name );
3718
3719         if ( !is_null_sid( &new_dom->sid ) ) {
3720                 sid_copy( &list[idx].sid, &new_dom->sid );
3721         } else {
3722                 sid_copy(&list[idx].sid, &global_sid_NULL);
3723         }
3724
3725         if ( new_dom->domain_flags != 0x0 )
3726                 list[idx].trust_flags = new_dom->domain_flags;  
3727
3728         if ( new_dom->domain_type != 0x0 )
3729                 list[idx].trust_type = new_dom->domain_type;
3730
3731         if ( new_dom->domain_trust_attribs != 0x0 )
3732                 list[idx].trust_attribs = new_dom->domain_trust_attribs;
3733
3734         if ( !set_only ) {
3735                 *domains = list;
3736                 *num_domains = idx + 1; 
3737         }
3738
3739         return true;
3740 }
3741
3742 /*********************************************************************
3743  ********************************************************************/
3744
3745 static TDB_DATA make_tdc_key( const char *domain_name )
3746 {
3747         char *keystr = NULL;
3748         TDB_DATA key = { NULL, 0 };
3749
3750         if ( !domain_name ) {
3751                 DEBUG(5,("make_tdc_key: Keyname workgroup is NULL!\n"));
3752                 return key;
3753         }
3754
3755         if (asprintf( &keystr, "TRUSTDOMCACHE/%s", domain_name ) == -1) {
3756                 return key;
3757         }
3758         key = string_term_tdb_data(keystr);
3759
3760         return key;     
3761 }
3762
3763 /*********************************************************************
3764  ********************************************************************/
3765
3766 static int pack_tdc_domains( struct winbindd_tdc_domain *domains, 
3767                              size_t num_domains,
3768                              unsigned char **buf )
3769 {
3770         unsigned char *buffer = NULL;
3771         int len = 0;
3772         int buflen = 0;
3773         int i = 0;
3774
3775         DEBUG(10,("pack_tdc_domains: Packing %d trusted domains\n",
3776                   (int)num_domains));
3777
3778         buflen = 0;
3779
3780  again: 
3781         len = 0;
3782
3783         /* Store the number of array items first */
3784         len += tdb_pack( buffer+len, buflen-len, "d", 
3785                          num_domains );
3786
3787         /* now pack each domain trust record */
3788         for ( i=0; i<num_domains; i++ ) {
3789
3790                 fstring tmp;
3791
3792                 if ( buflen > 0 ) {
3793                         DEBUG(10,("pack_tdc_domains: Packing domain %s (%s)\n",
3794                                   domains[i].domain_name,
3795                                   domains[i].dns_name ? domains[i].dns_name : "UNKNOWN" ));
3796                 }
3797
3798                 len += tdb_pack( buffer+len, buflen-len, "fffddd",
3799                                  domains[i].domain_name,
3800                                  domains[i].dns_name,
3801                                  sid_to_fstring(tmp, &domains[i].sid),
3802                                  domains[i].trust_flags,
3803                                  domains[i].trust_attribs,
3804                                  domains[i].trust_type );
3805         }
3806
3807         if ( buflen < len ) {
3808                 SAFE_FREE(buffer);
3809                 if ( (buffer = SMB_MALLOC_ARRAY(unsigned char, len)) == NULL ) {
3810                         DEBUG(0,("pack_tdc_domains: failed to alloc buffer!\n"));
3811                         buflen = -1;
3812                         goto done;
3813                 }
3814                 buflen = len;
3815                 goto again;
3816         }
3817
3818         *buf = buffer;  
3819
3820  done:  
3821         return buflen;  
3822 }
3823
3824 /*********************************************************************
3825  ********************************************************************/
3826
3827 static size_t unpack_tdc_domains( unsigned char *buf, int buflen, 
3828                                   struct winbindd_tdc_domain **domains )
3829 {
3830         fstring domain_name, dns_name, sid_string;      
3831         uint32 type, attribs, flags;
3832         int num_domains;
3833         int len = 0;
3834         int i;
3835         struct winbindd_tdc_domain *list = NULL;
3836
3837         /* get the number of domains */
3838         len += tdb_unpack( buf+len, buflen-len, "d", &num_domains);
3839         if ( len == -1 ) {
3840                 DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));               
3841                 return 0;
3842         }
3843
3844         list = TALLOC_ARRAY( NULL, struct winbindd_tdc_domain, num_domains );
3845         if ( !list ) {
3846                 DEBUG(0,("unpack_tdc_domains: Failed to talloc() domain list!\n"));
3847                 return 0;               
3848         }
3849
3850         for ( i=0; i<num_domains; i++ ) {
3851                 len += tdb_unpack( buf+len, buflen-len, "fffddd",
3852                                    domain_name,
3853                                    dns_name,
3854                                    sid_string,
3855                                    &flags,
3856                                    &attribs,
3857                                    &type );
3858
3859                 if ( len == -1 ) {
3860                         DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
3861                         TALLOC_FREE( list );                    
3862                         return 0;
3863                 }
3864
3865                 DEBUG(11,("unpack_tdc_domains: Unpacking domain %s (%s) "
3866                           "SID %s, flags = 0x%x, attribs = 0x%x, type = 0x%x\n",
3867                           domain_name, dns_name, sid_string,
3868                           flags, attribs, type));
3869
3870                 list[i].domain_name = talloc_strdup( list, domain_name );
3871                 list[i].dns_name = talloc_strdup( list, dns_name );
3872                 if ( !string_to_sid( &(list[i].sid), sid_string ) ) {                   
3873                         DEBUG(10,("unpack_tdc_domains: no SID for domain %s\n",
3874                                   domain_name));
3875                 }
3876                 list[i].trust_flags = flags;
3877                 list[i].trust_attribs = attribs;
3878                 list[i].trust_type = type;
3879         }
3880
3881         *domains = list;
3882
3883         return num_domains;
3884 }
3885
3886 /*********************************************************************
3887  ********************************************************************/
3888
3889 static bool wcache_tdc_store_list( struct winbindd_tdc_domain *domains, size_t num_domains )
3890 {
3891         TDB_DATA key = make_tdc_key( lp_workgroup() );   
3892         TDB_DATA data = { NULL, 0 };
3893         int ret;
3894
3895         if ( !key.dptr )
3896                 return false;
3897
3898         /* See if we were asked to delete the cache entry */
3899
3900         if ( !domains ) {
3901                 ret = tdb_delete( wcache->tdb, key );
3902                 goto done;
3903         }
3904
3905         data.dsize = pack_tdc_domains( domains, num_domains, &data.dptr );
3906
3907         if ( !data.dptr ) {
3908                 ret = -1;
3909                 goto done;
3910         }
3911
3912         ret = tdb_store( wcache->tdb, key, data, 0 );
3913
3914  done:
3915         SAFE_FREE( data.dptr );
3916         SAFE_FREE( key.dptr );
3917
3918         return ( ret != -1 );   
3919 }
3920
3921 /*********************************************************************
3922  ********************************************************************/
3923
3924 bool wcache_tdc_fetch_list( struct winbindd_tdc_domain **domains, size_t *num_domains )
3925 {
3926         TDB_DATA key = make_tdc_key( lp_workgroup() );
3927         TDB_DATA data = { NULL, 0 };
3928
3929         *domains = NULL;        
3930         *num_domains = 0;       
3931
3932         if ( !key.dptr )
3933                 return false;
3934
3935         data = tdb_fetch( wcache->tdb, key );
3936
3937         SAFE_FREE( key.dptr );
3938
3939         if ( !data.dptr ) 
3940                 return false;
3941
3942         *num_domains = unpack_tdc_domains( data.dptr, data.dsize, domains );
3943
3944         SAFE_FREE( data.dptr );
3945
3946         if ( !*domains )
3947                 return false;
3948
3949         return true;
3950 }
3951
3952 /*********************************************************************
3953  ********************************************************************/
3954
3955 bool wcache_tdc_add_domain( struct winbindd_domain *domain )
3956 {
3957         struct winbindd_tdc_domain *dom_list = NULL;
3958         size_t num_domains = 0;
3959         bool ret = false;
3960
3961         DEBUG(10,("wcache_tdc_add_domain: Adding domain %s (%s), SID %s, "
3962                   "flags = 0x%x, attributes = 0x%x, type = 0x%x\n",
3963                   domain->name, domain->alt_name, 
3964                   sid_string_dbg(&domain->sid),
3965                   domain->domain_flags,
3966                   domain->domain_trust_attribs,
3967                   domain->domain_type));        
3968
3969         if ( !init_wcache() ) {
3970                 return false;
3971         }
3972
3973         /* fetch the list */
3974
3975         wcache_tdc_fetch_list( &dom_list, &num_domains );
3976
3977         /* add the new domain */
3978
3979         if ( !add_wbdomain_to_tdc_array( domain, &dom_list, &num_domains ) ) {
3980                 goto done;              
3981         }       
3982
3983         /* pack the domain */
3984
3985         if ( !wcache_tdc_store_list( dom_list, num_domains ) ) {
3986                 goto done;              
3987         }
3988
3989         /* Success */
3990
3991         ret = true;
3992  done:
3993         TALLOC_FREE( dom_list );
3994
3995         return ret;     
3996 }
3997
3998 /*********************************************************************
3999  ********************************************************************/
4000
4001 struct winbindd_tdc_domain * wcache_tdc_fetch_domain( TALLOC_CTX *ctx, const char *name )
4002 {
4003         struct winbindd_tdc_domain *dom_list = NULL;
4004         size_t num_domains = 0;
4005         int i;
4006         struct winbindd_tdc_domain *d = NULL;   
4007
4008         DEBUG(10,("wcache_tdc_fetch_domain: Searching for domain %s\n", name));
4009
4010         if ( !init_wcache() ) {
4011                 return false;
4012         }
4013
4014         /* fetch the list */
4015
4016         wcache_tdc_fetch_list( &dom_list, &num_domains );
4017
4018         for ( i=0; i<num_domains; i++ ) {
4019                 if ( strequal(name, dom_list[i].domain_name) ||
4020                      strequal(name, dom_list[i].dns_name) )
4021                 {
4022                         DEBUG(10,("wcache_tdc_fetch_domain: Found domain %s\n",
4023                                   name));
4024
4025                         d = TALLOC_P( ctx, struct winbindd_tdc_domain );
4026                         if ( !d )
4027                                 break;                  
4028
4029                         d->domain_name = talloc_strdup( d, dom_list[i].domain_name );
4030                         d->dns_name = talloc_strdup( d, dom_list[i].dns_name );
4031                         sid_copy( &d->sid, &dom_list[i].sid );
4032                         d->trust_flags   = dom_list[i].trust_flags;
4033                         d->trust_type    = dom_list[i].trust_type;
4034                         d->trust_attribs = dom_list[i].trust_attribs;
4035
4036                         break;
4037                 }
4038         }
4039
4040         TALLOC_FREE( dom_list );
4041
4042         return d;       
4043 }
4044
4045
4046 /*********************************************************************
4047  ********************************************************************/
4048
4049 void wcache_tdc_clear( void )
4050 {
4051         if ( !init_wcache() )
4052                 return;
4053
4054         wcache_tdc_store_list( NULL, 0 );
4055
4056         return; 
4057 }
4058
4059
4060 /*********************************************************************
4061  ********************************************************************/
4062
4063 static void wcache_save_user_pwinfo(struct winbindd_domain *domain, 
4064                                     NTSTATUS status,
4065                                     const DOM_SID *user_sid,
4066                                     const char *homedir,
4067                                     const char *shell,
4068                                     const char *gecos,
4069                                     uint32 gid)
4070 {
4071         struct cache_entry *centry;
4072         fstring tmp;
4073
4074         if ( (centry = centry_start(domain, status)) == NULL )
4075                 return;
4076
4077         centry_put_string( centry, homedir );
4078         centry_put_string( centry, shell );
4079         centry_put_string( centry, gecos );
4080         centry_put_uint32( centry, gid );
4081
4082         centry_end(centry, "NSS/PWINFO/%s", sid_to_fstring(tmp, user_sid) );
4083
4084         DEBUG(10,("wcache_save_user_pwinfo: %s\n", sid_string_dbg(user_sid) ));
4085
4086         centry_free(centry);
4087 }
4088
4089 NTSTATUS nss_get_info_cached( struct winbindd_domain *domain, 
4090                               const DOM_SID *user_sid,
4091                               TALLOC_CTX *ctx,
4092                               ADS_STRUCT *ads, LDAPMessage *msg,
4093                               char **homedir, char **shell, char **gecos,
4094                               gid_t *p_gid)
4095 {
4096         struct winbind_cache *cache = get_cache(domain);
4097         struct cache_entry *centry = NULL;
4098         NTSTATUS nt_status;
4099         fstring tmp;
4100
4101         if (!cache->tdb)
4102                 goto do_query;
4103
4104         centry = wcache_fetch(cache, domain, "NSS/PWINFO/%s",
4105                               sid_to_fstring(tmp, user_sid));
4106
4107         if (!centry)
4108                 goto do_query;
4109
4110         *homedir = centry_string( centry, ctx );
4111         *shell   = centry_string( centry, ctx );
4112         *gecos   = centry_string( centry, ctx );
4113         *p_gid   = centry_uint32( centry );     
4114
4115         centry_free(centry);
4116
4117         DEBUG(10,("nss_get_info_cached: [Cached] - user_sid %s\n",
4118                   sid_string_dbg(user_sid)));
4119
4120         return NT_STATUS_OK;
4121
4122 do_query:
4123
4124         nt_status = nss_get_info( domain->name, user_sid, ctx, ads, msg, 
4125                                   homedir, shell, gecos, p_gid );
4126
4127         DEBUG(10, ("nss_get_info returned %s\n", nt_errstr(nt_status)));
4128
4129         if ( NT_STATUS_IS_OK(nt_status) ) {
4130                 DEBUG(10, ("result:\n\thomedir = '%s'\n", *homedir));
4131                 DEBUGADD(10, ("\tshell = '%s'\n", *shell));
4132                 DEBUGADD(10, ("\tgecos = '%s'\n", *gecos));
4133                 DEBUGADD(10, ("\tgid = '%u'\n", (unsigned int)*p_gid));
4134
4135                 wcache_save_user_pwinfo( domain, nt_status, user_sid,
4136                                          *homedir, *shell, *gecos, *p_gid );
4137         }       
4138
4139         if ( NT_STATUS_EQUAL( nt_status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND ) ) {
4140                 DEBUG(5,("nss_get_info_cached: Setting domain %s offline\n",
4141                          domain->name ));
4142                 set_domain_offline( domain );
4143         }
4144
4145         return nt_status;       
4146 }
4147
4148
4149 /* the cache backend methods are exposed via this structure */
4150 struct winbindd_methods cache_methods = {
4151         true,
4152         query_user_list,
4153         enum_dom_groups,
4154         enum_local_groups,
4155         name_to_sid,
4156         sid_to_name,
4157         rids_to_names,
4158         query_user,
4159         lookup_usergroups,
4160         lookup_useraliases,
4161         lookup_groupmem,
4162         sequence_number,
4163         lockout_policy,
4164         password_policy,
4165         trusted_domains
4166 };