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