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