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