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