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