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