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