s3: Call sid_check_is_domain instead of dom_sid_equal
[kai/samba.git] / source3 / winbindd / winbindd_cache.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    Winbind cache backend functions
5
6    Copyright (C) Andrew Tridgell 2001
7    Copyright (C) Gerald Carter   2003-2007
8    Copyright (C) Volker Lendecke 2005
9    Copyright (C) Guenther Deschner 2005
10    Copyright (C) Michael Adam    2007
11
12    This program is free software; you can redistribute it and/or modify
13    it under the terms of the GNU General Public License as published by
14    the Free Software Foundation; either version 3 of the License, or
15    (at your option) any later version.
16
17    This program is distributed in the hope that it will be useful,
18    but WITHOUT ANY WARRANTY; without even the implied warranty of
19    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20    GNU General Public License for more details.
21
22    You should have received a copy of the GNU General Public License
23    along with this program.  If not, see <http://www.gnu.org/licenses/>.
24 */
25
26 #include "includes.h"
27 #include "winbindd.h"
28 #include "tdb_validate.h"
29 #include "../libcli/auth/libcli_auth.h"
30 #include "../librpc/gen_ndr/ndr_wbint.h"
31 #include "ads.h"
32 #include "nss_info.h"
33 #include "../libcli/security/security.h"
34
35 #undef DBGC_CLASS
36 #define DBGC_CLASS DBGC_WINBIND
37
38 #define WINBINDD_CACHE_VERSION 2
39 #define WINBINDD_CACHE_VERSION_KEYSTR "WINBINDD_CACHE_VERSION"
40
41 extern struct winbindd_methods reconnect_methods;
42 #ifdef HAVE_ADS
43 extern struct winbindd_methods ads_methods;
44 #endif
45 extern struct winbindd_methods builtin_passdb_methods;
46 extern struct winbindd_methods sam_passdb_methods;
47
48 /*
49  * JRA. KEEP THIS LIST UP TO DATE IF YOU ADD CACHE ENTRIES.
50  * Here are the list of entry types that are *not* stored
51  * as form struct cache_entry in the cache.
52  */
53
54 static const char *non_centry_keys[] = {
55         "SEQNUM/",
56         "DR/",
57         "DE/",
58         "WINBINDD_OFFLINE",
59         WINBINDD_CACHE_VERSION_KEYSTR,
60         NULL
61 };
62
63 /************************************************************************
64  Is this key a non-centry type ?
65 ************************************************************************/
66
67 static bool is_non_centry_key(TDB_DATA kbuf)
68 {
69         int i;
70
71         if (kbuf.dptr == NULL || kbuf.dsize == 0) {
72                 return false;
73         }
74         for (i = 0; non_centry_keys[i] != NULL; i++) {
75                 size_t namelen = strlen(non_centry_keys[i]);
76                 if (kbuf.dsize < namelen) {
77                         continue;
78                 }
79                 if (strncmp(non_centry_keys[i], (const char *)kbuf.dptr, namelen) == 0) {
80                         return true;
81                 }
82         }
83         return false;
84 }
85
86 /* Global online/offline state - False when online. winbindd starts up online
87    and sets this to true if the first query fails and there's an entry in
88    the cache tdb telling us to stay offline. */
89
90 static bool global_winbindd_offline_state;
91
92 struct winbind_cache {
93         TDB_CONTEXT *tdb;
94 };
95
96 struct cache_entry {
97         NTSTATUS status;
98         uint32 sequence_number;
99         uint64_t timeout;
100         uint8 *data;
101         uint32 len, ofs;
102 };
103
104 void (*smb_panic_fn)(const char *const why) = smb_panic;
105
106 #define WINBINDD_MAX_CACHE_SIZE (50*1024*1024)
107
108 static struct winbind_cache *wcache;
109
110 /* get the winbind_cache structure */
111 static struct winbind_cache *get_cache(struct winbindd_domain *domain)
112 {
113         struct winbind_cache *ret = wcache;
114
115         /* We have to know what type of domain we are dealing with first. */
116
117         if (domain->internal) {
118                 domain->backend = &builtin_passdb_methods;
119                 domain->initialized = True;
120         }
121
122         if (strequal(domain->name, get_global_sam_name()) &&
123             sid_check_is_domain(&domain->sid)) {
124                 domain->backend = &sam_passdb_methods;
125                 domain->initialized = True;
126         }
127
128         if ( !domain->initialized ) {
129                 init_dc_connection( domain );
130         }
131
132         /* 
133            OK.  listen up becasue I'm only going to say this once.
134            We have the following scenarios to consider
135            (a) trusted AD domains on a Samba DC,
136            (b) trusted AD domains and we are joined to a non-kerberos domain
137            (c) trusted AD domains and we are joined to a kerberos (AD) domain
138
139            For (a) we can always contact the trusted domain using krb5 
140            since we have the domain trust account password
141
142            For (b) we can only use RPC since we have no way of 
143            getting a krb5 ticket in our own domain
144
145            For (c) we can always use krb5 since we have a kerberos trust
146
147            --jerry
148          */
149
150         if (!domain->backend) {
151 #ifdef HAVE_ADS
152                 struct winbindd_domain *our_domain = domain;
153
154                 /* find our domain first so we can figure out if we 
155                    are joined to a kerberized domain */
156
157                 if ( !domain->primary )
158                         our_domain = find_our_domain();
159
160                 if ((our_domain->active_directory || IS_DC)
161                     && domain->active_directory
162                     && !lp_winbind_rpc_only()) {
163                         DEBUG(5,("get_cache: Setting ADS methods for domain %s\n", domain->name));
164                         domain->backend = &ads_methods;
165                 } else {
166 #endif  /* HAVE_ADS */
167                         DEBUG(5,("get_cache: Setting MS-RPC methods for domain %s\n", domain->name));
168                         domain->backend = &reconnect_methods;
169 #ifdef HAVE_ADS
170                 }
171 #endif  /* HAVE_ADS */
172         }
173
174         if (ret)
175                 return ret;
176
177         ret = SMB_XMALLOC_P(struct winbind_cache);
178         ZERO_STRUCTP(ret);
179
180         wcache = ret;
181         wcache_flush_cache();
182
183         return ret;
184 }
185
186 /*
187   free a centry structure
188 */
189 static void centry_free(struct cache_entry *centry)
190 {
191         if (!centry)
192                 return;
193         SAFE_FREE(centry->data);
194         free(centry);
195 }
196
197 static bool centry_check_bytes(struct cache_entry *centry, size_t nbytes)
198 {
199         if (centry->len - centry->ofs < nbytes) {
200                 DEBUG(0,("centry corruption? needed %u bytes, have %d\n", 
201                          (unsigned int)nbytes,
202                          centry->len - centry->ofs));
203                 return false;
204         }
205         return true;
206 }
207
208 /*
209   pull a uint64_t from a cache entry
210 */
211 static uint64_t centry_uint64_t(struct cache_entry *centry)
212 {
213         uint64_t ret;
214
215         if (!centry_check_bytes(centry, 8)) {
216                 smb_panic_fn("centry_uint64_t");
217         }
218         ret = BVAL(centry->data, centry->ofs);
219         centry->ofs += 8;
220         return ret;
221 }
222
223 /*
224   pull a uint32 from a cache entry 
225 */
226 static uint32 centry_uint32(struct cache_entry *centry)
227 {
228         uint32 ret;
229
230         if (!centry_check_bytes(centry, 4)) {
231                 smb_panic_fn("centry_uint32");
232         }
233         ret = IVAL(centry->data, centry->ofs);
234         centry->ofs += 4;
235         return ret;
236 }
237
238 /*
239   pull a uint16 from a cache entry 
240 */
241 static uint16 centry_uint16(struct cache_entry *centry)
242 {
243         uint16 ret;
244         if (!centry_check_bytes(centry, 2)) {
245                 smb_panic_fn("centry_uint16");
246         }
247         ret = CVAL(centry->data, centry->ofs);
248         centry->ofs += 2;
249         return ret;
250 }
251
252 /*
253   pull a uint8 from a cache entry 
254 */
255 static uint8 centry_uint8(struct cache_entry *centry)
256 {
257         uint8 ret;
258         if (!centry_check_bytes(centry, 1)) {
259                 smb_panic_fn("centry_uint8");
260         }
261         ret = CVAL(centry->data, centry->ofs);
262         centry->ofs += 1;
263         return ret;
264 }
265
266 /*
267   pull a NTTIME from a cache entry 
268 */
269 static NTTIME centry_nttime(struct cache_entry *centry)
270 {
271         NTTIME ret;
272         if (!centry_check_bytes(centry, 8)) {
273                 smb_panic_fn("centry_nttime");
274         }
275         ret = IVAL(centry->data, centry->ofs);
276         centry->ofs += 4;
277         ret += (uint64)IVAL(centry->data, centry->ofs) << 32;
278         centry->ofs += 4;
279         return ret;
280 }
281
282 /*
283   pull a time_t from a cache entry. time_t stored portably as a 64-bit time.
284 */
285 static time_t centry_time(struct cache_entry *centry)
286 {
287         return (time_t)centry_nttime(centry);
288 }
289
290 /* pull a string from a cache entry, using the supplied
291    talloc context 
292 */
293 static char *centry_string(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
294 {
295         uint32 len;
296         char *ret;
297
298         len = centry_uint8(centry);
299
300         if (len == 0xFF) {
301                 /* a deliberate NULL string */
302                 return NULL;
303         }
304
305         if (!centry_check_bytes(centry, (size_t)len)) {
306                 smb_panic_fn("centry_string");
307         }
308
309         ret = TALLOC_ARRAY(mem_ctx, char, len+1);
310         if (!ret) {
311                 smb_panic_fn("centry_string out of memory\n");
312         }
313         memcpy(ret,centry->data + centry->ofs, len);
314         ret[len] = 0;
315         centry->ofs += len;
316         return ret;
317 }
318
319 /* pull a hash16 from a cache entry, using the supplied
320    talloc context 
321 */
322 static char *centry_hash16(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
323 {
324         uint32 len;
325         char *ret;
326
327         len = centry_uint8(centry);
328
329         if (len != 16) {
330                 DEBUG(0,("centry corruption? hash len (%u) != 16\n", 
331                         len ));
332                 return NULL;
333         }
334
335         if (!centry_check_bytes(centry, 16)) {
336                 return NULL;
337         }
338
339         ret = TALLOC_ARRAY(mem_ctx, char, 16);
340         if (!ret) {
341                 smb_panic_fn("centry_hash out of memory\n");
342         }
343         memcpy(ret,centry->data + centry->ofs, 16);
344         centry->ofs += 16;
345         return ret;
346 }
347
348 /* pull a sid from a cache entry, using the supplied
349    talloc context 
350 */
351 static bool centry_sid(struct cache_entry *centry, struct dom_sid *sid)
352 {
353         char *sid_string;
354         bool ret;
355
356         sid_string = centry_string(centry, talloc_tos());
357         if (sid_string == NULL) {
358                 return false;
359         }
360         ret = string_to_sid(sid, sid_string);
361         TALLOC_FREE(sid_string);
362         return ret;
363 }
364
365
366 /*
367   pull a NTSTATUS from a cache entry
368 */
369 static NTSTATUS centry_ntstatus(struct cache_entry *centry)
370 {
371         NTSTATUS status;
372
373         status = NT_STATUS(centry_uint32(centry));
374         return status;
375 }
376
377
378 /* the server is considered down if it can't give us a sequence number */
379 static bool wcache_server_down(struct winbindd_domain *domain)
380 {
381         bool ret;
382
383         if (!wcache->tdb)
384                 return false;
385
386         ret = (domain->sequence_number == DOM_SEQUENCE_NONE);
387
388         if (ret)
389                 DEBUG(10,("wcache_server_down: server for Domain %s down\n", 
390                         domain->name ));
391         return ret;
392 }
393
394 static bool wcache_fetch_seqnum(const char *domain_name, uint32_t *seqnum,
395                                 uint32_t *last_seq_check)
396 {
397         char *key;
398         TDB_DATA data;
399
400         if (wcache->tdb == NULL) {
401                 DEBUG(10,("wcache_fetch_seqnum: tdb == NULL\n"));
402                 return false;
403         }
404
405         key = talloc_asprintf(talloc_tos(), "SEQNUM/%s", domain_name);
406         if (key == NULL) {
407                 DEBUG(10, ("talloc failed\n"));
408                 return false;
409         }
410
411         data = tdb_fetch_bystring(wcache->tdb, key);
412         TALLOC_FREE(key);
413
414         if (data.dptr == NULL) {
415                 DEBUG(10, ("wcache_fetch_seqnum: %s not found\n",
416                            domain_name));
417                 return false;
418         }
419         if (data.dsize != 8) {
420                 DEBUG(10, ("wcache_fetch_seqnum: invalid data size %d\n",
421                            (int)data.dsize));
422                 SAFE_FREE(data.dptr);
423                 return false;
424         }
425
426         *seqnum = IVAL(data.dptr, 0);
427         *last_seq_check = IVAL(data.dptr, 4);
428         SAFE_FREE(data.dptr);
429
430         return true;
431 }
432
433 static NTSTATUS fetch_cache_seqnum( struct winbindd_domain *domain, time_t now )
434 {
435         uint32 last_check, time_diff;
436
437         if (!wcache_fetch_seqnum(domain->name, &domain->sequence_number,
438                                  &last_check)) {
439                 return NT_STATUS_UNSUCCESSFUL;
440         }
441         domain->last_seq_check = last_check;
442
443         /* have we expired? */
444
445         time_diff = now - domain->last_seq_check;
446         if ( time_diff > lp_winbind_cache_time() ) {
447                 DEBUG(10,("fetch_cache_seqnum: timeout [%s][%u @ %u]\n",
448                         domain->name, domain->sequence_number,
449                         (uint32)domain->last_seq_check));
450                 return NT_STATUS_UNSUCCESSFUL;
451         }
452
453         DEBUG(10,("fetch_cache_seqnum: success [%s][%u @ %u]\n", 
454                 domain->name, domain->sequence_number, 
455                 (uint32)domain->last_seq_check));
456
457         return NT_STATUS_OK;
458 }
459
460 bool wcache_store_seqnum(const char *domain_name, uint32_t seqnum,
461                          time_t last_seq_check)
462 {
463         char *key_str;
464         uint8_t buf[8];
465         int ret;
466
467         if (wcache->tdb == NULL) {
468                 DEBUG(10, ("wcache_store_seqnum: wcache->tdb == NULL\n"));
469                 return false;
470         }
471
472         key_str = talloc_asprintf(talloc_tos(), "SEQNUM/%s", domain_name);
473         if (key_str == NULL) {
474                 DEBUG(10, ("talloc_asprintf failed\n"));
475                 return false;
476         }
477
478         SIVAL(buf, 0, seqnum);
479         SIVAL(buf, 4, last_seq_check);
480
481         ret = tdb_store_bystring(wcache->tdb, key_str,
482                                  make_tdb_data(buf, sizeof(buf)), TDB_REPLACE);
483         TALLOC_FREE(key_str);
484         if (ret == -1) {
485                 DEBUG(10, ("tdb_store_bystring failed: %s\n",
486                            tdb_errorstr(wcache->tdb)));
487                 TALLOC_FREE(key_str);
488                 return false;
489         }
490
491         DEBUG(10, ("wcache_store_seqnum: success [%s][%u @ %u]\n",
492                    domain_name, seqnum, (unsigned)last_seq_check));
493
494         return true;
495 }
496
497 static bool store_cache_seqnum( struct winbindd_domain *domain )
498 {
499         return wcache_store_seqnum(domain->name, domain->sequence_number,
500                                    domain->last_seq_check);
501 }
502
503 /*
504   refresh the domain sequence number. If force is true
505   then always refresh it, no matter how recently we fetched it
506 */
507
508 static void refresh_sequence_number(struct winbindd_domain *domain, bool force)
509 {
510         NTSTATUS status;
511         unsigned time_diff;
512         time_t t = time(NULL);
513         unsigned cache_time = lp_winbind_cache_time();
514
515         if (is_domain_offline(domain)) {
516                 return;
517         }
518
519         get_cache( domain );
520
521 #if 0   /* JERRY -- disable as the default cache time is now 5 minutes */
522         /* trying to reconnect is expensive, don't do it too often */
523         if (domain->sequence_number == DOM_SEQUENCE_NONE) {
524                 cache_time *= 8;
525         }
526 #endif
527
528         time_diff = t - domain->last_seq_check;
529
530         /* see if we have to refetch the domain sequence number */
531         if (!force && (time_diff < cache_time) &&
532                         (domain->sequence_number != DOM_SEQUENCE_NONE) &&
533                         NT_STATUS_IS_OK(domain->last_status)) {
534                 DEBUG(10, ("refresh_sequence_number: %s time ok\n", domain->name));
535                 goto done;
536         }
537
538         /* try to get the sequence number from the tdb cache first */
539         /* this will update the timestamp as well */
540
541         status = fetch_cache_seqnum( domain, t );
542         if (NT_STATUS_IS_OK(status) &&
543                         (domain->sequence_number != DOM_SEQUENCE_NONE) &&
544                         NT_STATUS_IS_OK(domain->last_status)) {
545                 goto done;
546         }
547
548         /* important! make sure that we know if this is a native 
549            mode domain or not.  And that we can contact it. */
550
551         if ( winbindd_can_contact_domain( domain ) ) {          
552                 status = domain->backend->sequence_number(domain, 
553                                                           &domain->sequence_number);
554         } else {
555                 /* just use the current time */
556                 status = NT_STATUS_OK;
557                 domain->sequence_number = time(NULL);
558         }
559
560
561         /* the above call could have set our domain->backend to NULL when
562          * coming from offline to online mode, make sure to reinitialize the
563          * backend - Guenther */
564         get_cache( domain );
565
566         if (!NT_STATUS_IS_OK(status)) {
567                 DEBUG(10,("refresh_sequence_number: failed with %s\n", nt_errstr(status)));
568                 domain->sequence_number = DOM_SEQUENCE_NONE;
569         }
570
571         domain->last_status = status;
572         domain->last_seq_check = time(NULL);
573
574         /* save the new sequence number in the cache */
575         store_cache_seqnum( domain );
576
577 done:
578         DEBUG(10, ("refresh_sequence_number: %s seq number is now %d\n", 
579                    domain->name, domain->sequence_number));
580
581         return;
582 }
583
584 /*
585   decide if a cache entry has expired
586 */
587 static bool centry_expired(struct winbindd_domain *domain, const char *keystr, struct cache_entry *centry)
588 {
589         /* If we've been told to be offline - stay in that state... */
590         if (lp_winbind_offline_logon() && global_winbindd_offline_state) {
591                 DEBUG(10,("centry_expired: Key %s for domain %s valid as winbindd is globally offline.\n",
592                         keystr, domain->name ));
593                 return false;
594         }
595
596         /* when the domain is offline return the cached entry.
597          * This deals with transient offline states... */
598
599         if (!domain->online) {
600                 DEBUG(10,("centry_expired: Key %s for domain %s valid as domain is offline.\n",
601                         keystr, domain->name ));
602                 return false;
603         }
604
605         /* if the server is OK and our cache entry came from when it was down then
606            the entry is invalid */
607         if ((domain->sequence_number != DOM_SEQUENCE_NONE) &&  
608             (centry->sequence_number == DOM_SEQUENCE_NONE)) {
609                 DEBUG(10,("centry_expired: Key %s for domain %s invalid sequence.\n",
610                         keystr, domain->name ));
611                 return true;
612         }
613
614         /* if the server is down or the cache entry is not older than the
615            current sequence number or it did not timeout then it is OK */
616         if (wcache_server_down(domain)
617             || ((centry->sequence_number == domain->sequence_number)
618                 && (centry->timeout > time(NULL)))) {
619                 DEBUG(10,("centry_expired: Key %s for domain %s is good.\n",
620                         keystr, domain->name ));
621                 return false;
622         }
623
624         DEBUG(10,("centry_expired: Key %s for domain %s expired\n",
625                 keystr, domain->name ));
626
627         /* it's expired */
628         return true;
629 }
630
631 static struct cache_entry *wcache_fetch_raw(char *kstr)
632 {
633         TDB_DATA data;
634         struct cache_entry *centry;
635         TDB_DATA key;
636
637         key = string_tdb_data(kstr);
638         data = tdb_fetch(wcache->tdb, key);
639         if (!data.dptr) {
640                 /* a cache miss */
641                 return NULL;
642         }
643
644         centry = SMB_XMALLOC_P(struct cache_entry);
645         centry->data = (unsigned char *)data.dptr;
646         centry->len = data.dsize;
647         centry->ofs = 0;
648
649         if (centry->len < 16) {
650                 /* huh? corrupt cache? */
651                 DEBUG(10,("wcache_fetch_raw: Corrupt cache for key %s "
652                           "(len < 16)?\n", kstr));
653                 centry_free(centry);
654                 return NULL;
655         }
656
657         centry->status = centry_ntstatus(centry);
658         centry->sequence_number = centry_uint32(centry);
659         centry->timeout = centry_uint64_t(centry);
660
661         return centry;
662 }
663
664 static bool is_my_own_sam_domain(struct winbindd_domain *domain)
665 {
666         if (strequal(domain->name, get_global_sam_name()) &&
667              dom_sid_equal(&domain->sid, get_global_sam_sid())) {
668                 return true;
669         }
670
671         return false;
672 }
673
674 static bool is_builtin_domain(struct winbindd_domain *domain)
675 {
676         if (strequal(domain->name, "BUILTIN") &&
677             dom_sid_equal(&domain->sid, &global_sid_Builtin)) {
678                 return true;
679         }
680
681         return false;
682 }
683
684 /*
685   fetch an entry from the cache, with a varargs key. auto-fetch the sequence
686   number and return status
687 */
688 static struct cache_entry *wcache_fetch(struct winbind_cache *cache, 
689                                         struct winbindd_domain *domain,
690                                         const char *format, ...) PRINTF_ATTRIBUTE(3,4);
691 static struct cache_entry *wcache_fetch(struct winbind_cache *cache, 
692                                         struct winbindd_domain *domain,
693                                         const char *format, ...)
694 {
695         va_list ap;
696         char *kstr;
697         struct cache_entry *centry;
698
699         if (!winbindd_use_cache() ||
700             is_my_own_sam_domain(domain) ||
701             is_builtin_domain(domain)) {
702                 return NULL;
703         }
704
705         refresh_sequence_number(domain, false);
706
707         va_start(ap, format);
708         smb_xvasprintf(&kstr, format, ap);
709         va_end(ap);
710
711         centry = wcache_fetch_raw(kstr);
712         if (centry == NULL) {
713                 free(kstr);
714                 return NULL;
715         }
716
717         if (centry_expired(domain, kstr, centry)) {
718
719                 DEBUG(10,("wcache_fetch: entry %s expired for domain %s\n",
720                          kstr, domain->name ));
721
722                 centry_free(centry);
723                 free(kstr);
724                 return NULL;
725         }
726
727         DEBUG(10,("wcache_fetch: returning entry %s for domain %s\n",
728                  kstr, domain->name ));
729
730         free(kstr);
731         return centry;
732 }
733
734 static void wcache_delete(const char *format, ...) PRINTF_ATTRIBUTE(1,2);
735 static void wcache_delete(const char *format, ...)
736 {
737         va_list ap;
738         char *kstr;
739         TDB_DATA key;
740
741         va_start(ap, format);
742         smb_xvasprintf(&kstr, format, ap);
743         va_end(ap);
744
745         key = string_tdb_data(kstr);
746
747         tdb_delete(wcache->tdb, key);
748         free(kstr);
749 }
750
751 /*
752   make sure we have at least len bytes available in a centry 
753 */
754 static void centry_expand(struct cache_entry *centry, uint32 len)
755 {
756         if (centry->len - centry->ofs >= len)
757                 return;
758         centry->len *= 2;
759         centry->data = SMB_REALLOC_ARRAY(centry->data, unsigned char,
760                                          centry->len);
761         if (!centry->data) {
762                 DEBUG(0,("out of memory: needed %d bytes in centry_expand\n", centry->len));
763                 smb_panic_fn("out of memory in centry_expand");
764         }
765 }
766
767 /*
768   push a uint64_t into a centry
769 */
770 static void centry_put_uint64_t(struct cache_entry *centry, uint64_t v)
771 {
772         centry_expand(centry, 8);
773         SBVAL(centry->data, centry->ofs, v);
774         centry->ofs += 8;
775 }
776
777 /*
778   push a uint32 into a centry 
779 */
780 static void centry_put_uint32(struct cache_entry *centry, uint32 v)
781 {
782         centry_expand(centry, 4);
783         SIVAL(centry->data, centry->ofs, v);
784         centry->ofs += 4;
785 }
786
787 /*
788   push a uint16 into a centry 
789 */
790 static void centry_put_uint16(struct cache_entry *centry, uint16 v)
791 {
792         centry_expand(centry, 2);
793         SIVAL(centry->data, centry->ofs, v);
794         centry->ofs += 2;
795 }
796
797 /*
798   push a uint8 into a centry 
799 */
800 static void centry_put_uint8(struct cache_entry *centry, uint8 v)
801 {
802         centry_expand(centry, 1);
803         SCVAL(centry->data, centry->ofs, v);
804         centry->ofs += 1;
805 }
806
807 /* 
808    push a string into a centry 
809  */
810 static void centry_put_string(struct cache_entry *centry, const char *s)
811 {
812         int len;
813
814         if (!s) {
815                 /* null strings are marked as len 0xFFFF */
816                 centry_put_uint8(centry, 0xFF);
817                 return;
818         }
819
820         len = strlen(s);
821         /* can't handle more than 254 char strings. Truncating is probably best */
822         if (len > 254) {
823                 DEBUG(10,("centry_put_string: truncating len (%d) to: 254\n", len));
824                 len = 254;
825         }
826         centry_put_uint8(centry, len);
827         centry_expand(centry, len);
828         memcpy(centry->data + centry->ofs, s, len);
829         centry->ofs += len;
830 }
831
832 /* 
833    push a 16 byte hash into a centry - treat as 16 byte string.
834  */
835 static void centry_put_hash16(struct cache_entry *centry, const uint8 val[16])
836 {
837         centry_put_uint8(centry, 16);
838         centry_expand(centry, 16);
839         memcpy(centry->data + centry->ofs, val, 16);
840         centry->ofs += 16;
841 }
842
843 static void centry_put_sid(struct cache_entry *centry, const struct dom_sid *sid)
844 {
845         fstring sid_string;
846         centry_put_string(centry, sid_to_fstring(sid_string, sid));
847 }
848
849
850 /*
851   put NTSTATUS into a centry
852 */
853 static void centry_put_ntstatus(struct cache_entry *centry, NTSTATUS status)
854 {
855         uint32 status_value = NT_STATUS_V(status);
856         centry_put_uint32(centry, status_value);
857 }
858
859
860 /*
861   push a NTTIME into a centry 
862 */
863 static void centry_put_nttime(struct cache_entry *centry, NTTIME nt)
864 {
865         centry_expand(centry, 8);
866         SIVAL(centry->data, centry->ofs, nt & 0xFFFFFFFF);
867         centry->ofs += 4;
868         SIVAL(centry->data, centry->ofs, nt >> 32);
869         centry->ofs += 4;
870 }
871
872 /*
873   push a time_t into a centry - use a 64 bit size.
874   NTTIME here is being used as a convenient 64-bit size.
875 */
876 static void centry_put_time(struct cache_entry *centry, time_t t)
877 {
878         NTTIME nt = (NTTIME)t;
879         centry_put_nttime(centry, nt);
880 }
881
882 /*
883   start a centry for output. When finished, call centry_end()
884 */
885 struct cache_entry *centry_start(struct winbindd_domain *domain, NTSTATUS status)
886 {
887         struct cache_entry *centry;
888
889         if (!wcache->tdb)
890                 return NULL;
891
892         centry = SMB_XMALLOC_P(struct cache_entry);
893
894         centry->len = 8192; /* reasonable default */
895         centry->data = SMB_XMALLOC_ARRAY(uint8, centry->len);
896         centry->ofs = 0;
897         centry->sequence_number = domain->sequence_number;
898         centry->timeout = lp_winbind_cache_time() + time(NULL);
899         centry_put_ntstatus(centry, status);
900         centry_put_uint32(centry, centry->sequence_number);
901         centry_put_uint64_t(centry, centry->timeout);
902         return centry;
903 }
904
905 /*
906   finish a centry and write it to the tdb
907 */
908 static void centry_end(struct cache_entry *centry, const char *format, ...) PRINTF_ATTRIBUTE(2,3);
909 static void centry_end(struct cache_entry *centry, const char *format, ...)
910 {
911         va_list ap;
912         char *kstr;
913         TDB_DATA key, data;
914
915         if (!winbindd_use_cache()) {
916                 return;
917         }
918
919         va_start(ap, format);
920         smb_xvasprintf(&kstr, format, ap);
921         va_end(ap);
922
923         key = string_tdb_data(kstr);
924         data.dptr = centry->data;
925         data.dsize = centry->ofs;
926
927         tdb_store(wcache->tdb, key, data, TDB_REPLACE);
928         free(kstr);
929 }
930
931 static void wcache_save_name_to_sid(struct winbindd_domain *domain, 
932                                     NTSTATUS status, const char *domain_name,
933                                     const char *name, const struct dom_sid *sid,
934                                     enum lsa_SidType type)
935 {
936         struct cache_entry *centry;
937         fstring uname;
938
939         centry = centry_start(domain, status);
940         if (!centry)
941                 return;
942         centry_put_uint32(centry, type);
943         centry_put_sid(centry, sid);
944         fstrcpy(uname, name);
945         strupper_m(uname);
946         centry_end(centry, "NS/%s/%s", domain_name, uname);
947         DEBUG(10,("wcache_save_name_to_sid: %s\\%s -> %s (%s)\n", domain_name,
948                   uname, sid_string_dbg(sid), nt_errstr(status)));
949         centry_free(centry);
950 }
951
952 static void wcache_save_sid_to_name(struct winbindd_domain *domain, NTSTATUS status, 
953                                     const struct dom_sid *sid, const char *domain_name, const char *name, enum lsa_SidType type)
954 {
955         struct cache_entry *centry;
956         fstring sid_string;
957
958         centry = centry_start(domain, status);
959         if (!centry)
960                 return;
961
962         if (NT_STATUS_IS_OK(status)) {
963                 centry_put_uint32(centry, type);
964                 centry_put_string(centry, domain_name);
965                 centry_put_string(centry, name);
966         }
967
968         centry_end(centry, "SN/%s", sid_to_fstring(sid_string, sid));
969         DEBUG(10,("wcache_save_sid_to_name: %s -> %s (%s)\n", sid_string, 
970                   name, nt_errstr(status)));
971         centry_free(centry);
972 }
973
974
975 static void wcache_save_user(struct winbindd_domain *domain, NTSTATUS status,
976                              struct wbint_userinfo *info)
977 {
978         struct cache_entry *centry;
979         fstring sid_string;
980
981         if (is_null_sid(&info->user_sid)) {
982                 return;
983         }
984
985         centry = centry_start(domain, status);
986         if (!centry)
987                 return;
988         centry_put_string(centry, info->acct_name);
989         centry_put_string(centry, info->full_name);
990         centry_put_string(centry, info->homedir);
991         centry_put_string(centry, info->shell);
992         centry_put_uint32(centry, info->primary_gid);
993         centry_put_sid(centry, &info->user_sid);
994         centry_put_sid(centry, &info->group_sid);
995         centry_end(centry, "U/%s", sid_to_fstring(sid_string,
996                                                   &info->user_sid));
997         DEBUG(10,("wcache_save_user: %s (acct_name %s)\n", sid_string, info->acct_name));
998         centry_free(centry);
999 }
1000
1001 static void wcache_save_lockout_policy(struct winbindd_domain *domain,
1002                                        NTSTATUS status,
1003                                        struct samr_DomInfo12 *lockout_policy)
1004 {
1005         struct cache_entry *centry;
1006
1007         centry = centry_start(domain, status);
1008         if (!centry)
1009                 return;
1010
1011         centry_put_nttime(centry, lockout_policy->lockout_duration);
1012         centry_put_nttime(centry, lockout_policy->lockout_window);
1013         centry_put_uint16(centry, lockout_policy->lockout_threshold);
1014
1015         centry_end(centry, "LOC_POL/%s", domain->name);
1016
1017         DEBUG(10,("wcache_save_lockout_policy: %s\n", domain->name));
1018
1019         centry_free(centry);
1020 }
1021
1022
1023
1024 static void wcache_save_password_policy(struct winbindd_domain *domain,
1025                                         NTSTATUS status,
1026                                         struct samr_DomInfo1 *policy)
1027 {
1028         struct cache_entry *centry;
1029
1030         centry = centry_start(domain, status);
1031         if (!centry)
1032                 return;
1033
1034         centry_put_uint16(centry, policy->min_password_length);
1035         centry_put_uint16(centry, policy->password_history_length);
1036         centry_put_uint32(centry, policy->password_properties);
1037         centry_put_nttime(centry, policy->max_password_age);
1038         centry_put_nttime(centry, policy->min_password_age);
1039
1040         centry_end(centry, "PWD_POL/%s", domain->name);
1041
1042         DEBUG(10,("wcache_save_password_policy: %s\n", domain->name));
1043
1044         centry_free(centry);
1045 }
1046
1047 /***************************************************************************
1048  ***************************************************************************/
1049
1050 static void wcache_save_username_alias(struct winbindd_domain *domain,
1051                                        NTSTATUS status,
1052                                        const char *name, const char *alias)
1053 {
1054         struct cache_entry *centry;
1055         fstring uname;
1056
1057         if ( (centry = centry_start(domain, status)) == NULL )
1058                 return;
1059
1060         centry_put_string( centry, alias );
1061
1062         fstrcpy(uname, name);
1063         strupper_m(uname);
1064         centry_end(centry, "NSS/NA/%s", uname);
1065
1066         DEBUG(10,("wcache_save_username_alias: %s -> %s\n", name, alias ));
1067
1068         centry_free(centry);
1069 }
1070
1071 static void wcache_save_alias_username(struct winbindd_domain *domain,
1072                                        NTSTATUS status,
1073                                        const char *alias, const char *name)
1074 {
1075         struct cache_entry *centry;
1076         fstring uname;
1077
1078         if ( (centry = centry_start(domain, status)) == NULL )
1079                 return;
1080
1081         centry_put_string( centry, name );
1082
1083         fstrcpy(uname, alias);
1084         strupper_m(uname);
1085         centry_end(centry, "NSS/AN/%s", uname);
1086
1087         DEBUG(10,("wcache_save_alias_username: %s -> %s\n", alias, name ));
1088
1089         centry_free(centry);
1090 }
1091
1092 /***************************************************************************
1093  ***************************************************************************/
1094
1095 NTSTATUS resolve_username_to_alias( TALLOC_CTX *mem_ctx,
1096                                     struct winbindd_domain *domain,
1097                                     const char *name, char **alias )
1098 {
1099         struct winbind_cache *cache = get_cache(domain);
1100         struct cache_entry *centry = NULL;
1101         NTSTATUS status;
1102         char *upper_name;
1103
1104         if ( domain->internal )
1105                 return NT_STATUS_NOT_SUPPORTED;
1106
1107         if (!cache->tdb)
1108                 goto do_query;
1109
1110         if ( (upper_name = SMB_STRDUP(name)) == NULL )
1111                 return NT_STATUS_NO_MEMORY;
1112         strupper_m(upper_name);
1113
1114         centry = wcache_fetch(cache, domain, "NSS/NA/%s", upper_name);
1115
1116         SAFE_FREE( upper_name );
1117
1118         if (!centry)
1119                 goto do_query;
1120
1121         status = centry->status;
1122
1123         if (!NT_STATUS_IS_OK(status)) {
1124                 centry_free(centry);
1125                 return status;
1126         }
1127
1128         *alias = centry_string( centry, mem_ctx );
1129
1130         centry_free(centry);
1131
1132         DEBUG(10,("resolve_username_to_alias: [Cached] - mapped %s to %s\n",
1133                   name, *alias ? *alias : "(none)"));
1134
1135         return (*alias) ? NT_STATUS_OK : NT_STATUS_OBJECT_NAME_NOT_FOUND;
1136
1137 do_query:
1138
1139         /* If its not in cache and we are offline, then fail */
1140
1141         if ( get_global_winbindd_state_offline() || !domain->online ) {
1142                 DEBUG(8,("resolve_username_to_alias: rejecting query "
1143                          "in offline mode\n"));
1144                 return NT_STATUS_NOT_FOUND;
1145         }
1146
1147         status = nss_map_to_alias( mem_ctx, domain->name, name, alias );
1148
1149         if ( NT_STATUS_IS_OK( status ) ) {
1150                 wcache_save_username_alias(domain, status, name, *alias);
1151         }
1152
1153         if ( NT_STATUS_EQUAL( status, NT_STATUS_NONE_MAPPED ) ) {
1154                 wcache_save_username_alias(domain, status, name, "(NULL)");
1155         }
1156
1157         DEBUG(5,("resolve_username_to_alias: backend query returned %s\n",
1158                  nt_errstr(status)));
1159
1160         if ( NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ) {
1161                 set_domain_offline( domain );
1162         }
1163
1164         return status;
1165 }
1166
1167 /***************************************************************************
1168  ***************************************************************************/
1169
1170 NTSTATUS resolve_alias_to_username( TALLOC_CTX *mem_ctx,
1171                                     struct winbindd_domain *domain,
1172                                     const char *alias, char **name )
1173 {
1174         struct winbind_cache *cache = get_cache(domain);
1175         struct cache_entry *centry = NULL;
1176         NTSTATUS status;
1177         char *upper_name;
1178
1179         if ( domain->internal )
1180                 return  NT_STATUS_NOT_SUPPORTED;
1181
1182         if (!cache->tdb)
1183                 goto do_query;
1184
1185         if ( (upper_name = SMB_STRDUP(alias)) == NULL )
1186                 return NT_STATUS_NO_MEMORY;
1187         strupper_m(upper_name);
1188
1189         centry = wcache_fetch(cache, domain, "NSS/AN/%s", upper_name);
1190
1191         SAFE_FREE( upper_name );
1192
1193         if (!centry)
1194                 goto do_query;
1195
1196         status = centry->status;
1197
1198         if (!NT_STATUS_IS_OK(status)) {
1199                 centry_free(centry);
1200                 return status;
1201         }
1202
1203         *name = centry_string( centry, mem_ctx );
1204
1205         centry_free(centry);
1206
1207         DEBUG(10,("resolve_alias_to_username: [Cached] - mapped %s to %s\n",
1208                   alias, *name ? *name : "(none)"));
1209
1210         return (*name) ? NT_STATUS_OK : NT_STATUS_OBJECT_NAME_NOT_FOUND;
1211
1212 do_query:
1213
1214         /* If its not in cache and we are offline, then fail */
1215
1216         if ( get_global_winbindd_state_offline() || !domain->online ) {
1217                 DEBUG(8,("resolve_alias_to_username: rejecting query "
1218                          "in offline mode\n"));
1219                 return NT_STATUS_NOT_FOUND;
1220         }
1221
1222         /* an alias cannot contain a domain prefix or '@' */
1223
1224         if (strchr(alias, '\\') || strchr(alias, '@')) {
1225                 DEBUG(10,("resolve_alias_to_username: skipping fully "
1226                           "qualified name %s\n", alias));
1227                 return NT_STATUS_OBJECT_NAME_INVALID;
1228         }
1229
1230         status = nss_map_from_alias( mem_ctx, domain->name, alias, name );
1231
1232         if ( NT_STATUS_IS_OK( status ) ) {
1233                 wcache_save_alias_username( domain, status, alias, *name );
1234         }
1235
1236         if (NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) {
1237                 wcache_save_alias_username(domain, status, alias, "(NULL)");
1238         }
1239
1240         DEBUG(5,("resolve_alias_to_username: backend query returned %s\n",
1241                  nt_errstr(status)));
1242
1243         if ( NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ) {
1244                 set_domain_offline( domain );
1245         }
1246
1247         return status;
1248 }
1249
1250 NTSTATUS wcache_cached_creds_exist(struct winbindd_domain *domain, const struct dom_sid *sid)
1251 {
1252         struct winbind_cache *cache = get_cache(domain);
1253         TDB_DATA data;
1254         fstring key_str, tmp;
1255         uint32 rid;
1256
1257         if (!cache->tdb) {
1258                 return NT_STATUS_INTERNAL_DB_ERROR;
1259         }
1260
1261         if (is_null_sid(sid)) {
1262                 return NT_STATUS_INVALID_SID;
1263         }
1264
1265         if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1266                 return NT_STATUS_INVALID_SID;
1267         }
1268
1269         fstr_sprintf(key_str, "CRED/%s", sid_to_fstring(tmp, sid));
1270
1271         data = tdb_fetch(cache->tdb, string_tdb_data(key_str));
1272         if (!data.dptr) {
1273                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1274         }
1275
1276         SAFE_FREE(data.dptr);
1277         return NT_STATUS_OK;
1278 }
1279
1280 /* Lookup creds for a SID - copes with old (unsalted) creds as well
1281    as new salted ones. */
1282
1283 NTSTATUS wcache_get_creds(struct winbindd_domain *domain, 
1284                           TALLOC_CTX *mem_ctx, 
1285                           const struct dom_sid *sid,
1286                           const uint8 **cached_nt_pass,
1287                           const uint8 **cached_salt)
1288 {
1289         struct winbind_cache *cache = get_cache(domain);
1290         struct cache_entry *centry = NULL;
1291         NTSTATUS status;
1292         time_t t;
1293         uint32 rid;
1294         fstring tmp;
1295
1296         if (!cache->tdb) {
1297                 return NT_STATUS_INTERNAL_DB_ERROR;
1298         }
1299
1300         if (is_null_sid(sid)) {
1301                 return NT_STATUS_INVALID_SID;
1302         }
1303
1304         if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1305                 return NT_STATUS_INVALID_SID;
1306         }
1307
1308         /* Try and get a salted cred first. If we can't
1309            fall back to an unsalted cred. */
1310
1311         centry = wcache_fetch(cache, domain, "CRED/%s",
1312                               sid_to_fstring(tmp, sid));
1313         if (!centry) {
1314                 DEBUG(10,("wcache_get_creds: entry for [CRED/%s] not found\n", 
1315                           sid_string_dbg(sid)));
1316                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1317         }
1318
1319         t = centry_time(centry);
1320
1321         /* In the salted case this isn't actually the nt_hash itself,
1322            but the MD5 of the salt + nt_hash. Let the caller
1323            sort this out. It can tell as we only return the cached_salt
1324            if we are returning a salted cred. */
1325
1326         *cached_nt_pass = (const uint8 *)centry_hash16(centry, mem_ctx);
1327         if (*cached_nt_pass == NULL) {
1328                 fstring sidstr;
1329
1330                 sid_to_fstring(sidstr, sid);
1331
1332                 /* Bad (old) cred cache. Delete and pretend we
1333                    don't have it. */
1334                 DEBUG(0,("wcache_get_creds: bad entry for [CRED/%s] - deleting\n", 
1335                                 sidstr));
1336                 wcache_delete("CRED/%s", sidstr);
1337                 centry_free(centry);
1338                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1339         }
1340
1341         /* We only have 17 bytes more data in the salted cred case. */
1342         if (centry->len - centry->ofs == 17) {
1343                 *cached_salt = (const uint8 *)centry_hash16(centry, mem_ctx);
1344         } else {
1345                 *cached_salt = NULL;
1346         }
1347
1348         dump_data_pw("cached_nt_pass", *cached_nt_pass, NT_HASH_LEN);
1349         if (*cached_salt) {
1350                 dump_data_pw("cached_salt", *cached_salt, NT_HASH_LEN);
1351         }
1352
1353         status = centry->status;
1354
1355         DEBUG(10,("wcache_get_creds: [Cached] - cached creds for user %s status: %s\n",
1356                   sid_string_dbg(sid), nt_errstr(status) ));
1357
1358         centry_free(centry);
1359         return status;
1360 }
1361
1362 /* Store creds for a SID - only writes out new salted ones. */
1363
1364 NTSTATUS wcache_save_creds(struct winbindd_domain *domain, 
1365                            const struct dom_sid *sid,
1366                            const uint8 nt_pass[NT_HASH_LEN])
1367 {
1368         struct cache_entry *centry;
1369         fstring sid_string;
1370         uint32 rid;
1371         uint8 cred_salt[NT_HASH_LEN];
1372         uint8 salted_hash[NT_HASH_LEN];
1373
1374         if (is_null_sid(sid)) {
1375                 return NT_STATUS_INVALID_SID;
1376         }
1377
1378         if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1379                 return NT_STATUS_INVALID_SID;
1380         }
1381
1382         centry = centry_start(domain, NT_STATUS_OK);
1383         if (!centry) {
1384                 return NT_STATUS_INTERNAL_DB_ERROR;
1385         }
1386
1387         dump_data_pw("nt_pass", nt_pass, NT_HASH_LEN);
1388
1389         centry_put_time(centry, time(NULL));
1390
1391         /* Create a salt and then salt the hash. */
1392         generate_random_buffer(cred_salt, NT_HASH_LEN);
1393         E_md5hash(cred_salt, nt_pass, salted_hash);
1394
1395         centry_put_hash16(centry, salted_hash);
1396         centry_put_hash16(centry, cred_salt);
1397         centry_end(centry, "CRED/%s", sid_to_fstring(sid_string, sid));
1398
1399         DEBUG(10,("wcache_save_creds: %s\n", sid_string));
1400
1401         centry_free(centry);
1402
1403         return NT_STATUS_OK;
1404 }
1405
1406
1407 /* Query display info. This is the basic user list fn */
1408 static NTSTATUS query_user_list(struct winbindd_domain *domain,
1409                                 TALLOC_CTX *mem_ctx,
1410                                 uint32 *num_entries, 
1411                                 struct wbint_userinfo **info)
1412 {
1413         struct winbind_cache *cache = get_cache(domain);
1414         struct cache_entry *centry = NULL;
1415         NTSTATUS status;
1416         unsigned int i, retry;
1417         bool old_status = domain->online;
1418
1419         if (!cache->tdb)
1420                 goto do_query;
1421
1422         centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
1423         if (!centry)
1424                 goto do_query;
1425
1426 do_fetch_cache:
1427         *num_entries = centry_uint32(centry);
1428
1429         if (*num_entries == 0)
1430                 goto do_cached;
1431
1432         (*info) = TALLOC_ARRAY(mem_ctx, struct wbint_userinfo, *num_entries);
1433         if (! (*info)) {
1434                 smb_panic_fn("query_user_list out of memory");
1435         }
1436         for (i=0; i<(*num_entries); i++) {
1437                 (*info)[i].acct_name = centry_string(centry, mem_ctx);
1438                 (*info)[i].full_name = centry_string(centry, mem_ctx);
1439                 (*info)[i].homedir = centry_string(centry, mem_ctx);
1440                 (*info)[i].shell = centry_string(centry, mem_ctx);
1441                 centry_sid(centry, &(*info)[i].user_sid);
1442                 centry_sid(centry, &(*info)[i].group_sid);
1443         }
1444
1445 do_cached:      
1446         status = centry->status;
1447
1448         DEBUG(10,("query_user_list: [Cached] - cached list for domain %s status: %s\n",
1449                 domain->name, nt_errstr(status) ));
1450
1451         centry_free(centry);
1452         return status;
1453
1454 do_query:
1455         *num_entries = 0;
1456         *info = NULL;
1457
1458         /* Return status value returned by seq number check */
1459
1460         if (!NT_STATUS_IS_OK(domain->last_status))
1461                 return domain->last_status;
1462
1463         /* Put the query_user_list() in a retry loop.  There appears to be
1464          * some bug either with Windows 2000 or Samba's handling of large
1465          * rpc replies.  This manifests itself as sudden disconnection
1466          * at a random point in the enumeration of a large (60k) user list.
1467          * The retry loop simply tries the operation again. )-:  It's not
1468          * pretty but an acceptable workaround until we work out what the
1469          * real problem is. */
1470
1471         retry = 0;
1472         do {
1473
1474                 DEBUG(10,("query_user_list: [Cached] - doing backend query for list for domain %s\n",
1475                         domain->name ));
1476
1477                 status = domain->backend->query_user_list(domain, mem_ctx, num_entries, info);
1478                 if (!NT_STATUS_IS_OK(status)) {
1479                         DEBUG(3, ("query_user_list: returned 0x%08x, "
1480                                   "retrying\n", NT_STATUS_V(status)));
1481                 }
1482                 if (NT_STATUS_EQUAL(status, NT_STATUS_UNSUCCESSFUL)) {
1483                         DEBUG(3, ("query_user_list: flushing "
1484                                   "connection cache\n"));
1485                         invalidate_cm_connection(&domain->conn);
1486                 }
1487                 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1488                     NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1489                         if (!domain->internal && old_status) {
1490                                 set_domain_offline(domain);
1491                         }
1492                         /* store partial response. */
1493                         if (*num_entries > 0) {
1494                                 /*
1495                                  * humm, what about the status used for cache?
1496                                  * Should it be NT_STATUS_OK?
1497                                  */
1498                                 break;
1499                         }
1500                         /*
1501                          * domain is offline now, and there is no user entries,
1502                          * try to fetch from cache again.
1503                          */
1504                         if (cache->tdb && !domain->online && !domain->internal && old_status) {
1505                                 centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
1506                                 /* partial response... */
1507                                 if (!centry) {
1508                                         goto skip_save;
1509                                 } else {
1510                                         goto do_fetch_cache;
1511                                 }
1512                         } else {
1513                                 goto skip_save;
1514                         }
1515                 }
1516
1517         } while (NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_UNSUCCESSFUL) && 
1518                  (retry++ < 5));
1519
1520         /* and save it */
1521         refresh_sequence_number(domain, false);
1522         if (!NT_STATUS_IS_OK(status)) {
1523                 return status;
1524         }
1525         centry = centry_start(domain, status);
1526         if (!centry)
1527                 goto skip_save;
1528         centry_put_uint32(centry, *num_entries);
1529         for (i=0; i<(*num_entries); i++) {
1530                 centry_put_string(centry, (*info)[i].acct_name);
1531                 centry_put_string(centry, (*info)[i].full_name);
1532                 centry_put_string(centry, (*info)[i].homedir);
1533                 centry_put_string(centry, (*info)[i].shell);
1534                 centry_put_sid(centry, &(*info)[i].user_sid);
1535                 centry_put_sid(centry, &(*info)[i].group_sid);
1536                 if (domain->backend && domain->backend->consistent) {
1537                         /* when the backend is consistent we can pre-prime some mappings */
1538                         wcache_save_name_to_sid(domain, NT_STATUS_OK, 
1539                                                 domain->name,
1540                                                 (*info)[i].acct_name, 
1541                                                 &(*info)[i].user_sid,
1542                                                 SID_NAME_USER);
1543                         wcache_save_sid_to_name(domain, NT_STATUS_OK, 
1544                                                 &(*info)[i].user_sid,
1545                                                 domain->name,
1546                                                 (*info)[i].acct_name, 
1547                                                 SID_NAME_USER);
1548                         wcache_save_user(domain, NT_STATUS_OK, &(*info)[i]);
1549                 }
1550         }       
1551         centry_end(centry, "UL/%s", domain->name);
1552         centry_free(centry);
1553
1554 skip_save:
1555         return status;
1556 }
1557
1558 /* list all domain groups */
1559 static NTSTATUS enum_dom_groups(struct winbindd_domain *domain,
1560                                 TALLOC_CTX *mem_ctx,
1561                                 uint32 *num_entries, 
1562                                 struct acct_info **info)
1563 {
1564         struct winbind_cache *cache = get_cache(domain);
1565         struct cache_entry *centry = NULL;
1566         NTSTATUS status;
1567         unsigned int i;
1568         bool old_status;
1569
1570         old_status = domain->online;
1571         if (!cache->tdb)
1572                 goto do_query;
1573
1574         centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
1575         if (!centry)
1576                 goto do_query;
1577
1578 do_fetch_cache:
1579         *num_entries = centry_uint32(centry);
1580
1581         if (*num_entries == 0)
1582                 goto do_cached;
1583
1584         (*info) = TALLOC_ARRAY(mem_ctx, struct acct_info, *num_entries);
1585         if (! (*info)) {
1586                 smb_panic_fn("enum_dom_groups out of memory");
1587         }
1588         for (i=0; i<(*num_entries); i++) {
1589                 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1590                 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1591                 (*info)[i].rid = centry_uint32(centry);
1592         }
1593
1594 do_cached:      
1595         status = centry->status;
1596
1597         DEBUG(10,("enum_dom_groups: [Cached] - cached list for domain %s status: %s\n",
1598                 domain->name, nt_errstr(status) ));
1599
1600         centry_free(centry);
1601         return status;
1602
1603 do_query:
1604         *num_entries = 0;
1605         *info = NULL;
1606
1607         /* Return status value returned by seq number check */
1608
1609         if (!NT_STATUS_IS_OK(domain->last_status))
1610                 return domain->last_status;
1611
1612         DEBUG(10,("enum_dom_groups: [Cached] - doing backend query for list for domain %s\n",
1613                 domain->name ));
1614
1615         status = domain->backend->enum_dom_groups(domain, mem_ctx, num_entries, info);
1616
1617         if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1618             NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1619                 if (!domain->internal && old_status) {
1620                         set_domain_offline(domain);
1621                 }
1622                 if (cache->tdb &&
1623                         !domain->online &&
1624                         !domain->internal &&
1625                         old_status) {
1626                         centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
1627                         if (centry) {
1628                                 goto do_fetch_cache;
1629                         }
1630                 }
1631         }
1632         /* and save it */
1633         refresh_sequence_number(domain, false);
1634         if (!NT_STATUS_IS_OK(status)) {
1635                 return status;
1636         }
1637         centry = centry_start(domain, status);
1638         if (!centry)
1639                 goto skip_save;
1640         centry_put_uint32(centry, *num_entries);
1641         for (i=0; i<(*num_entries); i++) {
1642                 centry_put_string(centry, (*info)[i].acct_name);
1643                 centry_put_string(centry, (*info)[i].acct_desc);
1644                 centry_put_uint32(centry, (*info)[i].rid);
1645         }       
1646         centry_end(centry, "GL/%s/domain", domain->name);
1647         centry_free(centry);
1648
1649 skip_save:
1650         return status;
1651 }
1652
1653 /* list all domain groups */
1654 static NTSTATUS enum_local_groups(struct winbindd_domain *domain,
1655                                 TALLOC_CTX *mem_ctx,
1656                                 uint32 *num_entries, 
1657                                 struct acct_info **info)
1658 {
1659         struct winbind_cache *cache = get_cache(domain);
1660         struct cache_entry *centry = NULL;
1661         NTSTATUS status;
1662         unsigned int i;
1663         bool old_status;
1664
1665         old_status = domain->online;
1666         if (!cache->tdb)
1667                 goto do_query;
1668
1669         centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
1670         if (!centry)
1671                 goto do_query;
1672
1673 do_fetch_cache:
1674         *num_entries = centry_uint32(centry);
1675
1676         if (*num_entries == 0)
1677                 goto do_cached;
1678
1679         (*info) = TALLOC_ARRAY(mem_ctx, struct acct_info, *num_entries);
1680         if (! (*info)) {
1681                 smb_panic_fn("enum_dom_groups out of memory");
1682         }
1683         for (i=0; i<(*num_entries); i++) {
1684                 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1685                 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1686                 (*info)[i].rid = centry_uint32(centry);
1687         }
1688
1689 do_cached:      
1690
1691         /* If we are returning cached data and the domain controller
1692            is down then we don't know whether the data is up to date
1693            or not.  Return NT_STATUS_MORE_PROCESSING_REQUIRED to
1694            indicate this. */
1695
1696         if (wcache_server_down(domain)) {
1697                 DEBUG(10, ("enum_local_groups: returning cached user list and server was down\n"));
1698                 status = NT_STATUS_MORE_PROCESSING_REQUIRED;
1699         } else
1700                 status = centry->status;
1701
1702         DEBUG(10,("enum_local_groups: [Cached] - cached list for domain %s status: %s\n",
1703                 domain->name, nt_errstr(status) ));
1704
1705         centry_free(centry);
1706         return status;
1707
1708 do_query:
1709         *num_entries = 0;
1710         *info = NULL;
1711
1712         /* Return status value returned by seq number check */
1713
1714         if (!NT_STATUS_IS_OK(domain->last_status))
1715                 return domain->last_status;
1716
1717         DEBUG(10,("enum_local_groups: [Cached] - doing backend query for list for domain %s\n",
1718                 domain->name ));
1719
1720         status = domain->backend->enum_local_groups(domain, mem_ctx, num_entries, info);
1721
1722         if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1723                 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1724                 if (!domain->internal && old_status) {
1725                         set_domain_offline(domain);
1726                 }
1727                 if (cache->tdb &&
1728                         !domain->internal &&
1729                         !domain->online &&
1730                         old_status) {
1731                         centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
1732                         if (centry) {
1733                                 goto do_fetch_cache;
1734                         }
1735                 }
1736         }
1737         /* and save it */
1738         refresh_sequence_number(domain, false);
1739         if (!NT_STATUS_IS_OK(status)) {
1740                 return status;
1741         }
1742         centry = centry_start(domain, status);
1743         if (!centry)
1744                 goto skip_save;
1745         centry_put_uint32(centry, *num_entries);
1746         for (i=0; i<(*num_entries); i++) {
1747                 centry_put_string(centry, (*info)[i].acct_name);
1748                 centry_put_string(centry, (*info)[i].acct_desc);
1749                 centry_put_uint32(centry, (*info)[i].rid);
1750         }
1751         centry_end(centry, "GL/%s/local", domain->name);
1752         centry_free(centry);
1753
1754 skip_save:
1755         return status;
1756 }
1757
1758 NTSTATUS wcache_name_to_sid(struct winbindd_domain *domain,
1759                             const char *domain_name,
1760                             const char *name,
1761                             struct dom_sid *sid,
1762                             enum lsa_SidType *type)
1763 {
1764         struct winbind_cache *cache = get_cache(domain);
1765         struct cache_entry *centry;
1766         NTSTATUS status;
1767         char *uname;
1768
1769         if (cache->tdb == NULL) {
1770                 return NT_STATUS_NOT_FOUND;
1771         }
1772
1773         uname = talloc_strdup_upper(talloc_tos(), name);
1774         if (uname == NULL) {
1775                 return NT_STATUS_NO_MEMORY;
1776         }
1777
1778         centry = wcache_fetch(cache, domain, "NS/%s/%s", domain_name, uname);
1779         TALLOC_FREE(uname);
1780         if (centry == NULL) {
1781                 return NT_STATUS_NOT_FOUND;
1782         }
1783
1784         status = centry->status;
1785         if (NT_STATUS_IS_OK(status)) {
1786                 *type = (enum lsa_SidType)centry_uint32(centry);
1787                 centry_sid(centry, sid);
1788         }
1789
1790         DEBUG(10,("name_to_sid: [Cached] - cached name for domain %s status: "
1791                   "%s\n", domain->name, nt_errstr(status) ));
1792
1793         centry_free(centry);
1794         return status;
1795 }
1796
1797 /* convert a single name to a sid in a domain */
1798 static NTSTATUS name_to_sid(struct winbindd_domain *domain,
1799                             TALLOC_CTX *mem_ctx,
1800                             const char *domain_name,
1801                             const char *name,
1802                             uint32_t flags,
1803                             struct dom_sid *sid,
1804                             enum lsa_SidType *type)
1805 {
1806         NTSTATUS status;
1807         bool old_status;
1808
1809         old_status = domain->online;
1810
1811         status = wcache_name_to_sid(domain, domain_name, name, sid, type);
1812         if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
1813                 return status;
1814         }
1815
1816         ZERO_STRUCTP(sid);
1817
1818         /* If the seq number check indicated that there is a problem
1819          * with this DC, then return that status... except for
1820          * access_denied.  This is special because the dc may be in
1821          * "restrict anonymous = 1" mode, in which case it will deny
1822          * most unauthenticated operations, but *will* allow the LSA
1823          * name-to-sid that we try as a fallback. */
1824
1825         if (!(NT_STATUS_IS_OK(domain->last_status)
1826               || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1827                 return domain->last_status;
1828
1829         DEBUG(10,("name_to_sid: [Cached] - doing backend query for name for domain %s\n",
1830                 domain->name ));
1831
1832         status = domain->backend->name_to_sid(domain, mem_ctx, domain_name,
1833                                               name, flags, sid, type);
1834
1835         if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1836                 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1837                 if (!domain->internal && old_status) {
1838                         set_domain_offline(domain);
1839                 }
1840                 if (!domain->internal &&
1841                         !domain->online &&
1842                         old_status) {
1843                         NTSTATUS cache_status;
1844                         cache_status = wcache_name_to_sid(domain, domain_name, name, sid, type);
1845                         return cache_status;
1846                 }
1847         }
1848         /* and save it */
1849         refresh_sequence_number(domain, false);
1850
1851         if (domain->online &&
1852             (NT_STATUS_IS_OK(status) || NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED))) {
1853                 wcache_save_name_to_sid(domain, status, domain_name, name, sid, *type);
1854
1855                 /* Only save the reverse mapping if this was not a UPN */
1856                 if (!strchr(name, '@')) {
1857                         strupper_m(CONST_DISCARD(char *,domain_name));
1858                         strlower_m(CONST_DISCARD(char *,name));
1859                         wcache_save_sid_to_name(domain, status, sid, domain_name, name, *type);
1860                 }
1861         }
1862
1863         return status;
1864 }
1865
1866 NTSTATUS wcache_sid_to_name(struct winbindd_domain *domain,
1867                             const struct dom_sid *sid,
1868                             TALLOC_CTX *mem_ctx,
1869                             char **domain_name,
1870                             char **name,
1871                             enum lsa_SidType *type)
1872 {
1873         struct winbind_cache *cache = get_cache(domain);
1874         struct cache_entry *centry;
1875         char *sid_string;
1876         NTSTATUS status;
1877
1878         if (cache->tdb == NULL) {
1879                 return NT_STATUS_NOT_FOUND;
1880         }
1881
1882         sid_string = sid_string_tos(sid);
1883         if (sid_string == NULL) {
1884                 return NT_STATUS_NO_MEMORY;
1885         }
1886
1887         centry = wcache_fetch(cache, domain, "SN/%s", sid_string);
1888         TALLOC_FREE(sid_string);
1889         if (centry == NULL) {
1890                 return NT_STATUS_NOT_FOUND;
1891         }
1892
1893         if (NT_STATUS_IS_OK(centry->status)) {
1894                 *type = (enum lsa_SidType)centry_uint32(centry);
1895                 *domain_name = centry_string(centry, mem_ctx);
1896                 *name = centry_string(centry, mem_ctx);
1897         }
1898
1899         status = centry->status;
1900         centry_free(centry);
1901
1902         DEBUG(10,("sid_to_name: [Cached] - cached name for domain %s status: "
1903                   "%s\n", domain->name, nt_errstr(status) ));
1904
1905         return status;
1906 }
1907
1908 /* convert a sid to a user or group name. The sid is guaranteed to be in the domain
1909    given */
1910 static NTSTATUS sid_to_name(struct winbindd_domain *domain,
1911                             TALLOC_CTX *mem_ctx,
1912                             const struct dom_sid *sid,
1913                             char **domain_name,
1914                             char **name,
1915                             enum lsa_SidType *type)
1916 {
1917         NTSTATUS status;
1918         bool old_status;
1919
1920         old_status = domain->online;
1921         status = wcache_sid_to_name(domain, sid, mem_ctx, domain_name, name,
1922                                     type);
1923         if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
1924                 return status;
1925         }
1926
1927         *name = NULL;
1928         *domain_name = NULL;
1929
1930         /* If the seq number check indicated that there is a problem
1931          * with this DC, then return that status... except for
1932          * access_denied.  This is special because the dc may be in
1933          * "restrict anonymous = 1" mode, in which case it will deny
1934          * most unauthenticated operations, but *will* allow the LSA
1935          * sid-to-name that we try as a fallback. */
1936
1937         if (!(NT_STATUS_IS_OK(domain->last_status)
1938               || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1939                 return domain->last_status;
1940
1941         DEBUG(10,("sid_to_name: [Cached] - doing backend query for name for domain %s\n",
1942                 domain->name ));
1943
1944         status = domain->backend->sid_to_name(domain, mem_ctx, sid, domain_name, name, type);
1945
1946         if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1947                 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1948                 if (!domain->internal && old_status) {
1949                         set_domain_offline(domain);
1950                 }
1951                 if (!domain->internal &&
1952                         !domain->online &&
1953                         old_status) {
1954                         NTSTATUS cache_status;
1955                         cache_status = wcache_sid_to_name(domain, sid, mem_ctx,
1956                                                         domain_name, name, type);
1957                         return cache_status;
1958                 }
1959         }
1960         /* and save it */
1961         refresh_sequence_number(domain, false);
1962         if (!NT_STATUS_IS_OK(status)) {
1963                 return status;
1964         }
1965         wcache_save_sid_to_name(domain, status, sid, *domain_name, *name, *type);
1966
1967         /* We can't save the name to sid mapping here, as with sid history a
1968          * later name2sid would give the wrong sid. */
1969
1970         return status;
1971 }
1972
1973 static NTSTATUS rids_to_names(struct winbindd_domain *domain,
1974                               TALLOC_CTX *mem_ctx,
1975                               const struct dom_sid *domain_sid,
1976                               uint32 *rids,
1977                               size_t num_rids,
1978                               char **domain_name,
1979                               char ***names,
1980                               enum lsa_SidType **types)
1981 {
1982         struct winbind_cache *cache = get_cache(domain);
1983         size_t i;
1984         NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
1985         bool have_mapped;
1986         bool have_unmapped;
1987         bool old_status;
1988
1989         old_status = domain->online;
1990         *domain_name = NULL;
1991         *names = NULL;
1992         *types = NULL;
1993
1994         if (!cache->tdb) {
1995                 goto do_query;
1996         }
1997
1998         if (num_rids == 0) {
1999                 return NT_STATUS_OK;
2000         }
2001
2002         *names = TALLOC_ARRAY(mem_ctx, char *, num_rids);
2003         *types = TALLOC_ARRAY(mem_ctx, enum lsa_SidType, num_rids);
2004
2005         if ((*names == NULL) || (*types == NULL)) {
2006                 result = NT_STATUS_NO_MEMORY;
2007                 goto error;
2008         }
2009
2010         have_mapped = have_unmapped = false;
2011
2012         for (i=0; i<num_rids; i++) {
2013                 struct dom_sid sid;
2014                 struct cache_entry *centry;
2015                 fstring tmp;
2016
2017                 if (!sid_compose(&sid, domain_sid, rids[i])) {
2018                         result = NT_STATUS_INTERNAL_ERROR;
2019                         goto error;
2020                 }
2021
2022                 centry = wcache_fetch(cache, domain, "SN/%s",
2023                                       sid_to_fstring(tmp, &sid));
2024                 if (!centry) {
2025                         goto do_query;
2026                 }
2027
2028                 (*types)[i] = SID_NAME_UNKNOWN;
2029                 (*names)[i] = talloc_strdup(*names, "");
2030
2031                 if (NT_STATUS_IS_OK(centry->status)) {
2032                         char *dom;
2033                         have_mapped = true;
2034                         (*types)[i] = (enum lsa_SidType)centry_uint32(centry);
2035
2036                         dom = centry_string(centry, mem_ctx);
2037                         if (*domain_name == NULL) {
2038                                 *domain_name = dom;
2039                         } else {
2040                                 talloc_free(dom);
2041                         }
2042
2043                         (*names)[i] = centry_string(centry, *names);
2044
2045                 } else if (NT_STATUS_EQUAL(centry->status, NT_STATUS_NONE_MAPPED)) {
2046                         have_unmapped = true;
2047
2048                 } else {
2049                         /* something's definitely wrong */
2050                         result = centry->status;
2051                         goto error;
2052                 }
2053
2054                 centry_free(centry);
2055         }
2056
2057         if (!have_mapped) {
2058                 return NT_STATUS_NONE_MAPPED;
2059         }
2060         if (!have_unmapped) {
2061                 return NT_STATUS_OK;
2062         }
2063         return STATUS_SOME_UNMAPPED;
2064
2065  do_query:
2066
2067         TALLOC_FREE(*names);
2068         TALLOC_FREE(*types);
2069
2070         result = domain->backend->rids_to_names(domain, mem_ctx, domain_sid,
2071                                                 rids, num_rids, domain_name,
2072                                                 names, types);
2073
2074         if (NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
2075                 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2076                 if (!domain->internal && old_status) {
2077                         set_domain_offline(domain);
2078                 }
2079                 if (cache->tdb &&
2080                         !domain->internal &&
2081                         !domain->online &&
2082                         old_status) {
2083                         have_mapped = have_unmapped = false;
2084
2085                         for (i=0; i<num_rids; i++) {
2086                                 struct dom_sid sid;
2087                                 struct cache_entry *centry;
2088                                 fstring tmp;
2089
2090                                 if (!sid_compose(&sid, domain_sid, rids[i])) {
2091                                         result = NT_STATUS_INTERNAL_ERROR;
2092                                         goto error;
2093                                 }
2094
2095                                 centry = wcache_fetch(cache, domain, "SN/%s",
2096                                                       sid_to_fstring(tmp, &sid));
2097                                 if (!centry) {
2098                                         (*types)[i] = SID_NAME_UNKNOWN;
2099                                         (*names)[i] = talloc_strdup(*names, "");
2100                                         continue;
2101                                 }
2102
2103                                 (*types)[i] = SID_NAME_UNKNOWN;
2104                                 (*names)[i] = talloc_strdup(*names, "");
2105
2106                                 if (NT_STATUS_IS_OK(centry->status)) {
2107                                         char *dom;
2108                                         have_mapped = true;
2109                                         (*types)[i] = (enum lsa_SidType)centry_uint32(centry);
2110
2111                                         dom = centry_string(centry, mem_ctx);
2112                                         if (*domain_name == NULL) {
2113                                                 *domain_name = dom;
2114                                         } else {
2115                                                 talloc_free(dom);
2116                                         }
2117
2118                                         (*names)[i] = centry_string(centry, *names);
2119
2120                                 } else if (NT_STATUS_EQUAL(centry->status, NT_STATUS_NONE_MAPPED)) {
2121                                         have_unmapped = true;
2122
2123                                 } else {
2124                                         /* something's definitely wrong */
2125                                         result = centry->status;
2126                                         goto error;
2127                                 }
2128
2129                                 centry_free(centry);
2130                         }
2131
2132                         if (!have_mapped) {
2133                                 return NT_STATUS_NONE_MAPPED;
2134                         }
2135                         if (!have_unmapped) {
2136                                 return NT_STATUS_OK;
2137                         }
2138                         return STATUS_SOME_UNMAPPED;
2139                 }
2140         }
2141         /*
2142           None of the queried rids has been found so save all negative entries
2143         */
2144         if (NT_STATUS_EQUAL(result, NT_STATUS_NONE_MAPPED)) {
2145                 for (i = 0; i < num_rids; i++) {
2146                         struct dom_sid sid;
2147                         const char *name = "";
2148                         const enum lsa_SidType type = SID_NAME_UNKNOWN;
2149                         NTSTATUS status = NT_STATUS_NONE_MAPPED;
2150
2151                         if (!sid_compose(&sid, domain_sid, rids[i])) {
2152                                 return NT_STATUS_INTERNAL_ERROR;
2153                         }
2154
2155                         wcache_save_sid_to_name(domain, status, &sid, *domain_name,
2156                                                 name, type);
2157                 }
2158
2159                 return result;
2160         }
2161
2162         /*
2163           Some or all of the queried rids have been found.
2164         */
2165         if (!NT_STATUS_IS_OK(result) &&
2166             !NT_STATUS_EQUAL(result, STATUS_SOME_UNMAPPED)) {
2167                 return result;
2168         }
2169
2170         refresh_sequence_number(domain, false);
2171
2172         for (i=0; i<num_rids; i++) {
2173                 struct dom_sid sid;
2174                 NTSTATUS status;
2175
2176                 if (!sid_compose(&sid, domain_sid, rids[i])) {
2177                         result = NT_STATUS_INTERNAL_ERROR;
2178                         goto error;
2179                 }
2180
2181                 status = (*types)[i] == SID_NAME_UNKNOWN ?
2182                         NT_STATUS_NONE_MAPPED : NT_STATUS_OK;
2183
2184                 wcache_save_sid_to_name(domain, status, &sid, *domain_name,
2185                                         (*names)[i], (*types)[i]);
2186         }
2187
2188         return result;
2189
2190  error:
2191         TALLOC_FREE(*names);
2192         TALLOC_FREE(*types);
2193         return result;
2194 }
2195
2196 NTSTATUS wcache_query_user(struct winbindd_domain *domain,
2197                            TALLOC_CTX *mem_ctx,
2198                            const struct dom_sid *user_sid,
2199                            struct wbint_userinfo *info)
2200 {
2201         struct winbind_cache *cache = get_cache(domain);
2202         struct cache_entry *centry = NULL;
2203         NTSTATUS status;
2204         char *sid_string;
2205
2206         if (cache->tdb == NULL) {
2207                 return NT_STATUS_NOT_FOUND;
2208         }
2209
2210         sid_string = sid_string_tos(user_sid);
2211         if (sid_string == NULL) {
2212                 return NT_STATUS_NO_MEMORY;
2213         }
2214
2215         centry = wcache_fetch(cache, domain, "U/%s", sid_string);
2216         TALLOC_FREE(sid_string);
2217         if (centry == NULL) {
2218                 return NT_STATUS_NOT_FOUND;
2219         }
2220
2221         /*
2222          * If we have an access denied cache entry and a cached info3
2223          * in the samlogon cache then do a query.  This will force the
2224          * rpc back end to return the info3 data.
2225          */
2226
2227         if (NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED) &&
2228             netsamlogon_cache_have(user_sid)) {
2229                 DEBUG(10, ("query_user: cached access denied and have cached "
2230                            "info3\n"));
2231                 domain->last_status = NT_STATUS_OK;
2232                 centry_free(centry);
2233                 return NT_STATUS_NOT_FOUND;
2234         }
2235
2236         /* if status is not ok then this is a negative hit
2237            and the rest of the data doesn't matter */
2238         status = centry->status;
2239         if (NT_STATUS_IS_OK(status)) {
2240                 info->acct_name = centry_string(centry, mem_ctx);
2241                 info->full_name = centry_string(centry, mem_ctx);
2242                 info->homedir = centry_string(centry, mem_ctx);
2243                 info->shell = centry_string(centry, mem_ctx);
2244                 info->primary_gid = centry_uint32(centry);
2245                 centry_sid(centry, &info->user_sid);
2246                 centry_sid(centry, &info->group_sid);
2247         }
2248
2249         DEBUG(10,("query_user: [Cached] - cached info for domain %s status: "
2250                   "%s\n", domain->name, nt_errstr(status) ));
2251
2252         centry_free(centry);
2253         return status;
2254 }
2255
2256 /* Lookup user information from a rid */
2257 static NTSTATUS query_user(struct winbindd_domain *domain,
2258                            TALLOC_CTX *mem_ctx,
2259                            const struct dom_sid *user_sid,
2260                            struct wbint_userinfo *info)
2261 {
2262         NTSTATUS status;
2263         bool old_status;
2264
2265         old_status = domain->online;
2266         status = wcache_query_user(domain, mem_ctx, user_sid, info);
2267         if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2268                 return status;
2269         }
2270
2271         ZERO_STRUCTP(info);
2272
2273         /* Return status value returned by seq number check */
2274
2275         if (!NT_STATUS_IS_OK(domain->last_status))
2276                 return domain->last_status;
2277
2278         DEBUG(10,("query_user: [Cached] - doing backend query for info for domain %s\n",
2279                 domain->name ));
2280
2281         status = domain->backend->query_user(domain, mem_ctx, user_sid, info);
2282
2283         if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2284                 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2285                 if (!domain->internal && old_status) {
2286                         set_domain_offline(domain);
2287                 }
2288                 if (!domain->internal &&
2289                         !domain->online &&
2290                         old_status) {
2291                         NTSTATUS cache_status;
2292                         cache_status = wcache_query_user(domain, mem_ctx, user_sid, info);
2293                         return cache_status;
2294                 }
2295         }
2296         /* and save it */
2297         refresh_sequence_number(domain, false);
2298         if (!NT_STATUS_IS_OK(status)) {
2299                 return status;
2300         }
2301         wcache_save_user(domain, status, info);
2302
2303         return status;
2304 }
2305
2306 NTSTATUS wcache_lookup_usergroups(struct winbindd_domain *domain,
2307                                   TALLOC_CTX *mem_ctx,
2308                                   const struct dom_sid *user_sid,
2309                                   uint32_t *pnum_sids,
2310                                   struct dom_sid **psids)
2311 {
2312         struct winbind_cache *cache = get_cache(domain);
2313         struct cache_entry *centry = NULL;
2314         NTSTATUS status;
2315         uint32_t i, num_sids;
2316         struct dom_sid *sids;
2317         fstring sid_string;
2318
2319         if (cache->tdb == NULL) {
2320                 return NT_STATUS_NOT_FOUND;
2321         }
2322
2323         centry = wcache_fetch(cache, domain, "UG/%s",
2324                               sid_to_fstring(sid_string, user_sid));
2325         if (centry == NULL) {
2326                 return NT_STATUS_NOT_FOUND;
2327         }
2328
2329         /* If we have an access denied cache entry and a cached info3 in the
2330            samlogon cache then do a query.  This will force the rpc back end
2331            to return the info3 data. */
2332
2333         if (NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)
2334             && netsamlogon_cache_have(user_sid)) {
2335                 DEBUG(10, ("lookup_usergroups: cached access denied and have "
2336                            "cached info3\n"));
2337                 domain->last_status = NT_STATUS_OK;
2338                 centry_free(centry);
2339                 return NT_STATUS_NOT_FOUND;
2340         }
2341
2342         num_sids = centry_uint32(centry);
2343         sids = talloc_array(mem_ctx, struct dom_sid, num_sids);
2344         if (sids == NULL) {
2345                 centry_free(centry);
2346                 return NT_STATUS_NO_MEMORY;
2347         }
2348
2349         for (i=0; i<num_sids; i++) {
2350                 centry_sid(centry, &sids[i]);
2351         }
2352
2353         status = centry->status;
2354
2355         DEBUG(10,("lookup_usergroups: [Cached] - cached info for domain %s "
2356                   "status: %s\n", domain->name, nt_errstr(status)));
2357
2358         centry_free(centry);
2359
2360         *pnum_sids = num_sids;
2361         *psids = sids;
2362         return status;
2363 }
2364
2365 /* Lookup groups a user is a member of. */
2366 static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
2367                                   TALLOC_CTX *mem_ctx,
2368                                   const struct dom_sid *user_sid,
2369                                   uint32 *num_groups, struct dom_sid **user_gids)
2370 {
2371         struct cache_entry *centry = NULL;
2372         NTSTATUS status;
2373         unsigned int i;
2374         fstring sid_string;
2375         bool old_status;
2376
2377         old_status = domain->online;
2378         status = wcache_lookup_usergroups(domain, mem_ctx, user_sid,
2379                                           num_groups, user_gids);
2380         if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2381                 return status;
2382         }
2383
2384         (*num_groups) = 0;
2385         (*user_gids) = NULL;
2386
2387         /* Return status value returned by seq number check */
2388
2389         if (!NT_STATUS_IS_OK(domain->last_status))
2390                 return domain->last_status;
2391
2392         DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info for domain %s\n",
2393                 domain->name ));
2394
2395         status = domain->backend->lookup_usergroups(domain, mem_ctx, user_sid, num_groups, user_gids);
2396
2397         if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2398                 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2399                 if (!domain->internal && old_status) {
2400                         set_domain_offline(domain);
2401                 }
2402                 if (!domain->internal &&
2403                         !domain->online &&
2404                         old_status) {
2405                         NTSTATUS cache_status;
2406                         cache_status = wcache_lookup_usergroups(domain, mem_ctx, user_sid,
2407                                                           num_groups, user_gids);
2408                         return cache_status;
2409                 }
2410         }
2411         if ( NT_STATUS_EQUAL(status, NT_STATUS_SYNCHRONIZATION_REQUIRED) )
2412                 goto skip_save;
2413
2414         /* and save it */
2415         refresh_sequence_number(domain, false);
2416         if (!NT_STATUS_IS_OK(status)) {
2417                 return status;
2418         }
2419         centry = centry_start(domain, status);
2420         if (!centry)
2421                 goto skip_save;
2422
2423         centry_put_uint32(centry, *num_groups);
2424         for (i=0; i<(*num_groups); i++) {
2425                 centry_put_sid(centry, &(*user_gids)[i]);
2426         }       
2427
2428         centry_end(centry, "UG/%s", sid_to_fstring(sid_string, user_sid));
2429         centry_free(centry);
2430
2431 skip_save:
2432         return status;
2433 }
2434
2435 static char *wcache_make_sidlist(TALLOC_CTX *mem_ctx, uint32_t num_sids,
2436                                  const struct dom_sid *sids)
2437 {
2438         uint32_t i;
2439         char *sidlist;
2440
2441         sidlist = talloc_strdup(mem_ctx, "");
2442         if (sidlist == NULL) {
2443                 return NULL;
2444         }
2445         for (i=0; i<num_sids; i++) {
2446                 fstring tmp;
2447                 sidlist = talloc_asprintf_append_buffer(
2448                         sidlist, "/%s", sid_to_fstring(tmp, &sids[i]));
2449                 if (sidlist == NULL) {
2450                         return NULL;
2451                 }
2452         }
2453         return sidlist;
2454 }
2455
2456 NTSTATUS wcache_lookup_useraliases(struct winbindd_domain *domain,
2457                                    TALLOC_CTX *mem_ctx, uint32_t num_sids,
2458                                    const struct dom_sid *sids,
2459                                    uint32_t *pnum_aliases, uint32_t **paliases)
2460 {
2461         struct winbind_cache *cache = get_cache(domain);
2462         struct cache_entry *centry = NULL;
2463         uint32_t num_aliases;
2464         uint32_t *aliases;
2465         NTSTATUS status;
2466         char *sidlist;
2467         int i;
2468
2469         if (cache->tdb == NULL) {
2470                 return NT_STATUS_NOT_FOUND;
2471         }
2472
2473         if (num_sids == 0) {
2474                 *pnum_aliases = 0;
2475                 *paliases = NULL;
2476                 return NT_STATUS_OK;
2477         }
2478
2479         /* We need to cache indexed by the whole list of SIDs, the aliases
2480          * resulting might come from any of the SIDs. */
2481
2482         sidlist = wcache_make_sidlist(talloc_tos(), num_sids, sids);
2483         if (sidlist == NULL) {
2484                 return NT_STATUS_NO_MEMORY;
2485         }
2486
2487         centry = wcache_fetch(cache, domain, "UA%s", sidlist);
2488         TALLOC_FREE(sidlist);
2489         if (centry == NULL) {
2490                 return NT_STATUS_NOT_FOUND;
2491         }
2492
2493         num_aliases = centry_uint32(centry);
2494         aliases = talloc_array(mem_ctx, uint32_t, num_aliases);
2495         if (aliases == NULL) {
2496                 centry_free(centry);
2497                 return NT_STATUS_NO_MEMORY;
2498         }
2499
2500         for (i=0; i<num_aliases; i++) {
2501                 aliases[i] = centry_uint32(centry);
2502         }
2503
2504         status = centry->status;
2505
2506         DEBUG(10,("lookup_useraliases: [Cached] - cached info for domain: %s "
2507                   "status %s\n", domain->name, nt_errstr(status)));
2508
2509         centry_free(centry);
2510
2511         *pnum_aliases = num_aliases;
2512         *paliases = aliases;
2513
2514         return status;
2515 }
2516
2517 static NTSTATUS lookup_useraliases(struct winbindd_domain *domain,
2518                                    TALLOC_CTX *mem_ctx,
2519                                    uint32 num_sids, const struct dom_sid *sids,
2520                                    uint32 *num_aliases, uint32 **alias_rids)
2521 {
2522         struct cache_entry *centry = NULL;
2523         NTSTATUS status;
2524         char *sidlist;
2525         int i;
2526         bool old_status;
2527
2528         old_status = domain->online;
2529         status = wcache_lookup_useraliases(domain, mem_ctx, num_sids, sids,
2530                                            num_aliases, alias_rids);
2531         if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2532                 return status;
2533         }
2534
2535         (*num_aliases) = 0;
2536         (*alias_rids) = NULL;
2537
2538         if (!NT_STATUS_IS_OK(domain->last_status))
2539                 return domain->last_status;
2540
2541         DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info "
2542                   "for domain %s\n", domain->name ));
2543
2544         sidlist = wcache_make_sidlist(talloc_tos(), num_sids, sids);
2545         if (sidlist == NULL) {
2546                 return NT_STATUS_NO_MEMORY;
2547         }
2548
2549         status = domain->backend->lookup_useraliases(domain, mem_ctx,
2550                                                      num_sids, sids,
2551                                                      num_aliases, alias_rids);
2552
2553         if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2554                 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2555                 if (!domain->internal && old_status) {
2556                         set_domain_offline(domain);
2557                 }
2558                 if (!domain->internal &&
2559                         !domain->online &&
2560                         old_status) {
2561                         NTSTATUS cache_status;
2562                         cache_status = wcache_lookup_useraliases(domain, mem_ctx, num_sids,
2563                                                                  sids, num_aliases, alias_rids);
2564                         return cache_status;
2565                 }
2566         }
2567         /* and save it */
2568         refresh_sequence_number(domain, false);
2569         if (!NT_STATUS_IS_OK(status)) {
2570                 return status;
2571         }
2572         centry = centry_start(domain, status);
2573         if (!centry)
2574                 goto skip_save;
2575         centry_put_uint32(centry, *num_aliases);
2576         for (i=0; i<(*num_aliases); i++)
2577                 centry_put_uint32(centry, (*alias_rids)[i]);
2578         centry_end(centry, "UA%s", sidlist);
2579         centry_free(centry);
2580
2581  skip_save:
2582         return status;
2583 }
2584
2585 NTSTATUS wcache_lookup_groupmem(struct winbindd_domain *domain,
2586                                 TALLOC_CTX *mem_ctx,
2587                                 const struct dom_sid *group_sid,
2588                                 uint32_t *num_names,
2589                                 struct dom_sid **sid_mem, char ***names,
2590                                 uint32_t **name_types)
2591 {
2592         struct winbind_cache *cache = get_cache(domain);
2593         struct cache_entry *centry = NULL;
2594         NTSTATUS status;
2595         unsigned int i;
2596         char *sid_string;
2597
2598         if (cache->tdb == NULL) {
2599                 return NT_STATUS_NOT_FOUND;
2600         }
2601
2602         sid_string = sid_string_tos(group_sid);
2603         if (sid_string == NULL) {
2604                 return NT_STATUS_NO_MEMORY;
2605         }
2606
2607         centry = wcache_fetch(cache, domain, "GM/%s", sid_string);
2608         TALLOC_FREE(sid_string);
2609         if (centry == NULL) {
2610                 return NT_STATUS_NOT_FOUND;
2611         }
2612
2613         *sid_mem = NULL;
2614         *names = NULL;
2615         *name_types = NULL;
2616
2617         *num_names = centry_uint32(centry);
2618         if (*num_names == 0) {
2619                 centry_free(centry);
2620                 return NT_STATUS_OK;
2621         }
2622
2623         *sid_mem = talloc_array(mem_ctx, struct dom_sid, *num_names);
2624         *names = talloc_array(mem_ctx, char *, *num_names);
2625         *name_types = talloc_array(mem_ctx, uint32, *num_names);
2626
2627         if ((*sid_mem == NULL) || (*names == NULL) || (*name_types == NULL)) {
2628                 TALLOC_FREE(*sid_mem);
2629                 TALLOC_FREE(*names);
2630                 TALLOC_FREE(*name_types);
2631                 centry_free(centry);
2632                 return NT_STATUS_NO_MEMORY;
2633         }
2634
2635         for (i=0; i<(*num_names); i++) {
2636                 centry_sid(centry, &(*sid_mem)[i]);
2637                 (*names)[i] = centry_string(centry, mem_ctx);
2638                 (*name_types)[i] = centry_uint32(centry);
2639         }
2640
2641         status = centry->status;
2642
2643         DEBUG(10,("lookup_groupmem: [Cached] - cached info for domain %s "
2644                   "status: %s\n", domain->name, nt_errstr(status)));
2645
2646         centry_free(centry);
2647         return status;
2648 }
2649
2650 static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
2651                                 TALLOC_CTX *mem_ctx,
2652                                 const struct dom_sid *group_sid,
2653                                 enum lsa_SidType type,
2654                                 uint32 *num_names,
2655                                 struct dom_sid **sid_mem, char ***names,
2656                                 uint32 **name_types)
2657 {
2658         struct cache_entry *centry = NULL;
2659         NTSTATUS status;
2660         unsigned int i;
2661         fstring sid_string;
2662         bool old_status;
2663
2664         old_status = domain->online;
2665         status = wcache_lookup_groupmem(domain, mem_ctx, group_sid, num_names,
2666                                         sid_mem, names, name_types);
2667         if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2668                 return status;
2669         }
2670
2671         (*num_names) = 0;
2672         (*sid_mem) = NULL;
2673         (*names) = NULL;
2674         (*name_types) = NULL;
2675
2676         /* Return status value returned by seq number check */
2677
2678         if (!NT_STATUS_IS_OK(domain->last_status))
2679                 return domain->last_status;
2680
2681         DEBUG(10,("lookup_groupmem: [Cached] - doing backend query for info for domain %s\n",
2682                 domain->name ));
2683
2684         status = domain->backend->lookup_groupmem(domain, mem_ctx, group_sid,
2685                                                   type, num_names,
2686                                                   sid_mem, names, name_types);
2687
2688         if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2689                 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2690                 if (!domain->internal && old_status) {
2691                         set_domain_offline(domain);
2692                 }
2693                 if (!domain->internal &&
2694                         !domain->online &&
2695                         old_status) {
2696                         NTSTATUS cache_status;
2697                         cache_status = wcache_lookup_groupmem(domain, mem_ctx, group_sid,
2698                                                               num_names, sid_mem, names,
2699                                                               name_types);
2700                         return cache_status;
2701                 }
2702         }
2703         /* and save it */
2704         refresh_sequence_number(domain, false);
2705         if (!NT_STATUS_IS_OK(status)) {
2706                 return status;
2707         }
2708         centry = centry_start(domain, status);
2709         if (!centry)
2710                 goto skip_save;
2711         centry_put_uint32(centry, *num_names);
2712         for (i=0; i<(*num_names); i++) {
2713                 centry_put_sid(centry, &(*sid_mem)[i]);
2714                 centry_put_string(centry, (*names)[i]);
2715                 centry_put_uint32(centry, (*name_types)[i]);
2716         }       
2717         centry_end(centry, "GM/%s", sid_to_fstring(sid_string, group_sid));
2718         centry_free(centry);
2719
2720 skip_save:
2721         return status;
2722 }
2723
2724 /* find the sequence number for a domain */
2725 static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
2726 {
2727         refresh_sequence_number(domain, false);
2728
2729         *seq = domain->sequence_number;
2730
2731         return NT_STATUS_OK;
2732 }
2733
2734 /* enumerate trusted domains 
2735  * (we need to have the list of trustdoms in the cache when we go offline) -
2736  * Guenther */
2737 static NTSTATUS trusted_domains(struct winbindd_domain *domain,
2738                                 TALLOC_CTX *mem_ctx,
2739                                 struct netr_DomainTrustList *trusts)
2740 {
2741         NTSTATUS status;
2742         struct winbind_cache *cache;
2743         struct winbindd_tdc_domain *dom_list = NULL;
2744         size_t num_domains = 0;
2745         bool retval = false;
2746         int i;
2747         bool old_status;
2748
2749         old_status = domain->online;
2750         trusts->count = 0;
2751         trusts->array = NULL;
2752
2753         cache = get_cache(domain);
2754         if (!cache || !cache->tdb) {
2755                 goto do_query;
2756         }
2757
2758         if (domain->online) {
2759                 goto do_query;
2760         }
2761
2762         retval = wcache_tdc_fetch_list(&dom_list, &num_domains);
2763         if (!retval || !num_domains || !dom_list) {
2764                 TALLOC_FREE(dom_list);
2765                 goto do_query;
2766         }
2767
2768 do_fetch_cache:
2769         trusts->array = TALLOC_ZERO_ARRAY(mem_ctx, struct netr_DomainTrust, num_domains);
2770         if (!trusts->array) {
2771                 TALLOC_FREE(dom_list);
2772                 return NT_STATUS_NO_MEMORY;
2773         }
2774
2775         for (i = 0; i < num_domains; i++) {
2776                 struct netr_DomainTrust *trust;
2777                 struct dom_sid *sid;
2778                 struct winbindd_domain *dom;
2779
2780                 dom = find_domain_from_name_noinit(dom_list[i].domain_name);
2781                 if (dom && dom->internal) {
2782                         continue;
2783                 }
2784
2785                 trust = &trusts->array[trusts->count];
2786                 trust->netbios_name = talloc_strdup(trusts->array, dom_list[i].domain_name);
2787                 trust->dns_name = talloc_strdup(trusts->array, dom_list[i].dns_name);
2788                 sid = talloc(trusts->array, struct dom_sid);
2789                 if (!trust->netbios_name || !trust->dns_name ||
2790                         !sid) {
2791                         TALLOC_FREE(dom_list);
2792                         TALLOC_FREE(trusts->array);
2793                         return NT_STATUS_NO_MEMORY;
2794                 }
2795
2796                 trust->trust_flags = dom_list[i].trust_flags;
2797                 trust->trust_attributes = dom_list[i].trust_attribs;
2798                 trust->trust_type = dom_list[i].trust_type;
2799                 sid_copy(sid, &dom_list[i].sid);
2800                 trust->sid = sid;
2801                 trusts->count++;
2802         }
2803
2804         TALLOC_FREE(dom_list);
2805         return NT_STATUS_OK;
2806
2807 do_query:
2808         /* Return status value returned by seq number check */
2809
2810         if (!NT_STATUS_IS_OK(domain->last_status))
2811                 return domain->last_status;
2812
2813         DEBUG(10,("trusted_domains: [Cached] - doing backend query for info for domain %s\n",
2814                 domain->name ));
2815
2816         status = domain->backend->trusted_domains(domain, mem_ctx, trusts);
2817
2818         if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2819                 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2820                 if (!domain->internal && old_status) {
2821                         set_domain_offline(domain);
2822                 }
2823                 if (!domain->internal &&
2824                         !domain->online &&
2825                         old_status) {
2826                         retval = wcache_tdc_fetch_list(&dom_list, &num_domains);
2827                         if (retval && num_domains && dom_list) {
2828                                 TALLOC_FREE(trusts->array);
2829                                 trusts->count = 0;
2830                                 goto do_fetch_cache;
2831                         }
2832                 }
2833         }
2834         /* no trusts gives NT_STATUS_NO_MORE_ENTRIES resetting to NT_STATUS_OK
2835          * so that the generic centry handling still applies correctly -
2836          * Guenther*/
2837
2838         if (!NT_STATUS_IS_ERR(status)) {
2839                 status = NT_STATUS_OK;
2840         }
2841         return status;
2842 }       
2843
2844 /* get lockout policy */
2845 static NTSTATUS lockout_policy(struct winbindd_domain *domain,
2846                                TALLOC_CTX *mem_ctx,
2847                                struct samr_DomInfo12 *policy)
2848 {
2849         struct winbind_cache *cache = get_cache(domain);
2850         struct cache_entry *centry = NULL;
2851         NTSTATUS status;
2852         bool old_status;
2853
2854         old_status = domain->online;
2855         if (!cache->tdb)
2856                 goto do_query;
2857
2858         centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
2859
2860         if (!centry)
2861                 goto do_query;
2862
2863 do_fetch_cache:
2864         policy->lockout_duration = centry_nttime(centry);
2865         policy->lockout_window = centry_nttime(centry);
2866         policy->lockout_threshold = centry_uint16(centry);
2867
2868         status = centry->status;
2869
2870         DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2871                 domain->name, nt_errstr(status) ));
2872
2873         centry_free(centry);
2874         return status;
2875
2876 do_query:
2877         ZERO_STRUCTP(policy);
2878
2879         /* Return status value returned by seq number check */
2880
2881         if (!NT_STATUS_IS_OK(domain->last_status))
2882                 return domain->last_status;
2883
2884         DEBUG(10,("lockout_policy: [Cached] - doing backend query for info for domain %s\n",
2885                 domain->name ));
2886
2887         status = domain->backend->lockout_policy(domain, mem_ctx, policy);
2888
2889         if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2890                 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2891                 if (!domain->internal && old_status) {
2892                         set_domain_offline(domain);
2893                 }
2894                 if (cache->tdb &&
2895                         !domain->internal &&
2896                         !domain->online &&
2897                         old_status) {
2898                         centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
2899                         if (centry) {
2900                                 goto do_fetch_cache;
2901                         }
2902                 }
2903         }
2904         /* and save it */
2905         refresh_sequence_number(domain, false);
2906         if (!NT_STATUS_IS_OK(status)) {
2907                 return status;
2908         }
2909         wcache_save_lockout_policy(domain, status, policy);
2910
2911         return status;
2912 }
2913
2914 /* get password policy */
2915 static NTSTATUS password_policy(struct winbindd_domain *domain,
2916                                 TALLOC_CTX *mem_ctx,
2917                                 struct samr_DomInfo1 *policy)
2918 {
2919         struct winbind_cache *cache = get_cache(domain);
2920         struct cache_entry *centry = NULL;
2921         NTSTATUS status;
2922         bool old_status;
2923
2924         old_status = domain->online;
2925         if (!cache->tdb)
2926                 goto do_query;
2927
2928         centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
2929
2930         if (!centry)
2931                 goto do_query;
2932
2933 do_fetch_cache:
2934         policy->min_password_length = centry_uint16(centry);
2935         policy->password_history_length = centry_uint16(centry);
2936         policy->password_properties = centry_uint32(centry);
2937         policy->max_password_age = centry_nttime(centry);
2938         policy->min_password_age = centry_nttime(centry);
2939
2940         status = centry->status;
2941
2942         DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2943                 domain->name, nt_errstr(status) ));
2944
2945         centry_free(centry);
2946         return status;
2947
2948 do_query:
2949         ZERO_STRUCTP(policy);
2950
2951         /* Return status value returned by seq number check */
2952
2953         if (!NT_STATUS_IS_OK(domain->last_status))
2954                 return domain->last_status;
2955
2956         DEBUG(10,("password_policy: [Cached] - doing backend query for info for domain %s\n",
2957                 domain->name ));
2958
2959         status = domain->backend->password_policy(domain, mem_ctx, policy);
2960
2961         if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2962                 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2963                 if (!domain->internal && old_status) {
2964                         set_domain_offline(domain);
2965                 }
2966                 if (cache->tdb &&
2967                         !domain->internal &&
2968                         !domain->online &&
2969                         old_status) {
2970                         centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
2971                         if (centry) {
2972                                 goto do_fetch_cache;
2973                         }
2974                 }
2975         }
2976         /* and save it */
2977         refresh_sequence_number(domain, false);
2978         if (!NT_STATUS_IS_OK(status)) {
2979                 return status;
2980         }
2981         wcache_save_password_policy(domain, status, policy);
2982
2983         return status;
2984 }
2985
2986
2987 /* Invalidate cached user and group lists coherently */
2988
2989 static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, 
2990                        void *state)
2991 {
2992         if (strncmp((const char *)kbuf.dptr, "UL/", 3) == 0 ||
2993             strncmp((const char *)kbuf.dptr, "GL/", 3) == 0)
2994                 tdb_delete(the_tdb, kbuf);
2995
2996         return 0;
2997 }
2998
2999 /* Invalidate the getpwnam and getgroups entries for a winbindd domain */
3000
3001 void wcache_invalidate_samlogon(struct winbindd_domain *domain, 
3002                                 struct netr_SamInfo3 *info3)
3003 {
3004         struct dom_sid sid;
3005         fstring key_str, sid_string;
3006         struct winbind_cache *cache;
3007
3008         /* dont clear cached U/SID and UG/SID entries when we want to logon
3009          * offline - gd */
3010
3011         if (lp_winbind_offline_logon()) {
3012                 return;
3013         }
3014
3015         if (!domain)
3016                 return;
3017
3018         cache = get_cache(domain);
3019
3020         if (!cache->tdb) {
3021                 return;
3022         }
3023
3024         sid_compose(&sid, info3->base.domain_sid, info3->base.rid);
3025
3026         /* Clear U/SID cache entry */
3027         fstr_sprintf(key_str, "U/%s", sid_to_fstring(sid_string, &sid));
3028         DEBUG(10, ("wcache_invalidate_samlogon: clearing %s\n", key_str));
3029         tdb_delete(cache->tdb, string_tdb_data(key_str));
3030
3031         /* Clear UG/SID cache entry */
3032         fstr_sprintf(key_str, "UG/%s", sid_to_fstring(sid_string, &sid));
3033         DEBUG(10, ("wcache_invalidate_samlogon: clearing %s\n", key_str));
3034         tdb_delete(cache->tdb, string_tdb_data(key_str));
3035
3036         /* Samba/winbindd never needs this. */
3037         netsamlogon_clear_cached_user(info3);
3038 }
3039
3040 bool wcache_invalidate_cache(void)
3041 {
3042         struct winbindd_domain *domain;
3043
3044         for (domain = domain_list(); domain; domain = domain->next) {
3045                 struct winbind_cache *cache = get_cache(domain);
3046
3047                 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
3048                            "entries for %s\n", domain->name));
3049                 if (cache) {
3050                         if (cache->tdb) {
3051                                 tdb_traverse(cache->tdb, traverse_fn, NULL);
3052                         } else {
3053                                 return false;
3054                         }
3055                 }
3056         }
3057         return true;
3058 }
3059
3060 bool wcache_invalidate_cache_noinit(void)
3061 {
3062         struct winbindd_domain *domain;
3063
3064         for (domain = domain_list(); domain; domain = domain->next) {
3065                 struct winbind_cache *cache;
3066
3067                 /* Skip uninitialized domains. */
3068                 if (!domain->initialized && !domain->internal) {
3069                         continue;
3070                 }
3071
3072                 cache = get_cache(domain);
3073
3074                 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
3075                            "entries for %s\n", domain->name));
3076                 if (cache) {
3077                         if (cache->tdb) {
3078                                 tdb_traverse(cache->tdb, traverse_fn, NULL);
3079                                 /*
3080                                  * Flushing cache has nothing to with domains.
3081                                  * return here if we successfully flushed once.
3082                                  * To avoid unnecessary traversing the cache.
3083                                  */
3084                                 return true;
3085                         } else {
3086                                 return false;
3087                         }
3088                 }
3089         }
3090         return true;
3091 }
3092
3093 bool init_wcache(void)
3094 {
3095         if (wcache == NULL) {
3096                 wcache = SMB_XMALLOC_P(struct winbind_cache);
3097                 ZERO_STRUCTP(wcache);
3098         }
3099
3100         if (wcache->tdb != NULL)
3101                 return true;
3102
3103         /* when working offline we must not clear the cache on restart */
3104         wcache->tdb = tdb_open_log(cache_path("winbindd_cache.tdb"),
3105                                 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE, 
3106                                 TDB_INCOMPATIBLE_HASH |
3107                                         (lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST)),
3108                                 O_RDWR|O_CREAT, 0600);
3109
3110         if (wcache->tdb == NULL) {
3111                 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
3112                 return false;
3113         }
3114
3115         return true;
3116 }
3117
3118 /************************************************************************
3119  This is called by the parent to initialize the cache file.
3120  We don't need sophisticated locking here as we know we're the
3121  only opener.
3122 ************************************************************************/
3123
3124 bool initialize_winbindd_cache(void)
3125 {
3126         bool cache_bad = true;
3127         uint32 vers;
3128
3129         if (!init_wcache()) {
3130                 DEBUG(0,("initialize_winbindd_cache: init_wcache failed.\n"));
3131                 return false;
3132         }
3133
3134         /* Check version number. */
3135         if (tdb_fetch_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, &vers) &&
3136                         vers == WINBINDD_CACHE_VERSION) {
3137                 cache_bad = false;
3138         }
3139
3140         if (cache_bad) {
3141                 DEBUG(0,("initialize_winbindd_cache: clearing cache "
3142                         "and re-creating with version number %d\n",
3143                         WINBINDD_CACHE_VERSION ));
3144
3145                 tdb_close(wcache->tdb);
3146                 wcache->tdb = NULL;
3147
3148                 if (unlink(cache_path("winbindd_cache.tdb")) == -1) {
3149                         DEBUG(0,("initialize_winbindd_cache: unlink %s failed %s ",
3150                                 cache_path("winbindd_cache.tdb"),
3151                                 strerror(errno) ));
3152                         return false;
3153                 }
3154                 if (!init_wcache()) {
3155                         DEBUG(0,("initialize_winbindd_cache: re-initialization "
3156                                         "init_wcache failed.\n"));
3157                         return false;
3158                 }
3159
3160                 /* Write the version. */
3161                 if (!tdb_store_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, WINBINDD_CACHE_VERSION)) {
3162                         DEBUG(0,("initialize_winbindd_cache: version number store failed %s\n",
3163                                 tdb_errorstr(wcache->tdb) ));
3164                         return false;
3165                 }
3166         }
3167
3168         tdb_close(wcache->tdb);
3169         wcache->tdb = NULL;
3170         return true;
3171 }
3172
3173 void close_winbindd_cache(void)
3174 {
3175         if (!wcache) {
3176                 return;
3177         }
3178         if (wcache->tdb) {
3179                 tdb_close(wcache->tdb);
3180                 wcache->tdb = NULL;
3181         }
3182 }
3183
3184 bool lookup_cached_sid(TALLOC_CTX *mem_ctx, const struct dom_sid *sid,
3185                        char **domain_name, char **name,
3186                        enum lsa_SidType *type)
3187 {
3188         struct winbindd_domain *domain;
3189         NTSTATUS status;
3190
3191         domain = find_lookup_domain_from_sid(sid);
3192         if (domain == NULL) {
3193                 return false;
3194         }
3195         status = wcache_sid_to_name(domain, sid, mem_ctx, domain_name, name,
3196                                     type);
3197         return NT_STATUS_IS_OK(status);
3198 }
3199
3200 bool lookup_cached_name(const char *domain_name,
3201                         const char *name,
3202                         struct dom_sid *sid,
3203                         enum lsa_SidType *type)
3204 {
3205         struct winbindd_domain *domain;
3206         NTSTATUS status;
3207         bool original_online_state;
3208
3209         domain = find_lookup_domain_from_name(domain_name);
3210         if (domain == NULL) {
3211                 return false;
3212         }
3213
3214         /* If we are doing a cached logon, temporarily set the domain
3215            offline so the cache won't expire the entry */
3216
3217         original_online_state = domain->online;
3218         domain->online = false;
3219         status = wcache_name_to_sid(domain, domain_name, name, sid, type);
3220         domain->online = original_online_state;
3221
3222         return NT_STATUS_IS_OK(status);
3223 }
3224
3225 void cache_name2sid(struct winbindd_domain *domain, 
<