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