s3-winbind: Remove unused keys from list.
[Samba/gebeck_regimport.git] / source3 / winbindd / winbindd_cache.c
blob9aed366a47b83b8847fbe6da369a10568ce04b15
1 /*
2 Unix SMB/CIFS implementation.
4 Winbind cache backend functions
6 Copyright (C) Andrew Tridgell 2001
7 Copyright (C) Gerald Carter 2003-2007
8 Copyright (C) Volker Lendecke 2005
9 Copyright (C) Guenther Deschner 2005
10 Copyright (C) Michael Adam 2007
12 This program is free software; you can redistribute it and/or modify
13 it under the terms of the GNU General Public License as published by
14 the Free Software Foundation; either version 3 of the License, or
15 (at your option) any later version.
17 This program is distributed in the hope that it will be useful,
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 GNU General Public License for more details.
22 You should have received a copy of the GNU General Public License
23 along with this program. If not, see <http://www.gnu.org/licenses/>.
26 #include "includes.h"
27 #include "system/filesys.h"
28 #include "winbindd.h"
29 #include "tdb_validate.h"
30 #include "../libcli/auth/libcli_auth.h"
31 #include "../librpc/gen_ndr/ndr_wbint.h"
32 #include "ads.h"
33 #include "nss_info.h"
34 #include "../libcli/security/security.h"
35 #include "passdb/machine_sid.h"
36 #include "util_tdb.h"
38 #undef DBGC_CLASS
39 #define DBGC_CLASS DBGC_WINBIND
41 #define WINBINDD_CACHE_VERSION 2
42 #define WINBINDD_CACHE_VERSION_KEYSTR "WINBINDD_CACHE_VERSION"
44 extern struct winbindd_methods reconnect_methods;
45 #ifdef HAVE_ADS
46 extern struct winbindd_methods ads_methods;
47 #endif
48 extern struct winbindd_methods builtin_passdb_methods;
49 extern struct winbindd_methods sam_passdb_methods;
52 * JRA. KEEP THIS LIST UP TO DATE IF YOU ADD CACHE ENTRIES.
53 * Here are the list of entry types that are *not* stored
54 * as form struct cache_entry in the cache.
57 static const char *non_centry_keys[] = {
58 "SEQNUM/",
59 "WINBINDD_OFFLINE",
60 WINBINDD_CACHE_VERSION_KEYSTR,
61 NULL
64 /************************************************************************
65 Is this key a non-centry type ?
66 ************************************************************************/
68 static bool is_non_centry_key(TDB_DATA kbuf)
70 int i;
72 if (kbuf.dptr == NULL || kbuf.dsize == 0) {
73 return false;
75 for (i = 0; non_centry_keys[i] != NULL; i++) {
76 size_t namelen = strlen(non_centry_keys[i]);
77 if (kbuf.dsize < namelen) {
78 continue;
80 if (strncmp(non_centry_keys[i], (const char *)kbuf.dptr, namelen) == 0) {
81 return true;
84 return false;
87 /* Global online/offline state - False when online. winbindd starts up online
88 and sets this to true if the first query fails and there's an entry in
89 the cache tdb telling us to stay offline. */
91 static bool global_winbindd_offline_state;
93 struct winbind_cache {
94 TDB_CONTEXT *tdb;
97 struct cache_entry {
98 NTSTATUS status;
99 uint32 sequence_number;
100 uint64_t timeout;
101 uint8 *data;
102 uint32 len, ofs;
105 void (*smb_panic_fn)(const char *const why) = smb_panic;
107 #define WINBINDD_MAX_CACHE_SIZE (50*1024*1024)
109 static struct winbind_cache *wcache;
111 /* get the winbind_cache structure */
112 static struct winbind_cache *get_cache(struct winbindd_domain *domain)
114 struct winbind_cache *ret = wcache;
116 /* We have to know what type of domain we are dealing with first. */
118 if (domain->internal) {
119 domain->backend = &builtin_passdb_methods;
120 domain->initialized = True;
123 if (strequal(domain->name, get_global_sam_name()) &&
124 sid_check_is_domain(&domain->sid)) {
125 domain->backend = &sam_passdb_methods;
126 domain->initialized = True;
129 if ( !domain->initialized ) {
130 init_dc_connection( domain );
134 OK. listen up becasue I'm only going to say this once.
135 We have the following scenarios to consider
136 (a) trusted AD domains on a Samba DC,
137 (b) trusted AD domains and we are joined to a non-kerberos domain
138 (c) trusted AD domains and we are joined to a kerberos (AD) domain
140 For (a) we can always contact the trusted domain using krb5
141 since we have the domain trust account password
143 For (b) we can only use RPC since we have no way of
144 getting a krb5 ticket in our own domain
146 For (c) we can always use krb5 since we have a kerberos trust
148 --jerry
151 if (!domain->backend) {
152 #ifdef HAVE_ADS
153 struct winbindd_domain *our_domain = domain;
155 /* find our domain first so we can figure out if we
156 are joined to a kerberized domain */
158 if ( !domain->primary )
159 our_domain = find_our_domain();
161 if ((our_domain->active_directory || IS_DC)
162 && domain->active_directory
163 && !lp_winbind_rpc_only()) {
164 DEBUG(5,("get_cache: Setting ADS methods for domain %s\n", domain->name));
165 domain->backend = &ads_methods;
166 } else {
167 #endif /* HAVE_ADS */
168 DEBUG(5,("get_cache: Setting MS-RPC methods for domain %s\n", domain->name));
169 domain->backend = &reconnect_methods;
170 #ifdef HAVE_ADS
172 #endif /* HAVE_ADS */
175 if (ret)
176 return ret;
178 ret = SMB_XMALLOC_P(struct winbind_cache);
179 ZERO_STRUCTP(ret);
181 wcache = ret;
182 wcache_flush_cache();
184 return ret;
188 free a centry structure
190 static void centry_free(struct cache_entry *centry)
192 if (!centry)
193 return;
194 SAFE_FREE(centry->data);
195 free(centry);
198 static bool centry_check_bytes(struct cache_entry *centry, size_t nbytes)
200 if (centry->len - centry->ofs < nbytes) {
201 DEBUG(0,("centry corruption? needed %u bytes, have %d\n",
202 (unsigned int)nbytes,
203 centry->len - centry->ofs));
204 return false;
206 return true;
210 pull a uint64_t from a cache entry
212 static uint64_t centry_uint64_t(struct cache_entry *centry)
214 uint64_t ret;
216 if (!centry_check_bytes(centry, 8)) {
217 smb_panic_fn("centry_uint64_t");
219 ret = BVAL(centry->data, centry->ofs);
220 centry->ofs += 8;
221 return ret;
225 pull a uint32 from a cache entry
227 static uint32 centry_uint32(struct cache_entry *centry)
229 uint32 ret;
231 if (!centry_check_bytes(centry, 4)) {
232 smb_panic_fn("centry_uint32");
234 ret = IVAL(centry->data, centry->ofs);
235 centry->ofs += 4;
236 return ret;
240 pull a uint16 from a cache entry
242 static uint16 centry_uint16(struct cache_entry *centry)
244 uint16 ret;
245 if (!centry_check_bytes(centry, 2)) {
246 smb_panic_fn("centry_uint16");
248 ret = SVAL(centry->data, centry->ofs);
249 centry->ofs += 2;
250 return ret;
254 pull a uint8 from a cache entry
256 static uint8 centry_uint8(struct cache_entry *centry)
258 uint8 ret;
259 if (!centry_check_bytes(centry, 1)) {
260 smb_panic_fn("centry_uint8");
262 ret = CVAL(centry->data, centry->ofs);
263 centry->ofs += 1;
264 return ret;
268 pull a NTTIME from a cache entry
270 static NTTIME centry_nttime(struct cache_entry *centry)
272 NTTIME ret;
273 if (!centry_check_bytes(centry, 8)) {
274 smb_panic_fn("centry_nttime");
276 ret = IVAL(centry->data, centry->ofs);
277 centry->ofs += 4;
278 ret += (uint64)IVAL(centry->data, centry->ofs) << 32;
279 centry->ofs += 4;
280 return ret;
284 pull a time_t from a cache entry. time_t stored portably as a 64-bit time.
286 static time_t centry_time(struct cache_entry *centry)
288 return (time_t)centry_nttime(centry);
291 /* pull a string from a cache entry, using the supplied
292 talloc context
294 static char *centry_string(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
296 uint32 len;
297 char *ret;
299 len = centry_uint8(centry);
301 if (len == 0xFF) {
302 /* a deliberate NULL string */
303 return NULL;
306 if (!centry_check_bytes(centry, (size_t)len)) {
307 smb_panic_fn("centry_string");
310 ret = talloc_array(mem_ctx, char, len+1);
311 if (!ret) {
312 smb_panic_fn("centry_string out of memory\n");
314 memcpy(ret,centry->data + centry->ofs, len);
315 ret[len] = 0;
316 centry->ofs += len;
317 return ret;
320 /* pull a hash16 from a cache entry, using the supplied
321 talloc context
323 static char *centry_hash16(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
325 uint32 len;
326 char *ret;
328 len = centry_uint8(centry);
330 if (len != 16) {
331 DEBUG(0,("centry corruption? hash len (%u) != 16\n",
332 len ));
333 return NULL;
336 if (!centry_check_bytes(centry, 16)) {
337 return NULL;
340 ret = talloc_array(mem_ctx, char, 16);
341 if (!ret) {
342 smb_panic_fn("centry_hash out of memory\n");
344 memcpy(ret,centry->data + centry->ofs, 16);
345 centry->ofs += 16;
346 return ret;
349 /* pull a sid from a cache entry, using the supplied
350 talloc context
352 static bool centry_sid(struct cache_entry *centry, struct dom_sid *sid)
354 char *sid_string;
355 bool ret;
357 sid_string = centry_string(centry, talloc_tos());
358 if (sid_string == NULL) {
359 return false;
361 ret = string_to_sid(sid, sid_string);
362 TALLOC_FREE(sid_string);
363 return ret;
368 pull a NTSTATUS from a cache entry
370 static NTSTATUS centry_ntstatus(struct cache_entry *centry)
372 NTSTATUS status;
374 status = NT_STATUS(centry_uint32(centry));
375 return status;
379 /* the server is considered down if it can't give us a sequence number */
380 static bool wcache_server_down(struct winbindd_domain *domain)
382 bool ret;
384 if (!wcache->tdb)
385 return false;
387 ret = (domain->sequence_number == DOM_SEQUENCE_NONE);
389 if (ret)
390 DEBUG(10,("wcache_server_down: server for Domain %s down\n",
391 domain->name ));
392 return ret;
395 static bool wcache_fetch_seqnum(const char *domain_name, uint32_t *seqnum,
396 uint32_t *last_seq_check)
398 char *key;
399 TDB_DATA data;
401 if (wcache->tdb == NULL) {
402 DEBUG(10,("wcache_fetch_seqnum: tdb == NULL\n"));
403 return false;
406 key = talloc_asprintf(talloc_tos(), "SEQNUM/%s", domain_name);
407 if (key == NULL) {
408 DEBUG(10, ("talloc failed\n"));
409 return false;
412 data = tdb_fetch_bystring(wcache->tdb, key);
413 TALLOC_FREE(key);
415 if (data.dptr == NULL) {
416 DEBUG(10, ("wcache_fetch_seqnum: %s not found\n",
417 domain_name));
418 return false;
420 if (data.dsize != 8) {
421 DEBUG(10, ("wcache_fetch_seqnum: invalid data size %d\n",
422 (int)data.dsize));
423 SAFE_FREE(data.dptr);
424 return false;
427 *seqnum = IVAL(data.dptr, 0);
428 *last_seq_check = IVAL(data.dptr, 4);
429 SAFE_FREE(data.dptr);
431 return true;
434 static NTSTATUS fetch_cache_seqnum( struct winbindd_domain *domain, time_t now )
436 uint32 last_check, time_diff;
438 if (!wcache_fetch_seqnum(domain->name, &domain->sequence_number,
439 &last_check)) {
440 return NT_STATUS_UNSUCCESSFUL;
442 domain->last_seq_check = last_check;
444 /* have we expired? */
446 time_diff = now - domain->last_seq_check;
447 if ( time_diff > lp_winbind_cache_time() ) {
448 DEBUG(10,("fetch_cache_seqnum: timeout [%s][%u @ %u]\n",
449 domain->name, domain->sequence_number,
450 (uint32)domain->last_seq_check));
451 return NT_STATUS_UNSUCCESSFUL;
454 DEBUG(10,("fetch_cache_seqnum: success [%s][%u @ %u]\n",
455 domain->name, domain->sequence_number,
456 (uint32)domain->last_seq_check));
458 return NT_STATUS_OK;
461 bool wcache_store_seqnum(const char *domain_name, uint32_t seqnum,
462 time_t last_seq_check)
464 char *key_str;
465 uint8_t buf[8];
466 int ret;
468 if (wcache->tdb == NULL) {
469 DEBUG(10, ("wcache_store_seqnum: wcache->tdb == NULL\n"));
470 return false;
473 key_str = talloc_asprintf(talloc_tos(), "SEQNUM/%s", domain_name);
474 if (key_str == NULL) {
475 DEBUG(10, ("talloc_asprintf failed\n"));
476 return false;
479 SIVAL(buf, 0, seqnum);
480 SIVAL(buf, 4, last_seq_check);
482 ret = tdb_store_bystring(wcache->tdb, key_str,
483 make_tdb_data(buf, sizeof(buf)), TDB_REPLACE);
484 TALLOC_FREE(key_str);
485 if (ret != 0) {
486 DEBUG(10, ("tdb_store_bystring failed: %s\n",
487 tdb_errorstr_compat(wcache->tdb)));
488 TALLOC_FREE(key_str);
489 return false;
492 DEBUG(10, ("wcache_store_seqnum: success [%s][%u @ %u]\n",
493 domain_name, seqnum, (unsigned)last_seq_check));
495 return true;
498 static bool store_cache_seqnum( struct winbindd_domain *domain )
500 return wcache_store_seqnum(domain->name, domain->sequence_number,
501 domain->last_seq_check);
505 refresh the domain sequence number. If force is true
506 then always refresh it, no matter how recently we fetched it
509 static void refresh_sequence_number(struct winbindd_domain *domain, bool force)
511 NTSTATUS status;
512 unsigned time_diff;
513 time_t t = time(NULL);
514 unsigned cache_time = lp_winbind_cache_time();
516 if (is_domain_offline(domain)) {
517 return;
520 get_cache( domain );
522 #if 0 /* JERRY -- disable as the default cache time is now 5 minutes */
523 /* trying to reconnect is expensive, don't do it too often */
524 if (domain->sequence_number == DOM_SEQUENCE_NONE) {
525 cache_time *= 8;
527 #endif
529 time_diff = t - domain->last_seq_check;
531 /* see if we have to refetch the domain sequence number */
532 if (!force && (time_diff < cache_time) &&
533 (domain->sequence_number != DOM_SEQUENCE_NONE) &&
534 NT_STATUS_IS_OK(domain->last_status)) {
535 DEBUG(10, ("refresh_sequence_number: %s time ok\n", domain->name));
536 goto done;
539 /* try to get the sequence number from the tdb cache first */
540 /* this will update the timestamp as well */
542 status = fetch_cache_seqnum( domain, t );
543 if (NT_STATUS_IS_OK(status) &&
544 (domain->sequence_number != DOM_SEQUENCE_NONE) &&
545 NT_STATUS_IS_OK(domain->last_status)) {
546 goto done;
549 /* important! make sure that we know if this is a native
550 mode domain or not. And that we can contact it. */
552 if ( winbindd_can_contact_domain( domain ) ) {
553 status = domain->backend->sequence_number(domain,
554 &domain->sequence_number);
555 } else {
556 /* just use the current time */
557 status = NT_STATUS_OK;
558 domain->sequence_number = time(NULL);
562 /* the above call could have set our domain->backend to NULL when
563 * coming from offline to online mode, make sure to reinitialize the
564 * backend - Guenther */
565 get_cache( domain );
567 if (!NT_STATUS_IS_OK(status)) {
568 DEBUG(10,("refresh_sequence_number: failed with %s\n", nt_errstr(status)));
569 domain->sequence_number = DOM_SEQUENCE_NONE;
572 domain->last_status = status;
573 domain->last_seq_check = time(NULL);
575 /* save the new sequence number in the cache */
576 store_cache_seqnum( domain );
578 done:
579 DEBUG(10, ("refresh_sequence_number: %s seq number is now %d\n",
580 domain->name, domain->sequence_number));
582 return;
586 decide if a cache entry has expired
588 static bool centry_expired(struct winbindd_domain *domain, const char *keystr, struct cache_entry *centry)
590 /* If we've been told to be offline - stay in that state... */
591 if (lp_winbind_offline_logon() && global_winbindd_offline_state) {
592 DEBUG(10,("centry_expired: Key %s for domain %s valid as winbindd is globally offline.\n",
593 keystr, domain->name ));
594 return false;
597 /* when the domain is offline return the cached entry.
598 * This deals with transient offline states... */
600 if (!domain->online) {
601 DEBUG(10,("centry_expired: Key %s for domain %s valid as domain is offline.\n",
602 keystr, domain->name ));
603 return false;
606 /* if the server is OK and our cache entry came from when it was down then
607 the entry is invalid */
608 if ((domain->sequence_number != DOM_SEQUENCE_NONE) &&
609 (centry->sequence_number == DOM_SEQUENCE_NONE)) {
610 DEBUG(10,("centry_expired: Key %s for domain %s invalid sequence.\n",
611 keystr, domain->name ));
612 return true;
615 /* if the server is down or the cache entry is not older than the
616 current sequence number or it did not timeout then it is OK */
617 if (wcache_server_down(domain)
618 || ((centry->sequence_number == domain->sequence_number)
619 && (centry->timeout > time(NULL)))) {
620 DEBUG(10,("centry_expired: Key %s for domain %s is good.\n",
621 keystr, domain->name ));
622 return false;
625 DEBUG(10,("centry_expired: Key %s for domain %s expired\n",
626 keystr, domain->name ));
628 /* it's expired */
629 return true;
632 static struct cache_entry *wcache_fetch_raw(char *kstr)
634 TDB_DATA data;
635 struct cache_entry *centry;
636 TDB_DATA key;
638 key = string_tdb_data(kstr);
639 data = tdb_fetch_compat(wcache->tdb, key);
640 if (!data.dptr) {
641 /* a cache miss */
642 return NULL;
645 centry = SMB_XMALLOC_P(struct cache_entry);
646 centry->data = (unsigned char *)data.dptr;
647 centry->len = data.dsize;
648 centry->ofs = 0;
650 if (centry->len < 16) {
651 /* huh? corrupt cache? */
652 DEBUG(10,("wcache_fetch_raw: Corrupt cache for key %s "
653 "(len < 16)?\n", kstr));
654 centry_free(centry);
655 return NULL;
658 centry->status = centry_ntstatus(centry);
659 centry->sequence_number = centry_uint32(centry);
660 centry->timeout = centry_uint64_t(centry);
662 return centry;
665 static bool is_my_own_sam_domain(struct winbindd_domain *domain)
667 if (strequal(domain->name, get_global_sam_name()) &&
668 sid_check_is_domain(&domain->sid)) {
669 return true;
672 return false;
675 static bool is_builtin_domain(struct winbindd_domain *domain)
677 if (strequal(domain->name, "BUILTIN") &&
678 sid_check_is_builtin(&domain->sid)) {
679 return true;
682 return false;
686 fetch an entry from the cache, with a varargs key. auto-fetch the sequence
687 number and return status
689 static struct cache_entry *wcache_fetch(struct winbind_cache *cache,
690 struct winbindd_domain *domain,
691 const char *format, ...) PRINTF_ATTRIBUTE(3,4);
692 static struct cache_entry *wcache_fetch(struct winbind_cache *cache,
693 struct winbindd_domain *domain,
694 const char *format, ...)
696 va_list ap;
697 char *kstr;
698 struct cache_entry *centry;
700 if (!winbindd_use_cache() ||
701 is_my_own_sam_domain(domain) ||
702 is_builtin_domain(domain)) {
703 return NULL;
706 refresh_sequence_number(domain, false);
708 va_start(ap, format);
709 smb_xvasprintf(&kstr, format, ap);
710 va_end(ap);
712 centry = wcache_fetch_raw(kstr);
713 if (centry == NULL) {
714 free(kstr);
715 return NULL;
718 if (centry_expired(domain, kstr, centry)) {
720 DEBUG(10,("wcache_fetch: entry %s expired for domain %s\n",
721 kstr, domain->name ));
723 centry_free(centry);
724 free(kstr);
725 return NULL;
728 DEBUG(10,("wcache_fetch: returning entry %s for domain %s\n",
729 kstr, domain->name ));
731 free(kstr);
732 return centry;
735 static void wcache_delete(const char *format, ...) PRINTF_ATTRIBUTE(1,2);
736 static void wcache_delete(const char *format, ...)
738 va_list ap;
739 char *kstr;
740 TDB_DATA key;
742 va_start(ap, format);
743 smb_xvasprintf(&kstr, format, ap);
744 va_end(ap);
746 key = string_tdb_data(kstr);
748 tdb_delete(wcache->tdb, key);
749 free(kstr);
753 make sure we have at least len bytes available in a centry
755 static void centry_expand(struct cache_entry *centry, uint32 len)
757 if (centry->len - centry->ofs >= len)
758 return;
759 centry->len *= 2;
760 centry->data = SMB_REALLOC_ARRAY(centry->data, unsigned char,
761 centry->len);
762 if (!centry->data) {
763 DEBUG(0,("out of memory: needed %d bytes in centry_expand\n", centry->len));
764 smb_panic_fn("out of memory in centry_expand");
769 push a uint64_t into a centry
771 static void centry_put_uint64_t(struct cache_entry *centry, uint64_t v)
773 centry_expand(centry, 8);
774 SBVAL(centry->data, centry->ofs, v);
775 centry->ofs += 8;
779 push a uint32 into a centry
781 static void centry_put_uint32(struct cache_entry *centry, uint32 v)
783 centry_expand(centry, 4);
784 SIVAL(centry->data, centry->ofs, v);
785 centry->ofs += 4;
789 push a uint16 into a centry
791 static void centry_put_uint16(struct cache_entry *centry, uint16 v)
793 centry_expand(centry, 2);
794 SSVAL(centry->data, centry->ofs, v);
795 centry->ofs += 2;
799 push a uint8 into a centry
801 static void centry_put_uint8(struct cache_entry *centry, uint8 v)
803 centry_expand(centry, 1);
804 SCVAL(centry->data, centry->ofs, v);
805 centry->ofs += 1;
809 push a string into a centry
811 static void centry_put_string(struct cache_entry *centry, const char *s)
813 int len;
815 if (!s) {
816 /* null strings are marked as len 0xFFFF */
817 centry_put_uint8(centry, 0xFF);
818 return;
821 len = strlen(s);
822 /* can't handle more than 254 char strings. Truncating is probably best */
823 if (len > 254) {
824 DEBUG(10,("centry_put_string: truncating len (%d) to: 254\n", len));
825 len = 254;
827 centry_put_uint8(centry, len);
828 centry_expand(centry, len);
829 memcpy(centry->data + centry->ofs, s, len);
830 centry->ofs += len;
834 push a 16 byte hash into a centry - treat as 16 byte string.
836 static void centry_put_hash16(struct cache_entry *centry, const uint8 val[16])
838 centry_put_uint8(centry, 16);
839 centry_expand(centry, 16);
840 memcpy(centry->data + centry->ofs, val, 16);
841 centry->ofs += 16;
844 static void centry_put_sid(struct cache_entry *centry, const struct dom_sid *sid)
846 fstring sid_string;
847 centry_put_string(centry, sid_to_fstring(sid_string, sid));
852 put NTSTATUS into a centry
854 static void centry_put_ntstatus(struct cache_entry *centry, NTSTATUS status)
856 uint32 status_value = NT_STATUS_V(status);
857 centry_put_uint32(centry, status_value);
862 push a NTTIME into a centry
864 static void centry_put_nttime(struct cache_entry *centry, NTTIME nt)
866 centry_expand(centry, 8);
867 SIVAL(centry->data, centry->ofs, nt & 0xFFFFFFFF);
868 centry->ofs += 4;
869 SIVAL(centry->data, centry->ofs, nt >> 32);
870 centry->ofs += 4;
874 push a time_t into a centry - use a 64 bit size.
875 NTTIME here is being used as a convenient 64-bit size.
877 static void centry_put_time(struct cache_entry *centry, time_t t)
879 NTTIME nt = (NTTIME)t;
880 centry_put_nttime(centry, nt);
884 start a centry for output. When finished, call centry_end()
886 struct cache_entry *centry_start(struct winbindd_domain *domain, NTSTATUS status)
888 struct cache_entry *centry;
890 if (!wcache->tdb)
891 return NULL;
893 centry = SMB_XMALLOC_P(struct cache_entry);
895 centry->len = 8192; /* reasonable default */
896 centry->data = SMB_XMALLOC_ARRAY(uint8, centry->len);
897 centry->ofs = 0;
898 centry->sequence_number = domain->sequence_number;
899 centry->timeout = lp_winbind_cache_time() + time(NULL);
900 centry_put_ntstatus(centry, status);
901 centry_put_uint32(centry, centry->sequence_number);
902 centry_put_uint64_t(centry, centry->timeout);
903 return centry;
907 finish a centry and write it to the tdb
909 static void centry_end(struct cache_entry *centry, const char *format, ...) PRINTF_ATTRIBUTE(2,3);
910 static void centry_end(struct cache_entry *centry, const char *format, ...)
912 va_list ap;
913 char *kstr;
914 TDB_DATA key, data;
916 if (!winbindd_use_cache()) {
917 return;
920 va_start(ap, format);
921 smb_xvasprintf(&kstr, format, ap);
922 va_end(ap);
924 key = string_tdb_data(kstr);
925 data.dptr = centry->data;
926 data.dsize = centry->ofs;
928 tdb_store(wcache->tdb, key, data, TDB_REPLACE);
929 free(kstr);
932 static void wcache_save_name_to_sid(struct winbindd_domain *domain,
933 NTSTATUS status, const char *domain_name,
934 const char *name, const struct dom_sid *sid,
935 enum lsa_SidType type)
937 struct cache_entry *centry;
938 fstring uname;
940 centry = centry_start(domain, status);
941 if (!centry)
942 return;
943 centry_put_uint32(centry, type);
944 centry_put_sid(centry, sid);
945 fstrcpy(uname, name);
946 strupper_m(uname);
947 centry_end(centry, "NS/%s/%s", domain_name, uname);
948 DEBUG(10,("wcache_save_name_to_sid: %s\\%s -> %s (%s)\n", domain_name,
949 uname, sid_string_dbg(sid), nt_errstr(status)));
950 centry_free(centry);
953 static void wcache_save_sid_to_name(struct winbindd_domain *domain, NTSTATUS status,
954 const struct dom_sid *sid, const char *domain_name, const char *name, enum lsa_SidType type)
956 struct cache_entry *centry;
957 fstring sid_string;
959 centry = centry_start(domain, status);
960 if (!centry)
961 return;
963 if (NT_STATUS_IS_OK(status)) {
964 centry_put_uint32(centry, type);
965 centry_put_string(centry, domain_name);
966 centry_put_string(centry, name);
969 centry_end(centry, "SN/%s", sid_to_fstring(sid_string, sid));
970 DEBUG(10,("wcache_save_sid_to_name: %s -> %s\\%s (%s)\n", sid_string,
971 domain_name, name, nt_errstr(status)));
972 centry_free(centry);
976 static void wcache_save_user(struct winbindd_domain *domain, NTSTATUS status,
977 struct wbint_userinfo *info)
979 struct cache_entry *centry;
980 fstring sid_string;
982 if (is_null_sid(&info->user_sid)) {
983 return;
986 centry = centry_start(domain, status);
987 if (!centry)
988 return;
989 centry_put_string(centry, info->acct_name);
990 centry_put_string(centry, info->full_name);
991 centry_put_string(centry, info->homedir);
992 centry_put_string(centry, info->shell);
993 centry_put_uint32(centry, info->primary_gid);
994 centry_put_sid(centry, &info->user_sid);
995 centry_put_sid(centry, &info->group_sid);
996 centry_end(centry, "U/%s", sid_to_fstring(sid_string,
997 &info->user_sid));
998 DEBUG(10,("wcache_save_user: %s (acct_name %s)\n", sid_string, info->acct_name));
999 centry_free(centry);
1002 static void wcache_save_lockout_policy(struct winbindd_domain *domain,
1003 NTSTATUS status,
1004 struct samr_DomInfo12 *lockout_policy)
1006 struct cache_entry *centry;
1008 centry = centry_start(domain, status);
1009 if (!centry)
1010 return;
1012 centry_put_nttime(centry, lockout_policy->lockout_duration);
1013 centry_put_nttime(centry, lockout_policy->lockout_window);
1014 centry_put_uint16(centry, lockout_policy->lockout_threshold);
1016 centry_end(centry, "LOC_POL/%s", domain->name);
1018 DEBUG(10,("wcache_save_lockout_policy: %s\n", domain->name));
1020 centry_free(centry);
1025 static void wcache_save_password_policy(struct winbindd_domain *domain,
1026 NTSTATUS status,
1027 struct samr_DomInfo1 *policy)
1029 struct cache_entry *centry;
1031 centry = centry_start(domain, status);
1032 if (!centry)
1033 return;
1035 centry_put_uint16(centry, policy->min_password_length);
1036 centry_put_uint16(centry, policy->password_history_length);
1037 centry_put_uint32(centry, policy->password_properties);
1038 centry_put_nttime(centry, policy->max_password_age);
1039 centry_put_nttime(centry, policy->min_password_age);
1041 centry_end(centry, "PWD_POL/%s", domain->name);
1043 DEBUG(10,("wcache_save_password_policy: %s\n", domain->name));
1045 centry_free(centry);
1048 /***************************************************************************
1049 ***************************************************************************/
1051 static void wcache_save_username_alias(struct winbindd_domain *domain,
1052 NTSTATUS status,
1053 const char *name, const char *alias)
1055 struct cache_entry *centry;
1056 fstring uname;
1058 if ( (centry = centry_start(domain, status)) == NULL )
1059 return;
1061 centry_put_string( centry, alias );
1063 fstrcpy(uname, name);
1064 strupper_m(uname);
1065 centry_end(centry, "NSS/NA/%s", uname);
1067 DEBUG(10,("wcache_save_username_alias: %s -> %s\n", name, alias ));
1069 centry_free(centry);
1072 static void wcache_save_alias_username(struct winbindd_domain *domain,
1073 NTSTATUS status,
1074 const char *alias, const char *name)
1076 struct cache_entry *centry;
1077 fstring uname;
1079 if ( (centry = centry_start(domain, status)) == NULL )
1080 return;
1082 centry_put_string( centry, name );
1084 fstrcpy(uname, alias);
1085 strupper_m(uname);
1086 centry_end(centry, "NSS/AN/%s", uname);
1088 DEBUG(10,("wcache_save_alias_username: %s -> %s\n", alias, name ));
1090 centry_free(centry);
1093 /***************************************************************************
1094 ***************************************************************************/
1096 NTSTATUS resolve_username_to_alias( TALLOC_CTX *mem_ctx,
1097 struct winbindd_domain *domain,
1098 const char *name, char **alias )
1100 struct winbind_cache *cache = get_cache(domain);
1101 struct cache_entry *centry = NULL;
1102 NTSTATUS status;
1103 char *upper_name;
1105 if ( domain->internal )
1106 return NT_STATUS_NOT_SUPPORTED;
1108 if (!cache->tdb)
1109 goto do_query;
1111 if ( (upper_name = SMB_STRDUP(name)) == NULL )
1112 return NT_STATUS_NO_MEMORY;
1113 strupper_m(upper_name);
1115 centry = wcache_fetch(cache, domain, "NSS/NA/%s", upper_name);
1117 SAFE_FREE( upper_name );
1119 if (!centry)
1120 goto do_query;
1122 status = centry->status;
1124 if (!NT_STATUS_IS_OK(status)) {
1125 centry_free(centry);
1126 return status;
1129 *alias = centry_string( centry, mem_ctx );
1131 centry_free(centry);
1133 DEBUG(10,("resolve_username_to_alias: [Cached] - mapped %s to %s\n",
1134 name, *alias ? *alias : "(none)"));
1136 return (*alias) ? NT_STATUS_OK : NT_STATUS_OBJECT_NAME_NOT_FOUND;
1138 do_query:
1140 /* If its not in cache and we are offline, then fail */
1142 if ( get_global_winbindd_state_offline() || !domain->online ) {
1143 DEBUG(8,("resolve_username_to_alias: rejecting query "
1144 "in offline mode\n"));
1145 return NT_STATUS_NOT_FOUND;
1148 status = nss_map_to_alias( mem_ctx, domain->name, name, alias );
1150 if ( NT_STATUS_IS_OK( status ) ) {
1151 wcache_save_username_alias(domain, status, name, *alias);
1154 if ( NT_STATUS_EQUAL( status, NT_STATUS_NONE_MAPPED ) ) {
1155 wcache_save_username_alias(domain, status, name, "(NULL)");
1158 DEBUG(5,("resolve_username_to_alias: backend query returned %s\n",
1159 nt_errstr(status)));
1161 if ( NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ) {
1162 set_domain_offline( domain );
1165 return status;
1168 /***************************************************************************
1169 ***************************************************************************/
1171 NTSTATUS resolve_alias_to_username( TALLOC_CTX *mem_ctx,
1172 struct winbindd_domain *domain,
1173 const char *alias, char **name )
1175 struct winbind_cache *cache = get_cache(domain);
1176 struct cache_entry *centry = NULL;
1177 NTSTATUS status;
1178 char *upper_name;
1180 if ( domain->internal )
1181 return NT_STATUS_NOT_SUPPORTED;
1183 if (!cache->tdb)
1184 goto do_query;
1186 if ( (upper_name = SMB_STRDUP(alias)) == NULL )
1187 return NT_STATUS_NO_MEMORY;
1188 strupper_m(upper_name);
1190 centry = wcache_fetch(cache, domain, "NSS/AN/%s", upper_name);
1192 SAFE_FREE( upper_name );
1194 if (!centry)
1195 goto do_query;
1197 status = centry->status;
1199 if (!NT_STATUS_IS_OK(status)) {
1200 centry_free(centry);
1201 return status;
1204 *name = centry_string( centry, mem_ctx );
1206 centry_free(centry);
1208 DEBUG(10,("resolve_alias_to_username: [Cached] - mapped %s to %s\n",
1209 alias, *name ? *name : "(none)"));
1211 return (*name) ? NT_STATUS_OK : NT_STATUS_OBJECT_NAME_NOT_FOUND;
1213 do_query:
1215 /* If its not in cache and we are offline, then fail */
1217 if ( get_global_winbindd_state_offline() || !domain->online ) {
1218 DEBUG(8,("resolve_alias_to_username: rejecting query "
1219 "in offline mode\n"));
1220 return NT_STATUS_NOT_FOUND;
1223 /* an alias cannot contain a domain prefix or '@' */
1225 if (strchr(alias, '\\') || strchr(alias, '@')) {
1226 DEBUG(10,("resolve_alias_to_username: skipping fully "
1227 "qualified name %s\n", alias));
1228 return NT_STATUS_OBJECT_NAME_INVALID;
1231 status = nss_map_from_alias( mem_ctx, domain->name, alias, name );
1233 if ( NT_STATUS_IS_OK( status ) ) {
1234 wcache_save_alias_username( domain, status, alias, *name );
1237 if (NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) {
1238 wcache_save_alias_username(domain, status, alias, "(NULL)");
1241 DEBUG(5,("resolve_alias_to_username: backend query returned %s\n",
1242 nt_errstr(status)));
1244 if ( NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ) {
1245 set_domain_offline( domain );
1248 return status;
1251 NTSTATUS wcache_cached_creds_exist(struct winbindd_domain *domain, const struct dom_sid *sid)
1253 struct winbind_cache *cache = get_cache(domain);
1254 TDB_DATA data;
1255 fstring key_str, tmp;
1256 uint32 rid;
1258 if (!cache->tdb) {
1259 return NT_STATUS_INTERNAL_DB_ERROR;
1262 if (is_null_sid(sid)) {
1263 return NT_STATUS_INVALID_SID;
1266 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1267 return NT_STATUS_INVALID_SID;
1270 fstr_sprintf(key_str, "CRED/%s", sid_to_fstring(tmp, sid));
1272 data = tdb_fetch_compat(cache->tdb, string_tdb_data(key_str));
1273 if (!data.dptr) {
1274 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1277 SAFE_FREE(data.dptr);
1278 return NT_STATUS_OK;
1281 /* Lookup creds for a SID - copes with old (unsalted) creds as well
1282 as new salted ones. */
1284 NTSTATUS wcache_get_creds(struct winbindd_domain *domain,
1285 TALLOC_CTX *mem_ctx,
1286 const struct dom_sid *sid,
1287 const uint8 **cached_nt_pass,
1288 const uint8 **cached_salt)
1290 struct winbind_cache *cache = get_cache(domain);
1291 struct cache_entry *centry = NULL;
1292 NTSTATUS status;
1293 time_t t;
1294 uint32 rid;
1295 fstring tmp;
1297 if (!winbindd_use_cache()) {
1298 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1301 if (!cache->tdb) {
1302 return NT_STATUS_INTERNAL_DB_ERROR;
1305 if (is_null_sid(sid)) {
1306 return NT_STATUS_INVALID_SID;
1309 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1310 return NT_STATUS_INVALID_SID;
1313 /* Try and get a salted cred first. If we can't
1314 fall back to an unsalted cred. */
1316 centry = wcache_fetch(cache, domain, "CRED/%s",
1317 sid_to_fstring(tmp, sid));
1318 if (!centry) {
1319 DEBUG(10,("wcache_get_creds: entry for [CRED/%s] not found\n",
1320 sid_string_dbg(sid)));
1321 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1324 t = centry_time(centry);
1326 /* In the salted case this isn't actually the nt_hash itself,
1327 but the MD5 of the salt + nt_hash. Let the caller
1328 sort this out. It can tell as we only return the cached_salt
1329 if we are returning a salted cred. */
1331 *cached_nt_pass = (const uint8 *)centry_hash16(centry, mem_ctx);
1332 if (*cached_nt_pass == NULL) {
1333 fstring sidstr;
1335 sid_to_fstring(sidstr, sid);
1337 /* Bad (old) cred cache. Delete and pretend we
1338 don't have it. */
1339 DEBUG(0,("wcache_get_creds: bad entry for [CRED/%s] - deleting\n",
1340 sidstr));
1341 wcache_delete("CRED/%s", sidstr);
1342 centry_free(centry);
1343 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1346 /* We only have 17 bytes more data in the salted cred case. */
1347 if (centry->len - centry->ofs == 17) {
1348 *cached_salt = (const uint8 *)centry_hash16(centry, mem_ctx);
1349 } else {
1350 *cached_salt = NULL;
1353 dump_data_pw("cached_nt_pass", *cached_nt_pass, NT_HASH_LEN);
1354 if (*cached_salt) {
1355 dump_data_pw("cached_salt", *cached_salt, NT_HASH_LEN);
1358 status = centry->status;
1360 DEBUG(10,("wcache_get_creds: [Cached] - cached creds for user %s status: %s\n",
1361 sid_string_dbg(sid), nt_errstr(status) ));
1363 centry_free(centry);
1364 return status;
1367 /* Store creds for a SID - only writes out new salted ones. */
1369 NTSTATUS wcache_save_creds(struct winbindd_domain *domain,
1370 const struct dom_sid *sid,
1371 const uint8 nt_pass[NT_HASH_LEN])
1373 struct cache_entry *centry;
1374 fstring sid_string;
1375 uint32 rid;
1376 uint8 cred_salt[NT_HASH_LEN];
1377 uint8 salted_hash[NT_HASH_LEN];
1379 if (is_null_sid(sid)) {
1380 return NT_STATUS_INVALID_SID;
1383 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1384 return NT_STATUS_INVALID_SID;
1387 centry = centry_start(domain, NT_STATUS_OK);
1388 if (!centry) {
1389 return NT_STATUS_INTERNAL_DB_ERROR;
1392 dump_data_pw("nt_pass", nt_pass, NT_HASH_LEN);
1394 centry_put_time(centry, time(NULL));
1396 /* Create a salt and then salt the hash. */
1397 generate_random_buffer(cred_salt, NT_HASH_LEN);
1398 E_md5hash(cred_salt, nt_pass, salted_hash);
1400 centry_put_hash16(centry, salted_hash);
1401 centry_put_hash16(centry, cred_salt);
1402 centry_end(centry, "CRED/%s", sid_to_fstring(sid_string, sid));
1404 DEBUG(10,("wcache_save_creds: %s\n", sid_string));
1406 centry_free(centry);
1408 return NT_STATUS_OK;
1412 /* Query display info. This is the basic user list fn */
1413 static NTSTATUS query_user_list(struct winbindd_domain *domain,
1414 TALLOC_CTX *mem_ctx,
1415 uint32 *num_entries,
1416 struct wbint_userinfo **info)
1418 struct winbind_cache *cache = get_cache(domain);
1419 struct cache_entry *centry = NULL;
1420 NTSTATUS status;
1421 unsigned int i, retry;
1422 bool old_status = domain->online;
1424 if (!cache->tdb)
1425 goto do_query;
1427 centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
1428 if (!centry)
1429 goto do_query;
1431 do_fetch_cache:
1432 *num_entries = centry_uint32(centry);
1434 if (*num_entries == 0)
1435 goto do_cached;
1437 (*info) = talloc_array(mem_ctx, struct wbint_userinfo, *num_entries);
1438 if (! (*info)) {
1439 smb_panic_fn("query_user_list out of memory");
1441 for (i=0; i<(*num_entries); i++) {
1442 (*info)[i].acct_name = centry_string(centry, mem_ctx);
1443 (*info)[i].full_name = centry_string(centry, mem_ctx);
1444 (*info)[i].homedir = centry_string(centry, mem_ctx);
1445 (*info)[i].shell = centry_string(centry, mem_ctx);
1446 centry_sid(centry, &(*info)[i].user_sid);
1447 centry_sid(centry, &(*info)[i].group_sid);
1450 do_cached:
1451 status = centry->status;
1453 DEBUG(10,("query_user_list: [Cached] - cached list for domain %s status: %s\n",
1454 domain->name, nt_errstr(status) ));
1456 centry_free(centry);
1457 return status;
1459 do_query:
1460 *num_entries = 0;
1461 *info = NULL;
1463 /* Return status value returned by seq number check */
1465 if (!NT_STATUS_IS_OK(domain->last_status))
1466 return domain->last_status;
1468 /* Put the query_user_list() in a retry loop. There appears to be
1469 * some bug either with Windows 2000 or Samba's handling of large
1470 * rpc replies. This manifests itself as sudden disconnection
1471 * at a random point in the enumeration of a large (60k) user list.
1472 * The retry loop simply tries the operation again. )-: It's not
1473 * pretty but an acceptable workaround until we work out what the
1474 * real problem is. */
1476 retry = 0;
1477 do {
1479 DEBUG(10,("query_user_list: [Cached] - doing backend query for list for domain %s\n",
1480 domain->name ));
1482 status = domain->backend->query_user_list(domain, mem_ctx, num_entries, info);
1483 if (!NT_STATUS_IS_OK(status)) {
1484 DEBUG(3, ("query_user_list: returned 0x%08x, "
1485 "retrying\n", NT_STATUS_V(status)));
1487 if (NT_STATUS_EQUAL(status, NT_STATUS_UNSUCCESSFUL)) {
1488 DEBUG(3, ("query_user_list: flushing "
1489 "connection cache\n"));
1490 invalidate_cm_connection(&domain->conn);
1492 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1493 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1494 if (!domain->internal && old_status) {
1495 set_domain_offline(domain);
1497 /* store partial response. */
1498 if (*num_entries > 0) {
1500 * humm, what about the status used for cache?
1501 * Should it be NT_STATUS_OK?
1503 break;
1506 * domain is offline now, and there is no user entries,
1507 * try to fetch from cache again.
1509 if (cache->tdb && !domain->online && !domain->internal && old_status) {
1510 centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
1511 /* partial response... */
1512 if (!centry) {
1513 goto skip_save;
1514 } else {
1515 goto do_fetch_cache;
1517 } else {
1518 goto skip_save;
1522 } while (NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_UNSUCCESSFUL) &&
1523 (retry++ < 5));
1525 /* and save it */
1526 refresh_sequence_number(domain, false);
1527 if (!NT_STATUS_IS_OK(status)) {
1528 return status;
1530 centry = centry_start(domain, status);
1531 if (!centry)
1532 goto skip_save;
1533 centry_put_uint32(centry, *num_entries);
1534 for (i=0; i<(*num_entries); i++) {
1535 centry_put_string(centry, (*info)[i].acct_name);
1536 centry_put_string(centry, (*info)[i].full_name);
1537 centry_put_string(centry, (*info)[i].homedir);
1538 centry_put_string(centry, (*info)[i].shell);
1539 centry_put_sid(centry, &(*info)[i].user_sid);
1540 centry_put_sid(centry, &(*info)[i].group_sid);
1541 if (domain->backend && domain->backend->consistent) {
1542 /* when the backend is consistent we can pre-prime some mappings */
1543 wcache_save_name_to_sid(domain, NT_STATUS_OK,
1544 domain->name,
1545 (*info)[i].acct_name,
1546 &(*info)[i].user_sid,
1547 SID_NAME_USER);
1548 wcache_save_sid_to_name(domain, NT_STATUS_OK,
1549 &(*info)[i].user_sid,
1550 domain->name,
1551 (*info)[i].acct_name,
1552 SID_NAME_USER);
1553 wcache_save_user(domain, NT_STATUS_OK, &(*info)[i]);
1556 centry_end(centry, "UL/%s", domain->name);
1557 centry_free(centry);
1559 skip_save:
1560 return status;
1563 /* list all domain groups */
1564 static NTSTATUS enum_dom_groups(struct winbindd_domain *domain,
1565 TALLOC_CTX *mem_ctx,
1566 uint32 *num_entries,
1567 struct wb_acct_info **info)
1569 struct winbind_cache *cache = get_cache(domain);
1570 struct cache_entry *centry = NULL;
1571 NTSTATUS status;
1572 unsigned int i;
1573 bool old_status;
1575 old_status = domain->online;
1576 if (!cache->tdb)
1577 goto do_query;
1579 centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
1580 if (!centry)
1581 goto do_query;
1583 do_fetch_cache:
1584 *num_entries = centry_uint32(centry);
1586 if (*num_entries == 0)
1587 goto do_cached;
1589 (*info) = talloc_array(mem_ctx, struct wb_acct_info, *num_entries);
1590 if (! (*info)) {
1591 smb_panic_fn("enum_dom_groups out of memory");
1593 for (i=0; i<(*num_entries); i++) {
1594 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1595 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1596 (*info)[i].rid = centry_uint32(centry);
1599 do_cached:
1600 status = centry->status;
1602 DEBUG(10,("enum_dom_groups: [Cached] - cached list for domain %s status: %s\n",
1603 domain->name, nt_errstr(status) ));
1605 centry_free(centry);
1606 return status;
1608 do_query:
1609 *num_entries = 0;
1610 *info = NULL;
1612 /* Return status value returned by seq number check */
1614 if (!NT_STATUS_IS_OK(domain->last_status))
1615 return domain->last_status;
1617 DEBUG(10,("enum_dom_groups: [Cached] - doing backend query for list for domain %s\n",
1618 domain->name ));
1620 status = domain->backend->enum_dom_groups(domain, mem_ctx, num_entries, info);
1622 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1623 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1624 if (!domain->internal && old_status) {
1625 set_domain_offline(domain);
1627 if (cache->tdb &&
1628 !domain->online &&
1629 !domain->internal &&
1630 old_status) {
1631 centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
1632 if (centry) {
1633 goto do_fetch_cache;
1637 /* and save it */
1638 refresh_sequence_number(domain, false);
1639 if (!NT_STATUS_IS_OK(status)) {
1640 return status;
1642 centry = centry_start(domain, status);
1643 if (!centry)
1644 goto skip_save;
1645 centry_put_uint32(centry, *num_entries);
1646 for (i=0; i<(*num_entries); i++) {
1647 centry_put_string(centry, (*info)[i].acct_name);
1648 centry_put_string(centry, (*info)[i].acct_desc);
1649 centry_put_uint32(centry, (*info)[i].rid);
1651 centry_end(centry, "GL/%s/domain", domain->name);
1652 centry_free(centry);
1654 skip_save:
1655 return status;
1658 /* list all domain groups */
1659 static NTSTATUS enum_local_groups(struct winbindd_domain *domain,
1660 TALLOC_CTX *mem_ctx,
1661 uint32 *num_entries,
1662 struct wb_acct_info **info)
1664 struct winbind_cache *cache = get_cache(domain);
1665 struct cache_entry *centry = NULL;
1666 NTSTATUS status;
1667 unsigned int i;
1668 bool old_status;
1670 old_status = domain->online;
1671 if (!cache->tdb)
1672 goto do_query;
1674 centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
1675 if (!centry)
1676 goto do_query;
1678 do_fetch_cache:
1679 *num_entries = centry_uint32(centry);
1681 if (*num_entries == 0)
1682 goto do_cached;
1684 (*info) = talloc_array(mem_ctx, struct wb_acct_info, *num_entries);
1685 if (! (*info)) {
1686 smb_panic_fn("enum_dom_groups out of memory");
1688 for (i=0; i<(*num_entries); i++) {
1689 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1690 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1691 (*info)[i].rid = centry_uint32(centry);
1694 do_cached:
1696 /* If we are returning cached data and the domain controller
1697 is down then we don't know whether the data is up to date
1698 or not. Return NT_STATUS_MORE_PROCESSING_REQUIRED to
1699 indicate this. */
1701 if (wcache_server_down(domain)) {
1702 DEBUG(10, ("enum_local_groups: returning cached user list and server was down\n"));
1703 status = NT_STATUS_MORE_PROCESSING_REQUIRED;
1704 } else
1705 status = centry->status;
1707 DEBUG(10,("enum_local_groups: [Cached] - cached list for domain %s status: %s\n",
1708 domain->name, nt_errstr(status) ));
1710 centry_free(centry);
1711 return status;
1713 do_query:
1714 *num_entries = 0;
1715 *info = NULL;
1717 /* Return status value returned by seq number check */
1719 if (!NT_STATUS_IS_OK(domain->last_status))
1720 return domain->last_status;
1722 DEBUG(10,("enum_local_groups: [Cached] - doing backend query for list for domain %s\n",
1723 domain->name ));
1725 status = domain->backend->enum_local_groups(domain, mem_ctx, num_entries, info);
1727 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1728 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1729 if (!domain->internal && old_status) {
1730 set_domain_offline(domain);
1732 if (cache->tdb &&
1733 !domain->internal &&
1734 !domain->online &&
1735 old_status) {
1736 centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
1737 if (centry) {
1738 goto do_fetch_cache;
1742 /* and save it */
1743 refresh_sequence_number(domain, false);
1744 if (!NT_STATUS_IS_OK(status)) {
1745 return status;
1747 centry = centry_start(domain, status);
1748 if (!centry)
1749 goto skip_save;
1750 centry_put_uint32(centry, *num_entries);
1751 for (i=0; i<(*num_entries); i++) {
1752 centry_put_string(centry, (*info)[i].acct_name);
1753 centry_put_string(centry, (*info)[i].acct_desc);
1754 centry_put_uint32(centry, (*info)[i].rid);
1756 centry_end(centry, "GL/%s/local", domain->name);
1757 centry_free(centry);
1759 skip_save:
1760 return status;
1763 NTSTATUS wcache_name_to_sid(struct winbindd_domain *domain,
1764 const char *domain_name,
1765 const char *name,
1766 struct dom_sid *sid,
1767 enum lsa_SidType *type)
1769 struct winbind_cache *cache = get_cache(domain);
1770 struct cache_entry *centry;
1771 NTSTATUS status;
1772 char *uname;
1774 if (cache->tdb == NULL) {
1775 return NT_STATUS_NOT_FOUND;
1778 uname = talloc_strdup_upper(talloc_tos(), name);
1779 if (uname == NULL) {
1780 return NT_STATUS_NO_MEMORY;
1783 centry = wcache_fetch(cache, domain, "NS/%s/%s", domain_name, uname);
1784 TALLOC_FREE(uname);
1785 if (centry == NULL) {
1786 return NT_STATUS_NOT_FOUND;
1789 status = centry->status;
1790 if (NT_STATUS_IS_OK(status)) {
1791 *type = (enum lsa_SidType)centry_uint32(centry);
1792 centry_sid(centry, sid);
1795 DEBUG(10,("name_to_sid: [Cached] - cached name for domain %s status: "
1796 "%s\n", domain->name, nt_errstr(status) ));
1798 centry_free(centry);
1799 return status;
1802 /* convert a single name to a sid in a domain */
1803 static NTSTATUS name_to_sid(struct winbindd_domain *domain,
1804 TALLOC_CTX *mem_ctx,
1805 const char *domain_name,
1806 const char *name,
1807 uint32_t flags,
1808 struct dom_sid *sid,
1809 enum lsa_SidType *type)
1811 NTSTATUS status;
1812 bool old_status;
1814 old_status = domain->online;
1816 status = wcache_name_to_sid(domain, domain_name, name, sid, type);
1817 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
1818 return status;
1821 ZERO_STRUCTP(sid);
1823 /* If the seq number check indicated that there is a problem
1824 * with this DC, then return that status... except for
1825 * access_denied. This is special because the dc may be in
1826 * "restrict anonymous = 1" mode, in which case it will deny
1827 * most unauthenticated operations, but *will* allow the LSA
1828 * name-to-sid that we try as a fallback. */
1830 if (!(NT_STATUS_IS_OK(domain->last_status)
1831 || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1832 return domain->last_status;
1834 DEBUG(10,("name_to_sid: [Cached] - doing backend query for name for domain %s\n",
1835 domain->name ));
1837 status = domain->backend->name_to_sid(domain, mem_ctx, domain_name,
1838 name, flags, sid, type);
1840 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1841 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1842 if (!domain->internal && old_status) {
1843 set_domain_offline(domain);
1845 if (!domain->internal &&
1846 !domain->online &&
1847 old_status) {
1848 NTSTATUS cache_status;
1849 cache_status = wcache_name_to_sid(domain, domain_name, name, sid, type);
1850 return cache_status;
1853 /* and save it */
1854 refresh_sequence_number(domain, false);
1856 if (domain->online &&
1857 (NT_STATUS_IS_OK(status) || NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED))) {
1858 wcache_save_name_to_sid(domain, status, domain_name, name, sid, *type);
1860 /* Only save the reverse mapping if this was not a UPN */
1861 if (!strchr(name, '@')) {
1862 strupper_m(discard_const_p(char, domain_name));
1863 strlower_m(discard_const_p(char, name));
1864 wcache_save_sid_to_name(domain, status, sid, domain_name, name, *type);
1868 return status;
1871 NTSTATUS wcache_sid_to_name(struct winbindd_domain *domain,
1872 const struct dom_sid *sid,
1873 TALLOC_CTX *mem_ctx,
1874 char **domain_name,
1875 char **name,
1876 enum lsa_SidType *type)
1878 struct winbind_cache *cache = get_cache(domain);
1879 struct cache_entry *centry;
1880 char *sid_string;
1881 NTSTATUS status;
1883 if (cache->tdb == NULL) {
1884 return NT_STATUS_NOT_FOUND;
1887 sid_string = sid_string_tos(sid);
1888 if (sid_string == NULL) {
1889 return NT_STATUS_NO_MEMORY;
1892 centry = wcache_fetch(cache, domain, "SN/%s", sid_string);
1893 TALLOC_FREE(sid_string);
1894 if (centry == NULL) {
1895 return NT_STATUS_NOT_FOUND;
1898 if (NT_STATUS_IS_OK(centry->status)) {
1899 *type = (enum lsa_SidType)centry_uint32(centry);
1900 *domain_name = centry_string(centry, mem_ctx);
1901 *name = centry_string(centry, mem_ctx);
1904 status = centry->status;
1905 centry_free(centry);
1907 DEBUG(10,("sid_to_name: [Cached] - cached name for domain %s status: "
1908 "%s\n", domain->name, nt_errstr(status) ));
1910 return status;
1913 /* convert a sid to a user or group name. The sid is guaranteed to be in the domain
1914 given */
1915 static NTSTATUS sid_to_name(struct winbindd_domain *domain,
1916 TALLOC_CTX *mem_ctx,
1917 const struct dom_sid *sid,
1918 char **domain_name,
1919 char **name,
1920 enum lsa_SidType *type)
1922 NTSTATUS status;
1923 bool old_status;
1925 old_status = domain->online;
1926 status = wcache_sid_to_name(domain, sid, mem_ctx, domain_name, name,
1927 type);
1928 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
1929 return status;
1932 *name = NULL;
1933 *domain_name = NULL;
1935 /* If the seq number check indicated that there is a problem
1936 * with this DC, then return that status... except for
1937 * access_denied. This is special because the dc may be in
1938 * "restrict anonymous = 1" mode, in which case it will deny
1939 * most unauthenticated operations, but *will* allow the LSA
1940 * sid-to-name that we try as a fallback. */
1942 if (!(NT_STATUS_IS_OK(domain->last_status)
1943 || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1944 return domain->last_status;
1946 DEBUG(10,("sid_to_name: [Cached] - doing backend query for name for domain %s\n",
1947 domain->name ));
1949 status = domain->backend->sid_to_name(domain, mem_ctx, sid, domain_name, name, type);
1951 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1952 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1953 if (!domain->internal && old_status) {
1954 set_domain_offline(domain);
1956 if (!domain->internal &&
1957 !domain->online &&
1958 old_status) {
1959 NTSTATUS cache_status;
1960 cache_status = wcache_sid_to_name(domain, sid, mem_ctx,
1961 domain_name, name, type);
1962 return cache_status;
1965 /* and save it */
1966 refresh_sequence_number(domain, false);
1967 if (!NT_STATUS_IS_OK(status)) {
1968 return status;
1970 wcache_save_sid_to_name(domain, status, sid, *domain_name, *name, *type);
1972 /* We can't save the name to sid mapping here, as with sid history a
1973 * later name2sid would give the wrong sid. */
1975 return status;
1978 static NTSTATUS rids_to_names(struct winbindd_domain *domain,
1979 TALLOC_CTX *mem_ctx,
1980 const struct dom_sid *domain_sid,
1981 uint32 *rids,
1982 size_t num_rids,
1983 char **domain_name,
1984 char ***names,
1985 enum lsa_SidType **types)
1987 struct winbind_cache *cache = get_cache(domain);
1988 size_t i;
1989 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
1990 bool have_mapped;
1991 bool have_unmapped;
1992 bool old_status;
1994 old_status = domain->online;
1995 *domain_name = NULL;
1996 *names = NULL;
1997 *types = NULL;
1999 if (!cache->tdb) {
2000 goto do_query;
2003 if (num_rids == 0) {
2004 return NT_STATUS_OK;
2007 *names = talloc_array(mem_ctx, char *, num_rids);
2008 *types = talloc_array(mem_ctx, enum lsa_SidType, num_rids);
2010 if ((*names == NULL) || (*types == NULL)) {
2011 result = NT_STATUS_NO_MEMORY;
2012 goto error;
2015 have_mapped = have_unmapped = false;
2017 for (i=0; i<num_rids; i++) {
2018 struct dom_sid sid;
2019 struct cache_entry *centry;
2020 fstring tmp;
2022 if (!sid_compose(&sid, domain_sid, rids[i])) {
2023 result = NT_STATUS_INTERNAL_ERROR;
2024 goto error;
2027 centry = wcache_fetch(cache, domain, "SN/%s",
2028 sid_to_fstring(tmp, &sid));
2029 if (!centry) {
2030 goto do_query;
2033 (*types)[i] = SID_NAME_UNKNOWN;
2034 (*names)[i] = talloc_strdup(*names, "");
2036 if (NT_STATUS_IS_OK(centry->status)) {
2037 char *dom;
2038 have_mapped = true;
2039 (*types)[i] = (enum lsa_SidType)centry_uint32(centry);
2041 dom = centry_string(centry, mem_ctx);
2042 if (*domain_name == NULL) {
2043 *domain_name = dom;
2044 } else {
2045 talloc_free(dom);
2048 (*names)[i] = centry_string(centry, *names);
2050 } else if (NT_STATUS_EQUAL(centry->status, NT_STATUS_NONE_MAPPED)
2051 || NT_STATUS_EQUAL(centry->status, STATUS_SOME_UNMAPPED)) {
2052 have_unmapped = true;
2054 } else {
2055 /* something's definitely wrong */
2056 result = centry->status;
2057 goto error;
2060 centry_free(centry);
2063 if (!have_mapped) {
2064 return NT_STATUS_NONE_MAPPED;
2066 if (!have_unmapped) {
2067 return NT_STATUS_OK;
2069 return STATUS_SOME_UNMAPPED;
2071 do_query:
2073 TALLOC_FREE(*names);
2074 TALLOC_FREE(*types);
2076 result = domain->backend->rids_to_names(domain, mem_ctx, domain_sid,
2077 rids, num_rids, domain_name,
2078 names, types);
2080 if (NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
2081 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2082 if (!domain->internal && old_status) {
2083 set_domain_offline(domain);
2085 if (cache->tdb &&
2086 !domain->internal &&
2087 !domain->online &&
2088 old_status) {
2089 have_mapped = have_unmapped = false;
2091 for (i=0; i<num_rids; i++) {
2092 struct dom_sid sid;
2093 struct cache_entry *centry;
2094 fstring tmp;
2096 if (!sid_compose(&sid, domain_sid, rids[i])) {
2097 result = NT_STATUS_INTERNAL_ERROR;
2098 goto error;
2101 centry = wcache_fetch(cache, domain, "SN/%s",
2102 sid_to_fstring(tmp, &sid));
2103 if (!centry) {
2104 (*types)[i] = SID_NAME_UNKNOWN;
2105 (*names)[i] = talloc_strdup(*names, "");
2106 continue;
2109 (*types)[i] = SID_NAME_UNKNOWN;
2110 (*names)[i] = talloc_strdup(*names, "");
2112 if (NT_STATUS_IS_OK(centry->status)) {
2113 char *dom;
2114 have_mapped = true;
2115 (*types)[i] = (enum lsa_SidType)centry_uint32(centry);
2117 dom = centry_string(centry, mem_ctx);
2118 if (*domain_name == NULL) {
2119 *domain_name = dom;
2120 } else {
2121 talloc_free(dom);
2124 (*names)[i] = centry_string(centry, *names);
2126 } else if (NT_STATUS_EQUAL(centry->status, NT_STATUS_NONE_MAPPED)) {
2127 have_unmapped = true;
2129 } else {
2130 /* something's definitely wrong */
2131 result = centry->status;
2132 goto error;
2135 centry_free(centry);
2138 if (!have_mapped) {
2139 return NT_STATUS_NONE_MAPPED;
2141 if (!have_unmapped) {
2142 return NT_STATUS_OK;
2144 return STATUS_SOME_UNMAPPED;
2148 None of the queried rids has been found so save all negative entries
2150 if (NT_STATUS_EQUAL(result, NT_STATUS_NONE_MAPPED)) {
2151 for (i = 0; i < num_rids; i++) {
2152 struct dom_sid sid;
2153 const char *name = "";
2154 const enum lsa_SidType type = SID_NAME_UNKNOWN;
2155 NTSTATUS status = NT_STATUS_NONE_MAPPED;
2157 if (!sid_compose(&sid, domain_sid, rids[i])) {
2158 return NT_STATUS_INTERNAL_ERROR;
2161 wcache_save_sid_to_name(domain, status, &sid, *domain_name,
2162 name, type);
2165 return result;
2169 Some or all of the queried rids have been found.
2171 if (!NT_STATUS_IS_OK(result) &&
2172 !NT_STATUS_EQUAL(result, STATUS_SOME_UNMAPPED)) {
2173 return result;
2176 refresh_sequence_number(domain, false);
2178 for (i=0; i<num_rids; i++) {
2179 struct dom_sid sid;
2180 NTSTATUS status;
2182 if (!sid_compose(&sid, domain_sid, rids[i])) {
2183 result = NT_STATUS_INTERNAL_ERROR;
2184 goto error;
2187 status = (*types)[i] == SID_NAME_UNKNOWN ?
2188 NT_STATUS_NONE_MAPPED : NT_STATUS_OK;
2190 wcache_save_sid_to_name(domain, status, &sid, *domain_name,
2191 (*names)[i], (*types)[i]);
2194 return result;
2196 error:
2197 TALLOC_FREE(*names);
2198 TALLOC_FREE(*types);
2199 return result;
2202 NTSTATUS wcache_query_user(struct winbindd_domain *domain,
2203 TALLOC_CTX *mem_ctx,
2204 const struct dom_sid *user_sid,
2205 struct wbint_userinfo *info)
2207 struct winbind_cache *cache = get_cache(domain);
2208 struct cache_entry *centry = NULL;
2209 NTSTATUS status;
2210 char *sid_string;
2212 if (cache->tdb == NULL) {
2213 return NT_STATUS_NOT_FOUND;
2216 sid_string = sid_string_tos(user_sid);
2217 if (sid_string == NULL) {
2218 return NT_STATUS_NO_MEMORY;
2221 centry = wcache_fetch(cache, domain, "U/%s", sid_string);
2222 TALLOC_FREE(sid_string);
2223 if (centry == NULL) {
2224 return NT_STATUS_NOT_FOUND;
2228 * If we have an access denied cache entry and a cached info3
2229 * in the samlogon cache then do a query. This will force the
2230 * rpc back end to return the info3 data.
2233 if (NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED) &&
2234 netsamlogon_cache_have(user_sid)) {
2235 DEBUG(10, ("query_user: cached access denied and have cached "
2236 "info3\n"));
2237 domain->last_status = NT_STATUS_OK;
2238 centry_free(centry);
2239 return NT_STATUS_NOT_FOUND;
2242 /* if status is not ok then this is a negative hit
2243 and the rest of the data doesn't matter */
2244 status = centry->status;
2245 if (NT_STATUS_IS_OK(status)) {
2246 info->acct_name = centry_string(centry, mem_ctx);
2247 info->full_name = centry_string(centry, mem_ctx);
2248 info->homedir = centry_string(centry, mem_ctx);
2249 info->shell = centry_string(centry, mem_ctx);
2250 info->primary_gid = centry_uint32(centry);
2251 centry_sid(centry, &info->user_sid);
2252 centry_sid(centry, &info->group_sid);
2255 DEBUG(10,("query_user: [Cached] - cached info for domain %s status: "
2256 "%s\n", domain->name, nt_errstr(status) ));
2258 centry_free(centry);
2259 return status;
2262 /* Lookup user information from a rid */
2263 static NTSTATUS query_user(struct winbindd_domain *domain,
2264 TALLOC_CTX *mem_ctx,
2265 const struct dom_sid *user_sid,
2266 struct wbint_userinfo *info)
2268 NTSTATUS status;
2269 bool old_status;
2271 old_status = domain->online;
2272 status = wcache_query_user(domain, mem_ctx, user_sid, info);
2273 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2274 return status;
2277 ZERO_STRUCTP(info);
2279 /* Return status value returned by seq number check */
2281 if (!NT_STATUS_IS_OK(domain->last_status))
2282 return domain->last_status;
2284 DEBUG(10,("query_user: [Cached] - doing backend query for info for domain %s\n",
2285 domain->name ));
2287 status = domain->backend->query_user(domain, mem_ctx, user_sid, info);
2289 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2290 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2291 if (!domain->internal && old_status) {
2292 set_domain_offline(domain);
2294 if (!domain->internal &&
2295 !domain->online &&
2296 old_status) {
2297 NTSTATUS cache_status;
2298 cache_status = wcache_query_user(domain, mem_ctx, user_sid, info);
2299 return cache_status;
2302 /* and save it */
2303 refresh_sequence_number(domain, false);
2304 if (!NT_STATUS_IS_OK(status)) {
2305 return status;
2307 wcache_save_user(domain, status, info);
2309 return status;
2312 NTSTATUS wcache_lookup_usergroups(struct winbindd_domain *domain,
2313 TALLOC_CTX *mem_ctx,
2314 const struct dom_sid *user_sid,
2315 uint32_t *pnum_sids,
2316 struct dom_sid **psids)
2318 struct winbind_cache *cache = get_cache(domain);
2319 struct cache_entry *centry = NULL;
2320 NTSTATUS status;
2321 uint32_t i, num_sids;
2322 struct dom_sid *sids;
2323 fstring sid_string;
2325 if (cache->tdb == NULL) {
2326 return NT_STATUS_NOT_FOUND;
2329 centry = wcache_fetch(cache, domain, "UG/%s",
2330 sid_to_fstring(sid_string, user_sid));
2331 if (centry == NULL) {
2332 return NT_STATUS_NOT_FOUND;
2335 /* If we have an access denied cache entry and a cached info3 in the
2336 samlogon cache then do a query. This will force the rpc back end
2337 to return the info3 data. */
2339 if (NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)
2340 && netsamlogon_cache_have(user_sid)) {
2341 DEBUG(10, ("lookup_usergroups: cached access denied and have "
2342 "cached info3\n"));
2343 domain->last_status = NT_STATUS_OK;
2344 centry_free(centry);
2345 return NT_STATUS_NOT_FOUND;
2348 num_sids = centry_uint32(centry);
2349 sids = talloc_array(mem_ctx, struct dom_sid, num_sids);
2350 if (sids == NULL) {
2351 centry_free(centry);
2352 return NT_STATUS_NO_MEMORY;
2355 for (i=0; i<num_sids; i++) {
2356 centry_sid(centry, &sids[i]);
2359 status = centry->status;
2361 DEBUG(10,("lookup_usergroups: [Cached] - cached info for domain %s "
2362 "status: %s\n", domain->name, nt_errstr(status)));
2364 centry_free(centry);
2366 *pnum_sids = num_sids;
2367 *psids = sids;
2368 return status;
2371 /* Lookup groups a user is a member of. */
2372 static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
2373 TALLOC_CTX *mem_ctx,
2374 const struct dom_sid *user_sid,
2375 uint32 *num_groups, struct dom_sid **user_gids)
2377 struct cache_entry *centry = NULL;
2378 NTSTATUS status;
2379 unsigned int i;
2380 fstring sid_string;
2381 bool old_status;
2383 old_status = domain->online;
2384 status = wcache_lookup_usergroups(domain, mem_ctx, user_sid,
2385 num_groups, user_gids);
2386 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2387 return status;
2390 (*num_groups) = 0;
2391 (*user_gids) = NULL;
2393 /* Return status value returned by seq number check */
2395 if (!NT_STATUS_IS_OK(domain->last_status))
2396 return domain->last_status;
2398 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info for domain %s\n",
2399 domain->name ));
2401 status = domain->backend->lookup_usergroups(domain, mem_ctx, user_sid, num_groups, user_gids);
2403 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2404 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2405 if (!domain->internal && old_status) {
2406 set_domain_offline(domain);
2408 if (!domain->internal &&
2409 !domain->online &&
2410 old_status) {
2411 NTSTATUS cache_status;
2412 cache_status = wcache_lookup_usergroups(domain, mem_ctx, user_sid,
2413 num_groups, user_gids);
2414 return cache_status;
2417 if ( NT_STATUS_EQUAL(status, NT_STATUS_SYNCHRONIZATION_REQUIRED) )
2418 goto skip_save;
2420 /* and save it */
2421 refresh_sequence_number(domain, false);
2422 if (!NT_STATUS_IS_OK(status)) {
2423 return status;
2425 centry = centry_start(domain, status);
2426 if (!centry)
2427 goto skip_save;
2429 centry_put_uint32(centry, *num_groups);
2430 for (i=0; i<(*num_groups); i++) {
2431 centry_put_sid(centry, &(*user_gids)[i]);
2434 centry_end(centry, "UG/%s", sid_to_fstring(sid_string, user_sid));
2435 centry_free(centry);
2437 skip_save:
2438 return status;
2441 static char *wcache_make_sidlist(TALLOC_CTX *mem_ctx, uint32_t num_sids,
2442 const struct dom_sid *sids)
2444 uint32_t i;
2445 char *sidlist;
2447 sidlist = talloc_strdup(mem_ctx, "");
2448 if (sidlist == NULL) {
2449 return NULL;
2451 for (i=0; i<num_sids; i++) {
2452 fstring tmp;
2453 sidlist = talloc_asprintf_append_buffer(
2454 sidlist, "/%s", sid_to_fstring(tmp, &sids[i]));
2455 if (sidlist == NULL) {
2456 return NULL;
2459 return sidlist;
2462 NTSTATUS wcache_lookup_useraliases(struct winbindd_domain *domain,
2463 TALLOC_CTX *mem_ctx, uint32_t num_sids,
2464 const struct dom_sid *sids,
2465 uint32_t *pnum_aliases, uint32_t **paliases)
2467 struct winbind_cache *cache = get_cache(domain);
2468 struct cache_entry *centry = NULL;
2469 uint32_t num_aliases;
2470 uint32_t *aliases;
2471 NTSTATUS status;
2472 char *sidlist;
2473 int i;
2475 if (cache->tdb == NULL) {
2476 return NT_STATUS_NOT_FOUND;
2479 if (num_sids == 0) {
2480 *pnum_aliases = 0;
2481 *paliases = NULL;
2482 return NT_STATUS_OK;
2485 /* We need to cache indexed by the whole list of SIDs, the aliases
2486 * resulting might come from any of the SIDs. */
2488 sidlist = wcache_make_sidlist(talloc_tos(), num_sids, sids);
2489 if (sidlist == NULL) {
2490 return NT_STATUS_NO_MEMORY;
2493 centry = wcache_fetch(cache, domain, "UA%s", sidlist);
2494 TALLOC_FREE(sidlist);
2495 if (centry == NULL) {
2496 return NT_STATUS_NOT_FOUND;
2499 num_aliases = centry_uint32(centry);
2500 aliases = talloc_array(mem_ctx, uint32_t, num_aliases);
2501 if (aliases == NULL) {
2502 centry_free(centry);
2503 return NT_STATUS_NO_MEMORY;
2506 for (i=0; i<num_aliases; i++) {
2507 aliases[i] = centry_uint32(centry);
2510 status = centry->status;
2512 DEBUG(10,("lookup_useraliases: [Cached] - cached info for domain: %s "
2513 "status %s\n", domain->name, nt_errstr(status)));
2515 centry_free(centry);
2517 *pnum_aliases = num_aliases;
2518 *paliases = aliases;
2520 return status;
2523 static NTSTATUS lookup_useraliases(struct winbindd_domain *domain,
2524 TALLOC_CTX *mem_ctx,
2525 uint32 num_sids, const struct dom_sid *sids,
2526 uint32 *num_aliases, uint32 **alias_rids)
2528 struct cache_entry *centry = NULL;
2529 NTSTATUS status;
2530 char *sidlist;
2531 int i;
2532 bool old_status;
2534 old_status = domain->online;
2535 status = wcache_lookup_useraliases(domain, mem_ctx, num_sids, sids,
2536 num_aliases, alias_rids);
2537 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2538 return status;
2541 (*num_aliases) = 0;
2542 (*alias_rids) = NULL;
2544 if (!NT_STATUS_IS_OK(domain->last_status))
2545 return domain->last_status;
2547 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info "
2548 "for domain %s\n", domain->name ));
2550 sidlist = wcache_make_sidlist(talloc_tos(), num_sids, sids);
2551 if (sidlist == NULL) {
2552 return NT_STATUS_NO_MEMORY;
2555 status = domain->backend->lookup_useraliases(domain, mem_ctx,
2556 num_sids, sids,
2557 num_aliases, alias_rids);
2559 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2560 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2561 if (!domain->internal && old_status) {
2562 set_domain_offline(domain);
2564 if (!domain->internal &&
2565 !domain->online &&
2566 old_status) {
2567 NTSTATUS cache_status;
2568 cache_status = wcache_lookup_useraliases(domain, mem_ctx, num_sids,
2569 sids, num_aliases, alias_rids);
2570 return cache_status;
2573 /* and save it */
2574 refresh_sequence_number(domain, false);
2575 if (!NT_STATUS_IS_OK(status)) {
2576 return status;
2578 centry = centry_start(domain, status);
2579 if (!centry)
2580 goto skip_save;
2581 centry_put_uint32(centry, *num_aliases);
2582 for (i=0; i<(*num_aliases); i++)
2583 centry_put_uint32(centry, (*alias_rids)[i]);
2584 centry_end(centry, "UA%s", sidlist);
2585 centry_free(centry);
2587 skip_save:
2588 return status;
2591 NTSTATUS wcache_lookup_groupmem(struct winbindd_domain *domain,
2592 TALLOC_CTX *mem_ctx,
2593 const struct dom_sid *group_sid,
2594 uint32_t *num_names,
2595 struct dom_sid **sid_mem, char ***names,
2596 uint32_t **name_types)
2598 struct winbind_cache *cache = get_cache(domain);
2599 struct cache_entry *centry = NULL;
2600 NTSTATUS status;
2601 unsigned int i;
2602 char *sid_string;
2604 if (cache->tdb == NULL) {
2605 return NT_STATUS_NOT_FOUND;
2608 sid_string = sid_string_tos(group_sid);
2609 if (sid_string == NULL) {
2610 return NT_STATUS_NO_MEMORY;
2613 centry = wcache_fetch(cache, domain, "GM/%s", sid_string);
2614 TALLOC_FREE(sid_string);
2615 if (centry == NULL) {
2616 return NT_STATUS_NOT_FOUND;
2619 *sid_mem = NULL;
2620 *names = NULL;
2621 *name_types = NULL;
2623 *num_names = centry_uint32(centry);
2624 if (*num_names == 0) {
2625 centry_free(centry);
2626 return NT_STATUS_OK;
2629 *sid_mem = talloc_array(mem_ctx, struct dom_sid, *num_names);
2630 *names = talloc_array(mem_ctx, char *, *num_names);
2631 *name_types = talloc_array(mem_ctx, uint32, *num_names);
2633 if ((*sid_mem == NULL) || (*names == NULL) || (*name_types == NULL)) {
2634 TALLOC_FREE(*sid_mem);
2635 TALLOC_FREE(*names);
2636 TALLOC_FREE(*name_types);
2637 centry_free(centry);
2638 return NT_STATUS_NO_MEMORY;
2641 for (i=0; i<(*num_names); i++) {
2642 centry_sid(centry, &(*sid_mem)[i]);
2643 (*names)[i] = centry_string(centry, mem_ctx);
2644 (*name_types)[i] = centry_uint32(centry);
2647 status = centry->status;
2649 DEBUG(10,("lookup_groupmem: [Cached] - cached info for domain %s "
2650 "status: %s\n", domain->name, nt_errstr(status)));
2652 centry_free(centry);
2653 return status;
2656 static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
2657 TALLOC_CTX *mem_ctx,
2658 const struct dom_sid *group_sid,
2659 enum lsa_SidType type,
2660 uint32 *num_names,
2661 struct dom_sid **sid_mem, char ***names,
2662 uint32 **name_types)
2664 struct cache_entry *centry = NULL;
2665 NTSTATUS status;
2666 unsigned int i;
2667 fstring sid_string;
2668 bool old_status;
2670 old_status = domain->online;
2671 status = wcache_lookup_groupmem(domain, mem_ctx, group_sid, num_names,
2672 sid_mem, names, name_types);
2673 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2674 return status;
2677 (*num_names) = 0;
2678 (*sid_mem) = NULL;
2679 (*names) = NULL;
2680 (*name_types) = NULL;
2682 /* Return status value returned by seq number check */
2684 if (!NT_STATUS_IS_OK(domain->last_status))
2685 return domain->last_status;
2687 DEBUG(10,("lookup_groupmem: [Cached] - doing backend query for info for domain %s\n",
2688 domain->name ));
2690 status = domain->backend->lookup_groupmem(domain, mem_ctx, group_sid,
2691 type, num_names,
2692 sid_mem, names, name_types);
2694 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2695 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2696 if (!domain->internal && old_status) {
2697 set_domain_offline(domain);
2699 if (!domain->internal &&
2700 !domain->online &&
2701 old_status) {
2702 NTSTATUS cache_status;
2703 cache_status = wcache_lookup_groupmem(domain, mem_ctx, group_sid,
2704 num_names, sid_mem, names,
2705 name_types);
2706 return cache_status;
2709 /* and save it */
2710 refresh_sequence_number(domain, false);
2711 if (!NT_STATUS_IS_OK(status)) {
2712 return status;
2714 centry = centry_start(domain, status);
2715 if (!centry)
2716 goto skip_save;
2717 centry_put_uint32(centry, *num_names);
2718 for (i=0; i<(*num_names); i++) {
2719 centry_put_sid(centry, &(*sid_mem)[i]);
2720 centry_put_string(centry, (*names)[i]);
2721 centry_put_uint32(centry, (*name_types)[i]);
2723 centry_end(centry, "GM/%s", sid_to_fstring(sid_string, group_sid));
2724 centry_free(centry);
2726 skip_save:
2727 return status;
2730 /* find the sequence number for a domain */
2731 static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
2733 refresh_sequence_number(domain, false);
2735 *seq = domain->sequence_number;
2737 return NT_STATUS_OK;
2740 /* enumerate trusted domains
2741 * (we need to have the list of trustdoms in the cache when we go offline) -
2742 * Guenther */
2743 static NTSTATUS trusted_domains(struct winbindd_domain *domain,
2744 TALLOC_CTX *mem_ctx,
2745 struct netr_DomainTrustList *trusts)
2747 NTSTATUS status;
2748 struct winbind_cache *cache;
2749 struct winbindd_tdc_domain *dom_list = NULL;
2750 size_t num_domains = 0;
2751 bool retval = false;
2752 int i;
2753 bool old_status;
2755 old_status = domain->online;
2756 trusts->count = 0;
2757 trusts->array = NULL;
2759 cache = get_cache(domain);
2760 if (!cache || !cache->tdb) {
2761 goto do_query;
2764 if (domain->online) {
2765 goto do_query;
2768 retval = wcache_tdc_fetch_list(&dom_list, &num_domains);
2769 if (!retval || !num_domains || !dom_list) {
2770 TALLOC_FREE(dom_list);
2771 goto do_query;
2774 do_fetch_cache:
2775 trusts->array = talloc_zero_array(mem_ctx, struct netr_DomainTrust, num_domains);
2776 if (!trusts->array) {
2777 TALLOC_FREE(dom_list);
2778 return NT_STATUS_NO_MEMORY;
2781 for (i = 0; i < num_domains; i++) {
2782 struct netr_DomainTrust *trust;
2783 struct dom_sid *sid;
2784 struct winbindd_domain *dom;
2786 dom = find_domain_from_name_noinit(dom_list[i].domain_name);
2787 if (dom && dom->internal) {
2788 continue;
2791 trust = &trusts->array[trusts->count];
2792 trust->netbios_name = talloc_strdup(trusts->array, dom_list[i].domain_name);
2793 trust->dns_name = talloc_strdup(trusts->array, dom_list[i].dns_name);
2794 sid = talloc(trusts->array, struct dom_sid);
2795 if (!trust->netbios_name || !trust->dns_name ||
2796 !sid) {
2797 TALLOC_FREE(dom_list);
2798 TALLOC_FREE(trusts->array);
2799 return NT_STATUS_NO_MEMORY;
2802 trust->trust_flags = dom_list[i].trust_flags;
2803 trust->trust_attributes = dom_list[i].trust_attribs;
2804 trust->trust_type = dom_list[i].trust_type;
2805 sid_copy(sid, &dom_list[i].sid);
2806 trust->sid = sid;
2807 trusts->count++;
2810 TALLOC_FREE(dom_list);
2811 return NT_STATUS_OK;
2813 do_query:
2814 /* Return status value returned by seq number check */
2816 if (!NT_STATUS_IS_OK(domain->last_status))
2817 return domain->last_status;
2819 DEBUG(10,("trusted_domains: [Cached] - doing backend query for info for domain %s\n",
2820 domain->name ));
2822 status = domain->backend->trusted_domains(domain, mem_ctx, trusts);
2824 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2825 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2826 if (!domain->internal && old_status) {
2827 set_domain_offline(domain);
2829 if (!domain->internal &&
2830 !domain->online &&
2831 old_status) {
2832 retval = wcache_tdc_fetch_list(&dom_list, &num_domains);
2833 if (retval && num_domains && dom_list) {
2834 TALLOC_FREE(trusts->array);
2835 trusts->count = 0;
2836 goto do_fetch_cache;
2840 /* no trusts gives NT_STATUS_NO_MORE_ENTRIES resetting to NT_STATUS_OK
2841 * so that the generic centry handling still applies correctly -
2842 * Guenther*/
2844 if (!NT_STATUS_IS_ERR(status)) {
2845 status = NT_STATUS_OK;
2847 return status;
2850 /* get lockout policy */
2851 static NTSTATUS lockout_policy(struct winbindd_domain *domain,
2852 TALLOC_CTX *mem_ctx,
2853 struct samr_DomInfo12 *policy)
2855 struct winbind_cache *cache = get_cache(domain);
2856 struct cache_entry *centry = NULL;
2857 NTSTATUS status;
2858 bool old_status;
2860 old_status = domain->online;
2861 if (!cache->tdb)
2862 goto do_query;
2864 centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
2866 if (!centry)
2867 goto do_query;
2869 do_fetch_cache:
2870 policy->lockout_duration = centry_nttime(centry);
2871 policy->lockout_window = centry_nttime(centry);
2872 policy->lockout_threshold = centry_uint16(centry);
2874 status = centry->status;
2876 DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2877 domain->name, nt_errstr(status) ));
2879 centry_free(centry);
2880 return status;
2882 do_query:
2883 ZERO_STRUCTP(policy);
2885 /* Return status value returned by seq number check */
2887 if (!NT_STATUS_IS_OK(domain->last_status))
2888 return domain->last_status;
2890 DEBUG(10,("lockout_policy: [Cached] - doing backend query for info for domain %s\n",
2891 domain->name ));
2893 status = domain->backend->lockout_policy(domain, mem_ctx, policy);
2895 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2896 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2897 if (!domain->internal && old_status) {
2898 set_domain_offline(domain);
2900 if (cache->tdb &&
2901 !domain->internal &&
2902 !domain->online &&
2903 old_status) {
2904 centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
2905 if (centry) {
2906 goto do_fetch_cache;
2910 /* and save it */
2911 refresh_sequence_number(domain, false);
2912 if (!NT_STATUS_IS_OK(status)) {
2913 return status;
2915 wcache_save_lockout_policy(domain, status, policy);
2917 return status;
2920 /* get password policy */
2921 static NTSTATUS password_policy(struct winbindd_domain *domain,
2922 TALLOC_CTX *mem_ctx,
2923 struct samr_DomInfo1 *policy)
2925 struct winbind_cache *cache = get_cache(domain);
2926 struct cache_entry *centry = NULL;
2927 NTSTATUS status;
2928 bool old_status;
2930 old_status = domain->online;
2931 if (!cache->tdb)
2932 goto do_query;
2934 centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
2936 if (!centry)
2937 goto do_query;
2939 do_fetch_cache:
2940 policy->min_password_length = centry_uint16(centry);
2941 policy->password_history_length = centry_uint16(centry);
2942 policy->password_properties = centry_uint32(centry);
2943 policy->max_password_age = centry_nttime(centry);
2944 policy->min_password_age = centry_nttime(centry);
2946 status = centry->status;
2948 DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2949 domain->name, nt_errstr(status) ));
2951 centry_free(centry);
2952 return status;
2954 do_query:
2955 ZERO_STRUCTP(policy);
2957 /* Return status value returned by seq number check */
2959 if (!NT_STATUS_IS_OK(domain->last_status))
2960 return domain->last_status;
2962 DEBUG(10,("password_policy: [Cached] - doing backend query for info for domain %s\n",
2963 domain->name ));
2965 status = domain->backend->password_policy(domain, mem_ctx, policy);
2967 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2968 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2969 if (!domain->internal && old_status) {
2970 set_domain_offline(domain);
2972 if (cache->tdb &&
2973 !domain->internal &&
2974 !domain->online &&
2975 old_status) {
2976 centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
2977 if (centry) {
2978 goto do_fetch_cache;
2982 /* and save it */
2983 refresh_sequence_number(domain, false);
2984 if (!NT_STATUS_IS_OK(status)) {
2985 return status;
2987 wcache_save_password_policy(domain, status, policy);
2989 return status;
2993 /* Invalidate cached user and group lists coherently */
2995 static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
2996 void *state)
2998 if (strncmp((const char *)kbuf.dptr, "UL/", 3) == 0 ||
2999 strncmp((const char *)kbuf.dptr, "GL/", 3) == 0)
3000 tdb_delete(the_tdb, kbuf);
3002 return 0;
3005 /* Invalidate the getpwnam and getgroups entries for a winbindd domain */
3007 void wcache_invalidate_samlogon(struct winbindd_domain *domain,
3008 const struct dom_sid *sid)
3010 fstring key_str, sid_string;
3011 struct winbind_cache *cache;
3013 /* dont clear cached U/SID and UG/SID entries when we want to logon
3014 * offline - gd */
3016 if (lp_winbind_offline_logon()) {
3017 return;
3020 if (!domain)
3021 return;
3023 cache = get_cache(domain);
3025 if (!cache->tdb) {
3026 return;
3029 /* Clear U/SID cache entry */
3030 fstr_sprintf(key_str, "U/%s", sid_to_fstring(sid_string, sid));
3031 DEBUG(10, ("wcache_invalidate_samlogon: clearing %s\n", key_str));
3032 tdb_delete(cache->tdb, string_tdb_data(key_str));
3034 /* Clear UG/SID cache entry */
3035 fstr_sprintf(key_str, "UG/%s", sid_to_fstring(sid_string, sid));
3036 DEBUG(10, ("wcache_invalidate_samlogon: clearing %s\n", key_str));
3037 tdb_delete(cache->tdb, string_tdb_data(key_str));
3039 /* Samba/winbindd never needs this. */
3040 netsamlogon_clear_cached_user(sid);
3043 bool wcache_invalidate_cache(void)
3045 struct winbindd_domain *domain;
3047 for (domain = domain_list(); domain; domain = domain->next) {
3048 struct winbind_cache *cache = get_cache(domain);
3050 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
3051 "entries for %s\n", domain->name));
3052 if (cache) {
3053 if (cache->tdb) {
3054 tdb_traverse(cache->tdb, traverse_fn, NULL);
3055 } else {
3056 return false;
3060 return true;
3063 bool wcache_invalidate_cache_noinit(void)
3065 struct winbindd_domain *domain;
3067 for (domain = domain_list(); domain; domain = domain->next) {
3068 struct winbind_cache *cache;
3070 /* Skip uninitialized domains. */
3071 if (!domain->initialized && !domain->internal) {
3072 continue;
3075 cache = get_cache(domain);
3077 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
3078 "entries for %s\n", domain->name));
3079 if (cache) {
3080 if (cache->tdb) {
3081 tdb_traverse(cache->tdb, traverse_fn, NULL);
3083 * Flushing cache has nothing to with domains.
3084 * return here if we successfully flushed once.
3085 * To avoid unnecessary traversing the cache.
3087 return true;
3088 } else {
3089 return false;
3093 return true;
3096 bool init_wcache(void)
3098 if (wcache == NULL) {
3099 wcache = SMB_XMALLOC_P(struct winbind_cache);
3100 ZERO_STRUCTP(wcache);
3103 if (wcache->tdb != NULL)
3104 return true;
3106 /* when working offline we must not clear the cache on restart */
3107 wcache->tdb = tdb_open_log(state_path("winbindd_cache.tdb"),
3108 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
3109 TDB_INCOMPATIBLE_HASH |
3110 (lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST)),
3111 O_RDWR|O_CREAT, 0600);
3113 if (wcache->tdb == NULL) {
3114 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
3115 return false;
3118 return true;
3121 /************************************************************************
3122 This is called by the parent to initialize the cache file.
3123 We don't need sophisticated locking here as we know we're the
3124 only opener.
3125 ************************************************************************/
3127 bool initialize_winbindd_cache(void)
3129 bool cache_bad = true;
3130 uint32 vers;
3132 if (!init_wcache()) {
3133 DEBUG(0,("initialize_winbindd_cache: init_wcache failed.\n"));
3134 return false;
3137 /* Check version number. */
3138 if (tdb_fetch_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, &vers) &&
3139 vers == WINBINDD_CACHE_VERSION) {
3140 cache_bad = false;
3143 if (cache_bad) {
3144 DEBUG(0,("initialize_winbindd_cache: clearing cache "
3145 "and re-creating with version number %d\n",
3146 WINBINDD_CACHE_VERSION ));
3148 tdb_close(wcache->tdb);
3149 wcache->tdb = NULL;
3151 if (unlink(state_path("winbindd_cache.tdb")) == -1) {
3152 DEBUG(0,("initialize_winbindd_cache: unlink %s failed %s ",
3153 state_path("winbindd_cache.tdb"),
3154 strerror(errno) ));
3155 return false;
3157 if (!init_wcache()) {
3158 DEBUG(0,("initialize_winbindd_cache: re-initialization "
3159 "init_wcache failed.\n"));
3160 return false;
3163 /* Write the version. */
3164 if (!tdb_store_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, WINBINDD_CACHE_VERSION)) {
3165 DEBUG(0,("initialize_winbindd_cache: version number store failed %s\n",
3166 tdb_errorstr_compat(wcache->tdb) ));
3167 return false;
3171 tdb_close(wcache->tdb);
3172 wcache->tdb = NULL;
3173 return true;
3176 void close_winbindd_cache(void)
3178 if (!wcache) {
3179 return;
3181 if (wcache->tdb) {
3182 tdb_close(wcache->tdb);
3183 wcache->tdb = NULL;
3187 bool lookup_cached_sid(TALLOC_CTX *mem_ctx, const struct dom_sid *sid,
3188 char **domain_name, char **name,
3189 enum lsa_SidType *type)
3191 struct winbindd_domain *domain;
3192 NTSTATUS status;
3194 domain = find_lookup_domain_from_sid(sid);
3195 if (domain == NULL) {
3196 return false;
3198 status = wcache_sid_to_name(domain, sid, mem_ctx, domain_name, name,
3199 type);
3200 return NT_STATUS_IS_OK(status);
3203 bool lookup_cached_name(const char *domain_name,
3204 const char *name,
3205 struct dom_sid *sid,
3206 enum lsa_SidType *type)
3208 struct winbindd_domain *domain;
3209 NTSTATUS status;
3210 bool original_online_state;
3212 domain = find_lookup_domain_from_name(domain_name);
3213 if (domain == NULL) {
3214 return false;
3217 /* If we are doing a cached logon, temporarily set the domain
3218 offline so the cache won't expire the entry */
3220 original_online_state = domain->online;
3221 domain->online = false;
3222 status = wcache_name_to_sid(domain, domain_name, name, sid, type);
3223 domain->online = original_online_state;
3225 return NT_STATUS_IS_OK(status);
3228 void cache_name2sid(struct winbindd_domain *domain,
3229 const char *domain_name, const char *name,
3230 enum lsa_SidType type, const struct dom_sid *sid)
3232 refresh_sequence_number(domain, false);
3233 wcache_save_name_to_sid(domain, NT_STATUS_OK, domain_name, name,
3234 sid, type);
3238 * The original idea that this cache only contains centries has
3239 * been blurred - now other stuff gets put in here. Ensure we
3240 * ignore these things on cleanup.
3243 static int traverse_fn_cleanup(TDB_CONTEXT *the_tdb, TDB_DATA kbuf,
3244 TDB_DATA dbuf, void *state)
3246 struct cache_entry *centry;
3248 if (is_non_centry_key(kbuf)) {
3249 return 0;
3252 centry = wcache_fetch_raw((char *)kbuf.dptr);
3253 if (!centry) {
3254 return 0;
3257 if (!NT_STATUS_IS_OK(centry->status)) {
3258 DEBUG(10,("deleting centry %s\n", (const char *)kbuf.dptr));
3259 tdb_delete(the_tdb, kbuf);
3262 centry_free(centry);
3263 return 0;
3266 /* flush the cache */
3267 void wcache_flush_cache(void)
3269 if (!wcache)
3270 return;
3271 if (wcache->tdb) {
3272 tdb_close(wcache->tdb);
3273 wcache->tdb = NULL;
3275 if (!winbindd_use_cache()) {
3276 return;
3279 /* when working offline we must not clear the cache on restart */
3280 wcache->tdb = tdb_open_log(state_path("winbindd_cache.tdb"),
3281 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
3282 TDB_INCOMPATIBLE_HASH |
3283 (lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST)),
3284 O_RDWR|O_CREAT, 0600);
3286 if (!wcache->tdb) {
3287 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
3288 return;
3291 tdb_traverse(wcache->tdb, traverse_fn_cleanup, NULL);
3293 DEBUG(10,("wcache_flush_cache success\n"));
3296 /* Count cached creds */
3298 static int traverse_fn_cached_creds(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
3299 void *state)
3301 int *cred_count = (int*)state;
3303 if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
3304 (*cred_count)++;
3306 return 0;
3309 NTSTATUS wcache_count_cached_creds(struct winbindd_domain *domain, int *count)
3311 struct winbind_cache *cache = get_cache(domain);
3313 *count = 0;
3315 if (!cache->tdb) {
3316 return NT_STATUS_INTERNAL_DB_ERROR;
3319 tdb_traverse(cache->tdb, traverse_fn_cached_creds, (void *)count);
3321 return NT_STATUS_OK;
3324 struct cred_list {
3325 struct cred_list *prev, *next;
3326 TDB_DATA key;
3327 fstring name;
3328 time_t created;
3330 static struct cred_list *wcache_cred_list;
3332 static int traverse_fn_get_credlist(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
3333 void *state)
3335 struct cred_list *cred;
3337 if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
3339 cred = SMB_MALLOC_P(struct cred_list);
3340 if (cred == NULL) {
3341 DEBUG(0,("traverse_fn_remove_first_creds: failed to malloc new entry for list\n"));
3342 return -1;
3345 ZERO_STRUCTP(cred);
3347 /* save a copy of the key */
3349 fstrcpy(cred->name, (const char *)kbuf.dptr);
3350 DLIST_ADD(wcache_cred_list, cred);
3353 return 0;
3356 NTSTATUS wcache_remove_oldest_cached_creds(struct winbindd_domain *domain, const struct dom_sid *sid)
3358 struct winbind_cache *cache = get_cache(domain);
3359 NTSTATUS status;
3360 int ret;
3361 struct cred_list *cred, *oldest = NULL;
3363 if (!cache->tdb) {
3364 return NT_STATUS_INTERNAL_DB_ERROR;
3367 /* we possibly already have an entry */
3368 if (sid && NT_STATUS_IS_OK(wcache_cached_creds_exist(domain, sid))) {
3370 fstring key_str, tmp;
3372 DEBUG(11,("we already have an entry, deleting that\n"));
3374 fstr_sprintf(key_str, "CRED/%s", sid_to_fstring(tmp, sid));
3376 tdb_delete(cache->tdb, string_tdb_data(key_str));
3378 return NT_STATUS_OK;
3381 ret = tdb_traverse(cache->tdb, traverse_fn_get_credlist, NULL);
3382 if (ret == 0) {
3383 return NT_STATUS_OK;
3384 } else if ((ret < 0) || (wcache_cred_list == NULL)) {
3385 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
3388 ZERO_STRUCTP(oldest);
3390 for (cred = wcache_cred_list; cred; cred = cred->next) {
3392 TDB_DATA data;
3393 time_t t;
3395 data = tdb_fetch_compat(cache->tdb, string_tdb_data(cred->name));
3396 if (!data.dptr) {
3397 DEBUG(10,("wcache_remove_oldest_cached_creds: entry for [%s] not found\n",
3398 cred->name));
3399 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
3400 goto done;
3403 t = IVAL(data.dptr, 0);
3404 SAFE_FREE(data.dptr);
3406 if (!oldest) {
3407 oldest = SMB_MALLOC_P(struct cred_list);
3408 if (oldest == NULL) {
3409 status = NT_STATUS_NO_MEMORY;
3410 goto done;
3413 fstrcpy(oldest->name, cred->name);
3414 oldest->created = t;
3415 continue;
3418 if (t < oldest->created) {
3419 fstrcpy(oldest->name, cred->name);
3420 oldest->created = t;
3424 if (tdb_delete(cache->tdb, string_tdb_data(oldest->name)) == 0) {
3425 status = NT_STATUS_OK;
3426 } else {
3427 status = NT_STATUS_UNSUCCESSFUL;
3429 done:
3430 SAFE_FREE(wcache_cred_list);
3431 SAFE_FREE(oldest);
3433 return status;
3436 /* Change the global online/offline state. */
3437 bool set_global_winbindd_state_offline(void)
3439 TDB_DATA data;
3441 DEBUG(10,("set_global_winbindd_state_offline: offline requested.\n"));
3443 /* Only go offline if someone has created
3444 the key "WINBINDD_OFFLINE" in the cache tdb. */
3446 if (wcache == NULL || wcache->tdb == NULL) {
3447 DEBUG(10,("set_global_winbindd_state_offline: wcache not open yet.\n"));
3448 return false;
3451 if (!lp_winbind_offline_logon()) {
3452 DEBUG(10,("set_global_winbindd_state_offline: rejecting.\n"));
3453 return false;
3456 if (global_winbindd_offline_state) {
3457 /* Already offline. */
3458 return true;
3461 data = tdb_fetch_bystring( wcache->tdb, "WINBINDD_OFFLINE" );
3463 if (!data.dptr || data.dsize != 4) {
3464 DEBUG(10,("set_global_winbindd_state_offline: offline state not set.\n"));
3465 SAFE_FREE(data.dptr);
3466 return false;
3467 } else {
3468 DEBUG(10,("set_global_winbindd_state_offline: offline state set.\n"));
3469 global_winbindd_offline_state = true;
3470 SAFE_FREE(data.dptr);
3471 return true;
3475 void set_global_winbindd_state_online(void)
3477 DEBUG(10,("set_global_winbindd_state_online: online requested.\n"));
3479 if (!lp_winbind_offline_logon()) {
3480 DEBUG(10,("set_global_winbindd_state_online: rejecting.\n"));
3481 return;
3484 if (!global_winbindd_offline_state) {
3485 /* Already online. */
3486 return;
3488 global_winbindd_offline_state = false;
3490 if (!wcache->tdb) {
3491 return;
3494 /* Ensure there is no key "WINBINDD_OFFLINE" in the cache tdb. */
3495 tdb_delete_bystring(wcache->tdb, "WINBINDD_OFFLINE");
3498 bool get_global_winbindd_state_offline(void)
3500 return global_winbindd_offline_state;
3503 /***********************************************************************
3504 Validate functions for all possible cache tdb keys.
3505 ***********************************************************************/
3507 static struct cache_entry *create_centry_validate(const char *kstr, TDB_DATA data,
3508 struct tdb_validation_status *state)
3510 struct cache_entry *centry;
3512 centry = SMB_XMALLOC_P(struct cache_entry);
3513 centry->data = (unsigned char *)memdup(data.dptr, data.dsize);
3514 if (!centry->data) {
3515 SAFE_FREE(centry);
3516 return NULL;
3518 centry->len = data.dsize;
3519 centry->ofs = 0;
3521 if (centry->len < 16) {
3522 /* huh? corrupt cache? */
3523 DEBUG(0,("create_centry_validate: Corrupt cache for key %s "
3524 "(len < 16) ?\n", kstr));
3525 centry_free(centry);
3526 state->bad_entry = true;
3527 state->success = false;
3528 return NULL;
3531 centry->status = NT_STATUS(centry_uint32(centry));
3532 centry->sequence_number = centry_uint32(centry);
3533 centry->timeout = centry_uint64_t(centry);
3534 return centry;
3537 static int validate_seqnum(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3538 struct tdb_validation_status *state)
3540 if (dbuf.dsize != 8) {
3541 DEBUG(0,("validate_seqnum: Corrupt cache for key %s (len %u != 8) ?\n",
3542 keystr, (unsigned int)dbuf.dsize ));
3543 state->bad_entry = true;
3544 return 1;
3546 return 0;
3549 static int validate_ns(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3550 struct tdb_validation_status *state)
3552 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3553 if (!centry) {
3554 return 1;
3557 (void)centry_uint32(centry);
3558 if (NT_STATUS_IS_OK(centry->status)) {
3559 struct dom_sid sid;
3560 (void)centry_sid(centry, &sid);
3563 centry_free(centry);
3565 if (!(state->success)) {
3566 return 1;
3568 DEBUG(10,("validate_ns: %s ok\n", keystr));
3569 return 0;
3572 static int validate_sn(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3573 struct tdb_validation_status *state)
3575 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3576 if (!centry) {
3577 return 1;
3580 if (NT_STATUS_IS_OK(centry->status)) {
3581 (void)centry_uint32(centry);
3582 (void)centry_string(centry, mem_ctx);
3583 (void)centry_string(centry, mem_ctx);
3586 centry_free(centry);
3588 if (!(state->success)) {
3589 return 1;
3591 DEBUG(10,("validate_sn: %s ok\n", keystr));
3592 return 0;
3595 static int validate_u(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3596 struct tdb_validation_status *state)
3598 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3599 struct dom_sid sid;
3601 if (!centry) {
3602 return 1;
3605 (void)centry_string(centry, mem_ctx);
3606 (void)centry_string(centry, mem_ctx);
3607 (void)centry_string(centry, mem_ctx);
3608 (void)centry_string(centry, mem_ctx);
3609 (void)centry_uint32(centry);
3610 (void)centry_sid(centry, &sid);
3611 (void)centry_sid(centry, &sid);
3613 centry_free(centry);
3615 if (!(state->success)) {
3616 return 1;
3618 DEBUG(10,("validate_u: %s ok\n", keystr));
3619 return 0;
3622 static int validate_loc_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3623 struct tdb_validation_status *state)
3625 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3627 if (!centry) {
3628 return 1;
3631 (void)centry_nttime(centry);
3632 (void)centry_nttime(centry);
3633 (void)centry_uint16(centry);
3635 centry_free(centry);
3637 if (!(state->success)) {
3638 return 1;
3640 DEBUG(10,("validate_loc_pol: %s ok\n", keystr));
3641 return 0;
3644 static int validate_pwd_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3645 struct tdb_validation_status *state)
3647 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3649 if (!centry) {
3650 return 1;
3653 (void)centry_uint16(centry);
3654 (void)centry_uint16(centry);
3655 (void)centry_uint32(centry);
3656 (void)centry_nttime(centry);
3657 (void)centry_nttime(centry);
3659 centry_free(centry);
3661 if (!(state->success)) {
3662 return 1;
3664 DEBUG(10,("validate_pwd_pol: %s ok\n", keystr));
3665 return 0;
3668 static int validate_cred(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3669 struct tdb_validation_status *state)
3671 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3673 if (!centry) {
3674 return 1;
3677 (void)centry_time(centry);
3678 (void)centry_hash16(centry, mem_ctx);
3680 /* We only have 17 bytes more data in the salted cred case. */
3681 if (centry->len - centry->ofs == 17) {
3682 (void)centry_hash16(centry, mem_ctx);
3685 centry_free(centry);
3687 if (!(state->success)) {
3688 return 1;
3690 DEBUG(10,("validate_cred: %s ok\n", keystr));
3691 return 0;
3694 static int validate_ul(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3695 struct tdb_validation_status *state)
3697 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3698 int32 num_entries, i;
3700 if (!centry) {
3701 return 1;
3704 num_entries = (int32)centry_uint32(centry);
3706 for (i=0; i< num_entries; i++) {
3707 struct dom_sid sid;
3708 (void)centry_string(centry, mem_ctx);
3709 (void)centry_string(centry, mem_ctx);
3710 (void)centry_string(centry, mem_ctx);
3711 (void)centry_string(centry, mem_ctx);
3712 (void)centry_sid(centry, &sid);
3713 (void)centry_sid(centry, &sid);
3716 centry_free(centry);
3718 if (!(state->success)) {
3719 return 1;
3721 DEBUG(10,("validate_ul: %s ok\n", keystr));
3722 return 0;
3725 static int validate_gl(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3726 struct tdb_validation_status *state)
3728 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3729 int32 num_entries, i;
3731 if (!centry) {
3732 return 1;
3735 num_entries = centry_uint32(centry);
3737 for (i=0; i< num_entries; i++) {
3738 (void)centry_string(centry, mem_ctx);
3739 (void)centry_string(centry, mem_ctx);
3740 (void)centry_uint32(centry);
3743 centry_free(centry);
3745 if (!(state->success)) {
3746 return 1;
3748 DEBUG(10,("validate_gl: %s ok\n", keystr));
3749 return 0;
3752 static int validate_ug(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3753 struct tdb_validation_status *state)
3755 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3756 int32 num_groups, i;
3758 if (!centry) {
3759 return 1;
3762 num_groups = centry_uint32(centry);
3764 for (i=0; i< num_groups; i++) {
3765 struct dom_sid sid;
3766 centry_sid(centry, &sid);
3769 centry_free(centry);
3771 if (!(state->success)) {
3772 return 1;
3774 DEBUG(10,("validate_ug: %s ok\n", keystr));
3775 return 0;
3778 static int validate_ua(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3779 struct tdb_validation_status *state)
3781 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3782 int32 num_aliases, i;
3784 if (!centry) {
3785 return 1;
3788 num_aliases = centry_uint32(centry);
3790 for (i=0; i < num_aliases; i++) {
3791 (void)centry_uint32(centry);
3794 centry_free(centry);
3796 if (!(state->success)) {
3797 return 1;
3799 DEBUG(10,("validate_ua: %s ok\n", keystr));
3800 return 0;
3803 static int validate_gm(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3804 struct tdb_validation_status *state)
3806 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3807 int32 num_names, i;
3809 if (!centry) {
3810 return 1;
3813 num_names = centry_uint32(centry);
3815 for (i=0; i< num_names; i++) {
3816 struct dom_sid sid;
3817 centry_sid(centry, &sid);
3818 (void)centry_string(centry, mem_ctx);
3819 (void)centry_uint32(centry);
3822 centry_free(centry);
3824 if (!(state->success)) {
3825 return 1;
3827 DEBUG(10,("validate_gm: %s ok\n", keystr));
3828 return 0;
3831 static int validate_dr(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3832 struct tdb_validation_status *state)
3834 /* Can't say anything about this other than must be nonzero. */
3835 if (dbuf.dsize == 0) {
3836 DEBUG(0,("validate_dr: Corrupt cache for key %s (len == 0) ?\n",
3837 keystr));
3838 state->bad_entry = true;
3839 state->success = false;
3840 return 1;
3843 DEBUG(10,("validate_dr: %s ok\n", keystr));
3844 return 0;
3847 static int validate_de(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3848 struct tdb_validation_status *state)
3850 /* Can't say anything about this other than must be nonzero. */
3851 if (dbuf.dsize == 0) {
3852 DEBUG(0,("validate_de: Corrupt cache for key %s (len == 0) ?\n",
3853 keystr));
3854 state->bad_entry = true;
3855 state->success = false;
3856 return 1;
3859 DEBUG(10,("validate_de: %s ok\n", keystr));
3860 return 0;
3863 static int validate_pwinfo(TALLOC_CTX *mem_ctx, const char *keystr,
3864 TDB_DATA dbuf, struct tdb_validation_status *state)
3866 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3868 if (!centry) {
3869 return 1;
3872 (void)centry_string(centry, mem_ctx);
3873 (void)centry_string(centry, mem_ctx);
3874 (void)centry_string(centry, mem_ctx);
3875 (void)centry_uint32(centry);
3877 centry_free(centry);
3879 if (!(state->success)) {
3880 return 1;
3882 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3883 return 0;
3886 static int validate_nss_an(TALLOC_CTX *mem_ctx, const char *keystr,
3887 TDB_DATA dbuf,
3888 struct tdb_validation_status *state)
3890 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3892 if (!centry) {
3893 return 1;
3896 (void)centry_string( centry, mem_ctx );
3898 centry_free(centry);
3900 if (!(state->success)) {
3901 return 1;
3903 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3904 return 0;
3907 static int validate_nss_na(TALLOC_CTX *mem_ctx, const char *keystr,
3908 TDB_DATA dbuf,
3909 struct tdb_validation_status *state)
3911 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3913 if (!centry) {
3914 return 1;
3917 (void)centry_string( centry, mem_ctx );
3919 centry_free(centry);
3921 if (!(state->success)) {
3922 return 1;
3924 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3925 return 0;
3928 static int validate_trustdomcache(TALLOC_CTX *mem_ctx, const char *keystr,
3929 TDB_DATA dbuf,
3930 struct tdb_validation_status *state)
3932 if (dbuf.dsize == 0) {
3933 DEBUG(0, ("validate_trustdomcache: Corrupt cache for "
3934 "key %s (len ==0) ?\n", keystr));
3935 state->bad_entry = true;
3936 state->success = false;
3937 return 1;
3940 DEBUG(10, ("validate_trustdomcache: %s ok\n", keystr));
3941 DEBUGADD(10, (" Don't trust me, I am a DUMMY!\n"));
3942 return 0;
3945 static int validate_offline(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3946 struct tdb_validation_status *state)
3948 if (dbuf.dsize != 4) {
3949 DEBUG(0,("validate_offline: Corrupt cache for key %s (len %u != 4) ?\n",
3950 keystr, (unsigned int)dbuf.dsize ));
3951 state->bad_entry = true;
3952 state->success = false;
3953 return 1;
3955 DEBUG(10,("validate_offline: %s ok\n", keystr));
3956 return 0;
3959 static int validate_ndr(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3960 struct tdb_validation_status *state)
3963 * Ignore validation for now. The proper way to do this is with a
3964 * checksum. Just pure parsing does not really catch much.
3966 return 0;
3969 static int validate_cache_version(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3970 struct tdb_validation_status *state)
3972 if (dbuf.dsize != 4) {
3973 DEBUG(0, ("validate_cache_version: Corrupt cache for "
3974 "key %s (len %u != 4) ?\n",
3975 keystr, (unsigned int)dbuf.dsize));
3976 state->bad_entry = true;
3977 state->success = false;
3978 return 1;
3981 DEBUG(10, ("validate_cache_version: %s ok\n", keystr));
3982 return 0;
3985 /***********************************************************************
3986 A list of all possible cache tdb keys with associated validation
3987 functions.
3988 ***********************************************************************/
3990 struct key_val_struct {
3991 const char *keyname;
3992 int (*validate_data_fn)(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, struct tdb_validation_status* state);
3993 } key_val[] = {
3994 {"SEQNUM/", validate_seqnum},
3995 {"NS/", validate_ns},
3996 {"SN/", validate_sn},
3997 {"U/", validate_u},
3998 {"LOC_POL/", validate_loc_pol},
3999 {"PWD_POL/", validate_pwd_pol},
4000 {"CRED/", validate_cred},
4001 {"UL/", validate_ul},
4002 {"GL/", validate_gl},
4003 {"UG/", validate_ug},
4004 {"UA", validate_ua},
4005 {"GM/", validate_gm},
4006 {"DR/", validate_dr},
4007 {"DE/", validate_de},
4008 {"NSS/PWINFO/", validate_pwinfo},
4009 {"TRUSTDOMCACHE/", validate_trustdomcache},
4010 {"NSS/NA/", validate_nss_na},
4011 {"NSS/AN/", validate_nss_an},
4012 {"WINBINDD_OFFLINE", validate_offline},
4013 {"NDR/", validate_ndr},
4014 {WINBINDD_CACHE_VERSION_KEYSTR, validate_cache_version},
4015 {NULL, NULL}
4018 /***********************************************************************
4019 Function to look at every entry in the tdb and validate it as far as
4020 possible.
4021 ***********************************************************************/
4023 static int cache_traverse_validate_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, void *state)
4025 int i;
4026 unsigned int max_key_len = 1024;
4027 struct tdb_validation_status *v_state = (struct tdb_validation_status *)state;
4029 /* Paranoia check. */
4030 if (strncmp("UA/", (const char *)kbuf.dptr, 3) == 0) {
4031 max_key_len = 1024 * 1024;
4033 if (kbuf.dsize > max_key_len) {
4034 DEBUG(0, ("cache_traverse_validate_fn: key length too large: "
4035 "(%u) > (%u)\n\n",
4036 (unsigned int)kbuf.dsize, (unsigned int)max_key_len));
4037 return 1;
4040 for (i = 0; key_val[i].keyname; i++) {
4041 size_t namelen = strlen(key_val[i].keyname);
4042 if (kbuf.dsize >= namelen && (
4043 strncmp(key_val[i].keyname, (const char *)kbuf.dptr, namelen)) == 0) {
4044 TALLOC_CTX *mem_ctx;
4045 char *keystr;
4046 int ret;
4048 keystr = SMB_MALLOC_ARRAY(char, kbuf.dsize+1);
4049 if (!keystr) {
4050 return 1;
4052 memcpy(keystr, kbuf.dptr, kbuf.dsize);
4053 keystr[kbuf.dsize] = '\0';
4055 mem_ctx = talloc_init("validate_ctx");
4056 if (!mem_ctx) {
4057 SAFE_FREE(keystr);
4058 return 1;
4061 ret = key_val[i].validate_data_fn(mem_ctx, keystr, dbuf,
4062 v_state);
4064 SAFE_FREE(keystr);
4065 talloc_destroy(mem_ctx);
4066 return ret;
4070 DEBUG(0,("cache_traverse_validate_fn: unknown cache entry\nkey :\n"));
4071 dump_data(0, (uint8 *)kbuf.dptr, kbuf.dsize);
4072 DEBUG(0,("data :\n"));
4073 dump_data(0, (uint8 *)dbuf.dptr, dbuf.dsize);
4074 v_state->unknown_key = true;
4075 v_state->success = false;
4076 return 1; /* terminate. */
4079 static void validate_panic(const char *const why)
4081 DEBUG(0,("validating cache: would panic %s\n", why ));
4082 DEBUGADD(0, ("exiting instead (cache validation mode)\n"));
4083 exit(47);
4086 /***********************************************************************
4087 Try and validate every entry in the winbindd cache. If we fail here,
4088 delete the cache tdb and return non-zero.
4089 ***********************************************************************/
4091 int winbindd_validate_cache(void)
4093 int ret = -1;
4094 const char *tdb_path = state_path("winbindd_cache.tdb");
4095 TDB_CONTEXT *tdb = NULL;
4097 DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
4098 smb_panic_fn = validate_panic;
4101 tdb = tdb_open_log(tdb_path,
4102 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
4103 TDB_INCOMPATIBLE_HASH |
4104 ( lp_winbind_offline_logon()
4105 ? TDB_DEFAULT
4106 : TDB_DEFAULT | TDB_CLEAR_IF_FIRST ),
4107 O_RDWR|O_CREAT,
4108 0600);
4109 if (!tdb) {
4110 DEBUG(0, ("winbindd_validate_cache: "
4111 "error opening/initializing tdb\n"));
4112 goto done;
4114 tdb_close(tdb);
4116 ret = tdb_validate_and_backup(tdb_path, cache_traverse_validate_fn);
4118 if (ret != 0) {
4119 DEBUG(10, ("winbindd_validate_cache: validation not successful.\n"));
4120 DEBUGADD(10, ("removing tdb %s.\n", tdb_path));
4121 unlink(tdb_path);
4124 done:
4125 DEBUG(10, ("winbindd_validate_cache: restoring panic function\n"));
4126 smb_panic_fn = smb_panic;
4127 return ret;
4130 /***********************************************************************
4131 Try and validate every entry in the winbindd cache.
4132 ***********************************************************************/
4134 int winbindd_validate_cache_nobackup(void)
4136 int ret = -1;
4137 const char *tdb_path = state_path("winbindd_cache.tdb");
4139 DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
4140 smb_panic_fn = validate_panic;
4143 if (wcache == NULL || wcache->tdb == NULL) {
4144 ret = tdb_validate_open(tdb_path, cache_traverse_validate_fn);
4145 } else {
4146 ret = tdb_validate(wcache->tdb, cache_traverse_validate_fn);
4149 if (ret != 0) {
4150 DEBUG(10, ("winbindd_validate_cache_nobackup: validation not "
4151 "successful.\n"));
4154 DEBUG(10, ("winbindd_validate_cache_nobackup: restoring panic "
4155 "function\n"));
4156 smb_panic_fn = smb_panic;
4157 return ret;
4160 bool winbindd_cache_validate_and_initialize(void)
4162 close_winbindd_cache();
4164 if (lp_winbind_offline_logon()) {
4165 if (winbindd_validate_cache() < 0) {
4166 DEBUG(0, ("winbindd cache tdb corrupt and no backup "
4167 "could be restored.\n"));
4171 return initialize_winbindd_cache();
4174 /*********************************************************************
4175 ********************************************************************/
4177 static bool add_wbdomain_to_tdc_array( struct winbindd_domain *new_dom,
4178 struct winbindd_tdc_domain **domains,
4179 size_t *num_domains )
4181 struct winbindd_tdc_domain *list = NULL;
4182 size_t idx;
4183 int i;
4184 bool set_only = false;
4186 /* don't allow duplicates */
4188 idx = *num_domains;
4189 list = *domains;
4191 for ( i=0; i< (*num_domains); i++ ) {
4192 if ( strequal( new_dom->name, list[i].domain_name ) ) {
4193 DEBUG(10,("add_wbdomain_to_tdc_array: Found existing record for %s\n",
4194 new_dom->name));
4195 idx = i;
4196 set_only = true;
4198 break;
4202 if ( !set_only ) {
4203 if ( !*domains ) {
4204 list = talloc_array( NULL, struct winbindd_tdc_domain, 1 );
4205 idx = 0;
4206 } else {
4207 list = talloc_realloc( *domains, *domains,
4208 struct winbindd_tdc_domain,
4209 (*num_domains)+1);
4210 idx = *num_domains;
4213 ZERO_STRUCT( list[idx] );
4216 if ( !list )
4217 return false;
4219 list[idx].domain_name = talloc_strdup( list, new_dom->name );
4220 list[idx].dns_name = talloc_strdup( list, new_dom->alt_name );
4222 if ( !is_null_sid( &new_dom->sid ) ) {
4223 sid_copy( &list[idx].sid, &new_dom->sid );
4224 } else {
4225 sid_copy(&list[idx].sid, &global_sid_NULL);
4228 if ( new_dom->domain_flags != 0x0 )
4229 list[idx].trust_flags = new_dom->domain_flags;
4231 if ( new_dom->domain_type != 0x0 )
4232 list[idx].trust_type = new_dom->domain_type;
4234 if ( new_dom->domain_trust_attribs != 0x0 )
4235 list[idx].trust_attribs = new_dom->domain_trust_attribs;
4237 if ( !set_only ) {
4238 *domains = list;
4239 *num_domains = idx + 1;
4242 return true;
4245 /*********************************************************************
4246 ********************************************************************/
4248 static TDB_DATA make_tdc_key( const char *domain_name )
4250 char *keystr = NULL;
4251 TDB_DATA key = { NULL, 0 };
4253 if ( !domain_name ) {
4254 DEBUG(5,("make_tdc_key: Keyname workgroup is NULL!\n"));
4255 return key;
4258 if (asprintf( &keystr, "TRUSTDOMCACHE/%s", domain_name ) == -1) {
4259 return key;
4261 key = string_term_tdb_data(keystr);
4263 return key;
4266 /*********************************************************************
4267 ********************************************************************/
4269 static int pack_tdc_domains( struct winbindd_tdc_domain *domains,
4270 size_t num_domains,
4271 unsigned char **buf )
4273 unsigned char *buffer = NULL;
4274 int len = 0;
4275 int buflen = 0;
4276 int i = 0;
4278 DEBUG(10,("pack_tdc_domains: Packing %d trusted domains\n",
4279 (int)num_domains));
4281 buflen = 0;
4283 again:
4284 len = 0;
4286 /* Store the number of array items first */
4287 len += tdb_pack( buffer+len, buflen-len, "d",
4288 num_domains );
4290 /* now pack each domain trust record */
4291 for ( i=0; i<num_domains; i++ ) {
4293 fstring tmp;
4295 if ( buflen > 0 ) {
4296 DEBUG(10,("pack_tdc_domains: Packing domain %s (%s)\n",
4297 domains[i].domain_name,
4298 domains[i].dns_name ? domains[i].dns_name : "UNKNOWN" ));
4301 len += tdb_pack( buffer+len, buflen-len, "fffddd",
4302 domains[i].domain_name,
4303 domains[i].dns_name,
4304 sid_to_fstring(tmp, &domains[i].sid),
4305 domains[i].trust_flags,
4306 domains[i].trust_attribs,
4307 domains[i].trust_type );
4310 if ( buflen < len ) {
4311 SAFE_FREE(buffer);
4312 if ( (buffer = SMB_MALLOC_ARRAY(unsigned char, len)) == NULL ) {
4313 DEBUG(0,("pack_tdc_domains: failed to alloc buffer!\n"));
4314 buflen = -1;
4315 goto done;
4317 buflen = len;
4318 goto again;
4321 *buf = buffer;
4323 done:
4324 return buflen;
4327 /*********************************************************************
4328 ********************************************************************/
4330 static size_t unpack_tdc_domains( unsigned char *buf, int buflen,
4331 struct winbindd_tdc_domain **domains )
4333 fstring domain_name, dns_name, sid_string;
4334 uint32 type, attribs, flags;
4335 int num_domains;
4336 int len = 0;
4337 int i;
4338 struct winbindd_tdc_domain *list = NULL;
4340 /* get the number of domains */
4341 len += tdb_unpack( buf+len, buflen-len, "d", &num_domains);
4342 if ( len == -1 ) {
4343 DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
4344 return 0;
4347 list = talloc_array( NULL, struct winbindd_tdc_domain, num_domains );
4348 if ( !list ) {
4349 DEBUG(0,("unpack_tdc_domains: Failed to talloc() domain list!\n"));
4350 return 0;
4353 for ( i=0; i<num_domains; i++ ) {
4354 len += tdb_unpack( buf+len, buflen-len, "fffddd",
4355 domain_name,
4356 dns_name,
4357 sid_string,
4358 &flags,
4359 &attribs,
4360 &type );
4362 if ( len == -1 ) {
4363 DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
4364 TALLOC_FREE( list );
4365 return 0;
4368 DEBUG(11,("unpack_tdc_domains: Unpacking domain %s (%s) "
4369 "SID %s, flags = 0x%x, attribs = 0x%x, type = 0x%x\n",
4370 domain_name, dns_name, sid_string,
4371 flags, attribs, type));
4373 list[i].domain_name = talloc_strdup( list, domain_name );
4374 list[i].dns_name = talloc_strdup( list, dns_name );
4375 if ( !string_to_sid( &(list[i].sid), sid_string ) ) {
4376 DEBUG(10,("unpack_tdc_domains: no SID for domain %s\n",
4377 domain_name));
4379 list[i].trust_flags = flags;
4380 list[i].trust_attribs = attribs;
4381 list[i].trust_type = type;
4384 *domains = list;
4386 return num_domains;
4389 /*********************************************************************
4390 ********************************************************************/
4392 static bool wcache_tdc_store_list( struct winbindd_tdc_domain *domains, size_t num_domains )
4394 TDB_DATA key = make_tdc_key( lp_workgroup() );
4395 TDB_DATA data = { NULL, 0 };
4396 int ret;
4398 if ( !key.dptr )
4399 return false;
4401 /* See if we were asked to delete the cache entry */
4403 if ( !domains ) {
4404 ret = tdb_delete( wcache->tdb, key );
4405 goto done;
4408 data.dsize = pack_tdc_domains( domains, num_domains, &data.dptr );
4410 if ( !data.dptr ) {
4411 ret = -1;
4412 goto done;
4415 ret = tdb_store( wcache->tdb, key, data, 0 );
4417 done:
4418 SAFE_FREE( data.dptr );
4419 SAFE_FREE( key.dptr );
4421 return ( ret == 0 );
4424 /*********************************************************************
4425 ********************************************************************/
4427 bool wcache_tdc_fetch_list( struct winbindd_tdc_domain **domains, size_t *num_domains )
4429 TDB_DATA key = make_tdc_key( lp_workgroup() );
4430 TDB_DATA data = { NULL, 0 };
4432 *domains = NULL;
4433 *num_domains = 0;
4435 if ( !key.dptr )
4436 return false;
4438 data = tdb_fetch_compat( wcache->tdb, key );
4440 SAFE_FREE( key.dptr );
4442 if ( !data.dptr )
4443 return false;
4445 *num_domains = unpack_tdc_domains( data.dptr, data.dsize, domains );
4447 SAFE_FREE( data.dptr );
4449 if ( !*domains )
4450 return false;
4452 return true;
4455 /*********************************************************************
4456 ********************************************************************/
4458 bool wcache_tdc_add_domain( struct winbindd_domain *domain )
4460 struct winbindd_tdc_domain *dom_list = NULL;
4461 size_t num_domains = 0;
4462 bool ret = false;
4464 DEBUG(10,("wcache_tdc_add_domain: Adding domain %s (%s), SID %s, "
4465 "flags = 0x%x, attributes = 0x%x, type = 0x%x\n",
4466 domain->name, domain->alt_name,
4467 sid_string_dbg(&domain->sid),
4468 domain->domain_flags,
4469 domain->domain_trust_attribs,
4470 domain->domain_type));
4472 if ( !init_wcache() ) {
4473 return false;
4476 /* fetch the list */
4478 wcache_tdc_fetch_list( &dom_list, &num_domains );
4480 /* add the new domain */
4482 if ( !add_wbdomain_to_tdc_array( domain, &dom_list, &num_domains ) ) {
4483 goto done;
4486 /* pack the domain */
4488 if ( !wcache_tdc_store_list( dom_list, num_domains ) ) {
4489 goto done;
4492 /* Success */
4494 ret = true;
4495 done:
4496 TALLOC_FREE( dom_list );
4498 return ret;
4501 /*********************************************************************
4502 ********************************************************************/
4504 struct winbindd_tdc_domain * wcache_tdc_fetch_domain( TALLOC_CTX *ctx, const char *name )
4506 struct winbindd_tdc_domain *dom_list = NULL;
4507 size_t num_domains = 0;
4508 int i;
4509 struct winbindd_tdc_domain *d = NULL;
4511 DEBUG(10,("wcache_tdc_fetch_domain: Searching for domain %s\n", name));
4513 if ( !init_wcache() ) {
4514 return false;
4517 /* fetch the list */
4519 wcache_tdc_fetch_list( &dom_list, &num_domains );
4521 for ( i=0; i<num_domains; i++ ) {
4522 if ( strequal(name, dom_list[i].domain_name) ||
4523 strequal(name, dom_list[i].dns_name) )
4525 DEBUG(10,("wcache_tdc_fetch_domain: Found domain %s\n",
4526 name));
4528 d = talloc( ctx, struct winbindd_tdc_domain );
4529 if ( !d )
4530 break;
4532 d->domain_name = talloc_strdup( d, dom_list[i].domain_name );
4533 d->dns_name = talloc_strdup( d, dom_list[i].dns_name );
4534 sid_copy( &d->sid, &dom_list[i].sid );
4535 d->trust_flags = dom_list[i].trust_flags;
4536 d->trust_type = dom_list[i].trust_type;
4537 d->trust_attribs = dom_list[i].trust_attribs;
4539 break;
4543 TALLOC_FREE( dom_list );
4545 return d;
4548 /*********************************************************************
4549 ********************************************************************/
4551 struct winbindd_tdc_domain*
4552 wcache_tdc_fetch_domainbysid(TALLOC_CTX *ctx,
4553 const struct dom_sid *sid)
4555 struct winbindd_tdc_domain *dom_list = NULL;
4556 size_t num_domains = 0;
4557 int i;
4558 struct winbindd_tdc_domain *d = NULL;
4560 DEBUG(10,("wcache_tdc_fetch_domainbysid: Searching for domain %s\n",
4561 sid_string_dbg(sid)));
4563 if (!init_wcache()) {
4564 return false;
4567 /* fetch the list */
4569 wcache_tdc_fetch_list(&dom_list, &num_domains);
4571 for (i = 0; i<num_domains; i++) {
4572 if (dom_sid_equal(sid, &(dom_list[i].sid))) {
4573 DEBUG(10, ("wcache_tdc_fetch_domainbysid: "
4574 "Found domain %s for SID %s\n",
4575 dom_list[i].domain_name,
4576 sid_string_dbg(sid)));
4578 d = talloc(ctx, struct winbindd_tdc_domain);
4579 if (!d)
4580 break;
4582 d->domain_name = talloc_strdup(d,
4583 dom_list[i].domain_name);
4585 d->dns_name = talloc_strdup(d, dom_list[i].dns_name);
4586 sid_copy(&d->sid, &dom_list[i].sid);
4587 d->trust_flags = dom_list[i].trust_flags;
4588 d->trust_type = dom_list[i].trust_type;
4589 d->trust_attribs = dom_list[i].trust_attribs;
4591 break;
4595 TALLOC_FREE(dom_list);
4597 return d;
4601 /*********************************************************************
4602 ********************************************************************/
4604 void wcache_tdc_clear( void )
4606 if ( !init_wcache() )
4607 return;
4609 wcache_tdc_store_list( NULL, 0 );
4611 return;
4615 /*********************************************************************
4616 ********************************************************************/
4618 static void wcache_save_user_pwinfo(struct winbindd_domain *domain,
4619 NTSTATUS status,
4620 const struct dom_sid *user_sid,
4621 const char *homedir,
4622 const char *shell,
4623 const char *gecos,
4624 uint32 gid)
4626 struct cache_entry *centry;
4627 fstring tmp;
4629 if ( (centry = centry_start(domain, status)) == NULL )
4630 return;
4632 centry_put_string( centry, homedir );
4633 centry_put_string( centry, shell );
4634 centry_put_string( centry, gecos );
4635 centry_put_uint32( centry, gid );
4637 centry_end(centry, "NSS/PWINFO/%s", sid_to_fstring(tmp, user_sid) );
4639 DEBUG(10,("wcache_save_user_pwinfo: %s\n", sid_string_dbg(user_sid) ));
4641 centry_free(centry);
4644 #ifdef HAVE_ADS
4646 NTSTATUS nss_get_info_cached( struct winbindd_domain *domain,
4647 const struct dom_sid *user_sid,
4648 TALLOC_CTX *ctx,
4649 const char **homedir, const char **shell,
4650 const char **gecos, gid_t *p_gid)
4652 struct winbind_cache *cache = get_cache(domain);
4653 struct cache_entry *centry = NULL;
4654 NTSTATUS nt_status;
4655 fstring tmp;
4657 if (!cache->tdb)
4658 goto do_query;
4660 centry = wcache_fetch(cache, domain, "NSS/PWINFO/%s",
4661 sid_to_fstring(tmp, user_sid));
4663 if (!centry)
4664 goto do_query;
4666 *homedir = centry_string( centry, ctx );
4667 *shell = centry_string( centry, ctx );
4668 *gecos = centry_string( centry, ctx );
4669 *p_gid = centry_uint32( centry );
4671 centry_free(centry);
4673 DEBUG(10,("nss_get_info_cached: [Cached] - user_sid %s\n",
4674 sid_string_dbg(user_sid)));
4676 return NT_STATUS_OK;
4678 do_query:
4680 nt_status = nss_get_info( domain->name, user_sid, ctx,
4681 homedir, shell, gecos, p_gid );
4683 DEBUG(10, ("nss_get_info returned %s\n", nt_errstr(nt_status)));
4685 if ( NT_STATUS_IS_OK(nt_status) ) {
4686 DEBUG(10, ("result:\n\thomedir = '%s'\n", *homedir));
4687 DEBUGADD(10, ("\tshell = '%s'\n", *shell));
4688 DEBUGADD(10, ("\tgecos = '%s'\n", *gecos));
4689 DEBUGADD(10, ("\tgid = '%u'\n", (unsigned int)*p_gid));
4691 wcache_save_user_pwinfo( domain, nt_status, user_sid,
4692 *homedir, *shell, *gecos, *p_gid );
4695 if ( NT_STATUS_EQUAL( nt_status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND ) ) {
4696 DEBUG(5,("nss_get_info_cached: Setting domain %s offline\n",
4697 domain->name ));
4698 set_domain_offline( domain );
4701 return nt_status;
4704 #endif
4706 /* the cache backend methods are exposed via this structure */
4707 struct winbindd_methods cache_methods = {
4708 true,
4709 query_user_list,
4710 enum_dom_groups,
4711 enum_local_groups,
4712 name_to_sid,
4713 sid_to_name,
4714 rids_to_names,
4715 query_user,
4716 lookup_usergroups,
4717 lookup_useraliases,
4718 lookup_groupmem,
4719 sequence_number,
4720 lockout_policy,
4721 password_policy,
4722 trusted_domains
4725 static bool wcache_ndr_key(TALLOC_CTX *mem_ctx, char *domain_name,
4726 uint32_t opnum, const DATA_BLOB *req,
4727 TDB_DATA *pkey)
4729 char *key;
4730 size_t keylen;
4732 key = talloc_asprintf(mem_ctx, "NDR/%s/%d/", domain_name, (int)opnum);
4733 if (key == NULL) {
4734 return false;
4736 keylen = talloc_get_size(key) - 1;
4738 key = talloc_realloc(mem_ctx, key, char, keylen + req->length);
4739 if (key == NULL) {
4740 return false;
4742 memcpy(key + keylen, req->data, req->length);
4744 pkey->dptr = (uint8_t *)key;
4745 pkey->dsize = talloc_get_size(key);
4746 return true;
4749 static bool wcache_opnum_cacheable(uint32_t opnum)
4751 switch (opnum) {
4752 case NDR_WBINT_PING:
4753 case NDR_WBINT_QUERYSEQUENCENUMBER:
4754 case NDR_WBINT_ALLOCATEUID:
4755 case NDR_WBINT_ALLOCATEGID:
4756 case NDR_WBINT_CHECKMACHINEACCOUNT:
4757 case NDR_WBINT_CHANGEMACHINEACCOUNT:
4758 case NDR_WBINT_PINGDC:
4759 return false;
4761 return true;
4764 bool wcache_fetch_ndr(TALLOC_CTX *mem_ctx, struct winbindd_domain *domain,
4765 uint32_t opnum, const DATA_BLOB *req, DATA_BLOB *resp)
4767 TDB_DATA key, data;
4768 bool ret = false;
4770 if (!wcache_opnum_cacheable(opnum) ||
4771 is_my_own_sam_domain(domain) ||
4772 is_builtin_domain(domain)) {
4773 return false;
4776 if (wcache->tdb == NULL) {
4777 return false;
4780 if (!wcache_ndr_key(talloc_tos(), domain->name, opnum, req, &key)) {
4781 return false;
4783 data = tdb_fetch_compat(wcache->tdb, key);
4784 TALLOC_FREE(key.dptr);
4786 if (data.dptr == NULL) {
4787 return false;
4789 if (data.dsize < 12) {
4790 goto fail;
4793 if (!is_domain_offline(domain)) {
4794 uint32_t entry_seqnum, dom_seqnum, last_check;
4795 uint64_t entry_timeout;
4797 if (!wcache_fetch_seqnum(domain->name, &dom_seqnum,
4798 &last_check)) {
4799 goto fail;
4801 entry_seqnum = IVAL(data.dptr, 0);
4802 if (entry_seqnum != dom_seqnum) {
4803 DEBUG(10, ("Entry has wrong sequence number: %d\n",
4804 (int)entry_seqnum));
4805 goto fail;
4807 entry_timeout = BVAL(data.dptr, 4);
4808 if (time(NULL) > entry_timeout) {
4809 DEBUG(10, ("Entry has timed out\n"));
4810 goto fail;
4814 resp->data = (uint8_t *)talloc_memdup(mem_ctx, data.dptr + 12,
4815 data.dsize - 12);
4816 if (resp->data == NULL) {
4817 DEBUG(10, ("talloc failed\n"));
4818 goto fail;
4820 resp->length = data.dsize - 12;
4822 ret = true;
4823 fail:
4824 SAFE_FREE(data.dptr);
4825 return ret;
4828 void wcache_store_ndr(struct winbindd_domain *domain, uint32_t opnum,
4829 const DATA_BLOB *req, const DATA_BLOB *resp)
4831 TDB_DATA key, data;
4832 uint32_t dom_seqnum, last_check;
4833 uint64_t timeout;
4835 if (!wcache_opnum_cacheable(opnum) ||
4836 is_my_own_sam_domain(domain) ||
4837 is_builtin_domain(domain)) {
4838 return;
4841 if (wcache->tdb == NULL) {
4842 return;
4845 if (!wcache_fetch_seqnum(domain->name, &dom_seqnum, &last_check)) {
4846 DEBUG(10, ("could not fetch seqnum for domain %s\n",
4847 domain->name));
4848 return;
4851 if (!wcache_ndr_key(talloc_tos(), domain->name, opnum, req, &key)) {
4852 return;
4855 timeout = time(NULL) + lp_winbind_cache_time();
4857 data.dsize = resp->length + 12;
4858 data.dptr = talloc_array(key.dptr, uint8_t, data.dsize);
4859 if (data.dptr == NULL) {
4860 goto done;
4863 SIVAL(data.dptr, 0, dom_seqnum);
4864 SBVAL(data.dptr, 4, timeout);
4865 memcpy(data.dptr + 12, resp->data, resp->length);
4867 tdb_store(wcache->tdb, key, data, 0);
4869 done:
4870 TALLOC_FREE(key.dptr);
4871 return;