vfs_ceph_new: Remove unused symbol for ceph_readdir
[Samba.git] / source3 / winbindd / winbindd_cache.c
blob7a000c482592078a5823d8d2f37ca1b7d66627ed
1 /*
2 Unix SMB/CIFS implementation.
4 Winbind cache backend functions
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
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.
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.
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/>.
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"
41 #include "lib/crypto/gnutls_helpers.h"
42 #include <gnutls/crypto.h>
44 #undef DBGC_CLASS
45 #define DBGC_CLASS DBGC_WINBIND
47 #define WINBINDD_CACHE_VER1 1 /* initial db version */
48 #define WINBINDD_CACHE_VER2 2 /* second version with timeouts for NDR entries */
50 #define WINBINDD_CACHE_VERSION WINBINDD_CACHE_VER2
51 #define WINBINDD_CACHE_VERSION_KEYSTR "WINBINDD_CACHE_VERSION"
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;
60 static void wcache_flush_cache(void);
62 static bool opt_nocache = False;
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.
70 static const char *non_centry_keys[] = {
71 "NDR/",
72 "SEQNUM/",
73 "TRUSTDOMCACHE/",
74 "WINBINDD_OFFLINE",
75 WINBINDD_CACHE_VERSION_KEYSTR,
76 NULL
79 bool winbindd_use_idmap_cache(void)
81 return !opt_nocache;
84 bool winbindd_use_cache(void)
86 return !opt_nocache;
89 void winbindd_set_use_cache(bool use_cache)
91 opt_nocache = !use_cache;
94 void winbindd_flush_caches(void)
96 /* We need to invalidate cached user list entries on a SIGHUP
97 otherwise cached access denied errors due to restrict anonymous
98 hang around until the sequence number changes. */
100 if (!wcache_invalidate_cache()) {
101 DBG_ERR("invalidating the cache failed; revalidate the cache\n");
102 if (!winbindd_cache_validate_and_initialize()) {
103 exit(1);
108 /************************************************************************
109 Is this key a non-centry type ?
110 ************************************************************************/
112 static bool is_non_centry_key(TDB_DATA kbuf)
114 int i;
116 if (kbuf.dptr == NULL || kbuf.dsize == 0) {
117 return false;
119 for (i = 0; non_centry_keys[i] != NULL; i++) {
120 size_t namelen = strlen(non_centry_keys[i]);
121 if (kbuf.dsize < namelen) {
122 continue;
124 if (strncmp(non_centry_keys[i], (const char *)kbuf.dptr, namelen) == 0) {
125 return true;
128 return false;
131 struct winbind_cache {
132 TDB_CONTEXT *tdb;
135 struct cache_entry {
136 NTSTATUS status;
137 uint32_t sequence_number;
138 uint64_t timeout;
139 uint8_t *data;
140 uint32_t len, ofs;
143 void (*smb_panic_fn)(const char *const why) = smb_panic;
145 static struct winbind_cache *wcache;
147 static char *wcache_path(void)
150 * Data needs to be kept persistent in state directory for
151 * running with "winbindd offline logon".
153 return state_path(talloc_tos(), "winbindd_cache.tdb");
156 static void winbindd_domain_init_backend(struct winbindd_domain *domain)
158 if (domain->backend != NULL) {
159 return;
162 if (domain->internal) {
163 domain->backend = &builtin_passdb_methods;
166 if (dom_sid_equal(&domain->sid, &global_sid_Builtin)) {
167 domain->initialized = true;
170 if (strequal(domain->name, get_global_sam_name()) &&
171 sid_check_is_our_sam(&domain->sid))
173 domain->backend = &sam_passdb_methods;
176 if (!domain->initialized) {
177 /* We do not need a connection to an RW DC for cache operation */
178 init_dc_connection(domain, false);
181 #ifdef HAVE_ADS
182 if (domain->backend == NULL) {
183 struct winbindd_domain *our_domain = domain;
185 /* find our domain first so we can figure out if we
186 are joined to a kerberized domain */
188 if (!domain->primary) {
189 our_domain = find_our_domain();
192 if ((our_domain->active_directory || IS_DC)
193 && domain->active_directory
194 && !lp_winbind_rpc_only())
196 DBG_INFO("Setting ADS methods for domain %s\n",
197 domain->name);
198 domain->backend = &reconnect_ads_methods;
201 #endif /* HAVE_ADS */
203 if (domain->backend == NULL) {
204 DBG_INFO("Setting MS-RPC methods for domain %s\n", domain->name);
205 domain->backend = &reconnect_methods;
209 /* get the winbind_cache structure */
210 static struct winbind_cache *get_cache(struct winbindd_domain *domain)
212 struct winbind_cache *ret = wcache;
214 winbindd_domain_init_backend(domain);
216 if (ret != NULL) {
217 return ret;
220 ret = SMB_XMALLOC_P(struct winbind_cache);
221 ZERO_STRUCTP(ret);
223 wcache = ret;
224 wcache_flush_cache();
226 return ret;
230 free a centry structure
232 static void centry_free(struct cache_entry *centry)
234 if (!centry)
235 return;
236 SAFE_FREE(centry->data);
237 free(centry);
240 static bool centry_check_bytes(struct cache_entry *centry, size_t nbytes)
242 if (centry->len - centry->ofs < nbytes) {
243 DBG_ERR("centry corruption? needed %u bytes, have %d\n",
244 (unsigned int)nbytes,
245 centry->len - centry->ofs);
246 return false;
248 return true;
252 pull a uint64_t from a cache entry
254 static uint64_t centry_uint64_t(struct cache_entry *centry)
256 uint64_t ret;
258 if (!centry_check_bytes(centry, 8)) {
259 smb_panic_fn("centry_uint64_t");
261 ret = BVAL(centry->data, centry->ofs);
262 centry->ofs += 8;
263 return ret;
267 pull a uint32_t from a cache entry
269 static uint32_t centry_uint32(struct cache_entry *centry)
271 uint32_t ret;
273 if (!centry_check_bytes(centry, 4)) {
274 smb_panic_fn("centry_uint32");
276 ret = IVAL(centry->data, centry->ofs);
277 centry->ofs += 4;
278 return ret;
282 pull a uint16_t from a cache entry
284 static uint16_t centry_uint16(struct cache_entry *centry)
286 uint16_t ret;
287 if (!centry_check_bytes(centry, 2)) {
288 smb_panic_fn("centry_uint16");
290 ret = SVAL(centry->data, centry->ofs);
291 centry->ofs += 2;
292 return ret;
296 pull a uint8_t from a cache entry
298 static uint8_t centry_uint8(struct cache_entry *centry)
300 uint8_t ret;
301 if (!centry_check_bytes(centry, 1)) {
302 smb_panic_fn("centry_uint8");
304 ret = CVAL(centry->data, centry->ofs);
305 centry->ofs += 1;
306 return ret;
310 pull a NTTIME from a cache entry
312 static NTTIME centry_nttime(struct cache_entry *centry)
314 NTTIME ret;
315 if (!centry_check_bytes(centry, 8)) {
316 smb_panic_fn("centry_nttime");
318 ret = IVAL(centry->data, centry->ofs);
319 centry->ofs += 4;
320 ret += (uint64_t)IVAL(centry->data, centry->ofs) << 32;
321 centry->ofs += 4;
322 return ret;
326 pull a time_t from a cache entry. time_t stored portably as a 64-bit time.
328 static time_t centry_time(struct cache_entry *centry)
330 return (time_t)centry_nttime(centry);
333 /* pull a string from a cache entry, using the supplied
334 talloc context
336 static char *centry_string(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
338 uint32_t len;
339 char *ret;
341 len = centry_uint8(centry);
343 if (len == 0xFF) {
344 /* a deliberate NULL string */
345 return NULL;
348 if (!centry_check_bytes(centry, (size_t)len)) {
349 smb_panic_fn("centry_string");
352 ret = talloc_array(mem_ctx, char, len+1);
353 if (!ret) {
354 smb_panic_fn("centry_string out of memory\n");
356 memcpy(ret,centry->data + centry->ofs, len);
357 ret[len] = 0;
358 centry->ofs += len;
359 return ret;
362 /* pull a hash16 from a cache entry, using the supplied
363 talloc context
365 static char *centry_hash16(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
367 uint32_t len;
368 char *ret;
370 len = centry_uint8(centry);
372 if (len != 16) {
373 DBG_ERR("centry corruption? hash len (%u) != 16\n",
374 len );
375 return NULL;
378 if (!centry_check_bytes(centry, 16)) {
379 return NULL;
382 ret = talloc_array(mem_ctx, char, 16);
383 if (!ret) {
384 smb_panic_fn("centry_hash out of memory\n");
386 memcpy(ret,centry->data + centry->ofs, 16);
387 centry->ofs += 16;
388 return ret;
391 /* pull a sid from a cache entry, using the supplied
392 talloc context
394 static bool centry_sid(struct cache_entry *centry, struct dom_sid *sid)
396 char *sid_string;
397 bool ret;
399 sid_string = centry_string(centry, talloc_tos());
400 if (sid_string == NULL) {
401 return false;
403 ret = string_to_sid(sid, sid_string);
404 TALLOC_FREE(sid_string);
405 return ret;
410 pull a NTSTATUS from a cache entry
412 static NTSTATUS centry_ntstatus(struct cache_entry *centry)
414 NTSTATUS status;
416 status = NT_STATUS(centry_uint32(centry));
417 return status;
421 /* the server is considered down if it can't give us a sequence number */
422 static bool wcache_server_down(struct winbindd_domain *domain)
424 bool ret;
426 if (!wcache->tdb)
427 return false;
429 ret = (domain->sequence_number == DOM_SEQUENCE_NONE);
431 if (ret)
432 DBG_DEBUG("wcache_server_down: server for Domain %s down\n",
433 domain->name );
434 return ret;
437 struct wcache_seqnum_state {
438 uint32_t *seqnum;
439 uint32_t *last_seq_check;
442 static int wcache_seqnum_parser(TDB_DATA key, TDB_DATA data,
443 void *private_data)
445 struct wcache_seqnum_state *state = private_data;
447 if (data.dsize != 8) {
448 DBG_DEBUG("wcache_fetch_seqnum: invalid data size %d\n",
449 (int)data.dsize);
450 return -1;
453 *state->seqnum = IVAL(data.dptr, 0);
454 *state->last_seq_check = IVAL(data.dptr, 4);
455 return 0;
458 static bool wcache_fetch_seqnum(const char *domain_name, uint32_t *seqnum,
459 uint32_t *last_seq_check)
461 struct wcache_seqnum_state state = {
462 .seqnum = seqnum, .last_seq_check = last_seq_check
464 size_t len = strlen(domain_name);
465 char keystr[len+8];
466 TDB_DATA key = { .dptr = (uint8_t *)keystr, .dsize = sizeof(keystr) };
467 int ret;
469 if (wcache->tdb == NULL) {
470 DBG_DEBUG("wcache_fetch_seqnum: tdb == NULL\n");
471 return false;
474 snprintf(keystr, sizeof(keystr), "SEQNUM/%s", domain_name);
476 ret = tdb_parse_record(wcache->tdb, key, wcache_seqnum_parser,
477 &state);
478 return (ret == 0);
481 static NTSTATUS fetch_cache_seqnum( struct winbindd_domain *domain, time_t now )
483 uint32_t last_check, time_diff;
485 if (!wcache_fetch_seqnum(domain->name, &domain->sequence_number,
486 &last_check)) {
487 return NT_STATUS_UNSUCCESSFUL;
489 domain->last_seq_check = last_check;
491 /* have we expired? */
493 time_diff = now - domain->last_seq_check;
494 if ((int)time_diff > lp_winbind_cache_time()) {
495 DBG_DEBUG("fetch_cache_seqnum: timeout [%s][%u @ %u]\n",
496 domain->name, domain->sequence_number,
497 (uint32_t)domain->last_seq_check);
498 return NT_STATUS_UNSUCCESSFUL;
501 DBG_DEBUG("fetch_cache_seqnum: success [%s][%u @ %u]\n",
502 domain->name, domain->sequence_number,
503 (uint32_t)domain->last_seq_check);
505 return NT_STATUS_OK;
508 bool wcache_store_seqnum(const char *domain_name, uint32_t seqnum,
509 time_t last_seq_check)
511 size_t len = strlen(domain_name);
512 char keystr[len+8];
513 TDB_DATA key = { .dptr = (uint8_t *)keystr, .dsize = sizeof(keystr) };
514 uint8_t buf[8];
515 int ret;
517 if (wcache->tdb == NULL) {
518 DBG_DEBUG("wcache_store_seqnum: wcache->tdb == NULL\n");
519 return false;
522 snprintf(keystr, sizeof(keystr), "SEQNUM/%s", domain_name);
524 SIVAL(buf, 0, seqnum);
525 SIVAL(buf, 4, last_seq_check);
527 ret = tdb_store(wcache->tdb, key, make_tdb_data(buf, sizeof(buf)),
528 TDB_REPLACE);
529 if (ret != 0) {
530 DBG_DEBUG("tdb_store_bystring failed: %s\n",
531 tdb_errorstr(wcache->tdb));
532 return false;
535 DBG_DEBUG("wcache_store_seqnum: success [%s][%u @ %u]\n",
536 domain_name, seqnum, (unsigned)last_seq_check);
538 return true;
541 static bool store_cache_seqnum( struct winbindd_domain *domain )
543 return wcache_store_seqnum(domain->name, domain->sequence_number,
544 domain->last_seq_check);
548 refresh the domain sequence number on timeout.
551 static void refresh_sequence_number(struct winbindd_domain *domain)
553 NTSTATUS status;
554 unsigned time_diff;
555 time_t t = time(NULL);
556 unsigned cache_time = lp_winbind_cache_time();
558 if (is_domain_offline(domain)) {
559 return;
562 get_cache( domain );
564 time_diff = t - domain->last_seq_check;
566 /* see if we have to refetch the domain sequence number */
567 if ((time_diff < cache_time) &&
568 (domain->sequence_number != DOM_SEQUENCE_NONE) &&
569 NT_STATUS_IS_OK(domain->last_status)) {
570 DBG_DEBUG("refresh_sequence_number: %s time ok\n", domain->name);
571 goto done;
574 /* try to get the sequence number from the tdb cache first */
575 /* this will update the timestamp as well */
577 status = fetch_cache_seqnum( domain, t );
578 if (NT_STATUS_IS_OK(status) &&
579 (domain->sequence_number != DOM_SEQUENCE_NONE) &&
580 NT_STATUS_IS_OK(domain->last_status)) {
581 goto done;
584 /* just use the current time */
585 domain->last_status = NT_STATUS_OK;
586 domain->sequence_number = time(NULL);
587 domain->last_seq_check = time(NULL);
589 /* save the new sequence number in the cache */
590 store_cache_seqnum( domain );
592 done:
593 DBG_DEBUG("refresh_sequence_number: %s seq number is now %d\n",
594 domain->name, domain->sequence_number);
596 return;
600 decide if a cache entry has expired
602 static bool centry_expired(struct winbindd_domain *domain, const char *keystr, struct cache_entry *centry)
604 /* If we've been told to be offline - stay in that state... */
605 if (lp_winbind_offline_logon() && get_global_winbindd_state_offline()) {
606 DBG_DEBUG("centry_expired: Key %s for domain %s valid as winbindd is globally offline.\n",
607 keystr, domain->name );
608 return false;
611 /* when the domain is offline return the cached entry.
612 * This deals with transient offline states... */
614 if (!domain->online) {
615 DBG_DEBUG("centry_expired: Key %s for domain %s valid as domain is offline.\n",
616 keystr, domain->name );
617 return false;
620 /* if the server is OK and our cache entry came from when it was down then
621 the entry is invalid */
622 if ((domain->sequence_number != DOM_SEQUENCE_NONE) &&
623 (centry->sequence_number == DOM_SEQUENCE_NONE)) {
624 DBG_DEBUG("centry_expired: Key %s for domain %s invalid sequence.\n",
625 keystr, domain->name );
626 return true;
629 /* if the server is down or the cache entry is not older than the
630 current sequence number or it did not timeout then it is OK */
631 if (wcache_server_down(domain)
632 || ((centry->sequence_number == domain->sequence_number)
633 && ((time_t)centry->timeout > time(NULL)))) {
634 DBG_DEBUG("centry_expired: Key %s for domain %s is good.\n",
635 keystr, domain->name );
636 return false;
639 DBG_DEBUG("centry_expired: Key %s for domain %s expired\n",
640 keystr, domain->name );
642 /* it's expired */
643 return true;
646 static struct cache_entry *wcache_fetch_raw(char *kstr)
648 TDB_DATA data;
649 struct cache_entry *centry;
650 TDB_DATA key;
652 key = string_tdb_data(kstr);
653 data = tdb_fetch(wcache->tdb, key);
654 if (!data.dptr) {
655 /* a cache miss */
656 return NULL;
659 centry = SMB_XMALLOC_P(struct cache_entry);
660 centry->data = (unsigned char *)data.dptr;
661 centry->len = data.dsize;
662 centry->ofs = 0;
664 if (centry->len < 16) {
665 /* huh? corrupt cache? */
666 DBG_DEBUG("wcache_fetch_raw: Corrupt cache for key %s "
667 "(len < 16)?\n", kstr);
668 centry_free(centry);
669 return NULL;
672 centry->status = centry_ntstatus(centry);
673 centry->sequence_number = centry_uint32(centry);
674 centry->timeout = centry_uint64_t(centry);
676 return centry;
679 static bool is_my_own_sam_domain(struct winbindd_domain *domain)
681 if (strequal(domain->name, get_global_sam_name()) &&
682 sid_check_is_our_sam(&domain->sid)) {
683 return true;
686 return false;
689 static bool is_builtin_domain(struct winbindd_domain *domain)
691 if (strequal(domain->name, "BUILTIN") &&
692 sid_check_is_builtin(&domain->sid)) {
693 return true;
696 return false;
700 fetch an entry from the cache, with a varargs key. auto-fetch the sequence
701 number and return status
703 static struct cache_entry *wcache_fetch(struct winbind_cache *cache,
704 struct winbindd_domain *domain,
705 const char *format, ...) PRINTF_ATTRIBUTE(3,4);
706 static struct cache_entry *wcache_fetch(struct winbind_cache *cache,
707 struct winbindd_domain *domain,
708 const char *format, ...)
710 va_list ap;
711 char *kstr;
712 struct cache_entry *centry;
713 int ret;
715 if (!winbindd_use_cache() ||
716 is_my_own_sam_domain(domain) ||
717 is_builtin_domain(domain)) {
718 return NULL;
721 refresh_sequence_number(domain);
723 va_start(ap, format);
724 ret = vasprintf(&kstr, format, ap);
725 va_end(ap);
727 if (ret == -1) {
728 return NULL;
731 centry = wcache_fetch_raw(kstr);
732 if (centry == NULL) {
733 free(kstr);
734 return NULL;
737 if (centry_expired(domain, kstr, centry)) {
739 DBG_DEBUG("wcache_fetch: entry %s expired for domain %s\n",
740 kstr, domain->name );
742 centry_free(centry);
743 free(kstr);
744 return NULL;
747 DBG_DEBUG("wcache_fetch: returning entry %s for domain %s\n",
748 kstr, domain->name );
750 free(kstr);
751 return centry;
754 static void wcache_delete(const char *format, ...) PRINTF_ATTRIBUTE(1,2);
755 static void wcache_delete(const char *format, ...)
757 va_list ap;
758 char *kstr;
759 TDB_DATA key;
760 int ret;
762 va_start(ap, format);
763 ret = vasprintf(&kstr, format, ap);
764 va_end(ap);
766 if (ret == -1) {
767 return;
770 key = string_tdb_data(kstr);
772 tdb_delete(wcache->tdb, key);
773 free(kstr);
777 make sure we have at least len bytes available in a centry
779 static void centry_expand(struct cache_entry *centry, uint32_t len)
781 if (centry->len - centry->ofs >= len)
782 return;
783 centry->len *= 2;
784 centry->data = SMB_REALLOC_ARRAY(centry->data, unsigned char,
785 centry->len);
786 if (!centry->data) {
787 DBG_ERR("out of memory: needed %d bytes in centry_expand\n", centry->len);
788 smb_panic_fn("out of memory in centry_expand");
793 push a uint64_t into a centry
795 static void centry_put_uint64_t(struct cache_entry *centry, uint64_t v)
797 centry_expand(centry, 8);
798 SBVAL(centry->data, centry->ofs, v);
799 centry->ofs += 8;
803 push a uint32_t into a centry
805 static void centry_put_uint32(struct cache_entry *centry, uint32_t v)
807 centry_expand(centry, 4);
808 SIVAL(centry->data, centry->ofs, v);
809 centry->ofs += 4;
813 push a uint16_t into a centry
815 static void centry_put_uint16(struct cache_entry *centry, uint16_t v)
817 centry_expand(centry, 2);
818 SSVAL(centry->data, centry->ofs, v);
819 centry->ofs += 2;
823 push a uint8_t into a centry
825 static void centry_put_uint8(struct cache_entry *centry, uint8_t v)
827 centry_expand(centry, 1);
828 SCVAL(centry->data, centry->ofs, v);
829 centry->ofs += 1;
833 push a string into a centry
835 static void centry_put_string(struct cache_entry *centry, const char *s)
837 size_t len;
839 if (!s) {
840 /* null strings are marked as len 0xFFFF */
841 centry_put_uint8(centry, 0xFF);
842 return;
845 len = strlen(s);
846 /* can't handle more than 254 char strings. Truncating is probably best */
847 if (len > 254) {
848 DBG_DEBUG("centry_put_string: truncating len (%zu) to: 254\n",
849 len);
850 len = 254;
852 centry_put_uint8(centry, len);
853 centry_expand(centry, len);
854 memcpy(centry->data + centry->ofs, s, len);
855 centry->ofs += len;
859 push a 16 byte hash into a centry - treat as 16 byte string.
861 static void centry_put_hash16(struct cache_entry *centry, const uint8_t val[16])
863 centry_put_uint8(centry, 16);
864 centry_expand(centry, 16);
865 memcpy(centry->data + centry->ofs, val, 16);
866 centry->ofs += 16;
869 static void centry_put_sid(struct cache_entry *centry, const struct dom_sid *sid)
871 struct dom_sid_buf sid_string;
872 centry_put_string(centry, dom_sid_str_buf(sid, &sid_string));
877 put NTSTATUS into a centry
879 static void centry_put_ntstatus(struct cache_entry *centry, NTSTATUS status)
881 uint32_t status_value = NT_STATUS_V(status);
882 centry_put_uint32(centry, status_value);
887 push a NTTIME into a centry
889 static void centry_put_nttime(struct cache_entry *centry, NTTIME nt)
891 centry_expand(centry, 8);
892 SIVAL(centry->data, centry->ofs, nt & 0xFFFFFFFF);
893 centry->ofs += 4;
894 SIVAL(centry->data, centry->ofs, nt >> 32);
895 centry->ofs += 4;
899 push a time_t into a centry - use a 64 bit size.
900 NTTIME here is being used as a convenient 64-bit size.
902 static void centry_put_time(struct cache_entry *centry, time_t t)
904 NTTIME nt = (NTTIME)t;
905 centry_put_nttime(centry, nt);
909 start a centry for output. When finished, call centry_end()
911 static struct cache_entry *centry_start(struct winbindd_domain *domain,
912 NTSTATUS status)
914 struct cache_entry *centry;
916 if (!wcache->tdb)
917 return NULL;
919 centry = SMB_XMALLOC_P(struct cache_entry);
921 centry->len = 8192; /* reasonable default */
922 centry->data = SMB_XMALLOC_ARRAY(uint8_t, centry->len);
923 centry->ofs = 0;
924 centry->sequence_number = domain->sequence_number;
925 centry->timeout = lp_winbind_cache_time() + time(NULL);
926 centry_put_ntstatus(centry, status);
927 centry_put_uint32(centry, centry->sequence_number);
928 centry_put_uint64_t(centry, centry->timeout);
929 return centry;
933 finish a centry and write it to the tdb
935 static void centry_end(struct cache_entry *centry, const char *format, ...) PRINTF_ATTRIBUTE(2,3);
936 static void centry_end(struct cache_entry *centry, const char *format, ...)
938 va_list ap;
939 char *kstr;
940 TDB_DATA key, data;
941 int ret;
943 if (!winbindd_use_cache()) {
944 return;
947 va_start(ap, format);
948 ret = vasprintf(&kstr, format, ap);
949 va_end(ap);
951 if (ret == -1) {
952 return;
955 key = string_tdb_data(kstr);
956 data.dptr = centry->data;
957 data.dsize = centry->ofs;
959 tdb_store(wcache->tdb, key, data, TDB_REPLACE);
960 free(kstr);
963 static void wcache_save_name_to_sid(struct winbindd_domain *domain,
964 NTSTATUS status, const char *domain_name,
965 const char *name, const struct dom_sid *sid,
966 enum lsa_SidType type)
968 bool ok;
970 ok = namemap_cache_set_name2sid(domain_name, name, sid, type,
971 time(NULL) + lp_winbind_cache_time());
972 if (!ok) {
973 DBG_DEBUG("namemap_cache_set_name2sid failed\n");
977 * Don't store the reverse mapping. The name came from user
978 * input, and we might not have the correct capitalization,
979 * which is important for nsswitch.
983 static void wcache_save_sid_to_name(struct winbindd_domain *domain, NTSTATUS status,
984 const struct dom_sid *sid, const char *domain_name, const char *name, enum lsa_SidType type)
986 bool ok;
988 ok = namemap_cache_set_sid2name(sid, domain_name, name, type,
989 time(NULL) + lp_winbind_cache_time());
990 if (!ok) {
991 DBG_DEBUG("namemap_cache_set_sid2name failed\n");
994 if (type != SID_NAME_UNKNOWN) {
995 ok = namemap_cache_set_name2sid(
996 domain_name, name, sid, type,
997 time(NULL) + lp_winbind_cache_time());
998 if (!ok) {
999 DBG_DEBUG("namemap_cache_set_name2sid failed\n");
1004 static void wcache_save_lockout_policy(struct winbindd_domain *domain,
1005 NTSTATUS status,
1006 struct samr_DomInfo12 *lockout_policy)
1008 struct cache_entry *centry;
1010 centry = centry_start(domain, status);
1011 if (!centry)
1012 return;
1014 centry_put_nttime(centry, lockout_policy->lockout_duration);
1015 centry_put_nttime(centry, lockout_policy->lockout_window);
1016 centry_put_uint16(centry, lockout_policy->lockout_threshold);
1018 centry_end(centry, "LOC_POL/%s", domain->name);
1020 DBG_DEBUG("wcache_save_lockout_policy: %s\n", domain->name);
1022 centry_free(centry);
1027 static void wcache_save_password_policy(struct winbindd_domain *domain,
1028 NTSTATUS status,
1029 struct samr_DomInfo1 *policy)
1031 struct cache_entry *centry;
1033 centry = centry_start(domain, status);
1034 if (!centry)
1035 return;
1037 centry_put_uint16(centry, policy->min_password_length);
1038 centry_put_uint16(centry, policy->password_history_length);
1039 centry_put_uint32(centry, policy->password_properties);
1040 centry_put_nttime(centry, policy->max_password_age);
1041 centry_put_nttime(centry, policy->min_password_age);
1043 centry_end(centry, "PWD_POL/%s", domain->name);
1045 DBG_DEBUG("wcache_save_password_policy: %s\n", domain->name);
1047 centry_free(centry);
1050 /***************************************************************************
1051 ***************************************************************************/
1053 static void wcache_save_username_alias(struct winbindd_domain *domain,
1054 NTSTATUS status,
1055 const char *name, const char *alias)
1057 struct cache_entry *centry;
1058 fstring uname;
1060 if ( (centry = centry_start(domain, status)) == NULL )
1061 return;
1063 centry_put_string( centry, alias );
1065 fstrcpy(uname, name);
1066 (void)strupper_m(uname);
1067 centry_end(centry, "NSS/NA/%s", uname);
1069 DBG_DEBUG("wcache_save_username_alias: %s -> %s\n", name, alias );
1071 centry_free(centry);
1074 static void wcache_save_alias_username(struct winbindd_domain *domain,
1075 NTSTATUS status,
1076 const char *alias, const char *name)
1078 struct cache_entry *centry;
1079 fstring uname;
1081 if ( (centry = centry_start(domain, status)) == NULL )
1082 return;
1084 centry_put_string( centry, name );
1086 fstrcpy(uname, alias);
1087 (void)strupper_m(uname);
1088 centry_end(centry, "NSS/AN/%s", uname);
1090 DBG_DEBUG("wcache_save_alias_username: %s -> %s\n", alias, name );
1092 centry_free(centry);
1095 /***************************************************************************
1096 ***************************************************************************/
1098 NTSTATUS resolve_username_to_alias( TALLOC_CTX *mem_ctx,
1099 struct winbindd_domain *domain,
1100 const char *name, char **alias )
1102 struct winbind_cache *cache = get_cache(domain);
1103 struct cache_entry *centry = NULL;
1104 NTSTATUS status;
1105 char *upper_name;
1107 if ( domain->internal )
1108 return NT_STATUS_NOT_SUPPORTED;
1110 if (!cache->tdb)
1111 goto do_query;
1113 upper_name = talloc_strdup_upper(mem_ctx, name);
1114 if (upper_name == NULL) {
1115 return NT_STATUS_NO_MEMORY;
1118 centry = wcache_fetch(cache, domain, "NSS/NA/%s", upper_name);
1120 talloc_free(upper_name);
1122 if (!centry)
1123 goto do_query;
1125 status = centry->status;
1127 if (!NT_STATUS_IS_OK(status)) {
1128 centry_free(centry);
1129 return status;
1132 *alias = centry_string( centry, mem_ctx );
1134 centry_free(centry);
1136 DBG_DEBUG("resolve_username_to_alias: [Cached] - mapped %s to %s\n",
1137 name, *alias ? *alias : "(none)");
1139 return (*alias) ? NT_STATUS_OK : NT_STATUS_OBJECT_NAME_NOT_FOUND;
1141 do_query:
1143 /* If its not in cache and we are offline, then fail */
1145 if (is_domain_offline(domain)) {
1146 DBG_DEBUG("resolve_username_to_alias: rejecting query "
1147 "in offline mode\n");
1148 return NT_STATUS_NOT_FOUND;
1151 status = nss_map_to_alias( mem_ctx, domain->name, name, alias );
1153 if ( NT_STATUS_IS_OK( status ) ) {
1154 wcache_save_username_alias(domain, status, name, *alias);
1157 if ( NT_STATUS_EQUAL( status, NT_STATUS_NONE_MAPPED ) ) {
1158 wcache_save_username_alias(domain, status, name, "(NULL)");
1161 DBG_INFO("resolve_username_to_alias: backend query returned %s\n",
1162 nt_errstr(status));
1164 if ( NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ) {
1165 set_domain_offline( domain );
1168 return status;
1171 /***************************************************************************
1172 ***************************************************************************/
1174 NTSTATUS resolve_alias_to_username( TALLOC_CTX *mem_ctx,
1175 struct winbindd_domain *domain,
1176 const char *alias, char **name )
1178 struct winbind_cache *cache = get_cache(domain);
1179 struct cache_entry *centry = NULL;
1180 NTSTATUS status;
1181 char *upper_name;
1183 if ( domain->internal )
1184 return NT_STATUS_NOT_SUPPORTED;
1186 if (!cache->tdb)
1187 goto do_query;
1189 upper_name = talloc_strdup(mem_ctx, alias);
1190 if (upper_name == NULL) {
1191 return NT_STATUS_NO_MEMORY;
1193 if (!strupper_m(upper_name)) {
1194 talloc_free(upper_name);
1195 return NT_STATUS_INVALID_PARAMETER;
1198 centry = wcache_fetch(cache, domain, "NSS/AN/%s", upper_name);
1200 talloc_free(upper_name);
1202 if (!centry)
1203 goto do_query;
1205 status = centry->status;
1207 if (!NT_STATUS_IS_OK(status)) {
1208 centry_free(centry);
1209 return status;
1212 *name = centry_string( centry, mem_ctx );
1214 centry_free(centry);
1216 DBG_DEBUG("resolve_alias_to_username: [Cached] - mapped %s to %s\n",
1217 alias, *name ? *name : "(none)");
1219 return (*name) ? NT_STATUS_OK : NT_STATUS_OBJECT_NAME_NOT_FOUND;
1221 do_query:
1223 /* If its not in cache and we are offline, then fail */
1225 if (is_domain_offline(domain)) {
1226 DBG_DEBUG("resolve_alias_to_username: rejecting query "
1227 "in offline mode\n");
1228 return NT_STATUS_NOT_FOUND;
1231 /* an alias cannot contain a domain prefix or '@' */
1233 if (strchr(alias, '\\') || strchr(alias, '@')) {
1234 DBG_DEBUG("resolve_alias_to_username: skipping fully "
1235 "qualified name %s\n", alias);
1236 return NT_STATUS_OBJECT_NAME_INVALID;
1239 status = nss_map_from_alias( mem_ctx, domain->name, alias, name );
1241 if ( NT_STATUS_IS_OK( status ) ) {
1242 wcache_save_alias_username( domain, status, alias, *name );
1245 if (NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) {
1246 wcache_save_alias_username(domain, status, alias, "(NULL)");
1249 DBG_INFO("resolve_alias_to_username: backend query returned %s\n",
1250 nt_errstr(status));
1252 if ( NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ) {
1253 set_domain_offline( domain );
1256 return status;
1259 NTSTATUS wcache_cached_creds_exist(struct winbindd_domain *domain, const struct dom_sid *sid)
1261 struct winbind_cache *cache = get_cache(domain);
1262 int ret;
1263 struct dom_sid_buf tmp;
1264 fstring key_str;
1265 uint32_t rid;
1267 if (!cache->tdb) {
1268 return NT_STATUS_INTERNAL_DB_ERROR;
1271 if (is_null_sid(sid)) {
1272 return NT_STATUS_INVALID_SID;
1275 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1276 return NT_STATUS_INVALID_SID;
1279 fstr_sprintf(key_str, "CRED/%s", dom_sid_str_buf(sid, &tmp));
1281 ret = tdb_exists(cache->tdb, string_tdb_data(key_str));
1282 if (ret != 1) {
1283 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1286 return NT_STATUS_OK;
1289 /* Lookup creds for a SID - copes with old (unsalted) creds as well
1290 as new salted ones. */
1292 NTSTATUS wcache_get_creds(struct winbindd_domain *domain,
1293 TALLOC_CTX *mem_ctx,
1294 const struct dom_sid *sid,
1295 const uint8_t **cached_nt_pass,
1296 const uint8_t **cached_salt)
1298 struct winbind_cache *cache = get_cache(domain);
1299 struct cache_entry *centry = NULL;
1300 NTSTATUS status;
1301 uint32_t rid;
1302 struct dom_sid_buf sidstr;
1304 if (!cache->tdb) {
1305 return NT_STATUS_INTERNAL_DB_ERROR;
1308 if (is_null_sid(sid)) {
1309 return NT_STATUS_INVALID_SID;
1312 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1313 return NT_STATUS_INVALID_SID;
1316 /* Try and get a salted cred first. If we can't
1317 fall back to an unsalted cred. */
1319 centry = wcache_fetch(cache, domain, "CRED/%s",
1320 dom_sid_str_buf(sid, &sidstr));
1321 if (!centry) {
1322 DBG_DEBUG("wcache_get_creds: entry for [CRED/%s] not found\n",
1323 dom_sid_str_buf(sid, &sidstr));
1324 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1328 * We don't use the time element at this moment,
1329 * but we have to consume it, so that we don't
1330 * need to change the disk format of the cache.
1332 (void)centry_time(centry);
1334 /* In the salted case this isn't actually the nt_hash itself,
1335 but the MD5 of the salt + nt_hash. Let the caller
1336 sort this out. It can tell as we only return the cached_salt
1337 if we are returning a salted cred. */
1339 *cached_nt_pass = (const uint8_t *)centry_hash16(centry, mem_ctx);
1340 if (*cached_nt_pass == NULL) {
1342 dom_sid_str_buf(sid, &sidstr);
1344 /* Bad (old) cred cache. Delete and pretend we
1345 don't have it. */
1346 DBG_WARNING("wcache_get_creds: bad entry for [CRED/%s] - deleting\n",
1347 sidstr.buf);
1348 wcache_delete("CRED/%s", sidstr.buf);
1349 centry_free(centry);
1350 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1353 /* We only have 17 bytes more data in the salted cred case. */
1354 if (centry->len - centry->ofs == 17) {
1355 *cached_salt = (const uint8_t *)centry_hash16(centry, mem_ctx);
1356 } else {
1357 *cached_salt = NULL;
1360 dump_data_pw("cached_nt_pass", *cached_nt_pass, NT_HASH_LEN);
1361 if (*cached_salt) {
1362 dump_data_pw("cached_salt", *cached_salt, NT_HASH_LEN);
1365 status = centry->status;
1367 DBG_DEBUG("wcache_get_creds: [Cached] - cached creds for user %s status: %s\n",
1368 dom_sid_str_buf(sid, &sidstr),
1369 nt_errstr(status) );
1371 centry_free(centry);
1372 return status;
1375 /* Store creds for a SID - only writes out new salted ones. */
1377 NTSTATUS wcache_save_creds(struct winbindd_domain *domain,
1378 const struct dom_sid *sid,
1379 const uint8_t nt_pass[NT_HASH_LEN])
1381 struct cache_entry *centry;
1382 struct dom_sid_buf sid_str;
1383 uint32_t rid;
1384 uint8_t cred_salt[NT_HASH_LEN];
1385 uint8_t salted_hash[NT_HASH_LEN];
1386 gnutls_hash_hd_t hash_hnd = NULL;
1387 int rc;
1389 if (is_null_sid(sid)) {
1390 return NT_STATUS_INVALID_SID;
1393 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1394 return NT_STATUS_INVALID_SID;
1397 centry = centry_start(domain, NT_STATUS_OK);
1398 if (!centry) {
1399 return NT_STATUS_INTERNAL_DB_ERROR;
1402 dump_data_pw("nt_pass", nt_pass, NT_HASH_LEN);
1404 centry_put_time(centry, time(NULL));
1406 /* Create a salt and then salt the hash. */
1407 generate_random_buffer(cred_salt, NT_HASH_LEN);
1409 rc = gnutls_hash_init(&hash_hnd, GNUTLS_DIG_MD5);
1410 if (rc < 0) {
1411 centry_free(centry);
1412 return gnutls_error_to_ntstatus(rc, NT_STATUS_HASH_NOT_SUPPORTED);
1415 rc = gnutls_hash(hash_hnd, cred_salt, 16);
1416 if (rc < 0) {
1417 gnutls_hash_deinit(hash_hnd, NULL);
1418 centry_free(centry);
1419 return gnutls_error_to_ntstatus(rc, NT_STATUS_HASH_NOT_SUPPORTED);
1421 rc = gnutls_hash(hash_hnd, nt_pass, 16);
1422 if (rc < 0) {
1423 gnutls_hash_deinit(hash_hnd, NULL);
1424 centry_free(centry);
1425 return gnutls_error_to_ntstatus(rc, NT_STATUS_HASH_NOT_SUPPORTED);
1427 gnutls_hash_deinit(hash_hnd, salted_hash);
1429 centry_put_hash16(centry, salted_hash);
1430 centry_put_hash16(centry, cred_salt);
1431 centry_end(centry, "CRED/%s", dom_sid_str_buf(sid, &sid_str));
1433 DBG_DEBUG("wcache_save_creds: %s\n", sid_str.buf);
1435 centry_free(centry);
1437 return NT_STATUS_OK;
1441 /* Query display info. This is the basic user list fn */
1442 NTSTATUS wb_cache_query_user_list(struct winbindd_domain *domain,
1443 TALLOC_CTX *mem_ctx,
1444 uint32_t **prids)
1446 struct winbind_cache *cache = get_cache(domain);
1447 struct cache_entry *centry = NULL;
1448 uint32_t num_rids = 0;
1449 uint32_t *rids = NULL;
1450 NTSTATUS status;
1451 unsigned int i, retry;
1452 bool old_status = domain->online;
1454 *prids = NULL;
1456 if (!cache->tdb)
1457 goto do_query;
1459 centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
1460 if (!centry)
1461 goto do_query;
1463 do_fetch_cache:
1464 num_rids = centry_uint32(centry);
1466 if (num_rids == 0) {
1467 goto do_cached;
1470 rids = talloc_array(mem_ctx, uint32_t, num_rids);
1471 if (rids == NULL) {
1472 centry_free(centry);
1473 return NT_STATUS_NO_MEMORY;
1476 for (i=0; i<num_rids; i++) {
1477 rids[i] = centry_uint32(centry);
1480 do_cached:
1481 status = centry->status;
1483 DBG_DEBUG("query_user_list: [Cached] - cached list for domain %s status: %s\n",
1484 domain->name, nt_errstr(status) );
1486 centry_free(centry);
1487 return status;
1489 do_query:
1491 /* Put the query_user_list() in a retry loop. There appears to be
1492 * some bug either with Windows 2000 or Samba's handling of large
1493 * rpc replies. This manifests itself as sudden disconnection
1494 * at a random point in the enumeration of a large (60k) user list.
1495 * The retry loop simply tries the operation again. )-: It's not
1496 * pretty but an acceptable workaround until we work out what the
1497 * real problem is. */
1499 retry = 0;
1500 do {
1502 DBG_DEBUG("query_user_list: [Cached] - doing backend query for list for domain %s\n",
1503 domain->name );
1505 rids = NULL;
1506 status = domain->backend->query_user_list(domain, mem_ctx,
1507 &rids);
1508 num_rids = talloc_array_length(rids);
1510 if (!NT_STATUS_IS_OK(status)) {
1511 DBG_NOTICE("query_user_list: returned 0x%08x, "
1512 "retrying\n", NT_STATUS_V(status));
1514 reset_cm_connection_on_error(domain, NULL, status);
1515 if (NT_STATUS_EQUAL(status, NT_STATUS_UNSUCCESSFUL)) {
1516 DBG_NOTICE("query_user_list: flushing "
1517 "connection cache\n");
1518 invalidate_cm_connection(domain);
1520 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1521 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1522 if (!domain->internal && old_status) {
1523 set_domain_offline(domain);
1525 /* store partial response. */
1526 if (num_rids > 0) {
1528 * humm, what about the status used for cache?
1529 * Should it be NT_STATUS_OK?
1531 break;
1534 * domain is offline now, and there is no user entries,
1535 * try to fetch from cache again.
1537 if (cache->tdb && !domain->online && !domain->internal && old_status) {
1538 centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
1539 /* partial response... */
1540 if (!centry) {
1541 goto skip_save;
1542 } else {
1543 goto do_fetch_cache;
1545 } else {
1546 goto skip_save;
1550 } while (NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_UNSUCCESSFUL) &&
1551 (retry++ < 5));
1553 /* and save it */
1554 refresh_sequence_number(domain);
1555 if (!NT_STATUS_IS_OK(status)) {
1556 return status;
1558 centry = centry_start(domain, status);
1559 if (!centry)
1560 goto skip_save;
1561 centry_put_uint32(centry, num_rids);
1562 for (i=0; i<num_rids; i++) {
1563 centry_put_uint32(centry, rids[i]);
1565 centry_end(centry, "UL/%s", domain->name);
1566 centry_free(centry);
1568 *prids = rids;
1570 skip_save:
1571 return status;
1574 /* list all domain groups */
1575 NTSTATUS wb_cache_enum_dom_groups(struct winbindd_domain *domain,
1576 TALLOC_CTX *mem_ctx,
1577 uint32_t *num_entries,
1578 struct wb_acct_info **info)
1580 struct winbind_cache *cache = get_cache(domain);
1581 struct cache_entry *centry = NULL;
1582 NTSTATUS status;
1583 unsigned int i;
1584 bool old_status;
1586 old_status = domain->online;
1587 if (!cache->tdb)
1588 goto do_query;
1590 centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
1591 if (!centry)
1592 goto do_query;
1594 do_fetch_cache:
1595 *num_entries = centry_uint32(centry);
1597 if (*num_entries == 0)
1598 goto do_cached;
1600 (*info) = talloc_array(mem_ctx, struct wb_acct_info, *num_entries);
1601 if (! (*info)) {
1602 smb_panic_fn("enum_dom_groups out of memory");
1604 for (i=0; i<(*num_entries); i++) {
1605 (*info)[i].acct_name = centry_string(centry, (*info));
1606 (*info)[i].acct_desc = centry_string(centry, (*info));
1607 (*info)[i].rid = centry_uint32(centry);
1610 do_cached:
1611 status = centry->status;
1613 DBG_DEBUG("enum_dom_groups: [Cached] - cached list for domain %s status: %s\n",
1614 domain->name, nt_errstr(status) );
1616 centry_free(centry);
1617 return status;
1619 do_query:
1620 *num_entries = 0;
1621 *info = NULL;
1623 DBG_DEBUG("enum_dom_groups: [Cached] - doing backend query for list for domain %s\n",
1624 domain->name );
1626 status = domain->backend->enum_dom_groups(domain, mem_ctx, num_entries, info);
1628 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1629 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1630 if (!domain->internal && old_status) {
1631 set_domain_offline(domain);
1633 if (cache->tdb &&
1634 !domain->online &&
1635 !domain->internal &&
1636 old_status) {
1637 centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
1638 if (centry) {
1639 goto do_fetch_cache;
1643 /* and save it */
1644 refresh_sequence_number(domain);
1645 if (!NT_STATUS_IS_OK(status)) {
1646 return status;
1648 centry = centry_start(domain, status);
1649 if (!centry)
1650 goto skip_save;
1651 centry_put_uint32(centry, *num_entries);
1652 for (i=0; i<(*num_entries); i++) {
1653 centry_put_string(centry, (*info)[i].acct_name);
1654 centry_put_string(centry, (*info)[i].acct_desc);
1655 centry_put_uint32(centry, (*info)[i].rid);
1657 centry_end(centry, "GL/%s/domain", domain->name);
1658 centry_free(centry);
1660 skip_save:
1661 return status;
1664 /* list all domain groups */
1665 NTSTATUS wb_cache_enum_local_groups(struct winbindd_domain *domain,
1666 TALLOC_CTX *mem_ctx,
1667 uint32_t *num_entries,
1668 struct wb_acct_info **info)
1670 struct winbind_cache *cache = get_cache(domain);
1671 struct cache_entry *centry = NULL;
1672 NTSTATUS status;
1673 unsigned int i;
1674 bool old_status;
1676 old_status = domain->online;
1677 if (!cache->tdb)
1678 goto do_query;
1680 centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
1681 if (!centry)
1682 goto do_query;
1684 do_fetch_cache:
1685 *num_entries = centry_uint32(centry);
1687 if (*num_entries == 0)
1688 goto do_cached;
1690 (*info) = talloc_array(mem_ctx, struct wb_acct_info, *num_entries);
1691 if (! (*info)) {
1692 smb_panic_fn("enum_dom_groups out of memory");
1694 for (i=0; i<(*num_entries); i++) {
1695 (*info)[i].acct_name = centry_string(centry, (*info));
1696 (*info)[i].acct_desc = centry_string(centry, (*info));
1697 (*info)[i].rid = centry_uint32(centry);
1700 do_cached:
1702 /* If we are returning cached data and the domain controller
1703 is down then we don't know whether the data is up to date
1704 or not. Return NT_STATUS_MORE_PROCESSING_REQUIRED to
1705 indicate this. */
1707 if (wcache_server_down(domain)) {
1708 DBG_DEBUG("enum_local_groups: returning cached user list and server was down\n");
1709 status = NT_STATUS_MORE_PROCESSING_REQUIRED;
1710 } else
1711 status = centry->status;
1713 DBG_DEBUG("enum_local_groups: [Cached] - cached list for domain %s status: %s\n",
1714 domain->name, nt_errstr(status) );
1716 centry_free(centry);
1717 return status;
1719 do_query:
1720 *num_entries = 0;
1721 *info = NULL;
1723 DBG_DEBUG("enum_local_groups: [Cached] - doing backend query for list for domain %s\n",
1724 domain->name );
1726 status = domain->backend->enum_local_groups(domain, mem_ctx, num_entries, info);
1728 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1729 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1730 if (!domain->internal && old_status) {
1731 set_domain_offline(domain);
1733 if (cache->tdb &&
1734 !domain->internal &&
1735 !domain->online &&
1736 old_status) {
1737 centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
1738 if (centry) {
1739 goto do_fetch_cache;
1743 /* and save it */
1744 refresh_sequence_number(domain);
1745 if (!NT_STATUS_IS_OK(status)) {
1746 return status;
1748 centry = centry_start(domain, status);
1749 if (!centry)
1750 goto skip_save;
1751 centry_put_uint32(centry, *num_entries);
1752 for (i=0; i<(*num_entries); i++) {
1753 centry_put_string(centry, (*info)[i].acct_name);
1754 centry_put_string(centry, (*info)[i].acct_desc);
1755 centry_put_uint32(centry, (*info)[i].rid);
1757 centry_end(centry, "GL/%s/local", domain->name);
1758 centry_free(centry);
1760 skip_save:
1761 return status;
1764 struct wcache_name_to_sid_state {
1765 struct dom_sid *sid;
1766 enum lsa_SidType *type;
1767 bool offline;
1768 bool found;
1771 static void wcache_name_to_sid_fn(const struct dom_sid *sid,
1772 enum lsa_SidType type,
1773 bool expired,
1774 void *private_data)
1776 struct wcache_name_to_sid_state *state = private_data;
1778 *state->sid = *sid;
1779 *state->type = type;
1780 state->found = (!expired || state->offline);
1783 static NTSTATUS wcache_name_to_sid(struct winbindd_domain *domain,
1784 const char *domain_name,
1785 const char *name,
1786 struct dom_sid *sid,
1787 enum lsa_SidType *type)
1789 struct wcache_name_to_sid_state state = {
1790 .sid = sid, .type = type, .found = false,
1791 .offline = is_domain_offline(domain),
1793 bool ok;
1795 ok = namemap_cache_find_name(domain_name, name, wcache_name_to_sid_fn,
1796 &state);
1797 if (!ok) {
1798 DBG_DEBUG("namemap_cache_find_name failed\n");
1799 return NT_STATUS_NOT_FOUND;
1801 if (!state.found) {
1802 DBG_DEBUG("cache entry not found\n");
1803 return NT_STATUS_NOT_FOUND;
1806 return NT_STATUS_OK;
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)
1818 NTSTATUS status;
1819 bool was_online;
1820 const char *dom_name;
1822 was_online = domain->online;
1824 ZERO_STRUCTP(sid);
1825 *type = SID_NAME_UNKNOWN;
1827 status = wcache_name_to_sid(domain, domain_name, name, sid, type);
1828 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
1829 return status;
1832 DBG_DEBUG("name_to_sid: [Cached] - doing backend query for name for domain %s\n",
1833 domain->name );
1835 winbindd_domain_init_backend(domain);
1836 status = domain->backend->name_to_sid(domain, mem_ctx, domain_name,
1837 name, flags, &dom_name, sid, type);
1839 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1840 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND))
1842 if (!domain->internal && was_online) {
1843 /* Set the domain offline and query the cache again */
1844 set_domain_offline(domain);
1845 return wcache_name_to_sid(domain,
1846 domain_name,
1847 name,
1848 sid,
1849 type);
1852 /* and save it */
1854 if (domain->online && NT_STATUS_IS_OK(status)) {
1855 wcache_save_name_to_sid(domain, status, domain_name, name, sid,
1856 *type);
1858 /* Only save the reverse mapping if this was not a UPN */
1859 if (!strchr(name, '@')) {
1860 if (!strupper_m(discard_const_p(char, domain_name))) {
1861 return NT_STATUS_INVALID_PARAMETER;
1863 (void)strlower_m(discard_const_p(char, name));
1864 wcache_save_sid_to_name(domain, status, sid,
1865 dom_name, name, *type);
1869 return status;
1872 struct wcache_sid_to_name_state {
1873 TALLOC_CTX *mem_ctx;
1874 char **domain_name;
1875 char **name;
1876 enum lsa_SidType *type;
1877 bool offline;
1878 bool found;
1881 static void wcache_sid_to_name_fn(const char *domain,
1882 const char *name,
1883 enum lsa_SidType type,
1884 bool expired,
1885 void *private_data)
1887 struct wcache_sid_to_name_state *state = private_data;
1889 *state->domain_name = talloc_strdup(state->mem_ctx, domain);
1890 if (*state->domain_name == NULL) {
1891 return;
1893 *state->name = talloc_strdup(state->mem_ctx, name);
1894 if (*state->name == NULL) {
1895 return;
1897 *state->type = type;
1898 state->found = (!expired || state->offline);
1901 static NTSTATUS wcache_sid_to_name(struct winbindd_domain *domain,
1902 const struct dom_sid *sid,
1903 TALLOC_CTX *mem_ctx,
1904 char **domain_name,
1905 char **name,
1906 enum lsa_SidType *type)
1908 struct wcache_sid_to_name_state state = {
1909 .mem_ctx = mem_ctx, .found = false,
1910 .domain_name = domain_name, .name = name, .type = type,
1911 .offline = is_domain_offline(domain)
1913 bool ok;
1915 ok = namemap_cache_find_sid(sid, wcache_sid_to_name_fn, &state);
1916 if (!ok) {
1917 DBG_DEBUG("namemap_cache_find_name failed\n");
1918 return NT_STATUS_NOT_FOUND;
1920 if (!state.found) {
1921 DBG_DEBUG("cache entry not found\n");
1922 return NT_STATUS_NOT_FOUND;
1924 if (*type == SID_NAME_UNKNOWN) {
1925 return NT_STATUS_NONE_MAPPED;
1928 return NT_STATUS_OK;
1931 /* convert a sid to a user or group name. The sid is guaranteed to be in the domain
1932 given */
1933 NTSTATUS wb_cache_sid_to_name(struct winbindd_domain *domain,
1934 TALLOC_CTX *mem_ctx,
1935 const struct dom_sid *sid,
1936 char **domain_name,
1937 char **name,
1938 enum lsa_SidType *type)
1940 NTSTATUS status;
1941 bool old_status;
1943 old_status = domain->online;
1944 status = wcache_sid_to_name(domain, sid, mem_ctx, domain_name, name,
1945 type);
1946 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
1947 return status;
1950 *name = NULL;
1951 *domain_name = NULL;
1953 DBG_DEBUG("sid_to_name: [Cached] - doing backend query for name for domain %s\n",
1954 domain->name );
1956 winbindd_domain_init_backend(domain);
1958 status = domain->backend->sid_to_name(domain, mem_ctx, sid, domain_name, name, type);
1960 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1961 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1962 if (!domain->internal && old_status) {
1963 set_domain_offline(domain);
1965 if (!domain->internal &&
1966 !domain->online &&
1967 old_status) {
1968 NTSTATUS cache_status;
1969 cache_status = wcache_sid_to_name(domain, sid, mem_ctx,
1970 domain_name, name, type);
1971 return cache_status;
1974 /* and save it */
1975 if (!NT_STATUS_IS_OK(status)) {
1976 return status;
1978 wcache_save_sid_to_name(domain, status, sid, *domain_name, *name, *type);
1980 /* We can't save the name to sid mapping here, as with sid history a
1981 * later name2sid would give the wrong sid. */
1983 return status;
1986 NTSTATUS wb_cache_rids_to_names(struct winbindd_domain *domain,
1987 TALLOC_CTX *mem_ctx,
1988 const struct dom_sid *domain_sid,
1989 uint32_t *rids,
1990 size_t num_rids,
1991 char **domain_name,
1992 char ***names,
1993 enum lsa_SidType **types)
1995 struct winbind_cache *cache = get_cache(domain);
1996 size_t i;
1997 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
1998 bool have_mapped;
1999 bool have_unmapped;
2000 bool old_status;
2002 old_status = domain->online;
2003 *domain_name = NULL;
2004 *names = NULL;
2005 *types = NULL;
2007 if (!cache->tdb) {
2008 goto do_query;
2011 if (num_rids == 0) {
2012 return NT_STATUS_OK;
2015 *names = talloc_array(mem_ctx, char *, num_rids);
2016 *types = talloc_array(mem_ctx, enum lsa_SidType, num_rids);
2018 if ((*names == NULL) || (*types == NULL)) {
2019 result = NT_STATUS_NO_MEMORY;
2020 goto error;
2023 have_mapped = have_unmapped = false;
2025 for (i=0; i<num_rids; i++) {
2026 struct dom_sid sid;
2027 NTSTATUS status;
2028 enum lsa_SidType type;
2029 char *dom, *name;
2031 if (!sid_compose(&sid, domain_sid, rids[i])) {
2032 result = NT_STATUS_INTERNAL_ERROR;
2033 goto error;
2036 status = wcache_sid_to_name(domain, &sid, *names, &dom,
2037 &name, &type);
2039 (*types)[i] = SID_NAME_UNKNOWN;
2040 (*names)[i] = talloc_strdup(*names, "");
2042 if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2043 /* not cached */
2044 goto do_query;
2047 if (NT_STATUS_IS_OK(status)) {
2048 have_mapped = true;
2049 (*types)[i] = type;
2051 if (*domain_name == NULL) {
2052 *domain_name = dom;
2053 } else {
2054 TALLOC_FREE(dom);
2057 (*names)[i] = name;
2059 } else if (NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) {
2060 have_unmapped = true;
2061 } else {
2062 /* something's definitely wrong */
2063 result = status;
2064 goto error;
2068 if (!have_mapped) {
2069 return NT_STATUS_NONE_MAPPED;
2071 if (!have_unmapped) {
2072 return NT_STATUS_OK;
2074 return STATUS_SOME_UNMAPPED;
2076 do_query:
2078 TALLOC_FREE(*names);
2079 TALLOC_FREE(*types);
2081 result = domain->backend->rids_to_names(domain, mem_ctx, domain_sid,
2082 rids, num_rids, domain_name,
2083 names, types);
2085 if (NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
2086 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2087 if (!domain->internal && old_status) {
2088 set_domain_offline(domain);
2090 if (cache->tdb &&
2091 !domain->internal &&
2092 !domain->online &&
2093 old_status) {
2094 have_mapped = have_unmapped = false;
2096 *names = talloc_array(mem_ctx, char *, num_rids);
2097 if (*names == NULL) {
2098 result = NT_STATUS_NO_MEMORY;
2099 goto error;
2102 *types = talloc_array(mem_ctx, enum lsa_SidType,
2103 num_rids);
2104 if (*types == NULL) {
2105 result = NT_STATUS_NO_MEMORY;
2106 goto error;
2109 for (i=0; i<num_rids; i++) {
2110 struct dom_sid sid;
2111 NTSTATUS status;
2112 enum lsa_SidType type;
2113 char *dom, *name;
2115 if (!sid_compose(&sid, domain_sid, rids[i])) {
2116 result = NT_STATUS_INTERNAL_ERROR;
2117 goto error;
2120 status = wcache_sid_to_name(domain, &sid,
2121 *names, &dom,
2122 &name, &type);
2124 (*types)[i] = SID_NAME_UNKNOWN;
2125 (*names)[i] = talloc_strdup(*names, "");
2127 if (NT_STATUS_IS_OK(status)) {
2128 have_mapped = true;
2129 (*types)[i] = type;
2131 if (*domain_name == NULL) {
2132 *domain_name = dom;
2133 } else {
2134 TALLOC_FREE(dom);
2137 (*names)[i] = name;
2139 } else if (NT_STATUS_EQUAL(
2140 status,
2141 NT_STATUS_NONE_MAPPED)) {
2142 have_unmapped = true;
2143 } else {
2144 /* something's definitely wrong */
2145 result = status;
2146 goto error;
2150 if (!have_mapped) {
2151 return NT_STATUS_NONE_MAPPED;
2153 if (!have_unmapped) {
2154 return NT_STATUS_OK;
2156 return STATUS_SOME_UNMAPPED;
2160 None of the queried rids has been found so save all negative entries
2162 if (NT_STATUS_EQUAL(result, NT_STATUS_NONE_MAPPED)) {
2163 for (i = 0; i < num_rids; i++) {
2164 struct dom_sid sid;
2165 const char *name = "";
2166 const enum lsa_SidType type = SID_NAME_UNKNOWN;
2167 NTSTATUS status = NT_STATUS_NONE_MAPPED;
2169 if (!sid_compose(&sid, domain_sid, rids[i])) {
2170 return NT_STATUS_INTERNAL_ERROR;
2173 wcache_save_sid_to_name(domain, status, &sid, *domain_name,
2174 name, type);
2177 return result;
2181 Some or all of the queried rids have been found.
2183 if (!NT_STATUS_IS_OK(result) &&
2184 !NT_STATUS_EQUAL(result, STATUS_SOME_UNMAPPED)) {
2185 return result;
2188 refresh_sequence_number(domain);
2190 for (i=0; i<num_rids; i++) {
2191 struct dom_sid sid;
2192 NTSTATUS status;
2194 if (!sid_compose(&sid, domain_sid, rids[i])) {
2195 result = NT_STATUS_INTERNAL_ERROR;
2196 goto error;
2199 status = (*types)[i] == SID_NAME_UNKNOWN ?
2200 NT_STATUS_NONE_MAPPED : NT_STATUS_OK;
2202 wcache_save_sid_to_name(domain, status, &sid, *domain_name,
2203 (*names)[i], (*types)[i]);
2206 return result;
2208 error:
2209 TALLOC_FREE(*names);
2210 TALLOC_FREE(*types);
2211 return result;
2214 static NTSTATUS wcache_query_user(struct winbindd_domain *domain,
2215 TALLOC_CTX *mem_ctx,
2216 const struct dom_sid *user_sid,
2217 struct wbint_userinfo *info)
2219 struct winbind_cache *cache = get_cache(domain);
2220 struct cache_entry *centry = NULL;
2221 NTSTATUS status;
2222 struct dom_sid_buf sid_string;
2224 if (cache->tdb == NULL) {
2225 return NT_STATUS_NOT_FOUND;
2228 centry = wcache_fetch(
2229 cache, domain, "U/%s", dom_sid_str_buf(user_sid, &sid_string));
2230 if (centry == NULL) {
2231 return NT_STATUS_NOT_FOUND;
2234 /* if status is not ok then this is a negative hit
2235 and the rest of the data doesn't matter */
2236 status = centry->status;
2237 if (NT_STATUS_IS_OK(status)) {
2238 info->domain_name = centry_string(centry, mem_ctx);
2239 info->acct_name = centry_string(centry, mem_ctx);
2240 info->full_name = centry_string(centry, mem_ctx);
2241 info->homedir = centry_string(centry, mem_ctx);
2242 info->shell = centry_string(centry, mem_ctx);
2243 info->uid = centry_uint32(centry);
2244 info->primary_gid = centry_uint32(centry);
2245 info->primary_group_name = centry_string(centry, mem_ctx);
2246 centry_sid(centry, &info->user_sid);
2247 centry_sid(centry, &info->group_sid);
2250 DBG_DEBUG("query_user: [Cached] - cached info for domain %s status: "
2251 "%s\n", domain->name, nt_errstr(status) );
2253 centry_free(centry);
2254 return status;
2259 * @brief Query a fullname from the username cache (for further gecos processing)
2261 * @param domain A pointer to the winbindd_domain struct.
2262 * @param mem_ctx The talloc context.
2263 * @param user_sid The user sid.
2264 * @param full_name A pointer to the full_name string.
2266 * @return NTSTATUS code
2268 NTSTATUS wcache_query_user_fullname(struct winbindd_domain *domain,
2269 TALLOC_CTX *mem_ctx,
2270 const struct dom_sid *user_sid,
2271 const char **full_name)
2273 NTSTATUS status;
2274 struct wbint_userinfo info;
2276 status = wcache_query_user(domain, mem_ctx, user_sid, &info);
2277 if (!NT_STATUS_IS_OK(status)) {
2278 return status;
2281 if (info.full_name != NULL) {
2282 *full_name = talloc_strdup(mem_ctx, info.full_name);
2283 if (*full_name == NULL) {
2284 return NT_STATUS_NO_MEMORY;
2288 return NT_STATUS_OK;
2291 static NTSTATUS wcache_lookup_usergroups(struct winbindd_domain *domain,
2292 TALLOC_CTX *mem_ctx,
2293 const struct dom_sid *user_sid,
2294 uint32_t *pnum_sids,
2295 struct dom_sid **psids)
2297 struct winbind_cache *cache = get_cache(domain);
2298 struct cache_entry *centry = NULL;
2299 NTSTATUS status;
2300 uint32_t i, num_sids;
2301 struct dom_sid *sids;
2302 struct dom_sid_buf sid_string;
2304 if (cache->tdb == NULL) {
2305 return NT_STATUS_NOT_FOUND;
2308 centry = wcache_fetch(
2309 cache,
2310 domain,
2311 "UG/%s",
2312 dom_sid_str_buf(user_sid, &sid_string));
2313 if (centry == NULL) {
2314 return NT_STATUS_NOT_FOUND;
2317 num_sids = centry_uint32(centry);
2318 sids = talloc_array(mem_ctx, struct dom_sid, num_sids);
2319 if (sids == NULL) {
2320 centry_free(centry);
2321 return NT_STATUS_NO_MEMORY;
2324 for (i=0; i<num_sids; i++) {
2325 centry_sid(centry, &sids[i]);
2328 status = centry->status;
2330 DBG_DEBUG("lookup_usergroups: [Cached] - cached info for domain %s "
2331 "status: %s\n", domain->name, nt_errstr(status));
2333 centry_free(centry);
2335 *pnum_sids = num_sids;
2336 *psids = sids;
2337 return status;
2340 /* Lookup groups a user is a member of. */
2341 NTSTATUS wb_cache_lookup_usergroups(struct winbindd_domain *domain,
2342 TALLOC_CTX *mem_ctx,
2343 const struct dom_sid *user_sid,
2344 uint32_t *num_groups,
2345 struct dom_sid **user_gids)
2347 struct cache_entry *centry = NULL;
2348 NTSTATUS status;
2349 unsigned int i;
2350 struct dom_sid_buf sid_string;
2351 bool old_status;
2353 old_status = domain->online;
2354 status = wcache_lookup_usergroups(domain, mem_ctx, user_sid,
2355 num_groups, user_gids);
2356 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2357 return status;
2360 (*num_groups) = 0;
2361 (*user_gids) = NULL;
2363 DBG_DEBUG("lookup_usergroups: [Cached] - doing backend query for info for domain %s\n",
2364 domain->name );
2366 status = domain->backend->lookup_usergroups(domain, mem_ctx, user_sid, num_groups, user_gids);
2368 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2369 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2370 if (!domain->internal && old_status) {
2371 set_domain_offline(domain);
2373 if (!domain->internal &&
2374 !domain->online &&
2375 old_status) {
2376 NTSTATUS cache_status;
2377 cache_status = wcache_lookup_usergroups(domain, mem_ctx, user_sid,
2378 num_groups, user_gids);
2379 return cache_status;
2382 if ( NT_STATUS_EQUAL(status, NT_STATUS_SYNCHRONIZATION_REQUIRED) )
2383 goto skip_save;
2385 /* and save it */
2386 refresh_sequence_number(domain);
2387 if (!NT_STATUS_IS_OK(status)) {
2388 return status;
2390 centry = centry_start(domain, status);
2391 if (!centry)
2392 goto skip_save;
2394 centry_put_uint32(centry, *num_groups);
2395 for (i=0; i<(*num_groups); i++) {
2396 centry_put_sid(centry, &(*user_gids)[i]);
2399 centry_end(centry, "UG/%s", dom_sid_str_buf(user_sid, &sid_string));
2400 centry_free(centry);
2402 skip_save:
2403 return status;
2406 static char *wcache_make_sidlist(TALLOC_CTX *mem_ctx, uint32_t num_sids,
2407 const struct dom_sid *sids)
2409 uint32_t i;
2410 char *sidlist;
2412 sidlist = talloc_strdup(mem_ctx, "");
2413 if (sidlist == NULL) {
2414 return NULL;
2416 for (i=0; i<num_sids; i++) {
2417 struct dom_sid_buf tmp;
2418 sidlist = talloc_asprintf_append_buffer(
2419 sidlist,
2420 "/%s",
2421 dom_sid_str_buf(&sids[i], &tmp));
2422 if (sidlist == NULL) {
2423 return NULL;
2426 return sidlist;
2429 static NTSTATUS wcache_lookup_useraliases(struct winbindd_domain *domain,
2430 TALLOC_CTX *mem_ctx,
2431 uint32_t num_sids,
2432 const struct dom_sid *sids,
2433 uint32_t *pnum_aliases,
2434 uint32_t **paliases)
2436 struct winbind_cache *cache = get_cache(domain);
2437 struct cache_entry *centry = NULL;
2438 uint32_t i, num_aliases;
2439 uint32_t *aliases;
2440 NTSTATUS status;
2441 char *sidlist;
2443 if (cache->tdb == NULL) {
2444 return NT_STATUS_NOT_FOUND;
2447 if (num_sids == 0) {
2448 *pnum_aliases = 0;
2449 *paliases = NULL;
2450 return NT_STATUS_OK;
2453 /* We need to cache indexed by the whole list of SIDs, the aliases
2454 * resulting might come from any of the SIDs. */
2456 sidlist = wcache_make_sidlist(talloc_tos(), num_sids, sids);
2457 if (sidlist == NULL) {
2458 return NT_STATUS_NO_MEMORY;
2461 centry = wcache_fetch(cache, domain, "UA%s", sidlist);
2462 TALLOC_FREE(sidlist);
2463 if (centry == NULL) {
2464 return NT_STATUS_NOT_FOUND;
2467 num_aliases = centry_uint32(centry);
2468 aliases = talloc_array(mem_ctx, uint32_t, num_aliases);
2469 if (aliases == NULL) {
2470 centry_free(centry);
2471 return NT_STATUS_NO_MEMORY;
2474 for (i=0; i<num_aliases; i++) {
2475 aliases[i] = centry_uint32(centry);
2478 status = centry->status;
2480 DBG_DEBUG("lookup_useraliases: [Cached] - cached info for domain: %s "
2481 "status %s\n", domain->name, nt_errstr(status));
2483 centry_free(centry);
2485 *pnum_aliases = num_aliases;
2486 *paliases = aliases;
2488 return status;
2491 NTSTATUS wb_cache_lookup_useraliases(struct winbindd_domain *domain,
2492 TALLOC_CTX *mem_ctx,
2493 uint32_t num_sids,
2494 const struct dom_sid *sids,
2495 uint32_t *num_aliases,
2496 uint32_t **alias_rids)
2498 struct cache_entry *centry = NULL;
2499 NTSTATUS status;
2500 char *sidlist;
2501 uint32_t i;
2502 bool old_status;
2504 old_status = domain->online;
2505 status = wcache_lookup_useraliases(domain, mem_ctx, num_sids, sids,
2506 num_aliases, alias_rids);
2507 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2508 return status;
2511 (*num_aliases) = 0;
2512 (*alias_rids) = NULL;
2514 DBG_DEBUG("lookup_usergroups: [Cached] - doing backend query for info "
2515 "for domain %s\n", domain->name );
2517 sidlist = wcache_make_sidlist(talloc_tos(), num_sids, sids);
2518 if (sidlist == NULL) {
2519 return NT_STATUS_NO_MEMORY;
2522 status = domain->backend->lookup_useraliases(domain, mem_ctx,
2523 num_sids, sids,
2524 num_aliases, alias_rids);
2526 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2527 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2528 if (!domain->internal && old_status) {
2529 set_domain_offline(domain);
2531 if (!domain->internal &&
2532 !domain->online &&
2533 old_status) {
2534 NTSTATUS cache_status;
2535 cache_status = wcache_lookup_useraliases(domain, mem_ctx, num_sids,
2536 sids, num_aliases, alias_rids);
2537 return cache_status;
2540 /* and save it */
2541 refresh_sequence_number(domain);
2542 if (!NT_STATUS_IS_OK(status)) {
2543 return status;
2545 centry = centry_start(domain, status);
2546 if (!centry)
2547 goto skip_save;
2548 centry_put_uint32(centry, *num_aliases);
2549 for (i=0; i<(*num_aliases); i++)
2550 centry_put_uint32(centry, (*alias_rids)[i]);
2551 centry_end(centry, "UA%s", sidlist);
2552 centry_free(centry);
2554 skip_save:
2555 return status;
2558 static NTSTATUS wcache_lookup_aliasmem(struct winbindd_domain *domain,
2559 TALLOC_CTX *mem_ctx,
2560 const struct dom_sid *group_sid,
2561 uint32_t *num_names,
2562 struct dom_sid **sid_mem)
2564 struct winbind_cache *cache = get_cache(domain);
2565 struct cache_entry *centry = NULL;
2566 NTSTATUS status;
2567 unsigned int i;
2568 struct dom_sid_buf sid_string;
2570 if (cache->tdb == NULL) {
2571 return NT_STATUS_NOT_FOUND;
2574 centry = wcache_fetch(cache,
2575 domain,
2576 "AM/%s",
2577 dom_sid_str_buf(group_sid, &sid_string));
2578 if (centry == NULL) {
2579 return NT_STATUS_NOT_FOUND;
2582 *sid_mem = NULL;
2584 *num_names = centry_uint32(centry);
2585 if (*num_names == 0) {
2586 centry_free(centry);
2587 return NT_STATUS_OK;
2590 *sid_mem = talloc_array(mem_ctx, struct dom_sid, *num_names);
2591 if (*sid_mem == NULL) {
2592 TALLOC_FREE(*sid_mem);
2593 centry_free(centry);
2594 return NT_STATUS_NO_MEMORY;
2597 for (i = 0; i < (*num_names); i++) {
2598 centry_sid(centry, &(*sid_mem)[i]);
2601 status = centry->status;
2603 D_DEBUG("[Cached] - cached info for domain %s "
2604 "status: %s\n",
2605 domain->name,
2606 nt_errstr(status));
2608 centry_free(centry);
2609 return status;
2612 NTSTATUS wb_cache_lookup_aliasmem(struct winbindd_domain *domain,
2613 TALLOC_CTX *mem_ctx,
2614 const struct dom_sid *group_sid,
2615 enum lsa_SidType type,
2616 uint32_t *num_sids,
2617 struct dom_sid **sid_mem)
2619 struct cache_entry *centry = NULL;
2620 NTSTATUS status;
2621 unsigned int i;
2622 struct dom_sid_buf sid_string;
2623 bool old_status;
2625 old_status = domain->online;
2626 status = wcache_lookup_aliasmem(domain,
2627 mem_ctx,
2628 group_sid,
2629 num_sids,
2630 sid_mem);
2631 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2632 return status;
2635 (*num_sids) = 0;
2636 (*sid_mem) = NULL;
2638 D_DEBUG("[Cached] - doing backend query for info for domain %s\n",
2639 domain->name);
2641 status = domain->backend->lookup_aliasmem(domain,
2642 mem_ctx,
2643 group_sid,
2644 type,
2645 num_sids,
2646 sid_mem);
2648 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2649 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2650 if (!domain->internal && old_status) {
2651 set_domain_offline(domain);
2653 if (!domain->internal && !domain->online && old_status) {
2654 NTSTATUS cache_status;
2655 cache_status = wcache_lookup_aliasmem(domain,
2656 mem_ctx,
2657 group_sid,
2658 num_sids,
2659 sid_mem);
2660 return cache_status;
2663 /* and save it */
2664 refresh_sequence_number(domain);
2665 if (!NT_STATUS_IS_OK(status)) {
2666 return status;
2668 centry = centry_start(domain, status);
2669 if (!centry)
2670 goto skip_save;
2671 centry_put_uint32(centry, *num_sids);
2672 for (i = 0; i < (*num_sids); i++) {
2673 centry_put_sid(centry, &(*sid_mem)[i]);
2675 centry_end(centry, "AM/%s", dom_sid_str_buf(group_sid, &sid_string));
2676 centry_free(centry);
2678 skip_save:
2679 return status;
2682 static NTSTATUS wcache_lookup_groupmem(struct winbindd_domain *domain,
2683 TALLOC_CTX *mem_ctx,
2684 const struct dom_sid *group_sid,
2685 uint32_t *num_names,
2686 struct dom_sid **sid_mem, char ***names,
2687 uint32_t **name_types)
2689 struct winbind_cache *cache = get_cache(domain);
2690 struct cache_entry *centry = NULL;
2691 NTSTATUS status;
2692 unsigned int i;
2693 struct dom_sid_buf sid_string;
2695 if (cache->tdb == NULL) {
2696 return NT_STATUS_NOT_FOUND;
2699 centry = wcache_fetch(
2700 cache,
2701 domain,
2702 "GM/%s",
2703 dom_sid_str_buf(group_sid, &sid_string));
2704 if (centry == NULL) {
2705 return NT_STATUS_NOT_FOUND;
2708 *sid_mem = NULL;
2709 *names = NULL;
2710 *name_types = NULL;
2712 *num_names = centry_uint32(centry);
2713 if (*num_names == 0) {
2714 centry_free(centry);
2715 return NT_STATUS_OK;
2718 *sid_mem = talloc_array(mem_ctx, struct dom_sid, *num_names);
2719 *names = talloc_array(mem_ctx, char *, *num_names);
2720 *name_types = talloc_array(mem_ctx, uint32_t, *num_names);
2722 if ((*sid_mem == NULL) || (*names == NULL) || (*name_types == NULL)) {
2723 TALLOC_FREE(*sid_mem);
2724 TALLOC_FREE(*names);
2725 TALLOC_FREE(*name_types);
2726 centry_free(centry);
2727 return NT_STATUS_NO_MEMORY;
2730 for (i=0; i<(*num_names); i++) {
2731 centry_sid(centry, &(*sid_mem)[i]);
2732 (*names)[i] = centry_string(centry, mem_ctx);
2733 (*name_types)[i] = centry_uint32(centry);
2736 status = centry->status;
2738 DBG_DEBUG("lookup_groupmem: [Cached] - cached info for domain %s "
2739 "status: %s\n", domain->name, nt_errstr(status));
2741 centry_free(centry);
2742 return status;
2745 NTSTATUS wb_cache_lookup_groupmem(struct winbindd_domain *domain,
2746 TALLOC_CTX *mem_ctx,
2747 const struct dom_sid *group_sid,
2748 enum lsa_SidType type,
2749 uint32_t *num_names,
2750 struct dom_sid **sid_mem,
2751 char ***names,
2752 uint32_t **name_types)
2754 struct cache_entry *centry = NULL;
2755 NTSTATUS status;
2756 unsigned int i;
2757 struct dom_sid_buf sid_string;
2758 bool old_status;
2760 old_status = domain->online;
2761 status = wcache_lookup_groupmem(domain, mem_ctx, group_sid, num_names,
2762 sid_mem, names, name_types);
2763 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2764 return status;
2767 (*num_names) = 0;
2768 (*sid_mem) = NULL;
2769 (*names) = NULL;
2770 (*name_types) = NULL;
2772 DBG_DEBUG("lookup_groupmem: [Cached] - doing backend query for info for domain %s\n",
2773 domain->name );
2775 status = domain->backend->lookup_groupmem(domain, mem_ctx, group_sid,
2776 type, num_names,
2777 sid_mem, names, name_types);
2779 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2780 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2781 if (!domain->internal && old_status) {
2782 set_domain_offline(domain);
2784 if (!domain->internal &&
2785 !domain->online &&
2786 old_status) {
2787 NTSTATUS cache_status;
2788 cache_status = wcache_lookup_groupmem(domain, mem_ctx, group_sid,
2789 num_names, sid_mem, names,
2790 name_types);
2791 return cache_status;
2794 /* and save it */
2795 refresh_sequence_number(domain);
2796 if (!NT_STATUS_IS_OK(status)) {
2797 return status;
2799 centry = centry_start(domain, status);
2800 if (!centry)
2801 goto skip_save;
2802 centry_put_uint32(centry, *num_names);
2803 for (i=0; i<(*num_names); i++) {
2804 centry_put_sid(centry, &(*sid_mem)[i]);
2805 centry_put_string(centry, (*names)[i]);
2806 centry_put_uint32(centry, (*name_types)[i]);
2808 centry_end(centry,
2809 "GM/%s",
2810 dom_sid_str_buf(group_sid, &sid_string));
2811 centry_free(centry);
2813 skip_save:
2814 return status;
2817 /* find the sequence number for a domain */
2818 NTSTATUS wb_cache_sequence_number(struct winbindd_domain *domain,
2819 uint32_t *seq)
2821 refresh_sequence_number(domain);
2823 *seq = domain->sequence_number;
2825 return NT_STATUS_OK;
2828 /* enumerate trusted domains
2829 * (we need to have the list of trustdoms in the cache when we go offline) -
2830 * Guenther */
2831 NTSTATUS wb_cache_trusted_domains(struct winbindd_domain *domain,
2832 TALLOC_CTX *mem_ctx,
2833 struct netr_DomainTrustList *trusts)
2835 NTSTATUS status;
2836 struct winbind_cache *cache;
2837 struct winbindd_tdc_domain *dom_list = NULL;
2838 size_t num_domains = 0;
2839 bool retval = false;
2840 size_t i;
2841 bool old_status;
2843 old_status = domain->online;
2844 trusts->count = 0;
2845 trusts->array = NULL;
2847 cache = get_cache(domain);
2848 if (!cache || !cache->tdb) {
2849 goto do_query;
2852 if (domain->online) {
2853 goto do_query;
2856 retval = wcache_tdc_fetch_list(&dom_list, &num_domains);
2857 if (!retval || !num_domains || !dom_list) {
2858 TALLOC_FREE(dom_list);
2859 goto do_query;
2862 do_fetch_cache:
2863 trusts->array = talloc_zero_array(mem_ctx, struct netr_DomainTrust, num_domains);
2864 if (!trusts->array) {
2865 TALLOC_FREE(dom_list);
2866 return NT_STATUS_NO_MEMORY;
2869 for (i = 0; i < num_domains; i++) {
2870 struct netr_DomainTrust *trust;
2871 struct dom_sid *sid;
2872 struct winbindd_domain *dom;
2874 dom = find_domain_from_name_noinit(dom_list[i].domain_name);
2875 if (dom && dom->internal) {
2876 continue;
2879 trust = &trusts->array[trusts->count];
2880 trust->netbios_name = talloc_strdup(trusts->array, dom_list[i].domain_name);
2881 trust->dns_name = talloc_strdup(trusts->array, dom_list[i].dns_name);
2882 sid = talloc(trusts->array, struct dom_sid);
2883 if (!trust->netbios_name || !trust->dns_name ||
2884 !sid) {
2885 TALLOC_FREE(dom_list);
2886 TALLOC_FREE(trusts->array);
2887 return NT_STATUS_NO_MEMORY;
2890 trust->trust_flags = dom_list[i].trust_flags;
2891 trust->trust_attributes = dom_list[i].trust_attribs;
2892 trust->trust_type = dom_list[i].trust_type;
2893 sid_copy(sid, &dom_list[i].sid);
2894 trust->sid = sid;
2895 trusts->count++;
2898 TALLOC_FREE(dom_list);
2899 return NT_STATUS_OK;
2901 do_query:
2902 DBG_DEBUG("trusted_domains: [Cached] - doing backend query for info for domain %s\n",
2903 domain->name );
2905 status = domain->backend->trusted_domains(domain, mem_ctx, trusts);
2907 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2908 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2909 if (!domain->internal && old_status) {
2910 set_domain_offline(domain);
2912 if (!domain->internal &&
2913 !domain->online &&
2914 old_status) {
2915 retval = wcache_tdc_fetch_list(&dom_list, &num_domains);
2916 if (retval && num_domains && dom_list) {
2917 TALLOC_FREE(trusts->array);
2918 trusts->count = 0;
2919 goto do_fetch_cache;
2923 /* no trusts gives NT_STATUS_NO_MORE_ENTRIES resetting to NT_STATUS_OK
2924 * so that the generic centry handling still applies correctly -
2925 * Guenther*/
2927 if (!NT_STATUS_IS_ERR(status)) {
2928 status = NT_STATUS_OK;
2930 return status;
2933 /* get lockout policy */
2934 NTSTATUS wb_cache_lockout_policy(struct winbindd_domain *domain,
2935 TALLOC_CTX *mem_ctx,
2936 struct samr_DomInfo12 *policy)
2938 struct winbind_cache *cache = get_cache(domain);
2939 struct cache_entry *centry = NULL;
2940 NTSTATUS status;
2941 bool old_status;
2943 old_status = domain->online;
2944 if (!cache->tdb)
2945 goto do_query;
2947 centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
2949 if (!centry)
2950 goto do_query;
2952 do_fetch_cache:
2953 policy->lockout_duration = centry_nttime(centry);
2954 policy->lockout_window = centry_nttime(centry);
2955 policy->lockout_threshold = centry_uint16(centry);
2957 status = centry->status;
2959 DBG_DEBUG("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2960 domain->name, nt_errstr(status) );
2962 centry_free(centry);
2963 return status;
2965 do_query:
2966 ZERO_STRUCTP(policy);
2968 DBG_DEBUG("lockout_policy: [Cached] - doing backend query for info for domain %s\n",
2969 domain->name );
2971 status = domain->backend->lockout_policy(domain, mem_ctx, policy);
2973 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2974 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2975 if (!domain->internal && old_status) {
2976 set_domain_offline(domain);
2978 if (cache->tdb &&
2979 !domain->internal &&
2980 !domain->online &&
2981 old_status) {
2982 centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
2983 if (centry) {
2984 goto do_fetch_cache;
2988 /* and save it */
2989 refresh_sequence_number(domain);
2990 if (!NT_STATUS_IS_OK(status)) {
2991 return status;
2993 wcache_save_lockout_policy(domain, status, policy);
2995 return status;
2998 /* get password policy */
2999 NTSTATUS wb_cache_password_policy(struct winbindd_domain *domain,
3000 TALLOC_CTX *mem_ctx,
3001 struct samr_DomInfo1 *policy)
3003 struct winbind_cache *cache = get_cache(domain);
3004 struct cache_entry *centry = NULL;
3005 NTSTATUS status;
3006 bool old_status;
3008 old_status = domain->online;
3009 if (!cache->tdb)
3010 goto do_query;
3012 centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
3014 if (!centry)
3015 goto do_query;
3017 do_fetch_cache:
3018 policy->min_password_length = centry_uint16(centry);
3019 policy->password_history_length = centry_uint16(centry);
3020 policy->password_properties = centry_uint32(centry);
3021 policy->max_password_age = centry_nttime(centry);
3022 policy->min_password_age = centry_nttime(centry);
3024 status = centry->status;
3026 DBG_DEBUG("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
3027 domain->name, nt_errstr(status) );
3029 centry_free(centry);
3030 return status;
3032 do_query:
3033 ZERO_STRUCTP(policy);
3035 DBG_DEBUG("password_policy: [Cached] - doing backend query for info for domain %s\n",
3036 domain->name );
3038 status = domain->backend->password_policy(domain, mem_ctx, policy);
3040 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
3041 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
3042 if (!domain->internal && old_status) {
3043 set_domain_offline(domain);
3045 if (cache->tdb &&
3046 !domain->internal &&
3047 !domain->online &&
3048 old_status) {
3049 centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
3050 if (centry) {
3051 goto do_fetch_cache;
3055 /* and save it */
3056 refresh_sequence_number(domain);
3057 if (!NT_STATUS_IS_OK(status)) {
3058 return status;
3060 wcache_save_password_policy(domain, status, policy);
3062 return status;
3066 /* Invalidate cached user and group lists coherently */
3068 static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
3069 void *state)
3071 if (strncmp((const char *)kbuf.dptr, "UL/", 3) == 0 ||
3072 strncmp((const char *)kbuf.dptr, "GL/", 3) == 0)
3073 tdb_delete(the_tdb, kbuf);
3075 return 0;
3078 /* Invalidate the getpwnam and getgroups entries for a winbindd domain */
3080 void wcache_invalidate_samlogon(struct winbindd_domain *domain,
3081 const struct dom_sid *sid)
3083 fstring key_str;
3084 struct dom_sid_buf sid_string;
3085 struct winbind_cache *cache;
3087 /* don't clear cached U/SID and UG/SID entries when we want to logon
3088 * offline - gd */
3090 if (lp_winbind_offline_logon()) {
3091 return;
3094 if (!domain)
3095 return;
3097 cache = get_cache(domain);
3099 if (!cache->tdb) {
3100 return;
3103 /* Clear U/SID cache entry */
3104 fstr_sprintf(key_str, "U/%s", dom_sid_str_buf(sid, &sid_string));
3105 DBG_DEBUG("wcache_invalidate_samlogon: clearing %s\n", key_str);
3106 tdb_delete(cache->tdb, string_tdb_data(key_str));
3108 /* Clear UG/SID cache entry */
3109 fstr_sprintf(key_str, "UG/%s", dom_sid_str_buf(sid, &sid_string));
3110 DBG_DEBUG("wcache_invalidate_samlogon: clearing %s\n", key_str);
3111 tdb_delete(cache->tdb, string_tdb_data(key_str));
3113 /* Samba/winbindd never needs this. */
3114 netsamlogon_clear_cached_user(sid);
3117 bool wcache_invalidate_cache(void)
3119 struct winbindd_domain *domain;
3121 for (domain = domain_list(); domain; domain = domain->next) {
3122 struct winbind_cache *cache = get_cache(domain);
3124 DBG_DEBUG("wcache_invalidate_cache: invalidating cache "
3125 "entries for %s\n", domain->name);
3126 if (cache) {
3127 if (cache->tdb) {
3128 tdb_traverse(cache->tdb, traverse_fn, NULL);
3129 } else {
3130 return false;
3134 return true;
3137 bool wcache_invalidate_cache_noinit(void)
3139 struct winbindd_domain *domain;
3141 for (domain = domain_list(); domain; domain = domain->next) {
3142 struct winbind_cache *cache;
3144 /* Skip uninitialized domains. */
3145 if (!domain->initialized && !domain->internal) {
3146 continue;
3149 cache = get_cache(domain);
3151 DBG_DEBUG("wcache_invalidate_cache: invalidating cache "
3152 "entries for %s\n", domain->name);
3153 if (cache) {
3154 if (cache->tdb) {
3155 tdb_traverse(cache->tdb, traverse_fn, NULL);
3157 * Flushing cache has nothing to with domains.
3158 * return here if we successfully flushed once.
3159 * To avoid unnecessary traversing the cache.
3161 return true;
3162 } else {
3163 return false;
3167 return true;
3170 static bool init_wcache(void)
3172 char *db_path;
3174 if (wcache == NULL) {
3175 wcache = SMB_XMALLOC_P(struct winbind_cache);
3176 ZERO_STRUCTP(wcache);
3179 if (wcache->tdb != NULL)
3180 return true;
3182 db_path = wcache_path();
3183 if (db_path == NULL) {
3184 return false;
3187 /* when working offline we must not clear the cache on restart */
3188 wcache->tdb = tdb_open_log(db_path,
3189 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
3190 TDB_INCOMPATIBLE_HASH |
3191 (lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST)),
3192 O_RDWR|O_CREAT, 0600);
3193 TALLOC_FREE(db_path);
3194 if (wcache->tdb == NULL) {
3195 DBG_ERR("Failed to open winbindd_cache.tdb!\n");
3196 return false;
3199 return true;
3202 /************************************************************************
3203 This is called by the parent to initialize the cache file.
3204 We don't need sophisticated locking here as we know we're the
3205 only opener.
3206 ************************************************************************/
3208 bool initialize_winbindd_cache(void)
3210 bool cache_bad = false;
3211 uint32_t vers = 0;
3212 bool ok;
3214 if (!init_wcache()) {
3215 DBG_ERR("initialize_winbindd_cache: init_wcache failed.\n");
3216 return false;
3219 /* Check version number. */
3220 ok = tdb_fetch_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, &vers);
3221 if (!ok) {
3222 DBG_DEBUG("Failed to get cache version\n");
3223 cache_bad = true;
3225 if (vers != WINBINDD_CACHE_VERSION) {
3226 DBG_DEBUG("Invalid cache version %u != %u\n",
3227 vers,
3228 WINBINDD_CACHE_VERSION);
3229 cache_bad = true;
3232 if (cache_bad) {
3233 char *db_path;
3235 DBG_NOTICE("initialize_winbindd_cache: clearing cache "
3236 "and re-creating with version number %d\n",
3237 WINBINDD_CACHE_VERSION);
3239 tdb_close(wcache->tdb);
3240 wcache->tdb = NULL;
3242 db_path = wcache_path();
3243 if (db_path == NULL) {
3244 return false;
3247 if (unlink(db_path) == -1) {
3248 DBG_ERR("initialize_winbindd_cache: unlink %s failed %s\n",
3249 db_path,
3250 strerror(errno) );
3251 TALLOC_FREE(db_path);
3252 return false;
3254 TALLOC_FREE(db_path);
3255 if (!init_wcache()) {
3256 DBG_ERR("initialize_winbindd_cache: re-initialization "
3257 "init_wcache failed.\n");
3258 return false;
3261 /* Write the version. */
3262 if (!tdb_store_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, WINBINDD_CACHE_VERSION)) {
3263 DBG_ERR("initialize_winbindd_cache: version number store failed %s\n",
3264 tdb_errorstr(wcache->tdb) );
3265 return false;
3269 tdb_close(wcache->tdb);
3270 wcache->tdb = NULL;
3271 return true;
3274 void close_winbindd_cache(void)
3276 if (!wcache) {
3277 return;
3279 if (wcache->tdb) {
3280 tdb_close(wcache->tdb);
3281 wcache->tdb = NULL;
3285 bool lookup_cached_sid(TALLOC_CTX *mem_ctx, const struct dom_sid *sid,
3286 char **domain_name, char **name,
3287 enum lsa_SidType *type)
3289 struct winbindd_domain *domain;
3290 NTSTATUS status;
3292 domain = find_lookup_domain_from_sid(sid);
3293 if (domain == NULL) {
3294 return false;
3296 status = wcache_sid_to_name(domain, sid, mem_ctx, domain_name, name,
3297 type);
3298 return NT_STATUS_IS_OK(status);
3301 bool lookup_cached_name(const char *namespace,
3302 const char *domain_name,
3303 const char *name,
3304 struct dom_sid *sid,
3305 enum lsa_SidType *type)
3307 struct winbindd_domain *domain;
3308 NTSTATUS status;
3309 bool original_online_state;
3311 domain = find_lookup_domain_from_name(namespace);
3312 if (domain == NULL) {
3313 return false;
3316 /* If we are doing a cached logon, temporarily set the domain
3317 offline so the cache won't expire the entry */
3319 original_online_state = domain->online;
3320 domain->online = false;
3321 status = wcache_name_to_sid(domain, domain_name, name, sid, type);
3322 domain->online = original_online_state;
3324 return NT_STATUS_IS_OK(status);
3328 * Cache a name to sid without checking the sequence number.
3329 * Used when caching from a trusted PAC.
3332 void cache_name2sid_trusted(struct winbindd_domain *domain,
3333 const char *domain_name,
3334 const char *name,
3335 enum lsa_SidType type,
3336 const struct dom_sid *sid)
3339 * Ensure we store the mapping with the
3340 * existing sequence number from the cache.
3342 get_cache(domain);
3343 (void)fetch_cache_seqnum(domain, time(NULL));
3344 wcache_save_name_to_sid(domain,
3345 NT_STATUS_OK,
3346 domain_name,
3347 name,
3348 sid,
3349 type);
3352 void cache_name2sid(struct winbindd_domain *domain,
3353 const char *domain_name, const char *name,
3354 enum lsa_SidType type, const struct dom_sid *sid)
3356 refresh_sequence_number(domain);
3357 wcache_save_name_to_sid(domain, NT_STATUS_OK, domain_name, name,
3358 sid, type);
3362 * The original idea that this cache only contains centries has
3363 * been blurred - now other stuff gets put in here. Ensure we
3364 * ignore these things on cleanup.
3367 static int traverse_fn_cleanup(TDB_CONTEXT *the_tdb, TDB_DATA kbuf,
3368 TDB_DATA dbuf, void *state)
3370 struct cache_entry *centry;
3372 if (is_non_centry_key(kbuf)) {
3373 return 0;
3376 centry = wcache_fetch_raw((char *)kbuf.dptr);
3377 if (!centry) {
3378 return 0;
3381 if (!NT_STATUS_IS_OK(centry->status)) {
3382 DBG_DEBUG("deleting centry %s\n", (const char *)kbuf.dptr);
3383 tdb_delete(the_tdb, kbuf);
3386 centry_free(centry);
3387 return 0;
3390 /* flush the cache */
3391 static void wcache_flush_cache(void)
3393 char *db_path;
3395 if (!wcache)
3396 return;
3397 if (wcache->tdb) {
3398 tdb_close(wcache->tdb);
3399 wcache->tdb = NULL;
3401 if (!winbindd_use_cache()) {
3402 return;
3405 db_path = wcache_path();
3406 if (db_path == NULL) {
3407 return;
3410 /* when working offline we must not clear the cache on restart */
3411 wcache->tdb = tdb_open_log(db_path,
3412 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
3413 TDB_INCOMPATIBLE_HASH |
3414 (lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST)),
3415 O_RDWR|O_CREAT, 0600);
3416 TALLOC_FREE(db_path);
3417 if (!wcache->tdb) {
3418 DBG_ERR("Failed to open winbindd_cache.tdb!\n");
3419 return;
3422 tdb_traverse(wcache->tdb, traverse_fn_cleanup, NULL);
3424 DBG_DEBUG("wcache_flush_cache success\n");
3427 /* Count cached creds */
3429 static int traverse_fn_cached_creds(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
3430 void *state)
3432 int *cred_count = (int*)state;
3434 if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
3435 (*cred_count)++;
3437 return 0;
3440 NTSTATUS wcache_count_cached_creds(struct winbindd_domain *domain, int *count)
3442 struct winbind_cache *cache = get_cache(domain);
3444 *count = 0;
3446 if (!cache->tdb) {
3447 return NT_STATUS_INTERNAL_DB_ERROR;
3450 tdb_traverse(cache->tdb, traverse_fn_cached_creds, (void *)count);
3452 return NT_STATUS_OK;
3455 struct cred_list {
3456 struct cred_list *prev, *next;
3457 TDB_DATA key;
3458 fstring name;
3459 time_t created;
3461 static struct cred_list *wcache_cred_list;
3463 static int traverse_fn_get_credlist(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
3464 void *state)
3466 struct cred_list *cred;
3468 if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
3470 cred = SMB_MALLOC_P(struct cred_list);
3471 if (cred == NULL) {
3472 DBG_ERR("traverse_fn_remove_first_creds: failed to malloc new entry for list\n");
3473 return -1;
3476 ZERO_STRUCTP(cred);
3478 /* save a copy of the key */
3480 fstrcpy(cred->name, (const char *)kbuf.dptr);
3481 DLIST_ADD(wcache_cred_list, cred);
3484 return 0;
3487 NTSTATUS wcache_remove_oldest_cached_creds(struct winbindd_domain *domain, const struct dom_sid *sid)
3489 struct winbind_cache *cache = get_cache(domain);
3490 NTSTATUS status;
3491 int ret;
3492 struct cred_list *cred, *next, *oldest = NULL;
3494 if (!cache->tdb) {
3495 return NT_STATUS_INTERNAL_DB_ERROR;
3498 /* we possibly already have an entry */
3499 if (sid && NT_STATUS_IS_OK(wcache_cached_creds_exist(domain, sid))) {
3501 fstring key_str;
3502 struct dom_sid_buf tmp;
3504 DBG_DEBUG("we already have an entry, deleting that\n");
3506 fstr_sprintf(key_str, "CRED/%s", dom_sid_str_buf(sid, &tmp));
3508 tdb_delete(cache->tdb, string_tdb_data(key_str));
3510 return NT_STATUS_OK;
3513 ret = tdb_traverse(cache->tdb, traverse_fn_get_credlist, NULL);
3514 if (ret == 0) {
3515 return NT_STATUS_OK;
3516 } else if ((ret < 0) || (wcache_cred_list == NULL)) {
3517 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
3520 ZERO_STRUCTP(oldest);
3522 for (cred = wcache_cred_list; cred; cred = cred->next) {
3524 TDB_DATA data;
3525 time_t t;
3527 data = tdb_fetch(cache->tdb, string_tdb_data(cred->name));
3528 if (!data.dptr) {
3529 DBG_DEBUG("wcache_remove_oldest_cached_creds: entry for [%s] not found\n",
3530 cred->name);
3531 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
3532 goto done;
3535 t = IVAL(data.dptr, 0);
3536 SAFE_FREE(data.dptr);
3538 if (!oldest) {
3539 oldest = SMB_MALLOC_P(struct cred_list);
3540 if (oldest == NULL) {
3541 status = NT_STATUS_NO_MEMORY;
3542 goto done;
3545 fstrcpy(oldest->name, cred->name);
3546 oldest->created = t;
3547 continue;
3550 if (t < oldest->created) {
3551 fstrcpy(oldest->name, cred->name);
3552 oldest->created = t;
3556 if (tdb_delete(cache->tdb, string_tdb_data(oldest->name)) == 0) {
3557 status = NT_STATUS_OK;
3558 } else {
3559 status = NT_STATUS_UNSUCCESSFUL;
3561 done:
3562 for (cred = wcache_cred_list; cred; cred = next) {
3563 next = cred->next;
3564 DLIST_REMOVE(wcache_cred_list, cred);
3565 SAFE_FREE(cred);
3567 SAFE_FREE(oldest);
3569 return status;
3572 /* Change the global online/offline state. */
3573 bool set_global_winbindd_state_offline(void)
3575 bool ok;
3576 uint8_t buf[4] = {0};
3577 TDB_DATA data = {
3578 .dptr = buf,
3579 .dsize = sizeof(buf)
3581 int rc;
3583 DBG_NOTICE("Offline requested\n");
3585 if (wcache == NULL || wcache->tdb == NULL) {
3586 DBG_NOTICE("Winbind cache doesn't exist yet\n");
3587 return false;
3590 if (!lp_winbind_offline_logon()) {
3591 DBG_DEBUG("Rejecting request to set winbind offline, "
3592 "offline logons are disabled in smb.conf\n");
3593 return false;
3596 ok = get_global_winbindd_state_offline();
3597 if (ok) {
3598 return true;
3601 PUSH_LE_U32(buf, 0, time(NULL));
3603 rc = tdb_store_bystring(wcache->tdb,
3604 "WINBINDD_OFFLINE",
3605 data,
3606 TDB_INSERT);
3607 if (rc != 0) {
3608 return false;
3611 return true;
3615 void set_global_winbindd_state_online(void)
3617 DBG_DEBUG("set_global_winbindd_state_online: online requested.\n");
3619 if (!lp_winbind_offline_logon()) {
3620 DBG_DEBUG("Rejecting request to set winbind online, "
3621 "offline logons are disabled in smb.conf.\n");
3622 return;
3625 if (!wcache->tdb) {
3626 return;
3629 /* Ensure there is no key "WINBINDD_OFFLINE" in the cache tdb. */
3630 tdb_delete_bystring(wcache->tdb, "WINBINDD_OFFLINE");
3633 bool get_global_winbindd_state_offline(void)
3635 TDB_DATA data;
3637 data = tdb_fetch_bystring(wcache->tdb, "WINBINDD_OFFLINE");
3638 if (data.dptr == NULL || data.dsize != 4) {
3639 DBG_DEBUG("Offline state not set.\n");
3640 SAFE_FREE(data.dptr);
3641 return false;
3644 return true;
3647 /***********************************************************************
3648 Validate functions for all possible cache tdb keys.
3649 ***********************************************************************/
3651 static struct cache_entry *create_centry_validate(const char *kstr, TDB_DATA data,
3652 struct tdb_validation_status *state)
3654 struct cache_entry *centry;
3656 centry = SMB_XMALLOC_P(struct cache_entry);
3657 centry->data = (unsigned char *)smb_memdup(data.dptr, data.dsize);
3658 if (!centry->data) {
3659 SAFE_FREE(centry);
3660 return NULL;
3662 centry->len = data.dsize;
3663 centry->ofs = 0;
3665 if (centry->len < 16) {
3666 /* huh? corrupt cache? */
3667 DBG_ERR("create_centry_validate: Corrupt cache for key %s "
3668 "(len < 16) ?\n", kstr);
3669 centry_free(centry);
3670 state->bad_entry = true;
3671 state->success = false;
3672 return NULL;
3675 centry->status = NT_STATUS(centry_uint32(centry));
3676 centry->sequence_number = centry_uint32(centry);
3677 centry->timeout = centry_uint64_t(centry);
3678 return centry;
3681 static int validate_seqnum(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3682 struct tdb_validation_status *state)
3684 if (dbuf.dsize != 8) {
3685 DBG_ERR("validate_seqnum: Corrupt cache for key %s (len %u != 8) ?\n",
3686 keystr, (unsigned int)dbuf.dsize );
3687 state->bad_entry = true;
3688 return 1;
3690 return 0;
3693 static int validate_u(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3694 struct tdb_validation_status *state)
3696 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3697 struct dom_sid sid;
3699 if (!centry) {
3700 return 1;
3703 (void)centry_string(centry, mem_ctx);
3704 (void)centry_string(centry, mem_ctx);
3705 (void)centry_string(centry, mem_ctx);
3706 (void)centry_string(centry, mem_ctx);
3707 (void)centry_string(centry, mem_ctx);
3708 (void)centry_uint32(centry);
3709 (void)centry_uint32(centry);
3710 (void)centry_string(centry, mem_ctx);
3711 (void)centry_sid(centry, &sid);
3712 (void)centry_sid(centry, &sid);
3714 centry_free(centry);
3716 if (!(state->success)) {
3717 return 1;
3719 DBG_DEBUG("validate_u: %s ok\n", keystr);
3720 return 0;
3723 static int validate_loc_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3724 struct tdb_validation_status *state)
3726 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3728 if (!centry) {
3729 return 1;
3732 (void)centry_nttime(centry);
3733 (void)centry_nttime(centry);
3734 (void)centry_uint16(centry);
3736 centry_free(centry);
3738 if (!(state->success)) {
3739 return 1;
3741 DBG_DEBUG("validate_loc_pol: %s ok\n", keystr);
3742 return 0;
3745 static int validate_pwd_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3746 struct tdb_validation_status *state)
3748 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3750 if (!centry) {
3751 return 1;
3754 (void)centry_uint16(centry);
3755 (void)centry_uint16(centry);
3756 (void)centry_uint32(centry);
3757 (void)centry_nttime(centry);
3758 (void)centry_nttime(centry);
3760 centry_free(centry);
3762 if (!(state->success)) {
3763 return 1;
3765 DBG_DEBUG("validate_pwd_pol: %s ok\n", keystr);
3766 return 0;
3769 static int validate_cred(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3770 struct tdb_validation_status *state)
3772 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3774 if (!centry) {
3775 return 1;
3778 (void)centry_time(centry);
3779 (void)centry_hash16(centry, mem_ctx);
3781 /* We only have 17 bytes more data in the salted cred case. */
3782 if (centry->len - centry->ofs == 17) {
3783 (void)centry_hash16(centry, mem_ctx);
3786 centry_free(centry);
3788 if (!(state->success)) {
3789 return 1;
3791 DBG_DEBUG("validate_cred: %s ok\n", keystr);
3792 return 0;
3795 static int validate_ul(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3796 struct tdb_validation_status *state)
3798 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3799 int32_t num_entries, i;
3801 if (!centry) {
3802 return 1;
3805 num_entries = (int32_t)centry_uint32(centry);
3807 for (i=0; i< num_entries; i++) {
3808 (void)centry_uint32(centry);
3811 centry_free(centry);
3813 if (!(state->success)) {
3814 return 1;
3816 DBG_DEBUG("validate_ul: %s ok\n", keystr);
3817 return 0;
3820 static int validate_gl(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3821 struct tdb_validation_status *state)
3823 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3824 int32_t num_entries, i;
3826 if (!centry) {
3827 return 1;
3830 num_entries = centry_uint32(centry);
3832 for (i=0; i< num_entries; i++) {
3833 (void)centry_string(centry, mem_ctx);
3834 (void)centry_string(centry, mem_ctx);
3835 (void)centry_uint32(centry);
3838 centry_free(centry);
3840 if (!(state->success)) {
3841 return 1;
3843 DBG_DEBUG("validate_gl: %s ok\n", keystr);
3844 return 0;
3847 static int validate_ug(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3848 struct tdb_validation_status *state)
3850 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3851 int32_t num_groups, i;
3853 if (!centry) {
3854 return 1;
3857 num_groups = centry_uint32(centry);
3859 for (i=0; i< num_groups; i++) {
3860 struct dom_sid sid;
3861 centry_sid(centry, &sid);
3864 centry_free(centry);
3866 if (!(state->success)) {
3867 return 1;
3869 DBG_DEBUG("validate_ug: %s ok\n", keystr);
3870 return 0;
3873 static int validate_ua(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3874 struct tdb_validation_status *state)
3876 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3877 int32_t num_aliases, i;
3879 if (!centry) {
3880 return 1;
3883 num_aliases = centry_uint32(centry);
3885 for (i=0; i < num_aliases; i++) {
3886 (void)centry_uint32(centry);
3889 centry_free(centry);
3891 if (!(state->success)) {
3892 return 1;
3894 DBG_DEBUG("validate_ua: %s ok\n", keystr);
3895 return 0;
3898 static int validate_gm(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3899 struct tdb_validation_status *state)
3901 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3902 int32_t num_names, i;
3904 if (!centry) {
3905 return 1;
3908 num_names = centry_uint32(centry);
3910 for (i=0; i< num_names; i++) {
3911 struct dom_sid sid;
3912 centry_sid(centry, &sid);
3913 (void)centry_string(centry, mem_ctx);
3914 (void)centry_uint32(centry);
3917 centry_free(centry);
3919 if (!(state->success)) {
3920 return 1;
3922 DBG_DEBUG("validate_gm: %s ok\n", keystr);
3923 return 0;
3926 static int validate_dr(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3927 struct tdb_validation_status *state)
3929 /* Can't say anything about this other than must be nonzero. */
3930 if (dbuf.dsize == 0) {
3931 DBG_ERR("validate_dr: Corrupt cache for key %s (len == 0) ?\n",
3932 keystr);
3933 state->bad_entry = true;
3934 state->success = false;
3935 return 1;
3938 DBG_DEBUG("validate_dr: %s ok\n", keystr);
3939 return 0;
3942 static int validate_de(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3943 struct tdb_validation_status *state)
3945 /* Can't say anything about this other than must be nonzero. */
3946 if (dbuf.dsize == 0) {
3947 DBG_ERR("validate_de: Corrupt cache for key %s (len == 0) ?\n",
3948 keystr);
3949 state->bad_entry = true;
3950 state->success = false;
3951 return 1;
3954 DBG_DEBUG("validate_de: %s ok\n", keystr);
3955 return 0;
3958 static int validate_nss_an(TALLOC_CTX *mem_ctx, const char *keystr,
3959 TDB_DATA dbuf,
3960 struct tdb_validation_status *state)
3962 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3964 if (!centry) {
3965 return 1;
3968 (void)centry_string( centry, mem_ctx );
3970 centry_free(centry);
3972 if (!(state->success)) {
3973 return 1;
3975 DBG_DEBUG("validate_pwinfo: %s ok\n", keystr);
3976 return 0;
3979 static int validate_nss_na(TALLOC_CTX *mem_ctx, const char *keystr,
3980 TDB_DATA dbuf,
3981 struct tdb_validation_status *state)
3983 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3985 if (!centry) {
3986 return 1;
3989 (void)centry_string( centry, mem_ctx );
3991 centry_free(centry);
3993 if (!(state->success)) {
3994 return 1;
3996 DBG_DEBUG("%s ok\n", keystr);
3997 return 0;
4000 static int validate_trustdomcache(TALLOC_CTX *mem_ctx, const char *keystr,
4001 TDB_DATA dbuf,
4002 struct tdb_validation_status *state)
4004 if (dbuf.dsize == 0) {
4005 DBG_ERR("validate_trustdomcache: Corrupt cache for "
4006 "key %s (len ==0) ?\n", keystr);
4007 state->bad_entry = true;
4008 state->success = false;
4009 return 1;
4012 DBG_DEBUG("validate_trustdomcache: %s ok\n"
4013 " Don't trust me, I am a DUMMY!\n",
4014 keystr);
4015 return 0;
4018 static int validate_offline(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
4019 struct tdb_validation_status *state)
4021 if (dbuf.dsize != 4) {
4022 DBG_ERR("validate_offline: Corrupt cache for key %s (len %u != 4) ?\n",
4023 keystr, (unsigned int)dbuf.dsize );
4024 state->bad_entry = true;
4025 state->success = false;
4026 return 1;
4028 DBG_DEBUG("validate_offline: %s ok\n", keystr);
4029 return 0;
4032 static int validate_ndr(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
4033 struct tdb_validation_status *state)
4036 * Ignore validation for now. The proper way to do this is with a
4037 * checksum. Just pure parsing does not really catch much.
4039 return 0;
4042 static int validate_cache_version(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
4043 struct tdb_validation_status *state)
4045 if (dbuf.dsize != 4) {
4046 DBG_ERR("validate_cache_version: Corrupt cache for "
4047 "key %s (len %u != 4) ?\n",
4048 keystr, (unsigned int)dbuf.dsize);
4049 state->bad_entry = true;
4050 state->success = false;
4051 return 1;
4054 DBG_DEBUG("validate_cache_version: %s ok\n", keystr);
4055 return 0;
4058 /***********************************************************************
4059 A list of all possible cache tdb keys with associated validation
4060 functions.
4061 ***********************************************************************/
4063 struct key_val_struct {
4064 const char *keyname;
4065 int (*validate_data_fn)(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, struct tdb_validation_status* state);
4066 } key_val[] = {
4067 {"SEQNUM/", validate_seqnum},
4068 {"U/", validate_u},
4069 {"LOC_POL/", validate_loc_pol},
4070 {"PWD_POL/", validate_pwd_pol},
4071 {"CRED/", validate_cred},
4072 {"UL/", validate_ul},
4073 {"GL/", validate_gl},
4074 {"UG/", validate_ug},
4075 {"UA", validate_ua},
4076 {"GM/", validate_gm},
4077 {"DR/", validate_dr},
4078 {"DE/", validate_de},
4079 {"TRUSTDOMCACHE/", validate_trustdomcache},
4080 {"NSS/NA/", validate_nss_na},
4081 {"NSS/AN/", validate_nss_an},
4082 {"WINBINDD_OFFLINE", validate_offline},
4083 {"NDR/", validate_ndr},
4084 {WINBINDD_CACHE_VERSION_KEYSTR, validate_cache_version},
4085 {NULL, NULL}
4088 /***********************************************************************
4089 Function to look at every entry in the tdb and validate it as far as
4090 possible.
4091 ***********************************************************************/
4093 static int cache_traverse_validate_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, void *state)
4095 int i;
4096 unsigned int max_key_len = 1024;
4097 struct tdb_validation_status *v_state = (struct tdb_validation_status *)state;
4099 /* Paranoia check. */
4100 if (strncmp("UA/", (const char *)kbuf.dptr, 3) == 0 ||
4101 strncmp("NDR/", (const char *)kbuf.dptr, 4) == 0) {
4102 max_key_len = 1024 * 1024;
4104 if (kbuf.dsize > max_key_len) {
4105 DBG_ERR("cache_traverse_validate_fn: key length too large: "
4106 "(%u) > (%u)\n\n",
4107 (unsigned int)kbuf.dsize, (unsigned int)max_key_len);
4108 return 1;
4111 for (i = 0; key_val[i].keyname; i++) {
4112 size_t namelen = strlen(key_val[i].keyname);
4113 if (kbuf.dsize >= namelen && (
4114 strncmp(key_val[i].keyname, (const char *)kbuf.dptr, namelen)) == 0) {
4115 TALLOC_CTX *mem_ctx;
4116 char *keystr;
4117 int ret;
4119 keystr = SMB_MALLOC_ARRAY(char, kbuf.dsize+1);
4120 if (!keystr) {
4121 return 1;
4123 memcpy(keystr, kbuf.dptr, kbuf.dsize);
4124 keystr[kbuf.dsize] = '\0';
4126 mem_ctx = talloc_init("validate_ctx");
4127 if (!mem_ctx) {
4128 SAFE_FREE(keystr);
4129 return 1;
4132 ret = key_val[i].validate_data_fn(mem_ctx, keystr, dbuf,
4133 v_state);
4135 SAFE_FREE(keystr);
4136 talloc_destroy(mem_ctx);
4137 return ret;
4141 DBG_ERR("cache_traverse_validate_fn: unknown cache entry\nkey :\n");
4142 dump_data(0, (uint8_t *)kbuf.dptr, kbuf.dsize);
4143 DBG_ERR("data :\n");
4144 dump_data(0, (uint8_t *)dbuf.dptr, dbuf.dsize);
4145 v_state->unknown_key = true;
4146 v_state->success = false;
4147 return 1; /* terminate. */
4150 static void validate_panic(const char *const why)
4152 DBG_ERR("validating cache: would panic %s\n"
4153 "exiting instead (cache validation mode)\n", why );
4154 exit(47);
4157 static int wbcache_update_centry_fn(TDB_CONTEXT *tdb,
4158 TDB_DATA key,
4159 TDB_DATA data,
4160 void *state)
4162 uint64_t ctimeout;
4163 TDB_DATA blob;
4165 if (is_non_centry_key(key)) {
4166 return 0;
4169 if (data.dptr == NULL || data.dsize == 0) {
4170 if (tdb_delete(tdb, key) < 0) {
4171 DBG_ERR("tdb_delete for [%s] failed!\n",
4172 key.dptr);
4173 return 1;
4177 /* add timeout to blob (uint64_t) */
4178 blob.dsize = data.dsize + 8;
4180 blob.dptr = SMB_XMALLOC_ARRAY(uint8_t, blob.dsize);
4181 if (blob.dptr == NULL) {
4182 return 1;
4184 memset(blob.dptr, 0, blob.dsize);
4186 /* copy status and seqnum */
4187 memcpy(blob.dptr, data.dptr, 8);
4189 /* add timeout */
4190 ctimeout = lp_winbind_cache_time() + time(NULL);
4191 SBVAL(blob.dptr, 8, ctimeout);
4193 /* copy the rest */
4194 memcpy(blob.dptr + 16, data.dptr + 8, data.dsize - 8);
4196 if (tdb_store(tdb, key, blob, TDB_REPLACE) < 0) {
4197 DBG_ERR("tdb_store to update [%s] failed!\n",
4198 key.dptr);
4199 SAFE_FREE(blob.dptr);
4200 return 1;
4203 SAFE_FREE(blob.dptr);
4204 return 0;
4207 static bool wbcache_upgrade_v1_to_v2(TDB_CONTEXT *tdb)
4209 int rc;
4211 DBG_NOTICE("Upgrade to version 2 of the winbindd_cache.tdb\n");
4213 rc = tdb_traverse(tdb, wbcache_update_centry_fn, NULL);
4214 if (rc < 0) {
4215 return false;
4218 return true;
4221 /***********************************************************************
4222 Try and validate every entry in the winbindd cache. If we fail here,
4223 delete the cache tdb and return non-zero.
4224 ***********************************************************************/
4226 int winbindd_validate_cache(void)
4228 int ret = -1;
4229 char *tdb_path = NULL;
4230 TDB_CONTEXT *tdb = NULL;
4231 uint32_t vers_id;
4232 bool ok;
4234 DBG_DEBUG("winbindd_validate_cache: replacing panic function\n");
4235 smb_panic_fn = validate_panic;
4237 tdb_path = wcache_path();
4238 if (tdb_path == NULL) {
4239 goto done;
4242 tdb = tdb_open_log(tdb_path,
4243 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
4244 TDB_INCOMPATIBLE_HASH |
4245 ( lp_winbind_offline_logon()
4246 ? TDB_DEFAULT
4247 : TDB_DEFAULT | TDB_CLEAR_IF_FIRST ),
4248 O_RDWR|O_CREAT,
4249 0600);
4250 if (!tdb) {
4251 DBG_ERR("winbindd_validate_cache: "
4252 "error opening/initializing tdb\n");
4253 goto done;
4256 /* Version check and upgrade code. */
4257 if (!tdb_fetch_uint32(tdb, WINBINDD_CACHE_VERSION_KEYSTR, &vers_id)) {
4258 DBG_DEBUG("Fresh database\n");
4259 tdb_store_uint32(tdb, WINBINDD_CACHE_VERSION_KEYSTR, WINBINDD_CACHE_VERSION);
4260 vers_id = WINBINDD_CACHE_VERSION;
4263 if (vers_id != WINBINDD_CACHE_VERSION) {
4264 if (vers_id == WINBINDD_CACHE_VER1) {
4265 ok = wbcache_upgrade_v1_to_v2(tdb);
4266 if (!ok) {
4267 DBG_DEBUG("winbindd_validate_cache: upgrade to version 2 failed.\n");
4268 unlink(tdb_path);
4269 goto done;
4272 tdb_store_uint32(tdb,
4273 WINBINDD_CACHE_VERSION_KEYSTR,
4274 WINBINDD_CACHE_VERSION);
4275 vers_id = WINBINDD_CACHE_VER2;
4279 tdb_close(tdb);
4281 ret = tdb_validate_and_backup(tdb_path, cache_traverse_validate_fn);
4283 if (ret != 0) {
4284 DBG_DEBUG("winbindd_validate_cache: validation not successful.\n"
4285 "removing tdb %s.\n", tdb_path);
4286 unlink(tdb_path);
4289 done:
4290 TALLOC_FREE(tdb_path);
4291 DBG_DEBUG("winbindd_validate_cache: restoring panic function\n");
4292 smb_panic_fn = smb_panic;
4293 return ret;
4296 /***********************************************************************
4297 Try and validate every entry in the winbindd cache.
4298 ***********************************************************************/
4300 int winbindd_validate_cache_nobackup(void)
4302 int ret = -1;
4303 char *tdb_path;
4305 DBG_DEBUG("winbindd_validate_cache: replacing panic function\n");
4306 smb_panic_fn = validate_panic;
4308 tdb_path = wcache_path();
4309 if (tdb_path == NULL) {
4310 goto err_panic_restore;
4313 if (wcache == NULL || wcache->tdb == NULL) {
4314 ret = tdb_validate_open(tdb_path, cache_traverse_validate_fn);
4315 } else {
4316 ret = tdb_validate(wcache->tdb, cache_traverse_validate_fn);
4319 if (ret != 0) {
4320 DBG_DEBUG("winbindd_validate_cache_nobackup: validation not "
4321 "successful.\n");
4324 TALLOC_FREE(tdb_path);
4325 err_panic_restore:
4326 DBG_DEBUG("winbindd_validate_cache_nobackup: restoring panic "
4327 "function\n");
4328 smb_panic_fn = smb_panic;
4329 return ret;
4332 bool winbindd_cache_validate_and_initialize(void)
4334 close_winbindd_cache();
4336 if (lp_winbind_offline_logon()) {
4337 if (winbindd_validate_cache() < 0) {
4338 DBG_ERR("winbindd cache tdb corrupt and no backup "
4339 "could be restored.\n");
4343 return initialize_winbindd_cache();
4346 /*********************************************************************
4347 ********************************************************************/
4349 static bool add_wbdomain_to_tdc_array( struct winbindd_domain *new_dom,
4350 struct winbindd_tdc_domain **domains,
4351 size_t *num_domains )
4353 struct winbindd_tdc_domain *list = NULL;
4354 size_t i, idx;
4355 bool set_only = false;
4357 /* don't allow duplicates */
4359 idx = *num_domains;
4360 list = *domains;
4362 for ( i=0; i< (*num_domains); i++ ) {
4363 if ( strequal( new_dom->name, list[i].domain_name ) ) {
4364 DBG_DEBUG("add_wbdomain_to_tdc_array: Found existing record for %s\n",
4365 new_dom->name);
4366 idx = i;
4367 set_only = true;
4369 break;
4373 if ( !set_only ) {
4374 if ( !*domains ) {
4375 list = talloc_array( NULL, struct winbindd_tdc_domain, 1 );
4376 idx = 0;
4377 } else {
4378 list = talloc_realloc( *domains, *domains,
4379 struct winbindd_tdc_domain,
4380 (*num_domains)+1);
4381 idx = *num_domains;
4384 ZERO_STRUCT( list[idx] );
4387 if ( !list )
4388 return false;
4390 list[idx].domain_name = talloc_strdup(list, new_dom->name);
4391 if (list[idx].domain_name == NULL) {
4392 return false;
4394 if (new_dom->alt_name != NULL) {
4395 list[idx].dns_name = talloc_strdup(list, new_dom->alt_name);
4396 if (list[idx].dns_name == NULL) {
4397 return false;
4401 if ( !is_null_sid( &new_dom->sid ) ) {
4402 sid_copy( &list[idx].sid, &new_dom->sid );
4403 } else {
4404 sid_copy(&list[idx].sid, &global_sid_NULL);
4407 if ( new_dom->domain_flags != 0x0 )
4408 list[idx].trust_flags = new_dom->domain_flags;
4410 if ( new_dom->domain_type != 0x0 )
4411 list[idx].trust_type = new_dom->domain_type;
4413 if ( new_dom->domain_trust_attribs != 0x0 )
4414 list[idx].trust_attribs = new_dom->domain_trust_attribs;
4416 if ( !set_only ) {
4417 *domains = list;
4418 *num_domains = idx + 1;
4421 return true;
4424 /*********************************************************************
4425 ********************************************************************/
4427 static TDB_DATA make_tdc_key( const char *domain_name )
4429 char *keystr = NULL;
4430 TDB_DATA key = { NULL, 0 };
4432 if ( !domain_name ) {
4433 DBG_INFO("make_tdc_key: Keyname workgroup is NULL!\n");
4434 return key;
4437 if (asprintf( &keystr, "TRUSTDOMCACHE/%s", domain_name ) == -1) {
4438 return key;
4440 key = string_term_tdb_data(keystr);
4442 return key;
4445 /*********************************************************************
4446 ********************************************************************/
4448 static int pack_tdc_domains( struct winbindd_tdc_domain *domains,
4449 size_t num_domains,
4450 unsigned char **buf )
4452 unsigned char *buffer = NULL;
4453 int len = 0;
4454 int buflen = 0;
4455 size_t i = 0;
4457 DBG_DEBUG("pack_tdc_domains: Packing %d trusted domains\n",
4458 (int)num_domains);
4460 buflen = 0;
4462 again:
4463 len = 0;
4465 /* Store the number of array items first */
4466 len += tdb_pack( buffer ? buffer+len : NULL,
4467 buffer ? buflen-len : 0, "d",
4468 num_domains );
4470 /* now pack each domain trust record */
4471 for ( i=0; i<num_domains; i++ ) {
4473 struct dom_sid_buf tmp;
4475 if ( buflen > 0 ) {
4476 DBG_DEBUG("pack_tdc_domains: Packing domain %s (%s)\n",
4477 domains[i].domain_name,
4478 domains[i].dns_name ? domains[i].dns_name : "UNKNOWN" );
4481 len += tdb_pack( buffer ? buffer+len : NULL,
4482 buffer ? buflen-len : 0, "fffddd",
4483 domains[i].domain_name,
4484 domains[i].dns_name ? domains[i].dns_name : "",
4485 dom_sid_str_buf(&domains[i].sid, &tmp),
4486 domains[i].trust_flags,
4487 domains[i].trust_attribs,
4488 domains[i].trust_type );
4491 if ( buflen < len ) {
4492 SAFE_FREE(buffer);
4493 if ( (buffer = SMB_MALLOC_ARRAY(unsigned char, len)) == NULL ) {
4494 DBG_ERR("pack_tdc_domains: failed to alloc buffer!\n");
4495 buflen = -1;
4496 goto done;
4498 buflen = len;
4499 goto again;
4502 *buf = buffer;
4504 done:
4505 return buflen;
4508 /*********************************************************************
4509 ********************************************************************/
4511 static size_t unpack_tdc_domains( unsigned char *buf, int buflen,
4512 struct winbindd_tdc_domain **domains )
4514 fstring domain_name, dns_name, sid_string;
4515 uint32_t type, attribs, flags;
4516 int num_domains;
4517 int len = 0;
4518 int i;
4519 struct winbindd_tdc_domain *list = NULL;
4521 /* get the number of domains */
4522 len += tdb_unpack( buf+len, buflen-len, "d", &num_domains);
4523 if ( len == -1 ) {
4524 DBG_INFO("unpack_tdc_domains: Failed to unpack domain array\n");
4525 return 0;
4528 list = talloc_array( NULL, struct winbindd_tdc_domain, num_domains );
4529 if ( !list ) {
4530 DBG_ERR("unpack_tdc_domains: Failed to talloc() domain list!\n");
4531 return 0;
4534 for ( i=0; i<num_domains; i++ ) {
4535 int this_len;
4537 this_len = tdb_unpack( buf+len, buflen-len, "fffddd",
4538 domain_name,
4539 dns_name,
4540 sid_string,
4541 &flags,
4542 &attribs,
4543 &type );
4545 if ( this_len == -1 ) {
4546 DBG_INFO("unpack_tdc_domains: Failed to unpack domain array\n");
4547 TALLOC_FREE( list );
4548 return 0;
4550 len += this_len;
4552 DBG_DEBUG("unpack_tdc_domains: Unpacking domain %s (%s) "
4553 "SID %s, flags = 0x%x, attribs = 0x%x, type = 0x%x\n",
4554 domain_name, dns_name, sid_string,
4555 flags, attribs, type);
4557 list[i].domain_name = talloc_strdup( list, domain_name );
4558 list[i].dns_name = NULL;
4559 if (dns_name[0] != '\0') {
4560 list[i].dns_name = talloc_strdup(list, dns_name);
4562 if ( !string_to_sid( &(list[i].sid), sid_string ) ) {
4563 DBG_DEBUG("unpack_tdc_domains: no SID for domain %s\n",
4564 domain_name);
4566 list[i].trust_flags = flags;
4567 list[i].trust_attribs = attribs;
4568 list[i].trust_type = type;
4571 *domains = list;
4573 return num_domains;
4576 /*********************************************************************
4577 ********************************************************************/
4579 static bool wcache_tdc_store_list( struct winbindd_tdc_domain *domains, size_t num_domains )
4581 TDB_DATA key = make_tdc_key( lp_workgroup() );
4582 TDB_DATA data = { NULL, 0 };
4583 int ret;
4585 if ( !key.dptr )
4586 return false;
4588 /* See if we were asked to delete the cache entry */
4590 if ( !domains ) {
4591 ret = tdb_delete( wcache->tdb, key );
4592 goto done;
4595 data.dsize = pack_tdc_domains( domains, num_domains, &data.dptr );
4597 if ( !data.dptr ) {
4598 ret = -1;
4599 goto done;
4602 ret = tdb_store(wcache->tdb, key, data, TDB_REPLACE);
4604 done:
4605 SAFE_FREE( data.dptr );
4606 SAFE_FREE( key.dptr );
4608 return ( ret == 0 );
4611 /*********************************************************************
4612 ********************************************************************/
4614 bool wcache_tdc_fetch_list( struct winbindd_tdc_domain **domains, size_t *num_domains )
4616 TDB_DATA key = make_tdc_key( lp_workgroup() );
4617 TDB_DATA data = { NULL, 0 };
4619 *domains = NULL;
4620 *num_domains = 0;
4622 if ( !key.dptr )
4623 return false;
4625 data = tdb_fetch( wcache->tdb, key );
4627 SAFE_FREE( key.dptr );
4629 if ( !data.dptr )
4630 return false;
4632 *num_domains = unpack_tdc_domains( data.dptr, data.dsize, domains );
4634 SAFE_FREE( data.dptr );
4636 if ( !*domains )
4637 return false;
4639 return true;
4642 /*********************************************************************
4643 ********************************************************************/
4645 bool wcache_tdc_add_domain( struct winbindd_domain *domain )
4647 struct winbindd_tdc_domain *dom_list = NULL;
4648 size_t num_domains = 0;
4649 bool ret = false;
4650 struct dom_sid_buf buf;
4652 DBG_DEBUG("wcache_tdc_add_domain: Adding domain %s (%s), SID %s, "
4653 "flags = 0x%x, attributes = 0x%x, type = 0x%x\n",
4654 domain->name, domain->alt_name,
4655 dom_sid_str_buf(&domain->sid, &buf),
4656 domain->domain_flags,
4657 domain->domain_trust_attribs,
4658 domain->domain_type);
4660 if ( !init_wcache() ) {
4661 return false;
4664 /* fetch the list */
4666 wcache_tdc_fetch_list( &dom_list, &num_domains );
4668 /* add the new domain */
4670 if ( !add_wbdomain_to_tdc_array( domain, &dom_list, &num_domains ) ) {
4671 goto done;
4674 /* pack the domain */
4676 if ( !wcache_tdc_store_list( dom_list, num_domains ) ) {
4677 goto done;
4680 /* Success */
4682 ret = true;
4683 done:
4684 TALLOC_FREE( dom_list );
4686 return ret;
4689 static struct winbindd_tdc_domain *wcache_tdc_dup_domain(
4690 TALLOC_CTX *mem_ctx, const struct winbindd_tdc_domain *src)
4692 struct winbindd_tdc_domain *dst;
4694 dst = talloc(mem_ctx, struct winbindd_tdc_domain);
4695 if (dst == NULL) {
4696 goto fail;
4698 dst->domain_name = talloc_strdup(dst, src->domain_name);
4699 if (dst->domain_name == NULL) {
4700 goto fail;
4703 dst->dns_name = NULL;
4704 if (src->dns_name != NULL) {
4705 dst->dns_name = talloc_strdup(dst, src->dns_name);
4706 if (dst->dns_name == NULL) {
4707 goto fail;
4711 sid_copy(&dst->sid, &src->sid);
4712 dst->trust_flags = src->trust_flags;
4713 dst->trust_type = src->trust_type;
4714 dst->trust_attribs = src->trust_attribs;
4715 return dst;
4716 fail:
4717 TALLOC_FREE(dst);
4718 return NULL;
4721 /*********************************************************************
4722 ********************************************************************/
4724 struct winbindd_tdc_domain * wcache_tdc_fetch_domain( TALLOC_CTX *ctx, const char *name )
4726 struct winbindd_tdc_domain *dom_list = NULL;
4727 size_t num_domains = 0;
4728 size_t i;
4729 struct winbindd_tdc_domain *d = NULL;
4731 DBG_DEBUG("wcache_tdc_fetch_domain: Searching for domain %s\n", name);
4733 if ( !init_wcache() ) {
4734 return NULL;
4737 /* fetch the list */
4739 wcache_tdc_fetch_list( &dom_list, &num_domains );
4741 for ( i=0; i<num_domains; i++ ) {
4742 if ( strequal(name, dom_list[i].domain_name) ||
4743 strequal(name, dom_list[i].dns_name) )
4745 DBG_DEBUG("wcache_tdc_fetch_domain: Found domain %s\n",
4746 name);
4748 d = wcache_tdc_dup_domain(ctx, &dom_list[i]);
4749 break;
4753 TALLOC_FREE( dom_list );
4755 return d;
4758 /*********************************************************************
4759 ********************************************************************/
4761 void wcache_tdc_clear( void )
4763 if ( !init_wcache() )
4764 return;
4766 wcache_tdc_store_list( NULL, 0 );
4768 return;
4771 static bool wcache_ndr_key(TALLOC_CTX *mem_ctx, const char *domain_name,
4772 uint32_t opnum, const DATA_BLOB *req,
4773 TDB_DATA *pkey)
4775 char *key;
4776 size_t keylen;
4778 key = talloc_asprintf(mem_ctx, "NDR/%s/%d/", domain_name, (int)opnum);
4779 if (key == NULL) {
4780 return false;
4782 keylen = talloc_get_size(key) - 1;
4784 key = talloc_realloc(mem_ctx, key, char, keylen + req->length);
4785 if (key == NULL) {
4786 return false;
4788 memcpy(key + keylen, req->data, req->length);
4790 pkey->dptr = (uint8_t *)key;
4791 pkey->dsize = talloc_get_size(key);
4792 return true;
4795 static bool wcache_opnum_cacheable(uint32_t opnum)
4797 switch (opnum) {
4798 case NDR_WBINT_LOOKUPSID:
4799 case NDR_WBINT_LOOKUPSIDS:
4800 case NDR_WBINT_LOOKUPNAME:
4801 case NDR_WBINT_SIDS2UNIXIDS:
4802 case NDR_WBINT_UNIXIDS2SIDS:
4803 case NDR_WBINT_GETNSSINFO:
4804 case NDR_WBINT_LOOKUPUSERALIASES:
4805 case NDR_WBINT_LOOKUPUSERGROUPS:
4806 case NDR_WBINT_LOOKUPGROUPMEMBERS:
4807 case NDR_WBINT_QUERYGROUPLIST:
4808 case NDR_WBINT_QUERYUSERRIDLIST:
4809 case NDR_WBINT_DSGETDCNAME:
4810 case NDR_WBINT_LOOKUPRIDS:
4811 return true;
4813 return false;
4816 bool wcache_fetch_ndr(TALLOC_CTX *mem_ctx, struct winbindd_domain *domain,
4817 uint32_t opnum, const DATA_BLOB *req, DATA_BLOB *resp)
4819 TDB_DATA key, data;
4820 bool ret = false;
4822 if (!wcache_opnum_cacheable(opnum) ||
4823 is_my_own_sam_domain(domain) ||
4824 is_builtin_domain(domain)) {
4825 return false;
4828 if (wcache->tdb == NULL) {
4829 return false;
4832 if (!wcache_ndr_key(talloc_tos(), domain->name, opnum, req, &key)) {
4833 return false;
4835 data = tdb_fetch(wcache->tdb, key);
4836 TALLOC_FREE(key.dptr);
4838 if (data.dptr == NULL) {
4839 return false;
4841 if (data.dsize < 12) {
4842 goto fail;
4845 if (is_domain_online(domain)) {
4846 uint32_t entry_seqnum, dom_seqnum, last_check;
4847 uint64_t entry_timeout;
4849 if (!wcache_fetch_seqnum(domain->name, &dom_seqnum,
4850 &last_check)) {
4851 goto fail;
4853 entry_seqnum = IVAL(data.dptr, 0);
4854 if (entry_seqnum != dom_seqnum) {
4855 DBG_DEBUG("Entry has wrong sequence number: %d\n",
4856 (int)entry_seqnum);
4857 goto fail;
4859 entry_timeout = BVAL(data.dptr, 4);
4860 if (time(NULL) > (time_t)entry_timeout) {
4861 DBG_DEBUG("Entry has timed out\n");
4862 goto fail;
4866 resp->data = (uint8_t *)talloc_memdup(mem_ctx, data.dptr + 12,
4867 data.dsize - 12);
4868 if (resp->data == NULL) {
4869 DBG_DEBUG("talloc failed\n");
4870 goto fail;
4872 resp->length = data.dsize - 12;
4874 ret = true;
4875 fail:
4876 SAFE_FREE(data.dptr);
4877 return ret;
4880 void wcache_store_ndr(struct winbindd_domain *domain, uint32_t opnum,
4881 const DATA_BLOB *req, const DATA_BLOB *resp)
4883 TDB_DATA key, data;
4884 uint32_t dom_seqnum, last_check;
4885 uint64_t timeout;
4887 if (!wcache_opnum_cacheable(opnum) ||
4888 is_my_own_sam_domain(domain) ||
4889 is_builtin_domain(domain)) {
4890 return;
4893 if (wcache->tdb == NULL) {
4894 return;
4897 if (!wcache_fetch_seqnum(domain->name, &dom_seqnum, &last_check)) {
4898 DBG_DEBUG("could not fetch seqnum for domain %s\n",
4899 domain->name);
4900 return;
4903 if (!wcache_ndr_key(talloc_tos(), domain->name, opnum, req, &key)) {
4904 return;
4907 timeout = time(NULL) + lp_winbind_cache_time();
4909 data.dsize = resp->length + 12;
4910 data.dptr = talloc_array(key.dptr, uint8_t, data.dsize);
4911 if (data.dptr == NULL) {
4912 goto done;
4915 SIVAL(data.dptr, 0, dom_seqnum);
4916 SBVAL(data.dptr, 4, timeout);
4917 memcpy(data.dptr + 12, resp->data, resp->length);
4919 tdb_store(wcache->tdb, key, data, TDB_REPLACE);
4921 done:
4922 TALLOC_FREE(key.dptr);
4923 return;