s3-smbclient: Fix cli_errstr() usage (part of bug #7864)
[Samba/vl.git] / source3 / winbindd / winbindd_cache.c
blob832e8efe3c5000dc1e1b332b0412b8f52bed40f1
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"
33 #include "../libcli/security/security.h"
35 #undef DBGC_CLASS
36 #define DBGC_CLASS DBGC_WINBIND
38 #define WINBINDD_CACHE_VERSION 2
39 #define WINBINDD_CACHE_VERSION_KEYSTR "WINBINDD_CACHE_VERSION"
41 extern struct winbindd_methods reconnect_methods;
42 #ifdef HAVE_ADS
43 extern struct winbindd_methods ads_methods;
44 #endif
45 extern struct winbindd_methods builtin_passdb_methods;
46 extern struct winbindd_methods sam_passdb_methods;
49 * JRA. KEEP THIS LIST UP TO DATE IF YOU ADD CACHE ENTRIES.
50 * Here are the list of entry types that are *not* stored
51 * as form struct cache_entry in the cache.
54 static const char *non_centry_keys[] = {
55 "SEQNUM/",
56 "DR/",
57 "DE/",
58 "WINBINDD_OFFLINE",
59 WINBINDD_CACHE_VERSION_KEYSTR,
60 NULL
63 /************************************************************************
64 Is this key a non-centry type ?
65 ************************************************************************/
67 static bool is_non_centry_key(TDB_DATA kbuf)
69 int i;
71 if (kbuf.dptr == NULL || kbuf.dsize == 0) {
72 return false;
74 for (i = 0; non_centry_keys[i] != NULL; i++) {
75 size_t namelen = strlen(non_centry_keys[i]);
76 if (kbuf.dsize < namelen) {
77 continue;
79 if (strncmp(non_centry_keys[i], (const char *)kbuf.dptr, namelen) == 0) {
80 return true;
83 return false;
86 /* Global online/offline state - False when online. winbindd starts up online
87 and sets this to true if the first query fails and there's an entry in
88 the cache tdb telling us to stay offline. */
90 static bool global_winbindd_offline_state;
92 struct winbind_cache {
93 TDB_CONTEXT *tdb;
96 struct cache_entry {
97 NTSTATUS status;
98 uint32 sequence_number;
99 uint64_t timeout;
100 uint8 *data;
101 uint32 len, ofs;
104 void (*smb_panic_fn)(const char *const why) = smb_panic;
106 #define WINBINDD_MAX_CACHE_SIZE (50*1024*1024)
108 static struct winbind_cache *wcache;
110 /* get the winbind_cache structure */
111 static struct winbind_cache *get_cache(struct winbindd_domain *domain)
113 struct winbind_cache *ret = wcache;
115 /* We have to know what type of domain we are dealing with first. */
117 if (domain->internal) {
118 domain->backend = &builtin_passdb_methods;
119 domain->initialized = True;
122 if (strequal(domain->name, get_global_sam_name()) &&
123 sid_check_is_domain(&domain->sid)) {
124 domain->backend = &sam_passdb_methods;
125 domain->initialized = True;
128 if ( !domain->initialized ) {
129 init_dc_connection( domain );
133 OK. listen up becasue I'm only going to say this once.
134 We have the following scenarios to consider
135 (a) trusted AD domains on a Samba DC,
136 (b) trusted AD domains and we are joined to a non-kerberos domain
137 (c) trusted AD domains and we are joined to a kerberos (AD) domain
139 For (a) we can always contact the trusted domain using krb5
140 since we have the domain trust account password
142 For (b) we can only use RPC since we have no way of
143 getting a krb5 ticket in our own domain
145 For (c) we can always use krb5 since we have a kerberos trust
147 --jerry
150 if (!domain->backend) {
151 #ifdef HAVE_ADS
152 struct winbindd_domain *our_domain = domain;
154 /* find our domain first so we can figure out if we
155 are joined to a kerberized domain */
157 if ( !domain->primary )
158 our_domain = find_our_domain();
160 if ((our_domain->active_directory || IS_DC)
161 && domain->active_directory
162 && !lp_winbind_rpc_only()) {
163 DEBUG(5,("get_cache: Setting ADS methods for domain %s\n", domain->name));
164 domain->backend = &ads_methods;
165 } else {
166 #endif /* HAVE_ADS */
167 DEBUG(5,("get_cache: Setting MS-RPC methods for domain %s\n", domain->name));
168 domain->backend = &reconnect_methods;
169 #ifdef HAVE_ADS
171 #endif /* HAVE_ADS */
174 if (ret)
175 return ret;
177 ret = SMB_XMALLOC_P(struct winbind_cache);
178 ZERO_STRUCTP(ret);
180 wcache = ret;
181 wcache_flush_cache();
183 return ret;
187 free a centry structure
189 static void centry_free(struct cache_entry *centry)
191 if (!centry)
192 return;
193 SAFE_FREE(centry->data);
194 free(centry);
197 static bool centry_check_bytes(struct cache_entry *centry, size_t nbytes)
199 if (centry->len - centry->ofs < nbytes) {
200 DEBUG(0,("centry corruption? needed %u bytes, have %d\n",
201 (unsigned int)nbytes,
202 centry->len - centry->ofs));
203 return false;
205 return true;
209 pull a uint64_t from a cache entry
211 static uint64_t centry_uint64_t(struct cache_entry *centry)
213 uint64_t ret;
215 if (!centry_check_bytes(centry, 8)) {
216 smb_panic_fn("centry_uint64_t");
218 ret = BVAL(centry->data, centry->ofs);
219 centry->ofs += 8;
220 return ret;
224 pull a uint32 from a cache entry
226 static uint32 centry_uint32(struct cache_entry *centry)
228 uint32 ret;
230 if (!centry_check_bytes(centry, 4)) {
231 smb_panic_fn("centry_uint32");
233 ret = IVAL(centry->data, centry->ofs);
234 centry->ofs += 4;
235 return ret;
239 pull a uint16 from a cache entry
241 static uint16 centry_uint16(struct cache_entry *centry)
243 uint16 ret;
244 if (!centry_check_bytes(centry, 2)) {
245 smb_panic_fn("centry_uint16");
247 ret = CVAL(centry->data, centry->ofs);
248 centry->ofs += 2;
249 return ret;
253 pull a uint8 from a cache entry
255 static uint8 centry_uint8(struct cache_entry *centry)
257 uint8 ret;
258 if (!centry_check_bytes(centry, 1)) {
259 smb_panic_fn("centry_uint8");
261 ret = CVAL(centry->data, centry->ofs);
262 centry->ofs += 1;
263 return ret;
267 pull a NTTIME from a cache entry
269 static NTTIME centry_nttime(struct cache_entry *centry)
271 NTTIME ret;
272 if (!centry_check_bytes(centry, 8)) {
273 smb_panic_fn("centry_nttime");
275 ret = IVAL(centry->data, centry->ofs);
276 centry->ofs += 4;
277 ret += (uint64)IVAL(centry->data, centry->ofs) << 32;
278 centry->ofs += 4;
279 return ret;
283 pull a time_t from a cache entry. time_t stored portably as a 64-bit time.
285 static time_t centry_time(struct cache_entry *centry)
287 return (time_t)centry_nttime(centry);
290 /* pull a string from a cache entry, using the supplied
291 talloc context
293 static char *centry_string(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
295 uint32 len;
296 char *ret;
298 len = centry_uint8(centry);
300 if (len == 0xFF) {
301 /* a deliberate NULL string */
302 return NULL;
305 if (!centry_check_bytes(centry, (size_t)len)) {
306 smb_panic_fn("centry_string");
309 ret = TALLOC_ARRAY(mem_ctx, char, len+1);
310 if (!ret) {
311 smb_panic_fn("centry_string out of memory\n");
313 memcpy(ret,centry->data + centry->ofs, len);
314 ret[len] = 0;
315 centry->ofs += len;
316 return ret;
319 /* pull a hash16 from a cache entry, using the supplied
320 talloc context
322 static char *centry_hash16(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
324 uint32 len;
325 char *ret;
327 len = centry_uint8(centry);
329 if (len != 16) {
330 DEBUG(0,("centry corruption? hash len (%u) != 16\n",
331 len ));
332 return NULL;
335 if (!centry_check_bytes(centry, 16)) {
336 return NULL;
339 ret = TALLOC_ARRAY(mem_ctx, char, 16);
340 if (!ret) {
341 smb_panic_fn("centry_hash out of memory\n");
343 memcpy(ret,centry->data + centry->ofs, 16);
344 centry->ofs += 16;
345 return ret;
348 /* pull a sid from a cache entry, using the supplied
349 talloc context
351 static bool centry_sid(struct cache_entry *centry, struct dom_sid *sid)
353 char *sid_string;
354 bool ret;
356 sid_string = centry_string(centry, talloc_tos());
357 if (sid_string == NULL) {
358 return false;
360 ret = string_to_sid(sid, sid_string);
361 TALLOC_FREE(sid_string);
362 return ret;
367 pull a NTSTATUS from a cache entry
369 static NTSTATUS centry_ntstatus(struct cache_entry *centry)
371 NTSTATUS status;
373 status = NT_STATUS(centry_uint32(centry));
374 return status;
378 /* the server is considered down if it can't give us a sequence number */
379 static bool wcache_server_down(struct winbindd_domain *domain)
381 bool ret;
383 if (!wcache->tdb)
384 return false;
386 ret = (domain->sequence_number == DOM_SEQUENCE_NONE);
388 if (ret)
389 DEBUG(10,("wcache_server_down: server for Domain %s down\n",
390 domain->name ));
391 return ret;
394 static bool wcache_fetch_seqnum(const char *domain_name, uint32_t *seqnum,
395 uint32_t *last_seq_check)
397 char *key;
398 TDB_DATA data;
400 if (wcache->tdb == NULL) {
401 DEBUG(10,("wcache_fetch_seqnum: tdb == NULL\n"));
402 return false;
405 key = talloc_asprintf(talloc_tos(), "SEQNUM/%s", domain_name);
406 if (key == NULL) {
407 DEBUG(10, ("talloc failed\n"));
408 return false;
411 data = tdb_fetch_bystring(wcache->tdb, key);
412 TALLOC_FREE(key);
414 if (data.dptr == NULL) {
415 DEBUG(10, ("wcache_fetch_seqnum: %s not found\n",
416 domain_name));
417 return false;
419 if (data.dsize != 8) {
420 DEBUG(10, ("wcache_fetch_seqnum: invalid data size %d\n",
421 (int)data.dsize));
422 SAFE_FREE(data.dptr);
423 return false;
426 *seqnum = IVAL(data.dptr, 0);
427 *last_seq_check = IVAL(data.dptr, 4);
428 SAFE_FREE(data.dptr);
430 return true;
433 static NTSTATUS fetch_cache_seqnum( struct winbindd_domain *domain, time_t now )
435 uint32 last_check, time_diff;
437 if (!wcache_fetch_seqnum(domain->name, &domain->sequence_number,
438 &last_check)) {
439 return NT_STATUS_UNSUCCESSFUL;
441 domain->last_seq_check = last_check;
443 /* have we expired? */
445 time_diff = now - domain->last_seq_check;
446 if ( time_diff > lp_winbind_cache_time() ) {
447 DEBUG(10,("fetch_cache_seqnum: timeout [%s][%u @ %u]\n",
448 domain->name, domain->sequence_number,
449 (uint32)domain->last_seq_check));
450 return NT_STATUS_UNSUCCESSFUL;
453 DEBUG(10,("fetch_cache_seqnum: success [%s][%u @ %u]\n",
454 domain->name, domain->sequence_number,
455 (uint32)domain->last_seq_check));
457 return NT_STATUS_OK;
460 bool wcache_store_seqnum(const char *domain_name, uint32_t seqnum,
461 time_t last_seq_check)
463 char *key_str;
464 uint8_t buf[8];
465 int ret;
467 if (wcache->tdb == NULL) {
468 DEBUG(10, ("wcache_store_seqnum: wcache->tdb == NULL\n"));
469 return false;
472 key_str = talloc_asprintf(talloc_tos(), "SEQNUM/%s", domain_name);
473 if (key_str == NULL) {
474 DEBUG(10, ("talloc_asprintf failed\n"));
475 return false;
478 SIVAL(buf, 0, seqnum);
479 SIVAL(buf, 4, last_seq_check);
481 ret = tdb_store_bystring(wcache->tdb, key_str,
482 make_tdb_data(buf, sizeof(buf)), TDB_REPLACE);
483 TALLOC_FREE(key_str);
484 if (ret == -1) {
485 DEBUG(10, ("tdb_store_bystring failed: %s\n",
486 tdb_errorstr(wcache->tdb)));
487 TALLOC_FREE(key_str);
488 return false;
491 DEBUG(10, ("wcache_store_seqnum: success [%s][%u @ %u]\n",
492 domain_name, seqnum, (unsigned)last_seq_check));
494 return true;
497 static bool store_cache_seqnum( struct winbindd_domain *domain )
499 return wcache_store_seqnum(domain->name, domain->sequence_number,
500 domain->last_seq_check);
504 refresh the domain sequence number. If force is true
505 then always refresh it, no matter how recently we fetched it
508 static void refresh_sequence_number(struct winbindd_domain *domain, bool force)
510 NTSTATUS status;
511 unsigned time_diff;
512 time_t t = time(NULL);
513 unsigned cache_time = lp_winbind_cache_time();
515 if (is_domain_offline(domain)) {
516 return;
519 get_cache( domain );
521 #if 0 /* JERRY -- disable as the default cache time is now 5 minutes */
522 /* trying to reconnect is expensive, don't do it too often */
523 if (domain->sequence_number == DOM_SEQUENCE_NONE) {
524 cache_time *= 8;
526 #endif
528 time_diff = t - domain->last_seq_check;
530 /* see if we have to refetch the domain sequence number */
531 if (!force && (time_diff < cache_time) &&
532 (domain->sequence_number != DOM_SEQUENCE_NONE) &&
533 NT_STATUS_IS_OK(domain->last_status)) {
534 DEBUG(10, ("refresh_sequence_number: %s time ok\n", domain->name));
535 goto done;
538 /* try to get the sequence number from the tdb cache first */
539 /* this will update the timestamp as well */
541 status = fetch_cache_seqnum( domain, t );
542 if (NT_STATUS_IS_OK(status) &&
543 (domain->sequence_number != DOM_SEQUENCE_NONE) &&
544 NT_STATUS_IS_OK(domain->last_status)) {
545 goto done;
548 /* important! make sure that we know if this is a native
549 mode domain or not. And that we can contact it. */
551 if ( winbindd_can_contact_domain( domain ) ) {
552 status = domain->backend->sequence_number(domain,
553 &domain->sequence_number);
554 } else {
555 /* just use the current time */
556 status = NT_STATUS_OK;
557 domain->sequence_number = time(NULL);
561 /* the above call could have set our domain->backend to NULL when
562 * coming from offline to online mode, make sure to reinitialize the
563 * backend - Guenther */
564 get_cache( domain );
566 if (!NT_STATUS_IS_OK(status)) {
567 DEBUG(10,("refresh_sequence_number: failed with %s\n", nt_errstr(status)));
568 domain->sequence_number = DOM_SEQUENCE_NONE;
571 domain->last_status = status;
572 domain->last_seq_check = time(NULL);
574 /* save the new sequence number in the cache */
575 store_cache_seqnum( domain );
577 done:
578 DEBUG(10, ("refresh_sequence_number: %s seq number is now %d\n",
579 domain->name, domain->sequence_number));
581 return;
585 decide if a cache entry has expired
587 static bool centry_expired(struct winbindd_domain *domain, const char *keystr, struct cache_entry *centry)
589 /* If we've been told to be offline - stay in that state... */
590 if (lp_winbind_offline_logon() && global_winbindd_offline_state) {
591 DEBUG(10,("centry_expired: Key %s for domain %s valid as winbindd is globally offline.\n",
592 keystr, domain->name ));
593 return false;
596 /* when the domain is offline return the cached entry.
597 * This deals with transient offline states... */
599 if (!domain->online) {
600 DEBUG(10,("centry_expired: Key %s for domain %s valid as domain is offline.\n",
601 keystr, domain->name ));
602 return false;
605 /* if the server is OK and our cache entry came from when it was down then
606 the entry is invalid */
607 if ((domain->sequence_number != DOM_SEQUENCE_NONE) &&
608 (centry->sequence_number == DOM_SEQUENCE_NONE)) {
609 DEBUG(10,("centry_expired: Key %s for domain %s invalid sequence.\n",
610 keystr, domain->name ));
611 return true;
614 /* if the server is down or the cache entry is not older than the
615 current sequence number or it did not timeout then it is OK */
616 if (wcache_server_down(domain)
617 || ((centry->sequence_number == domain->sequence_number)
618 && (centry->timeout > time(NULL)))) {
619 DEBUG(10,("centry_expired: Key %s for domain %s is good.\n",
620 keystr, domain->name ));
621 return false;
624 DEBUG(10,("centry_expired: Key %s for domain %s expired\n",
625 keystr, domain->name ));
627 /* it's expired */
628 return true;
631 static struct cache_entry *wcache_fetch_raw(char *kstr)
633 TDB_DATA data;
634 struct cache_entry *centry;
635 TDB_DATA key;
637 key = string_tdb_data(kstr);
638 data = tdb_fetch(wcache->tdb, key);
639 if (!data.dptr) {
640 /* a cache miss */
641 return NULL;
644 centry = SMB_XMALLOC_P(struct cache_entry);
645 centry->data = (unsigned char *)data.dptr;
646 centry->len = data.dsize;
647 centry->ofs = 0;
649 if (centry->len < 16) {
650 /* huh? corrupt cache? */
651 DEBUG(10,("wcache_fetch_raw: Corrupt cache for key %s "
652 "(len < 16)?\n", kstr));
653 centry_free(centry);
654 return NULL;
657 centry->status = centry_ntstatus(centry);
658 centry->sequence_number = centry_uint32(centry);
659 centry->timeout = centry_uint64_t(centry);
661 return centry;
664 static bool is_my_own_sam_domain(struct winbindd_domain *domain)
666 if (strequal(domain->name, get_global_sam_name()) &&
667 sid_check_is_domain(&domain->sid)) {
668 return true;
671 return false;
674 static bool is_builtin_domain(struct winbindd_domain *domain)
676 if (strequal(domain->name, "BUILTIN") &&
677 sid_check_is_builtin(&domain->sid)) {
678 return true;
681 return false;
685 fetch an entry from the cache, with a varargs key. auto-fetch the sequence
686 number and return status
688 static struct cache_entry *wcache_fetch(struct winbind_cache *cache,
689 struct winbindd_domain *domain,
690 const char *format, ...) PRINTF_ATTRIBUTE(3,4);
691 static struct cache_entry *wcache_fetch(struct winbind_cache *cache,
692 struct winbindd_domain *domain,
693 const char *format, ...)
695 va_list ap;
696 char *kstr;
697 struct cache_entry *centry;
699 if (!winbindd_use_cache() ||
700 is_my_own_sam_domain(domain) ||
701 is_builtin_domain(domain)) {
702 return NULL;
705 refresh_sequence_number(domain, false);
707 va_start(ap, format);
708 smb_xvasprintf(&kstr, format, ap);
709 va_end(ap);
711 centry = wcache_fetch_raw(kstr);
712 if (centry == NULL) {
713 free(kstr);
714 return NULL;
717 if (centry_expired(domain, kstr, centry)) {
719 DEBUG(10,("wcache_fetch: entry %s expired for domain %s\n",
720 kstr, domain->name ));
722 centry_free(centry);
723 free(kstr);
724 return NULL;
727 DEBUG(10,("wcache_fetch: returning entry %s for domain %s\n",
728 kstr, domain->name ));
730 free(kstr);
731 return centry;
734 static void wcache_delete(const char *format, ...) PRINTF_ATTRIBUTE(1,2);
735 static void wcache_delete(const char *format, ...)
737 va_list ap;
738 char *kstr;
739 TDB_DATA key;
741 va_start(ap, format);
742 smb_xvasprintf(&kstr, format, ap);
743 va_end(ap);
745 key = string_tdb_data(kstr);
747 tdb_delete(wcache->tdb, key);
748 free(kstr);
752 make sure we have at least len bytes available in a centry
754 static void centry_expand(struct cache_entry *centry, uint32 len)
756 if (centry->len - centry->ofs >= len)
757 return;
758 centry->len *= 2;
759 centry->data = SMB_REALLOC_ARRAY(centry->data, unsigned char,
760 centry->len);
761 if (!centry->data) {
762 DEBUG(0,("out of memory: needed %d bytes in centry_expand\n", centry->len));
763 smb_panic_fn("out of memory in centry_expand");
768 push a uint64_t into a centry
770 static void centry_put_uint64_t(struct cache_entry *centry, uint64_t v)
772 centry_expand(centry, 8);
773 SBVAL(centry->data, centry->ofs, v);
774 centry->ofs += 8;
778 push a uint32 into a centry
780 static void centry_put_uint32(struct cache_entry *centry, uint32 v)
782 centry_expand(centry, 4);
783 SIVAL(centry->data, centry->ofs, v);
784 centry->ofs += 4;
788 push a uint16 into a centry
790 static void centry_put_uint16(struct cache_entry *centry, uint16 v)
792 centry_expand(centry, 2);
793 SIVAL(centry->data, centry->ofs, v);
794 centry->ofs += 2;
798 push a uint8 into a centry
800 static void centry_put_uint8(struct cache_entry *centry, uint8 v)
802 centry_expand(centry, 1);
803 SCVAL(centry->data, centry->ofs, v);
804 centry->ofs += 1;
808 push a string into a centry
810 static void centry_put_string(struct cache_entry *centry, const char *s)
812 int len;
814 if (!s) {
815 /* null strings are marked as len 0xFFFF */
816 centry_put_uint8(centry, 0xFF);
817 return;
820 len = strlen(s);
821 /* can't handle more than 254 char strings. Truncating is probably best */
822 if (len > 254) {
823 DEBUG(10,("centry_put_string: truncating len (%d) to: 254\n", len));
824 len = 254;
826 centry_put_uint8(centry, len);
827 centry_expand(centry, len);
828 memcpy(centry->data + centry->ofs, s, len);
829 centry->ofs += len;
833 push a 16 byte hash into a centry - treat as 16 byte string.
835 static void centry_put_hash16(struct cache_entry *centry, const uint8 val[16])
837 centry_put_uint8(centry, 16);
838 centry_expand(centry, 16);
839 memcpy(centry->data + centry->ofs, val, 16);
840 centry->ofs += 16;
843 static void centry_put_sid(struct cache_entry *centry, const struct dom_sid *sid)
845 fstring sid_string;
846 centry_put_string(centry, sid_to_fstring(sid_string, sid));
851 put NTSTATUS into a centry
853 static void centry_put_ntstatus(struct cache_entry *centry, NTSTATUS status)
855 uint32 status_value = NT_STATUS_V(status);
856 centry_put_uint32(centry, status_value);
861 push a NTTIME into a centry
863 static void centry_put_nttime(struct cache_entry *centry, NTTIME nt)
865 centry_expand(centry, 8);
866 SIVAL(centry->data, centry->ofs, nt & 0xFFFFFFFF);
867 centry->ofs += 4;
868 SIVAL(centry->data, centry->ofs, nt >> 32);
869 centry->ofs += 4;
873 push a time_t into a centry - use a 64 bit size.
874 NTTIME here is being used as a convenient 64-bit size.
876 static void centry_put_time(struct cache_entry *centry, time_t t)
878 NTTIME nt = (NTTIME)t;
879 centry_put_nttime(centry, nt);
883 start a centry for output. When finished, call centry_end()
885 struct cache_entry *centry_start(struct winbindd_domain *domain, NTSTATUS status)
887 struct cache_entry *centry;
889 if (!wcache->tdb)
890 return NULL;
892 centry = SMB_XMALLOC_P(struct cache_entry);
894 centry->len = 8192; /* reasonable default */
895 centry->data = SMB_XMALLOC_ARRAY(uint8, centry->len);
896 centry->ofs = 0;
897 centry->sequence_number = domain->sequence_number;
898 centry->timeout = lp_winbind_cache_time() + time(NULL);
899 centry_put_ntstatus(centry, status);
900 centry_put_uint32(centry, centry->sequence_number);
901 centry_put_uint64_t(centry, centry->timeout);
902 return centry;
906 finish a centry and write it to the tdb
908 static void centry_end(struct cache_entry *centry, const char *format, ...) PRINTF_ATTRIBUTE(2,3);
909 static void centry_end(struct cache_entry *centry, const char *format, ...)
911 va_list ap;
912 char *kstr;
913 TDB_DATA key, data;
915 if (!winbindd_use_cache()) {
916 return;
919 va_start(ap, format);
920 smb_xvasprintf(&kstr, format, ap);
921 va_end(ap);
923 key = string_tdb_data(kstr);
924 data.dptr = centry->data;
925 data.dsize = centry->ofs;
927 tdb_store(wcache->tdb, key, data, TDB_REPLACE);
928 free(kstr);
931 static void wcache_save_name_to_sid(struct winbindd_domain *domain,
932 NTSTATUS status, const char *domain_name,
933 const char *name, const struct dom_sid *sid,
934 enum lsa_SidType type)
936 struct cache_entry *centry;
937 fstring uname;
939 centry = centry_start(domain, status);
940 if (!centry)
941 return;
942 centry_put_uint32(centry, type);
943 centry_put_sid(centry, sid);
944 fstrcpy(uname, name);
945 strupper_m(uname);
946 centry_end(centry, "NS/%s/%s", domain_name, uname);
947 DEBUG(10,("wcache_save_name_to_sid: %s\\%s -> %s (%s)\n", domain_name,
948 uname, sid_string_dbg(sid), nt_errstr(status)));
949 centry_free(centry);
952 static void wcache_save_sid_to_name(struct winbindd_domain *domain, NTSTATUS status,
953 const struct dom_sid *sid, const char *domain_name, const char *name, enum lsa_SidType type)
955 struct cache_entry *centry;
956 fstring sid_string;
958 centry = centry_start(domain, status);
959 if (!centry)
960 return;
962 if (NT_STATUS_IS_OK(status)) {
963 centry_put_uint32(centry, type);
964 centry_put_string(centry, domain_name);
965 centry_put_string(centry, name);
968 centry_end(centry, "SN/%s", sid_to_fstring(sid_string, sid));
969 DEBUG(10,("wcache_save_sid_to_name: %s -> %s (%s)\n", sid_string,
970 name, nt_errstr(status)));
971 centry_free(centry);
975 static void wcache_save_user(struct winbindd_domain *domain, NTSTATUS status,
976 struct wbint_userinfo *info)
978 struct cache_entry *centry;
979 fstring sid_string;
981 if (is_null_sid(&info->user_sid)) {
982 return;
985 centry = centry_start(domain, status);
986 if (!centry)
987 return;
988 centry_put_string(centry, info->acct_name);
989 centry_put_string(centry, info->full_name);
990 centry_put_string(centry, info->homedir);
991 centry_put_string(centry, info->shell);
992 centry_put_uint32(centry, info->primary_gid);
993 centry_put_sid(centry, &info->user_sid);
994 centry_put_sid(centry, &info->group_sid);
995 centry_end(centry, "U/%s", sid_to_fstring(sid_string,
996 &info->user_sid));
997 DEBUG(10,("wcache_save_user: %s (acct_name %s)\n", sid_string, info->acct_name));
998 centry_free(centry);
1001 static void wcache_save_lockout_policy(struct winbindd_domain *domain,
1002 NTSTATUS status,
1003 struct samr_DomInfo12 *lockout_policy)
1005 struct cache_entry *centry;
1007 centry = centry_start(domain, status);
1008 if (!centry)
1009 return;
1011 centry_put_nttime(centry, lockout_policy->lockout_duration);
1012 centry_put_nttime(centry, lockout_policy->lockout_window);
1013 centry_put_uint16(centry, lockout_policy->lockout_threshold);
1015 centry_end(centry, "LOC_POL/%s", domain->name);
1017 DEBUG(10,("wcache_save_lockout_policy: %s\n", domain->name));
1019 centry_free(centry);
1024 static void wcache_save_password_policy(struct winbindd_domain *domain,
1025 NTSTATUS status,
1026 struct samr_DomInfo1 *policy)
1028 struct cache_entry *centry;
1030 centry = centry_start(domain, status);
1031 if (!centry)
1032 return;
1034 centry_put_uint16(centry, policy->min_password_length);
1035 centry_put_uint16(centry, policy->password_history_length);
1036 centry_put_uint32(centry, policy->password_properties);
1037 centry_put_nttime(centry, policy->max_password_age);
1038 centry_put_nttime(centry, policy->min_password_age);
1040 centry_end(centry, "PWD_POL/%s", domain->name);
1042 DEBUG(10,("wcache_save_password_policy: %s\n", domain->name));
1044 centry_free(centry);
1047 /***************************************************************************
1048 ***************************************************************************/
1050 static void wcache_save_username_alias(struct winbindd_domain *domain,
1051 NTSTATUS status,
1052 const char *name, const char *alias)
1054 struct cache_entry *centry;
1055 fstring uname;
1057 if ( (centry = centry_start(domain, status)) == NULL )
1058 return;
1060 centry_put_string( centry, alias );
1062 fstrcpy(uname, name);
1063 strupper_m(uname);
1064 centry_end(centry, "NSS/NA/%s", uname);
1066 DEBUG(10,("wcache_save_username_alias: %s -> %s\n", name, alias ));
1068 centry_free(centry);
1071 static void wcache_save_alias_username(struct winbindd_domain *domain,
1072 NTSTATUS status,
1073 const char *alias, const char *name)
1075 struct cache_entry *centry;
1076 fstring uname;
1078 if ( (centry = centry_start(domain, status)) == NULL )
1079 return;
1081 centry_put_string( centry, name );
1083 fstrcpy(uname, alias);
1084 strupper_m(uname);
1085 centry_end(centry, "NSS/AN/%s", uname);
1087 DEBUG(10,("wcache_save_alias_username: %s -> %s\n", alias, name ));
1089 centry_free(centry);
1092 /***************************************************************************
1093 ***************************************************************************/
1095 NTSTATUS resolve_username_to_alias( TALLOC_CTX *mem_ctx,
1096 struct winbindd_domain *domain,
1097 const char *name, char **alias )
1099 struct winbind_cache *cache = get_cache(domain);
1100 struct cache_entry *centry = NULL;
1101 NTSTATUS status;
1102 char *upper_name;
1104 if ( domain->internal )
1105 return NT_STATUS_NOT_SUPPORTED;
1107 if (!cache->tdb)
1108 goto do_query;
1110 if ( (upper_name = SMB_STRDUP(name)) == NULL )
1111 return NT_STATUS_NO_MEMORY;
1112 strupper_m(upper_name);
1114 centry = wcache_fetch(cache, domain, "NSS/NA/%s", upper_name);
1116 SAFE_FREE( upper_name );
1118 if (!centry)
1119 goto do_query;
1121 status = centry->status;
1123 if (!NT_STATUS_IS_OK(status)) {
1124 centry_free(centry);
1125 return status;
1128 *alias = centry_string( centry, mem_ctx );
1130 centry_free(centry);
1132 DEBUG(10,("resolve_username_to_alias: [Cached] - mapped %s to %s\n",
1133 name, *alias ? *alias : "(none)"));
1135 return (*alias) ? NT_STATUS_OK : NT_STATUS_OBJECT_NAME_NOT_FOUND;
1137 do_query:
1139 /* If its not in cache and we are offline, then fail */
1141 if ( get_global_winbindd_state_offline() || !domain->online ) {
1142 DEBUG(8,("resolve_username_to_alias: rejecting query "
1143 "in offline mode\n"));
1144 return NT_STATUS_NOT_FOUND;
1147 status = nss_map_to_alias( mem_ctx, domain->name, name, alias );
1149 if ( NT_STATUS_IS_OK( status ) ) {
1150 wcache_save_username_alias(domain, status, name, *alias);
1153 if ( NT_STATUS_EQUAL( status, NT_STATUS_NONE_MAPPED ) ) {
1154 wcache_save_username_alias(domain, status, name, "(NULL)");
1157 DEBUG(5,("resolve_username_to_alias: backend query returned %s\n",
1158 nt_errstr(status)));
1160 if ( NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ) {
1161 set_domain_offline( domain );
1164 return status;
1167 /***************************************************************************
1168 ***************************************************************************/
1170 NTSTATUS resolve_alias_to_username( TALLOC_CTX *mem_ctx,
1171 struct winbindd_domain *domain,
1172 const char *alias, char **name )
1174 struct winbind_cache *cache = get_cache(domain);
1175 struct cache_entry *centry = NULL;
1176 NTSTATUS status;
1177 char *upper_name;
1179 if ( domain->internal )
1180 return NT_STATUS_NOT_SUPPORTED;
1182 if (!cache->tdb)
1183 goto do_query;
1185 if ( (upper_name = SMB_STRDUP(alias)) == NULL )
1186 return NT_STATUS_NO_MEMORY;
1187 strupper_m(upper_name);
1189 centry = wcache_fetch(cache, domain, "NSS/AN/%s", upper_name);
1191 SAFE_FREE( upper_name );
1193 if (!centry)
1194 goto do_query;
1196 status = centry->status;
1198 if (!NT_STATUS_IS_OK(status)) {
1199 centry_free(centry);
1200 return status;
1203 *name = centry_string( centry, mem_ctx );
1205 centry_free(centry);
1207 DEBUG(10,("resolve_alias_to_username: [Cached] - mapped %s to %s\n",
1208 alias, *name ? *name : "(none)"));
1210 return (*name) ? NT_STATUS_OK : NT_STATUS_OBJECT_NAME_NOT_FOUND;
1212 do_query:
1214 /* If its not in cache and we are offline, then fail */
1216 if ( get_global_winbindd_state_offline() || !domain->online ) {
1217 DEBUG(8,("resolve_alias_to_username: rejecting query "
1218 "in offline mode\n"));
1219 return NT_STATUS_NOT_FOUND;
1222 /* an alias cannot contain a domain prefix or '@' */
1224 if (strchr(alias, '\\') || strchr(alias, '@')) {
1225 DEBUG(10,("resolve_alias_to_username: skipping fully "
1226 "qualified name %s\n", alias));
1227 return NT_STATUS_OBJECT_NAME_INVALID;
1230 status = nss_map_from_alias( mem_ctx, domain->name, alias, name );
1232 if ( NT_STATUS_IS_OK( status ) ) {
1233 wcache_save_alias_username( domain, status, alias, *name );
1236 if (NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) {
1237 wcache_save_alias_username(domain, status, alias, "(NULL)");
1240 DEBUG(5,("resolve_alias_to_username: backend query returned %s\n",
1241 nt_errstr(status)));
1243 if ( NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ) {
1244 set_domain_offline( domain );
1247 return status;
1250 NTSTATUS wcache_cached_creds_exist(struct winbindd_domain *domain, const struct dom_sid *sid)
1252 struct winbind_cache *cache = get_cache(domain);
1253 TDB_DATA data;
1254 fstring key_str, tmp;
1255 uint32 rid;
1257 if (!cache->tdb) {
1258 return NT_STATUS_INTERNAL_DB_ERROR;
1261 if (is_null_sid(sid)) {
1262 return NT_STATUS_INVALID_SID;
1265 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1266 return NT_STATUS_INVALID_SID;
1269 fstr_sprintf(key_str, "CRED/%s", sid_to_fstring(tmp, sid));
1271 data = tdb_fetch(cache->tdb, string_tdb_data(key_str));
1272 if (!data.dptr) {
1273 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1276 SAFE_FREE(data.dptr);
1277 return NT_STATUS_OK;
1280 /* Lookup creds for a SID - copes with old (unsalted) creds as well
1281 as new salted ones. */
1283 NTSTATUS wcache_get_creds(struct winbindd_domain *domain,
1284 TALLOC_CTX *mem_ctx,
1285 const struct dom_sid *sid,
1286 const uint8 **cached_nt_pass,
1287 const uint8 **cached_salt)
1289 struct winbind_cache *cache = get_cache(domain);
1290 struct cache_entry *centry = NULL;
1291 NTSTATUS status;
1292 time_t t;
1293 uint32 rid;
1294 fstring tmp;
1296 if (!cache->tdb) {
1297 return NT_STATUS_INTERNAL_DB_ERROR;
1300 if (is_null_sid(sid)) {
1301 return NT_STATUS_INVALID_SID;
1304 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1305 return NT_STATUS_INVALID_SID;
1308 /* Try and get a salted cred first. If we can't
1309 fall back to an unsalted cred. */
1311 centry = wcache_fetch(cache, domain, "CRED/%s",
1312 sid_to_fstring(tmp, sid));
1313 if (!centry) {
1314 DEBUG(10,("wcache_get_creds: entry for [CRED/%s] not found\n",
1315 sid_string_dbg(sid)));
1316 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1319 t = centry_time(centry);
1321 /* In the salted case this isn't actually the nt_hash itself,
1322 but the MD5 of the salt + nt_hash. Let the caller
1323 sort this out. It can tell as we only return the cached_salt
1324 if we are returning a salted cred. */
1326 *cached_nt_pass = (const uint8 *)centry_hash16(centry, mem_ctx);
1327 if (*cached_nt_pass == NULL) {
1328 fstring sidstr;
1330 sid_to_fstring(sidstr, sid);
1332 /* Bad (old) cred cache. Delete and pretend we
1333 don't have it. */
1334 DEBUG(0,("wcache_get_creds: bad entry for [CRED/%s] - deleting\n",
1335 sidstr));
1336 wcache_delete("CRED/%s", sidstr);
1337 centry_free(centry);
1338 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1341 /* We only have 17 bytes more data in the salted cred case. */
1342 if (centry->len - centry->ofs == 17) {
1343 *cached_salt = (const uint8 *)centry_hash16(centry, mem_ctx);
1344 } else {
1345 *cached_salt = NULL;
1348 dump_data_pw("cached_nt_pass", *cached_nt_pass, NT_HASH_LEN);
1349 if (*cached_salt) {
1350 dump_data_pw("cached_salt", *cached_salt, NT_HASH_LEN);
1353 status = centry->status;
1355 DEBUG(10,("wcache_get_creds: [Cached] - cached creds for user %s status: %s\n",
1356 sid_string_dbg(sid), nt_errstr(status) ));
1358 centry_free(centry);
1359 return status;
1362 /* Store creds for a SID - only writes out new salted ones. */
1364 NTSTATUS wcache_save_creds(struct winbindd_domain *domain,
1365 const struct dom_sid *sid,
1366 const uint8 nt_pass[NT_HASH_LEN])
1368 struct cache_entry *centry;
1369 fstring sid_string;
1370 uint32 rid;
1371 uint8 cred_salt[NT_HASH_LEN];
1372 uint8 salted_hash[NT_HASH_LEN];
1374 if (is_null_sid(sid)) {
1375 return NT_STATUS_INVALID_SID;
1378 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1379 return NT_STATUS_INVALID_SID;
1382 centry = centry_start(domain, NT_STATUS_OK);
1383 if (!centry) {
1384 return NT_STATUS_INTERNAL_DB_ERROR;
1387 dump_data_pw("nt_pass", nt_pass, NT_HASH_LEN);
1389 centry_put_time(centry, time(NULL));
1391 /* Create a salt and then salt the hash. */
1392 generate_random_buffer(cred_salt, NT_HASH_LEN);
1393 E_md5hash(cred_salt, nt_pass, salted_hash);
1395 centry_put_hash16(centry, salted_hash);
1396 centry_put_hash16(centry, cred_salt);
1397 centry_end(centry, "CRED/%s", sid_to_fstring(sid_string, sid));
1399 DEBUG(10,("wcache_save_creds: %s\n", sid_string));
1401 centry_free(centry);
1403 return NT_STATUS_OK;
1407 /* Query display info. This is the basic user list fn */
1408 static NTSTATUS query_user_list(struct winbindd_domain *domain,
1409 TALLOC_CTX *mem_ctx,
1410 uint32 *num_entries,
1411 struct wbint_userinfo **info)
1413 struct winbind_cache *cache = get_cache(domain);
1414 struct cache_entry *centry = NULL;
1415 NTSTATUS status;
1416 unsigned int i, retry;
1417 bool old_status = domain->online;
1419 if (!cache->tdb)
1420 goto do_query;
1422 centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
1423 if (!centry)
1424 goto do_query;
1426 do_fetch_cache:
1427 *num_entries = centry_uint32(centry);
1429 if (*num_entries == 0)
1430 goto do_cached;
1432 (*info) = TALLOC_ARRAY(mem_ctx, struct wbint_userinfo, *num_entries);
1433 if (! (*info)) {
1434 smb_panic_fn("query_user_list out of memory");
1436 for (i=0; i<(*num_entries); i++) {
1437 (*info)[i].acct_name = centry_string(centry, mem_ctx);
1438 (*info)[i].full_name = centry_string(centry, mem_ctx);
1439 (*info)[i].homedir = centry_string(centry, mem_ctx);
1440 (*info)[i].shell = centry_string(centry, mem_ctx);
1441 centry_sid(centry, &(*info)[i].user_sid);
1442 centry_sid(centry, &(*info)[i].group_sid);
1445 do_cached:
1446 status = centry->status;
1448 DEBUG(10,("query_user_list: [Cached] - cached list for domain %s status: %s\n",
1449 domain->name, nt_errstr(status) ));
1451 centry_free(centry);
1452 return status;
1454 do_query:
1455 *num_entries = 0;
1456 *info = NULL;
1458 /* Return status value returned by seq number check */
1460 if (!NT_STATUS_IS_OK(domain->last_status))
1461 return domain->last_status;
1463 /* Put the query_user_list() in a retry loop. There appears to be
1464 * some bug either with Windows 2000 or Samba's handling of large
1465 * rpc replies. This manifests itself as sudden disconnection
1466 * at a random point in the enumeration of a large (60k) user list.
1467 * The retry loop simply tries the operation again. )-: It's not
1468 * pretty but an acceptable workaround until we work out what the
1469 * real problem is. */
1471 retry = 0;
1472 do {
1474 DEBUG(10,("query_user_list: [Cached] - doing backend query for list for domain %s\n",
1475 domain->name ));
1477 status = domain->backend->query_user_list(domain, mem_ctx, num_entries, info);
1478 if (!NT_STATUS_IS_OK(status)) {
1479 DEBUG(3, ("query_user_list: returned 0x%08x, "
1480 "retrying\n", NT_STATUS_V(status)));
1482 if (NT_STATUS_EQUAL(status, NT_STATUS_UNSUCCESSFUL)) {
1483 DEBUG(3, ("query_user_list: flushing "
1484 "connection cache\n"));
1485 invalidate_cm_connection(&domain->conn);
1487 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1488 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1489 if (!domain->internal && old_status) {
1490 set_domain_offline(domain);
1492 /* store partial response. */
1493 if (*num_entries > 0) {
1495 * humm, what about the status used for cache?
1496 * Should it be NT_STATUS_OK?
1498 break;
1501 * domain is offline now, and there is no user entries,
1502 * try to fetch from cache again.
1504 if (cache->tdb && !domain->online && !domain->internal && old_status) {
1505 centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
1506 /* partial response... */
1507 if (!centry) {
1508 goto skip_save;
1509 } else {
1510 goto do_fetch_cache;
1512 } else {
1513 goto skip_save;
1517 } while (NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_UNSUCCESSFUL) &&
1518 (retry++ < 5));
1520 /* and save it */
1521 refresh_sequence_number(domain, false);
1522 if (!NT_STATUS_IS_OK(status)) {
1523 return status;
1525 centry = centry_start(domain, status);
1526 if (!centry)
1527 goto skip_save;
1528 centry_put_uint32(centry, *num_entries);
1529 for (i=0; i<(*num_entries); i++) {
1530 centry_put_string(centry, (*info)[i].acct_name);
1531 centry_put_string(centry, (*info)[i].full_name);
1532 centry_put_string(centry, (*info)[i].homedir);
1533 centry_put_string(centry, (*info)[i].shell);
1534 centry_put_sid(centry, &(*info)[i].user_sid);
1535 centry_put_sid(centry, &(*info)[i].group_sid);
1536 if (domain->backend && domain->backend->consistent) {
1537 /* when the backend is consistent we can pre-prime some mappings */
1538 wcache_save_name_to_sid(domain, NT_STATUS_OK,
1539 domain->name,
1540 (*info)[i].acct_name,
1541 &(*info)[i].user_sid,
1542 SID_NAME_USER);
1543 wcache_save_sid_to_name(domain, NT_STATUS_OK,
1544 &(*info)[i].user_sid,
1545 domain->name,
1546 (*info)[i].acct_name,
1547 SID_NAME_USER);
1548 wcache_save_user(domain, NT_STATUS_OK, &(*info)[i]);
1551 centry_end(centry, "UL/%s", domain->name);
1552 centry_free(centry);
1554 skip_save:
1555 return status;
1558 /* list all domain groups */
1559 static NTSTATUS enum_dom_groups(struct winbindd_domain *domain,
1560 TALLOC_CTX *mem_ctx,
1561 uint32 *num_entries,
1562 struct acct_info **info)
1564 struct winbind_cache *cache = get_cache(domain);
1565 struct cache_entry *centry = NULL;
1566 NTSTATUS status;
1567 unsigned int i;
1568 bool old_status;
1570 old_status = domain->online;
1571 if (!cache->tdb)
1572 goto do_query;
1574 centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
1575 if (!centry)
1576 goto do_query;
1578 do_fetch_cache:
1579 *num_entries = centry_uint32(centry);
1581 if (*num_entries == 0)
1582 goto do_cached;
1584 (*info) = TALLOC_ARRAY(mem_ctx, struct acct_info, *num_entries);
1585 if (! (*info)) {
1586 smb_panic_fn("enum_dom_groups out of memory");
1588 for (i=0; i<(*num_entries); i++) {
1589 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1590 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1591 (*info)[i].rid = centry_uint32(centry);
1594 do_cached:
1595 status = centry->status;
1597 DEBUG(10,("enum_dom_groups: [Cached] - cached list for domain %s status: %s\n",
1598 domain->name, nt_errstr(status) ));
1600 centry_free(centry);
1601 return status;
1603 do_query:
1604 *num_entries = 0;
1605 *info = NULL;
1607 /* Return status value returned by seq number check */
1609 if (!NT_STATUS_IS_OK(domain->last_status))
1610 return domain->last_status;
1612 DEBUG(10,("enum_dom_groups: [Cached] - doing backend query for list for domain %s\n",
1613 domain->name ));
1615 status = domain->backend->enum_dom_groups(domain, mem_ctx, num_entries, info);
1617 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1618 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1619 if (!domain->internal && old_status) {
1620 set_domain_offline(domain);
1622 if (cache->tdb &&
1623 !domain->online &&
1624 !domain->internal &&
1625 old_status) {
1626 centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
1627 if (centry) {
1628 goto do_fetch_cache;
1632 /* and save it */
1633 refresh_sequence_number(domain, false);
1634 if (!NT_STATUS_IS_OK(status)) {
1635 return status;
1637 centry = centry_start(domain, status);
1638 if (!centry)
1639 goto skip_save;
1640 centry_put_uint32(centry, *num_entries);
1641 for (i=0; i<(*num_entries); i++) {
1642 centry_put_string(centry, (*info)[i].acct_name);
1643 centry_put_string(centry, (*info)[i].acct_desc);
1644 centry_put_uint32(centry, (*info)[i].rid);
1646 centry_end(centry, "GL/%s/domain", domain->name);
1647 centry_free(centry);
1649 skip_save:
1650 return status;
1653 /* list all domain groups */
1654 static NTSTATUS enum_local_groups(struct winbindd_domain *domain,
1655 TALLOC_CTX *mem_ctx,
1656 uint32 *num_entries,
1657 struct acct_info **info)
1659 struct winbind_cache *cache = get_cache(domain);
1660 struct cache_entry *centry = NULL;
1661 NTSTATUS status;
1662 unsigned int i;
1663 bool old_status;
1665 old_status = domain->online;
1666 if (!cache->tdb)
1667 goto do_query;
1669 centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
1670 if (!centry)
1671 goto do_query;
1673 do_fetch_cache:
1674 *num_entries = centry_uint32(centry);
1676 if (*num_entries == 0)
1677 goto do_cached;
1679 (*info) = TALLOC_ARRAY(mem_ctx, struct acct_info, *num_entries);
1680 if (! (*info)) {
1681 smb_panic_fn("enum_dom_groups out of memory");
1683 for (i=0; i<(*num_entries); i++) {
1684 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1685 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1686 (*info)[i].rid = centry_uint32(centry);
1689 do_cached:
1691 /* If we are returning cached data and the domain controller
1692 is down then we don't know whether the data is up to date
1693 or not. Return NT_STATUS_MORE_PROCESSING_REQUIRED to
1694 indicate this. */
1696 if (wcache_server_down(domain)) {
1697 DEBUG(10, ("enum_local_groups: returning cached user list and server was down\n"));
1698 status = NT_STATUS_MORE_PROCESSING_REQUIRED;
1699 } else
1700 status = centry->status;
1702 DEBUG(10,("enum_local_groups: [Cached] - cached list for domain %s status: %s\n",
1703 domain->name, nt_errstr(status) ));
1705 centry_free(centry);
1706 return status;
1708 do_query:
1709 *num_entries = 0;
1710 *info = NULL;
1712 /* Return status value returned by seq number check */
1714 if (!NT_STATUS_IS_OK(domain->last_status))
1715 return domain->last_status;
1717 DEBUG(10,("enum_local_groups: [Cached] - doing backend query for list for domain %s\n",
1718 domain->name ));
1720 status = domain->backend->enum_local_groups(domain, mem_ctx, num_entries, info);
1722 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1723 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1724 if (!domain->internal && old_status) {
1725 set_domain_offline(domain);
1727 if (cache->tdb &&
1728 !domain->internal &&
1729 !domain->online &&
1730 old_status) {
1731 centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
1732 if (centry) {
1733 goto do_fetch_cache;
1737 /* and save it */
1738 refresh_sequence_number(domain, false);
1739 if (!NT_STATUS_IS_OK(status)) {
1740 return status;
1742 centry = centry_start(domain, status);
1743 if (!centry)
1744 goto skip_save;
1745 centry_put_uint32(centry, *num_entries);
1746 for (i=0; i<(*num_entries); i++) {
1747 centry_put_string(centry, (*info)[i].acct_name);
1748 centry_put_string(centry, (*info)[i].acct_desc);
1749 centry_put_uint32(centry, (*info)[i].rid);
1751 centry_end(centry, "GL/%s/local", domain->name);
1752 centry_free(centry);
1754 skip_save:
1755 return status;
1758 NTSTATUS wcache_name_to_sid(struct winbindd_domain *domain,
1759 const char *domain_name,
1760 const char *name,
1761 struct dom_sid *sid,
1762 enum lsa_SidType *type)
1764 struct winbind_cache *cache = get_cache(domain);
1765 struct cache_entry *centry;
1766 NTSTATUS status;
1767 char *uname;
1769 if (cache->tdb == NULL) {
1770 return NT_STATUS_NOT_FOUND;
1773 uname = talloc_strdup_upper(talloc_tos(), name);
1774 if (uname == NULL) {
1775 return NT_STATUS_NO_MEMORY;
1778 centry = wcache_fetch(cache, domain, "NS/%s/%s", domain_name, uname);
1779 TALLOC_FREE(uname);
1780 if (centry == NULL) {
1781 return NT_STATUS_NOT_FOUND;
1784 status = centry->status;
1785 if (NT_STATUS_IS_OK(status)) {
1786 *type = (enum lsa_SidType)centry_uint32(centry);
1787 centry_sid(centry, sid);
1790 DEBUG(10,("name_to_sid: [Cached] - cached name for domain %s status: "
1791 "%s\n", domain->name, nt_errstr(status) ));
1793 centry_free(centry);
1794 return status;
1797 /* convert a single name to a sid in a domain */
1798 static NTSTATUS name_to_sid(struct winbindd_domain *domain,
1799 TALLOC_CTX *mem_ctx,
1800 const char *domain_name,
1801 const char *name,
1802 uint32_t flags,
1803 struct dom_sid *sid,
1804 enum lsa_SidType *type)
1806 NTSTATUS status;
1807 bool old_status;
1809 old_status = domain->online;
1811 status = wcache_name_to_sid(domain, domain_name, name, sid, type);
1812 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
1813 return status;
1816 ZERO_STRUCTP(sid);
1818 /* If the seq number check indicated that there is a problem
1819 * with this DC, then return that status... except for
1820 * access_denied. This is special because the dc may be in
1821 * "restrict anonymous = 1" mode, in which case it will deny
1822 * most unauthenticated operations, but *will* allow the LSA
1823 * name-to-sid that we try as a fallback. */
1825 if (!(NT_STATUS_IS_OK(domain->last_status)
1826 || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1827 return domain->last_status;
1829 DEBUG(10,("name_to_sid: [Cached] - doing backend query for name for domain %s\n",
1830 domain->name ));
1832 status = domain->backend->name_to_sid(domain, mem_ctx, domain_name,
1833 name, flags, sid, type);
1835 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1836 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1837 if (!domain->internal && old_status) {
1838 set_domain_offline(domain);
1840 if (!domain->internal &&
1841 !domain->online &&
1842 old_status) {
1843 NTSTATUS cache_status;
1844 cache_status = wcache_name_to_sid(domain, domain_name, name, sid, type);
1845 return cache_status;
1848 /* and save it */
1849 refresh_sequence_number(domain, false);
1851 if (domain->online &&
1852 (NT_STATUS_IS_OK(status) || NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED))) {
1853 wcache_save_name_to_sid(domain, status, domain_name, name, sid, *type);
1855 /* Only save the reverse mapping if this was not a UPN */
1856 if (!strchr(name, '@')) {
1857 strupper_m(CONST_DISCARD(char *,domain_name));
1858 strlower_m(CONST_DISCARD(char *,name));
1859 wcache_save_sid_to_name(domain, status, sid, domain_name, name, *type);
1863 return status;
1866 NTSTATUS wcache_sid_to_name(struct winbindd_domain *domain,
1867 const struct dom_sid *sid,
1868 TALLOC_CTX *mem_ctx,
1869 char **domain_name,
1870 char **name,
1871 enum lsa_SidType *type)
1873 struct winbind_cache *cache = get_cache(domain);
1874 struct cache_entry *centry;
1875 char *sid_string;
1876 NTSTATUS status;
1878 if (cache->tdb == NULL) {
1879 return NT_STATUS_NOT_FOUND;
1882 sid_string = sid_string_tos(sid);
1883 if (sid_string == NULL) {
1884 return NT_STATUS_NO_MEMORY;
1887 centry = wcache_fetch(cache, domain, "SN/%s", sid_string);
1888 TALLOC_FREE(sid_string);
1889 if (centry == NULL) {
1890 return NT_STATUS_NOT_FOUND;
1893 if (NT_STATUS_IS_OK(centry->status)) {
1894 *type = (enum lsa_SidType)centry_uint32(centry);
1895 *domain_name = centry_string(centry, mem_ctx);
1896 *name = centry_string(centry, mem_ctx);
1899 status = centry->status;
1900 centry_free(centry);
1902 DEBUG(10,("sid_to_name: [Cached] - cached name for domain %s status: "
1903 "%s\n", domain->name, nt_errstr(status) ));
1905 return status;
1908 /* convert a sid to a user or group name. The sid is guaranteed to be in the domain
1909 given */
1910 static NTSTATUS sid_to_name(struct winbindd_domain *domain,
1911 TALLOC_CTX *mem_ctx,
1912 const struct dom_sid *sid,
1913 char **domain_name,
1914 char **name,
1915 enum lsa_SidType *type)
1917 NTSTATUS status;
1918 bool old_status;
1920 old_status = domain->online;
1921 status = wcache_sid_to_name(domain, sid, mem_ctx, domain_name, name,
1922 type);
1923 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
1924 return status;
1927 *name = NULL;
1928 *domain_name = NULL;
1930 /* If the seq number check indicated that there is a problem
1931 * with this DC, then return that status... except for
1932 * access_denied. This is special because the dc may be in
1933 * "restrict anonymous = 1" mode, in which case it will deny
1934 * most unauthenticated operations, but *will* allow the LSA
1935 * sid-to-name that we try as a fallback. */
1937 if (!(NT_STATUS_IS_OK(domain->last_status)
1938 || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1939 return domain->last_status;
1941 DEBUG(10,("sid_to_name: [Cached] - doing backend query for name for domain %s\n",
1942 domain->name ));
1944 status = domain->backend->sid_to_name(domain, mem_ctx, sid, domain_name, name, type);
1946 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1947 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1948 if (!domain->internal && old_status) {
1949 set_domain_offline(domain);
1951 if (!domain->internal &&
1952 !domain->online &&
1953 old_status) {
1954 NTSTATUS cache_status;
1955 cache_status = wcache_sid_to_name(domain, sid, mem_ctx,
1956 domain_name, name, type);
1957 return cache_status;
1960 /* and save it */
1961 refresh_sequence_number(domain, false);
1962 if (!NT_STATUS_IS_OK(status)) {
1963 return status;
1965 wcache_save_sid_to_name(domain, status, sid, *domain_name, *name, *type);
1967 /* We can't save the name to sid mapping here, as with sid history a
1968 * later name2sid would give the wrong sid. */
1970 return status;
1973 static NTSTATUS rids_to_names(struct winbindd_domain *domain,
1974 TALLOC_CTX *mem_ctx,
1975 const struct dom_sid *domain_sid,
1976 uint32 *rids,
1977 size_t num_rids,
1978 char **domain_name,
1979 char ***names,
1980 enum lsa_SidType **types)
1982 struct winbind_cache *cache = get_cache(domain);
1983 size_t i;
1984 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
1985 bool have_mapped;
1986 bool have_unmapped;
1987 bool old_status;
1989 old_status = domain->online;
1990 *domain_name = NULL;
1991 *names = NULL;
1992 *types = NULL;
1994 if (!cache->tdb) {
1995 goto do_query;
1998 if (num_rids == 0) {
1999 return NT_STATUS_OK;
2002 *names = TALLOC_ARRAY(mem_ctx, char *, num_rids);
2003 *types = TALLOC_ARRAY(mem_ctx, enum lsa_SidType, num_rids);
2005 if ((*names == NULL) || (*types == NULL)) {
2006 result = NT_STATUS_NO_MEMORY;
2007 goto error;
2010 have_mapped = have_unmapped = false;
2012 for (i=0; i<num_rids; i++) {
2013 struct dom_sid sid;
2014 struct cache_entry *centry;
2015 fstring tmp;
2017 if (!sid_compose(&sid, domain_sid, rids[i])) {
2018 result = NT_STATUS_INTERNAL_ERROR;
2019 goto error;
2022 centry = wcache_fetch(cache, domain, "SN/%s",
2023 sid_to_fstring(tmp, &sid));
2024 if (!centry) {
2025 goto do_query;
2028 (*types)[i] = SID_NAME_UNKNOWN;
2029 (*names)[i] = talloc_strdup(*names, "");
2031 if (NT_STATUS_IS_OK(centry->status)) {
2032 char *dom;
2033 have_mapped = true;
2034 (*types)[i] = (enum lsa_SidType)centry_uint32(centry);
2036 dom = centry_string(centry, mem_ctx);
2037 if (*domain_name == NULL) {
2038 *domain_name = dom;
2039 } else {
2040 talloc_free(dom);
2043 (*names)[i] = centry_string(centry, *names);
2045 } else if (NT_STATUS_EQUAL(centry->status, NT_STATUS_NONE_MAPPED)
2046 || NT_STATUS_EQUAL(centry->status, STATUS_SOME_UNMAPPED)) {
2047 have_unmapped = true;
2049 } else {
2050 /* something's definitely wrong */
2051 result = centry->status;
2052 goto error;
2055 centry_free(centry);
2058 if (!have_mapped) {
2059 return NT_STATUS_NONE_MAPPED;
2061 if (!have_unmapped) {
2062 return NT_STATUS_OK;
2064 return STATUS_SOME_UNMAPPED;
2066 do_query:
2068 TALLOC_FREE(*names);
2069 TALLOC_FREE(*types);
2071 result = domain->backend->rids_to_names(domain, mem_ctx, domain_sid,
2072 rids, num_rids, domain_name,
2073 names, types);
2075 if (NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
2076 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2077 if (!domain->internal && old_status) {
2078 set_domain_offline(domain);
2080 if (cache->tdb &&
2081 !domain->internal &&
2082 !domain->online &&
2083 old_status) {
2084 have_mapped = have_unmapped = false;
2086 for (i=0; i<num_rids; i++) {
2087 struct dom_sid sid;
2088 struct cache_entry *centry;
2089 fstring tmp;
2091 if (!sid_compose(&sid, domain_sid, rids[i])) {
2092 result = NT_STATUS_INTERNAL_ERROR;
2093 goto error;
2096 centry = wcache_fetch(cache, domain, "SN/%s",
2097 sid_to_fstring(tmp, &sid));
2098 if (!centry) {
2099 (*types)[i] = SID_NAME_UNKNOWN;
2100 (*names)[i] = talloc_strdup(*names, "");
2101 continue;
2104 (*types)[i] = SID_NAME_UNKNOWN;
2105 (*names)[i] = talloc_strdup(*names, "");
2107 if (NT_STATUS_IS_OK(centry->status)) {
2108 char *dom;
2109 have_mapped = true;
2110 (*types)[i] = (enum lsa_SidType)centry_uint32(centry);
2112 dom = centry_string(centry, mem_ctx);
2113 if (*domain_name == NULL) {
2114 *domain_name = dom;
2115 } else {
2116 talloc_free(dom);
2119 (*names)[i] = centry_string(centry, *names);
2121 } else if (NT_STATUS_EQUAL(centry->status, NT_STATUS_NONE_MAPPED)) {
2122 have_unmapped = true;
2124 } else {
2125 /* something's definitely wrong */
2126 result = centry->status;
2127 goto error;
2130 centry_free(centry);
2133 if (!have_mapped) {
2134 return NT_STATUS_NONE_MAPPED;
2136 if (!have_unmapped) {
2137 return NT_STATUS_OK;
2139 return STATUS_SOME_UNMAPPED;
2143 None of the queried rids has been found so save all negative entries
2145 if (NT_STATUS_EQUAL(result, NT_STATUS_NONE_MAPPED)) {
2146 for (i = 0; i < num_rids; i++) {
2147 struct dom_sid sid;
2148 const char *name = "";
2149 const enum lsa_SidType type = SID_NAME_UNKNOWN;
2150 NTSTATUS status = NT_STATUS_NONE_MAPPED;
2152 if (!sid_compose(&sid, domain_sid, rids[i])) {
2153 return NT_STATUS_INTERNAL_ERROR;
2156 wcache_save_sid_to_name(domain, status, &sid, *domain_name,
2157 name, type);
2160 return result;
2164 Some or all of the queried rids have been found.
2166 if (!NT_STATUS_IS_OK(result) &&
2167 !NT_STATUS_EQUAL(result, STATUS_SOME_UNMAPPED)) {
2168 return result;
2171 refresh_sequence_number(domain, false);
2173 for (i=0; i<num_rids; i++) {
2174 struct dom_sid sid;
2175 NTSTATUS status;
2177 if (!sid_compose(&sid, domain_sid, rids[i])) {
2178 result = NT_STATUS_INTERNAL_ERROR;
2179 goto error;
2182 status = (*types)[i] == SID_NAME_UNKNOWN ?
2183 NT_STATUS_NONE_MAPPED : NT_STATUS_OK;
2185 wcache_save_sid_to_name(domain, status, &sid, *domain_name,
2186 (*names)[i], (*types)[i]);
2189 return result;
2191 error:
2192 TALLOC_FREE(*names);
2193 TALLOC_FREE(*types);
2194 return result;
2197 NTSTATUS wcache_query_user(struct winbindd_domain *domain,
2198 TALLOC_CTX *mem_ctx,
2199 const struct dom_sid *user_sid,
2200 struct wbint_userinfo *info)
2202 struct winbind_cache *cache = get_cache(domain);
2203 struct cache_entry *centry = NULL;
2204 NTSTATUS status;
2205 char *sid_string;
2207 if (cache->tdb == NULL) {
2208 return NT_STATUS_NOT_FOUND;
2211 sid_string = sid_string_tos(user_sid);
2212 if (sid_string == NULL) {
2213 return NT_STATUS_NO_MEMORY;
2216 centry = wcache_fetch(cache, domain, "U/%s", sid_string);
2217 TALLOC_FREE(sid_string);
2218 if (centry == NULL) {
2219 return NT_STATUS_NOT_FOUND;
2223 * If we have an access denied cache entry and a cached info3
2224 * in the samlogon cache then do a query. This will force the
2225 * rpc back end to return the info3 data.
2228 if (NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED) &&
2229 netsamlogon_cache_have(user_sid)) {
2230 DEBUG(10, ("query_user: cached access denied and have cached "
2231 "info3\n"));
2232 domain->last_status = NT_STATUS_OK;
2233 centry_free(centry);
2234 return NT_STATUS_NOT_FOUND;
2237 /* if status is not ok then this is a negative hit
2238 and the rest of the data doesn't matter */
2239 status = centry->status;
2240 if (NT_STATUS_IS_OK(status)) {
2241 info->acct_name = centry_string(centry, mem_ctx);
2242 info->full_name = centry_string(centry, mem_ctx);
2243 info->homedir = centry_string(centry, mem_ctx);
2244 info->shell = centry_string(centry, mem_ctx);
2245 info->primary_gid = centry_uint32(centry);
2246 centry_sid(centry, &info->user_sid);
2247 centry_sid(centry, &info->group_sid);
2250 DEBUG(10,("query_user: [Cached] - cached info for domain %s status: "
2251 "%s\n", domain->name, nt_errstr(status) ));
2253 centry_free(centry);
2254 return status;
2257 /* Lookup user information from a rid */
2258 static NTSTATUS query_user(struct winbindd_domain *domain,
2259 TALLOC_CTX *mem_ctx,
2260 const struct dom_sid *user_sid,
2261 struct wbint_userinfo *info)
2263 NTSTATUS status;
2264 bool old_status;
2266 old_status = domain->online;
2267 status = wcache_query_user(domain, mem_ctx, user_sid, info);
2268 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2269 return status;
2272 ZERO_STRUCTP(info);
2274 /* Return status value returned by seq number check */
2276 if (!NT_STATUS_IS_OK(domain->last_status))
2277 return domain->last_status;
2279 DEBUG(10,("query_user: [Cached] - doing backend query for info for domain %s\n",
2280 domain->name ));
2282 status = domain->backend->query_user(domain, mem_ctx, user_sid, info);
2284 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2285 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2286 if (!domain->internal && old_status) {
2287 set_domain_offline(domain);
2289 if (!domain->internal &&
2290 !domain->online &&
2291 old_status) {
2292 NTSTATUS cache_status;
2293 cache_status = wcache_query_user(domain, mem_ctx, user_sid, info);
2294 return cache_status;
2297 /* and save it */
2298 refresh_sequence_number(domain, false);
2299 if (!NT_STATUS_IS_OK(status)) {
2300 return status;
2302 wcache_save_user(domain, status, info);
2304 return status;
2307 NTSTATUS wcache_lookup_usergroups(struct winbindd_domain *domain,
2308 TALLOC_CTX *mem_ctx,
2309 const struct dom_sid *user_sid,
2310 uint32_t *pnum_sids,
2311 struct dom_sid **psids)
2313 struct winbind_cache *cache = get_cache(domain);
2314 struct cache_entry *centry = NULL;
2315 NTSTATUS status;
2316 uint32_t i, num_sids;
2317 struct dom_sid *sids;
2318 fstring sid_string;
2320 if (cache->tdb == NULL) {
2321 return NT_STATUS_NOT_FOUND;
2324 centry = wcache_fetch(cache, domain, "UG/%s",
2325 sid_to_fstring(sid_string, user_sid));
2326 if (centry == NULL) {
2327 return NT_STATUS_NOT_FOUND;
2330 /* If we have an access denied cache entry and a cached info3 in the
2331 samlogon cache then do a query. This will force the rpc back end
2332 to return the info3 data. */
2334 if (NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)
2335 && netsamlogon_cache_have(user_sid)) {
2336 DEBUG(10, ("lookup_usergroups: cached access denied and have "
2337 "cached info3\n"));
2338 domain->last_status = NT_STATUS_OK;
2339 centry_free(centry);
2340 return NT_STATUS_NOT_FOUND;
2343 num_sids = centry_uint32(centry);
2344 sids = talloc_array(mem_ctx, struct dom_sid, num_sids);
2345 if (sids == NULL) {
2346 centry_free(centry);
2347 return NT_STATUS_NO_MEMORY;
2350 for (i=0; i<num_sids; i++) {
2351 centry_sid(centry, &sids[i]);
2354 status = centry->status;
2356 DEBUG(10,("lookup_usergroups: [Cached] - cached info for domain %s "
2357 "status: %s\n", domain->name, nt_errstr(status)));
2359 centry_free(centry);
2361 *pnum_sids = num_sids;
2362 *psids = sids;
2363 return status;
2366 /* Lookup groups a user is a member of. */
2367 static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
2368 TALLOC_CTX *mem_ctx,
2369 const struct dom_sid *user_sid,
2370 uint32 *num_groups, struct dom_sid **user_gids)
2372 struct cache_entry *centry = NULL;
2373 NTSTATUS status;
2374 unsigned int i;
2375 fstring sid_string;
2376 bool old_status;
2378 old_status = domain->online;
2379 status = wcache_lookup_usergroups(domain, mem_ctx, user_sid,
2380 num_groups, user_gids);
2381 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2382 return status;
2385 (*num_groups) = 0;
2386 (*user_gids) = NULL;
2388 /* Return status value returned by seq number check */
2390 if (!NT_STATUS_IS_OK(domain->last_status))
2391 return domain->last_status;
2393 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info for domain %s\n",
2394 domain->name ));
2396 status = domain->backend->lookup_usergroups(domain, mem_ctx, user_sid, num_groups, user_gids);
2398 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2399 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2400 if (!domain->internal && old_status) {
2401 set_domain_offline(domain);
2403 if (!domain->internal &&
2404 !domain->online &&
2405 old_status) {
2406 NTSTATUS cache_status;
2407 cache_status = wcache_lookup_usergroups(domain, mem_ctx, user_sid,
2408 num_groups, user_gids);
2409 return cache_status;
2412 if ( NT_STATUS_EQUAL(status, NT_STATUS_SYNCHRONIZATION_REQUIRED) )
2413 goto skip_save;
2415 /* and save it */
2416 refresh_sequence_number(domain, false);
2417 if (!NT_STATUS_IS_OK(status)) {
2418 return status;
2420 centry = centry_start(domain, status);
2421 if (!centry)
2422 goto skip_save;
2424 centry_put_uint32(centry, *num_groups);
2425 for (i=0; i<(*num_groups); i++) {
2426 centry_put_sid(centry, &(*user_gids)[i]);
2429 centry_end(centry, "UG/%s", sid_to_fstring(sid_string, user_sid));
2430 centry_free(centry);
2432 skip_save:
2433 return status;
2436 static char *wcache_make_sidlist(TALLOC_CTX *mem_ctx, uint32_t num_sids,
2437 const struct dom_sid *sids)
2439 uint32_t i;
2440 char *sidlist;
2442 sidlist = talloc_strdup(mem_ctx, "");
2443 if (sidlist == NULL) {
2444 return NULL;
2446 for (i=0; i<num_sids; i++) {
2447 fstring tmp;
2448 sidlist = talloc_asprintf_append_buffer(
2449 sidlist, "/%s", sid_to_fstring(tmp, &sids[i]));
2450 if (sidlist == NULL) {
2451 return NULL;
2454 return sidlist;
2457 NTSTATUS wcache_lookup_useraliases(struct winbindd_domain *domain,
2458 TALLOC_CTX *mem_ctx, uint32_t num_sids,
2459 const struct dom_sid *sids,
2460 uint32_t *pnum_aliases, uint32_t **paliases)
2462 struct winbind_cache *cache = get_cache(domain);
2463 struct cache_entry *centry = NULL;
2464 uint32_t num_aliases;
2465 uint32_t *aliases;
2466 NTSTATUS status;
2467 char *sidlist;
2468 int i;
2470 if (cache->tdb == NULL) {
2471 return NT_STATUS_NOT_FOUND;
2474 if (num_sids == 0) {
2475 *pnum_aliases = 0;
2476 *paliases = NULL;
2477 return NT_STATUS_OK;
2480 /* We need to cache indexed by the whole list of SIDs, the aliases
2481 * resulting might come from any of the SIDs. */
2483 sidlist = wcache_make_sidlist(talloc_tos(), num_sids, sids);
2484 if (sidlist == NULL) {
2485 return NT_STATUS_NO_MEMORY;
2488 centry = wcache_fetch(cache, domain, "UA%s", sidlist);
2489 TALLOC_FREE(sidlist);
2490 if (centry == NULL) {
2491 return NT_STATUS_NOT_FOUND;
2494 num_aliases = centry_uint32(centry);
2495 aliases = talloc_array(mem_ctx, uint32_t, num_aliases);
2496 if (aliases == NULL) {
2497 centry_free(centry);
2498 return NT_STATUS_NO_MEMORY;
2501 for (i=0; i<num_aliases; i++) {
2502 aliases[i] = centry_uint32(centry);
2505 status = centry->status;
2507 DEBUG(10,("lookup_useraliases: [Cached] - cached info for domain: %s "
2508 "status %s\n", domain->name, nt_errstr(status)));
2510 centry_free(centry);
2512 *pnum_aliases = num_aliases;
2513 *paliases = aliases;
2515 return status;
2518 static NTSTATUS lookup_useraliases(struct winbindd_domain *domain,
2519 TALLOC_CTX *mem_ctx,
2520 uint32 num_sids, const struct dom_sid *sids,
2521 uint32 *num_aliases, uint32 **alias_rids)
2523 struct cache_entry *centry = NULL;
2524 NTSTATUS status;
2525 char *sidlist;
2526 int i;
2527 bool old_status;
2529 old_status = domain->online;
2530 status = wcache_lookup_useraliases(domain, mem_ctx, num_sids, sids,
2531 num_aliases, alias_rids);
2532 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2533 return status;
2536 (*num_aliases) = 0;
2537 (*alias_rids) = NULL;
2539 if (!NT_STATUS_IS_OK(domain->last_status))
2540 return domain->last_status;
2542 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info "
2543 "for domain %s\n", domain->name ));
2545 sidlist = wcache_make_sidlist(talloc_tos(), num_sids, sids);
2546 if (sidlist == NULL) {
2547 return NT_STATUS_NO_MEMORY;
2550 status = domain->backend->lookup_useraliases(domain, mem_ctx,
2551 num_sids, sids,
2552 num_aliases, alias_rids);
2554 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2555 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2556 if (!domain->internal && old_status) {
2557 set_domain_offline(domain);
2559 if (!domain->internal &&
2560 !domain->online &&
2561 old_status) {
2562 NTSTATUS cache_status;
2563 cache_status = wcache_lookup_useraliases(domain, mem_ctx, num_sids,
2564 sids, num_aliases, alias_rids);
2565 return cache_status;
2568 /* and save it */
2569 refresh_sequence_number(domain, false);
2570 if (!NT_STATUS_IS_OK(status)) {
2571 return status;
2573 centry = centry_start(domain, status);
2574 if (!centry)
2575 goto skip_save;
2576 centry_put_uint32(centry, *num_aliases);
2577 for (i=0; i<(*num_aliases); i++)
2578 centry_put_uint32(centry, (*alias_rids)[i]);
2579 centry_end(centry, "UA%s", sidlist);
2580 centry_free(centry);
2582 skip_save:
2583 return status;
2586 NTSTATUS wcache_lookup_groupmem(struct winbindd_domain *domain,
2587 TALLOC_CTX *mem_ctx,
2588 const struct dom_sid *group_sid,
2589 uint32_t *num_names,
2590 struct dom_sid **sid_mem, char ***names,
2591 uint32_t **name_types)
2593 struct winbind_cache *cache = get_cache(domain);
2594 struct cache_entry *centry = NULL;
2595 NTSTATUS status;
2596 unsigned int i;
2597 char *sid_string;
2599 if (cache->tdb == NULL) {
2600 return NT_STATUS_NOT_FOUND;
2603 sid_string = sid_string_tos(group_sid);
2604 if (sid_string == NULL) {
2605 return NT_STATUS_NO_MEMORY;
2608 centry = wcache_fetch(cache, domain, "GM/%s", sid_string);
2609 TALLOC_FREE(sid_string);
2610 if (centry == NULL) {
2611 return NT_STATUS_NOT_FOUND;
2614 *sid_mem = NULL;
2615 *names = NULL;
2616 *name_types = NULL;
2618 *num_names = centry_uint32(centry);
2619 if (*num_names == 0) {
2620 centry_free(centry);
2621 return NT_STATUS_OK;
2624 *sid_mem = talloc_array(mem_ctx, struct dom_sid, *num_names);
2625 *names = talloc_array(mem_ctx, char *, *num_names);
2626 *name_types = talloc_array(mem_ctx, uint32, *num_names);
2628 if ((*sid_mem == NULL) || (*names == NULL) || (*name_types == NULL)) {
2629 TALLOC_FREE(*sid_mem);
2630 TALLOC_FREE(*names);
2631 TALLOC_FREE(*name_types);
2632 centry_free(centry);
2633 return NT_STATUS_NO_MEMORY;
2636 for (i=0; i<(*num_names); i++) {
2637 centry_sid(centry, &(*sid_mem)[i]);
2638 (*names)[i] = centry_string(centry, mem_ctx);
2639 (*name_types)[i] = centry_uint32(centry);
2642 status = centry->status;
2644 DEBUG(10,("lookup_groupmem: [Cached] - cached info for domain %s "
2645 "status: %s\n", domain->name, nt_errstr(status)));
2647 centry_free(centry);
2648 return status;
2651 static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
2652 TALLOC_CTX *mem_ctx,
2653 const struct dom_sid *group_sid,
2654 enum lsa_SidType type,
2655 uint32 *num_names,
2656 struct dom_sid **sid_mem, char ***names,
2657 uint32 **name_types)
2659 struct cache_entry *centry = NULL;
2660 NTSTATUS status;
2661 unsigned int i;
2662 fstring sid_string;
2663 bool old_status;
2665 old_status = domain->online;
2666 status = wcache_lookup_groupmem(domain, mem_ctx, group_sid, num_names,
2667 sid_mem, names, name_types);
2668 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2669 return status;
2672 (*num_names) = 0;
2673 (*sid_mem) = NULL;
2674 (*names) = NULL;
2675 (*name_types) = NULL;
2677 /* Return status value returned by seq number check */
2679 if (!NT_STATUS_IS_OK(domain->last_status))
2680 return domain->last_status;
2682 DEBUG(10,("lookup_groupmem: [Cached] - doing backend query for info for domain %s\n",
2683 domain->name ));
2685 status = domain->backend->lookup_groupmem(domain, mem_ctx, group_sid,
2686 type, num_names,
2687 sid_mem, names, name_types);
2689 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2690 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2691 if (!domain->internal && old_status) {
2692 set_domain_offline(domain);
2694 if (!domain->internal &&
2695 !domain->online &&
2696 old_status) {
2697 NTSTATUS cache_status;
2698 cache_status = wcache_lookup_groupmem(domain, mem_ctx, group_sid,
2699 num_names, sid_mem, names,
2700 name_types);
2701 return cache_status;
2704 /* and save it */
2705 refresh_sequence_number(domain, false);
2706 if (!NT_STATUS_IS_OK(status)) {
2707 return status;
2709 centry = centry_start(domain, status);
2710 if (!centry)
2711 goto skip_save;
2712 centry_put_uint32(centry, *num_names);
2713 for (i=0; i<(*num_names); i++) {
2714 centry_put_sid(centry, &(*sid_mem)[i]);
2715 centry_put_string(centry, (*names)[i]);
2716 centry_put_uint32(centry, (*name_types)[i]);
2718 centry_end(centry, "GM/%s", sid_to_fstring(sid_string, group_sid));
2719 centry_free(centry);
2721 skip_save:
2722 return status;
2725 /* find the sequence number for a domain */
2726 static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
2728 refresh_sequence_number(domain, false);
2730 *seq = domain->sequence_number;
2732 return NT_STATUS_OK;
2735 /* enumerate trusted domains
2736 * (we need to have the list of trustdoms in the cache when we go offline) -
2737 * Guenther */
2738 static NTSTATUS trusted_domains(struct winbindd_domain *domain,
2739 TALLOC_CTX *mem_ctx,
2740 struct netr_DomainTrustList *trusts)
2742 NTSTATUS status;
2743 struct winbind_cache *cache;
2744 struct winbindd_tdc_domain *dom_list = NULL;
2745 size_t num_domains = 0;
2746 bool retval = false;
2747 int i;
2748 bool old_status;
2750 old_status = domain->online;
2751 trusts->count = 0;
2752 trusts->array = NULL;
2754 cache = get_cache(domain);
2755 if (!cache || !cache->tdb) {
2756 goto do_query;
2759 if (domain->online) {
2760 goto do_query;
2763 retval = wcache_tdc_fetch_list(&dom_list, &num_domains);
2764 if (!retval || !num_domains || !dom_list) {
2765 TALLOC_FREE(dom_list);
2766 goto do_query;
2769 do_fetch_cache:
2770 trusts->array = TALLOC_ZERO_ARRAY(mem_ctx, struct netr_DomainTrust, num_domains);
2771 if (!trusts->array) {
2772 TALLOC_FREE(dom_list);
2773 return NT_STATUS_NO_MEMORY;
2776 for (i = 0; i < num_domains; i++) {
2777 struct netr_DomainTrust *trust;
2778 struct dom_sid *sid;
2779 struct winbindd_domain *dom;
2781 dom = find_domain_from_name_noinit(dom_list[i].domain_name);
2782 if (dom && dom->internal) {
2783 continue;
2786 trust = &trusts->array[trusts->count];
2787 trust->netbios_name = talloc_strdup(trusts->array, dom_list[i].domain_name);
2788 trust->dns_name = talloc_strdup(trusts->array, dom_list[i].dns_name);
2789 sid = talloc(trusts->array, struct dom_sid);
2790 if (!trust->netbios_name || !trust->dns_name ||
2791 !sid) {
2792 TALLOC_FREE(dom_list);
2793 TALLOC_FREE(trusts->array);
2794 return NT_STATUS_NO_MEMORY;
2797 trust->trust_flags = dom_list[i].trust_flags;
2798 trust->trust_attributes = dom_list[i].trust_attribs;
2799 trust->trust_type = dom_list[i].trust_type;
2800 sid_copy(sid, &dom_list[i].sid);
2801 trust->sid = sid;
2802 trusts->count++;
2805 TALLOC_FREE(dom_list);
2806 return NT_STATUS_OK;
2808 do_query:
2809 /* Return status value returned by seq number check */
2811 if (!NT_STATUS_IS_OK(domain->last_status))
2812 return domain->last_status;
2814 DEBUG(10,("trusted_domains: [Cached] - doing backend query for info for domain %s\n",
2815 domain->name ));
2817 status = domain->backend->trusted_domains(domain, mem_ctx, trusts);
2819 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2820 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2821 if (!domain->internal && old_status) {
2822 set_domain_offline(domain);
2824 if (!domain->internal &&
2825 !domain->online &&
2826 old_status) {
2827 retval = wcache_tdc_fetch_list(&dom_list, &num_domains);
2828 if (retval && num_domains && dom_list) {
2829 TALLOC_FREE(trusts->array);
2830 trusts->count = 0;
2831 goto do_fetch_cache;
2835 /* no trusts gives NT_STATUS_NO_MORE_ENTRIES resetting to NT_STATUS_OK
2836 * so that the generic centry handling still applies correctly -
2837 * Guenther*/
2839 if (!NT_STATUS_IS_ERR(status)) {
2840 status = NT_STATUS_OK;
2842 return status;
2845 /* get lockout policy */
2846 static NTSTATUS lockout_policy(struct winbindd_domain *domain,
2847 TALLOC_CTX *mem_ctx,
2848 struct samr_DomInfo12 *policy)
2850 struct winbind_cache *cache = get_cache(domain);
2851 struct cache_entry *centry = NULL;
2852 NTSTATUS status;
2853 bool old_status;
2855 old_status = domain->online;
2856 if (!cache->tdb)
2857 goto do_query;
2859 centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
2861 if (!centry)
2862 goto do_query;
2864 do_fetch_cache:
2865 policy->lockout_duration = centry_nttime(centry);
2866 policy->lockout_window = centry_nttime(centry);
2867 policy->lockout_threshold = centry_uint16(centry);
2869 status = centry->status;
2871 DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2872 domain->name, nt_errstr(status) ));
2874 centry_free(centry);
2875 return status;
2877 do_query:
2878 ZERO_STRUCTP(policy);
2880 /* Return status value returned by seq number check */
2882 if (!NT_STATUS_IS_OK(domain->last_status))
2883 return domain->last_status;
2885 DEBUG(10,("lockout_policy: [Cached] - doing backend query for info for domain %s\n",
2886 domain->name ));
2888 status = domain->backend->lockout_policy(domain, mem_ctx, policy);
2890 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2891 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2892 if (!domain->internal && old_status) {
2893 set_domain_offline(domain);
2895 if (cache->tdb &&
2896 !domain->internal &&
2897 !domain->online &&
2898 old_status) {
2899 centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
2900 if (centry) {
2901 goto do_fetch_cache;
2905 /* and save it */
2906 refresh_sequence_number(domain, false);
2907 if (!NT_STATUS_IS_OK(status)) {
2908 return status;
2910 wcache_save_lockout_policy(domain, status, policy);
2912 return status;
2915 /* get password policy */
2916 static NTSTATUS password_policy(struct winbindd_domain *domain,
2917 TALLOC_CTX *mem_ctx,
2918 struct samr_DomInfo1 *policy)
2920 struct winbind_cache *cache = get_cache(domain);
2921 struct cache_entry *centry = NULL;
2922 NTSTATUS status;
2923 bool old_status;
2925 old_status = domain->online;
2926 if (!cache->tdb)
2927 goto do_query;
2929 centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
2931 if (!centry)
2932 goto do_query;
2934 do_fetch_cache:
2935 policy->min_password_length = centry_uint16(centry);
2936 policy->password_history_length = centry_uint16(centry);
2937 policy->password_properties = centry_uint32(centry);
2938 policy->max_password_age = centry_nttime(centry);
2939 policy->min_password_age = centry_nttime(centry);
2941 status = centry->status;
2943 DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2944 domain->name, nt_errstr(status) ));
2946 centry_free(centry);
2947 return status;
2949 do_query:
2950 ZERO_STRUCTP(policy);
2952 /* Return status value returned by seq number check */
2954 if (!NT_STATUS_IS_OK(domain->last_status))
2955 return domain->last_status;
2957 DEBUG(10,("password_policy: [Cached] - doing backend query for info for domain %s\n",
2958 domain->name ));
2960 status = domain->backend->password_policy(domain, mem_ctx, policy);
2962 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2963 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2964 if (!domain->internal && old_status) {
2965 set_domain_offline(domain);
2967 if (cache->tdb &&
2968 !domain->internal &&
2969 !domain->online &&
2970 old_status) {
2971 centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
2972 if (centry) {
2973 goto do_fetch_cache;
2977 /* and save it */
2978 refresh_sequence_number(domain, false);
2979 if (!NT_STATUS_IS_OK(status)) {
2980 return status;
2982 wcache_save_password_policy(domain, status, policy);
2984 return status;
2988 /* Invalidate cached user and group lists coherently */
2990 static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
2991 void *state)
2993 if (strncmp((const char *)kbuf.dptr, "UL/", 3) == 0 ||
2994 strncmp((const char *)kbuf.dptr, "GL/", 3) == 0)
2995 tdb_delete(the_tdb, kbuf);
2997 return 0;
3000 /* Invalidate the getpwnam and getgroups entries for a winbindd domain */
3002 void wcache_invalidate_samlogon(struct winbindd_domain *domain,
3003 const struct dom_sid *sid)
3005 fstring key_str, sid_string;
3006 struct winbind_cache *cache;
3008 /* dont clear cached U/SID and UG/SID entries when we want to logon
3009 * offline - gd */
3011 if (lp_winbind_offline_logon()) {
3012 return;
3015 if (!domain)
3016 return;
3018 cache = get_cache(domain);
3020 if (!cache->tdb) {
3021 return;
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(sid);
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 TDB_INCOMPATIBLE_HASH |
3105 (lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST)),
3106 O_RDWR|O_CREAT, 0600);
3108 if (wcache->tdb == NULL) {
3109 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
3110 return false;
3113 return true;
3116 /************************************************************************
3117 This is called by the parent to initialize the cache file.
3118 We don't need sophisticated locking here as we know we're the
3119 only opener.
3120 ************************************************************************/
3122 bool initialize_winbindd_cache(void)
3124 bool cache_bad = true;
3125 uint32 vers;
3127 if (!init_wcache()) {
3128 DEBUG(0,("initialize_winbindd_cache: init_wcache failed.\n"));
3129 return false;
3132 /* Check version number. */
3133 if (tdb_fetch_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, &vers) &&
3134 vers == WINBINDD_CACHE_VERSION) {
3135 cache_bad = false;
3138 if (cache_bad) {
3139 DEBUG(0,("initialize_winbindd_cache: clearing cache "
3140 "and re-creating with version number %d\n",
3141 WINBINDD_CACHE_VERSION ));
3143 tdb_close(wcache->tdb);
3144 wcache->tdb = NULL;
3146 if (unlink(cache_path("winbindd_cache.tdb")) == -1) {
3147 DEBUG(0,("initialize_winbindd_cache: unlink %s failed %s ",
3148 cache_path("winbindd_cache.tdb"),
3149 strerror(errno) ));
3150 return false;
3152 if (!init_wcache()) {
3153 DEBUG(0,("initialize_winbindd_cache: re-initialization "
3154 "init_wcache failed.\n"));
3155 return false;
3158 /* Write the version. */
3159 if (!tdb_store_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, WINBINDD_CACHE_VERSION)) {
3160 DEBUG(0,("initialize_winbindd_cache: version number store failed %s\n",
3161 tdb_errorstr(wcache->tdb) ));
3162 return false;
3166 tdb_close(wcache->tdb);
3167 wcache->tdb = NULL;
3168 return true;
3171 void close_winbindd_cache(void)
3173 if (!wcache) {
3174 return;
3176 if (wcache->tdb) {
3177 tdb_close(wcache->tdb);
3178 wcache->tdb = NULL;
3182 bool lookup_cached_sid(TALLOC_CTX *mem_ctx, const struct dom_sid *sid,
3183 char **domain_name, char **name,
3184 enum lsa_SidType *type)
3186 struct winbindd_domain *domain;
3187 NTSTATUS status;
3189 domain = find_lookup_domain_from_sid(sid);
3190 if (domain == NULL) {
3191 return false;
3193 status = wcache_sid_to_name(domain, sid, mem_ctx, domain_name, name,
3194 type);
3195 return NT_STATUS_IS_OK(status);
3198 bool lookup_cached_name(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 TDB_INCOMPATIBLE_HASH |
3278 (lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST)),
3279 O_RDWR|O_CREAT, 0600);
3281 if (!wcache->tdb) {
3282 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
3283 return;
3286 tdb_traverse(wcache->tdb, traverse_fn_cleanup, NULL);
3288 DEBUG(10,("wcache_flush_cache success\n"));
3291 /* Count cached creds */
3293 static int traverse_fn_cached_creds(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
3294 void *state)
3296 int *cred_count = (int*)state;
3298 if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
3299 (*cred_count)++;
3301 return 0;
3304 NTSTATUS wcache_count_cached_creds(struct winbindd_domain *domain, int *count)
3306 struct winbind_cache *cache = get_cache(domain);
3308 *count = 0;
3310 if (!cache->tdb) {
3311 return NT_STATUS_INTERNAL_DB_ERROR;
3314 tdb_traverse(cache->tdb, traverse_fn_cached_creds, (void *)count);
3316 return NT_STATUS_OK;
3319 struct cred_list {
3320 struct cred_list *prev, *next;
3321 TDB_DATA key;
3322 fstring name;
3323 time_t created;
3325 static struct cred_list *wcache_cred_list;
3327 static int traverse_fn_get_credlist(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
3328 void *state)
3330 struct cred_list *cred;
3332 if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
3334 cred = SMB_MALLOC_P(struct cred_list);
3335 if (cred == NULL) {
3336 DEBUG(0,("traverse_fn_remove_first_creds: failed to malloc new entry for list\n"));
3337 return -1;
3340 ZERO_STRUCTP(cred);
3342 /* save a copy of the key */
3344 fstrcpy(cred->name, (const char *)kbuf.dptr);
3345 DLIST_ADD(wcache_cred_list, cred);
3348 return 0;
3351 NTSTATUS wcache_remove_oldest_cached_creds(struct winbindd_domain *domain, const struct dom_sid *sid)
3353 struct winbind_cache *cache = get_cache(domain);
3354 NTSTATUS status;
3355 int ret;
3356 struct cred_list *cred, *oldest = NULL;
3358 if (!cache->tdb) {
3359 return NT_STATUS_INTERNAL_DB_ERROR;
3362 /* we possibly already have an entry */
3363 if (sid && NT_STATUS_IS_OK(wcache_cached_creds_exist(domain, sid))) {
3365 fstring key_str, tmp;
3367 DEBUG(11,("we already have an entry, deleting that\n"));
3369 fstr_sprintf(key_str, "CRED/%s", sid_to_fstring(tmp, sid));
3371 tdb_delete(cache->tdb, string_tdb_data(key_str));
3373 return NT_STATUS_OK;
3376 ret = tdb_traverse(cache->tdb, traverse_fn_get_credlist, NULL);
3377 if (ret == 0) {
3378 return NT_STATUS_OK;
3379 } else if ((ret == -1) || (wcache_cred_list == NULL)) {
3380 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
3383 ZERO_STRUCTP(oldest);
3385 for (cred = wcache_cred_list; cred; cred = cred->next) {
3387 TDB_DATA data;
3388 time_t t;
3390 data = tdb_fetch(cache->tdb, string_tdb_data(cred->name));
3391 if (!data.dptr) {
3392 DEBUG(10,("wcache_remove_oldest_cached_creds: entry for [%s] not found\n",
3393 cred->name));
3394 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
3395 goto done;
3398 t = IVAL(data.dptr, 0);
3399 SAFE_FREE(data.dptr);
3401 if (!oldest) {
3402 oldest = SMB_MALLOC_P(struct cred_list);
3403 if (oldest == NULL) {
3404 status = NT_STATUS_NO_MEMORY;
3405 goto done;
3408 fstrcpy(oldest->name, cred->name);
3409 oldest->created = t;
3410 continue;
3413 if (t < oldest->created) {
3414 fstrcpy(oldest->name, cred->name);
3415 oldest->created = t;
3419 if (tdb_delete(cache->tdb, string_tdb_data(oldest->name)) == 0) {
3420 status = NT_STATUS_OK;
3421 } else {
3422 status = NT_STATUS_UNSUCCESSFUL;
3424 done:
3425 SAFE_FREE(wcache_cred_list);
3426 SAFE_FREE(oldest);
3428 return status;
3431 /* Change the global online/offline state. */
3432 bool set_global_winbindd_state_offline(void)
3434 TDB_DATA data;
3436 DEBUG(10,("set_global_winbindd_state_offline: offline requested.\n"));
3438 /* Only go offline if someone has created
3439 the key "WINBINDD_OFFLINE" in the cache tdb. */
3441 if (wcache == NULL || wcache->tdb == NULL) {
3442 DEBUG(10,("set_global_winbindd_state_offline: wcache not open yet.\n"));
3443 return false;
3446 if (!lp_winbind_offline_logon()) {
3447 DEBUG(10,("set_global_winbindd_state_offline: rejecting.\n"));
3448 return false;
3451 if (global_winbindd_offline_state) {
3452 /* Already offline. */
3453 return true;
3456 data = tdb_fetch_bystring( wcache->tdb, "WINBINDD_OFFLINE" );
3458 if (!data.dptr || data.dsize != 4) {
3459 DEBUG(10,("set_global_winbindd_state_offline: offline state not set.\n"));
3460 SAFE_FREE(data.dptr);
3461 return false;
3462 } else {
3463 DEBUG(10,("set_global_winbindd_state_offline: offline state set.\n"));
3464 global_winbindd_offline_state = true;
3465 SAFE_FREE(data.dptr);
3466 return true;
3470 void set_global_winbindd_state_online(void)
3472 DEBUG(10,("set_global_winbindd_state_online: online requested.\n"));
3474 if (!lp_winbind_offline_logon()) {
3475 DEBUG(10,("set_global_winbindd_state_online: rejecting.\n"));
3476 return;
3479 if (!global_winbindd_offline_state) {
3480 /* Already online. */
3481 return;
3483 global_winbindd_offline_state = false;
3485 if (!wcache->tdb) {
3486 return;
3489 /* Ensure there is no key "WINBINDD_OFFLINE" in the cache tdb. */
3490 tdb_delete_bystring(wcache->tdb, "WINBINDD_OFFLINE");
3493 bool get_global_winbindd_state_offline(void)
3495 return global_winbindd_offline_state;
3498 /***********************************************************************
3499 Validate functions for all possible cache tdb keys.
3500 ***********************************************************************/
3502 static struct cache_entry *create_centry_validate(const char *kstr, TDB_DATA data,
3503 struct tdb_validation_status *state)
3505 struct cache_entry *centry;
3507 centry = SMB_XMALLOC_P(struct cache_entry);
3508 centry->data = (unsigned char *)memdup(data.dptr, data.dsize);
3509 if (!centry->data) {
3510 SAFE_FREE(centry);
3511 return NULL;
3513 centry->len = data.dsize;
3514 centry->ofs = 0;
3516 if (centry->len < 16) {
3517 /* huh? corrupt cache? */
3518 DEBUG(0,("create_centry_validate: Corrupt cache for key %s "
3519 "(len < 16) ?\n", kstr));
3520 centry_free(centry);
3521 state->bad_entry = true;
3522 state->success = false;
3523 return NULL;
3526 centry->status = NT_STATUS(centry_uint32(centry));
3527 centry->sequence_number = centry_uint32(centry);
3528 centry->timeout = centry_uint64_t(centry);
3529 return centry;
3532 static int validate_seqnum(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3533 struct tdb_validation_status *state)
3535 if (dbuf.dsize != 8) {
3536 DEBUG(0,("validate_seqnum: Corrupt cache for key %s (len %u != 8) ?\n",
3537 keystr, (unsigned int)dbuf.dsize ));
3538 state->bad_entry = true;
3539 return 1;
3541 return 0;
3544 static int validate_ns(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3545 struct tdb_validation_status *state)
3547 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3548 if (!centry) {
3549 return 1;
3552 (void)centry_uint32(centry);
3553 if (NT_STATUS_IS_OK(centry->status)) {
3554 struct dom_sid sid;
3555 (void)centry_sid(centry, &sid);
3558 centry_free(centry);
3560 if (!(state->success)) {
3561 return 1;
3563 DEBUG(10,("validate_ns: %s ok\n", keystr));
3564 return 0;
3567 static int validate_sn(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3568 struct tdb_validation_status *state)
3570 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3571 if (!centry) {
3572 return 1;
3575 if (NT_STATUS_IS_OK(centry->status)) {
3576 (void)centry_uint32(centry);
3577 (void)centry_string(centry, mem_ctx);
3578 (void)centry_string(centry, mem_ctx);
3581 centry_free(centry);
3583 if (!(state->success)) {
3584 return 1;
3586 DEBUG(10,("validate_sn: %s ok\n", keystr));
3587 return 0;
3590 static int validate_u(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3591 struct tdb_validation_status *state)
3593 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3594 struct dom_sid sid;
3596 if (!centry) {
3597 return 1;
3600 (void)centry_string(centry, mem_ctx);
3601 (void)centry_string(centry, mem_ctx);
3602 (void)centry_string(centry, mem_ctx);
3603 (void)centry_string(centry, mem_ctx);
3604 (void)centry_uint32(centry);
3605 (void)centry_sid(centry, &sid);
3606 (void)centry_sid(centry, &sid);
3608 centry_free(centry);
3610 if (!(state->success)) {
3611 return 1;
3613 DEBUG(10,("validate_u: %s ok\n", keystr));
3614 return 0;
3617 static int validate_loc_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3618 struct tdb_validation_status *state)
3620 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3622 if (!centry) {
3623 return 1;
3626 (void)centry_nttime(centry);
3627 (void)centry_nttime(centry);
3628 (void)centry_uint16(centry);
3630 centry_free(centry);
3632 if (!(state->success)) {
3633 return 1;
3635 DEBUG(10,("validate_loc_pol: %s ok\n", keystr));
3636 return 0;
3639 static int validate_pwd_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3640 struct tdb_validation_status *state)
3642 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3644 if (!centry) {
3645 return 1;
3648 (void)centry_uint16(centry);
3649 (void)centry_uint16(centry);
3650 (void)centry_uint32(centry);
3651 (void)centry_nttime(centry);
3652 (void)centry_nttime(centry);
3654 centry_free(centry);
3656 if (!(state->success)) {
3657 return 1;
3659 DEBUG(10,("validate_pwd_pol: %s ok\n", keystr));
3660 return 0;
3663 static int validate_cred(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3664 struct tdb_validation_status *state)
3666 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3668 if (!centry) {
3669 return 1;
3672 (void)centry_time(centry);
3673 (void)centry_hash16(centry, mem_ctx);
3675 /* We only have 17 bytes more data in the salted cred case. */
3676 if (centry->len - centry->ofs == 17) {
3677 (void)centry_hash16(centry, mem_ctx);
3680 centry_free(centry);
3682 if (!(state->success)) {
3683 return 1;
3685 DEBUG(10,("validate_cred: %s ok\n", keystr));
3686 return 0;
3689 static int validate_ul(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3690 struct tdb_validation_status *state)
3692 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3693 int32 num_entries, i;
3695 if (!centry) {
3696 return 1;
3699 num_entries = (int32)centry_uint32(centry);
3701 for (i=0; i< num_entries; i++) {
3702 struct dom_sid sid;
3703 (void)centry_string(centry, mem_ctx);
3704 (void)centry_string(centry, mem_ctx);
3705 (void)centry_string(centry, mem_ctx);
3706 (void)centry_string(centry, mem_ctx);
3707 (void)centry_sid(centry, &sid);
3708 (void)centry_sid(centry, &sid);
3711 centry_free(centry);
3713 if (!(state->success)) {
3714 return 1;
3716 DEBUG(10,("validate_ul: %s ok\n", keystr));
3717 return 0;
3720 static int validate_gl(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3721 struct tdb_validation_status *state)
3723 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3724 int32 num_entries, i;
3726 if (!centry) {
3727 return 1;
3730 num_entries = centry_uint32(centry);
3732 for (i=0; i< num_entries; i++) {
3733 (void)centry_string(centry, mem_ctx);
3734 (void)centry_string(centry, mem_ctx);
3735 (void)centry_uint32(centry);
3738 centry_free(centry);
3740 if (!(state->success)) {
3741 return 1;
3743 DEBUG(10,("validate_gl: %s ok\n", keystr));
3744 return 0;
3747 static int validate_ug(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3748 struct tdb_validation_status *state)
3750 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3751 int32 num_groups, i;
3753 if (!centry) {
3754 return 1;
3757 num_groups = centry_uint32(centry);
3759 for (i=0; i< num_groups; i++) {
3760 struct dom_sid sid;
3761 centry_sid(centry, &sid);
3764 centry_free(centry);
3766 if (!(state->success)) {
3767 return 1;
3769 DEBUG(10,("validate_ug: %s ok\n", keystr));
3770 return 0;
3773 static int validate_ua(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3774 struct tdb_validation_status *state)
3776 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3777 int32 num_aliases, i;
3779 if (!centry) {
3780 return 1;
3783 num_aliases = centry_uint32(centry);
3785 for (i=0; i < num_aliases; i++) {
3786 (void)centry_uint32(centry);
3789 centry_free(centry);
3791 if (!(state->success)) {
3792 return 1;
3794 DEBUG(10,("validate_ua: %s ok\n", keystr));
3795 return 0;
3798 static int validate_gm(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3799 struct tdb_validation_status *state)
3801 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3802 int32 num_names, i;
3804 if (!centry) {
3805 return 1;
3808 num_names = centry_uint32(centry);
3810 for (i=0; i< num_names; i++) {
3811 struct dom_sid sid;
3812 centry_sid(centry, &sid);
3813 (void)centry_string(centry, mem_ctx);
3814 (void)centry_uint32(centry);
3817 centry_free(centry);
3819 if (!(state->success)) {
3820 return 1;
3822 DEBUG(10,("validate_gm: %s ok\n", keystr));
3823 return 0;
3826 static int validate_dr(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3827 struct tdb_validation_status *state)
3829 /* Can't say anything about this other than must be nonzero. */
3830 if (dbuf.dsize == 0) {
3831 DEBUG(0,("validate_dr: Corrupt cache for key %s (len == 0) ?\n",
3832 keystr));
3833 state->bad_entry = true;
3834 state->success = false;
3835 return 1;
3838 DEBUG(10,("validate_dr: %s ok\n", keystr));
3839 return 0;
3842 static int validate_de(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3843 struct tdb_validation_status *state)
3845 /* Can't say anything about this other than must be nonzero. */
3846 if (dbuf.dsize == 0) {
3847 DEBUG(0,("validate_de: Corrupt cache for key %s (len == 0) ?\n",
3848 keystr));
3849 state->bad_entry = true;
3850 state->success = false;
3851 return 1;
3854 DEBUG(10,("validate_de: %s ok\n", keystr));
3855 return 0;
3858 static int validate_pwinfo(TALLOC_CTX *mem_ctx, const char *keystr,
3859 TDB_DATA dbuf, struct tdb_validation_status *state)
3861 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3863 if (!centry) {
3864 return 1;
3867 (void)centry_string(centry, mem_ctx);
3868 (void)centry_string(centry, mem_ctx);
3869 (void)centry_string(centry, mem_ctx);
3870 (void)centry_uint32(centry);
3872 centry_free(centry);
3874 if (!(state->success)) {
3875 return 1;
3877 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3878 return 0;
3881 static int validate_nss_an(TALLOC_CTX *mem_ctx, const char *keystr,
3882 TDB_DATA dbuf,
3883 struct tdb_validation_status *state)
3885 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3887 if (!centry) {
3888 return 1;
3891 (void)centry_string( centry, mem_ctx );
3893 centry_free(centry);
3895 if (!(state->success)) {
3896 return 1;
3898 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3899 return 0;
3902 static int validate_nss_na(TALLOC_CTX *mem_ctx, const char *keystr,
3903 TDB_DATA dbuf,
3904 struct tdb_validation_status *state)
3906 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3908 if (!centry) {
3909 return 1;
3912 (void)centry_string( centry, mem_ctx );
3914 centry_free(centry);
3916 if (!(state->success)) {
3917 return 1;
3919 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3920 return 0;
3923 static int validate_trustdomcache(TALLOC_CTX *mem_ctx, const char *keystr,
3924 TDB_DATA dbuf,
3925 struct tdb_validation_status *state)
3927 if (dbuf.dsize == 0) {
3928 DEBUG(0, ("validate_trustdomcache: Corrupt cache for "
3929 "key %s (len ==0) ?\n", keystr));
3930 state->bad_entry = true;
3931 state->success = false;
3932 return 1;
3935 DEBUG(10, ("validate_trustdomcache: %s ok\n", keystr));
3936 DEBUGADD(10, (" Don't trust me, I am a DUMMY!\n"));
3937 return 0;
3940 static int validate_offline(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3941 struct tdb_validation_status *state)
3943 if (dbuf.dsize != 4) {
3944 DEBUG(0,("validate_offline: Corrupt cache for key %s (len %u != 4) ?\n",
3945 keystr, (unsigned int)dbuf.dsize ));
3946 state->bad_entry = true;
3947 state->success = false;
3948 return 1;
3950 DEBUG(10,("validate_offline: %s ok\n", keystr));
3951 return 0;
3954 static int validate_ndr(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3955 struct tdb_validation_status *state)
3958 * Ignore validation for now. The proper way to do this is with a
3959 * checksum. Just pure parsing does not really catch much.
3961 return 0;
3964 static int validate_cache_version(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3965 struct tdb_validation_status *state)
3967 if (dbuf.dsize != 4) {
3968 DEBUG(0, ("validate_cache_version: Corrupt cache for "
3969 "key %s (len %u != 4) ?\n",
3970 keystr, (unsigned int)dbuf.dsize));
3971 state->bad_entry = true;
3972 state->success = false;
3973 return 1;
3976 DEBUG(10, ("validate_cache_version: %s ok\n", keystr));
3977 return 0;
3980 /***********************************************************************
3981 A list of all possible cache tdb keys with associated validation
3982 functions.
3983 ***********************************************************************/
3985 struct key_val_struct {
3986 const char *keyname;
3987 int (*validate_data_fn)(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, struct tdb_validation_status* state);
3988 } key_val[] = {
3989 {"SEQNUM/", validate_seqnum},
3990 {"NS/", validate_ns},
3991 {"SN/", validate_sn},
3992 {"U/", validate_u},
3993 {"LOC_POL/", validate_loc_pol},
3994 {"PWD_POL/", validate_pwd_pol},
3995 {"CRED/", validate_cred},
3996 {"UL/", validate_ul},
3997 {"GL/", validate_gl},
3998 {"UG/", validate_ug},
3999 {"UA", validate_ua},
4000 {"GM/", validate_gm},
4001 {"DR/", validate_dr},
4002 {"DE/", validate_de},
4003 {"NSS/PWINFO/", validate_pwinfo},
4004 {"TRUSTDOMCACHE/", validate_trustdomcache},
4005 {"NSS/NA/", validate_nss_na},
4006 {"NSS/AN/", validate_nss_an},
4007 {"WINBINDD_OFFLINE", validate_offline},
4008 {"NDR/", validate_ndr},
4009 {WINBINDD_CACHE_VERSION_KEYSTR, validate_cache_version},
4010 {NULL, NULL}
4013 /***********************************************************************
4014 Function to look at every entry in the tdb and validate it as far as
4015 possible.
4016 ***********************************************************************/
4018 static int cache_traverse_validate_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, void *state)
4020 int i;
4021 unsigned int max_key_len = 1024;
4022 struct tdb_validation_status *v_state = (struct tdb_validation_status *)state;
4024 /* Paranoia check. */
4025 if (strncmp("UA/", (const char *)kbuf.dptr, 3) == 0) {
4026 max_key_len = 1024 * 1024;
4028 if (kbuf.dsize > max_key_len) {
4029 DEBUG(0, ("cache_traverse_validate_fn: key length too large: "
4030 "(%u) > (%u)\n\n",
4031 (unsigned int)kbuf.dsize, (unsigned int)max_key_len));
4032 return 1;
4035 for (i = 0; key_val[i].keyname; i++) {
4036 size_t namelen = strlen(key_val[i].keyname);
4037 if (kbuf.dsize >= namelen && (
4038 strncmp(key_val[i].keyname, (const char *)kbuf.dptr, namelen)) == 0) {
4039 TALLOC_CTX *mem_ctx;
4040 char *keystr;
4041 int ret;
4043 keystr = SMB_MALLOC_ARRAY(char, kbuf.dsize+1);
4044 if (!keystr) {
4045 return 1;
4047 memcpy(keystr, kbuf.dptr, kbuf.dsize);
4048 keystr[kbuf.dsize] = '\0';
4050 mem_ctx = talloc_init("validate_ctx");
4051 if (!mem_ctx) {
4052 SAFE_FREE(keystr);
4053 return 1;
4056 ret = key_val[i].validate_data_fn(mem_ctx, keystr, dbuf,
4057 v_state);
4059 SAFE_FREE(keystr);
4060 talloc_destroy(mem_ctx);
4061 return ret;
4065 DEBUG(0,("cache_traverse_validate_fn: unknown cache entry\nkey :\n"));
4066 dump_data(0, (uint8 *)kbuf.dptr, kbuf.dsize);
4067 DEBUG(0,("data :\n"));
4068 dump_data(0, (uint8 *)dbuf.dptr, dbuf.dsize);
4069 v_state->unknown_key = true;
4070 v_state->success = false;
4071 return 1; /* terminate. */
4074 static void validate_panic(const char *const why)
4076 DEBUG(0,("validating cache: would panic %s\n", why ));
4077 DEBUGADD(0, ("exiting instead (cache validation mode)\n"));
4078 exit(47);
4081 /***********************************************************************
4082 Try and validate every entry in the winbindd cache. If we fail here,
4083 delete the cache tdb and return non-zero.
4084 ***********************************************************************/
4086 int winbindd_validate_cache(void)
4088 int ret = -1;
4089 const char *tdb_path = cache_path("winbindd_cache.tdb");
4090 TDB_CONTEXT *tdb = NULL;
4092 DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
4093 smb_panic_fn = validate_panic;
4096 tdb = tdb_open_log(tdb_path,
4097 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
4098 TDB_INCOMPATIBLE_HASH |
4099 ( lp_winbind_offline_logon()
4100 ? TDB_DEFAULT
4101 : TDB_DEFAULT | TDB_CLEAR_IF_FIRST ),
4102 O_RDWR|O_CREAT,
4103 0600);
4104 if (!tdb) {
4105 DEBUG(0, ("winbindd_validate_cache: "
4106 "error opening/initializing tdb\n"));
4107 goto done;
4109 tdb_close(tdb);
4111 ret = tdb_validate_and_backup(tdb_path, cache_traverse_validate_fn);
4113 if (ret != 0) {
4114 DEBUG(10, ("winbindd_validate_cache: validation not successful.\n"));
4115 DEBUGADD(10, ("removing tdb %s.\n", tdb_path));
4116 unlink(tdb_path);
4119 done:
4120 DEBUG(10, ("winbindd_validate_cache: restoring panic function\n"));
4121 smb_panic_fn = smb_panic;
4122 return ret;
4125 /***********************************************************************
4126 Try and validate every entry in the winbindd cache.
4127 ***********************************************************************/
4129 int winbindd_validate_cache_nobackup(void)
4131 int ret = -1;
4132 const char *tdb_path = cache_path("winbindd_cache.tdb");
4134 DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
4135 smb_panic_fn = validate_panic;
4138 if (wcache == NULL || wcache->tdb == NULL) {
4139 ret = tdb_validate_open(tdb_path, cache_traverse_validate_fn);
4140 } else {
4141 ret = tdb_validate(wcache->tdb, cache_traverse_validate_fn);
4144 if (ret != 0) {
4145 DEBUG(10, ("winbindd_validate_cache_nobackup: validation not "
4146 "successful.\n"));
4149 DEBUG(10, ("winbindd_validate_cache_nobackup: restoring panic "
4150 "function\n"));
4151 smb_panic_fn = smb_panic;
4152 return ret;
4155 bool winbindd_cache_validate_and_initialize(void)
4157 close_winbindd_cache();
4159 if (lp_winbind_offline_logon()) {
4160 if (winbindd_validate_cache() < 0) {
4161 DEBUG(0, ("winbindd cache tdb corrupt and no backup "
4162 "could be restored.\n"));
4166 return initialize_winbindd_cache();
4169 /*********************************************************************
4170 ********************************************************************/
4172 static bool add_wbdomain_to_tdc_array( struct winbindd_domain *new_dom,
4173 struct winbindd_tdc_domain **domains,
4174 size_t *num_domains )
4176 struct winbindd_tdc_domain *list = NULL;
4177 size_t idx;
4178 int i;
4179 bool set_only = false;
4181 /* don't allow duplicates */
4183 idx = *num_domains;
4184 list = *domains;
4186 for ( i=0; i< (*num_domains); i++ ) {
4187 if ( strequal( new_dom->name, list[i].domain_name ) ) {
4188 DEBUG(10,("add_wbdomain_to_tdc_array: Found existing record for %s\n",
4189 new_dom->name));
4190 idx = i;
4191 set_only = true;
4193 break;
4197 if ( !set_only ) {
4198 if ( !*domains ) {
4199 list = TALLOC_ARRAY( NULL, struct winbindd_tdc_domain, 1 );
4200 idx = 0;
4201 } else {
4202 list = TALLOC_REALLOC_ARRAY( *domains, *domains,
4203 struct winbindd_tdc_domain,
4204 (*num_domains)+1);
4205 idx = *num_domains;
4208 ZERO_STRUCT( list[idx] );
4211 if ( !list )
4212 return false;
4214 list[idx].domain_name = talloc_strdup( list, new_dom->name );
4215 list[idx].dns_name = talloc_strdup( list, new_dom->alt_name );
4217 if ( !is_null_sid( &new_dom->sid ) ) {
4218 sid_copy( &list[idx].sid, &new_dom->sid );
4219 } else {
4220 sid_copy(&list[idx].sid, &global_sid_NULL);
4223 if ( new_dom->domain_flags != 0x0 )
4224 list[idx].trust_flags = new_dom->domain_flags;
4226 if ( new_dom->domain_type != 0x0 )
4227 list[idx].trust_type = new_dom->domain_type;
4229 if ( new_dom->domain_trust_attribs != 0x0 )
4230 list[idx].trust_attribs = new_dom->domain_trust_attribs;
4232 if ( !set_only ) {
4233 *domains = list;
4234 *num_domains = idx + 1;
4237 return true;
4240 /*********************************************************************
4241 ********************************************************************/
4243 static TDB_DATA make_tdc_key( const char *domain_name )
4245 char *keystr = NULL;
4246 TDB_DATA key = { NULL, 0 };
4248 if ( !domain_name ) {
4249 DEBUG(5,("make_tdc_key: Keyname workgroup is NULL!\n"));
4250 return key;
4253 if (asprintf( &keystr, "TRUSTDOMCACHE/%s", domain_name ) == -1) {
4254 return key;
4256 key = string_term_tdb_data(keystr);
4258 return key;
4261 /*********************************************************************
4262 ********************************************************************/
4264 static int pack_tdc_domains( struct winbindd_tdc_domain *domains,
4265 size_t num_domains,
4266 unsigned char **buf )
4268 unsigned char *buffer = NULL;
4269 int len = 0;
4270 int buflen = 0;
4271 int i = 0;
4273 DEBUG(10,("pack_tdc_domains: Packing %d trusted domains\n",
4274 (int)num_domains));
4276 buflen = 0;
4278 again:
4279 len = 0;
4281 /* Store the number of array items first */
4282 len += tdb_pack( buffer+len, buflen-len, "d",
4283 num_domains );
4285 /* now pack each domain trust record */
4286 for ( i=0; i<num_domains; i++ ) {
4288 fstring tmp;
4290 if ( buflen > 0 ) {
4291 DEBUG(10,("pack_tdc_domains: Packing domain %s (%s)\n",
4292 domains[i].domain_name,
4293 domains[i].dns_name ? domains[i].dns_name : "UNKNOWN" ));
4296 len += tdb_pack( buffer+len, buflen-len, "fffddd",
4297 domains[i].domain_name,
4298 domains[i].dns_name,
4299 sid_to_fstring(tmp, &domains[i].sid),
4300 domains[i].trust_flags,
4301 domains[i].trust_attribs,
4302 domains[i].trust_type );
4305 if ( buflen < len ) {
4306 SAFE_FREE(buffer);
4307 if ( (buffer = SMB_MALLOC_ARRAY(unsigned char, len)) == NULL ) {
4308 DEBUG(0,("pack_tdc_domains: failed to alloc buffer!\n"));
4309 buflen = -1;
4310 goto done;
4312 buflen = len;
4313 goto again;
4316 *buf = buffer;
4318 done:
4319 return buflen;
4322 /*********************************************************************
4323 ********************************************************************/
4325 static size_t unpack_tdc_domains( unsigned char *buf, int buflen,
4326 struct winbindd_tdc_domain **domains )
4328 fstring domain_name, dns_name, sid_string;
4329 uint32 type, attribs, flags;
4330 int num_domains;
4331 int len = 0;
4332 int i;
4333 struct winbindd_tdc_domain *list = NULL;
4335 /* get the number of domains */
4336 len += tdb_unpack( buf+len, buflen-len, "d", &num_domains);
4337 if ( len == -1 ) {
4338 DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
4339 return 0;
4342 list = TALLOC_ARRAY( NULL, struct winbindd_tdc_domain, num_domains );
4343 if ( !list ) {
4344 DEBUG(0,("unpack_tdc_domains: Failed to talloc() domain list!\n"));
4345 return 0;
4348 for ( i=0; i<num_domains; i++ ) {
4349 len += tdb_unpack( buf+len, buflen-len, "fffddd",
4350 domain_name,
4351 dns_name,
4352 sid_string,
4353 &flags,
4354 &attribs,
4355 &type );
4357 if ( len == -1 ) {
4358 DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
4359 TALLOC_FREE( list );
4360 return 0;
4363 DEBUG(11,("unpack_tdc_domains: Unpacking domain %s (%s) "
4364 "SID %s, flags = 0x%x, attribs = 0x%x, type = 0x%x\n",
4365 domain_name, dns_name, sid_string,
4366 flags, attribs, type));
4368 list[i].domain_name = talloc_strdup( list, domain_name );
4369 list[i].dns_name = talloc_strdup( list, dns_name );
4370 if ( !string_to_sid( &(list[i].sid), sid_string ) ) {
4371 DEBUG(10,("unpack_tdc_domains: no SID for domain %s\n",
4372 domain_name));
4374 list[i].trust_flags = flags;
4375 list[i].trust_attribs = attribs;
4376 list[i].trust_type = type;
4379 *domains = list;
4381 return num_domains;
4384 /*********************************************************************
4385 ********************************************************************/
4387 static bool wcache_tdc_store_list( struct winbindd_tdc_domain *domains, size_t num_domains )
4389 TDB_DATA key = make_tdc_key( lp_workgroup() );
4390 TDB_DATA data = { NULL, 0 };
4391 int ret;
4393 if ( !key.dptr )
4394 return false;
4396 /* See if we were asked to delete the cache entry */
4398 if ( !domains ) {
4399 ret = tdb_delete( wcache->tdb, key );
4400 goto done;
4403 data.dsize = pack_tdc_domains( domains, num_domains, &data.dptr );
4405 if ( !data.dptr ) {
4406 ret = -1;
4407 goto done;
4410 ret = tdb_store( wcache->tdb, key, data, 0 );
4412 done:
4413 SAFE_FREE( data.dptr );
4414 SAFE_FREE( key.dptr );
4416 return ( ret != -1 );
4419 /*********************************************************************
4420 ********************************************************************/
4422 bool wcache_tdc_fetch_list( struct winbindd_tdc_domain **domains, size_t *num_domains )
4424 TDB_DATA key = make_tdc_key( lp_workgroup() );
4425 TDB_DATA data = { NULL, 0 };
4427 *domains = NULL;
4428 *num_domains = 0;
4430 if ( !key.dptr )
4431 return false;
4433 data = tdb_fetch( wcache->tdb, key );
4435 SAFE_FREE( key.dptr );
4437 if ( !data.dptr )
4438 return false;
4440 *num_domains = unpack_tdc_domains( data.dptr, data.dsize, domains );
4442 SAFE_FREE( data.dptr );
4444 if ( !*domains )
4445 return false;
4447 return true;
4450 /*********************************************************************
4451 ********************************************************************/
4453 bool wcache_tdc_add_domain( struct winbindd_domain *domain )
4455 struct winbindd_tdc_domain *dom_list = NULL;
4456 size_t num_domains = 0;
4457 bool ret = false;
4459 DEBUG(10,("wcache_tdc_add_domain: Adding domain %s (%s), SID %s, "
4460 "flags = 0x%x, attributes = 0x%x, type = 0x%x\n",
4461 domain->name, domain->alt_name,
4462 sid_string_dbg(&domain->sid),
4463 domain->domain_flags,
4464 domain->domain_trust_attribs,
4465 domain->domain_type));
4467 if ( !init_wcache() ) {
4468 return false;
4471 /* fetch the list */
4473 wcache_tdc_fetch_list( &dom_list, &num_domains );
4475 /* add the new domain */
4477 if ( !add_wbdomain_to_tdc_array( domain, &dom_list, &num_domains ) ) {
4478 goto done;
4481 /* pack the domain */
4483 if ( !wcache_tdc_store_list( dom_list, num_domains ) ) {
4484 goto done;
4487 /* Success */
4489 ret = true;
4490 done:
4491 TALLOC_FREE( dom_list );
4493 return ret;
4496 /*********************************************************************
4497 ********************************************************************/
4499 struct winbindd_tdc_domain * wcache_tdc_fetch_domain( TALLOC_CTX *ctx, const char *name )
4501 struct winbindd_tdc_domain *dom_list = NULL;
4502 size_t num_domains = 0;
4503 int i;
4504 struct winbindd_tdc_domain *d = NULL;
4506 DEBUG(10,("wcache_tdc_fetch_domain: Searching for domain %s\n", name));
4508 if ( !init_wcache() ) {
4509 return false;
4512 /* fetch the list */
4514 wcache_tdc_fetch_list( &dom_list, &num_domains );
4516 for ( i=0; i<num_domains; i++ ) {
4517 if ( strequal(name, dom_list[i].domain_name) ||
4518 strequal(name, dom_list[i].dns_name) )
4520 DEBUG(10,("wcache_tdc_fetch_domain: Found domain %s\n",
4521 name));
4523 d = TALLOC_P( ctx, struct winbindd_tdc_domain );
4524 if ( !d )
4525 break;
4527 d->domain_name = talloc_strdup( d, dom_list[i].domain_name );
4528 d->dns_name = talloc_strdup( d, dom_list[i].dns_name );
4529 sid_copy( &d->sid, &dom_list[i].sid );
4530 d->trust_flags = dom_list[i].trust_flags;
4531 d->trust_type = dom_list[i].trust_type;
4532 d->trust_attribs = dom_list[i].trust_attribs;
4534 break;
4538 TALLOC_FREE( dom_list );
4540 return d;
4543 /*********************************************************************
4544 ********************************************************************/
4546 struct winbindd_tdc_domain*
4547 wcache_tdc_fetch_domainbysid(TALLOC_CTX *ctx,
4548 const struct dom_sid *sid)
4550 struct winbindd_tdc_domain *dom_list = NULL;
4551 size_t num_domains = 0;
4552 int i;
4553 struct winbindd_tdc_domain *d = NULL;
4555 DEBUG(10,("wcache_tdc_fetch_domainbysid: Searching for domain %s\n",
4556 sid_string_dbg(sid)));
4558 if (!init_wcache()) {
4559 return false;
4562 /* fetch the list */
4564 wcache_tdc_fetch_list(&dom_list, &num_domains);
4566 for (i = 0; i<num_domains; i++) {
4567 if (sid_equal(sid, &(dom_list[i].sid))) {
4568 DEBUG(10, ("wcache_tdc_fetch_domainbysid: "
4569 "Found domain %s for SID %s\n",
4570 dom_list[i].domain_name,
4571 sid_string_dbg(sid)));
4573 d = TALLOC_P(ctx, struct winbindd_tdc_domain);
4574 if (!d)
4575 break;
4577 d->domain_name = talloc_strdup(d,
4578 dom_list[i].domain_name);
4580 d->dns_name = talloc_strdup(d, dom_list[i].dns_name);
4581 sid_copy(&d->sid, &dom_list[i].sid);
4582 d->trust_flags = dom_list[i].trust_flags;
4583 d->trust_type = dom_list[i].trust_type;
4584 d->trust_attribs = dom_list[i].trust_attribs;
4586 break;
4590 TALLOC_FREE(dom_list);
4592 return d;
4596 /*********************************************************************
4597 ********************************************************************/
4599 void wcache_tdc_clear( void )
4601 if ( !init_wcache() )
4602 return;
4604 wcache_tdc_store_list( NULL, 0 );
4606 return;
4610 /*********************************************************************
4611 ********************************************************************/
4613 static void wcache_save_user_pwinfo(struct winbindd_domain *domain,
4614 NTSTATUS status,
4615 const struct dom_sid *user_sid,
4616 const char *homedir,
4617 const char *shell,
4618 const char *gecos,
4619 uint32 gid)
4621 struct cache_entry *centry;
4622 fstring tmp;
4624 if ( (centry = centry_start(domain, status)) == NULL )
4625 return;
4627 centry_put_string( centry, homedir );
4628 centry_put_string( centry, shell );
4629 centry_put_string( centry, gecos );
4630 centry_put_uint32( centry, gid );
4632 centry_end(centry, "NSS/PWINFO/%s", sid_to_fstring(tmp, user_sid) );
4634 DEBUG(10,("wcache_save_user_pwinfo: %s\n", sid_string_dbg(user_sid) ));
4636 centry_free(centry);
4639 #ifdef HAVE_ADS
4641 NTSTATUS nss_get_info_cached( struct winbindd_domain *domain,
4642 const struct dom_sid *user_sid,
4643 TALLOC_CTX *ctx,
4644 ADS_STRUCT *ads, LDAPMessage *msg,
4645 const char **homedir, const char **shell,
4646 const char **gecos, gid_t *p_gid)
4648 struct winbind_cache *cache = get_cache(domain);
4649 struct cache_entry *centry = NULL;
4650 NTSTATUS nt_status;
4651 fstring tmp;
4653 if (!cache->tdb)
4654 goto do_query;
4656 centry = wcache_fetch(cache, domain, "NSS/PWINFO/%s",
4657 sid_to_fstring(tmp, user_sid));
4659 if (!centry)
4660 goto do_query;
4662 *homedir = centry_string( centry, ctx );
4663 *shell = centry_string( centry, ctx );
4664 *gecos = centry_string( centry, ctx );
4665 *p_gid = centry_uint32( centry );
4667 centry_free(centry);
4669 DEBUG(10,("nss_get_info_cached: [Cached] - user_sid %s\n",
4670 sid_string_dbg(user_sid)));
4672 return NT_STATUS_OK;
4674 do_query:
4676 nt_status = nss_get_info( domain->name, user_sid, ctx, ads, msg,
4677 homedir, shell, gecos, p_gid );
4679 DEBUG(10, ("nss_get_info returned %s\n", nt_errstr(nt_status)));
4681 if ( NT_STATUS_IS_OK(nt_status) ) {
4682 DEBUG(10, ("result:\n\thomedir = '%s'\n", *homedir));
4683 DEBUGADD(10, ("\tshell = '%s'\n", *shell));
4684 DEBUGADD(10, ("\tgecos = '%s'\n", *gecos));
4685 DEBUGADD(10, ("\tgid = '%u'\n", (unsigned int)*p_gid));
4687 wcache_save_user_pwinfo( domain, nt_status, user_sid,
4688 *homedir, *shell, *gecos, *p_gid );
4691 if ( NT_STATUS_EQUAL( nt_status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND ) ) {
4692 DEBUG(5,("nss_get_info_cached: Setting domain %s offline\n",
4693 domain->name ));
4694 set_domain_offline( domain );
4697 return nt_status;
4700 #endif
4702 /* the cache backend methods are exposed via this structure */
4703 struct winbindd_methods cache_methods = {
4704 true,
4705 query_user_list,
4706 enum_dom_groups,
4707 enum_local_groups,
4708 name_to_sid,
4709 sid_to_name,
4710 rids_to_names,
4711 query_user,
4712 lookup_usergroups,
4713 lookup_useraliases,
4714 lookup_groupmem,
4715 sequence_number,
4716 lockout_policy,
4717 password_policy,
4718 trusted_domains
4721 static bool wcache_ndr_key(TALLOC_CTX *mem_ctx, char *domain_name,
4722 uint32_t opnum, const DATA_BLOB *req,
4723 TDB_DATA *pkey)
4725 char *key;
4726 size_t keylen;
4728 key = talloc_asprintf(mem_ctx, "NDR/%s/%d/", domain_name, (int)opnum);
4729 if (key == NULL) {
4730 return false;
4732 keylen = talloc_get_size(key) - 1;
4734 key = talloc_realloc(mem_ctx, key, char, keylen + req->length);
4735 if (key == NULL) {
4736 return false;
4738 memcpy(key + keylen, req->data, req->length);
4740 pkey->dptr = (uint8_t *)key;
4741 pkey->dsize = talloc_get_size(key);
4742 return true;
4745 static bool wcache_opnum_cacheable(uint32_t opnum)
4747 switch (opnum) {
4748 case NDR_WBINT_PING:
4749 case NDR_WBINT_QUERYSEQUENCENUMBER:
4750 case NDR_WBINT_ALLOCATEUID:
4751 case NDR_WBINT_ALLOCATEGID:
4752 case NDR_WBINT_CHECKMACHINEACCOUNT:
4753 case NDR_WBINT_CHANGEMACHINEACCOUNT:
4754 case NDR_WBINT_PINGDC:
4755 return false;
4757 return true;
4760 bool wcache_fetch_ndr(TALLOC_CTX *mem_ctx, struct winbindd_domain *domain,
4761 uint32_t opnum, const DATA_BLOB *req, DATA_BLOB *resp)
4763 TDB_DATA key, data;
4764 bool ret = false;
4766 if (!wcache_opnum_cacheable(opnum) ||
4767 is_my_own_sam_domain(domain) ||
4768 is_builtin_domain(domain)) {
4769 return false;
4772 if (wcache->tdb == NULL) {
4773 return false;
4776 if (!wcache_ndr_key(talloc_tos(), domain->name, opnum, req, &key)) {
4777 return false;
4779 data = tdb_fetch(wcache->tdb, key);
4780 TALLOC_FREE(key.dptr);
4782 if (data.dptr == NULL) {
4783 return false;
4785 if (data.dsize < 12) {
4786 goto fail;
4789 if (!is_domain_offline(domain)) {
4790 uint32_t entry_seqnum, dom_seqnum, last_check;
4791 uint64_t entry_timeout;
4793 if (!wcache_fetch_seqnum(domain->name, &dom_seqnum,
4794 &last_check)) {
4795 goto fail;
4797 entry_seqnum = IVAL(data.dptr, 0);
4798 if (entry_seqnum != dom_seqnum) {
4799 DEBUG(10, ("Entry has wrong sequence number: %d\n",
4800 (int)entry_seqnum));
4801 goto fail;
4803 entry_timeout = BVAL(data.dptr, 4);
4804 if (entry_timeout > time(NULL)) {
4805 DEBUG(10, ("Entry has timed out\n"));
4806 goto fail;
4810 resp->data = (uint8_t *)talloc_memdup(mem_ctx, data.dptr + 12,
4811 data.dsize - 12);
4812 if (resp->data == NULL) {
4813 DEBUG(10, ("talloc failed\n"));
4814 goto fail;
4816 resp->length = data.dsize - 12;
4818 ret = true;
4819 fail:
4820 SAFE_FREE(data.dptr);
4821 return ret;
4824 void wcache_store_ndr(struct winbindd_domain *domain, uint32_t opnum,
4825 const DATA_BLOB *req, const DATA_BLOB *resp)
4827 TDB_DATA key, data;
4828 uint32_t dom_seqnum, last_check;
4829 uint64_t timeout;
4831 if (!wcache_opnum_cacheable(opnum) ||
4832 is_my_own_sam_domain(domain) ||
4833 is_builtin_domain(domain)) {
4834 return;
4837 if (wcache->tdb == NULL) {
4838 return;
4841 if (!wcache_fetch_seqnum(domain->name, &dom_seqnum, &last_check)) {
4842 DEBUG(10, ("could not fetch seqnum for domain %s\n",
4843 domain->name));
4844 return;
4847 if (!wcache_ndr_key(talloc_tos(), domain->name, opnum, req, &key)) {
4848 return;
4851 timeout = time(NULL) + lp_winbind_cache_time();
4853 data.dsize = resp->length + 12;
4854 data.dptr = talloc_array(key.dptr, uint8_t, data.dsize);
4855 if (data.dptr == NULL) {
4856 goto done;
4859 SIVAL(data.dptr, 0, dom_seqnum);
4860 SBVAL(data.dptr, 4, timeout);
4861 memcpy(data.dptr + 12, resp->data, resp->length);
4863 tdb_store(wcache->tdb, key, data, 0);
4865 done:
4866 TALLOC_FREE(key.dptr);
4867 return;