s3/winbind: remove unused winbindd_check_cache_size
[Samba/gebeck_regimport.git] / source3 / winbindd / winbindd_cache.c
blob1ae0c70e2ed201ef9fff9a83303ac78809e6ca8b
1 /*
2 Unix SMB/CIFS implementation.
4 Winbind cache backend functions
6 Copyright (C) Andrew Tridgell 2001
7 Copyright (C) Gerald Carter 2003-2007
8 Copyright (C) Volker Lendecke 2005
9 Copyright (C) Guenther Deschner 2005
10 Copyright (C) Michael Adam 2007
12 This program is free software; you can redistribute it and/or modify
13 it under the terms of the GNU General Public License as published by
14 the Free Software Foundation; either version 3 of the License, or
15 (at your option) any later version.
17 This program is distributed in the hope that it will be useful,
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 GNU General Public License for more details.
22 You should have received a copy of the GNU General Public License
23 along with this program. If not, see <http://www.gnu.org/licenses/>.
26 #include "includes.h"
27 #include "winbindd.h"
28 #include "tdb_validate.h"
29 #include "../libcli/auth/libcli_auth.h"
30 #include "../librpc/gen_ndr/ndr_wbint.h"
31 #include "ads.h"
32 #include "nss_info.h"
34 #undef DBGC_CLASS
35 #define DBGC_CLASS DBGC_WINBIND
37 #define WINBINDD_CACHE_VERSION 1
38 #define WINBINDD_CACHE_VERSION_KEYSTR "WINBINDD_CACHE_VERSION"
40 extern struct winbindd_methods reconnect_methods;
41 #ifdef HAVE_ADS
42 extern struct winbindd_methods ads_methods;
43 #endif
44 extern struct winbindd_methods builtin_passdb_methods;
45 extern struct winbindd_methods sam_passdb_methods;
48 * JRA. KEEP THIS LIST UP TO DATE IF YOU ADD CACHE ENTRIES.
49 * Here are the list of entry types that are *not* stored
50 * as form struct cache_entry in the cache.
53 static const char *non_centry_keys[] = {
54 "SEQNUM/",
55 "DR/",
56 "DE/",
57 "WINBINDD_OFFLINE",
58 WINBINDD_CACHE_VERSION_KEYSTR,
59 NULL
62 /************************************************************************
63 Is this key a non-centry type ?
64 ************************************************************************/
66 static bool is_non_centry_key(TDB_DATA kbuf)
68 int i;
70 if (kbuf.dptr == NULL || kbuf.dsize == 0) {
71 return false;
73 for (i = 0; non_centry_keys[i] != NULL; i++) {
74 size_t namelen = strlen(non_centry_keys[i]);
75 if (kbuf.dsize < namelen) {
76 continue;
78 if (strncmp(non_centry_keys[i], (const char *)kbuf.dptr, namelen) == 0) {
79 return true;
82 return false;
85 /* Global online/offline state - False when online. winbindd starts up online
86 and sets this to true if the first query fails and there's an entry in
87 the cache tdb telling us to stay offline. */
89 static bool global_winbindd_offline_state;
91 struct winbind_cache {
92 TDB_CONTEXT *tdb;
95 struct cache_entry {
96 NTSTATUS status;
97 uint32 sequence_number;
98 uint8 *data;
99 uint32 len, ofs;
102 void (*smb_panic_fn)(const char *const why) = smb_panic;
104 #define WINBINDD_MAX_CACHE_SIZE (50*1024*1024)
106 static struct winbind_cache *wcache;
108 /* get the winbind_cache structure */
109 static struct winbind_cache *get_cache(struct winbindd_domain *domain)
111 struct winbind_cache *ret = wcache;
113 /* We have to know what type of domain we are dealing with first. */
115 if (domain->internal) {
116 domain->backend = &builtin_passdb_methods;
117 domain->initialized = True;
120 if (strequal(domain->name, get_global_sam_name()) &&
121 sid_equal(&domain->sid, get_global_sam_sid())) {
122 domain->backend = &sam_passdb_methods;
123 domain->initialized = True;
126 if ( !domain->initialized ) {
127 init_dc_connection( domain );
131 OK. listen up becasue I'm only going to say this once.
132 We have the following scenarios to consider
133 (a) trusted AD domains on a Samba DC,
134 (b) trusted AD domains and we are joined to a non-kerberos domain
135 (c) trusted AD domains and we are joined to a kerberos (AD) domain
137 For (a) we can always contact the trusted domain using krb5
138 since we have the domain trust account password
140 For (b) we can only use RPC since we have no way of
141 getting a krb5 ticket in our own domain
143 For (c) we can always use krb5 since we have a kerberos trust
145 --jerry
148 if (!domain->backend) {
149 #ifdef HAVE_ADS
150 struct winbindd_domain *our_domain = domain;
152 /* find our domain first so we can figure out if we
153 are joined to a kerberized domain */
155 if ( !domain->primary )
156 our_domain = find_our_domain();
158 if ((our_domain->active_directory || IS_DC)
159 && domain->active_directory
160 && !lp_winbind_rpc_only()) {
161 DEBUG(5,("get_cache: Setting ADS methods for domain %s\n", domain->name));
162 domain->backend = &ads_methods;
163 } else {
164 #endif /* HAVE_ADS */
165 DEBUG(5,("get_cache: Setting MS-RPC methods for domain %s\n", domain->name));
166 domain->backend = &reconnect_methods;
167 #ifdef HAVE_ADS
169 #endif /* HAVE_ADS */
172 if (ret)
173 return ret;
175 ret = SMB_XMALLOC_P(struct winbind_cache);
176 ZERO_STRUCTP(ret);
178 wcache = ret;
179 wcache_flush_cache();
181 return ret;
185 free a centry structure
187 static void centry_free(struct cache_entry *centry)
189 if (!centry)
190 return;
191 SAFE_FREE(centry->data);
192 free(centry);
195 static bool centry_check_bytes(struct cache_entry *centry, size_t nbytes)
197 if (centry->len - centry->ofs < nbytes) {
198 DEBUG(0,("centry corruption? needed %u bytes, have %d\n",
199 (unsigned int)nbytes,
200 centry->len - centry->ofs));
201 return false;
203 return true;
207 pull a uint32 from a cache entry
209 static uint32 centry_uint32(struct cache_entry *centry)
211 uint32 ret;
213 if (!centry_check_bytes(centry, 4)) {
214 smb_panic_fn("centry_uint32");
216 ret = IVAL(centry->data, centry->ofs);
217 centry->ofs += 4;
218 return ret;
222 pull a uint16 from a cache entry
224 static uint16 centry_uint16(struct cache_entry *centry)
226 uint16 ret;
227 if (!centry_check_bytes(centry, 2)) {
228 smb_panic_fn("centry_uint16");
230 ret = CVAL(centry->data, centry->ofs);
231 centry->ofs += 2;
232 return ret;
236 pull a uint8 from a cache entry
238 static uint8 centry_uint8(struct cache_entry *centry)
240 uint8 ret;
241 if (!centry_check_bytes(centry, 1)) {
242 smb_panic_fn("centry_uint8");
244 ret = CVAL(centry->data, centry->ofs);
245 centry->ofs += 1;
246 return ret;
250 pull a NTTIME from a cache entry
252 static NTTIME centry_nttime(struct cache_entry *centry)
254 NTTIME ret;
255 if (!centry_check_bytes(centry, 8)) {
256 smb_panic_fn("centry_nttime");
258 ret = IVAL(centry->data, centry->ofs);
259 centry->ofs += 4;
260 ret += (uint64_t)IVAL(centry->data, centry->ofs) << 32;
261 centry->ofs += 4;
262 return ret;
266 pull a time_t from a cache entry. time_t stored portably as a 64-bit time.
268 static time_t centry_time(struct cache_entry *centry)
270 return (time_t)centry_nttime(centry);
273 /* pull a string from a cache entry, using the supplied
274 talloc context
276 static char *centry_string(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
278 uint32 len;
279 char *ret;
281 len = centry_uint8(centry);
283 if (len == 0xFF) {
284 /* a deliberate NULL string */
285 return NULL;
288 if (!centry_check_bytes(centry, (size_t)len)) {
289 smb_panic_fn("centry_string");
292 ret = TALLOC_ARRAY(mem_ctx, char, len+1);
293 if (!ret) {
294 smb_panic_fn("centry_string out of memory\n");
296 memcpy(ret,centry->data + centry->ofs, len);
297 ret[len] = 0;
298 centry->ofs += len;
299 return ret;
302 /* pull a hash16 from a cache entry, using the supplied
303 talloc context
305 static char *centry_hash16(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
307 uint32 len;
308 char *ret;
310 len = centry_uint8(centry);
312 if (len != 16) {
313 DEBUG(0,("centry corruption? hash len (%u) != 16\n",
314 len ));
315 return NULL;
318 if (!centry_check_bytes(centry, 16)) {
319 return NULL;
322 ret = TALLOC_ARRAY(mem_ctx, char, 16);
323 if (!ret) {
324 smb_panic_fn("centry_hash out of memory\n");
326 memcpy(ret,centry->data + centry->ofs, 16);
327 centry->ofs += 16;
328 return ret;
331 /* pull a sid from a cache entry, using the supplied
332 talloc context
334 static bool centry_sid(struct cache_entry *centry, struct dom_sid *sid)
336 char *sid_string;
337 bool ret;
339 sid_string = centry_string(centry, talloc_tos());
340 if (sid_string == NULL) {
341 return false;
343 ret = string_to_sid(sid, sid_string);
344 TALLOC_FREE(sid_string);
345 return ret;
350 pull a NTSTATUS from a cache entry
352 static NTSTATUS centry_ntstatus(struct cache_entry *centry)
354 NTSTATUS status;
356 status = NT_STATUS(centry_uint32(centry));
357 return status;
361 /* the server is considered down if it can't give us a sequence number */
362 static bool wcache_server_down(struct winbindd_domain *domain)
364 bool ret;
366 if (!wcache->tdb)
367 return false;
369 ret = (domain->sequence_number == DOM_SEQUENCE_NONE);
371 if (ret)
372 DEBUG(10,("wcache_server_down: server for Domain %s down\n",
373 domain->name ));
374 return ret;
377 static bool wcache_fetch_seqnum(const char *domain_name, uint32_t *seqnum,
378 uint32_t *last_seq_check)
380 char *key;
381 TDB_DATA data;
383 if (wcache->tdb == NULL) {
384 DEBUG(10,("wcache_fetch_seqnum: tdb == NULL\n"));
385 return false;
388 key = talloc_asprintf(talloc_tos(), "SEQNUM/%s", domain_name);
389 if (key == NULL) {
390 DEBUG(10, ("talloc failed\n"));
391 return false;
394 data = tdb_fetch_bystring(wcache->tdb, key);
395 TALLOC_FREE(key);
397 if (data.dptr == NULL) {
398 DEBUG(10, ("wcache_fetch_seqnum: %s not found\n",
399 domain_name));
400 return false;
402 if (data.dsize != 8) {
403 DEBUG(10, ("wcache_fetch_seqnum: invalid data size %d\n",
404 (int)data.dsize));
405 SAFE_FREE(data.dptr);
406 return false;
409 *seqnum = IVAL(data.dptr, 0);
410 *last_seq_check = IVAL(data.dptr, 4);
411 SAFE_FREE(data.dptr);
413 return true;
416 static NTSTATUS fetch_cache_seqnum( struct winbindd_domain *domain, time_t now )
418 uint32 last_check, time_diff;
420 if (!wcache_fetch_seqnum(domain->name, &domain->sequence_number,
421 &last_check)) {
422 return NT_STATUS_UNSUCCESSFUL;
424 domain->last_seq_check = last_check;
426 /* have we expired? */
428 time_diff = now - domain->last_seq_check;
429 if ( time_diff > lp_winbind_cache_time() ) {
430 DEBUG(10,("fetch_cache_seqnum: timeout [%s][%u @ %u]\n",
431 domain->name, domain->sequence_number,
432 (uint32)domain->last_seq_check));
433 return NT_STATUS_UNSUCCESSFUL;
436 DEBUG(10,("fetch_cache_seqnum: success [%s][%u @ %u]\n",
437 domain->name, domain->sequence_number,
438 (uint32)domain->last_seq_check));
440 return NT_STATUS_OK;
443 bool wcache_store_seqnum(const char *domain_name, uint32_t seqnum,
444 time_t last_seq_check)
446 char *key_str;
447 uint8_t buf[8];
448 int ret;
450 if (wcache->tdb == NULL) {
451 DEBUG(10, ("wcache_store_seqnum: wcache->tdb == NULL\n"));
452 return false;
455 key_str = talloc_asprintf(talloc_tos(), "SEQNUM/%s", domain_name);
456 if (key_str == NULL) {
457 DEBUG(10, ("talloc_asprintf failed\n"));
458 return false;
461 SIVAL(buf, 0, seqnum);
462 SIVAL(buf, 4, last_seq_check);
464 ret = tdb_store_bystring(wcache->tdb, key_str,
465 make_tdb_data(buf, sizeof(buf)), TDB_REPLACE);
466 TALLOC_FREE(key_str);
467 if (ret == -1) {
468 DEBUG(10, ("tdb_store_bystring failed: %s\n",
469 tdb_errorstr(wcache->tdb)));
470 TALLOC_FREE(key_str);
471 return false;
474 DEBUG(10, ("wcache_store_seqnum: success [%s][%u @ %u]\n",
475 domain_name, seqnum, (unsigned)last_seq_check));
477 return true;
480 static bool store_cache_seqnum( struct winbindd_domain *domain )
482 return wcache_store_seqnum(domain->name, domain->sequence_number,
483 domain->last_seq_check);
487 refresh the domain sequence number. If force is true
488 then always refresh it, no matter how recently we fetched it
491 static void refresh_sequence_number(struct winbindd_domain *domain, bool force)
493 NTSTATUS status;
494 unsigned time_diff;
495 time_t t = time(NULL);
496 unsigned cache_time = lp_winbind_cache_time();
498 if (is_domain_offline(domain)) {
499 return;
502 get_cache( domain );
504 #if 0 /* JERRY -- disable as the default cache time is now 5 minutes */
505 /* trying to reconnect is expensive, don't do it too often */
506 if (domain->sequence_number == DOM_SEQUENCE_NONE) {
507 cache_time *= 8;
509 #endif
511 time_diff = t - domain->last_seq_check;
513 /* see if we have to refetch the domain sequence number */
514 if (!force && (time_diff < cache_time) &&
515 (domain->sequence_number != DOM_SEQUENCE_NONE) &&
516 NT_STATUS_IS_OK(domain->last_status)) {
517 DEBUG(10, ("refresh_sequence_number: %s time ok\n", domain->name));
518 goto done;
521 /* try to get the sequence number from the tdb cache first */
522 /* this will update the timestamp as well */
524 status = fetch_cache_seqnum( domain, t );
525 if (NT_STATUS_IS_OK(status) &&
526 (domain->sequence_number != DOM_SEQUENCE_NONE) &&
527 NT_STATUS_IS_OK(domain->last_status)) {
528 goto done;
531 /* important! make sure that we know if this is a native
532 mode domain or not. And that we can contact it. */
534 if ( winbindd_can_contact_domain( domain ) ) {
535 status = domain->backend->sequence_number(domain,
536 &domain->sequence_number);
537 } else {
538 /* just use the current time */
539 status = NT_STATUS_OK;
540 domain->sequence_number = time(NULL);
544 /* the above call could have set our domain->backend to NULL when
545 * coming from offline to online mode, make sure to reinitialize the
546 * backend - Guenther */
547 get_cache( domain );
549 if (!NT_STATUS_IS_OK(status)) {
550 DEBUG(10,("refresh_sequence_number: failed with %s\n", nt_errstr(status)));
551 domain->sequence_number = DOM_SEQUENCE_NONE;
554 domain->last_status = status;
555 domain->last_seq_check = time(NULL);
557 /* save the new sequence number in the cache */
558 store_cache_seqnum( domain );
560 done:
561 DEBUG(10, ("refresh_sequence_number: %s seq number is now %d\n",
562 domain->name, domain->sequence_number));
564 return;
568 decide if a cache entry has expired
570 static bool centry_expired(struct winbindd_domain *domain, const char *keystr, struct cache_entry *centry)
572 /* If we've been told to be offline - stay in that state... */
573 if (lp_winbind_offline_logon() && global_winbindd_offline_state) {
574 DEBUG(10,("centry_expired: Key %s for domain %s valid as winbindd is globally offline.\n",
575 keystr, domain->name ));
576 return false;
579 /* when the domain is offline return the cached entry.
580 * This deals with transient offline states... */
582 if (!domain->online) {
583 DEBUG(10,("centry_expired: Key %s for domain %s valid as domain is offline.\n",
584 keystr, domain->name ));
585 return false;
588 /* if the server is OK and our cache entry came from when it was down then
589 the entry is invalid */
590 if ((domain->sequence_number != DOM_SEQUENCE_NONE) &&
591 (centry->sequence_number == DOM_SEQUENCE_NONE)) {
592 DEBUG(10,("centry_expired: Key %s for domain %s invalid sequence.\n",
593 keystr, domain->name ));
594 return true;
597 /* if the server is down or the cache entry is not older than the
598 current sequence number then it is OK */
599 if (wcache_server_down(domain) ||
600 centry->sequence_number == domain->sequence_number) {
601 DEBUG(10,("centry_expired: Key %s for domain %s is good.\n",
602 keystr, domain->name ));
603 return false;
606 DEBUG(10,("centry_expired: Key %s for domain %s expired\n",
607 keystr, domain->name ));
609 /* it's expired */
610 return true;
613 static struct cache_entry *wcache_fetch_raw(char *kstr)
615 TDB_DATA data;
616 struct cache_entry *centry;
617 TDB_DATA key;
619 key = string_tdb_data(kstr);
620 data = tdb_fetch(wcache->tdb, key);
621 if (!data.dptr) {
622 /* a cache miss */
623 return NULL;
626 centry = SMB_XMALLOC_P(struct cache_entry);
627 centry->data = (unsigned char *)data.dptr;
628 centry->len = data.dsize;
629 centry->ofs = 0;
631 if (centry->len < 8) {
632 /* huh? corrupt cache? */
633 DEBUG(10,("wcache_fetch_raw: Corrupt cache for key %s (len < 8) ?\n", kstr));
634 centry_free(centry);
635 return NULL;
638 centry->status = centry_ntstatus(centry);
639 centry->sequence_number = centry_uint32(centry);
641 return centry;
644 static bool is_my_own_sam_domain(struct winbindd_domain *domain)
646 if (strequal(domain->name, get_global_sam_name()) &&
647 sid_equal(&domain->sid, get_global_sam_sid())) {
648 return true;
651 return false;
654 static bool is_builtin_domain(struct winbindd_domain *domain)
656 if (strequal(domain->name, "BUILTIN") &&
657 sid_equal(&domain->sid, &global_sid_Builtin)) {
658 return true;
661 return false;
665 fetch an entry from the cache, with a varargs key. auto-fetch the sequence
666 number and return status
668 static struct cache_entry *wcache_fetch(struct winbind_cache *cache,
669 struct winbindd_domain *domain,
670 const char *format, ...) PRINTF_ATTRIBUTE(3,4);
671 static struct cache_entry *wcache_fetch(struct winbind_cache *cache,
672 struct winbindd_domain *domain,
673 const char *format, ...)
675 va_list ap;
676 char *kstr;
677 struct cache_entry *centry;
679 if (!winbindd_use_cache() ||
680 is_my_own_sam_domain(domain) ||
681 is_builtin_domain(domain)) {
682 return NULL;
685 refresh_sequence_number(domain, false);
687 va_start(ap, format);
688 smb_xvasprintf(&kstr, format, ap);
689 va_end(ap);
691 centry = wcache_fetch_raw(kstr);
692 if (centry == NULL) {
693 free(kstr);
694 return NULL;
697 if (centry_expired(domain, kstr, centry)) {
699 DEBUG(10,("wcache_fetch: entry %s expired for domain %s\n",
700 kstr, domain->name ));
702 centry_free(centry);
703 free(kstr);
704 return NULL;
707 DEBUG(10,("wcache_fetch: returning entry %s for domain %s\n",
708 kstr, domain->name ));
710 free(kstr);
711 return centry;
714 static void wcache_delete(const char *format, ...) PRINTF_ATTRIBUTE(1,2);
715 static void wcache_delete(const char *format, ...)
717 va_list ap;
718 char *kstr;
719 TDB_DATA key;
721 va_start(ap, format);
722 smb_xvasprintf(&kstr, format, ap);
723 va_end(ap);
725 key = string_tdb_data(kstr);
727 tdb_delete(wcache->tdb, key);
728 free(kstr);
732 make sure we have at least len bytes available in a centry
734 static void centry_expand(struct cache_entry *centry, uint32 len)
736 if (centry->len - centry->ofs >= len)
737 return;
738 centry->len *= 2;
739 centry->data = SMB_REALLOC_ARRAY(centry->data, unsigned char,
740 centry->len);
741 if (!centry->data) {
742 DEBUG(0,("out of memory: needed %d bytes in centry_expand\n", centry->len));
743 smb_panic_fn("out of memory in centry_expand");
748 push a uint32 into a centry
750 static void centry_put_uint32(struct cache_entry *centry, uint32 v)
752 centry_expand(centry, 4);
753 SIVAL(centry->data, centry->ofs, v);
754 centry->ofs += 4;
758 push a uint16 into a centry
760 static void centry_put_uint16(struct cache_entry *centry, uint16 v)
762 centry_expand(centry, 2);
763 SIVAL(centry->data, centry->ofs, v);
764 centry->ofs += 2;
768 push a uint8 into a centry
770 static void centry_put_uint8(struct cache_entry *centry, uint8 v)
772 centry_expand(centry, 1);
773 SCVAL(centry->data, centry->ofs, v);
774 centry->ofs += 1;
778 push a string into a centry
780 static void centry_put_string(struct cache_entry *centry, const char *s)
782 int len;
784 if (!s) {
785 /* null strings are marked as len 0xFFFF */
786 centry_put_uint8(centry, 0xFF);
787 return;
790 len = strlen(s);
791 /* can't handle more than 254 char strings. Truncating is probably best */
792 if (len > 254) {
793 DEBUG(10,("centry_put_string: truncating len (%d) to: 254\n", len));
794 len = 254;
796 centry_put_uint8(centry, len);
797 centry_expand(centry, len);
798 memcpy(centry->data + centry->ofs, s, len);
799 centry->ofs += len;
803 push a 16 byte hash into a centry - treat as 16 byte string.
805 static void centry_put_hash16(struct cache_entry *centry, const uint8 val[16])
807 centry_put_uint8(centry, 16);
808 centry_expand(centry, 16);
809 memcpy(centry->data + centry->ofs, val, 16);
810 centry->ofs += 16;
813 static void centry_put_sid(struct cache_entry *centry, const struct dom_sid *sid)
815 fstring sid_string;
816 centry_put_string(centry, sid_to_fstring(sid_string, sid));
821 put NTSTATUS into a centry
823 static void centry_put_ntstatus(struct cache_entry *centry, NTSTATUS status)
825 uint32 status_value = NT_STATUS_V(status);
826 centry_put_uint32(centry, status_value);
831 push a NTTIME into a centry
833 static void centry_put_nttime(struct cache_entry *centry, NTTIME nt)
835 centry_expand(centry, 8);
836 SIVAL(centry->data, centry->ofs, nt & 0xFFFFFFFF);
837 centry->ofs += 4;
838 SIVAL(centry->data, centry->ofs, nt >> 32);
839 centry->ofs += 4;
843 push a time_t into a centry - use a 64 bit size.
844 NTTIME here is being used as a convenient 64-bit size.
846 static void centry_put_time(struct cache_entry *centry, time_t t)
848 NTTIME nt = (NTTIME)t;
849 centry_put_nttime(centry, nt);
853 start a centry for output. When finished, call centry_end()
855 struct cache_entry *centry_start(struct winbindd_domain *domain, NTSTATUS status)
857 struct cache_entry *centry;
859 if (!wcache->tdb)
860 return NULL;
862 centry = SMB_XMALLOC_P(struct cache_entry);
864 centry->len = 8192; /* reasonable default */
865 centry->data = SMB_XMALLOC_ARRAY(uint8, centry->len);
866 centry->ofs = 0;
867 centry->sequence_number = domain->sequence_number;
868 centry_put_ntstatus(centry, status);
869 centry_put_uint32(centry, centry->sequence_number);
870 return centry;
874 finish a centry and write it to the tdb
876 static void centry_end(struct cache_entry *centry, const char *format, ...) PRINTF_ATTRIBUTE(2,3);
877 static void centry_end(struct cache_entry *centry, const char *format, ...)
879 va_list ap;
880 char *kstr;
881 TDB_DATA key, data;
883 if (!winbindd_use_cache()) {
884 return;
887 va_start(ap, format);
888 smb_xvasprintf(&kstr, format, ap);
889 va_end(ap);
891 key = string_tdb_data(kstr);
892 data.dptr = centry->data;
893 data.dsize = centry->ofs;
895 tdb_store(wcache->tdb, key, data, TDB_REPLACE);
896 free(kstr);
899 static void wcache_save_name_to_sid(struct winbindd_domain *domain,
900 NTSTATUS status, const char *domain_name,
901 const char *name, const struct dom_sid *sid,
902 enum lsa_SidType type)
904 struct cache_entry *centry;
905 fstring uname;
907 centry = centry_start(domain, status);
908 if (!centry)
909 return;
910 centry_put_uint32(centry, type);
911 centry_put_sid(centry, sid);
912 fstrcpy(uname, name);
913 strupper_m(uname);
914 centry_end(centry, "NS/%s/%s", domain_name, uname);
915 DEBUG(10,("wcache_save_name_to_sid: %s\\%s -> %s (%s)\n", domain_name,
916 uname, sid_string_dbg(sid), nt_errstr(status)));
917 centry_free(centry);
920 static void wcache_save_sid_to_name(struct winbindd_domain *domain, NTSTATUS status,
921 const struct dom_sid *sid, const char *domain_name, const char *name, enum lsa_SidType type)
923 struct cache_entry *centry;
924 fstring sid_string;
926 centry = centry_start(domain, status);
927 if (!centry)
928 return;
930 if (NT_STATUS_IS_OK(status)) {
931 centry_put_uint32(centry, type);
932 centry_put_string(centry, domain_name);
933 centry_put_string(centry, name);
936 centry_end(centry, "SN/%s", sid_to_fstring(sid_string, sid));
937 DEBUG(10,("wcache_save_sid_to_name: %s -> %s (%s)\n", sid_string,
938 name, nt_errstr(status)));
939 centry_free(centry);
943 static void wcache_save_user(struct winbindd_domain *domain, NTSTATUS status,
944 struct wbint_userinfo *info)
946 struct cache_entry *centry;
947 fstring sid_string;
949 if (is_null_sid(&info->user_sid)) {
950 return;
953 centry = centry_start(domain, status);
954 if (!centry)
955 return;
956 centry_put_string(centry, info->acct_name);
957 centry_put_string(centry, info->full_name);
958 centry_put_string(centry, info->homedir);
959 centry_put_string(centry, info->shell);
960 centry_put_uint32(centry, info->primary_gid);
961 centry_put_sid(centry, &info->user_sid);
962 centry_put_sid(centry, &info->group_sid);
963 centry_end(centry, "U/%s", sid_to_fstring(sid_string,
964 &info->user_sid));
965 DEBUG(10,("wcache_save_user: %s (acct_name %s)\n", sid_string, info->acct_name));
966 centry_free(centry);
969 static void wcache_save_lockout_policy(struct winbindd_domain *domain,
970 NTSTATUS status,
971 struct samr_DomInfo12 *lockout_policy)
973 struct cache_entry *centry;
975 centry = centry_start(domain, status);
976 if (!centry)
977 return;
979 centry_put_nttime(centry, lockout_policy->lockout_duration);
980 centry_put_nttime(centry, lockout_policy->lockout_window);
981 centry_put_uint16(centry, lockout_policy->lockout_threshold);
983 centry_end(centry, "LOC_POL/%s", domain->name);
985 DEBUG(10,("wcache_save_lockout_policy: %s\n", domain->name));
987 centry_free(centry);
992 static void wcache_save_password_policy(struct winbindd_domain *domain,
993 NTSTATUS status,
994 struct samr_DomInfo1 *policy)
996 struct cache_entry *centry;
998 centry = centry_start(domain, status);
999 if (!centry)
1000 return;
1002 centry_put_uint16(centry, policy->min_password_length);
1003 centry_put_uint16(centry, policy->password_history_length);
1004 centry_put_uint32(centry, policy->password_properties);
1005 centry_put_nttime(centry, policy->max_password_age);
1006 centry_put_nttime(centry, policy->min_password_age);
1008 centry_end(centry, "PWD_POL/%s", domain->name);
1010 DEBUG(10,("wcache_save_password_policy: %s\n", domain->name));
1012 centry_free(centry);
1015 /***************************************************************************
1016 ***************************************************************************/
1018 static void wcache_save_username_alias(struct winbindd_domain *domain,
1019 NTSTATUS status,
1020 const char *name, const char *alias)
1022 struct cache_entry *centry;
1023 fstring uname;
1025 if ( (centry = centry_start(domain, status)) == NULL )
1026 return;
1028 centry_put_string( centry, alias );
1030 fstrcpy(uname, name);
1031 strupper_m(uname);
1032 centry_end(centry, "NSS/NA/%s", uname);
1034 DEBUG(10,("wcache_save_username_alias: %s -> %s\n", name, alias ));
1036 centry_free(centry);
1039 static void wcache_save_alias_username(struct winbindd_domain *domain,
1040 NTSTATUS status,
1041 const char *alias, const char *name)
1043 struct cache_entry *centry;
1044 fstring uname;
1046 if ( (centry = centry_start(domain, status)) == NULL )
1047 return;
1049 centry_put_string( centry, name );
1051 fstrcpy(uname, alias);
1052 strupper_m(uname);
1053 centry_end(centry, "NSS/AN/%s", uname);
1055 DEBUG(10,("wcache_save_alias_username: %s -> %s\n", alias, name ));
1057 centry_free(centry);
1060 /***************************************************************************
1061 ***************************************************************************/
1063 NTSTATUS resolve_username_to_alias( TALLOC_CTX *mem_ctx,
1064 struct winbindd_domain *domain,
1065 const char *name, char **alias )
1067 struct winbind_cache *cache = get_cache(domain);
1068 struct cache_entry *centry = NULL;
1069 NTSTATUS status;
1070 char *upper_name;
1072 if ( domain->internal )
1073 return NT_STATUS_NOT_SUPPORTED;
1075 if (!cache->tdb)
1076 goto do_query;
1078 if ( (upper_name = SMB_STRDUP(name)) == NULL )
1079 return NT_STATUS_NO_MEMORY;
1080 strupper_m(upper_name);
1082 centry = wcache_fetch(cache, domain, "NSS/NA/%s", upper_name);
1084 SAFE_FREE( upper_name );
1086 if (!centry)
1087 goto do_query;
1089 status = centry->status;
1091 if (!NT_STATUS_IS_OK(status)) {
1092 centry_free(centry);
1093 return status;
1096 *alias = centry_string( centry, mem_ctx );
1098 centry_free(centry);
1100 DEBUG(10,("resolve_username_to_alias: [Cached] - mapped %s to %s\n",
1101 name, *alias ? *alias : "(none)"));
1103 return (*alias) ? NT_STATUS_OK : NT_STATUS_OBJECT_NAME_NOT_FOUND;
1105 do_query:
1107 /* If its not in cache and we are offline, then fail */
1109 if ( get_global_winbindd_state_offline() || !domain->online ) {
1110 DEBUG(8,("resolve_username_to_alias: rejecting query "
1111 "in offline mode\n"));
1112 return NT_STATUS_NOT_FOUND;
1115 status = nss_map_to_alias( mem_ctx, domain->name, name, alias );
1117 if ( NT_STATUS_IS_OK( status ) ) {
1118 wcache_save_username_alias(domain, status, name, *alias);
1121 if ( NT_STATUS_EQUAL( status, NT_STATUS_NONE_MAPPED ) ) {
1122 wcache_save_username_alias(domain, status, name, "(NULL)");
1125 DEBUG(5,("resolve_username_to_alias: backend query returned %s\n",
1126 nt_errstr(status)));
1128 if ( NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ) {
1129 set_domain_offline( domain );
1132 return status;
1135 /***************************************************************************
1136 ***************************************************************************/
1138 NTSTATUS resolve_alias_to_username( TALLOC_CTX *mem_ctx,
1139 struct winbindd_domain *domain,
1140 const char *alias, char **name )
1142 struct winbind_cache *cache = get_cache(domain);
1143 struct cache_entry *centry = NULL;
1144 NTSTATUS status;
1145 char *upper_name;
1147 if ( domain->internal )
1148 return NT_STATUS_NOT_SUPPORTED;
1150 if (!cache->tdb)
1151 goto do_query;
1153 if ( (upper_name = SMB_STRDUP(alias)) == NULL )
1154 return NT_STATUS_NO_MEMORY;
1155 strupper_m(upper_name);
1157 centry = wcache_fetch(cache, domain, "NSS/AN/%s", upper_name);
1159 SAFE_FREE( upper_name );
1161 if (!centry)
1162 goto do_query;
1164 status = centry->status;
1166 if (!NT_STATUS_IS_OK(status)) {
1167 centry_free(centry);
1168 return status;
1171 *name = centry_string( centry, mem_ctx );
1173 centry_free(centry);
1175 DEBUG(10,("resolve_alias_to_username: [Cached] - mapped %s to %s\n",
1176 alias, *name ? *name : "(none)"));
1178 return (*name) ? NT_STATUS_OK : NT_STATUS_OBJECT_NAME_NOT_FOUND;
1180 do_query:
1182 /* If its not in cache and we are offline, then fail */
1184 if ( get_global_winbindd_state_offline() || !domain->online ) {
1185 DEBUG(8,("resolve_alias_to_username: rejecting query "
1186 "in offline mode\n"));
1187 return NT_STATUS_NOT_FOUND;
1190 /* an alias cannot contain a domain prefix or '@' */
1192 if (strchr(alias, '\\') || strchr(alias, '@')) {
1193 DEBUG(10,("resolve_alias_to_username: skipping fully "
1194 "qualified name %s\n", alias));
1195 return NT_STATUS_OBJECT_NAME_INVALID;
1198 status = nss_map_from_alias( mem_ctx, domain->name, alias, name );
1200 if ( NT_STATUS_IS_OK( status ) ) {
1201 wcache_save_alias_username( domain, status, alias, *name );
1204 if (NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) {
1205 wcache_save_alias_username(domain, status, alias, "(NULL)");
1208 DEBUG(5,("resolve_alias_to_username: backend query returned %s\n",
1209 nt_errstr(status)));
1211 if ( NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ) {
1212 set_domain_offline( domain );
1215 return status;
1218 NTSTATUS wcache_cached_creds_exist(struct winbindd_domain *domain, const struct dom_sid *sid)
1220 struct winbind_cache *cache = get_cache(domain);
1221 TDB_DATA data;
1222 fstring key_str, tmp;
1223 uint32 rid;
1225 if (!cache->tdb) {
1226 return NT_STATUS_INTERNAL_DB_ERROR;
1229 if (is_null_sid(sid)) {
1230 return NT_STATUS_INVALID_SID;
1233 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1234 return NT_STATUS_INVALID_SID;
1237 fstr_sprintf(key_str, "CRED/%s", sid_to_fstring(tmp, sid));
1239 data = tdb_fetch(cache->tdb, string_tdb_data(key_str));
1240 if (!data.dptr) {
1241 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1244 SAFE_FREE(data.dptr);
1245 return NT_STATUS_OK;
1248 /* Lookup creds for a SID - copes with old (unsalted) creds as well
1249 as new salted ones. */
1251 NTSTATUS wcache_get_creds(struct winbindd_domain *domain,
1252 TALLOC_CTX *mem_ctx,
1253 const struct dom_sid *sid,
1254 const uint8 **cached_nt_pass,
1255 const uint8 **cached_salt)
1257 struct winbind_cache *cache = get_cache(domain);
1258 struct cache_entry *centry = NULL;
1259 NTSTATUS status;
1260 time_t t;
1261 uint32 rid;
1262 fstring tmp;
1264 if (!cache->tdb) {
1265 return NT_STATUS_INTERNAL_DB_ERROR;
1268 if (is_null_sid(sid)) {
1269 return NT_STATUS_INVALID_SID;
1272 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1273 return NT_STATUS_INVALID_SID;
1276 /* Try and get a salted cred first. If we can't
1277 fall back to an unsalted cred. */
1279 centry = wcache_fetch(cache, domain, "CRED/%s",
1280 sid_to_fstring(tmp, sid));
1281 if (!centry) {
1282 DEBUG(10,("wcache_get_creds: entry for [CRED/%s] not found\n",
1283 sid_string_dbg(sid)));
1284 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1287 t = centry_time(centry);
1289 /* In the salted case this isn't actually the nt_hash itself,
1290 but the MD5 of the salt + nt_hash. Let the caller
1291 sort this out. It can tell as we only return the cached_salt
1292 if we are returning a salted cred. */
1294 *cached_nt_pass = (const uint8 *)centry_hash16(centry, mem_ctx);
1295 if (*cached_nt_pass == NULL) {
1296 fstring sidstr;
1298 sid_to_fstring(sidstr, sid);
1300 /* Bad (old) cred cache. Delete and pretend we
1301 don't have it. */
1302 DEBUG(0,("wcache_get_creds: bad entry for [CRED/%s] - deleting\n",
1303 sidstr));
1304 wcache_delete("CRED/%s", sidstr);
1305 centry_free(centry);
1306 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1309 /* We only have 17 bytes more data in the salted cred case. */
1310 if (centry->len - centry->ofs == 17) {
1311 *cached_salt = (const uint8 *)centry_hash16(centry, mem_ctx);
1312 } else {
1313 *cached_salt = NULL;
1316 dump_data_pw("cached_nt_pass", *cached_nt_pass, NT_HASH_LEN);
1317 if (*cached_salt) {
1318 dump_data_pw("cached_salt", *cached_salt, NT_HASH_LEN);
1321 status = centry->status;
1323 DEBUG(10,("wcache_get_creds: [Cached] - cached creds for user %s status: %s\n",
1324 sid_string_dbg(sid), nt_errstr(status) ));
1326 centry_free(centry);
1327 return status;
1330 /* Store creds for a SID - only writes out new salted ones. */
1332 NTSTATUS wcache_save_creds(struct winbindd_domain *domain,
1333 const struct dom_sid *sid,
1334 const uint8 nt_pass[NT_HASH_LEN])
1336 struct cache_entry *centry;
1337 fstring sid_string;
1338 uint32 rid;
1339 uint8 cred_salt[NT_HASH_LEN];
1340 uint8 salted_hash[NT_HASH_LEN];
1342 if (is_null_sid(sid)) {
1343 return NT_STATUS_INVALID_SID;
1346 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1347 return NT_STATUS_INVALID_SID;
1350 centry = centry_start(domain, NT_STATUS_OK);
1351 if (!centry) {
1352 return NT_STATUS_INTERNAL_DB_ERROR;
1355 dump_data_pw("nt_pass", nt_pass, NT_HASH_LEN);
1357 centry_put_time(centry, time(NULL));
1359 /* Create a salt and then salt the hash. */
1360 generate_random_buffer(cred_salt, NT_HASH_LEN);
1361 E_md5hash(cred_salt, nt_pass, salted_hash);
1363 centry_put_hash16(centry, salted_hash);
1364 centry_put_hash16(centry, cred_salt);
1365 centry_end(centry, "CRED/%s", sid_to_fstring(sid_string, sid));
1367 DEBUG(10,("wcache_save_creds: %s\n", sid_string));
1369 centry_free(centry);
1371 return NT_STATUS_OK;
1375 /* Query display info. This is the basic user list fn */
1376 static NTSTATUS query_user_list(struct winbindd_domain *domain,
1377 TALLOC_CTX *mem_ctx,
1378 uint32 *num_entries,
1379 struct wbint_userinfo **info)
1381 struct winbind_cache *cache = get_cache(domain);
1382 struct cache_entry *centry = NULL;
1383 NTSTATUS status;
1384 unsigned int i, retry;
1385 bool old_status = domain->online;
1387 if (!cache->tdb)
1388 goto do_query;
1390 centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
1391 if (!centry)
1392 goto do_query;
1394 do_fetch_cache:
1395 *num_entries = centry_uint32(centry);
1397 if (*num_entries == 0)
1398 goto do_cached;
1400 (*info) = TALLOC_ARRAY(mem_ctx, struct wbint_userinfo, *num_entries);
1401 if (! (*info)) {
1402 smb_panic_fn("query_user_list out of memory");
1404 for (i=0; i<(*num_entries); i++) {
1405 (*info)[i].acct_name = centry_string(centry, mem_ctx);
1406 (*info)[i].full_name = centry_string(centry, mem_ctx);
1407 (*info)[i].homedir = centry_string(centry, mem_ctx);
1408 (*info)[i].shell = centry_string(centry, mem_ctx);
1409 centry_sid(centry, &(*info)[i].user_sid);
1410 centry_sid(centry, &(*info)[i].group_sid);
1413 do_cached:
1414 status = centry->status;
1416 DEBUG(10,("query_user_list: [Cached] - cached list for domain %s status: %s\n",
1417 domain->name, nt_errstr(status) ));
1419 centry_free(centry);
1420 return status;
1422 do_query:
1423 *num_entries = 0;
1424 *info = NULL;
1426 /* Return status value returned by seq number check */
1428 if (!NT_STATUS_IS_OK(domain->last_status))
1429 return domain->last_status;
1431 /* Put the query_user_list() in a retry loop. There appears to be
1432 * some bug either with Windows 2000 or Samba's handling of large
1433 * rpc replies. This manifests itself as sudden disconnection
1434 * at a random point in the enumeration of a large (60k) user list.
1435 * The retry loop simply tries the operation again. )-: It's not
1436 * pretty but an acceptable workaround until we work out what the
1437 * real problem is. */
1439 retry = 0;
1440 do {
1442 DEBUG(10,("query_user_list: [Cached] - doing backend query for list for domain %s\n",
1443 domain->name ));
1445 status = domain->backend->query_user_list(domain, mem_ctx, num_entries, info);
1446 if (!NT_STATUS_IS_OK(status)) {
1447 DEBUG(3, ("query_user_list: returned 0x%08x, "
1448 "retrying\n", NT_STATUS_V(status)));
1450 if (NT_STATUS_EQUAL(status, NT_STATUS_UNSUCCESSFUL)) {
1451 DEBUG(3, ("query_user_list: flushing "
1452 "connection cache\n"));
1453 invalidate_cm_connection(&domain->conn);
1455 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1456 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1457 if (!domain->internal && old_status) {
1458 set_domain_offline(domain);
1460 /* store partial response. */
1461 if (*num_entries > 0) {
1463 * humm, what about the status used for cache?
1464 * Should it be NT_STATUS_OK?
1466 break;
1469 * domain is offline now, and there is no user entries,
1470 * try to fetch from cache again.
1472 if (cache->tdb && !domain->online && !domain->internal && old_status) {
1473 centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
1474 /* partial response... */
1475 if (!centry) {
1476 goto skip_save;
1477 } else {
1478 goto do_fetch_cache;
1480 } else {
1481 goto skip_save;
1485 } while (NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_UNSUCCESSFUL) &&
1486 (retry++ < 5));
1488 /* and save it */
1489 refresh_sequence_number(domain, false);
1490 if (!NT_STATUS_IS_OK(status)) {
1491 return status;
1493 centry = centry_start(domain, status);
1494 if (!centry)
1495 goto skip_save;
1496 centry_put_uint32(centry, *num_entries);
1497 for (i=0; i<(*num_entries); i++) {
1498 centry_put_string(centry, (*info)[i].acct_name);
1499 centry_put_string(centry, (*info)[i].full_name);
1500 centry_put_string(centry, (*info)[i].homedir);
1501 centry_put_string(centry, (*info)[i].shell);
1502 centry_put_sid(centry, &(*info)[i].user_sid);
1503 centry_put_sid(centry, &(*info)[i].group_sid);
1504 if (domain->backend && domain->backend->consistent) {
1505 /* when the backend is consistent we can pre-prime some mappings */
1506 wcache_save_name_to_sid(domain, NT_STATUS_OK,
1507 domain->name,
1508 (*info)[i].acct_name,
1509 &(*info)[i].user_sid,
1510 SID_NAME_USER);
1511 wcache_save_sid_to_name(domain, NT_STATUS_OK,
1512 &(*info)[i].user_sid,
1513 domain->name,
1514 (*info)[i].acct_name,
1515 SID_NAME_USER);
1516 wcache_save_user(domain, NT_STATUS_OK, &(*info)[i]);
1519 centry_end(centry, "UL/%s", domain->name);
1520 centry_free(centry);
1522 skip_save:
1523 return status;
1526 /* list all domain groups */
1527 static NTSTATUS enum_dom_groups(struct winbindd_domain *domain,
1528 TALLOC_CTX *mem_ctx,
1529 uint32 *num_entries,
1530 struct acct_info **info)
1532 struct winbind_cache *cache = get_cache(domain);
1533 struct cache_entry *centry = NULL;
1534 NTSTATUS status;
1535 unsigned int i;
1536 bool old_status;
1538 old_status = domain->online;
1539 if (!cache->tdb)
1540 goto do_query;
1542 centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
1543 if (!centry)
1544 goto do_query;
1546 do_fetch_cache:
1547 *num_entries = centry_uint32(centry);
1549 if (*num_entries == 0)
1550 goto do_cached;
1552 (*info) = TALLOC_ARRAY(mem_ctx, struct acct_info, *num_entries);
1553 if (! (*info)) {
1554 smb_panic_fn("enum_dom_groups out of memory");
1556 for (i=0; i<(*num_entries); i++) {
1557 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1558 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1559 (*info)[i].rid = centry_uint32(centry);
1562 do_cached:
1563 status = centry->status;
1565 DEBUG(10,("enum_dom_groups: [Cached] - cached list for domain %s status: %s\n",
1566 domain->name, nt_errstr(status) ));
1568 centry_free(centry);
1569 return status;
1571 do_query:
1572 *num_entries = 0;
1573 *info = NULL;
1575 /* Return status value returned by seq number check */
1577 if (!NT_STATUS_IS_OK(domain->last_status))
1578 return domain->last_status;
1580 DEBUG(10,("enum_dom_groups: [Cached] - doing backend query for list for domain %s\n",
1581 domain->name ));
1583 status = domain->backend->enum_dom_groups(domain, mem_ctx, num_entries, info);
1585 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1586 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1587 if (!domain->internal && old_status) {
1588 set_domain_offline(domain);
1590 if (cache->tdb &&
1591 !domain->online &&
1592 !domain->internal &&
1593 old_status) {
1594 centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
1595 if (centry) {
1596 goto do_fetch_cache;
1600 /* and save it */
1601 refresh_sequence_number(domain, false);
1602 if (!NT_STATUS_IS_OK(status)) {
1603 return status;
1605 centry = centry_start(domain, status);
1606 if (!centry)
1607 goto skip_save;
1608 centry_put_uint32(centry, *num_entries);
1609 for (i=0; i<(*num_entries); i++) {
1610 centry_put_string(centry, (*info)[i].acct_name);
1611 centry_put_string(centry, (*info)[i].acct_desc);
1612 centry_put_uint32(centry, (*info)[i].rid);
1614 centry_end(centry, "GL/%s/domain", domain->name);
1615 centry_free(centry);
1617 skip_save:
1618 return status;
1621 /* list all domain groups */
1622 static NTSTATUS enum_local_groups(struct winbindd_domain *domain,
1623 TALLOC_CTX *mem_ctx,
1624 uint32 *num_entries,
1625 struct acct_info **info)
1627 struct winbind_cache *cache = get_cache(domain);
1628 struct cache_entry *centry = NULL;
1629 NTSTATUS status;
1630 unsigned int i;
1631 bool old_status;
1633 old_status = domain->online;
1634 if (!cache->tdb)
1635 goto do_query;
1637 centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
1638 if (!centry)
1639 goto do_query;
1641 do_fetch_cache:
1642 *num_entries = centry_uint32(centry);
1644 if (*num_entries == 0)
1645 goto do_cached;
1647 (*info) = TALLOC_ARRAY(mem_ctx, struct acct_info, *num_entries);
1648 if (! (*info)) {
1649 smb_panic_fn("enum_dom_groups out of memory");
1651 for (i=0; i<(*num_entries); i++) {
1652 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1653 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1654 (*info)[i].rid = centry_uint32(centry);
1657 do_cached:
1659 /* If we are returning cached data and the domain controller
1660 is down then we don't know whether the data is up to date
1661 or not. Return NT_STATUS_MORE_PROCESSING_REQUIRED to
1662 indicate this. */
1664 if (wcache_server_down(domain)) {
1665 DEBUG(10, ("enum_local_groups: returning cached user list and server was down\n"));
1666 status = NT_STATUS_MORE_PROCESSING_REQUIRED;
1667 } else
1668 status = centry->status;
1670 DEBUG(10,("enum_local_groups: [Cached] - cached list for domain %s status: %s\n",
1671 domain->name, nt_errstr(status) ));
1673 centry_free(centry);
1674 return status;
1676 do_query:
1677 *num_entries = 0;
1678 *info = NULL;
1680 /* Return status value returned by seq number check */
1682 if (!NT_STATUS_IS_OK(domain->last_status))
1683 return domain->last_status;
1685 DEBUG(10,("enum_local_groups: [Cached] - doing backend query for list for domain %s\n",
1686 domain->name ));
1688 status = domain->backend->enum_local_groups(domain, mem_ctx, num_entries, info);
1690 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1691 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1692 if (!domain->internal && old_status) {
1693 set_domain_offline(domain);
1695 if (cache->tdb &&
1696 !domain->internal &&
1697 !domain->online &&
1698 old_status) {
1699 centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
1700 if (centry) {
1701 goto do_fetch_cache;
1705 /* and save it */
1706 refresh_sequence_number(domain, false);
1707 if (!NT_STATUS_IS_OK(status)) {
1708 return status;
1710 centry = centry_start(domain, status);
1711 if (!centry)
1712 goto skip_save;
1713 centry_put_uint32(centry, *num_entries);
1714 for (i=0; i<(*num_entries); i++) {
1715 centry_put_string(centry, (*info)[i].acct_name);
1716 centry_put_string(centry, (*info)[i].acct_desc);
1717 centry_put_uint32(centry, (*info)[i].rid);
1719 centry_end(centry, "GL/%s/local", domain->name);
1720 centry_free(centry);
1722 skip_save:
1723 return status;
1726 NTSTATUS wcache_name_to_sid(struct winbindd_domain *domain,
1727 const char *domain_name,
1728 const char *name,
1729 struct dom_sid *sid,
1730 enum lsa_SidType *type)
1732 struct winbind_cache *cache = get_cache(domain);
1733 struct cache_entry *centry;
1734 NTSTATUS status;
1735 char *uname;
1737 if (cache->tdb == NULL) {
1738 return NT_STATUS_NOT_FOUND;
1741 uname = talloc_strdup_upper(talloc_tos(), name);
1742 if (uname == NULL) {
1743 return NT_STATUS_NO_MEMORY;
1746 centry = wcache_fetch(cache, domain, "NS/%s/%s", domain_name, uname);
1747 TALLOC_FREE(uname);
1748 if (centry == NULL) {
1749 return NT_STATUS_NOT_FOUND;
1752 status = centry->status;
1753 if (NT_STATUS_IS_OK(status)) {
1754 *type = (enum lsa_SidType)centry_uint32(centry);
1755 centry_sid(centry, sid);
1758 DEBUG(10,("name_to_sid: [Cached] - cached name for domain %s status: "
1759 "%s\n", domain->name, nt_errstr(status) ));
1761 centry_free(centry);
1762 return status;
1765 /* convert a single name to a sid in a domain */
1766 static NTSTATUS name_to_sid(struct winbindd_domain *domain,
1767 TALLOC_CTX *mem_ctx,
1768 const char *domain_name,
1769 const char *name,
1770 uint32_t flags,
1771 struct dom_sid *sid,
1772 enum lsa_SidType *type)
1774 NTSTATUS status;
1775 bool old_status;
1777 old_status = domain->online;
1779 status = wcache_name_to_sid(domain, domain_name, name, sid, type);
1780 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
1781 return status;
1784 ZERO_STRUCTP(sid);
1786 /* If the seq number check indicated that there is a problem
1787 * with this DC, then return that status... except for
1788 * access_denied. This is special because the dc may be in
1789 * "restrict anonymous = 1" mode, in which case it will deny
1790 * most unauthenticated operations, but *will* allow the LSA
1791 * name-to-sid that we try as a fallback. */
1793 if (!(NT_STATUS_IS_OK(domain->last_status)
1794 || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1795 return domain->last_status;
1797 DEBUG(10,("name_to_sid: [Cached] - doing backend query for name for domain %s\n",
1798 domain->name ));
1800 status = domain->backend->name_to_sid(domain, mem_ctx, domain_name,
1801 name, flags, sid, type);
1803 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1804 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1805 if (!domain->internal && old_status) {
1806 set_domain_offline(domain);
1808 if (!domain->internal &&
1809 !domain->online &&
1810 old_status) {
1811 NTSTATUS cache_status;
1812 cache_status = wcache_name_to_sid(domain, domain_name, name, sid, type);
1813 return cache_status;
1816 /* and save it */
1817 refresh_sequence_number(domain, false);
1819 if (domain->online &&
1820 (NT_STATUS_IS_OK(status) || NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED))) {
1821 wcache_save_name_to_sid(domain, status, domain_name, name, sid, *type);
1823 /* Only save the reverse mapping if this was not a UPN */
1824 if (!strchr(name, '@')) {
1825 strupper_m(CONST_DISCARD(char *,domain_name));
1826 strlower_m(CONST_DISCARD(char *,name));
1827 wcache_save_sid_to_name(domain, status, sid, domain_name, name, *type);
1831 return status;
1834 NTSTATUS wcache_sid_to_name(struct winbindd_domain *domain,
1835 const struct dom_sid *sid,
1836 TALLOC_CTX *mem_ctx,
1837 char **domain_name,
1838 char **name,
1839 enum lsa_SidType *type)
1841 struct winbind_cache *cache = get_cache(domain);
1842 struct cache_entry *centry;
1843 char *sid_string;
1844 NTSTATUS status;
1846 if (cache->tdb == NULL) {
1847 return NT_STATUS_NOT_FOUND;
1850 sid_string = sid_string_tos(sid);
1851 if (sid_string == NULL) {
1852 return NT_STATUS_NO_MEMORY;
1855 centry = wcache_fetch(cache, domain, "SN/%s", sid_string);
1856 TALLOC_FREE(sid_string);
1857 if (centry == NULL) {
1858 return NT_STATUS_NOT_FOUND;
1861 if (NT_STATUS_IS_OK(centry->status)) {
1862 *type = (enum lsa_SidType)centry_uint32(centry);
1863 *domain_name = centry_string(centry, mem_ctx);
1864 *name = centry_string(centry, mem_ctx);
1867 status = centry->status;
1868 centry_free(centry);
1870 DEBUG(10,("sid_to_name: [Cached] - cached name for domain %s status: "
1871 "%s\n", domain->name, nt_errstr(status) ));
1873 return status;
1876 /* convert a sid to a user or group name. The sid is guaranteed to be in the domain
1877 given */
1878 static NTSTATUS sid_to_name(struct winbindd_domain *domain,
1879 TALLOC_CTX *mem_ctx,
1880 const struct dom_sid *sid,
1881 char **domain_name,
1882 char **name,
1883 enum lsa_SidType *type)
1885 NTSTATUS status;
1886 bool old_status;
1888 old_status = domain->online;
1889 status = wcache_sid_to_name(domain, sid, mem_ctx, domain_name, name,
1890 type);
1891 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
1892 return status;
1895 *name = NULL;
1896 *domain_name = NULL;
1898 /* If the seq number check indicated that there is a problem
1899 * with this DC, then return that status... except for
1900 * access_denied. This is special because the dc may be in
1901 * "restrict anonymous = 1" mode, in which case it will deny
1902 * most unauthenticated operations, but *will* allow the LSA
1903 * sid-to-name that we try as a fallback. */
1905 if (!(NT_STATUS_IS_OK(domain->last_status)
1906 || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1907 return domain->last_status;
1909 DEBUG(10,("sid_to_name: [Cached] - doing backend query for name for domain %s\n",
1910 domain->name ));
1912 status = domain->backend->sid_to_name(domain, mem_ctx, sid, domain_name, name, type);
1914 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1915 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1916 if (!domain->internal && old_status) {
1917 set_domain_offline(domain);
1919 if (!domain->internal &&
1920 !domain->online &&
1921 old_status) {
1922 NTSTATUS cache_status;
1923 cache_status = wcache_sid_to_name(domain, sid, mem_ctx,
1924 domain_name, name, type);
1925 return cache_status;
1928 /* and save it */
1929 refresh_sequence_number(domain, false);
1930 if (!NT_STATUS_IS_OK(status)) {
1931 return status;
1933 wcache_save_sid_to_name(domain, status, sid, *domain_name, *name, *type);
1935 /* We can't save the name to sid mapping here, as with sid history a
1936 * later name2sid would give the wrong sid. */
1938 return status;
1941 static NTSTATUS rids_to_names(struct winbindd_domain *domain,
1942 TALLOC_CTX *mem_ctx,
1943 const struct dom_sid *domain_sid,
1944 uint32 *rids,
1945 size_t num_rids,
1946 char **domain_name,
1947 char ***names,
1948 enum lsa_SidType **types)
1950 struct winbind_cache *cache = get_cache(domain);
1951 size_t i;
1952 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
1953 bool have_mapped;
1954 bool have_unmapped;
1955 bool old_status;
1957 old_status = domain->online;
1958 *domain_name = NULL;
1959 *names = NULL;
1960 *types = NULL;
1962 if (!cache->tdb) {
1963 goto do_query;
1966 if (num_rids == 0) {
1967 return NT_STATUS_OK;
1970 *names = TALLOC_ARRAY(mem_ctx, char *, num_rids);
1971 *types = TALLOC_ARRAY(mem_ctx, enum lsa_SidType, num_rids);
1973 if ((*names == NULL) || (*types == NULL)) {
1974 result = NT_STATUS_NO_MEMORY;
1975 goto error;
1978 have_mapped = have_unmapped = false;
1980 for (i=0; i<num_rids; i++) {
1981 struct dom_sid sid;
1982 struct cache_entry *centry;
1983 fstring tmp;
1985 if (!sid_compose(&sid, domain_sid, rids[i])) {
1986 result = NT_STATUS_INTERNAL_ERROR;
1987 goto error;
1990 centry = wcache_fetch(cache, domain, "SN/%s",
1991 sid_to_fstring(tmp, &sid));
1992 if (!centry) {
1993 goto do_query;
1996 (*types)[i] = SID_NAME_UNKNOWN;
1997 (*names)[i] = talloc_strdup(*names, "");
1999 if (NT_STATUS_IS_OK(centry->status)) {
2000 char *dom;
2001 have_mapped = true;
2002 (*types)[i] = (enum lsa_SidType)centry_uint32(centry);
2004 dom = centry_string(centry, mem_ctx);
2005 if (*domain_name == NULL) {
2006 *domain_name = dom;
2007 } else {
2008 talloc_free(dom);
2011 (*names)[i] = centry_string(centry, *names);
2013 } else if (NT_STATUS_EQUAL(centry->status, NT_STATUS_NONE_MAPPED)) {
2014 have_unmapped = true;
2016 } else {
2017 /* something's definitely wrong */
2018 result = centry->status;
2019 goto error;
2022 centry_free(centry);
2025 if (!have_mapped) {
2026 return NT_STATUS_NONE_MAPPED;
2028 if (!have_unmapped) {
2029 return NT_STATUS_OK;
2031 return STATUS_SOME_UNMAPPED;
2033 do_query:
2035 TALLOC_FREE(*names);
2036 TALLOC_FREE(*types);
2038 result = domain->backend->rids_to_names(domain, mem_ctx, domain_sid,
2039 rids, num_rids, domain_name,
2040 names, types);
2042 if (NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
2043 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2044 if (!domain->internal && old_status) {
2045 set_domain_offline(domain);
2047 if (cache->tdb &&
2048 !domain->internal &&
2049 !domain->online &&
2050 old_status) {
2051 have_mapped = have_unmapped = false;
2053 for (i=0; i<num_rids; i++) {
2054 struct dom_sid sid;
2055 struct cache_entry *centry;
2056 fstring tmp;
2058 if (!sid_compose(&sid, domain_sid, rids[i])) {
2059 result = NT_STATUS_INTERNAL_ERROR;
2060 goto error;
2063 centry = wcache_fetch(cache, domain, "SN/%s",
2064 sid_to_fstring(tmp, &sid));
2065 if (!centry) {
2066 (*types)[i] = SID_NAME_UNKNOWN;
2067 (*names)[i] = talloc_strdup(*names, "");
2068 continue;
2071 (*types)[i] = SID_NAME_UNKNOWN;
2072 (*names)[i] = talloc_strdup(*names, "");
2074 if (NT_STATUS_IS_OK(centry->status)) {
2075 char *dom;
2076 have_mapped = true;
2077 (*types)[i] = (enum lsa_SidType)centry_uint32(centry);
2079 dom = centry_string(centry, mem_ctx);
2080 if (*domain_name == NULL) {
2081 *domain_name = dom;
2082 } else {
2083 talloc_free(dom);
2086 (*names)[i] = centry_string(centry, *names);
2088 } else if (NT_STATUS_EQUAL(centry->status, NT_STATUS_NONE_MAPPED)) {
2089 have_unmapped = true;
2091 } else {
2092 /* something's definitely wrong */
2093 result = centry->status;
2094 goto error;
2097 centry_free(centry);
2100 if (!have_mapped) {
2101 return NT_STATUS_NONE_MAPPED;
2103 if (!have_unmapped) {
2104 return NT_STATUS_OK;
2106 return STATUS_SOME_UNMAPPED;
2110 None of the queried rids has been found so save all negative entries
2112 if (NT_STATUS_EQUAL(result, NT_STATUS_NONE_MAPPED)) {
2113 for (i = 0; i < num_rids; i++) {
2114 struct dom_sid sid;
2115 const char *name = "";
2116 const enum lsa_SidType type = SID_NAME_UNKNOWN;
2117 NTSTATUS status = NT_STATUS_NONE_MAPPED;
2119 if (!sid_compose(&sid, domain_sid, rids[i])) {
2120 return NT_STATUS_INTERNAL_ERROR;
2123 wcache_save_sid_to_name(domain, status, &sid, *domain_name,
2124 name, type);
2127 return result;
2131 Some or all of the queried rids have been found.
2133 if (!NT_STATUS_IS_OK(result) &&
2134 !NT_STATUS_EQUAL(result, STATUS_SOME_UNMAPPED)) {
2135 return result;
2138 refresh_sequence_number(domain, false);
2140 for (i=0; i<num_rids; i++) {
2141 struct dom_sid sid;
2142 NTSTATUS status;
2144 if (!sid_compose(&sid, domain_sid, rids[i])) {
2145 result = NT_STATUS_INTERNAL_ERROR;
2146 goto error;
2149 status = (*types)[i] == SID_NAME_UNKNOWN ?
2150 NT_STATUS_NONE_MAPPED : NT_STATUS_OK;
2152 wcache_save_sid_to_name(domain, status, &sid, *domain_name,
2153 (*names)[i], (*types)[i]);
2156 return result;
2158 error:
2159 TALLOC_FREE(*names);
2160 TALLOC_FREE(*types);
2161 return result;
2164 NTSTATUS wcache_query_user(struct winbindd_domain *domain,
2165 TALLOC_CTX *mem_ctx,
2166 const struct dom_sid *user_sid,
2167 struct wbint_userinfo *info)
2169 struct winbind_cache *cache = get_cache(domain);
2170 struct cache_entry *centry = NULL;
2171 NTSTATUS status;
2172 char *sid_string;
2174 if (cache->tdb == NULL) {
2175 return NT_STATUS_NOT_FOUND;
2178 sid_string = sid_string_tos(user_sid);
2179 if (sid_string == NULL) {
2180 return NT_STATUS_NO_MEMORY;
2183 centry = wcache_fetch(cache, domain, "U/%s", sid_string);
2184 TALLOC_FREE(sid_string);
2185 if (centry == NULL) {
2186 return NT_STATUS_NOT_FOUND;
2190 * If we have an access denied cache entry and a cached info3
2191 * in the samlogon cache then do a query. This will force the
2192 * rpc back end to return the info3 data.
2195 if (NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED) &&
2196 netsamlogon_cache_have(user_sid)) {
2197 DEBUG(10, ("query_user: cached access denied and have cached "
2198 "info3\n"));
2199 domain->last_status = NT_STATUS_OK;
2200 centry_free(centry);
2201 return NT_STATUS_NOT_FOUND;
2204 /* if status is not ok then this is a negative hit
2205 and the rest of the data doesn't matter */
2206 status = centry->status;
2207 if (NT_STATUS_IS_OK(status)) {
2208 info->acct_name = centry_string(centry, mem_ctx);
2209 info->full_name = centry_string(centry, mem_ctx);
2210 info->homedir = centry_string(centry, mem_ctx);
2211 info->shell = centry_string(centry, mem_ctx);
2212 info->primary_gid = centry_uint32(centry);
2213 centry_sid(centry, &info->user_sid);
2214 centry_sid(centry, &info->group_sid);
2217 DEBUG(10,("query_user: [Cached] - cached info for domain %s status: "
2218 "%s\n", domain->name, nt_errstr(status) ));
2220 centry_free(centry);
2221 return status;
2224 /* Lookup user information from a rid */
2225 static NTSTATUS query_user(struct winbindd_domain *domain,
2226 TALLOC_CTX *mem_ctx,
2227 const struct dom_sid *user_sid,
2228 struct wbint_userinfo *info)
2230 NTSTATUS status;
2231 bool old_status;
2233 old_status = domain->online;
2234 status = wcache_query_user(domain, mem_ctx, user_sid, info);
2235 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2236 return status;
2239 ZERO_STRUCTP(info);
2241 /* Return status value returned by seq number check */
2243 if (!NT_STATUS_IS_OK(domain->last_status))
2244 return domain->last_status;
2246 DEBUG(10,("query_user: [Cached] - doing backend query for info for domain %s\n",
2247 domain->name ));
2249 status = domain->backend->query_user(domain, mem_ctx, user_sid, info);
2251 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2252 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2253 if (!domain->internal && old_status) {
2254 set_domain_offline(domain);
2256 if (!domain->internal &&
2257 !domain->online &&
2258 old_status) {
2259 NTSTATUS cache_status;
2260 cache_status = wcache_query_user(domain, mem_ctx, user_sid, info);
2261 return cache_status;
2264 /* and save it */
2265 refresh_sequence_number(domain, false);
2266 if (!NT_STATUS_IS_OK(status)) {
2267 return status;
2269 wcache_save_user(domain, status, info);
2271 return status;
2274 NTSTATUS wcache_lookup_usergroups(struct winbindd_domain *domain,
2275 TALLOC_CTX *mem_ctx,
2276 const struct dom_sid *user_sid,
2277 uint32_t *pnum_sids,
2278 struct dom_sid **psids)
2280 struct winbind_cache *cache = get_cache(domain);
2281 struct cache_entry *centry = NULL;
2282 NTSTATUS status;
2283 uint32_t i, num_sids;
2284 struct dom_sid *sids;
2285 fstring sid_string;
2287 if (cache->tdb == NULL) {
2288 return NT_STATUS_NOT_FOUND;
2291 centry = wcache_fetch(cache, domain, "UG/%s",
2292 sid_to_fstring(sid_string, user_sid));
2293 if (centry == NULL) {
2294 return NT_STATUS_NOT_FOUND;
2297 /* If we have an access denied cache entry and a cached info3 in the
2298 samlogon cache then do a query. This will force the rpc back end
2299 to return the info3 data. */
2301 if (NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)
2302 && netsamlogon_cache_have(user_sid)) {
2303 DEBUG(10, ("lookup_usergroups: cached access denied and have "
2304 "cached info3\n"));
2305 domain->last_status = NT_STATUS_OK;
2306 centry_free(centry);
2307 return NT_STATUS_NOT_FOUND;
2310 num_sids = centry_uint32(centry);
2311 sids = talloc_array(mem_ctx, struct dom_sid, num_sids);
2312 if (sids == NULL) {
2313 centry_free(centry);
2314 return NT_STATUS_NO_MEMORY;
2317 for (i=0; i<num_sids; i++) {
2318 centry_sid(centry, &sids[i]);
2321 status = centry->status;
2323 DEBUG(10,("lookup_usergroups: [Cached] - cached info for domain %s "
2324 "status: %s\n", domain->name, nt_errstr(status)));
2326 centry_free(centry);
2328 *pnum_sids = num_sids;
2329 *psids = sids;
2330 return status;
2333 /* Lookup groups a user is a member of. */
2334 static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
2335 TALLOC_CTX *mem_ctx,
2336 const struct dom_sid *user_sid,
2337 uint32 *num_groups, struct dom_sid **user_gids)
2339 struct cache_entry *centry = NULL;
2340 NTSTATUS status;
2341 unsigned int i;
2342 fstring sid_string;
2343 bool old_status;
2345 old_status = domain->online;
2346 status = wcache_lookup_usergroups(domain, mem_ctx, user_sid,
2347 num_groups, user_gids);
2348 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2349 return status;
2352 (*num_groups) = 0;
2353 (*user_gids) = NULL;
2355 /* Return status value returned by seq number check */
2357 if (!NT_STATUS_IS_OK(domain->last_status))
2358 return domain->last_status;
2360 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info for domain %s\n",
2361 domain->name ));
2363 status = domain->backend->lookup_usergroups(domain, mem_ctx, user_sid, num_groups, user_gids);
2365 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2366 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2367 if (!domain->internal && old_status) {
2368 set_domain_offline(domain);
2370 if (!domain->internal &&
2371 !domain->online &&
2372 old_status) {
2373 NTSTATUS cache_status;
2374 cache_status = wcache_lookup_usergroups(domain, mem_ctx, user_sid,
2375 num_groups, user_gids);
2376 return cache_status;
2379 if ( NT_STATUS_EQUAL(status, NT_STATUS_SYNCHRONIZATION_REQUIRED) )
2380 goto skip_save;
2382 /* and save it */
2383 refresh_sequence_number(domain, false);
2384 if (!NT_STATUS_IS_OK(status)) {
2385 return status;
2387 centry = centry_start(domain, status);
2388 if (!centry)
2389 goto skip_save;
2391 centry_put_uint32(centry, *num_groups);
2392 for (i=0; i<(*num_groups); i++) {
2393 centry_put_sid(centry, &(*user_gids)[i]);
2396 centry_end(centry, "UG/%s", sid_to_fstring(sid_string, user_sid));
2397 centry_free(centry);
2399 skip_save:
2400 return status;
2403 static char *wcache_make_sidlist(TALLOC_CTX *mem_ctx, uint32_t num_sids,
2404 const struct dom_sid *sids)
2406 uint32_t i;
2407 char *sidlist;
2409 sidlist = talloc_strdup(mem_ctx, "");
2410 if (sidlist == NULL) {
2411 return NULL;
2413 for (i=0; i<num_sids; i++) {
2414 fstring tmp;
2415 sidlist = talloc_asprintf_append_buffer(
2416 sidlist, "/%s", sid_to_fstring(tmp, &sids[i]));
2417 if (sidlist == NULL) {
2418 return NULL;
2421 return sidlist;
2424 NTSTATUS wcache_lookup_useraliases(struct winbindd_domain *domain,
2425 TALLOC_CTX *mem_ctx, uint32_t num_sids,
2426 const struct dom_sid *sids,
2427 uint32_t *pnum_aliases, uint32_t **paliases)
2429 struct winbind_cache *cache = get_cache(domain);
2430 struct cache_entry *centry = NULL;
2431 uint32_t num_aliases;
2432 uint32_t *aliases;
2433 NTSTATUS status;
2434 char *sidlist;
2435 int i;
2437 if (cache->tdb == NULL) {
2438 return NT_STATUS_NOT_FOUND;
2441 if (num_sids == 0) {
2442 *pnum_aliases = 0;
2443 *paliases = NULL;
2444 return NT_STATUS_OK;
2447 /* We need to cache indexed by the whole list of SIDs, the aliases
2448 * resulting might come from any of the SIDs. */
2450 sidlist = wcache_make_sidlist(talloc_tos(), num_sids, sids);
2451 if (sidlist == NULL) {
2452 return NT_STATUS_NO_MEMORY;
2455 centry = wcache_fetch(cache, domain, "UA%s", sidlist);
2456 TALLOC_FREE(sidlist);
2457 if (centry == NULL) {
2458 return NT_STATUS_NOT_FOUND;
2461 num_aliases = centry_uint32(centry);
2462 aliases = talloc_array(mem_ctx, uint32_t, num_aliases);
2463 if (aliases == NULL) {
2464 centry_free(centry);
2465 return NT_STATUS_NO_MEMORY;
2468 for (i=0; i<num_aliases; i++) {
2469 aliases[i] = centry_uint32(centry);
2472 status = centry->status;
2474 DEBUG(10,("lookup_useraliases: [Cached] - cached info for domain: %s "
2475 "status %s\n", domain->name, nt_errstr(status)));
2477 centry_free(centry);
2479 *pnum_aliases = num_aliases;
2480 *paliases = aliases;
2482 return status;
2485 static NTSTATUS lookup_useraliases(struct winbindd_domain *domain,
2486 TALLOC_CTX *mem_ctx,
2487 uint32 num_sids, const struct dom_sid *sids,
2488 uint32 *num_aliases, uint32 **alias_rids)
2490 struct cache_entry *centry = NULL;
2491 NTSTATUS status;
2492 char *sidlist;
2493 int i;
2494 bool old_status;
2496 old_status = domain->online;
2497 status = wcache_lookup_useraliases(domain, mem_ctx, num_sids, sids,
2498 num_aliases, alias_rids);
2499 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2500 return status;
2503 (*num_aliases) = 0;
2504 (*alias_rids) = NULL;
2506 if (!NT_STATUS_IS_OK(domain->last_status))
2507 return domain->last_status;
2509 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info "
2510 "for domain %s\n", domain->name ));
2512 sidlist = wcache_make_sidlist(talloc_tos(), num_sids, sids);
2513 if (sidlist == NULL) {
2514 return NT_STATUS_NO_MEMORY;
2517 status = domain->backend->lookup_useraliases(domain, mem_ctx,
2518 num_sids, sids,
2519 num_aliases, alias_rids);
2521 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2522 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2523 if (!domain->internal && old_status) {
2524 set_domain_offline(domain);
2526 if (!domain->internal &&
2527 !domain->online &&
2528 old_status) {
2529 NTSTATUS cache_status;
2530 cache_status = wcache_lookup_useraliases(domain, mem_ctx, num_sids,
2531 sids, num_aliases, alias_rids);
2532 return cache_status;
2535 /* and save it */
2536 refresh_sequence_number(domain, false);
2537 if (!NT_STATUS_IS_OK(status)) {
2538 return status;
2540 centry = centry_start(domain, status);
2541 if (!centry)
2542 goto skip_save;
2543 centry_put_uint32(centry, *num_aliases);
2544 for (i=0; i<(*num_aliases); i++)
2545 centry_put_uint32(centry, (*alias_rids)[i]);
2546 centry_end(centry, "UA%s", sidlist);
2547 centry_free(centry);
2549 skip_save:
2550 return status;
2553 NTSTATUS wcache_lookup_groupmem(struct winbindd_domain *domain,
2554 TALLOC_CTX *mem_ctx,
2555 const struct dom_sid *group_sid,
2556 uint32_t *num_names,
2557 struct dom_sid **sid_mem, char ***names,
2558 uint32_t **name_types)
2560 struct winbind_cache *cache = get_cache(domain);
2561 struct cache_entry *centry = NULL;
2562 NTSTATUS status;
2563 unsigned int i;
2564 char *sid_string;
2566 if (cache->tdb == NULL) {
2567 return NT_STATUS_NOT_FOUND;
2570 sid_string = sid_string_tos(group_sid);
2571 if (sid_string == NULL) {
2572 return NT_STATUS_NO_MEMORY;
2575 centry = wcache_fetch(cache, domain, "GM/%s", sid_string);
2576 TALLOC_FREE(sid_string);
2577 if (centry == NULL) {
2578 return NT_STATUS_NOT_FOUND;
2581 *sid_mem = NULL;
2582 *names = NULL;
2583 *name_types = NULL;
2585 *num_names = centry_uint32(centry);
2586 if (*num_names == 0) {
2587 centry_free(centry);
2588 return NT_STATUS_OK;
2591 *sid_mem = talloc_array(mem_ctx, struct dom_sid, *num_names);
2592 *names = talloc_array(mem_ctx, char *, *num_names);
2593 *name_types = talloc_array(mem_ctx, uint32, *num_names);
2595 if ((*sid_mem == NULL) || (*names == NULL) || (*name_types == NULL)) {
2596 TALLOC_FREE(*sid_mem);
2597 TALLOC_FREE(*names);
2598 TALLOC_FREE(*name_types);
2599 centry_free(centry);
2600 return NT_STATUS_NO_MEMORY;
2603 for (i=0; i<(*num_names); i++) {
2604 centry_sid(centry, &(*sid_mem)[i]);
2605 (*names)[i] = centry_string(centry, mem_ctx);
2606 (*name_types)[i] = centry_uint32(centry);
2609 status = centry->status;
2611 DEBUG(10,("lookup_groupmem: [Cached] - cached info for domain %s "
2612 "status: %s\n", domain->name, nt_errstr(status)));
2614 centry_free(centry);
2615 return status;
2618 static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
2619 TALLOC_CTX *mem_ctx,
2620 const struct dom_sid *group_sid,
2621 enum lsa_SidType type,
2622 uint32 *num_names,
2623 struct dom_sid **sid_mem, char ***names,
2624 uint32 **name_types)
2626 struct cache_entry *centry = NULL;
2627 NTSTATUS status;
2628 unsigned int i;
2629 fstring sid_string;
2630 bool old_status;
2632 old_status = domain->online;
2633 status = wcache_lookup_groupmem(domain, mem_ctx, group_sid, num_names,
2634 sid_mem, names, name_types);
2635 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2636 return status;
2639 (*num_names) = 0;
2640 (*sid_mem) = NULL;
2641 (*names) = NULL;
2642 (*name_types) = NULL;
2644 /* Return status value returned by seq number check */
2646 if (!NT_STATUS_IS_OK(domain->last_status))
2647 return domain->last_status;
2649 DEBUG(10,("lookup_groupmem: [Cached] - doing backend query for info for domain %s\n",
2650 domain->name ));
2652 status = domain->backend->lookup_groupmem(domain, mem_ctx, group_sid,
2653 type, num_names,
2654 sid_mem, names, name_types);
2656 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2657 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2658 if (!domain->internal && old_status) {
2659 set_domain_offline(domain);
2661 if (!domain->internal &&
2662 !domain->online &&
2663 old_status) {
2664 NTSTATUS cache_status;
2665 cache_status = wcache_lookup_groupmem(domain, mem_ctx, group_sid,
2666 num_names, sid_mem, names,
2667 name_types);
2668 return cache_status;
2671 /* and save it */
2672 refresh_sequence_number(domain, false);
2673 if (!NT_STATUS_IS_OK(status)) {
2674 return status;
2676 centry = centry_start(domain, status);
2677 if (!centry)
2678 goto skip_save;
2679 centry_put_uint32(centry, *num_names);
2680 for (i=0; i<(*num_names); i++) {
2681 centry_put_sid(centry, &(*sid_mem)[i]);
2682 centry_put_string(centry, (*names)[i]);
2683 centry_put_uint32(centry, (*name_types)[i]);
2685 centry_end(centry, "GM/%s", sid_to_fstring(sid_string, group_sid));
2686 centry_free(centry);
2688 skip_save:
2689 return status;
2692 /* find the sequence number for a domain */
2693 static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
2695 refresh_sequence_number(domain, false);
2697 *seq = domain->sequence_number;
2699 return NT_STATUS_OK;
2702 /* enumerate trusted domains
2703 * (we need to have the list of trustdoms in the cache when we go offline) -
2704 * Guenther */
2705 static NTSTATUS trusted_domains(struct winbindd_domain *domain,
2706 TALLOC_CTX *mem_ctx,
2707 struct netr_DomainTrustList *trusts)
2709 NTSTATUS status;
2710 struct winbind_cache *cache;
2711 struct winbindd_tdc_domain *dom_list = NULL;
2712 size_t num_domains = 0;
2713 bool retval = false;
2714 int i;
2715 bool old_status;
2717 old_status = domain->online;
2718 trusts->count = 0;
2719 trusts->array = NULL;
2721 cache = get_cache(domain);
2722 if (!cache || !cache->tdb) {
2723 goto do_query;
2726 if (domain->online) {
2727 goto do_query;
2730 retval = wcache_tdc_fetch_list(&dom_list, &num_domains);
2731 if (!retval || !num_domains || !dom_list) {
2732 TALLOC_FREE(dom_list);
2733 goto do_query;
2736 do_fetch_cache:
2737 trusts->array = TALLOC_ZERO_ARRAY(mem_ctx, struct netr_DomainTrust, num_domains);
2738 if (!trusts->array) {
2739 TALLOC_FREE(dom_list);
2740 return NT_STATUS_NO_MEMORY;
2743 for (i = 0; i < num_domains; i++) {
2744 struct netr_DomainTrust *trust;
2745 struct dom_sid *sid;
2746 struct winbindd_domain *dom;
2748 dom = find_domain_from_name_noinit(dom_list[i].domain_name);
2749 if (dom && dom->internal) {
2750 continue;
2753 trust = &trusts->array[trusts->count];
2754 trust->netbios_name = talloc_strdup(trusts->array, dom_list[i].domain_name);
2755 trust->dns_name = talloc_strdup(trusts->array, dom_list[i].dns_name);
2756 sid = talloc(trusts->array, struct dom_sid);
2757 if (!trust->netbios_name || !trust->dns_name ||
2758 !sid) {
2759 TALLOC_FREE(dom_list);
2760 TALLOC_FREE(trusts->array);
2761 return NT_STATUS_NO_MEMORY;
2764 trust->trust_flags = dom_list[i].trust_flags;
2765 trust->trust_attributes = dom_list[i].trust_attribs;
2766 trust->trust_type = dom_list[i].trust_type;
2767 sid_copy(sid, &dom_list[i].sid);
2768 trust->sid = sid;
2769 trusts->count++;
2772 TALLOC_FREE(dom_list);
2773 return NT_STATUS_OK;
2775 do_query:
2776 /* Return status value returned by seq number check */
2778 if (!NT_STATUS_IS_OK(domain->last_status))
2779 return domain->last_status;
2781 DEBUG(10,("trusted_domains: [Cached] - doing backend query for info for domain %s\n",
2782 domain->name ));
2784 status = domain->backend->trusted_domains(domain, mem_ctx, trusts);
2786 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2787 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2788 if (!domain->internal && old_status) {
2789 set_domain_offline(domain);
2791 if (!domain->internal &&
2792 !domain->online &&
2793 old_status) {
2794 retval = wcache_tdc_fetch_list(&dom_list, &num_domains);
2795 if (retval && num_domains && dom_list) {
2796 TALLOC_FREE(trusts->array);
2797 trusts->count = 0;
2798 goto do_fetch_cache;
2802 /* no trusts gives NT_STATUS_NO_MORE_ENTRIES resetting to NT_STATUS_OK
2803 * so that the generic centry handling still applies correctly -
2804 * Guenther*/
2806 if (!NT_STATUS_IS_ERR(status)) {
2807 status = NT_STATUS_OK;
2809 return status;
2812 /* get lockout policy */
2813 static NTSTATUS lockout_policy(struct winbindd_domain *domain,
2814 TALLOC_CTX *mem_ctx,
2815 struct samr_DomInfo12 *policy)
2817 struct winbind_cache *cache = get_cache(domain);
2818 struct cache_entry *centry = NULL;
2819 NTSTATUS status;
2820 bool old_status;
2822 old_status = domain->online;
2823 if (!cache->tdb)
2824 goto do_query;
2826 centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
2828 if (!centry)
2829 goto do_query;
2831 do_fetch_cache:
2832 policy->lockout_duration = centry_nttime(centry);
2833 policy->lockout_window = centry_nttime(centry);
2834 policy->lockout_threshold = centry_uint16(centry);
2836 status = centry->status;
2838 DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2839 domain->name, nt_errstr(status) ));
2841 centry_free(centry);
2842 return status;
2844 do_query:
2845 ZERO_STRUCTP(policy);
2847 /* Return status value returned by seq number check */
2849 if (!NT_STATUS_IS_OK(domain->last_status))
2850 return domain->last_status;
2852 DEBUG(10,("lockout_policy: [Cached] - doing backend query for info for domain %s\n",
2853 domain->name ));
2855 status = domain->backend->lockout_policy(domain, mem_ctx, policy);
2857 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2858 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2859 if (!domain->internal && old_status) {
2860 set_domain_offline(domain);
2862 if (cache->tdb &&
2863 !domain->internal &&
2864 !domain->online &&
2865 old_status) {
2866 centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
2867 if (centry) {
2868 goto do_fetch_cache;
2872 /* and save it */
2873 refresh_sequence_number(domain, false);
2874 if (!NT_STATUS_IS_OK(status)) {
2875 return status;
2877 wcache_save_lockout_policy(domain, status, policy);
2879 return status;
2882 /* get password policy */
2883 static NTSTATUS password_policy(struct winbindd_domain *domain,
2884 TALLOC_CTX *mem_ctx,
2885 struct samr_DomInfo1 *policy)
2887 struct winbind_cache *cache = get_cache(domain);
2888 struct cache_entry *centry = NULL;
2889 NTSTATUS status;
2890 bool old_status;
2892 old_status = domain->online;
2893 if (!cache->tdb)
2894 goto do_query;
2896 centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
2898 if (!centry)
2899 goto do_query;
2901 do_fetch_cache:
2902 policy->min_password_length = centry_uint16(centry);
2903 policy->password_history_length = centry_uint16(centry);
2904 policy->password_properties = centry_uint32(centry);
2905 policy->max_password_age = centry_nttime(centry);
2906 policy->min_password_age = centry_nttime(centry);
2908 status = centry->status;
2910 DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2911 domain->name, nt_errstr(status) ));
2913 centry_free(centry);
2914 return status;
2916 do_query:
2917 ZERO_STRUCTP(policy);
2919 /* Return status value returned by seq number check */
2921 if (!NT_STATUS_IS_OK(domain->last_status))
2922 return domain->last_status;
2924 DEBUG(10,("password_policy: [Cached] - doing backend query for info for domain %s\n",
2925 domain->name ));
2927 status = domain->backend->password_policy(domain, mem_ctx, policy);
2929 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2930 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2931 if (!domain->internal && old_status) {
2932 set_domain_offline(domain);
2934 if (cache->tdb &&
2935 !domain->internal &&
2936 !domain->online &&
2937 old_status) {
2938 centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
2939 if (centry) {
2940 goto do_fetch_cache;
2944 /* and save it */
2945 refresh_sequence_number(domain, false);
2946 if (!NT_STATUS_IS_OK(status)) {
2947 return status;
2949 wcache_save_password_policy(domain, status, policy);
2951 return status;
2955 /* Invalidate cached user and group lists coherently */
2957 static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
2958 void *state)
2960 if (strncmp((const char *)kbuf.dptr, "UL/", 3) == 0 ||
2961 strncmp((const char *)kbuf.dptr, "GL/", 3) == 0)
2962 tdb_delete(the_tdb, kbuf);
2964 return 0;
2967 /* Invalidate the getpwnam and getgroups entries for a winbindd domain */
2969 void wcache_invalidate_samlogon(struct winbindd_domain *domain,
2970 struct netr_SamInfo3 *info3)
2972 struct dom_sid sid;
2973 fstring key_str, sid_string;
2974 struct winbind_cache *cache;
2976 /* dont clear cached U/SID and UG/SID entries when we want to logon
2977 * offline - gd */
2979 if (lp_winbind_offline_logon()) {
2980 return;
2983 if (!domain)
2984 return;
2986 cache = get_cache(domain);
2988 if (!cache->tdb) {
2989 return;
2992 sid_compose(&sid, info3->base.domain_sid, info3->base.rid);
2994 /* Clear U/SID cache entry */
2995 fstr_sprintf(key_str, "U/%s", sid_to_fstring(sid_string, &sid));
2996 DEBUG(10, ("wcache_invalidate_samlogon: clearing %s\n", key_str));
2997 tdb_delete(cache->tdb, string_tdb_data(key_str));
2999 /* Clear UG/SID cache entry */
3000 fstr_sprintf(key_str, "UG/%s", sid_to_fstring(sid_string, &sid));
3001 DEBUG(10, ("wcache_invalidate_samlogon: clearing %s\n", key_str));
3002 tdb_delete(cache->tdb, string_tdb_data(key_str));
3004 /* Samba/winbindd never needs this. */
3005 netsamlogon_clear_cached_user(info3);
3008 bool wcache_invalidate_cache(void)
3010 struct winbindd_domain *domain;
3012 for (domain = domain_list(); domain; domain = domain->next) {
3013 struct winbind_cache *cache = get_cache(domain);
3015 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
3016 "entries for %s\n", domain->name));
3017 if (cache) {
3018 if (cache->tdb) {
3019 tdb_traverse(cache->tdb, traverse_fn, NULL);
3020 } else {
3021 return false;
3025 return true;
3028 bool wcache_invalidate_cache_noinit(void)
3030 struct winbindd_domain *domain;
3032 for (domain = domain_list(); domain; domain = domain->next) {
3033 struct winbind_cache *cache;
3035 /* Skip uninitialized domains. */
3036 if (!domain->initialized && !domain->internal) {
3037 continue;
3040 cache = get_cache(domain);
3042 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
3043 "entries for %s\n", domain->name));
3044 if (cache) {
3045 if (cache->tdb) {
3046 tdb_traverse(cache->tdb, traverse_fn, NULL);
3048 * Flushing cache has nothing to with domains.
3049 * return here if we successfully flushed once.
3050 * To avoid unnecessary traversing the cache.
3052 return true;
3053 } else {
3054 return false;
3058 return true;
3061 bool init_wcache(void)
3063 if (wcache == NULL) {
3064 wcache = SMB_XMALLOC_P(struct winbind_cache);
3065 ZERO_STRUCTP(wcache);
3068 if (wcache->tdb != NULL)
3069 return true;
3071 /* when working offline we must not clear the cache on restart */
3072 wcache->tdb = tdb_open_log(cache_path("winbindd_cache.tdb"),
3073 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
3074 lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST),
3075 O_RDWR|O_CREAT, 0600);
3077 if (wcache->tdb == NULL) {
3078 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
3079 return false;
3082 return true;
3085 /************************************************************************
3086 This is called by the parent to initialize the cache file.
3087 We don't need sophisticated locking here as we know we're the
3088 only opener.
3089 ************************************************************************/
3091 bool initialize_winbindd_cache(void)
3093 bool cache_bad = true;
3094 uint32 vers;
3096 if (!init_wcache()) {
3097 DEBUG(0,("initialize_winbindd_cache: init_wcache failed.\n"));
3098 return false;
3101 /* Check version number. */
3102 if (tdb_fetch_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, &vers) &&
3103 vers == WINBINDD_CACHE_VERSION) {
3104 cache_bad = false;
3107 if (cache_bad) {
3108 DEBUG(0,("initialize_winbindd_cache: clearing cache "
3109 "and re-creating with version number %d\n",
3110 WINBINDD_CACHE_VERSION ));
3112 tdb_close(wcache->tdb);
3113 wcache->tdb = NULL;
3115 if (unlink(cache_path("winbindd_cache.tdb")) == -1) {
3116 DEBUG(0,("initialize_winbindd_cache: unlink %s failed %s ",
3117 cache_path("winbindd_cache.tdb"),
3118 strerror(errno) ));
3119 return false;
3121 if (!init_wcache()) {
3122 DEBUG(0,("initialize_winbindd_cache: re-initialization "
3123 "init_wcache failed.\n"));
3124 return false;
3127 /* Write the version. */
3128 if (!tdb_store_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, WINBINDD_CACHE_VERSION)) {
3129 DEBUG(0,("initialize_winbindd_cache: version number store failed %s\n",
3130 tdb_errorstr(wcache->tdb) ));
3131 return false;
3135 tdb_close(wcache->tdb);
3136 wcache->tdb = NULL;
3137 return true;
3140 void close_winbindd_cache(void)
3142 if (!wcache) {
3143 return;
3145 if (wcache->tdb) {
3146 tdb_close(wcache->tdb);
3147 wcache->tdb = NULL;
3151 bool lookup_cached_sid(TALLOC_CTX *mem_ctx, const struct dom_sid *sid,
3152 char **domain_name, char **name,
3153 enum lsa_SidType *type)
3155 struct winbindd_domain *domain;
3156 NTSTATUS status;
3158 domain = find_lookup_domain_from_sid(sid);
3159 if (domain == NULL) {
3160 return false;
3162 status = wcache_sid_to_name(domain, sid, mem_ctx, domain_name, name,
3163 type);
3164 return NT_STATUS_IS_OK(status);
3167 bool lookup_cached_name(const char *domain_name,
3168 const char *name,
3169 struct dom_sid *sid,
3170 enum lsa_SidType *type)
3172 struct winbindd_domain *domain;
3173 NTSTATUS status;
3174 bool original_online_state;
3176 domain = find_lookup_domain_from_name(domain_name);
3177 if (domain == NULL) {
3178 return false;
3181 /* If we are doing a cached logon, temporarily set the domain
3182 offline so the cache won't expire the entry */
3184 original_online_state = domain->online;
3185 domain->online = false;
3186 status = wcache_name_to_sid(domain, domain_name, name, sid, type);
3187 domain->online = original_online_state;
3189 return NT_STATUS_IS_OK(status);
3192 void cache_name2sid(struct winbindd_domain *domain,
3193 const char *domain_name, const char *name,
3194 enum lsa_SidType type, const struct dom_sid *sid)
3196 refresh_sequence_number(domain, false);
3197 wcache_save_name_to_sid(domain, NT_STATUS_OK, domain_name, name,
3198 sid, type);
3202 * The original idea that this cache only contains centries has
3203 * been blurred - now other stuff gets put in here. Ensure we
3204 * ignore these things on cleanup.
3207 static int traverse_fn_cleanup(TDB_CONTEXT *the_tdb, TDB_DATA kbuf,
3208 TDB_DATA dbuf, void *state)
3210 struct cache_entry *centry;
3212 if (is_non_centry_key(kbuf)) {
3213 return 0;
3216 centry = wcache_fetch_raw((char *)kbuf.dptr);
3217 if (!centry) {
3218 return 0;
3221 if (!NT_STATUS_IS_OK(centry->status)) {
3222 DEBUG(10,("deleting centry %s\n", (const char *)kbuf.dptr));
3223 tdb_delete(the_tdb, kbuf);
3226 centry_free(centry);
3227 return 0;
3230 /* flush the cache */
3231 void wcache_flush_cache(void)
3233 if (!wcache)
3234 return;
3235 if (wcache->tdb) {
3236 tdb_close(wcache->tdb);
3237 wcache->tdb = NULL;
3239 if (!winbindd_use_cache()) {
3240 return;
3243 /* when working offline we must not clear the cache on restart */
3244 wcache->tdb = tdb_open_log(cache_path("winbindd_cache.tdb"),
3245 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
3246 lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST),
3247 O_RDWR|O_CREAT, 0600);
3249 if (!wcache->tdb) {
3250 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
3251 return;
3254 tdb_traverse(wcache->tdb, traverse_fn_cleanup, NULL);
3256 DEBUG(10,("wcache_flush_cache success\n"));
3259 /* Count cached creds */
3261 static int traverse_fn_cached_creds(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
3262 void *state)
3264 int *cred_count = (int*)state;
3266 if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
3267 (*cred_count)++;
3269 return 0;
3272 NTSTATUS wcache_count_cached_creds(struct winbindd_domain *domain, int *count)
3274 struct winbind_cache *cache = get_cache(domain);
3276 *count = 0;
3278 if (!cache->tdb) {
3279 return NT_STATUS_INTERNAL_DB_ERROR;
3282 tdb_traverse(cache->tdb, traverse_fn_cached_creds, (void *)count);
3284 return NT_STATUS_OK;
3287 struct cred_list {
3288 struct cred_list *prev, *next;
3289 TDB_DATA key;
3290 fstring name;
3291 time_t created;
3293 static struct cred_list *wcache_cred_list;
3295 static int traverse_fn_get_credlist(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
3296 void *state)
3298 struct cred_list *cred;
3300 if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
3302 cred = SMB_MALLOC_P(struct cred_list);
3303 if (cred == NULL) {
3304 DEBUG(0,("traverse_fn_remove_first_creds: failed to malloc new entry for list\n"));
3305 return -1;
3308 ZERO_STRUCTP(cred);
3310 /* save a copy of the key */
3312 fstrcpy(cred->name, (const char *)kbuf.dptr);
3313 DLIST_ADD(wcache_cred_list, cred);
3316 return 0;
3319 NTSTATUS wcache_remove_oldest_cached_creds(struct winbindd_domain *domain, const struct dom_sid *sid)
3321 struct winbind_cache *cache = get_cache(domain);
3322 NTSTATUS status;
3323 int ret;
3324 struct cred_list *cred, *oldest = NULL;
3326 if (!cache->tdb) {
3327 return NT_STATUS_INTERNAL_DB_ERROR;
3330 /* we possibly already have an entry */
3331 if (sid && NT_STATUS_IS_OK(wcache_cached_creds_exist(domain, sid))) {
3333 fstring key_str, tmp;
3335 DEBUG(11,("we already have an entry, deleting that\n"));
3337 fstr_sprintf(key_str, "CRED/%s", sid_to_fstring(tmp, sid));
3339 tdb_delete(cache->tdb, string_tdb_data(key_str));
3341 return NT_STATUS_OK;
3344 ret = tdb_traverse(cache->tdb, traverse_fn_get_credlist, NULL);
3345 if (ret == 0) {
3346 return NT_STATUS_OK;
3347 } else if ((ret == -1) || (wcache_cred_list == NULL)) {
3348 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
3351 ZERO_STRUCTP(oldest);
3353 for (cred = wcache_cred_list; cred; cred = cred->next) {
3355 TDB_DATA data;
3356 time_t t;
3358 data = tdb_fetch(cache->tdb, string_tdb_data(cred->name));
3359 if (!data.dptr) {
3360 DEBUG(10,("wcache_remove_oldest_cached_creds: entry for [%s] not found\n",
3361 cred->name));
3362 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
3363 goto done;
3366 t = IVAL(data.dptr, 0);
3367 SAFE_FREE(data.dptr);
3369 if (!oldest) {
3370 oldest = SMB_MALLOC_P(struct cred_list);
3371 if (oldest == NULL) {
3372 status = NT_STATUS_NO_MEMORY;
3373 goto done;
3376 fstrcpy(oldest->name, cred->name);
3377 oldest->created = t;
3378 continue;
3381 if (t < oldest->created) {
3382 fstrcpy(oldest->name, cred->name);
3383 oldest->created = t;
3387 if (tdb_delete(cache->tdb, string_tdb_data(oldest->name)) == 0) {
3388 status = NT_STATUS_OK;
3389 } else {
3390 status = NT_STATUS_UNSUCCESSFUL;
3392 done:
3393 SAFE_FREE(wcache_cred_list);
3394 SAFE_FREE(oldest);
3396 return status;
3399 /* Change the global online/offline state. */
3400 bool set_global_winbindd_state_offline(void)
3402 TDB_DATA data;
3404 DEBUG(10,("set_global_winbindd_state_offline: offline requested.\n"));
3406 /* Only go offline if someone has created
3407 the key "WINBINDD_OFFLINE" in the cache tdb. */
3409 if (wcache == NULL || wcache->tdb == NULL) {
3410 DEBUG(10,("set_global_winbindd_state_offline: wcache not open yet.\n"));
3411 return false;
3414 if (!lp_winbind_offline_logon()) {
3415 DEBUG(10,("set_global_winbindd_state_offline: rejecting.\n"));
3416 return false;
3419 if (global_winbindd_offline_state) {
3420 /* Already offline. */
3421 return true;
3424 data = tdb_fetch_bystring( wcache->tdb, "WINBINDD_OFFLINE" );
3426 if (!data.dptr || data.dsize != 4) {
3427 DEBUG(10,("set_global_winbindd_state_offline: offline state not set.\n"));
3428 SAFE_FREE(data.dptr);
3429 return false;
3430 } else {
3431 DEBUG(10,("set_global_winbindd_state_offline: offline state set.\n"));
3432 global_winbindd_offline_state = true;
3433 SAFE_FREE(data.dptr);
3434 return true;
3438 void set_global_winbindd_state_online(void)
3440 DEBUG(10,("set_global_winbindd_state_online: online requested.\n"));
3442 if (!lp_winbind_offline_logon()) {
3443 DEBUG(10,("set_global_winbindd_state_online: rejecting.\n"));
3444 return;
3447 if (!global_winbindd_offline_state) {
3448 /* Already online. */
3449 return;
3451 global_winbindd_offline_state = false;
3453 if (!wcache->tdb) {
3454 return;
3457 /* Ensure there is no key "WINBINDD_OFFLINE" in the cache tdb. */
3458 tdb_delete_bystring(wcache->tdb, "WINBINDD_OFFLINE");
3461 bool get_global_winbindd_state_offline(void)
3463 return global_winbindd_offline_state;
3466 /***********************************************************************
3467 Validate functions for all possible cache tdb keys.
3468 ***********************************************************************/
3470 static struct cache_entry *create_centry_validate(const char *kstr, TDB_DATA data,
3471 struct tdb_validation_status *state)
3473 struct cache_entry *centry;
3475 centry = SMB_XMALLOC_P(struct cache_entry);
3476 centry->data = (unsigned char *)memdup(data.dptr, data.dsize);
3477 if (!centry->data) {
3478 SAFE_FREE(centry);
3479 return NULL;
3481 centry->len = data.dsize;
3482 centry->ofs = 0;
3484 if (centry->len < 8) {
3485 /* huh? corrupt cache? */
3486 DEBUG(0,("create_centry_validate: Corrupt cache for key %s (len < 8) ?\n", kstr));
3487 centry_free(centry);
3488 state->bad_entry = true;
3489 state->success = false;
3490 return NULL;
3493 centry->status = NT_STATUS(centry_uint32(centry));
3494 centry->sequence_number = centry_uint32(centry);
3495 return centry;
3498 static int validate_seqnum(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3499 struct tdb_validation_status *state)
3501 if (dbuf.dsize != 8) {
3502 DEBUG(0,("validate_seqnum: Corrupt cache for key %s (len %u != 8) ?\n",
3503 keystr, (unsigned int)dbuf.dsize ));
3504 state->bad_entry = true;
3505 return 1;
3507 return 0;
3510 static int validate_ns(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3511 struct tdb_validation_status *state)
3513 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3514 if (!centry) {
3515 return 1;
3518 (void)centry_uint32(centry);
3519 if (NT_STATUS_IS_OK(centry->status)) {
3520 struct dom_sid sid;
3521 (void)centry_sid(centry, &sid);
3524 centry_free(centry);
3526 if (!(state->success)) {
3527 return 1;
3529 DEBUG(10,("validate_ns: %s ok\n", keystr));
3530 return 0;
3533 static int validate_sn(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3534 struct tdb_validation_status *state)
3536 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3537 if (!centry) {
3538 return 1;
3541 if (NT_STATUS_IS_OK(centry->status)) {
3542 (void)centry_uint32(centry);
3543 (void)centry_string(centry, mem_ctx);
3544 (void)centry_string(centry, mem_ctx);
3547 centry_free(centry);
3549 if (!(state->success)) {
3550 return 1;
3552 DEBUG(10,("validate_sn: %s ok\n", keystr));
3553 return 0;
3556 static int validate_u(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3557 struct tdb_validation_status *state)
3559 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3560 struct dom_sid sid;
3562 if (!centry) {
3563 return 1;
3566 (void)centry_string(centry, mem_ctx);
3567 (void)centry_string(centry, mem_ctx);
3568 (void)centry_string(centry, mem_ctx);
3569 (void)centry_string(centry, mem_ctx);
3570 (void)centry_uint32(centry);
3571 (void)centry_sid(centry, &sid);
3572 (void)centry_sid(centry, &sid);
3574 centry_free(centry);
3576 if (!(state->success)) {
3577 return 1;
3579 DEBUG(10,("validate_u: %s ok\n", keystr));
3580 return 0;
3583 static int validate_loc_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3584 struct tdb_validation_status *state)
3586 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3588 if (!centry) {
3589 return 1;
3592 (void)centry_nttime(centry);
3593 (void)centry_nttime(centry);
3594 (void)centry_uint16(centry);
3596 centry_free(centry);
3598 if (!(state->success)) {
3599 return 1;
3601 DEBUG(10,("validate_loc_pol: %s ok\n", keystr));
3602 return 0;
3605 static int validate_pwd_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3606 struct tdb_validation_status *state)
3608 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3610 if (!centry) {
3611 return 1;
3614 (void)centry_uint16(centry);
3615 (void)centry_uint16(centry);
3616 (void)centry_uint32(centry);
3617 (void)centry_nttime(centry);
3618 (void)centry_nttime(centry);
3620 centry_free(centry);
3622 if (!(state->success)) {
3623 return 1;
3625 DEBUG(10,("validate_pwd_pol: %s ok\n", keystr));
3626 return 0;
3629 static int validate_cred(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3630 struct tdb_validation_status *state)
3632 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3634 if (!centry) {
3635 return 1;
3638 (void)centry_time(centry);
3639 (void)centry_hash16(centry, mem_ctx);
3641 /* We only have 17 bytes more data in the salted cred case. */
3642 if (centry->len - centry->ofs == 17) {
3643 (void)centry_hash16(centry, mem_ctx);
3646 centry_free(centry);
3648 if (!(state->success)) {
3649 return 1;
3651 DEBUG(10,("validate_cred: %s ok\n", keystr));
3652 return 0;
3655 static int validate_ul(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3656 struct tdb_validation_status *state)
3658 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3659 int32 num_entries, i;
3661 if (!centry) {
3662 return 1;
3665 num_entries = (int32)centry_uint32(centry);
3667 for (i=0; i< num_entries; i++) {
3668 struct dom_sid sid;
3669 (void)centry_string(centry, mem_ctx);
3670 (void)centry_string(centry, mem_ctx);
3671 (void)centry_string(centry, mem_ctx);
3672 (void)centry_string(centry, mem_ctx);
3673 (void)centry_sid(centry, &sid);
3674 (void)centry_sid(centry, &sid);
3677 centry_free(centry);
3679 if (!(state->success)) {
3680 return 1;
3682 DEBUG(10,("validate_ul: %s ok\n", keystr));
3683 return 0;
3686 static int validate_gl(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3687 struct tdb_validation_status *state)
3689 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3690 int32 num_entries, i;
3692 if (!centry) {
3693 return 1;
3696 num_entries = centry_uint32(centry);
3698 for (i=0; i< num_entries; i++) {
3699 (void)centry_string(centry, mem_ctx);
3700 (void)centry_string(centry, mem_ctx);
3701 (void)centry_uint32(centry);
3704 centry_free(centry);
3706 if (!(state->success)) {
3707 return 1;
3709 DEBUG(10,("validate_gl: %s ok\n", keystr));
3710 return 0;
3713 static int validate_ug(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3714 struct tdb_validation_status *state)
3716 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3717 int32 num_groups, i;
3719 if (!centry) {
3720 return 1;
3723 num_groups = centry_uint32(centry);
3725 for (i=0; i< num_groups; i++) {
3726 struct dom_sid sid;
3727 centry_sid(centry, &sid);
3730 centry_free(centry);
3732 if (!(state->success)) {
3733 return 1;
3735 DEBUG(10,("validate_ug: %s ok\n", keystr));
3736 return 0;
3739 static int validate_ua(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3740 struct tdb_validation_status *state)
3742 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3743 int32 num_aliases, i;
3745 if (!centry) {
3746 return 1;
3749 num_aliases = centry_uint32(centry);
3751 for (i=0; i < num_aliases; i++) {
3752 (void)centry_uint32(centry);
3755 centry_free(centry);
3757 if (!(state->success)) {
3758 return 1;
3760 DEBUG(10,("validate_ua: %s ok\n", keystr));
3761 return 0;
3764 static int validate_gm(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3765 struct tdb_validation_status *state)
3767 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3768 int32 num_names, i;
3770 if (!centry) {
3771 return 1;
3774 num_names = centry_uint32(centry);
3776 for (i=0; i< num_names; i++) {
3777 struct dom_sid sid;
3778 centry_sid(centry, &sid);
3779 (void)centry_string(centry, mem_ctx);
3780 (void)centry_uint32(centry);
3783 centry_free(centry);
3785 if (!(state->success)) {
3786 return 1;
3788 DEBUG(10,("validate_gm: %s ok\n", keystr));
3789 return 0;
3792 static int validate_dr(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3793 struct tdb_validation_status *state)
3795 /* Can't say anything about this other than must be nonzero. */
3796 if (dbuf.dsize == 0) {
3797 DEBUG(0,("validate_dr: Corrupt cache for key %s (len == 0) ?\n",
3798 keystr));
3799 state->bad_entry = true;
3800 state->success = false;
3801 return 1;
3804 DEBUG(10,("validate_dr: %s ok\n", keystr));
3805 return 0;
3808 static int validate_de(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3809 struct tdb_validation_status *state)
3811 /* Can't say anything about this other than must be nonzero. */
3812 if (dbuf.dsize == 0) {
3813 DEBUG(0,("validate_de: Corrupt cache for key %s (len == 0) ?\n",
3814 keystr));
3815 state->bad_entry = true;
3816 state->success = false;
3817 return 1;
3820 DEBUG(10,("validate_de: %s ok\n", keystr));
3821 return 0;
3824 static int validate_pwinfo(TALLOC_CTX *mem_ctx, const char *keystr,
3825 TDB_DATA dbuf, struct tdb_validation_status *state)
3827 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3829 if (!centry) {
3830 return 1;
3833 (void)centry_string(centry, mem_ctx);
3834 (void)centry_string(centry, mem_ctx);
3835 (void)centry_string(centry, mem_ctx);
3836 (void)centry_uint32(centry);
3838 centry_free(centry);
3840 if (!(state->success)) {
3841 return 1;
3843 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3844 return 0;
3847 static int validate_nss_an(TALLOC_CTX *mem_ctx, const char *keystr,
3848 TDB_DATA dbuf,
3849 struct tdb_validation_status *state)
3851 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3853 if (!centry) {
3854 return 1;
3857 (void)centry_string( centry, mem_ctx );
3859 centry_free(centry);
3861 if (!(state->success)) {
3862 return 1;
3864 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3865 return 0;
3868 static int validate_nss_na(TALLOC_CTX *mem_ctx, const char *keystr,
3869 TDB_DATA dbuf,
3870 struct tdb_validation_status *state)
3872 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3874 if (!centry) {
3875 return 1;
3878 (void)centry_string( centry, mem_ctx );
3880 centry_free(centry);
3882 if (!(state->success)) {
3883 return 1;
3885 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3886 return 0;
3889 static int validate_trustdomcache(TALLOC_CTX *mem_ctx, const char *keystr,
3890 TDB_DATA dbuf,
3891 struct tdb_validation_status *state)
3893 if (dbuf.dsize == 0) {
3894 DEBUG(0, ("validate_trustdomcache: Corrupt cache for "
3895 "key %s (len ==0) ?\n", keystr));
3896 state->bad_entry = true;
3897 state->success = false;
3898 return 1;
3901 DEBUG(10, ("validate_trustdomcache: %s ok\n", keystr));
3902 DEBUGADD(10, (" Don't trust me, I am a DUMMY!\n"));
3903 return 0;
3906 static int validate_offline(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3907 struct tdb_validation_status *state)
3909 if (dbuf.dsize != 4) {
3910 DEBUG(0,("validate_offline: Corrupt cache for key %s (len %u != 4) ?\n",
3911 keystr, (unsigned int)dbuf.dsize ));
3912 state->bad_entry = true;
3913 state->success = false;
3914 return 1;
3916 DEBUG(10,("validate_offline: %s ok\n", keystr));
3917 return 0;
3920 static int validate_ndr(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3921 struct tdb_validation_status *state)
3924 * Ignore validation for now. The proper way to do this is with a
3925 * checksum. Just pure parsing does not really catch much.
3927 return 0;
3930 static int validate_cache_version(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3931 struct tdb_validation_status *state)
3933 if (dbuf.dsize != 4) {
3934 DEBUG(0, ("validate_cache_version: Corrupt cache for "
3935 "key %s (len %u != 4) ?\n",
3936 keystr, (unsigned int)dbuf.dsize));
3937 state->bad_entry = true;
3938 state->success = false;
3939 return 1;
3942 DEBUG(10, ("validate_cache_version: %s ok\n", keystr));
3943 return 0;
3946 /***********************************************************************
3947 A list of all possible cache tdb keys with associated validation
3948 functions.
3949 ***********************************************************************/
3951 struct key_val_struct {
3952 const char *keyname;
3953 int (*validate_data_fn)(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, struct tdb_validation_status* state);
3954 } key_val[] = {
3955 {"SEQNUM/", validate_seqnum},
3956 {"NS/", validate_ns},
3957 {"SN/", validate_sn},
3958 {"U/", validate_u},
3959 {"LOC_POL/", validate_loc_pol},
3960 {"PWD_POL/", validate_pwd_pol},
3961 {"CRED/", validate_cred},
3962 {"UL/", validate_ul},
3963 {"GL/", validate_gl},
3964 {"UG/", validate_ug},
3965 {"UA", validate_ua},
3966 {"GM/", validate_gm},
3967 {"DR/", validate_dr},
3968 {"DE/", validate_de},
3969 {"NSS/PWINFO/", validate_pwinfo},
3970 {"TRUSTDOMCACHE/", validate_trustdomcache},
3971 {"NSS/NA/", validate_nss_na},
3972 {"NSS/AN/", validate_nss_an},
3973 {"WINBINDD_OFFLINE", validate_offline},
3974 {"NDR/", validate_ndr},
3975 {WINBINDD_CACHE_VERSION_KEYSTR, validate_cache_version},
3976 {NULL, NULL}
3979 /***********************************************************************
3980 Function to look at every entry in the tdb and validate it as far as
3981 possible.
3982 ***********************************************************************/
3984 static int cache_traverse_validate_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, void *state)
3986 int i;
3987 unsigned int max_key_len = 1024;
3988 struct tdb_validation_status *v_state = (struct tdb_validation_status *)state;
3990 /* Paranoia check. */
3991 if (strncmp("UA/", (const char *)kbuf.dptr, 3) == 0) {
3992 max_key_len = 1024 * 1024;
3994 if (kbuf.dsize > max_key_len) {
3995 DEBUG(0, ("cache_traverse_validate_fn: key length too large: "
3996 "(%u) > (%u)\n\n",
3997 (unsigned int)kbuf.dsize, (unsigned int)max_key_len));
3998 return 1;
4001 for (i = 0; key_val[i].keyname; i++) {
4002 size_t namelen = strlen(key_val[i].keyname);
4003 if (kbuf.dsize >= namelen && (
4004 strncmp(key_val[i].keyname, (const char *)kbuf.dptr, namelen)) == 0) {
4005 TALLOC_CTX *mem_ctx;
4006 char *keystr;
4007 int ret;
4009 keystr = SMB_MALLOC_ARRAY(char, kbuf.dsize+1);
4010 if (!keystr) {
4011 return 1;
4013 memcpy(keystr, kbuf.dptr, kbuf.dsize);
4014 keystr[kbuf.dsize] = '\0';
4016 mem_ctx = talloc_init("validate_ctx");
4017 if (!mem_ctx) {
4018 SAFE_FREE(keystr);
4019 return 1;
4022 ret = key_val[i].validate_data_fn(mem_ctx, keystr, dbuf,
4023 v_state);
4025 SAFE_FREE(keystr);
4026 talloc_destroy(mem_ctx);
4027 return ret;
4031 DEBUG(0,("cache_traverse_validate_fn: unknown cache entry\nkey :\n"));
4032 dump_data(0, (uint8 *)kbuf.dptr, kbuf.dsize);
4033 DEBUG(0,("data :\n"));
4034 dump_data(0, (uint8 *)dbuf.dptr, dbuf.dsize);
4035 v_state->unknown_key = true;
4036 v_state->success = false;
4037 return 1; /* terminate. */
4040 static void validate_panic(const char *const why)
4042 DEBUG(0,("validating cache: would panic %s\n", why ));
4043 DEBUGADD(0, ("exiting instead (cache validation mode)\n"));
4044 exit(47);
4047 /***********************************************************************
4048 Try and validate every entry in the winbindd cache. If we fail here,
4049 delete the cache tdb and return non-zero.
4050 ***********************************************************************/
4052 int winbindd_validate_cache(void)
4054 int ret = -1;
4055 const char *tdb_path = cache_path("winbindd_cache.tdb");
4056 TDB_CONTEXT *tdb = NULL;
4058 DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
4059 smb_panic_fn = validate_panic;
4062 tdb = tdb_open_log(tdb_path,
4063 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
4064 ( lp_winbind_offline_logon()
4065 ? TDB_DEFAULT
4066 : TDB_DEFAULT | TDB_CLEAR_IF_FIRST ),
4067 O_RDWR|O_CREAT,
4068 0600);
4069 if (!tdb) {
4070 DEBUG(0, ("winbindd_validate_cache: "
4071 "error opening/initializing tdb\n"));
4072 goto done;
4074 tdb_close(tdb);
4076 ret = tdb_validate_and_backup(tdb_path, cache_traverse_validate_fn);
4078 if (ret != 0) {
4079 DEBUG(10, ("winbindd_validate_cache: validation not successful.\n"));
4080 DEBUGADD(10, ("removing tdb %s.\n", tdb_path));
4081 unlink(tdb_path);
4084 done:
4085 DEBUG(10, ("winbindd_validate_cache: restoring panic function\n"));
4086 smb_panic_fn = smb_panic;
4087 return ret;
4090 /***********************************************************************
4091 Try and validate every entry in the winbindd cache.
4092 ***********************************************************************/
4094 int winbindd_validate_cache_nobackup(void)
4096 int ret = -1;
4097 const char *tdb_path = cache_path("winbindd_cache.tdb");
4099 DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
4100 smb_panic_fn = validate_panic;
4103 if (wcache == NULL || wcache->tdb == NULL) {
4104 ret = tdb_validate_open(tdb_path, cache_traverse_validate_fn);
4105 } else {
4106 ret = tdb_validate(wcache->tdb, cache_traverse_validate_fn);
4109 if (ret != 0) {
4110 DEBUG(10, ("winbindd_validate_cache_nobackup: validation not "
4111 "successful.\n"));
4114 DEBUG(10, ("winbindd_validate_cache_nobackup: restoring panic "
4115 "function\n"));
4116 smb_panic_fn = smb_panic;
4117 return ret;
4120 bool winbindd_cache_validate_and_initialize(void)
4122 close_winbindd_cache();
4124 if (lp_winbind_offline_logon()) {
4125 if (winbindd_validate_cache() < 0) {
4126 DEBUG(0, ("winbindd cache tdb corrupt and no backup "
4127 "could be restored.\n"));
4131 return initialize_winbindd_cache();
4134 /*********************************************************************
4135 ********************************************************************/
4137 static bool add_wbdomain_to_tdc_array( struct winbindd_domain *new_dom,
4138 struct winbindd_tdc_domain **domains,
4139 size_t *num_domains )
4141 struct winbindd_tdc_domain *list = NULL;
4142 size_t idx;
4143 int i;
4144 bool set_only = false;
4146 /* don't allow duplicates */
4148 idx = *num_domains;
4149 list = *domains;
4151 for ( i=0; i< (*num_domains); i++ ) {
4152 if ( strequal( new_dom->name, list[i].domain_name ) ) {
4153 DEBUG(10,("add_wbdomain_to_tdc_array: Found existing record for %s\n",
4154 new_dom->name));
4155 idx = i;
4156 set_only = true;
4158 break;
4162 if ( !set_only ) {
4163 if ( !*domains ) {
4164 list = TALLOC_ARRAY( NULL, struct winbindd_tdc_domain, 1 );
4165 idx = 0;
4166 } else {
4167 list = TALLOC_REALLOC_ARRAY( *domains, *domains,
4168 struct winbindd_tdc_domain,
4169 (*num_domains)+1);
4170 idx = *num_domains;
4173 ZERO_STRUCT( list[idx] );
4176 if ( !list )
4177 return false;
4179 list[idx].domain_name = talloc_strdup( list, new_dom->name );
4180 list[idx].dns_name = talloc_strdup( list, new_dom->alt_name );
4182 if ( !is_null_sid( &new_dom->sid ) ) {
4183 sid_copy( &list[idx].sid, &new_dom->sid );
4184 } else {
4185 sid_copy(&list[idx].sid, &global_sid_NULL);
4188 if ( new_dom->domain_flags != 0x0 )
4189 list[idx].trust_flags = new_dom->domain_flags;
4191 if ( new_dom->domain_type != 0x0 )
4192 list[idx].trust_type = new_dom->domain_type;
4194 if ( new_dom->domain_trust_attribs != 0x0 )
4195 list[idx].trust_attribs = new_dom->domain_trust_attribs;
4197 if ( !set_only ) {
4198 *domains = list;
4199 *num_domains = idx + 1;
4202 return true;
4205 /*********************************************************************
4206 ********************************************************************/
4208 static TDB_DATA make_tdc_key( const char *domain_name )
4210 char *keystr = NULL;
4211 TDB_DATA key = { NULL, 0 };
4213 if ( !domain_name ) {
4214 DEBUG(5,("make_tdc_key: Keyname workgroup is NULL!\n"));
4215 return key;
4218 if (asprintf( &keystr, "TRUSTDOMCACHE/%s", domain_name ) == -1) {
4219 return key;
4221 key = string_term_tdb_data(keystr);
4223 return key;
4226 /*********************************************************************
4227 ********************************************************************/
4229 static int pack_tdc_domains( struct winbindd_tdc_domain *domains,
4230 size_t num_domains,
4231 unsigned char **buf )
4233 unsigned char *buffer = NULL;
4234 int len = 0;
4235 int buflen = 0;
4236 int i = 0;
4238 DEBUG(10,("pack_tdc_domains: Packing %d trusted domains\n",
4239 (int)num_domains));
4241 buflen = 0;
4243 again:
4244 len = 0;
4246 /* Store the number of array items first */
4247 len += tdb_pack( buffer+len, buflen-len, "d",
4248 num_domains );
4250 /* now pack each domain trust record */
4251 for ( i=0; i<num_domains; i++ ) {
4253 fstring tmp;
4255 if ( buflen > 0 ) {
4256 DEBUG(10,("pack_tdc_domains: Packing domain %s (%s)\n",
4257 domains[i].domain_name,
4258 domains[i].dns_name ? domains[i].dns_name : "UNKNOWN" ));
4261 len += tdb_pack( buffer+len, buflen-len, "fffddd",
4262 domains[i].domain_name,
4263 domains[i].dns_name,
4264 sid_to_fstring(tmp, &domains[i].sid),
4265 domains[i].trust_flags,
4266 domains[i].trust_attribs,
4267 domains[i].trust_type );
4270 if ( buflen < len ) {
4271 SAFE_FREE(buffer);
4272 if ( (buffer = SMB_MALLOC_ARRAY(unsigned char, len)) == NULL ) {
4273 DEBUG(0,("pack_tdc_domains: failed to alloc buffer!\n"));
4274 buflen = -1;
4275 goto done;
4277 buflen = len;
4278 goto again;
4281 *buf = buffer;
4283 done:
4284 return buflen;
4287 /*********************************************************************
4288 ********************************************************************/
4290 static size_t unpack_tdc_domains( unsigned char *buf, int buflen,
4291 struct winbindd_tdc_domain **domains )
4293 fstring domain_name, dns_name, sid_string;
4294 uint32 type, attribs, flags;
4295 int num_domains;
4296 int len = 0;
4297 int i;
4298 struct winbindd_tdc_domain *list = NULL;
4300 /* get the number of domains */
4301 len += tdb_unpack( buf+len, buflen-len, "d", &num_domains);
4302 if ( len == -1 ) {
4303 DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
4304 return 0;
4307 list = TALLOC_ARRAY( NULL, struct winbindd_tdc_domain, num_domains );
4308 if ( !list ) {
4309 DEBUG(0,("unpack_tdc_domains: Failed to talloc() domain list!\n"));
4310 return 0;
4313 for ( i=0; i<num_domains; i++ ) {
4314 len += tdb_unpack( buf+len, buflen-len, "fffddd",
4315 domain_name,
4316 dns_name,
4317 sid_string,
4318 &flags,
4319 &attribs,
4320 &type );
4322 if ( len == -1 ) {
4323 DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
4324 TALLOC_FREE( list );
4325 return 0;
4328 DEBUG(11,("unpack_tdc_domains: Unpacking domain %s (%s) "
4329 "SID %s, flags = 0x%x, attribs = 0x%x, type = 0x%x\n",
4330 domain_name, dns_name, sid_string,
4331 flags, attribs, type));
4333 list[i].domain_name = talloc_strdup( list, domain_name );
4334 list[i].dns_name = talloc_strdup( list, dns_name );
4335 if ( !string_to_sid( &(list[i].sid), sid_string ) ) {
4336 DEBUG(10,("unpack_tdc_domains: no SID for domain %s\n",
4337 domain_name));
4339 list[i].trust_flags = flags;
4340 list[i].trust_attribs = attribs;
4341 list[i].trust_type = type;
4344 *domains = list;
4346 return num_domains;
4349 /*********************************************************************
4350 ********************************************************************/
4352 static bool wcache_tdc_store_list( struct winbindd_tdc_domain *domains, size_t num_domains )
4354 TDB_DATA key = make_tdc_key( lp_workgroup() );
4355 TDB_DATA data = { NULL, 0 };
4356 int ret;
4358 if ( !key.dptr )
4359 return false;
4361 /* See if we were asked to delete the cache entry */
4363 if ( !domains ) {
4364 ret = tdb_delete( wcache->tdb, key );
4365 goto done;
4368 data.dsize = pack_tdc_domains( domains, num_domains, &data.dptr );
4370 if ( !data.dptr ) {
4371 ret = -1;
4372 goto done;
4375 ret = tdb_store( wcache->tdb, key, data, 0 );
4377 done:
4378 SAFE_FREE( data.dptr );
4379 SAFE_FREE( key.dptr );
4381 return ( ret != -1 );
4384 /*********************************************************************
4385 ********************************************************************/
4387 bool wcache_tdc_fetch_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 };
4392 *domains = NULL;
4393 *num_domains = 0;
4395 if ( !key.dptr )
4396 return false;
4398 data = tdb_fetch( wcache->tdb, key );
4400 SAFE_FREE( key.dptr );
4402 if ( !data.dptr )
4403 return false;
4405 *num_domains = unpack_tdc_domains( data.dptr, data.dsize, domains );
4407 SAFE_FREE( data.dptr );
4409 if ( !*domains )
4410 return false;
4412 return true;
4415 /*********************************************************************
4416 ********************************************************************/
4418 bool wcache_tdc_add_domain( struct winbindd_domain *domain )
4420 struct winbindd_tdc_domain *dom_list = NULL;
4421 size_t num_domains = 0;
4422 bool ret = false;
4424 DEBUG(10,("wcache_tdc_add_domain: Adding domain %s (%s), SID %s, "
4425 "flags = 0x%x, attributes = 0x%x, type = 0x%x\n",
4426 domain->name, domain->alt_name,
4427 sid_string_dbg(&domain->sid),
4428 domain->domain_flags,
4429 domain->domain_trust_attribs,
4430 domain->domain_type));
4432 if ( !init_wcache() ) {
4433 return false;
4436 /* fetch the list */
4438 wcache_tdc_fetch_list( &dom_list, &num_domains );
4440 /* add the new domain */
4442 if ( !add_wbdomain_to_tdc_array( domain, &dom_list, &num_domains ) ) {
4443 goto done;
4446 /* pack the domain */
4448 if ( !wcache_tdc_store_list( dom_list, num_domains ) ) {
4449 goto done;
4452 /* Success */
4454 ret = true;
4455 done:
4456 TALLOC_FREE( dom_list );
4458 return ret;
4461 /*********************************************************************
4462 ********************************************************************/
4464 struct winbindd_tdc_domain * wcache_tdc_fetch_domain( TALLOC_CTX *ctx, const char *name )
4466 struct winbindd_tdc_domain *dom_list = NULL;
4467 size_t num_domains = 0;
4468 int i;
4469 struct winbindd_tdc_domain *d = NULL;
4471 DEBUG(10,("wcache_tdc_fetch_domain: Searching for domain %s\n", name));
4473 if ( !init_wcache() ) {
4474 return false;
4477 /* fetch the list */
4479 wcache_tdc_fetch_list( &dom_list, &num_domains );
4481 for ( i=0; i<num_domains; i++ ) {
4482 if ( strequal(name, dom_list[i].domain_name) ||
4483 strequal(name, dom_list[i].dns_name) )
4485 DEBUG(10,("wcache_tdc_fetch_domain: Found domain %s\n",
4486 name));
4488 d = TALLOC_P( ctx, struct winbindd_tdc_domain );
4489 if ( !d )
4490 break;
4492 d->domain_name = talloc_strdup( d, dom_list[i].domain_name );
4493 d->dns_name = talloc_strdup( d, dom_list[i].dns_name );
4494 sid_copy( &d->sid, &dom_list[i].sid );
4495 d->trust_flags = dom_list[i].trust_flags;
4496 d->trust_type = dom_list[i].trust_type;
4497 d->trust_attribs = dom_list[i].trust_attribs;
4499 break;
4503 TALLOC_FREE( dom_list );
4505 return d;
4509 /*********************************************************************
4510 ********************************************************************/
4512 void wcache_tdc_clear( void )
4514 if ( !init_wcache() )
4515 return;
4517 wcache_tdc_store_list( NULL, 0 );
4519 return;
4523 /*********************************************************************
4524 ********************************************************************/
4526 static void wcache_save_user_pwinfo(struct winbindd_domain *domain,
4527 NTSTATUS status,
4528 const struct dom_sid *user_sid,
4529 const char *homedir,
4530 const char *shell,
4531 const char *gecos,
4532 uint32 gid)
4534 struct cache_entry *centry;
4535 fstring tmp;
4537 if ( (centry = centry_start(domain, status)) == NULL )
4538 return;
4540 centry_put_string( centry, homedir );
4541 centry_put_string( centry, shell );
4542 centry_put_string( centry, gecos );
4543 centry_put_uint32( centry, gid );
4545 centry_end(centry, "NSS/PWINFO/%s", sid_to_fstring(tmp, user_sid) );
4547 DEBUG(10,("wcache_save_user_pwinfo: %s\n", sid_string_dbg(user_sid) ));
4549 centry_free(centry);
4552 NTSTATUS nss_get_info_cached( struct winbindd_domain *domain,
4553 const struct dom_sid *user_sid,
4554 TALLOC_CTX *ctx,
4555 ADS_STRUCT *ads, LDAPMessage *msg,
4556 const char **homedir, const char **shell,
4557 const char **gecos, gid_t *p_gid)
4559 struct winbind_cache *cache = get_cache(domain);
4560 struct cache_entry *centry = NULL;
4561 NTSTATUS nt_status;
4562 fstring tmp;
4564 if (!cache->tdb)
4565 goto do_query;
4567 centry = wcache_fetch(cache, domain, "NSS/PWINFO/%s",
4568 sid_to_fstring(tmp, user_sid));
4570 if (!centry)
4571 goto do_query;
4573 *homedir = centry_string( centry, ctx );
4574 *shell = centry_string( centry, ctx );
4575 *gecos = centry_string( centry, ctx );
4576 *p_gid = centry_uint32( centry );
4578 centry_free(centry);
4580 DEBUG(10,("nss_get_info_cached: [Cached] - user_sid %s\n",
4581 sid_string_dbg(user_sid)));
4583 return NT_STATUS_OK;
4585 do_query:
4587 nt_status = nss_get_info( domain->name, user_sid, ctx, ads, msg,
4588 homedir, shell, gecos, p_gid );
4590 DEBUG(10, ("nss_get_info returned %s\n", nt_errstr(nt_status)));
4592 if ( NT_STATUS_IS_OK(nt_status) ) {
4593 DEBUG(10, ("result:\n\thomedir = '%s'\n", *homedir));
4594 DEBUGADD(10, ("\tshell = '%s'\n", *shell));
4595 DEBUGADD(10, ("\tgecos = '%s'\n", *gecos));
4596 DEBUGADD(10, ("\tgid = '%u'\n", (unsigned int)*p_gid));
4598 wcache_save_user_pwinfo( domain, nt_status, user_sid,
4599 *homedir, *shell, *gecos, *p_gid );
4602 if ( NT_STATUS_EQUAL( nt_status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND ) ) {
4603 DEBUG(5,("nss_get_info_cached: Setting domain %s offline\n",
4604 domain->name ));
4605 set_domain_offline( domain );
4608 return nt_status;
4612 /* the cache backend methods are exposed via this structure */
4613 struct winbindd_methods cache_methods = {
4614 true,
4615 query_user_list,
4616 enum_dom_groups,
4617 enum_local_groups,
4618 name_to_sid,
4619 sid_to_name,
4620 rids_to_names,
4621 query_user,
4622 lookup_usergroups,
4623 lookup_useraliases,
4624 lookup_groupmem,
4625 sequence_number,
4626 lockout_policy,
4627 password_policy,
4628 trusted_domains
4631 static bool wcache_ndr_key(TALLOC_CTX *mem_ctx, char *domain_name,
4632 uint32_t opnum, const DATA_BLOB *req,
4633 TDB_DATA *pkey)
4635 char *key;
4636 size_t keylen;
4638 key = talloc_asprintf(mem_ctx, "NDR/%s/%d/", domain_name, (int)opnum);
4639 if (key == NULL) {
4640 return false;
4642 keylen = talloc_get_size(key) - 1;
4644 key = talloc_realloc(mem_ctx, key, char, keylen + req->length);
4645 if (key == NULL) {
4646 return false;
4648 memcpy(key + keylen, req->data, req->length);
4650 pkey->dptr = (uint8_t *)key;
4651 pkey->dsize = talloc_get_size(key);
4652 return true;
4655 static bool wcache_opnum_cacheable(uint32_t opnum)
4657 switch (opnum) {
4658 case NDR_WBINT_PING:
4659 case NDR_WBINT_QUERYSEQUENCENUMBER:
4660 case NDR_WBINT_ALLOCATEUID:
4661 case NDR_WBINT_ALLOCATEGID:
4662 case NDR_WBINT_CHECKMACHINEACCOUNT:
4663 case NDR_WBINT_CHANGEMACHINEACCOUNT:
4664 case NDR_WBINT_PINGDC:
4665 return false;
4667 return true;
4670 bool wcache_fetch_ndr(TALLOC_CTX *mem_ctx, struct winbindd_domain *domain,
4671 uint32_t opnum, const DATA_BLOB *req, DATA_BLOB *resp)
4673 TDB_DATA key, data;
4674 bool ret = false;
4676 if (!wcache_opnum_cacheable(opnum) ||
4677 is_my_own_sam_domain(domain) ||
4678 is_builtin_domain(domain)) {
4679 return false;
4682 if (wcache->tdb == NULL) {
4683 return false;
4686 if (!wcache_ndr_key(talloc_tos(), domain->name, opnum, req, &key)) {
4687 return false;
4689 data = tdb_fetch(wcache->tdb, key);
4690 TALLOC_FREE(key.dptr);
4692 if (data.dptr == NULL) {
4693 return false;
4695 if (data.dsize < 4) {
4696 goto fail;
4699 if (!is_domain_offline(domain)) {
4700 uint32_t entry_seqnum, dom_seqnum, last_check;
4702 if (!wcache_fetch_seqnum(domain->name, &dom_seqnum,
4703 &last_check)) {
4704 goto fail;
4706 entry_seqnum = IVAL(data.dptr, 0);
4707 if (entry_seqnum != dom_seqnum) {
4708 DEBUG(10, ("Entry has wrong sequence number: %d\n",
4709 (int)entry_seqnum));
4710 goto fail;
4714 resp->data = (uint8_t *)talloc_memdup(mem_ctx, data.dptr + 4,
4715 data.dsize - 4);
4716 if (resp->data == NULL) {
4717 DEBUG(10, ("talloc failed\n"));
4718 goto fail;
4720 resp->length = data.dsize - 4;
4722 ret = true;
4723 fail:
4724 SAFE_FREE(data.dptr);
4725 return ret;
4728 void wcache_store_ndr(struct winbindd_domain *domain, uint32_t opnum,
4729 const DATA_BLOB *req, const DATA_BLOB *resp)
4731 TDB_DATA key, data;
4732 uint32_t dom_seqnum, last_check;
4734 if (!wcache_opnum_cacheable(opnum) ||
4735 is_my_own_sam_domain(domain) ||
4736 is_builtin_domain(domain)) {
4737 return;
4740 if (wcache->tdb == NULL) {
4741 return;
4744 if (!wcache_fetch_seqnum(domain->name, &dom_seqnum, &last_check)) {
4745 DEBUG(10, ("could not fetch seqnum for domain %s\n",
4746 domain->name));
4747 return;
4750 if (!wcache_ndr_key(talloc_tos(), domain->name, opnum, req, &key)) {
4751 return;
4754 data.dsize = resp->length + 4;
4755 data.dptr = talloc_array(key.dptr, uint8_t, data.dsize);
4756 if (data.dptr == NULL) {
4757 goto done;
4760 SIVAL(data.dptr, 0, dom_seqnum);
4761 memcpy(data.dptr+4, resp->data, resp->length);
4763 tdb_store(wcache->tdb, key, data, 0);
4765 done:
4766 TALLOC_FREE(key.dptr);
4767 return;