lib:torture: add torture_assert_u32_[not_]equal[_goto] macros
[Samba.git] / source3 / winbindd / winbindd_cache.c
blob394b0c774a9ef2358155088a1984e15b6d715542
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 #undef DBGC_CLASS
41 #define DBGC_CLASS DBGC_WINBIND
43 #define WINBINDD_CACHE_VER1 1 /* initial db version */
44 #define WINBINDD_CACHE_VER2 2 /* second version with timeouts for NDR entries */
46 #define WINBINDD_CACHE_VERSION WINBINDD_CACHE_VER2
47 #define WINBINDD_CACHE_VERSION_KEYSTR "WINBINDD_CACHE_VERSION"
49 extern struct winbindd_methods reconnect_methods;
50 #ifdef HAVE_ADS
51 extern struct winbindd_methods reconnect_ads_methods;
52 #endif
53 extern struct winbindd_methods builtin_passdb_methods;
54 extern struct winbindd_methods sam_passdb_methods;
56 static void wcache_flush_cache(void);
59 * JRA. KEEP THIS LIST UP TO DATE IF YOU ADD CACHE ENTRIES.
60 * Here are the list of entry types that are *not* stored
61 * as form struct cache_entry in the cache.
64 static const char *non_centry_keys[] = {
65 "SEQNUM/",
66 "WINBINDD_OFFLINE",
67 WINBINDD_CACHE_VERSION_KEYSTR,
68 NULL
71 /************************************************************************
72 Is this key a non-centry type ?
73 ************************************************************************/
75 static bool is_non_centry_key(TDB_DATA kbuf)
77 int i;
79 if (kbuf.dptr == NULL || kbuf.dsize == 0) {
80 return false;
82 for (i = 0; non_centry_keys[i] != NULL; i++) {
83 size_t namelen = strlen(non_centry_keys[i]);
84 if (kbuf.dsize < namelen) {
85 continue;
87 if (strncmp(non_centry_keys[i], (const char *)kbuf.dptr, namelen) == 0) {
88 return true;
91 return false;
94 /* Global online/offline state - False when online. winbindd starts up online
95 and sets this to true if the first query fails and there's an entry in
96 the cache tdb telling us to stay offline. */
98 static bool global_winbindd_offline_state;
100 struct winbind_cache {
101 TDB_CONTEXT *tdb;
104 struct cache_entry {
105 NTSTATUS status;
106 uint32_t sequence_number;
107 uint64_t timeout;
108 uint8_t *data;
109 uint32_t len, ofs;
112 void (*smb_panic_fn)(const char *const why) = smb_panic;
114 static struct winbind_cache *wcache;
116 static char *wcache_path(void)
119 * Data needs to be kept persistent in state directory for
120 * running with "winbindd offline logon".
122 return state_path(talloc_tos(), "winbindd_cache.tdb");
125 static void winbindd_domain_init_backend(struct winbindd_domain *domain)
127 if (domain->backend != NULL) {
128 return;
131 if (domain->internal) {
132 domain->backend = &builtin_passdb_methods;
135 if (dom_sid_equal(&domain->sid, &global_sid_Builtin)) {
136 domain->initialized = true;
139 if (strequal(domain->name, get_global_sam_name()) &&
140 sid_check_is_our_sam(&domain->sid))
142 domain->backend = &sam_passdb_methods;
145 if (!domain->initialized) {
146 /* We do not need a connection to an RW DC for cache operation */
147 init_dc_connection(domain, false);
150 #ifdef HAVE_ADS
151 if (domain->backend == NULL) {
152 struct winbindd_domain *our_domain = domain;
154 /* find our domain first so we can figure out if we
155 are joined to a kerberized domain */
157 if (!domain->primary) {
158 our_domain = find_our_domain();
161 if ((our_domain->active_directory || IS_DC)
162 && domain->active_directory
163 && !lp_winbind_rpc_only())
165 DBG_INFO("Setting ADS methods for domain %s\n",
166 domain->name);
167 domain->backend = &reconnect_ads_methods;
170 #endif /* HAVE_ADS */
172 if (domain->backend == NULL) {
173 DBG_INFO("Setting MS-RPC methods for domain %s\n", domain->name);
174 domain->backend = &reconnect_methods;
178 /* get the winbind_cache structure */
179 static struct winbind_cache *get_cache(struct winbindd_domain *domain)
181 struct winbind_cache *ret = wcache;
183 winbindd_domain_init_backend(domain);
185 if (ret != NULL) {
186 return ret;
189 ret = SMB_XMALLOC_P(struct winbind_cache);
190 ZERO_STRUCTP(ret);
192 wcache = ret;
193 wcache_flush_cache();
195 return ret;
199 free a centry structure
201 static void centry_free(struct cache_entry *centry)
203 if (!centry)
204 return;
205 SAFE_FREE(centry->data);
206 free(centry);
209 static bool centry_check_bytes(struct cache_entry *centry, size_t nbytes)
211 if (centry->len - centry->ofs < nbytes) {
212 DEBUG(0,("centry corruption? needed %u bytes, have %d\n",
213 (unsigned int)nbytes,
214 centry->len - centry->ofs));
215 return false;
217 return true;
221 pull a uint64_t from a cache entry
223 static uint64_t centry_uint64_t(struct cache_entry *centry)
225 uint64_t ret;
227 if (!centry_check_bytes(centry, 8)) {
228 smb_panic_fn("centry_uint64_t");
230 ret = BVAL(centry->data, centry->ofs);
231 centry->ofs += 8;
232 return ret;
236 pull a uint32_t from a cache entry
238 static uint32_t centry_uint32(struct cache_entry *centry)
240 uint32_t ret;
242 if (!centry_check_bytes(centry, 4)) {
243 smb_panic_fn("centry_uint32");
245 ret = IVAL(centry->data, centry->ofs);
246 centry->ofs += 4;
247 return ret;
251 pull a uint16_t from a cache entry
253 static uint16_t centry_uint16(struct cache_entry *centry)
255 uint16_t ret;
256 if (!centry_check_bytes(centry, 2)) {
257 smb_panic_fn("centry_uint16");
259 ret = SVAL(centry->data, centry->ofs);
260 centry->ofs += 2;
261 return ret;
265 pull a uint8_t from a cache entry
267 static uint8_t centry_uint8(struct cache_entry *centry)
269 uint8_t ret;
270 if (!centry_check_bytes(centry, 1)) {
271 smb_panic_fn("centry_uint8");
273 ret = CVAL(centry->data, centry->ofs);
274 centry->ofs += 1;
275 return ret;
279 pull a NTTIME from a cache entry
281 static NTTIME centry_nttime(struct cache_entry *centry)
283 NTTIME ret;
284 if (!centry_check_bytes(centry, 8)) {
285 smb_panic_fn("centry_nttime");
287 ret = IVAL(centry->data, centry->ofs);
288 centry->ofs += 4;
289 ret += (uint64_t)IVAL(centry->data, centry->ofs) << 32;
290 centry->ofs += 4;
291 return ret;
295 pull a time_t from a cache entry. time_t stored portably as a 64-bit time.
297 static time_t centry_time(struct cache_entry *centry)
299 return (time_t)centry_nttime(centry);
302 /* pull a string from a cache entry, using the supplied
303 talloc context
305 static char *centry_string(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
307 uint32_t len;
308 char *ret;
310 len = centry_uint8(centry);
312 if (len == 0xFF) {
313 /* a deliberate NULL string */
314 return NULL;
317 if (!centry_check_bytes(centry, (size_t)len)) {
318 smb_panic_fn("centry_string");
321 ret = talloc_array(mem_ctx, char, len+1);
322 if (!ret) {
323 smb_panic_fn("centry_string out of memory\n");
325 memcpy(ret,centry->data + centry->ofs, len);
326 ret[len] = 0;
327 centry->ofs += len;
328 return ret;
331 /* pull a hash16 from a cache entry, using the supplied
332 talloc context
334 static char *centry_hash16(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
336 uint32_t len;
337 char *ret;
339 len = centry_uint8(centry);
341 if (len != 16) {
342 DEBUG(0,("centry corruption? hash len (%u) != 16\n",
343 len ));
344 return NULL;
347 if (!centry_check_bytes(centry, 16)) {
348 return NULL;
351 ret = talloc_array(mem_ctx, char, 16);
352 if (!ret) {
353 smb_panic_fn("centry_hash out of memory\n");
355 memcpy(ret,centry->data + centry->ofs, 16);
356 centry->ofs += 16;
357 return ret;
360 /* pull a sid from a cache entry, using the supplied
361 talloc context
363 static bool centry_sid(struct cache_entry *centry, struct dom_sid *sid)
365 char *sid_string;
366 bool ret;
368 sid_string = centry_string(centry, talloc_tos());
369 if (sid_string == NULL) {
370 return false;
372 ret = string_to_sid(sid, sid_string);
373 TALLOC_FREE(sid_string);
374 return ret;
379 pull a NTSTATUS from a cache entry
381 static NTSTATUS centry_ntstatus(struct cache_entry *centry)
383 NTSTATUS status;
385 status = NT_STATUS(centry_uint32(centry));
386 return status;
390 /* the server is considered down if it can't give us a sequence number */
391 static bool wcache_server_down(struct winbindd_domain *domain)
393 bool ret;
395 if (!wcache->tdb)
396 return false;
398 ret = (domain->sequence_number == DOM_SEQUENCE_NONE);
400 if (ret)
401 DEBUG(10,("wcache_server_down: server for Domain %s down\n",
402 domain->name ));
403 return ret;
406 struct wcache_seqnum_state {
407 uint32_t *seqnum;
408 uint32_t *last_seq_check;
411 static int wcache_seqnum_parser(TDB_DATA key, TDB_DATA data,
412 void *private_data)
414 struct wcache_seqnum_state *state = private_data;
416 if (data.dsize != 8) {
417 DEBUG(10, ("wcache_fetch_seqnum: invalid data size %d\n",
418 (int)data.dsize));
419 return -1;
422 *state->seqnum = IVAL(data.dptr, 0);
423 *state->last_seq_check = IVAL(data.dptr, 4);
424 return 0;
427 static bool wcache_fetch_seqnum(const char *domain_name, uint32_t *seqnum,
428 uint32_t *last_seq_check)
430 struct wcache_seqnum_state state = {
431 .seqnum = seqnum, .last_seq_check = last_seq_check
433 size_t len = strlen(domain_name);
434 char keystr[len+8];
435 TDB_DATA key = { .dptr = (uint8_t *)keystr, .dsize = sizeof(keystr) };
436 int ret;
438 if (wcache->tdb == NULL) {
439 DEBUG(10,("wcache_fetch_seqnum: tdb == NULL\n"));
440 return false;
443 snprintf(keystr, sizeof(keystr), "SEQNUM/%s", domain_name);
445 ret = tdb_parse_record(wcache->tdb, key, wcache_seqnum_parser,
446 &state);
447 return (ret == 0);
450 static NTSTATUS fetch_cache_seqnum( struct winbindd_domain *domain, time_t now )
452 uint32_t last_check, time_diff;
454 if (!wcache_fetch_seqnum(domain->name, &domain->sequence_number,
455 &last_check)) {
456 return NT_STATUS_UNSUCCESSFUL;
458 domain->last_seq_check = last_check;
460 /* have we expired? */
462 time_diff = now - domain->last_seq_check;
463 if ((int)time_diff > lp_winbind_cache_time()) {
464 DEBUG(10,("fetch_cache_seqnum: timeout [%s][%u @ %u]\n",
465 domain->name, domain->sequence_number,
466 (uint32_t)domain->last_seq_check));
467 return NT_STATUS_UNSUCCESSFUL;
470 DEBUG(10,("fetch_cache_seqnum: success [%s][%u @ %u]\n",
471 domain->name, domain->sequence_number,
472 (uint32_t)domain->last_seq_check));
474 return NT_STATUS_OK;
477 bool wcache_store_seqnum(const char *domain_name, uint32_t seqnum,
478 time_t last_seq_check)
480 size_t len = strlen(domain_name);
481 char keystr[len+8];
482 TDB_DATA key = { .dptr = (uint8_t *)keystr, .dsize = sizeof(keystr) };
483 uint8_t buf[8];
484 int ret;
486 if (wcache->tdb == NULL) {
487 DEBUG(10, ("wcache_store_seqnum: wcache->tdb == NULL\n"));
488 return false;
491 snprintf(keystr, sizeof(keystr), "SEQNUM/%s", domain_name);
493 SIVAL(buf, 0, seqnum);
494 SIVAL(buf, 4, last_seq_check);
496 ret = tdb_store(wcache->tdb, key, make_tdb_data(buf, sizeof(buf)),
497 TDB_REPLACE);
498 if (ret != 0) {
499 DEBUG(10, ("tdb_store_bystring failed: %s\n",
500 tdb_errorstr(wcache->tdb)));
501 return false;
504 DEBUG(10, ("wcache_store_seqnum: success [%s][%u @ %u]\n",
505 domain_name, seqnum, (unsigned)last_seq_check));
507 return true;
510 static bool store_cache_seqnum( struct winbindd_domain *domain )
512 return wcache_store_seqnum(domain->name, domain->sequence_number,
513 domain->last_seq_check);
517 refresh the domain sequence number on timeout.
520 static void refresh_sequence_number(struct winbindd_domain *domain)
522 NTSTATUS status;
523 unsigned time_diff;
524 time_t t = time(NULL);
525 unsigned cache_time = lp_winbind_cache_time();
527 if (is_domain_offline(domain)) {
528 return;
531 get_cache( domain );
533 #if 0 /* JERRY -- disable as the default cache time is now 5 minutes */
534 /* trying to reconnect is expensive, don't do it too often */
535 if (domain->sequence_number == DOM_SEQUENCE_NONE) {
536 cache_time *= 8;
538 #endif
540 time_diff = t - domain->last_seq_check;
542 /* see if we have to refetch the domain sequence number */
543 if ((time_diff < cache_time) &&
544 (domain->sequence_number != DOM_SEQUENCE_NONE) &&
545 NT_STATUS_IS_OK(domain->last_status)) {
546 DEBUG(10, ("refresh_sequence_number: %s time ok\n", domain->name));
547 goto done;
550 /* try to get the sequence number from the tdb cache first */
551 /* this will update the timestamp as well */
553 status = fetch_cache_seqnum( domain, t );
554 if (NT_STATUS_IS_OK(status) &&
555 (domain->sequence_number != DOM_SEQUENCE_NONE) &&
556 NT_STATUS_IS_OK(domain->last_status)) {
557 goto done;
560 /* important! make sure that we know if this is a native
561 mode domain or not. And that we can contact it. */
563 if ( winbindd_can_contact_domain( domain ) ) {
564 status = domain->backend->sequence_number(domain,
565 &domain->sequence_number);
566 } else {
567 /* just use the current time */
568 status = NT_STATUS_OK;
569 domain->sequence_number = time(NULL);
573 /* the above call could have set our domain->backend to NULL when
574 * coming from offline to online mode, make sure to reinitialize the
575 * backend - Guenther */
576 get_cache( domain );
578 if (!NT_STATUS_IS_OK(status)) {
579 DEBUG(10,("refresh_sequence_number: failed with %s\n", nt_errstr(status)));
580 domain->sequence_number = DOM_SEQUENCE_NONE;
583 domain->last_status = status;
584 domain->last_seq_check = time(NULL);
586 /* save the new sequence number in the cache */
587 store_cache_seqnum( domain );
589 done:
590 DEBUG(10, ("refresh_sequence_number: %s seq number is now %d\n",
591 domain->name, domain->sequence_number));
593 return;
597 decide if a cache entry has expired
599 static bool centry_expired(struct winbindd_domain *domain, const char *keystr, struct cache_entry *centry)
601 /* If we've been told to be offline - stay in that state... */
602 if (lp_winbind_offline_logon() && global_winbindd_offline_state) {
603 DEBUG(10,("centry_expired: Key %s for domain %s valid as winbindd is globally offline.\n",
604 keystr, domain->name ));
605 return false;
608 /* when the domain is offline return the cached entry.
609 * This deals with transient offline states... */
611 if (!domain->online) {
612 DEBUG(10,("centry_expired: Key %s for domain %s valid as domain is offline.\n",
613 keystr, domain->name ));
614 return false;
617 /* if the server is OK and our cache entry came from when it was down then
618 the entry is invalid */
619 if ((domain->sequence_number != DOM_SEQUENCE_NONE) &&
620 (centry->sequence_number == DOM_SEQUENCE_NONE)) {
621 DEBUG(10,("centry_expired: Key %s for domain %s invalid sequence.\n",
622 keystr, domain->name ));
623 return true;
626 /* if the server is down or the cache entry is not older than the
627 current sequence number or it did not timeout then it is OK */
628 if (wcache_server_down(domain)
629 || ((centry->sequence_number == domain->sequence_number)
630 && ((time_t)centry->timeout > time(NULL)))) {
631 DEBUG(10,("centry_expired: Key %s for domain %s is good.\n",
632 keystr, domain->name ));
633 return false;
636 DEBUG(10,("centry_expired: Key %s for domain %s expired\n",
637 keystr, domain->name ));
639 /* it's expired */
640 return true;
643 static struct cache_entry *wcache_fetch_raw(char *kstr)
645 TDB_DATA data;
646 struct cache_entry *centry;
647 TDB_DATA key;
649 key = string_tdb_data(kstr);
650 data = tdb_fetch(wcache->tdb, key);
651 if (!data.dptr) {
652 /* a cache miss */
653 return NULL;
656 centry = SMB_XMALLOC_P(struct cache_entry);
657 centry->data = (unsigned char *)data.dptr;
658 centry->len = data.dsize;
659 centry->ofs = 0;
661 if (centry->len < 16) {
662 /* huh? corrupt cache? */
663 DEBUG(10,("wcache_fetch_raw: Corrupt cache for key %s "
664 "(len < 16)?\n", kstr));
665 centry_free(centry);
666 return NULL;
669 centry->status = centry_ntstatus(centry);
670 centry->sequence_number = centry_uint32(centry);
671 centry->timeout = centry_uint64_t(centry);
673 return centry;
676 static bool is_my_own_sam_domain(struct winbindd_domain *domain)
678 if (strequal(domain->name, get_global_sam_name()) &&
679 sid_check_is_our_sam(&domain->sid)) {
680 return true;
683 return false;
686 static bool is_builtin_domain(struct winbindd_domain *domain)
688 if (strequal(domain->name, "BUILTIN") &&
689 sid_check_is_builtin(&domain->sid)) {
690 return true;
693 return false;
697 fetch an entry from the cache, with a varargs key. auto-fetch the sequence
698 number and return status
700 static struct cache_entry *wcache_fetch(struct winbind_cache *cache,
701 struct winbindd_domain *domain,
702 const char *format, ...) PRINTF_ATTRIBUTE(3,4);
703 static struct cache_entry *wcache_fetch(struct winbind_cache *cache,
704 struct winbindd_domain *domain,
705 const char *format, ...)
707 va_list ap;
708 char *kstr;
709 struct cache_entry *centry;
711 if (!winbindd_use_cache() ||
712 is_my_own_sam_domain(domain) ||
713 is_builtin_domain(domain)) {
714 return NULL;
717 refresh_sequence_number(domain);
719 va_start(ap, format);
720 smb_xvasprintf(&kstr, format, ap);
721 va_end(ap);
723 centry = wcache_fetch_raw(kstr);
724 if (centry == NULL) {
725 free(kstr);
726 return NULL;
729 if (centry_expired(domain, kstr, centry)) {
731 DEBUG(10,("wcache_fetch: entry %s expired for domain %s\n",
732 kstr, domain->name ));
734 centry_free(centry);
735 free(kstr);
736 return NULL;
739 DEBUG(10,("wcache_fetch: returning entry %s for domain %s\n",
740 kstr, domain->name ));
742 free(kstr);
743 return centry;
746 static void wcache_delete(const char *format, ...) PRINTF_ATTRIBUTE(1,2);
747 static void wcache_delete(const char *format, ...)
749 va_list ap;
750 char *kstr;
751 TDB_DATA key;
753 va_start(ap, format);
754 smb_xvasprintf(&kstr, format, ap);
755 va_end(ap);
757 key = string_tdb_data(kstr);
759 tdb_delete(wcache->tdb, key);
760 free(kstr);
764 make sure we have at least len bytes available in a centry
766 static void centry_expand(struct cache_entry *centry, uint32_t len)
768 if (centry->len - centry->ofs >= len)
769 return;
770 centry->len *= 2;
771 centry->data = SMB_REALLOC_ARRAY(centry->data, unsigned char,
772 centry->len);
773 if (!centry->data) {
774 DEBUG(0,("out of memory: needed %d bytes in centry_expand\n", centry->len));
775 smb_panic_fn("out of memory in centry_expand");
780 push a uint64_t into a centry
782 static void centry_put_uint64_t(struct cache_entry *centry, uint64_t v)
784 centry_expand(centry, 8);
785 SBVAL(centry->data, centry->ofs, v);
786 centry->ofs += 8;
790 push a uint32_t into a centry
792 static void centry_put_uint32(struct cache_entry *centry, uint32_t v)
794 centry_expand(centry, 4);
795 SIVAL(centry->data, centry->ofs, v);
796 centry->ofs += 4;
800 push a uint16_t into a centry
802 static void centry_put_uint16(struct cache_entry *centry, uint16_t v)
804 centry_expand(centry, 2);
805 SSVAL(centry->data, centry->ofs, v);
806 centry->ofs += 2;
810 push a uint8_t into a centry
812 static void centry_put_uint8(struct cache_entry *centry, uint8_t v)
814 centry_expand(centry, 1);
815 SCVAL(centry->data, centry->ofs, v);
816 centry->ofs += 1;
820 push a string into a centry
822 static void centry_put_string(struct cache_entry *centry, const char *s)
824 int len;
826 if (!s) {
827 /* null strings are marked as len 0xFFFF */
828 centry_put_uint8(centry, 0xFF);
829 return;
832 len = strlen(s);
833 /* can't handle more than 254 char strings. Truncating is probably best */
834 if (len > 254) {
835 DEBUG(10,("centry_put_string: truncating len (%d) to: 254\n", len));
836 len = 254;
838 centry_put_uint8(centry, len);
839 centry_expand(centry, len);
840 memcpy(centry->data + centry->ofs, s, len);
841 centry->ofs += len;
845 push a 16 byte hash into a centry - treat as 16 byte string.
847 static void centry_put_hash16(struct cache_entry *centry, const uint8_t val[16])
849 centry_put_uint8(centry, 16);
850 centry_expand(centry, 16);
851 memcpy(centry->data + centry->ofs, val, 16);
852 centry->ofs += 16;
855 static void centry_put_sid(struct cache_entry *centry, const struct dom_sid *sid)
857 struct dom_sid_buf sid_string;
858 centry_put_string(centry, dom_sid_str_buf(sid, &sid_string));
863 put NTSTATUS into a centry
865 static void centry_put_ntstatus(struct cache_entry *centry, NTSTATUS status)
867 uint32_t status_value = NT_STATUS_V(status);
868 centry_put_uint32(centry, status_value);
873 push a NTTIME into a centry
875 static void centry_put_nttime(struct cache_entry *centry, NTTIME nt)
877 centry_expand(centry, 8);
878 SIVAL(centry->data, centry->ofs, nt & 0xFFFFFFFF);
879 centry->ofs += 4;
880 SIVAL(centry->data, centry->ofs, nt >> 32);
881 centry->ofs += 4;
885 push a time_t into a centry - use a 64 bit size.
886 NTTIME here is being used as a convenient 64-bit size.
888 static void centry_put_time(struct cache_entry *centry, time_t t)
890 NTTIME nt = (NTTIME)t;
891 centry_put_nttime(centry, nt);
895 start a centry for output. When finished, call centry_end()
897 static struct cache_entry *centry_start(struct winbindd_domain *domain,
898 NTSTATUS status)
900 struct cache_entry *centry;
902 if (!wcache->tdb)
903 return NULL;
905 centry = SMB_XMALLOC_P(struct cache_entry);
907 centry->len = 8192; /* reasonable default */
908 centry->data = SMB_XMALLOC_ARRAY(uint8_t, centry->len);
909 centry->ofs = 0;
910 centry->sequence_number = domain->sequence_number;
911 centry->timeout = lp_winbind_cache_time() + time(NULL);
912 centry_put_ntstatus(centry, status);
913 centry_put_uint32(centry, centry->sequence_number);
914 centry_put_uint64_t(centry, centry->timeout);
915 return centry;
919 finish a centry and write it to the tdb
921 static void centry_end(struct cache_entry *centry, const char *format, ...) PRINTF_ATTRIBUTE(2,3);
922 static void centry_end(struct cache_entry *centry, const char *format, ...)
924 va_list ap;
925 char *kstr;
926 TDB_DATA key, data;
928 if (!winbindd_use_cache()) {
929 return;
932 va_start(ap, format);
933 smb_xvasprintf(&kstr, format, ap);
934 va_end(ap);
936 key = string_tdb_data(kstr);
937 data.dptr = centry->data;
938 data.dsize = centry->ofs;
940 tdb_store(wcache->tdb, key, data, TDB_REPLACE);
941 free(kstr);
944 static void wcache_save_name_to_sid(struct winbindd_domain *domain,
945 NTSTATUS status, const char *domain_name,
946 const char *name, const struct dom_sid *sid,
947 enum lsa_SidType type)
949 bool ok;
951 ok = namemap_cache_set_name2sid(domain_name, name, sid, type,
952 time(NULL) + lp_winbind_cache_time());
953 if (!ok) {
954 DBG_DEBUG("namemap_cache_set_name2sid failed\n");
958 * Don't store the reverse mapping. The name came from user
959 * input, and we might not have the correct capitalization,
960 * which is important for nsswitch.
964 static void wcache_save_sid_to_name(struct winbindd_domain *domain, NTSTATUS status,
965 const struct dom_sid *sid, const char *domain_name, const char *name, enum lsa_SidType type)
967 bool ok;
969 ok = namemap_cache_set_sid2name(sid, domain_name, name, type,
970 time(NULL) + lp_winbind_cache_time());
971 if (!ok) {
972 DBG_DEBUG("namemap_cache_set_sid2name failed\n");
975 if (type != SID_NAME_UNKNOWN) {
976 ok = namemap_cache_set_name2sid(
977 domain_name, name, sid, type,
978 time(NULL) + lp_winbind_cache_time());
979 if (!ok) {
980 DBG_DEBUG("namemap_cache_set_name2sid failed\n");
985 static void wcache_save_lockout_policy(struct winbindd_domain *domain,
986 NTSTATUS status,
987 struct samr_DomInfo12 *lockout_policy)
989 struct cache_entry *centry;
991 centry = centry_start(domain, status);
992 if (!centry)
993 return;
995 centry_put_nttime(centry, lockout_policy->lockout_duration);
996 centry_put_nttime(centry, lockout_policy->lockout_window);
997 centry_put_uint16(centry, lockout_policy->lockout_threshold);
999 centry_end(centry, "LOC_POL/%s", domain->name);
1001 DEBUG(10,("wcache_save_lockout_policy: %s\n", domain->name));
1003 centry_free(centry);
1008 static void wcache_save_password_policy(struct winbindd_domain *domain,
1009 NTSTATUS status,
1010 struct samr_DomInfo1 *policy)
1012 struct cache_entry *centry;
1014 centry = centry_start(domain, status);
1015 if (!centry)
1016 return;
1018 centry_put_uint16(centry, policy->min_password_length);
1019 centry_put_uint16(centry, policy->password_history_length);
1020 centry_put_uint32(centry, policy->password_properties);
1021 centry_put_nttime(centry, policy->max_password_age);
1022 centry_put_nttime(centry, policy->min_password_age);
1024 centry_end(centry, "PWD_POL/%s", domain->name);
1026 DEBUG(10,("wcache_save_password_policy: %s\n", domain->name));
1028 centry_free(centry);
1031 /***************************************************************************
1032 ***************************************************************************/
1034 static void wcache_save_username_alias(struct winbindd_domain *domain,
1035 NTSTATUS status,
1036 const char *name, const char *alias)
1038 struct cache_entry *centry;
1039 fstring uname;
1041 if ( (centry = centry_start(domain, status)) == NULL )
1042 return;
1044 centry_put_string( centry, alias );
1046 fstrcpy(uname, name);
1047 (void)strupper_m(uname);
1048 centry_end(centry, "NSS/NA/%s", uname);
1050 DEBUG(10,("wcache_save_username_alias: %s -> %s\n", name, alias ));
1052 centry_free(centry);
1055 static void wcache_save_alias_username(struct winbindd_domain *domain,
1056 NTSTATUS status,
1057 const char *alias, const char *name)
1059 struct cache_entry *centry;
1060 fstring uname;
1062 if ( (centry = centry_start(domain, status)) == NULL )
1063 return;
1065 centry_put_string( centry, name );
1067 fstrcpy(uname, alias);
1068 (void)strupper_m(uname);
1069 centry_end(centry, "NSS/AN/%s", uname);
1071 DEBUG(10,("wcache_save_alias_username: %s -> %s\n", alias, name ));
1073 centry_free(centry);
1076 /***************************************************************************
1077 ***************************************************************************/
1079 NTSTATUS resolve_username_to_alias( TALLOC_CTX *mem_ctx,
1080 struct winbindd_domain *domain,
1081 const char *name, char **alias )
1083 struct winbind_cache *cache = get_cache(domain);
1084 struct cache_entry *centry = NULL;
1085 NTSTATUS status;
1086 char *upper_name;
1088 if ( domain->internal )
1089 return NT_STATUS_NOT_SUPPORTED;
1091 if (!cache->tdb)
1092 goto do_query;
1094 upper_name = talloc_strdup_upper(mem_ctx, name);
1095 if (upper_name == NULL) {
1096 return NT_STATUS_NO_MEMORY;
1099 centry = wcache_fetch(cache, domain, "NSS/NA/%s", upper_name);
1101 talloc_free(upper_name);
1103 if (!centry)
1104 goto do_query;
1106 status = centry->status;
1108 if (!NT_STATUS_IS_OK(status)) {
1109 centry_free(centry);
1110 return status;
1113 *alias = centry_string( centry, mem_ctx );
1115 centry_free(centry);
1117 DEBUG(10,("resolve_username_to_alias: [Cached] - mapped %s to %s\n",
1118 name, *alias ? *alias : "(none)"));
1120 return (*alias) ? NT_STATUS_OK : NT_STATUS_OBJECT_NAME_NOT_FOUND;
1122 do_query:
1124 /* If its not in cache and we are offline, then fail */
1126 if (is_domain_offline(domain)) {
1127 DEBUG(8,("resolve_username_to_alias: rejecting query "
1128 "in offline mode\n"));
1129 return NT_STATUS_NOT_FOUND;
1132 status = nss_map_to_alias( mem_ctx, domain->name, name, alias );
1134 if ( NT_STATUS_IS_OK( status ) ) {
1135 wcache_save_username_alias(domain, status, name, *alias);
1138 if ( NT_STATUS_EQUAL( status, NT_STATUS_NONE_MAPPED ) ) {
1139 wcache_save_username_alias(domain, status, name, "(NULL)");
1142 DEBUG(5,("resolve_username_to_alias: backend query returned %s\n",
1143 nt_errstr(status)));
1145 if ( NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ) {
1146 set_domain_offline( domain );
1149 return status;
1152 /***************************************************************************
1153 ***************************************************************************/
1155 NTSTATUS resolve_alias_to_username( TALLOC_CTX *mem_ctx,
1156 struct winbindd_domain *domain,
1157 const char *alias, char **name )
1159 struct winbind_cache *cache = get_cache(domain);
1160 struct cache_entry *centry = NULL;
1161 NTSTATUS status;
1162 char *upper_name;
1164 if ( domain->internal )
1165 return NT_STATUS_NOT_SUPPORTED;
1167 if (!cache->tdb)
1168 goto do_query;
1170 upper_name = talloc_strdup(mem_ctx, alias);
1171 if (upper_name == NULL) {
1172 return NT_STATUS_NO_MEMORY;
1174 if (!strupper_m(upper_name)) {
1175 talloc_free(upper_name);
1176 return NT_STATUS_INVALID_PARAMETER;
1179 centry = wcache_fetch(cache, domain, "NSS/AN/%s", upper_name);
1181 talloc_free(upper_name);
1183 if (!centry)
1184 goto do_query;
1186 status = centry->status;
1188 if (!NT_STATUS_IS_OK(status)) {
1189 centry_free(centry);
1190 return status;
1193 *name = centry_string( centry, mem_ctx );
1195 centry_free(centry);
1197 DEBUG(10,("resolve_alias_to_username: [Cached] - mapped %s to %s\n",
1198 alias, *name ? *name : "(none)"));
1200 return (*name) ? NT_STATUS_OK : NT_STATUS_OBJECT_NAME_NOT_FOUND;
1202 do_query:
1204 /* If its not in cache and we are offline, then fail */
1206 if (is_domain_offline(domain)) {
1207 DEBUG(8,("resolve_alias_to_username: rejecting query "
1208 "in offline mode\n"));
1209 return NT_STATUS_NOT_FOUND;
1212 /* an alias cannot contain a domain prefix or '@' */
1214 if (strchr(alias, '\\') || strchr(alias, '@')) {
1215 DEBUG(10,("resolve_alias_to_username: skipping fully "
1216 "qualified name %s\n", alias));
1217 return NT_STATUS_OBJECT_NAME_INVALID;
1220 status = nss_map_from_alias( mem_ctx, domain->name, alias, name );
1222 if ( NT_STATUS_IS_OK( status ) ) {
1223 wcache_save_alias_username( domain, status, alias, *name );
1226 if (NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) {
1227 wcache_save_alias_username(domain, status, alias, "(NULL)");
1230 DEBUG(5,("resolve_alias_to_username: backend query returned %s\n",
1231 nt_errstr(status)));
1233 if ( NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ) {
1234 set_domain_offline( domain );
1237 return status;
1240 NTSTATUS wcache_cached_creds_exist(struct winbindd_domain *domain, const struct dom_sid *sid)
1242 struct winbind_cache *cache = get_cache(domain);
1243 int ret;
1244 struct dom_sid_buf tmp;
1245 fstring key_str;
1246 uint32_t rid;
1248 if (!cache->tdb) {
1249 return NT_STATUS_INTERNAL_DB_ERROR;
1252 if (is_null_sid(sid)) {
1253 return NT_STATUS_INVALID_SID;
1256 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1257 return NT_STATUS_INVALID_SID;
1260 fstr_sprintf(key_str, "CRED/%s", dom_sid_str_buf(sid, &tmp));
1262 ret = tdb_exists(cache->tdb, string_tdb_data(key_str));
1263 if (ret != 1) {
1264 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1267 return NT_STATUS_OK;
1270 /* Lookup creds for a SID - copes with old (unsalted) creds as well
1271 as new salted ones. */
1273 NTSTATUS wcache_get_creds(struct winbindd_domain *domain,
1274 TALLOC_CTX *mem_ctx,
1275 const struct dom_sid *sid,
1276 const uint8_t **cached_nt_pass,
1277 const uint8_t **cached_salt)
1279 struct winbind_cache *cache = get_cache(domain);
1280 struct cache_entry *centry = NULL;
1281 NTSTATUS status;
1282 uint32_t rid;
1283 struct dom_sid_buf sidstr;
1285 if (!cache->tdb) {
1286 return NT_STATUS_INTERNAL_DB_ERROR;
1289 if (is_null_sid(sid)) {
1290 return NT_STATUS_INVALID_SID;
1293 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1294 return NT_STATUS_INVALID_SID;
1297 /* Try and get a salted cred first. If we can't
1298 fall back to an unsalted cred. */
1300 centry = wcache_fetch(cache, domain, "CRED/%s",
1301 dom_sid_str_buf(sid, &sidstr));
1302 if (!centry) {
1303 DEBUG(10,("wcache_get_creds: entry for [CRED/%s] not found\n",
1304 dom_sid_str_buf(sid, &sidstr)));
1305 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1309 * We don't use the time element at this moment,
1310 * but we have to consume it, so that we don't
1311 * neet to change the disk format of the cache.
1313 (void)centry_time(centry);
1315 /* In the salted case this isn't actually the nt_hash itself,
1316 but the MD5 of the salt + nt_hash. Let the caller
1317 sort this out. It can tell as we only return the cached_salt
1318 if we are returning a salted cred. */
1320 *cached_nt_pass = (const uint8_t *)centry_hash16(centry, mem_ctx);
1321 if (*cached_nt_pass == NULL) {
1323 dom_sid_str_buf(sid, &sidstr);
1325 /* Bad (old) cred cache. Delete and pretend we
1326 don't have it. */
1327 DEBUG(0,("wcache_get_creds: bad entry for [CRED/%s] - deleting\n",
1328 sidstr.buf));
1329 wcache_delete("CRED/%s", sidstr.buf);
1330 centry_free(centry);
1331 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1334 /* We only have 17 bytes more data in the salted cred case. */
1335 if (centry->len - centry->ofs == 17) {
1336 *cached_salt = (const uint8_t *)centry_hash16(centry, mem_ctx);
1337 } else {
1338 *cached_salt = NULL;
1341 dump_data_pw("cached_nt_pass", *cached_nt_pass, NT_HASH_LEN);
1342 if (*cached_salt) {
1343 dump_data_pw("cached_salt", *cached_salt, NT_HASH_LEN);
1346 status = centry->status;
1348 DEBUG(10,("wcache_get_creds: [Cached] - cached creds for user %s status: %s\n",
1349 dom_sid_str_buf(sid, &sidstr),
1350 nt_errstr(status) ));
1352 centry_free(centry);
1353 return status;
1356 /* Store creds for a SID - only writes out new salted ones. */
1358 NTSTATUS wcache_save_creds(struct winbindd_domain *domain,
1359 const struct dom_sid *sid,
1360 const uint8_t nt_pass[NT_HASH_LEN])
1362 struct cache_entry *centry;
1363 struct dom_sid_buf sid_str;
1364 uint32_t rid;
1365 uint8_t cred_salt[NT_HASH_LEN];
1366 uint8_t salted_hash[NT_HASH_LEN];
1368 if (is_null_sid(sid)) {
1369 return NT_STATUS_INVALID_SID;
1372 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1373 return NT_STATUS_INVALID_SID;
1376 centry = centry_start(domain, NT_STATUS_OK);
1377 if (!centry) {
1378 return NT_STATUS_INTERNAL_DB_ERROR;
1381 dump_data_pw("nt_pass", nt_pass, NT_HASH_LEN);
1383 centry_put_time(centry, time(NULL));
1385 /* Create a salt and then salt the hash. */
1386 generate_random_buffer(cred_salt, NT_HASH_LEN);
1387 E_md5hash(cred_salt, nt_pass, salted_hash);
1389 centry_put_hash16(centry, salted_hash);
1390 centry_put_hash16(centry, cred_salt);
1391 centry_end(centry, "CRED/%s", dom_sid_str_buf(sid, &sid_str));
1393 DEBUG(10,("wcache_save_creds: %s\n", sid_str.buf));
1395 centry_free(centry);
1397 return NT_STATUS_OK;
1401 /* Query display info. This is the basic user list fn */
1402 NTSTATUS wb_cache_query_user_list(struct winbindd_domain *domain,
1403 TALLOC_CTX *mem_ctx,
1404 uint32_t **prids)
1406 struct winbind_cache *cache = get_cache(domain);
1407 struct cache_entry *centry = NULL;
1408 uint32_t num_rids = 0;
1409 uint32_t *rids = NULL;
1410 NTSTATUS status;
1411 unsigned int i, retry;
1412 bool old_status = domain->online;
1414 *prids = NULL;
1416 if (!cache->tdb)
1417 goto do_query;
1419 centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
1420 if (!centry)
1421 goto do_query;
1423 do_fetch_cache:
1424 num_rids = centry_uint32(centry);
1426 if (num_rids == 0) {
1427 goto do_cached;
1430 rids = talloc_array(mem_ctx, uint32_t, num_rids);
1431 if (rids == NULL) {
1432 centry_free(centry);
1433 return NT_STATUS_NO_MEMORY;
1436 for (i=0; i<num_rids; i++) {
1437 rids[i] = centry_uint32(centry);
1440 do_cached:
1441 status = centry->status;
1443 DEBUG(10,("query_user_list: [Cached] - cached list for domain %s status: %s\n",
1444 domain->name, nt_errstr(status) ));
1446 centry_free(centry);
1447 return status;
1449 do_query:
1451 /* Return status value returned by seq number check */
1453 if (!NT_STATUS_IS_OK(domain->last_status))
1454 return domain->last_status;
1456 /* Put the query_user_list() in a retry loop. There appears to be
1457 * some bug either with Windows 2000 or Samba's handling of large
1458 * rpc replies. This manifests itself as sudden disconnection
1459 * at a random point in the enumeration of a large (60k) user list.
1460 * The retry loop simply tries the operation again. )-: It's not
1461 * pretty but an acceptable workaround until we work out what the
1462 * real problem is. */
1464 retry = 0;
1465 do {
1467 DEBUG(10,("query_user_list: [Cached] - doing backend query for list for domain %s\n",
1468 domain->name ));
1470 rids = NULL;
1471 status = domain->backend->query_user_list(domain, mem_ctx,
1472 &rids);
1473 num_rids = talloc_array_length(rids);
1475 if (!NT_STATUS_IS_OK(status)) {
1476 DEBUG(3, ("query_user_list: returned 0x%08x, "
1477 "retrying\n", NT_STATUS_V(status)));
1479 if (NT_STATUS_EQUAL(status, NT_STATUS_UNSUCCESSFUL)) {
1480 DEBUG(3, ("query_user_list: flushing "
1481 "connection cache\n"));
1482 invalidate_cm_connection(domain);
1484 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1485 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1486 if (!domain->internal && old_status) {
1487 set_domain_offline(domain);
1489 /* store partial response. */
1490 if (num_rids > 0) {
1492 * humm, what about the status used for cache?
1493 * Should it be NT_STATUS_OK?
1495 break;
1498 * domain is offline now, and there is no user entries,
1499 * try to fetch from cache again.
1501 if (cache->tdb && !domain->online && !domain->internal && old_status) {
1502 centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
1503 /* partial response... */
1504 if (!centry) {
1505 goto skip_save;
1506 } else {
1507 goto do_fetch_cache;
1509 } else {
1510 goto skip_save;
1514 } while (NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_UNSUCCESSFUL) &&
1515 (retry++ < 5));
1517 /* and save it */
1518 refresh_sequence_number(domain);
1519 if (!NT_STATUS_IS_OK(status)) {
1520 return status;
1522 centry = centry_start(domain, status);
1523 if (!centry)
1524 goto skip_save;
1525 centry_put_uint32(centry, num_rids);
1526 for (i=0; i<num_rids; i++) {
1527 centry_put_uint32(centry, rids[i]);
1529 centry_end(centry, "UL/%s", domain->name);
1530 centry_free(centry);
1532 *prids = rids;
1534 skip_save:
1535 return status;
1538 /* list all domain groups */
1539 NTSTATUS wb_cache_enum_dom_groups(struct winbindd_domain *domain,
1540 TALLOC_CTX *mem_ctx,
1541 uint32_t *num_entries,
1542 struct wb_acct_info **info)
1544 struct winbind_cache *cache = get_cache(domain);
1545 struct cache_entry *centry = NULL;
1546 NTSTATUS status;
1547 unsigned int i;
1548 bool old_status;
1550 old_status = domain->online;
1551 if (!cache->tdb)
1552 goto do_query;
1554 centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
1555 if (!centry)
1556 goto do_query;
1558 do_fetch_cache:
1559 *num_entries = centry_uint32(centry);
1561 if (*num_entries == 0)
1562 goto do_cached;
1564 (*info) = talloc_array(mem_ctx, struct wb_acct_info, *num_entries);
1565 if (! (*info)) {
1566 smb_panic_fn("enum_dom_groups out of memory");
1568 for (i=0; i<(*num_entries); i++) {
1569 (*info)[i].acct_name = centry_string(centry, (*info));
1570 (*info)[i].acct_desc = centry_string(centry, (*info));
1571 (*info)[i].rid = centry_uint32(centry);
1574 do_cached:
1575 status = centry->status;
1577 DEBUG(10,("enum_dom_groups: [Cached] - cached list for domain %s status: %s\n",
1578 domain->name, nt_errstr(status) ));
1580 centry_free(centry);
1581 return status;
1583 do_query:
1584 *num_entries = 0;
1585 *info = NULL;
1587 /* Return status value returned by seq number check */
1589 if (!NT_STATUS_IS_OK(domain->last_status))
1590 return domain->last_status;
1592 DEBUG(10,("enum_dom_groups: [Cached] - doing backend query for list for domain %s\n",
1593 domain->name ));
1595 status = domain->backend->enum_dom_groups(domain, mem_ctx, num_entries, info);
1597 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1598 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1599 if (!domain->internal && old_status) {
1600 set_domain_offline(domain);
1602 if (cache->tdb &&
1603 !domain->online &&
1604 !domain->internal &&
1605 old_status) {
1606 centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
1607 if (centry) {
1608 goto do_fetch_cache;
1612 /* and save it */
1613 refresh_sequence_number(domain);
1614 if (!NT_STATUS_IS_OK(status)) {
1615 return status;
1617 centry = centry_start(domain, status);
1618 if (!centry)
1619 goto skip_save;
1620 centry_put_uint32(centry, *num_entries);
1621 for (i=0; i<(*num_entries); i++) {
1622 centry_put_string(centry, (*info)[i].acct_name);
1623 centry_put_string(centry, (*info)[i].acct_desc);
1624 centry_put_uint32(centry, (*info)[i].rid);
1626 centry_end(centry, "GL/%s/domain", domain->name);
1627 centry_free(centry);
1629 skip_save:
1630 return status;
1633 /* list all domain groups */
1634 NTSTATUS wb_cache_enum_local_groups(struct winbindd_domain *domain,
1635 TALLOC_CTX *mem_ctx,
1636 uint32_t *num_entries,
1637 struct wb_acct_info **info)
1639 struct winbind_cache *cache = get_cache(domain);
1640 struct cache_entry *centry = NULL;
1641 NTSTATUS status;
1642 unsigned int i;
1643 bool old_status;
1645 old_status = domain->online;
1646 if (!cache->tdb)
1647 goto do_query;
1649 centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
1650 if (!centry)
1651 goto do_query;
1653 do_fetch_cache:
1654 *num_entries = centry_uint32(centry);
1656 if (*num_entries == 0)
1657 goto do_cached;
1659 (*info) = talloc_array(mem_ctx, struct wb_acct_info, *num_entries);
1660 if (! (*info)) {
1661 smb_panic_fn("enum_dom_groups out of memory");
1663 for (i=0; i<(*num_entries); i++) {
1664 (*info)[i].acct_name = centry_string(centry, (*info));
1665 (*info)[i].acct_desc = centry_string(centry, (*info));
1666 (*info)[i].rid = centry_uint32(centry);
1669 do_cached:
1671 /* If we are returning cached data and the domain controller
1672 is down then we don't know whether the data is up to date
1673 or not. Return NT_STATUS_MORE_PROCESSING_REQUIRED to
1674 indicate this. */
1676 if (wcache_server_down(domain)) {
1677 DEBUG(10, ("enum_local_groups: returning cached user list and server was down\n"));
1678 status = NT_STATUS_MORE_PROCESSING_REQUIRED;
1679 } else
1680 status = centry->status;
1682 DEBUG(10,("enum_local_groups: [Cached] - cached list for domain %s status: %s\n",
1683 domain->name, nt_errstr(status) ));
1685 centry_free(centry);
1686 return status;
1688 do_query:
1689 *num_entries = 0;
1690 *info = NULL;
1692 /* Return status value returned by seq number check */
1694 if (!NT_STATUS_IS_OK(domain->last_status))
1695 return domain->last_status;
1697 DEBUG(10,("enum_local_groups: [Cached] - doing backend query for list for domain %s\n",
1698 domain->name ));
1700 status = domain->backend->enum_local_groups(domain, mem_ctx, num_entries, info);
1702 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1703 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1704 if (!domain->internal && old_status) {
1705 set_domain_offline(domain);
1707 if (cache->tdb &&
1708 !domain->internal &&
1709 !domain->online &&
1710 old_status) {
1711 centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
1712 if (centry) {
1713 goto do_fetch_cache;
1717 /* and save it */
1718 refresh_sequence_number(domain);
1719 if (!NT_STATUS_IS_OK(status)) {
1720 return status;
1722 centry = centry_start(domain, status);
1723 if (!centry)
1724 goto skip_save;
1725 centry_put_uint32(centry, *num_entries);
1726 for (i=0; i<(*num_entries); i++) {
1727 centry_put_string(centry, (*info)[i].acct_name);
1728 centry_put_string(centry, (*info)[i].acct_desc);
1729 centry_put_uint32(centry, (*info)[i].rid);
1731 centry_end(centry, "GL/%s/local", domain->name);
1732 centry_free(centry);
1734 skip_save:
1735 return status;
1738 struct wcache_name_to_sid_state {
1739 struct dom_sid *sid;
1740 enum lsa_SidType *type;
1741 bool offline;
1742 bool found;
1745 static void wcache_name_to_sid_fn(const struct dom_sid *sid,
1746 enum lsa_SidType type,
1747 bool expired,
1748 void *private_data)
1750 struct wcache_name_to_sid_state *state = private_data;
1752 *state->sid = *sid;
1753 *state->type = type;
1754 state->found = (!expired || state->offline);
1757 static NTSTATUS wcache_name_to_sid(struct winbindd_domain *domain,
1758 const char *domain_name,
1759 const char *name,
1760 struct dom_sid *sid,
1761 enum lsa_SidType *type)
1763 struct wcache_name_to_sid_state state = {
1764 .sid = sid, .type = type, .found = false,
1765 .offline = is_domain_offline(domain),
1767 bool ok;
1769 ok = namemap_cache_find_name(domain_name, name, wcache_name_to_sid_fn,
1770 &state);
1771 if (!ok) {
1772 DBG_DEBUG("namemap_cache_find_name failed\n");
1773 return NT_STATUS_NOT_FOUND;
1775 if (!state.found) {
1776 DBG_DEBUG("cache entry not found\n");
1777 return NT_STATUS_NOT_FOUND;
1779 if (*type == SID_NAME_UNKNOWN) {
1780 return NT_STATUS_NONE_MAPPED;
1783 return NT_STATUS_OK;
1786 /* convert a single name to a sid in a domain */
1787 NTSTATUS wb_cache_name_to_sid(struct winbindd_domain *domain,
1788 TALLOC_CTX *mem_ctx,
1789 const char *domain_name,
1790 const char *name,
1791 uint32_t flags,
1792 struct dom_sid *sid,
1793 enum lsa_SidType *type)
1795 NTSTATUS status;
1796 bool old_status;
1797 const char *dom_name;
1799 old_status = domain->online;
1801 status = wcache_name_to_sid(domain, domain_name, name, sid, type);
1802 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
1803 return status;
1806 ZERO_STRUCTP(sid);
1808 /* If the seq number check indicated that there is a problem
1809 * with this DC, then return that status... except for
1810 * access_denied. This is special because the dc may be in
1811 * "restrict anonymous = 1" mode, in which case it will deny
1812 * most unauthenticated operations, but *will* allow the LSA
1813 * name-to-sid that we try as a fallback. */
1815 if (!(NT_STATUS_IS_OK(domain->last_status)
1816 || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1817 return domain->last_status;
1819 DEBUG(10,("name_to_sid: [Cached] - doing backend query for name for domain %s\n",
1820 domain->name ));
1822 winbindd_domain_init_backend(domain);
1823 status = domain->backend->name_to_sid(domain, mem_ctx, domain_name,
1824 name, flags, &dom_name, sid, type);
1826 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1827 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1828 if (!domain->internal && old_status) {
1829 set_domain_offline(domain);
1831 if (!domain->internal &&
1832 !domain->online &&
1833 old_status) {
1834 NTSTATUS cache_status;
1835 cache_status = wcache_name_to_sid(domain, domain_name, name, sid, type);
1836 return cache_status;
1839 /* and save it */
1841 if (domain->online &&
1842 (NT_STATUS_IS_OK(status) || NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED))) {
1843 enum lsa_SidType save_type = *type;
1845 if (NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) {
1846 save_type = SID_NAME_UNKNOWN;
1849 wcache_save_name_to_sid(domain, status, domain_name, name, sid,
1850 save_type);
1852 /* Only save the reverse mapping if this was not a UPN */
1853 if (!strchr(name, '@')) {
1854 if (!strupper_m(discard_const_p(char, domain_name))) {
1855 return NT_STATUS_INVALID_PARAMETER;
1857 (void)strlower_m(discard_const_p(char, name));
1858 wcache_save_sid_to_name(domain, status, sid,
1859 dom_name, name, save_type);
1863 return status;
1866 struct wcache_sid_to_name_state {
1867 TALLOC_CTX *mem_ctx;
1868 char **domain_name;
1869 char **name;
1870 enum lsa_SidType *type;
1871 bool offline;
1872 bool found;
1875 static void wcache_sid_to_name_fn(const char *domain,
1876 const char *name,
1877 enum lsa_SidType type,
1878 bool expired,
1879 void *private_data)
1881 struct wcache_sid_to_name_state *state = private_data;
1883 *state->domain_name = talloc_strdup(state->mem_ctx, domain);
1884 if (*state->domain_name == NULL) {
1885 return;
1887 *state->name = talloc_strdup(state->mem_ctx, name);
1888 if (*state->name == NULL) {
1889 return;
1891 *state->type = type;
1892 state->found = (!expired || state->offline);
1895 static NTSTATUS wcache_sid_to_name(struct winbindd_domain *domain,
1896 const struct dom_sid *sid,
1897 TALLOC_CTX *mem_ctx,
1898 char **domain_name,
1899 char **name,
1900 enum lsa_SidType *type)
1902 struct wcache_sid_to_name_state state = {
1903 .mem_ctx = mem_ctx, .found = false,
1904 .domain_name = domain_name, .name = name, .type = type,
1905 .offline = is_domain_offline(domain)
1907 bool ok;
1909 ok = namemap_cache_find_sid(sid, wcache_sid_to_name_fn, &state);
1910 if (!ok) {
1911 DBG_DEBUG("namemap_cache_find_name failed\n");
1912 return NT_STATUS_NOT_FOUND;
1914 if (!state.found) {
1915 DBG_DEBUG("cache entry not found\n");
1916 return NT_STATUS_NOT_FOUND;
1918 if (*type == SID_NAME_UNKNOWN) {
1919 return NT_STATUS_NONE_MAPPED;
1922 return NT_STATUS_OK;
1925 /* convert a sid to a user or group name. The sid is guaranteed to be in the domain
1926 given */
1927 NTSTATUS wb_cache_sid_to_name(struct winbindd_domain *domain,
1928 TALLOC_CTX *mem_ctx,
1929 const struct dom_sid *sid,
1930 char **domain_name,
1931 char **name,
1932 enum lsa_SidType *type)
1934 NTSTATUS status;
1935 bool old_status;
1937 old_status = domain->online;
1938 status = wcache_sid_to_name(domain, sid, mem_ctx, domain_name, name,
1939 type);
1940 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
1941 return status;
1944 *name = NULL;
1945 *domain_name = NULL;
1947 /* If the seq number check indicated that there is a problem
1948 * with this DC, then return that status... except for
1949 * access_denied. This is special because the dc may be in
1950 * "restrict anonymous = 1" mode, in which case it will deny
1951 * most unauthenticated operations, but *will* allow the LSA
1952 * sid-to-name that we try as a fallback. */
1954 if (!(NT_STATUS_IS_OK(domain->last_status)
1955 || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1956 return domain->last_status;
1958 DEBUG(10,("sid_to_name: [Cached] - doing backend query for name for domain %s\n",
1959 domain->name ));
1961 winbindd_domain_init_backend(domain);
1963 status = domain->backend->sid_to_name(domain, mem_ctx, sid, domain_name, name, type);
1965 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1966 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1967 if (!domain->internal && old_status) {
1968 set_domain_offline(domain);
1970 if (!domain->internal &&
1971 !domain->online &&
1972 old_status) {
1973 NTSTATUS cache_status;
1974 cache_status = wcache_sid_to_name(domain, sid, mem_ctx,
1975 domain_name, name, type);
1976 return cache_status;
1979 /* and save it */
1980 if (!NT_STATUS_IS_OK(status)) {
1981 return status;
1983 wcache_save_sid_to_name(domain, status, sid, *domain_name, *name, *type);
1985 /* We can't save the name to sid mapping here, as with sid history a
1986 * later name2sid would give the wrong sid. */
1988 return status;
1991 NTSTATUS wb_cache_rids_to_names(struct winbindd_domain *domain,
1992 TALLOC_CTX *mem_ctx,
1993 const struct dom_sid *domain_sid,
1994 uint32_t *rids,
1995 size_t num_rids,
1996 char **domain_name,
1997 char ***names,
1998 enum lsa_SidType **types)
2000 struct winbind_cache *cache = get_cache(domain);
2001 size_t i;
2002 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
2003 bool have_mapped;
2004 bool have_unmapped;
2005 bool old_status;
2007 old_status = domain->online;
2008 *domain_name = NULL;
2009 *names = NULL;
2010 *types = NULL;
2012 if (!cache->tdb) {
2013 goto do_query;
2016 if (num_rids == 0) {
2017 return NT_STATUS_OK;
2020 *names = talloc_array(mem_ctx, char *, num_rids);
2021 *types = talloc_array(mem_ctx, enum lsa_SidType, num_rids);
2023 if ((*names == NULL) || (*types == NULL)) {
2024 result = NT_STATUS_NO_MEMORY;
2025 goto error;
2028 have_mapped = have_unmapped = false;
2030 for (i=0; i<num_rids; i++) {
2031 struct dom_sid sid;
2032 NTSTATUS status;
2033 enum lsa_SidType type;
2034 char *dom, *name;
2036 if (!sid_compose(&sid, domain_sid, rids[i])) {
2037 result = NT_STATUS_INTERNAL_ERROR;
2038 goto error;
2041 status = wcache_sid_to_name(domain, &sid, *names, &dom,
2042 &name, &type);
2044 (*types)[i] = SID_NAME_UNKNOWN;
2045 (*names)[i] = talloc_strdup(*names, "");
2047 if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2048 /* not cached */
2049 goto do_query;
2052 if (NT_STATUS_IS_OK(status)) {
2053 have_mapped = true;
2054 (*types)[i] = type;
2056 if (*domain_name == NULL) {
2057 *domain_name = dom;
2058 } else {
2059 TALLOC_FREE(dom);
2062 (*names)[i] = name;
2064 } else if (NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) {
2065 have_unmapped = true;
2066 } else {
2067 /* something's definitely wrong */
2068 result = status;
2069 goto error;
2073 if (!have_mapped) {
2074 return NT_STATUS_NONE_MAPPED;
2076 if (!have_unmapped) {
2077 return NT_STATUS_OK;
2079 return STATUS_SOME_UNMAPPED;
2081 do_query:
2083 TALLOC_FREE(*names);
2084 TALLOC_FREE(*types);
2086 result = domain->backend->rids_to_names(domain, mem_ctx, domain_sid,
2087 rids, num_rids, domain_name,
2088 names, types);
2090 if (NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
2091 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2092 if (!domain->internal && old_status) {
2093 set_domain_offline(domain);
2095 if (cache->tdb &&
2096 !domain->internal &&
2097 !domain->online &&
2098 old_status) {
2099 have_mapped = have_unmapped = false;
2101 *names = talloc_array(mem_ctx, char *, num_rids);
2102 if (*names == NULL) {
2103 result = NT_STATUS_NO_MEMORY;
2104 goto error;
2107 *types = talloc_array(mem_ctx, enum lsa_SidType,
2108 num_rids);
2109 if (*types == NULL) {
2110 result = NT_STATUS_NO_MEMORY;
2111 goto error;
2114 for (i=0; i<num_rids; i++) {
2115 struct dom_sid sid;
2116 NTSTATUS status;
2117 enum lsa_SidType type;
2118 char *dom, *name;
2120 if (!sid_compose(&sid, domain_sid, rids[i])) {
2121 result = NT_STATUS_INTERNAL_ERROR;
2122 goto error;
2125 status = wcache_sid_to_name(domain, &sid,
2126 *names, &dom,
2127 &name, &type);
2129 (*types)[i] = SID_NAME_UNKNOWN;
2130 (*names)[i] = talloc_strdup(*names, "");
2132 if (NT_STATUS_IS_OK(status)) {
2133 have_mapped = true;
2134 (*types)[i] = type;
2136 if (*domain_name == NULL) {
2137 *domain_name = dom;
2138 } else {
2139 TALLOC_FREE(dom);
2142 (*names)[i] = name;
2144 } else if (NT_STATUS_EQUAL(
2145 status,
2146 NT_STATUS_NONE_MAPPED)) {
2147 have_unmapped = true;
2148 } else {
2149 /* something's definitely wrong */
2150 result = status;
2151 goto error;
2155 if (!have_mapped) {
2156 return NT_STATUS_NONE_MAPPED;
2158 if (!have_unmapped) {
2159 return NT_STATUS_OK;
2161 return STATUS_SOME_UNMAPPED;
2165 None of the queried rids has been found so save all negative entries
2167 if (NT_STATUS_EQUAL(result, NT_STATUS_NONE_MAPPED)) {
2168 for (i = 0; i < num_rids; i++) {
2169 struct dom_sid sid;
2170 const char *name = "";
2171 const enum lsa_SidType type = SID_NAME_UNKNOWN;
2172 NTSTATUS status = NT_STATUS_NONE_MAPPED;
2174 if (!sid_compose(&sid, domain_sid, rids[i])) {
2175 return NT_STATUS_INTERNAL_ERROR;
2178 wcache_save_sid_to_name(domain, status, &sid, *domain_name,
2179 name, type);
2182 return result;
2186 Some or all of the queried rids have been found.
2188 if (!NT_STATUS_IS_OK(result) &&
2189 !NT_STATUS_EQUAL(result, STATUS_SOME_UNMAPPED)) {
2190 return result;
2193 refresh_sequence_number(domain);
2195 for (i=0; i<num_rids; i++) {
2196 struct dom_sid sid;
2197 NTSTATUS status;
2199 if (!sid_compose(&sid, domain_sid, rids[i])) {
2200 result = NT_STATUS_INTERNAL_ERROR;
2201 goto error;
2204 status = (*types)[i] == SID_NAME_UNKNOWN ?
2205 NT_STATUS_NONE_MAPPED : NT_STATUS_OK;
2207 wcache_save_sid_to_name(domain, status, &sid, *domain_name,
2208 (*names)[i], (*types)[i]);
2211 return result;
2213 error:
2214 TALLOC_FREE(*names);
2215 TALLOC_FREE(*types);
2216 return result;
2219 static NTSTATUS wcache_query_user(struct winbindd_domain *domain,
2220 TALLOC_CTX *mem_ctx,
2221 const struct dom_sid *user_sid,
2222 struct wbint_userinfo *info)
2224 struct winbind_cache *cache = get_cache(domain);
2225 struct cache_entry *centry = NULL;
2226 NTSTATUS status;
2227 struct dom_sid_buf sid_string;
2229 if (cache->tdb == NULL) {
2230 return NT_STATUS_NOT_FOUND;
2233 centry = wcache_fetch(
2234 cache, domain, "U/%s", dom_sid_str_buf(user_sid, &sid_string));
2235 if (centry == NULL) {
2236 return NT_STATUS_NOT_FOUND;
2240 * If we have an access denied cache entry and a cached info3
2241 * in the samlogon cache then do a query. This will force the
2242 * rpc back end to return the info3 data.
2245 if (NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED) &&
2246 netsamlogon_cache_have(user_sid)) {
2247 DEBUG(10, ("query_user: cached access denied and have cached "
2248 "info3\n"));
2249 domain->last_status = NT_STATUS_OK;
2250 centry_free(centry);
2251 return NT_STATUS_NOT_FOUND;
2254 /* if status is not ok then this is a negative hit
2255 and the rest of the data doesn't matter */
2256 status = centry->status;
2257 if (NT_STATUS_IS_OK(status)) {
2258 info->domain_name = centry_string(centry, mem_ctx);
2259 info->acct_name = centry_string(centry, mem_ctx);
2260 info->full_name = centry_string(centry, mem_ctx);
2261 info->homedir = centry_string(centry, mem_ctx);
2262 info->shell = centry_string(centry, mem_ctx);
2263 info->uid = centry_uint32(centry);
2264 info->primary_gid = centry_uint32(centry);
2265 info->primary_group_name = centry_string(centry, mem_ctx);
2266 centry_sid(centry, &info->user_sid);
2267 centry_sid(centry, &info->group_sid);
2270 DEBUG(10,("query_user: [Cached] - cached info for domain %s status: "
2271 "%s\n", domain->name, nt_errstr(status) ));
2273 centry_free(centry);
2274 return status;
2279 * @brief Query a fullname from the username cache (for further gecos processing)
2281 * @param domain A pointer to the winbindd_domain struct.
2282 * @param mem_ctx The talloc context.
2283 * @param user_sid The user sid.
2284 * @param full_name A pointer to the full_name string.
2286 * @return NTSTATUS code
2288 NTSTATUS wcache_query_user_fullname(struct winbindd_domain *domain,
2289 TALLOC_CTX *mem_ctx,
2290 const struct dom_sid *user_sid,
2291 const char **full_name)
2293 NTSTATUS status;
2294 struct wbint_userinfo info;
2296 status = wcache_query_user(domain, mem_ctx, user_sid, &info);
2297 if (!NT_STATUS_IS_OK(status)) {
2298 return status;
2301 if (info.full_name != NULL) {
2302 *full_name = talloc_strdup(mem_ctx, info.full_name);
2303 if (*full_name == NULL) {
2304 return NT_STATUS_NO_MEMORY;
2308 return NT_STATUS_OK;
2311 static NTSTATUS wcache_lookup_usergroups(struct winbindd_domain *domain,
2312 TALLOC_CTX *mem_ctx,
2313 const struct dom_sid *user_sid,
2314 uint32_t *pnum_sids,
2315 struct dom_sid **psids)
2317 struct winbind_cache *cache = get_cache(domain);
2318 struct cache_entry *centry = NULL;
2319 NTSTATUS status;
2320 uint32_t i, num_sids;
2321 struct dom_sid *sids;
2322 struct dom_sid_buf sid_string;
2324 if (cache->tdb == NULL) {
2325 return NT_STATUS_NOT_FOUND;
2328 centry = wcache_fetch(
2329 cache,
2330 domain,
2331 "UG/%s",
2332 dom_sid_str_buf(user_sid, &sid_string));
2333 if (centry == NULL) {
2334 return NT_STATUS_NOT_FOUND;
2337 /* If we have an access denied cache entry and a cached info3 in the
2338 samlogon cache then do a query. This will force the rpc back end
2339 to return the info3 data. */
2341 if (NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)
2342 && netsamlogon_cache_have(user_sid)) {
2343 DEBUG(10, ("lookup_usergroups: cached access denied and have "
2344 "cached info3\n"));
2345 domain->last_status = NT_STATUS_OK;
2346 centry_free(centry);
2347 return NT_STATUS_NOT_FOUND;
2350 num_sids = centry_uint32(centry);
2351 sids = talloc_array(mem_ctx, struct dom_sid, num_sids);
2352 if (sids == NULL) {
2353 centry_free(centry);
2354 return NT_STATUS_NO_MEMORY;
2357 for (i=0; i<num_sids; i++) {
2358 centry_sid(centry, &sids[i]);
2361 status = centry->status;
2363 DEBUG(10,("lookup_usergroups: [Cached] - cached info for domain %s "
2364 "status: %s\n", domain->name, nt_errstr(status)));
2366 centry_free(centry);
2368 *pnum_sids = num_sids;
2369 *psids = sids;
2370 return status;
2373 /* Lookup groups a user is a member of. */
2374 NTSTATUS wb_cache_lookup_usergroups(struct winbindd_domain *domain,
2375 TALLOC_CTX *mem_ctx,
2376 const struct dom_sid *user_sid,
2377 uint32_t *num_groups,
2378 struct dom_sid **user_gids)
2380 struct cache_entry *centry = NULL;
2381 NTSTATUS status;
2382 unsigned int i;
2383 struct dom_sid_buf sid_string;
2384 bool old_status;
2386 old_status = domain->online;
2387 status = wcache_lookup_usergroups(domain, mem_ctx, user_sid,
2388 num_groups, user_gids);
2389 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2390 return status;
2393 (*num_groups) = 0;
2394 (*user_gids) = NULL;
2396 /* Return status value returned by seq number check */
2398 if (!NT_STATUS_IS_OK(domain->last_status))
2399 return domain->last_status;
2401 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info for domain %s\n",
2402 domain->name ));
2404 status = domain->backend->lookup_usergroups(domain, mem_ctx, user_sid, num_groups, user_gids);
2406 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2407 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2408 if (!domain->internal && old_status) {
2409 set_domain_offline(domain);
2411 if (!domain->internal &&
2412 !domain->online &&
2413 old_status) {
2414 NTSTATUS cache_status;
2415 cache_status = wcache_lookup_usergroups(domain, mem_ctx, user_sid,
2416 num_groups, user_gids);
2417 return cache_status;
2420 if ( NT_STATUS_EQUAL(status, NT_STATUS_SYNCHRONIZATION_REQUIRED) )
2421 goto skip_save;
2423 /* and save it */
2424 refresh_sequence_number(domain);
2425 if (!NT_STATUS_IS_OK(status)) {
2426 return status;
2428 centry = centry_start(domain, status);
2429 if (!centry)
2430 goto skip_save;
2432 centry_put_uint32(centry, *num_groups);
2433 for (i=0; i<(*num_groups); i++) {
2434 centry_put_sid(centry, &(*user_gids)[i]);
2437 centry_end(centry, "UG/%s", dom_sid_str_buf(user_sid, &sid_string));
2438 centry_free(centry);
2440 skip_save:
2441 return status;
2444 static char *wcache_make_sidlist(TALLOC_CTX *mem_ctx, uint32_t num_sids,
2445 const struct dom_sid *sids)
2447 uint32_t i;
2448 char *sidlist;
2450 sidlist = talloc_strdup(mem_ctx, "");
2451 if (sidlist == NULL) {
2452 return NULL;
2454 for (i=0; i<num_sids; i++) {
2455 struct dom_sid_buf tmp;
2456 sidlist = talloc_asprintf_append_buffer(
2457 sidlist,
2458 "/%s",
2459 dom_sid_str_buf(&sids[i], &tmp));
2460 if (sidlist == NULL) {
2461 return NULL;
2464 return sidlist;
2467 static NTSTATUS wcache_lookup_useraliases(struct winbindd_domain *domain,
2468 TALLOC_CTX *mem_ctx,
2469 uint32_t num_sids,
2470 const struct dom_sid *sids,
2471 uint32_t *pnum_aliases,
2472 uint32_t **paliases)
2474 struct winbind_cache *cache = get_cache(domain);
2475 struct cache_entry *centry = NULL;
2476 uint32_t i, num_aliases;
2477 uint32_t *aliases;
2478 NTSTATUS status;
2479 char *sidlist;
2481 if (cache->tdb == NULL) {
2482 return NT_STATUS_NOT_FOUND;
2485 if (num_sids == 0) {
2486 *pnum_aliases = 0;
2487 *paliases = NULL;
2488 return NT_STATUS_OK;
2491 /* We need to cache indexed by the whole list of SIDs, the aliases
2492 * resulting might come from any of the SIDs. */
2494 sidlist = wcache_make_sidlist(talloc_tos(), num_sids, sids);
2495 if (sidlist == NULL) {
2496 return NT_STATUS_NO_MEMORY;
2499 centry = wcache_fetch(cache, domain, "UA%s", sidlist);
2500 TALLOC_FREE(sidlist);
2501 if (centry == NULL) {
2502 return NT_STATUS_NOT_FOUND;
2505 num_aliases = centry_uint32(centry);
2506 aliases = talloc_array(mem_ctx, uint32_t, num_aliases);
2507 if (aliases == NULL) {
2508 centry_free(centry);
2509 return NT_STATUS_NO_MEMORY;
2512 for (i=0; i<num_aliases; i++) {
2513 aliases[i] = centry_uint32(centry);
2516 status = centry->status;
2518 DEBUG(10,("lookup_useraliases: [Cached] - cached info for domain: %s "
2519 "status %s\n", domain->name, nt_errstr(status)));
2521 centry_free(centry);
2523 *pnum_aliases = num_aliases;
2524 *paliases = aliases;
2526 return status;
2529 NTSTATUS wb_cache_lookup_useraliases(struct winbindd_domain *domain,
2530 TALLOC_CTX *mem_ctx,
2531 uint32_t num_sids,
2532 const struct dom_sid *sids,
2533 uint32_t *num_aliases,
2534 uint32_t **alias_rids)
2536 struct cache_entry *centry = NULL;
2537 NTSTATUS status;
2538 char *sidlist;
2539 uint32_t i;
2540 bool old_status;
2542 old_status = domain->online;
2543 status = wcache_lookup_useraliases(domain, mem_ctx, num_sids, sids,
2544 num_aliases, alias_rids);
2545 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2546 return status;
2549 (*num_aliases) = 0;
2550 (*alias_rids) = NULL;
2552 if (!NT_STATUS_IS_OK(domain->last_status))
2553 return domain->last_status;
2555 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info "
2556 "for domain %s\n", domain->name ));
2558 sidlist = wcache_make_sidlist(talloc_tos(), num_sids, sids);
2559 if (sidlist == NULL) {
2560 return NT_STATUS_NO_MEMORY;
2563 status = domain->backend->lookup_useraliases(domain, mem_ctx,
2564 num_sids, sids,
2565 num_aliases, alias_rids);
2567 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2568 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2569 if (!domain->internal && old_status) {
2570 set_domain_offline(domain);
2572 if (!domain->internal &&
2573 !domain->online &&
2574 old_status) {
2575 NTSTATUS cache_status;
2576 cache_status = wcache_lookup_useraliases(domain, mem_ctx, num_sids,
2577 sids, num_aliases, alias_rids);
2578 return cache_status;
2581 /* and save it */
2582 refresh_sequence_number(domain);
2583 if (!NT_STATUS_IS_OK(status)) {
2584 return status;
2586 centry = centry_start(domain, status);
2587 if (!centry)
2588 goto skip_save;
2589 centry_put_uint32(centry, *num_aliases);
2590 for (i=0; i<(*num_aliases); i++)
2591 centry_put_uint32(centry, (*alias_rids)[i]);
2592 centry_end(centry, "UA%s", sidlist);
2593 centry_free(centry);
2595 skip_save:
2596 return status;
2599 static NTSTATUS wcache_lookup_groupmem(struct winbindd_domain *domain,
2600 TALLOC_CTX *mem_ctx,
2601 const struct dom_sid *group_sid,
2602 uint32_t *num_names,
2603 struct dom_sid **sid_mem, char ***names,
2604 uint32_t **name_types)
2606 struct winbind_cache *cache = get_cache(domain);
2607 struct cache_entry *centry = NULL;
2608 NTSTATUS status;
2609 unsigned int i;
2610 struct dom_sid_buf sid_string;
2612 if (cache->tdb == NULL) {
2613 return NT_STATUS_NOT_FOUND;
2616 centry = wcache_fetch(
2617 cache,
2618 domain,
2619 "GM/%s",
2620 dom_sid_str_buf(group_sid, &sid_string));
2621 if (centry == NULL) {
2622 return NT_STATUS_NOT_FOUND;
2625 *sid_mem = NULL;
2626 *names = NULL;
2627 *name_types = NULL;
2629 *num_names = centry_uint32(centry);
2630 if (*num_names == 0) {
2631 centry_free(centry);
2632 return NT_STATUS_OK;
2635 *sid_mem = talloc_array(mem_ctx, struct dom_sid, *num_names);
2636 *names = talloc_array(mem_ctx, char *, *num_names);
2637 *name_types = talloc_array(mem_ctx, uint32_t, *num_names);
2639 if ((*sid_mem == NULL) || (*names == NULL) || (*name_types == NULL)) {
2640 TALLOC_FREE(*sid_mem);
2641 TALLOC_FREE(*names);
2642 TALLOC_FREE(*name_types);
2643 centry_free(centry);
2644 return NT_STATUS_NO_MEMORY;
2647 for (i=0; i<(*num_names); i++) {
2648 centry_sid(centry, &(*sid_mem)[i]);
2649 (*names)[i] = centry_string(centry, mem_ctx);
2650 (*name_types)[i] = centry_uint32(centry);
2653 status = centry->status;
2655 DEBUG(10,("lookup_groupmem: [Cached] - cached info for domain %s "
2656 "status: %s\n", domain->name, nt_errstr(status)));
2658 centry_free(centry);
2659 return status;
2662 NTSTATUS wb_cache_lookup_groupmem(struct winbindd_domain *domain,
2663 TALLOC_CTX *mem_ctx,
2664 const struct dom_sid *group_sid,
2665 enum lsa_SidType type,
2666 uint32_t *num_names,
2667 struct dom_sid **sid_mem,
2668 char ***names,
2669 uint32_t **name_types)
2671 struct cache_entry *centry = NULL;
2672 NTSTATUS status;
2673 unsigned int i;
2674 struct dom_sid_buf sid_string;
2675 bool old_status;
2677 old_status = domain->online;
2678 status = wcache_lookup_groupmem(domain, mem_ctx, group_sid, num_names,
2679 sid_mem, names, name_types);
2680 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2681 return status;
2684 (*num_names) = 0;
2685 (*sid_mem) = NULL;
2686 (*names) = NULL;
2687 (*name_types) = NULL;
2689 /* Return status value returned by seq number check */
2691 if (!NT_STATUS_IS_OK(domain->last_status))
2692 return domain->last_status;
2694 DEBUG(10,("lookup_groupmem: [Cached] - doing backend query for info for domain %s\n",
2695 domain->name ));
2697 status = domain->backend->lookup_groupmem(domain, mem_ctx, group_sid,
2698 type, num_names,
2699 sid_mem, names, name_types);
2701 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2702 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2703 if (!domain->internal && old_status) {
2704 set_domain_offline(domain);
2706 if (!domain->internal &&
2707 !domain->online &&
2708 old_status) {
2709 NTSTATUS cache_status;
2710 cache_status = wcache_lookup_groupmem(domain, mem_ctx, group_sid,
2711 num_names, sid_mem, names,
2712 name_types);
2713 return cache_status;
2716 /* and save it */
2717 refresh_sequence_number(domain);
2718 if (!NT_STATUS_IS_OK(status)) {
2719 return status;
2721 centry = centry_start(domain, status);
2722 if (!centry)
2723 goto skip_save;
2724 centry_put_uint32(centry, *num_names);
2725 for (i=0; i<(*num_names); i++) {
2726 centry_put_sid(centry, &(*sid_mem)[i]);
2727 centry_put_string(centry, (*names)[i]);
2728 centry_put_uint32(centry, (*name_types)[i]);
2730 centry_end(centry,
2731 "GM/%s",
2732 dom_sid_str_buf(group_sid, &sid_string));
2733 centry_free(centry);
2735 skip_save:
2736 return status;
2739 /* find the sequence number for a domain */
2740 NTSTATUS wb_cache_sequence_number(struct winbindd_domain *domain,
2741 uint32_t *seq)
2743 refresh_sequence_number(domain);
2745 *seq = domain->sequence_number;
2747 return NT_STATUS_OK;
2750 /* enumerate trusted domains
2751 * (we need to have the list of trustdoms in the cache when we go offline) -
2752 * Guenther */
2753 NTSTATUS wb_cache_trusted_domains(struct winbindd_domain *domain,
2754 TALLOC_CTX *mem_ctx,
2755 struct netr_DomainTrustList *trusts)
2757 NTSTATUS status;
2758 struct winbind_cache *cache;
2759 struct winbindd_tdc_domain *dom_list = NULL;
2760 size_t num_domains = 0;
2761 bool retval = false;
2762 size_t i;
2763 bool old_status;
2765 old_status = domain->online;
2766 trusts->count = 0;
2767 trusts->array = NULL;
2769 cache = get_cache(domain);
2770 if (!cache || !cache->tdb) {
2771 goto do_query;
2774 if (domain->online) {
2775 goto do_query;
2778 retval = wcache_tdc_fetch_list(&dom_list, &num_domains);
2779 if (!retval || !num_domains || !dom_list) {
2780 TALLOC_FREE(dom_list);
2781 goto do_query;
2784 do_fetch_cache:
2785 trusts->array = talloc_zero_array(mem_ctx, struct netr_DomainTrust, num_domains);
2786 if (!trusts->array) {
2787 TALLOC_FREE(dom_list);
2788 return NT_STATUS_NO_MEMORY;
2791 for (i = 0; i < num_domains; i++) {
2792 struct netr_DomainTrust *trust;
2793 struct dom_sid *sid;
2794 struct winbindd_domain *dom;
2796 dom = find_domain_from_name_noinit(dom_list[i].domain_name);
2797 if (dom && dom->internal) {
2798 continue;
2801 trust = &trusts->array[trusts->count];
2802 trust->netbios_name = talloc_strdup(trusts->array, dom_list[i].domain_name);
2803 trust->dns_name = talloc_strdup(trusts->array, dom_list[i].dns_name);
2804 sid = talloc(trusts->array, struct dom_sid);
2805 if (!trust->netbios_name || !trust->dns_name ||
2806 !sid) {
2807 TALLOC_FREE(dom_list);
2808 TALLOC_FREE(trusts->array);
2809 return NT_STATUS_NO_MEMORY;
2812 trust->trust_flags = dom_list[i].trust_flags;
2813 trust->trust_attributes = dom_list[i].trust_attribs;
2814 trust->trust_type = dom_list[i].trust_type;
2815 sid_copy(sid, &dom_list[i].sid);
2816 trust->sid = sid;
2817 trusts->count++;
2820 TALLOC_FREE(dom_list);
2821 return NT_STATUS_OK;
2823 do_query:
2824 /* Return status value returned by seq number check */
2826 if (!NT_STATUS_IS_OK(domain->last_status))
2827 return domain->last_status;
2829 DEBUG(10,("trusted_domains: [Cached] - doing backend query for info for domain %s\n",
2830 domain->name ));
2832 status = domain->backend->trusted_domains(domain, mem_ctx, trusts);
2834 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2835 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2836 if (!domain->internal && old_status) {
2837 set_domain_offline(domain);
2839 if (!domain->internal &&
2840 !domain->online &&
2841 old_status) {
2842 retval = wcache_tdc_fetch_list(&dom_list, &num_domains);
2843 if (retval && num_domains && dom_list) {
2844 TALLOC_FREE(trusts->array);
2845 trusts->count = 0;
2846 goto do_fetch_cache;
2850 /* no trusts gives NT_STATUS_NO_MORE_ENTRIES resetting to NT_STATUS_OK
2851 * so that the generic centry handling still applies correctly -
2852 * Guenther*/
2854 if (!NT_STATUS_IS_ERR(status)) {
2855 status = NT_STATUS_OK;
2857 return status;
2860 /* get lockout policy */
2861 NTSTATUS wb_cache_lockout_policy(struct winbindd_domain *domain,
2862 TALLOC_CTX *mem_ctx,
2863 struct samr_DomInfo12 *policy)
2865 struct winbind_cache *cache = get_cache(domain);
2866 struct cache_entry *centry = NULL;
2867 NTSTATUS status;
2868 bool old_status;
2870 old_status = domain->online;
2871 if (!cache->tdb)
2872 goto do_query;
2874 centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
2876 if (!centry)
2877 goto do_query;
2879 do_fetch_cache:
2880 policy->lockout_duration = centry_nttime(centry);
2881 policy->lockout_window = centry_nttime(centry);
2882 policy->lockout_threshold = centry_uint16(centry);
2884 status = centry->status;
2886 DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2887 domain->name, nt_errstr(status) ));
2889 centry_free(centry);
2890 return status;
2892 do_query:
2893 ZERO_STRUCTP(policy);
2895 /* Return status value returned by seq number check */
2897 if (!NT_STATUS_IS_OK(domain->last_status))
2898 return domain->last_status;
2900 DEBUG(10,("lockout_policy: [Cached] - doing backend query for info for domain %s\n",
2901 domain->name ));
2903 status = domain->backend->lockout_policy(domain, mem_ctx, policy);
2905 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2906 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2907 if (!domain->internal && old_status) {
2908 set_domain_offline(domain);
2910 if (cache->tdb &&
2911 !domain->internal &&
2912 !domain->online &&
2913 old_status) {
2914 centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
2915 if (centry) {
2916 goto do_fetch_cache;
2920 /* and save it */
2921 refresh_sequence_number(domain);
2922 if (!NT_STATUS_IS_OK(status)) {
2923 return status;
2925 wcache_save_lockout_policy(domain, status, policy);
2927 return status;
2930 /* get password policy */
2931 NTSTATUS wb_cache_password_policy(struct winbindd_domain *domain,
2932 TALLOC_CTX *mem_ctx,
2933 struct samr_DomInfo1 *policy)
2935 struct winbind_cache *cache = get_cache(domain);
2936 struct cache_entry *centry = NULL;
2937 NTSTATUS status;
2938 bool old_status;
2940 old_status = domain->online;
2941 if (!cache->tdb)
2942 goto do_query;
2944 centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
2946 if (!centry)
2947 goto do_query;
2949 do_fetch_cache:
2950 policy->min_password_length = centry_uint16(centry);
2951 policy->password_history_length = centry_uint16(centry);
2952 policy->password_properties = centry_uint32(centry);
2953 policy->max_password_age = centry_nttime(centry);
2954 policy->min_password_age = centry_nttime(centry);
2956 status = centry->status;
2958 DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2959 domain->name, nt_errstr(status) ));
2961 centry_free(centry);
2962 return status;
2964 do_query:
2965 ZERO_STRUCTP(policy);
2967 /* Return status value returned by seq number check */
2969 if (!NT_STATUS_IS_OK(domain->last_status))
2970 return domain->last_status;
2972 DEBUG(10,("password_policy: [Cached] - doing backend query for info for domain %s\n",
2973 domain->name ));
2975 status = domain->backend->password_policy(domain, mem_ctx, policy);
2977 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2978 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2979 if (!domain->internal && old_status) {
2980 set_domain_offline(domain);
2982 if (cache->tdb &&
2983 !domain->internal &&
2984 !domain->online &&
2985 old_status) {
2986 centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
2987 if (centry) {
2988 goto do_fetch_cache;
2992 /* and save it */
2993 refresh_sequence_number(domain);
2994 if (!NT_STATUS_IS_OK(status)) {
2995 return status;
2997 wcache_save_password_policy(domain, status, policy);
2999 return status;
3003 /* Invalidate cached user and group lists coherently */
3005 static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
3006 void *state)
3008 if (strncmp((const char *)kbuf.dptr, "UL/", 3) == 0 ||
3009 strncmp((const char *)kbuf.dptr, "GL/", 3) == 0)
3010 tdb_delete(the_tdb, kbuf);
3012 return 0;
3015 /* Invalidate the getpwnam and getgroups entries for a winbindd domain */
3017 void wcache_invalidate_samlogon(struct winbindd_domain *domain,
3018 const struct dom_sid *sid)
3020 fstring key_str;
3021 struct dom_sid_buf sid_string;
3022 struct winbind_cache *cache;
3024 /* don't clear cached U/SID and UG/SID entries when we want to logon
3025 * offline - gd */
3027 if (lp_winbind_offline_logon()) {
3028 return;
3031 if (!domain)
3032 return;
3034 cache = get_cache(domain);
3036 if (!cache->tdb) {
3037 return;
3040 /* Clear U/SID cache entry */
3041 fstr_sprintf(key_str, "U/%s", dom_sid_str_buf(sid, &sid_string));
3042 DEBUG(10, ("wcache_invalidate_samlogon: clearing %s\n", key_str));
3043 tdb_delete(cache->tdb, string_tdb_data(key_str));
3045 /* Clear UG/SID cache entry */
3046 fstr_sprintf(key_str, "UG/%s", dom_sid_str_buf(sid, &sid_string));
3047 DEBUG(10, ("wcache_invalidate_samlogon: clearing %s\n", key_str));
3048 tdb_delete(cache->tdb, string_tdb_data(key_str));
3050 /* Samba/winbindd never needs this. */
3051 netsamlogon_clear_cached_user(sid);
3054 bool wcache_invalidate_cache(void)
3056 struct winbindd_domain *domain;
3058 for (domain = domain_list(); domain; domain = domain->next) {
3059 struct winbind_cache *cache = get_cache(domain);
3061 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
3062 "entries for %s\n", domain->name));
3063 if (cache) {
3064 if (cache->tdb) {
3065 tdb_traverse(cache->tdb, traverse_fn, NULL);
3066 } else {
3067 return false;
3071 return true;
3074 bool wcache_invalidate_cache_noinit(void)
3076 struct winbindd_domain *domain;
3078 for (domain = domain_list(); domain; domain = domain->next) {
3079 struct winbind_cache *cache;
3081 /* Skip uninitialized domains. */
3082 if (!domain->initialized && !domain->internal) {
3083 continue;
3086 cache = get_cache(domain);
3088 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
3089 "entries for %s\n", domain->name));
3090 if (cache) {
3091 if (cache->tdb) {
3092 tdb_traverse(cache->tdb, traverse_fn, NULL);
3094 * Flushing cache has nothing to with domains.
3095 * return here if we successfully flushed once.
3096 * To avoid unnecessary traversing the cache.
3098 return true;
3099 } else {
3100 return false;
3104 return true;
3107 static bool init_wcache(void)
3109 char *db_path;
3111 if (wcache == NULL) {
3112 wcache = SMB_XMALLOC_P(struct winbind_cache);
3113 ZERO_STRUCTP(wcache);
3116 if (wcache->tdb != NULL)
3117 return true;
3119 db_path = wcache_path();
3120 if (db_path == NULL) {
3121 return false;
3124 /* when working offline we must not clear the cache on restart */
3125 wcache->tdb = tdb_open_log(db_path,
3126 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
3127 TDB_INCOMPATIBLE_HASH |
3128 (lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST)),
3129 O_RDWR|O_CREAT, 0600);
3130 TALLOC_FREE(db_path);
3131 if (wcache->tdb == NULL) {
3132 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
3133 return false;
3136 return true;
3139 /************************************************************************
3140 This is called by the parent to initialize the cache file.
3141 We don't need sophisticated locking here as we know we're the
3142 only opener.
3143 ************************************************************************/
3145 bool initialize_winbindd_cache(void)
3147 bool cache_bad = true;
3148 uint32_t vers;
3150 if (!init_wcache()) {
3151 DEBUG(0,("initialize_winbindd_cache: init_wcache failed.\n"));
3152 return false;
3155 /* Check version number. */
3156 if (tdb_fetch_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, &vers) &&
3157 vers == WINBINDD_CACHE_VERSION) {
3158 cache_bad = false;
3161 if (cache_bad) {
3162 char *db_path;
3164 DEBUG(0,("initialize_winbindd_cache: clearing cache "
3165 "and re-creating with version number %d\n",
3166 WINBINDD_CACHE_VERSION ));
3168 tdb_close(wcache->tdb);
3169 wcache->tdb = NULL;
3171 db_path = wcache_path();
3172 if (db_path == NULL) {
3173 return false;
3176 if (unlink(db_path) == -1) {
3177 DEBUG(0,("initialize_winbindd_cache: unlink %s failed %s ",
3178 db_path,
3179 strerror(errno) ));
3180 TALLOC_FREE(db_path);
3181 return false;
3183 TALLOC_FREE(db_path);
3184 if (!init_wcache()) {
3185 DEBUG(0,("initialize_winbindd_cache: re-initialization "
3186 "init_wcache failed.\n"));
3187 return false;
3190 /* Write the version. */
3191 if (!tdb_store_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, WINBINDD_CACHE_VERSION)) {
3192 DEBUG(0,("initialize_winbindd_cache: version number store failed %s\n",
3193 tdb_errorstr(wcache->tdb) ));
3194 return false;
3198 tdb_close(wcache->tdb);
3199 wcache->tdb = NULL;
3200 return true;
3203 void close_winbindd_cache(void)
3205 if (!wcache) {
3206 return;
3208 if (wcache->tdb) {
3209 tdb_close(wcache->tdb);
3210 wcache->tdb = NULL;
3214 bool lookup_cached_sid(TALLOC_CTX *mem_ctx, const struct dom_sid *sid,
3215 char **domain_name, char **name,
3216 enum lsa_SidType *type)
3218 struct winbindd_domain *domain;
3219 NTSTATUS status;
3221 domain = find_lookup_domain_from_sid(sid);
3222 if (domain == NULL) {
3223 return false;
3225 status = wcache_sid_to_name(domain, sid, mem_ctx, domain_name, name,
3226 type);
3227 return NT_STATUS_IS_OK(status);
3230 bool lookup_cached_name(const char *namespace,
3231 const char *domain_name,
3232 const char *name,
3233 struct dom_sid *sid,
3234 enum lsa_SidType *type)
3236 struct winbindd_domain *domain;
3237 NTSTATUS status;
3238 bool original_online_state;
3240 domain = find_lookup_domain_from_name(namespace);
3241 if (domain == NULL) {
3242 return false;
3245 /* If we are doing a cached logon, temporarily set the domain
3246 offline so the cache won't expire the entry */
3248 original_online_state = domain->online;
3249 domain->online = false;
3250 status = wcache_name_to_sid(domain, domain_name, name, sid, type);
3251 domain->online = original_online_state;
3253 return NT_STATUS_IS_OK(status);
3257 * Cache a name to sid without checking the sequence number.
3258 * Used when caching from a trusted PAC.
3261 void cache_name2sid_trusted(struct winbindd_domain *domain,
3262 const char *domain_name,
3263 const char *name,
3264 enum lsa_SidType type,
3265 const struct dom_sid *sid)
3268 * Ensure we store the mapping with the
3269 * existing sequence number from the cache.
3271 get_cache(domain);
3272 (void)fetch_cache_seqnum(domain, time(NULL));
3273 wcache_save_name_to_sid(domain,
3274 NT_STATUS_OK,
3275 domain_name,
3276 name,
3277 sid,
3278 type);
3281 void cache_name2sid(struct winbindd_domain *domain,
3282 const char *domain_name, const char *name,
3283 enum lsa_SidType type, const struct dom_sid *sid)
3285 refresh_sequence_number(domain);
3286 wcache_save_name_to_sid(domain, NT_STATUS_OK, domain_name, name,
3287 sid, type);
3291 * The original idea that this cache only contains centries has
3292 * been blurred - now other stuff gets put in here. Ensure we
3293 * ignore these things on cleanup.
3296 static int traverse_fn_cleanup(TDB_CONTEXT *the_tdb, TDB_DATA kbuf,
3297 TDB_DATA dbuf, void *state)
3299 struct cache_entry *centry;
3301 if (is_non_centry_key(kbuf)) {
3302 return 0;
3305 centry = wcache_fetch_raw((char *)kbuf.dptr);
3306 if (!centry) {
3307 return 0;
3310 if (!NT_STATUS_IS_OK(centry->status)) {
3311 DEBUG(10,("deleting centry %s\n", (const char *)kbuf.dptr));
3312 tdb_delete(the_tdb, kbuf);
3315 centry_free(centry);
3316 return 0;
3319 /* flush the cache */
3320 static void wcache_flush_cache(void)
3322 char *db_path;
3324 if (!wcache)
3325 return;
3326 if (wcache->tdb) {
3327 tdb_close(wcache->tdb);
3328 wcache->tdb = NULL;
3330 if (!winbindd_use_cache()) {
3331 return;
3334 db_path = wcache_path();
3335 if (db_path == NULL) {
3336 return;
3339 /* when working offline we must not clear the cache on restart */
3340 wcache->tdb = tdb_open_log(db_path,
3341 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
3342 TDB_INCOMPATIBLE_HASH |
3343 (lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST)),
3344 O_RDWR|O_CREAT, 0600);
3345 TALLOC_FREE(db_path);
3346 if (!wcache->tdb) {
3347 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
3348 return;
3351 tdb_traverse(wcache->tdb, traverse_fn_cleanup, NULL);
3353 DEBUG(10,("wcache_flush_cache success\n"));
3356 /* Count cached creds */
3358 static int traverse_fn_cached_creds(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
3359 void *state)
3361 int *cred_count = (int*)state;
3363 if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
3364 (*cred_count)++;
3366 return 0;
3369 NTSTATUS wcache_count_cached_creds(struct winbindd_domain *domain, int *count)
3371 struct winbind_cache *cache = get_cache(domain);
3373 *count = 0;
3375 if (!cache->tdb) {
3376 return NT_STATUS_INTERNAL_DB_ERROR;
3379 tdb_traverse(cache->tdb, traverse_fn_cached_creds, (void *)count);
3381 return NT_STATUS_OK;
3384 struct cred_list {
3385 struct cred_list *prev, *next;
3386 TDB_DATA key;
3387 fstring name;
3388 time_t created;
3390 static struct cred_list *wcache_cred_list;
3392 static int traverse_fn_get_credlist(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
3393 void *state)
3395 struct cred_list *cred;
3397 if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
3399 cred = SMB_MALLOC_P(struct cred_list);
3400 if (cred == NULL) {
3401 DEBUG(0,("traverse_fn_remove_first_creds: failed to malloc new entry for list\n"));
3402 return -1;
3405 ZERO_STRUCTP(cred);
3407 /* save a copy of the key */
3409 fstrcpy(cred->name, (const char *)kbuf.dptr);
3410 DLIST_ADD(wcache_cred_list, cred);
3413 return 0;
3416 NTSTATUS wcache_remove_oldest_cached_creds(struct winbindd_domain *domain, const struct dom_sid *sid)
3418 struct winbind_cache *cache = get_cache(domain);
3419 NTSTATUS status;
3420 int ret;
3421 struct cred_list *cred, *next, *oldest = NULL;
3423 if (!cache->tdb) {
3424 return NT_STATUS_INTERNAL_DB_ERROR;
3427 /* we possibly already have an entry */
3428 if (sid && NT_STATUS_IS_OK(wcache_cached_creds_exist(domain, sid))) {
3430 fstring key_str;
3431 struct dom_sid_buf tmp;
3433 DEBUG(11,("we already have an entry, deleting that\n"));
3435 fstr_sprintf(key_str, "CRED/%s", dom_sid_str_buf(sid, &tmp));
3437 tdb_delete(cache->tdb, string_tdb_data(key_str));
3439 return NT_STATUS_OK;
3442 ret = tdb_traverse(cache->tdb, traverse_fn_get_credlist, NULL);
3443 if (ret == 0) {
3444 return NT_STATUS_OK;
3445 } else if ((ret < 0) || (wcache_cred_list == NULL)) {
3446 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
3449 ZERO_STRUCTP(oldest);
3451 for (cred = wcache_cred_list; cred; cred = cred->next) {
3453 TDB_DATA data;
3454 time_t t;
3456 data = tdb_fetch(cache->tdb, string_tdb_data(cred->name));
3457 if (!data.dptr) {
3458 DEBUG(10,("wcache_remove_oldest_cached_creds: entry for [%s] not found\n",
3459 cred->name));
3460 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
3461 goto done;
3464 t = IVAL(data.dptr, 0);
3465 SAFE_FREE(data.dptr);
3467 if (!oldest) {
3468 oldest = SMB_MALLOC_P(struct cred_list);
3469 if (oldest == NULL) {
3470 status = NT_STATUS_NO_MEMORY;
3471 goto done;
3474 fstrcpy(oldest->name, cred->name);
3475 oldest->created = t;
3476 continue;
3479 if (t < oldest->created) {
3480 fstrcpy(oldest->name, cred->name);
3481 oldest->created = t;
3485 if (tdb_delete(cache->tdb, string_tdb_data(oldest->name)) == 0) {
3486 status = NT_STATUS_OK;
3487 } else {
3488 status = NT_STATUS_UNSUCCESSFUL;
3490 done:
3491 for (cred = wcache_cred_list; cred; cred = next) {
3492 next = cred->next;
3493 DLIST_REMOVE(wcache_cred_list, cred);
3494 SAFE_FREE(cred);
3496 SAFE_FREE(oldest);
3498 return status;
3501 /* Change the global online/offline state. */
3502 bool set_global_winbindd_state_offline(void)
3504 TDB_DATA data;
3506 DEBUG(10,("set_global_winbindd_state_offline: offline requested.\n"));
3508 /* Only go offline if someone has created
3509 the key "WINBINDD_OFFLINE" in the cache tdb. */
3511 if (wcache == NULL || wcache->tdb == NULL) {
3512 DEBUG(10,("set_global_winbindd_state_offline: wcache not open yet.\n"));
3513 return false;
3516 if (!lp_winbind_offline_logon()) {
3517 DEBUG(10,("set_global_winbindd_state_offline: rejecting.\n"));
3518 return false;
3521 if (global_winbindd_offline_state) {
3522 /* Already offline. */
3523 return true;
3526 data = tdb_fetch_bystring( wcache->tdb, "WINBINDD_OFFLINE" );
3528 if (!data.dptr || data.dsize != 4) {
3529 DEBUG(10,("set_global_winbindd_state_offline: offline state not set.\n"));
3530 SAFE_FREE(data.dptr);
3531 return false;
3532 } else {
3533 DEBUG(10,("set_global_winbindd_state_offline: offline state set.\n"));
3534 global_winbindd_offline_state = true;
3535 SAFE_FREE(data.dptr);
3536 return true;
3540 void set_global_winbindd_state_online(void)
3542 DEBUG(10,("set_global_winbindd_state_online: online requested.\n"));
3544 if (!lp_winbind_offline_logon()) {
3545 DEBUG(10,("set_global_winbindd_state_online: rejecting.\n"));
3546 return;
3549 if (!global_winbindd_offline_state) {
3550 /* Already online. */
3551 return;
3553 global_winbindd_offline_state = false;
3555 if (!wcache->tdb) {
3556 return;
3559 /* Ensure there is no key "WINBINDD_OFFLINE" in the cache tdb. */
3560 tdb_delete_bystring(wcache->tdb, "WINBINDD_OFFLINE");
3563 bool get_global_winbindd_state_offline(void)
3565 return global_winbindd_offline_state;
3568 /***********************************************************************
3569 Validate functions for all possible cache tdb keys.
3570 ***********************************************************************/
3572 static struct cache_entry *create_centry_validate(const char *kstr, TDB_DATA data,
3573 struct tdb_validation_status *state)
3575 struct cache_entry *centry;
3577 centry = SMB_XMALLOC_P(struct cache_entry);
3578 centry->data = (unsigned char *)smb_memdup(data.dptr, data.dsize);
3579 if (!centry->data) {
3580 SAFE_FREE(centry);
3581 return NULL;
3583 centry->len = data.dsize;
3584 centry->ofs = 0;
3586 if (centry->len < 16) {
3587 /* huh? corrupt cache? */
3588 DEBUG(0,("create_centry_validate: Corrupt cache for key %s "
3589 "(len < 16) ?\n", kstr));
3590 centry_free(centry);
3591 state->bad_entry = true;
3592 state->success = false;
3593 return NULL;
3596 centry->status = NT_STATUS(centry_uint32(centry));
3597 centry->sequence_number = centry_uint32(centry);
3598 centry->timeout = centry_uint64_t(centry);
3599 return centry;
3602 static int validate_seqnum(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3603 struct tdb_validation_status *state)
3605 if (dbuf.dsize != 8) {
3606 DEBUG(0,("validate_seqnum: Corrupt cache for key %s (len %u != 8) ?\n",
3607 keystr, (unsigned int)dbuf.dsize ));
3608 state->bad_entry = true;
3609 return 1;
3611 return 0;
3614 static int validate_u(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3615 struct tdb_validation_status *state)
3617 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3618 struct dom_sid sid;
3620 if (!centry) {
3621 return 1;
3624 (void)centry_string(centry, mem_ctx);
3625 (void)centry_string(centry, mem_ctx);
3626 (void)centry_string(centry, mem_ctx);
3627 (void)centry_string(centry, mem_ctx);
3628 (void)centry_string(centry, mem_ctx);
3629 (void)centry_uint32(centry);
3630 (void)centry_uint32(centry);
3631 (void)centry_string(centry, mem_ctx);
3632 (void)centry_sid(centry, &sid);
3633 (void)centry_sid(centry, &sid);
3635 centry_free(centry);
3637 if (!(state->success)) {
3638 return 1;
3640 DEBUG(10,("validate_u: %s ok\n", keystr));
3641 return 0;
3644 static int validate_loc_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3645 struct tdb_validation_status *state)
3647 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3649 if (!centry) {
3650 return 1;
3653 (void)centry_nttime(centry);
3654 (void)centry_nttime(centry);
3655 (void)centry_uint16(centry);
3657 centry_free(centry);
3659 if (!(state->success)) {
3660 return 1;
3662 DEBUG(10,("validate_loc_pol: %s ok\n", keystr));
3663 return 0;
3666 static int validate_pwd_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3667 struct tdb_validation_status *state)
3669 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3671 if (!centry) {
3672 return 1;
3675 (void)centry_uint16(centry);
3676 (void)centry_uint16(centry);
3677 (void)centry_uint32(centry);
3678 (void)centry_nttime(centry);
3679 (void)centry_nttime(centry);
3681 centry_free(centry);
3683 if (!(state->success)) {
3684 return 1;
3686 DEBUG(10,("validate_pwd_pol: %s ok\n", keystr));
3687 return 0;
3690 static int validate_cred(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3691 struct tdb_validation_status *state)
3693 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3695 if (!centry) {
3696 return 1;
3699 (void)centry_time(centry);
3700 (void)centry_hash16(centry, mem_ctx);
3702 /* We only have 17 bytes more data in the salted cred case. */
3703 if (centry->len - centry->ofs == 17) {
3704 (void)centry_hash16(centry, mem_ctx);
3707 centry_free(centry);
3709 if (!(state->success)) {
3710 return 1;
3712 DEBUG(10,("validate_cred: %s ok\n", keystr));
3713 return 0;
3716 static int validate_ul(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3717 struct tdb_validation_status *state)
3719 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3720 int32_t num_entries, i;
3722 if (!centry) {
3723 return 1;
3726 num_entries = (int32_t)centry_uint32(centry);
3728 for (i=0; i< num_entries; i++) {
3729 (void)centry_uint32(centry);
3732 centry_free(centry);
3734 if (!(state->success)) {
3735 return 1;
3737 DEBUG(10,("validate_ul: %s ok\n", keystr));
3738 return 0;
3741 static int validate_gl(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3742 struct tdb_validation_status *state)
3744 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3745 int32_t num_entries, i;
3747 if (!centry) {
3748 return 1;
3751 num_entries = centry_uint32(centry);
3753 for (i=0; i< num_entries; i++) {
3754 (void)centry_string(centry, mem_ctx);
3755 (void)centry_string(centry, mem_ctx);
3756 (void)centry_uint32(centry);
3759 centry_free(centry);
3761 if (!(state->success)) {
3762 return 1;
3764 DEBUG(10,("validate_gl: %s ok\n", keystr));
3765 return 0;
3768 static int validate_ug(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3769 struct tdb_validation_status *state)
3771 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3772 int32_t num_groups, i;
3774 if (!centry) {
3775 return 1;
3778 num_groups = centry_uint32(centry);
3780 for (i=0; i< num_groups; i++) {
3781 struct dom_sid sid;
3782 centry_sid(centry, &sid);
3785 centry_free(centry);
3787 if (!(state->success)) {
3788 return 1;
3790 DEBUG(10,("validate_ug: %s ok\n", keystr));
3791 return 0;
3794 static int validate_ua(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3795 struct tdb_validation_status *state)
3797 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3798 int32_t num_aliases, i;
3800 if (!centry) {
3801 return 1;
3804 num_aliases = centry_uint32(centry);
3806 for (i=0; i < num_aliases; i++) {
3807 (void)centry_uint32(centry);
3810 centry_free(centry);
3812 if (!(state->success)) {
3813 return 1;
3815 DEBUG(10,("validate_ua: %s ok\n", keystr));
3816 return 0;
3819 static int validate_gm(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3820 struct tdb_validation_status *state)
3822 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3823 int32_t num_names, i;
3825 if (!centry) {
3826 return 1;
3829 num_names = centry_uint32(centry);
3831 for (i=0; i< num_names; i++) {
3832 struct dom_sid sid;
3833 centry_sid(centry, &sid);
3834 (void)centry_string(centry, mem_ctx);
3835 (void)centry_uint32(centry);
3838 centry_free(centry);
3840 if (!(state->success)) {
3841 return 1;
3843 DEBUG(10,("validate_gm: %s ok\n", keystr));
3844 return 0;
3847 static int validate_dr(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3848 struct tdb_validation_status *state)
3850 /* Can't say anything about this other than must be nonzero. */
3851 if (dbuf.dsize == 0) {
3852 DEBUG(0,("validate_dr: Corrupt cache for key %s (len == 0) ?\n",
3853 keystr));
3854 state->bad_entry = true;
3855 state->success = false;
3856 return 1;
3859 DEBUG(10,("validate_dr: %s ok\n", keystr));
3860 return 0;
3863 static int validate_de(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3864 struct tdb_validation_status *state)
3866 /* Can't say anything about this other than must be nonzero. */
3867 if (dbuf.dsize == 0) {
3868 DEBUG(0,("validate_de: Corrupt cache for key %s (len == 0) ?\n",
3869 keystr));
3870 state->bad_entry = true;
3871 state->success = false;
3872 return 1;
3875 DEBUG(10,("validate_de: %s ok\n", keystr));
3876 return 0;
3879 static int validate_nss_an(TALLOC_CTX *mem_ctx, const char *keystr,
3880 TDB_DATA dbuf,
3881 struct tdb_validation_status *state)
3883 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3885 if (!centry) {
3886 return 1;
3889 (void)centry_string( centry, mem_ctx );
3891 centry_free(centry);
3893 if (!(state->success)) {
3894 return 1;
3896 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3897 return 0;
3900 static int validate_nss_na(TALLOC_CTX *mem_ctx, const char *keystr,
3901 TDB_DATA dbuf,
3902 struct tdb_validation_status *state)
3904 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3906 if (!centry) {
3907 return 1;
3910 (void)centry_string( centry, mem_ctx );
3912 centry_free(centry);
3914 if (!(state->success)) {
3915 return 1;
3917 DBG_DEBUG("%s ok\n", keystr);
3918 return 0;
3921 static int validate_trustdomcache(TALLOC_CTX *mem_ctx, const char *keystr,
3922 TDB_DATA dbuf,
3923 struct tdb_validation_status *state)
3925 if (dbuf.dsize == 0) {
3926 DEBUG(0, ("validate_trustdomcache: Corrupt cache for "
3927 "key %s (len ==0) ?\n", keystr));
3928 state->bad_entry = true;
3929 state->success = false;
3930 return 1;
3933 DEBUG(10, ("validate_trustdomcache: %s ok\n", keystr));
3934 DEBUGADD(10, (" Don't trust me, I am a DUMMY!\n"));
3935 return 0;
3938 static int validate_offline(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3939 struct tdb_validation_status *state)
3941 if (dbuf.dsize != 4) {
3942 DEBUG(0,("validate_offline: Corrupt cache for key %s (len %u != 4) ?\n",
3943 keystr, (unsigned int)dbuf.dsize ));
3944 state->bad_entry = true;
3945 state->success = false;
3946 return 1;
3948 DEBUG(10,("validate_offline: %s ok\n", keystr));
3949 return 0;
3952 static int validate_ndr(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3953 struct tdb_validation_status *state)
3956 * Ignore validation for now. The proper way to do this is with a
3957 * checksum. Just pure parsing does not really catch much.
3959 return 0;
3962 static int validate_cache_version(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3963 struct tdb_validation_status *state)
3965 if (dbuf.dsize != 4) {
3966 DEBUG(0, ("validate_cache_version: Corrupt cache for "
3967 "key %s (len %u != 4) ?\n",
3968 keystr, (unsigned int)dbuf.dsize));
3969 state->bad_entry = true;
3970 state->success = false;
3971 return 1;
3974 DEBUG(10, ("validate_cache_version: %s ok\n", keystr));
3975 return 0;
3978 /***********************************************************************
3979 A list of all possible cache tdb keys with associated validation
3980 functions.
3981 ***********************************************************************/
3983 struct key_val_struct {
3984 const char *keyname;
3985 int (*validate_data_fn)(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, struct tdb_validation_status* state);
3986 } key_val[] = {
3987 {"SEQNUM/", validate_seqnum},
3988 {"U/", validate_u},
3989 {"LOC_POL/", validate_loc_pol},
3990 {"PWD_POL/", validate_pwd_pol},
3991 {"CRED/", validate_cred},
3992 {"UL/", validate_ul},
3993 {"GL/", validate_gl},
3994 {"UG/", validate_ug},
3995 {"UA", validate_ua},
3996 {"GM/", validate_gm},
3997 {"DR/", validate_dr},
3998 {"DE/", validate_de},
3999 {"TRUSTDOMCACHE/", validate_trustdomcache},
4000 {"NSS/NA/", validate_nss_na},
4001 {"NSS/AN/", validate_nss_an},
4002 {"WINBINDD_OFFLINE", validate_offline},
4003 {"NDR/", validate_ndr},
4004 {WINBINDD_CACHE_VERSION_KEYSTR, validate_cache_version},
4005 {NULL, NULL}
4008 /***********************************************************************
4009 Function to look at every entry in the tdb and validate it as far as
4010 possible.
4011 ***********************************************************************/
4013 static int cache_traverse_validate_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, void *state)
4015 int i;
4016 unsigned int max_key_len = 1024;
4017 struct tdb_validation_status *v_state = (struct tdb_validation_status *)state;
4019 /* Paranoia check. */
4020 if (strncmp("UA/", (const char *)kbuf.dptr, 3) == 0 ||
4021 strncmp("NDR/", (const char *)kbuf.dptr, 4) == 0) {
4022 max_key_len = 1024 * 1024;
4024 if (kbuf.dsize > max_key_len) {
4025 DEBUG(0, ("cache_traverse_validate_fn: key length too large: "
4026 "(%u) > (%u)\n\n",
4027 (unsigned int)kbuf.dsize, (unsigned int)max_key_len));
4028 return 1;
4031 for (i = 0; key_val[i].keyname; i++) {
4032 size_t namelen = strlen(key_val[i].keyname);
4033 if (kbuf.dsize >= namelen && (
4034 strncmp(key_val[i].keyname, (const char *)kbuf.dptr, namelen)) == 0) {
4035 TALLOC_CTX *mem_ctx;
4036 char *keystr;
4037 int ret;
4039 keystr = SMB_MALLOC_ARRAY(char, kbuf.dsize+1);
4040 if (!keystr) {
4041 return 1;
4043 memcpy(keystr, kbuf.dptr, kbuf.dsize);
4044 keystr[kbuf.dsize] = '\0';
4046 mem_ctx = talloc_init("validate_ctx");
4047 if (!mem_ctx) {
4048 SAFE_FREE(keystr);
4049 return 1;
4052 ret = key_val[i].validate_data_fn(mem_ctx, keystr, dbuf,
4053 v_state);
4055 SAFE_FREE(keystr);
4056 talloc_destroy(mem_ctx);
4057 return ret;
4061 DEBUG(0,("cache_traverse_validate_fn: unknown cache entry\nkey :\n"));
4062 dump_data(0, (uint8_t *)kbuf.dptr, kbuf.dsize);
4063 DEBUG(0,("data :\n"));
4064 dump_data(0, (uint8_t *)dbuf.dptr, dbuf.dsize);
4065 v_state->unknown_key = true;
4066 v_state->success = false;
4067 return 1; /* terminate. */
4070 static void validate_panic(const char *const why)
4072 DEBUG(0,("validating cache: would panic %s\n", why ));
4073 DEBUGADD(0, ("exiting instead (cache validation mode)\n"));
4074 exit(47);
4077 static int wbcache_update_centry_fn(TDB_CONTEXT *tdb,
4078 TDB_DATA key,
4079 TDB_DATA data,
4080 void *state)
4082 uint64_t ctimeout;
4083 TDB_DATA blob;
4085 if (is_non_centry_key(key)) {
4086 return 0;
4089 if (data.dptr == NULL || data.dsize == 0) {
4090 if (tdb_delete(tdb, key) < 0) {
4091 DEBUG(0, ("tdb_delete for [%s] failed!\n",
4092 key.dptr));
4093 return 1;
4097 /* add timeout to blob (uint64_t) */
4098 blob.dsize = data.dsize + 8;
4100 blob.dptr = SMB_XMALLOC_ARRAY(uint8_t, blob.dsize);
4101 if (blob.dptr == NULL) {
4102 return 1;
4104 memset(blob.dptr, 0, blob.dsize);
4106 /* copy status and seqnum */
4107 memcpy(blob.dptr, data.dptr, 8);
4109 /* add timeout */
4110 ctimeout = lp_winbind_cache_time() + time(NULL);
4111 SBVAL(blob.dptr, 8, ctimeout);
4113 /* copy the rest */
4114 memcpy(blob.dptr + 16, data.dptr + 8, data.dsize - 8);
4116 if (tdb_store(tdb, key, blob, TDB_REPLACE) < 0) {
4117 DEBUG(0, ("tdb_store to update [%s] failed!\n",
4118 key.dptr));
4119 SAFE_FREE(blob.dptr);
4120 return 1;
4123 SAFE_FREE(blob.dptr);
4124 return 0;
4127 static bool wbcache_upgrade_v1_to_v2(TDB_CONTEXT *tdb)
4129 int rc;
4131 DEBUG(1, ("Upgrade to version 2 of the winbindd_cache.tdb\n"));
4133 rc = tdb_traverse(tdb, wbcache_update_centry_fn, NULL);
4134 if (rc < 0) {
4135 return false;
4138 return true;
4141 /***********************************************************************
4142 Try and validate every entry in the winbindd cache. If we fail here,
4143 delete the cache tdb and return non-zero.
4144 ***********************************************************************/
4146 int winbindd_validate_cache(void)
4148 int ret = -1;
4149 char *tdb_path = NULL;
4150 TDB_CONTEXT *tdb = NULL;
4151 uint32_t vers_id;
4152 bool ok;
4154 DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
4155 smb_panic_fn = validate_panic;
4157 tdb_path = wcache_path();
4158 if (tdb_path == NULL) {
4159 goto done;
4162 tdb = tdb_open_log(tdb_path,
4163 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
4164 TDB_INCOMPATIBLE_HASH |
4165 ( lp_winbind_offline_logon()
4166 ? TDB_DEFAULT
4167 : TDB_DEFAULT | TDB_CLEAR_IF_FIRST ),
4168 O_RDWR|O_CREAT,
4169 0600);
4170 if (!tdb) {
4171 DEBUG(0, ("winbindd_validate_cache: "
4172 "error opening/initializing tdb\n"));
4173 goto done;
4176 /* Version check and upgrade code. */
4177 if (!tdb_fetch_uint32(tdb, WINBINDD_CACHE_VERSION_KEYSTR, &vers_id)) {
4178 DEBUG(10, ("Fresh database\n"));
4179 tdb_store_uint32(tdb, WINBINDD_CACHE_VERSION_KEYSTR, WINBINDD_CACHE_VERSION);
4180 vers_id = WINBINDD_CACHE_VERSION;
4183 if (vers_id != WINBINDD_CACHE_VERSION) {
4184 if (vers_id == WINBINDD_CACHE_VER1) {
4185 ok = wbcache_upgrade_v1_to_v2(tdb);
4186 if (!ok) {
4187 DEBUG(10, ("winbindd_validate_cache: upgrade to version 2 failed.\n"));
4188 unlink(tdb_path);
4189 goto done;
4192 tdb_store_uint32(tdb,
4193 WINBINDD_CACHE_VERSION_KEYSTR,
4194 WINBINDD_CACHE_VERSION);
4195 vers_id = WINBINDD_CACHE_VER2;
4199 tdb_close(tdb);
4201 ret = tdb_validate_and_backup(tdb_path, cache_traverse_validate_fn);
4203 if (ret != 0) {
4204 DEBUG(10, ("winbindd_validate_cache: validation not successful.\n"));
4205 DEBUGADD(10, ("removing tdb %s.\n", tdb_path));
4206 unlink(tdb_path);
4209 done:
4210 TALLOC_FREE(tdb_path);
4211 DEBUG(10, ("winbindd_validate_cache: restoring panic function\n"));
4212 smb_panic_fn = smb_panic;
4213 return ret;
4216 /***********************************************************************
4217 Try and validate every entry in the winbindd cache.
4218 ***********************************************************************/
4220 int winbindd_validate_cache_nobackup(void)
4222 int ret = -1;
4223 char *tdb_path;
4225 DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
4226 smb_panic_fn = validate_panic;
4228 tdb_path = wcache_path();
4229 if (tdb_path == NULL) {
4230 goto err_panic_restore;
4233 if (wcache == NULL || wcache->tdb == NULL) {
4234 ret = tdb_validate_open(tdb_path, cache_traverse_validate_fn);
4235 } else {
4236 ret = tdb_validate(wcache->tdb, cache_traverse_validate_fn);
4239 if (ret != 0) {
4240 DEBUG(10, ("winbindd_validate_cache_nobackup: validation not "
4241 "successful.\n"));
4244 TALLOC_FREE(tdb_path);
4245 err_panic_restore:
4246 DEBUG(10, ("winbindd_validate_cache_nobackup: restoring panic "
4247 "function\n"));
4248 smb_panic_fn = smb_panic;
4249 return ret;
4252 bool winbindd_cache_validate_and_initialize(void)
4254 close_winbindd_cache();
4256 if (lp_winbind_offline_logon()) {
4257 if (winbindd_validate_cache() < 0) {
4258 DEBUG(0, ("winbindd cache tdb corrupt and no backup "
4259 "could be restored.\n"));
4263 return initialize_winbindd_cache();
4266 /*********************************************************************
4267 ********************************************************************/
4269 static bool add_wbdomain_to_tdc_array( struct winbindd_domain *new_dom,
4270 struct winbindd_tdc_domain **domains,
4271 size_t *num_domains )
4273 struct winbindd_tdc_domain *list = NULL;
4274 size_t i, idx;
4275 bool set_only = false;
4277 /* don't allow duplicates */
4279 idx = *num_domains;
4280 list = *domains;
4282 for ( i=0; i< (*num_domains); i++ ) {
4283 if ( strequal( new_dom->name, list[i].domain_name ) ) {
4284 DEBUG(10,("add_wbdomain_to_tdc_array: Found existing record for %s\n",
4285 new_dom->name));
4286 idx = i;
4287 set_only = true;
4289 break;
4293 if ( !set_only ) {
4294 if ( !*domains ) {
4295 list = talloc_array( NULL, struct winbindd_tdc_domain, 1 );
4296 idx = 0;
4297 } else {
4298 list = talloc_realloc( *domains, *domains,
4299 struct winbindd_tdc_domain,
4300 (*num_domains)+1);
4301 idx = *num_domains;
4304 ZERO_STRUCT( list[idx] );
4307 if ( !list )
4308 return false;
4310 list[idx].domain_name = talloc_strdup(list, new_dom->name);
4311 if (list[idx].domain_name == NULL) {
4312 return false;
4314 if (new_dom->alt_name != NULL) {
4315 list[idx].dns_name = talloc_strdup(list, new_dom->alt_name);
4316 if (list[idx].dns_name == NULL) {
4317 return false;
4321 if ( !is_null_sid( &new_dom->sid ) ) {
4322 sid_copy( &list[idx].sid, &new_dom->sid );
4323 } else {
4324 sid_copy(&list[idx].sid, &global_sid_NULL);
4327 if ( new_dom->domain_flags != 0x0 )
4328 list[idx].trust_flags = new_dom->domain_flags;
4330 if ( new_dom->domain_type != 0x0 )
4331 list[idx].trust_type = new_dom->domain_type;
4333 if ( new_dom->domain_trust_attribs != 0x0 )
4334 list[idx].trust_attribs = new_dom->domain_trust_attribs;
4336 if ( !set_only ) {
4337 *domains = list;
4338 *num_domains = idx + 1;
4341 return true;
4344 /*********************************************************************
4345 ********************************************************************/
4347 static TDB_DATA make_tdc_key( const char *domain_name )
4349 char *keystr = NULL;
4350 TDB_DATA key = { NULL, 0 };
4352 if ( !domain_name ) {
4353 DEBUG(5,("make_tdc_key: Keyname workgroup is NULL!\n"));
4354 return key;
4357 if (asprintf( &keystr, "TRUSTDOMCACHE/%s", domain_name ) == -1) {
4358 return key;
4360 key = string_term_tdb_data(keystr);
4362 return key;
4365 /*********************************************************************
4366 ********************************************************************/
4368 static int pack_tdc_domains( struct winbindd_tdc_domain *domains,
4369 size_t num_domains,
4370 unsigned char **buf )
4372 unsigned char *buffer = NULL;
4373 int len = 0;
4374 int buflen = 0;
4375 size_t i = 0;
4377 DEBUG(10,("pack_tdc_domains: Packing %d trusted domains\n",
4378 (int)num_domains));
4380 buflen = 0;
4382 again:
4383 len = 0;
4385 /* Store the number of array items first */
4386 len += tdb_pack( buffer ? buffer+len : NULL,
4387 buffer ? buflen-len : 0, "d",
4388 num_domains );
4390 /* now pack each domain trust record */
4391 for ( i=0; i<num_domains; i++ ) {
4393 struct dom_sid_buf tmp;
4395 if ( buflen > 0 ) {
4396 DEBUG(10,("pack_tdc_domains: Packing domain %s (%s)\n",
4397 domains[i].domain_name,
4398 domains[i].dns_name ? domains[i].dns_name : "UNKNOWN" ));
4401 len += tdb_pack( buffer ? buffer+len : NULL,
4402 buffer ? buflen-len : 0, "fffddd",
4403 domains[i].domain_name,
4404 domains[i].dns_name ? domains[i].dns_name : "",
4405 dom_sid_str_buf(&domains[i].sid, &tmp),
4406 domains[i].trust_flags,
4407 domains[i].trust_attribs,
4408 domains[i].trust_type );
4411 if ( buflen < len ) {
4412 SAFE_FREE(buffer);
4413 if ( (buffer = SMB_MALLOC_ARRAY(unsigned char, len)) == NULL ) {
4414 DEBUG(0,("pack_tdc_domains: failed to alloc buffer!\n"));
4415 buflen = -1;
4416 goto done;
4418 buflen = len;
4419 goto again;
4422 *buf = buffer;
4424 done:
4425 return buflen;
4428 /*********************************************************************
4429 ********************************************************************/
4431 static size_t unpack_tdc_domains( unsigned char *buf, int buflen,
4432 struct winbindd_tdc_domain **domains )
4434 fstring domain_name, dns_name, sid_string;
4435 uint32_t type, attribs, flags;
4436 int num_domains;
4437 int len = 0;
4438 int i;
4439 struct winbindd_tdc_domain *list = NULL;
4441 /* get the number of domains */
4442 len += tdb_unpack( buf+len, buflen-len, "d", &num_domains);
4443 if ( len == -1 ) {
4444 DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
4445 return 0;
4448 list = talloc_array( NULL, struct winbindd_tdc_domain, num_domains );
4449 if ( !list ) {
4450 DEBUG(0,("unpack_tdc_domains: Failed to talloc() domain list!\n"));
4451 return 0;
4454 for ( i=0; i<num_domains; i++ ) {
4455 int this_len;
4457 this_len = tdb_unpack( buf+len, buflen-len, "fffddd",
4458 domain_name,
4459 dns_name,
4460 sid_string,
4461 &flags,
4462 &attribs,
4463 &type );
4465 if ( this_len == -1 ) {
4466 DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
4467 TALLOC_FREE( list );
4468 return 0;
4470 len += this_len;
4472 DEBUG(11,("unpack_tdc_domains: Unpacking domain %s (%s) "
4473 "SID %s, flags = 0x%x, attribs = 0x%x, type = 0x%x\n",
4474 domain_name, dns_name, sid_string,
4475 flags, attribs, type));
4477 list[i].domain_name = talloc_strdup( list, domain_name );
4478 list[i].dns_name = NULL;
4479 if (dns_name[0] != '\0') {
4480 list[i].dns_name = talloc_strdup(list, dns_name);
4482 if ( !string_to_sid( &(list[i].sid), sid_string ) ) {
4483 DEBUG(10,("unpack_tdc_domains: no SID for domain %s\n",
4484 domain_name));
4486 list[i].trust_flags = flags;
4487 list[i].trust_attribs = attribs;
4488 list[i].trust_type = type;
4491 *domains = list;
4493 return num_domains;
4496 /*********************************************************************
4497 ********************************************************************/
4499 static bool wcache_tdc_store_list( struct winbindd_tdc_domain *domains, size_t num_domains )
4501 TDB_DATA key = make_tdc_key( lp_workgroup() );
4502 TDB_DATA data = { NULL, 0 };
4503 int ret;
4505 if ( !key.dptr )
4506 return false;
4508 /* See if we were asked to delete the cache entry */
4510 if ( !domains ) {
4511 ret = tdb_delete( wcache->tdb, key );
4512 goto done;
4515 data.dsize = pack_tdc_domains( domains, num_domains, &data.dptr );
4517 if ( !data.dptr ) {
4518 ret = -1;
4519 goto done;
4522 ret = tdb_store( wcache->tdb, key, data, 0 );
4524 done:
4525 SAFE_FREE( data.dptr );
4526 SAFE_FREE( key.dptr );
4528 return ( ret == 0 );
4531 /*********************************************************************
4532 ********************************************************************/
4534 bool wcache_tdc_fetch_list( struct winbindd_tdc_domain **domains, size_t *num_domains )
4536 TDB_DATA key = make_tdc_key( lp_workgroup() );
4537 TDB_DATA data = { NULL, 0 };
4539 *domains = NULL;
4540 *num_domains = 0;
4542 if ( !key.dptr )
4543 return false;
4545 data = tdb_fetch( wcache->tdb, key );
4547 SAFE_FREE( key.dptr );
4549 if ( !data.dptr )
4550 return false;
4552 *num_domains = unpack_tdc_domains( data.dptr, data.dsize, domains );
4554 SAFE_FREE( data.dptr );
4556 if ( !*domains )
4557 return false;
4559 return true;
4562 /*********************************************************************
4563 ********************************************************************/
4565 bool wcache_tdc_add_domain( struct winbindd_domain *domain )
4567 struct winbindd_tdc_domain *dom_list = NULL;
4568 size_t num_domains = 0;
4569 bool ret = false;
4570 struct dom_sid_buf buf;
4572 DEBUG(10,("wcache_tdc_add_domain: Adding domain %s (%s), SID %s, "
4573 "flags = 0x%x, attributes = 0x%x, type = 0x%x\n",
4574 domain->name, domain->alt_name,
4575 dom_sid_str_buf(&domain->sid, &buf),
4576 domain->domain_flags,
4577 domain->domain_trust_attribs,
4578 domain->domain_type));
4580 if ( !init_wcache() ) {
4581 return false;
4584 /* fetch the list */
4586 wcache_tdc_fetch_list( &dom_list, &num_domains );
4588 /* add the new domain */
4590 if ( !add_wbdomain_to_tdc_array( domain, &dom_list, &num_domains ) ) {
4591 goto done;
4594 /* pack the domain */
4596 if ( !wcache_tdc_store_list( dom_list, num_domains ) ) {
4597 goto done;
4600 /* Success */
4602 ret = true;
4603 done:
4604 TALLOC_FREE( dom_list );
4606 return ret;
4609 static struct winbindd_tdc_domain *wcache_tdc_dup_domain(
4610 TALLOC_CTX *mem_ctx, const struct winbindd_tdc_domain *src)
4612 struct winbindd_tdc_domain *dst;
4614 dst = talloc(mem_ctx, struct winbindd_tdc_domain);
4615 if (dst == NULL) {
4616 goto fail;
4618 dst->domain_name = talloc_strdup(dst, src->domain_name);
4619 if (dst->domain_name == NULL) {
4620 goto fail;
4623 dst->dns_name = NULL;
4624 if (src->dns_name != NULL) {
4625 dst->dns_name = talloc_strdup(dst, src->dns_name);
4626 if (dst->dns_name == NULL) {
4627 goto fail;
4631 sid_copy(&dst->sid, &src->sid);
4632 dst->trust_flags = src->trust_flags;
4633 dst->trust_type = src->trust_type;
4634 dst->trust_attribs = src->trust_attribs;
4635 return dst;
4636 fail:
4637 TALLOC_FREE(dst);
4638 return NULL;
4641 /*********************************************************************
4642 ********************************************************************/
4644 struct winbindd_tdc_domain * wcache_tdc_fetch_domain( TALLOC_CTX *ctx, const char *name )
4646 struct winbindd_tdc_domain *dom_list = NULL;
4647 size_t num_domains = 0;
4648 size_t i;
4649 struct winbindd_tdc_domain *d = NULL;
4651 DEBUG(10,("wcache_tdc_fetch_domain: Searching for domain %s\n", name));
4653 if ( !init_wcache() ) {
4654 return NULL;
4657 /* fetch the list */
4659 wcache_tdc_fetch_list( &dom_list, &num_domains );
4661 for ( i=0; i<num_domains; i++ ) {
4662 if ( strequal(name, dom_list[i].domain_name) ||
4663 strequal(name, dom_list[i].dns_name) )
4665 DEBUG(10,("wcache_tdc_fetch_domain: Found domain %s\n",
4666 name));
4668 d = wcache_tdc_dup_domain(ctx, &dom_list[i]);
4669 break;
4673 TALLOC_FREE( dom_list );
4675 return d;
4678 /*********************************************************************
4679 ********************************************************************/
4681 void wcache_tdc_clear( void )
4683 if ( !init_wcache() )
4684 return;
4686 wcache_tdc_store_list( NULL, 0 );
4688 return;
4691 static bool wcache_ndr_key(TALLOC_CTX *mem_ctx, const char *domain_name,
4692 uint32_t opnum, const DATA_BLOB *req,
4693 TDB_DATA *pkey)
4695 char *key;
4696 size_t keylen;
4698 key = talloc_asprintf(mem_ctx, "NDR/%s/%d/", domain_name, (int)opnum);
4699 if (key == NULL) {
4700 return false;
4702 keylen = talloc_get_size(key) - 1;
4704 key = talloc_realloc(mem_ctx, key, char, keylen + req->length);
4705 if (key == NULL) {
4706 return false;
4708 memcpy(key + keylen, req->data, req->length);
4710 pkey->dptr = (uint8_t *)key;
4711 pkey->dsize = talloc_get_size(key);
4712 return true;
4715 static bool wcache_opnum_cacheable(uint32_t opnum)
4717 switch (opnum) {
4718 case NDR_WBINT_PING:
4719 case NDR_WBINT_QUERYSEQUENCENUMBER:
4720 case NDR_WBINT_ALLOCATEUID:
4721 case NDR_WBINT_ALLOCATEGID:
4722 case NDR_WBINT_CHECKMACHINEACCOUNT:
4723 case NDR_WBINT_CHANGEMACHINEACCOUNT:
4724 case NDR_WBINT_PINGDC:
4725 return false;
4727 return true;
4730 bool wcache_fetch_ndr(TALLOC_CTX *mem_ctx, struct winbindd_domain *domain,
4731 uint32_t opnum, const DATA_BLOB *req, DATA_BLOB *resp)
4733 TDB_DATA key, data;
4734 bool ret = false;
4736 if (!wcache_opnum_cacheable(opnum) ||
4737 is_my_own_sam_domain(domain) ||
4738 is_builtin_domain(domain)) {
4739 return false;
4742 if (wcache->tdb == NULL) {
4743 return false;
4746 if (!wcache_ndr_key(talloc_tos(), domain->name, opnum, req, &key)) {
4747 return false;
4749 data = tdb_fetch(wcache->tdb, key);
4750 TALLOC_FREE(key.dptr);
4752 if (data.dptr == NULL) {
4753 return false;
4755 if (data.dsize < 12) {
4756 goto fail;
4759 if (is_domain_online(domain)) {
4760 uint32_t entry_seqnum, dom_seqnum, last_check;
4761 uint64_t entry_timeout;
4763 if (!wcache_fetch_seqnum(domain->name, &dom_seqnum,
4764 &last_check)) {
4765 goto fail;
4767 entry_seqnum = IVAL(data.dptr, 0);
4768 if (entry_seqnum != dom_seqnum) {
4769 DEBUG(10, ("Entry has wrong sequence number: %d\n",
4770 (int)entry_seqnum));
4771 goto fail;
4773 entry_timeout = BVAL(data.dptr, 4);
4774 if (time(NULL) > (time_t)entry_timeout) {
4775 DEBUG(10, ("Entry has timed out\n"));
4776 goto fail;
4780 resp->data = (uint8_t *)talloc_memdup(mem_ctx, data.dptr + 12,
4781 data.dsize - 12);
4782 if (resp->data == NULL) {
4783 DEBUG(10, ("talloc failed\n"));
4784 goto fail;
4786 resp->length = data.dsize - 12;
4788 ret = true;
4789 fail:
4790 SAFE_FREE(data.dptr);
4791 return ret;
4794 void wcache_store_ndr(struct winbindd_domain *domain, uint32_t opnum,
4795 const DATA_BLOB *req, const DATA_BLOB *resp)
4797 TDB_DATA key, data;
4798 uint32_t dom_seqnum, last_check;
4799 uint64_t timeout;
4801 if (!wcache_opnum_cacheable(opnum) ||
4802 is_my_own_sam_domain(domain) ||
4803 is_builtin_domain(domain)) {
4804 return;
4807 if (wcache->tdb == NULL) {
4808 return;
4811 if (!wcache_fetch_seqnum(domain->name, &dom_seqnum, &last_check)) {
4812 DEBUG(10, ("could not fetch seqnum for domain %s\n",
4813 domain->name));
4814 return;
4817 if (!wcache_ndr_key(talloc_tos(), domain->name, opnum, req, &key)) {
4818 return;
4821 timeout = time(NULL) + lp_winbind_cache_time();
4823 data.dsize = resp->length + 12;
4824 data.dptr = talloc_array(key.dptr, uint8_t, data.dsize);
4825 if (data.dptr == NULL) {
4826 goto done;
4829 SIVAL(data.dptr, 0, dom_seqnum);
4830 SBVAL(data.dptr, 4, timeout);
4831 memcpy(data.dptr + 12, resp->data, resp->length);
4833 tdb_store(wcache->tdb, key, data, 0);
4835 done:
4836 TALLOC_FREE(key.dptr);
4837 return;