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