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