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