winbindd: Make wcache_name_to_sid static
[Samba.git] / source3 / winbindd / winbindd_cache.c
blob2fb41cead8c28704c77dea8818a13e3538b22330
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"
39 #undef DBGC_CLASS
40 #define DBGC_CLASS DBGC_WINBIND
42 #define WINBINDD_CACHE_VER1 1 /* initial db version */
43 #define WINBINDD_CACHE_VER2 2 /* second version with timeouts for NDR entries */
45 #define WINBINDD_CACHE_VERSION WINBINDD_CACHE_VER2
46 #define WINBINDD_CACHE_VERSION_KEYSTR "WINBINDD_CACHE_VERSION"
48 extern struct winbindd_methods reconnect_methods;
49 #ifdef HAVE_ADS
50 extern struct winbindd_methods reconnect_ads_methods;
51 #endif
52 extern struct winbindd_methods builtin_passdb_methods;
53 extern struct winbindd_methods sam_passdb_methods;
55 static void wcache_flush_cache(void);
58 * JRA. KEEP THIS LIST UP TO DATE IF YOU ADD CACHE ENTRIES.
59 * Here are the list of entry types that are *not* stored
60 * as form struct cache_entry in the cache.
63 static const char *non_centry_keys[] = {
64 "SEQNUM/",
65 "WINBINDD_OFFLINE",
66 WINBINDD_CACHE_VERSION_KEYSTR,
67 NULL
70 /************************************************************************
71 Is this key a non-centry type ?
72 ************************************************************************/
74 static bool is_non_centry_key(TDB_DATA kbuf)
76 int i;
78 if (kbuf.dptr == NULL || kbuf.dsize == 0) {
79 return false;
81 for (i = 0; non_centry_keys[i] != NULL; i++) {
82 size_t namelen = strlen(non_centry_keys[i]);
83 if (kbuf.dsize < namelen) {
84 continue;
86 if (strncmp(non_centry_keys[i], (const char *)kbuf.dptr, namelen) == 0) {
87 return true;
90 return false;
93 /* Global online/offline state - False when online. winbindd starts up online
94 and sets this to true if the first query fails and there's an entry in
95 the cache tdb telling us to stay offline. */
97 static bool global_winbindd_offline_state;
99 struct winbind_cache {
100 TDB_CONTEXT *tdb;
103 struct cache_entry {
104 NTSTATUS status;
105 uint32_t sequence_number;
106 uint64_t timeout;
107 uint8_t *data;
108 uint32_t len, ofs;
111 void (*smb_panic_fn)(const char *const why) = smb_panic;
113 static struct winbind_cache *wcache;
115 static char *wcache_path(void)
118 * Data needs to be kept persistent in state directory for
119 * running with "winbindd offline logon".
121 return state_path("winbindd_cache.tdb");
124 /* get the winbind_cache structure */
125 static struct winbind_cache *get_cache(struct winbindd_domain *domain)
127 struct winbind_cache *ret = wcache;
129 /* We have to know what type of domain we are dealing with first. */
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);
151 OK. Listen up because I'm only going to say this once.
152 We have the following scenarios to consider
153 (a) trusted AD domains on a Samba DC,
154 (b) trusted AD domains and we are joined to a non-kerberos domain
155 (c) trusted AD domains and we are joined to a kerberos (AD) domain
157 For (a) we can always contact the trusted domain using krb5
158 since we have the domain trust account password
160 For (b) we can only use RPC since we have no way of
161 getting a krb5 ticket in our own domain
163 For (c) we can always use krb5 since we have a kerberos trust
165 --jerry
168 #ifdef HAVE_ADS
169 if (domain->backend == NULL) {
170 struct winbindd_domain *our_domain = domain;
172 /* find our domain first so we can figure out if we
173 are joined to a kerberized domain */
175 if (!domain->primary) {
176 our_domain = find_our_domain();
179 if ((our_domain->active_directory || IS_DC)
180 && domain->active_directory
181 && !lp_winbind_rpc_only())
183 DBG_INFO("Setting ADS methods for domain %s\n",
184 domain->name);
185 domain->backend = &reconnect_ads_methods;
188 #endif /* HAVE_ADS */
190 if (domain->backend == NULL) {
191 DBG_INFO("Setting MS-RPC methods for domain %s\n", domain->name);
192 domain->backend = &reconnect_methods;
195 if (ret != NULL) {
196 return ret;
199 ret = SMB_XMALLOC_P(struct winbind_cache);
200 ZERO_STRUCTP(ret);
202 wcache = ret;
203 wcache_flush_cache();
205 return ret;
209 free a centry structure
211 static void centry_free(struct cache_entry *centry)
213 if (!centry)
214 return;
215 SAFE_FREE(centry->data);
216 free(centry);
219 static bool centry_check_bytes(struct cache_entry *centry, size_t nbytes)
221 if (centry->len - centry->ofs < nbytes) {
222 DEBUG(0,("centry corruption? needed %u bytes, have %d\n",
223 (unsigned int)nbytes,
224 centry->len - centry->ofs));
225 return false;
227 return true;
231 pull a uint64_t from a cache entry
233 static uint64_t centry_uint64_t(struct cache_entry *centry)
235 uint64_t ret;
237 if (!centry_check_bytes(centry, 8)) {
238 smb_panic_fn("centry_uint64_t");
240 ret = BVAL(centry->data, centry->ofs);
241 centry->ofs += 8;
242 return ret;
246 pull a uint32_t from a cache entry
248 static uint32_t centry_uint32(struct cache_entry *centry)
250 uint32_t ret;
252 if (!centry_check_bytes(centry, 4)) {
253 smb_panic_fn("centry_uint32");
255 ret = IVAL(centry->data, centry->ofs);
256 centry->ofs += 4;
257 return ret;
261 pull a uint16_t from a cache entry
263 static uint16_t centry_uint16(struct cache_entry *centry)
265 uint16_t ret;
266 if (!centry_check_bytes(centry, 2)) {
267 smb_panic_fn("centry_uint16");
269 ret = SVAL(centry->data, centry->ofs);
270 centry->ofs += 2;
271 return ret;
275 pull a uint8_t from a cache entry
277 static uint8_t centry_uint8(struct cache_entry *centry)
279 uint8_t ret;
280 if (!centry_check_bytes(centry, 1)) {
281 smb_panic_fn("centry_uint8");
283 ret = CVAL(centry->data, centry->ofs);
284 centry->ofs += 1;
285 return ret;
289 pull a NTTIME from a cache entry
291 static NTTIME centry_nttime(struct cache_entry *centry)
293 NTTIME ret;
294 if (!centry_check_bytes(centry, 8)) {
295 smb_panic_fn("centry_nttime");
297 ret = IVAL(centry->data, centry->ofs);
298 centry->ofs += 4;
299 ret += (uint64_t)IVAL(centry->data, centry->ofs) << 32;
300 centry->ofs += 4;
301 return ret;
305 pull a time_t from a cache entry. time_t stored portably as a 64-bit time.
307 static time_t centry_time(struct cache_entry *centry)
309 return (time_t)centry_nttime(centry);
312 /* pull a string from a cache entry, using the supplied
313 talloc context
315 static char *centry_string(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
317 uint32_t len;
318 char *ret;
320 len = centry_uint8(centry);
322 if (len == 0xFF) {
323 /* a deliberate NULL string */
324 return NULL;
327 if (!centry_check_bytes(centry, (size_t)len)) {
328 smb_panic_fn("centry_string");
331 ret = talloc_array(mem_ctx, char, len+1);
332 if (!ret) {
333 smb_panic_fn("centry_string out of memory\n");
335 memcpy(ret,centry->data + centry->ofs, len);
336 ret[len] = 0;
337 centry->ofs += len;
338 return ret;
341 /* pull a hash16 from a cache entry, using the supplied
342 talloc context
344 static char *centry_hash16(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
346 uint32_t len;
347 char *ret;
349 len = centry_uint8(centry);
351 if (len != 16) {
352 DEBUG(0,("centry corruption? hash len (%u) != 16\n",
353 len ));
354 return NULL;
357 if (!centry_check_bytes(centry, 16)) {
358 return NULL;
361 ret = talloc_array(mem_ctx, char, 16);
362 if (!ret) {
363 smb_panic_fn("centry_hash out of memory\n");
365 memcpy(ret,centry->data + centry->ofs, 16);
366 centry->ofs += 16;
367 return ret;
370 /* pull a sid from a cache entry, using the supplied
371 talloc context
373 static bool centry_sid(struct cache_entry *centry, struct dom_sid *sid)
375 char *sid_string;
376 bool ret;
378 sid_string = centry_string(centry, talloc_tos());
379 if (sid_string == NULL) {
380 return false;
382 ret = string_to_sid(sid, sid_string);
383 TALLOC_FREE(sid_string);
384 return ret;
389 pull a NTSTATUS from a cache entry
391 static NTSTATUS centry_ntstatus(struct cache_entry *centry)
393 NTSTATUS status;
395 status = NT_STATUS(centry_uint32(centry));
396 return status;
400 /* the server is considered down if it can't give us a sequence number */
401 static bool wcache_server_down(struct winbindd_domain *domain)
403 bool ret;
405 if (!wcache->tdb)
406 return false;
408 ret = (domain->sequence_number == DOM_SEQUENCE_NONE);
410 if (ret)
411 DEBUG(10,("wcache_server_down: server for Domain %s down\n",
412 domain->name ));
413 return ret;
416 struct wcache_seqnum_state {
417 uint32_t *seqnum;
418 uint32_t *last_seq_check;
421 static int wcache_seqnum_parser(TDB_DATA key, TDB_DATA data,
422 void *private_data)
424 struct wcache_seqnum_state *state = private_data;
426 if (data.dsize != 8) {
427 DEBUG(10, ("wcache_fetch_seqnum: invalid data size %d\n",
428 (int)data.dsize));
429 return -1;
432 *state->seqnum = IVAL(data.dptr, 0);
433 *state->last_seq_check = IVAL(data.dptr, 4);
434 return 0;
437 static bool wcache_fetch_seqnum(const char *domain_name, uint32_t *seqnum,
438 uint32_t *last_seq_check)
440 struct wcache_seqnum_state state = {
441 .seqnum = seqnum, .last_seq_check = last_seq_check
443 size_t len = strlen(domain_name);
444 char keystr[len+8];
445 TDB_DATA key = { .dptr = (uint8_t *)keystr, .dsize = sizeof(keystr) };
446 int ret;
448 if (wcache->tdb == NULL) {
449 DEBUG(10,("wcache_fetch_seqnum: tdb == NULL\n"));
450 return false;
453 snprintf(keystr, sizeof(keystr), "SEQNUM/%s", domain_name);
455 ret = tdb_parse_record(wcache->tdb, key, wcache_seqnum_parser,
456 &state);
457 return (ret == 0);
460 static NTSTATUS fetch_cache_seqnum( struct winbindd_domain *domain, time_t now )
462 uint32_t last_check, time_diff;
464 if (!wcache_fetch_seqnum(domain->name, &domain->sequence_number,
465 &last_check)) {
466 return NT_STATUS_UNSUCCESSFUL;
468 domain->last_seq_check = last_check;
470 /* have we expired? */
472 time_diff = now - domain->last_seq_check;
473 if ( time_diff > lp_winbind_cache_time() ) {
474 DEBUG(10,("fetch_cache_seqnum: timeout [%s][%u @ %u]\n",
475 domain->name, domain->sequence_number,
476 (uint32_t)domain->last_seq_check));
477 return NT_STATUS_UNSUCCESSFUL;
480 DEBUG(10,("fetch_cache_seqnum: success [%s][%u @ %u]\n",
481 domain->name, domain->sequence_number,
482 (uint32_t)domain->last_seq_check));
484 return NT_STATUS_OK;
487 bool wcache_store_seqnum(const char *domain_name, uint32_t seqnum,
488 time_t last_seq_check)
490 size_t len = strlen(domain_name);
491 char keystr[len+8];
492 TDB_DATA key = { .dptr = (uint8_t *)keystr, .dsize = sizeof(keystr) };
493 uint8_t buf[8];
494 int ret;
496 if (wcache->tdb == NULL) {
497 DEBUG(10, ("wcache_store_seqnum: wcache->tdb == NULL\n"));
498 return false;
501 snprintf(keystr, sizeof(keystr), "SEQNUM/%s", domain_name);
503 SIVAL(buf, 0, seqnum);
504 SIVAL(buf, 4, last_seq_check);
506 ret = tdb_store(wcache->tdb, key, make_tdb_data(buf, sizeof(buf)),
507 TDB_REPLACE);
508 if (ret != 0) {
509 DEBUG(10, ("tdb_store_bystring failed: %s\n",
510 tdb_errorstr(wcache->tdb)));
511 return false;
514 DEBUG(10, ("wcache_store_seqnum: success [%s][%u @ %u]\n",
515 domain_name, seqnum, (unsigned)last_seq_check));
517 return true;
520 static bool store_cache_seqnum( struct winbindd_domain *domain )
522 return wcache_store_seqnum(domain->name, domain->sequence_number,
523 domain->last_seq_check);
527 refresh the domain sequence number on timeout.
530 static void refresh_sequence_number(struct winbindd_domain *domain)
532 NTSTATUS status;
533 unsigned time_diff;
534 time_t t = time(NULL);
535 unsigned cache_time = lp_winbind_cache_time();
537 if (is_domain_offline(domain)) {
538 return;
541 get_cache( domain );
543 #if 0 /* JERRY -- disable as the default cache time is now 5 minutes */
544 /* trying to reconnect is expensive, don't do it too often */
545 if (domain->sequence_number == DOM_SEQUENCE_NONE) {
546 cache_time *= 8;
548 #endif
550 time_diff = t - domain->last_seq_check;
552 /* see if we have to refetch the domain sequence number */
553 if ((time_diff < cache_time) &&
554 (domain->sequence_number != DOM_SEQUENCE_NONE) &&
555 NT_STATUS_IS_OK(domain->last_status)) {
556 DEBUG(10, ("refresh_sequence_number: %s time ok\n", domain->name));
557 goto done;
560 /* try to get the sequence number from the tdb cache first */
561 /* this will update the timestamp as well */
563 status = fetch_cache_seqnum( domain, t );
564 if (NT_STATUS_IS_OK(status) &&
565 (domain->sequence_number != DOM_SEQUENCE_NONE) &&
566 NT_STATUS_IS_OK(domain->last_status)) {
567 goto done;
570 /* important! make sure that we know if this is a native
571 mode domain or not. And that we can contact it. */
573 if ( winbindd_can_contact_domain( domain ) ) {
574 status = domain->backend->sequence_number(domain,
575 &domain->sequence_number);
576 } else {
577 /* just use the current time */
578 status = NT_STATUS_OK;
579 domain->sequence_number = time(NULL);
583 /* the above call could have set our domain->backend to NULL when
584 * coming from offline to online mode, make sure to reinitialize the
585 * backend - Guenther */
586 get_cache( domain );
588 if (!NT_STATUS_IS_OK(status)) {
589 DEBUG(10,("refresh_sequence_number: failed with %s\n", nt_errstr(status)));
590 domain->sequence_number = DOM_SEQUENCE_NONE;
593 domain->last_status = status;
594 domain->last_seq_check = time(NULL);
596 /* save the new sequence number in the cache */
597 store_cache_seqnum( domain );
599 done:
600 DEBUG(10, ("refresh_sequence_number: %s seq number is now %d\n",
601 domain->name, domain->sequence_number));
603 return;
607 decide if a cache entry has expired
609 static bool centry_expired(struct winbindd_domain *domain, const char *keystr, struct cache_entry *centry)
611 /* If we've been told to be offline - stay in that state... */
612 if (lp_winbind_offline_logon() && global_winbindd_offline_state) {
613 DEBUG(10,("centry_expired: Key %s for domain %s valid as winbindd is globally offline.\n",
614 keystr, domain->name ));
615 return false;
618 /* when the domain is offline return the cached entry.
619 * This deals with transient offline states... */
621 if (!domain->online) {
622 DEBUG(10,("centry_expired: Key %s for domain %s valid as domain is offline.\n",
623 keystr, domain->name ));
624 return false;
627 /* if the server is OK and our cache entry came from when it was down then
628 the entry is invalid */
629 if ((domain->sequence_number != DOM_SEQUENCE_NONE) &&
630 (centry->sequence_number == DOM_SEQUENCE_NONE)) {
631 DEBUG(10,("centry_expired: Key %s for domain %s invalid sequence.\n",
632 keystr, domain->name ));
633 return true;
636 /* if the server is down or the cache entry is not older than the
637 current sequence number or it did not timeout then it is OK */
638 if (wcache_server_down(domain)
639 || ((centry->sequence_number == domain->sequence_number)
640 && (centry->timeout > time(NULL)))) {
641 DEBUG(10,("centry_expired: Key %s for domain %s is good.\n",
642 keystr, domain->name ));
643 return false;
646 DEBUG(10,("centry_expired: Key %s for domain %s expired\n",
647 keystr, domain->name ));
649 /* it's expired */
650 return true;
653 static struct cache_entry *wcache_fetch_raw(char *kstr)
655 TDB_DATA data;
656 struct cache_entry *centry;
657 TDB_DATA key;
659 key = string_tdb_data(kstr);
660 data = tdb_fetch(wcache->tdb, key);
661 if (!data.dptr) {
662 /* a cache miss */
663 return NULL;
666 centry = SMB_XMALLOC_P(struct cache_entry);
667 centry->data = (unsigned char *)data.dptr;
668 centry->len = data.dsize;
669 centry->ofs = 0;
671 if (centry->len < 16) {
672 /* huh? corrupt cache? */
673 DEBUG(10,("wcache_fetch_raw: Corrupt cache for key %s "
674 "(len < 16)?\n", kstr));
675 centry_free(centry);
676 return NULL;
679 centry->status = centry_ntstatus(centry);
680 centry->sequence_number = centry_uint32(centry);
681 centry->timeout = centry_uint64_t(centry);
683 return centry;
686 static bool is_my_own_sam_domain(struct winbindd_domain *domain)
688 if (strequal(domain->name, get_global_sam_name()) &&
689 sid_check_is_our_sam(&domain->sid)) {
690 return true;
693 return false;
696 static bool is_builtin_domain(struct winbindd_domain *domain)
698 if (strequal(domain->name, "BUILTIN") &&
699 sid_check_is_builtin(&domain->sid)) {
700 return true;
703 return false;
707 fetch an entry from the cache, with a varargs key. auto-fetch the sequence
708 number and return status
710 static struct cache_entry *wcache_fetch(struct winbind_cache *cache,
711 struct winbindd_domain *domain,
712 const char *format, ...) PRINTF_ATTRIBUTE(3,4);
713 static struct cache_entry *wcache_fetch(struct winbind_cache *cache,
714 struct winbindd_domain *domain,
715 const char *format, ...)
717 va_list ap;
718 char *kstr;
719 struct cache_entry *centry;
721 if (!winbindd_use_cache() ||
722 is_my_own_sam_domain(domain) ||
723 is_builtin_domain(domain)) {
724 return NULL;
727 refresh_sequence_number(domain);
729 va_start(ap, format);
730 smb_xvasprintf(&kstr, format, ap);
731 va_end(ap);
733 centry = wcache_fetch_raw(kstr);
734 if (centry == NULL) {
735 free(kstr);
736 return NULL;
739 if (centry_expired(domain, kstr, centry)) {
741 DEBUG(10,("wcache_fetch: entry %s expired for domain %s\n",
742 kstr, domain->name ));
744 centry_free(centry);
745 free(kstr);
746 return NULL;
749 DEBUG(10,("wcache_fetch: returning entry %s for domain %s\n",
750 kstr, domain->name ));
752 free(kstr);
753 return centry;
756 static void wcache_delete(const char *format, ...) PRINTF_ATTRIBUTE(1,2);
757 static void wcache_delete(const char *format, ...)
759 va_list ap;
760 char *kstr;
761 TDB_DATA key;
763 va_start(ap, format);
764 smb_xvasprintf(&kstr, format, ap);
765 va_end(ap);
767 key = string_tdb_data(kstr);
769 tdb_delete(wcache->tdb, key);
770 free(kstr);
774 make sure we have at least len bytes available in a centry
776 static void centry_expand(struct cache_entry *centry, uint32_t len)
778 if (centry->len - centry->ofs >= len)
779 return;
780 centry->len *= 2;
781 centry->data = SMB_REALLOC_ARRAY(centry->data, unsigned char,
782 centry->len);
783 if (!centry->data) {
784 DEBUG(0,("out of memory: needed %d bytes in centry_expand\n", centry->len));
785 smb_panic_fn("out of memory in centry_expand");
790 push a uint64_t into a centry
792 static void centry_put_uint64_t(struct cache_entry *centry, uint64_t v)
794 centry_expand(centry, 8);
795 SBVAL(centry->data, centry->ofs, v);
796 centry->ofs += 8;
800 push a uint32_t into a centry
802 static void centry_put_uint32(struct cache_entry *centry, uint32_t v)
804 centry_expand(centry, 4);
805 SIVAL(centry->data, centry->ofs, v);
806 centry->ofs += 4;
810 push a uint16_t into a centry
812 static void centry_put_uint16(struct cache_entry *centry, uint16_t v)
814 centry_expand(centry, 2);
815 SSVAL(centry->data, centry->ofs, v);
816 centry->ofs += 2;
820 push a uint8_t into a centry
822 static void centry_put_uint8(struct cache_entry *centry, uint8_t v)
824 centry_expand(centry, 1);
825 SCVAL(centry->data, centry->ofs, v);
826 centry->ofs += 1;
830 push a string into a centry
832 static void centry_put_string(struct cache_entry *centry, const char *s)
834 int len;
836 if (!s) {
837 /* null strings are marked as len 0xFFFF */
838 centry_put_uint8(centry, 0xFF);
839 return;
842 len = strlen(s);
843 /* can't handle more than 254 char strings. Truncating is probably best */
844 if (len > 254) {
845 DEBUG(10,("centry_put_string: truncating len (%d) to: 254\n", len));
846 len = 254;
848 centry_put_uint8(centry, len);
849 centry_expand(centry, len);
850 memcpy(centry->data + centry->ofs, s, len);
851 centry->ofs += len;
855 push a 16 byte hash into a centry - treat as 16 byte string.
857 static void centry_put_hash16(struct cache_entry *centry, const uint8_t val[16])
859 centry_put_uint8(centry, 16);
860 centry_expand(centry, 16);
861 memcpy(centry->data + centry->ofs, val, 16);
862 centry->ofs += 16;
865 static void centry_put_sid(struct cache_entry *centry, const struct dom_sid *sid)
867 fstring sid_string;
868 centry_put_string(centry, sid_to_fstring(sid_string, sid));
873 put NTSTATUS into a centry
875 static void centry_put_ntstatus(struct cache_entry *centry, NTSTATUS status)
877 uint32_t status_value = NT_STATUS_V(status);
878 centry_put_uint32(centry, status_value);
883 push a NTTIME into a centry
885 static void centry_put_nttime(struct cache_entry *centry, NTTIME nt)
887 centry_expand(centry, 8);
888 SIVAL(centry->data, centry->ofs, nt & 0xFFFFFFFF);
889 centry->ofs += 4;
890 SIVAL(centry->data, centry->ofs, nt >> 32);
891 centry->ofs += 4;
895 push a time_t into a centry - use a 64 bit size.
896 NTTIME here is being used as a convenient 64-bit size.
898 static void centry_put_time(struct cache_entry *centry, time_t t)
900 NTTIME nt = (NTTIME)t;
901 centry_put_nttime(centry, nt);
905 start a centry for output. When finished, call centry_end()
907 static struct cache_entry *centry_start(struct winbindd_domain *domain,
908 NTSTATUS status)
910 struct cache_entry *centry;
912 if (!wcache->tdb)
913 return NULL;
915 centry = SMB_XMALLOC_P(struct cache_entry);
917 centry->len = 8192; /* reasonable default */
918 centry->data = SMB_XMALLOC_ARRAY(uint8_t, centry->len);
919 centry->ofs = 0;
920 centry->sequence_number = domain->sequence_number;
921 centry->timeout = lp_winbind_cache_time() + time(NULL);
922 centry_put_ntstatus(centry, status);
923 centry_put_uint32(centry, centry->sequence_number);
924 centry_put_uint64_t(centry, centry->timeout);
925 return centry;
929 finish a centry and write it to the tdb
931 static void centry_end(struct cache_entry *centry, const char *format, ...) PRINTF_ATTRIBUTE(2,3);
932 static void centry_end(struct cache_entry *centry, const char *format, ...)
934 va_list ap;
935 char *kstr;
936 TDB_DATA key, data;
938 if (!winbindd_use_cache()) {
939 return;
942 va_start(ap, format);
943 smb_xvasprintf(&kstr, format, ap);
944 va_end(ap);
946 key = string_tdb_data(kstr);
947 data.dptr = centry->data;
948 data.dsize = centry->ofs;
950 tdb_store(wcache->tdb, key, data, TDB_REPLACE);
951 free(kstr);
954 static void wcache_save_name_to_sid(struct winbindd_domain *domain,
955 NTSTATUS status, const char *domain_name,
956 const char *name, const struct dom_sid *sid,
957 enum lsa_SidType type)
959 struct cache_entry *centry;
960 fstring uname;
962 centry = centry_start(domain, status);
963 if (!centry)
964 return;
966 if ((domain_name == NULL) || (domain_name[0] == '\0')) {
967 struct winbindd_domain *mydomain =
968 find_domain_from_sid_noinit(sid);
969 if (mydomain != NULL) {
970 domain_name = mydomain->name;
974 centry_put_uint32(centry, type);
975 centry_put_sid(centry, sid);
976 fstrcpy(uname, name);
977 (void)strupper_m(uname);
978 centry_end(centry, "NS/%s/%s", domain_name, uname);
979 DEBUG(10,("wcache_save_name_to_sid: %s\\%s -> %s (%s)\n", domain_name,
980 uname, sid_string_dbg(sid), nt_errstr(status)));
981 centry_free(centry);
984 static void wcache_save_sid_to_name(struct winbindd_domain *domain, NTSTATUS status,
985 const struct dom_sid *sid, const char *domain_name, const char *name, enum lsa_SidType type)
987 struct cache_entry *centry;
988 fstring sid_string;
990 centry = centry_start(domain, status);
991 if (!centry)
992 return;
994 if ((domain_name == NULL) || (domain_name[0] == '\0')) {
995 struct winbindd_domain *mydomain =
996 find_domain_from_sid_noinit(sid);
997 if (mydomain != NULL) {
998 domain_name = mydomain->name;
1002 if (NT_STATUS_IS_OK(status)) {
1003 centry_put_uint32(centry, type);
1004 centry_put_string(centry, domain_name);
1005 centry_put_string(centry, name);
1008 centry_end(centry, "SN/%s", sid_to_fstring(sid_string, sid));
1009 DEBUG(10,("wcache_save_sid_to_name: %s -> %s\\%s (%s)\n", sid_string,
1010 domain_name, name, nt_errstr(status)));
1011 centry_free(centry);
1014 static void wcache_save_lockout_policy(struct winbindd_domain *domain,
1015 NTSTATUS status,
1016 struct samr_DomInfo12 *lockout_policy)
1018 struct cache_entry *centry;
1020 centry = centry_start(domain, status);
1021 if (!centry)
1022 return;
1024 centry_put_nttime(centry, lockout_policy->lockout_duration);
1025 centry_put_nttime(centry, lockout_policy->lockout_window);
1026 centry_put_uint16(centry, lockout_policy->lockout_threshold);
1028 centry_end(centry, "LOC_POL/%s", domain->name);
1030 DEBUG(10,("wcache_save_lockout_policy: %s\n", domain->name));
1032 centry_free(centry);
1037 static void wcache_save_password_policy(struct winbindd_domain *domain,
1038 NTSTATUS status,
1039 struct samr_DomInfo1 *policy)
1041 struct cache_entry *centry;
1043 centry = centry_start(domain, status);
1044 if (!centry)
1045 return;
1047 centry_put_uint16(centry, policy->min_password_length);
1048 centry_put_uint16(centry, policy->password_history_length);
1049 centry_put_uint32(centry, policy->password_properties);
1050 centry_put_nttime(centry, policy->max_password_age);
1051 centry_put_nttime(centry, policy->min_password_age);
1053 centry_end(centry, "PWD_POL/%s", domain->name);
1055 DEBUG(10,("wcache_save_password_policy: %s\n", domain->name));
1057 centry_free(centry);
1060 /***************************************************************************
1061 ***************************************************************************/
1063 static void wcache_save_username_alias(struct winbindd_domain *domain,
1064 NTSTATUS status,
1065 const char *name, const char *alias)
1067 struct cache_entry *centry;
1068 fstring uname;
1070 if ( (centry = centry_start(domain, status)) == NULL )
1071 return;
1073 centry_put_string( centry, alias );
1075 fstrcpy(uname, name);
1076 (void)strupper_m(uname);
1077 centry_end(centry, "NSS/NA/%s", uname);
1079 DEBUG(10,("wcache_save_username_alias: %s -> %s\n", name, alias ));
1081 centry_free(centry);
1084 static void wcache_save_alias_username(struct winbindd_domain *domain,
1085 NTSTATUS status,
1086 const char *alias, const char *name)
1088 struct cache_entry *centry;
1089 fstring uname;
1091 if ( (centry = centry_start(domain, status)) == NULL )
1092 return;
1094 centry_put_string( centry, name );
1096 fstrcpy(uname, alias);
1097 (void)strupper_m(uname);
1098 centry_end(centry, "NSS/AN/%s", uname);
1100 DEBUG(10,("wcache_save_alias_username: %s -> %s\n", alias, name ));
1102 centry_free(centry);
1105 /***************************************************************************
1106 ***************************************************************************/
1108 NTSTATUS resolve_username_to_alias( TALLOC_CTX *mem_ctx,
1109 struct winbindd_domain *domain,
1110 const char *name, char **alias )
1112 struct winbind_cache *cache = get_cache(domain);
1113 struct cache_entry *centry = NULL;
1114 NTSTATUS status;
1115 char *upper_name;
1117 if ( domain->internal )
1118 return NT_STATUS_NOT_SUPPORTED;
1120 if (!cache->tdb)
1121 goto do_query;
1123 upper_name = talloc_strdup_upper(mem_ctx, name);
1124 if (upper_name == NULL) {
1125 return NT_STATUS_NO_MEMORY;
1128 centry = wcache_fetch(cache, domain, "NSS/NA/%s", upper_name);
1130 talloc_free(upper_name);
1132 if (!centry)
1133 goto do_query;
1135 status = centry->status;
1137 if (!NT_STATUS_IS_OK(status)) {
1138 centry_free(centry);
1139 return status;
1142 *alias = centry_string( centry, mem_ctx );
1144 centry_free(centry);
1146 DEBUG(10,("resolve_username_to_alias: [Cached] - mapped %s to %s\n",
1147 name, *alias ? *alias : "(none)"));
1149 return (*alias) ? NT_STATUS_OK : NT_STATUS_OBJECT_NAME_NOT_FOUND;
1151 do_query:
1153 /* If its not in cache and we are offline, then fail */
1155 if ( get_global_winbindd_state_offline() || !domain->online ) {
1156 DEBUG(8,("resolve_username_to_alias: rejecting query "
1157 "in offline mode\n"));
1158 return NT_STATUS_NOT_FOUND;
1161 status = nss_map_to_alias( mem_ctx, domain->name, name, alias );
1163 if ( NT_STATUS_IS_OK( status ) ) {
1164 wcache_save_username_alias(domain, status, name, *alias);
1167 if ( NT_STATUS_EQUAL( status, NT_STATUS_NONE_MAPPED ) ) {
1168 wcache_save_username_alias(domain, status, name, "(NULL)");
1171 DEBUG(5,("resolve_username_to_alias: backend query returned %s\n",
1172 nt_errstr(status)));
1174 if ( NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ) {
1175 set_domain_offline( domain );
1178 return status;
1181 /***************************************************************************
1182 ***************************************************************************/
1184 NTSTATUS resolve_alias_to_username( TALLOC_CTX *mem_ctx,
1185 struct winbindd_domain *domain,
1186 const char *alias, char **name )
1188 struct winbind_cache *cache = get_cache(domain);
1189 struct cache_entry *centry = NULL;
1190 NTSTATUS status;
1191 char *upper_name;
1193 if ( domain->internal )
1194 return NT_STATUS_NOT_SUPPORTED;
1196 if (!cache->tdb)
1197 goto do_query;
1199 upper_name = talloc_strdup(mem_ctx, alias);
1200 if (upper_name == NULL) {
1201 return NT_STATUS_NO_MEMORY;
1203 if (!strupper_m(upper_name)) {
1204 talloc_free(upper_name);
1205 return NT_STATUS_INVALID_PARAMETER;
1208 centry = wcache_fetch(cache, domain, "NSS/AN/%s", upper_name);
1210 talloc_free(upper_name);
1212 if (!centry)
1213 goto do_query;
1215 status = centry->status;
1217 if (!NT_STATUS_IS_OK(status)) {
1218 centry_free(centry);
1219 return status;
1222 *name = centry_string( centry, mem_ctx );
1224 centry_free(centry);
1226 DEBUG(10,("resolve_alias_to_username: [Cached] - mapped %s to %s\n",
1227 alias, *name ? *name : "(none)"));
1229 return (*name) ? NT_STATUS_OK : NT_STATUS_OBJECT_NAME_NOT_FOUND;
1231 do_query:
1233 /* If its not in cache and we are offline, then fail */
1235 if ( get_global_winbindd_state_offline() || !domain->online ) {
1236 DEBUG(8,("resolve_alias_to_username: rejecting query "
1237 "in offline mode\n"));
1238 return NT_STATUS_NOT_FOUND;
1241 /* an alias cannot contain a domain prefix or '@' */
1243 if (strchr(alias, '\\') || strchr(alias, '@')) {
1244 DEBUG(10,("resolve_alias_to_username: skipping fully "
1245 "qualified name %s\n", alias));
1246 return NT_STATUS_OBJECT_NAME_INVALID;
1249 status = nss_map_from_alias( mem_ctx, domain->name, alias, name );
1251 if ( NT_STATUS_IS_OK( status ) ) {
1252 wcache_save_alias_username( domain, status, alias, *name );
1255 if (NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) {
1256 wcache_save_alias_username(domain, status, alias, "(NULL)");
1259 DEBUG(5,("resolve_alias_to_username: backend query returned %s\n",
1260 nt_errstr(status)));
1262 if ( NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ) {
1263 set_domain_offline( domain );
1266 return status;
1269 NTSTATUS wcache_cached_creds_exist(struct winbindd_domain *domain, const struct dom_sid *sid)
1271 struct winbind_cache *cache = get_cache(domain);
1272 TDB_DATA data;
1273 fstring key_str, tmp;
1274 uint32_t rid;
1276 if (!cache->tdb) {
1277 return NT_STATUS_INTERNAL_DB_ERROR;
1280 if (is_null_sid(sid)) {
1281 return NT_STATUS_INVALID_SID;
1284 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1285 return NT_STATUS_INVALID_SID;
1288 fstr_sprintf(key_str, "CRED/%s", sid_to_fstring(tmp, sid));
1290 data = tdb_fetch(cache->tdb, string_tdb_data(key_str));
1291 if (!data.dptr) {
1292 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1295 SAFE_FREE(data.dptr);
1296 return NT_STATUS_OK;
1299 /* Lookup creds for a SID - copes with old (unsalted) creds as well
1300 as new salted ones. */
1302 NTSTATUS wcache_get_creds(struct winbindd_domain *domain,
1303 TALLOC_CTX *mem_ctx,
1304 const struct dom_sid *sid,
1305 const uint8_t **cached_nt_pass,
1306 const uint8_t **cached_salt)
1308 struct winbind_cache *cache = get_cache(domain);
1309 struct cache_entry *centry = NULL;
1310 NTSTATUS status;
1311 uint32_t rid;
1312 fstring tmp;
1314 if (!cache->tdb) {
1315 return NT_STATUS_INTERNAL_DB_ERROR;
1318 if (is_null_sid(sid)) {
1319 return NT_STATUS_INVALID_SID;
1322 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1323 return NT_STATUS_INVALID_SID;
1326 /* Try and get a salted cred first. If we can't
1327 fall back to an unsalted cred. */
1329 centry = wcache_fetch(cache, domain, "CRED/%s",
1330 sid_to_fstring(tmp, sid));
1331 if (!centry) {
1332 DEBUG(10,("wcache_get_creds: entry for [CRED/%s] not found\n",
1333 sid_string_dbg(sid)));
1334 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1338 * We don't use the time element at this moment,
1339 * but we have to consume it, so that we don't
1340 * neet to change the disk format of the cache.
1342 (void)centry_time(centry);
1344 /* In the salted case this isn't actually the nt_hash itself,
1345 but the MD5 of the salt + nt_hash. Let the caller
1346 sort this out. It can tell as we only return the cached_salt
1347 if we are returning a salted cred. */
1349 *cached_nt_pass = (const uint8_t *)centry_hash16(centry, mem_ctx);
1350 if (*cached_nt_pass == NULL) {
1351 fstring sidstr;
1353 sid_to_fstring(sidstr, sid);
1355 /* Bad (old) cred cache. Delete and pretend we
1356 don't have it. */
1357 DEBUG(0,("wcache_get_creds: bad entry for [CRED/%s] - deleting\n",
1358 sidstr));
1359 wcache_delete("CRED/%s", sidstr);
1360 centry_free(centry);
1361 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1364 /* We only have 17 bytes more data in the salted cred case. */
1365 if (centry->len - centry->ofs == 17) {
1366 *cached_salt = (const uint8_t *)centry_hash16(centry, mem_ctx);
1367 } else {
1368 *cached_salt = NULL;
1371 dump_data_pw("cached_nt_pass", *cached_nt_pass, NT_HASH_LEN);
1372 if (*cached_salt) {
1373 dump_data_pw("cached_salt", *cached_salt, NT_HASH_LEN);
1376 status = centry->status;
1378 DEBUG(10,("wcache_get_creds: [Cached] - cached creds for user %s status: %s\n",
1379 sid_string_dbg(sid), nt_errstr(status) ));
1381 centry_free(centry);
1382 return status;
1385 /* Store creds for a SID - only writes out new salted ones. */
1387 NTSTATUS wcache_save_creds(struct winbindd_domain *domain,
1388 const struct dom_sid *sid,
1389 const uint8_t nt_pass[NT_HASH_LEN])
1391 struct cache_entry *centry;
1392 fstring sid_string;
1393 uint32_t rid;
1394 uint8_t cred_salt[NT_HASH_LEN];
1395 uint8_t salted_hash[NT_HASH_LEN];
1397 if (is_null_sid(sid)) {
1398 return NT_STATUS_INVALID_SID;
1401 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1402 return NT_STATUS_INVALID_SID;
1405 centry = centry_start(domain, NT_STATUS_OK);
1406 if (!centry) {
1407 return NT_STATUS_INTERNAL_DB_ERROR;
1410 dump_data_pw("nt_pass", nt_pass, NT_HASH_LEN);
1412 centry_put_time(centry, time(NULL));
1414 /* Create a salt and then salt the hash. */
1415 generate_random_buffer(cred_salt, NT_HASH_LEN);
1416 E_md5hash(cred_salt, nt_pass, salted_hash);
1418 centry_put_hash16(centry, salted_hash);
1419 centry_put_hash16(centry, cred_salt);
1420 centry_end(centry, "CRED/%s", sid_to_fstring(sid_string, sid));
1422 DEBUG(10,("wcache_save_creds: %s\n", sid_string));
1424 centry_free(centry);
1426 return NT_STATUS_OK;
1430 /* Query display info. This is the basic user list fn */
1431 NTSTATUS wb_cache_query_user_list(struct winbindd_domain *domain,
1432 TALLOC_CTX *mem_ctx,
1433 uint32_t **prids)
1435 struct winbind_cache *cache = get_cache(domain);
1436 struct cache_entry *centry = NULL;
1437 uint32_t num_rids = 0;
1438 uint32_t *rids = NULL;
1439 NTSTATUS status;
1440 unsigned int i, retry;
1441 bool old_status = domain->online;
1443 *prids = NULL;
1445 if (!cache->tdb)
1446 goto do_query;
1448 centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
1449 if (!centry)
1450 goto do_query;
1452 do_fetch_cache:
1453 num_rids = centry_uint32(centry);
1455 if (num_rids == 0) {
1456 goto do_cached;
1459 rids = talloc_array(mem_ctx, uint32_t, num_rids);
1460 if (rids == NULL) {
1461 centry_free(centry);
1462 return NT_STATUS_NO_MEMORY;
1465 for (i=0; i<num_rids; i++) {
1466 rids[i] = centry_uint32(centry);
1469 do_cached:
1470 status = centry->status;
1472 DEBUG(10,("query_user_list: [Cached] - cached list for domain %s status: %s\n",
1473 domain->name, nt_errstr(status) ));
1475 centry_free(centry);
1476 return status;
1478 do_query:
1480 /* Return status value returned by seq number check */
1482 if (!NT_STATUS_IS_OK(domain->last_status))
1483 return domain->last_status;
1485 /* Put the query_user_list() in a retry loop. There appears to be
1486 * some bug either with Windows 2000 or Samba's handling of large
1487 * rpc replies. This manifests itself as sudden disconnection
1488 * at a random point in the enumeration of a large (60k) user list.
1489 * The retry loop simply tries the operation again. )-: It's not
1490 * pretty but an acceptable workaround until we work out what the
1491 * real problem is. */
1493 retry = 0;
1494 do {
1496 DEBUG(10,("query_user_list: [Cached] - doing backend query for list for domain %s\n",
1497 domain->name ));
1499 rids = NULL;
1500 status = domain->backend->query_user_list(domain, mem_ctx,
1501 &rids);
1502 num_rids = talloc_array_length(rids);
1504 if (!NT_STATUS_IS_OK(status)) {
1505 DEBUG(3, ("query_user_list: returned 0x%08x, "
1506 "retrying\n", NT_STATUS_V(status)));
1508 if (NT_STATUS_EQUAL(status, NT_STATUS_UNSUCCESSFUL)) {
1509 DEBUG(3, ("query_user_list: flushing "
1510 "connection cache\n"));
1511 invalidate_cm_connection(domain);
1513 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1514 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1515 if (!domain->internal && old_status) {
1516 set_domain_offline(domain);
1518 /* store partial response. */
1519 if (num_rids > 0) {
1521 * humm, what about the status used for cache?
1522 * Should it be NT_STATUS_OK?
1524 break;
1527 * domain is offline now, and there is no user entries,
1528 * try to fetch from cache again.
1530 if (cache->tdb && !domain->online && !domain->internal && old_status) {
1531 centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
1532 /* partial response... */
1533 if (!centry) {
1534 goto skip_save;
1535 } else {
1536 goto do_fetch_cache;
1538 } else {
1539 goto skip_save;
1543 } while (NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_UNSUCCESSFUL) &&
1544 (retry++ < 5));
1546 /* and save it */
1547 refresh_sequence_number(domain);
1548 if (!NT_STATUS_IS_OK(status)) {
1549 return status;
1551 centry = centry_start(domain, status);
1552 if (!centry)
1553 goto skip_save;
1554 centry_put_uint32(centry, num_rids);
1555 for (i=0; i<num_rids; i++) {
1556 centry_put_uint32(centry, rids[i]);
1558 centry_end(centry, "UL/%s", domain->name);
1559 centry_free(centry);
1561 *prids = rids;
1563 skip_save:
1564 return status;
1567 /* list all domain groups */
1568 NTSTATUS wb_cache_enum_dom_groups(struct winbindd_domain *domain,
1569 TALLOC_CTX *mem_ctx,
1570 uint32_t *num_entries,
1571 struct wb_acct_info **info)
1573 struct winbind_cache *cache = get_cache(domain);
1574 struct cache_entry *centry = NULL;
1575 NTSTATUS status;
1576 unsigned int i;
1577 bool old_status;
1579 old_status = domain->online;
1580 if (!cache->tdb)
1581 goto do_query;
1583 centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
1584 if (!centry)
1585 goto do_query;
1587 do_fetch_cache:
1588 *num_entries = centry_uint32(centry);
1590 if (*num_entries == 0)
1591 goto do_cached;
1593 (*info) = talloc_array(mem_ctx, struct wb_acct_info, *num_entries);
1594 if (! (*info)) {
1595 smb_panic_fn("enum_dom_groups out of memory");
1597 for (i=0; i<(*num_entries); i++) {
1598 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1599 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1600 (*info)[i].rid = centry_uint32(centry);
1603 do_cached:
1604 status = centry->status;
1606 DEBUG(10,("enum_dom_groups: [Cached] - cached list for domain %s status: %s\n",
1607 domain->name, nt_errstr(status) ));
1609 centry_free(centry);
1610 return status;
1612 do_query:
1613 *num_entries = 0;
1614 *info = NULL;
1616 /* Return status value returned by seq number check */
1618 if (!NT_STATUS_IS_OK(domain->last_status))
1619 return domain->last_status;
1621 DEBUG(10,("enum_dom_groups: [Cached] - doing backend query for list for domain %s\n",
1622 domain->name ));
1624 status = domain->backend->enum_dom_groups(domain, mem_ctx, num_entries, info);
1626 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1627 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1628 if (!domain->internal && old_status) {
1629 set_domain_offline(domain);
1631 if (cache->tdb &&
1632 !domain->online &&
1633 !domain->internal &&
1634 old_status) {
1635 centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
1636 if (centry) {
1637 goto do_fetch_cache;
1641 /* and save it */
1642 refresh_sequence_number(domain);
1643 if (!NT_STATUS_IS_OK(status)) {
1644 return status;
1646 centry = centry_start(domain, status);
1647 if (!centry)
1648 goto skip_save;
1649 centry_put_uint32(centry, *num_entries);
1650 for (i=0; i<(*num_entries); i++) {
1651 centry_put_string(centry, (*info)[i].acct_name);
1652 centry_put_string(centry, (*info)[i].acct_desc);
1653 centry_put_uint32(centry, (*info)[i].rid);
1655 centry_end(centry, "GL/%s/domain", domain->name);
1656 centry_free(centry);
1658 skip_save:
1659 return status;
1662 /* list all domain groups */
1663 NTSTATUS wb_cache_enum_local_groups(struct winbindd_domain *domain,
1664 TALLOC_CTX *mem_ctx,
1665 uint32_t *num_entries,
1666 struct wb_acct_info **info)
1668 struct winbind_cache *cache = get_cache(domain);
1669 struct cache_entry *centry = NULL;
1670 NTSTATUS status;
1671 unsigned int i;
1672 bool old_status;
1674 old_status = domain->online;
1675 if (!cache->tdb)
1676 goto do_query;
1678 centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
1679 if (!centry)
1680 goto do_query;
1682 do_fetch_cache:
1683 *num_entries = centry_uint32(centry);
1685 if (*num_entries == 0)
1686 goto do_cached;
1688 (*info) = talloc_array(mem_ctx, struct wb_acct_info, *num_entries);
1689 if (! (*info)) {
1690 smb_panic_fn("enum_dom_groups out of memory");
1692 for (i=0; i<(*num_entries); i++) {
1693 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1694 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1695 (*info)[i].rid = centry_uint32(centry);
1698 do_cached:
1700 /* If we are returning cached data and the domain controller
1701 is down then we don't know whether the data is up to date
1702 or not. Return NT_STATUS_MORE_PROCESSING_REQUIRED to
1703 indicate this. */
1705 if (wcache_server_down(domain)) {
1706 DEBUG(10, ("enum_local_groups: returning cached user list and server was down\n"));
1707 status = NT_STATUS_MORE_PROCESSING_REQUIRED;
1708 } else
1709 status = centry->status;
1711 DEBUG(10,("enum_local_groups: [Cached] - cached list for domain %s status: %s\n",
1712 domain->name, nt_errstr(status) ));
1714 centry_free(centry);
1715 return status;
1717 do_query:
1718 *num_entries = 0;
1719 *info = NULL;
1721 /* Return status value returned by seq number check */
1723 if (!NT_STATUS_IS_OK(domain->last_status))
1724 return domain->last_status;
1726 DEBUG(10,("enum_local_groups: [Cached] - doing backend query for list for domain %s\n",
1727 domain->name ));
1729 status = domain->backend->enum_local_groups(domain, mem_ctx, num_entries, info);
1731 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1732 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1733 if (!domain->internal && old_status) {
1734 set_domain_offline(domain);
1736 if (cache->tdb &&
1737 !domain->internal &&
1738 !domain->online &&
1739 old_status) {
1740 centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
1741 if (centry) {
1742 goto do_fetch_cache;
1746 /* and save it */
1747 refresh_sequence_number(domain);
1748 if (!NT_STATUS_IS_OK(status)) {
1749 return status;
1751 centry = centry_start(domain, status);
1752 if (!centry)
1753 goto skip_save;
1754 centry_put_uint32(centry, *num_entries);
1755 for (i=0; i<(*num_entries); i++) {
1756 centry_put_string(centry, (*info)[i].acct_name);
1757 centry_put_string(centry, (*info)[i].acct_desc);
1758 centry_put_uint32(centry, (*info)[i].rid);
1760 centry_end(centry, "GL/%s/local", domain->name);
1761 centry_free(centry);
1763 skip_save:
1764 return status;
1767 static NTSTATUS wcache_name_to_sid(struct winbindd_domain *domain,
1768 const char *domain_name,
1769 const char *name,
1770 struct dom_sid *sid,
1771 enum lsa_SidType *type)
1773 struct winbind_cache *cache = get_cache(domain);
1774 struct cache_entry *centry;
1775 NTSTATUS status;
1776 char *uname;
1778 if (cache->tdb == NULL) {
1779 return NT_STATUS_NOT_FOUND;
1782 uname = talloc_strdup_upper(talloc_tos(), name);
1783 if (uname == NULL) {
1784 return NT_STATUS_NO_MEMORY;
1787 if ((domain_name == NULL) || (domain_name[0] == '\0')) {
1788 domain_name = domain->name;
1791 centry = wcache_fetch(cache, domain, "NS/%s/%s", domain_name, uname);
1792 TALLOC_FREE(uname);
1793 if (centry == NULL) {
1794 return NT_STATUS_NOT_FOUND;
1797 status = centry->status;
1798 if (NT_STATUS_IS_OK(status)) {
1799 *type = (enum lsa_SidType)centry_uint32(centry);
1800 centry_sid(centry, sid);
1803 DEBUG(10,("name_to_sid: [Cached] - cached name for domain %s status: "
1804 "%s\n", domain->name, nt_errstr(status) ));
1806 centry_free(centry);
1807 return status;
1810 /* convert a single name to a sid in a domain */
1811 NTSTATUS wb_cache_name_to_sid(struct winbindd_domain *domain,
1812 TALLOC_CTX *mem_ctx,
1813 const char *domain_name,
1814 const char *name,
1815 uint32_t flags,
1816 struct dom_sid *sid,
1817 enum lsa_SidType *type)
1819 NTSTATUS status;
1820 bool old_status;
1822 old_status = domain->online;
1824 status = wcache_name_to_sid(domain, domain_name, name, sid, type);
1825 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
1826 return status;
1829 ZERO_STRUCTP(sid);
1831 /* If the seq number check indicated that there is a problem
1832 * with this DC, then return that status... except for
1833 * access_denied. This is special because the dc may be in
1834 * "restrict anonymous = 1" mode, in which case it will deny
1835 * most unauthenticated operations, but *will* allow the LSA
1836 * name-to-sid that we try as a fallback. */
1838 if (!(NT_STATUS_IS_OK(domain->last_status)
1839 || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1840 return domain->last_status;
1842 DEBUG(10,("name_to_sid: [Cached] - doing backend query for name for domain %s\n",
1843 domain->name ));
1845 status = domain->backend->name_to_sid(domain, mem_ctx, domain_name,
1846 name, flags, sid, type);
1848 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1849 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1850 if (!domain->internal && old_status) {
1851 set_domain_offline(domain);
1853 if (!domain->internal &&
1854 !domain->online &&
1855 old_status) {
1856 NTSTATUS cache_status;
1857 cache_status = wcache_name_to_sid(domain, domain_name, name, sid, type);
1858 return cache_status;
1861 /* and save it */
1862 refresh_sequence_number(domain);
1864 if (domain->online &&
1865 (NT_STATUS_IS_OK(status) || NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED))) {
1866 wcache_save_name_to_sid(domain, status, domain_name, name, sid, *type);
1868 /* Only save the reverse mapping if this was not a UPN */
1869 if (!strchr(name, '@')) {
1870 if (!strupper_m(discard_const_p(char, domain_name))) {
1871 return NT_STATUS_INVALID_PARAMETER;
1873 (void)strlower_m(discard_const_p(char, name));
1874 wcache_save_sid_to_name(domain, status, sid, domain_name, name, *type);
1878 return status;
1881 static NTSTATUS wcache_sid_to_name(struct winbindd_domain *domain,
1882 const struct dom_sid *sid,
1883 TALLOC_CTX *mem_ctx,
1884 char **domain_name,
1885 char **name,
1886 enum lsa_SidType *type)
1888 struct winbind_cache *cache = get_cache(domain);
1889 struct cache_entry *centry;
1890 char *sid_string;
1891 NTSTATUS status;
1893 if (cache->tdb == NULL) {
1894 return NT_STATUS_NOT_FOUND;
1897 sid_string = sid_string_tos(sid);
1898 if (sid_string == NULL) {
1899 return NT_STATUS_NO_MEMORY;
1902 centry = wcache_fetch(cache, domain, "SN/%s", sid_string);
1903 TALLOC_FREE(sid_string);
1904 if (centry == NULL) {
1905 return NT_STATUS_NOT_FOUND;
1908 if (NT_STATUS_IS_OK(centry->status)) {
1909 *type = (enum lsa_SidType)centry_uint32(centry);
1910 *domain_name = centry_string(centry, mem_ctx);
1911 *name = centry_string(centry, mem_ctx);
1914 status = centry->status;
1915 centry_free(centry);
1917 DEBUG(10,("sid_to_name: [Cached] - cached name for domain %s status: "
1918 "%s\n", domain->name, nt_errstr(status) ));
1920 return status;
1923 /* convert a sid to a user or group name. The sid is guaranteed to be in the domain
1924 given */
1925 NTSTATUS wb_cache_sid_to_name(struct winbindd_domain *domain,
1926 TALLOC_CTX *mem_ctx,
1927 const struct dom_sid *sid,
1928 char **domain_name,
1929 char **name,
1930 enum lsa_SidType *type)
1932 NTSTATUS status;
1933 bool old_status;
1935 old_status = domain->online;
1936 status = wcache_sid_to_name(domain, sid, mem_ctx, domain_name, name,
1937 type);
1938 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
1939 return status;
1942 *name = NULL;
1943 *domain_name = NULL;
1945 /* If the seq number check indicated that there is a problem
1946 * with this DC, then return that status... except for
1947 * access_denied. This is special because the dc may be in
1948 * "restrict anonymous = 1" mode, in which case it will deny
1949 * most unauthenticated operations, but *will* allow the LSA
1950 * sid-to-name that we try as a fallback. */
1952 if (!(NT_STATUS_IS_OK(domain->last_status)
1953 || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1954 return domain->last_status;
1956 DEBUG(10,("sid_to_name: [Cached] - doing backend query for name for domain %s\n",
1957 domain->name ));
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 refresh_sequence_number(domain);
1977 if (!NT_STATUS_IS_OK(status)) {
1978 return status;
1980 wcache_save_sid_to_name(domain, status, sid, *domain_name, *name, *type);
1982 /* We can't save the name to sid mapping here, as with sid history a
1983 * later name2sid would give the wrong sid. */
1985 return status;
1988 NTSTATUS wb_cache_rids_to_names(struct winbindd_domain *domain,
1989 TALLOC_CTX *mem_ctx,
1990 const struct dom_sid *domain_sid,
1991 uint32_t *rids,
1992 size_t num_rids,
1993 char **domain_name,
1994 char ***names,
1995 enum lsa_SidType **types)
1997 struct winbind_cache *cache = get_cache(domain);
1998 size_t i;
1999 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
2000 bool have_mapped;
2001 bool have_unmapped;
2002 bool old_status;
2004 old_status = domain->online;
2005 *domain_name = NULL;
2006 *names = NULL;
2007 *types = NULL;
2009 if (!cache->tdb) {
2010 goto do_query;
2013 if (num_rids == 0) {
2014 return NT_STATUS_OK;
2017 *names = talloc_array(mem_ctx, char *, num_rids);
2018 *types = talloc_array(mem_ctx, enum lsa_SidType, num_rids);
2020 if ((*names == NULL) || (*types == NULL)) {
2021 result = NT_STATUS_NO_MEMORY;
2022 goto error;
2025 have_mapped = have_unmapped = false;
2027 for (i=0; i<num_rids; i++) {
2028 struct dom_sid sid;
2029 struct cache_entry *centry;
2030 fstring tmp;
2032 if (!sid_compose(&sid, domain_sid, rids[i])) {
2033 result = NT_STATUS_INTERNAL_ERROR;
2034 goto error;
2037 centry = wcache_fetch(cache, domain, "SN/%s",
2038 sid_to_fstring(tmp, &sid));
2039 if (!centry) {
2040 goto do_query;
2043 (*types)[i] = SID_NAME_UNKNOWN;
2044 (*names)[i] = talloc_strdup(*names, "");
2046 if (NT_STATUS_IS_OK(centry->status)) {
2047 char *dom;
2048 have_mapped = true;
2049 (*types)[i] = (enum lsa_SidType)centry_uint32(centry);
2051 dom = centry_string(centry, mem_ctx);
2052 if (*domain_name == NULL) {
2053 *domain_name = dom;
2054 } else {
2055 talloc_free(dom);
2058 (*names)[i] = centry_string(centry, *names);
2060 } else if (NT_STATUS_EQUAL(centry->status, NT_STATUS_NONE_MAPPED)
2061 || NT_STATUS_EQUAL(centry->status, STATUS_SOME_UNMAPPED)) {
2062 have_unmapped = true;
2064 } else {
2065 /* something's definitely wrong */
2066 result = centry->status;
2067 centry_free(centry);
2068 goto error;
2071 centry_free(centry);
2074 if (!have_mapped) {
2075 return NT_STATUS_NONE_MAPPED;
2077 if (!have_unmapped) {
2078 return NT_STATUS_OK;
2080 return STATUS_SOME_UNMAPPED;
2082 do_query:
2084 TALLOC_FREE(*names);
2085 TALLOC_FREE(*types);
2087 result = domain->backend->rids_to_names(domain, mem_ctx, domain_sid,
2088 rids, num_rids, domain_name,
2089 names, types);
2091 if (NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
2092 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2093 if (!domain->internal && old_status) {
2094 set_domain_offline(domain);
2096 if (cache->tdb &&
2097 !domain->internal &&
2098 !domain->online &&
2099 old_status) {
2100 have_mapped = have_unmapped = false;
2102 *names = talloc_array(mem_ctx, char *, num_rids);
2103 if (*names == NULL) {
2104 result = NT_STATUS_NO_MEMORY;
2105 goto error;
2108 *types = talloc_array(mem_ctx, enum lsa_SidType,
2109 num_rids);
2110 if (*types == NULL) {
2111 result = NT_STATUS_NO_MEMORY;
2112 goto error;
2115 for (i=0; i<num_rids; i++) {
2116 struct dom_sid sid;
2117 struct cache_entry *centry;
2118 fstring tmp;
2120 if (!sid_compose(&sid, domain_sid, rids[i])) {
2121 result = NT_STATUS_INTERNAL_ERROR;
2122 goto error;
2125 centry = wcache_fetch(cache, domain, "SN/%s",
2126 sid_to_fstring(tmp, &sid));
2127 if (!centry) {
2128 (*types)[i] = SID_NAME_UNKNOWN;
2129 (*names)[i] = talloc_strdup(*names, "");
2130 continue;
2133 (*types)[i] = SID_NAME_UNKNOWN;
2134 (*names)[i] = talloc_strdup(*names, "");
2136 if (NT_STATUS_IS_OK(centry->status)) {
2137 char *dom;
2138 have_mapped = true;
2139 (*types)[i] = (enum lsa_SidType)centry_uint32(centry);
2141 dom = centry_string(centry, mem_ctx);
2142 if (*domain_name == NULL) {
2143 *domain_name = dom;
2144 } else {
2145 talloc_free(dom);
2148 (*names)[i] = centry_string(centry, *names);
2150 } else if (NT_STATUS_EQUAL(centry->status, NT_STATUS_NONE_MAPPED)) {
2151 have_unmapped = true;
2153 } else {
2154 /* something's definitely wrong */
2155 result = centry->status;
2156 centry_free(centry);
2157 goto error;
2160 centry_free(centry);
2163 if (!have_mapped) {
2164 return NT_STATUS_NONE_MAPPED;
2166 if (!have_unmapped) {
2167 return NT_STATUS_OK;
2169 return STATUS_SOME_UNMAPPED;
2173 None of the queried rids has been found so save all negative entries
2175 if (NT_STATUS_EQUAL(result, NT_STATUS_NONE_MAPPED)) {
2176 for (i = 0; i < num_rids; i++) {
2177 struct dom_sid sid;
2178 const char *name = "";
2179 const enum lsa_SidType type = SID_NAME_UNKNOWN;
2180 NTSTATUS status = NT_STATUS_NONE_MAPPED;
2182 if (!sid_compose(&sid, domain_sid, rids[i])) {
2183 return NT_STATUS_INTERNAL_ERROR;
2186 wcache_save_sid_to_name(domain, status, &sid, *domain_name,
2187 name, type);
2190 return result;
2194 Some or all of the queried rids have been found.
2196 if (!NT_STATUS_IS_OK(result) &&
2197 !NT_STATUS_EQUAL(result, STATUS_SOME_UNMAPPED)) {
2198 return result;
2201 refresh_sequence_number(domain);
2203 for (i=0; i<num_rids; i++) {
2204 struct dom_sid sid;
2205 NTSTATUS status;
2207 if (!sid_compose(&sid, domain_sid, rids[i])) {
2208 result = NT_STATUS_INTERNAL_ERROR;
2209 goto error;
2212 status = (*types)[i] == SID_NAME_UNKNOWN ?
2213 NT_STATUS_NONE_MAPPED : NT_STATUS_OK;
2215 wcache_save_sid_to_name(domain, status, &sid, *domain_name,
2216 (*names)[i], (*types)[i]);
2219 return result;
2221 error:
2222 TALLOC_FREE(*names);
2223 TALLOC_FREE(*types);
2224 return result;
2227 NTSTATUS wcache_query_user(struct winbindd_domain *domain,
2228 TALLOC_CTX *mem_ctx,
2229 const struct dom_sid *user_sid,
2230 struct wbint_userinfo *info)
2232 struct winbind_cache *cache = get_cache(domain);
2233 struct cache_entry *centry = NULL;
2234 NTSTATUS status;
2235 char *sid_string;
2237 if (cache->tdb == NULL) {
2238 return NT_STATUS_NOT_FOUND;
2241 sid_string = sid_string_tos(user_sid);
2242 if (sid_string == NULL) {
2243 return NT_STATUS_NO_MEMORY;
2246 centry = wcache_fetch(cache, domain, "U/%s", sid_string);
2247 TALLOC_FREE(sid_string);
2248 if (centry == NULL) {
2249 return NT_STATUS_NOT_FOUND;
2253 * If we have an access denied cache entry and a cached info3
2254 * in the samlogon cache then do a query. This will force the
2255 * rpc back end to return the info3 data.
2258 if (NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED) &&
2259 netsamlogon_cache_have(user_sid)) {
2260 DEBUG(10, ("query_user: cached access denied and have cached "
2261 "info3\n"));
2262 domain->last_status = NT_STATUS_OK;
2263 centry_free(centry);
2264 return NT_STATUS_NOT_FOUND;
2267 /* if status is not ok then this is a negative hit
2268 and the rest of the data doesn't matter */
2269 status = centry->status;
2270 if (NT_STATUS_IS_OK(status)) {
2271 info->domain_name = centry_string(centry, mem_ctx);
2272 info->acct_name = centry_string(centry, mem_ctx);
2273 info->full_name = centry_string(centry, mem_ctx);
2274 info->homedir = centry_string(centry, mem_ctx);
2275 info->shell = centry_string(centry, mem_ctx);
2276 info->uid = centry_uint32(centry);
2277 info->primary_gid = centry_uint32(centry);
2278 info->primary_group_name = centry_string(centry, mem_ctx);
2279 centry_sid(centry, &info->user_sid);
2280 centry_sid(centry, &info->group_sid);
2283 DEBUG(10,("query_user: [Cached] - cached info for domain %s status: "
2284 "%s\n", domain->name, nt_errstr(status) ));
2286 centry_free(centry);
2287 return status;
2292 * @brief Query a fullname from the username cache (for further gecos processing)
2294 * @param domain A pointer to the winbindd_domain struct.
2295 * @param mem_ctx The talloc context.
2296 * @param user_sid The user sid.
2297 * @param full_name A pointer to the full_name string.
2299 * @return NTSTATUS code
2301 NTSTATUS wcache_query_user_fullname(struct winbindd_domain *domain,
2302 TALLOC_CTX *mem_ctx,
2303 const struct dom_sid *user_sid,
2304 const char **full_name)
2306 NTSTATUS status;
2307 struct wbint_userinfo info;
2309 status = wcache_query_user(domain, mem_ctx, user_sid, &info);
2310 if (!NT_STATUS_IS_OK(status)) {
2311 return status;
2314 if (info.full_name != NULL) {
2315 *full_name = talloc_strdup(mem_ctx, info.full_name);
2316 if (*full_name == NULL) {
2317 return NT_STATUS_NO_MEMORY;
2321 return NT_STATUS_OK;
2324 NTSTATUS wcache_lookup_usergroups(struct winbindd_domain *domain,
2325 TALLOC_CTX *mem_ctx,
2326 const struct dom_sid *user_sid,
2327 uint32_t *pnum_sids,
2328 struct dom_sid **psids)
2330 struct winbind_cache *cache = get_cache(domain);
2331 struct cache_entry *centry = NULL;
2332 NTSTATUS status;
2333 uint32_t i, num_sids;
2334 struct dom_sid *sids;
2335 fstring sid_string;
2337 if (cache->tdb == NULL) {
2338 return NT_STATUS_NOT_FOUND;
2341 centry = wcache_fetch(cache, domain, "UG/%s",
2342 sid_to_fstring(sid_string, user_sid));
2343 if (centry == NULL) {
2344 return NT_STATUS_NOT_FOUND;
2347 /* If we have an access denied cache entry and a cached info3 in the
2348 samlogon cache then do a query. This will force the rpc back end
2349 to return the info3 data. */
2351 if (NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)
2352 && netsamlogon_cache_have(user_sid)) {
2353 DEBUG(10, ("lookup_usergroups: cached access denied and have "
2354 "cached info3\n"));
2355 domain->last_status = NT_STATUS_OK;
2356 centry_free(centry);
2357 return NT_STATUS_NOT_FOUND;
2360 num_sids = centry_uint32(centry);
2361 sids = talloc_array(mem_ctx, struct dom_sid, num_sids);
2362 if (sids == NULL) {
2363 centry_free(centry);
2364 return NT_STATUS_NO_MEMORY;
2367 for (i=0; i<num_sids; i++) {
2368 centry_sid(centry, &sids[i]);
2371 status = centry->status;
2373 DEBUG(10,("lookup_usergroups: [Cached] - cached info for domain %s "
2374 "status: %s\n", domain->name, nt_errstr(status)));
2376 centry_free(centry);
2378 *pnum_sids = num_sids;
2379 *psids = sids;
2380 return status;
2383 /* Lookup groups a user is a member of. */
2384 NTSTATUS wb_cache_lookup_usergroups(struct winbindd_domain *domain,
2385 TALLOC_CTX *mem_ctx,
2386 const struct dom_sid *user_sid,
2387 uint32_t *num_groups,
2388 struct dom_sid **user_gids)
2390 struct cache_entry *centry = NULL;
2391 NTSTATUS status;
2392 unsigned int i;
2393 fstring sid_string;
2394 bool old_status;
2396 old_status = domain->online;
2397 status = wcache_lookup_usergroups(domain, mem_ctx, user_sid,
2398 num_groups, user_gids);
2399 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2400 return status;
2403 (*num_groups) = 0;
2404 (*user_gids) = NULL;
2406 /* Return status value returned by seq number check */
2408 if (!NT_STATUS_IS_OK(domain->last_status))
2409 return domain->last_status;
2411 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info for domain %s\n",
2412 domain->name ));
2414 status = domain->backend->lookup_usergroups(domain, mem_ctx, user_sid, num_groups, user_gids);
2416 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2417 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2418 if (!domain->internal && old_status) {
2419 set_domain_offline(domain);
2421 if (!domain->internal &&
2422 !domain->online &&
2423 old_status) {
2424 NTSTATUS cache_status;
2425 cache_status = wcache_lookup_usergroups(domain, mem_ctx, user_sid,
2426 num_groups, user_gids);
2427 return cache_status;
2430 if ( NT_STATUS_EQUAL(status, NT_STATUS_SYNCHRONIZATION_REQUIRED) )
2431 goto skip_save;
2433 /* and save it */
2434 refresh_sequence_number(domain);
2435 if (!NT_STATUS_IS_OK(status)) {
2436 return status;
2438 centry = centry_start(domain, status);
2439 if (!centry)
2440 goto skip_save;
2442 centry_put_uint32(centry, *num_groups);
2443 for (i=0; i<(*num_groups); i++) {
2444 centry_put_sid(centry, &(*user_gids)[i]);
2447 centry_end(centry, "UG/%s", sid_to_fstring(sid_string, user_sid));
2448 centry_free(centry);
2450 skip_save:
2451 return status;
2454 static char *wcache_make_sidlist(TALLOC_CTX *mem_ctx, uint32_t num_sids,
2455 const struct dom_sid *sids)
2457 uint32_t i;
2458 char *sidlist;
2460 sidlist = talloc_strdup(mem_ctx, "");
2461 if (sidlist == NULL) {
2462 return NULL;
2464 for (i=0; i<num_sids; i++) {
2465 fstring tmp;
2466 sidlist = talloc_asprintf_append_buffer(
2467 sidlist, "/%s", sid_to_fstring(tmp, &sids[i]));
2468 if (sidlist == NULL) {
2469 return NULL;
2472 return sidlist;
2475 NTSTATUS wcache_lookup_useraliases(struct winbindd_domain *domain,
2476 TALLOC_CTX *mem_ctx, uint32_t num_sids,
2477 const struct dom_sid *sids,
2478 uint32_t *pnum_aliases, uint32_t **paliases)
2480 struct winbind_cache *cache = get_cache(domain);
2481 struct cache_entry *centry = NULL;
2482 uint32_t i, num_aliases;
2483 uint32_t *aliases;
2484 NTSTATUS status;
2485 char *sidlist;
2487 if (cache->tdb == NULL) {
2488 return NT_STATUS_NOT_FOUND;
2491 if (num_sids == 0) {
2492 *pnum_aliases = 0;
2493 *paliases = NULL;
2494 return NT_STATUS_OK;
2497 /* We need to cache indexed by the whole list of SIDs, the aliases
2498 * resulting might come from any of the SIDs. */
2500 sidlist = wcache_make_sidlist(talloc_tos(), num_sids, sids);
2501 if (sidlist == NULL) {
2502 return NT_STATUS_NO_MEMORY;
2505 centry = wcache_fetch(cache, domain, "UA%s", sidlist);
2506 TALLOC_FREE(sidlist);
2507 if (centry == NULL) {
2508 return NT_STATUS_NOT_FOUND;
2511 num_aliases = centry_uint32(centry);
2512 aliases = talloc_array(mem_ctx, uint32_t, num_aliases);
2513 if (aliases == NULL) {
2514 centry_free(centry);
2515 return NT_STATUS_NO_MEMORY;
2518 for (i=0; i<num_aliases; i++) {
2519 aliases[i] = centry_uint32(centry);
2522 status = centry->status;
2524 DEBUG(10,("lookup_useraliases: [Cached] - cached info for domain: %s "
2525 "status %s\n", domain->name, nt_errstr(status)));
2527 centry_free(centry);
2529 *pnum_aliases = num_aliases;
2530 *paliases = aliases;
2532 return status;
2535 NTSTATUS wb_cache_lookup_useraliases(struct winbindd_domain *domain,
2536 TALLOC_CTX *mem_ctx,
2537 uint32_t num_sids,
2538 const struct dom_sid *sids,
2539 uint32_t *num_aliases,
2540 uint32_t **alias_rids)
2542 struct cache_entry *centry = NULL;
2543 NTSTATUS status;
2544 char *sidlist;
2545 uint32_t i;
2546 bool old_status;
2548 old_status = domain->online;
2549 status = wcache_lookup_useraliases(domain, mem_ctx, num_sids, sids,
2550 num_aliases, alias_rids);
2551 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2552 return status;
2555 (*num_aliases) = 0;
2556 (*alias_rids) = NULL;
2558 if (!NT_STATUS_IS_OK(domain->last_status))
2559 return domain->last_status;
2561 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info "
2562 "for domain %s\n", domain->name ));
2564 sidlist = wcache_make_sidlist(talloc_tos(), num_sids, sids);
2565 if (sidlist == NULL) {
2566 return NT_STATUS_NO_MEMORY;
2569 status = domain->backend->lookup_useraliases(domain, mem_ctx,
2570 num_sids, sids,
2571 num_aliases, alias_rids);
2573 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2574 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2575 if (!domain->internal && old_status) {
2576 set_domain_offline(domain);
2578 if (!domain->internal &&
2579 !domain->online &&
2580 old_status) {
2581 NTSTATUS cache_status;
2582 cache_status = wcache_lookup_useraliases(domain, mem_ctx, num_sids,
2583 sids, num_aliases, alias_rids);
2584 return cache_status;
2587 /* and save it */
2588 refresh_sequence_number(domain);
2589 if (!NT_STATUS_IS_OK(status)) {
2590 return status;
2592 centry = centry_start(domain, status);
2593 if (!centry)
2594 goto skip_save;
2595 centry_put_uint32(centry, *num_aliases);
2596 for (i=0; i<(*num_aliases); i++)
2597 centry_put_uint32(centry, (*alias_rids)[i]);
2598 centry_end(centry, "UA%s", sidlist);
2599 centry_free(centry);
2601 skip_save:
2602 return status;
2605 static NTSTATUS wcache_lookup_groupmem(struct winbindd_domain *domain,
2606 TALLOC_CTX *mem_ctx,
2607 const struct dom_sid *group_sid,
2608 uint32_t *num_names,
2609 struct dom_sid **sid_mem, char ***names,
2610 uint32_t **name_types)
2612 struct winbind_cache *cache = get_cache(domain);
2613 struct cache_entry *centry = NULL;
2614 NTSTATUS status;
2615 unsigned int i;
2616 char *sid_string;
2618 if (cache->tdb == NULL) {
2619 return NT_STATUS_NOT_FOUND;
2622 sid_string = sid_string_tos(group_sid);
2623 if (sid_string == NULL) {
2624 return NT_STATUS_NO_MEMORY;
2627 centry = wcache_fetch(cache, domain, "GM/%s", sid_string);
2628 TALLOC_FREE(sid_string);
2629 if (centry == NULL) {
2630 return NT_STATUS_NOT_FOUND;
2633 *sid_mem = NULL;
2634 *names = NULL;
2635 *name_types = NULL;
2637 *num_names = centry_uint32(centry);
2638 if (*num_names == 0) {
2639 centry_free(centry);
2640 return NT_STATUS_OK;
2643 *sid_mem = talloc_array(mem_ctx, struct dom_sid, *num_names);
2644 *names = talloc_array(mem_ctx, char *, *num_names);
2645 *name_types = talloc_array(mem_ctx, uint32_t, *num_names);
2647 if ((*sid_mem == NULL) || (*names == NULL) || (*name_types == NULL)) {
2648 TALLOC_FREE(*sid_mem);
2649 TALLOC_FREE(*names);
2650 TALLOC_FREE(*name_types);
2651 centry_free(centry);
2652 return NT_STATUS_NO_MEMORY;
2655 for (i=0; i<(*num_names); i++) {
2656 centry_sid(centry, &(*sid_mem)[i]);
2657 (*names)[i] = centry_string(centry, mem_ctx);
2658 (*name_types)[i] = centry_uint32(centry);
2661 status = centry->status;
2663 DEBUG(10,("lookup_groupmem: [Cached] - cached info for domain %s "
2664 "status: %s\n", domain->name, nt_errstr(status)));
2666 centry_free(centry);
2667 return status;
2670 NTSTATUS wb_cache_lookup_groupmem(struct winbindd_domain *domain,
2671 TALLOC_CTX *mem_ctx,
2672 const struct dom_sid *group_sid,
2673 enum lsa_SidType type,
2674 uint32_t *num_names,
2675 struct dom_sid **sid_mem,
2676 char ***names,
2677 uint32_t **name_types)
2679 struct cache_entry *centry = NULL;
2680 NTSTATUS status;
2681 unsigned int i;
2682 fstring sid_string;
2683 bool old_status;
2685 old_status = domain->online;
2686 status = wcache_lookup_groupmem(domain, mem_ctx, group_sid, num_names,
2687 sid_mem, names, name_types);
2688 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2689 return status;
2692 (*num_names) = 0;
2693 (*sid_mem) = NULL;
2694 (*names) = NULL;
2695 (*name_types) = NULL;
2697 /* Return status value returned by seq number check */
2699 if (!NT_STATUS_IS_OK(domain->last_status))
2700 return domain->last_status;
2702 DEBUG(10,("lookup_groupmem: [Cached] - doing backend query for info for domain %s\n",
2703 domain->name ));
2705 status = domain->backend->lookup_groupmem(domain, mem_ctx, group_sid,
2706 type, num_names,
2707 sid_mem, names, name_types);
2709 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2710 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2711 if (!domain->internal && old_status) {
2712 set_domain_offline(domain);
2714 if (!domain->internal &&
2715 !domain->online &&
2716 old_status) {
2717 NTSTATUS cache_status;
2718 cache_status = wcache_lookup_groupmem(domain, mem_ctx, group_sid,
2719 num_names, sid_mem, names,
2720 name_types);
2721 return cache_status;
2724 /* and save it */
2725 refresh_sequence_number(domain);
2726 if (!NT_STATUS_IS_OK(status)) {
2727 return status;
2729 centry = centry_start(domain, status);
2730 if (!centry)
2731 goto skip_save;
2732 centry_put_uint32(centry, *num_names);
2733 for (i=0; i<(*num_names); i++) {
2734 centry_put_sid(centry, &(*sid_mem)[i]);
2735 centry_put_string(centry, (*names)[i]);
2736 centry_put_uint32(centry, (*name_types)[i]);
2738 centry_end(centry, "GM/%s", sid_to_fstring(sid_string, group_sid));
2739 centry_free(centry);
2741 skip_save:
2742 return status;
2745 /* find the sequence number for a domain */
2746 NTSTATUS wb_cache_sequence_number(struct winbindd_domain *domain,
2747 uint32_t *seq)
2749 refresh_sequence_number(domain);
2751 *seq = domain->sequence_number;
2753 return NT_STATUS_OK;
2756 /* enumerate trusted domains
2757 * (we need to have the list of trustdoms in the cache when we go offline) -
2758 * Guenther */
2759 NTSTATUS wb_cache_trusted_domains(struct winbindd_domain *domain,
2760 TALLOC_CTX *mem_ctx,
2761 struct netr_DomainTrustList *trusts)
2763 NTSTATUS status;
2764 struct winbind_cache *cache;
2765 struct winbindd_tdc_domain *dom_list = NULL;
2766 size_t num_domains = 0;
2767 bool retval = false;
2768 size_t i;
2769 bool old_status;
2771 old_status = domain->online;
2772 trusts->count = 0;
2773 trusts->array = NULL;
2775 cache = get_cache(domain);
2776 if (!cache || !cache->tdb) {
2777 goto do_query;
2780 if (domain->online) {
2781 goto do_query;
2784 retval = wcache_tdc_fetch_list(&dom_list, &num_domains);
2785 if (!retval || !num_domains || !dom_list) {
2786 TALLOC_FREE(dom_list);
2787 goto do_query;
2790 do_fetch_cache:
2791 trusts->array = talloc_zero_array(mem_ctx, struct netr_DomainTrust, num_domains);
2792 if (!trusts->array) {
2793 TALLOC_FREE(dom_list);
2794 return NT_STATUS_NO_MEMORY;
2797 for (i = 0; i < num_domains; i++) {
2798 struct netr_DomainTrust *trust;
2799 struct dom_sid *sid;
2800 struct winbindd_domain *dom;
2802 dom = find_domain_from_name_noinit(dom_list[i].domain_name);
2803 if (dom && dom->internal) {
2804 continue;
2807 trust = &trusts->array[trusts->count];
2808 trust->netbios_name = talloc_strdup(trusts->array, dom_list[i].domain_name);
2809 trust->dns_name = talloc_strdup(trusts->array, dom_list[i].dns_name);
2810 sid = talloc(trusts->array, struct dom_sid);
2811 if (!trust->netbios_name || !trust->dns_name ||
2812 !sid) {
2813 TALLOC_FREE(dom_list);
2814 TALLOC_FREE(trusts->array);
2815 return NT_STATUS_NO_MEMORY;
2818 trust->trust_flags = dom_list[i].trust_flags;
2819 trust->trust_attributes = dom_list[i].trust_attribs;
2820 trust->trust_type = dom_list[i].trust_type;
2821 sid_copy(sid, &dom_list[i].sid);
2822 trust->sid = sid;
2823 trusts->count++;
2826 TALLOC_FREE(dom_list);
2827 return NT_STATUS_OK;
2829 do_query:
2830 /* Return status value returned by seq number check */
2832 if (!NT_STATUS_IS_OK(domain->last_status))
2833 return domain->last_status;
2835 DEBUG(10,("trusted_domains: [Cached] - doing backend query for info for domain %s\n",
2836 domain->name ));
2838 status = domain->backend->trusted_domains(domain, mem_ctx, trusts);
2840 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2841 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2842 if (!domain->internal && old_status) {
2843 set_domain_offline(domain);
2845 if (!domain->internal &&
2846 !domain->online &&
2847 old_status) {
2848 retval = wcache_tdc_fetch_list(&dom_list, &num_domains);
2849 if (retval && num_domains && dom_list) {
2850 TALLOC_FREE(trusts->array);
2851 trusts->count = 0;
2852 goto do_fetch_cache;
2856 /* no trusts gives NT_STATUS_NO_MORE_ENTRIES resetting to NT_STATUS_OK
2857 * so that the generic centry handling still applies correctly -
2858 * Guenther*/
2860 if (!NT_STATUS_IS_ERR(status)) {
2861 status = NT_STATUS_OK;
2863 return status;
2866 /* get lockout policy */
2867 NTSTATUS wb_cache_lockout_policy(struct winbindd_domain *domain,
2868 TALLOC_CTX *mem_ctx,
2869 struct samr_DomInfo12 *policy)
2871 struct winbind_cache *cache = get_cache(domain);
2872 struct cache_entry *centry = NULL;
2873 NTSTATUS status;
2874 bool old_status;
2876 old_status = domain->online;
2877 if (!cache->tdb)
2878 goto do_query;
2880 centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
2882 if (!centry)
2883 goto do_query;
2885 do_fetch_cache:
2886 policy->lockout_duration = centry_nttime(centry);
2887 policy->lockout_window = centry_nttime(centry);
2888 policy->lockout_threshold = centry_uint16(centry);
2890 status = centry->status;
2892 DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2893 domain->name, nt_errstr(status) ));
2895 centry_free(centry);
2896 return status;
2898 do_query:
2899 ZERO_STRUCTP(policy);
2901 /* Return status value returned by seq number check */
2903 if (!NT_STATUS_IS_OK(domain->last_status))
2904 return domain->last_status;
2906 DEBUG(10,("lockout_policy: [Cached] - doing backend query for info for domain %s\n",
2907 domain->name ));
2909 status = domain->backend->lockout_policy(domain, mem_ctx, policy);
2911 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2912 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2913 if (!domain->internal && old_status) {
2914 set_domain_offline(domain);
2916 if (cache->tdb &&
2917 !domain->internal &&
2918 !domain->online &&
2919 old_status) {
2920 centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
2921 if (centry) {
2922 goto do_fetch_cache;
2926 /* and save it */
2927 refresh_sequence_number(domain);
2928 if (!NT_STATUS_IS_OK(status)) {
2929 return status;
2931 wcache_save_lockout_policy(domain, status, policy);
2933 return status;
2936 /* get password policy */
2937 NTSTATUS wb_cache_password_policy(struct winbindd_domain *domain,
2938 TALLOC_CTX *mem_ctx,
2939 struct samr_DomInfo1 *policy)
2941 struct winbind_cache *cache = get_cache(domain);
2942 struct cache_entry *centry = NULL;
2943 NTSTATUS status;
2944 bool old_status;
2946 old_status = domain->online;
2947 if (!cache->tdb)
2948 goto do_query;
2950 centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
2952 if (!centry)
2953 goto do_query;
2955 do_fetch_cache:
2956 policy->min_password_length = centry_uint16(centry);
2957 policy->password_history_length = centry_uint16(centry);
2958 policy->password_properties = centry_uint32(centry);
2959 policy->max_password_age = centry_nttime(centry);
2960 policy->min_password_age = centry_nttime(centry);
2962 status = centry->status;
2964 DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2965 domain->name, nt_errstr(status) ));
2967 centry_free(centry);
2968 return status;
2970 do_query:
2971 ZERO_STRUCTP(policy);
2973 /* Return status value returned by seq number check */
2975 if (!NT_STATUS_IS_OK(domain->last_status))
2976 return domain->last_status;
2978 DEBUG(10,("password_policy: [Cached] - doing backend query for info for domain %s\n",
2979 domain->name ));
2981 status = domain->backend->password_policy(domain, mem_ctx, policy);
2983 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2984 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2985 if (!domain->internal && old_status) {
2986 set_domain_offline(domain);
2988 if (cache->tdb &&
2989 !domain->internal &&
2990 !domain->online &&
2991 old_status) {
2992 centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
2993 if (centry) {
2994 goto do_fetch_cache;
2998 /* and save it */
2999 refresh_sequence_number(domain);
3000 if (!NT_STATUS_IS_OK(status)) {
3001 return status;
3003 wcache_save_password_policy(domain, status, policy);
3005 return status;
3009 /* Invalidate cached user and group lists coherently */
3011 static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
3012 void *state)
3014 if (strncmp((const char *)kbuf.dptr, "UL/", 3) == 0 ||
3015 strncmp((const char *)kbuf.dptr, "GL/", 3) == 0)
3016 tdb_delete(the_tdb, kbuf);
3018 return 0;
3021 /* Invalidate the getpwnam and getgroups entries for a winbindd domain */
3023 void wcache_invalidate_samlogon(struct winbindd_domain *domain,
3024 const struct dom_sid *sid)
3026 fstring key_str, sid_string;
3027 struct winbind_cache *cache;
3029 /* don't clear cached U/SID and UG/SID entries when we want to logon
3030 * offline - gd */
3032 if (lp_winbind_offline_logon()) {
3033 return;
3036 if (!domain)
3037 return;
3039 cache = get_cache(domain);
3041 if (!cache->tdb) {
3042 return;
3045 /* Clear U/SID cache entry */
3046 fstr_sprintf(key_str, "U/%s", sid_to_fstring(sid_string, sid));
3047 DEBUG(10, ("wcache_invalidate_samlogon: clearing %s\n", key_str));
3048 tdb_delete(cache->tdb, string_tdb_data(key_str));
3050 /* Clear UG/SID cache entry */
3051 fstr_sprintf(key_str, "UG/%s", sid_to_fstring(sid_string, sid));
3052 DEBUG(10, ("wcache_invalidate_samlogon: clearing %s\n", key_str));
3053 tdb_delete(cache->tdb, string_tdb_data(key_str));
3055 /* Samba/winbindd never needs this. */
3056 netsamlogon_clear_cached_user(sid);
3059 bool wcache_invalidate_cache(void)
3061 struct winbindd_domain *domain;
3063 for (domain = domain_list(); domain; domain = domain->next) {
3064 struct winbind_cache *cache = get_cache(domain);
3066 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
3067 "entries for %s\n", domain->name));
3068 if (cache) {
3069 if (cache->tdb) {
3070 tdb_traverse(cache->tdb, traverse_fn, NULL);
3071 } else {
3072 return false;
3076 return true;
3079 bool wcache_invalidate_cache_noinit(void)
3081 struct winbindd_domain *domain;
3083 for (domain = domain_list(); domain; domain = domain->next) {
3084 struct winbind_cache *cache;
3086 /* Skip uninitialized domains. */
3087 if (!domain->initialized && !domain->internal) {
3088 continue;
3091 cache = get_cache(domain);
3093 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
3094 "entries for %s\n", domain->name));
3095 if (cache) {
3096 if (cache->tdb) {
3097 tdb_traverse(cache->tdb, traverse_fn, NULL);
3099 * Flushing cache has nothing to with domains.
3100 * return here if we successfully flushed once.
3101 * To avoid unnecessary traversing the cache.
3103 return true;
3104 } else {
3105 return false;
3109 return true;
3112 bool init_wcache(void)
3114 char *db_path;
3116 if (wcache == NULL) {
3117 wcache = SMB_XMALLOC_P(struct winbind_cache);
3118 ZERO_STRUCTP(wcache);
3121 if (wcache->tdb != NULL)
3122 return true;
3124 db_path = wcache_path();
3125 if (db_path == NULL) {
3126 return false;
3129 /* when working offline we must not clear the cache on restart */
3130 wcache->tdb = tdb_open_log(db_path,
3131 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
3132 TDB_INCOMPATIBLE_HASH |
3133 (lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST)),
3134 O_RDWR|O_CREAT, 0600);
3135 TALLOC_FREE(db_path);
3136 if (wcache->tdb == NULL) {
3137 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
3138 return false;
3141 return true;
3144 /************************************************************************
3145 This is called by the parent to initialize the cache file.
3146 We don't need sophisticated locking here as we know we're the
3147 only opener.
3148 ************************************************************************/
3150 bool initialize_winbindd_cache(void)
3152 bool cache_bad = true;
3153 uint32_t vers;
3155 if (!init_wcache()) {
3156 DEBUG(0,("initialize_winbindd_cache: init_wcache failed.\n"));
3157 return false;
3160 /* Check version number. */
3161 if (tdb_fetch_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, &vers) &&
3162 vers == WINBINDD_CACHE_VERSION) {
3163 cache_bad = false;
3166 if (cache_bad) {
3167 char *db_path;
3169 DEBUG(0,("initialize_winbindd_cache: clearing cache "
3170 "and re-creating with version number %d\n",
3171 WINBINDD_CACHE_VERSION ));
3173 tdb_close(wcache->tdb);
3174 wcache->tdb = NULL;
3176 db_path = wcache_path();
3177 if (db_path == NULL) {
3178 return false;
3181 if (unlink(db_path) == -1) {
3182 DEBUG(0,("initialize_winbindd_cache: unlink %s failed %s ",
3183 db_path,
3184 strerror(errno) ));
3185 TALLOC_FREE(db_path);
3186 return false;
3188 TALLOC_FREE(db_path);
3189 if (!init_wcache()) {
3190 DEBUG(0,("initialize_winbindd_cache: re-initialization "
3191 "init_wcache failed.\n"));
3192 return false;
3195 /* Write the version. */
3196 if (!tdb_store_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, WINBINDD_CACHE_VERSION)) {
3197 DEBUG(0,("initialize_winbindd_cache: version number store failed %s\n",
3198 tdb_errorstr(wcache->tdb) ));
3199 return false;
3203 tdb_close(wcache->tdb);
3204 wcache->tdb = NULL;
3205 return true;
3208 void close_winbindd_cache(void)
3210 if (!wcache) {
3211 return;
3213 if (wcache->tdb) {
3214 tdb_close(wcache->tdb);
3215 wcache->tdb = NULL;
3219 bool lookup_cached_sid(TALLOC_CTX *mem_ctx, const struct dom_sid *sid,
3220 char **domain_name, char **name,
3221 enum lsa_SidType *type)
3223 struct winbindd_domain *domain;
3224 NTSTATUS status;
3226 domain = find_lookup_domain_from_sid(sid);
3227 if (domain == NULL) {
3228 return false;
3230 status = wcache_sid_to_name(domain, sid, mem_ctx, domain_name, name,
3231 type);
3232 return NT_STATUS_IS_OK(status);
3235 bool lookup_cached_name(const char *domain_name,
3236 const char *name,
3237 struct dom_sid *sid,
3238 enum lsa_SidType *type)
3240 struct winbindd_domain *domain;
3241 NTSTATUS status;
3242 bool original_online_state;
3244 domain = find_lookup_domain_from_name(domain_name);
3245 if (domain == NULL) {
3246 return false;
3249 /* If we are doing a cached logon, temporarily set the domain
3250 offline so the cache won't expire the entry */
3252 original_online_state = domain->online;
3253 domain->online = false;
3254 status = wcache_name_to_sid(domain, domain_name, name, sid, type);
3255 domain->online = original_online_state;
3257 return NT_STATUS_IS_OK(status);
3261 * Cache a name to sid without checking the sequence number.
3262 * Used when caching from a trusted PAC.
3265 void cache_name2sid_trusted(struct winbindd_domain *domain,
3266 const char *domain_name,
3267 const char *name,
3268 enum lsa_SidType type,
3269 const struct dom_sid *sid)
3272 * Ensure we store the mapping with the
3273 * existing sequence number from the cache.
3275 get_cache(domain);
3276 (void)fetch_cache_seqnum(domain, time(NULL));
3277 wcache_save_name_to_sid(domain,
3278 NT_STATUS_OK,
3279 domain_name,
3280 name,
3281 sid,
3282 type);
3285 void cache_name2sid(struct winbindd_domain *domain,
3286 const char *domain_name, const char *name,
3287 enum lsa_SidType type, const struct dom_sid *sid)
3289 refresh_sequence_number(domain);
3290 wcache_save_name_to_sid(domain, NT_STATUS_OK, domain_name, name,
3291 sid, type);
3295 * The original idea that this cache only contains centries has
3296 * been blurred - now other stuff gets put in here. Ensure we
3297 * ignore these things on cleanup.
3300 static int traverse_fn_cleanup(TDB_CONTEXT *the_tdb, TDB_DATA kbuf,
3301 TDB_DATA dbuf, void *state)
3303 struct cache_entry *centry;
3305 if (is_non_centry_key(kbuf)) {
3306 return 0;
3309 centry = wcache_fetch_raw((char *)kbuf.dptr);
3310 if (!centry) {
3311 return 0;
3314 if (!NT_STATUS_IS_OK(centry->status)) {
3315 DEBUG(10,("deleting centry %s\n", (const char *)kbuf.dptr));
3316 tdb_delete(the_tdb, kbuf);
3319 centry_free(centry);
3320 return 0;
3323 /* flush the cache */
3324 static void wcache_flush_cache(void)
3326 char *db_path;
3328 if (!wcache)
3329 return;
3330 if (wcache->tdb) {
3331 tdb_close(wcache->tdb);
3332 wcache->tdb = NULL;
3334 if (!winbindd_use_cache()) {
3335 return;
3338 db_path = wcache_path();
3339 if (db_path == NULL) {
3340 return;
3343 /* when working offline we must not clear the cache on restart */
3344 wcache->tdb = tdb_open_log(db_path,
3345 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
3346 TDB_INCOMPATIBLE_HASH |
3347 (lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST)),
3348 O_RDWR|O_CREAT, 0600);
3349 TALLOC_FREE(db_path);
3350 if (!wcache->tdb) {
3351 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
3352 return;
3355 tdb_traverse(wcache->tdb, traverse_fn_cleanup, NULL);
3357 DEBUG(10,("wcache_flush_cache success\n"));
3360 /* Count cached creds */
3362 static int traverse_fn_cached_creds(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
3363 void *state)
3365 int *cred_count = (int*)state;
3367 if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
3368 (*cred_count)++;
3370 return 0;
3373 NTSTATUS wcache_count_cached_creds(struct winbindd_domain *domain, int *count)
3375 struct winbind_cache *cache = get_cache(domain);
3377 *count = 0;
3379 if (!cache->tdb) {
3380 return NT_STATUS_INTERNAL_DB_ERROR;
3383 tdb_traverse(cache->tdb, traverse_fn_cached_creds, (void *)count);
3385 return NT_STATUS_OK;
3388 struct cred_list {
3389 struct cred_list *prev, *next;
3390 TDB_DATA key;
3391 fstring name;
3392 time_t created;
3394 static struct cred_list *wcache_cred_list;
3396 static int traverse_fn_get_credlist(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
3397 void *state)
3399 struct cred_list *cred;
3401 if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
3403 cred = SMB_MALLOC_P(struct cred_list);
3404 if (cred == NULL) {
3405 DEBUG(0,("traverse_fn_remove_first_creds: failed to malloc new entry for list\n"));
3406 return -1;
3409 ZERO_STRUCTP(cred);
3411 /* save a copy of the key */
3413 fstrcpy(cred->name, (const char *)kbuf.dptr);
3414 DLIST_ADD(wcache_cred_list, cred);
3417 return 0;
3420 NTSTATUS wcache_remove_oldest_cached_creds(struct winbindd_domain *domain, const struct dom_sid *sid)
3422 struct winbind_cache *cache = get_cache(domain);
3423 NTSTATUS status;
3424 int ret;
3425 struct cred_list *cred, *next, *oldest = NULL;
3427 if (!cache->tdb) {
3428 return NT_STATUS_INTERNAL_DB_ERROR;
3431 /* we possibly already have an entry */
3432 if (sid && NT_STATUS_IS_OK(wcache_cached_creds_exist(domain, sid))) {
3434 fstring key_str, tmp;
3436 DEBUG(11,("we already have an entry, deleting that\n"));
3438 fstr_sprintf(key_str, "CRED/%s", sid_to_fstring(tmp, sid));
3440 tdb_delete(cache->tdb, string_tdb_data(key_str));
3442 return NT_STATUS_OK;
3445 ret = tdb_traverse(cache->tdb, traverse_fn_get_credlist, NULL);
3446 if (ret == 0) {
3447 return NT_STATUS_OK;
3448 } else if ((ret < 0) || (wcache_cred_list == NULL)) {
3449 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
3452 ZERO_STRUCTP(oldest);
3454 for (cred = wcache_cred_list; cred; cred = cred->next) {
3456 TDB_DATA data;
3457 time_t t;
3459 data = tdb_fetch(cache->tdb, string_tdb_data(cred->name));
3460 if (!data.dptr) {
3461 DEBUG(10,("wcache_remove_oldest_cached_creds: entry for [%s] not found\n",
3462 cred->name));
3463 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
3464 goto done;
3467 t = IVAL(data.dptr, 0);
3468 SAFE_FREE(data.dptr);
3470 if (!oldest) {
3471 oldest = SMB_MALLOC_P(struct cred_list);
3472 if (oldest == NULL) {
3473 status = NT_STATUS_NO_MEMORY;
3474 goto done;
3477 fstrcpy(oldest->name, cred->name);
3478 oldest->created = t;
3479 continue;
3482 if (t < oldest->created) {
3483 fstrcpy(oldest->name, cred->name);
3484 oldest->created = t;
3488 if (tdb_delete(cache->tdb, string_tdb_data(oldest->name)) == 0) {
3489 status = NT_STATUS_OK;
3490 } else {
3491 status = NT_STATUS_UNSUCCESSFUL;
3493 done:
3494 for (cred = wcache_cred_list; cred; cred = next) {
3495 next = cred->next;
3496 DLIST_REMOVE(wcache_cred_list, cred);
3497 SAFE_FREE(cred);
3499 SAFE_FREE(oldest);
3501 return status;
3504 /* Change the global online/offline state. */
3505 bool set_global_winbindd_state_offline(void)
3507 TDB_DATA data;
3509 DEBUG(10,("set_global_winbindd_state_offline: offline requested.\n"));
3511 /* Only go offline if someone has created
3512 the key "WINBINDD_OFFLINE" in the cache tdb. */
3514 if (wcache == NULL || wcache->tdb == NULL) {
3515 DEBUG(10,("set_global_winbindd_state_offline: wcache not open yet.\n"));
3516 return false;
3519 if (!lp_winbind_offline_logon()) {
3520 DEBUG(10,("set_global_winbindd_state_offline: rejecting.\n"));
3521 return false;
3524 if (global_winbindd_offline_state) {
3525 /* Already offline. */
3526 return true;
3529 data = tdb_fetch_bystring( wcache->tdb, "WINBINDD_OFFLINE" );
3531 if (!data.dptr || data.dsize != 4) {
3532 DEBUG(10,("set_global_winbindd_state_offline: offline state not set.\n"));
3533 SAFE_FREE(data.dptr);
3534 return false;
3535 } else {
3536 DEBUG(10,("set_global_winbindd_state_offline: offline state set.\n"));
3537 global_winbindd_offline_state = true;
3538 SAFE_FREE(data.dptr);
3539 return true;
3543 void set_global_winbindd_state_online(void)
3545 DEBUG(10,("set_global_winbindd_state_online: online requested.\n"));
3547 if (!lp_winbind_offline_logon()) {
3548 DEBUG(10,("set_global_winbindd_state_online: rejecting.\n"));
3549 return;
3552 if (!global_winbindd_offline_state) {
3553 /* Already online. */
3554 return;
3556 global_winbindd_offline_state = false;
3558 if (!wcache->tdb) {
3559 return;
3562 /* Ensure there is no key "WINBINDD_OFFLINE" in the cache tdb. */
3563 tdb_delete_bystring(wcache->tdb, "WINBINDD_OFFLINE");
3566 bool get_global_winbindd_state_offline(void)
3568 return global_winbindd_offline_state;
3571 /***********************************************************************
3572 Validate functions for all possible cache tdb keys.
3573 ***********************************************************************/
3575 static struct cache_entry *create_centry_validate(const char *kstr, TDB_DATA data,
3576 struct tdb_validation_status *state)
3578 struct cache_entry *centry;
3580 centry = SMB_XMALLOC_P(struct cache_entry);
3581 centry->data = (unsigned char *)smb_memdup(data.dptr, data.dsize);
3582 if (!centry->data) {
3583 SAFE_FREE(centry);
3584 return NULL;
3586 centry->len = data.dsize;
3587 centry->ofs = 0;
3589 if (centry->len < 16) {
3590 /* huh? corrupt cache? */
3591 DEBUG(0,("create_centry_validate: Corrupt cache for key %s "
3592 "(len < 16) ?\n", kstr));
3593 centry_free(centry);
3594 state->bad_entry = true;
3595 state->success = false;
3596 return NULL;
3599 centry->status = NT_STATUS(centry_uint32(centry));
3600 centry->sequence_number = centry_uint32(centry);
3601 centry->timeout = centry_uint64_t(centry);
3602 return centry;
3605 static int validate_seqnum(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3606 struct tdb_validation_status *state)
3608 if (dbuf.dsize != 8) {
3609 DEBUG(0,("validate_seqnum: Corrupt cache for key %s (len %u != 8) ?\n",
3610 keystr, (unsigned int)dbuf.dsize ));
3611 state->bad_entry = true;
3612 return 1;
3614 return 0;
3617 static int validate_ns(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3618 struct tdb_validation_status *state)
3620 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3621 if (!centry) {
3622 return 1;
3625 (void)centry_uint32(centry);
3626 if (NT_STATUS_IS_OK(centry->status)) {
3627 struct dom_sid sid;
3628 (void)centry_sid(centry, &sid);
3631 centry_free(centry);
3633 if (!(state->success)) {
3634 return 1;
3636 DEBUG(10,("validate_ns: %s ok\n", keystr));
3637 return 0;
3640 static int validate_sn(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3641 struct tdb_validation_status *state)
3643 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3644 if (!centry) {
3645 return 1;
3648 if (NT_STATUS_IS_OK(centry->status)) {
3649 (void)centry_uint32(centry);
3650 (void)centry_string(centry, mem_ctx);
3651 (void)centry_string(centry, mem_ctx);
3654 centry_free(centry);
3656 if (!(state->success)) {
3657 return 1;
3659 DEBUG(10,("validate_sn: %s ok\n", keystr));
3660 return 0;
3663 static int validate_u(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3664 struct tdb_validation_status *state)
3666 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3667 struct dom_sid sid;
3669 if (!centry) {
3670 return 1;
3673 (void)centry_string(centry, mem_ctx);
3674 (void)centry_string(centry, mem_ctx);
3675 (void)centry_string(centry, mem_ctx);
3676 (void)centry_string(centry, mem_ctx);
3677 (void)centry_string(centry, mem_ctx);
3678 (void)centry_uint32(centry);
3679 (void)centry_uint32(centry);
3680 (void)centry_string(centry, mem_ctx);
3681 (void)centry_sid(centry, &sid);
3682 (void)centry_sid(centry, &sid);
3684 centry_free(centry);
3686 if (!(state->success)) {
3687 return 1;
3689 DEBUG(10,("validate_u: %s ok\n", keystr));
3690 return 0;
3693 static int validate_loc_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3694 struct tdb_validation_status *state)
3696 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3698 if (!centry) {
3699 return 1;
3702 (void)centry_nttime(centry);
3703 (void)centry_nttime(centry);
3704 (void)centry_uint16(centry);
3706 centry_free(centry);
3708 if (!(state->success)) {
3709 return 1;
3711 DEBUG(10,("validate_loc_pol: %s ok\n", keystr));
3712 return 0;
3715 static int validate_pwd_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3716 struct tdb_validation_status *state)
3718 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3720 if (!centry) {
3721 return 1;
3724 (void)centry_uint16(centry);
3725 (void)centry_uint16(centry);
3726 (void)centry_uint32(centry);
3727 (void)centry_nttime(centry);
3728 (void)centry_nttime(centry);
3730 centry_free(centry);
3732 if (!(state->success)) {
3733 return 1;
3735 DEBUG(10,("validate_pwd_pol: %s ok\n", keystr));
3736 return 0;
3739 static int validate_cred(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3740 struct tdb_validation_status *state)
3742 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3744 if (!centry) {
3745 return 1;
3748 (void)centry_time(centry);
3749 (void)centry_hash16(centry, mem_ctx);
3751 /* We only have 17 bytes more data in the salted cred case. */
3752 if (centry->len - centry->ofs == 17) {
3753 (void)centry_hash16(centry, mem_ctx);
3756 centry_free(centry);
3758 if (!(state->success)) {
3759 return 1;
3761 DEBUG(10,("validate_cred: %s ok\n", keystr));
3762 return 0;
3765 static int validate_ul(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3766 struct tdb_validation_status *state)
3768 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3769 int32_t num_entries, i;
3771 if (!centry) {
3772 return 1;
3775 num_entries = (int32_t)centry_uint32(centry);
3777 for (i=0; i< num_entries; i++) {
3778 (void)centry_uint32(centry);
3781 centry_free(centry);
3783 if (!(state->success)) {
3784 return 1;
3786 DEBUG(10,("validate_ul: %s ok\n", keystr));
3787 return 0;
3790 static int validate_gl(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3791 struct tdb_validation_status *state)
3793 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3794 int32_t num_entries, i;
3796 if (!centry) {
3797 return 1;
3800 num_entries = centry_uint32(centry);
3802 for (i=0; i< num_entries; i++) {
3803 (void)centry_string(centry, mem_ctx);
3804 (void)centry_string(centry, mem_ctx);
3805 (void)centry_uint32(centry);
3808 centry_free(centry);
3810 if (!(state->success)) {
3811 return 1;
3813 DEBUG(10,("validate_gl: %s ok\n", keystr));
3814 return 0;
3817 static int validate_ug(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3818 struct tdb_validation_status *state)
3820 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3821 int32_t num_groups, i;
3823 if (!centry) {
3824 return 1;
3827 num_groups = centry_uint32(centry);
3829 for (i=0; i< num_groups; i++) {
3830 struct dom_sid sid;
3831 centry_sid(centry, &sid);
3834 centry_free(centry);
3836 if (!(state->success)) {
3837 return 1;
3839 DEBUG(10,("validate_ug: %s ok\n", keystr));
3840 return 0;
3843 static int validate_ua(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3844 struct tdb_validation_status *state)
3846 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3847 int32_t num_aliases, i;
3849 if (!centry) {
3850 return 1;
3853 num_aliases = centry_uint32(centry);
3855 for (i=0; i < num_aliases; i++) {
3856 (void)centry_uint32(centry);
3859 centry_free(centry);
3861 if (!(state->success)) {
3862 return 1;
3864 DEBUG(10,("validate_ua: %s ok\n", keystr));
3865 return 0;
3868 static int validate_gm(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3869 struct tdb_validation_status *state)
3871 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3872 int32_t num_names, i;
3874 if (!centry) {
3875 return 1;
3878 num_names = centry_uint32(centry);
3880 for (i=0; i< num_names; i++) {
3881 struct dom_sid sid;
3882 centry_sid(centry, &sid);
3883 (void)centry_string(centry, mem_ctx);
3884 (void)centry_uint32(centry);
3887 centry_free(centry);
3889 if (!(state->success)) {
3890 return 1;
3892 DEBUG(10,("validate_gm: %s ok\n", keystr));
3893 return 0;
3896 static int validate_dr(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3897 struct tdb_validation_status *state)
3899 /* Can't say anything about this other than must be nonzero. */
3900 if (dbuf.dsize == 0) {
3901 DEBUG(0,("validate_dr: Corrupt cache for key %s (len == 0) ?\n",
3902 keystr));
3903 state->bad_entry = true;
3904 state->success = false;
3905 return 1;
3908 DEBUG(10,("validate_dr: %s ok\n", keystr));
3909 return 0;
3912 static int validate_de(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3913 struct tdb_validation_status *state)
3915 /* Can't say anything about this other than must be nonzero. */
3916 if (dbuf.dsize == 0) {
3917 DEBUG(0,("validate_de: Corrupt cache for key %s (len == 0) ?\n",
3918 keystr));
3919 state->bad_entry = true;
3920 state->success = false;
3921 return 1;
3924 DEBUG(10,("validate_de: %s ok\n", keystr));
3925 return 0;
3928 static int validate_nss_an(TALLOC_CTX *mem_ctx, const char *keystr,
3929 TDB_DATA dbuf,
3930 struct tdb_validation_status *state)
3932 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3934 if (!centry) {
3935 return 1;
3938 (void)centry_string( centry, mem_ctx );
3940 centry_free(centry);
3942 if (!(state->success)) {
3943 return 1;
3945 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3946 return 0;
3949 static int validate_nss_na(TALLOC_CTX *mem_ctx, const char *keystr,
3950 TDB_DATA dbuf,
3951 struct tdb_validation_status *state)
3953 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3955 if (!centry) {
3956 return 1;
3959 (void)centry_string( centry, mem_ctx );
3961 centry_free(centry);
3963 if (!(state->success)) {
3964 return 1;
3966 DBG_DEBUG("%s ok\n", keystr);
3967 return 0;
3970 static int validate_trustdomcache(TALLOC_CTX *mem_ctx, const char *keystr,
3971 TDB_DATA dbuf,
3972 struct tdb_validation_status *state)
3974 if (dbuf.dsize == 0) {
3975 DEBUG(0, ("validate_trustdomcache: Corrupt cache for "
3976 "key %s (len ==0) ?\n", keystr));
3977 state->bad_entry = true;
3978 state->success = false;
3979 return 1;
3982 DEBUG(10, ("validate_trustdomcache: %s ok\n", keystr));
3983 DEBUGADD(10, (" Don't trust me, I am a DUMMY!\n"));
3984 return 0;
3987 static int validate_offline(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3988 struct tdb_validation_status *state)
3990 if (dbuf.dsize != 4) {
3991 DEBUG(0,("validate_offline: Corrupt cache for key %s (len %u != 4) ?\n",
3992 keystr, (unsigned int)dbuf.dsize ));
3993 state->bad_entry = true;
3994 state->success = false;
3995 return 1;
3997 DEBUG(10,("validate_offline: %s ok\n", keystr));
3998 return 0;
4001 static int validate_ndr(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
4002 struct tdb_validation_status *state)
4005 * Ignore validation for now. The proper way to do this is with a
4006 * checksum. Just pure parsing does not really catch much.
4008 return 0;
4011 static int validate_cache_version(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
4012 struct tdb_validation_status *state)
4014 if (dbuf.dsize != 4) {
4015 DEBUG(0, ("validate_cache_version: Corrupt cache for "
4016 "key %s (len %u != 4) ?\n",
4017 keystr, (unsigned int)dbuf.dsize));
4018 state->bad_entry = true;
4019 state->success = false;
4020 return 1;
4023 DEBUG(10, ("validate_cache_version: %s ok\n", keystr));
4024 return 0;
4027 /***********************************************************************
4028 A list of all possible cache tdb keys with associated validation
4029 functions.
4030 ***********************************************************************/
4032 struct key_val_struct {
4033 const char *keyname;
4034 int (*validate_data_fn)(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, struct tdb_validation_status* state);
4035 } key_val[] = {
4036 {"SEQNUM/", validate_seqnum},
4037 {"NS/", validate_ns},
4038 {"SN/", validate_sn},
4039 {"U/", validate_u},
4040 {"LOC_POL/", validate_loc_pol},
4041 {"PWD_POL/", validate_pwd_pol},
4042 {"CRED/", validate_cred},
4043 {"UL/", validate_ul},
4044 {"GL/", validate_gl},
4045 {"UG/", validate_ug},
4046 {"UA", validate_ua},
4047 {"GM/", validate_gm},
4048 {"DR/", validate_dr},
4049 {"DE/", validate_de},
4050 {"TRUSTDOMCACHE/", validate_trustdomcache},
4051 {"NSS/NA/", validate_nss_na},
4052 {"NSS/AN/", validate_nss_an},
4053 {"WINBINDD_OFFLINE", validate_offline},
4054 {"NDR/", validate_ndr},
4055 {WINBINDD_CACHE_VERSION_KEYSTR, validate_cache_version},
4056 {NULL, NULL}
4059 /***********************************************************************
4060 Function to look at every entry in the tdb and validate it as far as
4061 possible.
4062 ***********************************************************************/
4064 static int cache_traverse_validate_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, void *state)
4066 int i;
4067 unsigned int max_key_len = 1024;
4068 struct tdb_validation_status *v_state = (struct tdb_validation_status *)state;
4070 /* Paranoia check. */
4071 if (strncmp("UA/", (const char *)kbuf.dptr, 3) == 0 ||
4072 strncmp("NDR/", (const char *)kbuf.dptr, 4) == 0) {
4073 max_key_len = 1024 * 1024;
4075 if (kbuf.dsize > max_key_len) {
4076 DEBUG(0, ("cache_traverse_validate_fn: key length too large: "
4077 "(%u) > (%u)\n\n",
4078 (unsigned int)kbuf.dsize, (unsigned int)max_key_len));
4079 return 1;
4082 for (i = 0; key_val[i].keyname; i++) {
4083 size_t namelen = strlen(key_val[i].keyname);
4084 if (kbuf.dsize >= namelen && (
4085 strncmp(key_val[i].keyname, (const char *)kbuf.dptr, namelen)) == 0) {
4086 TALLOC_CTX *mem_ctx;
4087 char *keystr;
4088 int ret;
4090 keystr = SMB_MALLOC_ARRAY(char, kbuf.dsize+1);
4091 if (!keystr) {
4092 return 1;
4094 memcpy(keystr, kbuf.dptr, kbuf.dsize);
4095 keystr[kbuf.dsize] = '\0';
4097 mem_ctx = talloc_init("validate_ctx");
4098 if (!mem_ctx) {
4099 SAFE_FREE(keystr);
4100 return 1;
4103 ret = key_val[i].validate_data_fn(mem_ctx, keystr, dbuf,
4104 v_state);
4106 SAFE_FREE(keystr);
4107 talloc_destroy(mem_ctx);
4108 return ret;
4112 DEBUG(0,("cache_traverse_validate_fn: unknown cache entry\nkey :\n"));
4113 dump_data(0, (uint8_t *)kbuf.dptr, kbuf.dsize);
4114 DEBUG(0,("data :\n"));
4115 dump_data(0, (uint8_t *)dbuf.dptr, dbuf.dsize);
4116 v_state->unknown_key = true;
4117 v_state->success = false;
4118 return 1; /* terminate. */
4121 static void validate_panic(const char *const why)
4123 DEBUG(0,("validating cache: would panic %s\n", why ));
4124 DEBUGADD(0, ("exiting instead (cache validation mode)\n"));
4125 exit(47);
4128 static int wbcache_update_centry_fn(TDB_CONTEXT *tdb,
4129 TDB_DATA key,
4130 TDB_DATA data,
4131 void *state)
4133 uint64_t ctimeout;
4134 TDB_DATA blob;
4136 if (is_non_centry_key(key)) {
4137 return 0;
4140 if (data.dptr == NULL || data.dsize == 0) {
4141 if (tdb_delete(tdb, key) < 0) {
4142 DEBUG(0, ("tdb_delete for [%s] failed!\n",
4143 key.dptr));
4144 return 1;
4148 /* add timeout to blob (uint64_t) */
4149 blob.dsize = data.dsize + 8;
4151 blob.dptr = SMB_XMALLOC_ARRAY(uint8_t, blob.dsize);
4152 if (blob.dptr == NULL) {
4153 return 1;
4155 memset(blob.dptr, 0, blob.dsize);
4157 /* copy status and seqnum */
4158 memcpy(blob.dptr, data.dptr, 8);
4160 /* add timeout */
4161 ctimeout = lp_winbind_cache_time() + time(NULL);
4162 SBVAL(blob.dptr, 8, ctimeout);
4164 /* copy the rest */
4165 memcpy(blob.dptr + 16, data.dptr + 8, data.dsize - 8);
4167 if (tdb_store(tdb, key, blob, TDB_REPLACE) < 0) {
4168 DEBUG(0, ("tdb_store to update [%s] failed!\n",
4169 key.dptr));
4170 SAFE_FREE(blob.dptr);
4171 return 1;
4174 SAFE_FREE(blob.dptr);
4175 return 0;
4178 static bool wbcache_upgrade_v1_to_v2(TDB_CONTEXT *tdb)
4180 int rc;
4182 DEBUG(1, ("Upgrade to version 2 of the winbindd_cache.tdb\n"));
4184 rc = tdb_traverse(tdb, wbcache_update_centry_fn, NULL);
4185 if (rc < 0) {
4186 return false;
4189 return true;
4192 /***********************************************************************
4193 Try and validate every entry in the winbindd cache. If we fail here,
4194 delete the cache tdb and return non-zero.
4195 ***********************************************************************/
4197 int winbindd_validate_cache(void)
4199 int ret = -1;
4200 char *tdb_path = NULL;
4201 TDB_CONTEXT *tdb = NULL;
4202 uint32_t vers_id;
4203 bool ok;
4205 DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
4206 smb_panic_fn = validate_panic;
4208 tdb_path = wcache_path();
4209 if (tdb_path == NULL) {
4210 goto done;
4213 tdb = tdb_open_log(tdb_path,
4214 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
4215 TDB_INCOMPATIBLE_HASH |
4216 ( lp_winbind_offline_logon()
4217 ? TDB_DEFAULT
4218 : TDB_DEFAULT | TDB_CLEAR_IF_FIRST ),
4219 O_RDWR|O_CREAT,
4220 0600);
4221 if (!tdb) {
4222 DEBUG(0, ("winbindd_validate_cache: "
4223 "error opening/initializing tdb\n"));
4224 goto done;
4227 /* Version check and upgrade code. */
4228 if (!tdb_fetch_uint32(tdb, WINBINDD_CACHE_VERSION_KEYSTR, &vers_id)) {
4229 DEBUG(10, ("Fresh database\n"));
4230 tdb_store_uint32(tdb, WINBINDD_CACHE_VERSION_KEYSTR, WINBINDD_CACHE_VERSION);
4231 vers_id = WINBINDD_CACHE_VERSION;
4234 if (vers_id != WINBINDD_CACHE_VERSION) {
4235 if (vers_id == WINBINDD_CACHE_VER1) {
4236 ok = wbcache_upgrade_v1_to_v2(tdb);
4237 if (!ok) {
4238 DEBUG(10, ("winbindd_validate_cache: upgrade to version 2 failed.\n"));
4239 unlink(tdb_path);
4240 goto done;
4243 tdb_store_uint32(tdb,
4244 WINBINDD_CACHE_VERSION_KEYSTR,
4245 WINBINDD_CACHE_VERSION);
4246 vers_id = WINBINDD_CACHE_VER2;
4250 tdb_close(tdb);
4252 ret = tdb_validate_and_backup(tdb_path, cache_traverse_validate_fn);
4254 if (ret != 0) {
4255 DEBUG(10, ("winbindd_validate_cache: validation not successful.\n"));
4256 DEBUGADD(10, ("removing tdb %s.\n", tdb_path));
4257 unlink(tdb_path);
4260 done:
4261 TALLOC_FREE(tdb_path);
4262 DEBUG(10, ("winbindd_validate_cache: restoring panic function\n"));
4263 smb_panic_fn = smb_panic;
4264 return ret;
4267 /***********************************************************************
4268 Try and validate every entry in the winbindd cache.
4269 ***********************************************************************/
4271 int winbindd_validate_cache_nobackup(void)
4273 int ret = -1;
4274 char *tdb_path;
4276 DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
4277 smb_panic_fn = validate_panic;
4279 tdb_path = wcache_path();
4280 if (tdb_path == NULL) {
4281 goto err_panic_restore;
4284 if (wcache == NULL || wcache->tdb == NULL) {
4285 ret = tdb_validate_open(tdb_path, cache_traverse_validate_fn);
4286 } else {
4287 ret = tdb_validate(wcache->tdb, cache_traverse_validate_fn);
4290 if (ret != 0) {
4291 DEBUG(10, ("winbindd_validate_cache_nobackup: validation not "
4292 "successful.\n"));
4295 TALLOC_FREE(tdb_path);
4296 err_panic_restore:
4297 DEBUG(10, ("winbindd_validate_cache_nobackup: restoring panic "
4298 "function\n"));
4299 smb_panic_fn = smb_panic;
4300 return ret;
4303 bool winbindd_cache_validate_and_initialize(void)
4305 close_winbindd_cache();
4307 if (lp_winbind_offline_logon()) {
4308 if (winbindd_validate_cache() < 0) {
4309 DEBUG(0, ("winbindd cache tdb corrupt and no backup "
4310 "could be restored.\n"));
4314 return initialize_winbindd_cache();
4317 /*********************************************************************
4318 ********************************************************************/
4320 static bool add_wbdomain_to_tdc_array( struct winbindd_domain *new_dom,
4321 struct winbindd_tdc_domain **domains,
4322 size_t *num_domains )
4324 struct winbindd_tdc_domain *list = NULL;
4325 size_t i, idx;
4326 bool set_only = false;
4328 /* don't allow duplicates */
4330 idx = *num_domains;
4331 list = *domains;
4333 for ( i=0; i< (*num_domains); i++ ) {
4334 if ( strequal( new_dom->name, list[i].domain_name ) ) {
4335 DEBUG(10,("add_wbdomain_to_tdc_array: Found existing record for %s\n",
4336 new_dom->name));
4337 idx = i;
4338 set_only = true;
4340 break;
4344 if ( !set_only ) {
4345 if ( !*domains ) {
4346 list = talloc_array( NULL, struct winbindd_tdc_domain, 1 );
4347 idx = 0;
4348 } else {
4349 list = talloc_realloc( *domains, *domains,
4350 struct winbindd_tdc_domain,
4351 (*num_domains)+1);
4352 idx = *num_domains;
4355 ZERO_STRUCT( list[idx] );
4358 if ( !list )
4359 return false;
4361 list[idx].domain_name = talloc_strdup(list, new_dom->name);
4362 if (list[idx].domain_name == NULL) {
4363 return false;
4365 if (new_dom->alt_name != NULL) {
4366 list[idx].dns_name = talloc_strdup(list, new_dom->alt_name);
4367 if (list[idx].dns_name == NULL) {
4368 return false;
4372 if ( !is_null_sid( &new_dom->sid ) ) {
4373 sid_copy( &list[idx].sid, &new_dom->sid );
4374 } else {
4375 sid_copy(&list[idx].sid, &global_sid_NULL);
4378 if ( new_dom->domain_flags != 0x0 )
4379 list[idx].trust_flags = new_dom->domain_flags;
4381 if ( new_dom->domain_type != 0x0 )
4382 list[idx].trust_type = new_dom->domain_type;
4384 if ( new_dom->domain_trust_attribs != 0x0 )
4385 list[idx].trust_attribs = new_dom->domain_trust_attribs;
4387 if ( !set_only ) {
4388 *domains = list;
4389 *num_domains = idx + 1;
4392 return true;
4395 /*********************************************************************
4396 ********************************************************************/
4398 static TDB_DATA make_tdc_key( const char *domain_name )
4400 char *keystr = NULL;
4401 TDB_DATA key = { NULL, 0 };
4403 if ( !domain_name ) {
4404 DEBUG(5,("make_tdc_key: Keyname workgroup is NULL!\n"));
4405 return key;
4408 if (asprintf( &keystr, "TRUSTDOMCACHE/%s", domain_name ) == -1) {
4409 return key;
4411 key = string_term_tdb_data(keystr);
4413 return key;
4416 /*********************************************************************
4417 ********************************************************************/
4419 static int pack_tdc_domains( struct winbindd_tdc_domain *domains,
4420 size_t num_domains,
4421 unsigned char **buf )
4423 unsigned char *buffer = NULL;
4424 int len = 0;
4425 int buflen = 0;
4426 size_t i = 0;
4428 DEBUG(10,("pack_tdc_domains: Packing %d trusted domains\n",
4429 (int)num_domains));
4431 buflen = 0;
4433 again:
4434 len = 0;
4436 /* Store the number of array items first */
4437 len += tdb_pack( buffer+len, buflen-len, "d",
4438 num_domains );
4440 /* now pack each domain trust record */
4441 for ( i=0; i<num_domains; i++ ) {
4443 fstring tmp;
4445 if ( buflen > 0 ) {
4446 DEBUG(10,("pack_tdc_domains: Packing domain %s (%s)\n",
4447 domains[i].domain_name,
4448 domains[i].dns_name ? domains[i].dns_name : "UNKNOWN" ));
4451 len += tdb_pack( buffer+len, buflen-len, "fffddd",
4452 domains[i].domain_name,
4453 domains[i].dns_name ? domains[i].dns_name : "",
4454 sid_to_fstring(tmp, &domains[i].sid),
4455 domains[i].trust_flags,
4456 domains[i].trust_attribs,
4457 domains[i].trust_type );
4460 if ( buflen < len ) {
4461 SAFE_FREE(buffer);
4462 if ( (buffer = SMB_MALLOC_ARRAY(unsigned char, len)) == NULL ) {
4463 DEBUG(0,("pack_tdc_domains: failed to alloc buffer!\n"));
4464 buflen = -1;
4465 goto done;
4467 buflen = len;
4468 goto again;
4471 *buf = buffer;
4473 done:
4474 return buflen;
4477 /*********************************************************************
4478 ********************************************************************/
4480 static size_t unpack_tdc_domains( unsigned char *buf, int buflen,
4481 struct winbindd_tdc_domain **domains )
4483 fstring domain_name, dns_name, sid_string;
4484 uint32_t type, attribs, flags;
4485 int num_domains;
4486 int len = 0;
4487 int i;
4488 struct winbindd_tdc_domain *list = NULL;
4490 /* get the number of domains */
4491 len += tdb_unpack( buf+len, buflen-len, "d", &num_domains);
4492 if ( len == -1 ) {
4493 DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
4494 return 0;
4497 list = talloc_array( NULL, struct winbindd_tdc_domain, num_domains );
4498 if ( !list ) {
4499 DEBUG(0,("unpack_tdc_domains: Failed to talloc() domain list!\n"));
4500 return 0;
4503 for ( i=0; i<num_domains; i++ ) {
4504 int this_len;
4506 this_len = tdb_unpack( buf+len, buflen-len, "fffddd",
4507 domain_name,
4508 dns_name,
4509 sid_string,
4510 &flags,
4511 &attribs,
4512 &type );
4514 if ( this_len == -1 ) {
4515 DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
4516 TALLOC_FREE( list );
4517 return 0;
4519 len += this_len;
4521 DEBUG(11,("unpack_tdc_domains: Unpacking domain %s (%s) "
4522 "SID %s, flags = 0x%x, attribs = 0x%x, type = 0x%x\n",
4523 domain_name, dns_name, sid_string,
4524 flags, attribs, type));
4526 list[i].domain_name = talloc_strdup( list, domain_name );
4527 list[i].dns_name = NULL;
4528 if (dns_name[0] != '\0') {
4529 list[i].dns_name = talloc_strdup(list, dns_name);
4531 if ( !string_to_sid( &(list[i].sid), sid_string ) ) {
4532 DEBUG(10,("unpack_tdc_domains: no SID for domain %s\n",
4533 domain_name));
4535 list[i].trust_flags = flags;
4536 list[i].trust_attribs = attribs;
4537 list[i].trust_type = type;
4540 *domains = list;
4542 return num_domains;
4545 /*********************************************************************
4546 ********************************************************************/
4548 static bool wcache_tdc_store_list( struct winbindd_tdc_domain *domains, size_t num_domains )
4550 TDB_DATA key = make_tdc_key( lp_workgroup() );
4551 TDB_DATA data = { NULL, 0 };
4552 int ret;
4554 if ( !key.dptr )
4555 return false;
4557 /* See if we were asked to delete the cache entry */
4559 if ( !domains ) {
4560 ret = tdb_delete( wcache->tdb, key );
4561 goto done;
4564 data.dsize = pack_tdc_domains( domains, num_domains, &data.dptr );
4566 if ( !data.dptr ) {
4567 ret = -1;
4568 goto done;
4571 ret = tdb_store( wcache->tdb, key, data, 0 );
4573 done:
4574 SAFE_FREE( data.dptr );
4575 SAFE_FREE( key.dptr );
4577 return ( ret == 0 );
4580 /*********************************************************************
4581 ********************************************************************/
4583 bool wcache_tdc_fetch_list( struct winbindd_tdc_domain **domains, size_t *num_domains )
4585 TDB_DATA key = make_tdc_key( lp_workgroup() );
4586 TDB_DATA data = { NULL, 0 };
4588 *domains = NULL;
4589 *num_domains = 0;
4591 if ( !key.dptr )
4592 return false;
4594 data = tdb_fetch( wcache->tdb, key );
4596 SAFE_FREE( key.dptr );
4598 if ( !data.dptr )
4599 return false;
4601 *num_domains = unpack_tdc_domains( data.dptr, data.dsize, domains );
4603 SAFE_FREE( data.dptr );
4605 if ( !*domains )
4606 return false;
4608 return true;
4611 /*********************************************************************
4612 ********************************************************************/
4614 bool wcache_tdc_add_domain( struct winbindd_domain *domain )
4616 struct winbindd_tdc_domain *dom_list = NULL;
4617 size_t num_domains = 0;
4618 bool ret = false;
4620 DEBUG(10,("wcache_tdc_add_domain: Adding domain %s (%s), SID %s, "
4621 "flags = 0x%x, attributes = 0x%x, type = 0x%x\n",
4622 domain->name, domain->alt_name,
4623 sid_string_dbg(&domain->sid),
4624 domain->domain_flags,
4625 domain->domain_trust_attribs,
4626 domain->domain_type));
4628 if ( !init_wcache() ) {
4629 return false;
4632 /* fetch the list */
4634 wcache_tdc_fetch_list( &dom_list, &num_domains );
4636 /* add the new domain */
4638 if ( !add_wbdomain_to_tdc_array( domain, &dom_list, &num_domains ) ) {
4639 goto done;
4642 /* pack the domain */
4644 if ( !wcache_tdc_store_list( dom_list, num_domains ) ) {
4645 goto done;
4648 /* Success */
4650 ret = true;
4651 done:
4652 TALLOC_FREE( dom_list );
4654 return ret;
4657 static struct winbindd_tdc_domain *wcache_tdc_dup_domain(
4658 TALLOC_CTX *mem_ctx, const struct winbindd_tdc_domain *src)
4660 struct winbindd_tdc_domain *dst;
4662 dst = talloc(mem_ctx, struct winbindd_tdc_domain);
4663 if (dst == NULL) {
4664 goto fail;
4666 dst->domain_name = talloc_strdup(dst, src->domain_name);
4667 if (dst->domain_name == NULL) {
4668 goto fail;
4671 dst->dns_name = NULL;
4672 if (src->dns_name != NULL) {
4673 dst->dns_name = talloc_strdup(dst, src->dns_name);
4674 if (dst->dns_name == NULL) {
4675 goto fail;
4679 sid_copy(&dst->sid, &src->sid);
4680 dst->trust_flags = src->trust_flags;
4681 dst->trust_type = src->trust_type;
4682 dst->trust_attribs = src->trust_attribs;
4683 return dst;
4684 fail:
4685 TALLOC_FREE(dst);
4686 return NULL;
4689 /*********************************************************************
4690 ********************************************************************/
4692 struct winbindd_tdc_domain * wcache_tdc_fetch_domain( TALLOC_CTX *ctx, const char *name )
4694 struct winbindd_tdc_domain *dom_list = NULL;
4695 size_t num_domains = 0;
4696 size_t i;
4697 struct winbindd_tdc_domain *d = NULL;
4699 DEBUG(10,("wcache_tdc_fetch_domain: Searching for domain %s\n", name));
4701 if ( !init_wcache() ) {
4702 return NULL;
4705 /* fetch the list */
4707 wcache_tdc_fetch_list( &dom_list, &num_domains );
4709 for ( i=0; i<num_domains; i++ ) {
4710 if ( strequal(name, dom_list[i].domain_name) ||
4711 strequal(name, dom_list[i].dns_name) )
4713 DEBUG(10,("wcache_tdc_fetch_domain: Found domain %s\n",
4714 name));
4716 d = wcache_tdc_dup_domain(ctx, &dom_list[i]);
4717 break;
4721 TALLOC_FREE( dom_list );
4723 return d;
4726 /*********************************************************************
4727 ********************************************************************/
4729 void wcache_tdc_clear( void )
4731 if ( !init_wcache() )
4732 return;
4734 wcache_tdc_store_list( NULL, 0 );
4736 return;
4739 static bool wcache_ndr_key(TALLOC_CTX *mem_ctx, const char *domain_name,
4740 uint32_t opnum, const DATA_BLOB *req,
4741 TDB_DATA *pkey)
4743 char *key;
4744 size_t keylen;
4746 key = talloc_asprintf(mem_ctx, "NDR/%s/%d/", domain_name, (int)opnum);
4747 if (key == NULL) {
4748 return false;
4750 keylen = talloc_get_size(key) - 1;
4752 key = talloc_realloc(mem_ctx, key, char, keylen + req->length);
4753 if (key == NULL) {
4754 return false;
4756 memcpy(key + keylen, req->data, req->length);
4758 pkey->dptr = (uint8_t *)key;
4759 pkey->dsize = talloc_get_size(key);
4760 return true;
4763 static bool wcache_opnum_cacheable(uint32_t opnum)
4765 switch (opnum) {
4766 case NDR_WBINT_PING:
4767 case NDR_WBINT_QUERYSEQUENCENUMBER:
4768 case NDR_WBINT_ALLOCATEUID:
4769 case NDR_WBINT_ALLOCATEGID:
4770 case NDR_WBINT_CHECKMACHINEACCOUNT:
4771 case NDR_WBINT_CHANGEMACHINEACCOUNT:
4772 case NDR_WBINT_PINGDC:
4773 return false;
4775 return true;
4778 bool wcache_fetch_ndr(TALLOC_CTX *mem_ctx, struct winbindd_domain *domain,
4779 uint32_t opnum, const DATA_BLOB *req, DATA_BLOB *resp)
4781 TDB_DATA key, data;
4782 bool ret = false;
4784 if (!wcache_opnum_cacheable(opnum) ||
4785 is_my_own_sam_domain(domain) ||
4786 is_builtin_domain(domain)) {
4787 return false;
4790 if (wcache->tdb == NULL) {
4791 return false;
4794 if (!wcache_ndr_key(talloc_tos(), domain->name, opnum, req, &key)) {
4795 return false;
4797 data = tdb_fetch(wcache->tdb, key);
4798 TALLOC_FREE(key.dptr);
4800 if (data.dptr == NULL) {
4801 return false;
4803 if (data.dsize < 12) {
4804 goto fail;
4807 if (!is_domain_offline(domain)) {
4808 uint32_t entry_seqnum, dom_seqnum, last_check;
4809 uint64_t entry_timeout;
4811 if (!wcache_fetch_seqnum(domain->name, &dom_seqnum,
4812 &last_check)) {
4813 goto fail;
4815 entry_seqnum = IVAL(data.dptr, 0);
4816 if (entry_seqnum != dom_seqnum) {
4817 DEBUG(10, ("Entry has wrong sequence number: %d\n",
4818 (int)entry_seqnum));
4819 goto fail;
4821 entry_timeout = BVAL(data.dptr, 4);
4822 if (time(NULL) > entry_timeout) {
4823 DEBUG(10, ("Entry has timed out\n"));
4824 goto fail;
4828 resp->data = (uint8_t *)talloc_memdup(mem_ctx, data.dptr + 12,
4829 data.dsize - 12);
4830 if (resp->data == NULL) {
4831 DEBUG(10, ("talloc failed\n"));
4832 goto fail;
4834 resp->length = data.dsize - 12;
4836 ret = true;
4837 fail:
4838 SAFE_FREE(data.dptr);
4839 return ret;
4842 void wcache_store_ndr(struct winbindd_domain *domain, uint32_t opnum,
4843 const DATA_BLOB *req, const DATA_BLOB *resp)
4845 TDB_DATA key, data;
4846 uint32_t dom_seqnum, last_check;
4847 uint64_t timeout;
4849 if (!wcache_opnum_cacheable(opnum) ||
4850 is_my_own_sam_domain(domain) ||
4851 is_builtin_domain(domain)) {
4852 return;
4855 if (wcache->tdb == NULL) {
4856 return;
4859 if (!wcache_fetch_seqnum(domain->name, &dom_seqnum, &last_check)) {
4860 DEBUG(10, ("could not fetch seqnum for domain %s\n",
4861 domain->name));
4862 return;
4865 if (!wcache_ndr_key(talloc_tos(), domain->name, opnum, req, &key)) {
4866 return;
4869 timeout = time(NULL) + lp_winbind_cache_time();
4871 data.dsize = resp->length + 12;
4872 data.dptr = talloc_array(key.dptr, uint8_t, data.dsize);
4873 if (data.dptr == NULL) {
4874 goto done;
4877 SIVAL(data.dptr, 0, dom_seqnum);
4878 SBVAL(data.dptr, 4, timeout);
4879 memcpy(data.dptr + 12, resp->data, resp->length);
4881 tdb_store(wcache->tdb, key, data, 0);
4883 done:
4884 TALLOC_FREE(key.dptr);
4885 return;