WHATSNEW: Add release notes for Samba 4.9.17.
[Samba.git] / source3 / winbindd / winbindd_cache.c
blob1af3d929e49193c89b4fc23e8043e24243cbb210
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("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 fstring sid_string;
858 centry_put_string(centry, sid_to_fstring(sid_string, sid));
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 fstring key_str, tmp;
1245 uint32_t rid;
1247 if (!cache->tdb) {
1248 return NT_STATUS_INTERNAL_DB_ERROR;
1251 if (is_null_sid(sid)) {
1252 return NT_STATUS_INVALID_SID;
1255 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1256 return NT_STATUS_INVALID_SID;
1259 fstr_sprintf(key_str, "CRED/%s", sid_to_fstring(tmp, sid));
1261 ret = tdb_exists(cache->tdb, string_tdb_data(key_str));
1262 if (ret != 1) {
1263 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1266 return NT_STATUS_OK;
1269 /* Lookup creds for a SID - copes with old (unsalted) creds as well
1270 as new salted ones. */
1272 NTSTATUS wcache_get_creds(struct winbindd_domain *domain,
1273 TALLOC_CTX *mem_ctx,
1274 const struct dom_sid *sid,
1275 const uint8_t **cached_nt_pass,
1276 const uint8_t **cached_salt)
1278 struct winbind_cache *cache = get_cache(domain);
1279 struct cache_entry *centry = NULL;
1280 NTSTATUS status;
1281 uint32_t rid;
1282 fstring tmp;
1284 if (!cache->tdb) {
1285 return NT_STATUS_INTERNAL_DB_ERROR;
1288 if (is_null_sid(sid)) {
1289 return NT_STATUS_INVALID_SID;
1292 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1293 return NT_STATUS_INVALID_SID;
1296 /* Try and get a salted cred first. If we can't
1297 fall back to an unsalted cred. */
1299 centry = wcache_fetch(cache, domain, "CRED/%s",
1300 sid_to_fstring(tmp, sid));
1301 if (!centry) {
1302 DEBUG(10,("wcache_get_creds: entry for [CRED/%s] not found\n",
1303 sid_string_dbg(sid)));
1304 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1308 * We don't use the time element at this moment,
1309 * but we have to consume it, so that we don't
1310 * neet to change the disk format of the cache.
1312 (void)centry_time(centry);
1314 /* In the salted case this isn't actually the nt_hash itself,
1315 but the MD5 of the salt + nt_hash. Let the caller
1316 sort this out. It can tell as we only return the cached_salt
1317 if we are returning a salted cred. */
1319 *cached_nt_pass = (const uint8_t *)centry_hash16(centry, mem_ctx);
1320 if (*cached_nt_pass == NULL) {
1321 fstring sidstr;
1323 sid_to_fstring(sidstr, sid);
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));
1329 wcache_delete("CRED/%s", sidstr);
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 sid_string_dbg(sid), nt_errstr(status) ));
1351 centry_free(centry);
1352 return status;
1355 /* Store creds for a SID - only writes out new salted ones. */
1357 NTSTATUS wcache_save_creds(struct winbindd_domain *domain,
1358 const struct dom_sid *sid,
1359 const uint8_t nt_pass[NT_HASH_LEN])
1361 struct cache_entry *centry;
1362 fstring sid_string;
1363 uint32_t rid;
1364 uint8_t cred_salt[NT_HASH_LEN];
1365 uint8_t salted_hash[NT_HASH_LEN];
1367 if (is_null_sid(sid)) {
1368 return NT_STATUS_INVALID_SID;
1371 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1372 return NT_STATUS_INVALID_SID;
1375 centry = centry_start(domain, NT_STATUS_OK);
1376 if (!centry) {
1377 return NT_STATUS_INTERNAL_DB_ERROR;
1380 dump_data_pw("nt_pass", nt_pass, NT_HASH_LEN);
1382 centry_put_time(centry, time(NULL));
1384 /* Create a salt and then salt the hash. */
1385 generate_random_buffer(cred_salt, NT_HASH_LEN);
1386 E_md5hash(cred_salt, nt_pass, salted_hash);
1388 centry_put_hash16(centry, salted_hash);
1389 centry_put_hash16(centry, cred_salt);
1390 centry_end(centry, "CRED/%s", sid_to_fstring(sid_string, sid));
1392 DEBUG(10,("wcache_save_creds: %s\n", sid_string));
1394 centry_free(centry);
1396 return NT_STATUS_OK;
1400 /* Query display info. This is the basic user list fn */
1401 NTSTATUS wb_cache_query_user_list(struct winbindd_domain *domain,
1402 TALLOC_CTX *mem_ctx,
1403 uint32_t **prids)
1405 struct winbind_cache *cache = get_cache(domain);
1406 struct cache_entry *centry = NULL;
1407 uint32_t num_rids = 0;
1408 uint32_t *rids = NULL;
1409 NTSTATUS status;
1410 unsigned int i, retry;
1411 bool old_status = domain->online;
1413 *prids = NULL;
1415 if (!cache->tdb)
1416 goto do_query;
1418 centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
1419 if (!centry)
1420 goto do_query;
1422 do_fetch_cache:
1423 num_rids = centry_uint32(centry);
1425 if (num_rids == 0) {
1426 goto do_cached;
1429 rids = talloc_array(mem_ctx, uint32_t, num_rids);
1430 if (rids == NULL) {
1431 centry_free(centry);
1432 return NT_STATUS_NO_MEMORY;
1435 for (i=0; i<num_rids; i++) {
1436 rids[i] = centry_uint32(centry);
1439 do_cached:
1440 status = centry->status;
1442 DEBUG(10,("query_user_list: [Cached] - cached list for domain %s status: %s\n",
1443 domain->name, nt_errstr(status) ));
1445 centry_free(centry);
1446 return status;
1448 do_query:
1450 /* Return status value returned by seq number check */
1452 if (!NT_STATUS_IS_OK(domain->last_status))
1453 return domain->last_status;
1455 /* Put the query_user_list() in a retry loop. There appears to be
1456 * some bug either with Windows 2000 or Samba's handling of large
1457 * rpc replies. This manifests itself as sudden disconnection
1458 * at a random point in the enumeration of a large (60k) user list.
1459 * The retry loop simply tries the operation again. )-: It's not
1460 * pretty but an acceptable workaround until we work out what the
1461 * real problem is. */
1463 retry = 0;
1464 do {
1466 DEBUG(10,("query_user_list: [Cached] - doing backend query for list for domain %s\n",
1467 domain->name ));
1469 rids = NULL;
1470 status = domain->backend->query_user_list(domain, mem_ctx,
1471 &rids);
1472 num_rids = talloc_array_length(rids);
1474 if (!NT_STATUS_IS_OK(status)) {
1475 DEBUG(3, ("query_user_list: returned 0x%08x, "
1476 "retrying\n", NT_STATUS_V(status)));
1478 if (NT_STATUS_EQUAL(status, NT_STATUS_UNSUCCESSFUL)) {
1479 DEBUG(3, ("query_user_list: flushing "
1480 "connection cache\n"));
1481 invalidate_cm_connection(domain);
1483 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1484 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1485 if (!domain->internal && old_status) {
1486 set_domain_offline(domain);
1488 /* store partial response. */
1489 if (num_rids > 0) {
1491 * humm, what about the status used for cache?
1492 * Should it be NT_STATUS_OK?
1494 break;
1497 * domain is offline now, and there is no user entries,
1498 * try to fetch from cache again.
1500 if (cache->tdb && !domain->online && !domain->internal && old_status) {
1501 centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
1502 /* partial response... */
1503 if (!centry) {
1504 goto skip_save;
1505 } else {
1506 goto do_fetch_cache;
1508 } else {
1509 goto skip_save;
1513 } while (NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_UNSUCCESSFUL) &&
1514 (retry++ < 5));
1516 /* and save it */
1517 refresh_sequence_number(domain);
1518 if (!NT_STATUS_IS_OK(status)) {
1519 return status;
1521 centry = centry_start(domain, status);
1522 if (!centry)
1523 goto skip_save;
1524 centry_put_uint32(centry, num_rids);
1525 for (i=0; i<num_rids; i++) {
1526 centry_put_uint32(centry, rids[i]);
1528 centry_end(centry, "UL/%s", domain->name);
1529 centry_free(centry);
1531 *prids = rids;
1533 skip_save:
1534 return status;
1537 /* list all domain groups */
1538 NTSTATUS wb_cache_enum_dom_groups(struct winbindd_domain *domain,
1539 TALLOC_CTX *mem_ctx,
1540 uint32_t *num_entries,
1541 struct wb_acct_info **info)
1543 struct winbind_cache *cache = get_cache(domain);
1544 struct cache_entry *centry = NULL;
1545 NTSTATUS status;
1546 unsigned int i;
1547 bool old_status;
1549 old_status = domain->online;
1550 if (!cache->tdb)
1551 goto do_query;
1553 centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
1554 if (!centry)
1555 goto do_query;
1557 do_fetch_cache:
1558 *num_entries = centry_uint32(centry);
1560 if (*num_entries == 0)
1561 goto do_cached;
1563 (*info) = talloc_array(mem_ctx, struct wb_acct_info, *num_entries);
1564 if (! (*info)) {
1565 smb_panic_fn("enum_dom_groups out of memory");
1567 for (i=0; i<(*num_entries); i++) {
1568 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1569 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1570 (*info)[i].rid = centry_uint32(centry);
1573 do_cached:
1574 status = centry->status;
1576 DEBUG(10,("enum_dom_groups: [Cached] - cached list for domain %s status: %s\n",
1577 domain->name, nt_errstr(status) ));
1579 centry_free(centry);
1580 return status;
1582 do_query:
1583 *num_entries = 0;
1584 *info = NULL;
1586 /* Return status value returned by seq number check */
1588 if (!NT_STATUS_IS_OK(domain->last_status))
1589 return domain->last_status;
1591 DEBUG(10,("enum_dom_groups: [Cached] - doing backend query for list for domain %s\n",
1592 domain->name ));
1594 status = domain->backend->enum_dom_groups(domain, mem_ctx, num_entries, info);
1596 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1597 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1598 if (!domain->internal && old_status) {
1599 set_domain_offline(domain);
1601 if (cache->tdb &&
1602 !domain->online &&
1603 !domain->internal &&
1604 old_status) {
1605 centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
1606 if (centry) {
1607 goto do_fetch_cache;
1611 /* and save it */
1612 refresh_sequence_number(domain);
1613 if (!NT_STATUS_IS_OK(status)) {
1614 return status;
1616 centry = centry_start(domain, status);
1617 if (!centry)
1618 goto skip_save;
1619 centry_put_uint32(centry, *num_entries);
1620 for (i=0; i<(*num_entries); i++) {
1621 centry_put_string(centry, (*info)[i].acct_name);
1622 centry_put_string(centry, (*info)[i].acct_desc);
1623 centry_put_uint32(centry, (*info)[i].rid);
1625 centry_end(centry, "GL/%s/domain", domain->name);
1626 centry_free(centry);
1628 skip_save:
1629 return status;
1632 /* list all domain groups */
1633 NTSTATUS wb_cache_enum_local_groups(struct winbindd_domain *domain,
1634 TALLOC_CTX *mem_ctx,
1635 uint32_t *num_entries,
1636 struct wb_acct_info **info)
1638 struct winbind_cache *cache = get_cache(domain);
1639 struct cache_entry *centry = NULL;
1640 NTSTATUS status;
1641 unsigned int i;
1642 bool old_status;
1644 old_status = domain->online;
1645 if (!cache->tdb)
1646 goto do_query;
1648 centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
1649 if (!centry)
1650 goto do_query;
1652 do_fetch_cache:
1653 *num_entries = centry_uint32(centry);
1655 if (*num_entries == 0)
1656 goto do_cached;
1658 (*info) = talloc_array(mem_ctx, struct wb_acct_info, *num_entries);
1659 if (! (*info)) {
1660 smb_panic_fn("enum_dom_groups out of memory");
1662 for (i=0; i<(*num_entries); i++) {
1663 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1664 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1665 (*info)[i].rid = centry_uint32(centry);
1668 do_cached:
1670 /* If we are returning cached data and the domain controller
1671 is down then we don't know whether the data is up to date
1672 or not. Return NT_STATUS_MORE_PROCESSING_REQUIRED to
1673 indicate this. */
1675 if (wcache_server_down(domain)) {
1676 DEBUG(10, ("enum_local_groups: returning cached user list and server was down\n"));
1677 status = NT_STATUS_MORE_PROCESSING_REQUIRED;
1678 } else
1679 status = centry->status;
1681 DEBUG(10,("enum_local_groups: [Cached] - cached list for domain %s status: %s\n",
1682 domain->name, nt_errstr(status) ));
1684 centry_free(centry);
1685 return status;
1687 do_query:
1688 *num_entries = 0;
1689 *info = NULL;
1691 /* Return status value returned by seq number check */
1693 if (!NT_STATUS_IS_OK(domain->last_status))
1694 return domain->last_status;
1696 DEBUG(10,("enum_local_groups: [Cached] - doing backend query for list for domain %s\n",
1697 domain->name ));
1699 status = domain->backend->enum_local_groups(domain, mem_ctx, num_entries, info);
1701 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1702 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1703 if (!domain->internal && old_status) {
1704 set_domain_offline(domain);
1706 if (cache->tdb &&
1707 !domain->internal &&
1708 !domain->online &&
1709 old_status) {
1710 centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
1711 if (centry) {
1712 goto do_fetch_cache;
1716 /* and save it */
1717 refresh_sequence_number(domain);
1718 if (!NT_STATUS_IS_OK(status)) {
1719 return status;
1721 centry = centry_start(domain, status);
1722 if (!centry)
1723 goto skip_save;
1724 centry_put_uint32(centry, *num_entries);
1725 for (i=0; i<(*num_entries); i++) {
1726 centry_put_string(centry, (*info)[i].acct_name);
1727 centry_put_string(centry, (*info)[i].acct_desc);
1728 centry_put_uint32(centry, (*info)[i].rid);
1730 centry_end(centry, "GL/%s/local", domain->name);
1731 centry_free(centry);
1733 skip_save:
1734 return status;
1737 struct wcache_name_to_sid_state {
1738 struct dom_sid *sid;
1739 enum lsa_SidType *type;
1740 bool offline;
1741 bool found;
1744 static void wcache_name_to_sid_fn(const struct dom_sid *sid,
1745 enum lsa_SidType type, time_t timeout,
1746 void *private_data)
1748 struct wcache_name_to_sid_state *state = private_data;
1750 *state->sid = *sid;
1751 *state->type = type;
1752 state->found = (state->offline || (timeout > time(NULL)));
1755 static NTSTATUS wcache_name_to_sid(struct winbindd_domain *domain,
1756 const char *domain_name,
1757 const char *name,
1758 struct dom_sid *sid,
1759 enum lsa_SidType *type)
1761 struct wcache_name_to_sid_state state = {
1762 .sid = sid, .type = type, .found = false,
1763 .offline = is_domain_offline(domain),
1765 bool ok;
1767 ok = namemap_cache_find_name(domain_name, name, wcache_name_to_sid_fn,
1768 &state);
1769 if (!ok) {
1770 DBG_DEBUG("namemap_cache_find_name failed\n");
1771 return NT_STATUS_NOT_FOUND;
1773 if (!state.found) {
1774 DBG_DEBUG("cache entry not found\n");
1775 return NT_STATUS_NOT_FOUND;
1777 if (*type == SID_NAME_UNKNOWN) {
1778 return NT_STATUS_NONE_MAPPED;
1781 return NT_STATUS_OK;
1784 /* convert a single name to a sid in a domain */
1785 NTSTATUS wb_cache_name_to_sid(struct winbindd_domain *domain,
1786 TALLOC_CTX *mem_ctx,
1787 const char *domain_name,
1788 const char *name,
1789 uint32_t flags,
1790 struct dom_sid *sid,
1791 enum lsa_SidType *type)
1793 NTSTATUS status;
1794 bool old_status;
1795 const char *dom_name;
1797 old_status = domain->online;
1799 status = wcache_name_to_sid(domain, domain_name, name, sid, type);
1800 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
1801 return status;
1804 ZERO_STRUCTP(sid);
1806 /* If the seq number check indicated that there is a problem
1807 * with this DC, then return that status... except for
1808 * access_denied. This is special because the dc may be in
1809 * "restrict anonymous = 1" mode, in which case it will deny
1810 * most unauthenticated operations, but *will* allow the LSA
1811 * name-to-sid that we try as a fallback. */
1813 if (!(NT_STATUS_IS_OK(domain->last_status)
1814 || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1815 return domain->last_status;
1817 DEBUG(10,("name_to_sid: [Cached] - doing backend query for name for domain %s\n",
1818 domain->name ));
1820 winbindd_domain_init_backend(domain);
1821 status = domain->backend->name_to_sid(domain, mem_ctx, domain_name,
1822 name, flags, &dom_name, sid, type);
1824 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1825 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1826 if (!domain->internal && old_status) {
1827 set_domain_offline(domain);
1829 if (!domain->internal &&
1830 !domain->online &&
1831 old_status) {
1832 NTSTATUS cache_status;
1833 cache_status = wcache_name_to_sid(domain, domain_name, name, sid, type);
1834 return cache_status;
1837 /* and save it */
1839 if (domain->online &&
1840 (NT_STATUS_IS_OK(status) || NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED))) {
1841 enum lsa_SidType save_type = *type;
1843 if (NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) {
1844 save_type = SID_NAME_UNKNOWN;
1847 wcache_save_name_to_sid(domain, status, domain_name, name, sid,
1848 save_type);
1850 /* Only save the reverse mapping if this was not a UPN */
1851 if (!strchr(name, '@')) {
1852 if (!strupper_m(discard_const_p(char, domain_name))) {
1853 return NT_STATUS_INVALID_PARAMETER;
1855 (void)strlower_m(discard_const_p(char, name));
1856 wcache_save_sid_to_name(domain, status, sid,
1857 dom_name, name, save_type);
1861 return status;
1864 struct wcache_sid_to_name_state {
1865 TALLOC_CTX *mem_ctx;
1866 char **domain_name;
1867 char **name;
1868 enum lsa_SidType *type;
1869 bool offline;
1870 bool found;
1873 static void wcache_sid_to_name_fn(const char *domain, const char *name,
1874 enum lsa_SidType type, time_t timeout,
1875 void *private_data)
1877 struct wcache_sid_to_name_state *state = private_data;
1879 *state->domain_name = talloc_strdup(state->mem_ctx, domain);
1880 if (*state->domain_name == NULL) {
1881 return;
1883 *state->name = talloc_strdup(state->mem_ctx, name);
1884 if (*state->name == NULL) {
1885 return;
1887 *state->type = type;
1888 state->found = (state->offline || (timeout > time(NULL)));
1891 static NTSTATUS wcache_sid_to_name(struct winbindd_domain *domain,
1892 const struct dom_sid *sid,
1893 TALLOC_CTX *mem_ctx,
1894 char **domain_name,
1895 char **name,
1896 enum lsa_SidType *type)
1898 struct wcache_sid_to_name_state state = {
1899 .mem_ctx = mem_ctx, .found = false,
1900 .domain_name = domain_name, .name = name, .type = type,
1901 .offline = is_domain_offline(domain)
1903 bool ok;
1905 ok = namemap_cache_find_sid(sid, wcache_sid_to_name_fn, &state);
1906 if (!ok) {
1907 DBG_DEBUG("namemap_cache_find_name failed\n");
1908 return NT_STATUS_NOT_FOUND;
1910 if (!state.found) {
1911 DBG_DEBUG("cache entry not found\n");
1912 return NT_STATUS_NOT_FOUND;
1914 if (*type == SID_NAME_UNKNOWN) {
1915 return NT_STATUS_NONE_MAPPED;
1918 return NT_STATUS_OK;
1921 /* convert a sid to a user or group name. The sid is guaranteed to be in the domain
1922 given */
1923 NTSTATUS wb_cache_sid_to_name(struct winbindd_domain *domain,
1924 TALLOC_CTX *mem_ctx,
1925 const struct dom_sid *sid,
1926 char **domain_name,
1927 char **name,
1928 enum lsa_SidType *type)
1930 NTSTATUS status;
1931 bool old_status;
1933 old_status = domain->online;
1934 status = wcache_sid_to_name(domain, sid, mem_ctx, domain_name, name,
1935 type);
1936 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
1937 return status;
1940 *name = NULL;
1941 *domain_name = NULL;
1943 /* If the seq number check indicated that there is a problem
1944 * with this DC, then return that status... except for
1945 * access_denied. This is special because the dc may be in
1946 * "restrict anonymous = 1" mode, in which case it will deny
1947 * most unauthenticated operations, but *will* allow the LSA
1948 * sid-to-name that we try as a fallback. */
1950 if (!(NT_STATUS_IS_OK(domain->last_status)
1951 || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1952 return domain->last_status;
1954 DEBUG(10,("sid_to_name: [Cached] - doing backend query for name for domain %s\n",
1955 domain->name ));
1957 winbindd_domain_init_backend(domain);
1959 status = domain->backend->sid_to_name(domain, mem_ctx, sid, domain_name, name, type);
1961 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1962 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1963 if (!domain->internal && old_status) {
1964 set_domain_offline(domain);
1966 if (!domain->internal &&
1967 !domain->online &&
1968 old_status) {
1969 NTSTATUS cache_status;
1970 cache_status = wcache_sid_to_name(domain, sid, mem_ctx,
1971 domain_name, name, type);
1972 return cache_status;
1975 /* and save it */
1976 if (!NT_STATUS_IS_OK(status)) {
1977 return status;
1979 wcache_save_sid_to_name(domain, status, sid, *domain_name, *name, *type);
1981 /* We can't save the name to sid mapping here, as with sid history a
1982 * later name2sid would give the wrong sid. */
1984 return status;
1987 NTSTATUS wb_cache_rids_to_names(struct winbindd_domain *domain,
1988 TALLOC_CTX *mem_ctx,
1989 const struct dom_sid *domain_sid,
1990 uint32_t *rids,
1991 size_t num_rids,
1992 char **domain_name,
1993 char ***names,
1994 enum lsa_SidType **types)
1996 struct winbind_cache *cache = get_cache(domain);
1997 size_t i;
1998 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
1999 bool have_mapped;
2000 bool have_unmapped;
2001 bool old_status;
2003 old_status = domain->online;
2004 *domain_name = NULL;
2005 *names = NULL;
2006 *types = NULL;
2008 if (!cache->tdb) {
2009 goto do_query;
2012 if (num_rids == 0) {
2013 return NT_STATUS_OK;
2016 *names = talloc_array(mem_ctx, char *, num_rids);
2017 *types = talloc_array(mem_ctx, enum lsa_SidType, num_rids);
2019 if ((*names == NULL) || (*types == NULL)) {
2020 result = NT_STATUS_NO_MEMORY;
2021 goto error;
2024 have_mapped = have_unmapped = false;
2026 for (i=0; i<num_rids; i++) {
2027 struct dom_sid sid;
2028 NTSTATUS status;
2029 enum lsa_SidType type;
2030 char *dom, *name;
2032 if (!sid_compose(&sid, domain_sid, rids[i])) {
2033 result = NT_STATUS_INTERNAL_ERROR;
2034 goto error;
2037 status = wcache_sid_to_name(domain, &sid, *names, &dom,
2038 &name, &type);
2040 (*types)[i] = SID_NAME_UNKNOWN;
2041 (*names)[i] = talloc_strdup(*names, "");
2043 if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2044 /* not cached */
2045 goto do_query;
2048 if (NT_STATUS_IS_OK(status)) {
2049 have_mapped = true;
2050 (*types)[i] = type;
2052 if (*domain_name == NULL) {
2053 *domain_name = dom;
2054 } else {
2055 TALLOC_FREE(dom);
2058 (*names)[i] = name;
2060 } else if (NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) {
2061 have_unmapped = true;
2062 } else {
2063 /* something's definitely wrong */
2064 result = status;
2065 goto error;
2069 if (!have_mapped) {
2070 return NT_STATUS_NONE_MAPPED;
2072 if (!have_unmapped) {
2073 return NT_STATUS_OK;
2075 return STATUS_SOME_UNMAPPED;
2077 do_query:
2079 TALLOC_FREE(*names);
2080 TALLOC_FREE(*types);
2082 result = domain->backend->rids_to_names(domain, mem_ctx, domain_sid,
2083 rids, num_rids, domain_name,
2084 names, types);
2086 if (NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
2087 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2088 if (!domain->internal && old_status) {
2089 set_domain_offline(domain);
2091 if (cache->tdb &&
2092 !domain->internal &&
2093 !domain->online &&
2094 old_status) {
2095 have_mapped = have_unmapped = false;
2097 *names = talloc_array(mem_ctx, char *, num_rids);
2098 if (*names == NULL) {
2099 result = NT_STATUS_NO_MEMORY;
2100 goto error;
2103 *types = talloc_array(mem_ctx, enum lsa_SidType,
2104 num_rids);
2105 if (*types == NULL) {
2106 result = NT_STATUS_NO_MEMORY;
2107 goto error;
2110 for (i=0; i<num_rids; i++) {
2111 struct dom_sid sid;
2112 NTSTATUS status;
2113 enum lsa_SidType type;
2114 char *dom, *name;
2116 if (!sid_compose(&sid, domain_sid, rids[i])) {
2117 result = NT_STATUS_INTERNAL_ERROR;
2118 goto error;
2121 status = wcache_sid_to_name(domain, &sid,
2122 *names, &dom,
2123 &name, &type);
2125 (*types)[i] = SID_NAME_UNKNOWN;
2126 (*names)[i] = talloc_strdup(*names, "");
2128 if (NT_STATUS_IS_OK(status)) {
2129 have_mapped = true;
2130 (*types)[i] = type;
2132 if (*domain_name == NULL) {
2133 *domain_name = dom;
2134 } else {
2135 TALLOC_FREE(dom);
2138 (*names)[i] = name;
2140 } else if (NT_STATUS_EQUAL(
2141 status,
2142 NT_STATUS_NONE_MAPPED)) {
2143 have_unmapped = true;
2144 } else {
2145 /* something's definitely wrong */
2146 result = status;
2147 goto error;
2151 if (!have_mapped) {
2152 return NT_STATUS_NONE_MAPPED;
2154 if (!have_unmapped) {
2155 return NT_STATUS_OK;
2157 return STATUS_SOME_UNMAPPED;
2161 None of the queried rids has been found so save all negative entries
2163 if (NT_STATUS_EQUAL(result, NT_STATUS_NONE_MAPPED)) {
2164 for (i = 0; i < num_rids; i++) {
2165 struct dom_sid sid;
2166 const char *name = "";
2167 const enum lsa_SidType type = SID_NAME_UNKNOWN;
2168 NTSTATUS status = NT_STATUS_NONE_MAPPED;
2170 if (!sid_compose(&sid, domain_sid, rids[i])) {
2171 return NT_STATUS_INTERNAL_ERROR;
2174 wcache_save_sid_to_name(domain, status, &sid, *domain_name,
2175 name, type);
2178 return result;
2182 Some or all of the queried rids have been found.
2184 if (!NT_STATUS_IS_OK(result) &&
2185 !NT_STATUS_EQUAL(result, STATUS_SOME_UNMAPPED)) {
2186 return result;
2189 refresh_sequence_number(domain);
2191 for (i=0; i<num_rids; i++) {
2192 struct dom_sid sid;
2193 NTSTATUS status;
2195 if (!sid_compose(&sid, domain_sid, rids[i])) {
2196 result = NT_STATUS_INTERNAL_ERROR;
2197 goto error;
2200 status = (*types)[i] == SID_NAME_UNKNOWN ?
2201 NT_STATUS_NONE_MAPPED : NT_STATUS_OK;
2203 wcache_save_sid_to_name(domain, status, &sid, *domain_name,
2204 (*names)[i], (*types)[i]);
2207 return result;
2209 error:
2210 TALLOC_FREE(*names);
2211 TALLOC_FREE(*types);
2212 return result;
2215 static NTSTATUS wcache_query_user(struct winbindd_domain *domain,
2216 TALLOC_CTX *mem_ctx,
2217 const struct dom_sid *user_sid,
2218 struct wbint_userinfo *info)
2220 struct winbind_cache *cache = get_cache(domain);
2221 struct cache_entry *centry = NULL;
2222 NTSTATUS status;
2223 char *sid_string;
2225 if (cache->tdb == NULL) {
2226 return NT_STATUS_NOT_FOUND;
2229 sid_string = sid_string_tos(user_sid);
2230 if (sid_string == NULL) {
2231 return NT_STATUS_NO_MEMORY;
2234 centry = wcache_fetch(cache, domain, "U/%s", sid_string);
2235 TALLOC_FREE(sid_string);
2236 if (centry == NULL) {
2237 return NT_STATUS_NOT_FOUND;
2241 * If we have an access denied cache entry and a cached info3
2242 * in the samlogon cache then do a query. This will force the
2243 * rpc back end to return the info3 data.
2246 if (NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED) &&
2247 netsamlogon_cache_have(user_sid)) {
2248 DEBUG(10, ("query_user: cached access denied and have cached "
2249 "info3\n"));
2250 domain->last_status = NT_STATUS_OK;
2251 centry_free(centry);
2252 return NT_STATUS_NOT_FOUND;
2255 /* if status is not ok then this is a negative hit
2256 and the rest of the data doesn't matter */
2257 status = centry->status;
2258 if (NT_STATUS_IS_OK(status)) {
2259 info->domain_name = centry_string(centry, mem_ctx);
2260 info->acct_name = centry_string(centry, mem_ctx);
2261 info->full_name = centry_string(centry, mem_ctx);
2262 info->homedir = centry_string(centry, mem_ctx);
2263 info->shell = centry_string(centry, mem_ctx);
2264 info->uid = centry_uint32(centry);
2265 info->primary_gid = centry_uint32(centry);
2266 info->primary_group_name = centry_string(centry, mem_ctx);
2267 centry_sid(centry, &info->user_sid);
2268 centry_sid(centry, &info->group_sid);
2271 DEBUG(10,("query_user: [Cached] - cached info for domain %s status: "
2272 "%s\n", domain->name, nt_errstr(status) ));
2274 centry_free(centry);
2275 return status;
2280 * @brief Query a fullname from the username cache (for further gecos processing)
2282 * @param domain A pointer to the winbindd_domain struct.
2283 * @param mem_ctx The talloc context.
2284 * @param user_sid The user sid.
2285 * @param full_name A pointer to the full_name string.
2287 * @return NTSTATUS code
2289 NTSTATUS wcache_query_user_fullname(struct winbindd_domain *domain,
2290 TALLOC_CTX *mem_ctx,
2291 const struct dom_sid *user_sid,
2292 const char **full_name)
2294 NTSTATUS status;
2295 struct wbint_userinfo info;
2297 status = wcache_query_user(domain, mem_ctx, user_sid, &info);
2298 if (!NT_STATUS_IS_OK(status)) {
2299 return status;
2302 if (info.full_name != NULL) {
2303 *full_name = talloc_strdup(mem_ctx, info.full_name);
2304 if (*full_name == NULL) {
2305 return NT_STATUS_NO_MEMORY;
2309 return NT_STATUS_OK;
2312 static NTSTATUS wcache_lookup_usergroups(struct winbindd_domain *domain,
2313 TALLOC_CTX *mem_ctx,
2314 const struct dom_sid *user_sid,
2315 uint32_t *pnum_sids,
2316 struct dom_sid **psids)
2318 struct winbind_cache *cache = get_cache(domain);
2319 struct cache_entry *centry = NULL;
2320 NTSTATUS status;
2321 uint32_t i, num_sids;
2322 struct dom_sid *sids;
2323 fstring sid_string;
2325 if (cache->tdb == NULL) {
2326 return NT_STATUS_NOT_FOUND;
2329 centry = wcache_fetch(cache, domain, "UG/%s",
2330 sid_to_fstring(sid_string, user_sid));
2331 if (centry == NULL) {
2332 return NT_STATUS_NOT_FOUND;
2335 /* If we have an access denied cache entry and a cached info3 in the
2336 samlogon cache then do a query. This will force the rpc back end
2337 to return the info3 data. */
2339 if (NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)
2340 && netsamlogon_cache_have(user_sid)) {
2341 DEBUG(10, ("lookup_usergroups: cached access denied and have "
2342 "cached info3\n"));
2343 domain->last_status = NT_STATUS_OK;
2344 centry_free(centry);
2345 return NT_STATUS_NOT_FOUND;
2348 num_sids = centry_uint32(centry);
2349 sids = talloc_array(mem_ctx, struct dom_sid, num_sids);
2350 if (sids == NULL) {
2351 centry_free(centry);
2352 return NT_STATUS_NO_MEMORY;
2355 for (i=0; i<num_sids; i++) {
2356 centry_sid(centry, &sids[i]);
2359 status = centry->status;
2361 DEBUG(10,("lookup_usergroups: [Cached] - cached info for domain %s "
2362 "status: %s\n", domain->name, nt_errstr(status)));
2364 centry_free(centry);
2366 *pnum_sids = num_sids;
2367 *psids = sids;
2368 return status;
2371 /* Lookup groups a user is a member of. */
2372 NTSTATUS wb_cache_lookup_usergroups(struct winbindd_domain *domain,
2373 TALLOC_CTX *mem_ctx,
2374 const struct dom_sid *user_sid,
2375 uint32_t *num_groups,
2376 struct dom_sid **user_gids)
2378 struct cache_entry *centry = NULL;
2379 NTSTATUS status;
2380 unsigned int i;
2381 fstring sid_string;
2382 bool old_status;
2384 old_status = domain->online;
2385 status = wcache_lookup_usergroups(domain, mem_ctx, user_sid,
2386 num_groups, user_gids);
2387 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2388 return status;
2391 (*num_groups) = 0;
2392 (*user_gids) = NULL;
2394 /* Return status value returned by seq number check */
2396 if (!NT_STATUS_IS_OK(domain->last_status))
2397 return domain->last_status;
2399 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info for domain %s\n",
2400 domain->name ));
2402 status = domain->backend->lookup_usergroups(domain, mem_ctx, user_sid, num_groups, user_gids);
2404 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2405 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2406 if (!domain->internal && old_status) {
2407 set_domain_offline(domain);
2409 if (!domain->internal &&
2410 !domain->online &&
2411 old_status) {
2412 NTSTATUS cache_status;
2413 cache_status = wcache_lookup_usergroups(domain, mem_ctx, user_sid,
2414 num_groups, user_gids);
2415 return cache_status;
2418 if ( NT_STATUS_EQUAL(status, NT_STATUS_SYNCHRONIZATION_REQUIRED) )
2419 goto skip_save;
2421 /* and save it */
2422 refresh_sequence_number(domain);
2423 if (!NT_STATUS_IS_OK(status)) {
2424 return status;
2426 centry = centry_start(domain, status);
2427 if (!centry)
2428 goto skip_save;
2430 centry_put_uint32(centry, *num_groups);
2431 for (i=0; i<(*num_groups); i++) {
2432 centry_put_sid(centry, &(*user_gids)[i]);
2435 centry_end(centry, "UG/%s", sid_to_fstring(sid_string, user_sid));
2436 centry_free(centry);
2438 skip_save:
2439 return status;
2442 static char *wcache_make_sidlist(TALLOC_CTX *mem_ctx, uint32_t num_sids,
2443 const struct dom_sid *sids)
2445 uint32_t i;
2446 char *sidlist;
2448 sidlist = talloc_strdup(mem_ctx, "");
2449 if (sidlist == NULL) {
2450 return NULL;
2452 for (i=0; i<num_sids; i++) {
2453 fstring tmp;
2454 sidlist = talloc_asprintf_append_buffer(
2455 sidlist, "/%s", sid_to_fstring(tmp, &sids[i]));
2456 if (sidlist == NULL) {
2457 return NULL;
2460 return sidlist;
2463 static NTSTATUS wcache_lookup_useraliases(struct winbindd_domain *domain,
2464 TALLOC_CTX *mem_ctx,
2465 uint32_t num_sids,
2466 const struct dom_sid *sids,
2467 uint32_t *pnum_aliases,
2468 uint32_t **paliases)
2470 struct winbind_cache *cache = get_cache(domain);
2471 struct cache_entry *centry = NULL;
2472 uint32_t i, num_aliases;
2473 uint32_t *aliases;
2474 NTSTATUS status;
2475 char *sidlist;
2477 if (cache->tdb == NULL) {
2478 return NT_STATUS_NOT_FOUND;
2481 if (num_sids == 0) {
2482 *pnum_aliases = 0;
2483 *paliases = NULL;
2484 return NT_STATUS_OK;
2487 /* We need to cache indexed by the whole list of SIDs, the aliases
2488 * resulting might come from any of the SIDs. */
2490 sidlist = wcache_make_sidlist(talloc_tos(), num_sids, sids);
2491 if (sidlist == NULL) {
2492 return NT_STATUS_NO_MEMORY;
2495 centry = wcache_fetch(cache, domain, "UA%s", sidlist);
2496 TALLOC_FREE(sidlist);
2497 if (centry == NULL) {
2498 return NT_STATUS_NOT_FOUND;
2501 num_aliases = centry_uint32(centry);
2502 aliases = talloc_array(mem_ctx, uint32_t, num_aliases);
2503 if (aliases == NULL) {
2504 centry_free(centry);
2505 return NT_STATUS_NO_MEMORY;
2508 for (i=0; i<num_aliases; i++) {
2509 aliases[i] = centry_uint32(centry);
2512 status = centry->status;
2514 DEBUG(10,("lookup_useraliases: [Cached] - cached info for domain: %s "
2515 "status %s\n", domain->name, nt_errstr(status)));
2517 centry_free(centry);
2519 *pnum_aliases = num_aliases;
2520 *paliases = aliases;
2522 return status;
2525 NTSTATUS wb_cache_lookup_useraliases(struct winbindd_domain *domain,
2526 TALLOC_CTX *mem_ctx,
2527 uint32_t num_sids,
2528 const struct dom_sid *sids,
2529 uint32_t *num_aliases,
2530 uint32_t **alias_rids)
2532 struct cache_entry *centry = NULL;
2533 NTSTATUS status;
2534 char *sidlist;
2535 uint32_t i;
2536 bool old_status;
2538 old_status = domain->online;
2539 status = wcache_lookup_useraliases(domain, mem_ctx, num_sids, sids,
2540 num_aliases, alias_rids);
2541 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2542 return status;
2545 (*num_aliases) = 0;
2546 (*alias_rids) = NULL;
2548 if (!NT_STATUS_IS_OK(domain->last_status))
2549 return domain->last_status;
2551 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info "
2552 "for domain %s\n", domain->name ));
2554 sidlist = wcache_make_sidlist(talloc_tos(), num_sids, sids);
2555 if (sidlist == NULL) {
2556 return NT_STATUS_NO_MEMORY;
2559 status = domain->backend->lookup_useraliases(domain, mem_ctx,
2560 num_sids, sids,
2561 num_aliases, alias_rids);
2563 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2564 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2565 if (!domain->internal && old_status) {
2566 set_domain_offline(domain);
2568 if (!domain->internal &&
2569 !domain->online &&
2570 old_status) {
2571 NTSTATUS cache_status;
2572 cache_status = wcache_lookup_useraliases(domain, mem_ctx, num_sids,
2573 sids, num_aliases, alias_rids);
2574 return cache_status;
2577 /* and save it */
2578 refresh_sequence_number(domain);
2579 if (!NT_STATUS_IS_OK(status)) {
2580 return status;
2582 centry = centry_start(domain, status);
2583 if (!centry)
2584 goto skip_save;
2585 centry_put_uint32(centry, *num_aliases);
2586 for (i=0; i<(*num_aliases); i++)
2587 centry_put_uint32(centry, (*alias_rids)[i]);
2588 centry_end(centry, "UA%s", sidlist);
2589 centry_free(centry);
2591 skip_save:
2592 return status;
2595 static NTSTATUS wcache_lookup_groupmem(struct winbindd_domain *domain,
2596 TALLOC_CTX *mem_ctx,
2597 const struct dom_sid *group_sid,
2598 uint32_t *num_names,
2599 struct dom_sid **sid_mem, char ***names,
2600 uint32_t **name_types)
2602 struct winbind_cache *cache = get_cache(domain);
2603 struct cache_entry *centry = NULL;
2604 NTSTATUS status;
2605 unsigned int i;
2606 char *sid_string;
2608 if (cache->tdb == NULL) {
2609 return NT_STATUS_NOT_FOUND;
2612 sid_string = sid_string_tos(group_sid);
2613 if (sid_string == NULL) {
2614 return NT_STATUS_NO_MEMORY;
2617 centry = wcache_fetch(cache, domain, "GM/%s", sid_string);
2618 TALLOC_FREE(sid_string);
2619 if (centry == NULL) {
2620 return NT_STATUS_NOT_FOUND;
2623 *sid_mem = NULL;
2624 *names = NULL;
2625 *name_types = NULL;
2627 *num_names = centry_uint32(centry);
2628 if (*num_names == 0) {
2629 centry_free(centry);
2630 return NT_STATUS_OK;
2633 *sid_mem = talloc_array(mem_ctx, struct dom_sid, *num_names);
2634 *names = talloc_array(mem_ctx, char *, *num_names);
2635 *name_types = talloc_array(mem_ctx, uint32_t, *num_names);
2637 if ((*sid_mem == NULL) || (*names == NULL) || (*name_types == NULL)) {
2638 TALLOC_FREE(*sid_mem);
2639 TALLOC_FREE(*names);
2640 TALLOC_FREE(*name_types);
2641 centry_free(centry);
2642 return NT_STATUS_NO_MEMORY;
2645 for (i=0; i<(*num_names); i++) {
2646 centry_sid(centry, &(*sid_mem)[i]);
2647 (*names)[i] = centry_string(centry, mem_ctx);
2648 (*name_types)[i] = centry_uint32(centry);
2651 status = centry->status;
2653 DEBUG(10,("lookup_groupmem: [Cached] - cached info for domain %s "
2654 "status: %s\n", domain->name, nt_errstr(status)));
2656 centry_free(centry);
2657 return status;
2660 NTSTATUS wb_cache_lookup_groupmem(struct winbindd_domain *domain,
2661 TALLOC_CTX *mem_ctx,
2662 const struct dom_sid *group_sid,
2663 enum lsa_SidType type,
2664 uint32_t *num_names,
2665 struct dom_sid **sid_mem,
2666 char ***names,
2667 uint32_t **name_types)
2669 struct cache_entry *centry = NULL;
2670 NTSTATUS status;
2671 unsigned int i;
2672 fstring sid_string;
2673 bool old_status;
2675 old_status = domain->online;
2676 status = wcache_lookup_groupmem(domain, mem_ctx, group_sid, num_names,
2677 sid_mem, names, name_types);
2678 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2679 return status;
2682 (*num_names) = 0;
2683 (*sid_mem) = NULL;
2684 (*names) = NULL;
2685 (*name_types) = NULL;
2687 /* Return status value returned by seq number check */
2689 if (!NT_STATUS_IS_OK(domain->last_status))
2690 return domain->last_status;
2692 DEBUG(10,("lookup_groupmem: [Cached] - doing backend query for info for domain %s\n",
2693 domain->name ));
2695 status = domain->backend->lookup_groupmem(domain, mem_ctx, group_sid,
2696 type, num_names,
2697 sid_mem, names, name_types);
2699 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2700 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2701 if (!domain->internal && old_status) {
2702 set_domain_offline(domain);
2704 if (!domain->internal &&
2705 !domain->online &&
2706 old_status) {
2707 NTSTATUS cache_status;
2708 cache_status = wcache_lookup_groupmem(domain, mem_ctx, group_sid,
2709 num_names, sid_mem, names,
2710 name_types);
2711 return cache_status;
2714 /* and save it */
2715 refresh_sequence_number(domain);
2716 if (!NT_STATUS_IS_OK(status)) {
2717 return status;
2719 centry = centry_start(domain, status);
2720 if (!centry)
2721 goto skip_save;
2722 centry_put_uint32(centry, *num_names);
2723 for (i=0; i<(*num_names); i++) {
2724 centry_put_sid(centry, &(*sid_mem)[i]);
2725 centry_put_string(centry, (*names)[i]);
2726 centry_put_uint32(centry, (*name_types)[i]);
2728 centry_end(centry, "GM/%s", sid_to_fstring(sid_string, group_sid));
2729 centry_free(centry);
2731 skip_save:
2732 return status;
2735 /* find the sequence number for a domain */
2736 NTSTATUS wb_cache_sequence_number(struct winbindd_domain *domain,
2737 uint32_t *seq)
2739 refresh_sequence_number(domain);
2741 *seq = domain->sequence_number;
2743 return NT_STATUS_OK;
2746 /* enumerate trusted domains
2747 * (we need to have the list of trustdoms in the cache when we go offline) -
2748 * Guenther */
2749 NTSTATUS wb_cache_trusted_domains(struct winbindd_domain *domain,
2750 TALLOC_CTX *mem_ctx,
2751 struct netr_DomainTrustList *trusts)
2753 NTSTATUS status;
2754 struct winbind_cache *cache;
2755 struct winbindd_tdc_domain *dom_list = NULL;
2756 size_t num_domains = 0;
2757 bool retval = false;
2758 size_t i;
2759 bool old_status;
2761 old_status = domain->online;
2762 trusts->count = 0;
2763 trusts->array = NULL;
2765 cache = get_cache(domain);
2766 if (!cache || !cache->tdb) {
2767 goto do_query;
2770 if (domain->online) {
2771 goto do_query;
2774 retval = wcache_tdc_fetch_list(&dom_list, &num_domains);
2775 if (!retval || !num_domains || !dom_list) {
2776 TALLOC_FREE(dom_list);
2777 goto do_query;
2780 do_fetch_cache:
2781 trusts->array = talloc_zero_array(mem_ctx, struct netr_DomainTrust, num_domains);
2782 if (!trusts->array) {
2783 TALLOC_FREE(dom_list);
2784 return NT_STATUS_NO_MEMORY;
2787 for (i = 0; i < num_domains; i++) {
2788 struct netr_DomainTrust *trust;
2789 struct dom_sid *sid;
2790 struct winbindd_domain *dom;
2792 dom = find_domain_from_name_noinit(dom_list[i].domain_name);
2793 if (dom && dom->internal) {
2794 continue;
2797 trust = &trusts->array[trusts->count];
2798 trust->netbios_name = talloc_strdup(trusts->array, dom_list[i].domain_name);
2799 trust->dns_name = talloc_strdup(trusts->array, dom_list[i].dns_name);
2800 sid = talloc(trusts->array, struct dom_sid);
2801 if (!trust->netbios_name || !trust->dns_name ||
2802 !sid) {
2803 TALLOC_FREE(dom_list);
2804 TALLOC_FREE(trusts->array);
2805 return NT_STATUS_NO_MEMORY;
2808 trust->trust_flags = dom_list[i].trust_flags;
2809 trust->trust_attributes = dom_list[i].trust_attribs;
2810 trust->trust_type = dom_list[i].trust_type;
2811 sid_copy(sid, &dom_list[i].sid);
2812 trust->sid = sid;
2813 trusts->count++;
2816 TALLOC_FREE(dom_list);
2817 return NT_STATUS_OK;
2819 do_query:
2820 /* Return status value returned by seq number check */
2822 if (!NT_STATUS_IS_OK(domain->last_status))
2823 return domain->last_status;
2825 DEBUG(10,("trusted_domains: [Cached] - doing backend query for info for domain %s\n",
2826 domain->name ));
2828 status = domain->backend->trusted_domains(domain, mem_ctx, trusts);
2830 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2831 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2832 if (!domain->internal && old_status) {
2833 set_domain_offline(domain);
2835 if (!domain->internal &&
2836 !domain->online &&
2837 old_status) {
2838 retval = wcache_tdc_fetch_list(&dom_list, &num_domains);
2839 if (retval && num_domains && dom_list) {
2840 TALLOC_FREE(trusts->array);
2841 trusts->count = 0;
2842 goto do_fetch_cache;
2846 /* no trusts gives NT_STATUS_NO_MORE_ENTRIES resetting to NT_STATUS_OK
2847 * so that the generic centry handling still applies correctly -
2848 * Guenther*/
2850 if (!NT_STATUS_IS_ERR(status)) {
2851 status = NT_STATUS_OK;
2853 return status;
2856 /* get lockout policy */
2857 NTSTATUS wb_cache_lockout_policy(struct winbindd_domain *domain,
2858 TALLOC_CTX *mem_ctx,
2859 struct samr_DomInfo12 *policy)
2861 struct winbind_cache *cache = get_cache(domain);
2862 struct cache_entry *centry = NULL;
2863 NTSTATUS status;
2864 bool old_status;
2866 old_status = domain->online;
2867 if (!cache->tdb)
2868 goto do_query;
2870 centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
2872 if (!centry)
2873 goto do_query;
2875 do_fetch_cache:
2876 policy->lockout_duration = centry_nttime(centry);
2877 policy->lockout_window = centry_nttime(centry);
2878 policy->lockout_threshold = centry_uint16(centry);
2880 status = centry->status;
2882 DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2883 domain->name, nt_errstr(status) ));
2885 centry_free(centry);
2886 return status;
2888 do_query:
2889 ZERO_STRUCTP(policy);
2891 /* Return status value returned by seq number check */
2893 if (!NT_STATUS_IS_OK(domain->last_status))
2894 return domain->last_status;
2896 DEBUG(10,("lockout_policy: [Cached] - doing backend query for info for domain %s\n",
2897 domain->name ));
2899 status = domain->backend->lockout_policy(domain, mem_ctx, policy);
2901 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2902 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2903 if (!domain->internal && old_status) {
2904 set_domain_offline(domain);
2906 if (cache->tdb &&
2907 !domain->internal &&
2908 !domain->online &&
2909 old_status) {
2910 centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
2911 if (centry) {
2912 goto do_fetch_cache;
2916 /* and save it */
2917 refresh_sequence_number(domain);
2918 if (!NT_STATUS_IS_OK(status)) {
2919 return status;
2921 wcache_save_lockout_policy(domain, status, policy);
2923 return status;
2926 /* get password policy */
2927 NTSTATUS wb_cache_password_policy(struct winbindd_domain *domain,
2928 TALLOC_CTX *mem_ctx,
2929 struct samr_DomInfo1 *policy)
2931 struct winbind_cache *cache = get_cache(domain);
2932 struct cache_entry *centry = NULL;
2933 NTSTATUS status;
2934 bool old_status;
2936 old_status = domain->online;
2937 if (!cache->tdb)
2938 goto do_query;
2940 centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
2942 if (!centry)
2943 goto do_query;
2945 do_fetch_cache:
2946 policy->min_password_length = centry_uint16(centry);
2947 policy->password_history_length = centry_uint16(centry);
2948 policy->password_properties = centry_uint32(centry);
2949 policy->max_password_age = centry_nttime(centry);
2950 policy->min_password_age = centry_nttime(centry);
2952 status = centry->status;
2954 DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2955 domain->name, nt_errstr(status) ));
2957 centry_free(centry);
2958 return status;
2960 do_query:
2961 ZERO_STRUCTP(policy);
2963 /* Return status value returned by seq number check */
2965 if (!NT_STATUS_IS_OK(domain->last_status))
2966 return domain->last_status;
2968 DEBUG(10,("password_policy: [Cached] - doing backend query for info for domain %s\n",
2969 domain->name ));
2971 status = domain->backend->password_policy(domain, mem_ctx, policy);
2973 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2974 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2975 if (!domain->internal && old_status) {
2976 set_domain_offline(domain);
2978 if (cache->tdb &&
2979 !domain->internal &&
2980 !domain->online &&
2981 old_status) {
2982 centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
2983 if (centry) {
2984 goto do_fetch_cache;
2988 /* and save it */
2989 refresh_sequence_number(domain);
2990 if (!NT_STATUS_IS_OK(status)) {
2991 return status;
2993 wcache_save_password_policy(domain, status, policy);
2995 return status;
2999 /* Invalidate cached user and group lists coherently */
3001 static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
3002 void *state)
3004 if (strncmp((const char *)kbuf.dptr, "UL/", 3) == 0 ||
3005 strncmp((const char *)kbuf.dptr, "GL/", 3) == 0)
3006 tdb_delete(the_tdb, kbuf);
3008 return 0;
3011 /* Invalidate the getpwnam and getgroups entries for a winbindd domain */
3013 void wcache_invalidate_samlogon(struct winbindd_domain *domain,
3014 const struct dom_sid *sid)
3016 fstring key_str, sid_string;
3017 struct winbind_cache *cache;
3019 /* don't clear cached U/SID and UG/SID entries when we want to logon
3020 * offline - gd */
3022 if (lp_winbind_offline_logon()) {
3023 return;
3026 if (!domain)
3027 return;
3029 cache = get_cache(domain);
3031 if (!cache->tdb) {
3032 return;
3035 /* Clear U/SID cache entry */
3036 fstr_sprintf(key_str, "U/%s", sid_to_fstring(sid_string, sid));
3037 DEBUG(10, ("wcache_invalidate_samlogon: clearing %s\n", key_str));
3038 tdb_delete(cache->tdb, string_tdb_data(key_str));
3040 /* Clear UG/SID cache entry */
3041 fstr_sprintf(key_str, "UG/%s", sid_to_fstring(sid_string, sid));
3042 DEBUG(10, ("wcache_invalidate_samlogon: clearing %s\n", key_str));
3043 tdb_delete(cache->tdb, string_tdb_data(key_str));
3045 /* Samba/winbindd never needs this. */
3046 netsamlogon_clear_cached_user(sid);
3049 bool wcache_invalidate_cache(void)
3051 struct winbindd_domain *domain;
3053 for (domain = domain_list(); domain; domain = domain->next) {
3054 struct winbind_cache *cache = get_cache(domain);
3056 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
3057 "entries for %s\n", domain->name));
3058 if (cache) {
3059 if (cache->tdb) {
3060 tdb_traverse(cache->tdb, traverse_fn, NULL);
3061 } else {
3062 return false;
3066 return true;
3069 bool wcache_invalidate_cache_noinit(void)
3071 struct winbindd_domain *domain;
3073 for (domain = domain_list(); domain; domain = domain->next) {
3074 struct winbind_cache *cache;
3076 /* Skip uninitialized domains. */
3077 if (!domain->initialized && !domain->internal) {
3078 continue;
3081 cache = get_cache(domain);
3083 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
3084 "entries for %s\n", domain->name));
3085 if (cache) {
3086 if (cache->tdb) {
3087 tdb_traverse(cache->tdb, traverse_fn, NULL);
3089 * Flushing cache has nothing to with domains.
3090 * return here if we successfully flushed once.
3091 * To avoid unnecessary traversing the cache.
3093 return true;
3094 } else {
3095 return false;
3099 return true;
3102 static bool init_wcache(void)
3104 char *db_path;
3106 if (wcache == NULL) {
3107 wcache = SMB_XMALLOC_P(struct winbind_cache);
3108 ZERO_STRUCTP(wcache);
3111 if (wcache->tdb != NULL)
3112 return true;
3114 db_path = wcache_path();
3115 if (db_path == NULL) {
3116 return false;
3119 /* when working offline we must not clear the cache on restart */
3120 wcache->tdb = tdb_open_log(db_path,
3121 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
3122 TDB_INCOMPATIBLE_HASH |
3123 (lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST)),
3124 O_RDWR|O_CREAT, 0600);
3125 TALLOC_FREE(db_path);
3126 if (wcache->tdb == NULL) {
3127 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
3128 return false;
3131 return true;
3134 /************************************************************************
3135 This is called by the parent to initialize the cache file.
3136 We don't need sophisticated locking here as we know we're the
3137 only opener.
3138 ************************************************************************/
3140 bool initialize_winbindd_cache(void)
3142 bool cache_bad = true;
3143 uint32_t vers;
3145 if (!init_wcache()) {
3146 DEBUG(0,("initialize_winbindd_cache: init_wcache failed.\n"));
3147 return false;
3150 /* Check version number. */
3151 if (tdb_fetch_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, &vers) &&
3152 vers == WINBINDD_CACHE_VERSION) {
3153 cache_bad = false;
3156 if (cache_bad) {
3157 char *db_path;
3159 DEBUG(0,("initialize_winbindd_cache: clearing cache "
3160 "and re-creating with version number %d\n",
3161 WINBINDD_CACHE_VERSION ));
3163 tdb_close(wcache->tdb);
3164 wcache->tdb = NULL;
3166 db_path = wcache_path();
3167 if (db_path == NULL) {
3168 return false;
3171 if (unlink(db_path) == -1) {
3172 DEBUG(0,("initialize_winbindd_cache: unlink %s failed %s ",
3173 db_path,
3174 strerror(errno) ));
3175 TALLOC_FREE(db_path);
3176 return false;
3178 TALLOC_FREE(db_path);
3179 if (!init_wcache()) {
3180 DEBUG(0,("initialize_winbindd_cache: re-initialization "
3181 "init_wcache failed.\n"));
3182 return false;
3185 /* Write the version. */
3186 if (!tdb_store_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, WINBINDD_CACHE_VERSION)) {
3187 DEBUG(0,("initialize_winbindd_cache: version number store failed %s\n",
3188 tdb_errorstr(wcache->tdb) ));
3189 return false;
3193 tdb_close(wcache->tdb);
3194 wcache->tdb = NULL;
3195 return true;
3198 void close_winbindd_cache(void)
3200 if (!wcache) {
3201 return;
3203 if (wcache->tdb) {
3204 tdb_close(wcache->tdb);
3205 wcache->tdb = NULL;
3209 bool lookup_cached_sid(TALLOC_CTX *mem_ctx, const struct dom_sid *sid,
3210 char **domain_name, char **name,
3211 enum lsa_SidType *type)
3213 struct winbindd_domain *domain;
3214 NTSTATUS status;
3216 domain = find_lookup_domain_from_sid(sid);
3217 if (domain == NULL) {
3218 return false;
3220 status = wcache_sid_to_name(domain, sid, mem_ctx, domain_name, name,
3221 type);
3222 return NT_STATUS_IS_OK(status);
3225 bool lookup_cached_name(const char *namespace,
3226 const char *domain_name,
3227 const char *name,
3228 struct dom_sid *sid,
3229 enum lsa_SidType *type)
3231 struct winbindd_domain *domain;
3232 NTSTATUS status;
3233 bool original_online_state;
3235 domain = find_lookup_domain_from_name(namespace);
3236 if (domain == NULL) {
3237 return false;
3240 /* If we are doing a cached logon, temporarily set the domain
3241 offline so the cache won't expire the entry */
3243 original_online_state = domain->online;
3244 domain->online = false;
3245 status = wcache_name_to_sid(domain, domain_name, name, sid, type);
3246 domain->online = original_online_state;
3248 return NT_STATUS_IS_OK(status);
3252 * Cache a name to sid without checking the sequence number.
3253 * Used when caching from a trusted PAC.
3256 void cache_name2sid_trusted(struct winbindd_domain *domain,
3257 const char *domain_name,
3258 const char *name,
3259 enum lsa_SidType type,
3260 const struct dom_sid *sid)
3263 * Ensure we store the mapping with the
3264 * existing sequence number from the cache.
3266 get_cache(domain);
3267 (void)fetch_cache_seqnum(domain, time(NULL));
3268 wcache_save_name_to_sid(domain,
3269 NT_STATUS_OK,
3270 domain_name,
3271 name,
3272 sid,
3273 type);
3276 void cache_name2sid(struct winbindd_domain *domain,
3277 const char *domain_name, const char *name,
3278 enum lsa_SidType type, const struct dom_sid *sid)
3280 refresh_sequence_number(domain);
3281 wcache_save_name_to_sid(domain, NT_STATUS_OK, domain_name, name,
3282 sid, type);
3286 * The original idea that this cache only contains centries has
3287 * been blurred - now other stuff gets put in here. Ensure we
3288 * ignore these things on cleanup.
3291 static int traverse_fn_cleanup(TDB_CONTEXT *the_tdb, TDB_DATA kbuf,
3292 TDB_DATA dbuf, void *state)
3294 struct cache_entry *centry;
3296 if (is_non_centry_key(kbuf)) {
3297 return 0;
3300 centry = wcache_fetch_raw((char *)kbuf.dptr);
3301 if (!centry) {
3302 return 0;
3305 if (!NT_STATUS_IS_OK(centry->status)) {
3306 DEBUG(10,("deleting centry %s\n", (const char *)kbuf.dptr));
3307 tdb_delete(the_tdb, kbuf);
3310 centry_free(centry);
3311 return 0;
3314 /* flush the cache */
3315 static void wcache_flush_cache(void)
3317 char *db_path;
3319 if (!wcache)
3320 return;
3321 if (wcache->tdb) {
3322 tdb_close(wcache->tdb);
3323 wcache->tdb = NULL;
3325 if (!winbindd_use_cache()) {
3326 return;
3329 db_path = wcache_path();
3330 if (db_path == NULL) {
3331 return;
3334 /* when working offline we must not clear the cache on restart */
3335 wcache->tdb = tdb_open_log(db_path,
3336 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
3337 TDB_INCOMPATIBLE_HASH |
3338 (lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST)),
3339 O_RDWR|O_CREAT, 0600);
3340 TALLOC_FREE(db_path);
3341 if (!wcache->tdb) {
3342 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
3343 return;
3346 tdb_traverse(wcache->tdb, traverse_fn_cleanup, NULL);
3348 DEBUG(10,("wcache_flush_cache success\n"));
3351 /* Count cached creds */
3353 static int traverse_fn_cached_creds(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
3354 void *state)
3356 int *cred_count = (int*)state;
3358 if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
3359 (*cred_count)++;
3361 return 0;
3364 NTSTATUS wcache_count_cached_creds(struct winbindd_domain *domain, int *count)
3366 struct winbind_cache *cache = get_cache(domain);
3368 *count = 0;
3370 if (!cache->tdb) {
3371 return NT_STATUS_INTERNAL_DB_ERROR;
3374 tdb_traverse(cache->tdb, traverse_fn_cached_creds, (void *)count);
3376 return NT_STATUS_OK;
3379 struct cred_list {
3380 struct cred_list *prev, *next;
3381 TDB_DATA key;
3382 fstring name;
3383 time_t created;
3385 static struct cred_list *wcache_cred_list;
3387 static int traverse_fn_get_credlist(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
3388 void *state)
3390 struct cred_list *cred;
3392 if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
3394 cred = SMB_MALLOC_P(struct cred_list);
3395 if (cred == NULL) {
3396 DEBUG(0,("traverse_fn_remove_first_creds: failed to malloc new entry for list\n"));
3397 return -1;
3400 ZERO_STRUCTP(cred);
3402 /* save a copy of the key */
3404 fstrcpy(cred->name, (const char *)kbuf.dptr);
3405 DLIST_ADD(wcache_cred_list, cred);
3408 return 0;
3411 NTSTATUS wcache_remove_oldest_cached_creds(struct winbindd_domain *domain, const struct dom_sid *sid)
3413 struct winbind_cache *cache = get_cache(domain);
3414 NTSTATUS status;
3415 int ret;
3416 struct cred_list *cred, *next, *oldest = NULL;
3418 if (!cache->tdb) {
3419 return NT_STATUS_INTERNAL_DB_ERROR;
3422 /* we possibly already have an entry */
3423 if (sid && NT_STATUS_IS_OK(wcache_cached_creds_exist(domain, sid))) {
3425 fstring key_str, tmp;
3427 DEBUG(11,("we already have an entry, deleting that\n"));
3429 fstr_sprintf(key_str, "CRED/%s", sid_to_fstring(tmp, sid));
3431 tdb_delete(cache->tdb, string_tdb_data(key_str));
3433 return NT_STATUS_OK;
3436 ret = tdb_traverse(cache->tdb, traverse_fn_get_credlist, NULL);
3437 if (ret == 0) {
3438 return NT_STATUS_OK;
3439 } else if ((ret < 0) || (wcache_cred_list == NULL)) {
3440 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
3443 ZERO_STRUCTP(oldest);
3445 for (cred = wcache_cred_list; cred; cred = cred->next) {
3447 TDB_DATA data;
3448 time_t t;
3450 data = tdb_fetch(cache->tdb, string_tdb_data(cred->name));
3451 if (!data.dptr) {
3452 DEBUG(10,("wcache_remove_oldest_cached_creds: entry for [%s] not found\n",
3453 cred->name));
3454 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
3455 goto done;
3458 t = IVAL(data.dptr, 0);
3459 SAFE_FREE(data.dptr);
3461 if (!oldest) {
3462 oldest = SMB_MALLOC_P(struct cred_list);
3463 if (oldest == NULL) {
3464 status = NT_STATUS_NO_MEMORY;
3465 goto done;
3468 fstrcpy(oldest->name, cred->name);
3469 oldest->created = t;
3470 continue;
3473 if (t < oldest->created) {
3474 fstrcpy(oldest->name, cred->name);
3475 oldest->created = t;
3479 if (tdb_delete(cache->tdb, string_tdb_data(oldest->name)) == 0) {
3480 status = NT_STATUS_OK;
3481 } else {
3482 status = NT_STATUS_UNSUCCESSFUL;
3484 done:
3485 for (cred = wcache_cred_list; cred; cred = next) {
3486 next = cred->next;
3487 DLIST_REMOVE(wcache_cred_list, cred);
3488 SAFE_FREE(cred);
3490 SAFE_FREE(oldest);
3492 return status;
3495 /* Change the global online/offline state. */
3496 bool set_global_winbindd_state_offline(void)
3498 TDB_DATA data;
3500 DEBUG(10,("set_global_winbindd_state_offline: offline requested.\n"));
3502 /* Only go offline if someone has created
3503 the key "WINBINDD_OFFLINE" in the cache tdb. */
3505 if (wcache == NULL || wcache->tdb == NULL) {
3506 DEBUG(10,("set_global_winbindd_state_offline: wcache not open yet.\n"));
3507 return false;
3510 if (!lp_winbind_offline_logon()) {
3511 DEBUG(10,("set_global_winbindd_state_offline: rejecting.\n"));
3512 return false;
3515 if (global_winbindd_offline_state) {
3516 /* Already offline. */
3517 return true;
3520 data = tdb_fetch_bystring( wcache->tdb, "WINBINDD_OFFLINE" );
3522 if (!data.dptr || data.dsize != 4) {
3523 DEBUG(10,("set_global_winbindd_state_offline: offline state not set.\n"));
3524 SAFE_FREE(data.dptr);
3525 return false;
3526 } else {
3527 DEBUG(10,("set_global_winbindd_state_offline: offline state set.\n"));
3528 global_winbindd_offline_state = true;
3529 SAFE_FREE(data.dptr);
3530 return true;
3534 void set_global_winbindd_state_online(void)
3536 DEBUG(10,("set_global_winbindd_state_online: online requested.\n"));
3538 if (!lp_winbind_offline_logon()) {
3539 DEBUG(10,("set_global_winbindd_state_online: rejecting.\n"));
3540 return;
3543 if (!global_winbindd_offline_state) {
3544 /* Already online. */
3545 return;
3547 global_winbindd_offline_state = false;
3549 if (!wcache->tdb) {
3550 return;
3553 /* Ensure there is no key "WINBINDD_OFFLINE" in the cache tdb. */
3554 tdb_delete_bystring(wcache->tdb, "WINBINDD_OFFLINE");
3557 bool get_global_winbindd_state_offline(void)
3559 return global_winbindd_offline_state;
3562 /***********************************************************************
3563 Validate functions for all possible cache tdb keys.
3564 ***********************************************************************/
3566 static struct cache_entry *create_centry_validate(const char *kstr, TDB_DATA data,
3567 struct tdb_validation_status *state)
3569 struct cache_entry *centry;
3571 centry = SMB_XMALLOC_P(struct cache_entry);
3572 centry->data = (unsigned char *)smb_memdup(data.dptr, data.dsize);
3573 if (!centry->data) {
3574 SAFE_FREE(centry);
3575 return NULL;
3577 centry->len = data.dsize;
3578 centry->ofs = 0;
3580 if (centry->len < 16) {
3581 /* huh? corrupt cache? */
3582 DEBUG(0,("create_centry_validate: Corrupt cache for key %s "
3583 "(len < 16) ?\n", kstr));
3584 centry_free(centry);
3585 state->bad_entry = true;
3586 state->success = false;
3587 return NULL;
3590 centry->status = NT_STATUS(centry_uint32(centry));
3591 centry->sequence_number = centry_uint32(centry);
3592 centry->timeout = centry_uint64_t(centry);
3593 return centry;
3596 static int validate_seqnum(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3597 struct tdb_validation_status *state)
3599 if (dbuf.dsize != 8) {
3600 DEBUG(0,("validate_seqnum: Corrupt cache for key %s (len %u != 8) ?\n",
3601 keystr, (unsigned int)dbuf.dsize ));
3602 state->bad_entry = true;
3603 return 1;
3605 return 0;
3608 static int validate_u(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3609 struct tdb_validation_status *state)
3611 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3612 struct dom_sid sid;
3614 if (!centry) {
3615 return 1;
3618 (void)centry_string(centry, mem_ctx);
3619 (void)centry_string(centry, mem_ctx);
3620 (void)centry_string(centry, mem_ctx);
3621 (void)centry_string(centry, mem_ctx);
3622 (void)centry_string(centry, mem_ctx);
3623 (void)centry_uint32(centry);
3624 (void)centry_uint32(centry);
3625 (void)centry_string(centry, mem_ctx);
3626 (void)centry_sid(centry, &sid);
3627 (void)centry_sid(centry, &sid);
3629 centry_free(centry);
3631 if (!(state->success)) {
3632 return 1;
3634 DEBUG(10,("validate_u: %s ok\n", keystr));
3635 return 0;
3638 static int validate_loc_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3639 struct tdb_validation_status *state)
3641 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3643 if (!centry) {
3644 return 1;
3647 (void)centry_nttime(centry);
3648 (void)centry_nttime(centry);
3649 (void)centry_uint16(centry);
3651 centry_free(centry);
3653 if (!(state->success)) {
3654 return 1;
3656 DEBUG(10,("validate_loc_pol: %s ok\n", keystr));
3657 return 0;
3660 static int validate_pwd_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3661 struct tdb_validation_status *state)
3663 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3665 if (!centry) {
3666 return 1;
3669 (void)centry_uint16(centry);
3670 (void)centry_uint16(centry);
3671 (void)centry_uint32(centry);
3672 (void)centry_nttime(centry);
3673 (void)centry_nttime(centry);
3675 centry_free(centry);
3677 if (!(state->success)) {
3678 return 1;
3680 DEBUG(10,("validate_pwd_pol: %s ok\n", keystr));
3681 return 0;
3684 static int validate_cred(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3685 struct tdb_validation_status *state)
3687 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3689 if (!centry) {
3690 return 1;
3693 (void)centry_time(centry);
3694 (void)centry_hash16(centry, mem_ctx);
3696 /* We only have 17 bytes more data in the salted cred case. */
3697 if (centry->len - centry->ofs == 17) {
3698 (void)centry_hash16(centry, mem_ctx);
3701 centry_free(centry);
3703 if (!(state->success)) {
3704 return 1;
3706 DEBUG(10,("validate_cred: %s ok\n", keystr));
3707 return 0;
3710 static int validate_ul(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3711 struct tdb_validation_status *state)
3713 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3714 int32_t num_entries, i;
3716 if (!centry) {
3717 return 1;
3720 num_entries = (int32_t)centry_uint32(centry);
3722 for (i=0; i< num_entries; i++) {
3723 (void)centry_uint32(centry);
3726 centry_free(centry);
3728 if (!(state->success)) {
3729 return 1;
3731 DEBUG(10,("validate_ul: %s ok\n", keystr));
3732 return 0;
3735 static int validate_gl(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3736 struct tdb_validation_status *state)
3738 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3739 int32_t num_entries, i;
3741 if (!centry) {
3742 return 1;
3745 num_entries = centry_uint32(centry);
3747 for (i=0; i< num_entries; i++) {
3748 (void)centry_string(centry, mem_ctx);
3749 (void)centry_string(centry, mem_ctx);
3750 (void)centry_uint32(centry);
3753 centry_free(centry);
3755 if (!(state->success)) {
3756 return 1;
3758 DEBUG(10,("validate_gl: %s ok\n", keystr));
3759 return 0;
3762 static int validate_ug(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3763 struct tdb_validation_status *state)
3765 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3766 int32_t num_groups, i;
3768 if (!centry) {
3769 return 1;
3772 num_groups = centry_uint32(centry);
3774 for (i=0; i< num_groups; i++) {
3775 struct dom_sid sid;
3776 centry_sid(centry, &sid);
3779 centry_free(centry);
3781 if (!(state->success)) {
3782 return 1;
3784 DEBUG(10,("validate_ug: %s ok\n", keystr));
3785 return 0;
3788 static int validate_ua(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3789 struct tdb_validation_status *state)
3791 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3792 int32_t num_aliases, i;
3794 if (!centry) {
3795 return 1;
3798 num_aliases = centry_uint32(centry);
3800 for (i=0; i < num_aliases; i++) {
3801 (void)centry_uint32(centry);
3804 centry_free(centry);
3806 if (!(state->success)) {
3807 return 1;
3809 DEBUG(10,("validate_ua: %s ok\n", keystr));
3810 return 0;
3813 static int validate_gm(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3814 struct tdb_validation_status *state)
3816 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3817 int32_t num_names, i;
3819 if (!centry) {
3820 return 1;
3823 num_names = centry_uint32(centry);
3825 for (i=0; i< num_names; i++) {
3826 struct dom_sid sid;
3827 centry_sid(centry, &sid);
3828 (void)centry_string(centry, mem_ctx);
3829 (void)centry_uint32(centry);
3832 centry_free(centry);
3834 if (!(state->success)) {
3835 return 1;
3837 DEBUG(10,("validate_gm: %s ok\n", keystr));
3838 return 0;
3841 static int validate_dr(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3842 struct tdb_validation_status *state)
3844 /* Can't say anything about this other than must be nonzero. */
3845 if (dbuf.dsize == 0) {
3846 DEBUG(0,("validate_dr: Corrupt cache for key %s (len == 0) ?\n",
3847 keystr));
3848 state->bad_entry = true;
3849 state->success = false;
3850 return 1;
3853 DEBUG(10,("validate_dr: %s ok\n", keystr));
3854 return 0;
3857 static int validate_de(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3858 struct tdb_validation_status *state)
3860 /* Can't say anything about this other than must be nonzero. */
3861 if (dbuf.dsize == 0) {
3862 DEBUG(0,("validate_de: Corrupt cache for key %s (len == 0) ?\n",
3863 keystr));
3864 state->bad_entry = true;
3865 state->success = false;
3866 return 1;
3869 DEBUG(10,("validate_de: %s ok\n", keystr));
3870 return 0;
3873 static int validate_nss_an(TALLOC_CTX *mem_ctx, const char *keystr,
3874 TDB_DATA dbuf,
3875 struct tdb_validation_status *state)
3877 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3879 if (!centry) {
3880 return 1;
3883 (void)centry_string( centry, mem_ctx );
3885 centry_free(centry);
3887 if (!(state->success)) {
3888 return 1;
3890 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3891 return 0;
3894 static int validate_nss_na(TALLOC_CTX *mem_ctx, const char *keystr,
3895 TDB_DATA dbuf,
3896 struct tdb_validation_status *state)
3898 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3900 if (!centry) {
3901 return 1;
3904 (void)centry_string( centry, mem_ctx );
3906 centry_free(centry);
3908 if (!(state->success)) {
3909 return 1;
3911 DBG_DEBUG("%s ok\n", keystr);
3912 return 0;
3915 static int validate_trustdomcache(TALLOC_CTX *mem_ctx, const char *keystr,
3916 TDB_DATA dbuf,
3917 struct tdb_validation_status *state)
3919 if (dbuf.dsize == 0) {
3920 DEBUG(0, ("validate_trustdomcache: Corrupt cache for "
3921 "key %s (len ==0) ?\n", keystr));
3922 state->bad_entry = true;
3923 state->success = false;
3924 return 1;
3927 DEBUG(10, ("validate_trustdomcache: %s ok\n", keystr));
3928 DEBUGADD(10, (" Don't trust me, I am a DUMMY!\n"));
3929 return 0;
3932 static int validate_offline(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3933 struct tdb_validation_status *state)
3935 if (dbuf.dsize != 4) {
3936 DEBUG(0,("validate_offline: Corrupt cache for key %s (len %u != 4) ?\n",
3937 keystr, (unsigned int)dbuf.dsize ));
3938 state->bad_entry = true;
3939 state->success = false;
3940 return 1;
3942 DEBUG(10,("validate_offline: %s ok\n", keystr));
3943 return 0;
3946 static int validate_ndr(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3947 struct tdb_validation_status *state)
3950 * Ignore validation for now. The proper way to do this is with a
3951 * checksum. Just pure parsing does not really catch much.
3953 return 0;
3956 static int validate_cache_version(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3957 struct tdb_validation_status *state)
3959 if (dbuf.dsize != 4) {
3960 DEBUG(0, ("validate_cache_version: Corrupt cache for "
3961 "key %s (len %u != 4) ?\n",
3962 keystr, (unsigned int)dbuf.dsize));
3963 state->bad_entry = true;
3964 state->success = false;
3965 return 1;
3968 DEBUG(10, ("validate_cache_version: %s ok\n", keystr));
3969 return 0;
3972 /***********************************************************************
3973 A list of all possible cache tdb keys with associated validation
3974 functions.
3975 ***********************************************************************/
3977 struct key_val_struct {
3978 const char *keyname;
3979 int (*validate_data_fn)(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, struct tdb_validation_status* state);
3980 } key_val[] = {
3981 {"SEQNUM/", validate_seqnum},
3982 {"U/", validate_u},
3983 {"LOC_POL/", validate_loc_pol},
3984 {"PWD_POL/", validate_pwd_pol},
3985 {"CRED/", validate_cred},
3986 {"UL/", validate_ul},
3987 {"GL/", validate_gl},
3988 {"UG/", validate_ug},
3989 {"UA", validate_ua},
3990 {"GM/", validate_gm},
3991 {"DR/", validate_dr},
3992 {"DE/", validate_de},
3993 {"TRUSTDOMCACHE/", validate_trustdomcache},
3994 {"NSS/NA/", validate_nss_na},
3995 {"NSS/AN/", validate_nss_an},
3996 {"WINBINDD_OFFLINE", validate_offline},
3997 {"NDR/", validate_ndr},
3998 {WINBINDD_CACHE_VERSION_KEYSTR, validate_cache_version},
3999 {NULL, NULL}
4002 /***********************************************************************
4003 Function to look at every entry in the tdb and validate it as far as
4004 possible.
4005 ***********************************************************************/
4007 static int cache_traverse_validate_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, void *state)
4009 int i;
4010 unsigned int max_key_len = 1024;
4011 struct tdb_validation_status *v_state = (struct tdb_validation_status *)state;
4013 /* Paranoia check. */
4014 if (strncmp("UA/", (const char *)kbuf.dptr, 3) == 0 ||
4015 strncmp("NDR/", (const char *)kbuf.dptr, 4) == 0) {
4016 max_key_len = 1024 * 1024;
4018 if (kbuf.dsize > max_key_len) {
4019 DEBUG(0, ("cache_traverse_validate_fn: key length too large: "
4020 "(%u) > (%u)\n\n",
4021 (unsigned int)kbuf.dsize, (unsigned int)max_key_len));
4022 return 1;
4025 for (i = 0; key_val[i].keyname; i++) {
4026 size_t namelen = strlen(key_val[i].keyname);
4027 if (kbuf.dsize >= namelen && (
4028 strncmp(key_val[i].keyname, (const char *)kbuf.dptr, namelen)) == 0) {
4029 TALLOC_CTX *mem_ctx;
4030 char *keystr;
4031 int ret;
4033 keystr = SMB_MALLOC_ARRAY(char, kbuf.dsize+1);
4034 if (!keystr) {
4035 return 1;
4037 memcpy(keystr, kbuf.dptr, kbuf.dsize);
4038 keystr[kbuf.dsize] = '\0';
4040 mem_ctx = talloc_init("validate_ctx");
4041 if (!mem_ctx) {
4042 SAFE_FREE(keystr);
4043 return 1;
4046 ret = key_val[i].validate_data_fn(mem_ctx, keystr, dbuf,
4047 v_state);
4049 SAFE_FREE(keystr);
4050 talloc_destroy(mem_ctx);
4051 return ret;
4055 DEBUG(0,("cache_traverse_validate_fn: unknown cache entry\nkey :\n"));
4056 dump_data(0, (uint8_t *)kbuf.dptr, kbuf.dsize);
4057 DEBUG(0,("data :\n"));
4058 dump_data(0, (uint8_t *)dbuf.dptr, dbuf.dsize);
4059 v_state->unknown_key = true;
4060 v_state->success = false;
4061 return 1; /* terminate. */
4064 static void validate_panic(const char *const why)
4066 DEBUG(0,("validating cache: would panic %s\n", why ));
4067 DEBUGADD(0, ("exiting instead (cache validation mode)\n"));
4068 exit(47);
4071 static int wbcache_update_centry_fn(TDB_CONTEXT *tdb,
4072 TDB_DATA key,
4073 TDB_DATA data,
4074 void *state)
4076 uint64_t ctimeout;
4077 TDB_DATA blob;
4079 if (is_non_centry_key(key)) {
4080 return 0;
4083 if (data.dptr == NULL || data.dsize == 0) {
4084 if (tdb_delete(tdb, key) < 0) {
4085 DEBUG(0, ("tdb_delete for [%s] failed!\n",
4086 key.dptr));
4087 return 1;
4091 /* add timeout to blob (uint64_t) */
4092 blob.dsize = data.dsize + 8;
4094 blob.dptr = SMB_XMALLOC_ARRAY(uint8_t, blob.dsize);
4095 if (blob.dptr == NULL) {
4096 return 1;
4098 memset(blob.dptr, 0, blob.dsize);
4100 /* copy status and seqnum */
4101 memcpy(blob.dptr, data.dptr, 8);
4103 /* add timeout */
4104 ctimeout = lp_winbind_cache_time() + time(NULL);
4105 SBVAL(blob.dptr, 8, ctimeout);
4107 /* copy the rest */
4108 memcpy(blob.dptr + 16, data.dptr + 8, data.dsize - 8);
4110 if (tdb_store(tdb, key, blob, TDB_REPLACE) < 0) {
4111 DEBUG(0, ("tdb_store to update [%s] failed!\n",
4112 key.dptr));
4113 SAFE_FREE(blob.dptr);
4114 return 1;
4117 SAFE_FREE(blob.dptr);
4118 return 0;
4121 static bool wbcache_upgrade_v1_to_v2(TDB_CONTEXT *tdb)
4123 int rc;
4125 DEBUG(1, ("Upgrade to version 2 of the winbindd_cache.tdb\n"));
4127 rc = tdb_traverse(tdb, wbcache_update_centry_fn, NULL);
4128 if (rc < 0) {
4129 return false;
4132 return true;
4135 /***********************************************************************
4136 Try and validate every entry in the winbindd cache. If we fail here,
4137 delete the cache tdb and return non-zero.
4138 ***********************************************************************/
4140 int winbindd_validate_cache(void)
4142 int ret = -1;
4143 char *tdb_path = NULL;
4144 TDB_CONTEXT *tdb = NULL;
4145 uint32_t vers_id;
4146 bool ok;
4148 DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
4149 smb_panic_fn = validate_panic;
4151 tdb_path = wcache_path();
4152 if (tdb_path == NULL) {
4153 goto done;
4156 tdb = tdb_open_log(tdb_path,
4157 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
4158 TDB_INCOMPATIBLE_HASH |
4159 ( lp_winbind_offline_logon()
4160 ? TDB_DEFAULT
4161 : TDB_DEFAULT | TDB_CLEAR_IF_FIRST ),
4162 O_RDWR|O_CREAT,
4163 0600);
4164 if (!tdb) {
4165 DEBUG(0, ("winbindd_validate_cache: "
4166 "error opening/initializing tdb\n"));
4167 goto done;
4170 /* Version check and upgrade code. */
4171 if (!tdb_fetch_uint32(tdb, WINBINDD_CACHE_VERSION_KEYSTR, &vers_id)) {
4172 DEBUG(10, ("Fresh database\n"));
4173 tdb_store_uint32(tdb, WINBINDD_CACHE_VERSION_KEYSTR, WINBINDD_CACHE_VERSION);
4174 vers_id = WINBINDD_CACHE_VERSION;
4177 if (vers_id != WINBINDD_CACHE_VERSION) {
4178 if (vers_id == WINBINDD_CACHE_VER1) {
4179 ok = wbcache_upgrade_v1_to_v2(tdb);
4180 if (!ok) {
4181 DEBUG(10, ("winbindd_validate_cache: upgrade to version 2 failed.\n"));
4182 unlink(tdb_path);
4183 goto done;
4186 tdb_store_uint32(tdb,
4187 WINBINDD_CACHE_VERSION_KEYSTR,
4188 WINBINDD_CACHE_VERSION);
4189 vers_id = WINBINDD_CACHE_VER2;
4193 tdb_close(tdb);
4195 ret = tdb_validate_and_backup(tdb_path, cache_traverse_validate_fn);
4197 if (ret != 0) {
4198 DEBUG(10, ("winbindd_validate_cache: validation not successful.\n"));
4199 DEBUGADD(10, ("removing tdb %s.\n", tdb_path));
4200 unlink(tdb_path);
4203 done:
4204 TALLOC_FREE(tdb_path);
4205 DEBUG(10, ("winbindd_validate_cache: restoring panic function\n"));
4206 smb_panic_fn = smb_panic;
4207 return ret;
4210 /***********************************************************************
4211 Try and validate every entry in the winbindd cache.
4212 ***********************************************************************/
4214 int winbindd_validate_cache_nobackup(void)
4216 int ret = -1;
4217 char *tdb_path;
4219 DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
4220 smb_panic_fn = validate_panic;
4222 tdb_path = wcache_path();
4223 if (tdb_path == NULL) {
4224 goto err_panic_restore;
4227 if (wcache == NULL || wcache->tdb == NULL) {
4228 ret = tdb_validate_open(tdb_path, cache_traverse_validate_fn);
4229 } else {
4230 ret = tdb_validate(wcache->tdb, cache_traverse_validate_fn);
4233 if (ret != 0) {
4234 DEBUG(10, ("winbindd_validate_cache_nobackup: validation not "
4235 "successful.\n"));
4238 TALLOC_FREE(tdb_path);
4239 err_panic_restore:
4240 DEBUG(10, ("winbindd_validate_cache_nobackup: restoring panic "
4241 "function\n"));
4242 smb_panic_fn = smb_panic;
4243 return ret;
4246 bool winbindd_cache_validate_and_initialize(void)
4248 close_winbindd_cache();
4250 if (lp_winbind_offline_logon()) {
4251 if (winbindd_validate_cache() < 0) {
4252 DEBUG(0, ("winbindd cache tdb corrupt and no backup "
4253 "could be restored.\n"));
4257 return initialize_winbindd_cache();
4260 /*********************************************************************
4261 ********************************************************************/
4263 static bool add_wbdomain_to_tdc_array( struct winbindd_domain *new_dom,
4264 struct winbindd_tdc_domain **domains,
4265 size_t *num_domains )
4267 struct winbindd_tdc_domain *list = NULL;
4268 size_t i, idx;
4269 bool set_only = false;
4271 /* don't allow duplicates */
4273 idx = *num_domains;
4274 list = *domains;
4276 for ( i=0; i< (*num_domains); i++ ) {
4277 if ( strequal( new_dom->name, list[i].domain_name ) ) {
4278 DEBUG(10,("add_wbdomain_to_tdc_array: Found existing record for %s\n",
4279 new_dom->name));
4280 idx = i;
4281 set_only = true;
4283 break;
4287 if ( !set_only ) {
4288 if ( !*domains ) {
4289 list = talloc_array( NULL, struct winbindd_tdc_domain, 1 );
4290 idx = 0;
4291 } else {
4292 list = talloc_realloc( *domains, *domains,
4293 struct winbindd_tdc_domain,
4294 (*num_domains)+1);
4295 idx = *num_domains;
4298 ZERO_STRUCT( list[idx] );
4301 if ( !list )
4302 return false;
4304 list[idx].domain_name = talloc_strdup(list, new_dom->name);
4305 if (list[idx].domain_name == NULL) {
4306 return false;
4308 if (new_dom->alt_name != NULL) {
4309 list[idx].dns_name = talloc_strdup(list, new_dom->alt_name);
4310 if (list[idx].dns_name == NULL) {
4311 return false;
4315 if ( !is_null_sid( &new_dom->sid ) ) {
4316 sid_copy( &list[idx].sid, &new_dom->sid );
4317 } else {
4318 sid_copy(&list[idx].sid, &global_sid_NULL);
4321 if ( new_dom->domain_flags != 0x0 )
4322 list[idx].trust_flags = new_dom->domain_flags;
4324 if ( new_dom->domain_type != 0x0 )
4325 list[idx].trust_type = new_dom->domain_type;
4327 if ( new_dom->domain_trust_attribs != 0x0 )
4328 list[idx].trust_attribs = new_dom->domain_trust_attribs;
4330 if ( !set_only ) {
4331 *domains = list;
4332 *num_domains = idx + 1;
4335 return true;
4338 /*********************************************************************
4339 ********************************************************************/
4341 static TDB_DATA make_tdc_key( const char *domain_name )
4343 char *keystr = NULL;
4344 TDB_DATA key = { NULL, 0 };
4346 if ( !domain_name ) {
4347 DEBUG(5,("make_tdc_key: Keyname workgroup is NULL!\n"));
4348 return key;
4351 if (asprintf( &keystr, "TRUSTDOMCACHE/%s", domain_name ) == -1) {
4352 return key;
4354 key = string_term_tdb_data(keystr);
4356 return key;
4359 /*********************************************************************
4360 ********************************************************************/
4362 static int pack_tdc_domains( struct winbindd_tdc_domain *domains,
4363 size_t num_domains,
4364 unsigned char **buf )
4366 unsigned char *buffer = NULL;
4367 int len = 0;
4368 int buflen = 0;
4369 size_t i = 0;
4371 DEBUG(10,("pack_tdc_domains: Packing %d trusted domains\n",
4372 (int)num_domains));
4374 buflen = 0;
4376 again:
4377 len = 0;
4379 /* Store the number of array items first */
4380 len += tdb_pack( buffer+len, buflen-len, "d",
4381 num_domains );
4383 /* now pack each domain trust record */
4384 for ( i=0; i<num_domains; i++ ) {
4386 fstring tmp;
4388 if ( buflen > 0 ) {
4389 DEBUG(10,("pack_tdc_domains: Packing domain %s (%s)\n",
4390 domains[i].domain_name,
4391 domains[i].dns_name ? domains[i].dns_name : "UNKNOWN" ));
4394 len += tdb_pack( buffer+len, buflen-len, "fffddd",
4395 domains[i].domain_name,
4396 domains[i].dns_name ? domains[i].dns_name : "",
4397 sid_to_fstring(tmp, &domains[i].sid),
4398 domains[i].trust_flags,
4399 domains[i].trust_attribs,
4400 domains[i].trust_type );
4403 if ( buflen < len ) {
4404 SAFE_FREE(buffer);
4405 if ( (buffer = SMB_MALLOC_ARRAY(unsigned char, len)) == NULL ) {
4406 DEBUG(0,("pack_tdc_domains: failed to alloc buffer!\n"));
4407 buflen = -1;
4408 goto done;
4410 buflen = len;
4411 goto again;
4414 *buf = buffer;
4416 done:
4417 return buflen;
4420 /*********************************************************************
4421 ********************************************************************/
4423 static size_t unpack_tdc_domains( unsigned char *buf, int buflen,
4424 struct winbindd_tdc_domain **domains )
4426 fstring domain_name, dns_name, sid_string;
4427 uint32_t type, attribs, flags;
4428 int num_domains;
4429 int len = 0;
4430 int i;
4431 struct winbindd_tdc_domain *list = NULL;
4433 /* get the number of domains */
4434 len += tdb_unpack( buf+len, buflen-len, "d", &num_domains);
4435 if ( len == -1 ) {
4436 DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
4437 return 0;
4440 list = talloc_array( NULL, struct winbindd_tdc_domain, num_domains );
4441 if ( !list ) {
4442 DEBUG(0,("unpack_tdc_domains: Failed to talloc() domain list!\n"));
4443 return 0;
4446 for ( i=0; i<num_domains; i++ ) {
4447 int this_len;
4449 this_len = tdb_unpack( buf+len, buflen-len, "fffddd",
4450 domain_name,
4451 dns_name,
4452 sid_string,
4453 &flags,
4454 &attribs,
4455 &type );
4457 if ( this_len == -1 ) {
4458 DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
4459 TALLOC_FREE( list );
4460 return 0;
4462 len += this_len;
4464 DEBUG(11,("unpack_tdc_domains: Unpacking domain %s (%s) "
4465 "SID %s, flags = 0x%x, attribs = 0x%x, type = 0x%x\n",
4466 domain_name, dns_name, sid_string,
4467 flags, attribs, type));
4469 list[i].domain_name = talloc_strdup( list, domain_name );
4470 list[i].dns_name = NULL;
4471 if (dns_name[0] != '\0') {
4472 list[i].dns_name = talloc_strdup(list, dns_name);
4474 if ( !string_to_sid( &(list[i].sid), sid_string ) ) {
4475 DEBUG(10,("unpack_tdc_domains: no SID for domain %s\n",
4476 domain_name));
4478 list[i].trust_flags = flags;
4479 list[i].trust_attribs = attribs;
4480 list[i].trust_type = type;
4483 *domains = list;
4485 return num_domains;
4488 /*********************************************************************
4489 ********************************************************************/
4491 static bool wcache_tdc_store_list( struct winbindd_tdc_domain *domains, size_t num_domains )
4493 TDB_DATA key = make_tdc_key( lp_workgroup() );
4494 TDB_DATA data = { NULL, 0 };
4495 int ret;
4497 if ( !key.dptr )
4498 return false;
4500 /* See if we were asked to delete the cache entry */
4502 if ( !domains ) {
4503 ret = tdb_delete( wcache->tdb, key );
4504 goto done;
4507 data.dsize = pack_tdc_domains( domains, num_domains, &data.dptr );
4509 if ( !data.dptr ) {
4510 ret = -1;
4511 goto done;
4514 ret = tdb_store( wcache->tdb, key, data, 0 );
4516 done:
4517 SAFE_FREE( data.dptr );
4518 SAFE_FREE( key.dptr );
4520 return ( ret == 0 );
4523 /*********************************************************************
4524 ********************************************************************/
4526 bool wcache_tdc_fetch_list( struct winbindd_tdc_domain **domains, size_t *num_domains )
4528 TDB_DATA key = make_tdc_key( lp_workgroup() );
4529 TDB_DATA data = { NULL, 0 };
4531 *domains = NULL;
4532 *num_domains = 0;
4534 if ( !key.dptr )
4535 return false;
4537 data = tdb_fetch( wcache->tdb, key );
4539 SAFE_FREE( key.dptr );
4541 if ( !data.dptr )
4542 return false;
4544 *num_domains = unpack_tdc_domains( data.dptr, data.dsize, domains );
4546 SAFE_FREE( data.dptr );
4548 if ( !*domains )
4549 return false;
4551 return true;
4554 /*********************************************************************
4555 ********************************************************************/
4557 bool wcache_tdc_add_domain( struct winbindd_domain *domain )
4559 struct winbindd_tdc_domain *dom_list = NULL;
4560 size_t num_domains = 0;
4561 bool ret = false;
4563 DEBUG(10,("wcache_tdc_add_domain: Adding domain %s (%s), SID %s, "
4564 "flags = 0x%x, attributes = 0x%x, type = 0x%x\n",
4565 domain->name, domain->alt_name,
4566 sid_string_dbg(&domain->sid),
4567 domain->domain_flags,
4568 domain->domain_trust_attribs,
4569 domain->domain_type));
4571 if ( !init_wcache() ) {
4572 return false;
4575 /* fetch the list */
4577 wcache_tdc_fetch_list( &dom_list, &num_domains );
4579 /* add the new domain */
4581 if ( !add_wbdomain_to_tdc_array( domain, &dom_list, &num_domains ) ) {
4582 goto done;
4585 /* pack the domain */
4587 if ( !wcache_tdc_store_list( dom_list, num_domains ) ) {
4588 goto done;
4591 /* Success */
4593 ret = true;
4594 done:
4595 TALLOC_FREE( dom_list );
4597 return ret;
4600 static struct winbindd_tdc_domain *wcache_tdc_dup_domain(
4601 TALLOC_CTX *mem_ctx, const struct winbindd_tdc_domain *src)
4603 struct winbindd_tdc_domain *dst;
4605 dst = talloc(mem_ctx, struct winbindd_tdc_domain);
4606 if (dst == NULL) {
4607 goto fail;
4609 dst->domain_name = talloc_strdup(dst, src->domain_name);
4610 if (dst->domain_name == NULL) {
4611 goto fail;
4614 dst->dns_name = NULL;
4615 if (src->dns_name != NULL) {
4616 dst->dns_name = talloc_strdup(dst, src->dns_name);
4617 if (dst->dns_name == NULL) {
4618 goto fail;
4622 sid_copy(&dst->sid, &src->sid);
4623 dst->trust_flags = src->trust_flags;
4624 dst->trust_type = src->trust_type;
4625 dst->trust_attribs = src->trust_attribs;
4626 return dst;
4627 fail:
4628 TALLOC_FREE(dst);
4629 return NULL;
4632 /*********************************************************************
4633 ********************************************************************/
4635 struct winbindd_tdc_domain * wcache_tdc_fetch_domain( TALLOC_CTX *ctx, const char *name )
4637 struct winbindd_tdc_domain *dom_list = NULL;
4638 size_t num_domains = 0;
4639 size_t i;
4640 struct winbindd_tdc_domain *d = NULL;
4642 DEBUG(10,("wcache_tdc_fetch_domain: Searching for domain %s\n", name));
4644 if ( !init_wcache() ) {
4645 return NULL;
4648 /* fetch the list */
4650 wcache_tdc_fetch_list( &dom_list, &num_domains );
4652 for ( i=0; i<num_domains; i++ ) {
4653 if ( strequal(name, dom_list[i].domain_name) ||
4654 strequal(name, dom_list[i].dns_name) )
4656 DEBUG(10,("wcache_tdc_fetch_domain: Found domain %s\n",
4657 name));
4659 d = wcache_tdc_dup_domain(ctx, &dom_list[i]);
4660 break;
4664 TALLOC_FREE( dom_list );
4666 return d;
4669 /*********************************************************************
4670 ********************************************************************/
4672 void wcache_tdc_clear( void )
4674 if ( !init_wcache() )
4675 return;
4677 wcache_tdc_store_list( NULL, 0 );
4679 return;
4682 static bool wcache_ndr_key(TALLOC_CTX *mem_ctx, const char *domain_name,
4683 uint32_t opnum, const DATA_BLOB *req,
4684 TDB_DATA *pkey)
4686 char *key;
4687 size_t keylen;
4689 key = talloc_asprintf(mem_ctx, "NDR/%s/%d/", domain_name, (int)opnum);
4690 if (key == NULL) {
4691 return false;
4693 keylen = talloc_get_size(key) - 1;
4695 key = talloc_realloc(mem_ctx, key, char, keylen + req->length);
4696 if (key == NULL) {
4697 return false;
4699 memcpy(key + keylen, req->data, req->length);
4701 pkey->dptr = (uint8_t *)key;
4702 pkey->dsize = talloc_get_size(key);
4703 return true;
4706 static bool wcache_opnum_cacheable(uint32_t opnum)
4708 switch (opnum) {
4709 case NDR_WBINT_PING:
4710 case NDR_WBINT_QUERYSEQUENCENUMBER:
4711 case NDR_WBINT_ALLOCATEUID:
4712 case NDR_WBINT_ALLOCATEGID:
4713 case NDR_WBINT_CHECKMACHINEACCOUNT:
4714 case NDR_WBINT_CHANGEMACHINEACCOUNT:
4715 case NDR_WBINT_PINGDC:
4716 return false;
4718 return true;
4721 bool wcache_fetch_ndr(TALLOC_CTX *mem_ctx, struct winbindd_domain *domain,
4722 uint32_t opnum, const DATA_BLOB *req, DATA_BLOB *resp)
4724 TDB_DATA key, data;
4725 bool ret = false;
4727 if (!wcache_opnum_cacheable(opnum) ||
4728 is_my_own_sam_domain(domain) ||
4729 is_builtin_domain(domain)) {
4730 return false;
4733 if (wcache->tdb == NULL) {
4734 return false;
4737 if (!wcache_ndr_key(talloc_tos(), domain->name, opnum, req, &key)) {
4738 return false;
4740 data = tdb_fetch(wcache->tdb, key);
4741 TALLOC_FREE(key.dptr);
4743 if (data.dptr == NULL) {
4744 return false;
4746 if (data.dsize < 12) {
4747 goto fail;
4750 if (is_domain_online(domain)) {
4751 uint32_t entry_seqnum, dom_seqnum, last_check;
4752 uint64_t entry_timeout;
4754 if (!wcache_fetch_seqnum(domain->name, &dom_seqnum,
4755 &last_check)) {
4756 goto fail;
4758 entry_seqnum = IVAL(data.dptr, 0);
4759 if (entry_seqnum != dom_seqnum) {
4760 DEBUG(10, ("Entry has wrong sequence number: %d\n",
4761 (int)entry_seqnum));
4762 goto fail;
4764 entry_timeout = BVAL(data.dptr, 4);
4765 if (time(NULL) > (time_t)entry_timeout) {
4766 DEBUG(10, ("Entry has timed out\n"));
4767 goto fail;
4771 resp->data = (uint8_t *)talloc_memdup(mem_ctx, data.dptr + 12,
4772 data.dsize - 12);
4773 if (resp->data == NULL) {
4774 DEBUG(10, ("talloc failed\n"));
4775 goto fail;
4777 resp->length = data.dsize - 12;
4779 ret = true;
4780 fail:
4781 SAFE_FREE(data.dptr);
4782 return ret;
4785 void wcache_store_ndr(struct winbindd_domain *domain, uint32_t opnum,
4786 const DATA_BLOB *req, const DATA_BLOB *resp)
4788 TDB_DATA key, data;
4789 uint32_t dom_seqnum, last_check;
4790 uint64_t timeout;
4792 if (!wcache_opnum_cacheable(opnum) ||
4793 is_my_own_sam_domain(domain) ||
4794 is_builtin_domain(domain)) {
4795 return;
4798 if (wcache->tdb == NULL) {
4799 return;
4802 if (!wcache_fetch_seqnum(domain->name, &dom_seqnum, &last_check)) {
4803 DEBUG(10, ("could not fetch seqnum for domain %s\n",
4804 domain->name));
4805 return;
4808 if (!wcache_ndr_key(talloc_tos(), domain->name, opnum, req, &key)) {
4809 return;
4812 timeout = time(NULL) + lp_winbind_cache_time();
4814 data.dsize = resp->length + 12;
4815 data.dptr = talloc_array(key.dptr, uint8_t, data.dsize);
4816 if (data.dptr == NULL) {
4817 goto done;
4820 SIVAL(data.dptr, 0, dom_seqnum);
4821 SBVAL(data.dptr, 4, timeout);
4822 memcpy(data.dptr + 12, resp->data, resp->length);
4824 tdb_store(wcache->tdb, key, data, 0);
4826 done:
4827 TALLOC_FREE(key.dptr);
4828 return;