s3:winbind: Add a generic cache for NDR based parent-child requests
[sfrench/samba-autobuild/.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 bool wcache_fetch_seqnum(const char *domain_name, uint32_t *seqnum,
396                                 uint32_t *last_seq_check)
397 {
398         char *key;
399         TDB_DATA data;
400
401         if (wcache->tdb == NULL) {
402                 DEBUG(10,("wcache_fetch_seqnum: tdb == NULL\n"));
403                 return false;
404         }
405
406         key = talloc_asprintf(talloc_tos(), "SEQNUM/%s", domain_name);
407         if (key == NULL) {
408                 DEBUG(10, ("talloc failed\n"));
409                 return false;
410         }
411
412         data = tdb_fetch_bystring(wcache->tdb, key);
413         TALLOC_FREE(key);
414
415         if (data.dptr == NULL) {
416                 DEBUG(10, ("wcache_fetch_seqnum: %s not found\n",
417                            domain_name));
418                 return false;
419         }
420         if (data.dsize != 8) {
421                 DEBUG(10, ("wcache_fetch_seqnum: invalid data size %d\n",
422                            (int)data.dsize));
423                 SAFE_FREE(data.dptr);
424                 return false;
425         }
426
427         *seqnum = IVAL(data.dptr, 0);
428         *last_seq_check = IVAL(data.dptr, 4);
429         SAFE_FREE(data.dptr);
430
431         return true;
432 }
433
434 static NTSTATUS fetch_cache_seqnum( struct winbindd_domain *domain, time_t now )
435 {
436         uint32 last_check, time_diff;
437
438         if (!wcache_fetch_seqnum(domain->name, &domain->sequence_number,
439                                  &last_check)) {
440                 return NT_STATUS_UNSUCCESSFUL;
441         }
442         domain->last_seq_check = last_check;
443
444         /* have we expired? */
445
446         time_diff = now - domain->last_seq_check;
447         if ( time_diff > lp_winbind_cache_time() ) {
448                 DEBUG(10,("fetch_cache_seqnum: timeout [%s][%u @ %u]\n",
449                         domain->name, domain->sequence_number,
450                         (uint32)domain->last_seq_check));
451                 return NT_STATUS_UNSUCCESSFUL;
452         }
453
454         DEBUG(10,("fetch_cache_seqnum: success [%s][%u @ %u]\n", 
455                 domain->name, domain->sequence_number, 
456                 (uint32)domain->last_seq_check));
457
458         return NT_STATUS_OK;
459 }
460
461 static NTSTATUS store_cache_seqnum( struct winbindd_domain *domain )
462 {
463         TDB_DATA data;
464         fstring key_str;
465         uint8 buf[8];
466
467         if (!wcache->tdb) {
468                 DEBUG(10,("store_cache_seqnum: tdb == NULL\n"));
469                 return NT_STATUS_UNSUCCESSFUL;
470         }
471
472         fstr_sprintf( key_str, "SEQNUM/%s", domain->name );
473
474         SIVAL(buf, 0, domain->sequence_number);
475         SIVAL(buf, 4, domain->last_seq_check);
476         data.dptr = buf;
477         data.dsize = 8;
478
479         if ( tdb_store_bystring( wcache->tdb, key_str, data, TDB_REPLACE) == -1 ) {
480                 DEBUG(10,("store_cache_seqnum: tdb_store fail key [%s]\n", key_str ));
481                 return NT_STATUS_UNSUCCESSFUL;
482         }
483
484         DEBUG(10,("store_cache_seqnum: success [%s][%u @ %u]\n", 
485                 domain->name, domain->sequence_number, 
486                 (uint32)domain->last_seq_check));
487
488         return NT_STATUS_OK;
489 }
490
491 /*
492   refresh the domain sequence number. If force is true
493   then always refresh it, no matter how recently we fetched it
494 */
495
496 static void refresh_sequence_number(struct winbindd_domain *domain, bool force)
497 {
498         NTSTATUS status;
499         unsigned time_diff;
500         time_t t = time(NULL);
501         unsigned cache_time = lp_winbind_cache_time();
502
503         if ( IS_DOMAIN_OFFLINE(domain) ) {
504                 return;
505         }
506
507         get_cache( domain );
508
509 #if 0   /* JERRY -- disable as the default cache time is now 5 minutes */
510         /* trying to reconnect is expensive, don't do it too often */
511         if (domain->sequence_number == DOM_SEQUENCE_NONE) {
512                 cache_time *= 8;
513         }
514 #endif
515
516         time_diff = t - domain->last_seq_check;
517
518         /* see if we have to refetch the domain sequence number */
519         if (!force && (time_diff < cache_time) &&
520                         (domain->sequence_number != DOM_SEQUENCE_NONE) &&
521                         NT_STATUS_IS_OK(domain->last_status)) {
522                 DEBUG(10, ("refresh_sequence_number: %s time ok\n", domain->name));
523                 goto done;
524         }
525
526         /* try to get the sequence number from the tdb cache first */
527         /* this will update the timestamp as well */
528
529         status = fetch_cache_seqnum( domain, t );
530         if (NT_STATUS_IS_OK(status) &&
531                         (domain->sequence_number != DOM_SEQUENCE_NONE) &&
532                         NT_STATUS_IS_OK(domain->last_status)) {
533                 goto done;
534         }
535
536         /* important! make sure that we know if this is a native 
537            mode domain or not.  And that we can contact it. */
538
539         if ( winbindd_can_contact_domain( domain ) ) {          
540                 status = domain->backend->sequence_number(domain, 
541                                                           &domain->sequence_number);
542         } else {
543                 /* just use the current time */
544                 status = NT_STATUS_OK;
545                 domain->sequence_number = time(NULL);
546         }
547
548
549         /* the above call could have set our domain->backend to NULL when
550          * coming from offline to online mode, make sure to reinitialize the
551          * backend - Guenther */
552         get_cache( domain );
553
554         if (!NT_STATUS_IS_OK(status)) {
555                 DEBUG(10,("refresh_sequence_number: failed with %s\n", nt_errstr(status)));
556                 domain->sequence_number = DOM_SEQUENCE_NONE;
557         }
558
559         domain->last_status = status;
560         domain->last_seq_check = time(NULL);
561
562         /* save the new sequence number in the cache */
563         store_cache_seqnum( domain );
564
565 done:
566         DEBUG(10, ("refresh_sequence_number: %s seq number is now %d\n", 
567                    domain->name, domain->sequence_number));
568
569         return;
570 }
571
572 /*
573   decide if a cache entry has expired
574 */
575 static bool centry_expired(struct winbindd_domain *domain, const char *keystr, struct cache_entry *centry)
576 {
577         /* If we've been told to be offline - stay in that state... */
578         if (lp_winbind_offline_logon() && global_winbindd_offline_state) {
579                 DEBUG(10,("centry_expired: Key %s for domain %s valid as winbindd is globally offline.\n",
580                         keystr, domain->name ));
581                 return false;
582         }
583
584         /* when the domain is offline return the cached entry.
585          * This deals with transient offline states... */
586
587         if (!domain->online) {
588                 DEBUG(10,("centry_expired: Key %s for domain %s valid as domain is offline.\n",
589                         keystr, domain->name ));
590                 return false;
591         }
592
593         /* if the server is OK and our cache entry came from when it was down then
594            the entry is invalid */
595         if ((domain->sequence_number != DOM_SEQUENCE_NONE) &&  
596             (centry->sequence_number == DOM_SEQUENCE_NONE)) {
597                 DEBUG(10,("centry_expired: Key %s for domain %s invalid sequence.\n",
598                         keystr, domain->name ));
599                 return true;
600         }
601
602         /* if the server is down or the cache entry is not older than the
603            current sequence number then it is OK */
604         if (wcache_server_down(domain) || 
605             centry->sequence_number == domain->sequence_number) {
606                 DEBUG(10,("centry_expired: Key %s for domain %s is good.\n",
607                         keystr, domain->name ));
608                 return false;
609         }
610
611         DEBUG(10,("centry_expired: Key %s for domain %s expired\n",
612                 keystr, domain->name ));
613
614         /* it's expired */
615         return true;
616 }
617
618 static struct cache_entry *wcache_fetch_raw(char *kstr)
619 {
620         TDB_DATA data;
621         struct cache_entry *centry;
622         TDB_DATA key;
623
624         key = string_tdb_data(kstr);
625         data = tdb_fetch(wcache->tdb, key);
626         if (!data.dptr) {
627                 /* a cache miss */
628                 return NULL;
629         }
630
631         centry = SMB_XMALLOC_P(struct cache_entry);
632         centry->data = (unsigned char *)data.dptr;
633         centry->len = data.dsize;
634         centry->ofs = 0;
635
636         if (centry->len < 8) {
637                 /* huh? corrupt cache? */
638                 DEBUG(10,("wcache_fetch_raw: Corrupt cache for key %s (len < 8) ?\n", kstr));
639                 centry_free(centry);
640                 return NULL;
641         }
642
643         centry->status = centry_ntstatus(centry);
644         centry->sequence_number = centry_uint32(centry);
645
646         return centry;
647 }
648
649 /*
650   fetch an entry from the cache, with a varargs key. auto-fetch the sequence
651   number and return status
652 */
653 static struct cache_entry *wcache_fetch(struct winbind_cache *cache, 
654                                         struct winbindd_domain *domain,
655                                         const char *format, ...) PRINTF_ATTRIBUTE(3,4);
656 static struct cache_entry *wcache_fetch(struct winbind_cache *cache, 
657                                         struct winbindd_domain *domain,
658                                         const char *format, ...)
659 {
660         va_list ap;
661         char *kstr;
662         struct cache_entry *centry;
663
664         if (!winbindd_use_cache()) {
665                 return NULL;
666         }
667
668         refresh_sequence_number(domain, false);
669
670         va_start(ap, format);
671         smb_xvasprintf(&kstr, format, ap);
672         va_end(ap);
673
674         centry = wcache_fetch_raw(kstr);
675         if (centry == NULL) {
676                 free(kstr);
677                 return NULL;
678         }
679
680         if (centry_expired(domain, kstr, centry)) {
681
682                 DEBUG(10,("wcache_fetch: entry %s expired for domain %s\n",
683                          kstr, domain->name ));
684
685                 centry_free(centry);
686                 free(kstr);
687                 return NULL;
688         }
689
690         DEBUG(10,("wcache_fetch: returning entry %s for domain %s\n",
691                  kstr, domain->name ));
692
693         free(kstr);
694         return centry;
695 }
696
697 static void wcache_delete(const char *format, ...) PRINTF_ATTRIBUTE(1,2);
698 static void wcache_delete(const char *format, ...)
699 {
700         va_list ap;
701         char *kstr;
702         TDB_DATA key;
703
704         va_start(ap, format);
705         smb_xvasprintf(&kstr, format, ap);
706         va_end(ap);
707
708         key = string_tdb_data(kstr);
709
710         tdb_delete(wcache->tdb, key);
711         free(kstr);
712 }
713
714 /*
715   make sure we have at least len bytes available in a centry 
716 */
717 static void centry_expand(struct cache_entry *centry, uint32 len)
718 {
719         if (centry->len - centry->ofs >= len)
720                 return;
721         centry->len *= 2;
722         centry->data = SMB_REALLOC_ARRAY(centry->data, unsigned char,
723                                          centry->len);
724         if (!centry->data) {
725                 DEBUG(0,("out of memory: needed %d bytes in centry_expand\n", centry->len));
726                 smb_panic_fn("out of memory in centry_expand");
727         }
728 }
729
730 /*
731   push a uint32 into a centry 
732 */
733 static void centry_put_uint32(struct cache_entry *centry, uint32 v)
734 {
735         centry_expand(centry, 4);
736         SIVAL(centry->data, centry->ofs, v);
737         centry->ofs += 4;
738 }
739
740 /*
741   push a uint16 into a centry 
742 */
743 static void centry_put_uint16(struct cache_entry *centry, uint16 v)
744 {
745         centry_expand(centry, 2);
746         SIVAL(centry->data, centry->ofs, v);
747         centry->ofs += 2;
748 }
749
750 /*
751   push a uint8 into a centry 
752 */
753 static void centry_put_uint8(struct cache_entry *centry, uint8 v)
754 {
755         centry_expand(centry, 1);
756         SCVAL(centry->data, centry->ofs, v);
757         centry->ofs += 1;
758 }
759
760 /* 
761    push a string into a centry 
762  */
763 static void centry_put_string(struct cache_entry *centry, const char *s)
764 {
765         int len;
766
767         if (!s) {
768                 /* null strings are marked as len 0xFFFF */
769                 centry_put_uint8(centry, 0xFF);
770                 return;
771         }
772
773         len = strlen(s);
774         /* can't handle more than 254 char strings. Truncating is probably best */
775         if (len > 254) {
776                 DEBUG(10,("centry_put_string: truncating len (%d) to: 254\n", len));
777                 len = 254;
778         }
779         centry_put_uint8(centry, len);
780         centry_expand(centry, len);
781         memcpy(centry->data + centry->ofs, s, len);
782         centry->ofs += len;
783 }
784
785 /* 
786    push a 16 byte hash into a centry - treat as 16 byte string.
787  */
788 static void centry_put_hash16(struct cache_entry *centry, const uint8 val[16])
789 {
790         centry_put_uint8(centry, 16);
791         centry_expand(centry, 16);
792         memcpy(centry->data + centry->ofs, val, 16);
793         centry->ofs += 16;
794 }
795
796 static void centry_put_sid(struct cache_entry *centry, const DOM_SID *sid) 
797 {
798         fstring sid_string;
799         centry_put_string(centry, sid_to_fstring(sid_string, sid));
800 }
801
802
803 /*
804   put NTSTATUS into a centry
805 */
806 static void centry_put_ntstatus(struct cache_entry *centry, NTSTATUS status)
807 {
808         uint32 status_value = NT_STATUS_V(status);
809         centry_put_uint32(centry, status_value);
810 }
811
812
813 /*
814   push a NTTIME into a centry 
815 */
816 static void centry_put_nttime(struct cache_entry *centry, NTTIME nt)
817 {
818         centry_expand(centry, 8);
819         SIVAL(centry->data, centry->ofs, nt & 0xFFFFFFFF);
820         centry->ofs += 4;
821         SIVAL(centry->data, centry->ofs, nt >> 32);
822         centry->ofs += 4;
823 }
824
825 /*
826   push a time_t into a centry - use a 64 bit size.
827   NTTIME here is being used as a convenient 64-bit size.
828 */
829 static void centry_put_time(struct cache_entry *centry, time_t t)
830 {
831         NTTIME nt = (NTTIME)t;
832         centry_put_nttime(centry, nt);
833 }
834
835 /*
836   start a centry for output. When finished, call centry_end()
837 */
838 struct cache_entry *centry_start(struct winbindd_domain *domain, NTSTATUS status)
839 {
840         struct cache_entry *centry;
841
842         if (!wcache->tdb)
843                 return NULL;
844
845         centry = SMB_XMALLOC_P(struct cache_entry);
846
847         centry->len = 8192; /* reasonable default */
848         centry->data = SMB_XMALLOC_ARRAY(uint8, centry->len);
849         centry->ofs = 0;
850         centry->sequence_number = domain->sequence_number;
851         centry_put_ntstatus(centry, status);
852         centry_put_uint32(centry, centry->sequence_number);
853         return centry;
854 }
855
856 /*
857   finish a centry and write it to the tdb
858 */
859 static void centry_end(struct cache_entry *centry, const char *format, ...) PRINTF_ATTRIBUTE(2,3);
860 static void centry_end(struct cache_entry *centry, const char *format, ...)
861 {
862         va_list ap;
863         char *kstr;
864         TDB_DATA key, data;
865
866         if (!winbindd_use_cache()) {
867                 return;
868         }
869
870         va_start(ap, format);
871         smb_xvasprintf(&kstr, format, ap);
872         va_end(ap);
873
874         key = string_tdb_data(kstr);
875         data.dptr = centry->data;
876         data.dsize = centry->ofs;
877
878         tdb_store(wcache->tdb, key, data, TDB_REPLACE);
879         free(kstr);
880 }
881
882 static void wcache_save_name_to_sid(struct winbindd_domain *domain, 
883                                     NTSTATUS status, const char *domain_name,
884                                     const char *name, const DOM_SID *sid, 
885                                     enum lsa_SidType type)
886 {
887         struct cache_entry *centry;
888         fstring uname;
889
890         centry = centry_start(domain, status);
891         if (!centry)
892                 return;
893         centry_put_uint32(centry, type);
894         centry_put_sid(centry, sid);
895         fstrcpy(uname, name);
896         strupper_m(uname);
897         centry_end(centry, "NS/%s/%s", domain_name, uname);
898         DEBUG(10,("wcache_save_name_to_sid: %s\\%s -> %s (%s)\n", domain_name,
899                   uname, sid_string_dbg(sid), nt_errstr(status)));
900         centry_free(centry);
901 }
902
903 static void wcache_save_sid_to_name(struct winbindd_domain *domain, NTSTATUS status, 
904                                     const DOM_SID *sid, const char *domain_name, const char *name, enum lsa_SidType type)
905 {
906         struct cache_entry *centry;
907         fstring sid_string;
908
909         centry = centry_start(domain, status);
910         if (!centry)
911                 return;
912
913         if (NT_STATUS_IS_OK(status)) {
914                 centry_put_uint32(centry, type);
915                 centry_put_string(centry, domain_name);
916                 centry_put_string(centry, name);
917         }
918
919         centry_end(centry, "SN/%s", sid_to_fstring(sid_string, sid));
920         DEBUG(10,("wcache_save_sid_to_name: %s -> %s (%s)\n", sid_string, 
921                   name, nt_errstr(status)));
922         centry_free(centry);
923 }
924
925
926 static void wcache_save_user(struct winbindd_domain *domain, NTSTATUS status,
927                              struct wbint_userinfo *info)
928 {
929         struct cache_entry *centry;
930         fstring sid_string;
931
932         if (is_null_sid(&info->user_sid)) {
933                 return;
934         }
935
936         centry = centry_start(domain, status);
937         if (!centry)
938                 return;
939         centry_put_string(centry, info->acct_name);
940         centry_put_string(centry, info->full_name);
941         centry_put_string(centry, info->homedir);
942         centry_put_string(centry, info->shell);
943         centry_put_uint32(centry, info->primary_gid);
944         centry_put_sid(centry, &info->user_sid);
945         centry_put_sid(centry, &info->group_sid);
946         centry_end(centry, "U/%s", sid_to_fstring(sid_string,
947                                                   &info->user_sid));
948         DEBUG(10,("wcache_save_user: %s (acct_name %s)\n", sid_string, info->acct_name));
949         centry_free(centry);
950 }
951
952 static void wcache_save_lockout_policy(struct winbindd_domain *domain,
953                                        NTSTATUS status,
954                                        struct samr_DomInfo12 *lockout_policy)
955 {
956         struct cache_entry *centry;
957
958         centry = centry_start(domain, status);
959         if (!centry)
960                 return;
961
962         centry_put_nttime(centry, lockout_policy->lockout_duration);
963         centry_put_nttime(centry, lockout_policy->lockout_window);
964         centry_put_uint16(centry, lockout_policy->lockout_threshold);
965
966         centry_end(centry, "LOC_POL/%s", domain->name);
967
968         DEBUG(10,("wcache_save_lockout_policy: %s\n", domain->name));
969
970         centry_free(centry);
971 }
972
973
974
975 static void wcache_save_password_policy(struct winbindd_domain *domain,
976                                         NTSTATUS status,
977                                         struct samr_DomInfo1 *policy)
978 {
979         struct cache_entry *centry;
980
981         centry = centry_start(domain, status);
982         if (!centry)
983                 return;
984
985         centry_put_uint16(centry, policy->min_password_length);
986         centry_put_uint16(centry, policy->password_history_length);
987         centry_put_uint32(centry, policy->password_properties);
988         centry_put_nttime(centry, policy->max_password_age);
989         centry_put_nttime(centry, policy->min_password_age);
990
991         centry_end(centry, "PWD_POL/%s", domain->name);
992
993         DEBUG(10,("wcache_save_password_policy: %s\n", domain->name));
994
995         centry_free(centry);
996 }
997
998 /***************************************************************************
999  ***************************************************************************/
1000
1001 static void wcache_save_username_alias(struct winbindd_domain *domain,
1002                                        NTSTATUS status,
1003                                        const char *name, const char *alias)
1004 {
1005         struct cache_entry *centry;
1006         fstring uname;
1007
1008         if ( (centry = centry_start(domain, status)) == NULL )
1009                 return;
1010
1011         centry_put_string( centry, alias );
1012
1013         fstrcpy(uname, name);
1014         strupper_m(uname);
1015         centry_end(centry, "NSS/NA/%s", uname);
1016
1017         DEBUG(10,("wcache_save_username_alias: %s -> %s\n", name, alias ));
1018
1019         centry_free(centry);
1020 }
1021
1022 static void wcache_save_alias_username(struct winbindd_domain *domain,
1023                                        NTSTATUS status,
1024                                        const char *alias, const char *name)
1025 {
1026         struct cache_entry *centry;
1027         fstring uname;
1028
1029         if ( (centry = centry_start(domain, status)) == NULL )
1030                 return;
1031
1032         centry_put_string( centry, name );
1033
1034         fstrcpy(uname, alias);
1035         strupper_m(uname);
1036         centry_end(centry, "NSS/AN/%s", uname);
1037
1038         DEBUG(10,("wcache_save_alias_username: %s -> %s\n", alias, name ));
1039
1040         centry_free(centry);
1041 }
1042
1043 /***************************************************************************
1044  ***************************************************************************/
1045
1046 NTSTATUS resolve_username_to_alias( TALLOC_CTX *mem_ctx,
1047                                     struct winbindd_domain *domain,
1048                                     const char *name, char **alias )
1049 {
1050         struct winbind_cache *cache = get_cache(domain);
1051         struct cache_entry *centry = NULL;
1052         NTSTATUS status;
1053         char *upper_name;
1054
1055         if ( domain->internal )
1056                 return NT_STATUS_NOT_SUPPORTED;
1057
1058         if (!cache->tdb)
1059                 goto do_query;
1060
1061         if ( (upper_name = SMB_STRDUP(name)) == NULL )
1062                 return NT_STATUS_NO_MEMORY;
1063         strupper_m(upper_name);
1064
1065         centry = wcache_fetch(cache, domain, "NSS/NA/%s", upper_name);
1066
1067         SAFE_FREE( upper_name );
1068
1069         if (!centry)
1070                 goto do_query;
1071
1072         status = centry->status;
1073
1074         if (!NT_STATUS_IS_OK(status)) {
1075                 centry_free(centry);
1076                 return status;
1077         }
1078
1079         *alias = centry_string( centry, mem_ctx );
1080
1081         centry_free(centry);
1082
1083         DEBUG(10,("resolve_username_to_alias: [Cached] - mapped %s to %s\n",
1084                   name, *alias ? *alias : "(none)"));
1085
1086         return (*alias) ? NT_STATUS_OK : NT_STATUS_OBJECT_NAME_NOT_FOUND;
1087
1088 do_query:
1089
1090         /* If its not in cache and we are offline, then fail */
1091
1092         if ( get_global_winbindd_state_offline() || !domain->online ) {
1093                 DEBUG(8,("resolve_username_to_alias: rejecting query "
1094                          "in offline mode\n"));
1095                 return NT_STATUS_NOT_FOUND;
1096         }
1097
1098         status = nss_map_to_alias( mem_ctx, domain->name, name, alias );
1099
1100         if ( NT_STATUS_IS_OK( status ) ) {
1101                 wcache_save_username_alias(domain, status, name, *alias);
1102         }
1103
1104         if ( NT_STATUS_EQUAL( status, NT_STATUS_NONE_MAPPED ) ) {
1105                 wcache_save_username_alias(domain, status, name, "(NULL)");
1106         }
1107
1108         DEBUG(5,("resolve_username_to_alias: backend query returned %s\n",
1109                  nt_errstr(status)));
1110
1111         if ( NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ) {
1112                 set_domain_offline( domain );
1113         }
1114
1115         return status;
1116 }
1117
1118 /***************************************************************************
1119  ***************************************************************************/
1120
1121 NTSTATUS resolve_alias_to_username( TALLOC_CTX *mem_ctx,
1122                                     struct winbindd_domain *domain,
1123                                     const char *alias, char **name )
1124 {
1125         struct winbind_cache *cache = get_cache(domain);
1126         struct cache_entry *centry = NULL;
1127         NTSTATUS status;
1128         char *upper_name;
1129
1130         if ( domain->internal )
1131                 return  NT_STATUS_NOT_SUPPORTED;
1132
1133         if (!cache->tdb)
1134                 goto do_query;
1135
1136         if ( (upper_name = SMB_STRDUP(alias)) == NULL )
1137                 return NT_STATUS_NO_MEMORY;
1138         strupper_m(upper_name);
1139
1140         centry = wcache_fetch(cache, domain, "NSS/AN/%s", upper_name);
1141
1142         SAFE_FREE( upper_name );
1143
1144         if (!centry)
1145                 goto do_query;
1146
1147         status = centry->status;
1148
1149         if (!NT_STATUS_IS_OK(status)) {
1150                 centry_free(centry);
1151                 return status;
1152         }
1153
1154         *name = centry_string( centry, mem_ctx );
1155
1156         centry_free(centry);
1157
1158         DEBUG(10,("resolve_alias_to_username: [Cached] - mapped %s to %s\n",
1159                   alias, *name ? *name : "(none)"));
1160
1161         return (*name) ? NT_STATUS_OK : NT_STATUS_OBJECT_NAME_NOT_FOUND;
1162
1163 do_query:
1164
1165         /* If its not in cache and we are offline, then fail */
1166
1167         if ( get_global_winbindd_state_offline() || !domain->online ) {
1168                 DEBUG(8,("resolve_alias_to_username: rejecting query "
1169                          "in offline mode\n"));
1170                 return NT_STATUS_NOT_FOUND;
1171         }
1172
1173         /* an alias cannot contain a domain prefix or '@' */
1174
1175         if (strchr(alias, '\\') || strchr(alias, '@')) {
1176                 DEBUG(10,("resolve_alias_to_username: skipping fully "
1177                           "qualified name %s\n", alias));
1178                 return NT_STATUS_OBJECT_NAME_INVALID;
1179         }
1180
1181         status = nss_map_from_alias( mem_ctx, domain->name, alias, name );
1182
1183         if ( NT_STATUS_IS_OK( status ) ) {
1184                 wcache_save_alias_username( domain, status, alias, *name );
1185         }
1186
1187         if (NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) {
1188                 wcache_save_alias_username(domain, status, alias, "(NULL)");
1189         }
1190
1191         DEBUG(5,("resolve_alias_to_username: backend query returned %s\n",
1192                  nt_errstr(status)));
1193
1194         if ( NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ) {
1195                 set_domain_offline( domain );
1196         }
1197
1198         return status;
1199 }
1200
1201 NTSTATUS wcache_cached_creds_exist(struct winbindd_domain *domain, const DOM_SID *sid)
1202 {
1203         struct winbind_cache *cache = get_cache(domain);
1204         TDB_DATA data;
1205         fstring key_str, tmp;
1206         uint32 rid;
1207
1208         if (!cache->tdb) {
1209                 return NT_STATUS_INTERNAL_DB_ERROR;
1210         }
1211
1212         if (is_null_sid(sid)) {
1213                 return NT_STATUS_INVALID_SID;
1214         }
1215
1216         if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1217                 return NT_STATUS_INVALID_SID;
1218         }
1219
1220         fstr_sprintf(key_str, "CRED/%s", sid_to_fstring(tmp, sid));
1221
1222         data = tdb_fetch(cache->tdb, string_tdb_data(key_str));
1223         if (!data.dptr) {
1224                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1225         }
1226
1227         SAFE_FREE(data.dptr);
1228         return NT_STATUS_OK;
1229 }
1230
1231 /* Lookup creds for a SID - copes with old (unsalted) creds as well
1232    as new salted ones. */
1233
1234 NTSTATUS wcache_get_creds(struct winbindd_domain *domain, 
1235                           TALLOC_CTX *mem_ctx, 
1236                           const DOM_SID *sid,
1237                           const uint8 **cached_nt_pass,
1238                           const uint8 **cached_salt)
1239 {
1240         struct winbind_cache *cache = get_cache(domain);
1241         struct cache_entry *centry = NULL;
1242         NTSTATUS status;
1243         time_t t;
1244         uint32 rid;
1245         fstring tmp;
1246
1247         if (!cache->tdb) {
1248                 return NT_STATUS_INTERNAL_DB_ERROR;
1249         }
1250
1251         if (is_null_sid(sid)) {
1252                 return NT_STATUS_INVALID_SID;
1253         }
1254
1255         if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1256                 return NT_STATUS_INVALID_SID;
1257         }
1258
1259         /* Try and get a salted cred first. If we can't
1260            fall back to an unsalted cred. */
1261
1262         centry = wcache_fetch(cache, domain, "CRED/%s",
1263                               sid_to_fstring(tmp, sid));
1264         if (!centry) {
1265                 DEBUG(10,("wcache_get_creds: entry for [CRED/%s] not found\n", 
1266                           sid_string_dbg(sid)));
1267                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1268         }
1269
1270         t = centry_time(centry);
1271
1272         /* In the salted case this isn't actually the nt_hash itself,
1273            but the MD5 of the salt + nt_hash. Let the caller
1274            sort this out. It can tell as we only return the cached_salt
1275            if we are returning a salted cred. */
1276
1277         *cached_nt_pass = (const uint8 *)centry_hash16(centry, mem_ctx);
1278         if (*cached_nt_pass == NULL) {
1279                 fstring sidstr;
1280
1281                 sid_to_fstring(sidstr, sid);
1282
1283                 /* Bad (old) cred cache. Delete and pretend we
1284                    don't have it. */
1285                 DEBUG(0,("wcache_get_creds: bad entry for [CRED/%s] - deleting\n", 
1286                                 sidstr));
1287                 wcache_delete("CRED/%s", sidstr);
1288                 centry_free(centry);
1289                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1290         }
1291
1292         /* We only have 17 bytes more data in the salted cred case. */
1293         if (centry->len - centry->ofs == 17) {
1294                 *cached_salt = (const uint8 *)centry_hash16(centry, mem_ctx);
1295         } else {
1296                 *cached_salt = NULL;
1297         }
1298
1299         dump_data_pw("cached_nt_pass", *cached_nt_pass, NT_HASH_LEN);
1300         if (*cached_salt) {
1301                 dump_data_pw("cached_salt", *cached_salt, NT_HASH_LEN);
1302         }
1303
1304         status = centry->status;
1305
1306         DEBUG(10,("wcache_get_creds: [Cached] - cached creds for user %s status: %s\n",
1307                   sid_string_dbg(sid), nt_errstr(status) ));
1308
1309         centry_free(centry);
1310         return status;
1311 }
1312
1313 /* Store creds for a SID - only writes out new salted ones. */
1314
1315 NTSTATUS wcache_save_creds(struct winbindd_domain *domain, 
1316                            TALLOC_CTX *mem_ctx, 
1317                            const DOM_SID *sid, 
1318                            const uint8 nt_pass[NT_HASH_LEN])
1319 {
1320         struct cache_entry *centry;
1321         fstring sid_string;
1322         uint32 rid;
1323         uint8 cred_salt[NT_HASH_LEN];
1324         uint8 salted_hash[NT_HASH_LEN];
1325
1326         if (is_null_sid(sid)) {
1327                 return NT_STATUS_INVALID_SID;
1328         }
1329
1330         if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1331                 return NT_STATUS_INVALID_SID;
1332         }
1333
1334         centry = centry_start(domain, NT_STATUS_OK);
1335         if (!centry) {
1336                 return NT_STATUS_INTERNAL_DB_ERROR;
1337         }
1338
1339         dump_data_pw("nt_pass", nt_pass, NT_HASH_LEN);
1340
1341         centry_put_time(centry, time(NULL));
1342
1343         /* Create a salt and then salt the hash. */
1344         generate_random_buffer(cred_salt, NT_HASH_LEN);
1345         E_md5hash(cred_salt, nt_pass, salted_hash);
1346
1347         centry_put_hash16(centry, salted_hash);
1348         centry_put_hash16(centry, cred_salt);
1349         centry_end(centry, "CRED/%s", sid_to_fstring(sid_string, sid));
1350
1351         DEBUG(10,("wcache_save_creds: %s\n", sid_string));
1352
1353         centry_free(centry);
1354
1355         return NT_STATUS_OK;
1356 }
1357
1358
1359 /* Query display info. This is the basic user list fn */
1360 static NTSTATUS query_user_list(struct winbindd_domain *domain,
1361                                 TALLOC_CTX *mem_ctx,
1362                                 uint32 *num_entries, 
1363                                 struct wbint_userinfo **info)
1364 {
1365         struct winbind_cache *cache = get_cache(domain);
1366         struct cache_entry *centry = NULL;
1367         NTSTATUS status;
1368         unsigned int i, retry;
1369
1370         if (!cache->tdb)
1371                 goto do_query;
1372
1373         centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
1374         if (!centry)
1375                 goto do_query;
1376
1377         *num_entries = centry_uint32(centry);
1378
1379         if (*num_entries == 0)
1380                 goto do_cached;
1381
1382         (*info) = TALLOC_ARRAY(mem_ctx, struct wbint_userinfo, *num_entries);
1383         if (! (*info)) {
1384                 smb_panic_fn("query_user_list out of memory");
1385         }
1386         for (i=0; i<(*num_entries); i++) {
1387                 (*info)[i].acct_name = centry_string(centry, mem_ctx);
1388                 (*info)[i].full_name = centry_string(centry, mem_ctx);
1389                 (*info)[i].homedir = centry_string(centry, mem_ctx);
1390                 (*info)[i].shell = centry_string(centry, mem_ctx);
1391                 centry_sid(centry, &(*info)[i].user_sid);
1392                 centry_sid(centry, &(*info)[i].group_sid);
1393         }
1394
1395 do_cached:      
1396         status = centry->status;
1397
1398         DEBUG(10,("query_user_list: [Cached] - cached list for domain %s status: %s\n",
1399                 domain->name, nt_errstr(status) ));
1400
1401         centry_free(centry);
1402         return status;
1403
1404 do_query:
1405         *num_entries = 0;
1406         *info = NULL;
1407
1408         /* Return status value returned by seq number check */
1409
1410         if (!NT_STATUS_IS_OK(domain->last_status))
1411                 return domain->last_status;
1412
1413         /* Put the query_user_list() in a retry loop.  There appears to be
1414          * some bug either with Windows 2000 or Samba's handling of large
1415          * rpc replies.  This manifests itself as sudden disconnection
1416          * at a random point in the enumeration of a large (60k) user list.
1417          * The retry loop simply tries the operation again. )-:  It's not
1418          * pretty but an acceptable workaround until we work out what the
1419          * real problem is. */
1420
1421         retry = 0;
1422         do {
1423
1424                 DEBUG(10,("query_user_list: [Cached] - doing backend query for list for domain %s\n",
1425                         domain->name ));
1426
1427                 status = domain->backend->query_user_list(domain, mem_ctx, num_entries, info);
1428                 if (!NT_STATUS_IS_OK(status)) {
1429                         DEBUG(3, ("query_user_list: returned 0x%08x, "
1430                                   "retrying\n", NT_STATUS_V(status)));
1431                 }
1432                 if (NT_STATUS_EQUAL(status, NT_STATUS_UNSUCCESSFUL)) {
1433                         DEBUG(3, ("query_user_list: flushing "
1434                                   "connection cache\n"));
1435                         invalidate_cm_connection(&domain->conn);
1436                 }
1437
1438         } while (NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_UNSUCCESSFUL) && 
1439                  (retry++ < 5));
1440
1441         /* and save it */
1442         refresh_sequence_number(domain, false);
1443         centry = centry_start(domain, status);
1444         if (!centry)
1445                 goto skip_save;
1446         centry_put_uint32(centry, *num_entries);
1447         for (i=0; i<(*num_entries); i++) {
1448                 centry_put_string(centry, (*info)[i].acct_name);
1449                 centry_put_string(centry, (*info)[i].full_name);
1450                 centry_put_string(centry, (*info)[i].homedir);
1451                 centry_put_string(centry, (*info)[i].shell);
1452                 centry_put_sid(centry, &(*info)[i].user_sid);
1453                 centry_put_sid(centry, &(*info)[i].group_sid);
1454                 if (domain->backend && domain->backend->consistent) {
1455                         /* when the backend is consistent we can pre-prime some mappings */
1456                         wcache_save_name_to_sid(domain, NT_STATUS_OK, 
1457                                                 domain->name,
1458                                                 (*info)[i].acct_name, 
1459                                                 &(*info)[i].user_sid,
1460                                                 SID_NAME_USER);
1461                         wcache_save_sid_to_name(domain, NT_STATUS_OK, 
1462                                                 &(*info)[i].user_sid,
1463                                                 domain->name,
1464                                                 (*info)[i].acct_name, 
1465                                                 SID_NAME_USER);
1466                         wcache_save_user(domain, NT_STATUS_OK, &(*info)[i]);
1467                 }
1468         }       
1469         centry_end(centry, "UL/%s", domain->name);
1470         centry_free(centry);
1471
1472 skip_save:
1473         return status;
1474 }
1475
1476 /* list all domain groups */
1477 static NTSTATUS enum_dom_groups(struct winbindd_domain *domain,
1478                                 TALLOC_CTX *mem_ctx,
1479                                 uint32 *num_entries, 
1480                                 struct acct_info **info)
1481 {
1482         struct winbind_cache *cache = get_cache(domain);
1483         struct cache_entry *centry = NULL;
1484         NTSTATUS status;
1485         unsigned int i;
1486
1487         if (!cache->tdb)
1488                 goto do_query;
1489
1490         centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
1491         if (!centry)
1492                 goto do_query;
1493
1494         *num_entries = centry_uint32(centry);
1495
1496         if (*num_entries == 0)
1497                 goto do_cached;
1498
1499         (*info) = TALLOC_ARRAY(mem_ctx, struct acct_info, *num_entries);
1500         if (! (*info)) {
1501                 smb_panic_fn("enum_dom_groups out of memory");
1502         }
1503         for (i=0; i<(*num_entries); i++) {
1504                 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1505                 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1506                 (*info)[i].rid = centry_uint32(centry);
1507         }
1508
1509 do_cached:      
1510         status = centry->status;
1511
1512         DEBUG(10,("enum_dom_groups: [Cached] - cached list for domain %s status: %s\n",
1513                 domain->name, nt_errstr(status) ));
1514
1515         centry_free(centry);
1516         return status;
1517
1518 do_query:
1519         *num_entries = 0;
1520         *info = NULL;
1521
1522         /* Return status value returned by seq number check */
1523
1524         if (!NT_STATUS_IS_OK(domain->last_status))
1525                 return domain->last_status;
1526
1527         DEBUG(10,("enum_dom_groups: [Cached] - doing backend query for list for domain %s\n",
1528                 domain->name ));
1529
1530         status = domain->backend->enum_dom_groups(domain, mem_ctx, num_entries, info);
1531
1532         /* and save it */
1533         refresh_sequence_number(domain, false);
1534         centry = centry_start(domain, status);
1535         if (!centry)
1536                 goto skip_save;
1537         centry_put_uint32(centry, *num_entries);
1538         for (i=0; i<(*num_entries); i++) {
1539                 centry_put_string(centry, (*info)[i].acct_name);
1540                 centry_put_string(centry, (*info)[i].acct_desc);
1541                 centry_put_uint32(centry, (*info)[i].rid);
1542         }       
1543         centry_end(centry, "GL/%s/domain", domain->name);
1544         centry_free(centry);
1545
1546 skip_save:
1547         return status;
1548 }
1549
1550 /* list all domain groups */
1551 static NTSTATUS enum_local_groups(struct winbindd_domain *domain,
1552                                 TALLOC_CTX *mem_ctx,
1553                                 uint32 *num_entries, 
1554                                 struct acct_info **info)
1555 {
1556         struct winbind_cache *cache = get_cache(domain);
1557         struct cache_entry *centry = NULL;
1558         NTSTATUS status;
1559         unsigned int i;
1560
1561         if (!cache->tdb)
1562                 goto do_query;
1563
1564         centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
1565         if (!centry)
1566                 goto do_query;
1567
1568         *num_entries = centry_uint32(centry);
1569
1570         if (*num_entries == 0)
1571                 goto do_cached;
1572
1573         (*info) = TALLOC_ARRAY(mem_ctx, struct acct_info, *num_entries);
1574         if (! (*info)) {
1575                 smb_panic_fn("enum_dom_groups out of memory");
1576         }
1577         for (i=0; i<(*num_entries); i++) {
1578                 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1579                 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1580                 (*info)[i].rid = centry_uint32(centry);
1581         }
1582
1583 do_cached:      
1584
1585         /* If we are returning cached data and the domain controller
1586            is down then we don't know whether the data is up to date
1587            or not.  Return NT_STATUS_MORE_PROCESSING_REQUIRED to
1588            indicate this. */
1589
1590         if (wcache_server_down(domain)) {
1591                 DEBUG(10, ("enum_local_groups: returning cached user list and server was down\n"));
1592                 status = NT_STATUS_MORE_PROCESSING_REQUIRED;
1593         } else
1594                 status = centry->status;
1595
1596         DEBUG(10,("enum_local_groups: [Cached] - cached list for domain %s status: %s\n",
1597                 domain->name, nt_errstr(status) ));
1598
1599         centry_free(centry);
1600         return status;
1601
1602 do_query:
1603         *num_entries = 0;
1604         *info = NULL;
1605
1606         /* Return status value returned by seq number check */
1607
1608         if (!NT_STATUS_IS_OK(domain->last_status))
1609                 return domain->last_status;
1610
1611         DEBUG(10,("enum_local_groups: [Cached] - doing backend query for list for domain %s\n",
1612                 domain->name ));
1613
1614         status = domain->backend->enum_local_groups(domain, mem_ctx, num_entries, info);
1615
1616         /* and save it */
1617         refresh_sequence_number(domain, false);
1618         centry = centry_start(domain, status);
1619         if (!centry)
1620                 goto skip_save;
1621         centry_put_uint32(centry, *num_entries);
1622         for (i=0; i<(*num_entries); i++) {
1623                 centry_put_string(centry, (*info)[i].acct_name);
1624                 centry_put_string(centry, (*info)[i].acct_desc);
1625                 centry_put_uint32(centry, (*info)[i].rid);
1626         }
1627         centry_end(centry, "GL/%s/local", domain->name);
1628         centry_free(centry);
1629
1630 skip_save:
1631         return status;
1632 }
1633
1634 NTSTATUS wcache_name_to_sid(struct winbindd_domain *domain,
1635                             const char *domain_name,
1636                             const char *name,
1637                             struct dom_sid *sid,
1638                             enum lsa_SidType *type)
1639 {
1640         struct winbind_cache *cache = get_cache(domain);
1641         struct cache_entry *centry;
1642         NTSTATUS status;
1643         char *uname;
1644
1645         if (cache->tdb == NULL) {
1646                 return NT_STATUS_NOT_FOUND;
1647         }
1648
1649         uname = talloc_strdup_upper(talloc_tos(), name);
1650         if (uname == NULL) {
1651                 return NT_STATUS_NO_MEMORY;
1652         }
1653
1654         centry = wcache_fetch(cache, domain, "NS/%s/%s", domain_name, uname);
1655         TALLOC_FREE(uname);
1656         if (centry == NULL) {
1657                 return NT_STATUS_NOT_FOUND;
1658         }
1659
1660         status = centry->status;
1661         if (NT_STATUS_IS_OK(status)) {
1662                 *type = (enum lsa_SidType)centry_uint32(centry);
1663                 centry_sid(centry, sid);
1664         }
1665
1666         DEBUG(10,("name_to_sid: [Cached] - cached name for domain %s status: "
1667                   "%s\n", domain->name, nt_errstr(status) ));
1668
1669         centry_free(centry);
1670         return status;
1671 }
1672
1673 /* convert a single name to a sid in a domain */
1674 static NTSTATUS name_to_sid(struct winbindd_domain *domain,
1675                             TALLOC_CTX *mem_ctx,
1676                             const char *domain_name,
1677                             const char *name,
1678                             uint32_t flags,
1679                             DOM_SID *sid,
1680                             enum lsa_SidType *type)
1681 {
1682         NTSTATUS status;
1683
1684         status = wcache_name_to_sid(domain, domain_name, name, sid, type);
1685         if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
1686                 return status;
1687         }
1688
1689         ZERO_STRUCTP(sid);
1690
1691         /* If the seq number check indicated that there is a problem
1692          * with this DC, then return that status... except for
1693          * access_denied.  This is special because the dc may be in
1694          * "restrict anonymous = 1" mode, in which case it will deny
1695          * most unauthenticated operations, but *will* allow the LSA
1696          * name-to-sid that we try as a fallback. */
1697
1698         if (!(NT_STATUS_IS_OK(domain->last_status)
1699               || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1700                 return domain->last_status;
1701
1702         DEBUG(10,("name_to_sid: [Cached] - doing backend query for name for domain %s\n",
1703                 domain->name ));
1704
1705         status = domain->backend->name_to_sid(domain, mem_ctx, domain_name,
1706                                               name, flags, sid, type);
1707
1708         /* and save it */
1709         refresh_sequence_number(domain, false);
1710
1711         if (domain->online &&
1712             (NT_STATUS_IS_OK(status) || NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED))) {
1713                 wcache_save_name_to_sid(domain, status, domain_name, name, sid, *type);
1714
1715                 /* Only save the reverse mapping if this was not a UPN */
1716                 if (!strchr(name, '@')) {
1717                         strupper_m(CONST_DISCARD(char *,domain_name));
1718                         strlower_m(CONST_DISCARD(char *,name));
1719                         wcache_save_sid_to_name(domain, status, sid, domain_name, name, *type);
1720                 }
1721         }
1722
1723         return status;
1724 }
1725
1726 NTSTATUS wcache_sid_to_name(struct winbindd_domain *domain,
1727                             const struct dom_sid *sid,
1728                             TALLOC_CTX *mem_ctx,
1729                             char **domain_name,
1730                             char **name,
1731                             enum lsa_SidType *type)
1732 {
1733         struct winbind_cache *cache = get_cache(domain);
1734         struct cache_entry *centry;
1735         char *sid_string;
1736         NTSTATUS status;
1737
1738         if (cache->tdb == NULL) {
1739                 return NT_STATUS_NOT_FOUND;
1740         }
1741
1742         sid_string = sid_string_tos(sid);
1743         if (sid_string == NULL) {
1744                 return NT_STATUS_NO_MEMORY;
1745         }
1746
1747         centry = wcache_fetch(cache, domain, "SN/%s", sid_string);
1748         TALLOC_FREE(sid_string);
1749         if (centry == NULL) {
1750                 return NT_STATUS_NOT_FOUND;
1751         }
1752
1753         if (NT_STATUS_IS_OK(centry->status)) {
1754                 *type = (enum lsa_SidType)centry_uint32(centry);
1755                 *domain_name = centry_string(centry, mem_ctx);
1756                 *name = centry_string(centry, mem_ctx);
1757         }
1758
1759         status = centry->status;
1760         centry_free(centry);
1761
1762         DEBUG(10,("sid_to_name: [Cached] - cached name for domain %s status: "
1763                   "%s\n", domain->name, nt_errstr(status) ));
1764
1765         return status;
1766 }
1767
1768 /* convert a sid to a user or group name. The sid is guaranteed to be in the domain
1769    given */
1770 static NTSTATUS sid_to_name(struct winbindd_domain *domain,
1771                             TALLOC_CTX *mem_ctx,
1772                             const DOM_SID *sid,
1773                             char **domain_name,
1774                             char **name,
1775                             enum lsa_SidType *type)
1776 {
1777         NTSTATUS status;
1778
1779         status = wcache_sid_to_name(domain, sid, mem_ctx, domain_name, name,
1780                                     type);
1781         if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
1782                 return status;
1783         }
1784
1785         *name = NULL;
1786         *domain_name = NULL;
1787
1788         /* If the seq number check indicated that there is a problem
1789          * with this DC, then return that status... except for
1790          * access_denied.  This is special because the dc may be in
1791          * "restrict anonymous = 1" mode, in which case it will deny
1792          * most unauthenticated operations, but *will* allow the LSA
1793          * sid-to-name that we try as a fallback. */
1794
1795         if (!(NT_STATUS_IS_OK(domain->last_status)
1796               || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1797                 return domain->last_status;
1798
1799         DEBUG(10,("sid_to_name: [Cached] - doing backend query for name for domain %s\n",
1800                 domain->name ));
1801
1802         status = domain->backend->sid_to_name(domain, mem_ctx, sid, domain_name, name, type);
1803
1804         /* and save it */
1805         refresh_sequence_number(domain, false);
1806         wcache_save_sid_to_name(domain, status, sid, *domain_name, *name, *type);
1807
1808         /* We can't save the name to sid mapping here, as with sid history a
1809          * later name2sid would give the wrong sid. */
1810
1811         return status;
1812 }
1813
1814 static NTSTATUS rids_to_names(struct winbindd_domain *domain,
1815                               TALLOC_CTX *mem_ctx,
1816                               const DOM_SID *domain_sid,
1817                               uint32 *rids,
1818                               size_t num_rids,
1819                               char **domain_name,
1820                               char ***names,
1821                               enum lsa_SidType **types)
1822 {
1823         struct winbind_cache *cache = get_cache(domain);
1824         size_t i;
1825         NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
1826         bool have_mapped;
1827         bool have_unmapped;
1828
1829         *domain_name = NULL;
1830         *names = NULL;
1831         *types = NULL;
1832
1833         if (!cache->tdb) {
1834                 goto do_query;
1835         }
1836
1837         if (num_rids == 0) {
1838                 return NT_STATUS_OK;
1839         }
1840
1841         *names = TALLOC_ARRAY(mem_ctx, char *, num_rids);
1842         *types = TALLOC_ARRAY(mem_ctx, enum lsa_SidType, num_rids);
1843
1844         if ((*names == NULL) || (*types == NULL)) {
1845                 result = NT_STATUS_NO_MEMORY;
1846                 goto error;
1847         }
1848
1849         have_mapped = have_unmapped = false;
1850
1851         for (i=0; i<num_rids; i++) {
1852                 DOM_SID sid;
1853                 struct cache_entry *centry;
1854                 fstring tmp;
1855
1856                 if (!sid_compose(&sid, domain_sid, rids[i])) {
1857                         result = NT_STATUS_INTERNAL_ERROR;
1858                         goto error;
1859                 }
1860
1861                 centry = wcache_fetch(cache, domain, "SN/%s",
1862                                       sid_to_fstring(tmp, &sid));
1863                 if (!centry) {
1864                         goto do_query;
1865                 }
1866
1867                 (*types)[i] = SID_NAME_UNKNOWN;
1868                 (*names)[i] = talloc_strdup(*names, "");
1869
1870                 if (NT_STATUS_IS_OK(centry->status)) {
1871                         char *dom;
1872                         have_mapped = true;
1873                         (*types)[i] = (enum lsa_SidType)centry_uint32(centry);
1874
1875                         dom = centry_string(centry, mem_ctx);
1876                         if (*domain_name == NULL) {
1877                                 *domain_name = dom;
1878                         } else {
1879                                 talloc_free(dom);
1880                         }
1881
1882                         (*names)[i] = centry_string(centry, *names);
1883
1884                 } else if (NT_STATUS_EQUAL(centry->status, NT_STATUS_NONE_MAPPED)) {
1885                         have_unmapped = true;
1886
1887                 } else {
1888                         /* something's definitely wrong */
1889                         result = centry->status;
1890                         goto error;
1891                 }
1892
1893                 centry_free(centry);
1894         }
1895
1896         if (!have_mapped) {
1897                 return NT_STATUS_NONE_MAPPED;
1898         }
1899         if (!have_unmapped) {
1900                 return NT_STATUS_OK;
1901         }
1902         return STATUS_SOME_UNMAPPED;
1903
1904  do_query:
1905
1906         TALLOC_FREE(*names);
1907         TALLOC_FREE(*types);
1908
1909         result = domain->backend->rids_to_names(domain, mem_ctx, domain_sid,
1910                                                 rids, num_rids, domain_name,
1911                                                 names, types);
1912
1913         /*
1914           None of the queried rids has been found so save all negative entries
1915         */
1916         if (NT_STATUS_EQUAL(result, NT_STATUS_NONE_MAPPED)) {
1917                 for (i = 0; i < num_rids; i++) {
1918                         DOM_SID sid;
1919                         const char *name = "";
1920                         const enum lsa_SidType type = SID_NAME_UNKNOWN;
1921                         NTSTATUS status = NT_STATUS_NONE_MAPPED;
1922
1923                         if (!sid_compose(&sid, domain_sid, rids[i])) {
1924                                 return NT_STATUS_INTERNAL_ERROR;
1925                         }
1926
1927                         wcache_save_sid_to_name(domain, status, &sid, *domain_name,
1928                                                 name, type);
1929                 }
1930
1931                 return result;
1932         }
1933
1934         /*
1935           Some or all of the queried rids have been found.
1936         */
1937         if (!NT_STATUS_IS_OK(result) &&
1938             !NT_STATUS_EQUAL(result, STATUS_SOME_UNMAPPED)) {
1939                 return result;
1940         }
1941
1942         refresh_sequence_number(domain, false);
1943
1944         for (i=0; i<num_rids; i++) {
1945                 DOM_SID sid;
1946                 NTSTATUS status;
1947
1948                 if (!sid_compose(&sid, domain_sid, rids[i])) {
1949                         result = NT_STATUS_INTERNAL_ERROR;
1950                         goto error;
1951                 }
1952
1953                 status = (*types)[i] == SID_NAME_UNKNOWN ?
1954                         NT_STATUS_NONE_MAPPED : NT_STATUS_OK;
1955
1956                 wcache_save_sid_to_name(domain, status, &sid, *domain_name,
1957                                         (*names)[i], (*types)[i]);
1958         }
1959
1960         return result;
1961
1962  error:
1963         TALLOC_FREE(*names);
1964         TALLOC_FREE(*types);
1965         return result;
1966 }
1967
1968 NTSTATUS wcache_query_user(struct winbindd_domain *domain,
1969                            TALLOC_CTX *mem_ctx,
1970                            const struct dom_sid *user_sid,
1971                            struct wbint_userinfo *info)
1972 {
1973         struct winbind_cache *cache = get_cache(domain);
1974         struct cache_entry *centry = NULL;
1975         NTSTATUS status;
1976         char *sid_string;
1977
1978         if (cache->tdb == NULL) {
1979                 return NT_STATUS_NOT_FOUND;
1980         }
1981
1982         sid_string = sid_string_tos(user_sid);
1983         if (sid_string == NULL) {
1984                 return NT_STATUS_NO_MEMORY;
1985         }
1986
1987         centry = wcache_fetch(cache, domain, "U/%s", sid_string);
1988         TALLOC_FREE(sid_string);
1989         if (centry == NULL) {
1990                 return NT_STATUS_NOT_FOUND;
1991         }
1992
1993         /*
1994          * If we have an access denied cache entry and a cached info3
1995          * in the samlogon cache then do a query.  This will force the
1996          * rpc back end to return the info3 data.
1997          */
1998
1999         if (NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED) &&
2000             netsamlogon_cache_have(user_sid)) {
2001                 DEBUG(10, ("query_user: cached access denied and have cached "
2002                            "info3\n"));
2003                 domain->last_status = NT_STATUS_OK;
2004                 centry_free(centry);
2005                 return NT_STATUS_NOT_FOUND;
2006         }
2007
2008         /* if status is not ok then this is a negative hit
2009            and the rest of the data doesn't matter */
2010         status = centry->status;
2011         if (NT_STATUS_IS_OK(status)) {
2012                 info->acct_name = centry_string(centry, mem_ctx);
2013                 info->full_name = centry_string(centry, mem_ctx);
2014                 info->homedir = centry_string(centry, mem_ctx);
2015                 info->shell = centry_string(centry, mem_ctx);
2016                 info->primary_gid = centry_uint32(centry);
2017                 centry_sid(centry, &info->user_sid);
2018                 centry_sid(centry, &info->group_sid);
2019         }
2020
2021         DEBUG(10,("query_user: [Cached] - cached info for domain %s status: "
2022                   "%s\n", domain->name, nt_errstr(status) ));
2023
2024         centry_free(centry);
2025         return status;
2026 }
2027
2028 /* Lookup user information from a rid */
2029 static NTSTATUS query_user(struct winbindd_domain *domain,
2030                            TALLOC_CTX *mem_ctx,
2031                            const DOM_SID *user_sid,
2032                            struct wbint_userinfo *info)
2033 {
2034         NTSTATUS status;
2035
2036         status = wcache_query_user(domain, mem_ctx, user_sid, info);
2037         if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2038                 return status;
2039         }
2040
2041         ZERO_STRUCTP(info);
2042
2043         /* Return status value returned by seq number check */
2044
2045         if (!NT_STATUS_IS_OK(domain->last_status))
2046                 return domain->last_status;
2047
2048         DEBUG(10,("query_user: [Cached] - doing backend query for info for domain %s\n",
2049                 domain->name ));
2050
2051         status = domain->backend->query_user(domain, mem_ctx, user_sid, info);
2052
2053         /* and save it */
2054         refresh_sequence_number(domain, false);
2055         wcache_save_user(domain, status, info);
2056
2057         return status;
2058 }
2059
2060 NTSTATUS wcache_lookup_usergroups(struct winbindd_domain *domain,
2061                                   TALLOC_CTX *mem_ctx,
2062                                   const struct dom_sid *user_sid,
2063                                   uint32_t *pnum_sids,
2064                                   struct dom_sid **psids)
2065 {
2066         struct winbind_cache *cache = get_cache(domain);
2067         struct cache_entry *centry = NULL;
2068         NTSTATUS status;
2069         uint32_t i, num_sids;
2070         struct dom_sid *sids;
2071         fstring sid_string;
2072
2073         if (cache->tdb == NULL) {
2074                 return NT_STATUS_NOT_FOUND;
2075         }
2076
2077         centry = wcache_fetch(cache, domain, "UG/%s",
2078                               sid_to_fstring(sid_string, user_sid));
2079         if (centry == NULL) {
2080                 return NT_STATUS_NOT_FOUND;
2081         }
2082
2083         /* If we have an access denied cache entry and a cached info3 in the
2084            samlogon cache then do a query.  This will force the rpc back end
2085            to return the info3 data. */
2086
2087         if (NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)
2088             && netsamlogon_cache_have(user_sid)) {
2089                 DEBUG(10, ("lookup_usergroups: cached access denied and have "
2090                            "cached info3\n"));
2091                 domain->last_status = NT_STATUS_OK;
2092                 centry_free(centry);
2093                 return NT_STATUS_NOT_FOUND;
2094         }
2095
2096         num_sids = centry_uint32(centry);
2097         sids = talloc_array(mem_ctx, struct dom_sid, num_sids);
2098         if (sids == NULL) {
2099                 return NT_STATUS_NO_MEMORY;
2100         }
2101
2102         for (i=0; i<num_sids; i++) {
2103                 centry_sid(centry, &sids[i]);
2104         }
2105
2106         status = centry->status;
2107
2108         DEBUG(10,("lookup_usergroups: [Cached] - cached info for domain %s "
2109                   "status: %s\n", domain->name, nt_errstr(status)));
2110
2111         centry_free(centry);
2112
2113         *pnum_sids = num_sids;
2114         *psids = sids;
2115         return status;
2116 }
2117
2118 /* Lookup groups a user is a member of. */
2119 static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
2120                                   TALLOC_CTX *mem_ctx,
2121                                   const DOM_SID *user_sid,
2122                                   uint32 *num_groups, DOM_SID **user_gids)
2123 {
2124         struct cache_entry *centry = NULL;
2125         NTSTATUS status;
2126         unsigned int i;
2127         fstring sid_string;
2128
2129         status = wcache_lookup_usergroups(domain, mem_ctx, user_sid,
2130                                           num_groups, user_gids);
2131         if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2132                 return status;
2133         }
2134
2135         (*num_groups) = 0;
2136         (*user_gids) = NULL;
2137
2138         /* Return status value returned by seq number check */
2139
2140         if (!NT_STATUS_IS_OK(domain->last_status))
2141                 return domain->last_status;
2142
2143         DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info for domain %s\n",
2144                 domain->name ));
2145
2146         status = domain->backend->lookup_usergroups(domain, mem_ctx, user_sid, num_groups, user_gids);
2147
2148         if ( NT_STATUS_EQUAL(status, NT_STATUS_SYNCHRONIZATION_REQUIRED) )
2149                 goto skip_save;
2150
2151         /* and save it */
2152         refresh_sequence_number(domain, false);
2153         centry = centry_start(domain, status);
2154         if (!centry)
2155                 goto skip_save;
2156
2157         centry_put_uint32(centry, *num_groups);
2158         for (i=0; i<(*num_groups); i++) {
2159                 centry_put_sid(centry, &(*user_gids)[i]);
2160         }       
2161
2162         centry_end(centry, "UG/%s", sid_to_fstring(sid_string, user_sid));
2163         centry_free(centry);
2164
2165 skip_save:
2166         return status;
2167 }
2168
2169 static char *wcache_make_sidlist(TALLOC_CTX *mem_ctx, uint32_t num_sids,
2170                                  const struct dom_sid *sids)
2171 {
2172         uint32_t i;
2173         char *sidlist;
2174
2175         sidlist = talloc_strdup(mem_ctx, "");
2176         if (sidlist == NULL) {
2177                 return NULL;
2178         }
2179         for (i=0; i<num_sids; i++) {
2180                 fstring tmp;
2181                 sidlist = talloc_asprintf_append_buffer(
2182                         sidlist, "/%s", sid_to_fstring(tmp, &sids[i]));
2183                 if (sidlist == NULL) {
2184                         return NULL;
2185                 }
2186         }
2187         return sidlist;
2188 }
2189
2190 NTSTATUS wcache_lookup_useraliases(struct winbindd_domain *domain,
2191                                    TALLOC_CTX *mem_ctx, uint32_t num_sids,
2192                                    const struct dom_sid *sids,
2193                                    uint32_t *pnum_aliases, uint32_t **paliases)
2194 {
2195         struct winbind_cache *cache = get_cache(domain);
2196         struct cache_entry *centry = NULL;
2197         uint32_t num_aliases;
2198         uint32_t *aliases;
2199         NTSTATUS status;
2200         char *sidlist;
2201         int i;
2202
2203         if (cache->tdb == NULL) {
2204                 return NT_STATUS_NOT_FOUND;
2205         }
2206
2207         if (num_sids == 0) {
2208                 *pnum_aliases = 0;
2209                 *paliases = NULL;
2210                 return NT_STATUS_OK;
2211         }
2212
2213         /* We need to cache indexed by the whole list of SIDs, the aliases
2214          * resulting might come from any of the SIDs. */
2215
2216         sidlist = wcache_make_sidlist(talloc_tos(), num_sids, sids);
2217         if (sidlist == NULL) {
2218                 return NT_STATUS_NO_MEMORY;
2219         }
2220
2221         centry = wcache_fetch(cache, domain, "UA%s", sidlist);
2222         TALLOC_FREE(sidlist);
2223         if (centry == NULL) {
2224                 return NT_STATUS_NOT_FOUND;
2225         }
2226
2227         num_aliases = centry_uint32(centry);
2228         aliases = talloc_array(mem_ctx, uint32_t, num_aliases);
2229         if (aliases == NULL) {
2230                 centry_free(centry);
2231                 return NT_STATUS_NO_MEMORY;
2232         }
2233
2234         for (i=0; i<num_aliases; i++) {
2235                 aliases[i] = centry_uint32(centry);
2236         }
2237
2238         status = centry->status;
2239
2240         DEBUG(10,("lookup_useraliases: [Cached] - cached info for domain: %s "
2241                   "status %s\n", domain->name, nt_errstr(status)));
2242
2243         centry_free(centry);
2244
2245         *pnum_aliases = num_aliases;
2246         *paliases = aliases;
2247
2248         return status;
2249 }
2250
2251 static NTSTATUS lookup_useraliases(struct winbindd_domain *domain,
2252                                    TALLOC_CTX *mem_ctx,
2253                                    uint32 num_sids, const DOM_SID *sids,
2254                                    uint32 *num_aliases, uint32 **alias_rids)
2255 {
2256         struct cache_entry *centry = NULL;
2257         NTSTATUS status;
2258         char *sidlist;
2259         int i;
2260
2261         status = wcache_lookup_useraliases(domain, mem_ctx, num_sids, sids,
2262                                            num_aliases, alias_rids);
2263         if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2264                 return status;
2265         }
2266
2267         (*num_aliases) = 0;
2268         (*alias_rids) = NULL;
2269
2270         if (!NT_STATUS_IS_OK(domain->last_status))
2271                 return domain->last_status;
2272
2273         DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info "
2274                   "for domain %s\n", domain->name ));
2275
2276         sidlist = wcache_make_sidlist(talloc_tos(), num_sids, sids);
2277         if (sidlist == NULL) {
2278                 return NT_STATUS_NO_MEMORY;
2279         }
2280
2281         status = domain->backend->lookup_useraliases(domain, mem_ctx,
2282                                                      num_sids, sids,
2283                                                      num_aliases, alias_rids);
2284
2285         /* and save it */
2286         refresh_sequence_number(domain, false);
2287         centry = centry_start(domain, status);
2288         if (!centry)
2289                 goto skip_save;
2290         centry_put_uint32(centry, *num_aliases);
2291         for (i=0; i<(*num_aliases); i++)
2292                 centry_put_uint32(centry, (*alias_rids)[i]);
2293         centry_end(centry, "UA%s", sidlist);
2294         centry_free(centry);
2295
2296  skip_save:
2297         return status;
2298 }
2299
2300 NTSTATUS wcache_lookup_groupmem(struct winbindd_domain *domain,
2301                                 TALLOC_CTX *mem_ctx,
2302                                 const struct dom_sid *group_sid,
2303                                 uint32_t *num_names,
2304                                 struct dom_sid **sid_mem, char ***names,
2305                                 uint32_t **name_types)
2306 {
2307         struct winbind_cache *cache = get_cache(domain);
2308         struct cache_entry *centry = NULL;
2309         NTSTATUS status;
2310         unsigned int i;
2311         char *sid_string;
2312
2313         if (cache->tdb == NULL) {
2314                 return NT_STATUS_NOT_FOUND;
2315         }
2316
2317         sid_string = sid_string_tos(group_sid);
2318         if (sid_string == NULL) {
2319                 return NT_STATUS_NO_MEMORY;
2320         }
2321
2322         centry = wcache_fetch(cache, domain, "GM/%s", sid_string);
2323         TALLOC_FREE(sid_string);
2324         if (centry == NULL) {
2325                 return NT_STATUS_NOT_FOUND;
2326         }
2327
2328         *sid_mem = NULL;
2329         *names = NULL;
2330         *name_types = NULL;
2331
2332         *num_names = centry_uint32(centry);
2333         if (*num_names == 0) {
2334                 centry_free(centry);
2335                 return NT_STATUS_OK;
2336         }
2337
2338         *sid_mem = talloc_array(mem_ctx, DOM_SID, *num_names);
2339         *names = talloc_array(mem_ctx, char *, *num_names);
2340         *name_types = talloc_array(mem_ctx, uint32, *num_names);
2341
2342         if ((*sid_mem == NULL) || (*names == NULL) || (*name_types == NULL)) {
2343                 TALLOC_FREE(*sid_mem);
2344                 TALLOC_FREE(*names);
2345                 TALLOC_FREE(*name_types);
2346                 centry_free(centry);
2347                 return NT_STATUS_NO_MEMORY;
2348         }
2349
2350         for (i=0; i<(*num_names); i++) {
2351                 centry_sid(centry, &(*sid_mem)[i]);
2352                 (*names)[i] = centry_string(centry, mem_ctx);
2353                 (*name_types)[i] = centry_uint32(centry);
2354         }
2355
2356         status = centry->status;
2357
2358         DEBUG(10,("lookup_groupmem: [Cached] - cached info for domain %s "
2359                   "status: %s\n", domain->name, nt_errstr(status)));
2360
2361         centry_free(centry);
2362         return status;
2363 }
2364
2365 static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
2366                                 TALLOC_CTX *mem_ctx,
2367                                 const DOM_SID *group_sid, uint32 *num_names,
2368                                 DOM_SID **sid_mem, char ***names,
2369                                 uint32 **name_types)
2370 {
2371         struct cache_entry *centry = NULL;
2372         NTSTATUS status;
2373         unsigned int i;
2374         fstring sid_string;
2375
2376         status = wcache_lookup_groupmem(domain, mem_ctx, group_sid, num_names,
2377                                         sid_mem, names, name_types);
2378         if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2379                 return status;
2380         }
2381
2382         (*num_names) = 0;
2383         (*sid_mem) = NULL;
2384         (*names) = NULL;
2385         (*name_types) = NULL;
2386
2387         /* Return status value returned by seq number check */
2388
2389         if (!NT_STATUS_IS_OK(domain->last_status))
2390                 return domain->last_status;
2391
2392         DEBUG(10,("lookup_groupmem: [Cached] - doing backend query for info for domain %s\n",
2393                 domain->name ));
2394
2395         status = domain->backend->lookup_groupmem(domain, mem_ctx, group_sid, num_names, 
2396                                                   sid_mem, names, name_types);
2397
2398         /* and save it */
2399         refresh_sequence_number(domain, false);
2400         centry = centry_start(domain, status);
2401         if (!centry)
2402                 goto skip_save;
2403         centry_put_uint32(centry, *num_names);
2404         for (i=0; i<(*num_names); i++) {
2405                 centry_put_sid(centry, &(*sid_mem)[i]);
2406                 centry_put_string(centry, (*names)[i]);
2407                 centry_put_uint32(centry, (*name_types)[i]);
2408         }       
2409         centry_end(centry, "GM/%s", sid_to_fstring(sid_string, group_sid));
2410         centry_free(centry);
2411
2412 skip_save:
2413         return status;
2414 }
2415
2416 /* find the sequence number for a domain */
2417 static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
2418 {
2419         refresh_sequence_number(domain, false);
2420
2421         *seq = domain->sequence_number;
2422
2423         return NT_STATUS_OK;
2424 }
2425
2426 /* enumerate trusted domains 
2427  * (we need to have the list of trustdoms in the cache when we go offline) -
2428  * Guenther */
2429 static NTSTATUS trusted_domains(struct winbindd_domain *domain,
2430                                 TALLOC_CTX *mem_ctx,
2431                                 uint32 *num_domains,
2432                                 char ***names,
2433                                 char ***alt_names,
2434                                 DOM_SID **dom_sids)
2435 {
2436         struct winbind_cache *cache = get_cache(domain);
2437         struct cache_entry *centry = NULL;
2438         NTSTATUS status;
2439         int i;
2440
2441         if (!cache->tdb)
2442                 goto do_query;
2443
2444         centry = wcache_fetch(cache, domain, "TRUSTDOMS/%s", domain->name);
2445
2446         if (!centry) {
2447                 goto do_query;
2448         }
2449
2450         *num_domains = centry_uint32(centry);
2451
2452         if (*num_domains) {
2453                 (*names)        = TALLOC_ARRAY(mem_ctx, char *, *num_domains);
2454                 (*alt_names)    = TALLOC_ARRAY(mem_ctx, char *, *num_domains);
2455                 (*dom_sids)     = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_domains);
2456
2457                 if (! (*dom_sids) || ! (*names) || ! (*alt_names)) {
2458                         smb_panic_fn("trusted_domains out of memory");
2459                 }
2460         } else {
2461                 (*names) = NULL;
2462                 (*alt_names) = NULL;
2463                 (*dom_sids) = NULL;
2464         }
2465
2466         for (i=0; i<(*num_domains); i++) {
2467                 (*names)[i] = centry_string(centry, mem_ctx);
2468                 (*alt_names)[i] = centry_string(centry, mem_ctx);
2469                 if (!centry_sid(centry, &(*dom_sids)[i])) {
2470                         sid_copy(&(*dom_sids)[i], &global_sid_NULL);
2471                 }
2472         }
2473
2474         status = centry->status;
2475
2476         DEBUG(10,("trusted_domains: [Cached] - cached info for domain %s (%d trusts) status: %s\n",
2477                 domain->name, *num_domains, nt_errstr(status) ));
2478
2479         centry_free(centry);
2480         return status;
2481
2482 do_query:
2483         (*num_domains) = 0;
2484         (*dom_sids) = NULL;
2485         (*names) = NULL;
2486         (*alt_names) = NULL;
2487
2488         /* Return status value returned by seq number check */
2489
2490         if (!NT_STATUS_IS_OK(domain->last_status))
2491                 return domain->last_status;
2492
2493         DEBUG(10,("trusted_domains: [Cached] - doing backend query for info for domain %s\n",
2494                 domain->name ));
2495
2496         status = domain->backend->trusted_domains(domain, mem_ctx, num_domains,
2497                                                 names, alt_names, dom_sids);
2498
2499         /* no trusts gives NT_STATUS_NO_MORE_ENTRIES resetting to NT_STATUS_OK
2500          * so that the generic centry handling still applies correctly -
2501          * Guenther*/
2502
2503         if (!NT_STATUS_IS_ERR(status)) {
2504                 status = NT_STATUS_OK;
2505         }
2506
2507
2508 #if 0    /* Disabled as we want the trust dom list to be managed by
2509             the main parent and always to make the query.  --jerry */
2510
2511         /* and save it */
2512         refresh_sequence_number(domain, false);
2513
2514         centry = centry_start(domain, status);
2515         if (!centry)
2516                 goto skip_save;
2517
2518         centry_put_uint32(centry, *num_domains);
2519
2520         for (i=0; i<(*num_domains); i++) {
2521                 centry_put_string(centry, (*names)[i]);
2522                 centry_put_string(centry, (*alt_names)[i]);
2523                 centry_put_sid(centry, &(*dom_sids)[i]);
2524         }
2525
2526         centry_end(centry, "TRUSTDOMS/%s", domain->name);
2527
2528         centry_free(centry);
2529
2530 skip_save:
2531 #endif
2532
2533         return status;
2534 }       
2535
2536 /* get lockout policy */
2537 static NTSTATUS lockout_policy(struct winbindd_domain *domain,
2538                                TALLOC_CTX *mem_ctx,
2539                                struct samr_DomInfo12 *policy)
2540 {
2541         struct winbind_cache *cache = get_cache(domain);
2542         struct cache_entry *centry = NULL;
2543         NTSTATUS status;
2544
2545         if (!cache->tdb)
2546                 goto do_query;
2547
2548         centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
2549
2550         if (!centry)
2551                 goto do_query;
2552
2553         policy->lockout_duration = centry_nttime(centry);
2554         policy->lockout_window = centry_nttime(centry);
2555         policy->lockout_threshold = centry_uint16(centry);
2556
2557         status = centry->status;
2558
2559         DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2560                 domain->name, nt_errstr(status) ));
2561
2562         centry_free(centry);
2563         return status;
2564
2565 do_query:
2566         ZERO_STRUCTP(policy);
2567
2568         /* Return status value returned by seq number check */
2569
2570         if (!NT_STATUS_IS_OK(domain->last_status))
2571                 return domain->last_status;
2572
2573         DEBUG(10,("lockout_policy: [Cached] - doing backend query for info for domain %s\n",
2574                 domain->name ));
2575
2576         status = domain->backend->lockout_policy(domain, mem_ctx, policy);
2577
2578         /* and save it */
2579         refresh_sequence_number(domain, false);
2580         wcache_save_lockout_policy(domain, status, policy);
2581
2582         return status;
2583 }
2584
2585 /* get password policy */
2586 static NTSTATUS password_policy(struct winbindd_domain *domain,
2587                                 TALLOC_CTX *mem_ctx,
2588                                 struct samr_DomInfo1 *policy)
2589 {
2590         struct winbind_cache *cache = get_cache(domain);
2591         struct cache_entry *centry = NULL;
2592         NTSTATUS status;
2593
2594         if (!cache->tdb)
2595                 goto do_query;
2596
2597         centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
2598
2599         if (!centry)
2600                 goto do_query;
2601
2602         policy->min_password_length = centry_uint16(centry);
2603         policy->password_history_length = centry_uint16(centry);
2604         policy->password_properties = centry_uint32(centry);
2605         policy->max_password_age = centry_nttime(centry);
2606         policy->min_password_age = centry_nttime(centry);
2607
2608         status = centry->status;
2609
2610         DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2611                 domain->name, nt_errstr(status) ));
2612
2613         centry_free(centry);
2614         return status;
2615
2616 do_query:
2617         ZERO_STRUCTP(policy);
2618
2619         /* Return status value returned by seq number check */
2620
2621         if (!NT_STATUS_IS_OK(domain->last_status))
2622                 return domain->last_status;
2623
2624         DEBUG(10,("password_policy: [Cached] - doing backend query for info for domain %s\n",
2625                 domain->name ));
2626
2627         status = domain->backend->password_policy(domain, mem_ctx, policy);
2628
2629         /* and save it */
2630         refresh_sequence_number(domain, false);
2631         if (NT_STATUS_IS_OK(status)) {
2632                 wcache_save_password_policy(domain, status, policy);
2633         }
2634
2635         return status;
2636 }
2637
2638
2639 /* Invalidate cached user and group lists coherently */
2640
2641 static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, 
2642                        void *state)
2643 {
2644         if (strncmp((const char *)kbuf.dptr, "UL/", 3) == 0 ||
2645             strncmp((const char *)kbuf.dptr, "GL/", 3) == 0)
2646                 tdb_delete(the_tdb, kbuf);
2647
2648         return 0;
2649 }
2650
2651 /* Invalidate the getpwnam and getgroups entries for a winbindd domain */
2652
2653 void wcache_invalidate_samlogon(struct winbindd_domain *domain, 
2654                                 struct netr_SamInfo3 *info3)
2655 {
2656         DOM_SID sid;
2657         fstring key_str, sid_string;
2658         struct winbind_cache *cache;
2659
2660         /* dont clear cached U/SID and UG/SID entries when we want to logon
2661          * offline - gd */
2662
2663         if (lp_winbind_offline_logon()) {
2664                 return;
2665         }
2666
2667         if (!domain)
2668                 return;
2669
2670         cache = get_cache(domain);
2671
2672         if (!cache->tdb) {
2673                 return;
2674         }
2675
2676         sid_copy(&sid, info3->base.domain_sid);
2677         sid_append_rid(&sid, info3->base.rid);
2678
2679         /* Clear U/SID cache entry */
2680         fstr_sprintf(key_str, "U/%s", sid_to_fstring(sid_string, &sid));
2681         DEBUG(10, ("wcache_invalidate_samlogon: clearing %s\n", key_str));
2682         tdb_delete(cache->tdb, string_tdb_data(key_str));
2683
2684         /* Clear UG/SID cache entry */
2685         fstr_sprintf(key_str, "UG/%s", sid_to_fstring(sid_string, &sid));
2686         DEBUG(10, ("wcache_invalidate_samlogon: clearing %s\n", key_str));
2687         tdb_delete(cache->tdb, string_tdb_data(key_str));
2688
2689         /* Samba/winbindd never needs this. */
2690         netsamlogon_clear_cached_user(info3);
2691 }
2692
2693 bool wcache_invalidate_cache(void)
2694 {
2695         struct winbindd_domain *domain;
2696
2697         for (domain = domain_list(); domain; domain = domain->next) {
2698                 struct winbind_cache *cache = get_cache(domain);
2699
2700                 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
2701                            "entries for %s\n", domain->name));
2702                 if (cache) {
2703                         if (cache->tdb) {
2704                                 tdb_traverse(cache->tdb, traverse_fn, NULL);
2705                         } else {
2706                                 return false;
2707                         }
2708                 }
2709         }
2710         return true;
2711 }
2712
2713 bool init_wcache(void)
2714 {
2715         if (wcache == NULL) {
2716                 wcache = SMB_XMALLOC_P(struct winbind_cache);
2717                 ZERO_STRUCTP(wcache);
2718         }
2719
2720         if (wcache->tdb != NULL)
2721                 return true;
2722
2723         /* when working offline we must not clear the cache on restart */
2724         wcache->tdb = tdb_open_log(cache_path("winbindd_cache.tdb"),
2725                                 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE, 
2726                                 lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST), 
2727                                 O_RDWR|O_CREAT, 0600);
2728
2729         if (wcache->tdb == NULL) {
2730                 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
2731                 return false;
2732         }
2733
2734         return true;
2735 }
2736
2737 /************************************************************************
2738  This is called by the parent to initialize the cache file.
2739  We don't need sophisticated locking here as we know we're the
2740  only opener.
2741 ************************************************************************/
2742
2743 bool initialize_winbindd_cache(void)
2744 {
2745         bool cache_bad = true;
2746         uint32 vers;
2747
2748         if (!init_wcache()) {
2749                 DEBUG(0,("initialize_winbindd_cache: init_wcache failed.\n"));
2750                 return false;
2751         }
2752
2753         /* Check version number. */
2754         if (tdb_fetch_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, &vers) &&
2755                         vers == WINBINDD_CACHE_VERSION) {
2756                 cache_bad = false;
2757         }
2758
2759         if (cache_bad) {
2760                 DEBUG(0,("initialize_winbindd_cache: clearing cache "
2761                         "and re-creating with version number %d\n",
2762                         WINBINDD_CACHE_VERSION ));
2763
2764                 tdb_close(wcache->tdb);
2765                 wcache->tdb = NULL;
2766
2767                 if (unlink(cache_path("winbindd_cache.tdb")) == -1) {
2768                         DEBUG(0,("initialize_winbindd_cache: unlink %s failed %s ",
2769                                 cache_path("winbindd_cache.tdb"),
2770                                 strerror(errno) ));
2771                         return false;
2772                 }
2773                 if (!init_wcache()) {
2774                         DEBUG(0,("initialize_winbindd_cache: re-initialization "
2775                                         "init_wcache failed.\n"));
2776                         return false;
2777                 }
2778
2779                 /* Write the version. */
2780                 if (!tdb_store_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, WINBINDD_CACHE_VERSION)) {
2781                         DEBUG(0,("initialize_winbindd_cache: version number store failed %s\n",
2782                                 tdb_errorstr(wcache->tdb) ));
2783                         return false;
2784                 }
2785         }
2786
2787         tdb_close(wcache->tdb);
2788         wcache->tdb = NULL;
2789         return true;
2790 }
2791
2792 void close_winbindd_cache(void)
2793 {
2794         if (!wcache) {
2795                 return;
2796         }
2797         if (wcache->tdb) {
2798                 tdb_close(wcache->tdb);
2799                 wcache->tdb = NULL;
2800         }
2801 }
2802
2803 bool lookup_cached_sid(TALLOC_CTX *mem_ctx, const DOM_SID *sid,
2804                        char **domain_name, char **name,
2805                        enum lsa_SidType *type)
2806 {
2807         struct winbindd_domain *domain;
2808         NTSTATUS status;
2809
2810         domain = find_lookup_domain_from_sid(sid);
2811         if (domain == NULL) {
2812                 return false;
2813         }
2814         status = wcache_sid_to_name(domain, sid, mem_ctx, domain_name, name,
2815                                     type);
2816         return NT_STATUS_IS_OK(status);
2817 }
2818
2819 bool lookup_cached_name(TALLOC_CTX *mem_ctx,
2820                         const char *domain_name,
2821                         const char *name,
2822                         DOM_SID *sid,
2823                         enum lsa_SidType *type)
2824 {
2825         struct winbindd_domain *domain;
2826         NTSTATUS status;
2827         bool original_online_state;
2828
2829         domain = find_lookup_domain_from_name(domain_name);
2830         if (domain == NULL) {
2831                 return false;
2832         }
2833
2834         /* If we are doing a cached logon, temporarily set the domain
2835            offline so the cache won't expire the entry */
2836
2837         original_online_state = domain->online;
2838         domain->online = false;
2839         status = wcache_name_to_sid(domain, domain_name, name, sid, type);
2840         domain->online = original_online_state;
2841
2842         return NT_STATUS_IS_OK(status);
2843 }
2844
2845 void cache_name2sid(struct winbindd_domain *domain, 
2846                     const char *domain_name, const char *name,
2847                     enum lsa_SidType type, const DOM_SID *sid)
2848 {
2849         refresh_sequence_number(domain, false);
2850         wcache_save_name_to_sid(domain, NT_STATUS_OK, domain_name, name,
2851                                 sid, type);
2852 }
2853
2854 /*
2855  * The original idea that this cache only contains centries has
2856  * been blurred - now other stuff gets put in here. Ensure we
2857  * ignore these things on cleanup.
2858  */
2859
2860 static int traverse_fn_cleanup(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, 
2861                                TDB_DATA dbuf, void *state)
2862 {
2863         struct cache_entry *centry;
2864
2865         if (is_non_centry_key(kbuf)) {
2866                 return 0;
2867         }
2868
2869         centry = wcache_fetch_raw((char *)kbuf.dptr);
2870         if (!centry) {
2871                 return 0;
2872         }
2873
2874         if (!NT_STATUS_IS_OK(centry->status)) {
2875                 DEBUG(10,("deleting centry %s\n", (const char *)kbuf.dptr));
2876                 tdb_delete(the_tdb, kbuf);
2877         }
2878
2879         centry_free(centry);
2880         return 0;
2881 }
2882
2883 /* flush the cache */
2884 void wcache_flush_cache(void)
2885 {
2886         if (!wcache)
2887                 return;
2888         if (wcache->tdb) {
2889                 tdb_close(wcache->tdb);
2890                 wcache->tdb = NULL;
2891         }
2892         if (!winbindd_use_cache()) {
2893                 return;
2894         }
2895
2896         /* when working offline we must not clear the cache on restart */
2897         wcache->tdb = tdb_open_log(cache_path("winbindd_cache.tdb"),
2898                                 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE, 
2899                                 lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST), 
2900                                 O_RDWR|O_CREAT, 0600);
2901
2902         if (!wcache->tdb) {
2903                 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
2904                 return;
2905         }
2906
2907         tdb_traverse(wcache->tdb, traverse_fn_cleanup, NULL);
2908
2909         DEBUG(10,("wcache_flush_cache success\n"));
2910 }
2911
2912 /* Count cached creds */
2913
2914 static int traverse_fn_cached_creds(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, 
2915                                     void *state)
2916 {
2917         int *cred_count = (int*)state;
2918  
2919         if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
2920                 (*cred_count)++;
2921         }
2922         return 0;
2923 }
2924
2925 NTSTATUS wcache_count_cached_creds(struct winbindd_domain *domain, int *count)
2926 {
2927         struct winbind_cache *cache = get_cache(domain);
2928
2929         *count = 0;
2930
2931         if (!cache->tdb) {
2932                 return NT_STATUS_INTERNAL_DB_ERROR;
2933         }
2934  
2935         tdb_traverse(cache->tdb, traverse_fn_cached_creds, (void *)count);
2936
2937         return NT_STATUS_OK;
2938 }
2939
2940 struct cred_list {
2941         struct cred_list *prev, *next;
2942         TDB_DATA key;
2943         fstring name;
2944         time_t created;
2945 };
2946 static struct cred_list *wcache_cred_list;
2947
2948 static int traverse_fn_get_credlist(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, 
2949                                     void *state)
2950 {
2951         struct cred_list *cred;
2952
2953         if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
2954
2955                 cred = SMB_MALLOC_P(struct cred_list);
2956                 if (cred == NULL) {
2957                         DEBUG(0,("traverse_fn_remove_first_creds: failed to malloc new entry for list\n"));
2958                         return -1;
2959                 }
2960
2961                 ZERO_STRUCTP(cred);
2962
2963                 /* save a copy of the key */
2964
2965                 fstrcpy(cred->name, (const char *)kbuf.dptr);           
2966                 DLIST_ADD(wcache_cred_list, cred);
2967         }
2968
2969         return 0;
2970 }
2971
2972 NTSTATUS wcache_remove_oldest_cached_creds(struct winbindd_domain *domain, const DOM_SID *sid) 
2973 {
2974         struct winbind_cache *cache = get_cache(domain);
2975         NTSTATUS status;
2976         int ret;
2977         struct cred_list *cred, *oldest = NULL;
2978
2979         if (!cache->tdb) {
2980                 return NT_STATUS_INTERNAL_DB_ERROR;
2981         }
2982
2983         /* we possibly already have an entry */
2984         if (sid && NT_STATUS_IS_OK(wcache_cached_creds_exist(domain, sid))) {
2985
2986                 fstring key_str, tmp;
2987
2988                 DEBUG(11,("we already have an entry, deleting that\n"));
2989
2990                 fstr_sprintf(key_str, "CRED/%s", sid_to_fstring(tmp, sid));
2991
2992                 tdb_delete(cache->tdb, string_tdb_data(key_str));
2993
2994                 return NT_STATUS_OK;
2995         }
2996
2997         ret = tdb_traverse(cache->tdb, traverse_fn_get_credlist, NULL);
2998         if (ret == 0) {
2999                 return NT_STATUS_OK;
3000         } else if ((ret == -1) || (wcache_cred_list == NULL)) {
3001                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
3002         }
3003
3004         ZERO_STRUCTP(oldest);
3005
3006         for (cred = wcache_cred_list; cred; cred = cred->next) {
3007
3008                 TDB_DATA data;
3009                 time_t t;
3010
3011                 data = tdb_fetch(cache->tdb, string_tdb_data(cred->name));
3012                 if (!data.dptr) {
3013                         DEBUG(10,("wcache_remove_oldest_cached_creds: entry for [%s] not found\n", 
3014                                 cred->name));
3015                         status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
3016                         goto done;
3017                 }
3018
3019                 t = IVAL(data.dptr, 0);
3020                 SAFE_FREE(data.dptr);
3021
3022                 if (!oldest) {
3023                         oldest = SMB_MALLOC_P(struct cred_list);
3024                         if (oldest == NULL) {
3025                                 status = NT_STATUS_NO_MEMORY;
3026                                 goto done;
3027                         }
3028
3029                         fstrcpy(oldest->name, cred->name);
3030                         oldest->created = t;
3031                         continue;
3032                 }
3033
3034                 if (t < oldest->created) {
3035                         fstrcpy(oldest->name, cred->name);
3036                         oldest->created = t;
3037                 }
3038         }
3039
3040         if (tdb_delete(cache->tdb, string_tdb_data(oldest->name)) == 0) {
3041                 status = NT_STATUS_OK;
3042         } else {
3043                 status = NT_STATUS_UNSUCCESSFUL;
3044         }
3045 done:
3046         SAFE_FREE(wcache_cred_list);
3047         SAFE_FREE(oldest);
3048
3049         return status;
3050 }
3051
3052 /* Change the global online/offline state. */
3053 bool set_global_winbindd_state_offline(void)
3054 {
3055         TDB_DATA data;
3056
3057         DEBUG(10,("set_global_winbindd_state_offline: offline requested.\n"));
3058
3059         /* Only go offline if someone has created
3060            the key "WINBINDD_OFFLINE" in the cache tdb. */
3061
3062         if (wcache == NULL || wcache->tdb == NULL) {
3063                 DEBUG(10,("set_global_winbindd_state_offline: wcache not open yet.\n"));
3064                 return false;
3065         }
3066
3067         if (!lp_winbind_offline_logon()) {
3068                 DEBUG(10,("set_global_winbindd_state_offline: rejecting.\n"));
3069                 return false;
3070         }
3071
3072         if (global_winbindd_offline_state) {
3073                 /* Already offline. */
3074                 return true;
3075         }
3076
3077         data = tdb_fetch_bystring( wcache->tdb, "WINBINDD_OFFLINE" );
3078
3079         if (!data.dptr || data.dsize != 4) {
3080                 DEBUG(10,("set_global_winbindd_state_offline: offline state not set.\n"));
3081                 SAFE_FREE(data.dptr);
3082                 return false;
3083         } else {
3084                 DEBUG(10,("set_global_winbindd_state_offline: offline state set.\n"));
3085                 global_winbindd_offline_state = true;
3086                 SAFE_FREE(data.dptr);
3087                 return true;
3088         }
3089 }
3090
3091 void set_global_winbindd_state_online(void)
3092 {
3093         DEBUG(10,("set_global_winbindd_state_online: online requested.\n"));
3094
3095         if (!lp_winbind_offline_logon()) {
3096                 DEBUG(10,("set_global_winbindd_state_online: rejecting.\n"));
3097                 return;
3098         }
3099
3100         if (!global_winbindd_offline_state) {
3101                 /* Already online. */
3102                 return;
3103         }
3104         global_winbindd_offline_state = false;
3105
3106         if (!wcache->tdb) {
3107                 return;
3108         }
3109
3110         /* Ensure there is no key "WINBINDD_OFFLINE" in the cache tdb. */
3111         tdb_delete_bystring(wcache->tdb, "WINBINDD_OFFLINE");
3112 }
3113
3114 bool get_global_winbindd_state_offline(void)
3115 {
3116         return global_winbindd_offline_state;
3117 }
3118
3119 /***********************************************************************
3120  Validate functions for all possible cache tdb keys.
3121 ***********************************************************************/
3122
3123 static struct cache_entry *create_centry_validate(const char *kstr, TDB_DATA data, 
3124                                                   struct tdb_validation_status *state)
3125 {
3126         struct cache_entry *centry;
3127
3128         centry = SMB_XMALLOC_P(struct cache_entry);
3129         centry->data = (unsigned char *)memdup(data.dptr, data.dsize);
3130         if (!centry->data) {
3131                 SAFE_FREE(centry);
3132                 return NULL;
3133         }
3134         centry->len = data.dsize;
3135         centry->ofs = 0;
3136
3137         if (centry->len < 8) {
3138                 /* huh? corrupt cache? */
3139                 DEBUG(0,("create_centry_validate: Corrupt cache for key %s (len < 8) ?\n", kstr));
3140                 centry_free(centry);
3141                 state->bad_entry = true;
3142                 state->success = false;
3143                 return NULL;
3144         }
3145
3146         centry->status = NT_STATUS(centry_uint32(centry));
3147         centry->sequence_number = centry_uint32(centry);
3148         return centry;
3149 }