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