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/>.
28 #include "tdb_validate.h"
29 #include "../libcli/auth/libcli_auth.h"
30 #include "../librpc/gen_ndr/ndr_wbint.h"
34 #define DBGC_CLASS DBGC_WINBIND
36 #define WINBINDD_CACHE_VERSION 1
37 #define WINBINDD_CACHE_VERSION_KEYSTR "WINBINDD_CACHE_VERSION"
39 extern struct winbindd_methods reconnect_methods;
41 extern struct winbindd_methods ads_methods;
43 extern struct winbindd_methods builtin_passdb_methods;
44 extern struct winbindd_methods sam_passdb_methods;
47 * JRA. KEEP THIS LIST UP TO DATE IF YOU ADD CACHE ENTRIES.
48 * Here are the list of entry types that are *not* stored
49 * as form struct cache_entry in the cache.
52 static const char *non_centry_keys[] = {
57 WINBINDD_CACHE_VERSION_KEYSTR,
61 /************************************************************************
62 Is this key a non-centry type ?
63 ************************************************************************/
65 static bool is_non_centry_key(TDB_DATA kbuf)
69 if (kbuf.dptr == NULL || kbuf.dsize == 0) {
72 for (i = 0; non_centry_keys[i] != NULL; i++) {
73 size_t namelen = strlen(non_centry_keys[i]);
74 if (kbuf.dsize < namelen) {
77 if (strncmp(non_centry_keys[i], (const char *)kbuf.dptr, namelen) == 0) {
84 /* Global online/offline state - False when online. winbindd starts up online
85 and sets this to true if the first query fails and there's an entry in
86 the cache tdb telling us to stay offline. */
88 static bool global_winbindd_offline_state;
90 struct winbind_cache {
96 uint32 sequence_number;
101 void (*smb_panic_fn)(const char *const why) = smb_panic;
103 #define WINBINDD_MAX_CACHE_SIZE (50*1024*1024)
105 static struct winbind_cache *wcache;
107 void winbindd_check_cache_size(time_t t)
109 static time_t last_check_time;
112 if (last_check_time == (time_t)0)
115 if (t - last_check_time < 60 && t - last_check_time > 0)
118 if (wcache == NULL || wcache->tdb == NULL) {
119 DEBUG(0, ("Unable to check size of tdb cache - cache not open !\n"));
123 if (fstat(tdb_fd(wcache->tdb), &st) == -1) {
124 DEBUG(0, ("Unable to check size of tdb cache %s!\n", strerror(errno) ));
128 if (st.st_size > WINBINDD_MAX_CACHE_SIZE) {
129 DEBUG(10,("flushing cache due to size (%lu) > (%lu)\n",
130 (unsigned long)st.st_size,
131 (unsigned long)WINBINDD_MAX_CACHE_SIZE));
132 wcache_flush_cache();
136 /* get the winbind_cache structure */
137 static struct winbind_cache *get_cache(struct winbindd_domain *domain)
139 struct winbind_cache *ret = wcache;
141 /* We have to know what type of domain we are dealing with first. */
143 if (domain->internal) {
144 domain->backend = &builtin_passdb_methods;
145 domain->initialized = True;
148 if (strequal(domain->name, get_global_sam_name()) &&
149 sid_equal(&domain->sid, get_global_sam_sid())) {
150 domain->backend = &sam_passdb_methods;
151 domain->initialized = True;
154 if ( !domain->initialized ) {
155 init_dc_connection( domain );
159 OK. listen up becasue I'm only going to say this once.
160 We have the following scenarios to consider
161 (a) trusted AD domains on a Samba DC,
162 (b) trusted AD domains and we are joined to a non-kerberos domain
163 (c) trusted AD domains and we are joined to a kerberos (AD) domain
165 For (a) we can always contact the trusted domain using krb5
166 since we have the domain trust account password
168 For (b) we can only use RPC since we have no way of
169 getting a krb5 ticket in our own domain
171 For (c) we can always use krb5 since we have a kerberos trust
176 if (!domain->backend) {
178 struct winbindd_domain *our_domain = domain;
180 /* find our domain first so we can figure out if we
181 are joined to a kerberized domain */
183 if ( !domain->primary )
184 our_domain = find_our_domain();
186 if ((our_domain->active_directory || IS_DC)
187 && domain->active_directory
188 && !lp_winbind_rpc_only()) {
189 DEBUG(5,("get_cache: Setting ADS methods for domain %s\n", domain->name));
190 domain->backend = &ads_methods;
192 #endif /* HAVE_ADS */
193 DEBUG(5,("get_cache: Setting MS-RPC methods for domain %s\n", domain->name));
194 domain->backend = &reconnect_methods;
197 #endif /* HAVE_ADS */
203 ret = SMB_XMALLOC_P(struct winbind_cache);
207 wcache_flush_cache();
213 free a centry structure
215 static void centry_free(struct cache_entry *centry)
219 SAFE_FREE(centry->data);
223 static bool centry_check_bytes(struct cache_entry *centry, size_t nbytes)
225 if (centry->len - centry->ofs < nbytes) {
226 DEBUG(0,("centry corruption? needed %u bytes, have %d\n",
227 (unsigned int)nbytes,
228 centry->len - centry->ofs));
235 pull a uint32 from a cache entry
237 static uint32 centry_uint32(struct cache_entry *centry)
241 if (!centry_check_bytes(centry, 4)) {
242 smb_panic_fn("centry_uint32");
244 ret = IVAL(centry->data, centry->ofs);
250 pull a uint16 from a cache entry
252 static uint16 centry_uint16(struct cache_entry *centry)
255 if (!centry_check_bytes(centry, 2)) {
256 smb_panic_fn("centry_uint16");
258 ret = CVAL(centry->data, centry->ofs);
264 pull a uint8 from a cache entry
266 static uint8 centry_uint8(struct cache_entry *centry)
269 if (!centry_check_bytes(centry, 1)) {
270 smb_panic_fn("centry_uint8");
272 ret = CVAL(centry->data, centry->ofs);
278 pull a NTTIME from a cache entry
280 static NTTIME centry_nttime(struct cache_entry *centry)
283 if (!centry_check_bytes(centry, 8)) {
284 smb_panic_fn("centry_nttime");
286 ret = IVAL(centry->data, centry->ofs);
288 ret += (uint64_t)IVAL(centry->data, centry->ofs) << 32;
294 pull a time_t from a cache entry. time_t stored portably as a 64-bit time.
296 static time_t centry_time(struct cache_entry *centry)
298 return (time_t)centry_nttime(centry);
301 /* pull a string from a cache entry, using the supplied
304 static char *centry_string(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
309 len = centry_uint8(centry);
312 /* a deliberate NULL string */
316 if (!centry_check_bytes(centry, (size_t)len)) {
317 smb_panic_fn("centry_string");
320 ret = TALLOC_ARRAY(mem_ctx, char, len+1);
322 smb_panic_fn("centry_string out of memory\n");
324 memcpy(ret,centry->data + centry->ofs, len);
330 /* pull a hash16 from a cache entry, using the supplied
333 static char *centry_hash16(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
338 len = centry_uint8(centry);
341 DEBUG(0,("centry corruption? hash len (%u) != 16\n",
346 if (!centry_check_bytes(centry, 16)) {
350 ret = TALLOC_ARRAY(mem_ctx, char, 16);
352 smb_panic_fn("centry_hash out of memory\n");
354 memcpy(ret,centry->data + centry->ofs, 16);
359 /* pull a sid from a cache entry, using the supplied
362 static bool centry_sid(struct cache_entry *centry, struct dom_sid *sid)
367 sid_string = centry_string(centry, talloc_tos());
368 if (sid_string == NULL) {
371 ret = string_to_sid(sid, sid_string);
372 TALLOC_FREE(sid_string);
378 pull a NTSTATUS from a cache entry
380 static NTSTATUS centry_ntstatus(struct cache_entry *centry)
384 status = NT_STATUS(centry_uint32(centry));
389 /* the server is considered down if it can't give us a sequence number */
390 static bool wcache_server_down(struct winbindd_domain *domain)
397 ret = (domain->sequence_number == DOM_SEQUENCE_NONE);
400 DEBUG(10,("wcache_server_down: server for Domain %s down\n",
405 static bool wcache_fetch_seqnum(const char *domain_name, uint32_t *seqnum,
406 uint32_t *last_seq_check)
411 if (wcache->tdb == NULL) {
412 DEBUG(10,("wcache_fetch_seqnum: tdb == NULL\n"));
416 key = talloc_asprintf(talloc_tos(), "SEQNUM/%s", domain_name);
418 DEBUG(10, ("talloc failed\n"));
422 data = tdb_fetch_bystring(wcache->tdb, key);
425 if (data.dptr == NULL) {
426 DEBUG(10, ("wcache_fetch_seqnum: %s not found\n",
430 if (data.dsize != 8) {
431 DEBUG(10, ("wcache_fetch_seqnum: invalid data size %d\n",
433 SAFE_FREE(data.dptr);
437 *seqnum = IVAL(data.dptr, 0);
438 *last_seq_check = IVAL(data.dptr, 4);
439 SAFE_FREE(data.dptr);
444 static NTSTATUS fetch_cache_seqnum( struct winbindd_domain *domain, time_t now )
446 uint32 last_check, time_diff;
448 if (!wcache_fetch_seqnum(domain->name, &domain->sequence_number,
450 return NT_STATUS_UNSUCCESSFUL;
452 domain->last_seq_check = last_check;
454 /* have we expired? */
456 time_diff = now - domain->last_seq_check;
457 if ( time_diff > lp_winbind_cache_time() ) {
458 DEBUG(10,("fetch_cache_seqnum: timeout [%s][%u @ %u]\n",
459 domain->name, domain->sequence_number,
460 (uint32)domain->last_seq_check));
461 return NT_STATUS_UNSUCCESSFUL;
464 DEBUG(10,("fetch_cache_seqnum: success [%s][%u @ %u]\n",
465 domain->name, domain->sequence_number,
466 (uint32)domain->last_seq_check));
471 bool wcache_store_seqnum(const char *domain_name, uint32_t seqnum,
472 time_t last_seq_check)
478 if (wcache->tdb == NULL) {
479 DEBUG(10, ("wcache_store_seqnum: wcache->tdb == NULL\n"));
483 key_str = talloc_asprintf(talloc_tos(), "SEQNUM/%s", domain_name);
484 if (key_str == NULL) {
485 DEBUG(10, ("talloc_asprintf failed\n"));
489 SIVAL(buf, 0, seqnum);
490 SIVAL(buf, 4, last_seq_check);
492 ret = tdb_store_bystring(wcache->tdb, key_str,
493 make_tdb_data(buf, sizeof(buf)), TDB_REPLACE);
494 TALLOC_FREE(key_str);
496 DEBUG(10, ("tdb_store_bystring failed: %s\n",
497 tdb_errorstr(wcache->tdb)));
498 TALLOC_FREE(key_str);
502 DEBUG(10, ("wcache_store_seqnum: success [%s][%u @ %u]\n",
503 domain_name, seqnum, (unsigned)last_seq_check));
508 static bool store_cache_seqnum( struct winbindd_domain *domain )
510 return wcache_store_seqnum(domain->name, domain->sequence_number,
511 domain->last_seq_check);
515 refresh the domain sequence number. If force is true
516 then always refresh it, no matter how recently we fetched it
519 static void refresh_sequence_number(struct winbindd_domain *domain, bool force)
523 time_t t = time(NULL);
524 unsigned cache_time = lp_winbind_cache_time();
526 if (is_domain_offline(domain)) {
532 #if 0 /* JERRY -- disable as the default cache time is now 5 minutes */
533 /* trying to reconnect is expensive, don't do it too often */
534 if (domain->sequence_number == DOM_SEQUENCE_NONE) {
539 time_diff = t - domain->last_seq_check;
541 /* see if we have to refetch the domain sequence number */
542 if (!force && (time_diff < cache_time) &&
543 (domain->sequence_number != DOM_SEQUENCE_NONE) &&
544 NT_STATUS_IS_OK(domain->last_status)) {
545 DEBUG(10, ("refresh_sequence_number: %s time ok\n", domain->name));
549 /* try to get the sequence number from the tdb cache first */
550 /* this will update the timestamp as well */
552 status = fetch_cache_seqnum( domain, t );
553 if (NT_STATUS_IS_OK(status) &&
554 (domain->sequence_number != DOM_SEQUENCE_NONE) &&
555 NT_STATUS_IS_OK(domain->last_status)) {
559 /* important! make sure that we know if this is a native
560 mode domain or not. And that we can contact it. */
562 if ( winbindd_can_contact_domain( domain ) ) {
563 status = domain->backend->sequence_number(domain,
564 &domain->sequence_number);
566 /* just use the current time */
567 status = NT_STATUS_OK;
568 domain->sequence_number = time(NULL);
572 /* the above call could have set our domain->backend to NULL when
573 * coming from offline to online mode, make sure to reinitialize the
574 * backend - Guenther */
577 if (!NT_STATUS_IS_OK(status)) {
578 DEBUG(10,("refresh_sequence_number: failed with %s\n", nt_errstr(status)));
579 domain->sequence_number = DOM_SEQUENCE_NONE;
582 domain->last_status = status;
583 domain->last_seq_check = time(NULL);
585 /* save the new sequence number in the cache */
586 store_cache_seqnum( domain );
589 DEBUG(10, ("refresh_sequence_number: %s seq number is now %d\n",
590 domain->name, domain->sequence_number));
596 decide if a cache entry has expired
598 static bool centry_expired(struct winbindd_domain *domain, const char *keystr, struct cache_entry *centry)
600 /* If we've been told to be offline - stay in that state... */
601 if (lp_winbind_offline_logon() && global_winbindd_offline_state) {
602 DEBUG(10,("centry_expired: Key %s for domain %s valid as winbindd is globally offline.\n",
603 keystr, domain->name ));
607 /* when the domain is offline return the cached entry.
608 * This deals with transient offline states... */
610 if (!domain->online) {
611 DEBUG(10,("centry_expired: Key %s for domain %s valid as domain is offline.\n",
612 keystr, domain->name ));
616 /* if the server is OK and our cache entry came from when it was down then
617 the entry is invalid */
618 if ((domain->sequence_number != DOM_SEQUENCE_NONE) &&
619 (centry->sequence_number == DOM_SEQUENCE_NONE)) {
620 DEBUG(10,("centry_expired: Key %s for domain %s invalid sequence.\n",
621 keystr, domain->name ));
625 /* if the server is down or the cache entry is not older than the
626 current sequence number then it is OK */
627 if (wcache_server_down(domain) ||
628 centry->sequence_number == domain->sequence_number) {
629 DEBUG(10,("centry_expired: Key %s for domain %s is good.\n",
630 keystr, domain->name ));
634 DEBUG(10,("centry_expired: Key %s for domain %s expired\n",
635 keystr, domain->name ));
641 static struct cache_entry *wcache_fetch_raw(char *kstr)
644 struct cache_entry *centry;
647 key = string_tdb_data(kstr);
648 data = tdb_fetch(wcache->tdb, key);
654 centry = SMB_XMALLOC_P(struct cache_entry);
655 centry->data = (unsigned char *)data.dptr;
656 centry->len = data.dsize;
659 if (centry->len < 8) {
660 /* huh? corrupt cache? */
661 DEBUG(10,("wcache_fetch_raw: Corrupt cache for key %s (len < 8) ?\n", kstr));
666 centry->status = centry_ntstatus(centry);
667 centry->sequence_number = centry_uint32(centry);
672 static bool is_my_own_sam_domain(struct winbindd_domain *domain)
674 if (strequal(domain->name, get_global_sam_name()) &&
675 sid_equal(&domain->sid, get_global_sam_sid())) {
682 static bool is_builtin_domain(struct winbindd_domain *domain)
684 if (strequal(domain->name, "BUILTIN") &&
685 sid_equal(&domain->sid, &global_sid_Builtin)) {
693 fetch an entry from the cache, with a varargs key. auto-fetch the sequence
694 number and return status
696 static struct cache_entry *wcache_fetch(struct winbind_cache *cache,
697 struct winbindd_domain *domain,
698 const char *format, ...) PRINTF_ATTRIBUTE(3,4);
699 static struct cache_entry *wcache_fetch(struct winbind_cache *cache,
700 struct winbindd_domain *domain,
701 const char *format, ...)
705 struct cache_entry *centry;
707 if (!winbindd_use_cache() ||
708 is_my_own_sam_domain(domain) ||
709 is_builtin_domain(domain)) {
713 refresh_sequence_number(domain, false);
715 va_start(ap, format);
716 smb_xvasprintf(&kstr, format, ap);
719 centry = wcache_fetch_raw(kstr);
720 if (centry == NULL) {
725 if (centry_expired(domain, kstr, centry)) {
727 DEBUG(10,("wcache_fetch: entry %s expired for domain %s\n",
728 kstr, domain->name ));
735 DEBUG(10,("wcache_fetch: returning entry %s for domain %s\n",
736 kstr, domain->name ));
742 static void wcache_delete(const char *format, ...) PRINTF_ATTRIBUTE(1,2);
743 static void wcache_delete(const char *format, ...)
749 va_start(ap, format);
750 smb_xvasprintf(&kstr, format, ap);
753 key = string_tdb_data(kstr);
755 tdb_delete(wcache->tdb, key);
760 make sure we have at least len bytes available in a centry
762 static void centry_expand(struct cache_entry *centry, uint32 len)
764 if (centry->len - centry->ofs >= len)
767 centry->data = SMB_REALLOC_ARRAY(centry->data, unsigned char,
770 DEBUG(0,("out of memory: needed %d bytes in centry_expand\n", centry->len));
771 smb_panic_fn("out of memory in centry_expand");
776 push a uint32 into a centry
778 static void centry_put_uint32(struct cache_entry *centry, uint32 v)
780 centry_expand(centry, 4);
781 SIVAL(centry->data, centry->ofs, v);
786 push a uint16 into a centry
788 static void centry_put_uint16(struct cache_entry *centry, uint16 v)
790 centry_expand(centry, 2);
791 SIVAL(centry->data, centry->ofs, v);
796 push a uint8 into a centry
798 static void centry_put_uint8(struct cache_entry *centry, uint8 v)
800 centry_expand(centry, 1);
801 SCVAL(centry->data, centry->ofs, v);
806 push a string into a centry
808 static void centry_put_string(struct cache_entry *centry, const char *s)
813 /* null strings are marked as len 0xFFFF */
814 centry_put_uint8(centry, 0xFF);
819 /* can't handle more than 254 char strings. Truncating is probably best */
821 DEBUG(10,("centry_put_string: truncating len (%d) to: 254\n", len));
824 centry_put_uint8(centry, len);
825 centry_expand(centry, len);
826 memcpy(centry->data + centry->ofs, s, len);
831 push a 16 byte hash into a centry - treat as 16 byte string.
833 static void centry_put_hash16(struct cache_entry *centry, const uint8 val[16])
835 centry_put_uint8(centry, 16);
836 centry_expand(centry, 16);
837 memcpy(centry->data + centry->ofs, val, 16);
841 static void centry_put_sid(struct cache_entry *centry, const struct dom_sid *sid)
844 centry_put_string(centry, sid_to_fstring(sid_string, sid));
849 put NTSTATUS into a centry
851 static void centry_put_ntstatus(struct cache_entry *centry, NTSTATUS status)
853 uint32 status_value = NT_STATUS_V(status);
854 centry_put_uint32(centry, status_value);
859 push a NTTIME into a centry
861 static void centry_put_nttime(struct cache_entry *centry, NTTIME nt)
863 centry_expand(centry, 8);
864 SIVAL(centry->data, centry->ofs, nt & 0xFFFFFFFF);
866 SIVAL(centry->data, centry->ofs, nt >> 32);
871 push a time_t into a centry - use a 64 bit size.
872 NTTIME here is being used as a convenient 64-bit size.
874 static void centry_put_time(struct cache_entry *centry, time_t t)
876 NTTIME nt = (NTTIME)t;
877 centry_put_nttime(centry, nt);
881 start a centry for output. When finished, call centry_end()
883 struct cache_entry *centry_start(struct winbindd_domain *domain, NTSTATUS status)
885 struct cache_entry *centry;
890 centry = SMB_XMALLOC_P(struct cache_entry);
892 centry->len = 8192; /* reasonable default */
893 centry->data = SMB_XMALLOC_ARRAY(uint8, centry->len);
895 centry->sequence_number = domain->sequence_number;
896 centry_put_ntstatus(centry, status);
897 centry_put_uint32(centry, centry->sequence_number);
902 finish a centry and write it to the tdb
904 static void centry_end(struct cache_entry *centry, const char *format, ...) PRINTF_ATTRIBUTE(2,3);
905 static void centry_end(struct cache_entry *centry, const char *format, ...)
911 if (!winbindd_use_cache()) {
915 va_start(ap, format);
916 smb_xvasprintf(&kstr, format, ap);
919 key = string_tdb_data(kstr);
920 data.dptr = centry->data;
921 data.dsize = centry->ofs;
923 tdb_store(wcache->tdb, key, data, TDB_REPLACE);
927 static void wcache_save_name_to_sid(struct winbindd_domain *domain,
928 NTSTATUS status, const char *domain_name,
929 const char *name, const struct dom_sid *sid,
930 enum lsa_SidType type)
932 struct cache_entry *centry;
935 centry = centry_start(domain, status);
938 centry_put_uint32(centry, type);
939 centry_put_sid(centry, sid);
940 fstrcpy(uname, name);
942 centry_end(centry, "NS/%s/%s", domain_name, uname);
943 DEBUG(10,("wcache_save_name_to_sid: %s\\%s -> %s (%s)\n", domain_name,
944 uname, sid_string_dbg(sid), nt_errstr(status)));
948 static void wcache_save_sid_to_name(struct winbindd_domain *domain, NTSTATUS status,
949 const struct dom_sid *sid, const char *domain_name, const char *name, enum lsa_SidType type)
951 struct cache_entry *centry;
954 centry = centry_start(domain, status);
958 if (NT_STATUS_IS_OK(status)) {
959 centry_put_uint32(centry, type);
960 centry_put_string(centry, domain_name);
961 centry_put_string(centry, name);
964 centry_end(centry, "SN/%s", sid_to_fstring(sid_string, sid));
965 DEBUG(10,("wcache_save_sid_to_name: %s -> %s (%s)\n", sid_string,
966 name, nt_errstr(status)));
971 static void wcache_save_user(struct winbindd_domain *domain, NTSTATUS status,
972 struct wbint_userinfo *info)
974 struct cache_entry *centry;
977 if (is_null_sid(&info->user_sid)) {
981 centry = centry_start(domain, status);
984 centry_put_string(centry, info->acct_name);
985 centry_put_string(centry, info->full_name);
986 centry_put_string(centry, info->homedir);
987 centry_put_string(centry, info->shell);
988 centry_put_uint32(centry, info->primary_gid);
989 centry_put_sid(centry, &info->user_sid);
990 centry_put_sid(centry, &info->group_sid);
991 centry_end(centry, "U/%s", sid_to_fstring(sid_string,
993 DEBUG(10,("wcache_save_user: %s (acct_name %s)\n", sid_string, info->acct_name));
997 static void wcache_save_lockout_policy(struct winbindd_domain *domain,
999 struct samr_DomInfo12 *lockout_policy)
1001 struct cache_entry *centry;
1003 centry = centry_start(domain, status);
1007 centry_put_nttime(centry, lockout_policy->lockout_duration);
1008 centry_put_nttime(centry, lockout_policy->lockout_window);
1009 centry_put_uint16(centry, lockout_policy->lockout_threshold);
1011 centry_end(centry, "LOC_POL/%s", domain->name);
1013 DEBUG(10,("wcache_save_lockout_policy: %s\n", domain->name));
1015 centry_free(centry);
1020 static void wcache_save_password_policy(struct winbindd_domain *domain,
1022 struct samr_DomInfo1 *policy)
1024 struct cache_entry *centry;
1026 centry = centry_start(domain, status);
1030 centry_put_uint16(centry, policy->min_password_length);
1031 centry_put_uint16(centry, policy->password_history_length);
1032 centry_put_uint32(centry, policy->password_properties);
1033 centry_put_nttime(centry, policy->max_password_age);
1034 centry_put_nttime(centry, policy->min_password_age);
1036 centry_end(centry, "PWD_POL/%s", domain->name);
1038 DEBUG(10,("wcache_save_password_policy: %s\n", domain->name));
1040 centry_free(centry);
1043 /***************************************************************************
1044 ***************************************************************************/
1046 static void wcache_save_username_alias(struct winbindd_domain *domain,
1048 const char *name, const char *alias)
1050 struct cache_entry *centry;
1053 if ( (centry = centry_start(domain, status)) == NULL )
1056 centry_put_string( centry, alias );
1058 fstrcpy(uname, name);
1060 centry_end(centry, "NSS/NA/%s", uname);
1062 DEBUG(10,("wcache_save_username_alias: %s -> %s\n", name, alias ));
1064 centry_free(centry);
1067 static void wcache_save_alias_username(struct winbindd_domain *domain,
1069 const char *alias, const char *name)
1071 struct cache_entry *centry;
1074 if ( (centry = centry_start(domain, status)) == NULL )
1077 centry_put_string( centry, name );
1079 fstrcpy(uname, alias);
1081 centry_end(centry, "NSS/AN/%s", uname);
1083 DEBUG(10,("wcache_save_alias_username: %s -> %s\n", alias, name ));
1085 centry_free(centry);
1088 /***************************************************************************
1089 ***************************************************************************/
1091 NTSTATUS resolve_username_to_alias( TALLOC_CTX *mem_ctx,
1092 struct winbindd_domain *domain,
1093 const char *name, char **alias )
1095 struct winbind_cache *cache = get_cache(domain);
1096 struct cache_entry *centry = NULL;
1100 if ( domain->internal )
1101 return NT_STATUS_NOT_SUPPORTED;
1106 if ( (upper_name = SMB_STRDUP(name)) == NULL )
1107 return NT_STATUS_NO_MEMORY;
1108 strupper_m(upper_name);
1110 centry = wcache_fetch(cache, domain, "NSS/NA/%s", upper_name);
1112 SAFE_FREE( upper_name );
1117 status = centry->status;
1119 if (!NT_STATUS_IS_OK(status)) {
1120 centry_free(centry);
1124 *alias = centry_string( centry, mem_ctx );
1126 centry_free(centry);
1128 DEBUG(10,("resolve_username_to_alias: [Cached] - mapped %s to %s\n",
1129 name, *alias ? *alias : "(none)"));
1131 return (*alias) ? NT_STATUS_OK : NT_STATUS_OBJECT_NAME_NOT_FOUND;
1135 /* If its not in cache and we are offline, then fail */
1137 if ( get_global_winbindd_state_offline() || !domain->online ) {
1138 DEBUG(8,("resolve_username_to_alias: rejecting query "
1139 "in offline mode\n"));
1140 return NT_STATUS_NOT_FOUND;
1143 status = nss_map_to_alias( mem_ctx, domain->name, name, alias );
1145 if ( NT_STATUS_IS_OK( status ) ) {
1146 wcache_save_username_alias(domain, status, name, *alias);
1149 if ( NT_STATUS_EQUAL( status, NT_STATUS_NONE_MAPPED ) ) {
1150 wcache_save_username_alias(domain, status, name, "(NULL)");
1153 DEBUG(5,("resolve_username_to_alias: backend query returned %s\n",
1154 nt_errstr(status)));
1156 if ( NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ) {
1157 set_domain_offline( domain );
1163 /***************************************************************************
1164 ***************************************************************************/
1166 NTSTATUS resolve_alias_to_username( TALLOC_CTX *mem_ctx,
1167 struct winbindd_domain *domain,
1168 const char *alias, char **name )
1170 struct winbind_cache *cache = get_cache(domain);
1171 struct cache_entry *centry = NULL;
1175 if ( domain->internal )
1176 return NT_STATUS_NOT_SUPPORTED;
1181 if ( (upper_name = SMB_STRDUP(alias)) == NULL )
1182 return NT_STATUS_NO_MEMORY;
1183 strupper_m(upper_name);
1185 centry = wcache_fetch(cache, domain, "NSS/AN/%s", upper_name);
1187 SAFE_FREE( upper_name );
1192 status = centry->status;
1194 if (!NT_STATUS_IS_OK(status)) {
1195 centry_free(centry);
1199 *name = centry_string( centry, mem_ctx );
1201 centry_free(centry);
1203 DEBUG(10,("resolve_alias_to_username: [Cached] - mapped %s to %s\n",
1204 alias, *name ? *name : "(none)"));
1206 return (*name) ? NT_STATUS_OK : NT_STATUS_OBJECT_NAME_NOT_FOUND;
1210 /* If its not in cache and we are offline, then fail */
1212 if ( get_global_winbindd_state_offline() || !domain->online ) {
1213 DEBUG(8,("resolve_alias_to_username: rejecting query "
1214 "in offline mode\n"));
1215 return NT_STATUS_NOT_FOUND;
1218 /* an alias cannot contain a domain prefix or '@' */
1220 if (strchr(alias, '\\') || strchr(alias, '@')) {
1221 DEBUG(10,("resolve_alias_to_username: skipping fully "
1222 "qualified name %s\n", alias));
1223 return NT_STATUS_OBJECT_NAME_INVALID;
1226 status = nss_map_from_alias( mem_ctx, domain->name, alias, name );
1228 if ( NT_STATUS_IS_OK( status ) ) {
1229 wcache_save_alias_username( domain, status, alias, *name );
1232 if (NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) {
1233 wcache_save_alias_username(domain, status, alias, "(NULL)");
1236 DEBUG(5,("resolve_alias_to_username: backend query returned %s\n",
1237 nt_errstr(status)));
1239 if ( NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ) {
1240 set_domain_offline( domain );
1246 NTSTATUS wcache_cached_creds_exist(struct winbindd_domain *domain, const struct dom_sid *sid)
1248 struct winbind_cache *cache = get_cache(domain);
1250 fstring key_str, tmp;
1254 return NT_STATUS_INTERNAL_DB_ERROR;
1257 if (is_null_sid(sid)) {
1258 return NT_STATUS_INVALID_SID;
1261 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1262 return NT_STATUS_INVALID_SID;
1265 fstr_sprintf(key_str, "CRED/%s", sid_to_fstring(tmp, sid));
1267 data = tdb_fetch(cache->tdb, string_tdb_data(key_str));
1269 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1272 SAFE_FREE(data.dptr);
1273 return NT_STATUS_OK;
1276 /* Lookup creds for a SID - copes with old (unsalted) creds as well
1277 as new salted ones. */
1279 NTSTATUS wcache_get_creds(struct winbindd_domain *domain,
1280 TALLOC_CTX *mem_ctx,
1281 const struct dom_sid *sid,
1282 const uint8 **cached_nt_pass,
1283 const uint8 **cached_salt)
1285 struct winbind_cache *cache = get_cache(domain);
1286 struct cache_entry *centry = NULL;
1293 return NT_STATUS_INTERNAL_DB_ERROR;
1296 if (is_null_sid(sid)) {
1297 return NT_STATUS_INVALID_SID;
1300 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1301 return NT_STATUS_INVALID_SID;
1304 /* Try and get a salted cred first. If we can't
1305 fall back to an unsalted cred. */
1307 centry = wcache_fetch(cache, domain, "CRED/%s",
1308 sid_to_fstring(tmp, sid));
1310 DEBUG(10,("wcache_get_creds: entry for [CRED/%s] not found\n",
1311 sid_string_dbg(sid)));
1312 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1315 t = centry_time(centry);
1317 /* In the salted case this isn't actually the nt_hash itself,
1318 but the MD5 of the salt + nt_hash. Let the caller
1319 sort this out. It can tell as we only return the cached_salt
1320 if we are returning a salted cred. */
1322 *cached_nt_pass = (const uint8 *)centry_hash16(centry, mem_ctx);
1323 if (*cached_nt_pass == NULL) {
1326 sid_to_fstring(sidstr, sid);
1328 /* Bad (old) cred cache. Delete and pretend we
1330 DEBUG(0,("wcache_get_creds: bad entry for [CRED/%s] - deleting\n",
1332 wcache_delete("CRED/%s", sidstr);
1333 centry_free(centry);
1334 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1337 /* We only have 17 bytes more data in the salted cred case. */
1338 if (centry->len - centry->ofs == 17) {
1339 *cached_salt = (const uint8 *)centry_hash16(centry, mem_ctx);
1341 *cached_salt = NULL;
1344 dump_data_pw("cached_nt_pass", *cached_nt_pass, NT_HASH_LEN);
1346 dump_data_pw("cached_salt", *cached_salt, NT_HASH_LEN);
1349 status = centry->status;
1351 DEBUG(10,("wcache_get_creds: [Cached] - cached creds for user %s status: %s\n",
1352 sid_string_dbg(sid), nt_errstr(status) ));
1354 centry_free(centry);
1358 /* Store creds for a SID - only writes out new salted ones. */
1360 NTSTATUS wcache_save_creds(struct winbindd_domain *domain,
1361 TALLOC_CTX *mem_ctx,
1362 const struct dom_sid *sid,
1363 const uint8 nt_pass[NT_HASH_LEN])
1365 struct cache_entry *centry;
1368 uint8 cred_salt[NT_HASH_LEN];
1369 uint8 salted_hash[NT_HASH_LEN];
1371 if (is_null_sid(sid)) {
1372 return NT_STATUS_INVALID_SID;
1375 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1376 return NT_STATUS_INVALID_SID;
1379 centry = centry_start(domain, NT_STATUS_OK);
1381 return NT_STATUS_INTERNAL_DB_ERROR;
1384 dump_data_pw("nt_pass", nt_pass, NT_HASH_LEN);
1386 centry_put_time(centry, time(NULL));
1388 /* Create a salt and then salt the hash. */
1389 generate_random_buffer(cred_salt, NT_HASH_LEN);
1390 E_md5hash(cred_salt, nt_pass, salted_hash);
1392 centry_put_hash16(centry, salted_hash);
1393 centry_put_hash16(centry, cred_salt);
1394 centry_end(centry, "CRED/%s", sid_to_fstring(sid_string, sid));
1396 DEBUG(10,("wcache_save_creds: %s\n", sid_string));
1398 centry_free(centry);
1400 return NT_STATUS_OK;
1404 /* Query display info. This is the basic user list fn */
1405 static NTSTATUS query_user_list(struct winbindd_domain *domain,
1406 TALLOC_CTX *mem_ctx,
1407 uint32 *num_entries,
1408 struct wbint_userinfo **info)
1410 struct winbind_cache *cache = get_cache(domain);
1411 struct cache_entry *centry = NULL;
1413 unsigned int i, retry;
1414 bool old_status = domain->online;
1419 centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
1424 *num_entries = centry_uint32(centry);
1426 if (*num_entries == 0)
1429 (*info) = TALLOC_ARRAY(mem_ctx, struct wbint_userinfo, *num_entries);
1431 smb_panic_fn("query_user_list out of memory");
1433 for (i=0; i<(*num_entries); i++) {
1434 (*info)[i].acct_name = centry_string(centry, mem_ctx);
1435 (*info)[i].full_name = centry_string(centry, mem_ctx);
1436 (*info)[i].homedir = centry_string(centry, mem_ctx);
1437 (*info)[i].shell = centry_string(centry, mem_ctx);
1438 centry_sid(centry, &(*info)[i].user_sid);
1439 centry_sid(centry, &(*info)[i].group_sid);
1443 status = centry->status;
1445 DEBUG(10,("query_user_list: [Cached] - cached list for domain %s status: %s\n",
1446 domain->name, nt_errstr(status) ));
1448 centry_free(centry);
1455 /* Return status value returned by seq number check */
1457 if (!NT_STATUS_IS_OK(domain->last_status))
1458 return domain->last_status;
1460 /* Put the query_user_list() in a retry loop. There appears to be
1461 * some bug either with Windows 2000 or Samba's handling of large
1462 * rpc replies. This manifests itself as sudden disconnection
1463 * at a random point in the enumeration of a large (60k) user list.
1464 * The retry loop simply tries the operation again. )-: It's not
1465 * pretty but an acceptable workaround until we work out what the
1466 * real problem is. */
1471 DEBUG(10,("query_user_list: [Cached] - doing backend query for list for domain %s\n",
1474 status = domain->backend->query_user_list(domain, mem_ctx, num_entries, info);
1475 if (!NT_STATUS_IS_OK(status)) {
1476 DEBUG(3, ("query_user_list: returned 0x%08x, "
1477 "retrying\n", NT_STATUS_V(status)));
1479 if (NT_STATUS_EQUAL(status, NT_STATUS_UNSUCCESSFUL)) {
1480 DEBUG(3, ("query_user_list: flushing "
1481 "connection cache\n"));
1482 invalidate_cm_connection(&domain->conn);
1484 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1485 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1486 if (!domain->internal && old_status) {
1487 set_domain_offline(domain);
1489 /* store partial response. */
1490 if (*num_entries > 0) {
1492 * humm, what about the status used for cache?
1493 * Should it be NT_STATUS_OK?
1498 * domain is offline now, and there is no user entries,
1499 * try to fetch from cache again.
1501 if (cache->tdb && !domain->online && !domain->internal && old_status) {
1502 centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
1503 /* partial response... */
1507 goto do_fetch_cache;
1514 } while (NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_UNSUCCESSFUL) &&
1518 refresh_sequence_number(domain, false);
1519 if (!NT_STATUS_IS_OK(status)) {
1522 centry = centry_start(domain, status);
1525 centry_put_uint32(centry, *num_entries);
1526 for (i=0; i<(*num_entries); i++) {
1527 centry_put_string(centry, (*info)[i].acct_name);
1528 centry_put_string(centry, (*info)[i].full_name);
1529 centry_put_string(centry, (*info)[i].homedir);
1530 centry_put_string(centry, (*info)[i].shell);
1531 centry_put_sid(centry, &(*info)[i].user_sid);
1532 centry_put_sid(centry, &(*info)[i].group_sid);
1533 if (domain->backend && domain->backend->consistent) {
1534 /* when the backend is consistent we can pre-prime some mappings */
1535 wcache_save_name_to_sid(domain, NT_STATUS_OK,
1537 (*info)[i].acct_name,
1538 &(*info)[i].user_sid,
1540 wcache_save_sid_to_name(domain, NT_STATUS_OK,
1541 &(*info)[i].user_sid,
1543 (*info)[i].acct_name,
1545 wcache_save_user(domain, NT_STATUS_OK, &(*info)[i]);
1548 centry_end(centry, "UL/%s", domain->name);
1549 centry_free(centry);
1555 /* list all domain groups */
1556 static NTSTATUS enum_dom_groups(struct winbindd_domain *domain,
1557 TALLOC_CTX *mem_ctx,
1558 uint32 *num_entries,
1559 struct acct_info **info)
1561 struct winbind_cache *cache = get_cache(domain);
1562 struct cache_entry *centry = NULL;
1567 old_status = domain->online;
1571 centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
1576 *num_entries = centry_uint32(centry);
1578 if (*num_entries == 0)
1581 (*info) = TALLOC_ARRAY(mem_ctx, struct acct_info, *num_entries);
1583 smb_panic_fn("enum_dom_groups out of memory");
1585 for (i=0; i<(*num_entries); i++) {
1586 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1587 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1588 (*info)[i].rid = centry_uint32(centry);
1592 status = centry->status;
1594 DEBUG(10,("enum_dom_groups: [Cached] - cached list for domain %s status: %s\n",
1595 domain->name, nt_errstr(status) ));
1597 centry_free(centry);
1604 /* Return status value returned by seq number check */
1606 if (!NT_STATUS_IS_OK(domain->last_status))
1607 return domain->last_status;
1609 DEBUG(10,("enum_dom_groups: [Cached] - doing backend query for list for domain %s\n",
1612 status = domain->backend->enum_dom_groups(domain, mem_ctx, num_entries, info);
1614 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1615 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1616 if (!domain->internal && old_status) {
1617 set_domain_offline(domain);
1621 !domain->internal &&
1623 centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
1625 goto do_fetch_cache;
1630 refresh_sequence_number(domain, false);
1631 if (!NT_STATUS_IS_OK(status)) {
1634 centry = centry_start(domain, status);
1637 centry_put_uint32(centry, *num_entries);
1638 for (i=0; i<(*num_entries); i++) {
1639 centry_put_string(centry, (*info)[i].acct_name);
1640 centry_put_string(centry, (*info)[i].acct_desc);
1641 centry_put_uint32(centry, (*info)[i].rid);
1643 centry_end(centry, "GL/%s/domain", domain->name);
1644 centry_free(centry);
1650 /* list all domain groups */
1651 static NTSTATUS enum_local_groups(struct winbindd_domain *domain,
1652 TALLOC_CTX *mem_ctx,
1653 uint32 *num_entries,
1654 struct acct_info **info)
1656 struct winbind_cache *cache = get_cache(domain);
1657 struct cache_entry *centry = NULL;
1662 old_status = domain->online;
1666 centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
1671 *num_entries = centry_uint32(centry);
1673 if (*num_entries == 0)
1676 (*info) = TALLOC_ARRAY(mem_ctx, struct acct_info, *num_entries);
1678 smb_panic_fn("enum_dom_groups out of memory");
1680 for (i=0; i<(*num_entries); i++) {
1681 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1682 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1683 (*info)[i].rid = centry_uint32(centry);
1688 /* If we are returning cached data and the domain controller
1689 is down then we don't know whether the data is up to date
1690 or not. Return NT_STATUS_MORE_PROCESSING_REQUIRED to
1693 if (wcache_server_down(domain)) {
1694 DEBUG(10, ("enum_local_groups: returning cached user list and server was down\n"));
1695 status = NT_STATUS_MORE_PROCESSING_REQUIRED;
1697 status = centry->status;
1699 DEBUG(10,("enum_local_groups: [Cached] - cached list for domain %s status: %s\n",
1700 domain->name, nt_errstr(status) ));
1702 centry_free(centry);
1709 /* Return status value returned by seq number check */
1711 if (!NT_STATUS_IS_OK(domain->last_status))
1712 return domain->last_status;
1714 DEBUG(10,("enum_local_groups: [Cached] - doing backend query for list for domain %s\n",
1717 status = domain->backend->enum_local_groups(domain, mem_ctx, num_entries, info);
1719 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1720 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1721 if (!domain->internal && old_status) {
1722 set_domain_offline(domain);
1725 !domain->internal &&
1728 centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
1730 goto do_fetch_cache;
1735 refresh_sequence_number(domain, false);
1736 if (!NT_STATUS_IS_OK(status)) {
1739 centry = centry_start(domain, status);
1742 centry_put_uint32(centry, *num_entries);
1743 for (i=0; i<(*num_entries); i++) {
1744 centry_put_string(centry, (*info)[i].acct_name);
1745 centry_put_string(centry, (*info)[i].acct_desc);
1746 centry_put_uint32(centry, (*info)[i].rid);
1748 centry_end(centry, "GL/%s/local", domain->name);
1749 centry_free(centry);
1755 NTSTATUS wcache_name_to_sid(struct winbindd_domain *domain,
1756 const char *domain_name,
1758 struct dom_sid *sid,
1759 enum lsa_SidType *type)
1761 struct winbind_cache *cache = get_cache(domain);
1762 struct cache_entry *centry;
1766 if (cache->tdb == NULL) {
1767 return NT_STATUS_NOT_FOUND;
1770 uname = talloc_strdup_upper(talloc_tos(), name);
1771 if (uname == NULL) {
1772 return NT_STATUS_NO_MEMORY;
1775 centry = wcache_fetch(cache, domain, "NS/%s/%s", domain_name, uname);
1777 if (centry == NULL) {
1778 return NT_STATUS_NOT_FOUND;
1781 status = centry->status;
1782 if (NT_STATUS_IS_OK(status)) {
1783 *type = (enum lsa_SidType)centry_uint32(centry);
1784 centry_sid(centry, sid);
1787 DEBUG(10,("name_to_sid: [Cached] - cached name for domain %s status: "
1788 "%s\n", domain->name, nt_errstr(status) ));
1790 centry_free(centry);
1794 /* convert a single name to a sid in a domain */
1795 static NTSTATUS name_to_sid(struct winbindd_domain *domain,
1796 TALLOC_CTX *mem_ctx,
1797 const char *domain_name,
1800 struct dom_sid *sid,
1801 enum lsa_SidType *type)
1806 old_status = domain->online;
1808 status = wcache_name_to_sid(domain, domain_name, name, sid, type);
1809 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
1815 /* If the seq number check indicated that there is a problem
1816 * with this DC, then return that status... except for
1817 * access_denied. This is special because the dc may be in
1818 * "restrict anonymous = 1" mode, in which case it will deny
1819 * most unauthenticated operations, but *will* allow the LSA
1820 * name-to-sid that we try as a fallback. */
1822 if (!(NT_STATUS_IS_OK(domain->last_status)
1823 || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1824 return domain->last_status;
1826 DEBUG(10,("name_to_sid: [Cached] - doing backend query for name for domain %s\n",
1829 status = domain->backend->name_to_sid(domain, mem_ctx, domain_name,
1830 name, flags, sid, type);
1832 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1833 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1834 if (!domain->internal && old_status) {
1835 set_domain_offline(domain);
1837 if (!domain->internal &&
1840 NTSTATUS cache_status;
1841 cache_status = wcache_name_to_sid(domain, domain_name, name, sid, type);
1842 return cache_status;
1846 refresh_sequence_number(domain, false);
1848 if (domain->online &&
1849 (NT_STATUS_IS_OK(status) || NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED))) {
1850 wcache_save_name_to_sid(domain, status, domain_name, name, sid, *type);
1852 /* Only save the reverse mapping if this was not a UPN */
1853 if (!strchr(name, '@')) {
1854 strupper_m(CONST_DISCARD(char *,domain_name));
1855 strlower_m(CONST_DISCARD(char *,name));
1856 wcache_save_sid_to_name(domain, status, sid, domain_name, name, *type);
1863 NTSTATUS wcache_sid_to_name(struct winbindd_domain *domain,
1864 const struct dom_sid *sid,
1865 TALLOC_CTX *mem_ctx,
1868 enum lsa_SidType *type)
1870 struct winbind_cache *cache = get_cache(domain);
1871 struct cache_entry *centry;
1875 if (cache->tdb == NULL) {
1876 return NT_STATUS_NOT_FOUND;
1879 sid_string = sid_string_tos(sid);
1880 if (sid_string == NULL) {
1881 return NT_STATUS_NO_MEMORY;
1884 centry = wcache_fetch(cache, domain, "SN/%s", sid_string);
1885 TALLOC_FREE(sid_string);
1886 if (centry == NULL) {
1887 return NT_STATUS_NOT_FOUND;
1890 if (NT_STATUS_IS_OK(centry->status)) {
1891 *type = (enum lsa_SidType)centry_uint32(centry);
1892 *domain_name = centry_string(centry, mem_ctx);
1893 *name = centry_string(centry, mem_ctx);
1896 status = centry->status;
1897 centry_free(centry);
1899 DEBUG(10,("sid_to_name: [Cached] - cached name for domain %s status: "
1900 "%s\n", domain->name, nt_errstr(status) ));
1905 /* convert a sid to a user or group name. The sid is guaranteed to be in the domain
1907 static NTSTATUS sid_to_name(struct winbindd_domain *domain,
1908 TALLOC_CTX *mem_ctx,
1909 const struct dom_sid *sid,
1912 enum lsa_SidType *type)
1917 old_status = domain->online;
1918 status = wcache_sid_to_name(domain, sid, mem_ctx, domain_name, name,
1920 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
1925 *domain_name = NULL;
1927 /* If the seq number check indicated that there is a problem
1928 * with this DC, then return that status... except for
1929 * access_denied. This is special because the dc may be in
1930 * "restrict anonymous = 1" mode, in which case it will deny
1931 * most unauthenticated operations, but *will* allow the LSA
1932 * sid-to-name that we try as a fallback. */
1934 if (!(NT_STATUS_IS_OK(domain->last_status)
1935 || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1936 return domain->last_status;
1938 DEBUG(10,("sid_to_name: [Cached] - doing backend query for name for domain %s\n",
1941 status = domain->backend->sid_to_name(domain, mem_ctx, sid, domain_name, name, type);
1943 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1944 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1945 if (!domain->internal && old_status) {
1946 set_domain_offline(domain);
1948 if (!domain->internal &&
1951 NTSTATUS cache_status;
1952 cache_status = wcache_sid_to_name(domain, sid, mem_ctx,
1953 domain_name, name, type);
1954 return cache_status;
1958 refresh_sequence_number(domain, false);
1959 if (!NT_STATUS_IS_OK(status)) {
1962 wcache_save_sid_to_name(domain, status, sid, *domain_name, *name, *type);
1964 /* We can't save the name to sid mapping here, as with sid history a
1965 * later name2sid would give the wrong sid. */
1970 static NTSTATUS rids_to_names(struct winbindd_domain *domain,
1971 TALLOC_CTX *mem_ctx,
1972 const struct dom_sid *domain_sid,
1977 enum lsa_SidType **types)
1979 struct winbind_cache *cache = get_cache(domain);
1981 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
1986 old_status = domain->online;
1987 *domain_name = NULL;
1995 if (num_rids == 0) {
1996 return NT_STATUS_OK;
1999 *names = TALLOC_ARRAY(mem_ctx, char *, num_rids);
2000 *types = TALLOC_ARRAY(mem_ctx, enum lsa_SidType, num_rids);
2002 if ((*names == NULL) || (*types == NULL)) {
2003 result = NT_STATUS_NO_MEMORY;
2007 have_mapped = have_unmapped = false;
2009 for (i=0; i<num_rids; i++) {
2011 struct cache_entry *centry;
2014 if (!sid_compose(&sid, domain_sid, rids[i])) {
2015 result = NT_STATUS_INTERNAL_ERROR;
2019 centry = wcache_fetch(cache, domain, "SN/%s",
2020 sid_to_fstring(tmp, &sid));
2025 (*types)[i] = SID_NAME_UNKNOWN;
2026 (*names)[i] = talloc_strdup(*names, "");
2028 if (NT_STATUS_IS_OK(centry->status)) {
2031 (*types)[i] = (enum lsa_SidType)centry_uint32(centry);
2033 dom = centry_string(centry, mem_ctx);
2034 if (*domain_name == NULL) {
2040 (*names)[i] = centry_string(centry, *names);
2042 } else if (NT_STATUS_EQUAL(centry->status, NT_STATUS_NONE_MAPPED)) {
2043 have_unmapped = true;
2046 /* something's definitely wrong */
2047 result = centry->status;
2051 centry_free(centry);
2055 return NT_STATUS_NONE_MAPPED;
2057 if (!have_unmapped) {
2058 return NT_STATUS_OK;
2060 return STATUS_SOME_UNMAPPED;
2064 TALLOC_FREE(*names);
2065 TALLOC_FREE(*types);
2067 result = domain->backend->rids_to_names(domain, mem_ctx, domain_sid,
2068 rids, num_rids, domain_name,
2071 if (NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
2072 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2073 if (!domain->internal && old_status) {
2074 set_domain_offline(domain);
2077 !domain->internal &&
2080 have_mapped = have_unmapped = false;
2082 for (i=0; i<num_rids; i++) {
2084 struct cache_entry *centry;
2087 if (!sid_compose(&sid, domain_sid, rids[i])) {
2088 result = NT_STATUS_INTERNAL_ERROR;
2092 centry = wcache_fetch(cache, domain, "SN/%s",
2093 sid_to_fstring(tmp, &sid));
2095 (*types)[i] = SID_NAME_UNKNOWN;
2096 (*names)[i] = talloc_strdup(*names, "");
2100 (*types)[i] = SID_NAME_UNKNOWN;
2101 (*names)[i] = talloc_strdup(*names, "");
2103 if (NT_STATUS_IS_OK(centry->status)) {
2106 (*types)[i] = (enum lsa_SidType)centry_uint32(centry);
2108 dom = centry_string(centry, mem_ctx);
2109 if (*domain_name == NULL) {
2115 (*names)[i] = centry_string(centry, *names);
2117 } else if (NT_STATUS_EQUAL(centry->status, NT_STATUS_NONE_MAPPED)) {
2118 have_unmapped = true;
2121 /* something's definitely wrong */
2122 result = centry->status;
2126 centry_free(centry);
2130 return NT_STATUS_NONE_MAPPED;
2132 if (!have_unmapped) {
2133 return NT_STATUS_OK;
2135 return STATUS_SOME_UNMAPPED;
2139 None of the queried rids has been found so save all negative entries
2141 if (NT_STATUS_EQUAL(result, NT_STATUS_NONE_MAPPED)) {
2142 for (i = 0; i < num_rids; i++) {
2144 const char *name = "";
2145 const enum lsa_SidType type = SID_NAME_UNKNOWN;
2146 NTSTATUS status = NT_STATUS_NONE_MAPPED;
2148 if (!sid_compose(&sid, domain_sid, rids[i])) {
2149 return NT_STATUS_INTERNAL_ERROR;
2152 wcache_save_sid_to_name(domain, status, &sid, *domain_name,
2160 Some or all of the queried rids have been found.
2162 if (!NT_STATUS_IS_OK(result) &&
2163 !NT_STATUS_EQUAL(result, STATUS_SOME_UNMAPPED)) {
2167 refresh_sequence_number(domain, false);
2169 for (i=0; i<num_rids; i++) {
2173 if (!sid_compose(&sid, domain_sid, rids[i])) {
2174 result = NT_STATUS_INTERNAL_ERROR;
2178 status = (*types)[i] == SID_NAME_UNKNOWN ?
2179 NT_STATUS_NONE_MAPPED : NT_STATUS_OK;
2181 wcache_save_sid_to_name(domain, status, &sid, *domain_name,
2182 (*names)[i], (*types)[i]);
2188 TALLOC_FREE(*names);
2189 TALLOC_FREE(*types);
2193 NTSTATUS wcache_query_user(struct winbindd_domain *domain,
2194 TALLOC_CTX *mem_ctx,
2195 const struct dom_sid *user_sid,
2196 struct wbint_userinfo *info)
2198 struct winbind_cache *cache = get_cache(domain);
2199 struct cache_entry *centry = NULL;
2203 if (cache->tdb == NULL) {
2204 return NT_STATUS_NOT_FOUND;
2207 sid_string = sid_string_tos(user_sid);
2208 if (sid_string == NULL) {
2209 return NT_STATUS_NO_MEMORY;
2212 centry = wcache_fetch(cache, domain, "U/%s", sid_string);
2213 TALLOC_FREE(sid_string);
2214 if (centry == NULL) {
2215 return NT_STATUS_NOT_FOUND;
2219 * If we have an access denied cache entry and a cached info3
2220 * in the samlogon cache then do a query. This will force the
2221 * rpc back end to return the info3 data.
2224 if (NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED) &&
2225 netsamlogon_cache_have(user_sid)) {
2226 DEBUG(10, ("query_user: cached access denied and have cached "
2228 domain->last_status = NT_STATUS_OK;
2229 centry_free(centry);
2230 return NT_STATUS_NOT_FOUND;
2233 /* if status is not ok then this is a negative hit
2234 and the rest of the data doesn't matter */
2235 status = centry->status;
2236 if (NT_STATUS_IS_OK(status)) {
2237 info->acct_name = centry_string(centry, mem_ctx);
2238 info->full_name = centry_string(centry, mem_ctx);
2239 info->homedir = centry_string(centry, mem_ctx);
2240 info->shell = centry_string(centry, mem_ctx);
2241 info->primary_gid = centry_uint32(centry);
2242 centry_sid(centry, &info->user_sid);
2243 centry_sid(centry, &info->group_sid);
2246 DEBUG(10,("query_user: [Cached] - cached info for domain %s status: "
2247 "%s\n", domain->name, nt_errstr(status) ));
2249 centry_free(centry);
2253 /* Lookup user information from a rid */
2254 static NTSTATUS query_user(struct winbindd_domain *domain,
2255 TALLOC_CTX *mem_ctx,
2256 const struct dom_sid *user_sid,
2257 struct wbint_userinfo *info)
2262 old_status = domain->online;
2263 status = wcache_query_user(domain, mem_ctx, user_sid, info);
2264 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2270 /* Return status value returned by seq number check */
2272 if (!NT_STATUS_IS_OK(domain->last_status))
2273 return domain->last_status;
2275 DEBUG(10,("query_user: [Cached] - doing backend query for info for domain %s\n",
2278 status = domain->backend->query_user(domain, mem_ctx, user_sid, info);
2280 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2281 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2282 if (!domain->internal && old_status) {
2283 set_domain_offline(domain);
2285 if (!domain->internal &&
2288 NTSTATUS cache_status;
2289 cache_status = wcache_query_user(domain, mem_ctx, user_sid, info);
2290 return cache_status;
2294 refresh_sequence_number(domain, false);
2295 if (!NT_STATUS_IS_OK(status)) {
2298 wcache_save_user(domain, status, info);
2303 NTSTATUS wcache_lookup_usergroups(struct winbindd_domain *domain,
2304 TALLOC_CTX *mem_ctx,
2305 const struct dom_sid *user_sid,
2306 uint32_t *pnum_sids,
2307 struct dom_sid **psids)
2309 struct winbind_cache *cache = get_cache(domain);
2310 struct cache_entry *centry = NULL;
2312 uint32_t i, num_sids;
2313 struct dom_sid *sids;
2316 if (cache->tdb == NULL) {
2317 return NT_STATUS_NOT_FOUND;
2320 centry = wcache_fetch(cache, domain, "UG/%s",
2321 sid_to_fstring(sid_string, user_sid));
2322 if (centry == NULL) {
2323 return NT_STATUS_NOT_FOUND;
2326 /* If we have an access denied cache entry and a cached info3 in the
2327 samlogon cache then do a query. This will force the rpc back end
2328 to return the info3 data. */
2330 if (NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)
2331 && netsamlogon_cache_have(user_sid)) {
2332 DEBUG(10, ("lookup_usergroups: cached access denied and have "
2334 domain->last_status = NT_STATUS_OK;
2335 centry_free(centry);
2336 return NT_STATUS_NOT_FOUND;
2339 num_sids = centry_uint32(centry);
2340 sids = talloc_array(mem_ctx, struct dom_sid, num_sids);
2342 centry_free(centry);
2343 return NT_STATUS_NO_MEMORY;
2346 for (i=0; i<num_sids; i++) {
2347 centry_sid(centry, &sids[i]);
2350 status = centry->status;
2352 DEBUG(10,("lookup_usergroups: [Cached] - cached info for domain %s "
2353 "status: %s\n", domain->name, nt_errstr(status)));
2355 centry_free(centry);
2357 *pnum_sids = num_sids;
2362 /* Lookup groups a user is a member of. */
2363 static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
2364 TALLOC_CTX *mem_ctx,
2365 const struct dom_sid *user_sid,
2366 uint32 *num_groups, struct dom_sid **user_gids)
2368 struct cache_entry *centry = NULL;
2374 old_status = domain->online;
2375 status = wcache_lookup_usergroups(domain, mem_ctx, user_sid,
2376 num_groups, user_gids);
2377 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2382 (*user_gids) = NULL;
2384 /* Return status value returned by seq number check */
2386 if (!NT_STATUS_IS_OK(domain->last_status))
2387 return domain->last_status;
2389 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info for domain %s\n",
2392 status = domain->backend->lookup_usergroups(domain, mem_ctx, user_sid, num_groups, user_gids);
2394 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2395 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2396 if (!domain->internal && old_status) {
2397 set_domain_offline(domain);
2399 if (!domain->internal &&
2402 NTSTATUS cache_status;
2403 cache_status = wcache_lookup_usergroups(domain, mem_ctx, user_sid,
2404 num_groups, user_gids);
2405 return cache_status;
2408 if ( NT_STATUS_EQUAL(status, NT_STATUS_SYNCHRONIZATION_REQUIRED) )
2412 refresh_sequence_number(domain, false);
2413 if (!NT_STATUS_IS_OK(status)) {
2416 centry = centry_start(domain, status);
2420 centry_put_uint32(centry, *num_groups);
2421 for (i=0; i<(*num_groups); i++) {
2422 centry_put_sid(centry, &(*user_gids)[i]);
2425 centry_end(centry, "UG/%s", sid_to_fstring(sid_string, user_sid));
2426 centry_free(centry);
2432 static char *wcache_make_sidlist(TALLOC_CTX *mem_ctx, uint32_t num_sids,
2433 const struct dom_sid *sids)
2438 sidlist = talloc_strdup(mem_ctx, "");
2439 if (sidlist == NULL) {
2442 for (i=0; i<num_sids; i++) {
2444 sidlist = talloc_asprintf_append_buffer(
2445 sidlist, "/%s", sid_to_fstring(tmp, &sids[i]));
2446 if (sidlist == NULL) {
2453 NTSTATUS wcache_lookup_useraliases(struct winbindd_domain *domain,
2454 TALLOC_CTX *mem_ctx, uint32_t num_sids,
2455 const struct dom_sid *sids,
2456 uint32_t *pnum_aliases, uint32_t **paliases)
2458 struct winbind_cache *cache = get_cache(domain);
2459 struct cache_entry *centry = NULL;
2460 uint32_t num_aliases;
2466 if (cache->tdb == NULL) {
2467 return NT_STATUS_NOT_FOUND;
2470 if (num_sids == 0) {
2473 return NT_STATUS_OK;
2476 /* We need to cache indexed by the whole list of SIDs, the aliases
2477 * resulting might come from any of the SIDs. */
2479 sidlist = wcache_make_sidlist(talloc_tos(), num_sids, sids);
2480 if (sidlist == NULL) {
2481 return NT_STATUS_NO_MEMORY;
2484 centry = wcache_fetch(cache, domain, "UA%s", sidlist);
2485 TALLOC_FREE(sidlist);
2486 if (centry == NULL) {
2487 return NT_STATUS_NOT_FOUND;
2490 num_aliases = centry_uint32(centry);
2491 aliases = talloc_array(mem_ctx, uint32_t, num_aliases);
2492 if (aliases == NULL) {
2493 centry_free(centry);
2494 return NT_STATUS_NO_MEMORY;
2497 for (i=0; i<num_aliases; i++) {
2498 aliases[i] = centry_uint32(centry);
2501 status = centry->status;
2503 DEBUG(10,("lookup_useraliases: [Cached] - cached info for domain: %s "
2504 "status %s\n", domain->name, nt_errstr(status)));
2506 centry_free(centry);
2508 *pnum_aliases = num_aliases;
2509 *paliases = aliases;
2514 static NTSTATUS lookup_useraliases(struct winbindd_domain *domain,
2515 TALLOC_CTX *mem_ctx,
2516 uint32 num_sids, const struct dom_sid *sids,
2517 uint32 *num_aliases, uint32 **alias_rids)
2519 struct cache_entry *centry = NULL;
2525 old_status = domain->online;
2526 status = wcache_lookup_useraliases(domain, mem_ctx, num_sids, sids,
2527 num_aliases, alias_rids);
2528 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2533 (*alias_rids) = NULL;
2535 if (!NT_STATUS_IS_OK(domain->last_status))
2536 return domain->last_status;
2538 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info "
2539 "for domain %s\n", domain->name ));
2541 sidlist = wcache_make_sidlist(talloc_tos(), num_sids, sids);
2542 if (sidlist == NULL) {
2543 return NT_STATUS_NO_MEMORY;
2546 status = domain->backend->lookup_useraliases(domain, mem_ctx,
2548 num_aliases, alias_rids);
2550 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2551 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2552 if (!domain->internal && old_status) {
2553 set_domain_offline(domain);
2555 if (!domain->internal &&
2558 NTSTATUS cache_status;
2559 cache_status = wcache_lookup_useraliases(domain, mem_ctx, num_sids,
2560 sids, num_aliases, alias_rids);
2561 return cache_status;
2565 refresh_sequence_number(domain, false);
2566 if (!NT_STATUS_IS_OK(status)) {
2569 centry = centry_start(domain, status);
2572 centry_put_uint32(centry, *num_aliases);
2573 for (i=0; i<(*num_aliases); i++)
2574 centry_put_uint32(centry, (*alias_rids)[i]);
2575 centry_end(centry, "UA%s", sidlist);
2576 centry_free(centry);
2582 NTSTATUS wcache_lookup_groupmem(struct winbindd_domain *domain,
2583 TALLOC_CTX *mem_ctx,
2584 const struct dom_sid *group_sid,
2585 uint32_t *num_names,
2586 struct dom_sid **sid_mem, char ***names,
2587 uint32_t **name_types)
2589 struct winbind_cache *cache = get_cache(domain);
2590 struct cache_entry *centry = NULL;
2595 if (cache->tdb == NULL) {
2596 return NT_STATUS_NOT_FOUND;
2599 sid_string = sid_string_tos(group_sid);
2600 if (sid_string == NULL) {
2601 return NT_STATUS_NO_MEMORY;
2604 centry = wcache_fetch(cache, domain, "GM/%s", sid_string);
2605 TALLOC_FREE(sid_string);
2606 if (centry == NULL) {
2607 return NT_STATUS_NOT_FOUND;
2614 *num_names = centry_uint32(centry);
2615 if (*num_names == 0) {
2616 centry_free(centry);
2617 return NT_STATUS_OK;
2620 *sid_mem = talloc_array(mem_ctx, struct dom_sid, *num_names);
2621 *names = talloc_array(mem_ctx, char *, *num_names);
2622 *name_types = talloc_array(mem_ctx, uint32, *num_names);
2624 if ((*sid_mem == NULL) || (*names == NULL) || (*name_types == NULL)) {
2625 TALLOC_FREE(*sid_mem);
2626 TALLOC_FREE(*names);
2627 TALLOC_FREE(*name_types);
2628 centry_free(centry);
2629 return NT_STATUS_NO_MEMORY;
2632 for (i=0; i<(*num_names); i++) {
2633 centry_sid(centry, &(*sid_mem)[i]);
2634 (*names)[i] = centry_string(centry, mem_ctx);
2635 (*name_types)[i] = centry_uint32(centry);
2638 status = centry->status;
2640 DEBUG(10,("lookup_groupmem: [Cached] - cached info for domain %s "
2641 "status: %s\n", domain->name, nt_errstr(status)));
2643 centry_free(centry);
2647 static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
2648 TALLOC_CTX *mem_ctx,
2649 const struct dom_sid *group_sid,
2650 enum lsa_SidType type,
2652 struct dom_sid **sid_mem, char ***names,
2653 uint32 **name_types)
2655 struct cache_entry *centry = NULL;
2661 old_status = domain->online;
2662 status = wcache_lookup_groupmem(domain, mem_ctx, group_sid, num_names,
2663 sid_mem, names, name_types);
2664 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2671 (*name_types) = NULL;
2673 /* Return status value returned by seq number check */
2675 if (!NT_STATUS_IS_OK(domain->last_status))
2676 return domain->last_status;
2678 DEBUG(10,("lookup_groupmem: [Cached] - doing backend query for info for domain %s\n",
2681 status = domain->backend->lookup_groupmem(domain, mem_ctx, group_sid,
2683 sid_mem, names, name_types);
2685 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2686 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2687 if (!domain->internal && old_status) {
2688 set_domain_offline(domain);
2690 if (!domain->internal &&
2693 NTSTATUS cache_status;
2694 cache_status = wcache_lookup_groupmem(domain, mem_ctx, group_sid,
2695 num_names, sid_mem, names,
2697 return cache_status;
2701 refresh_sequence_number(domain, false);
2702 if (!NT_STATUS_IS_OK(status)) {
2705 centry = centry_start(domain, status);
2708 centry_put_uint32(centry, *num_names);
2709 for (i=0; i<(*num_names); i++) {
2710 centry_put_sid(centry, &(*sid_mem)[i]);
2711 centry_put_string(centry, (*names)[i]);
2712 centry_put_uint32(centry, (*name_types)[i]);
2714 centry_end(centry, "GM/%s", sid_to_fstring(sid_string, group_sid));
2715 centry_free(centry);
2721 /* find the sequence number for a domain */
2722 static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
2724 refresh_sequence_number(domain, false);
2726 *seq = domain->sequence_number;
2728 return NT_STATUS_OK;
2731 /* enumerate trusted domains
2732 * (we need to have the list of trustdoms in the cache when we go offline) -
2734 static NTSTATUS trusted_domains(struct winbindd_domain *domain,
2735 TALLOC_CTX *mem_ctx,
2736 struct netr_DomainTrustList *trusts)
2739 struct winbind_cache *cache;
2740 struct winbindd_tdc_domain *dom_list = NULL;
2741 size_t num_domains = 0;
2742 bool retval = false;
2746 old_status = domain->online;
2748 trusts->array = NULL;
2750 cache = get_cache(domain);
2751 if (!cache || !cache->tdb) {
2755 if (domain->online) {
2759 retval = wcache_tdc_fetch_list(&dom_list, &num_domains);
2760 if (!retval || !num_domains || !dom_list) {
2761 TALLOC_FREE(dom_list);
2766 trusts->array = TALLOC_ZERO_ARRAY(mem_ctx, struct netr_DomainTrust, num_domains);
2767 if (!trusts->array) {
2768 TALLOC_FREE(dom_list);
2769 return NT_STATUS_NO_MEMORY;
2772 for (i = 0; i < num_domains; i++) {
2773 struct netr_DomainTrust *trust;
2774 struct dom_sid *sid;
2775 struct winbindd_domain *dom;
2777 dom = find_domain_from_name_noinit(dom_list[i].domain_name);
2778 if (dom && dom->internal) {
2782 trust = &trusts->array[trusts->count];
2783 trust->netbios_name = talloc_strdup(trusts->array, dom_list[i].domain_name);
2784 trust->dns_name = talloc_strdup(trusts->array, dom_list[i].dns_name);
2785 sid = talloc(trusts->array, struct dom_sid);
2786 if (!trust->netbios_name || !trust->dns_name ||
2788 TALLOC_FREE(dom_list);
2789 TALLOC_FREE(trusts->array);
2790 return NT_STATUS_NO_MEMORY;
2793 trust->trust_flags = dom_list[i].trust_flags;
2794 trust->trust_attributes = dom_list[i].trust_attribs;
2795 trust->trust_type = dom_list[i].trust_type;
2796 sid_copy(sid, &dom_list[i].sid);
2801 TALLOC_FREE(dom_list);
2802 return NT_STATUS_OK;
2805 /* Return status value returned by seq number check */
2807 if (!NT_STATUS_IS_OK(domain->last_status))
2808 return domain->last_status;
2810 DEBUG(10,("trusted_domains: [Cached] - doing backend query for info for domain %s\n",
2813 status = domain->backend->trusted_domains(domain, mem_ctx, trusts);
2815 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2816 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2817 if (!domain->internal && old_status) {
2818 set_domain_offline(domain);
2820 if (!domain->internal &&
2823 retval = wcache_tdc_fetch_list(&dom_list, &num_domains);
2824 if (retval && num_domains && dom_list) {
2825 TALLOC_FREE(trusts->array);
2827 goto do_fetch_cache;
2831 /* no trusts gives NT_STATUS_NO_MORE_ENTRIES resetting to NT_STATUS_OK
2832 * so that the generic centry handling still applies correctly -
2835 if (!NT_STATUS_IS_ERR(status)) {
2836 status = NT_STATUS_OK;
2841 /* get lockout policy */
2842 static NTSTATUS lockout_policy(struct winbindd_domain *domain,
2843 TALLOC_CTX *mem_ctx,
2844 struct samr_DomInfo12 *policy)
2846 struct winbind_cache *cache = get_cache(domain);
2847 struct cache_entry *centry = NULL;
2851 old_status = domain->online;
2855 centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
2861 policy->lockout_duration = centry_nttime(centry);
2862 policy->lockout_window = centry_nttime(centry);
2863 policy->lockout_threshold = centry_uint16(centry);
2865 status = centry->status;
2867 DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2868 domain->name, nt_errstr(status) ));
2870 centry_free(centry);
2874 ZERO_STRUCTP(policy);
2876 /* Return status value returned by seq number check */
2878 if (!NT_STATUS_IS_OK(domain->last_status))
2879 return domain->last_status;
2881 DEBUG(10,("lockout_policy: [Cached] - doing backend query for info for domain %s\n",
2884 status = domain->backend->lockout_policy(domain, mem_ctx, policy);
2886 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2887 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2888 if (!domain->internal && old_status) {
2889 set_domain_offline(domain);
2892 !domain->internal &&
2895 centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
2897 goto do_fetch_cache;
2902 refresh_sequence_number(domain, false);
2903 if (!NT_STATUS_IS_OK(status)) {
2906 wcache_save_lockout_policy(domain, status, policy);
2911 /* get password policy */
2912 static NTSTATUS password_policy(struct winbindd_domain *domain,
2913 TALLOC_CTX *mem_ctx,
2914 struct samr_DomInfo1 *policy)
2916 struct winbind_cache *cache = get_cache(domain);
2917 struct cache_entry *centry = NULL;
2921 old_status = domain->online;
2925 centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
2931 policy->min_password_length = centry_uint16(centry);
2932 policy->password_history_length = centry_uint16(centry);
2933 policy->password_properties = centry_uint32(centry);
2934 policy->max_password_age = centry_nttime(centry);
2935 policy->min_password_age = centry_nttime(centry);
2937 status = centry->status;
2939 DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2940 domain->name, nt_errstr(status) ));
2942 centry_free(centry);
2946 ZERO_STRUCTP(policy);
2948 /* Return status value returned by seq number check */
2950 if (!NT_STATUS_IS_OK(domain->last_status))
2951 return domain->last_status;
2953 DEBUG(10,("password_policy: [Cached] - doing backend query for info for domain %s\n",
2956 status = domain->backend->password_policy(domain, mem_ctx, policy);
2958 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2959 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2960 if (!domain->internal && old_status) {
2961 set_domain_offline(domain);
2964 !domain->internal &&
2967 centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
2969 goto do_fetch_cache;
2974 refresh_sequence_number(domain, false);
2975 if (!NT_STATUS_IS_OK(status)) {
2978 wcache_save_password_policy(domain, status, policy);
2984 /* Invalidate cached user and group lists coherently */
2986 static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
2989 if (strncmp((const char *)kbuf.dptr, "UL/", 3) == 0 ||
2990 strncmp((const char *)kbuf.dptr, "GL/", 3) == 0)
2991 tdb_delete(the_tdb, kbuf);
2996 /* Invalidate the getpwnam and getgroups entries for a winbindd domain */
2998 void wcache_invalidate_samlogon(struct winbindd_domain *domain,
2999 struct netr_SamInfo3 *info3)
3002 fstring key_str, sid_string;
3003 struct winbind_cache *cache;
3005 /* dont clear cached U/SID and UG/SID entries when we want to logon
3008 if (lp_winbind_offline_logon()) {
3015 cache = get_cache(domain);
3021 sid_compose(&sid, info3->base.domain_sid, info3->base.rid);
3023 /* Clear U/SID cache entry */
3024 fstr_sprintf(key_str, "U/%s", sid_to_fstring(sid_string, &sid));
3025 DEBUG(10, ("wcache_invalidate_samlogon: clearing %s\n", key_str));
3026 tdb_delete(cache->tdb, string_tdb_data(key_str));
3028 /* Clear UG/SID cache entry */
3029 fstr_sprintf(key_str, "UG/%s", sid_to_fstring(sid_string, &sid));
3030 DEBUG(10, ("wcache_invalidate_samlogon: clearing %s\n", key_str));
3031 tdb_delete(cache->tdb, string_tdb_data(key_str));
3033 /* Samba/winbindd never needs this. */
3034 netsamlogon_clear_cached_user(info3);
3037 bool wcache_invalidate_cache(void)
3039 struct winbindd_domain *domain;
3041 for (domain = domain_list(); domain; domain = domain->next) {
3042 struct winbind_cache *cache = get_cache(domain);
3044 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
3045 "entries for %s\n", domain->name));
3048 tdb_traverse(cache->tdb, traverse_fn, NULL);
3057 bool wcache_invalidate_cache_noinit(void)
3059 struct winbindd_domain *domain;
3061 for (domain = domain_list(); domain; domain = domain->next) {
3062 struct winbind_cache *cache;
3064 /* Skip uninitialized domains. */
3065 if (!domain->initialized && !domain->internal) {
3069 cache = get_cache(domain);
3071 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
3072 "entries for %s\n", domain->name));
3075 tdb_traverse(cache->tdb, traverse_fn, NULL);
3077 * Flushing cache has nothing to with domains.
3078 * return here if we successfully flushed once.
3079 * To avoid unnecessary traversing the cache.
3090 bool init_wcache(void)
3092 if (wcache == NULL) {
3093 wcache = SMB_XMALLOC_P(struct winbind_cache);
3094 ZERO_STRUCTP(wcache);
3097 if (wcache->tdb != NULL)
3100 /* when working offline we must not clear the cache on restart */
3101 wcache->tdb = tdb_open_log(cache_path("winbindd_cache.tdb"),
3102 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
3103 lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST),
3104 O_RDWR|O_CREAT, 0600);
3106 if (wcache->tdb == NULL) {
3107 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
3114 /************************************************************************
3115 This is called by the parent to initialize the cache file.
3116 We don't need sophisticated locking here as we know we're the
3118 ************************************************************************/
3120 bool initialize_winbindd_cache(void)
3122 bool cache_bad = true;
3125 if (!init_wcache()) {
3126 DEBUG(0,("initialize_winbindd_cache: init_wcache failed.\n"));
3130 /* Check version number. */
3131 if (tdb_fetch_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, &vers) &&
3132 vers == WINBINDD_CACHE_VERSION) {
3137 DEBUG(0,("initialize_winbindd_cache: clearing cache "
3138 "and re-creating with version number %d\n",
3139 WINBINDD_CACHE_VERSION ));
3141 tdb_close(wcache->tdb);
3144 if (unlink(cache_path("winbindd_cache.tdb")) == -1) {
3145 DEBUG(0,("initialize_winbindd_cache: unlink %s failed %s ",
3146 cache_path("winbindd_cache.tdb"),
3150 if (!init_wcache()) {
3151 DEBUG(0,("initialize_winbindd_cache: re-initialization "
3152 "init_wcache failed.\n"));
3156 /* Write the version. */
3157 if (!tdb_store_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, WINBINDD_CACHE_VERSION)) {
3158 DEBUG(0,("initialize_winbindd_cache: version number store failed %s\n",
3159 tdb_errorstr(wcache->tdb) ));
3164 tdb_close(wcache->tdb);
3169 void close_winbindd_cache(void)
3175 tdb_close(wcache->tdb);
3180 bool lookup_cached_sid(TALLOC_CTX *mem_ctx, const struct dom_sid *sid,
3181 char **domain_name, char **name,
3182 enum lsa_SidType *type)
3184 struct winbindd_domain *domain;
3187 domain = find_lookup_domain_from_sid(sid);
3188 if (domain == NULL) {
3191 status = wcache_sid_to_name(domain, sid, mem_ctx, domain_name, name,
3193 return NT_STATUS_IS_OK(status);
3196 bool lookup_cached_name(TALLOC_CTX *mem_ctx,
3197 const char *domain_name,
3199 struct dom_sid *sid,
3200 enum lsa_SidType *type)
3202 struct winbindd_domain *domain;
3204 bool original_online_state;
3206 domain = find_lookup_domain_from_name(domain_name);
3207 if (domain == NULL) {
3211 /* If we are doing a cached logon, temporarily set the domain
3212 offline so the cache won't expire the entry */
3214 original_online_state = domain->online;
3215 domain->online = false;
3216 status = wcache_name_to_sid(domain, domain_name, name, sid, type);
3217 domain->online = original_online_state;
3219 return NT_STATUS_IS_OK(status);
3222 void cache_name2sid(struct winbindd_domain *domain,
3223 const char *domain_name, const char *name,
3224 enum lsa_SidType type, const struct dom_sid *sid)
3226 refresh_sequence_number(domain, false);
3227 wcache_save_name_to_sid(domain, NT_STATUS_OK, domain_name, name,
3232 * The original idea that this cache only contains centries has
3233 * been blurred - now other stuff gets put in here. Ensure we
3234 * ignore these things on cleanup.
3237 static int traverse_fn_cleanup(TDB_CONTEXT *the_tdb, TDB_DATA kbuf,
3238 TDB_DATA dbuf, void *state)
3240 struct cache_entry *centry;
3242 if (is_non_centry_key(kbuf)) {
3246 centry = wcache_fetch_raw((char *)kbuf.dptr);
3251 if (!NT_STATUS_IS_OK(centry->status)) {
3252 DEBUG(10,("deleting centry %s\n", (const char *)kbuf.dptr));
3253 tdb_delete(the_tdb, kbuf);
3256 centry_free(centry);
3260 /* flush the cache */
3261 void wcache_flush_cache(void)
3266 tdb_close(wcache->tdb);
3269 if (!winbindd_use_cache()) {
3273 /* when working offline we must not clear the cache on restart */
3274 wcache->tdb = tdb_open_log(cache_path("winbindd_cache.tdb"),
3275 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
3276 lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST),
3277 O_RDWR|O_CREAT, 0600);
3280 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
3284 tdb_traverse(wcache->tdb, traverse_fn_cleanup, NULL);
3286 DEBUG(10,("wcache_flush_cache success\n"));
3289 /* Count cached creds */
3291 static int traverse_fn_cached_creds(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
3294 int *cred_count = (int*)state;
3296 if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
3302 NTSTATUS wcache_count_cached_creds(struct winbindd_domain *domain, int *count)
3304 struct winbind_cache *cache = get_cache(domain);
3309 return NT_STATUS_INTERNAL_DB_ERROR;
3312 tdb_traverse(cache->tdb, traverse_fn_cached_creds, (void *)count);
3314 return NT_STATUS_OK;
3318 struct cred_list *prev, *next;
3323 static struct cred_list *wcache_cred_list;
3325 static int traverse_fn_get_credlist(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
3328 struct cred_list *cred;
3330 if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
3332 cred = SMB_MALLOC_P(struct cred_list);
3334 DEBUG(0,("traverse_fn_remove_first_creds: failed to malloc new entry for list\n"));
3340 /* save a copy of the key */
3342 fstrcpy(cred->name, (const char *)kbuf.dptr);
3343 DLIST_ADD(wcache_cred_list, cred);
3349 NTSTATUS wcache_remove_oldest_cached_creds(struct winbindd_domain *domain, const struct dom_sid *sid)
3351 struct winbind_cache *cache = get_cache(domain);
3354 struct cred_list *cred, *oldest = NULL;
3357 return NT_STATUS_INTERNAL_DB_ERROR;
3360 /* we possibly already have an entry */
3361 if (sid && NT_STATUS_IS_OK(wcache_cached_creds_exist(domain, sid))) {
3363 fstring key_str, tmp;
3365 DEBUG(11,("we already have an entry, deleting that\n"));
3367 fstr_sprintf(key_str, "CRED/%s", sid_to_fstring(tmp, sid));
3369 tdb_delete(cache->tdb, string_tdb_data(key_str));
3371 return NT_STATUS_OK;
3374 ret = tdb_traverse(cache->tdb, traverse_fn_get_credlist, NULL);
3376 return NT_STATUS_OK;
3377 } else if ((ret == -1) || (wcache_cred_list == NULL)) {
3378 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
3381 ZERO_STRUCTP(oldest);
3383 for (cred = wcache_cred_list; cred; cred = cred->next) {
3388 data = tdb_fetch(cache->tdb, string_tdb_data(cred->name));
3390 DEBUG(10,("wcache_remove_oldest_cached_creds: entry for [%s] not found\n",
3392 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
3396 t = IVAL(data.dptr, 0);
3397 SAFE_FREE(data.dptr);
3400 oldest = SMB_MALLOC_P(struct cred_list);
3401 if (oldest == NULL) {
3402 status = NT_STATUS_NO_MEMORY;
3406 fstrcpy(oldest->name, cred->name);
3407 oldest->created = t;
3411 if (t < oldest->created) {
3412 fstrcpy(oldest->name, cred->name);
3413 oldest->created = t;
3417 if (tdb_delete(cache->tdb, string_tdb_data(oldest->name)) == 0) {
3418 status = NT_STATUS_OK;
3420 status = NT_STATUS_UNSUCCESSFUL;
3423 SAFE_FREE(wcache_cred_list);
3429 /* Change the global online/offline state. */
3430 bool set_global_winbindd_state_offline(void)
3434 DEBUG(10,("set_global_winbindd_state_offline: offline requested.\n"));
3436 /* Only go offline if someone has created
3437 the key "WINBINDD_OFFLINE" in the cache tdb. */
3439 if (wcache == NULL || wcache->tdb == NULL) {
3440 DEBUG(10,("set_global_winbindd_state_offline: wcache not open yet.\n"));
3444 if (!lp_winbind_offline_logon()) {
3445 DEBUG(10,("set_global_winbindd_state_offline: rejecting.\n"));
3449 if (global_winbindd_offline_state) {
3450 /* Already offline. */
3454 data = tdb_fetch_bystring( wcache->tdb, "WINBINDD_OFFLINE" );
3456 if (!data.dptr || data.dsize != 4) {
3457 DEBUG(10,("set_global_winbindd_state_offline: offline state not set.\n"));
3458 SAFE_FREE(data.dptr);
3461 DEBUG(10,("set_global_winbindd_state_offline: offline state set.\n"));
3462 global_winbindd_offline_state = true;
3463 SAFE_FREE(data.dptr);
3468 void set_global_winbindd_state_online(void)
3470 DEBUG(10,("set_global_winbindd_state_online: online requested.\n"));
3472 if (!lp_winbind_offline_logon()) {
3473 DEBUG(10,("set_global_winbindd_state_online: rejecting.\n"));
3477 if (!global_winbindd_offline_state) {
3478 /* Already online. */
3481 global_winbindd_offline_state = false;
3487 /* Ensure there is no key "WINBINDD_OFFLINE" in the cache tdb. */
3488 tdb_delete_bystring(wcache->tdb, "WINBINDD_OFFLINE");
3491 bool get_global_winbindd_state_offline(void)
3493 return global_winbindd_offline_state;
3496 /***********************************************************************
3497 Validate functions for all possible cache tdb keys.
3498 ***********************************************************************/
3500 static struct cache_entry *create_centry_validate(const char *kstr, TDB_DATA data,
3501 struct tdb_validation_status *state)
3503 struct cache_entry *centry;
3505 centry = SMB_XMALLOC_P(struct cache_entry);
3506 centry->data = (unsigned char *)memdup(data.dptr, data.dsize);
3507 if (!centry->data) {
3511 centry->len = data.dsize;
3514 if (centry->len < 8) {
3515 /* huh? corrupt cache? */
3516 DEBUG(0,("create_centry_validate: Corrupt cache for key %s (len < 8) ?\n", kstr));
3517 centry_free(centry);
3518 state->bad_entry = true;
3519 state->success = false;
3523 centry->status = NT_STATUS(centry_uint32(centry));
3524 centry->sequence_number = centry_uint32(centry);
3528 static int validate_seqnum(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3529 struct tdb_validation_status *state)
3531 if (dbuf.dsize != 8) {
3532 DEBUG(0,("validate_seqnum: Corrupt cache for key %s (len %u != 8) ?\n",
3533 keystr, (unsigned int)dbuf.dsize ));
3534 state->bad_entry = true;
3540 static int validate_ns(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3541 struct tdb_validation_status *state)
3543 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3548 (void)centry_uint32(centry);
3549 if (NT_STATUS_IS_OK(centry->status)) {
3551 (void)centry_sid(centry, &sid);
3554 centry_free(centry);
3556 if (!(state->success)) {
3559 DEBUG(10,("validate_ns: %s ok\n", keystr));
3563 static int validate_sn(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3564 struct tdb_validation_status *state)
3566 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3571 if (NT_STATUS_IS_OK(centry->status)) {
3572 (void)centry_uint32(centry);
3573 (void)centry_string(centry, mem_ctx);
3574 (void)centry_string(centry, mem_ctx);
3577 centry_free(centry);
3579 if (!(state->success)) {
3582 DEBUG(10,("validate_sn: %s ok\n", keystr));
3586 static int validate_u(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3587 struct tdb_validation_status *state)
3589 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3596 (void)centry_string(centry, mem_ctx);
3597 (void)centry_string(centry, mem_ctx);
3598 (void)centry_string(centry, mem_ctx);
3599 (void)centry_string(centry, mem_ctx);
3600 (void)centry_uint32(centry);
3601 (void)centry_sid(centry, &sid);
3602 (void)centry_sid(centry, &sid);
3604 centry_free(centry);
3606 if (!(state->success)) {
3609 DEBUG(10,("validate_u: %s ok\n", keystr));
3613 static int validate_loc_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3614 struct tdb_validation_status *state)
3616 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3622 (void)centry_nttime(centry);
3623 (void)centry_nttime(centry);
3624 (void)centry_uint16(centry);
3626 centry_free(centry);
3628 if (!(state->success)) {
3631 DEBUG(10,("validate_loc_pol: %s ok\n", keystr));
3635 static int validate_pwd_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3636 struct tdb_validation_status *state)
3638 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3644 (void)centry_uint16(centry);
3645 (void)centry_uint16(centry);
3646 (void)centry_uint32(centry);
3647 (void)centry_nttime(centry);
3648 (void)centry_nttime(centry);
3650 centry_free(centry);
3652 if (!(state->success)) {
3655 DEBUG(10,("validate_pwd_pol: %s ok\n", keystr));
3659 static int validate_cred(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3660 struct tdb_validation_status *state)
3662 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3668 (void)centry_time(centry);
3669 (void)centry_hash16(centry, mem_ctx);
3671 /* We only have 17 bytes more data in the salted cred case. */
3672 if (centry->len - centry->ofs == 17) {
3673 (void)centry_hash16(centry, mem_ctx);
3676 centry_free(centry);
3678 if (!(state->success)) {
3681 DEBUG(10,("validate_cred: %s ok\n", keystr));
3685 static int validate_ul(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3686 struct tdb_validation_status *state)
3688 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3689 int32 num_entries, i;
3695 num_entries = (int32)centry_uint32(centry);
3697 for (i=0; i< num_entries; i++) {
3699 (void)centry_string(centry, mem_ctx);
3700 (void)centry_string(centry, mem_ctx);
3701 (void)centry_string(centry, mem_ctx);
3702 (void)centry_string(centry, mem_ctx);
3703 (void)centry_sid(centry, &sid);
3704 (void)centry_sid(centry, &sid);
3707 centry_free(centry);
3709 if (!(state->success)) {
3712 DEBUG(10,("validate_ul: %s ok\n", keystr));
3716 static int validate_gl(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3717 struct tdb_validation_status *state)
3719 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3720 int32 num_entries, i;
3726 num_entries = centry_uint32(centry);
3728 for (i=0; i< num_entries; i++) {
3729 (void)centry_string(centry, mem_ctx);
3730 (void)centry_string(centry, mem_ctx);
3731 (void)centry_uint32(centry);
3734 centry_free(centry);
3736 if (!(state->success)) {
3739 DEBUG(10,("validate_gl: %s ok\n", keystr));
3743 static int validate_ug(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3744 struct tdb_validation_status *state)
3746 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3747 int32 num_groups, i;
3753 num_groups = centry_uint32(centry);
3755 for (i=0; i< num_groups; i++) {
3757 centry_sid(centry, &sid);
3760 centry_free(centry);
3762 if (!(state->success)) {
3765 DEBUG(10,("validate_ug: %s ok\n", keystr));
3769 static int validate_ua(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3770 struct tdb_validation_status *state)
3772 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3773 int32 num_aliases, i;
3779 num_aliases = centry_uint32(centry);
3781 for (i=0; i < num_aliases; i++) {
3782 (void)centry_uint32(centry);
3785 centry_free(centry);
3787 if (!(state->success)) {
3790 DEBUG(10,("validate_ua: %s ok\n", keystr));
3794 static int validate_gm(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3795 struct tdb_validation_status *state)
3797 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3804 num_names = centry_uint32(centry);
3806 for (i=0; i< num_names; i++) {
3808 centry_sid(centry, &sid);
3809 (void)centry_string(centry, mem_ctx);
3810 (void)centry_uint32(centry);
3813 centry_free(centry);
3815 if (!(state->success)) {
3818 DEBUG(10,("validate_gm: %s ok\n", keystr));
3822 static int validate_dr(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3823 struct tdb_validation_status *state)
3825 /* Can't say anything about this other than must be nonzero. */
3826 if (dbuf.dsize == 0) {
3827 DEBUG(0,("validate_dr: Corrupt cache for key %s (len == 0) ?\n",
3829 state->bad_entry = true;
3830 state->success = false;
3834 DEBUG(10,("validate_dr: %s ok\n", keystr));
3838 static int validate_de(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3839 struct tdb_validation_status *state)
3841 /* Can't say anything about this other than must be nonzero. */
3842 if (dbuf.dsize == 0) {
3843 DEBUG(0,("validate_de: Corrupt cache for key %s (len == 0) ?\n",
3845 state->bad_entry = true;
3846 state->success = false;
3850 DEBUG(10,("validate_de: %s ok\n", keystr));
3854 static int validate_pwinfo(TALLOC_CTX *mem_ctx, const char *keystr,
3855 TDB_DATA dbuf, struct tdb_validation_status *state)
3857 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3863 (void)centry_string(centry, mem_ctx);
3864 (void)centry_string(centry, mem_ctx);
3865 (void)centry_string(centry, mem_ctx);
3866 (void)centry_uint32(centry);
3868 centry_free(centry);
3870 if (!(state->success)) {
3873 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3877 static int validate_nss_an(TALLOC_CTX *mem_ctx, const char *keystr,
3879 struct tdb_validation_status *state)
3881 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3887 (void)centry_string( centry, mem_ctx );
3889 centry_free(centry);
3891 if (!(state->success)) {
3894 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3898 static int validate_nss_na(TALLOC_CTX *mem_ctx, const char *keystr,
3900 struct tdb_validation_status *state)
3902 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3908 (void)centry_string( centry, mem_ctx );
3910 centry_free(centry);
3912 if (!(state->success)) {
3915 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3919 static int validate_trustdomcache(TALLOC_CTX *mem_ctx, const char *keystr,
3921 struct tdb_validation_status *state)
3923 if (dbuf.dsize == 0) {
3924 DEBUG(0, ("validate_trustdomcache: Corrupt cache for "
3925 "key %s (len ==0) ?\n", keystr));
3926 state->bad_entry = true;
3927 state->success = false;
3931 DEBUG(10, ("validate_trustdomcache: %s ok\n", keystr));
3932 DEBUGADD(10, (" Don't trust me, I am a DUMMY!\n"));
3936 static int validate_offline(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3937 struct tdb_validation_status *state)
3939 if (dbuf.dsize != 4) {
3940 DEBUG(0,("validate_offline: Corrupt cache for key %s (len %u != 4) ?\n",
3941 keystr, (unsigned int)dbuf.dsize ));
3942 state->bad_entry = true;
3943 state->success = false;
3946 DEBUG(10,("validate_offline: %s ok\n", keystr));
3950 static int validate_ndr(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3951 struct tdb_validation_status *state)
3954 * Ignore validation for now. The proper way to do this is with a
3955 * checksum. Just pure parsing does not really catch much.
3960 static int validate_cache_version(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3961 struct tdb_validation_status *state)
3963 if (dbuf.dsize != 4) {
3964 DEBUG(0, ("validate_cache_version: Corrupt cache for "
3965 "key %s (len %u != 4) ?\n",
3966 keystr, (unsigned int)dbuf.dsize));
3967 state->bad_entry = true;
3968 state->success = false;
3972 DEBUG(10, ("validate_cache_version: %s ok\n", keystr));
3976 /***********************************************************************
3977 A list of all possible cache tdb keys with associated validation
3979 ***********************************************************************/
3981 struct key_val_struct {
3982 const char *keyname;
3983 int (*validate_data_fn)(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, struct tdb_validation_status* state);
3985 {"SEQNUM/", validate_seqnum},
3986 {"NS/", validate_ns},
3987 {"SN/", validate_sn},
3989 {"LOC_POL/", validate_loc_pol},
3990 {"PWD_POL/", validate_pwd_pol},
3991 {"CRED/", validate_cred},
3992 {"UL/", validate_ul},
3993 {"GL/", validate_gl},
3994 {"UG/", validate_ug},
3995 {"UA", validate_ua},
3996 {"GM/", validate_gm},
3997 {"DR/", validate_dr},
3998 {"DE/", validate_de},
3999 {"NSS/PWINFO/", validate_pwinfo},
4000 {"TRUSTDOMCACHE/", validate_trustdomcache},
4001 {"NSS/NA/", validate_nss_na},
4002 {"NSS/AN/", validate_nss_an},
4003 {"WINBINDD_OFFLINE", validate_offline},
4004 {"NDR/", validate_ndr},
4005 {WINBINDD_CACHE_VERSION_KEYSTR, validate_cache_version},
4009 /***********************************************************************
4010 Function to look at every entry in the tdb and validate it as far as
4012 ***********************************************************************/
4014 static int cache_traverse_validate_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, void *state)
4017 unsigned int max_key_len = 1024;
4018 struct tdb_validation_status *v_state = (struct tdb_validation_status *)state;
4020 /* Paranoia check. */
4021 if (strncmp("UA/", (const char *)kbuf.dptr, 3) == 0) {
4022 max_key_len = 1024 * 1024;
4024 if (kbuf.dsize > max_key_len) {
4025 DEBUG(0, ("cache_traverse_validate_fn: key length too large: "
4027 (unsigned int)kbuf.dsize, (unsigned int)max_key_len));
4031 for (i = 0; key_val[i].keyname; i++) {
4032 size_t namelen = strlen(key_val[i].keyname);
4033 if (kbuf.dsize >= namelen && (
4034 strncmp(key_val[i].keyname, (const char *)kbuf.dptr, namelen)) == 0) {
4035 TALLOC_CTX *mem_ctx;
4039 keystr = SMB_MALLOC_ARRAY(char, kbuf.dsize+1);
4043 memcpy(keystr, kbuf.dptr, kbuf.dsize);
4044 keystr[kbuf.dsize] = '\0';
4046 mem_ctx = talloc_init("validate_ctx");
4052 ret = key_val[i].validate_data_fn(mem_ctx, keystr, dbuf,
4056 talloc_destroy(mem_ctx);
4061 DEBUG(0,("cache_traverse_validate_fn: unknown cache entry\nkey :\n"));
4062 dump_data(0, (uint8 *)kbuf.dptr, kbuf.dsize);
4063 DEBUG(0,("data :\n"));
4064 dump_data(0, (uint8 *)dbuf.dptr, dbuf.dsize);
4065 v_state->unknown_key = true;
4066 v_state->success = false;
4067 return 1; /* terminate. */
4070 static void validate_panic(const char *const why)
4072 DEBUG(0,("validating cache: would panic %s\n", why ));
4073 DEBUGADD(0, ("exiting instead (cache validation mode)\n"));
4077 /***********************************************************************
4078 Try and validate every entry in the winbindd cache. If we fail here,
4079 delete the cache tdb and return non-zero.
4080 ***********************************************************************/
4082 int winbindd_validate_cache(void)
4085 const char *tdb_path = cache_path("winbindd_cache.tdb");
4086 TDB_CONTEXT *tdb = NULL;
4088 DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
4089 smb_panic_fn = validate_panic;
4092 tdb = tdb_open_log(tdb_path,
4093 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
4094 ( lp_winbind_offline_logon()
4096 : TDB_DEFAULT | TDB_CLEAR_IF_FIRST ),
4100 DEBUG(0, ("winbindd_validate_cache: "
4101 "error opening/initializing tdb\n"));
4106 ret = tdb_validate_and_backup(tdb_path, cache_traverse_validate_fn);
4109 DEBUG(10, ("winbindd_validate_cache: validation not successful.\n"));
4110 DEBUGADD(10, ("removing tdb %s.\n", tdb_path));
4115 DEBUG(10, ("winbindd_validate_cache: restoring panic function\n"));
4116 smb_panic_fn = smb_panic;
4120 /***********************************************************************
4121 Try and validate every entry in the winbindd cache.
4122 ***********************************************************************/
4124 int winbindd_validate_cache_nobackup(void)
4127 const char *tdb_path = cache_path("winbindd_cache.tdb");
4129 DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
4130 smb_panic_fn = validate_panic;
4133 if (wcache == NULL || wcache->tdb == NULL) {
4134 ret = tdb_validate_open(tdb_path, cache_traverse_validate_fn);
4136 ret = tdb_validate(wcache->tdb, cache_traverse_validate_fn);
4140 DEBUG(10, ("winbindd_validate_cache_nobackup: validation not "
4144 DEBUG(10, ("winbindd_validate_cache_nobackup: restoring panic "
4146 smb_panic_fn = smb_panic;
4150 bool winbindd_cache_validate_and_initialize(void)
4152 close_winbindd_cache();
4154 if (lp_winbind_offline_logon()) {
4155 if (winbindd_validate_cache() < 0) {
4156 DEBUG(0, ("winbindd cache tdb corrupt and no backup "
4157 "could be restored.\n"));
4161 return initialize_winbindd_cache();
4164 /*********************************************************************
4165 ********************************************************************/
4167 static bool add_wbdomain_to_tdc_array( struct winbindd_domain *new_dom,
4168 struct winbindd_tdc_domain **domains,
4169 size_t *num_domains )
4171 struct winbindd_tdc_domain *list = NULL;
4174 bool set_only = false;
4176 /* don't allow duplicates */
4181 for ( i=0; i< (*num_domains); i++ ) {
4182 if ( strequal( new_dom->name, list[i].domain_name ) ) {
4183 DEBUG(10,("add_wbdomain_to_tdc_array: Found existing record for %s\n",
4194 list = TALLOC_ARRAY( NULL, struct winbindd_tdc_domain, 1 );
4197 list = TALLOC_REALLOC_ARRAY( *domains, *domains,
4198 struct winbindd_tdc_domain,
4203 ZERO_STRUCT( list[idx] );
4209 list[idx].domain_name = talloc_strdup( list, new_dom->name );
4210 list[idx].dns_name = talloc_strdup( list, new_dom->alt_name );
4212 if ( !is_null_sid( &new_dom->sid ) ) {
4213 sid_copy( &list[idx].sid, &new_dom->sid );
4215 sid_copy(&list[idx].sid, &global_sid_NULL);
4218 if ( new_dom->domain_flags != 0x0 )
4219 list[idx].trust_flags = new_dom->domain_flags;
4221 if ( new_dom->domain_type != 0x0 )
4222 list[idx].trust_type = new_dom->domain_type;
4224 if ( new_dom->domain_trust_attribs != 0x0 )
4225 list[idx].trust_attribs = new_dom->domain_trust_attribs;
4229 *num_domains = idx + 1;
4235 /*********************************************************************
4236 ********************************************************************/
4238 static TDB_DATA make_tdc_key( const char *domain_name )
4240 char *keystr = NULL;
4241 TDB_DATA key = { NULL, 0 };
4243 if ( !domain_name ) {
4244 DEBUG(5,("make_tdc_key: Keyname workgroup is NULL!\n"));
4248 if (asprintf( &keystr, "TRUSTDOMCACHE/%s", domain_name ) == -1) {
4251 key = string_term_tdb_data(keystr);
4256 /*********************************************************************
4257 ********************************************************************/
4259 static int pack_tdc_domains( struct winbindd_tdc_domain *domains,
4261 unsigned char **buf )
4263 unsigned char *buffer = NULL;
4268 DEBUG(10,("pack_tdc_domains: Packing %d trusted domains\n",
4276 /* Store the number of array items first */
4277 len += tdb_pack( buffer+len, buflen-len, "d",
4280 /* now pack each domain trust record */
4281 for ( i=0; i<num_domains; i++ ) {
4286 DEBUG(10,("pack_tdc_domains: Packing domain %s (%s)\n",
4287 domains[i].domain_name,
4288 domains[i].dns_name ? domains[i].dns_name : "UNKNOWN" ));
4291 len += tdb_pack( buffer+len, buflen-len, "fffddd",
4292 domains[i].domain_name,
4293 domains[i].dns_name,
4294 sid_to_fstring(tmp, &domains[i].sid),
4295 domains[i].trust_flags,
4296 domains[i].trust_attribs,
4297 domains[i].trust_type );
4300 if ( buflen < len ) {
4302 if ( (buffer = SMB_MALLOC_ARRAY(unsigned char, len)) == NULL ) {
4303 DEBUG(0,("pack_tdc_domains: failed to alloc buffer!\n"));
4317 /*********************************************************************
4318 ********************************************************************/
4320 static size_t unpack_tdc_domains( unsigned char *buf, int buflen,
4321 struct winbindd_tdc_domain **domains )
4323 fstring domain_name, dns_name, sid_string;
4324 uint32 type, attribs, flags;
4328 struct winbindd_tdc_domain *list = NULL;
4330 /* get the number of domains */
4331 len += tdb_unpack( buf+len, buflen-len, "d", &num_domains);
4333 DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
4337 list = TALLOC_ARRAY( NULL, struct winbindd_tdc_domain, num_domains );
4339 DEBUG(0,("unpack_tdc_domains: Failed to talloc() domain list!\n"));
4343 for ( i=0; i<num_domains; i++ ) {
4344 len += tdb_unpack( buf+len, buflen-len, "fffddd",
4353 DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
4354 TALLOC_FREE( list );
4358 DEBUG(11,("unpack_tdc_domains: Unpacking domain %s (%s) "
4359 "SID %s, flags = 0x%x, attribs = 0x%x, type = 0x%x\n",
4360 domain_name, dns_name, sid_string,
4361 flags, attribs, type));
4363 list[i].domain_name = talloc_strdup( list, domain_name );
4364 list[i].dns_name = talloc_strdup( list, dns_name );
4365 if ( !string_to_sid( &(list[i].sid), sid_string ) ) {
4366 DEBUG(10,("unpack_tdc_domains: no SID for domain %s\n",
4369 list[i].trust_flags = flags;
4370 list[i].trust_attribs = attribs;
4371 list[i].trust_type = type;
4379 /*********************************************************************
4380 ********************************************************************/
4382 static bool wcache_tdc_store_list( struct winbindd_tdc_domain *domains, size_t num_domains )
4384 TDB_DATA key = make_tdc_key( lp_workgroup() );
4385 TDB_DATA data = { NULL, 0 };
4391 /* See if we were asked to delete the cache entry */
4394 ret = tdb_delete( wcache->tdb, key );
4398 data.dsize = pack_tdc_domains( domains, num_domains, &data.dptr );
4405 ret = tdb_store( wcache->tdb, key, data, 0 );
4408 SAFE_FREE( data.dptr );
4409 SAFE_FREE( key.dptr );
4411 return ( ret != -1 );
4414 /*********************************************************************
4415 ********************************************************************/
4417 bool wcache_tdc_fetch_list( struct winbindd_tdc_domain **domains, size_t *num_domains )
4419 TDB_DATA key = make_tdc_key( lp_workgroup() );
4420 TDB_DATA data = { NULL, 0 };
4428 data = tdb_fetch( wcache->tdb, key );
4430 SAFE_FREE( key.dptr );
4435 *num_domains = unpack_tdc_domains( data.dptr, data.dsize, domains );
4437 SAFE_FREE( data.dptr );
4445 /*********************************************************************
4446 ********************************************************************/
4448 bool wcache_tdc_add_domain( struct winbindd_domain *domain )
4450 struct winbindd_tdc_domain *dom_list = NULL;
4451 size_t num_domains = 0;
4454 DEBUG(10,("wcache_tdc_add_domain: Adding domain %s (%s), SID %s, "
4455 "flags = 0x%x, attributes = 0x%x, type = 0x%x\n",
4456 domain->name, domain->alt_name,
4457 sid_string_dbg(&domain->sid),
4458 domain->domain_flags,
4459 domain->domain_trust_attribs,
4460 domain->domain_type));
4462 if ( !init_wcache() ) {
4466 /* fetch the list */
4468 wcache_tdc_fetch_list( &dom_list, &num_domains );
4470 /* add the new domain */
4472 if ( !add_wbdomain_to_tdc_array( domain, &dom_list, &num_domains ) ) {
4476 /* pack the domain */
4478 if ( !wcache_tdc_store_list( dom_list, num_domains ) ) {
4486 TALLOC_FREE( dom_list );
4491 /*********************************************************************
4492 ********************************************************************/
4494 struct winbindd_tdc_domain * wcache_tdc_fetch_domain( TALLOC_CTX *ctx, const char *name )
4496 struct winbindd_tdc_domain *dom_list = NULL;
4497 size_t num_domains = 0;
4499 struct winbindd_tdc_domain *d = NULL;
4501 DEBUG(10,("wcache_tdc_fetch_domain: Searching for domain %s\n", name));
4503 if ( !init_wcache() ) {
4507 /* fetch the list */
4509 wcache_tdc_fetch_list( &dom_list, &num_domains );
4511 for ( i=0; i<num_domains; i++ ) {
4512 if ( strequal(name, dom_list[i].domain_name) ||
4513 strequal(name, dom_list[i].dns_name) )
4515 DEBUG(10,("wcache_tdc_fetch_domain: Found domain %s\n",
4518 d = TALLOC_P( ctx, struct winbindd_tdc_domain );
4522 d->domain_name = talloc_strdup( d, dom_list[i].domain_name );
4523 d->dns_name = talloc_strdup( d, dom_list[i].dns_name );
4524 sid_copy( &d->sid, &dom_list[i].sid );
4525 d->trust_flags = dom_list[i].trust_flags;
4526 d->trust_type = dom_list[i].trust_type;
4527 d->trust_attribs = dom_list[i].trust_attribs;
4533 TALLOC_FREE( dom_list );
4539 /*********************************************************************
4540 ********************************************************************/
4542 void wcache_tdc_clear( void )
4544 if ( !init_wcache() )
4547 wcache_tdc_store_list( NULL, 0 );
4553 /*********************************************************************
4554 ********************************************************************/
4556 static void wcache_save_user_pwinfo(struct winbindd_domain *domain,
4558 const struct dom_sid *user_sid,
4559 const char *homedir,
4564 struct cache_entry *centry;
4567 if ( (centry = centry_start(domain, status)) == NULL )
4570 centry_put_string( centry, homedir );
4571 centry_put_string( centry, shell );
4572 centry_put_string( centry, gecos );
4573 centry_put_uint32( centry, gid );
4575 centry_end(centry, "NSS/PWINFO/%s", sid_to_fstring(tmp, user_sid) );
4577 DEBUG(10,("wcache_save_user_pwinfo: %s\n", sid_string_dbg(user_sid) ));
4579 centry_free(centry);
4582 NTSTATUS nss_get_info_cached( struct winbindd_domain *domain,
4583 const struct dom_sid *user_sid,
4585 ADS_STRUCT *ads, LDAPMessage *msg,
4586 const char **homedir, const char **shell,
4587 const char **gecos, gid_t *p_gid)
4589 struct winbind_cache *cache = get_cache(domain);
4590 struct cache_entry *centry = NULL;
4597 centry = wcache_fetch(cache, domain, "NSS/PWINFO/%s",
4598 sid_to_fstring(tmp, user_sid));
4603 *homedir = centry_string( centry, ctx );
4604 *shell = centry_string( centry, ctx );
4605 *gecos = centry_string( centry, ctx );
4606 *p_gid = centry_uint32( centry );
4608 centry_free(centry);
4610 DEBUG(10,("nss_get_info_cached: [Cached] - user_sid %s\n",
4611 sid_string_dbg(user_sid)));
4613 return NT_STATUS_OK;
4617 nt_status = nss_get_info( domain->name, user_sid, ctx, ads, msg,
4618 homedir, shell, gecos, p_gid );
4620 DEBUG(10, ("nss_get_info returned %s\n", nt_errstr(nt_status)));
4622 if ( NT_STATUS_IS_OK(nt_status) ) {
4623 DEBUG(10, ("result:\n\thomedir = '%s'\n", *homedir));
4624 DEBUGADD(10, ("\tshell = '%s'\n", *shell));
4625 DEBUGADD(10, ("\tgecos = '%s'\n", *gecos));
4626 DEBUGADD(10, ("\tgid = '%u'\n", (unsigned int)*p_gid));
4628 wcache_save_user_pwinfo( domain, nt_status, user_sid,
4629 *homedir, *shell, *gecos, *p_gid );
4632 if ( NT_STATUS_EQUAL( nt_status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND ) ) {
4633 DEBUG(5,("nss_get_info_cached: Setting domain %s offline\n",
4635 set_domain_offline( domain );
4642 /* the cache backend methods are exposed via this structure */
4643 struct winbindd_methods cache_methods = {
4661 static bool wcache_ndr_key(TALLOC_CTX *mem_ctx, char *domain_name,
4662 uint32_t opnum, const DATA_BLOB *req,
4668 key = talloc_asprintf(mem_ctx, "NDR/%s/%d/", domain_name, (int)opnum);
4672 keylen = talloc_get_size(key) - 1;
4674 key = talloc_realloc(mem_ctx, key, char, keylen + req->length);
4678 memcpy(key + keylen, req->data, req->length);
4680 pkey->dptr = (uint8_t *)key;
4681 pkey->dsize = talloc_get_size(key);
4685 static bool wcache_opnum_cacheable(uint32_t opnum)
4688 case NDR_WBINT_PING:
4689 case NDR_WBINT_QUERYSEQUENCENUMBER:
4690 case NDR_WBINT_ALLOCATEUID:
4691 case NDR_WBINT_ALLOCATEGID:
4692 case NDR_WBINT_CHECKMACHINEACCOUNT:
4693 case NDR_WBINT_CHANGEMACHINEACCOUNT:
4694 case NDR_WBINT_PINGDC:
4700 bool wcache_fetch_ndr(TALLOC_CTX *mem_ctx, struct winbindd_domain *domain,
4701 uint32_t opnum, const DATA_BLOB *req, DATA_BLOB *resp)
4706 if (!wcache_opnum_cacheable(opnum) ||
4707 is_my_own_sam_domain(domain) ||
4708 is_builtin_domain(domain)) {
4712 if (wcache->tdb == NULL) {
4716 if (!wcache_ndr_key(talloc_tos(), domain->name, opnum, req, &key)) {
4719 data = tdb_fetch(wcache->tdb, key);
4720 TALLOC_FREE(key.dptr);
4722 if (data.dptr == NULL) {
4725 if (data.dsize < 4) {
4729 if (!is_domain_offline(domain)) {
4730 uint32_t entry_seqnum, dom_seqnum, last_check;
4732 if (!wcache_fetch_seqnum(domain->name, &dom_seqnum,
4736 entry_seqnum = IVAL(data.dptr, 0);
4737 if (entry_seqnum != dom_seqnum) {
4738 DEBUG(10, ("Entry has wrong sequence number: %d\n",
4739 (int)entry_seqnum));
4744 resp->data = (uint8_t *)talloc_memdup(mem_ctx, data.dptr + 4,
4746 if (resp->data == NULL) {
4747 DEBUG(10, ("talloc failed\n"));
4750 resp->length = data.dsize - 4;
4754 SAFE_FREE(data.dptr);
4758 void wcache_store_ndr(struct winbindd_domain *domain, uint32_t opnum,
4759 const DATA_BLOB *req, const DATA_BLOB *resp)
4762 uint32_t dom_seqnum, last_check;
4764 if (!wcache_opnum_cacheable(opnum) ||
4765 is_my_own_sam_domain(domain) ||
4766 is_builtin_domain(domain)) {
4770 if (wcache->tdb == NULL) {
4774 if (!wcache_fetch_seqnum(domain->name, &dom_seqnum, &last_check)) {
4775 DEBUG(10, ("could not fetch seqnum for domain %s\n",
4780 if (!wcache_ndr_key(talloc_tos(), domain->name, opnum, req, &key)) {
4784 data.dsize = resp->length + 4;
4785 data.dptr = talloc_array(key.dptr, uint8_t, data.dsize);
4786 if (data.dptr == NULL) {
4790 SIVAL(data.dptr, 0, dom_seqnum);
4791 memcpy(data.dptr+4, resp->data, resp->length);
4793 tdb_store(wcache->tdb, key, data, 0);
4796 TALLOC_FREE(key.dptr);