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