python/samba/tests/krb5: Add tests for password expiry with krb5 ENC-TS
[Samba.git] / source3 / winbindd / winbindd_cache.c
blob833862ab99eff06d4fd185383423776d2d25879b
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 int 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 (%d) to: 254\n", len);
849 len = 254;
851 centry_put_uint8(centry, len);
852 centry_expand(centry, len);
853 memcpy(centry->data + centry->ofs, s, len);
854 centry->ofs += len;
858 push a 16 byte hash into a centry - treat as 16 byte string.
860 static void centry_put_hash16(struct cache_entry *centry, const uint8_t val[16])
862 centry_put_uint8(centry, 16);
863 centry_expand(centry, 16);
864 memcpy(centry->data + centry->ofs, val, 16);
865 centry->ofs += 16;
868 static void centry_put_sid(struct cache_entry *centry, const struct dom_sid *sid)
870 struct dom_sid_buf sid_string;
871 centry_put_string(centry, dom_sid_str_buf(sid, &sid_string));
876 put NTSTATUS into a centry
878 static void centry_put_ntstatus(struct cache_entry *centry, NTSTATUS status)
880 uint32_t status_value = NT_STATUS_V(status);
881 centry_put_uint32(centry, status_value);
886 push a NTTIME into a centry
888 static void centry_put_nttime(struct cache_entry *centry, NTTIME nt)
890 centry_expand(centry, 8);
891 SIVAL(centry->data, centry->ofs, nt & 0xFFFFFFFF);
892 centry->ofs += 4;
893 SIVAL(centry->data, centry->ofs, nt >> 32);
894 centry->ofs += 4;
898 push a time_t into a centry - use a 64 bit size.
899 NTTIME here is being used as a convenient 64-bit size.
901 static void centry_put_time(struct cache_entry *centry, time_t t)
903 NTTIME nt = (NTTIME)t;
904 centry_put_nttime(centry, nt);
908 start a centry for output. When finished, call centry_end()
910 static struct cache_entry *centry_start(struct winbindd_domain *domain,
911 NTSTATUS status)
913 struct cache_entry *centry;
915 if (!wcache->tdb)
916 return NULL;
918 centry = SMB_XMALLOC_P(struct cache_entry);
920 centry->len = 8192; /* reasonable default */
921 centry->data = SMB_XMALLOC_ARRAY(uint8_t, centry->len);
922 centry->ofs = 0;
923 centry->sequence_number = domain->sequence_number;
924 centry->timeout = lp_winbind_cache_time() + time(NULL);
925 centry_put_ntstatus(centry, status);
926 centry_put_uint32(centry, centry->sequence_number);
927 centry_put_uint64_t(centry, centry->timeout);
928 return centry;
932 finish a centry and write it to the tdb
934 static void centry_end(struct cache_entry *centry, const char *format, ...) PRINTF_ATTRIBUTE(2,3);
935 static void centry_end(struct cache_entry *centry, const char *format, ...)
937 va_list ap;
938 char *kstr;
939 TDB_DATA key, data;
940 int ret;
942 if (!winbindd_use_cache()) {
943 return;
946 va_start(ap, format);
947 ret = vasprintf(&kstr, format, ap);
948 va_end(ap);
950 if (ret == -1) {
951 return;
954 key = string_tdb_data(kstr);
955 data.dptr = centry->data;
956 data.dsize = centry->ofs;
958 tdb_store(wcache->tdb, key, data, TDB_REPLACE);
959 free(kstr);
962 static void wcache_save_name_to_sid(struct winbindd_domain *domain,
963 NTSTATUS status, const char *domain_name,
964 const char *name, const struct dom_sid *sid,
965 enum lsa_SidType type)
967 bool ok;
969 ok = namemap_cache_set_name2sid(domain_name, name, sid, type,
970 time(NULL) + lp_winbind_cache_time());
971 if (!ok) {
972 DBG_DEBUG("namemap_cache_set_name2sid failed\n");
976 * Don't store the reverse mapping. The name came from user
977 * input, and we might not have the correct capitalization,
978 * which is important for nsswitch.
982 static void wcache_save_sid_to_name(struct winbindd_domain *domain, NTSTATUS status,
983 const struct dom_sid *sid, const char *domain_name, const char *name, enum lsa_SidType type)
985 bool ok;
987 ok = namemap_cache_set_sid2name(sid, domain_name, name, type,
988 time(NULL) + lp_winbind_cache_time());
989 if (!ok) {
990 DBG_DEBUG("namemap_cache_set_sid2name failed\n");
993 if (type != SID_NAME_UNKNOWN) {
994 ok = namemap_cache_set_name2sid(
995 domain_name, name, sid, type,
996 time(NULL) + lp_winbind_cache_time());
997 if (!ok) {
998 DBG_DEBUG("namemap_cache_set_name2sid failed\n");
1003 static void wcache_save_lockout_policy(struct winbindd_domain *domain,
1004 NTSTATUS status,
1005 struct samr_DomInfo12 *lockout_policy)
1007 struct cache_entry *centry;
1009 centry = centry_start(domain, status);
1010 if (!centry)
1011 return;
1013 centry_put_nttime(centry, lockout_policy->lockout_duration);
1014 centry_put_nttime(centry, lockout_policy->lockout_window);
1015 centry_put_uint16(centry, lockout_policy->lockout_threshold);
1017 centry_end(centry, "LOC_POL/%s", domain->name);
1019 DBG_DEBUG("wcache_save_lockout_policy: %s\n", domain->name);
1021 centry_free(centry);
1026 static void wcache_save_password_policy(struct winbindd_domain *domain,
1027 NTSTATUS status,
1028 struct samr_DomInfo1 *policy)
1030 struct cache_entry *centry;
1032 centry = centry_start(domain, status);
1033 if (!centry)
1034 return;
1036 centry_put_uint16(centry, policy->min_password_length);
1037 centry_put_uint16(centry, policy->password_history_length);
1038 centry_put_uint32(centry, policy->password_properties);
1039 centry_put_nttime(centry, policy->max_password_age);
1040 centry_put_nttime(centry, policy->min_password_age);
1042 centry_end(centry, "PWD_POL/%s", domain->name);
1044 DBG_DEBUG("wcache_save_password_policy: %s\n", domain->name);
1046 centry_free(centry);
1049 /***************************************************************************
1050 ***************************************************************************/
1052 static void wcache_save_username_alias(struct winbindd_domain *domain,
1053 NTSTATUS status,
1054 const char *name, const char *alias)
1056 struct cache_entry *centry;
1057 fstring uname;
1059 if ( (centry = centry_start(domain, status)) == NULL )
1060 return;
1062 centry_put_string( centry, alias );
1064 fstrcpy(uname, name);
1065 (void)strupper_m(uname);
1066 centry_end(centry, "NSS/NA/%s", uname);
1068 DBG_DEBUG("wcache_save_username_alias: %s -> %s\n", name, alias );
1070 centry_free(centry);
1073 static void wcache_save_alias_username(struct winbindd_domain *domain,
1074 NTSTATUS status,
1075 const char *alias, const char *name)
1077 struct cache_entry *centry;
1078 fstring uname;
1080 if ( (centry = centry_start(domain, status)) == NULL )
1081 return;
1083 centry_put_string( centry, name );
1085 fstrcpy(uname, alias);
1086 (void)strupper_m(uname);
1087 centry_end(centry, "NSS/AN/%s", uname);
1089 DBG_DEBUG("wcache_save_alias_username: %s -> %s\n", alias, name );
1091 centry_free(centry);
1094 /***************************************************************************
1095 ***************************************************************************/
1097 NTSTATUS resolve_username_to_alias( TALLOC_CTX *mem_ctx,
1098 struct winbindd_domain *domain,
1099 const char *name, char **alias )
1101 struct winbind_cache *cache = get_cache(domain);
1102 struct cache_entry *centry = NULL;
1103 NTSTATUS status;
1104 char *upper_name;
1106 if ( domain->internal )
1107 return NT_STATUS_NOT_SUPPORTED;
1109 if (!cache->tdb)
1110 goto do_query;
1112 upper_name = talloc_strdup_upper(mem_ctx, name);
1113 if (upper_name == NULL) {
1114 return NT_STATUS_NO_MEMORY;
1117 centry = wcache_fetch(cache, domain, "NSS/NA/%s", upper_name);
1119 talloc_free(upper_name);
1121 if (!centry)
1122 goto do_query;
1124 status = centry->status;
1126 if (!NT_STATUS_IS_OK(status)) {
1127 centry_free(centry);
1128 return status;
1131 *alias = centry_string( centry, mem_ctx );
1133 centry_free(centry);
1135 DBG_DEBUG("resolve_username_to_alias: [Cached] - mapped %s to %s\n",
1136 name, *alias ? *alias : "(none)");
1138 return (*alias) ? NT_STATUS_OK : NT_STATUS_OBJECT_NAME_NOT_FOUND;
1140 do_query:
1142 /* If its not in cache and we are offline, then fail */
1144 if (is_domain_offline(domain)) {
1145 DBG_DEBUG("resolve_username_to_alias: rejecting query "
1146 "in offline mode\n");
1147 return NT_STATUS_NOT_FOUND;
1150 status = nss_map_to_alias( mem_ctx, domain->name, name, alias );
1152 if ( NT_STATUS_IS_OK( status ) ) {
1153 wcache_save_username_alias(domain, status, name, *alias);
1156 if ( NT_STATUS_EQUAL( status, NT_STATUS_NONE_MAPPED ) ) {
1157 wcache_save_username_alias(domain, status, name, "(NULL)");
1160 DBG_INFO("resolve_username_to_alias: backend query returned %s\n",
1161 nt_errstr(status));
1163 if ( NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ) {
1164 set_domain_offline( domain );
1167 return status;
1170 /***************************************************************************
1171 ***************************************************************************/
1173 NTSTATUS resolve_alias_to_username( TALLOC_CTX *mem_ctx,
1174 struct winbindd_domain *domain,
1175 const char *alias, char **name )
1177 struct winbind_cache *cache = get_cache(domain);
1178 struct cache_entry *centry = NULL;
1179 NTSTATUS status;
1180 char *upper_name;
1182 if ( domain->internal )
1183 return NT_STATUS_NOT_SUPPORTED;
1185 if (!cache->tdb)
1186 goto do_query;
1188 upper_name = talloc_strdup(mem_ctx, alias);
1189 if (upper_name == NULL) {
1190 return NT_STATUS_NO_MEMORY;
1192 if (!strupper_m(upper_name)) {
1193 talloc_free(upper_name);
1194 return NT_STATUS_INVALID_PARAMETER;
1197 centry = wcache_fetch(cache, domain, "NSS/AN/%s", upper_name);
1199 talloc_free(upper_name);
1201 if (!centry)
1202 goto do_query;
1204 status = centry->status;
1206 if (!NT_STATUS_IS_OK(status)) {
1207 centry_free(centry);
1208 return status;
1211 *name = centry_string( centry, mem_ctx );
1213 centry_free(centry);
1215 DBG_DEBUG("resolve_alias_to_username: [Cached] - mapped %s to %s\n",
1216 alias, *name ? *name : "(none)");
1218 return (*name) ? NT_STATUS_OK : NT_STATUS_OBJECT_NAME_NOT_FOUND;
1220 do_query:
1222 /* If its not in cache and we are offline, then fail */
1224 if (is_domain_offline(domain)) {
1225 DBG_DEBUG("resolve_alias_to_username: rejecting query "
1226 "in offline mode\n");
1227 return NT_STATUS_NOT_FOUND;
1230 /* an alias cannot contain a domain prefix or '@' */
1232 if (strchr(alias, '\\') || strchr(alias, '@')) {
1233 DBG_DEBUG("resolve_alias_to_username: skipping fully "
1234 "qualified name %s\n", alias);
1235 return NT_STATUS_OBJECT_NAME_INVALID;
1238 status = nss_map_from_alias( mem_ctx, domain->name, alias, name );
1240 if ( NT_STATUS_IS_OK( status ) ) {
1241 wcache_save_alias_username( domain, status, alias, *name );
1244 if (NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) {
1245 wcache_save_alias_username(domain, status, alias, "(NULL)");
1248 DBG_INFO("resolve_alias_to_username: backend query returned %s\n",
1249 nt_errstr(status));
1251 if ( NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ) {
1252 set_domain_offline( domain );
1255 return status;
1258 NTSTATUS wcache_cached_creds_exist(struct winbindd_domain *domain, const struct dom_sid *sid)
1260 struct winbind_cache *cache = get_cache(domain);
1261 int ret;
1262 struct dom_sid_buf tmp;
1263 fstring key_str;
1264 uint32_t rid;
1266 if (!cache->tdb) {
1267 return NT_STATUS_INTERNAL_DB_ERROR;
1270 if (is_null_sid(sid)) {
1271 return NT_STATUS_INVALID_SID;
1274 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1275 return NT_STATUS_INVALID_SID;
1278 fstr_sprintf(key_str, "CRED/%s", dom_sid_str_buf(sid, &tmp));
1280 ret = tdb_exists(cache->tdb, string_tdb_data(key_str));
1281 if (ret != 1) {
1282 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1285 return NT_STATUS_OK;
1288 /* Lookup creds for a SID - copes with old (unsalted) creds as well
1289 as new salted ones. */
1291 NTSTATUS wcache_get_creds(struct winbindd_domain *domain,
1292 TALLOC_CTX *mem_ctx,
1293 const struct dom_sid *sid,
1294 const uint8_t **cached_nt_pass,
1295 const uint8_t **cached_salt)
1297 struct winbind_cache *cache = get_cache(domain);
1298 struct cache_entry *centry = NULL;
1299 NTSTATUS status;
1300 uint32_t rid;
1301 struct dom_sid_buf sidstr;
1303 if (!cache->tdb) {
1304 return NT_STATUS_INTERNAL_DB_ERROR;
1307 if (is_null_sid(sid)) {
1308 return NT_STATUS_INVALID_SID;
1311 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1312 return NT_STATUS_INVALID_SID;
1315 /* Try and get a salted cred first. If we can't
1316 fall back to an unsalted cred. */
1318 centry = wcache_fetch(cache, domain, "CRED/%s",
1319 dom_sid_str_buf(sid, &sidstr));
1320 if (!centry) {
1321 DBG_DEBUG("wcache_get_creds: entry for [CRED/%s] not found\n",
1322 dom_sid_str_buf(sid, &sidstr));
1323 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1327 * We don't use the time element at this moment,
1328 * but we have to consume it, so that we don't
1329 * need to change the disk format of the cache.
1331 (void)centry_time(centry);
1333 /* In the salted case this isn't actually the nt_hash itself,
1334 but the MD5 of the salt + nt_hash. Let the caller
1335 sort this out. It can tell as we only return the cached_salt
1336 if we are returning a salted cred. */
1338 *cached_nt_pass = (const uint8_t *)centry_hash16(centry, mem_ctx);
1339 if (*cached_nt_pass == NULL) {
1341 dom_sid_str_buf(sid, &sidstr);
1343 /* Bad (old) cred cache. Delete and pretend we
1344 don't have it. */
1345 DBG_WARNING("wcache_get_creds: bad entry for [CRED/%s] - deleting\n",
1346 sidstr.buf);
1347 wcache_delete("CRED/%s", sidstr.buf);
1348 centry_free(centry);
1349 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1352 /* We only have 17 bytes more data in the salted cred case. */
1353 if (centry->len - centry->ofs == 17) {
1354 *cached_salt = (const uint8_t *)centry_hash16(centry, mem_ctx);
1355 } else {
1356 *cached_salt = NULL;
1359 dump_data_pw("cached_nt_pass", *cached_nt_pass, NT_HASH_LEN);
1360 if (*cached_salt) {
1361 dump_data_pw("cached_salt", *cached_salt, NT_HASH_LEN);
1364 status = centry->status;
1366 DBG_DEBUG("wcache_get_creds: [Cached] - cached creds for user %s status: %s\n",
1367 dom_sid_str_buf(sid, &sidstr),
1368 nt_errstr(status) );
1370 centry_free(centry);
1371 return status;
1374 /* Store creds for a SID - only writes out new salted ones. */
1376 NTSTATUS wcache_save_creds(struct winbindd_domain *domain,
1377 const struct dom_sid *sid,
1378 const uint8_t nt_pass[NT_HASH_LEN])
1380 struct cache_entry *centry;
1381 struct dom_sid_buf sid_str;
1382 uint32_t rid;
1383 uint8_t cred_salt[NT_HASH_LEN];
1384 uint8_t salted_hash[NT_HASH_LEN];
1385 gnutls_hash_hd_t hash_hnd = NULL;
1386 int rc;
1388 if (is_null_sid(sid)) {
1389 return NT_STATUS_INVALID_SID;
1392 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1393 return NT_STATUS_INVALID_SID;
1396 centry = centry_start(domain, NT_STATUS_OK);
1397 if (!centry) {
1398 return NT_STATUS_INTERNAL_DB_ERROR;
1401 dump_data_pw("nt_pass", nt_pass, NT_HASH_LEN);
1403 centry_put_time(centry, time(NULL));
1405 /* Create a salt and then salt the hash. */
1406 generate_random_buffer(cred_salt, NT_HASH_LEN);
1408 rc = gnutls_hash_init(&hash_hnd, GNUTLS_DIG_MD5);
1409 if (rc < 0) {
1410 centry_free(centry);
1411 return gnutls_error_to_ntstatus(rc, NT_STATUS_HASH_NOT_SUPPORTED);
1414 rc = gnutls_hash(hash_hnd, cred_salt, 16);
1415 if (rc < 0) {
1416 gnutls_hash_deinit(hash_hnd, NULL);
1417 centry_free(centry);
1418 return gnutls_error_to_ntstatus(rc, NT_STATUS_HASH_NOT_SUPPORTED);
1420 rc = gnutls_hash(hash_hnd, nt_pass, 16);
1421 if (rc < 0) {
1422 gnutls_hash_deinit(hash_hnd, NULL);
1423 centry_free(centry);
1424 return gnutls_error_to_ntstatus(rc, NT_STATUS_HASH_NOT_SUPPORTED);
1426 gnutls_hash_deinit(hash_hnd, salted_hash);
1428 centry_put_hash16(centry, salted_hash);
1429 centry_put_hash16(centry, cred_salt);
1430 centry_end(centry, "CRED/%s", dom_sid_str_buf(sid, &sid_str));
1432 DBG_DEBUG("wcache_save_creds: %s\n", sid_str.buf);
1434 centry_free(centry);
1436 return NT_STATUS_OK;
1440 /* Query display info. This is the basic user list fn */
1441 NTSTATUS wb_cache_query_user_list(struct winbindd_domain *domain,
1442 TALLOC_CTX *mem_ctx,
1443 uint32_t **prids)
1445 struct winbind_cache *cache = get_cache(domain);
1446 struct cache_entry *centry = NULL;
1447 uint32_t num_rids = 0;
1448 uint32_t *rids = NULL;
1449 NTSTATUS status;
1450 unsigned int i, retry;
1451 bool old_status = domain->online;
1453 *prids = NULL;
1455 if (!cache->tdb)
1456 goto do_query;
1458 centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
1459 if (!centry)
1460 goto do_query;
1462 do_fetch_cache:
1463 num_rids = centry_uint32(centry);
1465 if (num_rids == 0) {
1466 goto do_cached;
1469 rids = talloc_array(mem_ctx, uint32_t, num_rids);
1470 if (rids == NULL) {
1471 centry_free(centry);
1472 return NT_STATUS_NO_MEMORY;
1475 for (i=0; i<num_rids; i++) {
1476 rids[i] = centry_uint32(centry);
1479 do_cached:
1480 status = centry->status;
1482 DBG_DEBUG("query_user_list: [Cached] - cached list for domain %s status: %s\n",
1483 domain->name, nt_errstr(status) );
1485 centry_free(centry);
1486 return status;
1488 do_query:
1490 /* Put the query_user_list() in a retry loop. There appears to be
1491 * some bug either with Windows 2000 or Samba's handling of large
1492 * rpc replies. This manifests itself as sudden disconnection
1493 * at a random point in the enumeration of a large (60k) user list.
1494 * The retry loop simply tries the operation again. )-: It's not
1495 * pretty but an acceptable workaround until we work out what the
1496 * real problem is. */
1498 retry = 0;
1499 do {
1501 DBG_DEBUG("query_user_list: [Cached] - doing backend query for list for domain %s\n",
1502 domain->name );
1504 rids = NULL;
1505 status = domain->backend->query_user_list(domain, mem_ctx,
1506 &rids);
1507 num_rids = talloc_array_length(rids);
1509 if (!NT_STATUS_IS_OK(status)) {
1510 DBG_NOTICE("query_user_list: returned 0x%08x, "
1511 "retrying\n", NT_STATUS_V(status));
1513 reset_cm_connection_on_error(domain, NULL, status);
1514 if (NT_STATUS_EQUAL(status, NT_STATUS_UNSUCCESSFUL)) {
1515 DBG_NOTICE("query_user_list: flushing "
1516 "connection cache\n");
1517 invalidate_cm_connection(domain);
1519 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1520 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1521 if (!domain->internal && old_status) {
1522 set_domain_offline(domain);
1524 /* store partial response. */
1525 if (num_rids > 0) {
1527 * humm, what about the status used for cache?
1528 * Should it be NT_STATUS_OK?
1530 break;
1533 * domain is offline now, and there is no user entries,
1534 * try to fetch from cache again.
1536 if (cache->tdb && !domain->online && !domain->internal && old_status) {
1537 centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
1538 /* partial response... */
1539 if (!centry) {
1540 goto skip_save;
1541 } else {
1542 goto do_fetch_cache;
1544 } else {
1545 goto skip_save;
1549 } while (NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_UNSUCCESSFUL) &&
1550 (retry++ < 5));
1552 /* and save it */
1553 refresh_sequence_number(domain);
1554 if (!NT_STATUS_IS_OK(status)) {
1555 return status;
1557 centry = centry_start(domain, status);
1558 if (!centry)
1559 goto skip_save;
1560 centry_put_uint32(centry, num_rids);
1561 for (i=0; i<num_rids; i++) {
1562 centry_put_uint32(centry, rids[i]);
1564 centry_end(centry, "UL/%s", domain->name);
1565 centry_free(centry);
1567 *prids = rids;
1569 skip_save:
1570 return status;
1573 /* list all domain groups */
1574 NTSTATUS wb_cache_enum_dom_groups(struct winbindd_domain *domain,
1575 TALLOC_CTX *mem_ctx,
1576 uint32_t *num_entries,
1577 struct wb_acct_info **info)
1579 struct winbind_cache *cache = get_cache(domain);
1580 struct cache_entry *centry = NULL;
1581 NTSTATUS status;
1582 unsigned int i;
1583 bool old_status;
1585 old_status = domain->online;
1586 if (!cache->tdb)
1587 goto do_query;
1589 centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
1590 if (!centry)
1591 goto do_query;
1593 do_fetch_cache:
1594 *num_entries = centry_uint32(centry);
1596 if (*num_entries == 0)
1597 goto do_cached;
1599 (*info) = talloc_array(mem_ctx, struct wb_acct_info, *num_entries);
1600 if (! (*info)) {
1601 smb_panic_fn("enum_dom_groups out of memory");
1603 for (i=0; i<(*num_entries); i++) {
1604 (*info)[i].acct_name = centry_string(centry, (*info));
1605 (*info)[i].acct_desc = centry_string(centry, (*info));
1606 (*info)[i].rid = centry_uint32(centry);
1609 do_cached:
1610 status = centry->status;
1612 DBG_DEBUG("enum_dom_groups: [Cached] - cached list for domain %s status: %s\n",
1613 domain->name, nt_errstr(status) );
1615 centry_free(centry);
1616 return status;
1618 do_query:
1619 *num_entries = 0;
1620 *info = NULL;
1622 DBG_DEBUG("enum_dom_groups: [Cached] - doing backend query for list for domain %s\n",
1623 domain->name );
1625 status = domain->backend->enum_dom_groups(domain, mem_ctx, num_entries, info);
1627 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1628 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1629 if (!domain->internal && old_status) {
1630 set_domain_offline(domain);
1632 if (cache->tdb &&
1633 !domain->online &&
1634 !domain->internal &&
1635 old_status) {
1636 centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
1637 if (centry) {
1638 goto do_fetch_cache;
1642 /* and save it */
1643 refresh_sequence_number(domain);
1644 if (!NT_STATUS_IS_OK(status)) {
1645 return status;
1647 centry = centry_start(domain, status);
1648 if (!centry)
1649 goto skip_save;
1650 centry_put_uint32(centry, *num_entries);
1651 for (i=0; i<(*num_entries); i++) {
1652 centry_put_string(centry, (*info)[i].acct_name);
1653 centry_put_string(centry, (*info)[i].acct_desc);
1654 centry_put_uint32(centry, (*info)[i].rid);
1656 centry_end(centry, "GL/%s/domain", domain->name);
1657 centry_free(centry);
1659 skip_save:
1660 return status;
1663 /* list all domain groups */
1664 NTSTATUS wb_cache_enum_local_groups(struct winbindd_domain *domain,
1665 TALLOC_CTX *mem_ctx,
1666 uint32_t *num_entries,
1667 struct wb_acct_info **info)
1669 struct winbind_cache *cache = get_cache(domain);
1670 struct cache_entry *centry = NULL;
1671 NTSTATUS status;
1672 unsigned int i;
1673 bool old_status;
1675 old_status = domain->online;
1676 if (!cache->tdb)
1677 goto do_query;
1679 centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
1680 if (!centry)
1681 goto do_query;
1683 do_fetch_cache:
1684 *num_entries = centry_uint32(centry);
1686 if (*num_entries == 0)
1687 goto do_cached;
1689 (*info) = talloc_array(mem_ctx, struct wb_acct_info, *num_entries);
1690 if (! (*info)) {
1691 smb_panic_fn("enum_dom_groups out of memory");
1693 for (i=0; i<(*num_entries); i++) {
1694 (*info)[i].acct_name = centry_string(centry, (*info));
1695 (*info)[i].acct_desc = centry_string(centry, (*info));
1696 (*info)[i].rid = centry_uint32(centry);
1699 do_cached:
1701 /* If we are returning cached data and the domain controller
1702 is down then we don't know whether the data is up to date
1703 or not. Return NT_STATUS_MORE_PROCESSING_REQUIRED to
1704 indicate this. */
1706 if (wcache_server_down(domain)) {
1707 DBG_DEBUG("enum_local_groups: returning cached user list and server was down\n");
1708 status = NT_STATUS_MORE_PROCESSING_REQUIRED;
1709 } else
1710 status = centry->status;
1712 DBG_DEBUG("enum_local_groups: [Cached] - cached list for domain %s status: %s\n",
1713 domain->name, nt_errstr(status) );
1715 centry_free(centry);
1716 return status;
1718 do_query:
1719 *num_entries = 0;
1720 *info = NULL;
1722 DBG_DEBUG("enum_local_groups: [Cached] - doing backend query for list for domain %s\n",
1723 domain->name );
1725 status = domain->backend->enum_local_groups(domain, mem_ctx, num_entries, info);
1727 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1728 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1729 if (!domain->internal && old_status) {
1730 set_domain_offline(domain);
1732 if (cache->tdb &&
1733 !domain->internal &&
1734 !domain->online &&
1735 old_status) {
1736 centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
1737 if (centry) {
1738 goto do_fetch_cache;
1742 /* and save it */
1743 refresh_sequence_number(domain);
1744 if (!NT_STATUS_IS_OK(status)) {
1745 return status;
1747 centry = centry_start(domain, status);
1748 if (!centry)
1749 goto skip_save;
1750 centry_put_uint32(centry, *num_entries);
1751 for (i=0; i<(*num_entries); i++) {
1752 centry_put_string(centry, (*info)[i].acct_name);
1753 centry_put_string(centry, (*info)[i].acct_desc);
1754 centry_put_uint32(centry, (*info)[i].rid);
1756 centry_end(centry, "GL/%s/local", domain->name);
1757 centry_free(centry);
1759 skip_save:
1760 return status;
1763 struct wcache_name_to_sid_state {
1764 struct dom_sid *sid;
1765 enum lsa_SidType *type;
1766 bool offline;
1767 bool found;
1770 static void wcache_name_to_sid_fn(const struct dom_sid *sid,
1771 enum lsa_SidType type,
1772 bool expired,
1773 void *private_data)
1775 struct wcache_name_to_sid_state *state = private_data;
1777 *state->sid = *sid;
1778 *state->type = type;
1779 state->found = (!expired || state->offline);
1782 static NTSTATUS wcache_name_to_sid(struct winbindd_domain *domain,
1783 const char *domain_name,
1784 const char *name,
1785 struct dom_sid *sid,
1786 enum lsa_SidType *type)
1788 struct wcache_name_to_sid_state state = {
1789 .sid = sid, .type = type, .found = false,
1790 .offline = is_domain_offline(domain),
1792 bool ok;
1794 ok = namemap_cache_find_name(domain_name, name, wcache_name_to_sid_fn,
1795 &state);
1796 if (!ok) {
1797 DBG_DEBUG("namemap_cache_find_name failed\n");
1798 return NT_STATUS_NOT_FOUND;
1800 if (!state.found) {
1801 DBG_DEBUG("cache entry not found\n");
1802 return NT_STATUS_NOT_FOUND;
1804 if (*type == SID_NAME_UNKNOWN) {
1805 return NT_STATUS_NONE_MAPPED;
1808 return NT_STATUS_OK;
1811 /* convert a single name to a sid in a domain */
1812 NTSTATUS wb_cache_name_to_sid(struct winbindd_domain *domain,
1813 TALLOC_CTX *mem_ctx,
1814 const char *domain_name,
1815 const char *name,
1816 uint32_t flags,
1817 struct dom_sid *sid,
1818 enum lsa_SidType *type)
1820 NTSTATUS status;
1821 bool old_status;
1822 const char *dom_name;
1824 old_status = domain->online;
1826 status = wcache_name_to_sid(domain, domain_name, name, sid, type);
1827 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
1828 return status;
1831 ZERO_STRUCTP(sid);
1833 DBG_DEBUG("name_to_sid: [Cached] - doing backend query for name for domain %s\n",
1834 domain->name );
1836 winbindd_domain_init_backend(domain);
1837 status = domain->backend->name_to_sid(domain, mem_ctx, domain_name,
1838 name, flags, &dom_name, sid, type);
1840 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1841 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1842 if (!domain->internal && old_status) {
1843 set_domain_offline(domain);
1845 if (!domain->internal &&
1846 !domain->online &&
1847 old_status) {
1848 NTSTATUS cache_status;
1849 cache_status = wcache_name_to_sid(domain, domain_name, name, sid, type);
1850 return cache_status;
1853 /* and save it */
1855 if (domain->online &&
1856 (NT_STATUS_IS_OK(status) || NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED))) {
1857 enum lsa_SidType save_type = *type;
1859 if (NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) {
1860 save_type = SID_NAME_UNKNOWN;
1863 wcache_save_name_to_sid(domain, status, domain_name, name, sid,
1864 save_type);
1866 /* Only save the reverse mapping if this was not a UPN */
1867 if (!strchr(name, '@')) {
1868 if (!strupper_m(discard_const_p(char, domain_name))) {
1869 return NT_STATUS_INVALID_PARAMETER;
1871 (void)strlower_m(discard_const_p(char, name));
1872 wcache_save_sid_to_name(domain, status, sid,
1873 dom_name, name, save_type);
1877 return status;
1880 struct wcache_sid_to_name_state {
1881 TALLOC_CTX *mem_ctx;
1882 char **domain_name;
1883 char **name;
1884 enum lsa_SidType *type;
1885 bool offline;
1886 bool found;
1889 static void wcache_sid_to_name_fn(const char *domain,
1890 const char *name,
1891 enum lsa_SidType type,
1892 bool expired,
1893 void *private_data)
1895 struct wcache_sid_to_name_state *state = private_data;
1897 *state->domain_name = talloc_strdup(state->mem_ctx, domain);
1898 if (*state->domain_name == NULL) {
1899 return;
1901 *state->name = talloc_strdup(state->mem_ctx, name);
1902 if (*state->name == NULL) {
1903 return;
1905 *state->type = type;
1906 state->found = (!expired || state->offline);
1909 static NTSTATUS wcache_sid_to_name(struct winbindd_domain *domain,
1910 const struct dom_sid *sid,
1911 TALLOC_CTX *mem_ctx,
1912 char **domain_name,
1913 char **name,
1914 enum lsa_SidType *type)
1916 struct wcache_sid_to_name_state state = {
1917 .mem_ctx = mem_ctx, .found = false,
1918 .domain_name = domain_name, .name = name, .type = type,
1919 .offline = is_domain_offline(domain)
1921 bool ok;
1923 ok = namemap_cache_find_sid(sid, wcache_sid_to_name_fn, &state);
1924 if (!ok) {
1925 DBG_DEBUG("namemap_cache_find_name failed\n");
1926 return NT_STATUS_NOT_FOUND;
1928 if (!state.found) {
1929 DBG_DEBUG("cache entry not found\n");
1930 return NT_STATUS_NOT_FOUND;
1932 if (*type == SID_NAME_UNKNOWN) {
1933 return NT_STATUS_NONE_MAPPED;
1936 return NT_STATUS_OK;
1939 /* convert a sid to a user or group name. The sid is guaranteed to be in the domain
1940 given */
1941 NTSTATUS wb_cache_sid_to_name(struct winbindd_domain *domain,
1942 TALLOC_CTX *mem_ctx,
1943 const struct dom_sid *sid,
1944 char **domain_name,
1945 char **name,
1946 enum lsa_SidType *type)
1948 NTSTATUS status;
1949 bool old_status;
1951 old_status = domain->online;
1952 status = wcache_sid_to_name(domain, sid, mem_ctx, domain_name, name,
1953 type);
1954 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
1955 return status;
1958 *name = NULL;
1959 *domain_name = NULL;
1961 DBG_DEBUG("sid_to_name: [Cached] - doing backend query for name for domain %s\n",
1962 domain->name );
1964 winbindd_domain_init_backend(domain);
1966 status = domain->backend->sid_to_name(domain, mem_ctx, sid, domain_name, name, type);
1968 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1969 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1970 if (!domain->internal && old_status) {
1971 set_domain_offline(domain);
1973 if (!domain->internal &&
1974 !domain->online &&
1975 old_status) {
1976 NTSTATUS cache_status;
1977 cache_status = wcache_sid_to_name(domain, sid, mem_ctx,
1978 domain_name, name, type);
1979 return cache_status;
1982 /* and save it */
1983 if (!NT_STATUS_IS_OK(status)) {
1984 return status;
1986 wcache_save_sid_to_name(domain, status, sid, *domain_name, *name, *type);
1988 /* We can't save the name to sid mapping here, as with sid history a
1989 * later name2sid would give the wrong sid. */
1991 return status;
1994 NTSTATUS wb_cache_rids_to_names(struct winbindd_domain *domain,
1995 TALLOC_CTX *mem_ctx,
1996 const struct dom_sid *domain_sid,
1997 uint32_t *rids,
1998 size_t num_rids,
1999 char **domain_name,
2000 char ***names,
2001 enum lsa_SidType **types)
2003 struct winbind_cache *cache = get_cache(domain);
2004 size_t i;
2005 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
2006 bool have_mapped;
2007 bool have_unmapped;
2008 bool old_status;
2010 old_status = domain->online;
2011 *domain_name = NULL;
2012 *names = NULL;
2013 *types = NULL;
2015 if (!cache->tdb) {
2016 goto do_query;
2019 if (num_rids == 0) {
2020 return NT_STATUS_OK;
2023 *names = talloc_array(mem_ctx, char *, num_rids);
2024 *types = talloc_array(mem_ctx, enum lsa_SidType, num_rids);
2026 if ((*names == NULL) || (*types == NULL)) {
2027 result = NT_STATUS_NO_MEMORY;
2028 goto error;
2031 have_mapped = have_unmapped = false;
2033 for (i=0; i<num_rids; i++) {
2034 struct dom_sid sid;
2035 NTSTATUS status;
2036 enum lsa_SidType type;
2037 char *dom, *name;
2039 if (!sid_compose(&sid, domain_sid, rids[i])) {
2040 result = NT_STATUS_INTERNAL_ERROR;
2041 goto error;
2044 status = wcache_sid_to_name(domain, &sid, *names, &dom,
2045 &name, &type);
2047 (*types)[i] = SID_NAME_UNKNOWN;
2048 (*names)[i] = talloc_strdup(*names, "");
2050 if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2051 /* not cached */
2052 goto do_query;
2055 if (NT_STATUS_IS_OK(status)) {
2056 have_mapped = true;
2057 (*types)[i] = type;
2059 if (*domain_name == NULL) {
2060 *domain_name = dom;
2061 } else {
2062 TALLOC_FREE(dom);
2065 (*names)[i] = name;
2067 } else if (NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) {
2068 have_unmapped = true;
2069 } else {
2070 /* something's definitely wrong */
2071 result = status;
2072 goto error;
2076 if (!have_mapped) {
2077 return NT_STATUS_NONE_MAPPED;
2079 if (!have_unmapped) {
2080 return NT_STATUS_OK;
2082 return STATUS_SOME_UNMAPPED;
2084 do_query:
2086 TALLOC_FREE(*names);
2087 TALLOC_FREE(*types);
2089 result = domain->backend->rids_to_names(domain, mem_ctx, domain_sid,
2090 rids, num_rids, domain_name,
2091 names, types);
2093 if (NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
2094 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2095 if (!domain->internal && old_status) {
2096 set_domain_offline(domain);
2098 if (cache->tdb &&
2099 !domain->internal &&
2100 !domain->online &&
2101 old_status) {
2102 have_mapped = have_unmapped = false;
2104 *names = talloc_array(mem_ctx, char *, num_rids);
2105 if (*names == NULL) {
2106 result = NT_STATUS_NO_MEMORY;
2107 goto error;
2110 *types = talloc_array(mem_ctx, enum lsa_SidType,
2111 num_rids);
2112 if (*types == NULL) {
2113 result = NT_STATUS_NO_MEMORY;
2114 goto error;
2117 for (i=0; i<num_rids; i++) {
2118 struct dom_sid sid;
2119 NTSTATUS status;
2120 enum lsa_SidType type;
2121 char *dom, *name;
2123 if (!sid_compose(&sid, domain_sid, rids[i])) {
2124 result = NT_STATUS_INTERNAL_ERROR;
2125 goto error;
2128 status = wcache_sid_to_name(domain, &sid,
2129 *names, &dom,
2130 &name, &type);
2132 (*types)[i] = SID_NAME_UNKNOWN;
2133 (*names)[i] = talloc_strdup(*names, "");
2135 if (NT_STATUS_IS_OK(status)) {
2136 have_mapped = true;
2137 (*types)[i] = type;
2139 if (*domain_name == NULL) {
2140 *domain_name = dom;
2141 } else {
2142 TALLOC_FREE(dom);
2145 (*names)[i] = name;
2147 } else if (NT_STATUS_EQUAL(
2148 status,
2149 NT_STATUS_NONE_MAPPED)) {
2150 have_unmapped = true;
2151 } else {
2152 /* something's definitely wrong */
2153 result = status;
2154 goto error;
2158 if (!have_mapped) {
2159 return NT_STATUS_NONE_MAPPED;
2161 if (!have_unmapped) {
2162 return NT_STATUS_OK;
2164 return STATUS_SOME_UNMAPPED;
2168 None of the queried rids has been found so save all negative entries
2170 if (NT_STATUS_EQUAL(result, NT_STATUS_NONE_MAPPED)) {
2171 for (i = 0; i < num_rids; i++) {
2172 struct dom_sid sid;
2173 const char *name = "";
2174 const enum lsa_SidType type = SID_NAME_UNKNOWN;
2175 NTSTATUS status = NT_STATUS_NONE_MAPPED;
2177 if (!sid_compose(&sid, domain_sid, rids[i])) {
2178 return NT_STATUS_INTERNAL_ERROR;
2181 wcache_save_sid_to_name(domain, status, &sid, *domain_name,
2182 name, type);
2185 return result;
2189 Some or all of the queried rids have been found.
2191 if (!NT_STATUS_IS_OK(result) &&
2192 !NT_STATUS_EQUAL(result, STATUS_SOME_UNMAPPED)) {
2193 return result;
2196 refresh_sequence_number(domain);
2198 for (i=0; i<num_rids; i++) {
2199 struct dom_sid sid;
2200 NTSTATUS status;
2202 if (!sid_compose(&sid, domain_sid, rids[i])) {
2203 result = NT_STATUS_INTERNAL_ERROR;
2204 goto error;
2207 status = (*types)[i] == SID_NAME_UNKNOWN ?
2208 NT_STATUS_NONE_MAPPED : NT_STATUS_OK;
2210 wcache_save_sid_to_name(domain, status, &sid, *domain_name,
2211 (*names)[i], (*types)[i]);
2214 return result;
2216 error:
2217 TALLOC_FREE(*names);
2218 TALLOC_FREE(*types);
2219 return result;
2222 static NTSTATUS wcache_query_user(struct winbindd_domain *domain,
2223 TALLOC_CTX *mem_ctx,
2224 const struct dom_sid *user_sid,
2225 struct wbint_userinfo *info)
2227 struct winbind_cache *cache = get_cache(domain);
2228 struct cache_entry *centry = NULL;
2229 NTSTATUS status;
2230 struct dom_sid_buf sid_string;
2232 if (cache->tdb == NULL) {
2233 return NT_STATUS_NOT_FOUND;
2236 centry = wcache_fetch(
2237 cache, domain, "U/%s", dom_sid_str_buf(user_sid, &sid_string));
2238 if (centry == NULL) {
2239 return NT_STATUS_NOT_FOUND;
2242 /* if status is not ok then this is a negative hit
2243 and the rest of the data doesn't matter */
2244 status = centry->status;
2245 if (NT_STATUS_IS_OK(status)) {
2246 info->domain_name = centry_string(centry, mem_ctx);
2247 info->acct_name = centry_string(centry, mem_ctx);
2248 info->full_name = centry_string(centry, mem_ctx);
2249 info->homedir = centry_string(centry, mem_ctx);
2250 info->shell = centry_string(centry, mem_ctx);
2251 info->uid = centry_uint32(centry);
2252 info->primary_gid = centry_uint32(centry);
2253 info->primary_group_name = centry_string(centry, mem_ctx);
2254 centry_sid(centry, &info->user_sid);
2255 centry_sid(centry, &info->group_sid);
2258 DBG_DEBUG("query_user: [Cached] - cached info for domain %s status: "
2259 "%s\n", domain->name, nt_errstr(status) );
2261 centry_free(centry);
2262 return status;
2267 * @brief Query a fullname from the username cache (for further gecos processing)
2269 * @param domain A pointer to the winbindd_domain struct.
2270 * @param mem_ctx The talloc context.
2271 * @param user_sid The user sid.
2272 * @param full_name A pointer to the full_name string.
2274 * @return NTSTATUS code
2276 NTSTATUS wcache_query_user_fullname(struct winbindd_domain *domain,
2277 TALLOC_CTX *mem_ctx,
2278 const struct dom_sid *user_sid,
2279 const char **full_name)
2281 NTSTATUS status;
2282 struct wbint_userinfo info;
2284 status = wcache_query_user(domain, mem_ctx, user_sid, &info);
2285 if (!NT_STATUS_IS_OK(status)) {
2286 return status;
2289 if (info.full_name != NULL) {
2290 *full_name = talloc_strdup(mem_ctx, info.full_name);
2291 if (*full_name == NULL) {
2292 return NT_STATUS_NO_MEMORY;
2296 return NT_STATUS_OK;
2299 static NTSTATUS wcache_lookup_usergroups(struct winbindd_domain *domain,
2300 TALLOC_CTX *mem_ctx,
2301 const struct dom_sid *user_sid,
2302 uint32_t *pnum_sids,
2303 struct dom_sid **psids)
2305 struct winbind_cache *cache = get_cache(domain);
2306 struct cache_entry *centry = NULL;
2307 NTSTATUS status;
2308 uint32_t i, num_sids;
2309 struct dom_sid *sids;
2310 struct dom_sid_buf sid_string;
2312 if (cache->tdb == NULL) {
2313 return NT_STATUS_NOT_FOUND;
2316 centry = wcache_fetch(
2317 cache,
2318 domain,
2319 "UG/%s",
2320 dom_sid_str_buf(user_sid, &sid_string));
2321 if (centry == NULL) {
2322 return NT_STATUS_NOT_FOUND;
2325 num_sids = centry_uint32(centry);
2326 sids = talloc_array(mem_ctx, struct dom_sid, num_sids);
2327 if (sids == NULL) {
2328 centry_free(centry);
2329 return NT_STATUS_NO_MEMORY;
2332 for (i=0; i<num_sids; i++) {
2333 centry_sid(centry, &sids[i]);
2336 status = centry->status;
2338 DBG_DEBUG("lookup_usergroups: [Cached] - cached info for domain %s "
2339 "status: %s\n", domain->name, nt_errstr(status));
2341 centry_free(centry);
2343 *pnum_sids = num_sids;
2344 *psids = sids;
2345 return status;
2348 /* Lookup groups a user is a member of. */
2349 NTSTATUS wb_cache_lookup_usergroups(struct winbindd_domain *domain,
2350 TALLOC_CTX *mem_ctx,
2351 const struct dom_sid *user_sid,
2352 uint32_t *num_groups,
2353 struct dom_sid **user_gids)
2355 struct cache_entry *centry = NULL;
2356 NTSTATUS status;
2357 unsigned int i;
2358 struct dom_sid_buf sid_string;
2359 bool old_status;
2361 old_status = domain->online;
2362 status = wcache_lookup_usergroups(domain, mem_ctx, user_sid,
2363 num_groups, user_gids);
2364 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2365 return status;
2368 (*num_groups) = 0;
2369 (*user_gids) = NULL;
2371 DBG_DEBUG("lookup_usergroups: [Cached] - doing backend query for info for domain %s\n",
2372 domain->name );
2374 status = domain->backend->lookup_usergroups(domain, mem_ctx, user_sid, num_groups, user_gids);
2376 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2377 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2378 if (!domain->internal && old_status) {
2379 set_domain_offline(domain);
2381 if (!domain->internal &&
2382 !domain->online &&
2383 old_status) {
2384 NTSTATUS cache_status;
2385 cache_status = wcache_lookup_usergroups(domain, mem_ctx, user_sid,
2386 num_groups, user_gids);
2387 return cache_status;
2390 if ( NT_STATUS_EQUAL(status, NT_STATUS_SYNCHRONIZATION_REQUIRED) )
2391 goto skip_save;
2393 /* and save it */
2394 refresh_sequence_number(domain);
2395 if (!NT_STATUS_IS_OK(status)) {
2396 return status;
2398 centry = centry_start(domain, status);
2399 if (!centry)
2400 goto skip_save;
2402 centry_put_uint32(centry, *num_groups);
2403 for (i=0; i<(*num_groups); i++) {
2404 centry_put_sid(centry, &(*user_gids)[i]);
2407 centry_end(centry, "UG/%s", dom_sid_str_buf(user_sid, &sid_string));
2408 centry_free(centry);
2410 skip_save:
2411 return status;
2414 static char *wcache_make_sidlist(TALLOC_CTX *mem_ctx, uint32_t num_sids,
2415 const struct dom_sid *sids)
2417 uint32_t i;
2418 char *sidlist;
2420 sidlist = talloc_strdup(mem_ctx, "");
2421 if (sidlist == NULL) {
2422 return NULL;
2424 for (i=0; i<num_sids; i++) {
2425 struct dom_sid_buf tmp;
2426 sidlist = talloc_asprintf_append_buffer(
2427 sidlist,
2428 "/%s",
2429 dom_sid_str_buf(&sids[i], &tmp));
2430 if (sidlist == NULL) {
2431 return NULL;
2434 return sidlist;
2437 static NTSTATUS wcache_lookup_useraliases(struct winbindd_domain *domain,
2438 TALLOC_CTX *mem_ctx,
2439 uint32_t num_sids,
2440 const struct dom_sid *sids,
2441 uint32_t *pnum_aliases,
2442 uint32_t **paliases)
2444 struct winbind_cache *cache = get_cache(domain);
2445 struct cache_entry *centry = NULL;
2446 uint32_t i, num_aliases;
2447 uint32_t *aliases;
2448 NTSTATUS status;
2449 char *sidlist;
2451 if (cache->tdb == NULL) {
2452 return NT_STATUS_NOT_FOUND;
2455 if (num_sids == 0) {
2456 *pnum_aliases = 0;
2457 *paliases = NULL;
2458 return NT_STATUS_OK;
2461 /* We need to cache indexed by the whole list of SIDs, the aliases
2462 * resulting might come from any of the SIDs. */
2464 sidlist = wcache_make_sidlist(talloc_tos(), num_sids, sids);
2465 if (sidlist == NULL) {
2466 return NT_STATUS_NO_MEMORY;
2469 centry = wcache_fetch(cache, domain, "UA%s", sidlist);
2470 TALLOC_FREE(sidlist);
2471 if (centry == NULL) {
2472 return NT_STATUS_NOT_FOUND;
2475 num_aliases = centry_uint32(centry);
2476 aliases = talloc_array(mem_ctx, uint32_t, num_aliases);
2477 if (aliases == NULL) {
2478 centry_free(centry);
2479 return NT_STATUS_NO_MEMORY;
2482 for (i=0; i<num_aliases; i++) {
2483 aliases[i] = centry_uint32(centry);
2486 status = centry->status;
2488 DBG_DEBUG("lookup_useraliases: [Cached] - cached info for domain: %s "
2489 "status %s\n", domain->name, nt_errstr(status));
2491 centry_free(centry);
2493 *pnum_aliases = num_aliases;
2494 *paliases = aliases;
2496 return status;
2499 NTSTATUS wb_cache_lookup_useraliases(struct winbindd_domain *domain,
2500 TALLOC_CTX *mem_ctx,
2501 uint32_t num_sids,
2502 const struct dom_sid *sids,
2503 uint32_t *num_aliases,
2504 uint32_t **alias_rids)
2506 struct cache_entry *centry = NULL;
2507 NTSTATUS status;
2508 char *sidlist;
2509 uint32_t i;
2510 bool old_status;
2512 old_status = domain->online;
2513 status = wcache_lookup_useraliases(domain, mem_ctx, num_sids, sids,
2514 num_aliases, alias_rids);
2515 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2516 return status;
2519 (*num_aliases) = 0;
2520 (*alias_rids) = NULL;
2522 DBG_DEBUG("lookup_usergroups: [Cached] - doing backend query for info "
2523 "for domain %s\n", domain->name );
2525 sidlist = wcache_make_sidlist(talloc_tos(), num_sids, sids);
2526 if (sidlist == NULL) {
2527 return NT_STATUS_NO_MEMORY;
2530 status = domain->backend->lookup_useraliases(domain, mem_ctx,
2531 num_sids, sids,
2532 num_aliases, alias_rids);
2534 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2535 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2536 if (!domain->internal && old_status) {
2537 set_domain_offline(domain);
2539 if (!domain->internal &&
2540 !domain->online &&
2541 old_status) {
2542 NTSTATUS cache_status;
2543 cache_status = wcache_lookup_useraliases(domain, mem_ctx, num_sids,
2544 sids, num_aliases, alias_rids);
2545 return cache_status;
2548 /* and save it */
2549 refresh_sequence_number(domain);
2550 if (!NT_STATUS_IS_OK(status)) {
2551 return status;
2553 centry = centry_start(domain, status);
2554 if (!centry)
2555 goto skip_save;
2556 centry_put_uint32(centry, *num_aliases);
2557 for (i=0; i<(*num_aliases); i++)
2558 centry_put_uint32(centry, (*alias_rids)[i]);
2559 centry_end(centry, "UA%s", sidlist);
2560 centry_free(centry);
2562 skip_save:
2563 return status;
2566 static NTSTATUS wcache_lookup_aliasmem(struct winbindd_domain *domain,
2567 TALLOC_CTX *mem_ctx,
2568 const struct dom_sid *group_sid,
2569 uint32_t *num_names,
2570 struct dom_sid **sid_mem)
2572 struct winbind_cache *cache = get_cache(domain);
2573 struct cache_entry *centry = NULL;
2574 NTSTATUS status;
2575 unsigned int i;
2576 struct dom_sid_buf sid_string;
2578 if (cache->tdb == NULL) {
2579 return NT_STATUS_NOT_FOUND;
2582 centry = wcache_fetch(cache,
2583 domain,
2584 "AM/%s",
2585 dom_sid_str_buf(group_sid, &sid_string));
2586 if (centry == NULL) {
2587 return NT_STATUS_NOT_FOUND;
2590 *sid_mem = NULL;
2592 *num_names = centry_uint32(centry);
2593 if (*num_names == 0) {
2594 centry_free(centry);
2595 return NT_STATUS_OK;
2598 *sid_mem = talloc_array(mem_ctx, struct dom_sid, *num_names);
2599 if (*sid_mem == NULL) {
2600 TALLOC_FREE(*sid_mem);
2601 centry_free(centry);
2602 return NT_STATUS_NO_MEMORY;
2605 for (i = 0; i < (*num_names); i++) {
2606 centry_sid(centry, &(*sid_mem)[i]);
2609 status = centry->status;
2611 D_DEBUG("[Cached] - cached info for domain %s "
2612 "status: %s\n",
2613 domain->name,
2614 nt_errstr(status));
2616 centry_free(centry);
2617 return status;
2620 NTSTATUS wb_cache_lookup_aliasmem(struct winbindd_domain *domain,
2621 TALLOC_CTX *mem_ctx,
2622 const struct dom_sid *group_sid,
2623 enum lsa_SidType type,
2624 uint32_t *num_sids,
2625 struct dom_sid **sid_mem)
2627 struct cache_entry *centry = NULL;
2628 NTSTATUS status;
2629 unsigned int i;
2630 struct dom_sid_buf sid_string;
2631 bool old_status;
2633 old_status = domain->online;
2634 status = wcache_lookup_aliasmem(domain,
2635 mem_ctx,
2636 group_sid,
2637 num_sids,
2638 sid_mem);
2639 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2640 return status;
2643 (*num_sids) = 0;
2644 (*sid_mem) = NULL;
2646 D_DEBUG("[Cached] - doing backend query for info for domain %s\n",
2647 domain->name);
2649 status = domain->backend->lookup_aliasmem(domain,
2650 mem_ctx,
2651 group_sid,
2652 type,
2653 num_sids,
2654 sid_mem);
2656 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2657 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2658 if (!domain->internal && old_status) {
2659 set_domain_offline(domain);
2661 if (!domain->internal && !domain->online && old_status) {
2662 NTSTATUS cache_status;
2663 cache_status = wcache_lookup_aliasmem(domain,
2664 mem_ctx,
2665 group_sid,
2666 num_sids,
2667 sid_mem);
2668 return cache_status;
2671 /* and save it */
2672 refresh_sequence_number(domain);
2673 if (!NT_STATUS_IS_OK(status)) {
2674 return status;
2676 centry = centry_start(domain, status);
2677 if (!centry)
2678 goto skip_save;
2679 centry_put_uint32(centry, *num_sids);
2680 for (i = 0; i < (*num_sids); i++) {
2681 centry_put_sid(centry, &(*sid_mem)[i]);
2683 centry_end(centry, "AM/%s", dom_sid_str_buf(group_sid, &sid_string));
2684 centry_free(centry);
2686 skip_save:
2687 return status;
2690 static NTSTATUS wcache_lookup_groupmem(struct winbindd_domain *domain,
2691 TALLOC_CTX *mem_ctx,
2692 const struct dom_sid *group_sid,
2693 uint32_t *num_names,
2694 struct dom_sid **sid_mem, char ***names,
2695 uint32_t **name_types)
2697 struct winbind_cache *cache = get_cache(domain);
2698 struct cache_entry *centry = NULL;
2699 NTSTATUS status;
2700 unsigned int i;
2701 struct dom_sid_buf sid_string;
2703 if (cache->tdb == NULL) {
2704 return NT_STATUS_NOT_FOUND;
2707 centry = wcache_fetch(
2708 cache,
2709 domain,
2710 "GM/%s",
2711 dom_sid_str_buf(group_sid, &sid_string));
2712 if (centry == NULL) {
2713 return NT_STATUS_NOT_FOUND;
2716 *sid_mem = NULL;
2717 *names = NULL;
2718 *name_types = NULL;
2720 *num_names = centry_uint32(centry);
2721 if (*num_names == 0) {
2722 centry_free(centry);
2723 return NT_STATUS_OK;
2726 *sid_mem = talloc_array(mem_ctx, struct dom_sid, *num_names);
2727 *names = talloc_array(mem_ctx, char *, *num_names);
2728 *name_types = talloc_array(mem_ctx, uint32_t, *num_names);
2730 if ((*sid_mem == NULL) || (*names == NULL) || (*name_types == NULL)) {
2731 TALLOC_FREE(*sid_mem);
2732 TALLOC_FREE(*names);
2733 TALLOC_FREE(*name_types);
2734 centry_free(centry);
2735 return NT_STATUS_NO_MEMORY;
2738 for (i=0; i<(*num_names); i++) {
2739 centry_sid(centry, &(*sid_mem)[i]);
2740 (*names)[i] = centry_string(centry, mem_ctx);
2741 (*name_types)[i] = centry_uint32(centry);
2744 status = centry->status;
2746 DBG_DEBUG("lookup_groupmem: [Cached] - cached info for domain %s "
2747 "status: %s\n", domain->name, nt_errstr(status));
2749 centry_free(centry);
2750 return status;
2753 NTSTATUS wb_cache_lookup_groupmem(struct winbindd_domain *domain,
2754 TALLOC_CTX *mem_ctx,
2755 const struct dom_sid *group_sid,
2756 enum lsa_SidType type,
2757 uint32_t *num_names,
2758 struct dom_sid **sid_mem,
2759 char ***names,
2760 uint32_t **name_types)
2762 struct cache_entry *centry = NULL;
2763 NTSTATUS status;
2764 unsigned int i;
2765 struct dom_sid_buf sid_string;
2766 bool old_status;
2768 old_status = domain->online;
2769 status = wcache_lookup_groupmem(domain, mem_ctx, group_sid, num_names,
2770 sid_mem, names, name_types);
2771 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2772 return status;
2775 (*num_names) = 0;
2776 (*sid_mem) = NULL;
2777 (*names) = NULL;
2778 (*name_types) = NULL;
2780 DBG_DEBUG("lookup_groupmem: [Cached] - doing backend query for info for domain %s\n",
2781 domain->name );
2783 status = domain->backend->lookup_groupmem(domain, mem_ctx, group_sid,
2784 type, num_names,
2785 sid_mem, names, name_types);
2787 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2788 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2789 if (!domain->internal && old_status) {
2790 set_domain_offline(domain);
2792 if (!domain->internal &&
2793 !domain->online &&
2794 old_status) {
2795 NTSTATUS cache_status;
2796 cache_status = wcache_lookup_groupmem(domain, mem_ctx, group_sid,
2797 num_names, sid_mem, names,
2798 name_types);
2799 return cache_status;
2802 /* and save it */
2803 refresh_sequence_number(domain);
2804 if (!NT_STATUS_IS_OK(status)) {
2805 return status;
2807 centry = centry_start(domain, status);
2808 if (!centry)
2809 goto skip_save;
2810 centry_put_uint32(centry, *num_names);
2811 for (i=0; i<(*num_names); i++) {
2812 centry_put_sid(centry, &(*sid_mem)[i]);
2813 centry_put_string(centry, (*names)[i]);
2814 centry_put_uint32(centry, (*name_types)[i]);
2816 centry_end(centry,
2817 "GM/%s",
2818 dom_sid_str_buf(group_sid, &sid_string));
2819 centry_free(centry);
2821 skip_save:
2822 return status;
2825 /* find the sequence number for a domain */
2826 NTSTATUS wb_cache_sequence_number(struct winbindd_domain *domain,
2827 uint32_t *seq)
2829 refresh_sequence_number(domain);
2831 *seq = domain->sequence_number;
2833 return NT_STATUS_OK;
2836 /* enumerate trusted domains
2837 * (we need to have the list of trustdoms in the cache when we go offline) -
2838 * Guenther */
2839 NTSTATUS wb_cache_trusted_domains(struct winbindd_domain *domain,
2840 TALLOC_CTX *mem_ctx,
2841 struct netr_DomainTrustList *trusts)
2843 NTSTATUS status;
2844 struct winbind_cache *cache;
2845 struct winbindd_tdc_domain *dom_list = NULL;
2846 size_t num_domains = 0;
2847 bool retval = false;
2848 size_t i;
2849 bool old_status;
2851 old_status = domain->online;
2852 trusts->count = 0;
2853 trusts->array = NULL;
2855 cache = get_cache(domain);
2856 if (!cache || !cache->tdb) {
2857 goto do_query;
2860 if (domain->online) {
2861 goto do_query;
2864 retval = wcache_tdc_fetch_list(&dom_list, &num_domains);
2865 if (!retval || !num_domains || !dom_list) {
2866 TALLOC_FREE(dom_list);
2867 goto do_query;
2870 do_fetch_cache:
2871 trusts->array = talloc_zero_array(mem_ctx, struct netr_DomainTrust, num_domains);
2872 if (!trusts->array) {
2873 TALLOC_FREE(dom_list);
2874 return NT_STATUS_NO_MEMORY;
2877 for (i = 0; i < num_domains; i++) {
2878 struct netr_DomainTrust *trust;
2879 struct dom_sid *sid;
2880 struct winbindd_domain *dom;
2882 dom = find_domain_from_name_noinit(dom_list[i].domain_name);
2883 if (dom && dom->internal) {
2884 continue;
2887 trust = &trusts->array[trusts->count];
2888 trust->netbios_name = talloc_strdup(trusts->array, dom_list[i].domain_name);
2889 trust->dns_name = talloc_strdup(trusts->array, dom_list[i].dns_name);
2890 sid = talloc(trusts->array, struct dom_sid);
2891 if (!trust->netbios_name || !trust->dns_name ||
2892 !sid) {
2893 TALLOC_FREE(dom_list);
2894 TALLOC_FREE(trusts->array);
2895 return NT_STATUS_NO_MEMORY;
2898 trust->trust_flags = dom_list[i].trust_flags;
2899 trust->trust_attributes = dom_list[i].trust_attribs;
2900 trust->trust_type = dom_list[i].trust_type;
2901 sid_copy(sid, &dom_list[i].sid);
2902 trust->sid = sid;
2903 trusts->count++;
2906 TALLOC_FREE(dom_list);
2907 return NT_STATUS_OK;
2909 do_query:
2910 DBG_DEBUG("trusted_domains: [Cached] - doing backend query for info for domain %s\n",
2911 domain->name );
2913 status = domain->backend->trusted_domains(domain, mem_ctx, trusts);
2915 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2916 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2917 if (!domain->internal && old_status) {
2918 set_domain_offline(domain);
2920 if (!domain->internal &&
2921 !domain->online &&
2922 old_status) {
2923 retval = wcache_tdc_fetch_list(&dom_list, &num_domains);
2924 if (retval && num_domains && dom_list) {
2925 TALLOC_FREE(trusts->array);
2926 trusts->count = 0;
2927 goto do_fetch_cache;
2931 /* no trusts gives NT_STATUS_NO_MORE_ENTRIES resetting to NT_STATUS_OK
2932 * so that the generic centry handling still applies correctly -
2933 * Guenther*/
2935 if (!NT_STATUS_IS_ERR(status)) {
2936 status = NT_STATUS_OK;
2938 return status;
2941 /* get lockout policy */
2942 NTSTATUS wb_cache_lockout_policy(struct winbindd_domain *domain,
2943 TALLOC_CTX *mem_ctx,
2944 struct samr_DomInfo12 *policy)
2946 struct winbind_cache *cache = get_cache(domain);
2947 struct cache_entry *centry = NULL;
2948 NTSTATUS status;
2949 bool old_status;
2951 old_status = domain->online;
2952 if (!cache->tdb)
2953 goto do_query;
2955 centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
2957 if (!centry)
2958 goto do_query;
2960 do_fetch_cache:
2961 policy->lockout_duration = centry_nttime(centry);
2962 policy->lockout_window = centry_nttime(centry);
2963 policy->lockout_threshold = centry_uint16(centry);
2965 status = centry->status;
2967 DBG_DEBUG("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2968 domain->name, nt_errstr(status) );
2970 centry_free(centry);
2971 return status;
2973 do_query:
2974 ZERO_STRUCTP(policy);
2976 DBG_DEBUG("lockout_policy: [Cached] - doing backend query for info for domain %s\n",
2977 domain->name );
2979 status = domain->backend->lockout_policy(domain, mem_ctx, policy);
2981 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2982 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2983 if (!domain->internal && old_status) {
2984 set_domain_offline(domain);
2986 if (cache->tdb &&
2987 !domain->internal &&
2988 !domain->online &&
2989 old_status) {
2990 centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
2991 if (centry) {
2992 goto do_fetch_cache;
2996 /* and save it */
2997 refresh_sequence_number(domain);
2998 if (!NT_STATUS_IS_OK(status)) {
2999 return status;
3001 wcache_save_lockout_policy(domain, status, policy);
3003 return status;
3006 /* get password policy */
3007 NTSTATUS wb_cache_password_policy(struct winbindd_domain *domain,
3008 TALLOC_CTX *mem_ctx,
3009 struct samr_DomInfo1 *policy)
3011 struct winbind_cache *cache = get_cache(domain);
3012 struct cache_entry *centry = NULL;
3013 NTSTATUS status;
3014 bool old_status;
3016 old_status = domain->online;
3017 if (!cache->tdb)
3018 goto do_query;
3020 centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
3022 if (!centry)
3023 goto do_query;
3025 do_fetch_cache:
3026 policy->min_password_length = centry_uint16(centry);
3027 policy->password_history_length = centry_uint16(centry);
3028 policy->password_properties = centry_uint32(centry);
3029 policy->max_password_age = centry_nttime(centry);
3030 policy->min_password_age = centry_nttime(centry);
3032 status = centry->status;
3034 DBG_DEBUG("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
3035 domain->name, nt_errstr(status) );
3037 centry_free(centry);
3038 return status;
3040 do_query:
3041 ZERO_STRUCTP(policy);
3043 DBG_DEBUG("password_policy: [Cached] - doing backend query for info for domain %s\n",
3044 domain->name );
3046 status = domain->backend->password_policy(domain, mem_ctx, policy);
3048 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
3049 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
3050 if (!domain->internal && old_status) {
3051 set_domain_offline(domain);
3053 if (cache->tdb &&
3054 !domain->internal &&
3055 !domain->online &&
3056 old_status) {
3057 centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
3058 if (centry) {
3059 goto do_fetch_cache;
3063 /* and save it */
3064 refresh_sequence_number(domain);
3065 if (!NT_STATUS_IS_OK(status)) {
3066 return status;
3068 wcache_save_password_policy(domain, status, policy);
3070 return status;
3074 /* Invalidate cached user and group lists coherently */
3076 static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
3077 void *state)
3079 if (strncmp((const char *)kbuf.dptr, "UL/", 3) == 0 ||
3080 strncmp((const char *)kbuf.dptr, "GL/", 3) == 0)
3081 tdb_delete(the_tdb, kbuf);
3083 return 0;
3086 /* Invalidate the getpwnam and getgroups entries for a winbindd domain */
3088 void wcache_invalidate_samlogon(struct winbindd_domain *domain,
3089 const struct dom_sid *sid)
3091 fstring key_str;
3092 struct dom_sid_buf sid_string;
3093 struct winbind_cache *cache;
3095 /* don't clear cached U/SID and UG/SID entries when we want to logon
3096 * offline - gd */
3098 if (lp_winbind_offline_logon()) {
3099 return;
3102 if (!domain)
3103 return;
3105 cache = get_cache(domain);
3107 if (!cache->tdb) {
3108 return;
3111 /* Clear U/SID cache entry */
3112 fstr_sprintf(key_str, "U/%s", dom_sid_str_buf(sid, &sid_string));
3113 DBG_DEBUG("wcache_invalidate_samlogon: clearing %s\n", key_str);
3114 tdb_delete(cache->tdb, string_tdb_data(key_str));
3116 /* Clear UG/SID cache entry */
3117 fstr_sprintf(key_str, "UG/%s", dom_sid_str_buf(sid, &sid_string));
3118 DBG_DEBUG("wcache_invalidate_samlogon: clearing %s\n", key_str);
3119 tdb_delete(cache->tdb, string_tdb_data(key_str));
3121 /* Samba/winbindd never needs this. */
3122 netsamlogon_clear_cached_user(sid);
3125 bool wcache_invalidate_cache(void)
3127 struct winbindd_domain *domain;
3129 for (domain = domain_list(); domain; domain = domain->next) {
3130 struct winbind_cache *cache = get_cache(domain);
3132 DBG_DEBUG("wcache_invalidate_cache: invalidating cache "
3133 "entries for %s\n", domain->name);
3134 if (cache) {
3135 if (cache->tdb) {
3136 tdb_traverse(cache->tdb, traverse_fn, NULL);
3137 } else {
3138 return false;
3142 return true;
3145 bool wcache_invalidate_cache_noinit(void)
3147 struct winbindd_domain *domain;
3149 for (domain = domain_list(); domain; domain = domain->next) {
3150 struct winbind_cache *cache;
3152 /* Skip uninitialized domains. */
3153 if (!domain->initialized && !domain->internal) {
3154 continue;
3157 cache = get_cache(domain);
3159 DBG_DEBUG("wcache_invalidate_cache: invalidating cache "
3160 "entries for %s\n", domain->name);
3161 if (cache) {
3162 if (cache->tdb) {
3163 tdb_traverse(cache->tdb, traverse_fn, NULL);
3165 * Flushing cache has nothing to with domains.
3166 * return here if we successfully flushed once.
3167 * To avoid unnecessary traversing the cache.
3169 return true;
3170 } else {
3171 return false;
3175 return true;
3178 static bool init_wcache(void)
3180 char *db_path;
3182 if (wcache == NULL) {
3183 wcache = SMB_XMALLOC_P(struct winbind_cache);
3184 ZERO_STRUCTP(wcache);
3187 if (wcache->tdb != NULL)
3188 return true;
3190 db_path = wcache_path();
3191 if (db_path == NULL) {
3192 return false;
3195 /* when working offline we must not clear the cache on restart */
3196 wcache->tdb = tdb_open_log(db_path,
3197 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
3198 TDB_INCOMPATIBLE_HASH |
3199 (lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST)),
3200 O_RDWR|O_CREAT, 0600);
3201 TALLOC_FREE(db_path);
3202 if (wcache->tdb == NULL) {
3203 DBG_ERR("Failed to open winbindd_cache.tdb!\n");
3204 return false;
3207 return true;
3210 /************************************************************************
3211 This is called by the parent to initialize the cache file.
3212 We don't need sophisticated locking here as we know we're the
3213 only opener.
3214 ************************************************************************/
3216 bool initialize_winbindd_cache(void)
3218 bool cache_bad = false;
3219 uint32_t vers = 0;
3220 bool ok;
3222 if (!init_wcache()) {
3223 DBG_ERR("initialize_winbindd_cache: init_wcache failed.\n");
3224 return false;
3227 /* Check version number. */
3228 ok = tdb_fetch_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, &vers);
3229 if (!ok) {
3230 DBG_DEBUG("Failed to get cache version\n");
3231 cache_bad = true;
3233 if (vers != WINBINDD_CACHE_VERSION) {
3234 DBG_DEBUG("Invalid cache version %u != %u\n",
3235 vers,
3236 WINBINDD_CACHE_VERSION);
3237 cache_bad = true;
3240 if (cache_bad) {
3241 char *db_path;
3243 DBG_NOTICE("initialize_winbindd_cache: clearing cache "
3244 "and re-creating with version number %d\n",
3245 WINBINDD_CACHE_VERSION);
3247 tdb_close(wcache->tdb);
3248 wcache->tdb = NULL;
3250 db_path = wcache_path();
3251 if (db_path == NULL) {
3252 return false;
3255 if (unlink(db_path) == -1) {
3256 DBG_ERR("initialize_winbindd_cache: unlink %s failed %s\n",
3257 db_path,
3258 strerror(errno) );
3259 TALLOC_FREE(db_path);
3260 return false;
3262 TALLOC_FREE(db_path);
3263 if (!init_wcache()) {
3264 DBG_ERR("initialize_winbindd_cache: re-initialization "
3265 "init_wcache failed.\n");
3266 return false;
3269 /* Write the version. */
3270 if (!tdb_store_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, WINBINDD_CACHE_VERSION)) {
3271 DBG_ERR("initialize_winbindd_cache: version number store failed %s\n",
3272 tdb_errorstr(wcache->tdb) );
3273 return false;
3277 tdb_close(wcache->tdb);
3278 wcache->tdb = NULL;
3279 return true;
3282 void close_winbindd_cache(void)
3284 if (!wcache) {
3285 return;
3287 if (wcache->tdb) {
3288 tdb_close(wcache->tdb);
3289 wcache->tdb = NULL;
3293 bool lookup_cached_sid(TALLOC_CTX *mem_ctx, const struct dom_sid *sid,
3294 char **domain_name, char **name,
3295 enum lsa_SidType *type)
3297 struct winbindd_domain *domain;
3298 NTSTATUS status;
3300 domain = find_lookup_domain_from_sid(sid);
3301 if (domain == NULL) {
3302 return false;
3304 status = wcache_sid_to_name(domain, sid, mem_ctx, domain_name, name,
3305 type);
3306 return NT_STATUS_IS_OK(status);
3309 bool lookup_cached_name(const char *namespace,
3310 const char *domain_name,
3311 const char *name,
3312 struct dom_sid *sid,
3313 enum lsa_SidType *type)
3315 struct winbindd_domain *domain;
3316 NTSTATUS status;
3317 bool original_online_state;
3319 domain = find_lookup_domain_from_name(namespace);
3320 if (domain == NULL) {
3321 return false;
3324 /* If we are doing a cached logon, temporarily set the domain
3325 offline so the cache won't expire the entry */
3327 original_online_state = domain->online;
3328 domain->online = false;
3329 status = wcache_name_to_sid(domain, domain_name, name, sid, type);
3330 domain->online = original_online_state;
3332 return NT_STATUS_IS_OK(status);
3336 * Cache a name to sid without checking the sequence number.
3337 * Used when caching from a trusted PAC.
3340 void cache_name2sid_trusted(struct winbindd_domain *domain,
3341 const char *domain_name,
3342 const char *name,
3343 enum lsa_SidType type,
3344 const struct dom_sid *sid)
3347 * Ensure we store the mapping with the
3348 * existing sequence number from the cache.
3350 get_cache(domain);
3351 (void)fetch_cache_seqnum(domain, time(NULL));
3352 wcache_save_name_to_sid(domain,
3353 NT_STATUS_OK,
3354 domain_name,
3355 name,
3356 sid,
3357 type);
3360 void cache_name2sid(struct winbindd_domain *domain,
3361 const char *domain_name, const char *name,
3362 enum lsa_SidType type, const struct dom_sid *sid)
3364 refresh_sequence_number(domain);
3365 wcache_save_name_to_sid(domain, NT_STATUS_OK, domain_name, name,
3366 sid, type);
3370 * The original idea that this cache only contains centries has
3371 * been blurred - now other stuff gets put in here. Ensure we
3372 * ignore these things on cleanup.
3375 static int traverse_fn_cleanup(TDB_CONTEXT *the_tdb, TDB_DATA kbuf,
3376 TDB_DATA dbuf, void *state)
3378 struct cache_entry *centry;
3380 if (is_non_centry_key(kbuf)) {
3381 return 0;
3384 centry = wcache_fetch_raw((char *)kbuf.dptr);
3385 if (!centry) {
3386 return 0;
3389 if (!NT_STATUS_IS_OK(centry->status)) {
3390 DBG_DEBUG("deleting centry %s\n", (const char *)kbuf.dptr);
3391 tdb_delete(the_tdb, kbuf);
3394 centry_free(centry);
3395 return 0;
3398 /* flush the cache */
3399 static void wcache_flush_cache(void)
3401 char *db_path;
3403 if (!wcache)
3404 return;
3405 if (wcache->tdb) {
3406 tdb_close(wcache->tdb);
3407 wcache->tdb = NULL;
3409 if (!winbindd_use_cache()) {
3410 return;
3413 db_path = wcache_path();
3414 if (db_path == NULL) {
3415 return;
3418 /* when working offline we must not clear the cache on restart */
3419 wcache->tdb = tdb_open_log(db_path,
3420 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
3421 TDB_INCOMPATIBLE_HASH |
3422 (lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST)),
3423 O_RDWR|O_CREAT, 0600);
3424 TALLOC_FREE(db_path);
3425 if (!wcache->tdb) {
3426 DBG_ERR("Failed to open winbindd_cache.tdb!\n");
3427 return;
3430 tdb_traverse(wcache->tdb, traverse_fn_cleanup, NULL);
3432 DBG_DEBUG("wcache_flush_cache success\n");
3435 /* Count cached creds */
3437 static int traverse_fn_cached_creds(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
3438 void *state)
3440 int *cred_count = (int*)state;
3442 if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
3443 (*cred_count)++;
3445 return 0;
3448 NTSTATUS wcache_count_cached_creds(struct winbindd_domain *domain, int *count)
3450 struct winbind_cache *cache = get_cache(domain);
3452 *count = 0;
3454 if (!cache->tdb) {
3455 return NT_STATUS_INTERNAL_DB_ERROR;
3458 tdb_traverse(cache->tdb, traverse_fn_cached_creds, (void *)count);
3460 return NT_STATUS_OK;
3463 struct cred_list {
3464 struct cred_list *prev, *next;
3465 TDB_DATA key;
3466 fstring name;
3467 time_t created;
3469 static struct cred_list *wcache_cred_list;
3471 static int traverse_fn_get_credlist(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
3472 void *state)
3474 struct cred_list *cred;
3476 if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
3478 cred = SMB_MALLOC_P(struct cred_list);
3479 if (cred == NULL) {
3480 DBG_ERR("traverse_fn_remove_first_creds: failed to malloc new entry for list\n");
3481 return -1;
3484 ZERO_STRUCTP(cred);
3486 /* save a copy of the key */
3488 fstrcpy(cred->name, (const char *)kbuf.dptr);
3489 DLIST_ADD(wcache_cred_list, cred);
3492 return 0;
3495 NTSTATUS wcache_remove_oldest_cached_creds(struct winbindd_domain *domain, const struct dom_sid *sid)
3497 struct winbind_cache *cache = get_cache(domain);
3498 NTSTATUS status;
3499 int ret;
3500 struct cred_list *cred, *next, *oldest = NULL;
3502 if (!cache->tdb) {
3503 return NT_STATUS_INTERNAL_DB_ERROR;
3506 /* we possibly already have an entry */
3507 if (sid && NT_STATUS_IS_OK(wcache_cached_creds_exist(domain, sid))) {
3509 fstring key_str;
3510 struct dom_sid_buf tmp;
3512 DBG_DEBUG("we already have an entry, deleting that\n");
3514 fstr_sprintf(key_str, "CRED/%s", dom_sid_str_buf(sid, &tmp));
3516 tdb_delete(cache->tdb, string_tdb_data(key_str));
3518 return NT_STATUS_OK;
3521 ret = tdb_traverse(cache->tdb, traverse_fn_get_credlist, NULL);
3522 if (ret == 0) {
3523 return NT_STATUS_OK;
3524 } else if ((ret < 0) || (wcache_cred_list == NULL)) {
3525 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
3528 ZERO_STRUCTP(oldest);
3530 for (cred = wcache_cred_list; cred; cred = cred->next) {
3532 TDB_DATA data;
3533 time_t t;
3535 data = tdb_fetch(cache->tdb, string_tdb_data(cred->name));
3536 if (!data.dptr) {
3537 DBG_DEBUG("wcache_remove_oldest_cached_creds: entry for [%s] not found\n",
3538 cred->name);
3539 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
3540 goto done;
3543 t = IVAL(data.dptr, 0);
3544 SAFE_FREE(data.dptr);
3546 if (!oldest) {
3547 oldest = SMB_MALLOC_P(struct cred_list);
3548 if (oldest == NULL) {
3549 status = NT_STATUS_NO_MEMORY;
3550 goto done;
3553 fstrcpy(oldest->name, cred->name);
3554 oldest->created = t;
3555 continue;
3558 if (t < oldest->created) {
3559 fstrcpy(oldest->name, cred->name);
3560 oldest->created = t;
3564 if (tdb_delete(cache->tdb, string_tdb_data(oldest->name)) == 0) {
3565 status = NT_STATUS_OK;
3566 } else {
3567 status = NT_STATUS_UNSUCCESSFUL;
3569 done:
3570 for (cred = wcache_cred_list; cred; cred = next) {
3571 next = cred->next;
3572 DLIST_REMOVE(wcache_cred_list, cred);
3573 SAFE_FREE(cred);
3575 SAFE_FREE(oldest);
3577 return status;
3580 /* Change the global online/offline state. */
3581 bool set_global_winbindd_state_offline(void)
3583 bool ok;
3584 uint8_t buf[4] = {0};
3585 TDB_DATA data = {
3586 .dptr = buf,
3587 .dsize = sizeof(buf)
3589 int rc;
3591 DBG_NOTICE("Offline requested\n");
3593 if (wcache == NULL || wcache->tdb == NULL) {
3594 DBG_NOTICE("Winbind cache doesn't exist yet\n");
3595 return false;
3598 if (!lp_winbind_offline_logon()) {
3599 DBG_DEBUG("Rejecting request to set winbind offline, "
3600 "offline logons are disabled in smb.conf\n");
3601 return false;
3604 ok = get_global_winbindd_state_offline();
3605 if (ok) {
3606 return true;
3609 PUSH_LE_U32(buf, 0, time(NULL));
3611 rc = tdb_store_bystring(wcache->tdb,
3612 "WINBINDD_OFFLINE",
3613 data,
3614 TDB_INSERT);
3615 if (rc != 0) {
3616 return false;
3619 return true;
3623 void set_global_winbindd_state_online(void)
3625 DBG_DEBUG("set_global_winbindd_state_online: online requested.\n");
3627 if (!lp_winbind_offline_logon()) {
3628 DBG_DEBUG("Rejecting request to set winbind online, "
3629 "offline logons are disabled in smb.conf.\n");
3630 return;
3633 if (!wcache->tdb) {
3634 return;
3637 /* Ensure there is no key "WINBINDD_OFFLINE" in the cache tdb. */
3638 tdb_delete_bystring(wcache->tdb, "WINBINDD_OFFLINE");
3641 bool get_global_winbindd_state_offline(void)
3643 TDB_DATA data;
3645 data = tdb_fetch_bystring(wcache->tdb, "WINBINDD_OFFLINE");
3646 if (data.dptr == NULL || data.dsize != 4) {
3647 DBG_DEBUG("Offline state not set.\n");
3648 SAFE_FREE(data.dptr);
3649 return false;
3652 return true;
3655 /***********************************************************************
3656 Validate functions for all possible cache tdb keys.
3657 ***********************************************************************/
3659 static struct cache_entry *create_centry_validate(const char *kstr, TDB_DATA data,
3660 struct tdb_validation_status *state)
3662 struct cache_entry *centry;
3664 centry = SMB_XMALLOC_P(struct cache_entry);
3665 centry->data = (unsigned char *)smb_memdup(data.dptr, data.dsize);
3666 if (!centry->data) {
3667 SAFE_FREE(centry);
3668 return NULL;
3670 centry->len = data.dsize;
3671 centry->ofs = 0;
3673 if (centry->len < 16) {
3674 /* huh? corrupt cache? */
3675 DBG_ERR("create_centry_validate: Corrupt cache for key %s "
3676 "(len < 16) ?\n", kstr);
3677 centry_free(centry);
3678 state->bad_entry = true;
3679 state->success = false;
3680 return NULL;
3683 centry->status = NT_STATUS(centry_uint32(centry));
3684 centry->sequence_number = centry_uint32(centry);
3685 centry->timeout = centry_uint64_t(centry);
3686 return centry;
3689 static int validate_seqnum(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3690 struct tdb_validation_status *state)
3692 if (dbuf.dsize != 8) {
3693 DBG_ERR("validate_seqnum: Corrupt cache for key %s (len %u != 8) ?\n",
3694 keystr, (unsigned int)dbuf.dsize );
3695 state->bad_entry = true;
3696 return 1;
3698 return 0;
3701 static int validate_u(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3702 struct tdb_validation_status *state)
3704 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3705 struct dom_sid sid;
3707 if (!centry) {
3708 return 1;
3711 (void)centry_string(centry, mem_ctx);
3712 (void)centry_string(centry, mem_ctx);
3713 (void)centry_string(centry, mem_ctx);
3714 (void)centry_string(centry, mem_ctx);
3715 (void)centry_string(centry, mem_ctx);
3716 (void)centry_uint32(centry);
3717 (void)centry_uint32(centry);
3718 (void)centry_string(centry, mem_ctx);
3719 (void)centry_sid(centry, &sid);
3720 (void)centry_sid(centry, &sid);
3722 centry_free(centry);
3724 if (!(state->success)) {
3725 return 1;
3727 DBG_DEBUG("validate_u: %s ok\n", keystr);
3728 return 0;
3731 static int validate_loc_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3732 struct tdb_validation_status *state)
3734 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3736 if (!centry) {
3737 return 1;
3740 (void)centry_nttime(centry);
3741 (void)centry_nttime(centry);
3742 (void)centry_uint16(centry);
3744 centry_free(centry);
3746 if (!(state->success)) {
3747 return 1;
3749 DBG_DEBUG("validate_loc_pol: %s ok\n", keystr);
3750 return 0;
3753 static int validate_pwd_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3754 struct tdb_validation_status *state)
3756 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3758 if (!centry) {
3759 return 1;
3762 (void)centry_uint16(centry);
3763 (void)centry_uint16(centry);
3764 (void)centry_uint32(centry);
3765 (void)centry_nttime(centry);
3766 (void)centry_nttime(centry);
3768 centry_free(centry);
3770 if (!(state->success)) {
3771 return 1;
3773 DBG_DEBUG("validate_pwd_pol: %s ok\n", keystr);
3774 return 0;
3777 static int validate_cred(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3778 struct tdb_validation_status *state)
3780 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3782 if (!centry) {
3783 return 1;
3786 (void)centry_time(centry);
3787 (void)centry_hash16(centry, mem_ctx);
3789 /* We only have 17 bytes more data in the salted cred case. */
3790 if (centry->len - centry->ofs == 17) {
3791 (void)centry_hash16(centry, mem_ctx);
3794 centry_free(centry);
3796 if (!(state->success)) {
3797 return 1;
3799 DBG_DEBUG("validate_cred: %s ok\n", keystr);
3800 return 0;
3803 static int validate_ul(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3804 struct tdb_validation_status *state)
3806 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3807 int32_t num_entries, i;
3809 if (!centry) {
3810 return 1;
3813 num_entries = (int32_t)centry_uint32(centry);
3815 for (i=0; i< num_entries; i++) {
3816 (void)centry_uint32(centry);
3819 centry_free(centry);
3821 if (!(state->success)) {
3822 return 1;
3824 DBG_DEBUG("validate_ul: %s ok\n", keystr);
3825 return 0;
3828 static int validate_gl(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3829 struct tdb_validation_status *state)
3831 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3832 int32_t num_entries, i;
3834 if (!centry) {
3835 return 1;
3838 num_entries = centry_uint32(centry);
3840 for (i=0; i< num_entries; i++) {
3841 (void)centry_string(centry, mem_ctx);
3842 (void)centry_string(centry, mem_ctx);
3843 (void)centry_uint32(centry);
3846 centry_free(centry);
3848 if (!(state->success)) {
3849 return 1;
3851 DBG_DEBUG("validate_gl: %s ok\n", keystr);
3852 return 0;
3855 static int validate_ug(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3856 struct tdb_validation_status *state)
3858 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3859 int32_t num_groups, i;
3861 if (!centry) {
3862 return 1;
3865 num_groups = centry_uint32(centry);
3867 for (i=0; i< num_groups; i++) {
3868 struct dom_sid sid;
3869 centry_sid(centry, &sid);
3872 centry_free(centry);
3874 if (!(state->success)) {
3875 return 1;
3877 DBG_DEBUG("validate_ug: %s ok\n", keystr);
3878 return 0;
3881 static int validate_ua(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3882 struct tdb_validation_status *state)
3884 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3885 int32_t num_aliases, i;
3887 if (!centry) {
3888 return 1;
3891 num_aliases = centry_uint32(centry);
3893 for (i=0; i < num_aliases; i++) {
3894 (void)centry_uint32(centry);
3897 centry_free(centry);
3899 if (!(state->success)) {
3900 return 1;
3902 DBG_DEBUG("validate_ua: %s ok\n", keystr);
3903 return 0;
3906 static int validate_gm(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3907 struct tdb_validation_status *state)
3909 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3910 int32_t num_names, i;
3912 if (!centry) {
3913 return 1;
3916 num_names = centry_uint32(centry);
3918 for (i=0; i< num_names; i++) {
3919 struct dom_sid sid;
3920 centry_sid(centry, &sid);
3921 (void)centry_string(centry, mem_ctx);
3922 (void)centry_uint32(centry);
3925 centry_free(centry);
3927 if (!(state->success)) {
3928 return 1;
3930 DBG_DEBUG("validate_gm: %s ok\n", keystr);
3931 return 0;
3934 static int validate_dr(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3935 struct tdb_validation_status *state)
3937 /* Can't say anything about this other than must be nonzero. */
3938 if (dbuf.dsize == 0) {
3939 DBG_ERR("validate_dr: Corrupt cache for key %s (len == 0) ?\n",
3940 keystr);
3941 state->bad_entry = true;
3942 state->success = false;
3943 return 1;
3946 DBG_DEBUG("validate_dr: %s ok\n", keystr);
3947 return 0;
3950 static int validate_de(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3951 struct tdb_validation_status *state)
3953 /* Can't say anything about this other than must be nonzero. */
3954 if (dbuf.dsize == 0) {
3955 DBG_ERR("validate_de: Corrupt cache for key %s (len == 0) ?\n",
3956 keystr);
3957 state->bad_entry = true;
3958 state->success = false;
3959 return 1;
3962 DBG_DEBUG("validate_de: %s ok\n", keystr);
3963 return 0;
3966 static int validate_nss_an(TALLOC_CTX *mem_ctx, const char *keystr,
3967 TDB_DATA dbuf,
3968 struct tdb_validation_status *state)
3970 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3972 if (!centry) {
3973 return 1;
3976 (void)centry_string( centry, mem_ctx );
3978 centry_free(centry);
3980 if (!(state->success)) {
3981 return 1;
3983 DBG_DEBUG("validate_pwinfo: %s ok\n", keystr);
3984 return 0;
3987 static int validate_nss_na(TALLOC_CTX *mem_ctx, const char *keystr,
3988 TDB_DATA dbuf,
3989 struct tdb_validation_status *state)
3991 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3993 if (!centry) {
3994 return 1;
3997 (void)centry_string( centry, mem_ctx );
3999 centry_free(centry);
4001 if (!(state->success)) {
4002 return 1;
4004 DBG_DEBUG("%s ok\n", keystr);
4005 return 0;
4008 static int validate_trustdomcache(TALLOC_CTX *mem_ctx, const char *keystr,
4009 TDB_DATA dbuf,
4010 struct tdb_validation_status *state)
4012 if (dbuf.dsize == 0) {
4013 DBG_ERR("validate_trustdomcache: Corrupt cache for "
4014 "key %s (len ==0) ?\n", keystr);
4015 state->bad_entry = true;
4016 state->success = false;
4017 return 1;
4020 DBG_DEBUG("validate_trustdomcache: %s ok\n"
4021 " Don't trust me, I am a DUMMY!\n",
4022 keystr);
4023 return 0;
4026 static int validate_offline(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
4027 struct tdb_validation_status *state)
4029 if (dbuf.dsize != 4) {
4030 DBG_ERR("validate_offline: Corrupt cache for key %s (len %u != 4) ?\n",
4031 keystr, (unsigned int)dbuf.dsize );
4032 state->bad_entry = true;
4033 state->success = false;
4034 return 1;
4036 DBG_DEBUG("validate_offline: %s ok\n", keystr);
4037 return 0;
4040 static int validate_ndr(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
4041 struct tdb_validation_status *state)
4044 * Ignore validation for now. The proper way to do this is with a
4045 * checksum. Just pure parsing does not really catch much.
4047 return 0;
4050 static int validate_cache_version(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
4051 struct tdb_validation_status *state)
4053 if (dbuf.dsize != 4) {
4054 DBG_ERR("validate_cache_version: Corrupt cache for "
4055 "key %s (len %u != 4) ?\n",
4056 keystr, (unsigned int)dbuf.dsize);
4057 state->bad_entry = true;
4058 state->success = false;
4059 return 1;
4062 DBG_DEBUG("validate_cache_version: %s ok\n", keystr);
4063 return 0;
4066 /***********************************************************************
4067 A list of all possible cache tdb keys with associated validation
4068 functions.
4069 ***********************************************************************/
4071 struct key_val_struct {
4072 const char *keyname;
4073 int (*validate_data_fn)(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, struct tdb_validation_status* state);
4074 } key_val[] = {
4075 {"SEQNUM/", validate_seqnum},
4076 {"U/", validate_u},
4077 {"LOC_POL/", validate_loc_pol},
4078 {"PWD_POL/", validate_pwd_pol},
4079 {"CRED/", validate_cred},
4080 {"UL/", validate_ul},
4081 {"GL/", validate_gl},
4082 {"UG/", validate_ug},
4083 {"UA", validate_ua},
4084 {"GM/", validate_gm},
4085 {"DR/", validate_dr},
4086 {"DE/", validate_de},
4087 {"TRUSTDOMCACHE/", validate_trustdomcache},
4088 {"NSS/NA/", validate_nss_na},
4089 {"NSS/AN/", validate_nss_an},
4090 {"WINBINDD_OFFLINE", validate_offline},
4091 {"NDR/", validate_ndr},
4092 {WINBINDD_CACHE_VERSION_KEYSTR, validate_cache_version},
4093 {NULL, NULL}
4096 /***********************************************************************
4097 Function to look at every entry in the tdb and validate it as far as
4098 possible.
4099 ***********************************************************************/
4101 static int cache_traverse_validate_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, void *state)
4103 int i;
4104 unsigned int max_key_len = 1024;
4105 struct tdb_validation_status *v_state = (struct tdb_validation_status *)state;
4107 /* Paranoia check. */
4108 if (strncmp("UA/", (const char *)kbuf.dptr, 3) == 0 ||
4109 strncmp("NDR/", (const char *)kbuf.dptr, 4) == 0) {
4110 max_key_len = 1024 * 1024;
4112 if (kbuf.dsize > max_key_len) {
4113 DBG_ERR("cache_traverse_validate_fn: key length too large: "
4114 "(%u) > (%u)\n\n",
4115 (unsigned int)kbuf.dsize, (unsigned int)max_key_len);
4116 return 1;
4119 for (i = 0; key_val[i].keyname; i++) {
4120 size_t namelen = strlen(key_val[i].keyname);
4121 if (kbuf.dsize >= namelen && (
4122 strncmp(key_val[i].keyname, (const char *)kbuf.dptr, namelen)) == 0) {
4123 TALLOC_CTX *mem_ctx;
4124 char *keystr;
4125 int ret;
4127 keystr = SMB_MALLOC_ARRAY(char, kbuf.dsize+1);
4128 if (!keystr) {
4129 return 1;
4131 memcpy(keystr, kbuf.dptr, kbuf.dsize);
4132 keystr[kbuf.dsize] = '\0';
4134 mem_ctx = talloc_init("validate_ctx");
4135 if (!mem_ctx) {
4136 SAFE_FREE(keystr);
4137 return 1;
4140 ret = key_val[i].validate_data_fn(mem_ctx, keystr, dbuf,
4141 v_state);
4143 SAFE_FREE(keystr);
4144 talloc_destroy(mem_ctx);
4145 return ret;
4149 DBG_ERR("cache_traverse_validate_fn: unknown cache entry\nkey :\n");
4150 dump_data(0, (uint8_t *)kbuf.dptr, kbuf.dsize);
4151 DBG_ERR("data :\n");
4152 dump_data(0, (uint8_t *)dbuf.dptr, dbuf.dsize);
4153 v_state->unknown_key = true;
4154 v_state->success = false;
4155 return 1; /* terminate. */
4158 static void validate_panic(const char *const why)
4160 DBG_ERR("validating cache: would panic %s\n"
4161 "exiting instead (cache validation mode)\n", why );
4162 exit(47);
4165 static int wbcache_update_centry_fn(TDB_CONTEXT *tdb,
4166 TDB_DATA key,
4167 TDB_DATA data,
4168 void *state)
4170 uint64_t ctimeout;
4171 TDB_DATA blob;
4173 if (is_non_centry_key(key)) {
4174 return 0;
4177 if (data.dptr == NULL || data.dsize == 0) {
4178 if (tdb_delete(tdb, key) < 0) {
4179 DBG_ERR("tdb_delete for [%s] failed!\n",
4180 key.dptr);
4181 return 1;
4185 /* add timeout to blob (uint64_t) */
4186 blob.dsize = data.dsize + 8;
4188 blob.dptr = SMB_XMALLOC_ARRAY(uint8_t, blob.dsize);
4189 if (blob.dptr == NULL) {
4190 return 1;
4192 memset(blob.dptr, 0, blob.dsize);
4194 /* copy status and seqnum */
4195 memcpy(blob.dptr, data.dptr, 8);
4197 /* add timeout */
4198 ctimeout = lp_winbind_cache_time() + time(NULL);
4199 SBVAL(blob.dptr, 8, ctimeout);
4201 /* copy the rest */
4202 memcpy(blob.dptr + 16, data.dptr + 8, data.dsize - 8);
4204 if (tdb_store(tdb, key, blob, TDB_REPLACE) < 0) {
4205 DBG_ERR("tdb_store to update [%s] failed!\n",
4206 key.dptr);
4207 SAFE_FREE(blob.dptr);
4208 return 1;
4211 SAFE_FREE(blob.dptr);
4212 return 0;
4215 static bool wbcache_upgrade_v1_to_v2(TDB_CONTEXT *tdb)
4217 int rc;
4219 DBG_NOTICE("Upgrade to version 2 of the winbindd_cache.tdb\n");
4221 rc = tdb_traverse(tdb, wbcache_update_centry_fn, NULL);
4222 if (rc < 0) {
4223 return false;
4226 return true;
4229 /***********************************************************************
4230 Try and validate every entry in the winbindd cache. If we fail here,
4231 delete the cache tdb and return non-zero.
4232 ***********************************************************************/
4234 int winbindd_validate_cache(void)
4236 int ret = -1;
4237 char *tdb_path = NULL;
4238 TDB_CONTEXT *tdb = NULL;
4239 uint32_t vers_id;
4240 bool ok;
4242 DBG_DEBUG("winbindd_validate_cache: replacing panic function\n");
4243 smb_panic_fn = validate_panic;
4245 tdb_path = wcache_path();
4246 if (tdb_path == NULL) {
4247 goto done;
4250 tdb = tdb_open_log(tdb_path,
4251 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
4252 TDB_INCOMPATIBLE_HASH |
4253 ( lp_winbind_offline_logon()
4254 ? TDB_DEFAULT
4255 : TDB_DEFAULT | TDB_CLEAR_IF_FIRST ),
4256 O_RDWR|O_CREAT,
4257 0600);
4258 if (!tdb) {
4259 DBG_ERR("winbindd_validate_cache: "
4260 "error opening/initializing tdb\n");
4261 goto done;
4264 /* Version check and upgrade code. */
4265 if (!tdb_fetch_uint32(tdb, WINBINDD_CACHE_VERSION_KEYSTR, &vers_id)) {
4266 DBG_DEBUG("Fresh database\n");
4267 tdb_store_uint32(tdb, WINBINDD_CACHE_VERSION_KEYSTR, WINBINDD_CACHE_VERSION);
4268 vers_id = WINBINDD_CACHE_VERSION;
4271 if (vers_id != WINBINDD_CACHE_VERSION) {
4272 if (vers_id == WINBINDD_CACHE_VER1) {
4273 ok = wbcache_upgrade_v1_to_v2(tdb);
4274 if (!ok) {
4275 DBG_DEBUG("winbindd_validate_cache: upgrade to version 2 failed.\n");
4276 unlink(tdb_path);
4277 goto done;
4280 tdb_store_uint32(tdb,
4281 WINBINDD_CACHE_VERSION_KEYSTR,
4282 WINBINDD_CACHE_VERSION);
4283 vers_id = WINBINDD_CACHE_VER2;
4287 tdb_close(tdb);
4289 ret = tdb_validate_and_backup(tdb_path, cache_traverse_validate_fn);
4291 if (ret != 0) {
4292 DBG_DEBUG("winbindd_validate_cache: validation not successful.\n"
4293 "removing tdb %s.\n", tdb_path);
4294 unlink(tdb_path);
4297 done:
4298 TALLOC_FREE(tdb_path);
4299 DBG_DEBUG("winbindd_validate_cache: restoring panic function\n");
4300 smb_panic_fn = smb_panic;
4301 return ret;
4304 /***********************************************************************
4305 Try and validate every entry in the winbindd cache.
4306 ***********************************************************************/
4308 int winbindd_validate_cache_nobackup(void)
4310 int ret = -1;
4311 char *tdb_path;
4313 DBG_DEBUG("winbindd_validate_cache: replacing panic function\n");
4314 smb_panic_fn = validate_panic;
4316 tdb_path = wcache_path();
4317 if (tdb_path == NULL) {
4318 goto err_panic_restore;
4321 if (wcache == NULL || wcache->tdb == NULL) {
4322 ret = tdb_validate_open(tdb_path, cache_traverse_validate_fn);
4323 } else {
4324 ret = tdb_validate(wcache->tdb, cache_traverse_validate_fn);
4327 if (ret != 0) {
4328 DBG_DEBUG("winbindd_validate_cache_nobackup: validation not "
4329 "successful.\n");
4332 TALLOC_FREE(tdb_path);
4333 err_panic_restore:
4334 DBG_DEBUG("winbindd_validate_cache_nobackup: restoring panic "
4335 "function\n");
4336 smb_panic_fn = smb_panic;
4337 return ret;
4340 bool winbindd_cache_validate_and_initialize(void)
4342 close_winbindd_cache();
4344 if (lp_winbind_offline_logon()) {
4345 if (winbindd_validate_cache() < 0) {
4346 DBG_ERR("winbindd cache tdb corrupt and no backup "
4347 "could be restored.\n");
4351 return initialize_winbindd_cache();
4354 /*********************************************************************
4355 ********************************************************************/
4357 static bool add_wbdomain_to_tdc_array( struct winbindd_domain *new_dom,
4358 struct winbindd_tdc_domain **domains,
4359 size_t *num_domains )
4361 struct winbindd_tdc_domain *list = NULL;
4362 size_t i, idx;
4363 bool set_only = false;
4365 /* don't allow duplicates */
4367 idx = *num_domains;
4368 list = *domains;
4370 for ( i=0; i< (*num_domains); i++ ) {
4371 if ( strequal( new_dom->name, list[i].domain_name ) ) {
4372 DBG_DEBUG("add_wbdomain_to_tdc_array: Found existing record for %s\n",
4373 new_dom->name);
4374 idx = i;
4375 set_only = true;
4377 break;
4381 if ( !set_only ) {
4382 if ( !*domains ) {
4383 list = talloc_array( NULL, struct winbindd_tdc_domain, 1 );
4384 idx = 0;
4385 } else {
4386 list = talloc_realloc( *domains, *domains,
4387 struct winbindd_tdc_domain,
4388 (*num_domains)+1);
4389 idx = *num_domains;
4392 ZERO_STRUCT( list[idx] );
4395 if ( !list )
4396 return false;
4398 list[idx].domain_name = talloc_strdup(list, new_dom->name);
4399 if (list[idx].domain_name == NULL) {
4400 return false;
4402 if (new_dom->alt_name != NULL) {
4403 list[idx].dns_name = talloc_strdup(list, new_dom->alt_name);
4404 if (list[idx].dns_name == NULL) {
4405 return false;
4409 if ( !is_null_sid( &new_dom->sid ) ) {
4410 sid_copy( &list[idx].sid, &new_dom->sid );
4411 } else {
4412 sid_copy(&list[idx].sid, &global_sid_NULL);
4415 if ( new_dom->domain_flags != 0x0 )
4416 list[idx].trust_flags = new_dom->domain_flags;
4418 if ( new_dom->domain_type != 0x0 )
4419 list[idx].trust_type = new_dom->domain_type;
4421 if ( new_dom->domain_trust_attribs != 0x0 )
4422 list[idx].trust_attribs = new_dom->domain_trust_attribs;
4424 if ( !set_only ) {
4425 *domains = list;
4426 *num_domains = idx + 1;
4429 return true;
4432 /*********************************************************************
4433 ********************************************************************/
4435 static TDB_DATA make_tdc_key( const char *domain_name )
4437 char *keystr = NULL;
4438 TDB_DATA key = { NULL, 0 };
4440 if ( !domain_name ) {
4441 DBG_INFO("make_tdc_key: Keyname workgroup is NULL!\n");
4442 return key;
4445 if (asprintf( &keystr, "TRUSTDOMCACHE/%s", domain_name ) == -1) {
4446 return key;
4448 key = string_term_tdb_data(keystr);
4450 return key;
4453 /*********************************************************************
4454 ********************************************************************/
4456 static int pack_tdc_domains( struct winbindd_tdc_domain *domains,
4457 size_t num_domains,
4458 unsigned char **buf )
4460 unsigned char *buffer = NULL;
4461 int len = 0;
4462 int buflen = 0;
4463 size_t i = 0;
4465 DBG_DEBUG("pack_tdc_domains: Packing %d trusted domains\n",
4466 (int)num_domains);
4468 buflen = 0;
4470 again:
4471 len = 0;
4473 /* Store the number of array items first */
4474 len += tdb_pack( buffer ? buffer+len : NULL,
4475 buffer ? buflen-len : 0, "d",
4476 num_domains );
4478 /* now pack each domain trust record */
4479 for ( i=0; i<num_domains; i++ ) {
4481 struct dom_sid_buf tmp;
4483 if ( buflen > 0 ) {
4484 DBG_DEBUG("pack_tdc_domains: Packing domain %s (%s)\n",
4485 domains[i].domain_name,
4486 domains[i].dns_name ? domains[i].dns_name : "UNKNOWN" );
4489 len += tdb_pack( buffer ? buffer+len : NULL,
4490 buffer ? buflen-len : 0, "fffddd",
4491 domains[i].domain_name,
4492 domains[i].dns_name ? domains[i].dns_name : "",
4493 dom_sid_str_buf(&domains[i].sid, &tmp),
4494 domains[i].trust_flags,
4495 domains[i].trust_attribs,
4496 domains[i].trust_type );
4499 if ( buflen < len ) {
4500 SAFE_FREE(buffer);
4501 if ( (buffer = SMB_MALLOC_ARRAY(unsigned char, len)) == NULL ) {
4502 DBG_ERR("pack_tdc_domains: failed to alloc buffer!\n");
4503 buflen = -1;
4504 goto done;
4506 buflen = len;
4507 goto again;
4510 *buf = buffer;
4512 done:
4513 return buflen;
4516 /*********************************************************************
4517 ********************************************************************/
4519 static size_t unpack_tdc_domains( unsigned char *buf, int buflen,
4520 struct winbindd_tdc_domain **domains )
4522 fstring domain_name, dns_name, sid_string;
4523 uint32_t type, attribs, flags;
4524 int num_domains;
4525 int len = 0;
4526 int i;
4527 struct winbindd_tdc_domain *list = NULL;
4529 /* get the number of domains */
4530 len += tdb_unpack( buf+len, buflen-len, "d", &num_domains);
4531 if ( len == -1 ) {
4532 DBG_INFO("unpack_tdc_domains: Failed to unpack domain array\n");
4533 return 0;
4536 list = talloc_array( NULL, struct winbindd_tdc_domain, num_domains );
4537 if ( !list ) {
4538 DBG_ERR("unpack_tdc_domains: Failed to talloc() domain list!\n");
4539 return 0;
4542 for ( i=0; i<num_domains; i++ ) {
4543 int this_len;
4545 this_len = tdb_unpack( buf+len, buflen-len, "fffddd",
4546 domain_name,
4547 dns_name,
4548 sid_string,
4549 &flags,
4550 &attribs,
4551 &type );
4553 if ( this_len == -1 ) {
4554 DBG_INFO("unpack_tdc_domains: Failed to unpack domain array\n");
4555 TALLOC_FREE( list );
4556 return 0;
4558 len += this_len;
4560 DBG_DEBUG("unpack_tdc_domains: Unpacking domain %s (%s) "
4561 "SID %s, flags = 0x%x, attribs = 0x%x, type = 0x%x\n",
4562 domain_name, dns_name, sid_string,
4563 flags, attribs, type);
4565 list[i].domain_name = talloc_strdup( list, domain_name );
4566 list[i].dns_name = NULL;
4567 if (dns_name[0] != '\0') {
4568 list[i].dns_name = talloc_strdup(list, dns_name);
4570 if ( !string_to_sid( &(list[i].sid), sid_string ) ) {
4571 DBG_DEBUG("unpack_tdc_domains: no SID for domain %s\n",
4572 domain_name);
4574 list[i].trust_flags = flags;
4575 list[i].trust_attribs = attribs;
4576 list[i].trust_type = type;
4579 *domains = list;
4581 return num_domains;
4584 /*********************************************************************
4585 ********************************************************************/
4587 static bool wcache_tdc_store_list( struct winbindd_tdc_domain *domains, size_t num_domains )
4589 TDB_DATA key = make_tdc_key( lp_workgroup() );
4590 TDB_DATA data = { NULL, 0 };
4591 int ret;
4593 if ( !key.dptr )
4594 return false;
4596 /* See if we were asked to delete the cache entry */
4598 if ( !domains ) {
4599 ret = tdb_delete( wcache->tdb, key );
4600 goto done;
4603 data.dsize = pack_tdc_domains( domains, num_domains, &data.dptr );
4605 if ( !data.dptr ) {
4606 ret = -1;
4607 goto done;
4610 ret = tdb_store(wcache->tdb, key, data, TDB_REPLACE);
4612 done:
4613 SAFE_FREE( data.dptr );
4614 SAFE_FREE( key.dptr );
4616 return ( ret == 0 );
4619 /*********************************************************************
4620 ********************************************************************/
4622 bool wcache_tdc_fetch_list( struct winbindd_tdc_domain **domains, size_t *num_domains )
4624 TDB_DATA key = make_tdc_key( lp_workgroup() );
4625 TDB_DATA data = { NULL, 0 };
4627 *domains = NULL;
4628 *num_domains = 0;
4630 if ( !key.dptr )
4631 return false;
4633 data = tdb_fetch( wcache->tdb, key );
4635 SAFE_FREE( key.dptr );
4637 if ( !data.dptr )
4638 return false;
4640 *num_domains = unpack_tdc_domains( data.dptr, data.dsize, domains );
4642 SAFE_FREE( data.dptr );
4644 if ( !*domains )
4645 return false;
4647 return true;
4650 /*********************************************************************
4651 ********************************************************************/
4653 bool wcache_tdc_add_domain( struct winbindd_domain *domain )
4655 struct winbindd_tdc_domain *dom_list = NULL;
4656 size_t num_domains = 0;
4657 bool ret = false;
4658 struct dom_sid_buf buf;
4660 DBG_DEBUG("wcache_tdc_add_domain: Adding domain %s (%s), SID %s, "
4661 "flags = 0x%x, attributes = 0x%x, type = 0x%x\n",
4662 domain->name, domain->alt_name,
4663 dom_sid_str_buf(&domain->sid, &buf),
4664 domain->domain_flags,
4665 domain->domain_trust_attribs,
4666 domain->domain_type);
4668 if ( !init_wcache() ) {
4669 return false;
4672 /* fetch the list */
4674 wcache_tdc_fetch_list( &dom_list, &num_domains );
4676 /* add the new domain */
4678 if ( !add_wbdomain_to_tdc_array( domain, &dom_list, &num_domains ) ) {
4679 goto done;
4682 /* pack the domain */
4684 if ( !wcache_tdc_store_list( dom_list, num_domains ) ) {
4685 goto done;
4688 /* Success */
4690 ret = true;
4691 done:
4692 TALLOC_FREE( dom_list );
4694 return ret;
4697 static struct winbindd_tdc_domain *wcache_tdc_dup_domain(
4698 TALLOC_CTX *mem_ctx, const struct winbindd_tdc_domain *src)
4700 struct winbindd_tdc_domain *dst;
4702 dst = talloc(mem_ctx, struct winbindd_tdc_domain);
4703 if (dst == NULL) {
4704 goto fail;
4706 dst->domain_name = talloc_strdup(dst, src->domain_name);
4707 if (dst->domain_name == NULL) {
4708 goto fail;
4711 dst->dns_name = NULL;
4712 if (src->dns_name != NULL) {
4713 dst->dns_name = talloc_strdup(dst, src->dns_name);
4714 if (dst->dns_name == NULL) {
4715 goto fail;
4719 sid_copy(&dst->sid, &src->sid);
4720 dst->trust_flags = src->trust_flags;
4721 dst->trust_type = src->trust_type;
4722 dst->trust_attribs = src->trust_attribs;
4723 return dst;
4724 fail:
4725 TALLOC_FREE(dst);
4726 return NULL;
4729 /*********************************************************************
4730 ********************************************************************/
4732 struct winbindd_tdc_domain * wcache_tdc_fetch_domain( TALLOC_CTX *ctx, const char *name )
4734 struct winbindd_tdc_domain *dom_list = NULL;
4735 size_t num_domains = 0;
4736 size_t i;
4737 struct winbindd_tdc_domain *d = NULL;
4739 DBG_DEBUG("wcache_tdc_fetch_domain: Searching for domain %s\n", name);
4741 if ( !init_wcache() ) {
4742 return NULL;
4745 /* fetch the list */
4747 wcache_tdc_fetch_list( &dom_list, &num_domains );
4749 for ( i=0; i<num_domains; i++ ) {
4750 if ( strequal(name, dom_list[i].domain_name) ||
4751 strequal(name, dom_list[i].dns_name) )
4753 DBG_DEBUG("wcache_tdc_fetch_domain: Found domain %s\n",
4754 name);
4756 d = wcache_tdc_dup_domain(ctx, &dom_list[i]);
4757 break;
4761 TALLOC_FREE( dom_list );
4763 return d;
4766 /*********************************************************************
4767 ********************************************************************/
4769 void wcache_tdc_clear( void )
4771 if ( !init_wcache() )
4772 return;
4774 wcache_tdc_store_list( NULL, 0 );
4776 return;
4779 static bool wcache_ndr_key(TALLOC_CTX *mem_ctx, const char *domain_name,
4780 uint32_t opnum, const DATA_BLOB *req,
4781 TDB_DATA *pkey)
4783 char *key;
4784 size_t keylen;
4786 key = talloc_asprintf(mem_ctx, "NDR/%s/%d/", domain_name, (int)opnum);
4787 if (key == NULL) {
4788 return false;
4790 keylen = talloc_get_size(key) - 1;
4792 key = talloc_realloc(mem_ctx, key, char, keylen + req->length);
4793 if (key == NULL) {
4794 return false;
4796 memcpy(key + keylen, req->data, req->length);
4798 pkey->dptr = (uint8_t *)key;
4799 pkey->dsize = talloc_get_size(key);
4800 return true;
4803 static bool wcache_opnum_cacheable(uint32_t opnum)
4805 switch (opnum) {
4806 case NDR_WBINT_LOOKUPSID:
4807 case NDR_WBINT_LOOKUPSIDS:
4808 case NDR_WBINT_LOOKUPNAME:
4809 case NDR_WBINT_SIDS2UNIXIDS:
4810 case NDR_WBINT_UNIXIDS2SIDS:
4811 case NDR_WBINT_GETNSSINFO:
4812 case NDR_WBINT_LOOKUPUSERALIASES:
4813 case NDR_WBINT_LOOKUPUSERGROUPS:
4814 case NDR_WBINT_LOOKUPGROUPMEMBERS:
4815 case NDR_WBINT_QUERYGROUPLIST:
4816 case NDR_WBINT_QUERYUSERRIDLIST:
4817 case NDR_WBINT_DSGETDCNAME:
4818 case NDR_WBINT_LOOKUPRIDS:
4819 return true;
4821 return false;
4824 bool wcache_fetch_ndr(TALLOC_CTX *mem_ctx, struct winbindd_domain *domain,
4825 uint32_t opnum, const DATA_BLOB *req, DATA_BLOB *resp)
4827 TDB_DATA key, data;
4828 bool ret = false;
4830 if (!wcache_opnum_cacheable(opnum) ||
4831 is_my_own_sam_domain(domain) ||
4832 is_builtin_domain(domain)) {
4833 return false;
4836 if (wcache->tdb == NULL) {
4837 return false;
4840 if (!wcache_ndr_key(talloc_tos(), domain->name, opnum, req, &key)) {
4841 return false;
4843 data = tdb_fetch(wcache->tdb, key);
4844 TALLOC_FREE(key.dptr);
4846 if (data.dptr == NULL) {
4847 return false;
4849 if (data.dsize < 12) {
4850 goto fail;
4853 if (is_domain_online(domain)) {
4854 uint32_t entry_seqnum, dom_seqnum, last_check;
4855 uint64_t entry_timeout;
4857 if (!wcache_fetch_seqnum(domain->name, &dom_seqnum,
4858 &last_check)) {
4859 goto fail;
4861 entry_seqnum = IVAL(data.dptr, 0);
4862 if (entry_seqnum != dom_seqnum) {
4863 DBG_DEBUG("Entry has wrong sequence number: %d\n",
4864 (int)entry_seqnum);
4865 goto fail;
4867 entry_timeout = BVAL(data.dptr, 4);
4868 if (time(NULL) > (time_t)entry_timeout) {
4869 DBG_DEBUG("Entry has timed out\n");
4870 goto fail;
4874 resp->data = (uint8_t *)talloc_memdup(mem_ctx, data.dptr + 12,
4875 data.dsize - 12);
4876 if (resp->data == NULL) {
4877 DBG_DEBUG("talloc failed\n");
4878 goto fail;
4880 resp->length = data.dsize - 12;
4882 ret = true;
4883 fail:
4884 SAFE_FREE(data.dptr);
4885 return ret;
4888 void wcache_store_ndr(struct winbindd_domain *domain, uint32_t opnum,
4889 const DATA_BLOB *req, const DATA_BLOB *resp)
4891 TDB_DATA key, data;
4892 uint32_t dom_seqnum, last_check;
4893 uint64_t timeout;
4895 if (!wcache_opnum_cacheable(opnum) ||
4896 is_my_own_sam_domain(domain) ||
4897 is_builtin_domain(domain)) {
4898 return;
4901 if (wcache->tdb == NULL) {
4902 return;
4905 if (!wcache_fetch_seqnum(domain->name, &dom_seqnum, &last_check)) {
4906 DBG_DEBUG("could not fetch seqnum for domain %s\n",
4907 domain->name);
4908 return;
4911 if (!wcache_ndr_key(talloc_tos(), domain->name, opnum, req, &key)) {
4912 return;
4915 timeout = time(NULL) + lp_winbind_cache_time();
4917 data.dsize = resp->length + 12;
4918 data.dptr = talloc_array(key.dptr, uint8_t, data.dsize);
4919 if (data.dptr == NULL) {
4920 goto done;
4923 SIVAL(data.dptr, 0, dom_seqnum);
4924 SBVAL(data.dptr, 4, timeout);
4925 memcpy(data.dptr + 12, resp->data, resp->length);
4927 tdb_store(wcache->tdb, key, data, TDB_REPLACE);
4929 done:
4930 TALLOC_FREE(key.dptr);
4931 return;