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