winbindd: Make wcache_lookup_useraliases static
[Samba.git] / source3 / winbindd / winbindd_cache.c
blobe19ffbc24435fea75975667692bf0d296aef51c1
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 static NTSTATUS wcache_lookup_useraliases(struct winbindd_domain *domain,
2476 TALLOC_CTX *mem_ctx,
2477 uint32_t num_sids,
2478 const struct dom_sid *sids,
2479 uint32_t *pnum_aliases,
2480 uint32_t **paliases)
2482 struct winbind_cache *cache = get_cache(domain);
2483 struct cache_entry *centry = NULL;
2484 uint32_t i, num_aliases;
2485 uint32_t *aliases;
2486 NTSTATUS status;
2487 char *sidlist;
2489 if (cache->tdb == NULL) {
2490 return NT_STATUS_NOT_FOUND;
2493 if (num_sids == 0) {
2494 *pnum_aliases = 0;
2495 *paliases = NULL;
2496 return NT_STATUS_OK;
2499 /* We need to cache indexed by the whole list of SIDs, the aliases
2500 * resulting might come from any of the SIDs. */
2502 sidlist = wcache_make_sidlist(talloc_tos(), num_sids, sids);
2503 if (sidlist == NULL) {
2504 return NT_STATUS_NO_MEMORY;
2507 centry = wcache_fetch(cache, domain, "UA%s", sidlist);
2508 TALLOC_FREE(sidlist);
2509 if (centry == NULL) {
2510 return NT_STATUS_NOT_FOUND;
2513 num_aliases = centry_uint32(centry);
2514 aliases = talloc_array(mem_ctx, uint32_t, num_aliases);
2515 if (aliases == NULL) {
2516 centry_free(centry);
2517 return NT_STATUS_NO_MEMORY;
2520 for (i=0; i<num_aliases; i++) {
2521 aliases[i] = centry_uint32(centry);
2524 status = centry->status;
2526 DEBUG(10,("lookup_useraliases: [Cached] - cached info for domain: %s "
2527 "status %s\n", domain->name, nt_errstr(status)));
2529 centry_free(centry);
2531 *pnum_aliases = num_aliases;
2532 *paliases = aliases;
2534 return status;
2537 NTSTATUS wb_cache_lookup_useraliases(struct winbindd_domain *domain,
2538 TALLOC_CTX *mem_ctx,
2539 uint32_t num_sids,
2540 const struct dom_sid *sids,
2541 uint32_t *num_aliases,
2542 uint32_t **alias_rids)
2544 struct cache_entry *centry = NULL;
2545 NTSTATUS status;
2546 char *sidlist;
2547 uint32_t i;
2548 bool old_status;
2550 old_status = domain->online;
2551 status = wcache_lookup_useraliases(domain, mem_ctx, num_sids, sids,
2552 num_aliases, alias_rids);
2553 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2554 return status;
2557 (*num_aliases) = 0;
2558 (*alias_rids) = NULL;
2560 if (!NT_STATUS_IS_OK(domain->last_status))
2561 return domain->last_status;
2563 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info "
2564 "for domain %s\n", domain->name ));
2566 sidlist = wcache_make_sidlist(talloc_tos(), num_sids, sids);
2567 if (sidlist == NULL) {
2568 return NT_STATUS_NO_MEMORY;
2571 status = domain->backend->lookup_useraliases(domain, mem_ctx,
2572 num_sids, sids,
2573 num_aliases, alias_rids);
2575 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2576 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2577 if (!domain->internal && old_status) {
2578 set_domain_offline(domain);
2580 if (!domain->internal &&
2581 !domain->online &&
2582 old_status) {
2583 NTSTATUS cache_status;
2584 cache_status = wcache_lookup_useraliases(domain, mem_ctx, num_sids,
2585 sids, num_aliases, alias_rids);
2586 return cache_status;
2589 /* and save it */
2590 refresh_sequence_number(domain);
2591 if (!NT_STATUS_IS_OK(status)) {
2592 return status;
2594 centry = centry_start(domain, status);
2595 if (!centry)
2596 goto skip_save;
2597 centry_put_uint32(centry, *num_aliases);
2598 for (i=0; i<(*num_aliases); i++)
2599 centry_put_uint32(centry, (*alias_rids)[i]);
2600 centry_end(centry, "UA%s", sidlist);
2601 centry_free(centry);
2603 skip_save:
2604 return status;
2607 static NTSTATUS wcache_lookup_groupmem(struct winbindd_domain *domain,
2608 TALLOC_CTX *mem_ctx,
2609 const struct dom_sid *group_sid,
2610 uint32_t *num_names,
2611 struct dom_sid **sid_mem, char ***names,
2612 uint32_t **name_types)
2614 struct winbind_cache *cache = get_cache(domain);
2615 struct cache_entry *centry = NULL;
2616 NTSTATUS status;
2617 unsigned int i;
2618 char *sid_string;
2620 if (cache->tdb == NULL) {
2621 return NT_STATUS_NOT_FOUND;
2624 sid_string = sid_string_tos(group_sid);
2625 if (sid_string == NULL) {
2626 return NT_STATUS_NO_MEMORY;
2629 centry = wcache_fetch(cache, domain, "GM/%s", sid_string);
2630 TALLOC_FREE(sid_string);
2631 if (centry == NULL) {
2632 return NT_STATUS_NOT_FOUND;
2635 *sid_mem = NULL;
2636 *names = NULL;
2637 *name_types = NULL;
2639 *num_names = centry_uint32(centry);
2640 if (*num_names == 0) {
2641 centry_free(centry);
2642 return NT_STATUS_OK;
2645 *sid_mem = talloc_array(mem_ctx, struct dom_sid, *num_names);
2646 *names = talloc_array(mem_ctx, char *, *num_names);
2647 *name_types = talloc_array(mem_ctx, uint32_t, *num_names);
2649 if ((*sid_mem == NULL) || (*names == NULL) || (*name_types == NULL)) {
2650 TALLOC_FREE(*sid_mem);
2651 TALLOC_FREE(*names);
2652 TALLOC_FREE(*name_types);
2653 centry_free(centry);
2654 return NT_STATUS_NO_MEMORY;
2657 for (i=0; i<(*num_names); i++) {
2658 centry_sid(centry, &(*sid_mem)[i]);
2659 (*names)[i] = centry_string(centry, mem_ctx);
2660 (*name_types)[i] = centry_uint32(centry);
2663 status = centry->status;
2665 DEBUG(10,("lookup_groupmem: [Cached] - cached info for domain %s "
2666 "status: %s\n", domain->name, nt_errstr(status)));
2668 centry_free(centry);
2669 return status;
2672 NTSTATUS wb_cache_lookup_groupmem(struct winbindd_domain *domain,
2673 TALLOC_CTX *mem_ctx,
2674 const struct dom_sid *group_sid,
2675 enum lsa_SidType type,
2676 uint32_t *num_names,
2677 struct dom_sid **sid_mem,
2678 char ***names,
2679 uint32_t **name_types)
2681 struct cache_entry *centry = NULL;
2682 NTSTATUS status;
2683 unsigned int i;
2684 fstring sid_string;
2685 bool old_status;
2687 old_status = domain->online;
2688 status = wcache_lookup_groupmem(domain, mem_ctx, group_sid, num_names,
2689 sid_mem, names, name_types);
2690 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2691 return status;
2694 (*num_names) = 0;
2695 (*sid_mem) = NULL;
2696 (*names) = NULL;
2697 (*name_types) = NULL;
2699 /* Return status value returned by seq number check */
2701 if (!NT_STATUS_IS_OK(domain->last_status))
2702 return domain->last_status;
2704 DEBUG(10,("lookup_groupmem: [Cached] - doing backend query for info for domain %s\n",
2705 domain->name ));
2707 status = domain->backend->lookup_groupmem(domain, mem_ctx, group_sid,
2708 type, num_names,
2709 sid_mem, names, name_types);
2711 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2712 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2713 if (!domain->internal && old_status) {
2714 set_domain_offline(domain);
2716 if (!domain->internal &&
2717 !domain->online &&
2718 old_status) {
2719 NTSTATUS cache_status;
2720 cache_status = wcache_lookup_groupmem(domain, mem_ctx, group_sid,
2721 num_names, sid_mem, names,
2722 name_types);
2723 return cache_status;
2726 /* and save it */
2727 refresh_sequence_number(domain);
2728 if (!NT_STATUS_IS_OK(status)) {
2729 return status;
2731 centry = centry_start(domain, status);
2732 if (!centry)
2733 goto skip_save;
2734 centry_put_uint32(centry, *num_names);
2735 for (i=0; i<(*num_names); i++) {
2736 centry_put_sid(centry, &(*sid_mem)[i]);
2737 centry_put_string(centry, (*names)[i]);
2738 centry_put_uint32(centry, (*name_types)[i]);
2740 centry_end(centry, "GM/%s", sid_to_fstring(sid_string, group_sid));
2741 centry_free(centry);
2743 skip_save:
2744 return status;
2747 /* find the sequence number for a domain */
2748 NTSTATUS wb_cache_sequence_number(struct winbindd_domain *domain,
2749 uint32_t *seq)
2751 refresh_sequence_number(domain);
2753 *seq = domain->sequence_number;
2755 return NT_STATUS_OK;
2758 /* enumerate trusted domains
2759 * (we need to have the list of trustdoms in the cache when we go offline) -
2760 * Guenther */
2761 NTSTATUS wb_cache_trusted_domains(struct winbindd_domain *domain,
2762 TALLOC_CTX *mem_ctx,
2763 struct netr_DomainTrustList *trusts)
2765 NTSTATUS status;
2766 struct winbind_cache *cache;
2767 struct winbindd_tdc_domain *dom_list = NULL;
2768 size_t num_domains = 0;
2769 bool retval = false;
2770 size_t i;
2771 bool old_status;
2773 old_status = domain->online;
2774 trusts->count = 0;
2775 trusts->array = NULL;
2777 cache = get_cache(domain);
2778 if (!cache || !cache->tdb) {
2779 goto do_query;
2782 if (domain->online) {
2783 goto do_query;
2786 retval = wcache_tdc_fetch_list(&dom_list, &num_domains);
2787 if (!retval || !num_domains || !dom_list) {
2788 TALLOC_FREE(dom_list);
2789 goto do_query;
2792 do_fetch_cache:
2793 trusts->array = talloc_zero_array(mem_ctx, struct netr_DomainTrust, num_domains);
2794 if (!trusts->array) {
2795 TALLOC_FREE(dom_list);
2796 return NT_STATUS_NO_MEMORY;
2799 for (i = 0; i < num_domains; i++) {
2800 struct netr_DomainTrust *trust;
2801 struct dom_sid *sid;
2802 struct winbindd_domain *dom;
2804 dom = find_domain_from_name_noinit(dom_list[i].domain_name);
2805 if (dom && dom->internal) {
2806 continue;
2809 trust = &trusts->array[trusts->count];
2810 trust->netbios_name = talloc_strdup(trusts->array, dom_list[i].domain_name);
2811 trust->dns_name = talloc_strdup(trusts->array, dom_list[i].dns_name);
2812 sid = talloc(trusts->array, struct dom_sid);
2813 if (!trust->netbios_name || !trust->dns_name ||
2814 !sid) {
2815 TALLOC_FREE(dom_list);
2816 TALLOC_FREE(trusts->array);
2817 return NT_STATUS_NO_MEMORY;
2820 trust->trust_flags = dom_list[i].trust_flags;
2821 trust->trust_attributes = dom_list[i].trust_attribs;
2822 trust->trust_type = dom_list[i].trust_type;
2823 sid_copy(sid, &dom_list[i].sid);
2824 trust->sid = sid;
2825 trusts->count++;
2828 TALLOC_FREE(dom_list);
2829 return NT_STATUS_OK;
2831 do_query:
2832 /* Return status value returned by seq number check */
2834 if (!NT_STATUS_IS_OK(domain->last_status))
2835 return domain->last_status;
2837 DEBUG(10,("trusted_domains: [Cached] - doing backend query for info for domain %s\n",
2838 domain->name ));
2840 status = domain->backend->trusted_domains(domain, mem_ctx, trusts);
2842 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2843 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2844 if (!domain->internal && old_status) {
2845 set_domain_offline(domain);
2847 if (!domain->internal &&
2848 !domain->online &&
2849 old_status) {
2850 retval = wcache_tdc_fetch_list(&dom_list, &num_domains);
2851 if (retval && num_domains && dom_list) {
2852 TALLOC_FREE(trusts->array);
2853 trusts->count = 0;
2854 goto do_fetch_cache;
2858 /* no trusts gives NT_STATUS_NO_MORE_ENTRIES resetting to NT_STATUS_OK
2859 * so that the generic centry handling still applies correctly -
2860 * Guenther*/
2862 if (!NT_STATUS_IS_ERR(status)) {
2863 status = NT_STATUS_OK;
2865 return status;
2868 /* get lockout policy */
2869 NTSTATUS wb_cache_lockout_policy(struct winbindd_domain *domain,
2870 TALLOC_CTX *mem_ctx,
2871 struct samr_DomInfo12 *policy)
2873 struct winbind_cache *cache = get_cache(domain);
2874 struct cache_entry *centry = NULL;
2875 NTSTATUS status;
2876 bool old_status;
2878 old_status = domain->online;
2879 if (!cache->tdb)
2880 goto do_query;
2882 centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
2884 if (!centry)
2885 goto do_query;
2887 do_fetch_cache:
2888 policy->lockout_duration = centry_nttime(centry);
2889 policy->lockout_window = centry_nttime(centry);
2890 policy->lockout_threshold = centry_uint16(centry);
2892 status = centry->status;
2894 DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2895 domain->name, nt_errstr(status) ));
2897 centry_free(centry);
2898 return status;
2900 do_query:
2901 ZERO_STRUCTP(policy);
2903 /* Return status value returned by seq number check */
2905 if (!NT_STATUS_IS_OK(domain->last_status))
2906 return domain->last_status;
2908 DEBUG(10,("lockout_policy: [Cached] - doing backend query for info for domain %s\n",
2909 domain->name ));
2911 status = domain->backend->lockout_policy(domain, mem_ctx, policy);
2913 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2914 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2915 if (!domain->internal && old_status) {
2916 set_domain_offline(domain);
2918 if (cache->tdb &&
2919 !domain->internal &&
2920 !domain->online &&
2921 old_status) {
2922 centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
2923 if (centry) {
2924 goto do_fetch_cache;
2928 /* and save it */
2929 refresh_sequence_number(domain);
2930 if (!NT_STATUS_IS_OK(status)) {
2931 return status;
2933 wcache_save_lockout_policy(domain, status, policy);
2935 return status;
2938 /* get password policy */
2939 NTSTATUS wb_cache_password_policy(struct winbindd_domain *domain,
2940 TALLOC_CTX *mem_ctx,
2941 struct samr_DomInfo1 *policy)
2943 struct winbind_cache *cache = get_cache(domain);
2944 struct cache_entry *centry = NULL;
2945 NTSTATUS status;
2946 bool old_status;
2948 old_status = domain->online;
2949 if (!cache->tdb)
2950 goto do_query;
2952 centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
2954 if (!centry)
2955 goto do_query;
2957 do_fetch_cache:
2958 policy->min_password_length = centry_uint16(centry);
2959 policy->password_history_length = centry_uint16(centry);
2960 policy->password_properties = centry_uint32(centry);
2961 policy->max_password_age = centry_nttime(centry);
2962 policy->min_password_age = centry_nttime(centry);
2964 status = centry->status;
2966 DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2967 domain->name, nt_errstr(status) ));
2969 centry_free(centry);
2970 return status;
2972 do_query:
2973 ZERO_STRUCTP(policy);
2975 /* Return status value returned by seq number check */
2977 if (!NT_STATUS_IS_OK(domain->last_status))
2978 return domain->last_status;
2980 DEBUG(10,("password_policy: [Cached] - doing backend query for info for domain %s\n",
2981 domain->name ));
2983 status = domain->backend->password_policy(domain, mem_ctx, policy);
2985 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2986 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2987 if (!domain->internal && old_status) {
2988 set_domain_offline(domain);
2990 if (cache->tdb &&
2991 !domain->internal &&
2992 !domain->online &&
2993 old_status) {
2994 centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
2995 if (centry) {
2996 goto do_fetch_cache;
3000 /* and save it */
3001 refresh_sequence_number(domain);
3002 if (!NT_STATUS_IS_OK(status)) {
3003 return status;
3005 wcache_save_password_policy(domain, status, policy);
3007 return status;
3011 /* Invalidate cached user and group lists coherently */
3013 static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
3014 void *state)
3016 if (strncmp((const char *)kbuf.dptr, "UL/", 3) == 0 ||
3017 strncmp((const char *)kbuf.dptr, "GL/", 3) == 0)
3018 tdb_delete(the_tdb, kbuf);
3020 return 0;
3023 /* Invalidate the getpwnam and getgroups entries for a winbindd domain */
3025 void wcache_invalidate_samlogon(struct winbindd_domain *domain,
3026 const struct dom_sid *sid)
3028 fstring key_str, sid_string;
3029 struct winbind_cache *cache;
3031 /* don't clear cached U/SID and UG/SID entries when we want to logon
3032 * offline - gd */
3034 if (lp_winbind_offline_logon()) {
3035 return;
3038 if (!domain)
3039 return;
3041 cache = get_cache(domain);
3043 if (!cache->tdb) {
3044 return;
3047 /* Clear U/SID cache entry */
3048 fstr_sprintf(key_str, "U/%s", sid_to_fstring(sid_string, sid));
3049 DEBUG(10, ("wcache_invalidate_samlogon: clearing %s\n", key_str));
3050 tdb_delete(cache->tdb, string_tdb_data(key_str));
3052 /* Clear UG/SID cache entry */
3053 fstr_sprintf(key_str, "UG/%s", sid_to_fstring(sid_string, sid));
3054 DEBUG(10, ("wcache_invalidate_samlogon: clearing %s\n", key_str));
3055 tdb_delete(cache->tdb, string_tdb_data(key_str));
3057 /* Samba/winbindd never needs this. */
3058 netsamlogon_clear_cached_user(sid);
3061 bool wcache_invalidate_cache(void)
3063 struct winbindd_domain *domain;
3065 for (domain = domain_list(); domain; domain = domain->next) {
3066 struct winbind_cache *cache = get_cache(domain);
3068 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
3069 "entries for %s\n", domain->name));
3070 if (cache) {
3071 if (cache->tdb) {
3072 tdb_traverse(cache->tdb, traverse_fn, NULL);
3073 } else {
3074 return false;
3078 return true;
3081 bool wcache_invalidate_cache_noinit(void)
3083 struct winbindd_domain *domain;
3085 for (domain = domain_list(); domain; domain = domain->next) {
3086 struct winbind_cache *cache;
3088 /* Skip uninitialized domains. */
3089 if (!domain->initialized && !domain->internal) {
3090 continue;
3093 cache = get_cache(domain);
3095 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
3096 "entries for %s\n", domain->name));
3097 if (cache) {
3098 if (cache->tdb) {
3099 tdb_traverse(cache->tdb, traverse_fn, NULL);
3101 * Flushing cache has nothing to with domains.
3102 * return here if we successfully flushed once.
3103 * To avoid unnecessary traversing the cache.
3105 return true;
3106 } else {
3107 return false;
3111 return true;
3114 bool init_wcache(void)
3116 char *db_path;
3118 if (wcache == NULL) {
3119 wcache = SMB_XMALLOC_P(struct winbind_cache);
3120 ZERO_STRUCTP(wcache);
3123 if (wcache->tdb != NULL)
3124 return true;
3126 db_path = wcache_path();
3127 if (db_path == NULL) {
3128 return false;
3131 /* when working offline we must not clear the cache on restart */
3132 wcache->tdb = tdb_open_log(db_path,
3133 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
3134 TDB_INCOMPATIBLE_HASH |
3135 (lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST)),
3136 O_RDWR|O_CREAT, 0600);
3137 TALLOC_FREE(db_path);
3138 if (wcache->tdb == NULL) {
3139 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
3140 return false;
3143 return true;
3146 /************************************************************************
3147 This is called by the parent to initialize the cache file.
3148 We don't need sophisticated locking here as we know we're the
3149 only opener.
3150 ************************************************************************/
3152 bool initialize_winbindd_cache(void)
3154 bool cache_bad = true;
3155 uint32_t vers;
3157 if (!init_wcache()) {
3158 DEBUG(0,("initialize_winbindd_cache: init_wcache failed.\n"));
3159 return false;
3162 /* Check version number. */
3163 if (tdb_fetch_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, &vers) &&
3164 vers == WINBINDD_CACHE_VERSION) {
3165 cache_bad = false;
3168 if (cache_bad) {
3169 char *db_path;
3171 DEBUG(0,("initialize_winbindd_cache: clearing cache "
3172 "and re-creating with version number %d\n",
3173 WINBINDD_CACHE_VERSION ));
3175 tdb_close(wcache->tdb);
3176 wcache->tdb = NULL;
3178 db_path = wcache_path();
3179 if (db_path == NULL) {
3180 return false;
3183 if (unlink(db_path) == -1) {
3184 DEBUG(0,("initialize_winbindd_cache: unlink %s failed %s ",
3185 db_path,
3186 strerror(errno) ));
3187 TALLOC_FREE(db_path);
3188 return false;
3190 TALLOC_FREE(db_path);
3191 if (!init_wcache()) {
3192 DEBUG(0,("initialize_winbindd_cache: re-initialization "
3193 "init_wcache failed.\n"));
3194 return false;
3197 /* Write the version. */
3198 if (!tdb_store_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, WINBINDD_CACHE_VERSION)) {
3199 DEBUG(0,("initialize_winbindd_cache: version number store failed %s\n",
3200 tdb_errorstr(wcache->tdb) ));
3201 return false;
3205 tdb_close(wcache->tdb);
3206 wcache->tdb = NULL;
3207 return true;
3210 void close_winbindd_cache(void)
3212 if (!wcache) {
3213 return;
3215 if (wcache->tdb) {
3216 tdb_close(wcache->tdb);
3217 wcache->tdb = NULL;
3221 bool lookup_cached_sid(TALLOC_CTX *mem_ctx, const struct dom_sid *sid,
3222 char **domain_name, char **name,
3223 enum lsa_SidType *type)
3225 struct winbindd_domain *domain;
3226 NTSTATUS status;
3228 domain = find_lookup_domain_from_sid(sid);
3229 if (domain == NULL) {
3230 return false;
3232 status = wcache_sid_to_name(domain, sid, mem_ctx, domain_name, name,
3233 type);
3234 return NT_STATUS_IS_OK(status);
3237 bool lookup_cached_name(const char *domain_name,
3238 const char *name,
3239 struct dom_sid *sid,
3240 enum lsa_SidType *type)
3242 struct winbindd_domain *domain;
3243 NTSTATUS status;
3244 bool original_online_state;
3246 domain = find_lookup_domain_from_name(domain_name);
3247 if (domain == NULL) {
3248 return false;
3251 /* If we are doing a cached logon, temporarily set the domain
3252 offline so the cache won't expire the entry */
3254 original_online_state = domain->online;
3255 domain->online = false;
3256 status = wcache_name_to_sid(domain, domain_name, name, sid, type);
3257 domain->online = original_online_state;
3259 return NT_STATUS_IS_OK(status);
3263 * Cache a name to sid without checking the sequence number.
3264 * Used when caching from a trusted PAC.
3267 void cache_name2sid_trusted(struct winbindd_domain *domain,
3268 const char *domain_name,
3269 const char *name,
3270 enum lsa_SidType type,
3271 const struct dom_sid *sid)
3274 * Ensure we store the mapping with the
3275 * existing sequence number from the cache.
3277 get_cache(domain);
3278 (void)fetch_cache_seqnum(domain, time(NULL));
3279 wcache_save_name_to_sid(domain,
3280 NT_STATUS_OK,
3281 domain_name,
3282 name,
3283 sid,
3284 type);
3287 void cache_name2sid(struct winbindd_domain *domain,
3288 const char *domain_name, const char *name,
3289 enum lsa_SidType type, const struct dom_sid *sid)
3291 refresh_sequence_number(domain);
3292 wcache_save_name_to_sid(domain, NT_STATUS_OK, domain_name, name,
3293 sid, type);
3297 * The original idea that this cache only contains centries has
3298 * been blurred - now other stuff gets put in here. Ensure we
3299 * ignore these things on cleanup.
3302 static int traverse_fn_cleanup(TDB_CONTEXT *the_tdb, TDB_DATA kbuf,
3303 TDB_DATA dbuf, void *state)
3305 struct cache_entry *centry;
3307 if (is_non_centry_key(kbuf)) {
3308 return 0;
3311 centry = wcache_fetch_raw((char *)kbuf.dptr);
3312 if (!centry) {
3313 return 0;
3316 if (!NT_STATUS_IS_OK(centry->status)) {
3317 DEBUG(10,("deleting centry %s\n", (const char *)kbuf.dptr));
3318 tdb_delete(the_tdb, kbuf);
3321 centry_free(centry);
3322 return 0;
3325 /* flush the cache */
3326 static void wcache_flush_cache(void)
3328 char *db_path;
3330 if (!wcache)
3331 return;
3332 if (wcache->tdb) {
3333 tdb_close(wcache->tdb);
3334 wcache->tdb = NULL;
3336 if (!winbindd_use_cache()) {
3337 return;
3340 db_path = wcache_path();
3341 if (db_path == NULL) {
3342 return;
3345 /* when working offline we must not clear the cache on restart */
3346 wcache->tdb = tdb_open_log(db_path,
3347 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
3348 TDB_INCOMPATIBLE_HASH |
3349 (lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST)),
3350 O_RDWR|O_CREAT, 0600);
3351 TALLOC_FREE(db_path);
3352 if (!wcache->tdb) {
3353 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
3354 return;
3357 tdb_traverse(wcache->tdb, traverse_fn_cleanup, NULL);
3359 DEBUG(10,("wcache_flush_cache success\n"));
3362 /* Count cached creds */
3364 static int traverse_fn_cached_creds(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
3365 void *state)
3367 int *cred_count = (int*)state;
3369 if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
3370 (*cred_count)++;
3372 return 0;
3375 NTSTATUS wcache_count_cached_creds(struct winbindd_domain *domain, int *count)
3377 struct winbind_cache *cache = get_cache(domain);
3379 *count = 0;
3381 if (!cache->tdb) {
3382 return NT_STATUS_INTERNAL_DB_ERROR;
3385 tdb_traverse(cache->tdb, traverse_fn_cached_creds, (void *)count);
3387 return NT_STATUS_OK;
3390 struct cred_list {
3391 struct cred_list *prev, *next;
3392 TDB_DATA key;
3393 fstring name;
3394 time_t created;
3396 static struct cred_list *wcache_cred_list;
3398 static int traverse_fn_get_credlist(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
3399 void *state)
3401 struct cred_list *cred;
3403 if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
3405 cred = SMB_MALLOC_P(struct cred_list);
3406 if (cred == NULL) {
3407 DEBUG(0,("traverse_fn_remove_first_creds: failed to malloc new entry for list\n"));
3408 return -1;
3411 ZERO_STRUCTP(cred);
3413 /* save a copy of the key */
3415 fstrcpy(cred->name, (const char *)kbuf.dptr);
3416 DLIST_ADD(wcache_cred_list, cred);
3419 return 0;
3422 NTSTATUS wcache_remove_oldest_cached_creds(struct winbindd_domain *domain, const struct dom_sid *sid)
3424 struct winbind_cache *cache = get_cache(domain);
3425 NTSTATUS status;
3426 int ret;
3427 struct cred_list *cred, *next, *oldest = NULL;
3429 if (!cache->tdb) {
3430 return NT_STATUS_INTERNAL_DB_ERROR;
3433 /* we possibly already have an entry */
3434 if (sid && NT_STATUS_IS_OK(wcache_cached_creds_exist(domain, sid))) {
3436 fstring key_str, tmp;
3438 DEBUG(11,("we already have an entry, deleting that\n"));
3440 fstr_sprintf(key_str, "CRED/%s", sid_to_fstring(tmp, sid));
3442 tdb_delete(cache->tdb, string_tdb_data(key_str));
3444 return NT_STATUS_OK;
3447 ret = tdb_traverse(cache->tdb, traverse_fn_get_credlist, NULL);
3448 if (ret == 0) {
3449 return NT_STATUS_OK;
3450 } else if ((ret < 0) || (wcache_cred_list == NULL)) {
3451 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
3454 ZERO_STRUCTP(oldest);
3456 for (cred = wcache_cred_list; cred; cred = cred->next) {
3458 TDB_DATA data;
3459 time_t t;
3461 data = tdb_fetch(cache->tdb, string_tdb_data(cred->name));
3462 if (!data.dptr) {
3463 DEBUG(10,("wcache_remove_oldest_cached_creds: entry for [%s] not found\n",
3464 cred->name));
3465 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
3466 goto done;
3469 t = IVAL(data.dptr, 0);
3470 SAFE_FREE(data.dptr);
3472 if (!oldest) {
3473 oldest = SMB_MALLOC_P(struct cred_list);
3474 if (oldest == NULL) {
3475 status = NT_STATUS_NO_MEMORY;
3476 goto done;
3479 fstrcpy(oldest->name, cred->name);
3480 oldest->created = t;
3481 continue;
3484 if (t < oldest->created) {
3485 fstrcpy(oldest->name, cred->name);
3486 oldest->created = t;
3490 if (tdb_delete(cache->tdb, string_tdb_data(oldest->name)) == 0) {
3491 status = NT_STATUS_OK;
3492 } else {
3493 status = NT_STATUS_UNSUCCESSFUL;
3495 done:
3496 for (cred = wcache_cred_list; cred; cred = next) {
3497 next = cred->next;
3498 DLIST_REMOVE(wcache_cred_list, cred);
3499 SAFE_FREE(cred);
3501 SAFE_FREE(oldest);
3503 return status;
3506 /* Change the global online/offline state. */
3507 bool set_global_winbindd_state_offline(void)
3509 TDB_DATA data;
3511 DEBUG(10,("set_global_winbindd_state_offline: offline requested.\n"));
3513 /* Only go offline if someone has created
3514 the key "WINBINDD_OFFLINE" in the cache tdb. */
3516 if (wcache == NULL || wcache->tdb == NULL) {
3517 DEBUG(10,("set_global_winbindd_state_offline: wcache not open yet.\n"));
3518 return false;
3521 if (!lp_winbind_offline_logon()) {
3522 DEBUG(10,("set_global_winbindd_state_offline: rejecting.\n"));
3523 return false;
3526 if (global_winbindd_offline_state) {
3527 /* Already offline. */
3528 return true;
3531 data = tdb_fetch_bystring( wcache->tdb, "WINBINDD_OFFLINE" );
3533 if (!data.dptr || data.dsize != 4) {
3534 DEBUG(10,("set_global_winbindd_state_offline: offline state not set.\n"));
3535 SAFE_FREE(data.dptr);
3536 return false;
3537 } else {
3538 DEBUG(10,("set_global_winbindd_state_offline: offline state set.\n"));
3539 global_winbindd_offline_state = true;
3540 SAFE_FREE(data.dptr);
3541 return true;
3545 void set_global_winbindd_state_online(void)
3547 DEBUG(10,("set_global_winbindd_state_online: online requested.\n"));
3549 if (!lp_winbind_offline_logon()) {
3550 DEBUG(10,("set_global_winbindd_state_online: rejecting.\n"));
3551 return;
3554 if (!global_winbindd_offline_state) {
3555 /* Already online. */
3556 return;
3558 global_winbindd_offline_state = false;
3560 if (!wcache->tdb) {
3561 return;
3564 /* Ensure there is no key "WINBINDD_OFFLINE" in the cache tdb. */
3565 tdb_delete_bystring(wcache->tdb, "WINBINDD_OFFLINE");
3568 bool get_global_winbindd_state_offline(void)
3570 return global_winbindd_offline_state;
3573 /***********************************************************************
3574 Validate functions for all possible cache tdb keys.
3575 ***********************************************************************/
3577 static struct cache_entry *create_centry_validate(const char *kstr, TDB_DATA data,
3578 struct tdb_validation_status *state)
3580 struct cache_entry *centry;
3582 centry = SMB_XMALLOC_P(struct cache_entry);
3583 centry->data = (unsigned char *)smb_memdup(data.dptr, data.dsize);
3584 if (!centry->data) {
3585 SAFE_FREE(centry);
3586 return NULL;
3588 centry->len = data.dsize;
3589 centry->ofs = 0;
3591 if (centry->len < 16) {
3592 /* huh? corrupt cache? */
3593 DEBUG(0,("create_centry_validate: Corrupt cache for key %s "
3594 "(len < 16) ?\n", kstr));
3595 centry_free(centry);
3596 state->bad_entry = true;
3597 state->success = false;
3598 return NULL;
3601 centry->status = NT_STATUS(centry_uint32(centry));
3602 centry->sequence_number = centry_uint32(centry);
3603 centry->timeout = centry_uint64_t(centry);
3604 return centry;
3607 static int validate_seqnum(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3608 struct tdb_validation_status *state)
3610 if (dbuf.dsize != 8) {
3611 DEBUG(0,("validate_seqnum: Corrupt cache for key %s (len %u != 8) ?\n",
3612 keystr, (unsigned int)dbuf.dsize ));
3613 state->bad_entry = true;
3614 return 1;
3616 return 0;
3619 static int validate_ns(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3620 struct tdb_validation_status *state)
3622 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3623 if (!centry) {
3624 return 1;
3627 (void)centry_uint32(centry);
3628 if (NT_STATUS_IS_OK(centry->status)) {
3629 struct dom_sid sid;
3630 (void)centry_sid(centry, &sid);
3633 centry_free(centry);
3635 if (!(state->success)) {
3636 return 1;
3638 DEBUG(10,("validate_ns: %s ok\n", keystr));
3639 return 0;
3642 static int validate_sn(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3643 struct tdb_validation_status *state)
3645 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3646 if (!centry) {
3647 return 1;
3650 if (NT_STATUS_IS_OK(centry->status)) {
3651 (void)centry_uint32(centry);
3652 (void)centry_string(centry, mem_ctx);
3653 (void)centry_string(centry, mem_ctx);
3656 centry_free(centry);
3658 if (!(state->success)) {
3659 return 1;
3661 DEBUG(10,("validate_sn: %s ok\n", keystr));
3662 return 0;
3665 static int validate_u(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3666 struct tdb_validation_status *state)
3668 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3669 struct dom_sid sid;
3671 if (!centry) {
3672 return 1;
3675 (void)centry_string(centry, mem_ctx);
3676 (void)centry_string(centry, mem_ctx);
3677 (void)centry_string(centry, mem_ctx);
3678 (void)centry_string(centry, mem_ctx);
3679 (void)centry_string(centry, mem_ctx);
3680 (void)centry_uint32(centry);
3681 (void)centry_uint32(centry);
3682 (void)centry_string(centry, mem_ctx);
3683 (void)centry_sid(centry, &sid);
3684 (void)centry_sid(centry, &sid);
3686 centry_free(centry);
3688 if (!(state->success)) {
3689 return 1;
3691 DEBUG(10,("validate_u: %s ok\n", keystr));
3692 return 0;
3695 static int validate_loc_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3696 struct tdb_validation_status *state)
3698 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3700 if (!centry) {
3701 return 1;
3704 (void)centry_nttime(centry);
3705 (void)centry_nttime(centry);
3706 (void)centry_uint16(centry);
3708 centry_free(centry);
3710 if (!(state->success)) {
3711 return 1;
3713 DEBUG(10,("validate_loc_pol: %s ok\n", keystr));
3714 return 0;
3717 static int validate_pwd_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3718 struct tdb_validation_status *state)
3720 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3722 if (!centry) {
3723 return 1;
3726 (void)centry_uint16(centry);
3727 (void)centry_uint16(centry);
3728 (void)centry_uint32(centry);
3729 (void)centry_nttime(centry);
3730 (void)centry_nttime(centry);
3732 centry_free(centry);
3734 if (!(state->success)) {
3735 return 1;
3737 DEBUG(10,("validate_pwd_pol: %s ok\n", keystr));
3738 return 0;
3741 static int validate_cred(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3742 struct tdb_validation_status *state)
3744 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3746 if (!centry) {
3747 return 1;
3750 (void)centry_time(centry);
3751 (void)centry_hash16(centry, mem_ctx);
3753 /* We only have 17 bytes more data in the salted cred case. */
3754 if (centry->len - centry->ofs == 17) {
3755 (void)centry_hash16(centry, mem_ctx);
3758 centry_free(centry);
3760 if (!(state->success)) {
3761 return 1;
3763 DEBUG(10,("validate_cred: %s ok\n", keystr));
3764 return 0;
3767 static int validate_ul(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3768 struct tdb_validation_status *state)
3770 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3771 int32_t num_entries, i;
3773 if (!centry) {
3774 return 1;
3777 num_entries = (int32_t)centry_uint32(centry);
3779 for (i=0; i< num_entries; i++) {
3780 (void)centry_uint32(centry);
3783 centry_free(centry);
3785 if (!(state->success)) {
3786 return 1;
3788 DEBUG(10,("validate_ul: %s ok\n", keystr));
3789 return 0;
3792 static int validate_gl(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3793 struct tdb_validation_status *state)
3795 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3796 int32_t num_entries, i;
3798 if (!centry) {
3799 return 1;
3802 num_entries = centry_uint32(centry);
3804 for (i=0; i< num_entries; i++) {
3805 (void)centry_string(centry, mem_ctx);
3806 (void)centry_string(centry, mem_ctx);
3807 (void)centry_uint32(centry);
3810 centry_free(centry);
3812 if (!(state->success)) {
3813 return 1;
3815 DEBUG(10,("validate_gl: %s ok\n", keystr));
3816 return 0;
3819 static int validate_ug(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3820 struct tdb_validation_status *state)
3822 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3823 int32_t num_groups, i;
3825 if (!centry) {
3826 return 1;
3829 num_groups = centry_uint32(centry);
3831 for (i=0; i< num_groups; i++) {
3832 struct dom_sid sid;
3833 centry_sid(centry, &sid);
3836 centry_free(centry);
3838 if (!(state->success)) {
3839 return 1;
3841 DEBUG(10,("validate_ug: %s ok\n", keystr));
3842 return 0;
3845 static int validate_ua(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3846 struct tdb_validation_status *state)
3848 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3849 int32_t num_aliases, i;
3851 if (!centry) {
3852 return 1;
3855 num_aliases = centry_uint32(centry);
3857 for (i=0; i < num_aliases; i++) {
3858 (void)centry_uint32(centry);
3861 centry_free(centry);
3863 if (!(state->success)) {
3864 return 1;
3866 DEBUG(10,("validate_ua: %s ok\n", keystr));
3867 return 0;
3870 static int validate_gm(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3871 struct tdb_validation_status *state)
3873 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3874 int32_t num_names, i;
3876 if (!centry) {
3877 return 1;
3880 num_names = centry_uint32(centry);
3882 for (i=0; i< num_names; i++) {
3883 struct dom_sid sid;
3884 centry_sid(centry, &sid);
3885 (void)centry_string(centry, mem_ctx);
3886 (void)centry_uint32(centry);
3889 centry_free(centry);
3891 if (!(state->success)) {
3892 return 1;
3894 DEBUG(10,("validate_gm: %s ok\n", keystr));
3895 return 0;
3898 static int validate_dr(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3899 struct tdb_validation_status *state)
3901 /* Can't say anything about this other than must be nonzero. */
3902 if (dbuf.dsize == 0) {
3903 DEBUG(0,("validate_dr: Corrupt cache for key %s (len == 0) ?\n",
3904 keystr));
3905 state->bad_entry = true;
3906 state->success = false;
3907 return 1;
3910 DEBUG(10,("validate_dr: %s ok\n", keystr));
3911 return 0;
3914 static int validate_de(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3915 struct tdb_validation_status *state)
3917 /* Can't say anything about this other than must be nonzero. */
3918 if (dbuf.dsize == 0) {
3919 DEBUG(0,("validate_de: Corrupt cache for key %s (len == 0) ?\n",
3920 keystr));
3921 state->bad_entry = true;
3922 state->success = false;
3923 return 1;
3926 DEBUG(10,("validate_de: %s ok\n", keystr));
3927 return 0;
3930 static int validate_nss_an(TALLOC_CTX *mem_ctx, const char *keystr,
3931 TDB_DATA dbuf,
3932 struct tdb_validation_status *state)
3934 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3936 if (!centry) {
3937 return 1;
3940 (void)centry_string( centry, mem_ctx );
3942 centry_free(centry);
3944 if (!(state->success)) {
3945 return 1;
3947 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3948 return 0;
3951 static int validate_nss_na(TALLOC_CTX *mem_ctx, const char *keystr,
3952 TDB_DATA dbuf,
3953 struct tdb_validation_status *state)
3955 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3957 if (!centry) {
3958 return 1;
3961 (void)centry_string( centry, mem_ctx );
3963 centry_free(centry);
3965 if (!(state->success)) {
3966 return 1;
3968 DBG_DEBUG("%s ok\n", keystr);
3969 return 0;
3972 static int validate_trustdomcache(TALLOC_CTX *mem_ctx, const char *keystr,
3973 TDB_DATA dbuf,
3974 struct tdb_validation_status *state)
3976 if (dbuf.dsize == 0) {
3977 DEBUG(0, ("validate_trustdomcache: Corrupt cache for "
3978 "key %s (len ==0) ?\n", keystr));
3979 state->bad_entry = true;
3980 state->success = false;
3981 return 1;
3984 DEBUG(10, ("validate_trustdomcache: %s ok\n", keystr));
3985 DEBUGADD(10, (" Don't trust me, I am a DUMMY!\n"));
3986 return 0;
3989 static int validate_offline(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3990 struct tdb_validation_status *state)
3992 if (dbuf.dsize != 4) {
3993 DEBUG(0,("validate_offline: Corrupt cache for key %s (len %u != 4) ?\n",
3994 keystr, (unsigned int)dbuf.dsize ));
3995 state->bad_entry = true;
3996 state->success = false;
3997 return 1;
3999 DEBUG(10,("validate_offline: %s ok\n", keystr));
4000 return 0;
4003 static int validate_ndr(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
4004 struct tdb_validation_status *state)
4007 * Ignore validation for now. The proper way to do this is with a
4008 * checksum. Just pure parsing does not really catch much.
4010 return 0;
4013 static int validate_cache_version(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
4014 struct tdb_validation_status *state)
4016 if (dbuf.dsize != 4) {
4017 DEBUG(0, ("validate_cache_version: Corrupt cache for "
4018 "key %s (len %u != 4) ?\n",
4019 keystr, (unsigned int)dbuf.dsize));
4020 state->bad_entry = true;
4021 state->success = false;
4022 return 1;
4025 DEBUG(10, ("validate_cache_version: %s ok\n", keystr));
4026 return 0;
4029 /***********************************************************************
4030 A list of all possible cache tdb keys with associated validation
4031 functions.
4032 ***********************************************************************/
4034 struct key_val_struct {
4035 const char *keyname;
4036 int (*validate_data_fn)(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, struct tdb_validation_status* state);
4037 } key_val[] = {
4038 {"SEQNUM/", validate_seqnum},
4039 {"NS/", validate_ns},
4040 {"SN/", validate_sn},
4041 {"U/", validate_u},
4042 {"LOC_POL/", validate_loc_pol},
4043 {"PWD_POL/", validate_pwd_pol},
4044 {"CRED/", validate_cred},
4045 {"UL/", validate_ul},
4046 {"GL/", validate_gl},
4047 {"UG/", validate_ug},
4048 {"UA", validate_ua},
4049 {"GM/", validate_gm},
4050 {"DR/", validate_dr},
4051 {"DE/", validate_de},
4052 {"TRUSTDOMCACHE/", validate_trustdomcache},
4053 {"NSS/NA/", validate_nss_na},
4054 {"NSS/AN/", validate_nss_an},
4055 {"WINBINDD_OFFLINE", validate_offline},
4056 {"NDR/", validate_ndr},
4057 {WINBINDD_CACHE_VERSION_KEYSTR, validate_cache_version},
4058 {NULL, NULL}
4061 /***********************************************************************
4062 Function to look at every entry in the tdb and validate it as far as
4063 possible.
4064 ***********************************************************************/
4066 static int cache_traverse_validate_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, void *state)
4068 int i;
4069 unsigned int max_key_len = 1024;
4070 struct tdb_validation_status *v_state = (struct tdb_validation_status *)state;
4072 /* Paranoia check. */
4073 if (strncmp("UA/", (const char *)kbuf.dptr, 3) == 0 ||
4074 strncmp("NDR/", (const char *)kbuf.dptr, 4) == 0) {
4075 max_key_len = 1024 * 1024;
4077 if (kbuf.dsize > max_key_len) {
4078 DEBUG(0, ("cache_traverse_validate_fn: key length too large: "
4079 "(%u) > (%u)\n\n",
4080 (unsigned int)kbuf.dsize, (unsigned int)max_key_len));
4081 return 1;
4084 for (i = 0; key_val[i].keyname; i++) {
4085 size_t namelen = strlen(key_val[i].keyname);
4086 if (kbuf.dsize >= namelen && (
4087 strncmp(key_val[i].keyname, (const char *)kbuf.dptr, namelen)) == 0) {
4088 TALLOC_CTX *mem_ctx;
4089 char *keystr;
4090 int ret;
4092 keystr = SMB_MALLOC_ARRAY(char, kbuf.dsize+1);
4093 if (!keystr) {
4094 return 1;
4096 memcpy(keystr, kbuf.dptr, kbuf.dsize);
4097 keystr[kbuf.dsize] = '\0';
4099 mem_ctx = talloc_init("validate_ctx");
4100 if (!mem_ctx) {
4101 SAFE_FREE(keystr);
4102 return 1;
4105 ret = key_val[i].validate_data_fn(mem_ctx, keystr, dbuf,
4106 v_state);
4108 SAFE_FREE(keystr);
4109 talloc_destroy(mem_ctx);
4110 return ret;
4114 DEBUG(0,("cache_traverse_validate_fn: unknown cache entry\nkey :\n"));
4115 dump_data(0, (uint8_t *)kbuf.dptr, kbuf.dsize);
4116 DEBUG(0,("data :\n"));
4117 dump_data(0, (uint8_t *)dbuf.dptr, dbuf.dsize);
4118 v_state->unknown_key = true;
4119 v_state->success = false;
4120 return 1; /* terminate. */
4123 static void validate_panic(const char *const why)
4125 DEBUG(0,("validating cache: would panic %s\n", why ));
4126 DEBUGADD(0, ("exiting instead (cache validation mode)\n"));
4127 exit(47);
4130 static int wbcache_update_centry_fn(TDB_CONTEXT *tdb,
4131 TDB_DATA key,
4132 TDB_DATA data,
4133 void *state)
4135 uint64_t ctimeout;
4136 TDB_DATA blob;
4138 if (is_non_centry_key(key)) {
4139 return 0;
4142 if (data.dptr == NULL || data.dsize == 0) {
4143 if (tdb_delete(tdb, key) < 0) {
4144 DEBUG(0, ("tdb_delete for [%s] failed!\n",
4145 key.dptr));
4146 return 1;
4150 /* add timeout to blob (uint64_t) */
4151 blob.dsize = data.dsize + 8;
4153 blob.dptr = SMB_XMALLOC_ARRAY(uint8_t, blob.dsize);
4154 if (blob.dptr == NULL) {
4155 return 1;
4157 memset(blob.dptr, 0, blob.dsize);
4159 /* copy status and seqnum */
4160 memcpy(blob.dptr, data.dptr, 8);
4162 /* add timeout */
4163 ctimeout = lp_winbind_cache_time() + time(NULL);
4164 SBVAL(blob.dptr, 8, ctimeout);
4166 /* copy the rest */
4167 memcpy(blob.dptr + 16, data.dptr + 8, data.dsize - 8);
4169 if (tdb_store(tdb, key, blob, TDB_REPLACE) < 0) {
4170 DEBUG(0, ("tdb_store to update [%s] failed!\n",
4171 key.dptr));
4172 SAFE_FREE(blob.dptr);
4173 return 1;
4176 SAFE_FREE(blob.dptr);
4177 return 0;
4180 static bool wbcache_upgrade_v1_to_v2(TDB_CONTEXT *tdb)
4182 int rc;
4184 DEBUG(1, ("Upgrade to version 2 of the winbindd_cache.tdb\n"));
4186 rc = tdb_traverse(tdb, wbcache_update_centry_fn, NULL);
4187 if (rc < 0) {
4188 return false;
4191 return true;
4194 /***********************************************************************
4195 Try and validate every entry in the winbindd cache. If we fail here,
4196 delete the cache tdb and return non-zero.
4197 ***********************************************************************/
4199 int winbindd_validate_cache(void)
4201 int ret = -1;
4202 char *tdb_path = NULL;
4203 TDB_CONTEXT *tdb = NULL;
4204 uint32_t vers_id;
4205 bool ok;
4207 DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
4208 smb_panic_fn = validate_panic;
4210 tdb_path = wcache_path();
4211 if (tdb_path == NULL) {
4212 goto done;
4215 tdb = tdb_open_log(tdb_path,
4216 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
4217 TDB_INCOMPATIBLE_HASH |
4218 ( lp_winbind_offline_logon()
4219 ? TDB_DEFAULT
4220 : TDB_DEFAULT | TDB_CLEAR_IF_FIRST ),
4221 O_RDWR|O_CREAT,
4222 0600);
4223 if (!tdb) {
4224 DEBUG(0, ("winbindd_validate_cache: "
4225 "error opening/initializing tdb\n"));
4226 goto done;
4229 /* Version check and upgrade code. */
4230 if (!tdb_fetch_uint32(tdb, WINBINDD_CACHE_VERSION_KEYSTR, &vers_id)) {
4231 DEBUG(10, ("Fresh database\n"));
4232 tdb_store_uint32(tdb, WINBINDD_CACHE_VERSION_KEYSTR, WINBINDD_CACHE_VERSION);
4233 vers_id = WINBINDD_CACHE_VERSION;
4236 if (vers_id != WINBINDD_CACHE_VERSION) {
4237 if (vers_id == WINBINDD_CACHE_VER1) {
4238 ok = wbcache_upgrade_v1_to_v2(tdb);
4239 if (!ok) {
4240 DEBUG(10, ("winbindd_validate_cache: upgrade to version 2 failed.\n"));
4241 unlink(tdb_path);
4242 goto done;
4245 tdb_store_uint32(tdb,
4246 WINBINDD_CACHE_VERSION_KEYSTR,
4247 WINBINDD_CACHE_VERSION);
4248 vers_id = WINBINDD_CACHE_VER2;
4252 tdb_close(tdb);
4254 ret = tdb_validate_and_backup(tdb_path, cache_traverse_validate_fn);
4256 if (ret != 0) {
4257 DEBUG(10, ("winbindd_validate_cache: validation not successful.\n"));
4258 DEBUGADD(10, ("removing tdb %s.\n", tdb_path));
4259 unlink(tdb_path);
4262 done:
4263 TALLOC_FREE(tdb_path);
4264 DEBUG(10, ("winbindd_validate_cache: restoring panic function\n"));
4265 smb_panic_fn = smb_panic;
4266 return ret;
4269 /***********************************************************************
4270 Try and validate every entry in the winbindd cache.
4271 ***********************************************************************/
4273 int winbindd_validate_cache_nobackup(void)
4275 int ret = -1;
4276 char *tdb_path;
4278 DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
4279 smb_panic_fn = validate_panic;
4281 tdb_path = wcache_path();
4282 if (tdb_path == NULL) {
4283 goto err_panic_restore;
4286 if (wcache == NULL || wcache->tdb == NULL) {
4287 ret = tdb_validate_open(tdb_path, cache_traverse_validate_fn);
4288 } else {
4289 ret = tdb_validate(wcache->tdb, cache_traverse_validate_fn);
4292 if (ret != 0) {
4293 DEBUG(10, ("winbindd_validate_cache_nobackup: validation not "
4294 "successful.\n"));
4297 TALLOC_FREE(tdb_path);
4298 err_panic_restore:
4299 DEBUG(10, ("winbindd_validate_cache_nobackup: restoring panic "
4300 "function\n"));
4301 smb_panic_fn = smb_panic;
4302 return ret;
4305 bool winbindd_cache_validate_and_initialize(void)
4307 close_winbindd_cache();
4309 if (lp_winbind_offline_logon()) {
4310 if (winbindd_validate_cache() < 0) {
4311 DEBUG(0, ("winbindd cache tdb corrupt and no backup "
4312 "could be restored.\n"));
4316 return initialize_winbindd_cache();
4319 /*********************************************************************
4320 ********************************************************************/
4322 static bool add_wbdomain_to_tdc_array( struct winbindd_domain *new_dom,
4323 struct winbindd_tdc_domain **domains,
4324 size_t *num_domains )
4326 struct winbindd_tdc_domain *list = NULL;
4327 size_t i, idx;
4328 bool set_only = false;
4330 /* don't allow duplicates */
4332 idx = *num_domains;
4333 list = *domains;
4335 for ( i=0; i< (*num_domains); i++ ) {
4336 if ( strequal( new_dom->name, list[i].domain_name ) ) {
4337 DEBUG(10,("add_wbdomain_to_tdc_array: Found existing record for %s\n",
4338 new_dom->name));
4339 idx = i;
4340 set_only = true;
4342 break;
4346 if ( !set_only ) {
4347 if ( !*domains ) {
4348 list = talloc_array( NULL, struct winbindd_tdc_domain, 1 );
4349 idx = 0;
4350 } else {
4351 list = talloc_realloc( *domains, *domains,
4352 struct winbindd_tdc_domain,
4353 (*num_domains)+1);
4354 idx = *num_domains;
4357 ZERO_STRUCT( list[idx] );
4360 if ( !list )
4361 return false;
4363 list[idx].domain_name = talloc_strdup(list, new_dom->name);
4364 if (list[idx].domain_name == NULL) {
4365 return false;
4367 if (new_dom->alt_name != NULL) {
4368 list[idx].dns_name = talloc_strdup(list, new_dom->alt_name);
4369 if (list[idx].dns_name == NULL) {
4370 return false;
4374 if ( !is_null_sid( &new_dom->sid ) ) {
4375 sid_copy( &list[idx].sid, &new_dom->sid );
4376 } else {
4377 sid_copy(&list[idx].sid, &global_sid_NULL);
4380 if ( new_dom->domain_flags != 0x0 )
4381 list[idx].trust_flags = new_dom->domain_flags;
4383 if ( new_dom->domain_type != 0x0 )
4384 list[idx].trust_type = new_dom->domain_type;
4386 if ( new_dom->domain_trust_attribs != 0x0 )
4387 list[idx].trust_attribs = new_dom->domain_trust_attribs;
4389 if ( !set_only ) {
4390 *domains = list;
4391 *num_domains = idx + 1;
4394 return true;
4397 /*********************************************************************
4398 ********************************************************************/
4400 static TDB_DATA make_tdc_key( const char *domain_name )
4402 char *keystr = NULL;
4403 TDB_DATA key = { NULL, 0 };
4405 if ( !domain_name ) {
4406 DEBUG(5,("make_tdc_key: Keyname workgroup is NULL!\n"));
4407 return key;
4410 if (asprintf( &keystr, "TRUSTDOMCACHE/%s", domain_name ) == -1) {
4411 return key;
4413 key = string_term_tdb_data(keystr);
4415 return key;
4418 /*********************************************************************
4419 ********************************************************************/
4421 static int pack_tdc_domains( struct winbindd_tdc_domain *domains,
4422 size_t num_domains,
4423 unsigned char **buf )
4425 unsigned char *buffer = NULL;
4426 int len = 0;
4427 int buflen = 0;
4428 size_t i = 0;
4430 DEBUG(10,("pack_tdc_domains: Packing %d trusted domains\n",
4431 (int)num_domains));
4433 buflen = 0;
4435 again:
4436 len = 0;
4438 /* Store the number of array items first */
4439 len += tdb_pack( buffer+len, buflen-len, "d",
4440 num_domains );
4442 /* now pack each domain trust record */
4443 for ( i=0; i<num_domains; i++ ) {
4445 fstring tmp;
4447 if ( buflen > 0 ) {
4448 DEBUG(10,("pack_tdc_domains: Packing domain %s (%s)\n",
4449 domains[i].domain_name,
4450 domains[i].dns_name ? domains[i].dns_name : "UNKNOWN" ));
4453 len += tdb_pack( buffer+len, buflen-len, "fffddd",
4454 domains[i].domain_name,
4455 domains[i].dns_name ? domains[i].dns_name : "",
4456 sid_to_fstring(tmp, &domains[i].sid),
4457 domains[i].trust_flags,
4458 domains[i].trust_attribs,
4459 domains[i].trust_type );
4462 if ( buflen < len ) {
4463 SAFE_FREE(buffer);
4464 if ( (buffer = SMB_MALLOC_ARRAY(unsigned char, len)) == NULL ) {
4465 DEBUG(0,("pack_tdc_domains: failed to alloc buffer!\n"));
4466 buflen = -1;
4467 goto done;
4469 buflen = len;
4470 goto again;
4473 *buf = buffer;
4475 done:
4476 return buflen;
4479 /*********************************************************************
4480 ********************************************************************/
4482 static size_t unpack_tdc_domains( unsigned char *buf, int buflen,
4483 struct winbindd_tdc_domain **domains )
4485 fstring domain_name, dns_name, sid_string;
4486 uint32_t type, attribs, flags;
4487 int num_domains;
4488 int len = 0;
4489 int i;
4490 struct winbindd_tdc_domain *list = NULL;
4492 /* get the number of domains */
4493 len += tdb_unpack( buf+len, buflen-len, "d", &num_domains);
4494 if ( len == -1 ) {
4495 DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
4496 return 0;
4499 list = talloc_array( NULL, struct winbindd_tdc_domain, num_domains );
4500 if ( !list ) {
4501 DEBUG(0,("unpack_tdc_domains: Failed to talloc() domain list!\n"));
4502 return 0;
4505 for ( i=0; i<num_domains; i++ ) {
4506 int this_len;
4508 this_len = tdb_unpack( buf+len, buflen-len, "fffddd",
4509 domain_name,
4510 dns_name,
4511 sid_string,
4512 &flags,
4513 &attribs,
4514 &type );
4516 if ( this_len == -1 ) {
4517 DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
4518 TALLOC_FREE( list );
4519 return 0;
4521 len += this_len;
4523 DEBUG(11,("unpack_tdc_domains: Unpacking domain %s (%s) "
4524 "SID %s, flags = 0x%x, attribs = 0x%x, type = 0x%x\n",
4525 domain_name, dns_name, sid_string,
4526 flags, attribs, type));
4528 list[i].domain_name = talloc_strdup( list, domain_name );
4529 list[i].dns_name = NULL;
4530 if (dns_name[0] != '\0') {
4531 list[i].dns_name = talloc_strdup(list, dns_name);
4533 if ( !string_to_sid( &(list[i].sid), sid_string ) ) {
4534 DEBUG(10,("unpack_tdc_domains: no SID for domain %s\n",
4535 domain_name));
4537 list[i].trust_flags = flags;
4538 list[i].trust_attribs = attribs;
4539 list[i].trust_type = type;
4542 *domains = list;
4544 return num_domains;
4547 /*********************************************************************
4548 ********************************************************************/
4550 static bool wcache_tdc_store_list( struct winbindd_tdc_domain *domains, size_t num_domains )
4552 TDB_DATA key = make_tdc_key( lp_workgroup() );
4553 TDB_DATA data = { NULL, 0 };
4554 int ret;
4556 if ( !key.dptr )
4557 return false;
4559 /* See if we were asked to delete the cache entry */
4561 if ( !domains ) {
4562 ret = tdb_delete( wcache->tdb, key );
4563 goto done;
4566 data.dsize = pack_tdc_domains( domains, num_domains, &data.dptr );
4568 if ( !data.dptr ) {
4569 ret = -1;
4570 goto done;
4573 ret = tdb_store( wcache->tdb, key, data, 0 );
4575 done:
4576 SAFE_FREE( data.dptr );
4577 SAFE_FREE( key.dptr );
4579 return ( ret == 0 );
4582 /*********************************************************************
4583 ********************************************************************/
4585 bool wcache_tdc_fetch_list( struct winbindd_tdc_domain **domains, size_t *num_domains )
4587 TDB_DATA key = make_tdc_key( lp_workgroup() );
4588 TDB_DATA data = { NULL, 0 };
4590 *domains = NULL;
4591 *num_domains = 0;
4593 if ( !key.dptr )
4594 return false;
4596 data = tdb_fetch( wcache->tdb, key );
4598 SAFE_FREE( key.dptr );
4600 if ( !data.dptr )
4601 return false;
4603 *num_domains = unpack_tdc_domains( data.dptr, data.dsize, domains );
4605 SAFE_FREE( data.dptr );
4607 if ( !*domains )
4608 return false;
4610 return true;
4613 /*********************************************************************
4614 ********************************************************************/
4616 bool wcache_tdc_add_domain( struct winbindd_domain *domain )
4618 struct winbindd_tdc_domain *dom_list = NULL;
4619 size_t num_domains = 0;
4620 bool ret = false;
4622 DEBUG(10,("wcache_tdc_add_domain: Adding domain %s (%s), SID %s, "
4623 "flags = 0x%x, attributes = 0x%x, type = 0x%x\n",
4624 domain->name, domain->alt_name,
4625 sid_string_dbg(&domain->sid),
4626 domain->domain_flags,
4627 domain->domain_trust_attribs,
4628 domain->domain_type));
4630 if ( !init_wcache() ) {
4631 return false;
4634 /* fetch the list */
4636 wcache_tdc_fetch_list( &dom_list, &num_domains );
4638 /* add the new domain */
4640 if ( !add_wbdomain_to_tdc_array( domain, &dom_list, &num_domains ) ) {
4641 goto done;
4644 /* pack the domain */
4646 if ( !wcache_tdc_store_list( dom_list, num_domains ) ) {
4647 goto done;
4650 /* Success */
4652 ret = true;
4653 done:
4654 TALLOC_FREE( dom_list );
4656 return ret;
4659 static struct winbindd_tdc_domain *wcache_tdc_dup_domain(
4660 TALLOC_CTX *mem_ctx, const struct winbindd_tdc_domain *src)
4662 struct winbindd_tdc_domain *dst;
4664 dst = talloc(mem_ctx, struct winbindd_tdc_domain);
4665 if (dst == NULL) {
4666 goto fail;
4668 dst->domain_name = talloc_strdup(dst, src->domain_name);
4669 if (dst->domain_name == NULL) {
4670 goto fail;
4673 dst->dns_name = NULL;
4674 if (src->dns_name != NULL) {
4675 dst->dns_name = talloc_strdup(dst, src->dns_name);
4676 if (dst->dns_name == NULL) {
4677 goto fail;
4681 sid_copy(&dst->sid, &src->sid);
4682 dst->trust_flags = src->trust_flags;
4683 dst->trust_type = src->trust_type;
4684 dst->trust_attribs = src->trust_attribs;
4685 return dst;
4686 fail:
4687 TALLOC_FREE(dst);
4688 return NULL;
4691 /*********************************************************************
4692 ********************************************************************/
4694 struct winbindd_tdc_domain * wcache_tdc_fetch_domain( TALLOC_CTX *ctx, const char *name )
4696 struct winbindd_tdc_domain *dom_list = NULL;
4697 size_t num_domains = 0;
4698 size_t i;
4699 struct winbindd_tdc_domain *d = NULL;
4701 DEBUG(10,("wcache_tdc_fetch_domain: Searching for domain %s\n", name));
4703 if ( !init_wcache() ) {
4704 return NULL;
4707 /* fetch the list */
4709 wcache_tdc_fetch_list( &dom_list, &num_domains );
4711 for ( i=0; i<num_domains; i++ ) {
4712 if ( strequal(name, dom_list[i].domain_name) ||
4713 strequal(name, dom_list[i].dns_name) )
4715 DEBUG(10,("wcache_tdc_fetch_domain: Found domain %s\n",
4716 name));
4718 d = wcache_tdc_dup_domain(ctx, &dom_list[i]);
4719 break;
4723 TALLOC_FREE( dom_list );
4725 return d;
4728 /*********************************************************************
4729 ********************************************************************/
4731 void wcache_tdc_clear( void )
4733 if ( !init_wcache() )
4734 return;
4736 wcache_tdc_store_list( NULL, 0 );
4738 return;
4741 static bool wcache_ndr_key(TALLOC_CTX *mem_ctx, const char *domain_name,
4742 uint32_t opnum, const DATA_BLOB *req,
4743 TDB_DATA *pkey)
4745 char *key;
4746 size_t keylen;
4748 key = talloc_asprintf(mem_ctx, "NDR/%s/%d/", domain_name, (int)opnum);
4749 if (key == NULL) {
4750 return false;
4752 keylen = talloc_get_size(key) - 1;
4754 key = talloc_realloc(mem_ctx, key, char, keylen + req->length);
4755 if (key == NULL) {
4756 return false;
4758 memcpy(key + keylen, req->data, req->length);
4760 pkey->dptr = (uint8_t *)key;
4761 pkey->dsize = talloc_get_size(key);
4762 return true;
4765 static bool wcache_opnum_cacheable(uint32_t opnum)
4767 switch (opnum) {
4768 case NDR_WBINT_PING:
4769 case NDR_WBINT_QUERYSEQUENCENUMBER:
4770 case NDR_WBINT_ALLOCATEUID:
4771 case NDR_WBINT_ALLOCATEGID:
4772 case NDR_WBINT_CHECKMACHINEACCOUNT:
4773 case NDR_WBINT_CHANGEMACHINEACCOUNT:
4774 case NDR_WBINT_PINGDC:
4775 return false;
4777 return true;
4780 bool wcache_fetch_ndr(TALLOC_CTX *mem_ctx, struct winbindd_domain *domain,
4781 uint32_t opnum, const DATA_BLOB *req, DATA_BLOB *resp)
4783 TDB_DATA key, data;
4784 bool ret = false;
4786 if (!wcache_opnum_cacheable(opnum) ||
4787 is_my_own_sam_domain(domain) ||
4788 is_builtin_domain(domain)) {
4789 return false;
4792 if (wcache->tdb == NULL) {
4793 return false;
4796 if (!wcache_ndr_key(talloc_tos(), domain->name, opnum, req, &key)) {
4797 return false;
4799 data = tdb_fetch(wcache->tdb, key);
4800 TALLOC_FREE(key.dptr);
4802 if (data.dptr == NULL) {
4803 return false;
4805 if (data.dsize < 12) {
4806 goto fail;
4809 if (!is_domain_offline(domain)) {
4810 uint32_t entry_seqnum, dom_seqnum, last_check;
4811 uint64_t entry_timeout;
4813 if (!wcache_fetch_seqnum(domain->name, &dom_seqnum,
4814 &last_check)) {
4815 goto fail;
4817 entry_seqnum = IVAL(data.dptr, 0);
4818 if (entry_seqnum != dom_seqnum) {
4819 DEBUG(10, ("Entry has wrong sequence number: %d\n",
4820 (int)entry_seqnum));
4821 goto fail;
4823 entry_timeout = BVAL(data.dptr, 4);
4824 if (time(NULL) > entry_timeout) {
4825 DEBUG(10, ("Entry has timed out\n"));
4826 goto fail;
4830 resp->data = (uint8_t *)talloc_memdup(mem_ctx, data.dptr + 12,
4831 data.dsize - 12);
4832 if (resp->data == NULL) {
4833 DEBUG(10, ("talloc failed\n"));
4834 goto fail;
4836 resp->length = data.dsize - 12;
4838 ret = true;
4839 fail:
4840 SAFE_FREE(data.dptr);
4841 return ret;
4844 void wcache_store_ndr(struct winbindd_domain *domain, uint32_t opnum,
4845 const DATA_BLOB *req, const DATA_BLOB *resp)
4847 TDB_DATA key, data;
4848 uint32_t dom_seqnum, last_check;
4849 uint64_t timeout;
4851 if (!wcache_opnum_cacheable(opnum) ||
4852 is_my_own_sam_domain(domain) ||
4853 is_builtin_domain(domain)) {
4854 return;
4857 if (wcache->tdb == NULL) {
4858 return;
4861 if (!wcache_fetch_seqnum(domain->name, &dom_seqnum, &last_check)) {
4862 DEBUG(10, ("could not fetch seqnum for domain %s\n",
4863 domain->name));
4864 return;
4867 if (!wcache_ndr_key(talloc_tos(), domain->name, opnum, req, &key)) {
4868 return;
4871 timeout = time(NULL) + lp_winbind_cache_time();
4873 data.dsize = resp->length + 12;
4874 data.dptr = talloc_array(key.dptr, uint8_t, data.dsize);
4875 if (data.dptr == NULL) {
4876 goto done;
4879 SIVAL(data.dptr, 0, dom_seqnum);
4880 SBVAL(data.dptr, 4, timeout);
4881 memcpy(data.dptr + 12, resp->data, resp->length);
4883 tdb_store(wcache->tdb, key, data, 0);
4885 done:
4886 TALLOC_FREE(key.dptr);
4887 return;