tests/krb5: Fix method for creating invalid length zeroed checksum
[Samba.git] / source3 / winbindd / winbindd_cache.c
blob63368fd18217939433c11c3726cf7592cb5a2ece
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"
40 #include "lib/crypto/gnutls_helpers.h"
41 #include <gnutls/crypto.h>
43 #undef DBGC_CLASS
44 #define DBGC_CLASS DBGC_WINBIND
46 #define WINBINDD_CACHE_VER1 1 /* initial db version */
47 #define WINBINDD_CACHE_VER2 2 /* second version with timeouts for NDR entries */
49 #define WINBINDD_CACHE_VERSION WINBINDD_CACHE_VER2
50 #define WINBINDD_CACHE_VERSION_KEYSTR "WINBINDD_CACHE_VERSION"
52 extern struct winbindd_methods reconnect_methods;
53 #ifdef HAVE_ADS
54 extern struct winbindd_methods reconnect_ads_methods;
55 #endif
56 extern struct winbindd_methods builtin_passdb_methods;
57 extern struct winbindd_methods sam_passdb_methods;
59 static void wcache_flush_cache(void);
62 * JRA. KEEP THIS LIST UP TO DATE IF YOU ADD CACHE ENTRIES.
63 * Here are the list of entry types that are *not* stored
64 * as form struct cache_entry in the cache.
67 static const char *non_centry_keys[] = {
68 "SEQNUM/",
69 "WINBINDD_OFFLINE",
70 WINBINDD_CACHE_VERSION_KEYSTR,
71 NULL
74 /************************************************************************
75 Is this key a non-centry type ?
76 ************************************************************************/
78 static bool is_non_centry_key(TDB_DATA kbuf)
80 int i;
82 if (kbuf.dptr == NULL || kbuf.dsize == 0) {
83 return false;
85 for (i = 0; non_centry_keys[i] != NULL; i++) {
86 size_t namelen = strlen(non_centry_keys[i]);
87 if (kbuf.dsize < namelen) {
88 continue;
90 if (strncmp(non_centry_keys[i], (const char *)kbuf.dptr, namelen) == 0) {
91 return true;
94 return false;
97 /* Global online/offline state - False when online. winbindd starts up online
98 and sets this to true if the first query fails and there's an entry in
99 the cache tdb telling us to stay offline. */
101 static bool global_winbindd_offline_state;
103 struct winbind_cache {
104 TDB_CONTEXT *tdb;
107 struct cache_entry {
108 NTSTATUS status;
109 uint32_t sequence_number;
110 uint64_t timeout;
111 uint8_t *data;
112 uint32_t len, ofs;
115 void (*smb_panic_fn)(const char *const why) = smb_panic;
117 static struct winbind_cache *wcache;
119 static char *wcache_path(void)
122 * Data needs to be kept persistent in state directory for
123 * running with "winbindd offline logon".
125 return state_path(talloc_tos(), "winbindd_cache.tdb");
128 static void winbindd_domain_init_backend(struct winbindd_domain *domain)
130 if (domain->backend != NULL) {
131 return;
134 if (domain->internal) {
135 domain->backend = &builtin_passdb_methods;
138 if (dom_sid_equal(&domain->sid, &global_sid_Builtin)) {
139 domain->initialized = true;
142 if (strequal(domain->name, get_global_sam_name()) &&
143 sid_check_is_our_sam(&domain->sid))
145 domain->backend = &sam_passdb_methods;
148 if (!domain->initialized) {
149 /* We do not need a connection to an RW DC for cache operation */
150 init_dc_connection(domain, false);
153 #ifdef HAVE_ADS
154 if (domain->backend == NULL) {
155 struct winbindd_domain *our_domain = domain;
157 /* find our domain first so we can figure out if we
158 are joined to a kerberized domain */
160 if (!domain->primary) {
161 our_domain = find_our_domain();
164 if ((our_domain->active_directory || IS_DC)
165 && domain->active_directory
166 && !lp_winbind_rpc_only())
168 DBG_INFO("Setting ADS methods for domain %s\n",
169 domain->name);
170 domain->backend = &reconnect_ads_methods;
173 #endif /* HAVE_ADS */
175 if (domain->backend == NULL) {
176 DBG_INFO("Setting MS-RPC methods for domain %s\n", domain->name);
177 domain->backend = &reconnect_methods;
181 /* get the winbind_cache structure */
182 static struct winbind_cache *get_cache(struct winbindd_domain *domain)
184 struct winbind_cache *ret = wcache;
186 winbindd_domain_init_backend(domain);
188 if (ret != NULL) {
189 return ret;
192 ret = SMB_XMALLOC_P(struct winbind_cache);
193 ZERO_STRUCTP(ret);
195 wcache = ret;
196 wcache_flush_cache();
198 return ret;
202 free a centry structure
204 static void centry_free(struct cache_entry *centry)
206 if (!centry)
207 return;
208 SAFE_FREE(centry->data);
209 free(centry);
212 static bool centry_check_bytes(struct cache_entry *centry, size_t nbytes)
214 if (centry->len - centry->ofs < nbytes) {
215 DEBUG(0,("centry corruption? needed %u bytes, have %d\n",
216 (unsigned int)nbytes,
217 centry->len - centry->ofs));
218 return false;
220 return true;
224 pull a uint64_t from a cache entry
226 static uint64_t centry_uint64_t(struct cache_entry *centry)
228 uint64_t ret;
230 if (!centry_check_bytes(centry, 8)) {
231 smb_panic_fn("centry_uint64_t");
233 ret = BVAL(centry->data, centry->ofs);
234 centry->ofs += 8;
235 return ret;
239 pull a uint32_t from a cache entry
241 static uint32_t centry_uint32(struct cache_entry *centry)
243 uint32_t ret;
245 if (!centry_check_bytes(centry, 4)) {
246 smb_panic_fn("centry_uint32");
248 ret = IVAL(centry->data, centry->ofs);
249 centry->ofs += 4;
250 return ret;
254 pull a uint16_t from a cache entry
256 static uint16_t centry_uint16(struct cache_entry *centry)
258 uint16_t ret;
259 if (!centry_check_bytes(centry, 2)) {
260 smb_panic_fn("centry_uint16");
262 ret = SVAL(centry->data, centry->ofs);
263 centry->ofs += 2;
264 return ret;
268 pull a uint8_t from a cache entry
270 static uint8_t centry_uint8(struct cache_entry *centry)
272 uint8_t ret;
273 if (!centry_check_bytes(centry, 1)) {
274 smb_panic_fn("centry_uint8");
276 ret = CVAL(centry->data, centry->ofs);
277 centry->ofs += 1;
278 return ret;
282 pull a NTTIME from a cache entry
284 static NTTIME centry_nttime(struct cache_entry *centry)
286 NTTIME ret;
287 if (!centry_check_bytes(centry, 8)) {
288 smb_panic_fn("centry_nttime");
290 ret = IVAL(centry->data, centry->ofs);
291 centry->ofs += 4;
292 ret += (uint64_t)IVAL(centry->data, centry->ofs) << 32;
293 centry->ofs += 4;
294 return ret;
298 pull a time_t from a cache entry. time_t stored portably as a 64-bit time.
300 static time_t centry_time(struct cache_entry *centry)
302 return (time_t)centry_nttime(centry);
305 /* pull a string from a cache entry, using the supplied
306 talloc context
308 static char *centry_string(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
310 uint32_t len;
311 char *ret;
313 len = centry_uint8(centry);
315 if (len == 0xFF) {
316 /* a deliberate NULL string */
317 return NULL;
320 if (!centry_check_bytes(centry, (size_t)len)) {
321 smb_panic_fn("centry_string");
324 ret = talloc_array(mem_ctx, char, len+1);
325 if (!ret) {
326 smb_panic_fn("centry_string out of memory\n");
328 memcpy(ret,centry->data + centry->ofs, len);
329 ret[len] = 0;
330 centry->ofs += len;
331 return ret;
334 /* pull a hash16 from a cache entry, using the supplied
335 talloc context
337 static char *centry_hash16(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
339 uint32_t len;
340 char *ret;
342 len = centry_uint8(centry);
344 if (len != 16) {
345 DEBUG(0,("centry corruption? hash len (%u) != 16\n",
346 len ));
347 return NULL;
350 if (!centry_check_bytes(centry, 16)) {
351 return NULL;
354 ret = talloc_array(mem_ctx, char, 16);
355 if (!ret) {
356 smb_panic_fn("centry_hash out of memory\n");
358 memcpy(ret,centry->data + centry->ofs, 16);
359 centry->ofs += 16;
360 return ret;
363 /* pull a sid from a cache entry, using the supplied
364 talloc context
366 static bool centry_sid(struct cache_entry *centry, struct dom_sid *sid)
368 char *sid_string;
369 bool ret;
371 sid_string = centry_string(centry, talloc_tos());
372 if (sid_string == NULL) {
373 return false;
375 ret = string_to_sid(sid, sid_string);
376 TALLOC_FREE(sid_string);
377 return ret;
382 pull a NTSTATUS from a cache entry
384 static NTSTATUS centry_ntstatus(struct cache_entry *centry)
386 NTSTATUS status;
388 status = NT_STATUS(centry_uint32(centry));
389 return status;
393 /* the server is considered down if it can't give us a sequence number */
394 static bool wcache_server_down(struct winbindd_domain *domain)
396 bool ret;
398 if (!wcache->tdb)
399 return false;
401 ret = (domain->sequence_number == DOM_SEQUENCE_NONE);
403 if (ret)
404 DEBUG(10,("wcache_server_down: server for Domain %s down\n",
405 domain->name ));
406 return ret;
409 struct wcache_seqnum_state {
410 uint32_t *seqnum;
411 uint32_t *last_seq_check;
414 static int wcache_seqnum_parser(TDB_DATA key, TDB_DATA data,
415 void *private_data)
417 struct wcache_seqnum_state *state = private_data;
419 if (data.dsize != 8) {
420 DEBUG(10, ("wcache_fetch_seqnum: invalid data size %d\n",
421 (int)data.dsize));
422 return -1;
425 *state->seqnum = IVAL(data.dptr, 0);
426 *state->last_seq_check = IVAL(data.dptr, 4);
427 return 0;
430 static bool wcache_fetch_seqnum(const char *domain_name, uint32_t *seqnum,
431 uint32_t *last_seq_check)
433 struct wcache_seqnum_state state = {
434 .seqnum = seqnum, .last_seq_check = last_seq_check
436 size_t len = strlen(domain_name);
437 char keystr[len+8];
438 TDB_DATA key = { .dptr = (uint8_t *)keystr, .dsize = sizeof(keystr) };
439 int ret;
441 if (wcache->tdb == NULL) {
442 DEBUG(10,("wcache_fetch_seqnum: tdb == NULL\n"));
443 return false;
446 snprintf(keystr, sizeof(keystr), "SEQNUM/%s", domain_name);
448 ret = tdb_parse_record(wcache->tdb, key, wcache_seqnum_parser,
449 &state);
450 return (ret == 0);
453 static NTSTATUS fetch_cache_seqnum( struct winbindd_domain *domain, time_t now )
455 uint32_t last_check, time_diff;
457 if (!wcache_fetch_seqnum(domain->name, &domain->sequence_number,
458 &last_check)) {
459 return NT_STATUS_UNSUCCESSFUL;
461 domain->last_seq_check = last_check;
463 /* have we expired? */
465 time_diff = now - domain->last_seq_check;
466 if ((int)time_diff > lp_winbind_cache_time()) {
467 DEBUG(10,("fetch_cache_seqnum: timeout [%s][%u @ %u]\n",
468 domain->name, domain->sequence_number,
469 (uint32_t)domain->last_seq_check));
470 return NT_STATUS_UNSUCCESSFUL;
473 DEBUG(10,("fetch_cache_seqnum: success [%s][%u @ %u]\n",
474 domain->name, domain->sequence_number,
475 (uint32_t)domain->last_seq_check));
477 return NT_STATUS_OK;
480 bool wcache_store_seqnum(const char *domain_name, uint32_t seqnum,
481 time_t last_seq_check)
483 size_t len = strlen(domain_name);
484 char keystr[len+8];
485 TDB_DATA key = { .dptr = (uint8_t *)keystr, .dsize = sizeof(keystr) };
486 uint8_t buf[8];
487 int ret;
489 if (wcache->tdb == NULL) {
490 DEBUG(10, ("wcache_store_seqnum: wcache->tdb == NULL\n"));
491 return false;
494 snprintf(keystr, sizeof(keystr), "SEQNUM/%s", domain_name);
496 SIVAL(buf, 0, seqnum);
497 SIVAL(buf, 4, last_seq_check);
499 ret = tdb_store(wcache->tdb, key, make_tdb_data(buf, sizeof(buf)),
500 TDB_REPLACE);
501 if (ret != 0) {
502 DEBUG(10, ("tdb_store_bystring failed: %s\n",
503 tdb_errorstr(wcache->tdb)));
504 return false;
507 DEBUG(10, ("wcache_store_seqnum: success [%s][%u @ %u]\n",
508 domain_name, seqnum, (unsigned)last_seq_check));
510 return true;
513 static bool store_cache_seqnum( struct winbindd_domain *domain )
515 return wcache_store_seqnum(domain->name, domain->sequence_number,
516 domain->last_seq_check);
520 refresh the domain sequence number on timeout.
523 static void refresh_sequence_number(struct winbindd_domain *domain)
525 NTSTATUS status;
526 unsigned time_diff;
527 time_t t = time(NULL);
528 unsigned cache_time = lp_winbind_cache_time();
530 if (is_domain_offline(domain)) {
531 return;
534 get_cache( domain );
536 #if 0 /* JERRY -- disable as the default cache time is now 5 minutes */
537 /* trying to reconnect is expensive, don't do it too often */
538 if (domain->sequence_number == DOM_SEQUENCE_NONE) {
539 cache_time *= 8;
541 #endif
543 time_diff = t - domain->last_seq_check;
545 /* see if we have to refetch the domain sequence number */
546 if ((time_diff < cache_time) &&
547 (domain->sequence_number != DOM_SEQUENCE_NONE) &&
548 NT_STATUS_IS_OK(domain->last_status)) {
549 DEBUG(10, ("refresh_sequence_number: %s time ok\n", domain->name));
550 goto done;
553 /* try to get the sequence number from the tdb cache first */
554 /* this will update the timestamp as well */
556 status = fetch_cache_seqnum( domain, t );
557 if (NT_STATUS_IS_OK(status) &&
558 (domain->sequence_number != DOM_SEQUENCE_NONE) &&
559 NT_STATUS_IS_OK(domain->last_status)) {
560 goto done;
563 /* important! make sure that we know if this is a native
564 mode domain or not. And that we can contact it. */
566 if ( winbindd_can_contact_domain( domain ) ) {
567 status = domain->backend->sequence_number(domain,
568 &domain->sequence_number);
569 } else {
570 /* just use the current time */
571 status = NT_STATUS_OK;
572 domain->sequence_number = time(NULL);
576 /* the above call could have set our domain->backend to NULL when
577 * coming from offline to online mode, make sure to reinitialize the
578 * backend - Guenther */
579 get_cache( domain );
581 if (!NT_STATUS_IS_OK(status)) {
582 DEBUG(10,("refresh_sequence_number: failed with %s\n", nt_errstr(status)));
583 domain->sequence_number = DOM_SEQUENCE_NONE;
586 domain->last_status = status;
587 domain->last_seq_check = time(NULL);
589 /* save the new sequence number in the cache */
590 store_cache_seqnum( domain );
592 done:
593 DEBUG(10, ("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() && global_winbindd_offline_state) {
606 DEBUG(10,("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 DEBUG(10,("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 DEBUG(10,("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 DEBUG(10,("centry_expired: Key %s for domain %s is good.\n",
635 keystr, domain->name ));
636 return false;
639 DEBUG(10,("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 DEBUG(10,("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 DEBUG(10,("wcache_fetch: entry %s expired for domain %s\n",
740 kstr, domain->name ));
742 centry_free(centry);
743 free(kstr);
744 return NULL;
747 DEBUG(10,("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 DEBUG(0,("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 DEBUG(10,("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 DEBUG(10,("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 DEBUG(10,("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 DEBUG(10,("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 DEBUG(10,("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 DEBUG(10,("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 DEBUG(8,("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 DEBUG(5,("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 DEBUG(10,("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 DEBUG(8,("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 DEBUG(10,("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 DEBUG(5,("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 DEBUG(10,("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 * neet 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 DEBUG(0,("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 DEBUG(10,("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 DEBUG(10,("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 DEBUG(10,("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 /* Return status value returned by seq number check */
1492 if (!NT_STATUS_IS_OK(domain->last_status))
1493 return domain->last_status;
1495 /* Put the query_user_list() in a retry loop. There appears to be
1496 * some bug either with Windows 2000 or Samba's handling of large
1497 * rpc replies. This manifests itself as sudden disconnection
1498 * at a random point in the enumeration of a large (60k) user list.
1499 * The retry loop simply tries the operation again. )-: It's not
1500 * pretty but an acceptable workaround until we work out what the
1501 * real problem is. */
1503 retry = 0;
1504 do {
1506 DEBUG(10,("query_user_list: [Cached] - doing backend query for list for domain %s\n",
1507 domain->name ));
1509 rids = NULL;
1510 status = domain->backend->query_user_list(domain, mem_ctx,
1511 &rids);
1512 num_rids = talloc_array_length(rids);
1514 if (!NT_STATUS_IS_OK(status)) {
1515 DEBUG(3, ("query_user_list: returned 0x%08x, "
1516 "retrying\n", NT_STATUS_V(status)));
1518 if (NT_STATUS_EQUAL(status, NT_STATUS_UNSUCCESSFUL)) {
1519 DEBUG(3, ("query_user_list: flushing "
1520 "connection cache\n"));
1521 invalidate_cm_connection(domain);
1523 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1524 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1525 if (!domain->internal && old_status) {
1526 set_domain_offline(domain);
1528 /* store partial response. */
1529 if (num_rids > 0) {
1531 * humm, what about the status used for cache?
1532 * Should it be NT_STATUS_OK?
1534 break;
1537 * domain is offline now, and there is no user entries,
1538 * try to fetch from cache again.
1540 if (cache->tdb && !domain->online && !domain->internal && old_status) {
1541 centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
1542 /* partial response... */
1543 if (!centry) {
1544 goto skip_save;
1545 } else {
1546 goto do_fetch_cache;
1548 } else {
1549 goto skip_save;
1553 } while (NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_UNSUCCESSFUL) &&
1554 (retry++ < 5));
1556 /* and save it */
1557 refresh_sequence_number(domain);
1558 if (!NT_STATUS_IS_OK(status)) {
1559 return status;
1561 centry = centry_start(domain, status);
1562 if (!centry)
1563 goto skip_save;
1564 centry_put_uint32(centry, num_rids);
1565 for (i=0; i<num_rids; i++) {
1566 centry_put_uint32(centry, rids[i]);
1568 centry_end(centry, "UL/%s", domain->name);
1569 centry_free(centry);
1571 *prids = rids;
1573 skip_save:
1574 return status;
1577 /* list all domain groups */
1578 NTSTATUS wb_cache_enum_dom_groups(struct winbindd_domain *domain,
1579 TALLOC_CTX *mem_ctx,
1580 uint32_t *num_entries,
1581 struct wb_acct_info **info)
1583 struct winbind_cache *cache = get_cache(domain);
1584 struct cache_entry *centry = NULL;
1585 NTSTATUS status;
1586 unsigned int i;
1587 bool old_status;
1589 old_status = domain->online;
1590 if (!cache->tdb)
1591 goto do_query;
1593 centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
1594 if (!centry)
1595 goto do_query;
1597 do_fetch_cache:
1598 *num_entries = centry_uint32(centry);
1600 if (*num_entries == 0)
1601 goto do_cached;
1603 (*info) = talloc_array(mem_ctx, struct wb_acct_info, *num_entries);
1604 if (! (*info)) {
1605 smb_panic_fn("enum_dom_groups out of memory");
1607 for (i=0; i<(*num_entries); i++) {
1608 (*info)[i].acct_name = centry_string(centry, (*info));
1609 (*info)[i].acct_desc = centry_string(centry, (*info));
1610 (*info)[i].rid = centry_uint32(centry);
1613 do_cached:
1614 status = centry->status;
1616 DEBUG(10,("enum_dom_groups: [Cached] - cached list for domain %s status: %s\n",
1617 domain->name, nt_errstr(status) ));
1619 centry_free(centry);
1620 return status;
1622 do_query:
1623 *num_entries = 0;
1624 *info = NULL;
1626 /* Return status value returned by seq number check */
1628 if (!NT_STATUS_IS_OK(domain->last_status))
1629 return domain->last_status;
1631 DEBUG(10,("enum_dom_groups: [Cached] - doing backend query for list for domain %s\n",
1632 domain->name ));
1634 status = domain->backend->enum_dom_groups(domain, mem_ctx, num_entries, info);
1636 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1637 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1638 if (!domain->internal && old_status) {
1639 set_domain_offline(domain);
1641 if (cache->tdb &&
1642 !domain->online &&
1643 !domain->internal &&
1644 old_status) {
1645 centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
1646 if (centry) {
1647 goto do_fetch_cache;
1651 /* and save it */
1652 refresh_sequence_number(domain);
1653 if (!NT_STATUS_IS_OK(status)) {
1654 return status;
1656 centry = centry_start(domain, status);
1657 if (!centry)
1658 goto skip_save;
1659 centry_put_uint32(centry, *num_entries);
1660 for (i=0; i<(*num_entries); i++) {
1661 centry_put_string(centry, (*info)[i].acct_name);
1662 centry_put_string(centry, (*info)[i].acct_desc);
1663 centry_put_uint32(centry, (*info)[i].rid);
1665 centry_end(centry, "GL/%s/domain", domain->name);
1666 centry_free(centry);
1668 skip_save:
1669 return status;
1672 /* list all domain groups */
1673 NTSTATUS wb_cache_enum_local_groups(struct winbindd_domain *domain,
1674 TALLOC_CTX *mem_ctx,
1675 uint32_t *num_entries,
1676 struct wb_acct_info **info)
1678 struct winbind_cache *cache = get_cache(domain);
1679 struct cache_entry *centry = NULL;
1680 NTSTATUS status;
1681 unsigned int i;
1682 bool old_status;
1684 old_status = domain->online;
1685 if (!cache->tdb)
1686 goto do_query;
1688 centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
1689 if (!centry)
1690 goto do_query;
1692 do_fetch_cache:
1693 *num_entries = centry_uint32(centry);
1695 if (*num_entries == 0)
1696 goto do_cached;
1698 (*info) = talloc_array(mem_ctx, struct wb_acct_info, *num_entries);
1699 if (! (*info)) {
1700 smb_panic_fn("enum_dom_groups out of memory");
1702 for (i=0; i<(*num_entries); i++) {
1703 (*info)[i].acct_name = centry_string(centry, (*info));
1704 (*info)[i].acct_desc = centry_string(centry, (*info));
1705 (*info)[i].rid = centry_uint32(centry);
1708 do_cached:
1710 /* If we are returning cached data and the domain controller
1711 is down then we don't know whether the data is up to date
1712 or not. Return NT_STATUS_MORE_PROCESSING_REQUIRED to
1713 indicate this. */
1715 if (wcache_server_down(domain)) {
1716 DEBUG(10, ("enum_local_groups: returning cached user list and server was down\n"));
1717 status = NT_STATUS_MORE_PROCESSING_REQUIRED;
1718 } else
1719 status = centry->status;
1721 DEBUG(10,("enum_local_groups: [Cached] - cached list for domain %s status: %s\n",
1722 domain->name, nt_errstr(status) ));
1724 centry_free(centry);
1725 return status;
1727 do_query:
1728 *num_entries = 0;
1729 *info = NULL;
1731 /* Return status value returned by seq number check */
1733 if (!NT_STATUS_IS_OK(domain->last_status))
1734 return domain->last_status;
1736 DEBUG(10,("enum_local_groups: [Cached] - doing backend query for list for domain %s\n",
1737 domain->name ));
1739 status = domain->backend->enum_local_groups(domain, mem_ctx, num_entries, info);
1741 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1742 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1743 if (!domain->internal && old_status) {
1744 set_domain_offline(domain);
1746 if (cache->tdb &&
1747 !domain->internal &&
1748 !domain->online &&
1749 old_status) {
1750 centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
1751 if (centry) {
1752 goto do_fetch_cache;
1756 /* and save it */
1757 refresh_sequence_number(domain);
1758 if (!NT_STATUS_IS_OK(status)) {
1759 return status;
1761 centry = centry_start(domain, status);
1762 if (!centry)
1763 goto skip_save;
1764 centry_put_uint32(centry, *num_entries);
1765 for (i=0; i<(*num_entries); i++) {
1766 centry_put_string(centry, (*info)[i].acct_name);
1767 centry_put_string(centry, (*info)[i].acct_desc);
1768 centry_put_uint32(centry, (*info)[i].rid);
1770 centry_end(centry, "GL/%s/local", domain->name);
1771 centry_free(centry);
1773 skip_save:
1774 return status;
1777 struct wcache_name_to_sid_state {
1778 struct dom_sid *sid;
1779 enum lsa_SidType *type;
1780 bool offline;
1781 bool found;
1784 static void wcache_name_to_sid_fn(const struct dom_sid *sid,
1785 enum lsa_SidType type,
1786 bool expired,
1787 void *private_data)
1789 struct wcache_name_to_sid_state *state = private_data;
1791 *state->sid = *sid;
1792 *state->type = type;
1793 state->found = (!expired || state->offline);
1796 static NTSTATUS wcache_name_to_sid(struct winbindd_domain *domain,
1797 const char *domain_name,
1798 const char *name,
1799 struct dom_sid *sid,
1800 enum lsa_SidType *type)
1802 struct wcache_name_to_sid_state state = {
1803 .sid = sid, .type = type, .found = false,
1804 .offline = is_domain_offline(domain),
1806 bool ok;
1808 ok = namemap_cache_find_name(domain_name, name, wcache_name_to_sid_fn,
1809 &state);
1810 if (!ok) {
1811 DBG_DEBUG("namemap_cache_find_name failed\n");
1812 return NT_STATUS_NOT_FOUND;
1814 if (!state.found) {
1815 DBG_DEBUG("cache entry not found\n");
1816 return NT_STATUS_NOT_FOUND;
1818 if (*type == SID_NAME_UNKNOWN) {
1819 return NT_STATUS_NONE_MAPPED;
1822 return NT_STATUS_OK;
1825 /* convert a single name to a sid in a domain */
1826 NTSTATUS wb_cache_name_to_sid(struct winbindd_domain *domain,
1827 TALLOC_CTX *mem_ctx,
1828 const char *domain_name,
1829 const char *name,
1830 uint32_t flags,
1831 struct dom_sid *sid,
1832 enum lsa_SidType *type)
1834 NTSTATUS status;
1835 bool old_status;
1836 const char *dom_name;
1838 old_status = domain->online;
1840 status = wcache_name_to_sid(domain, domain_name, name, sid, type);
1841 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
1842 return status;
1845 ZERO_STRUCTP(sid);
1847 /* If the seq number check indicated that there is a problem
1848 * with this DC, then return that status... except for
1849 * access_denied. This is special because the dc may be in
1850 * "restrict anonymous = 1" mode, in which case it will deny
1851 * most unauthenticated operations, but *will* allow the LSA
1852 * name-to-sid that we try as a fallback. */
1854 if (!(NT_STATUS_IS_OK(domain->last_status)
1855 || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1856 return domain->last_status;
1858 DEBUG(10,("name_to_sid: [Cached] - doing backend query for name for domain %s\n",
1859 domain->name ));
1861 winbindd_domain_init_backend(domain);
1862 status = domain->backend->name_to_sid(domain, mem_ctx, domain_name,
1863 name, flags, &dom_name, sid, type);
1865 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1866 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1867 if (!domain->internal && old_status) {
1868 set_domain_offline(domain);
1870 if (!domain->internal &&
1871 !domain->online &&
1872 old_status) {
1873 NTSTATUS cache_status;
1874 cache_status = wcache_name_to_sid(domain, domain_name, name, sid, type);
1875 return cache_status;
1878 /* and save it */
1880 if (domain->online &&
1881 (NT_STATUS_IS_OK(status) || NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED))) {
1882 enum lsa_SidType save_type = *type;
1884 if (NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) {
1885 save_type = SID_NAME_UNKNOWN;
1888 wcache_save_name_to_sid(domain, status, domain_name, name, sid,
1889 save_type);
1891 /* Only save the reverse mapping if this was not a UPN */
1892 if (!strchr(name, '@')) {
1893 if (!strupper_m(discard_const_p(char, domain_name))) {
1894 return NT_STATUS_INVALID_PARAMETER;
1896 (void)strlower_m(discard_const_p(char, name));
1897 wcache_save_sid_to_name(domain, status, sid,
1898 dom_name, name, save_type);
1902 return status;
1905 struct wcache_sid_to_name_state {
1906 TALLOC_CTX *mem_ctx;
1907 char **domain_name;
1908 char **name;
1909 enum lsa_SidType *type;
1910 bool offline;
1911 bool found;
1914 static void wcache_sid_to_name_fn(const char *domain,
1915 const char *name,
1916 enum lsa_SidType type,
1917 bool expired,
1918 void *private_data)
1920 struct wcache_sid_to_name_state *state = private_data;
1922 *state->domain_name = talloc_strdup(state->mem_ctx, domain);
1923 if (*state->domain_name == NULL) {
1924 return;
1926 *state->name = talloc_strdup(state->mem_ctx, name);
1927 if (*state->name == NULL) {
1928 return;
1930 *state->type = type;
1931 state->found = (!expired || state->offline);
1934 static NTSTATUS wcache_sid_to_name(struct winbindd_domain *domain,
1935 const struct dom_sid *sid,
1936 TALLOC_CTX *mem_ctx,
1937 char **domain_name,
1938 char **name,
1939 enum lsa_SidType *type)
1941 struct wcache_sid_to_name_state state = {
1942 .mem_ctx = mem_ctx, .found = false,
1943 .domain_name = domain_name, .name = name, .type = type,
1944 .offline = is_domain_offline(domain)
1946 bool ok;
1948 ok = namemap_cache_find_sid(sid, wcache_sid_to_name_fn, &state);
1949 if (!ok) {
1950 DBG_DEBUG("namemap_cache_find_name failed\n");
1951 return NT_STATUS_NOT_FOUND;
1953 if (!state.found) {
1954 DBG_DEBUG("cache entry not found\n");
1955 return NT_STATUS_NOT_FOUND;
1957 if (*type == SID_NAME_UNKNOWN) {
1958 return NT_STATUS_NONE_MAPPED;
1961 return NT_STATUS_OK;
1964 /* convert a sid to a user or group name. The sid is guaranteed to be in the domain
1965 given */
1966 NTSTATUS wb_cache_sid_to_name(struct winbindd_domain *domain,
1967 TALLOC_CTX *mem_ctx,
1968 const struct dom_sid *sid,
1969 char **domain_name,
1970 char **name,
1971 enum lsa_SidType *type)
1973 NTSTATUS status;
1974 bool old_status;
1976 old_status = domain->online;
1977 status = wcache_sid_to_name(domain, sid, mem_ctx, domain_name, name,
1978 type);
1979 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
1980 return status;
1983 *name = NULL;
1984 *domain_name = NULL;
1986 /* If the seq number check indicated that there is a problem
1987 * with this DC, then return that status... except for
1988 * access_denied. This is special because the dc may be in
1989 * "restrict anonymous = 1" mode, in which case it will deny
1990 * most unauthenticated operations, but *will* allow the LSA
1991 * sid-to-name that we try as a fallback. */
1993 if (!(NT_STATUS_IS_OK(domain->last_status)
1994 || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1995 return domain->last_status;
1997 DEBUG(10,("sid_to_name: [Cached] - doing backend query for name for domain %s\n",
1998 domain->name ));
2000 winbindd_domain_init_backend(domain);
2002 status = domain->backend->sid_to_name(domain, mem_ctx, sid, domain_name, name, type);
2004 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2005 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2006 if (!domain->internal && old_status) {
2007 set_domain_offline(domain);
2009 if (!domain->internal &&
2010 !domain->online &&
2011 old_status) {
2012 NTSTATUS cache_status;
2013 cache_status = wcache_sid_to_name(domain, sid, mem_ctx,
2014 domain_name, name, type);
2015 return cache_status;
2018 /* and save it */
2019 if (!NT_STATUS_IS_OK(status)) {
2020 return status;
2022 wcache_save_sid_to_name(domain, status, sid, *domain_name, *name, *type);
2024 /* We can't save the name to sid mapping here, as with sid history a
2025 * later name2sid would give the wrong sid. */
2027 return status;
2030 NTSTATUS wb_cache_rids_to_names(struct winbindd_domain *domain,
2031 TALLOC_CTX *mem_ctx,
2032 const struct dom_sid *domain_sid,
2033 uint32_t *rids,
2034 size_t num_rids,
2035 char **domain_name,
2036 char ***names,
2037 enum lsa_SidType **types)
2039 struct winbind_cache *cache = get_cache(domain);
2040 size_t i;
2041 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
2042 bool have_mapped;
2043 bool have_unmapped;
2044 bool old_status;
2046 old_status = domain->online;
2047 *domain_name = NULL;
2048 *names = NULL;
2049 *types = NULL;
2051 if (!cache->tdb) {
2052 goto do_query;
2055 if (num_rids == 0) {
2056 return NT_STATUS_OK;
2059 *names = talloc_array(mem_ctx, char *, num_rids);
2060 *types = talloc_array(mem_ctx, enum lsa_SidType, num_rids);
2062 if ((*names == NULL) || (*types == NULL)) {
2063 result = NT_STATUS_NO_MEMORY;
2064 goto error;
2067 have_mapped = have_unmapped = false;
2069 for (i=0; i<num_rids; i++) {
2070 struct dom_sid sid;
2071 NTSTATUS status;
2072 enum lsa_SidType type;
2073 char *dom, *name;
2075 if (!sid_compose(&sid, domain_sid, rids[i])) {
2076 result = NT_STATUS_INTERNAL_ERROR;
2077 goto error;
2080 status = wcache_sid_to_name(domain, &sid, *names, &dom,
2081 &name, &type);
2083 (*types)[i] = SID_NAME_UNKNOWN;
2084 (*names)[i] = talloc_strdup(*names, "");
2086 if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2087 /* not cached */
2088 goto do_query;
2091 if (NT_STATUS_IS_OK(status)) {
2092 have_mapped = true;
2093 (*types)[i] = type;
2095 if (*domain_name == NULL) {
2096 *domain_name = dom;
2097 } else {
2098 TALLOC_FREE(dom);
2101 (*names)[i] = name;
2103 } else if (NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) {
2104 have_unmapped = true;
2105 } else {
2106 /* something's definitely wrong */
2107 result = status;
2108 goto error;
2112 if (!have_mapped) {
2113 return NT_STATUS_NONE_MAPPED;
2115 if (!have_unmapped) {
2116 return NT_STATUS_OK;
2118 return STATUS_SOME_UNMAPPED;
2120 do_query:
2122 TALLOC_FREE(*names);
2123 TALLOC_FREE(*types);
2125 result = domain->backend->rids_to_names(domain, mem_ctx, domain_sid,
2126 rids, num_rids, domain_name,
2127 names, types);
2129 if (NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
2130 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2131 if (!domain->internal && old_status) {
2132 set_domain_offline(domain);
2134 if (cache->tdb &&
2135 !domain->internal &&
2136 !domain->online &&
2137 old_status) {
2138 have_mapped = have_unmapped = false;
2140 *names = talloc_array(mem_ctx, char *, num_rids);
2141 if (*names == NULL) {
2142 result = NT_STATUS_NO_MEMORY;
2143 goto error;
2146 *types = talloc_array(mem_ctx, enum lsa_SidType,
2147 num_rids);
2148 if (*types == NULL) {
2149 result = NT_STATUS_NO_MEMORY;
2150 goto error;
2153 for (i=0; i<num_rids; i++) {
2154 struct dom_sid sid;
2155 NTSTATUS status;
2156 enum lsa_SidType type;
2157 char *dom, *name;
2159 if (!sid_compose(&sid, domain_sid, rids[i])) {
2160 result = NT_STATUS_INTERNAL_ERROR;
2161 goto error;
2164 status = wcache_sid_to_name(domain, &sid,
2165 *names, &dom,
2166 &name, &type);
2168 (*types)[i] = SID_NAME_UNKNOWN;
2169 (*names)[i] = talloc_strdup(*names, "");
2171 if (NT_STATUS_IS_OK(status)) {
2172 have_mapped = true;
2173 (*types)[i] = type;
2175 if (*domain_name == NULL) {
2176 *domain_name = dom;
2177 } else {
2178 TALLOC_FREE(dom);
2181 (*names)[i] = name;
2183 } else if (NT_STATUS_EQUAL(
2184 status,
2185 NT_STATUS_NONE_MAPPED)) {
2186 have_unmapped = true;
2187 } else {
2188 /* something's definitely wrong */
2189 result = status;
2190 goto error;
2194 if (!have_mapped) {
2195 return NT_STATUS_NONE_MAPPED;
2197 if (!have_unmapped) {
2198 return NT_STATUS_OK;
2200 return STATUS_SOME_UNMAPPED;
2204 None of the queried rids has been found so save all negative entries
2206 if (NT_STATUS_EQUAL(result, NT_STATUS_NONE_MAPPED)) {
2207 for (i = 0; i < num_rids; i++) {
2208 struct dom_sid sid;
2209 const char *name = "";
2210 const enum lsa_SidType type = SID_NAME_UNKNOWN;
2211 NTSTATUS status = NT_STATUS_NONE_MAPPED;
2213 if (!sid_compose(&sid, domain_sid, rids[i])) {
2214 return NT_STATUS_INTERNAL_ERROR;
2217 wcache_save_sid_to_name(domain, status, &sid, *domain_name,
2218 name, type);
2221 return result;
2225 Some or all of the queried rids have been found.
2227 if (!NT_STATUS_IS_OK(result) &&
2228 !NT_STATUS_EQUAL(result, STATUS_SOME_UNMAPPED)) {
2229 return result;
2232 refresh_sequence_number(domain);
2234 for (i=0; i<num_rids; i++) {
2235 struct dom_sid sid;
2236 NTSTATUS status;
2238 if (!sid_compose(&sid, domain_sid, rids[i])) {
2239 result = NT_STATUS_INTERNAL_ERROR;
2240 goto error;
2243 status = (*types)[i] == SID_NAME_UNKNOWN ?
2244 NT_STATUS_NONE_MAPPED : NT_STATUS_OK;
2246 wcache_save_sid_to_name(domain, status, &sid, *domain_name,
2247 (*names)[i], (*types)[i]);
2250 return result;
2252 error:
2253 TALLOC_FREE(*names);
2254 TALLOC_FREE(*types);
2255 return result;
2258 static NTSTATUS wcache_query_user(struct winbindd_domain *domain,
2259 TALLOC_CTX *mem_ctx,
2260 const struct dom_sid *user_sid,
2261 struct wbint_userinfo *info)
2263 struct winbind_cache *cache = get_cache(domain);
2264 struct cache_entry *centry = NULL;
2265 NTSTATUS status;
2266 struct dom_sid_buf sid_string;
2268 if (cache->tdb == NULL) {
2269 return NT_STATUS_NOT_FOUND;
2272 centry = wcache_fetch(
2273 cache, domain, "U/%s", dom_sid_str_buf(user_sid, &sid_string));
2274 if (centry == NULL) {
2275 return NT_STATUS_NOT_FOUND;
2279 * If we have an access denied cache entry and a cached info3
2280 * in the samlogon cache then do a query. This will force the
2281 * rpc back end to return the info3 data.
2284 if (NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED) &&
2285 netsamlogon_cache_have(user_sid)) {
2286 DEBUG(10, ("query_user: cached access denied and have cached "
2287 "info3\n"));
2288 domain->last_status = NT_STATUS_OK;
2289 centry_free(centry);
2290 return NT_STATUS_NOT_FOUND;
2293 /* if status is not ok then this is a negative hit
2294 and the rest of the data doesn't matter */
2295 status = centry->status;
2296 if (NT_STATUS_IS_OK(status)) {
2297 info->domain_name = centry_string(centry, mem_ctx);
2298 info->acct_name = centry_string(centry, mem_ctx);
2299 info->full_name = centry_string(centry, mem_ctx);
2300 info->homedir = centry_string(centry, mem_ctx);
2301 info->shell = centry_string(centry, mem_ctx);
2302 info->uid = centry_uint32(centry);
2303 info->primary_gid = centry_uint32(centry);
2304 info->primary_group_name = centry_string(centry, mem_ctx);
2305 centry_sid(centry, &info->user_sid);
2306 centry_sid(centry, &info->group_sid);
2309 DEBUG(10,("query_user: [Cached] - cached info for domain %s status: "
2310 "%s\n", domain->name, nt_errstr(status) ));
2312 centry_free(centry);
2313 return status;
2318 * @brief Query a fullname from the username cache (for further gecos processing)
2320 * @param domain A pointer to the winbindd_domain struct.
2321 * @param mem_ctx The talloc context.
2322 * @param user_sid The user sid.
2323 * @param full_name A pointer to the full_name string.
2325 * @return NTSTATUS code
2327 NTSTATUS wcache_query_user_fullname(struct winbindd_domain *domain,
2328 TALLOC_CTX *mem_ctx,
2329 const struct dom_sid *user_sid,
2330 const char **full_name)
2332 NTSTATUS status;
2333 struct wbint_userinfo info;
2335 status = wcache_query_user(domain, mem_ctx, user_sid, &info);
2336 if (!NT_STATUS_IS_OK(status)) {
2337 return status;
2340 if (info.full_name != NULL) {
2341 *full_name = talloc_strdup(mem_ctx, info.full_name);
2342 if (*full_name == NULL) {
2343 return NT_STATUS_NO_MEMORY;
2347 return NT_STATUS_OK;
2350 static NTSTATUS wcache_lookup_usergroups(struct winbindd_domain *domain,
2351 TALLOC_CTX *mem_ctx,
2352 const struct dom_sid *user_sid,
2353 uint32_t *pnum_sids,
2354 struct dom_sid **psids)
2356 struct winbind_cache *cache = get_cache(domain);
2357 struct cache_entry *centry = NULL;
2358 NTSTATUS status;
2359 uint32_t i, num_sids;
2360 struct dom_sid *sids;
2361 struct dom_sid_buf sid_string;
2363 if (cache->tdb == NULL) {
2364 return NT_STATUS_NOT_FOUND;
2367 centry = wcache_fetch(
2368 cache,
2369 domain,
2370 "UG/%s",
2371 dom_sid_str_buf(user_sid, &sid_string));
2372 if (centry == NULL) {
2373 return NT_STATUS_NOT_FOUND;
2376 /* If we have an access denied cache entry and a cached info3 in the
2377 samlogon cache then do a query. This will force the rpc back end
2378 to return the info3 data. */
2380 if (NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)
2381 && netsamlogon_cache_have(user_sid)) {
2382 DEBUG(10, ("lookup_usergroups: cached access denied and have "
2383 "cached info3\n"));
2384 domain->last_status = NT_STATUS_OK;
2385 centry_free(centry);
2386 return NT_STATUS_NOT_FOUND;
2389 num_sids = centry_uint32(centry);
2390 sids = talloc_array(mem_ctx, struct dom_sid, num_sids);
2391 if (sids == NULL) {
2392 centry_free(centry);
2393 return NT_STATUS_NO_MEMORY;
2396 for (i=0; i<num_sids; i++) {
2397 centry_sid(centry, &sids[i]);
2400 status = centry->status;
2402 DEBUG(10,("lookup_usergroups: [Cached] - cached info for domain %s "
2403 "status: %s\n", domain->name, nt_errstr(status)));
2405 centry_free(centry);
2407 *pnum_sids = num_sids;
2408 *psids = sids;
2409 return status;
2412 /* Lookup groups a user is a member of. */
2413 NTSTATUS wb_cache_lookup_usergroups(struct winbindd_domain *domain,
2414 TALLOC_CTX *mem_ctx,
2415 const struct dom_sid *user_sid,
2416 uint32_t *num_groups,
2417 struct dom_sid **user_gids)
2419 struct cache_entry *centry = NULL;
2420 NTSTATUS status;
2421 unsigned int i;
2422 struct dom_sid_buf sid_string;
2423 bool old_status;
2425 old_status = domain->online;
2426 status = wcache_lookup_usergroups(domain, mem_ctx, user_sid,
2427 num_groups, user_gids);
2428 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2429 return status;
2432 (*num_groups) = 0;
2433 (*user_gids) = NULL;
2435 /* Return status value returned by seq number check */
2437 if (!NT_STATUS_IS_OK(domain->last_status))
2438 return domain->last_status;
2440 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info for domain %s\n",
2441 domain->name ));
2443 status = domain->backend->lookup_usergroups(domain, mem_ctx, user_sid, num_groups, user_gids);
2445 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2446 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2447 if (!domain->internal && old_status) {
2448 set_domain_offline(domain);
2450 if (!domain->internal &&
2451 !domain->online &&
2452 old_status) {
2453 NTSTATUS cache_status;
2454 cache_status = wcache_lookup_usergroups(domain, mem_ctx, user_sid,
2455 num_groups, user_gids);
2456 return cache_status;
2459 if ( NT_STATUS_EQUAL(status, NT_STATUS_SYNCHRONIZATION_REQUIRED) )
2460 goto skip_save;
2462 /* and save it */
2463 refresh_sequence_number(domain);
2464 if (!NT_STATUS_IS_OK(status)) {
2465 return status;
2467 centry = centry_start(domain, status);
2468 if (!centry)
2469 goto skip_save;
2471 centry_put_uint32(centry, *num_groups);
2472 for (i=0; i<(*num_groups); i++) {
2473 centry_put_sid(centry, &(*user_gids)[i]);
2476 centry_end(centry, "UG/%s", dom_sid_str_buf(user_sid, &sid_string));
2477 centry_free(centry);
2479 skip_save:
2480 return status;
2483 static char *wcache_make_sidlist(TALLOC_CTX *mem_ctx, uint32_t num_sids,
2484 const struct dom_sid *sids)
2486 uint32_t i;
2487 char *sidlist;
2489 sidlist = talloc_strdup(mem_ctx, "");
2490 if (sidlist == NULL) {
2491 return NULL;
2493 for (i=0; i<num_sids; i++) {
2494 struct dom_sid_buf tmp;
2495 sidlist = talloc_asprintf_append_buffer(
2496 sidlist,
2497 "/%s",
2498 dom_sid_str_buf(&sids[i], &tmp));
2499 if (sidlist == NULL) {
2500 return NULL;
2503 return sidlist;
2506 static NTSTATUS wcache_lookup_useraliases(struct winbindd_domain *domain,
2507 TALLOC_CTX *mem_ctx,
2508 uint32_t num_sids,
2509 const struct dom_sid *sids,
2510 uint32_t *pnum_aliases,
2511 uint32_t **paliases)
2513 struct winbind_cache *cache = get_cache(domain);
2514 struct cache_entry *centry = NULL;
2515 uint32_t i, num_aliases;
2516 uint32_t *aliases;
2517 NTSTATUS status;
2518 char *sidlist;
2520 if (cache->tdb == NULL) {
2521 return NT_STATUS_NOT_FOUND;
2524 if (num_sids == 0) {
2525 *pnum_aliases = 0;
2526 *paliases = NULL;
2527 return NT_STATUS_OK;
2530 /* We need to cache indexed by the whole list of SIDs, the aliases
2531 * resulting might come from any of the SIDs. */
2533 sidlist = wcache_make_sidlist(talloc_tos(), num_sids, sids);
2534 if (sidlist == NULL) {
2535 return NT_STATUS_NO_MEMORY;
2538 centry = wcache_fetch(cache, domain, "UA%s", sidlist);
2539 TALLOC_FREE(sidlist);
2540 if (centry == NULL) {
2541 return NT_STATUS_NOT_FOUND;
2544 num_aliases = centry_uint32(centry);
2545 aliases = talloc_array(mem_ctx, uint32_t, num_aliases);
2546 if (aliases == NULL) {
2547 centry_free(centry);
2548 return NT_STATUS_NO_MEMORY;
2551 for (i=0; i<num_aliases; i++) {
2552 aliases[i] = centry_uint32(centry);
2555 status = centry->status;
2557 DEBUG(10,("lookup_useraliases: [Cached] - cached info for domain: %s "
2558 "status %s\n", domain->name, nt_errstr(status)));
2560 centry_free(centry);
2562 *pnum_aliases = num_aliases;
2563 *paliases = aliases;
2565 return status;
2568 NTSTATUS wb_cache_lookup_useraliases(struct winbindd_domain *domain,
2569 TALLOC_CTX *mem_ctx,
2570 uint32_t num_sids,
2571 const struct dom_sid *sids,
2572 uint32_t *num_aliases,
2573 uint32_t **alias_rids)
2575 struct cache_entry *centry = NULL;
2576 NTSTATUS status;
2577 char *sidlist;
2578 uint32_t i;
2579 bool old_status;
2581 old_status = domain->online;
2582 status = wcache_lookup_useraliases(domain, mem_ctx, num_sids, sids,
2583 num_aliases, alias_rids);
2584 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2585 return status;
2588 (*num_aliases) = 0;
2589 (*alias_rids) = NULL;
2591 if (!NT_STATUS_IS_OK(domain->last_status))
2592 return domain->last_status;
2594 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info "
2595 "for domain %s\n", domain->name ));
2597 sidlist = wcache_make_sidlist(talloc_tos(), num_sids, sids);
2598 if (sidlist == NULL) {
2599 return NT_STATUS_NO_MEMORY;
2602 status = domain->backend->lookup_useraliases(domain, mem_ctx,
2603 num_sids, sids,
2604 num_aliases, alias_rids);
2606 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2607 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2608 if (!domain->internal && old_status) {
2609 set_domain_offline(domain);
2611 if (!domain->internal &&
2612 !domain->online &&
2613 old_status) {
2614 NTSTATUS cache_status;
2615 cache_status = wcache_lookup_useraliases(domain, mem_ctx, num_sids,
2616 sids, num_aliases, alias_rids);
2617 return cache_status;
2620 /* and save it */
2621 refresh_sequence_number(domain);
2622 if (!NT_STATUS_IS_OK(status)) {
2623 return status;
2625 centry = centry_start(domain, status);
2626 if (!centry)
2627 goto skip_save;
2628 centry_put_uint32(centry, *num_aliases);
2629 for (i=0; i<(*num_aliases); i++)
2630 centry_put_uint32(centry, (*alias_rids)[i]);
2631 centry_end(centry, "UA%s", sidlist);
2632 centry_free(centry);
2634 skip_save:
2635 return status;
2638 static NTSTATUS wcache_lookup_groupmem(struct winbindd_domain *domain,
2639 TALLOC_CTX *mem_ctx,
2640 const struct dom_sid *group_sid,
2641 uint32_t *num_names,
2642 struct dom_sid **sid_mem, char ***names,
2643 uint32_t **name_types)
2645 struct winbind_cache *cache = get_cache(domain);
2646 struct cache_entry *centry = NULL;
2647 NTSTATUS status;
2648 unsigned int i;
2649 struct dom_sid_buf sid_string;
2651 if (cache->tdb == NULL) {
2652 return NT_STATUS_NOT_FOUND;
2655 centry = wcache_fetch(
2656 cache,
2657 domain,
2658 "GM/%s",
2659 dom_sid_str_buf(group_sid, &sid_string));
2660 if (centry == NULL) {
2661 return NT_STATUS_NOT_FOUND;
2664 *sid_mem = NULL;
2665 *names = NULL;
2666 *name_types = NULL;
2668 *num_names = centry_uint32(centry);
2669 if (*num_names == 0) {
2670 centry_free(centry);
2671 return NT_STATUS_OK;
2674 *sid_mem = talloc_array(mem_ctx, struct dom_sid, *num_names);
2675 *names = talloc_array(mem_ctx, char *, *num_names);
2676 *name_types = talloc_array(mem_ctx, uint32_t, *num_names);
2678 if ((*sid_mem == NULL) || (*names == NULL) || (*name_types == NULL)) {
2679 TALLOC_FREE(*sid_mem);
2680 TALLOC_FREE(*names);
2681 TALLOC_FREE(*name_types);
2682 centry_free(centry);
2683 return NT_STATUS_NO_MEMORY;
2686 for (i=0; i<(*num_names); i++) {
2687 centry_sid(centry, &(*sid_mem)[i]);
2688 (*names)[i] = centry_string(centry, mem_ctx);
2689 (*name_types)[i] = centry_uint32(centry);
2692 status = centry->status;
2694 DEBUG(10,("lookup_groupmem: [Cached] - cached info for domain %s "
2695 "status: %s\n", domain->name, nt_errstr(status)));
2697 centry_free(centry);
2698 return status;
2701 NTSTATUS wb_cache_lookup_groupmem(struct winbindd_domain *domain,
2702 TALLOC_CTX *mem_ctx,
2703 const struct dom_sid *group_sid,
2704 enum lsa_SidType type,
2705 uint32_t *num_names,
2706 struct dom_sid **sid_mem,
2707 char ***names,
2708 uint32_t **name_types)
2710 struct cache_entry *centry = NULL;
2711 NTSTATUS status;
2712 unsigned int i;
2713 struct dom_sid_buf sid_string;
2714 bool old_status;
2716 old_status = domain->online;
2717 status = wcache_lookup_groupmem(domain, mem_ctx, group_sid, num_names,
2718 sid_mem, names, name_types);
2719 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2720 return status;
2723 (*num_names) = 0;
2724 (*sid_mem) = NULL;
2725 (*names) = NULL;
2726 (*name_types) = NULL;
2728 /* Return status value returned by seq number check */
2730 if (!NT_STATUS_IS_OK(domain->last_status))
2731 return domain->last_status;
2733 DEBUG(10,("lookup_groupmem: [Cached] - doing backend query for info for domain %s\n",
2734 domain->name ));
2736 status = domain->backend->lookup_groupmem(domain, mem_ctx, group_sid,
2737 type, num_names,
2738 sid_mem, names, name_types);
2740 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2741 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2742 if (!domain->internal && old_status) {
2743 set_domain_offline(domain);
2745 if (!domain->internal &&
2746 !domain->online &&
2747 old_status) {
2748 NTSTATUS cache_status;
2749 cache_status = wcache_lookup_groupmem(domain, mem_ctx, group_sid,
2750 num_names, sid_mem, names,
2751 name_types);
2752 return cache_status;
2755 /* and save it */
2756 refresh_sequence_number(domain);
2757 if (!NT_STATUS_IS_OK(status)) {
2758 return status;
2760 centry = centry_start(domain, status);
2761 if (!centry)
2762 goto skip_save;
2763 centry_put_uint32(centry, *num_names);
2764 for (i=0; i<(*num_names); i++) {
2765 centry_put_sid(centry, &(*sid_mem)[i]);
2766 centry_put_string(centry, (*names)[i]);
2767 centry_put_uint32(centry, (*name_types)[i]);
2769 centry_end(centry,
2770 "GM/%s",
2771 dom_sid_str_buf(group_sid, &sid_string));
2772 centry_free(centry);
2774 skip_save:
2775 return status;
2778 /* find the sequence number for a domain */
2779 NTSTATUS wb_cache_sequence_number(struct winbindd_domain *domain,
2780 uint32_t *seq)
2782 refresh_sequence_number(domain);
2784 *seq = domain->sequence_number;
2786 return NT_STATUS_OK;
2789 /* enumerate trusted domains
2790 * (we need to have the list of trustdoms in the cache when we go offline) -
2791 * Guenther */
2792 NTSTATUS wb_cache_trusted_domains(struct winbindd_domain *domain,
2793 TALLOC_CTX *mem_ctx,
2794 struct netr_DomainTrustList *trusts)
2796 NTSTATUS status;
2797 struct winbind_cache *cache;
2798 struct winbindd_tdc_domain *dom_list = NULL;
2799 size_t num_domains = 0;
2800 bool retval = false;
2801 size_t i;
2802 bool old_status;
2804 old_status = domain->online;
2805 trusts->count = 0;
2806 trusts->array = NULL;
2808 cache = get_cache(domain);
2809 if (!cache || !cache->tdb) {
2810 goto do_query;
2813 if (domain->online) {
2814 goto do_query;
2817 retval = wcache_tdc_fetch_list(&dom_list, &num_domains);
2818 if (!retval || !num_domains || !dom_list) {
2819 TALLOC_FREE(dom_list);
2820 goto do_query;
2823 do_fetch_cache:
2824 trusts->array = talloc_zero_array(mem_ctx, struct netr_DomainTrust, num_domains);
2825 if (!trusts->array) {
2826 TALLOC_FREE(dom_list);
2827 return NT_STATUS_NO_MEMORY;
2830 for (i = 0; i < num_domains; i++) {
2831 struct netr_DomainTrust *trust;
2832 struct dom_sid *sid;
2833 struct winbindd_domain *dom;
2835 dom = find_domain_from_name_noinit(dom_list[i].domain_name);
2836 if (dom && dom->internal) {
2837 continue;
2840 trust = &trusts->array[trusts->count];
2841 trust->netbios_name = talloc_strdup(trusts->array, dom_list[i].domain_name);
2842 trust->dns_name = talloc_strdup(trusts->array, dom_list[i].dns_name);
2843 sid = talloc(trusts->array, struct dom_sid);
2844 if (!trust->netbios_name || !trust->dns_name ||
2845 !sid) {
2846 TALLOC_FREE(dom_list);
2847 TALLOC_FREE(trusts->array);
2848 return NT_STATUS_NO_MEMORY;
2851 trust->trust_flags = dom_list[i].trust_flags;
2852 trust->trust_attributes = dom_list[i].trust_attribs;
2853 trust->trust_type = dom_list[i].trust_type;
2854 sid_copy(sid, &dom_list[i].sid);
2855 trust->sid = sid;
2856 trusts->count++;
2859 TALLOC_FREE(dom_list);
2860 return NT_STATUS_OK;
2862 do_query:
2863 /* Return status value returned by seq number check */
2865 if (!NT_STATUS_IS_OK(domain->last_status))
2866 return domain->last_status;
2868 DEBUG(10,("trusted_domains: [Cached] - doing backend query for info for domain %s\n",
2869 domain->name ));
2871 status = domain->backend->trusted_domains(domain, mem_ctx, trusts);
2873 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2874 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2875 if (!domain->internal && old_status) {
2876 set_domain_offline(domain);
2878 if (!domain->internal &&
2879 !domain->online &&
2880 old_status) {
2881 retval = wcache_tdc_fetch_list(&dom_list, &num_domains);
2882 if (retval && num_domains && dom_list) {
2883 TALLOC_FREE(trusts->array);
2884 trusts->count = 0;
2885 goto do_fetch_cache;
2889 /* no trusts gives NT_STATUS_NO_MORE_ENTRIES resetting to NT_STATUS_OK
2890 * so that the generic centry handling still applies correctly -
2891 * Guenther*/
2893 if (!NT_STATUS_IS_ERR(status)) {
2894 status = NT_STATUS_OK;
2896 return status;
2899 /* get lockout policy */
2900 NTSTATUS wb_cache_lockout_policy(struct winbindd_domain *domain,
2901 TALLOC_CTX *mem_ctx,
2902 struct samr_DomInfo12 *policy)
2904 struct winbind_cache *cache = get_cache(domain);
2905 struct cache_entry *centry = NULL;
2906 NTSTATUS status;
2907 bool old_status;
2909 old_status = domain->online;
2910 if (!cache->tdb)
2911 goto do_query;
2913 centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
2915 if (!centry)
2916 goto do_query;
2918 do_fetch_cache:
2919 policy->lockout_duration = centry_nttime(centry);
2920 policy->lockout_window = centry_nttime(centry);
2921 policy->lockout_threshold = centry_uint16(centry);
2923 status = centry->status;
2925 DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2926 domain->name, nt_errstr(status) ));
2928 centry_free(centry);
2929 return status;
2931 do_query:
2932 ZERO_STRUCTP(policy);
2934 /* Return status value returned by seq number check */
2936 if (!NT_STATUS_IS_OK(domain->last_status))
2937 return domain->last_status;
2939 DEBUG(10,("lockout_policy: [Cached] - doing backend query for info for domain %s\n",
2940 domain->name ));
2942 status = domain->backend->lockout_policy(domain, mem_ctx, policy);
2944 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2945 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2946 if (!domain->internal && old_status) {
2947 set_domain_offline(domain);
2949 if (cache->tdb &&
2950 !domain->internal &&
2951 !domain->online &&
2952 old_status) {
2953 centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
2954 if (centry) {
2955 goto do_fetch_cache;
2959 /* and save it */
2960 refresh_sequence_number(domain);
2961 if (!NT_STATUS_IS_OK(status)) {
2962 return status;
2964 wcache_save_lockout_policy(domain, status, policy);
2966 return status;
2969 /* get password policy */
2970 NTSTATUS wb_cache_password_policy(struct winbindd_domain *domain,
2971 TALLOC_CTX *mem_ctx,
2972 struct samr_DomInfo1 *policy)
2974 struct winbind_cache *cache = get_cache(domain);
2975 struct cache_entry *centry = NULL;
2976 NTSTATUS status;
2977 bool old_status;
2979 old_status = domain->online;
2980 if (!cache->tdb)
2981 goto do_query;
2983 centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
2985 if (!centry)
2986 goto do_query;
2988 do_fetch_cache:
2989 policy->min_password_length = centry_uint16(centry);
2990 policy->password_history_length = centry_uint16(centry);
2991 policy->password_properties = centry_uint32(centry);
2992 policy->max_password_age = centry_nttime(centry);
2993 policy->min_password_age = centry_nttime(centry);
2995 status = centry->status;
2997 DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2998 domain->name, nt_errstr(status) ));
3000 centry_free(centry);
3001 return status;
3003 do_query:
3004 ZERO_STRUCTP(policy);
3006 /* Return status value returned by seq number check */
3008 if (!NT_STATUS_IS_OK(domain->last_status))
3009 return domain->last_status;
3011 DEBUG(10,("password_policy: [Cached] - doing backend query for info for domain %s\n",
3012 domain->name ));
3014 status = domain->backend->password_policy(domain, mem_ctx, policy);
3016 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
3017 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
3018 if (!domain->internal && old_status) {
3019 set_domain_offline(domain);
3021 if (cache->tdb &&
3022 !domain->internal &&
3023 !domain->online &&
3024 old_status) {
3025 centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
3026 if (centry) {
3027 goto do_fetch_cache;
3031 /* and save it */
3032 refresh_sequence_number(domain);
3033 if (!NT_STATUS_IS_OK(status)) {
3034 return status;
3036 wcache_save_password_policy(domain, status, policy);
3038 return status;
3042 /* Invalidate cached user and group lists coherently */
3044 static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
3045 void *state)
3047 if (strncmp((const char *)kbuf.dptr, "UL/", 3) == 0 ||
3048 strncmp((const char *)kbuf.dptr, "GL/", 3) == 0)
3049 tdb_delete(the_tdb, kbuf);
3051 return 0;
3054 /* Invalidate the getpwnam and getgroups entries for a winbindd domain */
3056 void wcache_invalidate_samlogon(struct winbindd_domain *domain,
3057 const struct dom_sid *sid)
3059 fstring key_str;
3060 struct dom_sid_buf sid_string;
3061 struct winbind_cache *cache;
3063 /* don't clear cached U/SID and UG/SID entries when we want to logon
3064 * offline - gd */
3066 if (lp_winbind_offline_logon()) {
3067 return;
3070 if (!domain)
3071 return;
3073 cache = get_cache(domain);
3075 if (!cache->tdb) {
3076 return;
3079 /* Clear U/SID cache entry */
3080 fstr_sprintf(key_str, "U/%s", dom_sid_str_buf(sid, &sid_string));
3081 DEBUG(10, ("wcache_invalidate_samlogon: clearing %s\n", key_str));
3082 tdb_delete(cache->tdb, string_tdb_data(key_str));
3084 /* Clear UG/SID cache entry */
3085 fstr_sprintf(key_str, "UG/%s", dom_sid_str_buf(sid, &sid_string));
3086 DEBUG(10, ("wcache_invalidate_samlogon: clearing %s\n", key_str));
3087 tdb_delete(cache->tdb, string_tdb_data(key_str));
3089 /* Samba/winbindd never needs this. */
3090 netsamlogon_clear_cached_user(sid);
3093 bool wcache_invalidate_cache(void)
3095 struct winbindd_domain *domain;
3097 for (domain = domain_list(); domain; domain = domain->next) {
3098 struct winbind_cache *cache = get_cache(domain);
3100 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
3101 "entries for %s\n", domain->name));
3102 if (cache) {
3103 if (cache->tdb) {
3104 tdb_traverse(cache->tdb, traverse_fn, NULL);
3105 } else {
3106 return false;
3110 return true;
3113 bool wcache_invalidate_cache_noinit(void)
3115 struct winbindd_domain *domain;
3117 for (domain = domain_list(); domain; domain = domain->next) {
3118 struct winbind_cache *cache;
3120 /* Skip uninitialized domains. */
3121 if (!domain->initialized && !domain->internal) {
3122 continue;
3125 cache = get_cache(domain);
3127 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
3128 "entries for %s\n", domain->name));
3129 if (cache) {
3130 if (cache->tdb) {
3131 tdb_traverse(cache->tdb, traverse_fn, NULL);
3133 * Flushing cache has nothing to with domains.
3134 * return here if we successfully flushed once.
3135 * To avoid unnecessary traversing the cache.
3137 return true;
3138 } else {
3139 return false;
3143 return true;
3146 static bool init_wcache(void)
3148 char *db_path;
3150 if (wcache == NULL) {
3151 wcache = SMB_XMALLOC_P(struct winbind_cache);
3152 ZERO_STRUCTP(wcache);
3155 if (wcache->tdb != NULL)
3156 return true;
3158 db_path = wcache_path();
3159 if (db_path == NULL) {
3160 return false;
3163 /* when working offline we must not clear the cache on restart */
3164 wcache->tdb = tdb_open_log(db_path,
3165 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
3166 TDB_INCOMPATIBLE_HASH |
3167 (lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST)),
3168 O_RDWR|O_CREAT, 0600);
3169 TALLOC_FREE(db_path);
3170 if (wcache->tdb == NULL) {
3171 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
3172 return false;
3175 return true;
3178 /************************************************************************
3179 This is called by the parent to initialize the cache file.
3180 We don't need sophisticated locking here as we know we're the
3181 only opener.
3182 ************************************************************************/
3184 bool initialize_winbindd_cache(void)
3186 bool cache_bad = true;
3187 uint32_t vers;
3189 if (!init_wcache()) {
3190 DEBUG(0,("initialize_winbindd_cache: init_wcache failed.\n"));
3191 return false;
3194 /* Check version number. */
3195 if (tdb_fetch_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, &vers) &&
3196 vers == WINBINDD_CACHE_VERSION) {
3197 cache_bad = false;
3200 if (cache_bad) {
3201 char *db_path;
3203 DEBUG(0,("initialize_winbindd_cache: clearing cache "
3204 "and re-creating with version number %d\n",
3205 WINBINDD_CACHE_VERSION ));
3207 tdb_close(wcache->tdb);
3208 wcache->tdb = NULL;
3210 db_path = wcache_path();
3211 if (db_path == NULL) {
3212 return false;
3215 if (unlink(db_path) == -1) {
3216 DEBUG(0,("initialize_winbindd_cache: unlink %s failed %s ",
3217 db_path,
3218 strerror(errno) ));
3219 TALLOC_FREE(db_path);
3220 return false;
3222 TALLOC_FREE(db_path);
3223 if (!init_wcache()) {
3224 DEBUG(0,("initialize_winbindd_cache: re-initialization "
3225 "init_wcache failed.\n"));
3226 return false;
3229 /* Write the version. */
3230 if (!tdb_store_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, WINBINDD_CACHE_VERSION)) {
3231 DEBUG(0,("initialize_winbindd_cache: version number store failed %s\n",
3232 tdb_errorstr(wcache->tdb) ));
3233 return false;
3237 tdb_close(wcache->tdb);
3238 wcache->tdb = NULL;
3239 return true;
3242 void close_winbindd_cache(void)
3244 if (!wcache) {
3245 return;
3247 if (wcache->tdb) {
3248 tdb_close(wcache->tdb);
3249 wcache->tdb = NULL;
3253 bool lookup_cached_sid(TALLOC_CTX *mem_ctx, const struct dom_sid *sid,
3254 char **domain_name, char **name,
3255 enum lsa_SidType *type)
3257 struct winbindd_domain *domain;
3258 NTSTATUS status;
3260 domain = find_lookup_domain_from_sid(sid);
3261 if (domain == NULL) {
3262 return false;
3264 status = wcache_sid_to_name(domain, sid, mem_ctx, domain_name, name,
3265 type);
3266 return NT_STATUS_IS_OK(status);
3269 bool lookup_cached_name(const char *namespace,
3270 const char *domain_name,
3271 const char *name,
3272 struct dom_sid *sid,
3273 enum lsa_SidType *type)
3275 struct winbindd_domain *domain;
3276 NTSTATUS status;
3277 bool original_online_state;
3279 domain = find_lookup_domain_from_name(namespace);
3280 if (domain == NULL) {
3281 return false;
3284 /* If we are doing a cached logon, temporarily set the domain
3285 offline so the cache won't expire the entry */
3287 original_online_state = domain->online;
3288 domain->online = false;
3289 status = wcache_name_to_sid(domain, domain_name, name, sid, type);
3290 domain->online = original_online_state;
3292 return NT_STATUS_IS_OK(status);
3296 * Cache a name to sid without checking the sequence number.
3297 * Used when caching from a trusted PAC.
3300 void cache_name2sid_trusted(struct winbindd_domain *domain,
3301 const char *domain_name,
3302 const char *name,
3303 enum lsa_SidType type,
3304 const struct dom_sid *sid)
3307 * Ensure we store the mapping with the
3308 * existing sequence number from the cache.
3310 get_cache(domain);
3311 (void)fetch_cache_seqnum(domain, time(NULL));
3312 wcache_save_name_to_sid(domain,
3313 NT_STATUS_OK,
3314 domain_name,
3315 name,
3316 sid,
3317 type);
3320 void cache_name2sid(struct winbindd_domain *domain,
3321 const char *domain_name, const char *name,
3322 enum lsa_SidType type, const struct dom_sid *sid)
3324 refresh_sequence_number(domain);
3325 wcache_save_name_to_sid(domain, NT_STATUS_OK, domain_name, name,
3326 sid, type);
3330 * The original idea that this cache only contains centries has
3331 * been blurred - now other stuff gets put in here. Ensure we
3332 * ignore these things on cleanup.
3335 static int traverse_fn_cleanup(TDB_CONTEXT *the_tdb, TDB_DATA kbuf,
3336 TDB_DATA dbuf, void *state)
3338 struct cache_entry *centry;
3340 if (is_non_centry_key(kbuf)) {
3341 return 0;
3344 centry = wcache_fetch_raw((char *)kbuf.dptr);
3345 if (!centry) {
3346 return 0;
3349 if (!NT_STATUS_IS_OK(centry->status)) {
3350 DEBUG(10,("deleting centry %s\n", (const char *)kbuf.dptr));
3351 tdb_delete(the_tdb, kbuf);
3354 centry_free(centry);
3355 return 0;
3358 /* flush the cache */
3359 static void wcache_flush_cache(void)
3361 char *db_path;
3363 if (!wcache)
3364 return;
3365 if (wcache->tdb) {
3366 tdb_close(wcache->tdb);
3367 wcache->tdb = NULL;
3369 if (!winbindd_use_cache()) {
3370 return;
3373 db_path = wcache_path();
3374 if (db_path == NULL) {
3375 return;
3378 /* when working offline we must not clear the cache on restart */
3379 wcache->tdb = tdb_open_log(db_path,
3380 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
3381 TDB_INCOMPATIBLE_HASH |
3382 (lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST)),
3383 O_RDWR|O_CREAT, 0600);
3384 TALLOC_FREE(db_path);
3385 if (!wcache->tdb) {
3386 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
3387 return;
3390 tdb_traverse(wcache->tdb, traverse_fn_cleanup, NULL);
3392 DEBUG(10,("wcache_flush_cache success\n"));
3395 /* Count cached creds */
3397 static int traverse_fn_cached_creds(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
3398 void *state)
3400 int *cred_count = (int*)state;
3402 if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
3403 (*cred_count)++;
3405 return 0;
3408 NTSTATUS wcache_count_cached_creds(struct winbindd_domain *domain, int *count)
3410 struct winbind_cache *cache = get_cache(domain);
3412 *count = 0;
3414 if (!cache->tdb) {
3415 return NT_STATUS_INTERNAL_DB_ERROR;
3418 tdb_traverse(cache->tdb, traverse_fn_cached_creds, (void *)count);
3420 return NT_STATUS_OK;
3423 struct cred_list {
3424 struct cred_list *prev, *next;
3425 TDB_DATA key;
3426 fstring name;
3427 time_t created;
3429 static struct cred_list *wcache_cred_list;
3431 static int traverse_fn_get_credlist(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
3432 void *state)
3434 struct cred_list *cred;
3436 if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
3438 cred = SMB_MALLOC_P(struct cred_list);
3439 if (cred == NULL) {
3440 DEBUG(0,("traverse_fn_remove_first_creds: failed to malloc new entry for list\n"));
3441 return -1;
3444 ZERO_STRUCTP(cred);
3446 /* save a copy of the key */
3448 fstrcpy(cred->name, (const char *)kbuf.dptr);
3449 DLIST_ADD(wcache_cred_list, cred);
3452 return 0;
3455 NTSTATUS wcache_remove_oldest_cached_creds(struct winbindd_domain *domain, const struct dom_sid *sid)
3457 struct winbind_cache *cache = get_cache(domain);
3458 NTSTATUS status;
3459 int ret;
3460 struct cred_list *cred, *next, *oldest = NULL;
3462 if (!cache->tdb) {
3463 return NT_STATUS_INTERNAL_DB_ERROR;
3466 /* we possibly already have an entry */
3467 if (sid && NT_STATUS_IS_OK(wcache_cached_creds_exist(domain, sid))) {
3469 fstring key_str;
3470 struct dom_sid_buf tmp;
3472 DEBUG(11,("we already have an entry, deleting that\n"));
3474 fstr_sprintf(key_str, "CRED/%s", dom_sid_str_buf(sid, &tmp));
3476 tdb_delete(cache->tdb, string_tdb_data(key_str));
3478 return NT_STATUS_OK;
3481 ret = tdb_traverse(cache->tdb, traverse_fn_get_credlist, NULL);
3482 if (ret == 0) {
3483 return NT_STATUS_OK;
3484 } else if ((ret < 0) || (wcache_cred_list == NULL)) {
3485 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
3488 ZERO_STRUCTP(oldest);
3490 for (cred = wcache_cred_list; cred; cred = cred->next) {
3492 TDB_DATA data;
3493 time_t t;
3495 data = tdb_fetch(cache->tdb, string_tdb_data(cred->name));
3496 if (!data.dptr) {
3497 DEBUG(10,("wcache_remove_oldest_cached_creds: entry for [%s] not found\n",
3498 cred->name));
3499 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
3500 goto done;
3503 t = IVAL(data.dptr, 0);
3504 SAFE_FREE(data.dptr);
3506 if (!oldest) {
3507 oldest = SMB_MALLOC_P(struct cred_list);
3508 if (oldest == NULL) {
3509 status = NT_STATUS_NO_MEMORY;
3510 goto done;
3513 fstrcpy(oldest->name, cred->name);
3514 oldest->created = t;
3515 continue;
3518 if (t < oldest->created) {
3519 fstrcpy(oldest->name, cred->name);
3520 oldest->created = t;
3524 if (tdb_delete(cache->tdb, string_tdb_data(oldest->name)) == 0) {
3525 status = NT_STATUS_OK;
3526 } else {
3527 status = NT_STATUS_UNSUCCESSFUL;
3529 done:
3530 for (cred = wcache_cred_list; cred; cred = next) {
3531 next = cred->next;
3532 DLIST_REMOVE(wcache_cred_list, cred);
3533 SAFE_FREE(cred);
3535 SAFE_FREE(oldest);
3537 return status;
3540 /* Change the global online/offline state. */
3541 bool set_global_winbindd_state_offline(void)
3543 TDB_DATA data;
3545 DEBUG(10,("set_global_winbindd_state_offline: offline requested.\n"));
3547 /* Only go offline if someone has created
3548 the key "WINBINDD_OFFLINE" in the cache tdb. */
3550 if (wcache == NULL || wcache->tdb == NULL) {
3551 DEBUG(10,("set_global_winbindd_state_offline: wcache not open yet.\n"));
3552 return false;
3555 if (!lp_winbind_offline_logon()) {
3556 DEBUG(10,("set_global_winbindd_state_offline: rejecting.\n"));
3557 return false;
3560 if (global_winbindd_offline_state) {
3561 /* Already offline. */
3562 return true;
3565 data = tdb_fetch_bystring( wcache->tdb, "WINBINDD_OFFLINE" );
3567 if (!data.dptr || data.dsize != 4) {
3568 DEBUG(10,("set_global_winbindd_state_offline: offline state not set.\n"));
3569 SAFE_FREE(data.dptr);
3570 return false;
3571 } else {
3572 DEBUG(10,("set_global_winbindd_state_offline: offline state set.\n"));
3573 global_winbindd_offline_state = true;
3574 SAFE_FREE(data.dptr);
3575 return true;
3579 void set_global_winbindd_state_online(void)
3581 DEBUG(10,("set_global_winbindd_state_online: online requested.\n"));
3583 if (!lp_winbind_offline_logon()) {
3584 DEBUG(10,("set_global_winbindd_state_online: rejecting.\n"));
3585 return;
3588 if (!global_winbindd_offline_state) {
3589 /* Already online. */
3590 return;
3592 global_winbindd_offline_state = false;
3594 if (!wcache->tdb) {
3595 return;
3598 /* Ensure there is no key "WINBINDD_OFFLINE" in the cache tdb. */
3599 tdb_delete_bystring(wcache->tdb, "WINBINDD_OFFLINE");
3602 bool get_global_winbindd_state_offline(void)
3604 return global_winbindd_offline_state;
3607 /***********************************************************************
3608 Validate functions for all possible cache tdb keys.
3609 ***********************************************************************/
3611 static struct cache_entry *create_centry_validate(const char *kstr, TDB_DATA data,
3612 struct tdb_validation_status *state)
3614 struct cache_entry *centry;
3616 centry = SMB_XMALLOC_P(struct cache_entry);
3617 centry->data = (unsigned char *)smb_memdup(data.dptr, data.dsize);
3618 if (!centry->data) {
3619 SAFE_FREE(centry);
3620 return NULL;
3622 centry->len = data.dsize;
3623 centry->ofs = 0;
3625 if (centry->len < 16) {
3626 /* huh? corrupt cache? */
3627 DEBUG(0,("create_centry_validate: Corrupt cache for key %s "
3628 "(len < 16) ?\n", kstr));
3629 centry_free(centry);
3630 state->bad_entry = true;
3631 state->success = false;
3632 return NULL;
3635 centry->status = NT_STATUS(centry_uint32(centry));
3636 centry->sequence_number = centry_uint32(centry);
3637 centry->timeout = centry_uint64_t(centry);
3638 return centry;
3641 static int validate_seqnum(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3642 struct tdb_validation_status *state)
3644 if (dbuf.dsize != 8) {
3645 DEBUG(0,("validate_seqnum: Corrupt cache for key %s (len %u != 8) ?\n",
3646 keystr, (unsigned int)dbuf.dsize ));
3647 state->bad_entry = true;
3648 return 1;
3650 return 0;
3653 static int validate_u(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3654 struct tdb_validation_status *state)
3656 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3657 struct dom_sid sid;
3659 if (!centry) {
3660 return 1;
3663 (void)centry_string(centry, mem_ctx);
3664 (void)centry_string(centry, mem_ctx);
3665 (void)centry_string(centry, mem_ctx);
3666 (void)centry_string(centry, mem_ctx);
3667 (void)centry_string(centry, mem_ctx);
3668 (void)centry_uint32(centry);
3669 (void)centry_uint32(centry);
3670 (void)centry_string(centry, mem_ctx);
3671 (void)centry_sid(centry, &sid);
3672 (void)centry_sid(centry, &sid);
3674 centry_free(centry);
3676 if (!(state->success)) {
3677 return 1;
3679 DEBUG(10,("validate_u: %s ok\n", keystr));
3680 return 0;
3683 static int validate_loc_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3684 struct tdb_validation_status *state)
3686 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3688 if (!centry) {
3689 return 1;
3692 (void)centry_nttime(centry);
3693 (void)centry_nttime(centry);
3694 (void)centry_uint16(centry);
3696 centry_free(centry);
3698 if (!(state->success)) {
3699 return 1;
3701 DEBUG(10,("validate_loc_pol: %s ok\n", keystr));
3702 return 0;
3705 static int validate_pwd_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3706 struct tdb_validation_status *state)
3708 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3710 if (!centry) {
3711 return 1;
3714 (void)centry_uint16(centry);
3715 (void)centry_uint16(centry);
3716 (void)centry_uint32(centry);
3717 (void)centry_nttime(centry);
3718 (void)centry_nttime(centry);
3720 centry_free(centry);
3722 if (!(state->success)) {
3723 return 1;
3725 DEBUG(10,("validate_pwd_pol: %s ok\n", keystr));
3726 return 0;
3729 static int validate_cred(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3730 struct tdb_validation_status *state)
3732 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3734 if (!centry) {
3735 return 1;
3738 (void)centry_time(centry);
3739 (void)centry_hash16(centry, mem_ctx);
3741 /* We only have 17 bytes more data in the salted cred case. */
3742 if (centry->len - centry->ofs == 17) {
3743 (void)centry_hash16(centry, mem_ctx);
3746 centry_free(centry);
3748 if (!(state->success)) {
3749 return 1;
3751 DEBUG(10,("validate_cred: %s ok\n", keystr));
3752 return 0;
3755 static int validate_ul(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3756 struct tdb_validation_status *state)
3758 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3759 int32_t num_entries, i;
3761 if (!centry) {
3762 return 1;
3765 num_entries = (int32_t)centry_uint32(centry);
3767 for (i=0; i< num_entries; i++) {
3768 (void)centry_uint32(centry);
3771 centry_free(centry);
3773 if (!(state->success)) {
3774 return 1;
3776 DEBUG(10,("validate_ul: %s ok\n", keystr));
3777 return 0;
3780 static int validate_gl(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3781 struct tdb_validation_status *state)
3783 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3784 int32_t num_entries, i;
3786 if (!centry) {
3787 return 1;
3790 num_entries = centry_uint32(centry);
3792 for (i=0; i< num_entries; i++) {
3793 (void)centry_string(centry, mem_ctx);
3794 (void)centry_string(centry, mem_ctx);
3795 (void)centry_uint32(centry);
3798 centry_free(centry);
3800 if (!(state->success)) {
3801 return 1;
3803 DEBUG(10,("validate_gl: %s ok\n", keystr));
3804 return 0;
3807 static int validate_ug(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3808 struct tdb_validation_status *state)
3810 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3811 int32_t num_groups, i;
3813 if (!centry) {
3814 return 1;
3817 num_groups = centry_uint32(centry);
3819 for (i=0; i< num_groups; i++) {
3820 struct dom_sid sid;
3821 centry_sid(centry, &sid);
3824 centry_free(centry);
3826 if (!(state->success)) {
3827 return 1;
3829 DEBUG(10,("validate_ug: %s ok\n", keystr));
3830 return 0;
3833 static int validate_ua(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3834 struct tdb_validation_status *state)
3836 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3837 int32_t num_aliases, i;
3839 if (!centry) {
3840 return 1;
3843 num_aliases = centry_uint32(centry);
3845 for (i=0; i < num_aliases; i++) {
3846 (void)centry_uint32(centry);
3849 centry_free(centry);
3851 if (!(state->success)) {
3852 return 1;
3854 DEBUG(10,("validate_ua: %s ok\n", keystr));
3855 return 0;
3858 static int validate_gm(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3859 struct tdb_validation_status *state)
3861 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3862 int32_t num_names, i;
3864 if (!centry) {
3865 return 1;
3868 num_names = centry_uint32(centry);
3870 for (i=0; i< num_names; i++) {
3871 struct dom_sid sid;
3872 centry_sid(centry, &sid);
3873 (void)centry_string(centry, mem_ctx);
3874 (void)centry_uint32(centry);
3877 centry_free(centry);
3879 if (!(state->success)) {
3880 return 1;
3882 DEBUG(10,("validate_gm: %s ok\n", keystr));
3883 return 0;
3886 static int validate_dr(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3887 struct tdb_validation_status *state)
3889 /* Can't say anything about this other than must be nonzero. */
3890 if (dbuf.dsize == 0) {
3891 DEBUG(0,("validate_dr: Corrupt cache for key %s (len == 0) ?\n",
3892 keystr));
3893 state->bad_entry = true;
3894 state->success = false;
3895 return 1;
3898 DEBUG(10,("validate_dr: %s ok\n", keystr));
3899 return 0;
3902 static int validate_de(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3903 struct tdb_validation_status *state)
3905 /* Can't say anything about this other than must be nonzero. */
3906 if (dbuf.dsize == 0) {
3907 DEBUG(0,("validate_de: Corrupt cache for key %s (len == 0) ?\n",
3908 keystr));
3909 state->bad_entry = true;
3910 state->success = false;
3911 return 1;
3914 DEBUG(10,("validate_de: %s ok\n", keystr));
3915 return 0;
3918 static int validate_nss_an(TALLOC_CTX *mem_ctx, const char *keystr,
3919 TDB_DATA dbuf,
3920 struct tdb_validation_status *state)
3922 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3924 if (!centry) {
3925 return 1;
3928 (void)centry_string( centry, mem_ctx );
3930 centry_free(centry);
3932 if (!(state->success)) {
3933 return 1;
3935 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3936 return 0;
3939 static int validate_nss_na(TALLOC_CTX *mem_ctx, const char *keystr,
3940 TDB_DATA dbuf,
3941 struct tdb_validation_status *state)
3943 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3945 if (!centry) {
3946 return 1;
3949 (void)centry_string( centry, mem_ctx );
3951 centry_free(centry);
3953 if (!(state->success)) {
3954 return 1;
3956 DBG_DEBUG("%s ok\n", keystr);
3957 return 0;
3960 static int validate_trustdomcache(TALLOC_CTX *mem_ctx, const char *keystr,
3961 TDB_DATA dbuf,
3962 struct tdb_validation_status *state)
3964 if (dbuf.dsize == 0) {
3965 DEBUG(0, ("validate_trustdomcache: Corrupt cache for "
3966 "key %s (len ==0) ?\n", keystr));
3967 state->bad_entry = true;
3968 state->success = false;
3969 return 1;
3972 DEBUG(10, ("validate_trustdomcache: %s ok\n", keystr));
3973 DEBUGADD(10, (" Don't trust me, I am a DUMMY!\n"));
3974 return 0;
3977 static int validate_offline(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3978 struct tdb_validation_status *state)
3980 if (dbuf.dsize != 4) {
3981 DEBUG(0,("validate_offline: Corrupt cache for key %s (len %u != 4) ?\n",
3982 keystr, (unsigned int)dbuf.dsize ));
3983 state->bad_entry = true;
3984 state->success = false;
3985 return 1;
3987 DEBUG(10,("validate_offline: %s ok\n", keystr));
3988 return 0;
3991 static int validate_ndr(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3992 struct tdb_validation_status *state)
3995 * Ignore validation for now. The proper way to do this is with a
3996 * checksum. Just pure parsing does not really catch much.
3998 return 0;
4001 static int validate_cache_version(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
4002 struct tdb_validation_status *state)
4004 if (dbuf.dsize != 4) {
4005 DEBUG(0, ("validate_cache_version: Corrupt cache for "
4006 "key %s (len %u != 4) ?\n",
4007 keystr, (unsigned int)dbuf.dsize));
4008 state->bad_entry = true;
4009 state->success = false;
4010 return 1;
4013 DEBUG(10, ("validate_cache_version: %s ok\n", keystr));
4014 return 0;
4017 /***********************************************************************
4018 A list of all possible cache tdb keys with associated validation
4019 functions.
4020 ***********************************************************************/
4022 struct key_val_struct {
4023 const char *keyname;
4024 int (*validate_data_fn)(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, struct tdb_validation_status* state);
4025 } key_val[] = {
4026 {"SEQNUM/", validate_seqnum},
4027 {"U/", validate_u},
4028 {"LOC_POL/", validate_loc_pol},
4029 {"PWD_POL/", validate_pwd_pol},
4030 {"CRED/", validate_cred},
4031 {"UL/", validate_ul},
4032 {"GL/", validate_gl},
4033 {"UG/", validate_ug},
4034 {"UA", validate_ua},
4035 {"GM/", validate_gm},
4036 {"DR/", validate_dr},
4037 {"DE/", validate_de},
4038 {"TRUSTDOMCACHE/", validate_trustdomcache},
4039 {"NSS/NA/", validate_nss_na},
4040 {"NSS/AN/", validate_nss_an},
4041 {"WINBINDD_OFFLINE", validate_offline},
4042 {"NDR/", validate_ndr},
4043 {WINBINDD_CACHE_VERSION_KEYSTR, validate_cache_version},
4044 {NULL, NULL}
4047 /***********************************************************************
4048 Function to look at every entry in the tdb and validate it as far as
4049 possible.
4050 ***********************************************************************/
4052 static int cache_traverse_validate_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, void *state)
4054 int i;
4055 unsigned int max_key_len = 1024;
4056 struct tdb_validation_status *v_state = (struct tdb_validation_status *)state;
4058 /* Paranoia check. */
4059 if (strncmp("UA/", (const char *)kbuf.dptr, 3) == 0 ||
4060 strncmp("NDR/", (const char *)kbuf.dptr, 4) == 0) {
4061 max_key_len = 1024 * 1024;
4063 if (kbuf.dsize > max_key_len) {
4064 DEBUG(0, ("cache_traverse_validate_fn: key length too large: "
4065 "(%u) > (%u)\n\n",
4066 (unsigned int)kbuf.dsize, (unsigned int)max_key_len));
4067 return 1;
4070 for (i = 0; key_val[i].keyname; i++) {
4071 size_t namelen = strlen(key_val[i].keyname);
4072 if (kbuf.dsize >= namelen && (
4073 strncmp(key_val[i].keyname, (const char *)kbuf.dptr, namelen)) == 0) {
4074 TALLOC_CTX *mem_ctx;
4075 char *keystr;
4076 int ret;
4078 keystr = SMB_MALLOC_ARRAY(char, kbuf.dsize+1);
4079 if (!keystr) {
4080 return 1;
4082 memcpy(keystr, kbuf.dptr, kbuf.dsize);
4083 keystr[kbuf.dsize] = '\0';
4085 mem_ctx = talloc_init("validate_ctx");
4086 if (!mem_ctx) {
4087 SAFE_FREE(keystr);
4088 return 1;
4091 ret = key_val[i].validate_data_fn(mem_ctx, keystr, dbuf,
4092 v_state);
4094 SAFE_FREE(keystr);
4095 talloc_destroy(mem_ctx);
4096 return ret;
4100 DEBUG(0,("cache_traverse_validate_fn: unknown cache entry\nkey :\n"));
4101 dump_data(0, (uint8_t *)kbuf.dptr, kbuf.dsize);
4102 DEBUG(0,("data :\n"));
4103 dump_data(0, (uint8_t *)dbuf.dptr, dbuf.dsize);
4104 v_state->unknown_key = true;
4105 v_state->success = false;
4106 return 1; /* terminate. */
4109 static void validate_panic(const char *const why)
4111 DEBUG(0,("validating cache: would panic %s\n", why ));
4112 DEBUGADD(0, ("exiting instead (cache validation mode)\n"));
4113 exit(47);
4116 static int wbcache_update_centry_fn(TDB_CONTEXT *tdb,
4117 TDB_DATA key,
4118 TDB_DATA data,
4119 void *state)
4121 uint64_t ctimeout;
4122 TDB_DATA blob;
4124 if (is_non_centry_key(key)) {
4125 return 0;
4128 if (data.dptr == NULL || data.dsize == 0) {
4129 if (tdb_delete(tdb, key) < 0) {
4130 DEBUG(0, ("tdb_delete for [%s] failed!\n",
4131 key.dptr));
4132 return 1;
4136 /* add timeout to blob (uint64_t) */
4137 blob.dsize = data.dsize + 8;
4139 blob.dptr = SMB_XMALLOC_ARRAY(uint8_t, blob.dsize);
4140 if (blob.dptr == NULL) {
4141 return 1;
4143 memset(blob.dptr, 0, blob.dsize);
4145 /* copy status and seqnum */
4146 memcpy(blob.dptr, data.dptr, 8);
4148 /* add timeout */
4149 ctimeout = lp_winbind_cache_time() + time(NULL);
4150 SBVAL(blob.dptr, 8, ctimeout);
4152 /* copy the rest */
4153 memcpy(blob.dptr + 16, data.dptr + 8, data.dsize - 8);
4155 if (tdb_store(tdb, key, blob, TDB_REPLACE) < 0) {
4156 DEBUG(0, ("tdb_store to update [%s] failed!\n",
4157 key.dptr));
4158 SAFE_FREE(blob.dptr);
4159 return 1;
4162 SAFE_FREE(blob.dptr);
4163 return 0;
4166 static bool wbcache_upgrade_v1_to_v2(TDB_CONTEXT *tdb)
4168 int rc;
4170 DEBUG(1, ("Upgrade to version 2 of the winbindd_cache.tdb\n"));
4172 rc = tdb_traverse(tdb, wbcache_update_centry_fn, NULL);
4173 if (rc < 0) {
4174 return false;
4177 return true;
4180 /***********************************************************************
4181 Try and validate every entry in the winbindd cache. If we fail here,
4182 delete the cache tdb and return non-zero.
4183 ***********************************************************************/
4185 int winbindd_validate_cache(void)
4187 int ret = -1;
4188 char *tdb_path = NULL;
4189 TDB_CONTEXT *tdb = NULL;
4190 uint32_t vers_id;
4191 bool ok;
4193 DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
4194 smb_panic_fn = validate_panic;
4196 tdb_path = wcache_path();
4197 if (tdb_path == NULL) {
4198 goto done;
4201 tdb = tdb_open_log(tdb_path,
4202 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
4203 TDB_INCOMPATIBLE_HASH |
4204 ( lp_winbind_offline_logon()
4205 ? TDB_DEFAULT
4206 : TDB_DEFAULT | TDB_CLEAR_IF_FIRST ),
4207 O_RDWR|O_CREAT,
4208 0600);
4209 if (!tdb) {
4210 DEBUG(0, ("winbindd_validate_cache: "
4211 "error opening/initializing tdb\n"));
4212 goto done;
4215 /* Version check and upgrade code. */
4216 if (!tdb_fetch_uint32(tdb, WINBINDD_CACHE_VERSION_KEYSTR, &vers_id)) {
4217 DEBUG(10, ("Fresh database\n"));
4218 tdb_store_uint32(tdb, WINBINDD_CACHE_VERSION_KEYSTR, WINBINDD_CACHE_VERSION);
4219 vers_id = WINBINDD_CACHE_VERSION;
4222 if (vers_id != WINBINDD_CACHE_VERSION) {
4223 if (vers_id == WINBINDD_CACHE_VER1) {
4224 ok = wbcache_upgrade_v1_to_v2(tdb);
4225 if (!ok) {
4226 DEBUG(10, ("winbindd_validate_cache: upgrade to version 2 failed.\n"));
4227 unlink(tdb_path);
4228 goto done;
4231 tdb_store_uint32(tdb,
4232 WINBINDD_CACHE_VERSION_KEYSTR,
4233 WINBINDD_CACHE_VERSION);
4234 vers_id = WINBINDD_CACHE_VER2;
4238 tdb_close(tdb);
4240 ret = tdb_validate_and_backup(tdb_path, cache_traverse_validate_fn);
4242 if (ret != 0) {
4243 DEBUG(10, ("winbindd_validate_cache: validation not successful.\n"));
4244 DEBUGADD(10, ("removing tdb %s.\n", tdb_path));
4245 unlink(tdb_path);
4248 done:
4249 TALLOC_FREE(tdb_path);
4250 DEBUG(10, ("winbindd_validate_cache: restoring panic function\n"));
4251 smb_panic_fn = smb_panic;
4252 return ret;
4255 /***********************************************************************
4256 Try and validate every entry in the winbindd cache.
4257 ***********************************************************************/
4259 int winbindd_validate_cache_nobackup(void)
4261 int ret = -1;
4262 char *tdb_path;
4264 DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
4265 smb_panic_fn = validate_panic;
4267 tdb_path = wcache_path();
4268 if (tdb_path == NULL) {
4269 goto err_panic_restore;
4272 if (wcache == NULL || wcache->tdb == NULL) {
4273 ret = tdb_validate_open(tdb_path, cache_traverse_validate_fn);
4274 } else {
4275 ret = tdb_validate(wcache->tdb, cache_traverse_validate_fn);
4278 if (ret != 0) {
4279 DEBUG(10, ("winbindd_validate_cache_nobackup: validation not "
4280 "successful.\n"));
4283 TALLOC_FREE(tdb_path);
4284 err_panic_restore:
4285 DEBUG(10, ("winbindd_validate_cache_nobackup: restoring panic "
4286 "function\n"));
4287 smb_panic_fn = smb_panic;
4288 return ret;
4291 bool winbindd_cache_validate_and_initialize(void)
4293 close_winbindd_cache();
4295 if (lp_winbind_offline_logon()) {
4296 if (winbindd_validate_cache() < 0) {
4297 DEBUG(0, ("winbindd cache tdb corrupt and no backup "
4298 "could be restored.\n"));
4302 return initialize_winbindd_cache();
4305 /*********************************************************************
4306 ********************************************************************/
4308 static bool add_wbdomain_to_tdc_array( struct winbindd_domain *new_dom,
4309 struct winbindd_tdc_domain **domains,
4310 size_t *num_domains )
4312 struct winbindd_tdc_domain *list = NULL;
4313 size_t i, idx;
4314 bool set_only = false;
4316 /* don't allow duplicates */
4318 idx = *num_domains;
4319 list = *domains;
4321 for ( i=0; i< (*num_domains); i++ ) {
4322 if ( strequal( new_dom->name, list[i].domain_name ) ) {
4323 DEBUG(10,("add_wbdomain_to_tdc_array: Found existing record for %s\n",
4324 new_dom->name));
4325 idx = i;
4326 set_only = true;
4328 break;
4332 if ( !set_only ) {
4333 if ( !*domains ) {
4334 list = talloc_array( NULL, struct winbindd_tdc_domain, 1 );
4335 idx = 0;
4336 } else {
4337 list = talloc_realloc( *domains, *domains,
4338 struct winbindd_tdc_domain,
4339 (*num_domains)+1);
4340 idx = *num_domains;
4343 ZERO_STRUCT( list[idx] );
4346 if ( !list )
4347 return false;
4349 list[idx].domain_name = talloc_strdup(list, new_dom->name);
4350 if (list[idx].domain_name == NULL) {
4351 return false;
4353 if (new_dom->alt_name != NULL) {
4354 list[idx].dns_name = talloc_strdup(list, new_dom->alt_name);
4355 if (list[idx].dns_name == NULL) {
4356 return false;
4360 if ( !is_null_sid( &new_dom->sid ) ) {
4361 sid_copy( &list[idx].sid, &new_dom->sid );
4362 } else {
4363 sid_copy(&list[idx].sid, &global_sid_NULL);
4366 if ( new_dom->domain_flags != 0x0 )
4367 list[idx].trust_flags = new_dom->domain_flags;
4369 if ( new_dom->domain_type != 0x0 )
4370 list[idx].trust_type = new_dom->domain_type;
4372 if ( new_dom->domain_trust_attribs != 0x0 )
4373 list[idx].trust_attribs = new_dom->domain_trust_attribs;
4375 if ( !set_only ) {
4376 *domains = list;
4377 *num_domains = idx + 1;
4380 return true;
4383 /*********************************************************************
4384 ********************************************************************/
4386 static TDB_DATA make_tdc_key( const char *domain_name )
4388 char *keystr = NULL;
4389 TDB_DATA key = { NULL, 0 };
4391 if ( !domain_name ) {
4392 DEBUG(5,("make_tdc_key: Keyname workgroup is NULL!\n"));
4393 return key;
4396 if (asprintf( &keystr, "TRUSTDOMCACHE/%s", domain_name ) == -1) {
4397 return key;
4399 key = string_term_tdb_data(keystr);
4401 return key;
4404 /*********************************************************************
4405 ********************************************************************/
4407 static int pack_tdc_domains( struct winbindd_tdc_domain *domains,
4408 size_t num_domains,
4409 unsigned char **buf )
4411 unsigned char *buffer = NULL;
4412 int len = 0;
4413 int buflen = 0;
4414 size_t i = 0;
4416 DEBUG(10,("pack_tdc_domains: Packing %d trusted domains\n",
4417 (int)num_domains));
4419 buflen = 0;
4421 again:
4422 len = 0;
4424 /* Store the number of array items first */
4425 len += tdb_pack( buffer ? buffer+len : NULL,
4426 buffer ? buflen-len : 0, "d",
4427 num_domains );
4429 /* now pack each domain trust record */
4430 for ( i=0; i<num_domains; i++ ) {
4432 struct dom_sid_buf tmp;
4434 if ( buflen > 0 ) {
4435 DEBUG(10,("pack_tdc_domains: Packing domain %s (%s)\n",
4436 domains[i].domain_name,
4437 domains[i].dns_name ? domains[i].dns_name : "UNKNOWN" ));
4440 len += tdb_pack( buffer ? buffer+len : NULL,
4441 buffer ? buflen-len : 0, "fffddd",
4442 domains[i].domain_name,
4443 domains[i].dns_name ? domains[i].dns_name : "",
4444 dom_sid_str_buf(&domains[i].sid, &tmp),
4445 domains[i].trust_flags,
4446 domains[i].trust_attribs,
4447 domains[i].trust_type );
4450 if ( buflen < len ) {
4451 SAFE_FREE(buffer);
4452 if ( (buffer = SMB_MALLOC_ARRAY(unsigned char, len)) == NULL ) {
4453 DEBUG(0,("pack_tdc_domains: failed to alloc buffer!\n"));
4454 buflen = -1;
4455 goto done;
4457 buflen = len;
4458 goto again;
4461 *buf = buffer;
4463 done:
4464 return buflen;
4467 /*********************************************************************
4468 ********************************************************************/
4470 static size_t unpack_tdc_domains( unsigned char *buf, int buflen,
4471 struct winbindd_tdc_domain **domains )
4473 fstring domain_name, dns_name, sid_string;
4474 uint32_t type, attribs, flags;
4475 int num_domains;
4476 int len = 0;
4477 int i;
4478 struct winbindd_tdc_domain *list = NULL;
4480 /* get the number of domains */
4481 len += tdb_unpack( buf+len, buflen-len, "d", &num_domains);
4482 if ( len == -1 ) {
4483 DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
4484 return 0;
4487 list = talloc_array( NULL, struct winbindd_tdc_domain, num_domains );
4488 if ( !list ) {
4489 DEBUG(0,("unpack_tdc_domains: Failed to talloc() domain list!\n"));
4490 return 0;
4493 for ( i=0; i<num_domains; i++ ) {
4494 int this_len;
4496 this_len = tdb_unpack( buf+len, buflen-len, "fffddd",
4497 domain_name,
4498 dns_name,
4499 sid_string,
4500 &flags,
4501 &attribs,
4502 &type );
4504 if ( this_len == -1 ) {
4505 DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
4506 TALLOC_FREE( list );
4507 return 0;
4509 len += this_len;
4511 DEBUG(11,("unpack_tdc_domains: Unpacking domain %s (%s) "
4512 "SID %s, flags = 0x%x, attribs = 0x%x, type = 0x%x\n",
4513 domain_name, dns_name, sid_string,
4514 flags, attribs, type));
4516 list[i].domain_name = talloc_strdup( list, domain_name );
4517 list[i].dns_name = NULL;
4518 if (dns_name[0] != '\0') {
4519 list[i].dns_name = talloc_strdup(list, dns_name);
4521 if ( !string_to_sid( &(list[i].sid), sid_string ) ) {
4522 DEBUG(10,("unpack_tdc_domains: no SID for domain %s\n",
4523 domain_name));
4525 list[i].trust_flags = flags;
4526 list[i].trust_attribs = attribs;
4527 list[i].trust_type = type;
4530 *domains = list;
4532 return num_domains;
4535 /*********************************************************************
4536 ********************************************************************/
4538 static bool wcache_tdc_store_list( struct winbindd_tdc_domain *domains, size_t num_domains )
4540 TDB_DATA key = make_tdc_key( lp_workgroup() );
4541 TDB_DATA data = { NULL, 0 };
4542 int ret;
4544 if ( !key.dptr )
4545 return false;
4547 /* See if we were asked to delete the cache entry */
4549 if ( !domains ) {
4550 ret = tdb_delete( wcache->tdb, key );
4551 goto done;
4554 data.dsize = pack_tdc_domains( domains, num_domains, &data.dptr );
4556 if ( !data.dptr ) {
4557 ret = -1;
4558 goto done;
4561 ret = tdb_store( wcache->tdb, key, data, 0 );
4563 done:
4564 SAFE_FREE( data.dptr );
4565 SAFE_FREE( key.dptr );
4567 return ( ret == 0 );
4570 /*********************************************************************
4571 ********************************************************************/
4573 bool wcache_tdc_fetch_list( struct winbindd_tdc_domain **domains, size_t *num_domains )
4575 TDB_DATA key = make_tdc_key( lp_workgroup() );
4576 TDB_DATA data = { NULL, 0 };
4578 *domains = NULL;
4579 *num_domains = 0;
4581 if ( !key.dptr )
4582 return false;
4584 data = tdb_fetch( wcache->tdb, key );
4586 SAFE_FREE( key.dptr );
4588 if ( !data.dptr )
4589 return false;
4591 *num_domains = unpack_tdc_domains( data.dptr, data.dsize, domains );
4593 SAFE_FREE( data.dptr );
4595 if ( !*domains )
4596 return false;
4598 return true;
4601 /*********************************************************************
4602 ********************************************************************/
4604 bool wcache_tdc_add_domain( struct winbindd_domain *domain )
4606 struct winbindd_tdc_domain *dom_list = NULL;
4607 size_t num_domains = 0;
4608 bool ret = false;
4609 struct dom_sid_buf buf;
4611 DEBUG(10,("wcache_tdc_add_domain: Adding domain %s (%s), SID %s, "
4612 "flags = 0x%x, attributes = 0x%x, type = 0x%x\n",
4613 domain->name, domain->alt_name,
4614 dom_sid_str_buf(&domain->sid, &buf),
4615 domain->domain_flags,
4616 domain->domain_trust_attribs,
4617 domain->domain_type));
4619 if ( !init_wcache() ) {
4620 return false;
4623 /* fetch the list */
4625 wcache_tdc_fetch_list( &dom_list, &num_domains );
4627 /* add the new domain */
4629 if ( !add_wbdomain_to_tdc_array( domain, &dom_list, &num_domains ) ) {
4630 goto done;
4633 /* pack the domain */
4635 if ( !wcache_tdc_store_list( dom_list, num_domains ) ) {
4636 goto done;
4639 /* Success */
4641 ret = true;
4642 done:
4643 TALLOC_FREE( dom_list );
4645 return ret;
4648 static struct winbindd_tdc_domain *wcache_tdc_dup_domain(
4649 TALLOC_CTX *mem_ctx, const struct winbindd_tdc_domain *src)
4651 struct winbindd_tdc_domain *dst;
4653 dst = talloc(mem_ctx, struct winbindd_tdc_domain);
4654 if (dst == NULL) {
4655 goto fail;
4657 dst->domain_name = talloc_strdup(dst, src->domain_name);
4658 if (dst->domain_name == NULL) {
4659 goto fail;
4662 dst->dns_name = NULL;
4663 if (src->dns_name != NULL) {
4664 dst->dns_name = talloc_strdup(dst, src->dns_name);
4665 if (dst->dns_name == NULL) {
4666 goto fail;
4670 sid_copy(&dst->sid, &src->sid);
4671 dst->trust_flags = src->trust_flags;
4672 dst->trust_type = src->trust_type;
4673 dst->trust_attribs = src->trust_attribs;
4674 return dst;
4675 fail:
4676 TALLOC_FREE(dst);
4677 return NULL;
4680 /*********************************************************************
4681 ********************************************************************/
4683 struct winbindd_tdc_domain * wcache_tdc_fetch_domain( TALLOC_CTX *ctx, const char *name )
4685 struct winbindd_tdc_domain *dom_list = NULL;
4686 size_t num_domains = 0;
4687 size_t i;
4688 struct winbindd_tdc_domain *d = NULL;
4690 DEBUG(10,("wcache_tdc_fetch_domain: Searching for domain %s\n", name));
4692 if ( !init_wcache() ) {
4693 return NULL;
4696 /* fetch the list */
4698 wcache_tdc_fetch_list( &dom_list, &num_domains );
4700 for ( i=0; i<num_domains; i++ ) {
4701 if ( strequal(name, dom_list[i].domain_name) ||
4702 strequal(name, dom_list[i].dns_name) )
4704 DEBUG(10,("wcache_tdc_fetch_domain: Found domain %s\n",
4705 name));
4707 d = wcache_tdc_dup_domain(ctx, &dom_list[i]);
4708 break;
4712 TALLOC_FREE( dom_list );
4714 return d;
4717 /*********************************************************************
4718 ********************************************************************/
4720 void wcache_tdc_clear( void )
4722 if ( !init_wcache() )
4723 return;
4725 wcache_tdc_store_list( NULL, 0 );
4727 return;
4730 static bool wcache_ndr_key(TALLOC_CTX *mem_ctx, const char *domain_name,
4731 uint32_t opnum, const DATA_BLOB *req,
4732 TDB_DATA *pkey)
4734 char *key;
4735 size_t keylen;
4737 key = talloc_asprintf(mem_ctx, "NDR/%s/%d/", domain_name, (int)opnum);
4738 if (key == NULL) {
4739 return false;
4741 keylen = talloc_get_size(key) - 1;
4743 key = talloc_realloc(mem_ctx, key, char, keylen + req->length);
4744 if (key == NULL) {
4745 return false;
4747 memcpy(key + keylen, req->data, req->length);
4749 pkey->dptr = (uint8_t *)key;
4750 pkey->dsize = talloc_get_size(key);
4751 return true;
4754 static bool wcache_opnum_cacheable(uint32_t opnum)
4756 switch (opnum) {
4757 case NDR_WBINT_PING:
4758 case NDR_WBINT_QUERYSEQUENCENUMBER:
4759 case NDR_WBINT_ALLOCATEUID:
4760 case NDR_WBINT_ALLOCATEGID:
4761 case NDR_WBINT_CHECKMACHINEACCOUNT:
4762 case NDR_WBINT_CHANGEMACHINEACCOUNT:
4763 case NDR_WBINT_PINGDC:
4764 return false;
4766 return true;
4769 bool wcache_fetch_ndr(TALLOC_CTX *mem_ctx, struct winbindd_domain *domain,
4770 uint32_t opnum, const DATA_BLOB *req, DATA_BLOB *resp)
4772 TDB_DATA key, data;
4773 bool ret = false;
4775 if (!wcache_opnum_cacheable(opnum) ||
4776 is_my_own_sam_domain(domain) ||
4777 is_builtin_domain(domain)) {
4778 return false;
4781 if (wcache->tdb == NULL) {
4782 return false;
4785 if (!wcache_ndr_key(talloc_tos(), domain->name, opnum, req, &key)) {
4786 return false;
4788 data = tdb_fetch(wcache->tdb, key);
4789 TALLOC_FREE(key.dptr);
4791 if (data.dptr == NULL) {
4792 return false;
4794 if (data.dsize < 12) {
4795 goto fail;
4798 if (is_domain_online(domain)) {
4799 uint32_t entry_seqnum, dom_seqnum, last_check;
4800 uint64_t entry_timeout;
4802 if (!wcache_fetch_seqnum(domain->name, &dom_seqnum,
4803 &last_check)) {
4804 goto fail;
4806 entry_seqnum = IVAL(data.dptr, 0);
4807 if (entry_seqnum != dom_seqnum) {
4808 DEBUG(10, ("Entry has wrong sequence number: %d\n",
4809 (int)entry_seqnum));
4810 goto fail;
4812 entry_timeout = BVAL(data.dptr, 4);
4813 if (time(NULL) > (time_t)entry_timeout) {
4814 DEBUG(10, ("Entry has timed out\n"));
4815 goto fail;
4819 resp->data = (uint8_t *)talloc_memdup(mem_ctx, data.dptr + 12,
4820 data.dsize - 12);
4821 if (resp->data == NULL) {
4822 DEBUG(10, ("talloc failed\n"));
4823 goto fail;
4825 resp->length = data.dsize - 12;
4827 ret = true;
4828 fail:
4829 SAFE_FREE(data.dptr);
4830 return ret;
4833 void wcache_store_ndr(struct winbindd_domain *domain, uint32_t opnum,
4834 const DATA_BLOB *req, const DATA_BLOB *resp)
4836 TDB_DATA key, data;
4837 uint32_t dom_seqnum, last_check;
4838 uint64_t timeout;
4840 if (!wcache_opnum_cacheable(opnum) ||
4841 is_my_own_sam_domain(domain) ||
4842 is_builtin_domain(domain)) {
4843 return;
4846 if (wcache->tdb == NULL) {
4847 return;
4850 if (!wcache_fetch_seqnum(domain->name, &dom_seqnum, &last_check)) {
4851 DEBUG(10, ("could not fetch seqnum for domain %s\n",
4852 domain->name));
4853 return;
4856 if (!wcache_ndr_key(talloc_tos(), domain->name, opnum, req, &key)) {
4857 return;
4860 timeout = time(NULL) + lp_winbind_cache_time();
4862 data.dsize = resp->length + 12;
4863 data.dptr = talloc_array(key.dptr, uint8_t, data.dsize);
4864 if (data.dptr == NULL) {
4865 goto done;
4868 SIVAL(data.dptr, 0, dom_seqnum);
4869 SBVAL(data.dptr, 4, timeout);
4870 memcpy(data.dptr + 12, resp->data, resp->length);
4872 tdb_store(wcache->tdb, key, data, 0);
4874 done:
4875 TALLOC_FREE(key.dptr);
4876 return;