upgradeprovision: do not try to remove/change attribute before the RID Set object...
[Samba/vl.git] / source3 / winbindd / winbindd_cache.c
blob5d12c2b3bc29e16dda607ea3ccdb636777f5a2a5
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 "winbindd.h"
28 #include "tdb_validate.h"
29 #include "../libcli/auth/libcli_auth.h"
30 #include "../librpc/gen_ndr/ndr_wbint.h"
31 #include "ads.h"
32 #include "nss_info.h"
34 #undef DBGC_CLASS
35 #define DBGC_CLASS DBGC_WINBIND
37 #define WINBINDD_CACHE_VERSION 1
38 #define WINBINDD_CACHE_VERSION_KEYSTR "WINBINDD_CACHE_VERSION"
40 extern struct winbindd_methods reconnect_methods;
41 #ifdef HAVE_ADS
42 extern struct winbindd_methods ads_methods;
43 #endif
44 extern struct winbindd_methods builtin_passdb_methods;
45 extern struct winbindd_methods sam_passdb_methods;
48 * JRA. KEEP THIS LIST UP TO DATE IF YOU ADD CACHE ENTRIES.
49 * Here are the list of entry types that are *not* stored
50 * as form struct cache_entry in the cache.
53 static const char *non_centry_keys[] = {
54 "SEQNUM/",
55 "DR/",
56 "DE/",
57 "WINBINDD_OFFLINE",
58 WINBINDD_CACHE_VERSION_KEYSTR,
59 NULL
62 /************************************************************************
63 Is this key a non-centry type ?
64 ************************************************************************/
66 static bool is_non_centry_key(TDB_DATA kbuf)
68 int i;
70 if (kbuf.dptr == NULL || kbuf.dsize == 0) {
71 return false;
73 for (i = 0; non_centry_keys[i] != NULL; i++) {
74 size_t namelen = strlen(non_centry_keys[i]);
75 if (kbuf.dsize < namelen) {
76 continue;
78 if (strncmp(non_centry_keys[i], (const char *)kbuf.dptr, namelen) == 0) {
79 return true;
82 return false;
85 /* Global online/offline state - False when online. winbindd starts up online
86 and sets this to true if the first query fails and there's an entry in
87 the cache tdb telling us to stay offline. */
89 static bool global_winbindd_offline_state;
91 struct winbind_cache {
92 TDB_CONTEXT *tdb;
95 struct cache_entry {
96 NTSTATUS status;
97 uint32 sequence_number;
98 uint8 *data;
99 uint32 len, ofs;
102 void (*smb_panic_fn)(const char *const why) = smb_panic;
104 #define WINBINDD_MAX_CACHE_SIZE (50*1024*1024)
106 static struct winbind_cache *wcache;
108 void winbindd_check_cache_size(time_t t)
110 static time_t last_check_time;
111 struct stat st;
113 if (last_check_time == (time_t)0)
114 last_check_time = t;
116 if (t - last_check_time < 60 && t - last_check_time > 0)
117 return;
119 if (wcache == NULL || wcache->tdb == NULL) {
120 DEBUG(0, ("Unable to check size of tdb cache - cache not open !\n"));
121 return;
124 if (fstat(tdb_fd(wcache->tdb), &st) == -1) {
125 DEBUG(0, ("Unable to check size of tdb cache %s!\n", strerror(errno) ));
126 return;
129 if (st.st_size > WINBINDD_MAX_CACHE_SIZE) {
130 DEBUG(10,("flushing cache due to size (%lu) > (%lu)\n",
131 (unsigned long)st.st_size,
132 (unsigned long)WINBINDD_MAX_CACHE_SIZE));
133 wcache_flush_cache();
137 /* get the winbind_cache structure */
138 static struct winbind_cache *get_cache(struct winbindd_domain *domain)
140 struct winbind_cache *ret = wcache;
142 /* We have to know what type of domain we are dealing with first. */
144 if (domain->internal) {
145 domain->backend = &builtin_passdb_methods;
146 domain->initialized = True;
149 if (strequal(domain->name, get_global_sam_name()) &&
150 sid_equal(&domain->sid, get_global_sam_sid())) {
151 domain->backend = &sam_passdb_methods;
152 domain->initialized = True;
155 if ( !domain->initialized ) {
156 init_dc_connection( domain );
160 OK. listen up becasue I'm only going to say this once.
161 We have the following scenarios to consider
162 (a) trusted AD domains on a Samba DC,
163 (b) trusted AD domains and we are joined to a non-kerberos domain
164 (c) trusted AD domains and we are joined to a kerberos (AD) domain
166 For (a) we can always contact the trusted domain using krb5
167 since we have the domain trust account password
169 For (b) we can only use RPC since we have no way of
170 getting a krb5 ticket in our own domain
172 For (c) we can always use krb5 since we have a kerberos trust
174 --jerry
177 if (!domain->backend) {
178 #ifdef HAVE_ADS
179 struct winbindd_domain *our_domain = domain;
181 /* find our domain first so we can figure out if we
182 are joined to a kerberized domain */
184 if ( !domain->primary )
185 our_domain = find_our_domain();
187 if ((our_domain->active_directory || IS_DC)
188 && domain->active_directory
189 && !lp_winbind_rpc_only()) {
190 DEBUG(5,("get_cache: Setting ADS methods for domain %s\n", domain->name));
191 domain->backend = &ads_methods;
192 } else {
193 #endif /* HAVE_ADS */
194 DEBUG(5,("get_cache: Setting MS-RPC methods for domain %s\n", domain->name));
195 domain->backend = &reconnect_methods;
196 #ifdef HAVE_ADS
198 #endif /* HAVE_ADS */
201 if (ret)
202 return ret;
204 ret = SMB_XMALLOC_P(struct winbind_cache);
205 ZERO_STRUCTP(ret);
207 wcache = ret;
208 wcache_flush_cache();
210 return ret;
214 free a centry structure
216 static void centry_free(struct cache_entry *centry)
218 if (!centry)
219 return;
220 SAFE_FREE(centry->data);
221 free(centry);
224 static bool centry_check_bytes(struct cache_entry *centry, size_t nbytes)
226 if (centry->len - centry->ofs < nbytes) {
227 DEBUG(0,("centry corruption? needed %u bytes, have %d\n",
228 (unsigned int)nbytes,
229 centry->len - centry->ofs));
230 return false;
232 return true;
236 pull a uint32 from a cache entry
238 static uint32 centry_uint32(struct cache_entry *centry)
240 uint32 ret;
242 if (!centry_check_bytes(centry, 4)) {
243 smb_panic_fn("centry_uint32");
245 ret = IVAL(centry->data, centry->ofs);
246 centry->ofs += 4;
247 return ret;
251 pull a uint16 from a cache entry
253 static uint16 centry_uint16(struct cache_entry *centry)
255 uint16 ret;
256 if (!centry_check_bytes(centry, 2)) {
257 smb_panic_fn("centry_uint16");
259 ret = CVAL(centry->data, centry->ofs);
260 centry->ofs += 2;
261 return ret;
265 pull a uint8 from a cache entry
267 static uint8 centry_uint8(struct cache_entry *centry)
269 uint8 ret;
270 if (!centry_check_bytes(centry, 1)) {
271 smb_panic_fn("centry_uint8");
273 ret = CVAL(centry->data, centry->ofs);
274 centry->ofs += 1;
275 return ret;
279 pull a NTTIME from a cache entry
281 static NTTIME centry_nttime(struct cache_entry *centry)
283 NTTIME ret;
284 if (!centry_check_bytes(centry, 8)) {
285 smb_panic_fn("centry_nttime");
287 ret = IVAL(centry->data, centry->ofs);
288 centry->ofs += 4;
289 ret += (uint64_t)IVAL(centry->data, centry->ofs) << 32;
290 centry->ofs += 4;
291 return ret;
295 pull a time_t from a cache entry. time_t stored portably as a 64-bit time.
297 static time_t centry_time(struct cache_entry *centry)
299 return (time_t)centry_nttime(centry);
302 /* pull a string from a cache entry, using the supplied
303 talloc context
305 static char *centry_string(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
307 uint32 len;
308 char *ret;
310 len = centry_uint8(centry);
312 if (len == 0xFF) {
313 /* a deliberate NULL string */
314 return NULL;
317 if (!centry_check_bytes(centry, (size_t)len)) {
318 smb_panic_fn("centry_string");
321 ret = TALLOC_ARRAY(mem_ctx, char, len+1);
322 if (!ret) {
323 smb_panic_fn("centry_string out of memory\n");
325 memcpy(ret,centry->data + centry->ofs, len);
326 ret[len] = 0;
327 centry->ofs += len;
328 return ret;
331 /* pull a hash16 from a cache entry, using the supplied
332 talloc context
334 static char *centry_hash16(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
336 uint32 len;
337 char *ret;
339 len = centry_uint8(centry);
341 if (len != 16) {
342 DEBUG(0,("centry corruption? hash len (%u) != 16\n",
343 len ));
344 return NULL;
347 if (!centry_check_bytes(centry, 16)) {
348 return NULL;
351 ret = TALLOC_ARRAY(mem_ctx, char, 16);
352 if (!ret) {
353 smb_panic_fn("centry_hash out of memory\n");
355 memcpy(ret,centry->data + centry->ofs, 16);
356 centry->ofs += 16;
357 return ret;
360 /* pull a sid from a cache entry, using the supplied
361 talloc context
363 static bool centry_sid(struct cache_entry *centry, struct dom_sid *sid)
365 char *sid_string;
366 bool ret;
368 sid_string = centry_string(centry, talloc_tos());
369 if (sid_string == NULL) {
370 return false;
372 ret = string_to_sid(sid, sid_string);
373 TALLOC_FREE(sid_string);
374 return ret;
379 pull a NTSTATUS from a cache entry
381 static NTSTATUS centry_ntstatus(struct cache_entry *centry)
383 NTSTATUS status;
385 status = NT_STATUS(centry_uint32(centry));
386 return status;
390 /* the server is considered down if it can't give us a sequence number */
391 static bool wcache_server_down(struct winbindd_domain *domain)
393 bool ret;
395 if (!wcache->tdb)
396 return false;
398 ret = (domain->sequence_number == DOM_SEQUENCE_NONE);
400 if (ret)
401 DEBUG(10,("wcache_server_down: server for Domain %s down\n",
402 domain->name ));
403 return ret;
406 static bool wcache_fetch_seqnum(const char *domain_name, uint32_t *seqnum,
407 uint32_t *last_seq_check)
409 char *key;
410 TDB_DATA data;
412 if (wcache->tdb == NULL) {
413 DEBUG(10,("wcache_fetch_seqnum: tdb == NULL\n"));
414 return false;
417 key = talloc_asprintf(talloc_tos(), "SEQNUM/%s", domain_name);
418 if (key == NULL) {
419 DEBUG(10, ("talloc failed\n"));
420 return false;
423 data = tdb_fetch_bystring(wcache->tdb, key);
424 TALLOC_FREE(key);
426 if (data.dptr == NULL) {
427 DEBUG(10, ("wcache_fetch_seqnum: %s not found\n",
428 domain_name));
429 return false;
431 if (data.dsize != 8) {
432 DEBUG(10, ("wcache_fetch_seqnum: invalid data size %d\n",
433 (int)data.dsize));
434 SAFE_FREE(data.dptr);
435 return false;
438 *seqnum = IVAL(data.dptr, 0);
439 *last_seq_check = IVAL(data.dptr, 4);
440 SAFE_FREE(data.dptr);
442 return true;
445 static NTSTATUS fetch_cache_seqnum( struct winbindd_domain *domain, time_t now )
447 uint32 last_check, time_diff;
449 if (!wcache_fetch_seqnum(domain->name, &domain->sequence_number,
450 &last_check)) {
451 return NT_STATUS_UNSUCCESSFUL;
453 domain->last_seq_check = last_check;
455 /* have we expired? */
457 time_diff = now - domain->last_seq_check;
458 if ( time_diff > lp_winbind_cache_time() ) {
459 DEBUG(10,("fetch_cache_seqnum: timeout [%s][%u @ %u]\n",
460 domain->name, domain->sequence_number,
461 (uint32)domain->last_seq_check));
462 return NT_STATUS_UNSUCCESSFUL;
465 DEBUG(10,("fetch_cache_seqnum: success [%s][%u @ %u]\n",
466 domain->name, domain->sequence_number,
467 (uint32)domain->last_seq_check));
469 return NT_STATUS_OK;
472 bool wcache_store_seqnum(const char *domain_name, uint32_t seqnum,
473 time_t last_seq_check)
475 char *key_str;
476 uint8_t buf[8];
477 int ret;
479 if (wcache->tdb == NULL) {
480 DEBUG(10, ("wcache_store_seqnum: wcache->tdb == NULL\n"));
481 return false;
484 key_str = talloc_asprintf(talloc_tos(), "SEQNUM/%s", domain_name);
485 if (key_str == NULL) {
486 DEBUG(10, ("talloc_asprintf failed\n"));
487 return false;
490 SIVAL(buf, 0, seqnum);
491 SIVAL(buf, 4, last_seq_check);
493 ret = tdb_store_bystring(wcache->tdb, key_str,
494 make_tdb_data(buf, sizeof(buf)), TDB_REPLACE);
495 TALLOC_FREE(key_str);
496 if (ret == -1) {
497 DEBUG(10, ("tdb_store_bystring failed: %s\n",
498 tdb_errorstr(wcache->tdb)));
499 TALLOC_FREE(key_str);
500 return false;
503 DEBUG(10, ("wcache_store_seqnum: success [%s][%u @ %u]\n",
504 domain_name, seqnum, (unsigned)last_seq_check));
506 return true;
509 static bool store_cache_seqnum( struct winbindd_domain *domain )
511 return wcache_store_seqnum(domain->name, domain->sequence_number,
512 domain->last_seq_check);
516 refresh the domain sequence number. If force is true
517 then always refresh it, no matter how recently we fetched it
520 static void refresh_sequence_number(struct winbindd_domain *domain, bool force)
522 NTSTATUS status;
523 unsigned time_diff;
524 time_t t = time(NULL);
525 unsigned cache_time = lp_winbind_cache_time();
527 if (is_domain_offline(domain)) {
528 return;
531 get_cache( domain );
533 #if 0 /* JERRY -- disable as the default cache time is now 5 minutes */
534 /* trying to reconnect is expensive, don't do it too often */
535 if (domain->sequence_number == DOM_SEQUENCE_NONE) {
536 cache_time *= 8;
538 #endif
540 time_diff = t - domain->last_seq_check;
542 /* see if we have to refetch the domain sequence number */
543 if (!force && (time_diff < cache_time) &&
544 (domain->sequence_number != DOM_SEQUENCE_NONE) &&
545 NT_STATUS_IS_OK(domain->last_status)) {
546 DEBUG(10, ("refresh_sequence_number: %s time ok\n", domain->name));
547 goto done;
550 /* try to get the sequence number from the tdb cache first */
551 /* this will update the timestamp as well */
553 status = fetch_cache_seqnum( domain, t );
554 if (NT_STATUS_IS_OK(status) &&
555 (domain->sequence_number != DOM_SEQUENCE_NONE) &&
556 NT_STATUS_IS_OK(domain->last_status)) {
557 goto done;
560 /* important! make sure that we know if this is a native
561 mode domain or not. And that we can contact it. */
563 if ( winbindd_can_contact_domain( domain ) ) {
564 status = domain->backend->sequence_number(domain,
565 &domain->sequence_number);
566 } else {
567 /* just use the current time */
568 status = NT_STATUS_OK;
569 domain->sequence_number = time(NULL);
573 /* the above call could have set our domain->backend to NULL when
574 * coming from offline to online mode, make sure to reinitialize the
575 * backend - Guenther */
576 get_cache( domain );
578 if (!NT_STATUS_IS_OK(status)) {
579 DEBUG(10,("refresh_sequence_number: failed with %s\n", nt_errstr(status)));
580 domain->sequence_number = DOM_SEQUENCE_NONE;
583 domain->last_status = status;
584 domain->last_seq_check = time(NULL);
586 /* save the new sequence number in the cache */
587 store_cache_seqnum( domain );
589 done:
590 DEBUG(10, ("refresh_sequence_number: %s seq number is now %d\n",
591 domain->name, domain->sequence_number));
593 return;
597 decide if a cache entry has expired
599 static bool centry_expired(struct winbindd_domain *domain, const char *keystr, struct cache_entry *centry)
601 /* If we've been told to be offline - stay in that state... */
602 if (lp_winbind_offline_logon() && global_winbindd_offline_state) {
603 DEBUG(10,("centry_expired: Key %s for domain %s valid as winbindd is globally offline.\n",
604 keystr, domain->name ));
605 return false;
608 /* when the domain is offline return the cached entry.
609 * This deals with transient offline states... */
611 if (!domain->online) {
612 DEBUG(10,("centry_expired: Key %s for domain %s valid as domain is offline.\n",
613 keystr, domain->name ));
614 return false;
617 /* if the server is OK and our cache entry came from when it was down then
618 the entry is invalid */
619 if ((domain->sequence_number != DOM_SEQUENCE_NONE) &&
620 (centry->sequence_number == DOM_SEQUENCE_NONE)) {
621 DEBUG(10,("centry_expired: Key %s for domain %s invalid sequence.\n",
622 keystr, domain->name ));
623 return true;
626 /* if the server is down or the cache entry is not older than the
627 current sequence number then it is OK */
628 if (wcache_server_down(domain) ||
629 centry->sequence_number == domain->sequence_number) {
630 DEBUG(10,("centry_expired: Key %s for domain %s is good.\n",
631 keystr, domain->name ));
632 return false;
635 DEBUG(10,("centry_expired: Key %s for domain %s expired\n",
636 keystr, domain->name ));
638 /* it's expired */
639 return true;
642 static struct cache_entry *wcache_fetch_raw(char *kstr)
644 TDB_DATA data;
645 struct cache_entry *centry;
646 TDB_DATA key;
648 key = string_tdb_data(kstr);
649 data = tdb_fetch(wcache->tdb, key);
650 if (!data.dptr) {
651 /* a cache miss */
652 return NULL;
655 centry = SMB_XMALLOC_P(struct cache_entry);
656 centry->data = (unsigned char *)data.dptr;
657 centry->len = data.dsize;
658 centry->ofs = 0;
660 if (centry->len < 8) {
661 /* huh? corrupt cache? */
662 DEBUG(10,("wcache_fetch_raw: Corrupt cache for key %s (len < 8) ?\n", kstr));
663 centry_free(centry);
664 return NULL;
667 centry->status = centry_ntstatus(centry);
668 centry->sequence_number = centry_uint32(centry);
670 return centry;
673 static bool is_my_own_sam_domain(struct winbindd_domain *domain)
675 if (strequal(domain->name, get_global_sam_name()) &&
676 sid_equal(&domain->sid, get_global_sam_sid())) {
677 return true;
680 return false;
683 static bool is_builtin_domain(struct winbindd_domain *domain)
685 if (strequal(domain->name, "BUILTIN") &&
686 sid_equal(&domain->sid, &global_sid_Builtin)) {
687 return true;
690 return false;
694 fetch an entry from the cache, with a varargs key. auto-fetch the sequence
695 number and return status
697 static struct cache_entry *wcache_fetch(struct winbind_cache *cache,
698 struct winbindd_domain *domain,
699 const char *format, ...) PRINTF_ATTRIBUTE(3,4);
700 static struct cache_entry *wcache_fetch(struct winbind_cache *cache,
701 struct winbindd_domain *domain,
702 const char *format, ...)
704 va_list ap;
705 char *kstr;
706 struct cache_entry *centry;
708 if (!winbindd_use_cache() ||
709 is_my_own_sam_domain(domain) ||
710 is_builtin_domain(domain)) {
711 return NULL;
714 refresh_sequence_number(domain, false);
716 va_start(ap, format);
717 smb_xvasprintf(&kstr, format, ap);
718 va_end(ap);
720 centry = wcache_fetch_raw(kstr);
721 if (centry == NULL) {
722 free(kstr);
723 return NULL;
726 if (centry_expired(domain, kstr, centry)) {
728 DEBUG(10,("wcache_fetch: entry %s expired for domain %s\n",
729 kstr, domain->name ));
731 centry_free(centry);
732 free(kstr);
733 return NULL;
736 DEBUG(10,("wcache_fetch: returning entry %s for domain %s\n",
737 kstr, domain->name ));
739 free(kstr);
740 return centry;
743 static void wcache_delete(const char *format, ...) PRINTF_ATTRIBUTE(1,2);
744 static void wcache_delete(const char *format, ...)
746 va_list ap;
747 char *kstr;
748 TDB_DATA key;
750 va_start(ap, format);
751 smb_xvasprintf(&kstr, format, ap);
752 va_end(ap);
754 key = string_tdb_data(kstr);
756 tdb_delete(wcache->tdb, key);
757 free(kstr);
761 make sure we have at least len bytes available in a centry
763 static void centry_expand(struct cache_entry *centry, uint32 len)
765 if (centry->len - centry->ofs >= len)
766 return;
767 centry->len *= 2;
768 centry->data = SMB_REALLOC_ARRAY(centry->data, unsigned char,
769 centry->len);
770 if (!centry->data) {
771 DEBUG(0,("out of memory: needed %d bytes in centry_expand\n", centry->len));
772 smb_panic_fn("out of memory in centry_expand");
777 push a uint32 into a centry
779 static void centry_put_uint32(struct cache_entry *centry, uint32 v)
781 centry_expand(centry, 4);
782 SIVAL(centry->data, centry->ofs, v);
783 centry->ofs += 4;
787 push a uint16 into a centry
789 static void centry_put_uint16(struct cache_entry *centry, uint16 v)
791 centry_expand(centry, 2);
792 SIVAL(centry->data, centry->ofs, v);
793 centry->ofs += 2;
797 push a uint8 into a centry
799 static void centry_put_uint8(struct cache_entry *centry, uint8 v)
801 centry_expand(centry, 1);
802 SCVAL(centry->data, centry->ofs, v);
803 centry->ofs += 1;
807 push a string into a centry
809 static void centry_put_string(struct cache_entry *centry, const char *s)
811 int len;
813 if (!s) {
814 /* null strings are marked as len 0xFFFF */
815 centry_put_uint8(centry, 0xFF);
816 return;
819 len = strlen(s);
820 /* can't handle more than 254 char strings. Truncating is probably best */
821 if (len > 254) {
822 DEBUG(10,("centry_put_string: truncating len (%d) to: 254\n", len));
823 len = 254;
825 centry_put_uint8(centry, len);
826 centry_expand(centry, len);
827 memcpy(centry->data + centry->ofs, s, len);
828 centry->ofs += len;
832 push a 16 byte hash into a centry - treat as 16 byte string.
834 static void centry_put_hash16(struct cache_entry *centry, const uint8 val[16])
836 centry_put_uint8(centry, 16);
837 centry_expand(centry, 16);
838 memcpy(centry->data + centry->ofs, val, 16);
839 centry->ofs += 16;
842 static void centry_put_sid(struct cache_entry *centry, const struct dom_sid *sid)
844 fstring sid_string;
845 centry_put_string(centry, sid_to_fstring(sid_string, sid));
850 put NTSTATUS into a centry
852 static void centry_put_ntstatus(struct cache_entry *centry, NTSTATUS status)
854 uint32 status_value = NT_STATUS_V(status);
855 centry_put_uint32(centry, status_value);
860 push a NTTIME into a centry
862 static void centry_put_nttime(struct cache_entry *centry, NTTIME nt)
864 centry_expand(centry, 8);
865 SIVAL(centry->data, centry->ofs, nt & 0xFFFFFFFF);
866 centry->ofs += 4;
867 SIVAL(centry->data, centry->ofs, nt >> 32);
868 centry->ofs += 4;
872 push a time_t into a centry - use a 64 bit size.
873 NTTIME here is being used as a convenient 64-bit size.
875 static void centry_put_time(struct cache_entry *centry, time_t t)
877 NTTIME nt = (NTTIME)t;
878 centry_put_nttime(centry, nt);
882 start a centry for output. When finished, call centry_end()
884 struct cache_entry *centry_start(struct winbindd_domain *domain, NTSTATUS status)
886 struct cache_entry *centry;
888 if (!wcache->tdb)
889 return NULL;
891 centry = SMB_XMALLOC_P(struct cache_entry);
893 centry->len = 8192; /* reasonable default */
894 centry->data = SMB_XMALLOC_ARRAY(uint8, centry->len);
895 centry->ofs = 0;
896 centry->sequence_number = domain->sequence_number;
897 centry_put_ntstatus(centry, status);
898 centry_put_uint32(centry, centry->sequence_number);
899 return centry;
903 finish a centry and write it to the tdb
905 static void centry_end(struct cache_entry *centry, const char *format, ...) PRINTF_ATTRIBUTE(2,3);
906 static void centry_end(struct cache_entry *centry, const char *format, ...)
908 va_list ap;
909 char *kstr;
910 TDB_DATA key, data;
912 if (!winbindd_use_cache()) {
913 return;
916 va_start(ap, format);
917 smb_xvasprintf(&kstr, format, ap);
918 va_end(ap);
920 key = string_tdb_data(kstr);
921 data.dptr = centry->data;
922 data.dsize = centry->ofs;
924 tdb_store(wcache->tdb, key, data, TDB_REPLACE);
925 free(kstr);
928 static void wcache_save_name_to_sid(struct winbindd_domain *domain,
929 NTSTATUS status, const char *domain_name,
930 const char *name, const struct dom_sid *sid,
931 enum lsa_SidType type)
933 struct cache_entry *centry;
934 fstring uname;
936 centry = centry_start(domain, status);
937 if (!centry)
938 return;
939 centry_put_uint32(centry, type);
940 centry_put_sid(centry, sid);
941 fstrcpy(uname, name);
942 strupper_m(uname);
943 centry_end(centry, "NS/%s/%s", domain_name, uname);
944 DEBUG(10,("wcache_save_name_to_sid: %s\\%s -> %s (%s)\n", domain_name,
945 uname, sid_string_dbg(sid), nt_errstr(status)));
946 centry_free(centry);
949 static void wcache_save_sid_to_name(struct winbindd_domain *domain, NTSTATUS status,
950 const struct dom_sid *sid, const char *domain_name, const char *name, enum lsa_SidType type)
952 struct cache_entry *centry;
953 fstring sid_string;
955 centry = centry_start(domain, status);
956 if (!centry)
957 return;
959 if (NT_STATUS_IS_OK(status)) {
960 centry_put_uint32(centry, type);
961 centry_put_string(centry, domain_name);
962 centry_put_string(centry, name);
965 centry_end(centry, "SN/%s", sid_to_fstring(sid_string, sid));
966 DEBUG(10,("wcache_save_sid_to_name: %s -> %s (%s)\n", sid_string,
967 name, nt_errstr(status)));
968 centry_free(centry);
972 static void wcache_save_user(struct winbindd_domain *domain, NTSTATUS status,
973 struct wbint_userinfo *info)
975 struct cache_entry *centry;
976 fstring sid_string;
978 if (is_null_sid(&info->user_sid)) {
979 return;
982 centry = centry_start(domain, status);
983 if (!centry)
984 return;
985 centry_put_string(centry, info->acct_name);
986 centry_put_string(centry, info->full_name);
987 centry_put_string(centry, info->homedir);
988 centry_put_string(centry, info->shell);
989 centry_put_uint32(centry, info->primary_gid);
990 centry_put_sid(centry, &info->user_sid);
991 centry_put_sid(centry, &info->group_sid);
992 centry_end(centry, "U/%s", sid_to_fstring(sid_string,
993 &info->user_sid));
994 DEBUG(10,("wcache_save_user: %s (acct_name %s)\n", sid_string, info->acct_name));
995 centry_free(centry);
998 static void wcache_save_lockout_policy(struct winbindd_domain *domain,
999 NTSTATUS status,
1000 struct samr_DomInfo12 *lockout_policy)
1002 struct cache_entry *centry;
1004 centry = centry_start(domain, status);
1005 if (!centry)
1006 return;
1008 centry_put_nttime(centry, lockout_policy->lockout_duration);
1009 centry_put_nttime(centry, lockout_policy->lockout_window);
1010 centry_put_uint16(centry, lockout_policy->lockout_threshold);
1012 centry_end(centry, "LOC_POL/%s", domain->name);
1014 DEBUG(10,("wcache_save_lockout_policy: %s\n", domain->name));
1016 centry_free(centry);
1021 static void wcache_save_password_policy(struct winbindd_domain *domain,
1022 NTSTATUS status,
1023 struct samr_DomInfo1 *policy)
1025 struct cache_entry *centry;
1027 centry = centry_start(domain, status);
1028 if (!centry)
1029 return;
1031 centry_put_uint16(centry, policy->min_password_length);
1032 centry_put_uint16(centry, policy->password_history_length);
1033 centry_put_uint32(centry, policy->password_properties);
1034 centry_put_nttime(centry, policy->max_password_age);
1035 centry_put_nttime(centry, policy->min_password_age);
1037 centry_end(centry, "PWD_POL/%s", domain->name);
1039 DEBUG(10,("wcache_save_password_policy: %s\n", domain->name));
1041 centry_free(centry);
1044 /***************************************************************************
1045 ***************************************************************************/
1047 static void wcache_save_username_alias(struct winbindd_domain *domain,
1048 NTSTATUS status,
1049 const char *name, const char *alias)
1051 struct cache_entry *centry;
1052 fstring uname;
1054 if ( (centry = centry_start(domain, status)) == NULL )
1055 return;
1057 centry_put_string( centry, alias );
1059 fstrcpy(uname, name);
1060 strupper_m(uname);
1061 centry_end(centry, "NSS/NA/%s", uname);
1063 DEBUG(10,("wcache_save_username_alias: %s -> %s\n", name, alias ));
1065 centry_free(centry);
1068 static void wcache_save_alias_username(struct winbindd_domain *domain,
1069 NTSTATUS status,
1070 const char *alias, const char *name)
1072 struct cache_entry *centry;
1073 fstring uname;
1075 if ( (centry = centry_start(domain, status)) == NULL )
1076 return;
1078 centry_put_string( centry, name );
1080 fstrcpy(uname, alias);
1081 strupper_m(uname);
1082 centry_end(centry, "NSS/AN/%s", uname);
1084 DEBUG(10,("wcache_save_alias_username: %s -> %s\n", alias, name ));
1086 centry_free(centry);
1089 /***************************************************************************
1090 ***************************************************************************/
1092 NTSTATUS resolve_username_to_alias( TALLOC_CTX *mem_ctx,
1093 struct winbindd_domain *domain,
1094 const char *name, char **alias )
1096 struct winbind_cache *cache = get_cache(domain);
1097 struct cache_entry *centry = NULL;
1098 NTSTATUS status;
1099 char *upper_name;
1101 if ( domain->internal )
1102 return NT_STATUS_NOT_SUPPORTED;
1104 if (!cache->tdb)
1105 goto do_query;
1107 if ( (upper_name = SMB_STRDUP(name)) == NULL )
1108 return NT_STATUS_NO_MEMORY;
1109 strupper_m(upper_name);
1111 centry = wcache_fetch(cache, domain, "NSS/NA/%s", upper_name);
1113 SAFE_FREE( upper_name );
1115 if (!centry)
1116 goto do_query;
1118 status = centry->status;
1120 if (!NT_STATUS_IS_OK(status)) {
1121 centry_free(centry);
1122 return status;
1125 *alias = centry_string( centry, mem_ctx );
1127 centry_free(centry);
1129 DEBUG(10,("resolve_username_to_alias: [Cached] - mapped %s to %s\n",
1130 name, *alias ? *alias : "(none)"));
1132 return (*alias) ? NT_STATUS_OK : NT_STATUS_OBJECT_NAME_NOT_FOUND;
1134 do_query:
1136 /* If its not in cache and we are offline, then fail */
1138 if ( get_global_winbindd_state_offline() || !domain->online ) {
1139 DEBUG(8,("resolve_username_to_alias: rejecting query "
1140 "in offline mode\n"));
1141 return NT_STATUS_NOT_FOUND;
1144 status = nss_map_to_alias( mem_ctx, domain->name, name, alias );
1146 if ( NT_STATUS_IS_OK( status ) ) {
1147 wcache_save_username_alias(domain, status, name, *alias);
1150 if ( NT_STATUS_EQUAL( status, NT_STATUS_NONE_MAPPED ) ) {
1151 wcache_save_username_alias(domain, status, name, "(NULL)");
1154 DEBUG(5,("resolve_username_to_alias: backend query returned %s\n",
1155 nt_errstr(status)));
1157 if ( NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ) {
1158 set_domain_offline( domain );
1161 return status;
1164 /***************************************************************************
1165 ***************************************************************************/
1167 NTSTATUS resolve_alias_to_username( TALLOC_CTX *mem_ctx,
1168 struct winbindd_domain *domain,
1169 const char *alias, char **name )
1171 struct winbind_cache *cache = get_cache(domain);
1172 struct cache_entry *centry = NULL;
1173 NTSTATUS status;
1174 char *upper_name;
1176 if ( domain->internal )
1177 return NT_STATUS_NOT_SUPPORTED;
1179 if (!cache->tdb)
1180 goto do_query;
1182 if ( (upper_name = SMB_STRDUP(alias)) == NULL )
1183 return NT_STATUS_NO_MEMORY;
1184 strupper_m(upper_name);
1186 centry = wcache_fetch(cache, domain, "NSS/AN/%s", upper_name);
1188 SAFE_FREE( upper_name );
1190 if (!centry)
1191 goto do_query;
1193 status = centry->status;
1195 if (!NT_STATUS_IS_OK(status)) {
1196 centry_free(centry);
1197 return status;
1200 *name = centry_string( centry, mem_ctx );
1202 centry_free(centry);
1204 DEBUG(10,("resolve_alias_to_username: [Cached] - mapped %s to %s\n",
1205 alias, *name ? *name : "(none)"));
1207 return (*name) ? NT_STATUS_OK : NT_STATUS_OBJECT_NAME_NOT_FOUND;
1209 do_query:
1211 /* If its not in cache and we are offline, then fail */
1213 if ( get_global_winbindd_state_offline() || !domain->online ) {
1214 DEBUG(8,("resolve_alias_to_username: rejecting query "
1215 "in offline mode\n"));
1216 return NT_STATUS_NOT_FOUND;
1219 /* an alias cannot contain a domain prefix or '@' */
1221 if (strchr(alias, '\\') || strchr(alias, '@')) {
1222 DEBUG(10,("resolve_alias_to_username: skipping fully "
1223 "qualified name %s\n", alias));
1224 return NT_STATUS_OBJECT_NAME_INVALID;
1227 status = nss_map_from_alias( mem_ctx, domain->name, alias, name );
1229 if ( NT_STATUS_IS_OK( status ) ) {
1230 wcache_save_alias_username( domain, status, alias, *name );
1233 if (NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) {
1234 wcache_save_alias_username(domain, status, alias, "(NULL)");
1237 DEBUG(5,("resolve_alias_to_username: backend query returned %s\n",
1238 nt_errstr(status)));
1240 if ( NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ) {
1241 set_domain_offline( domain );
1244 return status;
1247 NTSTATUS wcache_cached_creds_exist(struct winbindd_domain *domain, const struct dom_sid *sid)
1249 struct winbind_cache *cache = get_cache(domain);
1250 TDB_DATA data;
1251 fstring key_str, tmp;
1252 uint32 rid;
1254 if (!cache->tdb) {
1255 return NT_STATUS_INTERNAL_DB_ERROR;
1258 if (is_null_sid(sid)) {
1259 return NT_STATUS_INVALID_SID;
1262 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1263 return NT_STATUS_INVALID_SID;
1266 fstr_sprintf(key_str, "CRED/%s", sid_to_fstring(tmp, sid));
1268 data = tdb_fetch(cache->tdb, string_tdb_data(key_str));
1269 if (!data.dptr) {
1270 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1273 SAFE_FREE(data.dptr);
1274 return NT_STATUS_OK;
1277 /* Lookup creds for a SID - copes with old (unsalted) creds as well
1278 as new salted ones. */
1280 NTSTATUS wcache_get_creds(struct winbindd_domain *domain,
1281 TALLOC_CTX *mem_ctx,
1282 const struct dom_sid *sid,
1283 const uint8 **cached_nt_pass,
1284 const uint8 **cached_salt)
1286 struct winbind_cache *cache = get_cache(domain);
1287 struct cache_entry *centry = NULL;
1288 NTSTATUS status;
1289 time_t t;
1290 uint32 rid;
1291 fstring tmp;
1293 if (!cache->tdb) {
1294 return NT_STATUS_INTERNAL_DB_ERROR;
1297 if (is_null_sid(sid)) {
1298 return NT_STATUS_INVALID_SID;
1301 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1302 return NT_STATUS_INVALID_SID;
1305 /* Try and get a salted cred first. If we can't
1306 fall back to an unsalted cred. */
1308 centry = wcache_fetch(cache, domain, "CRED/%s",
1309 sid_to_fstring(tmp, sid));
1310 if (!centry) {
1311 DEBUG(10,("wcache_get_creds: entry for [CRED/%s] not found\n",
1312 sid_string_dbg(sid)));
1313 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1316 t = centry_time(centry);
1318 /* In the salted case this isn't actually the nt_hash itself,
1319 but the MD5 of the salt + nt_hash. Let the caller
1320 sort this out. It can tell as we only return the cached_salt
1321 if we are returning a salted cred. */
1323 *cached_nt_pass = (const uint8 *)centry_hash16(centry, mem_ctx);
1324 if (*cached_nt_pass == NULL) {
1325 fstring sidstr;
1327 sid_to_fstring(sidstr, sid);
1329 /* Bad (old) cred cache. Delete and pretend we
1330 don't have it. */
1331 DEBUG(0,("wcache_get_creds: bad entry for [CRED/%s] - deleting\n",
1332 sidstr));
1333 wcache_delete("CRED/%s", sidstr);
1334 centry_free(centry);
1335 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1338 /* We only have 17 bytes more data in the salted cred case. */
1339 if (centry->len - centry->ofs == 17) {
1340 *cached_salt = (const uint8 *)centry_hash16(centry, mem_ctx);
1341 } else {
1342 *cached_salt = NULL;
1345 dump_data_pw("cached_nt_pass", *cached_nt_pass, NT_HASH_LEN);
1346 if (*cached_salt) {
1347 dump_data_pw("cached_salt", *cached_salt, NT_HASH_LEN);
1350 status = centry->status;
1352 DEBUG(10,("wcache_get_creds: [Cached] - cached creds for user %s status: %s\n",
1353 sid_string_dbg(sid), nt_errstr(status) ));
1355 centry_free(centry);
1356 return status;
1359 /* Store creds for a SID - only writes out new salted ones. */
1361 NTSTATUS wcache_save_creds(struct winbindd_domain *domain,
1362 TALLOC_CTX *mem_ctx,
1363 const struct dom_sid *sid,
1364 const uint8 nt_pass[NT_HASH_LEN])
1366 struct cache_entry *centry;
1367 fstring sid_string;
1368 uint32 rid;
1369 uint8 cred_salt[NT_HASH_LEN];
1370 uint8 salted_hash[NT_HASH_LEN];
1372 if (is_null_sid(sid)) {
1373 return NT_STATUS_INVALID_SID;
1376 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1377 return NT_STATUS_INVALID_SID;
1380 centry = centry_start(domain, NT_STATUS_OK);
1381 if (!centry) {
1382 return NT_STATUS_INTERNAL_DB_ERROR;
1385 dump_data_pw("nt_pass", nt_pass, NT_HASH_LEN);
1387 centry_put_time(centry, time(NULL));
1389 /* Create a salt and then salt the hash. */
1390 generate_random_buffer(cred_salt, NT_HASH_LEN);
1391 E_md5hash(cred_salt, nt_pass, salted_hash);
1393 centry_put_hash16(centry, salted_hash);
1394 centry_put_hash16(centry, cred_salt);
1395 centry_end(centry, "CRED/%s", sid_to_fstring(sid_string, sid));
1397 DEBUG(10,("wcache_save_creds: %s\n", sid_string));
1399 centry_free(centry);
1401 return NT_STATUS_OK;
1405 /* Query display info. This is the basic user list fn */
1406 static NTSTATUS query_user_list(struct winbindd_domain *domain,
1407 TALLOC_CTX *mem_ctx,
1408 uint32 *num_entries,
1409 struct wbint_userinfo **info)
1411 struct winbind_cache *cache = get_cache(domain);
1412 struct cache_entry *centry = NULL;
1413 NTSTATUS status;
1414 unsigned int i, retry;
1415 bool old_status = domain->online;
1417 if (!cache->tdb)
1418 goto do_query;
1420 centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
1421 if (!centry)
1422 goto do_query;
1424 do_fetch_cache:
1425 *num_entries = centry_uint32(centry);
1427 if (*num_entries == 0)
1428 goto do_cached;
1430 (*info) = TALLOC_ARRAY(mem_ctx, struct wbint_userinfo, *num_entries);
1431 if (! (*info)) {
1432 smb_panic_fn("query_user_list out of memory");
1434 for (i=0; i<(*num_entries); i++) {
1435 (*info)[i].acct_name = centry_string(centry, mem_ctx);
1436 (*info)[i].full_name = centry_string(centry, mem_ctx);
1437 (*info)[i].homedir = centry_string(centry, mem_ctx);
1438 (*info)[i].shell = centry_string(centry, mem_ctx);
1439 centry_sid(centry, &(*info)[i].user_sid);
1440 centry_sid(centry, &(*info)[i].group_sid);
1443 do_cached:
1444 status = centry->status;
1446 DEBUG(10,("query_user_list: [Cached] - cached list for domain %s status: %s\n",
1447 domain->name, nt_errstr(status) ));
1449 centry_free(centry);
1450 return status;
1452 do_query:
1453 *num_entries = 0;
1454 *info = NULL;
1456 /* Return status value returned by seq number check */
1458 if (!NT_STATUS_IS_OK(domain->last_status))
1459 return domain->last_status;
1461 /* Put the query_user_list() in a retry loop. There appears to be
1462 * some bug either with Windows 2000 or Samba's handling of large
1463 * rpc replies. This manifests itself as sudden disconnection
1464 * at a random point in the enumeration of a large (60k) user list.
1465 * The retry loop simply tries the operation again. )-: It's not
1466 * pretty but an acceptable workaround until we work out what the
1467 * real problem is. */
1469 retry = 0;
1470 do {
1472 DEBUG(10,("query_user_list: [Cached] - doing backend query for list for domain %s\n",
1473 domain->name ));
1475 status = domain->backend->query_user_list(domain, mem_ctx, num_entries, info);
1476 if (!NT_STATUS_IS_OK(status)) {
1477 DEBUG(3, ("query_user_list: returned 0x%08x, "
1478 "retrying\n", NT_STATUS_V(status)));
1480 if (NT_STATUS_EQUAL(status, NT_STATUS_UNSUCCESSFUL)) {
1481 DEBUG(3, ("query_user_list: flushing "
1482 "connection cache\n"));
1483 invalidate_cm_connection(&domain->conn);
1485 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1486 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1487 if (!domain->internal && old_status) {
1488 set_domain_offline(domain);
1490 /* store partial response. */
1491 if (*num_entries > 0) {
1493 * humm, what about the status used for cache?
1494 * Should it be NT_STATUS_OK?
1496 break;
1499 * domain is offline now, and there is no user entries,
1500 * try to fetch from cache again.
1502 if (cache->tdb && !domain->online && !domain->internal && old_status) {
1503 centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
1504 /* partial response... */
1505 if (!centry) {
1506 goto skip_save;
1507 } else {
1508 goto do_fetch_cache;
1510 } else {
1511 goto skip_save;
1515 } while (NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_UNSUCCESSFUL) &&
1516 (retry++ < 5));
1518 /* and save it */
1519 refresh_sequence_number(domain, false);
1520 if (!NT_STATUS_IS_OK(status)) {
1521 return status;
1523 centry = centry_start(domain, status);
1524 if (!centry)
1525 goto skip_save;
1526 centry_put_uint32(centry, *num_entries);
1527 for (i=0; i<(*num_entries); i++) {
1528 centry_put_string(centry, (*info)[i].acct_name);
1529 centry_put_string(centry, (*info)[i].full_name);
1530 centry_put_string(centry, (*info)[i].homedir);
1531 centry_put_string(centry, (*info)[i].shell);
1532 centry_put_sid(centry, &(*info)[i].user_sid);
1533 centry_put_sid(centry, &(*info)[i].group_sid);
1534 if (domain->backend && domain->backend->consistent) {
1535 /* when the backend is consistent we can pre-prime some mappings */
1536 wcache_save_name_to_sid(domain, NT_STATUS_OK,
1537 domain->name,
1538 (*info)[i].acct_name,
1539 &(*info)[i].user_sid,
1540 SID_NAME_USER);
1541 wcache_save_sid_to_name(domain, NT_STATUS_OK,
1542 &(*info)[i].user_sid,
1543 domain->name,
1544 (*info)[i].acct_name,
1545 SID_NAME_USER);
1546 wcache_save_user(domain, NT_STATUS_OK, &(*info)[i]);
1549 centry_end(centry, "UL/%s", domain->name);
1550 centry_free(centry);
1552 skip_save:
1553 return status;
1556 /* list all domain groups */
1557 static NTSTATUS enum_dom_groups(struct winbindd_domain *domain,
1558 TALLOC_CTX *mem_ctx,
1559 uint32 *num_entries,
1560 struct acct_info **info)
1562 struct winbind_cache *cache = get_cache(domain);
1563 struct cache_entry *centry = NULL;
1564 NTSTATUS status;
1565 unsigned int i;
1566 bool old_status;
1568 old_status = domain->online;
1569 if (!cache->tdb)
1570 goto do_query;
1572 centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
1573 if (!centry)
1574 goto do_query;
1576 do_fetch_cache:
1577 *num_entries = centry_uint32(centry);
1579 if (*num_entries == 0)
1580 goto do_cached;
1582 (*info) = TALLOC_ARRAY(mem_ctx, struct acct_info, *num_entries);
1583 if (! (*info)) {
1584 smb_panic_fn("enum_dom_groups out of memory");
1586 for (i=0; i<(*num_entries); i++) {
1587 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1588 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1589 (*info)[i].rid = centry_uint32(centry);
1592 do_cached:
1593 status = centry->status;
1595 DEBUG(10,("enum_dom_groups: [Cached] - cached list for domain %s status: %s\n",
1596 domain->name, nt_errstr(status) ));
1598 centry_free(centry);
1599 return status;
1601 do_query:
1602 *num_entries = 0;
1603 *info = NULL;
1605 /* Return status value returned by seq number check */
1607 if (!NT_STATUS_IS_OK(domain->last_status))
1608 return domain->last_status;
1610 DEBUG(10,("enum_dom_groups: [Cached] - doing backend query for list for domain %s\n",
1611 domain->name ));
1613 status = domain->backend->enum_dom_groups(domain, mem_ctx, num_entries, info);
1615 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1616 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1617 if (!domain->internal && old_status) {
1618 set_domain_offline(domain);
1620 if (cache->tdb &&
1621 !domain->online &&
1622 !domain->internal &&
1623 old_status) {
1624 centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
1625 if (centry) {
1626 goto do_fetch_cache;
1630 /* and save it */
1631 refresh_sequence_number(domain, false);
1632 if (!NT_STATUS_IS_OK(status)) {
1633 return status;
1635 centry = centry_start(domain, status);
1636 if (!centry)
1637 goto skip_save;
1638 centry_put_uint32(centry, *num_entries);
1639 for (i=0; i<(*num_entries); i++) {
1640 centry_put_string(centry, (*info)[i].acct_name);
1641 centry_put_string(centry, (*info)[i].acct_desc);
1642 centry_put_uint32(centry, (*info)[i].rid);
1644 centry_end(centry, "GL/%s/domain", domain->name);
1645 centry_free(centry);
1647 skip_save:
1648 return status;
1651 /* list all domain groups */
1652 static NTSTATUS enum_local_groups(struct winbindd_domain *domain,
1653 TALLOC_CTX *mem_ctx,
1654 uint32 *num_entries,
1655 struct acct_info **info)
1657 struct winbind_cache *cache = get_cache(domain);
1658 struct cache_entry *centry = NULL;
1659 NTSTATUS status;
1660 unsigned int i;
1661 bool old_status;
1663 old_status = domain->online;
1664 if (!cache->tdb)
1665 goto do_query;
1667 centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
1668 if (!centry)
1669 goto do_query;
1671 do_fetch_cache:
1672 *num_entries = centry_uint32(centry);
1674 if (*num_entries == 0)
1675 goto do_cached;
1677 (*info) = TALLOC_ARRAY(mem_ctx, struct acct_info, *num_entries);
1678 if (! (*info)) {
1679 smb_panic_fn("enum_dom_groups out of memory");
1681 for (i=0; i<(*num_entries); i++) {
1682 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1683 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1684 (*info)[i].rid = centry_uint32(centry);
1687 do_cached:
1689 /* If we are returning cached data and the domain controller
1690 is down then we don't know whether the data is up to date
1691 or not. Return NT_STATUS_MORE_PROCESSING_REQUIRED to
1692 indicate this. */
1694 if (wcache_server_down(domain)) {
1695 DEBUG(10, ("enum_local_groups: returning cached user list and server was down\n"));
1696 status = NT_STATUS_MORE_PROCESSING_REQUIRED;
1697 } else
1698 status = centry->status;
1700 DEBUG(10,("enum_local_groups: [Cached] - cached list for domain %s status: %s\n",
1701 domain->name, nt_errstr(status) ));
1703 centry_free(centry);
1704 return status;
1706 do_query:
1707 *num_entries = 0;
1708 *info = NULL;
1710 /* Return status value returned by seq number check */
1712 if (!NT_STATUS_IS_OK(domain->last_status))
1713 return domain->last_status;
1715 DEBUG(10,("enum_local_groups: [Cached] - doing backend query for list for domain %s\n",
1716 domain->name ));
1718 status = domain->backend->enum_local_groups(domain, mem_ctx, num_entries, info);
1720 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1721 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1722 if (!domain->internal && old_status) {
1723 set_domain_offline(domain);
1725 if (cache->tdb &&
1726 !domain->internal &&
1727 !domain->online &&
1728 old_status) {
1729 centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
1730 if (centry) {
1731 goto do_fetch_cache;
1735 /* and save it */
1736 refresh_sequence_number(domain, false);
1737 if (!NT_STATUS_IS_OK(status)) {
1738 return status;
1740 centry = centry_start(domain, status);
1741 if (!centry)
1742 goto skip_save;
1743 centry_put_uint32(centry, *num_entries);
1744 for (i=0; i<(*num_entries); i++) {
1745 centry_put_string(centry, (*info)[i].acct_name);
1746 centry_put_string(centry, (*info)[i].acct_desc);
1747 centry_put_uint32(centry, (*info)[i].rid);
1749 centry_end(centry, "GL/%s/local", domain->name);
1750 centry_free(centry);
1752 skip_save:
1753 return status;
1756 NTSTATUS wcache_name_to_sid(struct winbindd_domain *domain,
1757 const char *domain_name,
1758 const char *name,
1759 struct dom_sid *sid,
1760 enum lsa_SidType *type)
1762 struct winbind_cache *cache = get_cache(domain);
1763 struct cache_entry *centry;
1764 NTSTATUS status;
1765 char *uname;
1767 if (cache->tdb == NULL) {
1768 return NT_STATUS_NOT_FOUND;
1771 uname = talloc_strdup_upper(talloc_tos(), name);
1772 if (uname == NULL) {
1773 return NT_STATUS_NO_MEMORY;
1776 centry = wcache_fetch(cache, domain, "NS/%s/%s", domain_name, uname);
1777 TALLOC_FREE(uname);
1778 if (centry == NULL) {
1779 return NT_STATUS_NOT_FOUND;
1782 status = centry->status;
1783 if (NT_STATUS_IS_OK(status)) {
1784 *type = (enum lsa_SidType)centry_uint32(centry);
1785 centry_sid(centry, sid);
1788 DEBUG(10,("name_to_sid: [Cached] - cached name for domain %s status: "
1789 "%s\n", domain->name, nt_errstr(status) ));
1791 centry_free(centry);
1792 return status;
1795 /* convert a single name to a sid in a domain */
1796 static NTSTATUS name_to_sid(struct winbindd_domain *domain,
1797 TALLOC_CTX *mem_ctx,
1798 const char *domain_name,
1799 const char *name,
1800 uint32_t flags,
1801 struct dom_sid *sid,
1802 enum lsa_SidType *type)
1804 NTSTATUS status;
1805 bool old_status;
1807 old_status = domain->online;
1809 status = wcache_name_to_sid(domain, domain_name, name, sid, type);
1810 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
1811 return status;
1814 ZERO_STRUCTP(sid);
1816 /* If the seq number check indicated that there is a problem
1817 * with this DC, then return that status... except for
1818 * access_denied. This is special because the dc may be in
1819 * "restrict anonymous = 1" mode, in which case it will deny
1820 * most unauthenticated operations, but *will* allow the LSA
1821 * name-to-sid that we try as a fallback. */
1823 if (!(NT_STATUS_IS_OK(domain->last_status)
1824 || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1825 return domain->last_status;
1827 DEBUG(10,("name_to_sid: [Cached] - doing backend query for name for domain %s\n",
1828 domain->name ));
1830 status = domain->backend->name_to_sid(domain, mem_ctx, domain_name,
1831 name, flags, sid, type);
1833 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1834 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1835 if (!domain->internal && old_status) {
1836 set_domain_offline(domain);
1838 if (!domain->internal &&
1839 !domain->online &&
1840 old_status) {
1841 NTSTATUS cache_status;
1842 cache_status = wcache_name_to_sid(domain, domain_name, name, sid, type);
1843 return cache_status;
1846 /* and save it */
1847 refresh_sequence_number(domain, false);
1849 if (domain->online &&
1850 (NT_STATUS_IS_OK(status) || NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED))) {
1851 wcache_save_name_to_sid(domain, status, domain_name, name, sid, *type);
1853 /* Only save the reverse mapping if this was not a UPN */
1854 if (!strchr(name, '@')) {
1855 strupper_m(CONST_DISCARD(char *,domain_name));
1856 strlower_m(CONST_DISCARD(char *,name));
1857 wcache_save_sid_to_name(domain, status, sid, domain_name, name, *type);
1861 return status;
1864 NTSTATUS wcache_sid_to_name(struct winbindd_domain *domain,
1865 const struct dom_sid *sid,
1866 TALLOC_CTX *mem_ctx,
1867 char **domain_name,
1868 char **name,
1869 enum lsa_SidType *type)
1871 struct winbind_cache *cache = get_cache(domain);
1872 struct cache_entry *centry;
1873 char *sid_string;
1874 NTSTATUS status;
1876 if (cache->tdb == NULL) {
1877 return NT_STATUS_NOT_FOUND;
1880 sid_string = sid_string_tos(sid);
1881 if (sid_string == NULL) {
1882 return NT_STATUS_NO_MEMORY;
1885 centry = wcache_fetch(cache, domain, "SN/%s", sid_string);
1886 TALLOC_FREE(sid_string);
1887 if (centry == NULL) {
1888 return NT_STATUS_NOT_FOUND;
1891 if (NT_STATUS_IS_OK(centry->status)) {
1892 *type = (enum lsa_SidType)centry_uint32(centry);
1893 *domain_name = centry_string(centry, mem_ctx);
1894 *name = centry_string(centry, mem_ctx);
1897 status = centry->status;
1898 centry_free(centry);
1900 DEBUG(10,("sid_to_name: [Cached] - cached name for domain %s status: "
1901 "%s\n", domain->name, nt_errstr(status) ));
1903 return status;
1906 /* convert a sid to a user or group name. The sid is guaranteed to be in the domain
1907 given */
1908 static NTSTATUS sid_to_name(struct winbindd_domain *domain,
1909 TALLOC_CTX *mem_ctx,
1910 const struct dom_sid *sid,
1911 char **domain_name,
1912 char **name,
1913 enum lsa_SidType *type)
1915 NTSTATUS status;
1916 bool old_status;
1918 old_status = domain->online;
1919 status = wcache_sid_to_name(domain, sid, mem_ctx, domain_name, name,
1920 type);
1921 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
1922 return status;
1925 *name = NULL;
1926 *domain_name = NULL;
1928 /* If the seq number check indicated that there is a problem
1929 * with this DC, then return that status... except for
1930 * access_denied. This is special because the dc may be in
1931 * "restrict anonymous = 1" mode, in which case it will deny
1932 * most unauthenticated operations, but *will* allow the LSA
1933 * sid-to-name that we try as a fallback. */
1935 if (!(NT_STATUS_IS_OK(domain->last_status)
1936 || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1937 return domain->last_status;
1939 DEBUG(10,("sid_to_name: [Cached] - doing backend query for name for domain %s\n",
1940 domain->name ));
1942 status = domain->backend->sid_to_name(domain, mem_ctx, sid, domain_name, name, type);
1944 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1945 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1946 if (!domain->internal && old_status) {
1947 set_domain_offline(domain);
1949 if (!domain->internal &&
1950 !domain->online &&
1951 old_status) {
1952 NTSTATUS cache_status;
1953 cache_status = wcache_sid_to_name(domain, sid, mem_ctx,
1954 domain_name, name, type);
1955 return cache_status;
1958 /* and save it */
1959 refresh_sequence_number(domain, false);
1960 if (!NT_STATUS_IS_OK(status)) {
1961 return status;
1963 wcache_save_sid_to_name(domain, status, sid, *domain_name, *name, *type);
1965 /* We can't save the name to sid mapping here, as with sid history a
1966 * later name2sid would give the wrong sid. */
1968 return status;
1971 static NTSTATUS rids_to_names(struct winbindd_domain *domain,
1972 TALLOC_CTX *mem_ctx,
1973 const struct dom_sid *domain_sid,
1974 uint32 *rids,
1975 size_t num_rids,
1976 char **domain_name,
1977 char ***names,
1978 enum lsa_SidType **types)
1980 struct winbind_cache *cache = get_cache(domain);
1981 size_t i;
1982 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
1983 bool have_mapped;
1984 bool have_unmapped;
1985 bool old_status;
1987 old_status = domain->online;
1988 *domain_name = NULL;
1989 *names = NULL;
1990 *types = NULL;
1992 if (!cache->tdb) {
1993 goto do_query;
1996 if (num_rids == 0) {
1997 return NT_STATUS_OK;
2000 *names = TALLOC_ARRAY(mem_ctx, char *, num_rids);
2001 *types = TALLOC_ARRAY(mem_ctx, enum lsa_SidType, num_rids);
2003 if ((*names == NULL) || (*types == NULL)) {
2004 result = NT_STATUS_NO_MEMORY;
2005 goto error;
2008 have_mapped = have_unmapped = false;
2010 for (i=0; i<num_rids; i++) {
2011 struct dom_sid sid;
2012 struct cache_entry *centry;
2013 fstring tmp;
2015 if (!sid_compose(&sid, domain_sid, rids[i])) {
2016 result = NT_STATUS_INTERNAL_ERROR;
2017 goto error;
2020 centry = wcache_fetch(cache, domain, "SN/%s",
2021 sid_to_fstring(tmp, &sid));
2022 if (!centry) {
2023 goto do_query;
2026 (*types)[i] = SID_NAME_UNKNOWN;
2027 (*names)[i] = talloc_strdup(*names, "");
2029 if (NT_STATUS_IS_OK(centry->status)) {
2030 char *dom;
2031 have_mapped = true;
2032 (*types)[i] = (enum lsa_SidType)centry_uint32(centry);
2034 dom = centry_string(centry, mem_ctx);
2035 if (*domain_name == NULL) {
2036 *domain_name = dom;
2037 } else {
2038 talloc_free(dom);
2041 (*names)[i] = centry_string(centry, *names);
2043 } else if (NT_STATUS_EQUAL(centry->status, NT_STATUS_NONE_MAPPED)) {
2044 have_unmapped = true;
2046 } else {
2047 /* something's definitely wrong */
2048 result = centry->status;
2049 goto error;
2052 centry_free(centry);
2055 if (!have_mapped) {
2056 return NT_STATUS_NONE_MAPPED;
2058 if (!have_unmapped) {
2059 return NT_STATUS_OK;
2061 return STATUS_SOME_UNMAPPED;
2063 do_query:
2065 TALLOC_FREE(*names);
2066 TALLOC_FREE(*types);
2068 result = domain->backend->rids_to_names(domain, mem_ctx, domain_sid,
2069 rids, num_rids, domain_name,
2070 names, types);
2072 if (NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
2073 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2074 if (!domain->internal && old_status) {
2075 set_domain_offline(domain);
2077 if (cache->tdb &&
2078 !domain->internal &&
2079 !domain->online &&
2080 old_status) {
2081 have_mapped = have_unmapped = false;
2083 for (i=0; i<num_rids; i++) {
2084 struct dom_sid sid;
2085 struct cache_entry *centry;
2086 fstring tmp;
2088 if (!sid_compose(&sid, domain_sid, rids[i])) {
2089 result = NT_STATUS_INTERNAL_ERROR;
2090 goto error;
2093 centry = wcache_fetch(cache, domain, "SN/%s",
2094 sid_to_fstring(tmp, &sid));
2095 if (!centry) {
2096 (*types)[i] = SID_NAME_UNKNOWN;
2097 (*names)[i] = talloc_strdup(*names, "");
2098 continue;
2101 (*types)[i] = SID_NAME_UNKNOWN;
2102 (*names)[i] = talloc_strdup(*names, "");
2104 if (NT_STATUS_IS_OK(centry->status)) {
2105 char *dom;
2106 have_mapped = true;
2107 (*types)[i] = (enum lsa_SidType)centry_uint32(centry);
2109 dom = centry_string(centry, mem_ctx);
2110 if (*domain_name == NULL) {
2111 *domain_name = dom;
2112 } else {
2113 talloc_free(dom);
2116 (*names)[i] = centry_string(centry, *names);
2118 } else if (NT_STATUS_EQUAL(centry->status, NT_STATUS_NONE_MAPPED)) {
2119 have_unmapped = true;
2121 } else {
2122 /* something's definitely wrong */
2123 result = centry->status;
2124 goto error;
2127 centry_free(centry);
2130 if (!have_mapped) {
2131 return NT_STATUS_NONE_MAPPED;
2133 if (!have_unmapped) {
2134 return NT_STATUS_OK;
2136 return STATUS_SOME_UNMAPPED;
2140 None of the queried rids has been found so save all negative entries
2142 if (NT_STATUS_EQUAL(result, NT_STATUS_NONE_MAPPED)) {
2143 for (i = 0; i < num_rids; i++) {
2144 struct dom_sid sid;
2145 const char *name = "";
2146 const enum lsa_SidType type = SID_NAME_UNKNOWN;
2147 NTSTATUS status = NT_STATUS_NONE_MAPPED;
2149 if (!sid_compose(&sid, domain_sid, rids[i])) {
2150 return NT_STATUS_INTERNAL_ERROR;
2153 wcache_save_sid_to_name(domain, status, &sid, *domain_name,
2154 name, type);
2157 return result;
2161 Some or all of the queried rids have been found.
2163 if (!NT_STATUS_IS_OK(result) &&
2164 !NT_STATUS_EQUAL(result, STATUS_SOME_UNMAPPED)) {
2165 return result;
2168 refresh_sequence_number(domain, false);
2170 for (i=0; i<num_rids; i++) {
2171 struct dom_sid sid;
2172 NTSTATUS status;
2174 if (!sid_compose(&sid, domain_sid, rids[i])) {
2175 result = NT_STATUS_INTERNAL_ERROR;
2176 goto error;
2179 status = (*types)[i] == SID_NAME_UNKNOWN ?
2180 NT_STATUS_NONE_MAPPED : NT_STATUS_OK;
2182 wcache_save_sid_to_name(domain, status, &sid, *domain_name,
2183 (*names)[i], (*types)[i]);
2186 return result;
2188 error:
2189 TALLOC_FREE(*names);
2190 TALLOC_FREE(*types);
2191 return result;
2194 NTSTATUS wcache_query_user(struct winbindd_domain *domain,
2195 TALLOC_CTX *mem_ctx,
2196 const struct dom_sid *user_sid,
2197 struct wbint_userinfo *info)
2199 struct winbind_cache *cache = get_cache(domain);
2200 struct cache_entry *centry = NULL;
2201 NTSTATUS status;
2202 char *sid_string;
2204 if (cache->tdb == NULL) {
2205 return NT_STATUS_NOT_FOUND;
2208 sid_string = sid_string_tos(user_sid);
2209 if (sid_string == NULL) {
2210 return NT_STATUS_NO_MEMORY;
2213 centry = wcache_fetch(cache, domain, "U/%s", sid_string);
2214 TALLOC_FREE(sid_string);
2215 if (centry == NULL) {
2216 return NT_STATUS_NOT_FOUND;
2220 * If we have an access denied cache entry and a cached info3
2221 * in the samlogon cache then do a query. This will force the
2222 * rpc back end to return the info3 data.
2225 if (NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED) &&
2226 netsamlogon_cache_have(user_sid)) {
2227 DEBUG(10, ("query_user: cached access denied and have cached "
2228 "info3\n"));
2229 domain->last_status = NT_STATUS_OK;
2230 centry_free(centry);
2231 return NT_STATUS_NOT_FOUND;
2234 /* if status is not ok then this is a negative hit
2235 and the rest of the data doesn't matter */
2236 status = centry->status;
2237 if (NT_STATUS_IS_OK(status)) {
2238 info->acct_name = centry_string(centry, mem_ctx);
2239 info->full_name = centry_string(centry, mem_ctx);
2240 info->homedir = centry_string(centry, mem_ctx);
2241 info->shell = centry_string(centry, mem_ctx);
2242 info->primary_gid = centry_uint32(centry);
2243 centry_sid(centry, &info->user_sid);
2244 centry_sid(centry, &info->group_sid);
2247 DEBUG(10,("query_user: [Cached] - cached info for domain %s status: "
2248 "%s\n", domain->name, nt_errstr(status) ));
2250 centry_free(centry);
2251 return status;
2254 /* Lookup user information from a rid */
2255 static NTSTATUS query_user(struct winbindd_domain *domain,
2256 TALLOC_CTX *mem_ctx,
2257 const struct dom_sid *user_sid,
2258 struct wbint_userinfo *info)
2260 NTSTATUS status;
2261 bool old_status;
2263 old_status = domain->online;
2264 status = wcache_query_user(domain, mem_ctx, user_sid, info);
2265 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2266 return status;
2269 ZERO_STRUCTP(info);
2271 /* Return status value returned by seq number check */
2273 if (!NT_STATUS_IS_OK(domain->last_status))
2274 return domain->last_status;
2276 DEBUG(10,("query_user: [Cached] - doing backend query for info for domain %s\n",
2277 domain->name ));
2279 status = domain->backend->query_user(domain, mem_ctx, user_sid, info);
2281 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2282 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2283 if (!domain->internal && old_status) {
2284 set_domain_offline(domain);
2286 if (!domain->internal &&
2287 !domain->online &&
2288 old_status) {
2289 NTSTATUS cache_status;
2290 cache_status = wcache_query_user(domain, mem_ctx, user_sid, info);
2291 return cache_status;
2294 /* and save it */
2295 refresh_sequence_number(domain, false);
2296 if (!NT_STATUS_IS_OK(status)) {
2297 return status;
2299 wcache_save_user(domain, status, info);
2301 return status;
2304 NTSTATUS wcache_lookup_usergroups(struct winbindd_domain *domain,
2305 TALLOC_CTX *mem_ctx,
2306 const struct dom_sid *user_sid,
2307 uint32_t *pnum_sids,
2308 struct dom_sid **psids)
2310 struct winbind_cache *cache = get_cache(domain);
2311 struct cache_entry *centry = NULL;
2312 NTSTATUS status;
2313 uint32_t i, num_sids;
2314 struct dom_sid *sids;
2315 fstring sid_string;
2317 if (cache->tdb == NULL) {
2318 return NT_STATUS_NOT_FOUND;
2321 centry = wcache_fetch(cache, domain, "UG/%s",
2322 sid_to_fstring(sid_string, user_sid));
2323 if (centry == NULL) {
2324 return NT_STATUS_NOT_FOUND;
2327 /* If we have an access denied cache entry and a cached info3 in the
2328 samlogon cache then do a query. This will force the rpc back end
2329 to return the info3 data. */
2331 if (NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)
2332 && netsamlogon_cache_have(user_sid)) {
2333 DEBUG(10, ("lookup_usergroups: cached access denied and have "
2334 "cached info3\n"));
2335 domain->last_status = NT_STATUS_OK;
2336 centry_free(centry);
2337 return NT_STATUS_NOT_FOUND;
2340 num_sids = centry_uint32(centry);
2341 sids = talloc_array(mem_ctx, struct dom_sid, num_sids);
2342 if (sids == NULL) {
2343 centry_free(centry);
2344 return NT_STATUS_NO_MEMORY;
2347 for (i=0; i<num_sids; i++) {
2348 centry_sid(centry, &sids[i]);
2351 status = centry->status;
2353 DEBUG(10,("lookup_usergroups: [Cached] - cached info for domain %s "
2354 "status: %s\n", domain->name, nt_errstr(status)));
2356 centry_free(centry);
2358 *pnum_sids = num_sids;
2359 *psids = sids;
2360 return status;
2363 /* Lookup groups a user is a member of. */
2364 static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
2365 TALLOC_CTX *mem_ctx,
2366 const struct dom_sid *user_sid,
2367 uint32 *num_groups, struct dom_sid **user_gids)
2369 struct cache_entry *centry = NULL;
2370 NTSTATUS status;
2371 unsigned int i;
2372 fstring sid_string;
2373 bool old_status;
2375 old_status = domain->online;
2376 status = wcache_lookup_usergroups(domain, mem_ctx, user_sid,
2377 num_groups, user_gids);
2378 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2379 return status;
2382 (*num_groups) = 0;
2383 (*user_gids) = NULL;
2385 /* Return status value returned by seq number check */
2387 if (!NT_STATUS_IS_OK(domain->last_status))
2388 return domain->last_status;
2390 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info for domain %s\n",
2391 domain->name ));
2393 status = domain->backend->lookup_usergroups(domain, mem_ctx, user_sid, num_groups, user_gids);
2395 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2396 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2397 if (!domain->internal && old_status) {
2398 set_domain_offline(domain);
2400 if (!domain->internal &&
2401 !domain->online &&
2402 old_status) {
2403 NTSTATUS cache_status;
2404 cache_status = wcache_lookup_usergroups(domain, mem_ctx, user_sid,
2405 num_groups, user_gids);
2406 return cache_status;
2409 if ( NT_STATUS_EQUAL(status, NT_STATUS_SYNCHRONIZATION_REQUIRED) )
2410 goto skip_save;
2412 /* and save it */
2413 refresh_sequence_number(domain, false);
2414 if (!NT_STATUS_IS_OK(status)) {
2415 return status;
2417 centry = centry_start(domain, status);
2418 if (!centry)
2419 goto skip_save;
2421 centry_put_uint32(centry, *num_groups);
2422 for (i=0; i<(*num_groups); i++) {
2423 centry_put_sid(centry, &(*user_gids)[i]);
2426 centry_end(centry, "UG/%s", sid_to_fstring(sid_string, user_sid));
2427 centry_free(centry);
2429 skip_save:
2430 return status;
2433 static char *wcache_make_sidlist(TALLOC_CTX *mem_ctx, uint32_t num_sids,
2434 const struct dom_sid *sids)
2436 uint32_t i;
2437 char *sidlist;
2439 sidlist = talloc_strdup(mem_ctx, "");
2440 if (sidlist == NULL) {
2441 return NULL;
2443 for (i=0; i<num_sids; i++) {
2444 fstring tmp;
2445 sidlist = talloc_asprintf_append_buffer(
2446 sidlist, "/%s", sid_to_fstring(tmp, &sids[i]));
2447 if (sidlist == NULL) {
2448 return NULL;
2451 return sidlist;
2454 NTSTATUS wcache_lookup_useraliases(struct winbindd_domain *domain,
2455 TALLOC_CTX *mem_ctx, uint32_t num_sids,
2456 const struct dom_sid *sids,
2457 uint32_t *pnum_aliases, uint32_t **paliases)
2459 struct winbind_cache *cache = get_cache(domain);
2460 struct cache_entry *centry = NULL;
2461 uint32_t num_aliases;
2462 uint32_t *aliases;
2463 NTSTATUS status;
2464 char *sidlist;
2465 int i;
2467 if (cache->tdb == NULL) {
2468 return NT_STATUS_NOT_FOUND;
2471 if (num_sids == 0) {
2472 *pnum_aliases = 0;
2473 *paliases = NULL;
2474 return NT_STATUS_OK;
2477 /* We need to cache indexed by the whole list of SIDs, the aliases
2478 * resulting might come from any of the SIDs. */
2480 sidlist = wcache_make_sidlist(talloc_tos(), num_sids, sids);
2481 if (sidlist == NULL) {
2482 return NT_STATUS_NO_MEMORY;
2485 centry = wcache_fetch(cache, domain, "UA%s", sidlist);
2486 TALLOC_FREE(sidlist);
2487 if (centry == NULL) {
2488 return NT_STATUS_NOT_FOUND;
2491 num_aliases = centry_uint32(centry);
2492 aliases = talloc_array(mem_ctx, uint32_t, num_aliases);
2493 if (aliases == NULL) {
2494 centry_free(centry);
2495 return NT_STATUS_NO_MEMORY;
2498 for (i=0; i<num_aliases; i++) {
2499 aliases[i] = centry_uint32(centry);
2502 status = centry->status;
2504 DEBUG(10,("lookup_useraliases: [Cached] - cached info for domain: %s "
2505 "status %s\n", domain->name, nt_errstr(status)));
2507 centry_free(centry);
2509 *pnum_aliases = num_aliases;
2510 *paliases = aliases;
2512 return status;
2515 static NTSTATUS lookup_useraliases(struct winbindd_domain *domain,
2516 TALLOC_CTX *mem_ctx,
2517 uint32 num_sids, const struct dom_sid *sids,
2518 uint32 *num_aliases, uint32 **alias_rids)
2520 struct cache_entry *centry = NULL;
2521 NTSTATUS status;
2522 char *sidlist;
2523 int i;
2524 bool old_status;
2526 old_status = domain->online;
2527 status = wcache_lookup_useraliases(domain, mem_ctx, num_sids, sids,
2528 num_aliases, alias_rids);
2529 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2530 return status;
2533 (*num_aliases) = 0;
2534 (*alias_rids) = NULL;
2536 if (!NT_STATUS_IS_OK(domain->last_status))
2537 return domain->last_status;
2539 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info "
2540 "for domain %s\n", domain->name ));
2542 sidlist = wcache_make_sidlist(talloc_tos(), num_sids, sids);
2543 if (sidlist == NULL) {
2544 return NT_STATUS_NO_MEMORY;
2547 status = domain->backend->lookup_useraliases(domain, mem_ctx,
2548 num_sids, sids,
2549 num_aliases, alias_rids);
2551 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2552 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2553 if (!domain->internal && old_status) {
2554 set_domain_offline(domain);
2556 if (!domain->internal &&
2557 !domain->online &&
2558 old_status) {
2559 NTSTATUS cache_status;
2560 cache_status = wcache_lookup_useraliases(domain, mem_ctx, num_sids,
2561 sids, num_aliases, alias_rids);
2562 return cache_status;
2565 /* and save it */
2566 refresh_sequence_number(domain, false);
2567 if (!NT_STATUS_IS_OK(status)) {
2568 return status;
2570 centry = centry_start(domain, status);
2571 if (!centry)
2572 goto skip_save;
2573 centry_put_uint32(centry, *num_aliases);
2574 for (i=0; i<(*num_aliases); i++)
2575 centry_put_uint32(centry, (*alias_rids)[i]);
2576 centry_end(centry, "UA%s", sidlist);
2577 centry_free(centry);
2579 skip_save:
2580 return status;
2583 NTSTATUS wcache_lookup_groupmem(struct winbindd_domain *domain,
2584 TALLOC_CTX *mem_ctx,
2585 const struct dom_sid *group_sid,
2586 uint32_t *num_names,
2587 struct dom_sid **sid_mem, char ***names,
2588 uint32_t **name_types)
2590 struct winbind_cache *cache = get_cache(domain);
2591 struct cache_entry *centry = NULL;
2592 NTSTATUS status;
2593 unsigned int i;
2594 char *sid_string;
2596 if (cache->tdb == NULL) {
2597 return NT_STATUS_NOT_FOUND;
2600 sid_string = sid_string_tos(group_sid);
2601 if (sid_string == NULL) {
2602 return NT_STATUS_NO_MEMORY;
2605 centry = wcache_fetch(cache, domain, "GM/%s", sid_string);
2606 TALLOC_FREE(sid_string);
2607 if (centry == NULL) {
2608 return NT_STATUS_NOT_FOUND;
2611 *sid_mem = NULL;
2612 *names = NULL;
2613 *name_types = NULL;
2615 *num_names = centry_uint32(centry);
2616 if (*num_names == 0) {
2617 centry_free(centry);
2618 return NT_STATUS_OK;
2621 *sid_mem = talloc_array(mem_ctx, struct dom_sid, *num_names);
2622 *names = talloc_array(mem_ctx, char *, *num_names);
2623 *name_types = talloc_array(mem_ctx, uint32, *num_names);
2625 if ((*sid_mem == NULL) || (*names == NULL) || (*name_types == NULL)) {
2626 TALLOC_FREE(*sid_mem);
2627 TALLOC_FREE(*names);
2628 TALLOC_FREE(*name_types);
2629 centry_free(centry);
2630 return NT_STATUS_NO_MEMORY;
2633 for (i=0; i<(*num_names); i++) {
2634 centry_sid(centry, &(*sid_mem)[i]);
2635 (*names)[i] = centry_string(centry, mem_ctx);
2636 (*name_types)[i] = centry_uint32(centry);
2639 status = centry->status;
2641 DEBUG(10,("lookup_groupmem: [Cached] - cached info for domain %s "
2642 "status: %s\n", domain->name, nt_errstr(status)));
2644 centry_free(centry);
2645 return status;
2648 static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
2649 TALLOC_CTX *mem_ctx,
2650 const struct dom_sid *group_sid,
2651 enum lsa_SidType type,
2652 uint32 *num_names,
2653 struct dom_sid **sid_mem, char ***names,
2654 uint32 **name_types)
2656 struct cache_entry *centry = NULL;
2657 NTSTATUS status;
2658 unsigned int i;
2659 fstring sid_string;
2660 bool old_status;
2662 old_status = domain->online;
2663 status = wcache_lookup_groupmem(domain, mem_ctx, group_sid, num_names,
2664 sid_mem, names, name_types);
2665 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2666 return status;
2669 (*num_names) = 0;
2670 (*sid_mem) = NULL;
2671 (*names) = NULL;
2672 (*name_types) = NULL;
2674 /* Return status value returned by seq number check */
2676 if (!NT_STATUS_IS_OK(domain->last_status))
2677 return domain->last_status;
2679 DEBUG(10,("lookup_groupmem: [Cached] - doing backend query for info for domain %s\n",
2680 domain->name ));
2682 status = domain->backend->lookup_groupmem(domain, mem_ctx, group_sid,
2683 type, num_names,
2684 sid_mem, names, name_types);
2686 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2687 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2688 if (!domain->internal && old_status) {
2689 set_domain_offline(domain);
2691 if (!domain->internal &&
2692 !domain->online &&
2693 old_status) {
2694 NTSTATUS cache_status;
2695 cache_status = wcache_lookup_groupmem(domain, mem_ctx, group_sid,
2696 num_names, sid_mem, names,
2697 name_types);
2698 return cache_status;
2701 /* and save it */
2702 refresh_sequence_number(domain, false);
2703 if (!NT_STATUS_IS_OK(status)) {
2704 return status;
2706 centry = centry_start(domain, status);
2707 if (!centry)
2708 goto skip_save;
2709 centry_put_uint32(centry, *num_names);
2710 for (i=0; i<(*num_names); i++) {
2711 centry_put_sid(centry, &(*sid_mem)[i]);
2712 centry_put_string(centry, (*names)[i]);
2713 centry_put_uint32(centry, (*name_types)[i]);
2715 centry_end(centry, "GM/%s", sid_to_fstring(sid_string, group_sid));
2716 centry_free(centry);
2718 skip_save:
2719 return status;
2722 /* find the sequence number for a domain */
2723 static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
2725 refresh_sequence_number(domain, false);
2727 *seq = domain->sequence_number;
2729 return NT_STATUS_OK;
2732 /* enumerate trusted domains
2733 * (we need to have the list of trustdoms in the cache when we go offline) -
2734 * Guenther */
2735 static NTSTATUS trusted_domains(struct winbindd_domain *domain,
2736 TALLOC_CTX *mem_ctx,
2737 struct netr_DomainTrustList *trusts)
2739 NTSTATUS status;
2740 struct winbind_cache *cache;
2741 struct winbindd_tdc_domain *dom_list = NULL;
2742 size_t num_domains = 0;
2743 bool retval = false;
2744 int i;
2745 bool old_status;
2747 old_status = domain->online;
2748 trusts->count = 0;
2749 trusts->array = NULL;
2751 cache = get_cache(domain);
2752 if (!cache || !cache->tdb) {
2753 goto do_query;
2756 if (domain->online) {
2757 goto do_query;
2760 retval = wcache_tdc_fetch_list(&dom_list, &num_domains);
2761 if (!retval || !num_domains || !dom_list) {
2762 TALLOC_FREE(dom_list);
2763 goto do_query;
2766 do_fetch_cache:
2767 trusts->array = TALLOC_ZERO_ARRAY(mem_ctx, struct netr_DomainTrust, num_domains);
2768 if (!trusts->array) {
2769 TALLOC_FREE(dom_list);
2770 return NT_STATUS_NO_MEMORY;
2773 for (i = 0; i < num_domains; i++) {
2774 struct netr_DomainTrust *trust;
2775 struct dom_sid *sid;
2776 struct winbindd_domain *dom;
2778 dom = find_domain_from_name_noinit(dom_list[i].domain_name);
2779 if (dom && dom->internal) {
2780 continue;
2783 trust = &trusts->array[trusts->count];
2784 trust->netbios_name = talloc_strdup(trusts->array, dom_list[i].domain_name);
2785 trust->dns_name = talloc_strdup(trusts->array, dom_list[i].dns_name);
2786 sid = talloc(trusts->array, struct dom_sid);
2787 if (!trust->netbios_name || !trust->dns_name ||
2788 !sid) {
2789 TALLOC_FREE(dom_list);
2790 TALLOC_FREE(trusts->array);
2791 return NT_STATUS_NO_MEMORY;
2794 trust->trust_flags = dom_list[i].trust_flags;
2795 trust->trust_attributes = dom_list[i].trust_attribs;
2796 trust->trust_type = dom_list[i].trust_type;
2797 sid_copy(sid, &dom_list[i].sid);
2798 trust->sid = sid;
2799 trusts->count++;
2802 TALLOC_FREE(dom_list);
2803 return NT_STATUS_OK;
2805 do_query:
2806 /* Return status value returned by seq number check */
2808 if (!NT_STATUS_IS_OK(domain->last_status))
2809 return domain->last_status;
2811 DEBUG(10,("trusted_domains: [Cached] - doing backend query for info for domain %s\n",
2812 domain->name ));
2814 status = domain->backend->trusted_domains(domain, mem_ctx, trusts);
2816 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2817 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2818 if (!domain->internal && old_status) {
2819 set_domain_offline(domain);
2821 if (!domain->internal &&
2822 !domain->online &&
2823 old_status) {
2824 retval = wcache_tdc_fetch_list(&dom_list, &num_domains);
2825 if (retval && num_domains && dom_list) {
2826 TALLOC_FREE(trusts->array);
2827 trusts->count = 0;
2828 goto do_fetch_cache;
2832 /* no trusts gives NT_STATUS_NO_MORE_ENTRIES resetting to NT_STATUS_OK
2833 * so that the generic centry handling still applies correctly -
2834 * Guenther*/
2836 if (!NT_STATUS_IS_ERR(status)) {
2837 status = NT_STATUS_OK;
2839 return status;
2842 /* get lockout policy */
2843 static NTSTATUS lockout_policy(struct winbindd_domain *domain,
2844 TALLOC_CTX *mem_ctx,
2845 struct samr_DomInfo12 *policy)
2847 struct winbind_cache *cache = get_cache(domain);
2848 struct cache_entry *centry = NULL;
2849 NTSTATUS status;
2850 bool old_status;
2852 old_status = domain->online;
2853 if (!cache->tdb)
2854 goto do_query;
2856 centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
2858 if (!centry)
2859 goto do_query;
2861 do_fetch_cache:
2862 policy->lockout_duration = centry_nttime(centry);
2863 policy->lockout_window = centry_nttime(centry);
2864 policy->lockout_threshold = centry_uint16(centry);
2866 status = centry->status;
2868 DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2869 domain->name, nt_errstr(status) ));
2871 centry_free(centry);
2872 return status;
2874 do_query:
2875 ZERO_STRUCTP(policy);
2877 /* Return status value returned by seq number check */
2879 if (!NT_STATUS_IS_OK(domain->last_status))
2880 return domain->last_status;
2882 DEBUG(10,("lockout_policy: [Cached] - doing backend query for info for domain %s\n",
2883 domain->name ));
2885 status = domain->backend->lockout_policy(domain, mem_ctx, policy);
2887 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2888 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2889 if (!domain->internal && old_status) {
2890 set_domain_offline(domain);
2892 if (cache->tdb &&
2893 !domain->internal &&
2894 !domain->online &&
2895 old_status) {
2896 centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
2897 if (centry) {
2898 goto do_fetch_cache;
2902 /* and save it */
2903 refresh_sequence_number(domain, false);
2904 if (!NT_STATUS_IS_OK(status)) {
2905 return status;
2907 wcache_save_lockout_policy(domain, status, policy);
2909 return status;
2912 /* get password policy */
2913 static NTSTATUS password_policy(struct winbindd_domain *domain,
2914 TALLOC_CTX *mem_ctx,
2915 struct samr_DomInfo1 *policy)
2917 struct winbind_cache *cache = get_cache(domain);
2918 struct cache_entry *centry = NULL;
2919 NTSTATUS status;
2920 bool old_status;
2922 old_status = domain->online;
2923 if (!cache->tdb)
2924 goto do_query;
2926 centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
2928 if (!centry)
2929 goto do_query;
2931 do_fetch_cache:
2932 policy->min_password_length = centry_uint16(centry);
2933 policy->password_history_length = centry_uint16(centry);
2934 policy->password_properties = centry_uint32(centry);
2935 policy->max_password_age = centry_nttime(centry);
2936 policy->min_password_age = centry_nttime(centry);
2938 status = centry->status;
2940 DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2941 domain->name, nt_errstr(status) ));
2943 centry_free(centry);
2944 return status;
2946 do_query:
2947 ZERO_STRUCTP(policy);
2949 /* Return status value returned by seq number check */
2951 if (!NT_STATUS_IS_OK(domain->last_status))
2952 return domain->last_status;
2954 DEBUG(10,("password_policy: [Cached] - doing backend query for info for domain %s\n",
2955 domain->name ));
2957 status = domain->backend->password_policy(domain, mem_ctx, policy);
2959 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2960 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2961 if (!domain->internal && old_status) {
2962 set_domain_offline(domain);
2964 if (cache->tdb &&
2965 !domain->internal &&
2966 !domain->online &&
2967 old_status) {
2968 centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
2969 if (centry) {
2970 goto do_fetch_cache;
2974 /* and save it */
2975 refresh_sequence_number(domain, false);
2976 if (!NT_STATUS_IS_OK(status)) {
2977 return status;
2979 wcache_save_password_policy(domain, status, policy);
2981 return status;
2985 /* Invalidate cached user and group lists coherently */
2987 static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
2988 void *state)
2990 if (strncmp((const char *)kbuf.dptr, "UL/", 3) == 0 ||
2991 strncmp((const char *)kbuf.dptr, "GL/", 3) == 0)
2992 tdb_delete(the_tdb, kbuf);
2994 return 0;
2997 /* Invalidate the getpwnam and getgroups entries for a winbindd domain */
2999 void wcache_invalidate_samlogon(struct winbindd_domain *domain,
3000 struct netr_SamInfo3 *info3)
3002 struct dom_sid sid;
3003 fstring key_str, sid_string;
3004 struct winbind_cache *cache;
3006 /* dont clear cached U/SID and UG/SID entries when we want to logon
3007 * offline - gd */
3009 if (lp_winbind_offline_logon()) {
3010 return;
3013 if (!domain)
3014 return;
3016 cache = get_cache(domain);
3018 if (!cache->tdb) {
3019 return;
3022 sid_compose(&sid, info3->base.domain_sid, info3->base.rid);
3024 /* Clear U/SID cache entry */
3025 fstr_sprintf(key_str, "U/%s", sid_to_fstring(sid_string, &sid));
3026 DEBUG(10, ("wcache_invalidate_samlogon: clearing %s\n", key_str));
3027 tdb_delete(cache->tdb, string_tdb_data(key_str));
3029 /* Clear UG/SID cache entry */
3030 fstr_sprintf(key_str, "UG/%s", sid_to_fstring(sid_string, &sid));
3031 DEBUG(10, ("wcache_invalidate_samlogon: clearing %s\n", key_str));
3032 tdb_delete(cache->tdb, string_tdb_data(key_str));
3034 /* Samba/winbindd never needs this. */
3035 netsamlogon_clear_cached_user(info3);
3038 bool wcache_invalidate_cache(void)
3040 struct winbindd_domain *domain;
3042 for (domain = domain_list(); domain; domain = domain->next) {
3043 struct winbind_cache *cache = get_cache(domain);
3045 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
3046 "entries for %s\n", domain->name));
3047 if (cache) {
3048 if (cache->tdb) {
3049 tdb_traverse(cache->tdb, traverse_fn, NULL);
3050 } else {
3051 return false;
3055 return true;
3058 bool wcache_invalidate_cache_noinit(void)
3060 struct winbindd_domain *domain;
3062 for (domain = domain_list(); domain; domain = domain->next) {
3063 struct winbind_cache *cache;
3065 /* Skip uninitialized domains. */
3066 if (!domain->initialized && !domain->internal) {
3067 continue;
3070 cache = get_cache(domain);
3072 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
3073 "entries for %s\n", domain->name));
3074 if (cache) {
3075 if (cache->tdb) {
3076 tdb_traverse(cache->tdb, traverse_fn, NULL);
3078 * Flushing cache has nothing to with domains.
3079 * return here if we successfully flushed once.
3080 * To avoid unnecessary traversing the cache.
3082 return true;
3083 } else {
3084 return false;
3088 return true;
3091 bool init_wcache(void)
3093 if (wcache == NULL) {
3094 wcache = SMB_XMALLOC_P(struct winbind_cache);
3095 ZERO_STRUCTP(wcache);
3098 if (wcache->tdb != NULL)
3099 return true;
3101 /* when working offline we must not clear the cache on restart */
3102 wcache->tdb = tdb_open_log(cache_path("winbindd_cache.tdb"),
3103 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
3104 lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST),
3105 O_RDWR|O_CREAT, 0600);
3107 if (wcache->tdb == NULL) {
3108 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
3109 return false;
3112 return true;
3115 /************************************************************************
3116 This is called by the parent to initialize the cache file.
3117 We don't need sophisticated locking here as we know we're the
3118 only opener.
3119 ************************************************************************/
3121 bool initialize_winbindd_cache(void)
3123 bool cache_bad = true;
3124 uint32 vers;
3126 if (!init_wcache()) {
3127 DEBUG(0,("initialize_winbindd_cache: init_wcache failed.\n"));
3128 return false;
3131 /* Check version number. */
3132 if (tdb_fetch_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, &vers) &&
3133 vers == WINBINDD_CACHE_VERSION) {
3134 cache_bad = false;
3137 if (cache_bad) {
3138 DEBUG(0,("initialize_winbindd_cache: clearing cache "
3139 "and re-creating with version number %d\n",
3140 WINBINDD_CACHE_VERSION ));
3142 tdb_close(wcache->tdb);
3143 wcache->tdb = NULL;
3145 if (unlink(cache_path("winbindd_cache.tdb")) == -1) {
3146 DEBUG(0,("initialize_winbindd_cache: unlink %s failed %s ",
3147 cache_path("winbindd_cache.tdb"),
3148 strerror(errno) ));
3149 return false;
3151 if (!init_wcache()) {
3152 DEBUG(0,("initialize_winbindd_cache: re-initialization "
3153 "init_wcache failed.\n"));
3154 return false;
3157 /* Write the version. */
3158 if (!tdb_store_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, WINBINDD_CACHE_VERSION)) {
3159 DEBUG(0,("initialize_winbindd_cache: version number store failed %s\n",
3160 tdb_errorstr(wcache->tdb) ));
3161 return false;
3165 tdb_close(wcache->tdb);
3166 wcache->tdb = NULL;
3167 return true;
3170 void close_winbindd_cache(void)
3172 if (!wcache) {
3173 return;
3175 if (wcache->tdb) {
3176 tdb_close(wcache->tdb);
3177 wcache->tdb = NULL;
3181 bool lookup_cached_sid(TALLOC_CTX *mem_ctx, const struct dom_sid *sid,
3182 char **domain_name, char **name,
3183 enum lsa_SidType *type)
3185 struct winbindd_domain *domain;
3186 NTSTATUS status;
3188 domain = find_lookup_domain_from_sid(sid);
3189 if (domain == NULL) {
3190 return false;
3192 status = wcache_sid_to_name(domain, sid, mem_ctx, domain_name, name,
3193 type);
3194 return NT_STATUS_IS_OK(status);
3197 bool lookup_cached_name(TALLOC_CTX *mem_ctx,
3198 const char *domain_name,
3199 const char *name,
3200 struct dom_sid *sid,
3201 enum lsa_SidType *type)
3203 struct winbindd_domain *domain;
3204 NTSTATUS status;
3205 bool original_online_state;
3207 domain = find_lookup_domain_from_name(domain_name);
3208 if (domain == NULL) {
3209 return false;
3212 /* If we are doing a cached logon, temporarily set the domain
3213 offline so the cache won't expire the entry */
3215 original_online_state = domain->online;
3216 domain->online = false;
3217 status = wcache_name_to_sid(domain, domain_name, name, sid, type);
3218 domain->online = original_online_state;
3220 return NT_STATUS_IS_OK(status);
3223 void cache_name2sid(struct winbindd_domain *domain,
3224 const char *domain_name, const char *name,
3225 enum lsa_SidType type, const struct dom_sid *sid)
3227 refresh_sequence_number(domain, false);
3228 wcache_save_name_to_sid(domain, NT_STATUS_OK, domain_name, name,
3229 sid, type);
3233 * The original idea that this cache only contains centries has
3234 * been blurred - now other stuff gets put in here. Ensure we
3235 * ignore these things on cleanup.
3238 static int traverse_fn_cleanup(TDB_CONTEXT *the_tdb, TDB_DATA kbuf,
3239 TDB_DATA dbuf, void *state)
3241 struct cache_entry *centry;
3243 if (is_non_centry_key(kbuf)) {
3244 return 0;
3247 centry = wcache_fetch_raw((char *)kbuf.dptr);
3248 if (!centry) {
3249 return 0;
3252 if (!NT_STATUS_IS_OK(centry->status)) {
3253 DEBUG(10,("deleting centry %s\n", (const char *)kbuf.dptr));
3254 tdb_delete(the_tdb, kbuf);
3257 centry_free(centry);
3258 return 0;
3261 /* flush the cache */
3262 void wcache_flush_cache(void)
3264 if (!wcache)
3265 return;
3266 if (wcache->tdb) {
3267 tdb_close(wcache->tdb);
3268 wcache->tdb = NULL;
3270 if (!winbindd_use_cache()) {
3271 return;
3274 /* when working offline we must not clear the cache on restart */
3275 wcache->tdb = tdb_open_log(cache_path("winbindd_cache.tdb"),
3276 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
3277 lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST),
3278 O_RDWR|O_CREAT, 0600);
3280 if (!wcache->tdb) {
3281 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
3282 return;
3285 tdb_traverse(wcache->tdb, traverse_fn_cleanup, NULL);
3287 DEBUG(10,("wcache_flush_cache success\n"));
3290 /* Count cached creds */
3292 static int traverse_fn_cached_creds(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
3293 void *state)
3295 int *cred_count = (int*)state;
3297 if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
3298 (*cred_count)++;
3300 return 0;
3303 NTSTATUS wcache_count_cached_creds(struct winbindd_domain *domain, int *count)
3305 struct winbind_cache *cache = get_cache(domain);
3307 *count = 0;
3309 if (!cache->tdb) {
3310 return NT_STATUS_INTERNAL_DB_ERROR;
3313 tdb_traverse(cache->tdb, traverse_fn_cached_creds, (void *)count);
3315 return NT_STATUS_OK;
3318 struct cred_list {
3319 struct cred_list *prev, *next;
3320 TDB_DATA key;
3321 fstring name;
3322 time_t created;
3324 static struct cred_list *wcache_cred_list;
3326 static int traverse_fn_get_credlist(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
3327 void *state)
3329 struct cred_list *cred;
3331 if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
3333 cred = SMB_MALLOC_P(struct cred_list);
3334 if (cred == NULL) {
3335 DEBUG(0,("traverse_fn_remove_first_creds: failed to malloc new entry for list\n"));
3336 return -1;
3339 ZERO_STRUCTP(cred);
3341 /* save a copy of the key */
3343 fstrcpy(cred->name, (const char *)kbuf.dptr);
3344 DLIST_ADD(wcache_cred_list, cred);
3347 return 0;
3350 NTSTATUS wcache_remove_oldest_cached_creds(struct winbindd_domain *domain, const struct dom_sid *sid)
3352 struct winbind_cache *cache = get_cache(domain);
3353 NTSTATUS status;
3354 int ret;
3355 struct cred_list *cred, *oldest = NULL;
3357 if (!cache->tdb) {
3358 return NT_STATUS_INTERNAL_DB_ERROR;
3361 /* we possibly already have an entry */
3362 if (sid && NT_STATUS_IS_OK(wcache_cached_creds_exist(domain, sid))) {
3364 fstring key_str, tmp;
3366 DEBUG(11,("we already have an entry, deleting that\n"));
3368 fstr_sprintf(key_str, "CRED/%s", sid_to_fstring(tmp, sid));
3370 tdb_delete(cache->tdb, string_tdb_data(key_str));
3372 return NT_STATUS_OK;
3375 ret = tdb_traverse(cache->tdb, traverse_fn_get_credlist, NULL);
3376 if (ret == 0) {
3377 return NT_STATUS_OK;
3378 } else if ((ret == -1) || (wcache_cred_list == NULL)) {
3379 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
3382 ZERO_STRUCTP(oldest);
3384 for (cred = wcache_cred_list; cred; cred = cred->next) {
3386 TDB_DATA data;
3387 time_t t;
3389 data = tdb_fetch(cache->tdb, string_tdb_data(cred->name));
3390 if (!data.dptr) {
3391 DEBUG(10,("wcache_remove_oldest_cached_creds: entry for [%s] not found\n",
3392 cred->name));
3393 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
3394 goto done;
3397 t = IVAL(data.dptr, 0);
3398 SAFE_FREE(data.dptr);
3400 if (!oldest) {
3401 oldest = SMB_MALLOC_P(struct cred_list);
3402 if (oldest == NULL) {
3403 status = NT_STATUS_NO_MEMORY;
3404 goto done;
3407 fstrcpy(oldest->name, cred->name);
3408 oldest->created = t;
3409 continue;
3412 if (t < oldest->created) {
3413 fstrcpy(oldest->name, cred->name);
3414 oldest->created = t;
3418 if (tdb_delete(cache->tdb, string_tdb_data(oldest->name)) == 0) {
3419 status = NT_STATUS_OK;
3420 } else {
3421 status = NT_STATUS_UNSUCCESSFUL;
3423 done:
3424 SAFE_FREE(wcache_cred_list);
3425 SAFE_FREE(oldest);
3427 return status;
3430 /* Change the global online/offline state. */
3431 bool set_global_winbindd_state_offline(void)
3433 TDB_DATA data;
3435 DEBUG(10,("set_global_winbindd_state_offline: offline requested.\n"));
3437 /* Only go offline if someone has created
3438 the key "WINBINDD_OFFLINE" in the cache tdb. */
3440 if (wcache == NULL || wcache->tdb == NULL) {
3441 DEBUG(10,("set_global_winbindd_state_offline: wcache not open yet.\n"));
3442 return false;
3445 if (!lp_winbind_offline_logon()) {
3446 DEBUG(10,("set_global_winbindd_state_offline: rejecting.\n"));
3447 return false;
3450 if (global_winbindd_offline_state) {
3451 /* Already offline. */
3452 return true;
3455 data = tdb_fetch_bystring( wcache->tdb, "WINBINDD_OFFLINE" );
3457 if (!data.dptr || data.dsize != 4) {
3458 DEBUG(10,("set_global_winbindd_state_offline: offline state not set.\n"));
3459 SAFE_FREE(data.dptr);
3460 return false;
3461 } else {
3462 DEBUG(10,("set_global_winbindd_state_offline: offline state set.\n"));
3463 global_winbindd_offline_state = true;
3464 SAFE_FREE(data.dptr);
3465 return true;
3469 void set_global_winbindd_state_online(void)
3471 DEBUG(10,("set_global_winbindd_state_online: online requested.\n"));
3473 if (!lp_winbind_offline_logon()) {
3474 DEBUG(10,("set_global_winbindd_state_online: rejecting.\n"));
3475 return;
3478 if (!global_winbindd_offline_state) {
3479 /* Already online. */
3480 return;
3482 global_winbindd_offline_state = false;
3484 if (!wcache->tdb) {
3485 return;
3488 /* Ensure there is no key "WINBINDD_OFFLINE" in the cache tdb. */
3489 tdb_delete_bystring(wcache->tdb, "WINBINDD_OFFLINE");
3492 bool get_global_winbindd_state_offline(void)
3494 return global_winbindd_offline_state;
3497 /***********************************************************************
3498 Validate functions for all possible cache tdb keys.
3499 ***********************************************************************/
3501 static struct cache_entry *create_centry_validate(const char *kstr, TDB_DATA data,
3502 struct tdb_validation_status *state)
3504 struct cache_entry *centry;
3506 centry = SMB_XMALLOC_P(struct cache_entry);
3507 centry->data = (unsigned char *)memdup(data.dptr, data.dsize);
3508 if (!centry->data) {
3509 SAFE_FREE(centry);
3510 return NULL;
3512 centry->len = data.dsize;
3513 centry->ofs = 0;
3515 if (centry->len < 8) {
3516 /* huh? corrupt cache? */
3517 DEBUG(0,("create_centry_validate: Corrupt cache for key %s (len < 8) ?\n", kstr));
3518 centry_free(centry);
3519 state->bad_entry = true;
3520 state->success = false;
3521 return NULL;
3524 centry->status = NT_STATUS(centry_uint32(centry));
3525 centry->sequence_number = centry_uint32(centry);
3526 return centry;
3529 static int validate_seqnum(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3530 struct tdb_validation_status *state)
3532 if (dbuf.dsize != 8) {
3533 DEBUG(0,("validate_seqnum: Corrupt cache for key %s (len %u != 8) ?\n",
3534 keystr, (unsigned int)dbuf.dsize ));
3535 state->bad_entry = true;
3536 return 1;
3538 return 0;
3541 static int validate_ns(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3542 struct tdb_validation_status *state)
3544 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3545 if (!centry) {
3546 return 1;
3549 (void)centry_uint32(centry);
3550 if (NT_STATUS_IS_OK(centry->status)) {
3551 struct dom_sid sid;
3552 (void)centry_sid(centry, &sid);
3555 centry_free(centry);
3557 if (!(state->success)) {
3558 return 1;
3560 DEBUG(10,("validate_ns: %s ok\n", keystr));
3561 return 0;
3564 static int validate_sn(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3565 struct tdb_validation_status *state)
3567 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3568 if (!centry) {
3569 return 1;
3572 if (NT_STATUS_IS_OK(centry->status)) {
3573 (void)centry_uint32(centry);
3574 (void)centry_string(centry, mem_ctx);
3575 (void)centry_string(centry, mem_ctx);
3578 centry_free(centry);
3580 if (!(state->success)) {
3581 return 1;
3583 DEBUG(10,("validate_sn: %s ok\n", keystr));
3584 return 0;
3587 static int validate_u(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3588 struct tdb_validation_status *state)
3590 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3591 struct dom_sid sid;
3593 if (!centry) {
3594 return 1;
3597 (void)centry_string(centry, mem_ctx);
3598 (void)centry_string(centry, mem_ctx);
3599 (void)centry_string(centry, mem_ctx);
3600 (void)centry_string(centry, mem_ctx);
3601 (void)centry_uint32(centry);
3602 (void)centry_sid(centry, &sid);
3603 (void)centry_sid(centry, &sid);
3605 centry_free(centry);
3607 if (!(state->success)) {
3608 return 1;
3610 DEBUG(10,("validate_u: %s ok\n", keystr));
3611 return 0;
3614 static int validate_loc_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3615 struct tdb_validation_status *state)
3617 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3619 if (!centry) {
3620 return 1;
3623 (void)centry_nttime(centry);
3624 (void)centry_nttime(centry);
3625 (void)centry_uint16(centry);
3627 centry_free(centry);
3629 if (!(state->success)) {
3630 return 1;
3632 DEBUG(10,("validate_loc_pol: %s ok\n", keystr));
3633 return 0;
3636 static int validate_pwd_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3637 struct tdb_validation_status *state)
3639 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3641 if (!centry) {
3642 return 1;
3645 (void)centry_uint16(centry);
3646 (void)centry_uint16(centry);
3647 (void)centry_uint32(centry);
3648 (void)centry_nttime(centry);
3649 (void)centry_nttime(centry);
3651 centry_free(centry);
3653 if (!(state->success)) {
3654 return 1;
3656 DEBUG(10,("validate_pwd_pol: %s ok\n", keystr));
3657 return 0;
3660 static int validate_cred(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3661 struct tdb_validation_status *state)
3663 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3665 if (!centry) {
3666 return 1;
3669 (void)centry_time(centry);
3670 (void)centry_hash16(centry, mem_ctx);
3672 /* We only have 17 bytes more data in the salted cred case. */
3673 if (centry->len - centry->ofs == 17) {
3674 (void)centry_hash16(centry, mem_ctx);
3677 centry_free(centry);
3679 if (!(state->success)) {
3680 return 1;
3682 DEBUG(10,("validate_cred: %s ok\n", keystr));
3683 return 0;
3686 static int validate_ul(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3687 struct tdb_validation_status *state)
3689 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3690 int32 num_entries, i;
3692 if (!centry) {
3693 return 1;
3696 num_entries = (int32)centry_uint32(centry);
3698 for (i=0; i< num_entries; i++) {
3699 struct dom_sid sid;
3700 (void)centry_string(centry, mem_ctx);
3701 (void)centry_string(centry, mem_ctx);
3702 (void)centry_string(centry, mem_ctx);
3703 (void)centry_string(centry, mem_ctx);
3704 (void)centry_sid(centry, &sid);
3705 (void)centry_sid(centry, &sid);
3708 centry_free(centry);
3710 if (!(state->success)) {
3711 return 1;
3713 DEBUG(10,("validate_ul: %s ok\n", keystr));
3714 return 0;
3717 static int validate_gl(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);
3721 int32 num_entries, i;
3723 if (!centry) {
3724 return 1;
3727 num_entries = centry_uint32(centry);
3729 for (i=0; i< num_entries; i++) {
3730 (void)centry_string(centry, mem_ctx);
3731 (void)centry_string(centry, mem_ctx);
3732 (void)centry_uint32(centry);
3735 centry_free(centry);
3737 if (!(state->success)) {
3738 return 1;
3740 DEBUG(10,("validate_gl: %s ok\n", keystr));
3741 return 0;
3744 static int validate_ug(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3745 struct tdb_validation_status *state)
3747 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3748 int32 num_groups, i;
3750 if (!centry) {
3751 return 1;
3754 num_groups = centry_uint32(centry);
3756 for (i=0; i< num_groups; i++) {
3757 struct dom_sid sid;
3758 centry_sid(centry, &sid);
3761 centry_free(centry);
3763 if (!(state->success)) {
3764 return 1;
3766 DEBUG(10,("validate_ug: %s ok\n", keystr));
3767 return 0;
3770 static int validate_ua(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3771 struct tdb_validation_status *state)
3773 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3774 int32 num_aliases, i;
3776 if (!centry) {
3777 return 1;
3780 num_aliases = centry_uint32(centry);
3782 for (i=0; i < num_aliases; i++) {
3783 (void)centry_uint32(centry);
3786 centry_free(centry);
3788 if (!(state->success)) {
3789 return 1;
3791 DEBUG(10,("validate_ua: %s ok\n", keystr));
3792 return 0;
3795 static int validate_gm(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3796 struct tdb_validation_status *state)
3798 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3799 int32 num_names, i;
3801 if (!centry) {
3802 return 1;
3805 num_names = centry_uint32(centry);
3807 for (i=0; i< num_names; i++) {
3808 struct dom_sid sid;
3809 centry_sid(centry, &sid);
3810 (void)centry_string(centry, mem_ctx);
3811 (void)centry_uint32(centry);
3814 centry_free(centry);
3816 if (!(state->success)) {
3817 return 1;
3819 DEBUG(10,("validate_gm: %s ok\n", keystr));
3820 return 0;
3823 static int validate_dr(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3824 struct tdb_validation_status *state)
3826 /* Can't say anything about this other than must be nonzero. */
3827 if (dbuf.dsize == 0) {
3828 DEBUG(0,("validate_dr: Corrupt cache for key %s (len == 0) ?\n",
3829 keystr));
3830 state->bad_entry = true;
3831 state->success = false;
3832 return 1;
3835 DEBUG(10,("validate_dr: %s ok\n", keystr));
3836 return 0;
3839 static int validate_de(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3840 struct tdb_validation_status *state)
3842 /* Can't say anything about this other than must be nonzero. */
3843 if (dbuf.dsize == 0) {
3844 DEBUG(0,("validate_de: Corrupt cache for key %s (len == 0) ?\n",
3845 keystr));
3846 state->bad_entry = true;
3847 state->success = false;
3848 return 1;
3851 DEBUG(10,("validate_de: %s ok\n", keystr));
3852 return 0;
3855 static int validate_pwinfo(TALLOC_CTX *mem_ctx, const char *keystr,
3856 TDB_DATA dbuf, struct tdb_validation_status *state)
3858 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3860 if (!centry) {
3861 return 1;
3864 (void)centry_string(centry, mem_ctx);
3865 (void)centry_string(centry, mem_ctx);
3866 (void)centry_string(centry, mem_ctx);
3867 (void)centry_uint32(centry);
3869 centry_free(centry);
3871 if (!(state->success)) {
3872 return 1;
3874 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3875 return 0;
3878 static int validate_nss_an(TALLOC_CTX *mem_ctx, const char *keystr,
3879 TDB_DATA dbuf,
3880 struct tdb_validation_status *state)
3882 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3884 if (!centry) {
3885 return 1;
3888 (void)centry_string( centry, mem_ctx );
3890 centry_free(centry);
3892 if (!(state->success)) {
3893 return 1;
3895 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3896 return 0;
3899 static int validate_nss_na(TALLOC_CTX *mem_ctx, const char *keystr,
3900 TDB_DATA dbuf,
3901 struct tdb_validation_status *state)
3903 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3905 if (!centry) {
3906 return 1;
3909 (void)centry_string( centry, mem_ctx );
3911 centry_free(centry);
3913 if (!(state->success)) {
3914 return 1;
3916 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3917 return 0;
3920 static int validate_trustdomcache(TALLOC_CTX *mem_ctx, const char *keystr,
3921 TDB_DATA dbuf,
3922 struct tdb_validation_status *state)
3924 if (dbuf.dsize == 0) {
3925 DEBUG(0, ("validate_trustdomcache: Corrupt cache for "
3926 "key %s (len ==0) ?\n", keystr));
3927 state->bad_entry = true;
3928 state->success = false;
3929 return 1;
3932 DEBUG(10, ("validate_trustdomcache: %s ok\n", keystr));
3933 DEBUGADD(10, (" Don't trust me, I am a DUMMY!\n"));
3934 return 0;
3937 static int validate_offline(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3938 struct tdb_validation_status *state)
3940 if (dbuf.dsize != 4) {
3941 DEBUG(0,("validate_offline: Corrupt cache for key %s (len %u != 4) ?\n",
3942 keystr, (unsigned int)dbuf.dsize ));
3943 state->bad_entry = true;
3944 state->success = false;
3945 return 1;
3947 DEBUG(10,("validate_offline: %s ok\n", keystr));
3948 return 0;
3951 static int validate_ndr(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3952 struct tdb_validation_status *state)
3955 * Ignore validation for now. The proper way to do this is with a
3956 * checksum. Just pure parsing does not really catch much.
3958 return 0;
3961 static int validate_cache_version(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3962 struct tdb_validation_status *state)
3964 if (dbuf.dsize != 4) {
3965 DEBUG(0, ("validate_cache_version: Corrupt cache for "
3966 "key %s (len %u != 4) ?\n",
3967 keystr, (unsigned int)dbuf.dsize));
3968 state->bad_entry = true;
3969 state->success = false;
3970 return 1;
3973 DEBUG(10, ("validate_cache_version: %s ok\n", keystr));
3974 return 0;
3977 /***********************************************************************
3978 A list of all possible cache tdb keys with associated validation
3979 functions.
3980 ***********************************************************************/
3982 struct key_val_struct {
3983 const char *keyname;
3984 int (*validate_data_fn)(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, struct tdb_validation_status* state);
3985 } key_val[] = {
3986 {"SEQNUM/", validate_seqnum},
3987 {"NS/", validate_ns},
3988 {"SN/", validate_sn},
3989 {"U/", validate_u},
3990 {"LOC_POL/", validate_loc_pol},
3991 {"PWD_POL/", validate_pwd_pol},
3992 {"CRED/", validate_cred},
3993 {"UL/", validate_ul},
3994 {"GL/", validate_gl},
3995 {"UG/", validate_ug},
3996 {"UA", validate_ua},
3997 {"GM/", validate_gm},
3998 {"DR/", validate_dr},
3999 {"DE/", validate_de},
4000 {"NSS/PWINFO/", validate_pwinfo},
4001 {"TRUSTDOMCACHE/", validate_trustdomcache},
4002 {"NSS/NA/", validate_nss_na},
4003 {"NSS/AN/", validate_nss_an},
4004 {"WINBINDD_OFFLINE", validate_offline},
4005 {"NDR/", validate_ndr},
4006 {WINBINDD_CACHE_VERSION_KEYSTR, validate_cache_version},
4007 {NULL, NULL}
4010 /***********************************************************************
4011 Function to look at every entry in the tdb and validate it as far as
4012 possible.
4013 ***********************************************************************/
4015 static int cache_traverse_validate_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, void *state)
4017 int i;
4018 unsigned int max_key_len = 1024;
4019 struct tdb_validation_status *v_state = (struct tdb_validation_status *)state;
4021 /* Paranoia check. */
4022 if (strncmp("UA/", (const char *)kbuf.dptr, 3) == 0) {
4023 max_key_len = 1024 * 1024;
4025 if (kbuf.dsize > max_key_len) {
4026 DEBUG(0, ("cache_traverse_validate_fn: key length too large: "
4027 "(%u) > (%u)\n\n",
4028 (unsigned int)kbuf.dsize, (unsigned int)max_key_len));
4029 return 1;
4032 for (i = 0; key_val[i].keyname; i++) {
4033 size_t namelen = strlen(key_val[i].keyname);
4034 if (kbuf.dsize >= namelen && (
4035 strncmp(key_val[i].keyname, (const char *)kbuf.dptr, namelen)) == 0) {
4036 TALLOC_CTX *mem_ctx;
4037 char *keystr;
4038 int ret;
4040 keystr = SMB_MALLOC_ARRAY(char, kbuf.dsize+1);
4041 if (!keystr) {
4042 return 1;
4044 memcpy(keystr, kbuf.dptr, kbuf.dsize);
4045 keystr[kbuf.dsize] = '\0';
4047 mem_ctx = talloc_init("validate_ctx");
4048 if (!mem_ctx) {
4049 SAFE_FREE(keystr);
4050 return 1;
4053 ret = key_val[i].validate_data_fn(mem_ctx, keystr, dbuf,
4054 v_state);
4056 SAFE_FREE(keystr);
4057 talloc_destroy(mem_ctx);
4058 return ret;
4062 DEBUG(0,("cache_traverse_validate_fn: unknown cache entry\nkey :\n"));
4063 dump_data(0, (uint8 *)kbuf.dptr, kbuf.dsize);
4064 DEBUG(0,("data :\n"));
4065 dump_data(0, (uint8 *)dbuf.dptr, dbuf.dsize);
4066 v_state->unknown_key = true;
4067 v_state->success = false;
4068 return 1; /* terminate. */
4071 static void validate_panic(const char *const why)
4073 DEBUG(0,("validating cache: would panic %s\n", why ));
4074 DEBUGADD(0, ("exiting instead (cache validation mode)\n"));
4075 exit(47);
4078 /***********************************************************************
4079 Try and validate every entry in the winbindd cache. If we fail here,
4080 delete the cache tdb and return non-zero.
4081 ***********************************************************************/
4083 int winbindd_validate_cache(void)
4085 int ret = -1;
4086 const char *tdb_path = cache_path("winbindd_cache.tdb");
4087 TDB_CONTEXT *tdb = NULL;
4089 DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
4090 smb_panic_fn = validate_panic;
4093 tdb = tdb_open_log(tdb_path,
4094 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
4095 ( lp_winbind_offline_logon()
4096 ? TDB_DEFAULT
4097 : TDB_DEFAULT | TDB_CLEAR_IF_FIRST ),
4098 O_RDWR|O_CREAT,
4099 0600);
4100 if (!tdb) {
4101 DEBUG(0, ("winbindd_validate_cache: "
4102 "error opening/initializing tdb\n"));
4103 goto done;
4105 tdb_close(tdb);
4107 ret = tdb_validate_and_backup(tdb_path, cache_traverse_validate_fn);
4109 if (ret != 0) {
4110 DEBUG(10, ("winbindd_validate_cache: validation not successful.\n"));
4111 DEBUGADD(10, ("removing tdb %s.\n", tdb_path));
4112 unlink(tdb_path);
4115 done:
4116 DEBUG(10, ("winbindd_validate_cache: restoring panic function\n"));
4117 smb_panic_fn = smb_panic;
4118 return ret;
4121 /***********************************************************************
4122 Try and validate every entry in the winbindd cache.
4123 ***********************************************************************/
4125 int winbindd_validate_cache_nobackup(void)
4127 int ret = -1;
4128 const char *tdb_path = cache_path("winbindd_cache.tdb");
4130 DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
4131 smb_panic_fn = validate_panic;
4134 if (wcache == NULL || wcache->tdb == NULL) {
4135 ret = tdb_validate_open(tdb_path, cache_traverse_validate_fn);
4136 } else {
4137 ret = tdb_validate(wcache->tdb, cache_traverse_validate_fn);
4140 if (ret != 0) {
4141 DEBUG(10, ("winbindd_validate_cache_nobackup: validation not "
4142 "successful.\n"));
4145 DEBUG(10, ("winbindd_validate_cache_nobackup: restoring panic "
4146 "function\n"));
4147 smb_panic_fn = smb_panic;
4148 return ret;
4151 bool winbindd_cache_validate_and_initialize(void)
4153 close_winbindd_cache();
4155 if (lp_winbind_offline_logon()) {
4156 if (winbindd_validate_cache() < 0) {
4157 DEBUG(0, ("winbindd cache tdb corrupt and no backup "
4158 "could be restored.\n"));
4162 return initialize_winbindd_cache();
4165 /*********************************************************************
4166 ********************************************************************/
4168 static bool add_wbdomain_to_tdc_array( struct winbindd_domain *new_dom,
4169 struct winbindd_tdc_domain **domains,
4170 size_t *num_domains )
4172 struct winbindd_tdc_domain *list = NULL;
4173 size_t idx;
4174 int i;
4175 bool set_only = false;
4177 /* don't allow duplicates */
4179 idx = *num_domains;
4180 list = *domains;
4182 for ( i=0; i< (*num_domains); i++ ) {
4183 if ( strequal( new_dom->name, list[i].domain_name ) ) {
4184 DEBUG(10,("add_wbdomain_to_tdc_array: Found existing record for %s\n",
4185 new_dom->name));
4186 idx = i;
4187 set_only = true;
4189 break;
4193 if ( !set_only ) {
4194 if ( !*domains ) {
4195 list = TALLOC_ARRAY( NULL, struct winbindd_tdc_domain, 1 );
4196 idx = 0;
4197 } else {
4198 list = TALLOC_REALLOC_ARRAY( *domains, *domains,
4199 struct winbindd_tdc_domain,
4200 (*num_domains)+1);
4201 idx = *num_domains;
4204 ZERO_STRUCT( list[idx] );
4207 if ( !list )
4208 return false;
4210 list[idx].domain_name = talloc_strdup( list, new_dom->name );
4211 list[idx].dns_name = talloc_strdup( list, new_dom->alt_name );
4213 if ( !is_null_sid( &new_dom->sid ) ) {
4214 sid_copy( &list[idx].sid, &new_dom->sid );
4215 } else {
4216 sid_copy(&list[idx].sid, &global_sid_NULL);
4219 if ( new_dom->domain_flags != 0x0 )
4220 list[idx].trust_flags = new_dom->domain_flags;
4222 if ( new_dom->domain_type != 0x0 )
4223 list[idx].trust_type = new_dom->domain_type;
4225 if ( new_dom->domain_trust_attribs != 0x0 )
4226 list[idx].trust_attribs = new_dom->domain_trust_attribs;
4228 if ( !set_only ) {
4229 *domains = list;
4230 *num_domains = idx + 1;
4233 return true;
4236 /*********************************************************************
4237 ********************************************************************/
4239 static TDB_DATA make_tdc_key( const char *domain_name )
4241 char *keystr = NULL;
4242 TDB_DATA key = { NULL, 0 };
4244 if ( !domain_name ) {
4245 DEBUG(5,("make_tdc_key: Keyname workgroup is NULL!\n"));
4246 return key;
4249 if (asprintf( &keystr, "TRUSTDOMCACHE/%s", domain_name ) == -1) {
4250 return key;
4252 key = string_term_tdb_data(keystr);
4254 return key;
4257 /*********************************************************************
4258 ********************************************************************/
4260 static int pack_tdc_domains( struct winbindd_tdc_domain *domains,
4261 size_t num_domains,
4262 unsigned char **buf )
4264 unsigned char *buffer = NULL;
4265 int len = 0;
4266 int buflen = 0;
4267 int i = 0;
4269 DEBUG(10,("pack_tdc_domains: Packing %d trusted domains\n",
4270 (int)num_domains));
4272 buflen = 0;
4274 again:
4275 len = 0;
4277 /* Store the number of array items first */
4278 len += tdb_pack( buffer+len, buflen-len, "d",
4279 num_domains );
4281 /* now pack each domain trust record */
4282 for ( i=0; i<num_domains; i++ ) {
4284 fstring tmp;
4286 if ( buflen > 0 ) {
4287 DEBUG(10,("pack_tdc_domains: Packing domain %s (%s)\n",
4288 domains[i].domain_name,
4289 domains[i].dns_name ? domains[i].dns_name : "UNKNOWN" ));
4292 len += tdb_pack( buffer+len, buflen-len, "fffddd",
4293 domains[i].domain_name,
4294 domains[i].dns_name,
4295 sid_to_fstring(tmp, &domains[i].sid),
4296 domains[i].trust_flags,
4297 domains[i].trust_attribs,
4298 domains[i].trust_type );
4301 if ( buflen < len ) {
4302 SAFE_FREE(buffer);
4303 if ( (buffer = SMB_MALLOC_ARRAY(unsigned char, len)) == NULL ) {
4304 DEBUG(0,("pack_tdc_domains: failed to alloc buffer!\n"));
4305 buflen = -1;
4306 goto done;
4308 buflen = len;
4309 goto again;
4312 *buf = buffer;
4314 done:
4315 return buflen;
4318 /*********************************************************************
4319 ********************************************************************/
4321 static size_t unpack_tdc_domains( unsigned char *buf, int buflen,
4322 struct winbindd_tdc_domain **domains )
4324 fstring domain_name, dns_name, sid_string;
4325 uint32 type, attribs, flags;
4326 int num_domains;
4327 int len = 0;
4328 int i;
4329 struct winbindd_tdc_domain *list = NULL;
4331 /* get the number of domains */
4332 len += tdb_unpack( buf+len, buflen-len, "d", &num_domains);
4333 if ( len == -1 ) {
4334 DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
4335 return 0;
4338 list = TALLOC_ARRAY( NULL, struct winbindd_tdc_domain, num_domains );
4339 if ( !list ) {
4340 DEBUG(0,("unpack_tdc_domains: Failed to talloc() domain list!\n"));
4341 return 0;
4344 for ( i=0; i<num_domains; i++ ) {
4345 len += tdb_unpack( buf+len, buflen-len, "fffddd",
4346 domain_name,
4347 dns_name,
4348 sid_string,
4349 &flags,
4350 &attribs,
4351 &type );
4353 if ( len == -1 ) {
4354 DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
4355 TALLOC_FREE( list );
4356 return 0;
4359 DEBUG(11,("unpack_tdc_domains: Unpacking domain %s (%s) "
4360 "SID %s, flags = 0x%x, attribs = 0x%x, type = 0x%x\n",
4361 domain_name, dns_name, sid_string,
4362 flags, attribs, type));
4364 list[i].domain_name = talloc_strdup( list, domain_name );
4365 list[i].dns_name = talloc_strdup( list, dns_name );
4366 if ( !string_to_sid( &(list[i].sid), sid_string ) ) {
4367 DEBUG(10,("unpack_tdc_domains: no SID for domain %s\n",
4368 domain_name));
4370 list[i].trust_flags = flags;
4371 list[i].trust_attribs = attribs;
4372 list[i].trust_type = type;
4375 *domains = list;
4377 return num_domains;
4380 /*********************************************************************
4381 ********************************************************************/
4383 static bool wcache_tdc_store_list( struct winbindd_tdc_domain *domains, size_t num_domains )
4385 TDB_DATA key = make_tdc_key( lp_workgroup() );
4386 TDB_DATA data = { NULL, 0 };
4387 int ret;
4389 if ( !key.dptr )
4390 return false;
4392 /* See if we were asked to delete the cache entry */
4394 if ( !domains ) {
4395 ret = tdb_delete( wcache->tdb, key );
4396 goto done;
4399 data.dsize = pack_tdc_domains( domains, num_domains, &data.dptr );
4401 if ( !data.dptr ) {
4402 ret = -1;
4403 goto done;
4406 ret = tdb_store( wcache->tdb, key, data, 0 );
4408 done:
4409 SAFE_FREE( data.dptr );
4410 SAFE_FREE( key.dptr );
4412 return ( ret != -1 );
4415 /*********************************************************************
4416 ********************************************************************/
4418 bool wcache_tdc_fetch_list( struct winbindd_tdc_domain **domains, size_t *num_domains )
4420 TDB_DATA key = make_tdc_key( lp_workgroup() );
4421 TDB_DATA data = { NULL, 0 };
4423 *domains = NULL;
4424 *num_domains = 0;
4426 if ( !key.dptr )
4427 return false;
4429 data = tdb_fetch( wcache->tdb, key );
4431 SAFE_FREE( key.dptr );
4433 if ( !data.dptr )
4434 return false;
4436 *num_domains = unpack_tdc_domains( data.dptr, data.dsize, domains );
4438 SAFE_FREE( data.dptr );
4440 if ( !*domains )
4441 return false;
4443 return true;
4446 /*********************************************************************
4447 ********************************************************************/
4449 bool wcache_tdc_add_domain( struct winbindd_domain *domain )
4451 struct winbindd_tdc_domain *dom_list = NULL;
4452 size_t num_domains = 0;
4453 bool ret = false;
4455 DEBUG(10,("wcache_tdc_add_domain: Adding domain %s (%s), SID %s, "
4456 "flags = 0x%x, attributes = 0x%x, type = 0x%x\n",
4457 domain->name, domain->alt_name,
4458 sid_string_dbg(&domain->sid),
4459 domain->domain_flags,
4460 domain->domain_trust_attribs,
4461 domain->domain_type));
4463 if ( !init_wcache() ) {
4464 return false;
4467 /* fetch the list */
4469 wcache_tdc_fetch_list( &dom_list, &num_domains );
4471 /* add the new domain */
4473 if ( !add_wbdomain_to_tdc_array( domain, &dom_list, &num_domains ) ) {
4474 goto done;
4477 /* pack the domain */
4479 if ( !wcache_tdc_store_list( dom_list, num_domains ) ) {
4480 goto done;
4483 /* Success */
4485 ret = true;
4486 done:
4487 TALLOC_FREE( dom_list );
4489 return ret;
4492 /*********************************************************************
4493 ********************************************************************/
4495 struct winbindd_tdc_domain * wcache_tdc_fetch_domain( TALLOC_CTX *ctx, const char *name )
4497 struct winbindd_tdc_domain *dom_list = NULL;
4498 size_t num_domains = 0;
4499 int i;
4500 struct winbindd_tdc_domain *d = NULL;
4502 DEBUG(10,("wcache_tdc_fetch_domain: Searching for domain %s\n", name));
4504 if ( !init_wcache() ) {
4505 return false;
4508 /* fetch the list */
4510 wcache_tdc_fetch_list( &dom_list, &num_domains );
4512 for ( i=0; i<num_domains; i++ ) {
4513 if ( strequal(name, dom_list[i].domain_name) ||
4514 strequal(name, dom_list[i].dns_name) )
4516 DEBUG(10,("wcache_tdc_fetch_domain: Found domain %s\n",
4517 name));
4519 d = TALLOC_P( ctx, struct winbindd_tdc_domain );
4520 if ( !d )
4521 break;
4523 d->domain_name = talloc_strdup( d, dom_list[i].domain_name );
4524 d->dns_name = talloc_strdup( d, dom_list[i].dns_name );
4525 sid_copy( &d->sid, &dom_list[i].sid );
4526 d->trust_flags = dom_list[i].trust_flags;
4527 d->trust_type = dom_list[i].trust_type;
4528 d->trust_attribs = dom_list[i].trust_attribs;
4530 break;
4534 TALLOC_FREE( dom_list );
4536 return d;
4540 /*********************************************************************
4541 ********************************************************************/
4543 void wcache_tdc_clear( void )
4545 if ( !init_wcache() )
4546 return;
4548 wcache_tdc_store_list( NULL, 0 );
4550 return;
4554 /*********************************************************************
4555 ********************************************************************/
4557 static void wcache_save_user_pwinfo(struct winbindd_domain *domain,
4558 NTSTATUS status,
4559 const struct dom_sid *user_sid,
4560 const char *homedir,
4561 const char *shell,
4562 const char *gecos,
4563 uint32 gid)
4565 struct cache_entry *centry;
4566 fstring tmp;
4568 if ( (centry = centry_start(domain, status)) == NULL )
4569 return;
4571 centry_put_string( centry, homedir );
4572 centry_put_string( centry, shell );
4573 centry_put_string( centry, gecos );
4574 centry_put_uint32( centry, gid );
4576 centry_end(centry, "NSS/PWINFO/%s", sid_to_fstring(tmp, user_sid) );
4578 DEBUG(10,("wcache_save_user_pwinfo: %s\n", sid_string_dbg(user_sid) ));
4580 centry_free(centry);
4583 NTSTATUS nss_get_info_cached( struct winbindd_domain *domain,
4584 const struct dom_sid *user_sid,
4585 TALLOC_CTX *ctx,
4586 ADS_STRUCT *ads, LDAPMessage *msg,
4587 const char **homedir, const char **shell,
4588 const char **gecos, gid_t *p_gid)
4590 struct winbind_cache *cache = get_cache(domain);
4591 struct cache_entry *centry = NULL;
4592 NTSTATUS nt_status;
4593 fstring tmp;
4595 if (!cache->tdb)
4596 goto do_query;
4598 centry = wcache_fetch(cache, domain, "NSS/PWINFO/%s",
4599 sid_to_fstring(tmp, user_sid));
4601 if (!centry)
4602 goto do_query;
4604 *homedir = centry_string( centry, ctx );
4605 *shell = centry_string( centry, ctx );
4606 *gecos = centry_string( centry, ctx );
4607 *p_gid = centry_uint32( centry );
4609 centry_free(centry);
4611 DEBUG(10,("nss_get_info_cached: [Cached] - user_sid %s\n",
4612 sid_string_dbg(user_sid)));
4614 return NT_STATUS_OK;
4616 do_query:
4618 nt_status = nss_get_info( domain->name, user_sid, ctx, ads, msg,
4619 homedir, shell, gecos, p_gid );
4621 DEBUG(10, ("nss_get_info returned %s\n", nt_errstr(nt_status)));
4623 if ( NT_STATUS_IS_OK(nt_status) ) {
4624 DEBUG(10, ("result:\n\thomedir = '%s'\n", *homedir));
4625 DEBUGADD(10, ("\tshell = '%s'\n", *shell));
4626 DEBUGADD(10, ("\tgecos = '%s'\n", *gecos));
4627 DEBUGADD(10, ("\tgid = '%u'\n", (unsigned int)*p_gid));
4629 wcache_save_user_pwinfo( domain, nt_status, user_sid,
4630 *homedir, *shell, *gecos, *p_gid );
4633 if ( NT_STATUS_EQUAL( nt_status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND ) ) {
4634 DEBUG(5,("nss_get_info_cached: Setting domain %s offline\n",
4635 domain->name ));
4636 set_domain_offline( domain );
4639 return nt_status;
4643 /* the cache backend methods are exposed via this structure */
4644 struct winbindd_methods cache_methods = {
4645 true,
4646 query_user_list,
4647 enum_dom_groups,
4648 enum_local_groups,
4649 name_to_sid,
4650 sid_to_name,
4651 rids_to_names,
4652 query_user,
4653 lookup_usergroups,
4654 lookup_useraliases,
4655 lookup_groupmem,
4656 sequence_number,
4657 lockout_policy,
4658 password_policy,
4659 trusted_domains
4662 static bool wcache_ndr_key(TALLOC_CTX *mem_ctx, char *domain_name,
4663 uint32_t opnum, const DATA_BLOB *req,
4664 TDB_DATA *pkey)
4666 char *key;
4667 size_t keylen;
4669 key = talloc_asprintf(mem_ctx, "NDR/%s/%d/", domain_name, (int)opnum);
4670 if (key == NULL) {
4671 return false;
4673 keylen = talloc_get_size(key) - 1;
4675 key = talloc_realloc(mem_ctx, key, char, keylen + req->length);
4676 if (key == NULL) {
4677 return false;
4679 memcpy(key + keylen, req->data, req->length);
4681 pkey->dptr = (uint8_t *)key;
4682 pkey->dsize = talloc_get_size(key);
4683 return true;
4686 static bool wcache_opnum_cacheable(uint32_t opnum)
4688 switch (opnum) {
4689 case NDR_WBINT_PING:
4690 case NDR_WBINT_QUERYSEQUENCENUMBER:
4691 case NDR_WBINT_ALLOCATEUID:
4692 case NDR_WBINT_ALLOCATEGID:
4693 case NDR_WBINT_CHECKMACHINEACCOUNT:
4694 case NDR_WBINT_CHANGEMACHINEACCOUNT:
4695 case NDR_WBINT_PINGDC:
4696 return false;
4698 return true;
4701 bool wcache_fetch_ndr(TALLOC_CTX *mem_ctx, struct winbindd_domain *domain,
4702 uint32_t opnum, const DATA_BLOB *req, DATA_BLOB *resp)
4704 TDB_DATA key, data;
4705 bool ret = false;
4707 if (!wcache_opnum_cacheable(opnum) ||
4708 is_my_own_sam_domain(domain) ||
4709 is_builtin_domain(domain)) {
4710 return false;
4713 if (wcache->tdb == NULL) {
4714 return false;
4717 if (!wcache_ndr_key(talloc_tos(), domain->name, opnum, req, &key)) {
4718 return false;
4720 data = tdb_fetch(wcache->tdb, key);
4721 TALLOC_FREE(key.dptr);
4723 if (data.dptr == NULL) {
4724 return false;
4726 if (data.dsize < 4) {
4727 goto fail;
4730 if (!is_domain_offline(domain)) {
4731 uint32_t entry_seqnum, dom_seqnum, last_check;
4733 if (!wcache_fetch_seqnum(domain->name, &dom_seqnum,
4734 &last_check)) {
4735 goto fail;
4737 entry_seqnum = IVAL(data.dptr, 0);
4738 if (entry_seqnum != dom_seqnum) {
4739 DEBUG(10, ("Entry has wrong sequence number: %d\n",
4740 (int)entry_seqnum));
4741 goto fail;
4745 resp->data = (uint8_t *)talloc_memdup(mem_ctx, data.dptr + 4,
4746 data.dsize - 4);
4747 if (resp->data == NULL) {
4748 DEBUG(10, ("talloc failed\n"));
4749 goto fail;
4751 resp->length = data.dsize - 4;
4753 ret = true;
4754 fail:
4755 SAFE_FREE(data.dptr);
4756 return ret;
4759 void wcache_store_ndr(struct winbindd_domain *domain, uint32_t opnum,
4760 const DATA_BLOB *req, const DATA_BLOB *resp)
4762 TDB_DATA key, data;
4763 uint32_t dom_seqnum, last_check;
4765 if (!wcache_opnum_cacheable(opnum) ||
4766 is_my_own_sam_domain(domain) ||
4767 is_builtin_domain(domain)) {
4768 return;
4771 if (wcache->tdb == NULL) {
4772 return;
4775 if (!wcache_fetch_seqnum(domain->name, &dom_seqnum, &last_check)) {
4776 DEBUG(10, ("could not fetch seqnum for domain %s\n",
4777 domain->name));
4778 return;
4781 if (!wcache_ndr_key(talloc_tos(), domain->name, opnum, req, &key)) {
4782 return;
4785 data.dsize = resp->length + 4;
4786 data.dptr = talloc_array(key.dptr, uint8_t, data.dsize);
4787 if (data.dptr == NULL) {
4788 goto done;
4791 SIVAL(data.dptr, 0, dom_seqnum);
4792 memcpy(data.dptr+4, resp->data, resp->length);
4794 tdb_store(wcache->tdb, key, data, 0);
4796 done:
4797 TALLOC_FREE(key.dptr);
4798 return;