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/>.
27 #include "system/filesys.h"
29 #include "tdb_validate.h"
30 #include "../libcli/auth/libcli_auth.h"
31 #include "../librpc/gen_ndr/ndr_wbint.h"
34 #include "../libcli/security/security.h"
37 #define DBGC_CLASS DBGC_WINBIND
39 #define WINBINDD_CACHE_VERSION 2
40 #define WINBINDD_CACHE_VERSION_KEYSTR "WINBINDD_CACHE_VERSION"
42 extern struct winbindd_methods reconnect_methods;
44 extern struct winbindd_methods ads_methods;
46 extern struct winbindd_methods builtin_passdb_methods;
47 extern struct winbindd_methods sam_passdb_methods;
50 * JRA. KEEP THIS LIST UP TO DATE IF YOU ADD CACHE ENTRIES.
51 * Here are the list of entry types that are *not* stored
52 * as form struct cache_entry in the cache.
55 static const char *non_centry_keys[] = {
60 WINBINDD_CACHE_VERSION_KEYSTR,
64 /************************************************************************
65 Is this key a non-centry type ?
66 ************************************************************************/
68 static bool is_non_centry_key(TDB_DATA kbuf)
72 if (kbuf.dptr == NULL || kbuf.dsize == 0) {
75 for (i = 0; non_centry_keys[i] != NULL; i++) {
76 size_t namelen = strlen(non_centry_keys[i]);
77 if (kbuf.dsize < namelen) {
80 if (strncmp(non_centry_keys[i], (const char *)kbuf.dptr, namelen) == 0) {
87 /* Global online/offline state - False when online. winbindd starts up online
88 and sets this to true if the first query fails and there's an entry in
89 the cache tdb telling us to stay offline. */
91 static bool global_winbindd_offline_state;
93 struct winbind_cache {
99 uint32 sequence_number;
105 void (*smb_panic_fn)(const char *const why) = smb_panic;
107 #define WINBINDD_MAX_CACHE_SIZE (50*1024*1024)
109 static struct winbind_cache *wcache;
111 /* get the winbind_cache structure */
112 static struct winbind_cache *get_cache(struct winbindd_domain *domain)
114 struct winbind_cache *ret = wcache;
116 /* We have to know what type of domain we are dealing with first. */
118 if (domain->internal) {
119 domain->backend = &builtin_passdb_methods;
120 domain->initialized = True;
123 if (strequal(domain->name, get_global_sam_name()) &&
124 sid_check_is_domain(&domain->sid)) {
125 domain->backend = &sam_passdb_methods;
126 domain->initialized = True;
129 if ( !domain->initialized ) {
130 init_dc_connection( domain );
134 OK. listen up becasue I'm only going to say this once.
135 We have the following scenarios to consider
136 (a) trusted AD domains on a Samba DC,
137 (b) trusted AD domains and we are joined to a non-kerberos domain
138 (c) trusted AD domains and we are joined to a kerberos (AD) domain
140 For (a) we can always contact the trusted domain using krb5
141 since we have the domain trust account password
143 For (b) we can only use RPC since we have no way of
144 getting a krb5 ticket in our own domain
146 For (c) we can always use krb5 since we have a kerberos trust
151 if (!domain->backend) {
153 struct winbindd_domain *our_domain = domain;
155 /* find our domain first so we can figure out if we
156 are joined to a kerberized domain */
158 if ( !domain->primary )
159 our_domain = find_our_domain();
161 if ((our_domain->active_directory || IS_DC)
162 && domain->active_directory
163 && !lp_winbind_rpc_only()) {
164 DEBUG(5,("get_cache: Setting ADS methods for domain %s\n", domain->name));
165 domain->backend = &ads_methods;
167 #endif /* HAVE_ADS */
168 DEBUG(5,("get_cache: Setting MS-RPC methods for domain %s\n", domain->name));
169 domain->backend = &reconnect_methods;
172 #endif /* HAVE_ADS */
178 ret = SMB_XMALLOC_P(struct winbind_cache);
182 wcache_flush_cache();
188 free a centry structure
190 static void centry_free(struct cache_entry *centry)
194 SAFE_FREE(centry->data);
198 static bool centry_check_bytes(struct cache_entry *centry, size_t nbytes)
200 if (centry->len - centry->ofs < nbytes) {
201 DEBUG(0,("centry corruption? needed %u bytes, have %d\n",
202 (unsigned int)nbytes,
203 centry->len - centry->ofs));
210 pull a uint64_t from a cache entry
212 static uint64_t centry_uint64_t(struct cache_entry *centry)
216 if (!centry_check_bytes(centry, 8)) {
217 smb_panic_fn("centry_uint64_t");
219 ret = BVAL(centry->data, centry->ofs);
225 pull a uint32 from a cache entry
227 static uint32 centry_uint32(struct cache_entry *centry)
231 if (!centry_check_bytes(centry, 4)) {
232 smb_panic_fn("centry_uint32");
234 ret = IVAL(centry->data, centry->ofs);
240 pull a uint16 from a cache entry
242 static uint16 centry_uint16(struct cache_entry *centry)
245 if (!centry_check_bytes(centry, 2)) {
246 smb_panic_fn("centry_uint16");
248 ret = CVAL(centry->data, centry->ofs);
254 pull a uint8 from a cache entry
256 static uint8 centry_uint8(struct cache_entry *centry)
259 if (!centry_check_bytes(centry, 1)) {
260 smb_panic_fn("centry_uint8");
262 ret = CVAL(centry->data, centry->ofs);
268 pull a NTTIME from a cache entry
270 static NTTIME centry_nttime(struct cache_entry *centry)
273 if (!centry_check_bytes(centry, 8)) {
274 smb_panic_fn("centry_nttime");
276 ret = IVAL(centry->data, centry->ofs);
278 ret += (uint64)IVAL(centry->data, centry->ofs) << 32;
284 pull a time_t from a cache entry. time_t stored portably as a 64-bit time.
286 static time_t centry_time(struct cache_entry *centry)
288 return (time_t)centry_nttime(centry);
291 /* pull a string from a cache entry, using the supplied
294 static char *centry_string(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
299 len = centry_uint8(centry);
302 /* a deliberate NULL string */
306 if (!centry_check_bytes(centry, (size_t)len)) {
307 smb_panic_fn("centry_string");
310 ret = TALLOC_ARRAY(mem_ctx, char, len+1);
312 smb_panic_fn("centry_string out of memory\n");
314 memcpy(ret,centry->data + centry->ofs, len);
320 /* pull a hash16 from a cache entry, using the supplied
323 static char *centry_hash16(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
328 len = centry_uint8(centry);
331 DEBUG(0,("centry corruption? hash len (%u) != 16\n",
336 if (!centry_check_bytes(centry, 16)) {
340 ret = TALLOC_ARRAY(mem_ctx, char, 16);
342 smb_panic_fn("centry_hash out of memory\n");
344 memcpy(ret,centry->data + centry->ofs, 16);
349 /* pull a sid from a cache entry, using the supplied
352 static bool centry_sid(struct cache_entry *centry, struct dom_sid *sid)
357 sid_string = centry_string(centry, talloc_tos());
358 if (sid_string == NULL) {
361 ret = string_to_sid(sid, sid_string);
362 TALLOC_FREE(sid_string);
368 pull a NTSTATUS from a cache entry
370 static NTSTATUS centry_ntstatus(struct cache_entry *centry)
374 status = NT_STATUS(centry_uint32(centry));
379 /* the server is considered down if it can't give us a sequence number */
380 static bool wcache_server_down(struct winbindd_domain *domain)
387 ret = (domain->sequence_number == DOM_SEQUENCE_NONE);
390 DEBUG(10,("wcache_server_down: server for Domain %s down\n",
395 static bool wcache_fetch_seqnum(const char *domain_name, uint32_t *seqnum,
396 uint32_t *last_seq_check)
401 if (wcache->tdb == NULL) {
402 DEBUG(10,("wcache_fetch_seqnum: tdb == NULL\n"));
406 key = talloc_asprintf(talloc_tos(), "SEQNUM/%s", domain_name);
408 DEBUG(10, ("talloc failed\n"));
412 data = tdb_fetch_bystring(wcache->tdb, key);
415 if (data.dptr == NULL) {
416 DEBUG(10, ("wcache_fetch_seqnum: %s not found\n",
420 if (data.dsize != 8) {
421 DEBUG(10, ("wcache_fetch_seqnum: invalid data size %d\n",
423 SAFE_FREE(data.dptr);
427 *seqnum = IVAL(data.dptr, 0);
428 *last_seq_check = IVAL(data.dptr, 4);
429 SAFE_FREE(data.dptr);
434 static NTSTATUS fetch_cache_seqnum( struct winbindd_domain *domain, time_t now )
436 uint32 last_check, time_diff;
438 if (!wcache_fetch_seqnum(domain->name, &domain->sequence_number,
440 return NT_STATUS_UNSUCCESSFUL;
442 domain->last_seq_check = last_check;
444 /* have we expired? */
446 time_diff = now - domain->last_seq_check;
447 if ( time_diff > lp_winbind_cache_time() ) {
448 DEBUG(10,("fetch_cache_seqnum: timeout [%s][%u @ %u]\n",
449 domain->name, domain->sequence_number,
450 (uint32)domain->last_seq_check));
451 return NT_STATUS_UNSUCCESSFUL;
454 DEBUG(10,("fetch_cache_seqnum: success [%s][%u @ %u]\n",
455 domain->name, domain->sequence_number,
456 (uint32)domain->last_seq_check));
461 bool wcache_store_seqnum(const char *domain_name, uint32_t seqnum,
462 time_t last_seq_check)
468 if (wcache->tdb == NULL) {
469 DEBUG(10, ("wcache_store_seqnum: wcache->tdb == NULL\n"));
473 key_str = talloc_asprintf(talloc_tos(), "SEQNUM/%s", domain_name);
474 if (key_str == NULL) {
475 DEBUG(10, ("talloc_asprintf failed\n"));
479 SIVAL(buf, 0, seqnum);
480 SIVAL(buf, 4, last_seq_check);
482 ret = tdb_store_bystring(wcache->tdb, key_str,
483 make_tdb_data(buf, sizeof(buf)), TDB_REPLACE);
484 TALLOC_FREE(key_str);
486 DEBUG(10, ("tdb_store_bystring failed: %s\n",
487 tdb_errorstr(wcache->tdb)));
488 TALLOC_FREE(key_str);
492 DEBUG(10, ("wcache_store_seqnum: success [%s][%u @ %u]\n",
493 domain_name, seqnum, (unsigned)last_seq_check));
498 static bool store_cache_seqnum( struct winbindd_domain *domain )
500 return wcache_store_seqnum(domain->name, domain->sequence_number,
501 domain->last_seq_check);
505 refresh the domain sequence number. If force is true
506 then always refresh it, no matter how recently we fetched it
509 static void refresh_sequence_number(struct winbindd_domain *domain, bool force)
513 time_t t = time(NULL);
514 unsigned cache_time = lp_winbind_cache_time();
516 if (is_domain_offline(domain)) {
522 #if 0 /* JERRY -- disable as the default cache time is now 5 minutes */
523 /* trying to reconnect is expensive, don't do it too often */
524 if (domain->sequence_number == DOM_SEQUENCE_NONE) {
529 time_diff = t - domain->last_seq_check;
531 /* see if we have to refetch the domain sequence number */
532 if (!force && (time_diff < cache_time) &&
533 (domain->sequence_number != DOM_SEQUENCE_NONE) &&
534 NT_STATUS_IS_OK(domain->last_status)) {
535 DEBUG(10, ("refresh_sequence_number: %s time ok\n", domain->name));
539 /* try to get the sequence number from the tdb cache first */
540 /* this will update the timestamp as well */
542 status = fetch_cache_seqnum( domain, t );
543 if (NT_STATUS_IS_OK(status) &&
544 (domain->sequence_number != DOM_SEQUENCE_NONE) &&
545 NT_STATUS_IS_OK(domain->last_status)) {
549 /* important! make sure that we know if this is a native
550 mode domain or not. And that we can contact it. */
552 if ( winbindd_can_contact_domain( domain ) ) {
553 status = domain->backend->sequence_number(domain,
554 &domain->sequence_number);
556 /* just use the current time */
557 status = NT_STATUS_OK;
558 domain->sequence_number = time(NULL);
562 /* the above call could have set our domain->backend to NULL when
563 * coming from offline to online mode, make sure to reinitialize the
564 * backend - Guenther */
567 if (!NT_STATUS_IS_OK(status)) {
568 DEBUG(10,("refresh_sequence_number: failed with %s\n", nt_errstr(status)));
569 domain->sequence_number = DOM_SEQUENCE_NONE;
572 domain->last_status = status;
573 domain->last_seq_check = time(NULL);
575 /* save the new sequence number in the cache */
576 store_cache_seqnum( domain );
579 DEBUG(10, ("refresh_sequence_number: %s seq number is now %d\n",
580 domain->name, domain->sequence_number));
586 decide if a cache entry has expired
588 static bool centry_expired(struct winbindd_domain *domain, const char *keystr, struct cache_entry *centry)
590 /* If we've been told to be offline - stay in that state... */
591 if (lp_winbind_offline_logon() && global_winbindd_offline_state) {
592 DEBUG(10,("centry_expired: Key %s for domain %s valid as winbindd is globally offline.\n",
593 keystr, domain->name ));
597 /* when the domain is offline return the cached entry.
598 * This deals with transient offline states... */
600 if (!domain->online) {
601 DEBUG(10,("centry_expired: Key %s for domain %s valid as domain is offline.\n",
602 keystr, domain->name ));
606 /* if the server is OK and our cache entry came from when it was down then
607 the entry is invalid */
608 if ((domain->sequence_number != DOM_SEQUENCE_NONE) &&
609 (centry->sequence_number == DOM_SEQUENCE_NONE)) {
610 DEBUG(10,("centry_expired: Key %s for domain %s invalid sequence.\n",
611 keystr, domain->name ));
615 /* if the server is down or the cache entry is not older than the
616 current sequence number or it did not timeout then it is OK */
617 if (wcache_server_down(domain)
618 || ((centry->sequence_number == domain->sequence_number)
619 && (centry->timeout > time(NULL)))) {
620 DEBUG(10,("centry_expired: Key %s for domain %s is good.\n",
621 keystr, domain->name ));
625 DEBUG(10,("centry_expired: Key %s for domain %s expired\n",
626 keystr, domain->name ));
632 static struct cache_entry *wcache_fetch_raw(char *kstr)
635 struct cache_entry *centry;
638 key = string_tdb_data(kstr);
639 data = tdb_fetch(wcache->tdb, key);
645 centry = SMB_XMALLOC_P(struct cache_entry);
646 centry->data = (unsigned char *)data.dptr;
647 centry->len = data.dsize;
650 if (centry->len < 16) {
651 /* huh? corrupt cache? */
652 DEBUG(10,("wcache_fetch_raw: Corrupt cache for key %s "
653 "(len < 16)?\n", kstr));
658 centry->status = centry_ntstatus(centry);
659 centry->sequence_number = centry_uint32(centry);
660 centry->timeout = centry_uint64_t(centry);
665 static bool is_my_own_sam_domain(struct winbindd_domain *domain)
667 if (strequal(domain->name, get_global_sam_name()) &&
668 sid_check_is_domain(&domain->sid)) {
675 static bool is_builtin_domain(struct winbindd_domain *domain)
677 if (strequal(domain->name, "BUILTIN") &&
678 sid_check_is_builtin(&domain->sid)) {
686 fetch an entry from the cache, with a varargs key. auto-fetch the sequence
687 number and return status
689 static struct cache_entry *wcache_fetch(struct winbind_cache *cache,
690 struct winbindd_domain *domain,
691 const char *format, ...) PRINTF_ATTRIBUTE(3,4);
692 static struct cache_entry *wcache_fetch(struct winbind_cache *cache,
693 struct winbindd_domain *domain,
694 const char *format, ...)
698 struct cache_entry *centry;
700 if (!winbindd_use_cache() ||
701 is_my_own_sam_domain(domain) ||
702 is_builtin_domain(domain)) {
706 refresh_sequence_number(domain, false);
708 va_start(ap, format);
709 smb_xvasprintf(&kstr, format, ap);
712 centry = wcache_fetch_raw(kstr);
713 if (centry == NULL) {
718 if (centry_expired(domain, kstr, centry)) {
720 DEBUG(10,("wcache_fetch: entry %s expired for domain %s\n",
721 kstr, domain->name ));
728 DEBUG(10,("wcache_fetch: returning entry %s for domain %s\n",
729 kstr, domain->name ));
735 static void wcache_delete(const char *format, ...) PRINTF_ATTRIBUTE(1,2);
736 static void wcache_delete(const char *format, ...)
742 va_start(ap, format);
743 smb_xvasprintf(&kstr, format, ap);
746 key = string_tdb_data(kstr);
748 tdb_delete(wcache->tdb, key);
753 make sure we have at least len bytes available in a centry
755 static void centry_expand(struct cache_entry *centry, uint32 len)
757 if (centry->len - centry->ofs >= len)
760 centry->data = SMB_REALLOC_ARRAY(centry->data, unsigned char,
763 DEBUG(0,("out of memory: needed %d bytes in centry_expand\n", centry->len));
764 smb_panic_fn("out of memory in centry_expand");
769 push a uint64_t into a centry
771 static void centry_put_uint64_t(struct cache_entry *centry, uint64_t v)
773 centry_expand(centry, 8);
774 SBVAL(centry->data, centry->ofs, v);
779 push a uint32 into a centry
781 static void centry_put_uint32(struct cache_entry *centry, uint32 v)
783 centry_expand(centry, 4);
784 SIVAL(centry->data, centry->ofs, v);
789 push a uint16 into a centry
791 static void centry_put_uint16(struct cache_entry *centry, uint16 v)
793 centry_expand(centry, 2);
794 SIVAL(centry->data, centry->ofs, v);
799 push a uint8 into a centry
801 static void centry_put_uint8(struct cache_entry *centry, uint8 v)
803 centry_expand(centry, 1);
804 SCVAL(centry->data, centry->ofs, v);
809 push a string into a centry
811 static void centry_put_string(struct cache_entry *centry, const char *s)
816 /* null strings are marked as len 0xFFFF */
817 centry_put_uint8(centry, 0xFF);
822 /* can't handle more than 254 char strings. Truncating is probably best */
824 DEBUG(10,("centry_put_string: truncating len (%d) to: 254\n", len));
827 centry_put_uint8(centry, len);
828 centry_expand(centry, len);
829 memcpy(centry->data + centry->ofs, s, len);
834 push a 16 byte hash into a centry - treat as 16 byte string.
836 static void centry_put_hash16(struct cache_entry *centry, const uint8 val[16])
838 centry_put_uint8(centry, 16);
839 centry_expand(centry, 16);
840 memcpy(centry->data + centry->ofs, val, 16);
844 static void centry_put_sid(struct cache_entry *centry, const struct dom_sid *sid)
847 centry_put_string(centry, sid_to_fstring(sid_string, sid));
852 put NTSTATUS into a centry
854 static void centry_put_ntstatus(struct cache_entry *centry, NTSTATUS status)
856 uint32 status_value = NT_STATUS_V(status);
857 centry_put_uint32(centry, status_value);
862 push a NTTIME into a centry
864 static void centry_put_nttime(struct cache_entry *centry, NTTIME nt)
866 centry_expand(centry, 8);
867 SIVAL(centry->data, centry->ofs, nt & 0xFFFFFFFF);
869 SIVAL(centry->data, centry->ofs, nt >> 32);
874 push a time_t into a centry - use a 64 bit size.
875 NTTIME here is being used as a convenient 64-bit size.
877 static void centry_put_time(struct cache_entry *centry, time_t t)
879 NTTIME nt = (NTTIME)t;
880 centry_put_nttime(centry, nt);
884 start a centry for output. When finished, call centry_end()
886 struct cache_entry *centry_start(struct winbindd_domain *domain, NTSTATUS status)
888 struct cache_entry *centry;
893 centry = SMB_XMALLOC_P(struct cache_entry);
895 centry->len = 8192; /* reasonable default */
896 centry->data = SMB_XMALLOC_ARRAY(uint8, centry->len);
898 centry->sequence_number = domain->sequence_number;
899 centry->timeout = lp_winbind_cache_time() + time(NULL);
900 centry_put_ntstatus(centry, status);
901 centry_put_uint32(centry, centry->sequence_number);
902 centry_put_uint64_t(centry, centry->timeout);
907 finish a centry and write it to the tdb
909 static void centry_end(struct cache_entry *centry, const char *format, ...) PRINTF_ATTRIBUTE(2,3);
910 static void centry_end(struct cache_entry *centry, const char *format, ...)
916 if (!winbindd_use_cache()) {
920 va_start(ap, format);
921 smb_xvasprintf(&kstr, format, ap);
924 key = string_tdb_data(kstr);
925 data.dptr = centry->data;
926 data.dsize = centry->ofs;
928 tdb_store(wcache->tdb, key, data, TDB_REPLACE);
932 static void wcache_save_name_to_sid(struct winbindd_domain *domain,
933 NTSTATUS status, const char *domain_name,
934 const char *name, const struct dom_sid *sid,
935 enum lsa_SidType type)
937 struct cache_entry *centry;
940 centry = centry_start(domain, status);
943 centry_put_uint32(centry, type);
944 centry_put_sid(centry, sid);
945 fstrcpy(uname, name);
947 centry_end(centry, "NS/%s/%s", domain_name, uname);
948 DEBUG(10,("wcache_save_name_to_sid: %s\\%s -> %s (%s)\n", domain_name,
949 uname, sid_string_dbg(sid), nt_errstr(status)));
953 static void wcache_save_sid_to_name(struct winbindd_domain *domain, NTSTATUS status,
954 const struct dom_sid *sid, const char *domain_name, const char *name, enum lsa_SidType type)
956 struct cache_entry *centry;
959 centry = centry_start(domain, status);
963 if (NT_STATUS_IS_OK(status)) {
964 centry_put_uint32(centry, type);
965 centry_put_string(centry, domain_name);
966 centry_put_string(centry, name);
969 centry_end(centry, "SN/%s", sid_to_fstring(sid_string, sid));
970 DEBUG(10,("wcache_save_sid_to_name: %s -> %s (%s)\n", sid_string,
971 name, nt_errstr(status)));
976 static void wcache_save_user(struct winbindd_domain *domain, NTSTATUS status,
977 struct wbint_userinfo *info)
979 struct cache_entry *centry;
982 if (is_null_sid(&info->user_sid)) {
986 centry = centry_start(domain, status);
989 centry_put_string(centry, info->acct_name);
990 centry_put_string(centry, info->full_name);
991 centry_put_string(centry, info->homedir);
992 centry_put_string(centry, info->shell);
993 centry_put_uint32(centry, info->primary_gid);
994 centry_put_sid(centry, &info->user_sid);
995 centry_put_sid(centry, &info->group_sid);
996 centry_end(centry, "U/%s", sid_to_fstring(sid_string,
998 DEBUG(10,("wcache_save_user: %s (acct_name %s)\n", sid_string, info->acct_name));
1002 static void wcache_save_lockout_policy(struct winbindd_domain *domain,
1004 struct samr_DomInfo12 *lockout_policy)
1006 struct cache_entry *centry;
1008 centry = centry_start(domain, status);
1012 centry_put_nttime(centry, lockout_policy->lockout_duration);
1013 centry_put_nttime(centry, lockout_policy->lockout_window);
1014 centry_put_uint16(centry, lockout_policy->lockout_threshold);
1016 centry_end(centry, "LOC_POL/%s", domain->name);
1018 DEBUG(10,("wcache_save_lockout_policy: %s\n", domain->name));
1020 centry_free(centry);
1025 static void wcache_save_password_policy(struct winbindd_domain *domain,
1027 struct samr_DomInfo1 *policy)
1029 struct cache_entry *centry;
1031 centry = centry_start(domain, status);
1035 centry_put_uint16(centry, policy->min_password_length);
1036 centry_put_uint16(centry, policy->password_history_length);
1037 centry_put_uint32(centry, policy->password_properties);
1038 centry_put_nttime(centry, policy->max_password_age);
1039 centry_put_nttime(centry, policy->min_password_age);
1041 centry_end(centry, "PWD_POL/%s", domain->name);
1043 DEBUG(10,("wcache_save_password_policy: %s\n", domain->name));
1045 centry_free(centry);
1048 /***************************************************************************
1049 ***************************************************************************/
1051 static void wcache_save_username_alias(struct winbindd_domain *domain,
1053 const char *name, const char *alias)
1055 struct cache_entry *centry;
1058 if ( (centry = centry_start(domain, status)) == NULL )
1061 centry_put_string( centry, alias );
1063 fstrcpy(uname, name);
1065 centry_end(centry, "NSS/NA/%s", uname);
1067 DEBUG(10,("wcache_save_username_alias: %s -> %s\n", name, alias ));
1069 centry_free(centry);
1072 static void wcache_save_alias_username(struct winbindd_domain *domain,
1074 const char *alias, const char *name)
1076 struct cache_entry *centry;
1079 if ( (centry = centry_start(domain, status)) == NULL )
1082 centry_put_string( centry, name );
1084 fstrcpy(uname, alias);
1086 centry_end(centry, "NSS/AN/%s", uname);
1088 DEBUG(10,("wcache_save_alias_username: %s -> %s\n", alias, name ));
1090 centry_free(centry);
1093 /***************************************************************************
1094 ***************************************************************************/
1096 NTSTATUS resolve_username_to_alias( TALLOC_CTX *mem_ctx,
1097 struct winbindd_domain *domain,
1098 const char *name, char **alias )
1100 struct winbind_cache *cache = get_cache(domain);
1101 struct cache_entry *centry = NULL;
1105 if ( domain->internal )
1106 return NT_STATUS_NOT_SUPPORTED;
1111 if ( (upper_name = SMB_STRDUP(name)) == NULL )
1112 return NT_STATUS_NO_MEMORY;
1113 strupper_m(upper_name);
1115 centry = wcache_fetch(cache, domain, "NSS/NA/%s", upper_name);
1117 SAFE_FREE( upper_name );
1122 status = centry->status;
1124 if (!NT_STATUS_IS_OK(status)) {
1125 centry_free(centry);
1129 *alias = centry_string( centry, mem_ctx );
1131 centry_free(centry);
1133 DEBUG(10,("resolve_username_to_alias: [Cached] - mapped %s to %s\n",
1134 name, *alias ? *alias : "(none)"));
1136 return (*alias) ? NT_STATUS_OK : NT_STATUS_OBJECT_NAME_NOT_FOUND;
1140 /* If its not in cache and we are offline, then fail */
1142 if ( get_global_winbindd_state_offline() || !domain->online ) {
1143 DEBUG(8,("resolve_username_to_alias: rejecting query "
1144 "in offline mode\n"));
1145 return NT_STATUS_NOT_FOUND;
1148 status = nss_map_to_alias( mem_ctx, domain->name, name, alias );
1150 if ( NT_STATUS_IS_OK( status ) ) {
1151 wcache_save_username_alias(domain, status, name, *alias);
1154 if ( NT_STATUS_EQUAL( status, NT_STATUS_NONE_MAPPED ) ) {
1155 wcache_save_username_alias(domain, status, name, "(NULL)");
1158 DEBUG(5,("resolve_username_to_alias: backend query returned %s\n",
1159 nt_errstr(status)));
1161 if ( NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ) {
1162 set_domain_offline( domain );
1168 /***************************************************************************
1169 ***************************************************************************/
1171 NTSTATUS resolve_alias_to_username( TALLOC_CTX *mem_ctx,
1172 struct winbindd_domain *domain,
1173 const char *alias, char **name )
1175 struct winbind_cache *cache = get_cache(domain);
1176 struct cache_entry *centry = NULL;
1180 if ( domain->internal )
1181 return NT_STATUS_NOT_SUPPORTED;
1186 if ( (upper_name = SMB_STRDUP(alias)) == NULL )
1187 return NT_STATUS_NO_MEMORY;
1188 strupper_m(upper_name);
1190 centry = wcache_fetch(cache, domain, "NSS/AN/%s", upper_name);
1192 SAFE_FREE( upper_name );
1197 status = centry->status;
1199 if (!NT_STATUS_IS_OK(status)) {
1200 centry_free(centry);
1204 *name = centry_string( centry, mem_ctx );
1206 centry_free(centry);
1208 DEBUG(10,("resolve_alias_to_username: [Cached] - mapped %s to %s\n",
1209 alias, *name ? *name : "(none)"));
1211 return (*name) ? NT_STATUS_OK : NT_STATUS_OBJECT_NAME_NOT_FOUND;
1215 /* If its not in cache and we are offline, then fail */
1217 if ( get_global_winbindd_state_offline() || !domain->online ) {
1218 DEBUG(8,("resolve_alias_to_username: rejecting query "
1219 "in offline mode\n"));
1220 return NT_STATUS_NOT_FOUND;
1223 /* an alias cannot contain a domain prefix or '@' */
1225 if (strchr(alias, '\\') || strchr(alias, '@')) {
1226 DEBUG(10,("resolve_alias_to_username: skipping fully "
1227 "qualified name %s\n", alias));
1228 return NT_STATUS_OBJECT_NAME_INVALID;
1231 status = nss_map_from_alias( mem_ctx, domain->name, alias, name );
1233 if ( NT_STATUS_IS_OK( status ) ) {
1234 wcache_save_alias_username( domain, status, alias, *name );
1237 if (NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) {
1238 wcache_save_alias_username(domain, status, alias, "(NULL)");
1241 DEBUG(5,("resolve_alias_to_username: backend query returned %s\n",
1242 nt_errstr(status)));
1244 if ( NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ) {
1245 set_domain_offline( domain );
1251 NTSTATUS wcache_cached_creds_exist(struct winbindd_domain *domain, const struct dom_sid *sid)
1253 struct winbind_cache *cache = get_cache(domain);
1255 fstring key_str, tmp;
1259 return NT_STATUS_INTERNAL_DB_ERROR;
1262 if (is_null_sid(sid)) {
1263 return NT_STATUS_INVALID_SID;
1266 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1267 return NT_STATUS_INVALID_SID;
1270 fstr_sprintf(key_str, "CRED/%s", sid_to_fstring(tmp, sid));
1272 data = tdb_fetch(cache->tdb, string_tdb_data(key_str));
1274 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1277 SAFE_FREE(data.dptr);
1278 return NT_STATUS_OK;
1281 /* Lookup creds for a SID - copes with old (unsalted) creds as well
1282 as new salted ones. */
1284 NTSTATUS wcache_get_creds(struct winbindd_domain *domain,
1285 TALLOC_CTX *mem_ctx,
1286 const struct dom_sid *sid,
1287 const uint8 **cached_nt_pass,
1288 const uint8 **cached_salt)
1290 struct winbind_cache *cache = get_cache(domain);
1291 struct cache_entry *centry = NULL;
1298 return NT_STATUS_INTERNAL_DB_ERROR;
1301 if (is_null_sid(sid)) {
1302 return NT_STATUS_INVALID_SID;
1305 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1306 return NT_STATUS_INVALID_SID;
1309 /* Try and get a salted cred first. If we can't
1310 fall back to an unsalted cred. */
1312 centry = wcache_fetch(cache, domain, "CRED/%s",
1313 sid_to_fstring(tmp, sid));
1315 DEBUG(10,("wcache_get_creds: entry for [CRED/%s] not found\n",
1316 sid_string_dbg(sid)));
1317 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1320 t = centry_time(centry);
1322 /* In the salted case this isn't actually the nt_hash itself,
1323 but the MD5 of the salt + nt_hash. Let the caller
1324 sort this out. It can tell as we only return the cached_salt
1325 if we are returning a salted cred. */
1327 *cached_nt_pass = (const uint8 *)centry_hash16(centry, mem_ctx);
1328 if (*cached_nt_pass == NULL) {
1331 sid_to_fstring(sidstr, sid);
1333 /* Bad (old) cred cache. Delete and pretend we
1335 DEBUG(0,("wcache_get_creds: bad entry for [CRED/%s] - deleting\n",
1337 wcache_delete("CRED/%s", sidstr);
1338 centry_free(centry);
1339 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1342 /* We only have 17 bytes more data in the salted cred case. */
1343 if (centry->len - centry->ofs == 17) {
1344 *cached_salt = (const uint8 *)centry_hash16(centry, mem_ctx);
1346 *cached_salt = NULL;
1349 dump_data_pw("cached_nt_pass", *cached_nt_pass, NT_HASH_LEN);
1351 dump_data_pw("cached_salt", *cached_salt, NT_HASH_LEN);
1354 status = centry->status;
1356 DEBUG(10,("wcache_get_creds: [Cached] - cached creds for user %s status: %s\n",
1357 sid_string_dbg(sid), nt_errstr(status) ));
1359 centry_free(centry);
1363 /* Store creds for a SID - only writes out new salted ones. */
1365 NTSTATUS wcache_save_creds(struct winbindd_domain *domain,
1366 const struct dom_sid *sid,
1367 const uint8 nt_pass[NT_HASH_LEN])
1369 struct cache_entry *centry;
1372 uint8 cred_salt[NT_HASH_LEN];
1373 uint8 salted_hash[NT_HASH_LEN];
1375 if (is_null_sid(sid)) {
1376 return NT_STATUS_INVALID_SID;
1379 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1380 return NT_STATUS_INVALID_SID;
1383 centry = centry_start(domain, NT_STATUS_OK);
1385 return NT_STATUS_INTERNAL_DB_ERROR;
1388 dump_data_pw("nt_pass", nt_pass, NT_HASH_LEN);
1390 centry_put_time(centry, time(NULL));
1392 /* Create a salt and then salt the hash. */
1393 generate_random_buffer(cred_salt, NT_HASH_LEN);
1394 E_md5hash(cred_salt, nt_pass, salted_hash);
1396 centry_put_hash16(centry, salted_hash);
1397 centry_put_hash16(centry, cred_salt);
1398 centry_end(centry, "CRED/%s", sid_to_fstring(sid_string, sid));
1400 DEBUG(10,("wcache_save_creds: %s\n", sid_string));
1402 centry_free(centry);
1404 return NT_STATUS_OK;
1408 /* Query display info. This is the basic user list fn */
1409 static NTSTATUS query_user_list(struct winbindd_domain *domain,
1410 TALLOC_CTX *mem_ctx,
1411 uint32 *num_entries,
1412 struct wbint_userinfo **info)
1414 struct winbind_cache *cache = get_cache(domain);
1415 struct cache_entry *centry = NULL;
1417 unsigned int i, retry;
1418 bool old_status = domain->online;
1423 centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
1428 *num_entries = centry_uint32(centry);
1430 if (*num_entries == 0)
1433 (*info) = TALLOC_ARRAY(mem_ctx, struct wbint_userinfo, *num_entries);
1435 smb_panic_fn("query_user_list out of memory");
1437 for (i=0; i<(*num_entries); i++) {
1438 (*info)[i].acct_name = centry_string(centry, mem_ctx);
1439 (*info)[i].full_name = centry_string(centry, mem_ctx);
1440 (*info)[i].homedir = centry_string(centry, mem_ctx);
1441 (*info)[i].shell = centry_string(centry, mem_ctx);
1442 centry_sid(centry, &(*info)[i].user_sid);
1443 centry_sid(centry, &(*info)[i].group_sid);
1447 status = centry->status;
1449 DEBUG(10,("query_user_list: [Cached] - cached list for domain %s status: %s\n",
1450 domain->name, nt_errstr(status) ));
1452 centry_free(centry);
1459 /* Return status value returned by seq number check */
1461 if (!NT_STATUS_IS_OK(domain->last_status))
1462 return domain->last_status;
1464 /* Put the query_user_list() in a retry loop. There appears to be
1465 * some bug either with Windows 2000 or Samba's handling of large
1466 * rpc replies. This manifests itself as sudden disconnection
1467 * at a random point in the enumeration of a large (60k) user list.
1468 * The retry loop simply tries the operation again. )-: It's not
1469 * pretty but an acceptable workaround until we work out what the
1470 * real problem is. */
1475 DEBUG(10,("query_user_list: [Cached] - doing backend query for list for domain %s\n",
1478 status = domain->backend->query_user_list(domain, mem_ctx, num_entries, info);
1479 if (!NT_STATUS_IS_OK(status)) {
1480 DEBUG(3, ("query_user_list: returned 0x%08x, "
1481 "retrying\n", NT_STATUS_V(status)));
1483 if (NT_STATUS_EQUAL(status, NT_STATUS_UNSUCCESSFUL)) {
1484 DEBUG(3, ("query_user_list: flushing "
1485 "connection cache\n"));
1486 invalidate_cm_connection(&domain->conn);
1488 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1489 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1490 if (!domain->internal && old_status) {
1491 set_domain_offline(domain);
1493 /* store partial response. */
1494 if (*num_entries > 0) {
1496 * humm, what about the status used for cache?
1497 * Should it be NT_STATUS_OK?
1502 * domain is offline now, and there is no user entries,
1503 * try to fetch from cache again.
1505 if (cache->tdb && !domain->online && !domain->internal && old_status) {
1506 centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
1507 /* partial response... */
1511 goto do_fetch_cache;
1518 } while (NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_UNSUCCESSFUL) &&
1522 refresh_sequence_number(domain, false);
1523 if (!NT_STATUS_IS_OK(status)) {
1526 centry = centry_start(domain, status);
1529 centry_put_uint32(centry, *num_entries);
1530 for (i=0; i<(*num_entries); i++) {
1531 centry_put_string(centry, (*info)[i].acct_name);
1532 centry_put_string(centry, (*info)[i].full_name);
1533 centry_put_string(centry, (*info)[i].homedir);
1534 centry_put_string(centry, (*info)[i].shell);
1535 centry_put_sid(centry, &(*info)[i].user_sid);
1536 centry_put_sid(centry, &(*info)[i].group_sid);
1537 if (domain->backend && domain->backend->consistent) {
1538 /* when the backend is consistent we can pre-prime some mappings */
1539 wcache_save_name_to_sid(domain, NT_STATUS_OK,
1541 (*info)[i].acct_name,
1542 &(*info)[i].user_sid,
1544 wcache_save_sid_to_name(domain, NT_STATUS_OK,
1545 &(*info)[i].user_sid,
1547 (*info)[i].acct_name,
1549 wcache_save_user(domain, NT_STATUS_OK, &(*info)[i]);
1552 centry_end(centry, "UL/%s", domain->name);
1553 centry_free(centry);
1559 /* list all domain groups */
1560 static NTSTATUS enum_dom_groups(struct winbindd_domain *domain,
1561 TALLOC_CTX *mem_ctx,
1562 uint32 *num_entries,
1563 struct acct_info **info)
1565 struct winbind_cache *cache = get_cache(domain);
1566 struct cache_entry *centry = NULL;
1571 old_status = domain->online;
1575 centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
1580 *num_entries = centry_uint32(centry);
1582 if (*num_entries == 0)
1585 (*info) = TALLOC_ARRAY(mem_ctx, struct acct_info, *num_entries);
1587 smb_panic_fn("enum_dom_groups out of memory");
1589 for (i=0; i<(*num_entries); i++) {
1590 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1591 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1592 (*info)[i].rid = centry_uint32(centry);
1596 status = centry->status;
1598 DEBUG(10,("enum_dom_groups: [Cached] - cached list for domain %s status: %s\n",
1599 domain->name, nt_errstr(status) ));
1601 centry_free(centry);
1608 /* Return status value returned by seq number check */
1610 if (!NT_STATUS_IS_OK(domain->last_status))
1611 return domain->last_status;
1613 DEBUG(10,("enum_dom_groups: [Cached] - doing backend query for list for domain %s\n",
1616 status = domain->backend->enum_dom_groups(domain, mem_ctx, num_entries, info);
1618 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1619 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1620 if (!domain->internal && old_status) {
1621 set_domain_offline(domain);
1625 !domain->internal &&
1627 centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
1629 goto do_fetch_cache;
1634 refresh_sequence_number(domain, false);
1635 if (!NT_STATUS_IS_OK(status)) {
1638 centry = centry_start(domain, status);
1641 centry_put_uint32(centry, *num_entries);
1642 for (i=0; i<(*num_entries); i++) {
1643 centry_put_string(centry, (*info)[i].acct_name);
1644 centry_put_string(centry, (*info)[i].acct_desc);
1645 centry_put_uint32(centry, (*info)[i].rid);
1647 centry_end(centry, "GL/%s/domain", domain->name);
1648 centry_free(centry);
1654 /* list all domain groups */
1655 static NTSTATUS enum_local_groups(struct winbindd_domain *domain,
1656 TALLOC_CTX *mem_ctx,
1657 uint32 *num_entries,
1658 struct acct_info **info)
1660 struct winbind_cache *cache = get_cache(domain);
1661 struct cache_entry *centry = NULL;
1666 old_status = domain->online;
1670 centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
1675 *num_entries = centry_uint32(centry);
1677 if (*num_entries == 0)
1680 (*info) = TALLOC_ARRAY(mem_ctx, struct acct_info, *num_entries);
1682 smb_panic_fn("enum_dom_groups out of memory");
1684 for (i=0; i<(*num_entries); i++) {
1685 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1686 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1687 (*info)[i].rid = centry_uint32(centry);
1692 /* If we are returning cached data and the domain controller
1693 is down then we don't know whether the data is up to date
1694 or not. Return NT_STATUS_MORE_PROCESSING_REQUIRED to
1697 if (wcache_server_down(domain)) {
1698 DEBUG(10, ("enum_local_groups: returning cached user list and server was down\n"));
1699 status = NT_STATUS_MORE_PROCESSING_REQUIRED;
1701 status = centry->status;
1703 DEBUG(10,("enum_local_groups: [Cached] - cached list for domain %s status: %s\n",
1704 domain->name, nt_errstr(status) ));
1706 centry_free(centry);
1713 /* Return status value returned by seq number check */
1715 if (!NT_STATUS_IS_OK(domain->last_status))
1716 return domain->last_status;
1718 DEBUG(10,("enum_local_groups: [Cached] - doing backend query for list for domain %s\n",
1721 status = domain->backend->enum_local_groups(domain, mem_ctx, num_entries, info);
1723 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1724 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1725 if (!domain->internal && old_status) {
1726 set_domain_offline(domain);
1729 !domain->internal &&
1732 centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
1734 goto do_fetch_cache;
1739 refresh_sequence_number(domain, false);
1740 if (!NT_STATUS_IS_OK(status)) {
1743 centry = centry_start(domain, status);
1746 centry_put_uint32(centry, *num_entries);
1747 for (i=0; i<(*num_entries); i++) {
1748 centry_put_string(centry, (*info)[i].acct_name);
1749 centry_put_string(centry, (*info)[i].acct_desc);
1750 centry_put_uint32(centry, (*info)[i].rid);
1752 centry_end(centry, "GL/%s/local", domain->name);
1753 centry_free(centry);
1759 NTSTATUS wcache_name_to_sid(struct winbindd_domain *domain,
1760 const char *domain_name,
1762 struct dom_sid *sid,
1763 enum lsa_SidType *type)
1765 struct winbind_cache *cache = get_cache(domain);
1766 struct cache_entry *centry;
1770 if (cache->tdb == NULL) {
1771 return NT_STATUS_NOT_FOUND;
1774 uname = talloc_strdup_upper(talloc_tos(), name);
1775 if (uname == NULL) {
1776 return NT_STATUS_NO_MEMORY;
1779 centry = wcache_fetch(cache, domain, "NS/%s/%s", domain_name, uname);
1781 if (centry == NULL) {
1782 return NT_STATUS_NOT_FOUND;
1785 status = centry->status;
1786 if (NT_STATUS_IS_OK(status)) {
1787 *type = (enum lsa_SidType)centry_uint32(centry);
1788 centry_sid(centry, sid);
1791 DEBUG(10,("name_to_sid: [Cached] - cached name for domain %s status: "
1792 "%s\n", domain->name, nt_errstr(status) ));
1794 centry_free(centry);
1798 /* convert a single name to a sid in a domain */
1799 static NTSTATUS name_to_sid(struct winbindd_domain *domain,
1800 TALLOC_CTX *mem_ctx,
1801 const char *domain_name,
1804 struct dom_sid *sid,
1805 enum lsa_SidType *type)
1810 old_status = domain->online;
1812 status = wcache_name_to_sid(domain, domain_name, name, sid, type);
1813 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
1819 /* If the seq number check indicated that there is a problem
1820 * with this DC, then return that status... except for
1821 * access_denied. This is special because the dc may be in
1822 * "restrict anonymous = 1" mode, in which case it will deny
1823 * most unauthenticated operations, but *will* allow the LSA
1824 * name-to-sid that we try as a fallback. */
1826 if (!(NT_STATUS_IS_OK(domain->last_status)
1827 || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1828 return domain->last_status;
1830 DEBUG(10,("name_to_sid: [Cached] - doing backend query for name for domain %s\n",
1833 status = domain->backend->name_to_sid(domain, mem_ctx, domain_name,
1834 name, flags, sid, type);
1836 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1837 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1838 if (!domain->internal && old_status) {
1839 set_domain_offline(domain);
1841 if (!domain->internal &&
1844 NTSTATUS cache_status;
1845 cache_status = wcache_name_to_sid(domain, domain_name, name, sid, type);
1846 return cache_status;
1850 refresh_sequence_number(domain, false);
1852 if (domain->online &&
1853 (NT_STATUS_IS_OK(status) || NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED))) {
1854 wcache_save_name_to_sid(domain, status, domain_name, name, sid, *type);
1856 /* Only save the reverse mapping if this was not a UPN */
1857 if (!strchr(name, '@')) {
1858 strupper_m(CONST_DISCARD(char *,domain_name));
1859 strlower_m(CONST_DISCARD(char *,name));
1860 wcache_save_sid_to_name(domain, status, sid, domain_name, name, *type);
1867 NTSTATUS wcache_sid_to_name(struct winbindd_domain *domain,
1868 const struct dom_sid *sid,
1869 TALLOC_CTX *mem_ctx,
1872 enum lsa_SidType *type)
1874 struct winbind_cache *cache = get_cache(domain);
1875 struct cache_entry *centry;
1879 if (cache->tdb == NULL) {
1880 return NT_STATUS_NOT_FOUND;
1883 sid_string = sid_string_tos(sid);
1884 if (sid_string == NULL) {
1885 return NT_STATUS_NO_MEMORY;
1888 centry = wcache_fetch(cache, domain, "SN/%s", sid_string);
1889 TALLOC_FREE(sid_string);
1890 if (centry == NULL) {
1891 return NT_STATUS_NOT_FOUND;
1894 if (NT_STATUS_IS_OK(centry->status)) {
1895 *type = (enum lsa_SidType)centry_uint32(centry);
1896 *domain_name = centry_string(centry, mem_ctx);
1897 *name = centry_string(centry, mem_ctx);
1900 status = centry->status;
1901 centry_free(centry);
1903 DEBUG(10,("sid_to_name: [Cached] - cached name for domain %s status: "
1904 "%s\n", domain->name, nt_errstr(status) ));
1909 /* convert a sid to a user or group name. The sid is guaranteed to be in the domain
1911 static NTSTATUS sid_to_name(struct winbindd_domain *domain,
1912 TALLOC_CTX *mem_ctx,
1913 const struct dom_sid *sid,
1916 enum lsa_SidType *type)
1921 old_status = domain->online;
1922 status = wcache_sid_to_name(domain, sid, mem_ctx, domain_name, name,
1924 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
1929 *domain_name = NULL;
1931 /* If the seq number check indicated that there is a problem
1932 * with this DC, then return that status... except for
1933 * access_denied. This is special because the dc may be in
1934 * "restrict anonymous = 1" mode, in which case it will deny
1935 * most unauthenticated operations, but *will* allow the LSA
1936 * sid-to-name that we try as a fallback. */
1938 if (!(NT_STATUS_IS_OK(domain->last_status)
1939 || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1940 return domain->last_status;
1942 DEBUG(10,("sid_to_name: [Cached] - doing backend query for name for domain %s\n",
1945 status = domain->backend->sid_to_name(domain, mem_ctx, sid, domain_name, name, type);
1947 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1948 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1949 if (!domain->internal && old_status) {
1950 set_domain_offline(domain);
1952 if (!domain->internal &&
1955 NTSTATUS cache_status;
1956 cache_status = wcache_sid_to_name(domain, sid, mem_ctx,
1957 domain_name, name, type);
1958 return cache_status;
1962 refresh_sequence_number(domain, false);
1963 if (!NT_STATUS_IS_OK(status)) {
1966 wcache_save_sid_to_name(domain, status, sid, *domain_name, *name, *type);
1968 /* We can't save the name to sid mapping here, as with sid history a
1969 * later name2sid would give the wrong sid. */
1974 static NTSTATUS rids_to_names(struct winbindd_domain *domain,
1975 TALLOC_CTX *mem_ctx,
1976 const struct dom_sid *domain_sid,
1981 enum lsa_SidType **types)
1983 struct winbind_cache *cache = get_cache(domain);
1985 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
1990 old_status = domain->online;
1991 *domain_name = NULL;
1999 if (num_rids == 0) {
2000 return NT_STATUS_OK;
2003 *names = TALLOC_ARRAY(mem_ctx, char *, num_rids);
2004 *types = TALLOC_ARRAY(mem_ctx, enum lsa_SidType, num_rids);
2006 if ((*names == NULL) || (*types == NULL)) {
2007 result = NT_STATUS_NO_MEMORY;
2011 have_mapped = have_unmapped = false;
2013 for (i=0; i<num_rids; i++) {
2015 struct cache_entry *centry;
2018 if (!sid_compose(&sid, domain_sid, rids[i])) {
2019 result = NT_STATUS_INTERNAL_ERROR;
2023 centry = wcache_fetch(cache, domain, "SN/%s",
2024 sid_to_fstring(tmp, &sid));
2029 (*types)[i] = SID_NAME_UNKNOWN;
2030 (*names)[i] = talloc_strdup(*names, "");
2032 if (NT_STATUS_IS_OK(centry->status)) {
2035 (*types)[i] = (enum lsa_SidType)centry_uint32(centry);
2037 dom = centry_string(centry, mem_ctx);
2038 if (*domain_name == NULL) {
2044 (*names)[i] = centry_string(centry, *names);
2046 } else if (NT_STATUS_EQUAL(centry->status, NT_STATUS_NONE_MAPPED)
2047 || NT_STATUS_EQUAL(centry->status, STATUS_SOME_UNMAPPED)) {
2048 have_unmapped = true;
2051 /* something's definitely wrong */
2052 result = centry->status;
2056 centry_free(centry);
2060 return NT_STATUS_NONE_MAPPED;
2062 if (!have_unmapped) {
2063 return NT_STATUS_OK;
2065 return STATUS_SOME_UNMAPPED;
2069 TALLOC_FREE(*names);
2070 TALLOC_FREE(*types);
2072 result = domain->backend->rids_to_names(domain, mem_ctx, domain_sid,
2073 rids, num_rids, domain_name,
2076 if (NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
2077 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2078 if (!domain->internal && old_status) {
2079 set_domain_offline(domain);
2082 !domain->internal &&
2085 have_mapped = have_unmapped = false;
2087 for (i=0; i<num_rids; i++) {
2089 struct cache_entry *centry;
2092 if (!sid_compose(&sid, domain_sid, rids[i])) {
2093 result = NT_STATUS_INTERNAL_ERROR;
2097 centry = wcache_fetch(cache, domain, "SN/%s",
2098 sid_to_fstring(tmp, &sid));
2100 (*types)[i] = SID_NAME_UNKNOWN;
2101 (*names)[i] = talloc_strdup(*names, "");
2105 (*types)[i] = SID_NAME_UNKNOWN;
2106 (*names)[i] = talloc_strdup(*names, "");
2108 if (NT_STATUS_IS_OK(centry->status)) {
2111 (*types)[i] = (enum lsa_SidType)centry_uint32(centry);
2113 dom = centry_string(centry, mem_ctx);
2114 if (*domain_name == NULL) {
2120 (*names)[i] = centry_string(centry, *names);
2122 } else if (NT_STATUS_EQUAL(centry->status, NT_STATUS_NONE_MAPPED)) {
2123 have_unmapped = true;
2126 /* something's definitely wrong */
2127 result = centry->status;
2131 centry_free(centry);
2135 return NT_STATUS_NONE_MAPPED;
2137 if (!have_unmapped) {
2138 return NT_STATUS_OK;
2140 return STATUS_SOME_UNMAPPED;
2144 None of the queried rids has been found so save all negative entries
2146 if (NT_STATUS_EQUAL(result, NT_STATUS_NONE_MAPPED)) {
2147 for (i = 0; i < num_rids; i++) {
2149 const char *name = "";
2150 const enum lsa_SidType type = SID_NAME_UNKNOWN;
2151 NTSTATUS status = NT_STATUS_NONE_MAPPED;
2153 if (!sid_compose(&sid, domain_sid, rids[i])) {
2154 return NT_STATUS_INTERNAL_ERROR;
2157 wcache_save_sid_to_name(domain, status, &sid, *domain_name,
2165 Some or all of the queried rids have been found.
2167 if (!NT_STATUS_IS_OK(result) &&
2168 !NT_STATUS_EQUAL(result, STATUS_SOME_UNMAPPED)) {
2172 refresh_sequence_number(domain, false);
2174 for (i=0; i<num_rids; i++) {
2178 if (!sid_compose(&sid, domain_sid, rids[i])) {
2179 result = NT_STATUS_INTERNAL_ERROR;
2183 status = (*types)[i] == SID_NAME_UNKNOWN ?
2184 NT_STATUS_NONE_MAPPED : NT_STATUS_OK;
2186 wcache_save_sid_to_name(domain, status, &sid, *domain_name,
2187 (*names)[i], (*types)[i]);
2193 TALLOC_FREE(*names);
2194 TALLOC_FREE(*types);
2198 NTSTATUS wcache_query_user(struct winbindd_domain *domain,
2199 TALLOC_CTX *mem_ctx,
2200 const struct dom_sid *user_sid,
2201 struct wbint_userinfo *info)
2203 struct winbind_cache *cache = get_cache(domain);
2204 struct cache_entry *centry = NULL;
2208 if (cache->tdb == NULL) {
2209 return NT_STATUS_NOT_FOUND;
2212 sid_string = sid_string_tos(user_sid);
2213 if (sid_string == NULL) {
2214 return NT_STATUS_NO_MEMORY;
2217 centry = wcache_fetch(cache, domain, "U/%s", sid_string);
2218 TALLOC_FREE(sid_string);
2219 if (centry == NULL) {
2220 return NT_STATUS_NOT_FOUND;
2224 * If we have an access denied cache entry and a cached info3
2225 * in the samlogon cache then do a query. This will force the
2226 * rpc back end to return the info3 data.
2229 if (NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED) &&
2230 netsamlogon_cache_have(user_sid)) {
2231 DEBUG(10, ("query_user: cached access denied and have cached "
2233 domain->last_status = NT_STATUS_OK;
2234 centry_free(centry);
2235 return NT_STATUS_NOT_FOUND;
2238 /* if status is not ok then this is a negative hit
2239 and the rest of the data doesn't matter */
2240 status = centry->status;
2241 if (NT_STATUS_IS_OK(status)) {
2242 info->acct_name = centry_string(centry, mem_ctx);
2243 info->full_name = centry_string(centry, mem_ctx);
2244 info->homedir = centry_string(centry, mem_ctx);
2245 info->shell = centry_string(centry, mem_ctx);
2246 info->primary_gid = centry_uint32(centry);
2247 centry_sid(centry, &info->user_sid);
2248 centry_sid(centry, &info->group_sid);
2251 DEBUG(10,("query_user: [Cached] - cached info for domain %s status: "
2252 "%s\n", domain->name, nt_errstr(status) ));
2254 centry_free(centry);
2258 /* Lookup user information from a rid */
2259 static NTSTATUS query_user(struct winbindd_domain *domain,
2260 TALLOC_CTX *mem_ctx,
2261 const struct dom_sid *user_sid,
2262 struct wbint_userinfo *info)
2267 old_status = domain->online;
2268 status = wcache_query_user(domain, mem_ctx, user_sid, info);
2269 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2275 /* Return status value returned by seq number check */
2277 if (!NT_STATUS_IS_OK(domain->last_status))
2278 return domain->last_status;
2280 DEBUG(10,("query_user: [Cached] - doing backend query for info for domain %s\n",
2283 status = domain->backend->query_user(domain, mem_ctx, user_sid, info);
2285 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2286 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2287 if (!domain->internal && old_status) {
2288 set_domain_offline(domain);
2290 if (!domain->internal &&
2293 NTSTATUS cache_status;
2294 cache_status = wcache_query_user(domain, mem_ctx, user_sid, info);
2295 return cache_status;
2299 refresh_sequence_number(domain, false);
2300 if (!NT_STATUS_IS_OK(status)) {
2303 wcache_save_user(domain, status, info);
2308 NTSTATUS wcache_lookup_usergroups(struct winbindd_domain *domain,
2309 TALLOC_CTX *mem_ctx,
2310 const struct dom_sid *user_sid,
2311 uint32_t *pnum_sids,
2312 struct dom_sid **psids)
2314 struct winbind_cache *cache = get_cache(domain);
2315 struct cache_entry *centry = NULL;
2317 uint32_t i, num_sids;
2318 struct dom_sid *sids;
2321 if (cache->tdb == NULL) {
2322 return NT_STATUS_NOT_FOUND;
2325 centry = wcache_fetch(cache, domain, "UG/%s",
2326 sid_to_fstring(sid_string, user_sid));
2327 if (centry == NULL) {
2328 return NT_STATUS_NOT_FOUND;
2331 /* If we have an access denied cache entry and a cached info3 in the
2332 samlogon cache then do a query. This will force the rpc back end
2333 to return the info3 data. */
2335 if (NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)
2336 && netsamlogon_cache_have(user_sid)) {
2337 DEBUG(10, ("lookup_usergroups: cached access denied and have "
2339 domain->last_status = NT_STATUS_OK;
2340 centry_free(centry);
2341 return NT_STATUS_NOT_FOUND;
2344 num_sids = centry_uint32(centry);
2345 sids = talloc_array(mem_ctx, struct dom_sid, num_sids);
2347 centry_free(centry);
2348 return NT_STATUS_NO_MEMORY;
2351 for (i=0; i<num_sids; i++) {
2352 centry_sid(centry, &sids[i]);
2355 status = centry->status;
2357 DEBUG(10,("lookup_usergroups: [Cached] - cached info for domain %s "
2358 "status: %s\n", domain->name, nt_errstr(status)));
2360 centry_free(centry);
2362 *pnum_sids = num_sids;
2367 /* Lookup groups a user is a member of. */
2368 static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
2369 TALLOC_CTX *mem_ctx,
2370 const struct dom_sid *user_sid,
2371 uint32 *num_groups, struct dom_sid **user_gids)
2373 struct cache_entry *centry = NULL;
2379 old_status = domain->online;
2380 status = wcache_lookup_usergroups(domain, mem_ctx, user_sid,
2381 num_groups, user_gids);
2382 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2387 (*user_gids) = NULL;
2389 /* Return status value returned by seq number check */
2391 if (!NT_STATUS_IS_OK(domain->last_status))
2392 return domain->last_status;
2394 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info for domain %s\n",
2397 status = domain->backend->lookup_usergroups(domain, mem_ctx, user_sid, num_groups, user_gids);
2399 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2400 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2401 if (!domain->internal && old_status) {
2402 set_domain_offline(domain);
2404 if (!domain->internal &&
2407 NTSTATUS cache_status;
2408 cache_status = wcache_lookup_usergroups(domain, mem_ctx, user_sid,
2409 num_groups, user_gids);
2410 return cache_status;
2413 if ( NT_STATUS_EQUAL(status, NT_STATUS_SYNCHRONIZATION_REQUIRED) )
2417 refresh_sequence_number(domain, false);
2418 if (!NT_STATUS_IS_OK(status)) {
2421 centry = centry_start(domain, status);
2425 centry_put_uint32(centry, *num_groups);
2426 for (i=0; i<(*num_groups); i++) {
2427 centry_put_sid(centry, &(*user_gids)[i]);
2430 centry_end(centry, "UG/%s", sid_to_fstring(sid_string, user_sid));
2431 centry_free(centry);
2437 static char *wcache_make_sidlist(TALLOC_CTX *mem_ctx, uint32_t num_sids,
2438 const struct dom_sid *sids)
2443 sidlist = talloc_strdup(mem_ctx, "");
2444 if (sidlist == NULL) {
2447 for (i=0; i<num_sids; i++) {
2449 sidlist = talloc_asprintf_append_buffer(
2450 sidlist, "/%s", sid_to_fstring(tmp, &sids[i]));
2451 if (sidlist == NULL) {
2458 NTSTATUS wcache_lookup_useraliases(struct winbindd_domain *domain,
2459 TALLOC_CTX *mem_ctx, uint32_t num_sids,
2460 const struct dom_sid *sids,
2461 uint32_t *pnum_aliases, uint32_t **paliases)
2463 struct winbind_cache *cache = get_cache(domain);
2464 struct cache_entry *centry = NULL;
2465 uint32_t num_aliases;
2471 if (cache->tdb == NULL) {
2472 return NT_STATUS_NOT_FOUND;
2475 if (num_sids == 0) {
2478 return NT_STATUS_OK;
2481 /* We need to cache indexed by the whole list of SIDs, the aliases
2482 * resulting might come from any of the SIDs. */
2484 sidlist = wcache_make_sidlist(talloc_tos(), num_sids, sids);
2485 if (sidlist == NULL) {
2486 return NT_STATUS_NO_MEMORY;
2489 centry = wcache_fetch(cache, domain, "UA%s", sidlist);
2490 TALLOC_FREE(sidlist);
2491 if (centry == NULL) {
2492 return NT_STATUS_NOT_FOUND;
2495 num_aliases = centry_uint32(centry);
2496 aliases = talloc_array(mem_ctx, uint32_t, num_aliases);
2497 if (aliases == NULL) {
2498 centry_free(centry);
2499 return NT_STATUS_NO_MEMORY;
2502 for (i=0; i<num_aliases; i++) {
2503 aliases[i] = centry_uint32(centry);
2506 status = centry->status;
2508 DEBUG(10,("lookup_useraliases: [Cached] - cached info for domain: %s "
2509 "status %s\n", domain->name, nt_errstr(status)));
2511 centry_free(centry);
2513 *pnum_aliases = num_aliases;
2514 *paliases = aliases;
2519 static NTSTATUS lookup_useraliases(struct winbindd_domain *domain,
2520 TALLOC_CTX *mem_ctx,
2521 uint32 num_sids, const struct dom_sid *sids,
2522 uint32 *num_aliases, uint32 **alias_rids)
2524 struct cache_entry *centry = NULL;
2530 old_status = domain->online;
2531 status = wcache_lookup_useraliases(domain, mem_ctx, num_sids, sids,
2532 num_aliases, alias_rids);
2533 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2538 (*alias_rids) = NULL;
2540 if (!NT_STATUS_IS_OK(domain->last_status))
2541 return domain->last_status;
2543 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info "
2544 "for domain %s\n", domain->name ));
2546 sidlist = wcache_make_sidlist(talloc_tos(), num_sids, sids);
2547 if (sidlist == NULL) {
2548 return NT_STATUS_NO_MEMORY;
2551 status = domain->backend->lookup_useraliases(domain, mem_ctx,
2553 num_aliases, alias_rids);
2555 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2556 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2557 if (!domain->internal && old_status) {
2558 set_domain_offline(domain);
2560 if (!domain->internal &&
2563 NTSTATUS cache_status;
2564 cache_status = wcache_lookup_useraliases(domain, mem_ctx, num_sids,
2565 sids, num_aliases, alias_rids);
2566 return cache_status;
2570 refresh_sequence_number(domain, false);
2571 if (!NT_STATUS_IS_OK(status)) {
2574 centry = centry_start(domain, status);
2577 centry_put_uint32(centry, *num_aliases);
2578 for (i=0; i<(*num_aliases); i++)
2579 centry_put_uint32(centry, (*alias_rids)[i]);
2580 centry_end(centry, "UA%s", sidlist);
2581 centry_free(centry);
2587 NTSTATUS wcache_lookup_groupmem(struct winbindd_domain *domain,
2588 TALLOC_CTX *mem_ctx,
2589 const struct dom_sid *group_sid,
2590 uint32_t *num_names,
2591 struct dom_sid **sid_mem, char ***names,
2592 uint32_t **name_types)
2594 struct winbind_cache *cache = get_cache(domain);
2595 struct cache_entry *centry = NULL;
2600 if (cache->tdb == NULL) {
2601 return NT_STATUS_NOT_FOUND;
2604 sid_string = sid_string_tos(group_sid);
2605 if (sid_string == NULL) {
2606 return NT_STATUS_NO_MEMORY;
2609 centry = wcache_fetch(cache, domain, "GM/%s", sid_string);
2610 TALLOC_FREE(sid_string);
2611 if (centry == NULL) {
2612 return NT_STATUS_NOT_FOUND;
2619 *num_names = centry_uint32(centry);
2620 if (*num_names == 0) {
2621 centry_free(centry);
2622 return NT_STATUS_OK;
2625 *sid_mem = talloc_array(mem_ctx, struct dom_sid, *num_names);
2626 *names = talloc_array(mem_ctx, char *, *num_names);
2627 *name_types = talloc_array(mem_ctx, uint32, *num_names);
2629 if ((*sid_mem == NULL) || (*names == NULL) || (*name_types == NULL)) {
2630 TALLOC_FREE(*sid_mem);
2631 TALLOC_FREE(*names);
2632 TALLOC_FREE(*name_types);
2633 centry_free(centry);
2634 return NT_STATUS_NO_MEMORY;
2637 for (i=0; i<(*num_names); i++) {
2638 centry_sid(centry, &(*sid_mem)[i]);
2639 (*names)[i] = centry_string(centry, mem_ctx);
2640 (*name_types)[i] = centry_uint32(centry);
2643 status = centry->status;
2645 DEBUG(10,("lookup_groupmem: [Cached] - cached info for domain %s "
2646 "status: %s\n", domain->name, nt_errstr(status)));
2648 centry_free(centry);
2652 static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
2653 TALLOC_CTX *mem_ctx,
2654 const struct dom_sid *group_sid,
2655 enum lsa_SidType type,
2657 struct dom_sid **sid_mem, char ***names,
2658 uint32 **name_types)
2660 struct cache_entry *centry = NULL;
2666 old_status = domain->online;
2667 status = wcache_lookup_groupmem(domain, mem_ctx, group_sid, num_names,
2668 sid_mem, names, name_types);
2669 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2676 (*name_types) = NULL;
2678 /* Return status value returned by seq number check */
2680 if (!NT_STATUS_IS_OK(domain->last_status))
2681 return domain->last_status;
2683 DEBUG(10,("lookup_groupmem: [Cached] - doing backend query for info for domain %s\n",
2686 status = domain->backend->lookup_groupmem(domain, mem_ctx, group_sid,
2688 sid_mem, names, name_types);
2690 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2691 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2692 if (!domain->internal && old_status) {
2693 set_domain_offline(domain);
2695 if (!domain->internal &&
2698 NTSTATUS cache_status;
2699 cache_status = wcache_lookup_groupmem(domain, mem_ctx, group_sid,
2700 num_names, sid_mem, names,
2702 return cache_status;
2706 refresh_sequence_number(domain, false);
2707 if (!NT_STATUS_IS_OK(status)) {
2710 centry = centry_start(domain, status);
2713 centry_put_uint32(centry, *num_names);
2714 for (i=0; i<(*num_names); i++) {
2715 centry_put_sid(centry, &(*sid_mem)[i]);
2716 centry_put_string(centry, (*names)[i]);
2717 centry_put_uint32(centry, (*name_types)[i]);
2719 centry_end(centry, "GM/%s", sid_to_fstring(sid_string, group_sid));
2720 centry_free(centry);
2726 /* find the sequence number for a domain */
2727 static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
2729 refresh_sequence_number(domain, false);
2731 *seq = domain->sequence_number;
2733 return NT_STATUS_OK;
2736 /* enumerate trusted domains
2737 * (we need to have the list of trustdoms in the cache when we go offline) -
2739 static NTSTATUS trusted_domains(struct winbindd_domain *domain,
2740 TALLOC_CTX *mem_ctx,
2741 struct netr_DomainTrustList *trusts)
2744 struct winbind_cache *cache;
2745 struct winbindd_tdc_domain *dom_list = NULL;
2746 size_t num_domains = 0;
2747 bool retval = false;
2751 old_status = domain->online;
2753 trusts->array = NULL;
2755 cache = get_cache(domain);
2756 if (!cache || !cache->tdb) {
2760 if (domain->online) {
2764 retval = wcache_tdc_fetch_list(&dom_list, &num_domains);
2765 if (!retval || !num_domains || !dom_list) {
2766 TALLOC_FREE(dom_list);
2771 trusts->array = TALLOC_ZERO_ARRAY(mem_ctx, struct netr_DomainTrust, num_domains);
2772 if (!trusts->array) {
2773 TALLOC_FREE(dom_list);
2774 return NT_STATUS_NO_MEMORY;
2777 for (i = 0; i < num_domains; i++) {
2778 struct netr_DomainTrust *trust;
2779 struct dom_sid *sid;
2780 struct winbindd_domain *dom;
2782 dom = find_domain_from_name_noinit(dom_list[i].domain_name);
2783 if (dom && dom->internal) {
2787 trust = &trusts->array[trusts->count];
2788 trust->netbios_name = talloc_strdup(trusts->array, dom_list[i].domain_name);
2789 trust->dns_name = talloc_strdup(trusts->array, dom_list[i].dns_name);
2790 sid = talloc(trusts->array, struct dom_sid);
2791 if (!trust->netbios_name || !trust->dns_name ||
2793 TALLOC_FREE(dom_list);
2794 TALLOC_FREE(trusts->array);
2795 return NT_STATUS_NO_MEMORY;
2798 trust->trust_flags = dom_list[i].trust_flags;
2799 trust->trust_attributes = dom_list[i].trust_attribs;
2800 trust->trust_type = dom_list[i].trust_type;
2801 sid_copy(sid, &dom_list[i].sid);
2806 TALLOC_FREE(dom_list);
2807 return NT_STATUS_OK;
2810 /* Return status value returned by seq number check */
2812 if (!NT_STATUS_IS_OK(domain->last_status))
2813 return domain->last_status;
2815 DEBUG(10,("trusted_domains: [Cached] - doing backend query for info for domain %s\n",
2818 status = domain->backend->trusted_domains(domain, mem_ctx, trusts);
2820 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2821 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2822 if (!domain->internal && old_status) {
2823 set_domain_offline(domain);
2825 if (!domain->internal &&
2828 retval = wcache_tdc_fetch_list(&dom_list, &num_domains);
2829 if (retval && num_domains && dom_list) {
2830 TALLOC_FREE(trusts->array);
2832 goto do_fetch_cache;
2836 /* no trusts gives NT_STATUS_NO_MORE_ENTRIES resetting to NT_STATUS_OK
2837 * so that the generic centry handling still applies correctly -
2840 if (!NT_STATUS_IS_ERR(status)) {
2841 status = NT_STATUS_OK;
2846 /* get lockout policy */
2847 static NTSTATUS lockout_policy(struct winbindd_domain *domain,
2848 TALLOC_CTX *mem_ctx,
2849 struct samr_DomInfo12 *policy)
2851 struct winbind_cache *cache = get_cache(domain);
2852 struct cache_entry *centry = NULL;
2856 old_status = domain->online;
2860 centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
2866 policy->lockout_duration = centry_nttime(centry);
2867 policy->lockout_window = centry_nttime(centry);
2868 policy->lockout_threshold = centry_uint16(centry);
2870 status = centry->status;
2872 DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2873 domain->name, nt_errstr(status) ));
2875 centry_free(centry);
2879 ZERO_STRUCTP(policy);
2881 /* Return status value returned by seq number check */
2883 if (!NT_STATUS_IS_OK(domain->last_status))
2884 return domain->last_status;
2886 DEBUG(10,("lockout_policy: [Cached] - doing backend query for info for domain %s\n",
2889 status = domain->backend->lockout_policy(domain, mem_ctx, policy);
2891 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2892 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2893 if (!domain->internal && old_status) {
2894 set_domain_offline(domain);
2897 !domain->internal &&
2900 centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
2902 goto do_fetch_cache;
2907 refresh_sequence_number(domain, false);
2908 if (!NT_STATUS_IS_OK(status)) {
2911 wcache_save_lockout_policy(domain, status, policy);
2916 /* get password policy */
2917 static NTSTATUS password_policy(struct winbindd_domain *domain,
2918 TALLOC_CTX *mem_ctx,
2919 struct samr_DomInfo1 *policy)
2921 struct winbind_cache *cache = get_cache(domain);
2922 struct cache_entry *centry = NULL;
2926 old_status = domain->online;
2930 centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
2936 policy->min_password_length = centry_uint16(centry);
2937 policy->password_history_length = centry_uint16(centry);
2938 policy->password_properties = centry_uint32(centry);
2939 policy->max_password_age = centry_nttime(centry);
2940 policy->min_password_age = centry_nttime(centry);
2942 status = centry->status;
2944 DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2945 domain->name, nt_errstr(status) ));
2947 centry_free(centry);
2951 ZERO_STRUCTP(policy);
2953 /* Return status value returned by seq number check */
2955 if (!NT_STATUS_IS_OK(domain->last_status))
2956 return domain->last_status;
2958 DEBUG(10,("password_policy: [Cached] - doing backend query for info for domain %s\n",
2961 status = domain->backend->password_policy(domain, mem_ctx, policy);
2963 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2964 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2965 if (!domain->internal && old_status) {
2966 set_domain_offline(domain);
2969 !domain->internal &&
2972 centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
2974 goto do_fetch_cache;
2979 refresh_sequence_number(domain, false);
2980 if (!NT_STATUS_IS_OK(status)) {
2983 wcache_save_password_policy(domain, status, policy);
2989 /* Invalidate cached user and group lists coherently */
2991 static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
2994 if (strncmp((const char *)kbuf.dptr, "UL/", 3) == 0 ||
2995 strncmp((const char *)kbuf.dptr, "GL/", 3) == 0)
2996 tdb_delete(the_tdb, kbuf);
3001 /* Invalidate the getpwnam and getgroups entries for a winbindd domain */
3003 void wcache_invalidate_samlogon(struct winbindd_domain *domain,
3004 const struct dom_sid *sid)
3006 fstring key_str, sid_string;
3007 struct winbind_cache *cache;
3009 /* dont clear cached U/SID and UG/SID entries when we want to logon
3012 if (lp_winbind_offline_logon()) {
3019 cache = get_cache(domain);
3025 /* Clear U/SID cache entry */
3026 fstr_sprintf(key_str, "U/%s", sid_to_fstring(sid_string, sid));
3027 DEBUG(10, ("wcache_invalidate_samlogon: clearing %s\n", key_str));
3028 tdb_delete(cache->tdb, string_tdb_data(key_str));
3030 /* Clear UG/SID cache entry */
3031 fstr_sprintf(key_str, "UG/%s", sid_to_fstring(sid_string, sid));
3032 DEBUG(10, ("wcache_invalidate_samlogon: clearing %s\n", key_str));
3033 tdb_delete(cache->tdb, string_tdb_data(key_str));
3035 /* Samba/winbindd never needs this. */
3036 netsamlogon_clear_cached_user(sid);
3039 bool wcache_invalidate_cache(void)
3041 struct winbindd_domain *domain;
3043 for (domain = domain_list(); domain; domain = domain->next) {
3044 struct winbind_cache *cache = get_cache(domain);
3046 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
3047 "entries for %s\n", domain->name));
3050 tdb_traverse(cache->tdb, traverse_fn, NULL);
3059 bool wcache_invalidate_cache_noinit(void)
3061 struct winbindd_domain *domain;
3063 for (domain = domain_list(); domain; domain = domain->next) {
3064 struct winbind_cache *cache;
3066 /* Skip uninitialized domains. */
3067 if (!domain->initialized && !domain->internal) {
3071 cache = get_cache(domain);
3073 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
3074 "entries for %s\n", domain->name));
3077 tdb_traverse(cache->tdb, traverse_fn, NULL);
3079 * Flushing cache has nothing to with domains.
3080 * return here if we successfully flushed once.
3081 * To avoid unnecessary traversing the cache.
3092 bool init_wcache(void)
3094 if (wcache == NULL) {
3095 wcache = SMB_XMALLOC_P(struct winbind_cache);
3096 ZERO_STRUCTP(wcache);
3099 if (wcache->tdb != NULL)
3102 /* when working offline we must not clear the cache on restart */
3103 wcache->tdb = tdb_open_log(cache_path("winbindd_cache.tdb"),
3104 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
3105 TDB_INCOMPATIBLE_HASH |
3106 (lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST)),
3107 O_RDWR|O_CREAT, 0600);
3109 if (wcache->tdb == NULL) {
3110 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
3117 /************************************************************************
3118 This is called by the parent to initialize the cache file.
3119 We don't need sophisticated locking here as we know we're the
3121 ************************************************************************/
3123 bool initialize_winbindd_cache(void)
3125 bool cache_bad = true;
3128 if (!init_wcache()) {
3129 DEBUG(0,("initialize_winbindd_cache: init_wcache failed.\n"));
3133 /* Check version number. */
3134 if (tdb_fetch_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, &vers) &&
3135 vers == WINBINDD_CACHE_VERSION) {
3140 DEBUG(0,("initialize_winbindd_cache: clearing cache "
3141 "and re-creating with version number %d\n",
3142 WINBINDD_CACHE_VERSION ));
3144 tdb_close(wcache->tdb);
3147 if (unlink(cache_path("winbindd_cache.tdb")) == -1) {
3148 DEBUG(0,("initialize_winbindd_cache: unlink %s failed %s ",
3149 cache_path("winbindd_cache.tdb"),
3153 if (!init_wcache()) {
3154 DEBUG(0,("initialize_winbindd_cache: re-initialization "
3155 "init_wcache failed.\n"));
3159 /* Write the version. */
3160 if (!tdb_store_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, WINBINDD_CACHE_VERSION)) {
3161 DEBUG(0,("initialize_winbindd_cache: version number store failed %s\n",
3162 tdb_errorstr(wcache->tdb) ));
3167 tdb_close(wcache->tdb);
3172 void close_winbindd_cache(void)
3178 tdb_close(wcache->tdb);
3183 bool lookup_cached_sid(TALLOC_CTX *mem_ctx, const struct dom_sid *sid,
3184 char **domain_name, char **name,
3185 enum lsa_SidType *type)
3187 struct winbindd_domain *domain;
3190 domain = find_lookup_domain_from_sid(sid);
3191 if (domain == NULL) {
3194 status = wcache_sid_to_name(domain, sid, mem_ctx, domain_name, name,
3196 return NT_STATUS_IS_OK(status);
3199 bool lookup_cached_name(const char *domain_name,
3201 struct dom_sid *sid,
3202 enum lsa_SidType *type)
3204 struct winbindd_domain *domain;
3206 bool original_online_state;
3208 domain = find_lookup_domain_from_name(domain_name);
3209 if (domain == NULL) {
3213 /* If we are doing a cached logon, temporarily set the domain
3214 offline so the cache won't expire the entry */
3216 original_online_state = domain->online;
3217 domain->online = false;
3218 status = wcache_name_to_sid(domain, domain_name, name, sid, type);
3219 domain->online = original_online_state;
3221 return NT_STATUS_IS_OK(status);
3224 void cache_name2sid(struct winbindd_domain *domain,
3225 const char *domain_name, const char *name,
3226 enum lsa_SidType type, const struct dom_sid *sid)
3228 refresh_sequence_number(domain, false);
3229 wcache_save_name_to_sid(domain, NT_STATUS_OK, domain_name, name,
3234 * The original idea that this cache only contains centries has
3235 * been blurred - now other stuff gets put in here. Ensure we
3236 * ignore these things on cleanup.
3239 static int traverse_fn_cleanup(TDB_CONTEXT *the_tdb, TDB_DATA kbuf,
3240 TDB_DATA dbuf, void *state)
3242 struct cache_entry *centry;
3244 if (is_non_centry_key(kbuf)) {
3248 centry = wcache_fetch_raw((char *)kbuf.dptr);
3253 if (!NT_STATUS_IS_OK(centry->status)) {
3254 DEBUG(10,("deleting centry %s\n", (const char *)kbuf.dptr));
3255 tdb_delete(the_tdb, kbuf);
3258 centry_free(centry);
3262 /* flush the cache */
3263 void wcache_flush_cache(void)
3268 tdb_close(wcache->tdb);
3271 if (!winbindd_use_cache()) {
3275 /* when working offline we must not clear the cache on restart */
3276 wcache->tdb = tdb_open_log(cache_path("winbindd_cache.tdb"),
3277 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
3278 TDB_INCOMPATIBLE_HASH |
3279 (lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST)),
3280 O_RDWR|O_CREAT, 0600);
3283 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
3287 tdb_traverse(wcache->tdb, traverse_fn_cleanup, NULL);
3289 DEBUG(10,("wcache_flush_cache success\n"));
3292 /* Count cached creds */
3294 static int traverse_fn_cached_creds(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
3297 int *cred_count = (int*)state;
3299 if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
3305 NTSTATUS wcache_count_cached_creds(struct winbindd_domain *domain, int *count)
3307 struct winbind_cache *cache = get_cache(domain);
3312 return NT_STATUS_INTERNAL_DB_ERROR;
3315 tdb_traverse(cache->tdb, traverse_fn_cached_creds, (void *)count);
3317 return NT_STATUS_OK;
3321 struct cred_list *prev, *next;
3326 static struct cred_list *wcache_cred_list;
3328 static int traverse_fn_get_credlist(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
3331 struct cred_list *cred;
3333 if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
3335 cred = SMB_MALLOC_P(struct cred_list);
3337 DEBUG(0,("traverse_fn_remove_first_creds: failed to malloc new entry for list\n"));
3343 /* save a copy of the key */
3345 fstrcpy(cred->name, (const char *)kbuf.dptr);
3346 DLIST_ADD(wcache_cred_list, cred);
3352 NTSTATUS wcache_remove_oldest_cached_creds(struct winbindd_domain *domain, const struct dom_sid *sid)
3354 struct winbind_cache *cache = get_cache(domain);
3357 struct cred_list *cred, *oldest = NULL;
3360 return NT_STATUS_INTERNAL_DB_ERROR;
3363 /* we possibly already have an entry */
3364 if (sid && NT_STATUS_IS_OK(wcache_cached_creds_exist(domain, sid))) {
3366 fstring key_str, tmp;
3368 DEBUG(11,("we already have an entry, deleting that\n"));
3370 fstr_sprintf(key_str, "CRED/%s", sid_to_fstring(tmp, sid));
3372 tdb_delete(cache->tdb, string_tdb_data(key_str));
3374 return NT_STATUS_OK;
3377 ret = tdb_traverse(cache->tdb, traverse_fn_get_credlist, NULL);
3379 return NT_STATUS_OK;
3380 } else if ((ret == -1) || (wcache_cred_list == NULL)) {
3381 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
3384 ZERO_STRUCTP(oldest);
3386 for (cred = wcache_cred_list; cred; cred = cred->next) {
3391 data = tdb_fetch(cache->tdb, string_tdb_data(cred->name));
3393 DEBUG(10,("wcache_remove_oldest_cached_creds: entry for [%s] not found\n",
3395 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
3399 t = IVAL(data.dptr, 0);
3400 SAFE_FREE(data.dptr);
3403 oldest = SMB_MALLOC_P(struct cred_list);
3404 if (oldest == NULL) {
3405 status = NT_STATUS_NO_MEMORY;
3409 fstrcpy(oldest->name, cred->name);
3410 oldest->created = t;
3414 if (t < oldest->created) {
3415 fstrcpy(oldest->name, cred->name);
3416 oldest->created = t;
3420 if (tdb_delete(cache->tdb, string_tdb_data(oldest->name)) == 0) {
3421 status = NT_STATUS_OK;
3423 status = NT_STATUS_UNSUCCESSFUL;
3426 SAFE_FREE(wcache_cred_list);
3432 /* Change the global online/offline state. */
3433 bool set_global_winbindd_state_offline(void)
3437 DEBUG(10,("set_global_winbindd_state_offline: offline requested.\n"));
3439 /* Only go offline if someone has created
3440 the key "WINBINDD_OFFLINE" in the cache tdb. */
3442 if (wcache == NULL || wcache->tdb == NULL) {
3443 DEBUG(10,("set_global_winbindd_state_offline: wcache not open yet.\n"));
3447 if (!lp_winbind_offline_logon()) {
3448 DEBUG(10,("set_global_winbindd_state_offline: rejecting.\n"));
3452 if (global_winbindd_offline_state) {
3453 /* Already offline. */
3457 data = tdb_fetch_bystring( wcache->tdb, "WINBINDD_OFFLINE" );
3459 if (!data.dptr || data.dsize != 4) {
3460 DEBUG(10,("set_global_winbindd_state_offline: offline state not set.\n"));
3461 SAFE_FREE(data.dptr);
3464 DEBUG(10,("set_global_winbindd_state_offline: offline state set.\n"));
3465 global_winbindd_offline_state = true;
3466 SAFE_FREE(data.dptr);
3471 void set_global_winbindd_state_online(void)
3473 DEBUG(10,("set_global_winbindd_state_online: online requested.\n"));
3475 if (!lp_winbind_offline_logon()) {
3476 DEBUG(10,("set_global_winbindd_state_online: rejecting.\n"));
3480 if (!global_winbindd_offline_state) {
3481 /* Already online. */
3484 global_winbindd_offline_state = false;
3490 /* Ensure there is no key "WINBINDD_OFFLINE" in the cache tdb. */
3491 tdb_delete_bystring(wcache->tdb, "WINBINDD_OFFLINE");
3494 bool get_global_winbindd_state_offline(void)
3496 return global_winbindd_offline_state;
3499 /***********************************************************************
3500 Validate functions for all possible cache tdb keys.
3501 ***********************************************************************/
3503 static struct cache_entry *create_centry_validate(const char *kstr, TDB_DATA data,
3504 struct tdb_validation_status *state)
3506 struct cache_entry *centry;
3508 centry = SMB_XMALLOC_P(struct cache_entry);
3509 centry->data = (unsigned char *)memdup(data.dptr, data.dsize);
3510 if (!centry->data) {
3514 centry->len = data.dsize;
3517 if (centry->len < 16) {
3518 /* huh? corrupt cache? */
3519 DEBUG(0,("create_centry_validate: Corrupt cache for key %s "
3520 "(len < 16) ?\n", kstr));
3521 centry_free(centry);
3522 state->bad_entry = true;
3523 state->success = false;
3527 centry->status = NT_STATUS(centry_uint32(centry));
3528 centry->sequence_number = centry_uint32(centry);
3529 centry->timeout = centry_uint64_t(centry);
3533 static int validate_seqnum(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3534 struct tdb_validation_status *state)
3536 if (dbuf.dsize != 8) {
3537 DEBUG(0,("validate_seqnum: Corrupt cache for key %s (len %u != 8) ?\n",
3538 keystr, (unsigned int)dbuf.dsize ));
3539 state->bad_entry = true;
3545 static int validate_ns(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3546 struct tdb_validation_status *state)
3548 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3553 (void)centry_uint32(centry);
3554 if (NT_STATUS_IS_OK(centry->status)) {
3556 (void)centry_sid(centry, &sid);
3559 centry_free(centry);
3561 if (!(state->success)) {
3564 DEBUG(10,("validate_ns: %s ok\n", keystr));
3568 static int validate_sn(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3569 struct tdb_validation_status *state)
3571 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3576 if (NT_STATUS_IS_OK(centry->status)) {
3577 (void)centry_uint32(centry);
3578 (void)centry_string(centry, mem_ctx);
3579 (void)centry_string(centry, mem_ctx);
3582 centry_free(centry);
3584 if (!(state->success)) {
3587 DEBUG(10,("validate_sn: %s ok\n", keystr));
3591 static int validate_u(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3592 struct tdb_validation_status *state)
3594 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3601 (void)centry_string(centry, mem_ctx);
3602 (void)centry_string(centry, mem_ctx);
3603 (void)centry_string(centry, mem_ctx);
3604 (void)centry_string(centry, mem_ctx);
3605 (void)centry_uint32(centry);
3606 (void)centry_sid(centry, &sid);
3607 (void)centry_sid(centry, &sid);
3609 centry_free(centry);
3611 if (!(state->success)) {
3614 DEBUG(10,("validate_u: %s ok\n", keystr));
3618 static int validate_loc_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3619 struct tdb_validation_status *state)
3621 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3627 (void)centry_nttime(centry);
3628 (void)centry_nttime(centry);
3629 (void)centry_uint16(centry);
3631 centry_free(centry);
3633 if (!(state->success)) {
3636 DEBUG(10,("validate_loc_pol: %s ok\n", keystr));
3640 static int validate_pwd_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3641 struct tdb_validation_status *state)
3643 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3649 (void)centry_uint16(centry);
3650 (void)centry_uint16(centry);
3651 (void)centry_uint32(centry);
3652 (void)centry_nttime(centry);
3653 (void)centry_nttime(centry);
3655 centry_free(centry);
3657 if (!(state->success)) {
3660 DEBUG(10,("validate_pwd_pol: %s ok\n", keystr));
3664 static int validate_cred(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3665 struct tdb_validation_status *state)
3667 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3673 (void)centry_time(centry);
3674 (void)centry_hash16(centry, mem_ctx);
3676 /* We only have 17 bytes more data in the salted cred case. */
3677 if (centry->len - centry->ofs == 17) {
3678 (void)centry_hash16(centry, mem_ctx);
3681 centry_free(centry);
3683 if (!(state->success)) {
3686 DEBUG(10,("validate_cred: %s ok\n", keystr));
3690 static int validate_ul(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3691 struct tdb_validation_status *state)
3693 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3694 int32 num_entries, i;
3700 num_entries = (int32)centry_uint32(centry);
3702 for (i=0; i< num_entries; i++) {
3704 (void)centry_string(centry, mem_ctx);
3705 (void)centry_string(centry, mem_ctx);
3706 (void)centry_string(centry, mem_ctx);
3707 (void)centry_string(centry, mem_ctx);
3708 (void)centry_sid(centry, &sid);
3709 (void)centry_sid(centry, &sid);
3712 centry_free(centry);
3714 if (!(state->success)) {
3717 DEBUG(10,("validate_ul: %s ok\n", keystr));
3721 static int validate_gl(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3722 struct tdb_validation_status *state)
3724 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3725 int32 num_entries, i;
3731 num_entries = centry_uint32(centry);
3733 for (i=0; i< num_entries; i++) {
3734 (void)centry_string(centry, mem_ctx);
3735 (void)centry_string(centry, mem_ctx);
3736 (void)centry_uint32(centry);
3739 centry_free(centry);
3741 if (!(state->success)) {
3744 DEBUG(10,("validate_gl: %s ok\n", keystr));
3748 static int validate_ug(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3749 struct tdb_validation_status *state)
3751 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3752 int32 num_groups, i;
3758 num_groups = centry_uint32(centry);
3760 for (i=0; i< num_groups; i++) {
3762 centry_sid(centry, &sid);
3765 centry_free(centry);
3767 if (!(state->success)) {
3770 DEBUG(10,("validate_ug: %s ok\n", keystr));
3774 static int validate_ua(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3775 struct tdb_validation_status *state)
3777 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3778 int32 num_aliases, i;
3784 num_aliases = centry_uint32(centry);
3786 for (i=0; i < num_aliases; i++) {
3787 (void)centry_uint32(centry);
3790 centry_free(centry);
3792 if (!(state->success)) {
3795 DEBUG(10,("validate_ua: %s ok\n", keystr));
3799 static int validate_gm(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3800 struct tdb_validation_status *state)
3802 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3809 num_names = centry_uint32(centry);
3811 for (i=0; i< num_names; i++) {
3813 centry_sid(centry, &sid);
3814 (void)centry_string(centry, mem_ctx);
3815 (void)centry_uint32(centry);
3818 centry_free(centry);
3820 if (!(state->success)) {
3823 DEBUG(10,("validate_gm: %s ok\n", keystr));
3827 static int validate_dr(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3828 struct tdb_validation_status *state)
3830 /* Can't say anything about this other than must be nonzero. */
3831 if (dbuf.dsize == 0) {
3832 DEBUG(0,("validate_dr: Corrupt cache for key %s (len == 0) ?\n",
3834 state->bad_entry = true;
3835 state->success = false;
3839 DEBUG(10,("validate_dr: %s ok\n", keystr));
3843 static int validate_de(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3844 struct tdb_validation_status *state)
3846 /* Can't say anything about this other than must be nonzero. */
3847 if (dbuf.dsize == 0) {
3848 DEBUG(0,("validate_de: Corrupt cache for key %s (len == 0) ?\n",
3850 state->bad_entry = true;
3851 state->success = false;
3855 DEBUG(10,("validate_de: %s ok\n", keystr));
3859 static int validate_pwinfo(TALLOC_CTX *mem_ctx, const char *keystr,
3860 TDB_DATA dbuf, struct tdb_validation_status *state)
3862 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3868 (void)centry_string(centry, mem_ctx);
3869 (void)centry_string(centry, mem_ctx);
3870 (void)centry_string(centry, mem_ctx);
3871 (void)centry_uint32(centry);
3873 centry_free(centry);
3875 if (!(state->success)) {
3878 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3882 static int validate_nss_an(TALLOC_CTX *mem_ctx, const char *keystr,
3884 struct tdb_validation_status *state)
3886 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3892 (void)centry_string( centry, mem_ctx );
3894 centry_free(centry);
3896 if (!(state->success)) {
3899 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3903 static int validate_nss_na(TALLOC_CTX *mem_ctx, const char *keystr,
3905 struct tdb_validation_status *state)
3907 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3913 (void)centry_string( centry, mem_ctx );
3915 centry_free(centry);
3917 if (!(state->success)) {
3920 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3924 static int validate_trustdomcache(TALLOC_CTX *mem_ctx, const char *keystr,
3926 struct tdb_validation_status *state)
3928 if (dbuf.dsize == 0) {
3929 DEBUG(0, ("validate_trustdomcache: Corrupt cache for "
3930 "key %s (len ==0) ?\n", keystr));
3931 state->bad_entry = true;
3932 state->success = false;
3936 DEBUG(10, ("validate_trustdomcache: %s ok\n", keystr));
3937 DEBUGADD(10, (" Don't trust me, I am a DUMMY!\n"));
3941 static int validate_offline(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3942 struct tdb_validation_status *state)
3944 if (dbuf.dsize != 4) {
3945 DEBUG(0,("validate_offline: Corrupt cache for key %s (len %u != 4) ?\n",
3946 keystr, (unsigned int)dbuf.dsize ));
3947 state->bad_entry = true;
3948 state->success = false;
3951 DEBUG(10,("validate_offline: %s ok\n", keystr));
3955 static int validate_ndr(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3956 struct tdb_validation_status *state)
3959 * Ignore validation for now. The proper way to do this is with a
3960 * checksum. Just pure parsing does not really catch much.
3965 static int validate_cache_version(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3966 struct tdb_validation_status *state)
3968 if (dbuf.dsize != 4) {
3969 DEBUG(0, ("validate_cache_version: Corrupt cache for "
3970 "key %s (len %u != 4) ?\n",
3971 keystr, (unsigned int)dbuf.dsize));
3972 state->bad_entry = true;
3973 state->success = false;
3977 DEBUG(10, ("validate_cache_version: %s ok\n", keystr));
3981 /***********************************************************************
3982 A list of all possible cache tdb keys with associated validation
3984 ***********************************************************************/
3986 struct key_val_struct {
3987 const char *keyname;
3988 int (*validate_data_fn)(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, struct tdb_validation_status* state);
3990 {"SEQNUM/", validate_seqnum},
3991 {"NS/", validate_ns},
3992 {"SN/", validate_sn},
3994 {"LOC_POL/", validate_loc_pol},
3995 {"PWD_POL/", validate_pwd_pol},
3996 {"CRED/", validate_cred},
3997 {"UL/", validate_ul},
3998 {"GL/", validate_gl},
3999 {"UG/", validate_ug},
4000 {"UA", validate_ua},
4001 {"GM/", validate_gm},
4002 {"DR/", validate_dr},
4003 {"DE/", validate_de},
4004 {"NSS/PWINFO/", validate_pwinfo},
4005 {"TRUSTDOMCACHE/", validate_trustdomcache},
4006 {"NSS/NA/", validate_nss_na},
4007 {"NSS/AN/", validate_nss_an},
4008 {"WINBINDD_OFFLINE", validate_offline},
4009 {"NDR/", validate_ndr},
4010 {WINBINDD_CACHE_VERSION_KEYSTR, validate_cache_version},
4014 /***********************************************************************
4015 Function to look at every entry in the tdb and validate it as far as
4017 ***********************************************************************/
4019 static int cache_traverse_validate_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, void *state)
4022 unsigned int max_key_len = 1024;
4023 struct tdb_validation_status *v_state = (struct tdb_validation_status *)state;
4025 /* Paranoia check. */
4026 if (strncmp("UA/", (const char *)kbuf.dptr, 3) == 0) {
4027 max_key_len = 1024 * 1024;
4029 if (kbuf.dsize > max_key_len) {
4030 DEBUG(0, ("cache_traverse_validate_fn: key length too large: "
4032 (unsigned int)kbuf.dsize, (unsigned int)max_key_len));
4036 for (i = 0; key_val[i].keyname; i++) {
4037 size_t namelen = strlen(key_val[i].keyname);
4038 if (kbuf.dsize >= namelen && (
4039 strncmp(key_val[i].keyname, (const char *)kbuf.dptr, namelen)) == 0) {
4040 TALLOC_CTX *mem_ctx;
4044 keystr = SMB_MALLOC_ARRAY(char, kbuf.dsize+1);
4048 memcpy(keystr, kbuf.dptr, kbuf.dsize);
4049 keystr[kbuf.dsize] = '\0';
4051 mem_ctx = talloc_init("validate_ctx");
4057 ret = key_val[i].validate_data_fn(mem_ctx, keystr, dbuf,
4061 talloc_destroy(mem_ctx);
4066 DEBUG(0,("cache_traverse_validate_fn: unknown cache entry\nkey :\n"));
4067 dump_data(0, (uint8 *)kbuf.dptr, kbuf.dsize);
4068 DEBUG(0,("data :\n"));
4069 dump_data(0, (uint8 *)dbuf.dptr, dbuf.dsize);
4070 v_state->unknown_key = true;
4071 v_state->success = false;
4072 return 1; /* terminate. */
4075 static void validate_panic(const char *const why)
4077 DEBUG(0,("validating cache: would panic %s\n", why ));
4078 DEBUGADD(0, ("exiting instead (cache validation mode)\n"));
4082 /***********************************************************************
4083 Try and validate every entry in the winbindd cache. If we fail here,
4084 delete the cache tdb and return non-zero.
4085 ***********************************************************************/
4087 int winbindd_validate_cache(void)
4090 const char *tdb_path = cache_path("winbindd_cache.tdb");
4091 TDB_CONTEXT *tdb = NULL;
4093 DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
4094 smb_panic_fn = validate_panic;
4097 tdb = tdb_open_log(tdb_path,
4098 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
4099 TDB_INCOMPATIBLE_HASH |
4100 ( lp_winbind_offline_logon()
4102 : TDB_DEFAULT | TDB_CLEAR_IF_FIRST ),
4106 DEBUG(0, ("winbindd_validate_cache: "
4107 "error opening/initializing tdb\n"));
4112 ret = tdb_validate_and_backup(tdb_path, cache_traverse_validate_fn);
4115 DEBUG(10, ("winbindd_validate_cache: validation not successful.\n"));
4116 DEBUGADD(10, ("removing tdb %s.\n", tdb_path));
4121 DEBUG(10, ("winbindd_validate_cache: restoring panic function\n"));
4122 smb_panic_fn = smb_panic;
4126 /***********************************************************************
4127 Try and validate every entry in the winbindd cache.
4128 ***********************************************************************/
4130 int winbindd_validate_cache_nobackup(void)
4133 const char *tdb_path = cache_path("winbindd_cache.tdb");
4135 DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
4136 smb_panic_fn = validate_panic;
4139 if (wcache == NULL || wcache->tdb == NULL) {
4140 ret = tdb_validate_open(tdb_path, cache_traverse_validate_fn);
4142 ret = tdb_validate(wcache->tdb, cache_traverse_validate_fn);
4146 DEBUG(10, ("winbindd_validate_cache_nobackup: validation not "
4150 DEBUG(10, ("winbindd_validate_cache_nobackup: restoring panic "
4152 smb_panic_fn = smb_panic;
4156 bool winbindd_cache_validate_and_initialize(void)
4158 close_winbindd_cache();
4160 if (lp_winbind_offline_logon()) {
4161 if (winbindd_validate_cache() < 0) {
4162 DEBUG(0, ("winbindd cache tdb corrupt and no backup "
4163 "could be restored.\n"));
4167 return initialize_winbindd_cache();
4170 /*********************************************************************
4171 ********************************************************************/
4173 static bool add_wbdomain_to_tdc_array( struct winbindd_domain *new_dom,
4174 struct winbindd_tdc_domain **domains,
4175 size_t *num_domains )
4177 struct winbindd_tdc_domain *list = NULL;
4180 bool set_only = false;
4182 /* don't allow duplicates */
4187 for ( i=0; i< (*num_domains); i++ ) {
4188 if ( strequal( new_dom->name, list[i].domain_name ) ) {
4189 DEBUG(10,("add_wbdomain_to_tdc_array: Found existing record for %s\n",
4200 list = TALLOC_ARRAY( NULL, struct winbindd_tdc_domain, 1 );
4203 list = TALLOC_REALLOC_ARRAY( *domains, *domains,
4204 struct winbindd_tdc_domain,
4209 ZERO_STRUCT( list[idx] );
4215 list[idx].domain_name = talloc_strdup( list, new_dom->name );
4216 list[idx].dns_name = talloc_strdup( list, new_dom->alt_name );
4218 if ( !is_null_sid( &new_dom->sid ) ) {
4219 sid_copy( &list[idx].sid, &new_dom->sid );
4221 sid_copy(&list[idx].sid, &global_sid_NULL);
4224 if ( new_dom->domain_flags != 0x0 )
4225 list[idx].trust_flags = new_dom->domain_flags;
4227 if ( new_dom->domain_type != 0x0 )
4228 list[idx].trust_type = new_dom->domain_type;
4230 if ( new_dom->domain_trust_attribs != 0x0 )
4231 list[idx].trust_attribs = new_dom->domain_trust_attribs;
4235 *num_domains = idx + 1;
4241 /*********************************************************************
4242 ********************************************************************/
4244 static TDB_DATA make_tdc_key( const char *domain_name )
4246 char *keystr = NULL;
4247 TDB_DATA key = { NULL, 0 };
4249 if ( !domain_name ) {
4250 DEBUG(5,("make_tdc_key: Keyname workgroup is NULL!\n"));
4254 if (asprintf( &keystr, "TRUSTDOMCACHE/%s", domain_name ) == -1) {
4257 key = string_term_tdb_data(keystr);
4262 /*********************************************************************
4263 ********************************************************************/
4265 static int pack_tdc_domains( struct winbindd_tdc_domain *domains,
4267 unsigned char **buf )
4269 unsigned char *buffer = NULL;
4274 DEBUG(10,("pack_tdc_domains: Packing %d trusted domains\n",
4282 /* Store the number of array items first */
4283 len += tdb_pack( buffer+len, buflen-len, "d",
4286 /* now pack each domain trust record */
4287 for ( i=0; i<num_domains; i++ ) {
4292 DEBUG(10,("pack_tdc_domains: Packing domain %s (%s)\n",
4293 domains[i].domain_name,
4294 domains[i].dns_name ? domains[i].dns_name : "UNKNOWN" ));
4297 len += tdb_pack( buffer+len, buflen-len, "fffddd",
4298 domains[i].domain_name,
4299 domains[i].dns_name,
4300 sid_to_fstring(tmp, &domains[i].sid),
4301 domains[i].trust_flags,
4302 domains[i].trust_attribs,
4303 domains[i].trust_type );
4306 if ( buflen < len ) {
4308 if ( (buffer = SMB_MALLOC_ARRAY(unsigned char, len)) == NULL ) {
4309 DEBUG(0,("pack_tdc_domains: failed to alloc buffer!\n"));
4323 /*********************************************************************
4324 ********************************************************************/
4326 static size_t unpack_tdc_domains( unsigned char *buf, int buflen,
4327 struct winbindd_tdc_domain **domains )
4329 fstring domain_name, dns_name, sid_string;
4330 uint32 type, attribs, flags;
4334 struct winbindd_tdc_domain *list = NULL;
4336 /* get the number of domains */
4337 len += tdb_unpack( buf+len, buflen-len, "d", &num_domains);
4339 DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
4343 list = TALLOC_ARRAY( NULL, struct winbindd_tdc_domain, num_domains );
4345 DEBUG(0,("unpack_tdc_domains: Failed to talloc() domain list!\n"));
4349 for ( i=0; i<num_domains; i++ ) {
4350 len += tdb_unpack( buf+len, buflen-len, "fffddd",
4359 DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
4360 TALLOC_FREE( list );
4364 DEBUG(11,("unpack_tdc_domains: Unpacking domain %s (%s) "
4365 "SID %s, flags = 0x%x, attribs = 0x%x, type = 0x%x\n",
4366 domain_name, dns_name, sid_string,
4367 flags, attribs, type));
4369 list[i].domain_name = talloc_strdup( list, domain_name );
4370 list[i].dns_name = talloc_strdup( list, dns_name );
4371 if ( !string_to_sid( &(list[i].sid), sid_string ) ) {
4372 DEBUG(10,("unpack_tdc_domains: no SID for domain %s\n",
4375 list[i].trust_flags = flags;
4376 list[i].trust_attribs = attribs;
4377 list[i].trust_type = type;
4385 /*********************************************************************
4386 ********************************************************************/
4388 static bool wcache_tdc_store_list( struct winbindd_tdc_domain *domains, size_t num_domains )
4390 TDB_DATA key = make_tdc_key( lp_workgroup() );
4391 TDB_DATA data = { NULL, 0 };
4397 /* See if we were asked to delete the cache entry */
4400 ret = tdb_delete( wcache->tdb, key );
4404 data.dsize = pack_tdc_domains( domains, num_domains, &data.dptr );
4411 ret = tdb_store( wcache->tdb, key, data, 0 );
4414 SAFE_FREE( data.dptr );
4415 SAFE_FREE( key.dptr );
4417 return ( ret != -1 );
4420 /*********************************************************************
4421 ********************************************************************/
4423 bool wcache_tdc_fetch_list( struct winbindd_tdc_domain **domains, size_t *num_domains )
4425 TDB_DATA key = make_tdc_key( lp_workgroup() );
4426 TDB_DATA data = { NULL, 0 };
4434 data = tdb_fetch( wcache->tdb, key );
4436 SAFE_FREE( key.dptr );
4441 *num_domains = unpack_tdc_domains( data.dptr, data.dsize, domains );
4443 SAFE_FREE( data.dptr );
4451 /*********************************************************************
4452 ********************************************************************/
4454 bool wcache_tdc_add_domain( struct winbindd_domain *domain )
4456 struct winbindd_tdc_domain *dom_list = NULL;
4457 size_t num_domains = 0;
4460 DEBUG(10,("wcache_tdc_add_domain: Adding domain %s (%s), SID %s, "
4461 "flags = 0x%x, attributes = 0x%x, type = 0x%x\n",
4462 domain->name, domain->alt_name,
4463 sid_string_dbg(&domain->sid),
4464 domain->domain_flags,
4465 domain->domain_trust_attribs,
4466 domain->domain_type));
4468 if ( !init_wcache() ) {
4472 /* fetch the list */
4474 wcache_tdc_fetch_list( &dom_list, &num_domains );
4476 /* add the new domain */
4478 if ( !add_wbdomain_to_tdc_array( domain, &dom_list, &num_domains ) ) {
4482 /* pack the domain */
4484 if ( !wcache_tdc_store_list( dom_list, num_domains ) ) {
4492 TALLOC_FREE( dom_list );
4497 /*********************************************************************
4498 ********************************************************************/
4500 struct winbindd_tdc_domain * wcache_tdc_fetch_domain( TALLOC_CTX *ctx, const char *name )
4502 struct winbindd_tdc_domain *dom_list = NULL;
4503 size_t num_domains = 0;
4505 struct winbindd_tdc_domain *d = NULL;
4507 DEBUG(10,("wcache_tdc_fetch_domain: Searching for domain %s\n", name));
4509 if ( !init_wcache() ) {
4513 /* fetch the list */
4515 wcache_tdc_fetch_list( &dom_list, &num_domains );
4517 for ( i=0; i<num_domains; i++ ) {
4518 if ( strequal(name, dom_list[i].domain_name) ||
4519 strequal(name, dom_list[i].dns_name) )
4521 DEBUG(10,("wcache_tdc_fetch_domain: Found domain %s\n",
4524 d = TALLOC_P( ctx, struct winbindd_tdc_domain );
4528 d->domain_name = talloc_strdup( d, dom_list[i].domain_name );
4529 d->dns_name = talloc_strdup( d, dom_list[i].dns_name );
4530 sid_copy( &d->sid, &dom_list[i].sid );
4531 d->trust_flags = dom_list[i].trust_flags;
4532 d->trust_type = dom_list[i].trust_type;
4533 d->trust_attribs = dom_list[i].trust_attribs;
4539 TALLOC_FREE( dom_list );
4544 /*********************************************************************
4545 ********************************************************************/
4547 struct winbindd_tdc_domain*
4548 wcache_tdc_fetch_domainbysid(TALLOC_CTX *ctx,
4549 const struct dom_sid *sid)
4551 struct winbindd_tdc_domain *dom_list = NULL;
4552 size_t num_domains = 0;
4554 struct winbindd_tdc_domain *d = NULL;
4556 DEBUG(10,("wcache_tdc_fetch_domainbysid: Searching for domain %s\n",
4557 sid_string_dbg(sid)));
4559 if (!init_wcache()) {
4563 /* fetch the list */
4565 wcache_tdc_fetch_list(&dom_list, &num_domains);
4567 for (i = 0; i<num_domains; i++) {
4568 if (sid_equal(sid, &(dom_list[i].sid))) {
4569 DEBUG(10, ("wcache_tdc_fetch_domainbysid: "
4570 "Found domain %s for SID %s\n",
4571 dom_list[i].domain_name,
4572 sid_string_dbg(sid)));
4574 d = TALLOC_P(ctx, struct winbindd_tdc_domain);
4578 d->domain_name = talloc_strdup(d,
4579 dom_list[i].domain_name);
4581 d->dns_name = talloc_strdup(d, dom_list[i].dns_name);
4582 sid_copy(&d->sid, &dom_list[i].sid);
4583 d->trust_flags = dom_list[i].trust_flags;
4584 d->trust_type = dom_list[i].trust_type;
4585 d->trust_attribs = dom_list[i].trust_attribs;
4591 TALLOC_FREE(dom_list);
4597 /*********************************************************************
4598 ********************************************************************/
4600 void wcache_tdc_clear( void )
4602 if ( !init_wcache() )
4605 wcache_tdc_store_list( NULL, 0 );
4611 /*********************************************************************
4612 ********************************************************************/
4614 static void wcache_save_user_pwinfo(struct winbindd_domain *domain,
4616 const struct dom_sid *user_sid,
4617 const char *homedir,
4622 struct cache_entry *centry;
4625 if ( (centry = centry_start(domain, status)) == NULL )
4628 centry_put_string( centry, homedir );
4629 centry_put_string( centry, shell );
4630 centry_put_string( centry, gecos );
4631 centry_put_uint32( centry, gid );
4633 centry_end(centry, "NSS/PWINFO/%s", sid_to_fstring(tmp, user_sid) );
4635 DEBUG(10,("wcache_save_user_pwinfo: %s\n", sid_string_dbg(user_sid) ));
4637 centry_free(centry);
4642 NTSTATUS nss_get_info_cached( struct winbindd_domain *domain,
4643 const struct dom_sid *user_sid,
4645 const char **homedir, const char **shell,
4646 const char **gecos, gid_t *p_gid)
4648 struct winbind_cache *cache = get_cache(domain);
4649 struct cache_entry *centry = NULL;
4656 centry = wcache_fetch(cache, domain, "NSS/PWINFO/%s",
4657 sid_to_fstring(tmp, user_sid));
4662 *homedir = centry_string( centry, ctx );
4663 *shell = centry_string( centry, ctx );
4664 *gecos = centry_string( centry, ctx );
4665 *p_gid = centry_uint32( centry );
4667 centry_free(centry);
4669 DEBUG(10,("nss_get_info_cached: [Cached] - user_sid %s\n",
4670 sid_string_dbg(user_sid)));
4672 return NT_STATUS_OK;
4676 nt_status = nss_get_info( domain->name, user_sid, ctx,
4677 homedir, shell, gecos, p_gid );
4679 DEBUG(10, ("nss_get_info returned %s\n", nt_errstr(nt_status)));
4681 if ( NT_STATUS_IS_OK(nt_status) ) {
4682 DEBUG(10, ("result:\n\thomedir = '%s'\n", *homedir));
4683 DEBUGADD(10, ("\tshell = '%s'\n", *shell));
4684 DEBUGADD(10, ("\tgecos = '%s'\n", *gecos));
4685 DEBUGADD(10, ("\tgid = '%u'\n", (unsigned int)*p_gid));
4687 wcache_save_user_pwinfo( domain, nt_status, user_sid,
4688 *homedir, *shell, *gecos, *p_gid );
4691 if ( NT_STATUS_EQUAL( nt_status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND ) ) {
4692 DEBUG(5,("nss_get_info_cached: Setting domain %s offline\n",
4694 set_domain_offline( domain );
4702 /* the cache backend methods are exposed via this structure */
4703 struct winbindd_methods cache_methods = {
4721 static bool wcache_ndr_key(TALLOC_CTX *mem_ctx, char *domain_name,
4722 uint32_t opnum, const DATA_BLOB *req,
4728 key = talloc_asprintf(mem_ctx, "NDR/%s/%d/", domain_name, (int)opnum);
4732 keylen = talloc_get_size(key) - 1;
4734 key = talloc_realloc(mem_ctx, key, char, keylen + req->length);
4738 memcpy(key + keylen, req->data, req->length);
4740 pkey->dptr = (uint8_t *)key;
4741 pkey->dsize = talloc_get_size(key);
4745 static bool wcache_opnum_cacheable(uint32_t opnum)
4748 case NDR_WBINT_PING:
4749 case NDR_WBINT_QUERYSEQUENCENUMBER:
4750 case NDR_WBINT_ALLOCATEUID:
4751 case NDR_WBINT_ALLOCATEGID:
4752 case NDR_WBINT_CHECKMACHINEACCOUNT:
4753 case NDR_WBINT_CHANGEMACHINEACCOUNT:
4754 case NDR_WBINT_PINGDC:
4760 bool wcache_fetch_ndr(TALLOC_CTX *mem_ctx, struct winbindd_domain *domain,
4761 uint32_t opnum, const DATA_BLOB *req, DATA_BLOB *resp)
4766 if (!wcache_opnum_cacheable(opnum) ||
4767 is_my_own_sam_domain(domain) ||
4768 is_builtin_domain(domain)) {
4772 if (wcache->tdb == NULL) {
4776 if (!wcache_ndr_key(talloc_tos(), domain->name, opnum, req, &key)) {
4779 data = tdb_fetch(wcache->tdb, key);
4780 TALLOC_FREE(key.dptr);
4782 if (data.dptr == NULL) {
4785 if (data.dsize < 12) {
4789 if (!is_domain_offline(domain)) {
4790 uint32_t entry_seqnum, dom_seqnum, last_check;
4791 uint64_t entry_timeout;
4793 if (!wcache_fetch_seqnum(domain->name, &dom_seqnum,
4797 entry_seqnum = IVAL(data.dptr, 0);
4798 if (entry_seqnum != dom_seqnum) {
4799 DEBUG(10, ("Entry has wrong sequence number: %d\n",
4800 (int)entry_seqnum));
4803 entry_timeout = BVAL(data.dptr, 4);
4804 if (entry_timeout > time(NULL)) {
4805 DEBUG(10, ("Entry has timed out\n"));
4810 resp->data = (uint8_t *)talloc_memdup(mem_ctx, data.dptr + 12,
4812 if (resp->data == NULL) {
4813 DEBUG(10, ("talloc failed\n"));
4816 resp->length = data.dsize - 12;
4820 SAFE_FREE(data.dptr);
4824 void wcache_store_ndr(struct winbindd_domain *domain, uint32_t opnum,
4825 const DATA_BLOB *req, const DATA_BLOB *resp)
4828 uint32_t dom_seqnum, last_check;
4831 if (!wcache_opnum_cacheable(opnum) ||
4832 is_my_own_sam_domain(domain) ||
4833 is_builtin_domain(domain)) {
4837 if (wcache->tdb == NULL) {
4841 if (!wcache_fetch_seqnum(domain->name, &dom_seqnum, &last_check)) {
4842 DEBUG(10, ("could not fetch seqnum for domain %s\n",
4847 if (!wcache_ndr_key(talloc_tos(), domain->name, opnum, req, &key)) {
4851 timeout = time(NULL) + lp_winbind_cache_time();
4853 data.dsize = resp->length + 12;
4854 data.dptr = talloc_array(key.dptr, uint8_t, data.dsize);
4855 if (data.dptr == NULL) {
4859 SIVAL(data.dptr, 0, dom_seqnum);
4860 SBVAL(data.dptr, 4, timeout);
4861 memcpy(data.dptr + 12, resp->data, resp->length);
4863 tdb_store(wcache->tdb, key, data, 0);
4866 TALLOC_FREE(key.dptr);