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_winbind.h"
34 #include "../libcli/security/security.h"
35 #include "passdb/machine_sid.h"
39 #define DBGC_CLASS DBGC_WINBIND
41 #define WINBINDD_CACHE_VER1 1 /* initial db version */
42 #define WINBINDD_CACHE_VER2 2 /* second version with timeouts for NDR entries */
44 #define WINBINDD_CACHE_VERSION WINBINDD_CACHE_VER2
45 #define WINBINDD_CACHE_VERSION_KEYSTR "WINBINDD_CACHE_VERSION"
47 extern struct winbindd_methods reconnect_methods;
49 extern struct winbindd_methods ads_methods;
51 extern struct winbindd_methods builtin_passdb_methods;
52 extern struct winbindd_methods sam_passdb_methods;
55 * JRA. KEEP THIS LIST UP TO DATE IF YOU ADD CACHE ENTRIES.
56 * Here are the list of entry types that are *not* stored
57 * as form struct cache_entry in the cache.
60 static const char *non_centry_keys[] = {
63 WINBINDD_CACHE_VERSION_KEYSTR,
67 /************************************************************************
68 Is this key a non-centry type ?
69 ************************************************************************/
71 static bool is_non_centry_key(TDB_DATA kbuf)
75 if (kbuf.dptr == NULL || kbuf.dsize == 0) {
78 for (i = 0; non_centry_keys[i] != NULL; i++) {
79 size_t namelen = strlen(non_centry_keys[i]);
80 if (kbuf.dsize < namelen) {
83 if (strncmp(non_centry_keys[i], (const char *)kbuf.dptr, namelen) == 0) {
90 /* Global online/offline state - False when online. winbindd starts up online
91 and sets this to true if the first query fails and there's an entry in
92 the cache tdb telling us to stay offline. */
94 static bool global_winbindd_offline_state;
96 struct winbind_cache {
102 uint32 sequence_number;
108 void (*smb_panic_fn)(const char *const why) = smb_panic;
110 #define WINBINDD_MAX_CACHE_SIZE (50*1024*1024)
112 static struct winbind_cache *wcache;
114 /* get the winbind_cache structure */
115 static struct winbind_cache *get_cache(struct winbindd_domain *domain)
117 struct winbind_cache *ret = wcache;
119 /* We have to know what type of domain we are dealing with first. */
121 if (domain->internal) {
122 domain->backend = &builtin_passdb_methods;
125 if (dom_sid_equal(&domain->sid, &global_sid_Builtin)) {
126 domain->initialized = true;
129 if (strequal(domain->name, get_global_sam_name()) &&
130 sid_check_is_our_sam(&domain->sid)) {
131 domain->backend = &sam_passdb_methods;
134 if ( !domain->initialized ) {
135 /* We do not need a connection to an RW DC for cache operation */
136 init_dc_connection(domain, false);
140 OK. listen up becasue I'm only going to say this once.
141 We have the following scenarios to consider
142 (a) trusted AD domains on a Samba DC,
143 (b) trusted AD domains and we are joined to a non-kerberos domain
144 (c) trusted AD domains and we are joined to a kerberos (AD) domain
146 For (a) we can always contact the trusted domain using krb5
147 since we have the domain trust account password
149 For (b) we can only use RPC since we have no way of
150 getting a krb5 ticket in our own domain
152 For (c) we can always use krb5 since we have a kerberos trust
157 if (!domain->backend) {
159 struct winbindd_domain *our_domain = domain;
161 /* find our domain first so we can figure out if we
162 are joined to a kerberized domain */
164 if ( !domain->primary )
165 our_domain = find_our_domain();
167 if ((our_domain->active_directory || IS_DC)
168 && domain->active_directory
169 && !lp_winbind_rpc_only()) {
170 DEBUG(5,("get_cache: Setting ADS methods for domain %s\n", domain->name));
171 domain->backend = &ads_methods;
173 #endif /* HAVE_ADS */
174 DEBUG(5,("get_cache: Setting MS-RPC methods for domain %s\n", domain->name));
175 domain->backend = &reconnect_methods;
178 #endif /* HAVE_ADS */
184 ret = SMB_XMALLOC_P(struct winbind_cache);
188 wcache_flush_cache();
194 free a centry structure
196 static void centry_free(struct cache_entry *centry)
200 SAFE_FREE(centry->data);
204 static bool centry_check_bytes(struct cache_entry *centry, size_t nbytes)
206 if (centry->len - centry->ofs < nbytes) {
207 DEBUG(0,("centry corruption? needed %u bytes, have %d\n",
208 (unsigned int)nbytes,
209 centry->len - centry->ofs));
216 pull a uint64_t from a cache entry
218 static uint64_t centry_uint64_t(struct cache_entry *centry)
222 if (!centry_check_bytes(centry, 8)) {
223 smb_panic_fn("centry_uint64_t");
225 ret = BVAL(centry->data, centry->ofs);
231 pull a uint32 from a cache entry
233 static uint32 centry_uint32(struct cache_entry *centry)
237 if (!centry_check_bytes(centry, 4)) {
238 smb_panic_fn("centry_uint32");
240 ret = IVAL(centry->data, centry->ofs);
246 pull a uint16 from a cache entry
248 static uint16 centry_uint16(struct cache_entry *centry)
251 if (!centry_check_bytes(centry, 2)) {
252 smb_panic_fn("centry_uint16");
254 ret = SVAL(centry->data, centry->ofs);
260 pull a uint8 from a cache entry
262 static uint8 centry_uint8(struct cache_entry *centry)
265 if (!centry_check_bytes(centry, 1)) {
266 smb_panic_fn("centry_uint8");
268 ret = CVAL(centry->data, centry->ofs);
274 pull a NTTIME from a cache entry
276 static NTTIME centry_nttime(struct cache_entry *centry)
279 if (!centry_check_bytes(centry, 8)) {
280 smb_panic_fn("centry_nttime");
282 ret = IVAL(centry->data, centry->ofs);
284 ret += (uint64)IVAL(centry->data, centry->ofs) << 32;
290 pull a time_t from a cache entry. time_t stored portably as a 64-bit time.
292 static time_t centry_time(struct cache_entry *centry)
294 return (time_t)centry_nttime(centry);
297 /* pull a string from a cache entry, using the supplied
300 static char *centry_string(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
305 len = centry_uint8(centry);
308 /* a deliberate NULL string */
312 if (!centry_check_bytes(centry, (size_t)len)) {
313 smb_panic_fn("centry_string");
316 ret = talloc_array(mem_ctx, char, len+1);
318 smb_panic_fn("centry_string out of memory\n");
320 memcpy(ret,centry->data + centry->ofs, len);
326 /* pull a hash16 from a cache entry, using the supplied
329 static char *centry_hash16(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
334 len = centry_uint8(centry);
337 DEBUG(0,("centry corruption? hash len (%u) != 16\n",
342 if (!centry_check_bytes(centry, 16)) {
346 ret = talloc_array(mem_ctx, char, 16);
348 smb_panic_fn("centry_hash out of memory\n");
350 memcpy(ret,centry->data + centry->ofs, 16);
355 /* pull a sid from a cache entry, using the supplied
358 static bool centry_sid(struct cache_entry *centry, struct dom_sid *sid)
363 sid_string = centry_string(centry, talloc_tos());
364 if (sid_string == NULL) {
367 ret = string_to_sid(sid, sid_string);
368 TALLOC_FREE(sid_string);
374 pull a NTSTATUS from a cache entry
376 static NTSTATUS centry_ntstatus(struct cache_entry *centry)
380 status = NT_STATUS(centry_uint32(centry));
385 /* the server is considered down if it can't give us a sequence number */
386 static bool wcache_server_down(struct winbindd_domain *domain)
393 ret = (domain->sequence_number == DOM_SEQUENCE_NONE);
396 DEBUG(10,("wcache_server_down: server for Domain %s down\n",
401 static bool wcache_fetch_seqnum(const char *domain_name, uint32_t *seqnum,
402 uint32_t *last_seq_check)
407 if (wcache->tdb == NULL) {
408 DEBUG(10,("wcache_fetch_seqnum: tdb == NULL\n"));
412 key = talloc_asprintf(talloc_tos(), "SEQNUM/%s", domain_name);
414 DEBUG(10, ("talloc failed\n"));
418 data = tdb_fetch_bystring(wcache->tdb, key);
421 if (data.dptr == NULL) {
422 DEBUG(10, ("wcache_fetch_seqnum: %s not found\n",
426 if (data.dsize != 8) {
427 DEBUG(10, ("wcache_fetch_seqnum: invalid data size %d\n",
429 SAFE_FREE(data.dptr);
433 *seqnum = IVAL(data.dptr, 0);
434 *last_seq_check = IVAL(data.dptr, 4);
435 SAFE_FREE(data.dptr);
440 static NTSTATUS fetch_cache_seqnum( struct winbindd_domain *domain, time_t now )
442 uint32 last_check, time_diff;
444 if (!wcache_fetch_seqnum(domain->name, &domain->sequence_number,
446 return NT_STATUS_UNSUCCESSFUL;
448 domain->last_seq_check = last_check;
450 /* have we expired? */
452 time_diff = now - domain->last_seq_check;
453 if ( time_diff > lp_winbind_cache_time() ) {
454 DEBUG(10,("fetch_cache_seqnum: timeout [%s][%u @ %u]\n",
455 domain->name, domain->sequence_number,
456 (uint32)domain->last_seq_check));
457 return NT_STATUS_UNSUCCESSFUL;
460 DEBUG(10,("fetch_cache_seqnum: success [%s][%u @ %u]\n",
461 domain->name, domain->sequence_number,
462 (uint32)domain->last_seq_check));
467 bool wcache_store_seqnum(const char *domain_name, uint32_t seqnum,
468 time_t last_seq_check)
474 if (wcache->tdb == NULL) {
475 DEBUG(10, ("wcache_store_seqnum: wcache->tdb == NULL\n"));
479 key_str = talloc_asprintf(talloc_tos(), "SEQNUM/%s", domain_name);
480 if (key_str == NULL) {
481 DEBUG(10, ("talloc_asprintf failed\n"));
485 SIVAL(buf, 0, seqnum);
486 SIVAL(buf, 4, last_seq_check);
488 ret = tdb_store_bystring(wcache->tdb, key_str,
489 make_tdb_data(buf, sizeof(buf)), TDB_REPLACE);
490 TALLOC_FREE(key_str);
492 DEBUG(10, ("tdb_store_bystring failed: %s\n",
493 tdb_errorstr_compat(wcache->tdb)));
494 TALLOC_FREE(key_str);
498 DEBUG(10, ("wcache_store_seqnum: success [%s][%u @ %u]\n",
499 domain_name, seqnum, (unsigned)last_seq_check));
504 static bool store_cache_seqnum( struct winbindd_domain *domain )
506 return wcache_store_seqnum(domain->name, domain->sequence_number,
507 domain->last_seq_check);
511 refresh the domain sequence number. If force is true
512 then always refresh it, no matter how recently we fetched it
515 static void refresh_sequence_number(struct winbindd_domain *domain, bool force)
519 time_t t = time(NULL);
520 unsigned cache_time = lp_winbind_cache_time();
522 if (is_domain_offline(domain)) {
528 #if 0 /* JERRY -- disable as the default cache time is now 5 minutes */
529 /* trying to reconnect is expensive, don't do it too often */
530 if (domain->sequence_number == DOM_SEQUENCE_NONE) {
535 time_diff = t - domain->last_seq_check;
537 /* see if we have to refetch the domain sequence number */
538 if (!force && (time_diff < cache_time) &&
539 (domain->sequence_number != DOM_SEQUENCE_NONE) &&
540 NT_STATUS_IS_OK(domain->last_status)) {
541 DEBUG(10, ("refresh_sequence_number: %s time ok\n", domain->name));
545 /* try to get the sequence number from the tdb cache first */
546 /* this will update the timestamp as well */
548 status = fetch_cache_seqnum( domain, t );
549 if (NT_STATUS_IS_OK(status) &&
550 (domain->sequence_number != DOM_SEQUENCE_NONE) &&
551 NT_STATUS_IS_OK(domain->last_status)) {
555 /* important! make sure that we know if this is a native
556 mode domain or not. And that we can contact it. */
558 if ( winbindd_can_contact_domain( domain ) ) {
559 status = domain->backend->sequence_number(domain,
560 &domain->sequence_number);
562 /* just use the current time */
563 status = NT_STATUS_OK;
564 domain->sequence_number = time(NULL);
568 /* the above call could have set our domain->backend to NULL when
569 * coming from offline to online mode, make sure to reinitialize the
570 * backend - Guenther */
573 if (!NT_STATUS_IS_OK(status)) {
574 DEBUG(10,("refresh_sequence_number: failed with %s\n", nt_errstr(status)));
575 domain->sequence_number = DOM_SEQUENCE_NONE;
578 domain->last_status = status;
579 domain->last_seq_check = time(NULL);
581 /* save the new sequence number in the cache */
582 store_cache_seqnum( domain );
585 DEBUG(10, ("refresh_sequence_number: %s seq number is now %d\n",
586 domain->name, domain->sequence_number));
592 decide if a cache entry has expired
594 static bool centry_expired(struct winbindd_domain *domain, const char *keystr, struct cache_entry *centry)
596 /* If we've been told to be offline - stay in that state... */
597 if (lp_winbind_offline_logon() && global_winbindd_offline_state) {
598 DEBUG(10,("centry_expired: Key %s for domain %s valid as winbindd is globally offline.\n",
599 keystr, domain->name ));
603 /* when the domain is offline return the cached entry.
604 * This deals with transient offline states... */
606 if (!domain->online) {
607 DEBUG(10,("centry_expired: Key %s for domain %s valid as domain is offline.\n",
608 keystr, domain->name ));
612 /* if the server is OK and our cache entry came from when it was down then
613 the entry is invalid */
614 if ((domain->sequence_number != DOM_SEQUENCE_NONE) &&
615 (centry->sequence_number == DOM_SEQUENCE_NONE)) {
616 DEBUG(10,("centry_expired: Key %s for domain %s invalid sequence.\n",
617 keystr, domain->name ));
621 /* if the server is down or the cache entry is not older than the
622 current sequence number or it did not timeout then it is OK */
623 if (wcache_server_down(domain)
624 || ((centry->sequence_number == domain->sequence_number)
625 && (centry->timeout > time(NULL)))) {
626 DEBUG(10,("centry_expired: Key %s for domain %s is good.\n",
627 keystr, domain->name ));
631 DEBUG(10,("centry_expired: Key %s for domain %s expired\n",
632 keystr, domain->name ));
638 static struct cache_entry *wcache_fetch_raw(char *kstr)
641 struct cache_entry *centry;
644 key = string_tdb_data(kstr);
645 data = tdb_fetch_compat(wcache->tdb, key);
651 centry = SMB_XMALLOC_P(struct cache_entry);
652 centry->data = (unsigned char *)data.dptr;
653 centry->len = data.dsize;
656 if (centry->len < 16) {
657 /* huh? corrupt cache? */
658 DEBUG(10,("wcache_fetch_raw: Corrupt cache for key %s "
659 "(len < 16)?\n", kstr));
664 centry->status = centry_ntstatus(centry);
665 centry->sequence_number = centry_uint32(centry);
666 centry->timeout = centry_uint64_t(centry);
671 static bool is_my_own_sam_domain(struct winbindd_domain *domain)
673 if (strequal(domain->name, get_global_sam_name()) &&
674 sid_check_is_our_sam(&domain->sid)) {
681 static bool is_builtin_domain(struct winbindd_domain *domain)
683 if (strequal(domain->name, "BUILTIN") &&
684 sid_check_is_builtin(&domain->sid)) {
692 fetch an entry from the cache, with a varargs key. auto-fetch the sequence
693 number and return status
695 static struct cache_entry *wcache_fetch(struct winbind_cache *cache,
696 struct winbindd_domain *domain,
697 const char *format, ...) PRINTF_ATTRIBUTE(3,4);
698 static struct cache_entry *wcache_fetch(struct winbind_cache *cache,
699 struct winbindd_domain *domain,
700 const char *format, ...)
704 struct cache_entry *centry;
706 if (!winbindd_use_cache() ||
707 is_my_own_sam_domain(domain) ||
708 is_builtin_domain(domain)) {
712 refresh_sequence_number(domain, false);
714 va_start(ap, format);
715 smb_xvasprintf(&kstr, format, ap);
718 centry = wcache_fetch_raw(kstr);
719 if (centry == NULL) {
724 if (centry_expired(domain, kstr, centry)) {
726 DEBUG(10,("wcache_fetch: entry %s expired for domain %s\n",
727 kstr, domain->name ));
734 DEBUG(10,("wcache_fetch: returning entry %s for domain %s\n",
735 kstr, domain->name ));
741 static void wcache_delete(const char *format, ...) PRINTF_ATTRIBUTE(1,2);
742 static void wcache_delete(const char *format, ...)
748 va_start(ap, format);
749 smb_xvasprintf(&kstr, format, ap);
752 key = string_tdb_data(kstr);
754 tdb_delete(wcache->tdb, key);
759 make sure we have at least len bytes available in a centry
761 static void centry_expand(struct cache_entry *centry, uint32 len)
763 if (centry->len - centry->ofs >= len)
766 centry->data = SMB_REALLOC_ARRAY(centry->data, unsigned char,
769 DEBUG(0,("out of memory: needed %d bytes in centry_expand\n", centry->len));
770 smb_panic_fn("out of memory in centry_expand");
775 push a uint64_t into a centry
777 static void centry_put_uint64_t(struct cache_entry *centry, uint64_t v)
779 centry_expand(centry, 8);
780 SBVAL(centry->data, centry->ofs, v);
785 push a uint32 into a centry
787 static void centry_put_uint32(struct cache_entry *centry, uint32 v)
789 centry_expand(centry, 4);
790 SIVAL(centry->data, centry->ofs, v);
795 push a uint16 into a centry
797 static void centry_put_uint16(struct cache_entry *centry, uint16 v)
799 centry_expand(centry, 2);
800 SSVAL(centry->data, centry->ofs, v);
805 push a uint8 into a centry
807 static void centry_put_uint8(struct cache_entry *centry, uint8 v)
809 centry_expand(centry, 1);
810 SCVAL(centry->data, centry->ofs, v);
815 push a string into a centry
817 static void centry_put_string(struct cache_entry *centry, const char *s)
822 /* null strings are marked as len 0xFFFF */
823 centry_put_uint8(centry, 0xFF);
828 /* can't handle more than 254 char strings. Truncating is probably best */
830 DEBUG(10,("centry_put_string: truncating len (%d) to: 254\n", len));
833 centry_put_uint8(centry, len);
834 centry_expand(centry, len);
835 memcpy(centry->data + centry->ofs, s, len);
840 push a 16 byte hash into a centry - treat as 16 byte string.
842 static void centry_put_hash16(struct cache_entry *centry, const uint8 val[16])
844 centry_put_uint8(centry, 16);
845 centry_expand(centry, 16);
846 memcpy(centry->data + centry->ofs, val, 16);
850 static void centry_put_sid(struct cache_entry *centry, const struct dom_sid *sid)
853 centry_put_string(centry, sid_to_fstring(sid_string, sid));
858 put NTSTATUS into a centry
860 static void centry_put_ntstatus(struct cache_entry *centry, NTSTATUS status)
862 uint32 status_value = NT_STATUS_V(status);
863 centry_put_uint32(centry, status_value);
868 push a NTTIME into a centry
870 static void centry_put_nttime(struct cache_entry *centry, NTTIME nt)
872 centry_expand(centry, 8);
873 SIVAL(centry->data, centry->ofs, nt & 0xFFFFFFFF);
875 SIVAL(centry->data, centry->ofs, nt >> 32);
880 push a time_t into a centry - use a 64 bit size.
881 NTTIME here is being used as a convenient 64-bit size.
883 static void centry_put_time(struct cache_entry *centry, time_t t)
885 NTTIME nt = (NTTIME)t;
886 centry_put_nttime(centry, nt);
890 start a centry for output. When finished, call centry_end()
892 static struct cache_entry *centry_start(struct winbindd_domain *domain,
895 struct cache_entry *centry;
900 centry = SMB_XMALLOC_P(struct cache_entry);
902 centry->len = 8192; /* reasonable default */
903 centry->data = SMB_XMALLOC_ARRAY(uint8, centry->len);
905 centry->sequence_number = domain->sequence_number;
906 centry->timeout = lp_winbind_cache_time() + time(NULL);
907 centry_put_ntstatus(centry, status);
908 centry_put_uint32(centry, centry->sequence_number);
909 centry_put_uint64_t(centry, centry->timeout);
914 finish a centry and write it to the tdb
916 static void centry_end(struct cache_entry *centry, const char *format, ...) PRINTF_ATTRIBUTE(2,3);
917 static void centry_end(struct cache_entry *centry, const char *format, ...)
923 if (!winbindd_use_cache()) {
927 va_start(ap, format);
928 smb_xvasprintf(&kstr, format, ap);
931 key = string_tdb_data(kstr);
932 data.dptr = centry->data;
933 data.dsize = centry->ofs;
935 tdb_store(wcache->tdb, key, data, TDB_REPLACE);
939 static void wcache_save_name_to_sid(struct winbindd_domain *domain,
940 NTSTATUS status, const char *domain_name,
941 const char *name, const struct dom_sid *sid,
942 enum lsa_SidType type)
944 struct cache_entry *centry;
947 centry = centry_start(domain, status);
951 if ((domain_name == NULL) || (domain_name[0] == '\0')) {
952 struct winbindd_domain *mydomain =
953 find_domain_from_sid_noinit(sid);
954 if (mydomain != NULL) {
955 domain_name = mydomain->name;
959 centry_put_uint32(centry, type);
960 centry_put_sid(centry, sid);
961 fstrcpy(uname, name);
962 (void)strupper_m(uname);
963 centry_end(centry, "NS/%s/%s", domain_name, uname);
964 DEBUG(10,("wcache_save_name_to_sid: %s\\%s -> %s (%s)\n", domain_name,
965 uname, sid_string_dbg(sid), nt_errstr(status)));
969 static void wcache_save_sid_to_name(struct winbindd_domain *domain, NTSTATUS status,
970 const struct dom_sid *sid, const char *domain_name, const char *name, enum lsa_SidType type)
972 struct cache_entry *centry;
975 centry = centry_start(domain, status);
979 if ((domain_name == NULL) || (domain_name[0] == '\0')) {
980 struct winbindd_domain *mydomain =
981 find_domain_from_sid_noinit(sid);
982 if (mydomain != NULL) {
983 domain_name = mydomain->name;
987 if (NT_STATUS_IS_OK(status)) {
988 centry_put_uint32(centry, type);
989 centry_put_string(centry, domain_name);
990 centry_put_string(centry, name);
993 centry_end(centry, "SN/%s", sid_to_fstring(sid_string, sid));
994 DEBUG(10,("wcache_save_sid_to_name: %s -> %s\\%s (%s)\n", sid_string,
995 domain_name, name, nt_errstr(status)));
1000 static void wcache_save_user(struct winbindd_domain *domain, NTSTATUS status,
1001 struct wbint_userinfo *info)
1003 struct cache_entry *centry;
1006 if (is_null_sid(&info->user_sid)) {
1010 centry = centry_start(domain, status);
1013 centry_put_string(centry, info->acct_name);
1014 centry_put_string(centry, info->full_name);
1015 centry_put_string(centry, info->homedir);
1016 centry_put_string(centry, info->shell);
1017 centry_put_uint32(centry, info->primary_gid);
1018 centry_put_sid(centry, &info->user_sid);
1019 centry_put_sid(centry, &info->group_sid);
1020 centry_end(centry, "U/%s", sid_to_fstring(sid_string,
1022 DEBUG(10,("wcache_save_user: %s (acct_name %s)\n", sid_string, info->acct_name));
1023 centry_free(centry);
1026 static void wcache_save_lockout_policy(struct winbindd_domain *domain,
1028 struct samr_DomInfo12 *lockout_policy)
1030 struct cache_entry *centry;
1032 centry = centry_start(domain, status);
1036 centry_put_nttime(centry, lockout_policy->lockout_duration);
1037 centry_put_nttime(centry, lockout_policy->lockout_window);
1038 centry_put_uint16(centry, lockout_policy->lockout_threshold);
1040 centry_end(centry, "LOC_POL/%s", domain->name);
1042 DEBUG(10,("wcache_save_lockout_policy: %s\n", domain->name));
1044 centry_free(centry);
1049 static void wcache_save_password_policy(struct winbindd_domain *domain,
1051 struct samr_DomInfo1 *policy)
1053 struct cache_entry *centry;
1055 centry = centry_start(domain, status);
1059 centry_put_uint16(centry, policy->min_password_length);
1060 centry_put_uint16(centry, policy->password_history_length);
1061 centry_put_uint32(centry, policy->password_properties);
1062 centry_put_nttime(centry, policy->max_password_age);
1063 centry_put_nttime(centry, policy->min_password_age);
1065 centry_end(centry, "PWD_POL/%s", domain->name);
1067 DEBUG(10,("wcache_save_password_policy: %s\n", domain->name));
1069 centry_free(centry);
1072 /***************************************************************************
1073 ***************************************************************************/
1075 static void wcache_save_username_alias(struct winbindd_domain *domain,
1077 const char *name, const char *alias)
1079 struct cache_entry *centry;
1082 if ( (centry = centry_start(domain, status)) == NULL )
1085 centry_put_string( centry, alias );
1087 fstrcpy(uname, name);
1088 (void)strupper_m(uname);
1089 centry_end(centry, "NSS/NA/%s", uname);
1091 DEBUG(10,("wcache_save_username_alias: %s -> %s\n", name, alias ));
1093 centry_free(centry);
1096 static void wcache_save_alias_username(struct winbindd_domain *domain,
1098 const char *alias, const char *name)
1100 struct cache_entry *centry;
1103 if ( (centry = centry_start(domain, status)) == NULL )
1106 centry_put_string( centry, name );
1108 fstrcpy(uname, alias);
1109 (void)strupper_m(uname);
1110 centry_end(centry, "NSS/AN/%s", uname);
1112 DEBUG(10,("wcache_save_alias_username: %s -> %s\n", alias, name ));
1114 centry_free(centry);
1117 /***************************************************************************
1118 ***************************************************************************/
1120 NTSTATUS resolve_username_to_alias( TALLOC_CTX *mem_ctx,
1121 struct winbindd_domain *domain,
1122 const char *name, char **alias )
1124 struct winbind_cache *cache = get_cache(domain);
1125 struct cache_entry *centry = NULL;
1129 if ( domain->internal )
1130 return NT_STATUS_NOT_SUPPORTED;
1135 upper_name = talloc_strdup(mem_ctx, name);
1136 if (upper_name == NULL) {
1137 return NT_STATUS_NO_MEMORY;
1139 if (!strupper_m(upper_name)) {
1140 talloc_free(upper_name);
1141 return NT_STATUS_INVALID_PARAMETER;
1144 centry = wcache_fetch(cache, domain, "NSS/NA/%s", upper_name);
1146 talloc_free(upper_name);
1151 status = centry->status;
1153 if (!NT_STATUS_IS_OK(status)) {
1154 centry_free(centry);
1158 *alias = centry_string( centry, mem_ctx );
1160 centry_free(centry);
1162 DEBUG(10,("resolve_username_to_alias: [Cached] - mapped %s to %s\n",
1163 name, *alias ? *alias : "(none)"));
1165 return (*alias) ? NT_STATUS_OK : NT_STATUS_OBJECT_NAME_NOT_FOUND;
1169 /* If its not in cache and we are offline, then fail */
1171 if ( get_global_winbindd_state_offline() || !domain->online ) {
1172 DEBUG(8,("resolve_username_to_alias: rejecting query "
1173 "in offline mode\n"));
1174 return NT_STATUS_NOT_FOUND;
1177 status = nss_map_to_alias( mem_ctx, domain->name, name, alias );
1179 if ( NT_STATUS_IS_OK( status ) ) {
1180 wcache_save_username_alias(domain, status, name, *alias);
1183 if ( NT_STATUS_EQUAL( status, NT_STATUS_NONE_MAPPED ) ) {
1184 wcache_save_username_alias(domain, status, name, "(NULL)");
1187 DEBUG(5,("resolve_username_to_alias: backend query returned %s\n",
1188 nt_errstr(status)));
1190 if ( NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ) {
1191 set_domain_offline( domain );
1197 /***************************************************************************
1198 ***************************************************************************/
1200 NTSTATUS resolve_alias_to_username( TALLOC_CTX *mem_ctx,
1201 struct winbindd_domain *domain,
1202 const char *alias, char **name )
1204 struct winbind_cache *cache = get_cache(domain);
1205 struct cache_entry *centry = NULL;
1209 if ( domain->internal )
1210 return NT_STATUS_NOT_SUPPORTED;
1215 upper_name = talloc_strdup(mem_ctx, alias);
1216 if (upper_name == NULL) {
1217 return NT_STATUS_NO_MEMORY;
1219 if (!strupper_m(upper_name)) {
1220 talloc_free(upper_name);
1221 return NT_STATUS_INVALID_PARAMETER;
1224 centry = wcache_fetch(cache, domain, "NSS/AN/%s", upper_name);
1226 talloc_free(upper_name);
1231 status = centry->status;
1233 if (!NT_STATUS_IS_OK(status)) {
1234 centry_free(centry);
1238 *name = centry_string( centry, mem_ctx );
1240 centry_free(centry);
1242 DEBUG(10,("resolve_alias_to_username: [Cached] - mapped %s to %s\n",
1243 alias, *name ? *name : "(none)"));
1245 return (*name) ? NT_STATUS_OK : NT_STATUS_OBJECT_NAME_NOT_FOUND;
1249 /* If its not in cache and we are offline, then fail */
1251 if ( get_global_winbindd_state_offline() || !domain->online ) {
1252 DEBUG(8,("resolve_alias_to_username: rejecting query "
1253 "in offline mode\n"));
1254 return NT_STATUS_NOT_FOUND;
1257 /* an alias cannot contain a domain prefix or '@' */
1259 if (strchr(alias, '\\') || strchr(alias, '@')) {
1260 DEBUG(10,("resolve_alias_to_username: skipping fully "
1261 "qualified name %s\n", alias));
1262 return NT_STATUS_OBJECT_NAME_INVALID;
1265 status = nss_map_from_alias( mem_ctx, domain->name, alias, name );
1267 if ( NT_STATUS_IS_OK( status ) ) {
1268 wcache_save_alias_username( domain, status, alias, *name );
1271 if (NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) {
1272 wcache_save_alias_username(domain, status, alias, "(NULL)");
1275 DEBUG(5,("resolve_alias_to_username: backend query returned %s\n",
1276 nt_errstr(status)));
1278 if ( NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ) {
1279 set_domain_offline( domain );
1285 NTSTATUS wcache_cached_creds_exist(struct winbindd_domain *domain, const struct dom_sid *sid)
1287 struct winbind_cache *cache = get_cache(domain);
1289 fstring key_str, tmp;
1293 return NT_STATUS_INTERNAL_DB_ERROR;
1296 if (is_null_sid(sid)) {
1297 return NT_STATUS_INVALID_SID;
1300 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1301 return NT_STATUS_INVALID_SID;
1304 fstr_sprintf(key_str, "CRED/%s", sid_to_fstring(tmp, sid));
1306 data = tdb_fetch_compat(cache->tdb, string_tdb_data(key_str));
1308 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1311 SAFE_FREE(data.dptr);
1312 return NT_STATUS_OK;
1315 /* Lookup creds for a SID - copes with old (unsalted) creds as well
1316 as new salted ones. */
1318 NTSTATUS wcache_get_creds(struct winbindd_domain *domain,
1319 TALLOC_CTX *mem_ctx,
1320 const struct dom_sid *sid,
1321 const uint8 **cached_nt_pass,
1322 const uint8 **cached_salt)
1324 struct winbind_cache *cache = get_cache(domain);
1325 struct cache_entry *centry = NULL;
1331 return NT_STATUS_INTERNAL_DB_ERROR;
1334 if (is_null_sid(sid)) {
1335 return NT_STATUS_INVALID_SID;
1338 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1339 return NT_STATUS_INVALID_SID;
1342 /* Try and get a salted cred first. If we can't
1343 fall back to an unsalted cred. */
1345 centry = wcache_fetch(cache, domain, "CRED/%s",
1346 sid_to_fstring(tmp, sid));
1348 DEBUG(10,("wcache_get_creds: entry for [CRED/%s] not found\n",
1349 sid_string_dbg(sid)));
1350 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1354 * We don't use the time element at this moment,
1355 * but we have to consume it, so that we don't
1356 * neet to change the disk format of the cache.
1358 (void)centry_time(centry);
1360 /* In the salted case this isn't actually the nt_hash itself,
1361 but the MD5 of the salt + nt_hash. Let the caller
1362 sort this out. It can tell as we only return the cached_salt
1363 if we are returning a salted cred. */
1365 *cached_nt_pass = (const uint8 *)centry_hash16(centry, mem_ctx);
1366 if (*cached_nt_pass == NULL) {
1369 sid_to_fstring(sidstr, sid);
1371 /* Bad (old) cred cache. Delete and pretend we
1373 DEBUG(0,("wcache_get_creds: bad entry for [CRED/%s] - deleting\n",
1375 wcache_delete("CRED/%s", sidstr);
1376 centry_free(centry);
1377 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1380 /* We only have 17 bytes more data in the salted cred case. */
1381 if (centry->len - centry->ofs == 17) {
1382 *cached_salt = (const uint8 *)centry_hash16(centry, mem_ctx);
1384 *cached_salt = NULL;
1387 dump_data_pw("cached_nt_pass", *cached_nt_pass, NT_HASH_LEN);
1389 dump_data_pw("cached_salt", *cached_salt, NT_HASH_LEN);
1392 status = centry->status;
1394 DEBUG(10,("wcache_get_creds: [Cached] - cached creds for user %s status: %s\n",
1395 sid_string_dbg(sid), nt_errstr(status) ));
1397 centry_free(centry);
1401 /* Store creds for a SID - only writes out new salted ones. */
1403 NTSTATUS wcache_save_creds(struct winbindd_domain *domain,
1404 const struct dom_sid *sid,
1405 const uint8 nt_pass[NT_HASH_LEN])
1407 struct cache_entry *centry;
1410 uint8 cred_salt[NT_HASH_LEN];
1411 uint8 salted_hash[NT_HASH_LEN];
1413 if (is_null_sid(sid)) {
1414 return NT_STATUS_INVALID_SID;
1417 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1418 return NT_STATUS_INVALID_SID;
1421 centry = centry_start(domain, NT_STATUS_OK);
1423 return NT_STATUS_INTERNAL_DB_ERROR;
1426 dump_data_pw("nt_pass", nt_pass, NT_HASH_LEN);
1428 centry_put_time(centry, time(NULL));
1430 /* Create a salt and then salt the hash. */
1431 generate_random_buffer(cred_salt, NT_HASH_LEN);
1432 E_md5hash(cred_salt, nt_pass, salted_hash);
1434 centry_put_hash16(centry, salted_hash);
1435 centry_put_hash16(centry, cred_salt);
1436 centry_end(centry, "CRED/%s", sid_to_fstring(sid_string, sid));
1438 DEBUG(10,("wcache_save_creds: %s\n", sid_string));
1440 centry_free(centry);
1442 return NT_STATUS_OK;
1446 /* Query display info. This is the basic user list fn */
1447 static NTSTATUS query_user_list(struct winbindd_domain *domain,
1448 TALLOC_CTX *mem_ctx,
1449 uint32 *num_entries,
1450 struct wbint_userinfo **info)
1452 struct winbind_cache *cache = get_cache(domain);
1453 struct cache_entry *centry = NULL;
1455 unsigned int i, retry;
1456 bool old_status = domain->online;
1461 centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
1466 *num_entries = centry_uint32(centry);
1468 if (*num_entries == 0)
1471 (*info) = talloc_array(mem_ctx, struct wbint_userinfo, *num_entries);
1473 smb_panic_fn("query_user_list out of memory");
1475 for (i=0; i<(*num_entries); i++) {
1476 (*info)[i].acct_name = centry_string(centry, mem_ctx);
1477 (*info)[i].full_name = centry_string(centry, mem_ctx);
1478 (*info)[i].homedir = centry_string(centry, mem_ctx);
1479 (*info)[i].shell = centry_string(centry, mem_ctx);
1480 centry_sid(centry, &(*info)[i].user_sid);
1481 centry_sid(centry, &(*info)[i].group_sid);
1485 status = centry->status;
1487 DEBUG(10,("query_user_list: [Cached] - cached list for domain %s status: %s\n",
1488 domain->name, nt_errstr(status) ));
1490 centry_free(centry);
1497 /* Return status value returned by seq number check */
1499 if (!NT_STATUS_IS_OK(domain->last_status))
1500 return domain->last_status;
1502 /* Put the query_user_list() in a retry loop. There appears to be
1503 * some bug either with Windows 2000 or Samba's handling of large
1504 * rpc replies. This manifests itself as sudden disconnection
1505 * at a random point in the enumeration of a large (60k) user list.
1506 * The retry loop simply tries the operation again. )-: It's not
1507 * pretty but an acceptable workaround until we work out what the
1508 * real problem is. */
1513 DEBUG(10,("query_user_list: [Cached] - doing backend query for list for domain %s\n",
1516 status = domain->backend->query_user_list(domain, mem_ctx, num_entries, info);
1517 if (!NT_STATUS_IS_OK(status)) {
1518 DEBUG(3, ("query_user_list: returned 0x%08x, "
1519 "retrying\n", NT_STATUS_V(status)));
1521 if (NT_STATUS_EQUAL(status, NT_STATUS_UNSUCCESSFUL)) {
1522 DEBUG(3, ("query_user_list: flushing "
1523 "connection cache\n"));
1524 invalidate_cm_connection(&domain->conn);
1526 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1527 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1528 if (!domain->internal && old_status) {
1529 set_domain_offline(domain);
1531 /* store partial response. */
1532 if (*num_entries > 0) {
1534 * humm, what about the status used for cache?
1535 * Should it be NT_STATUS_OK?
1540 * domain is offline now, and there is no user entries,
1541 * try to fetch from cache again.
1543 if (cache->tdb && !domain->online && !domain->internal && old_status) {
1544 centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
1545 /* partial response... */
1549 goto do_fetch_cache;
1556 } while (NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_UNSUCCESSFUL) &&
1560 refresh_sequence_number(domain, false);
1561 if (!NT_STATUS_IS_OK(status)) {
1564 centry = centry_start(domain, status);
1567 centry_put_uint32(centry, *num_entries);
1568 for (i=0; i<(*num_entries); i++) {
1569 centry_put_string(centry, (*info)[i].acct_name);
1570 centry_put_string(centry, (*info)[i].full_name);
1571 centry_put_string(centry, (*info)[i].homedir);
1572 centry_put_string(centry, (*info)[i].shell);
1573 centry_put_sid(centry, &(*info)[i].user_sid);
1574 centry_put_sid(centry, &(*info)[i].group_sid);
1575 if (domain->backend && domain->backend->consistent) {
1576 /* when the backend is consistent we can pre-prime some mappings */
1577 wcache_save_name_to_sid(domain, NT_STATUS_OK,
1579 (*info)[i].acct_name,
1580 &(*info)[i].user_sid,
1582 wcache_save_sid_to_name(domain, NT_STATUS_OK,
1583 &(*info)[i].user_sid,
1585 (*info)[i].acct_name,
1587 wcache_save_user(domain, NT_STATUS_OK, &(*info)[i]);
1590 centry_end(centry, "UL/%s", domain->name);
1591 centry_free(centry);
1597 /* list all domain groups */
1598 static NTSTATUS enum_dom_groups(struct winbindd_domain *domain,
1599 TALLOC_CTX *mem_ctx,
1600 uint32 *num_entries,
1601 struct wb_acct_info **info)
1603 struct winbind_cache *cache = get_cache(domain);
1604 struct cache_entry *centry = NULL;
1609 old_status = domain->online;
1613 centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
1618 *num_entries = centry_uint32(centry);
1620 if (*num_entries == 0)
1623 (*info) = talloc_array(mem_ctx, struct wb_acct_info, *num_entries);
1625 smb_panic_fn("enum_dom_groups out of memory");
1627 for (i=0; i<(*num_entries); i++) {
1628 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1629 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1630 (*info)[i].rid = centry_uint32(centry);
1634 status = centry->status;
1636 DEBUG(10,("enum_dom_groups: [Cached] - cached list for domain %s status: %s\n",
1637 domain->name, nt_errstr(status) ));
1639 centry_free(centry);
1646 /* Return status value returned by seq number check */
1648 if (!NT_STATUS_IS_OK(domain->last_status))
1649 return domain->last_status;
1651 DEBUG(10,("enum_dom_groups: [Cached] - doing backend query for list for domain %s\n",
1654 status = domain->backend->enum_dom_groups(domain, mem_ctx, num_entries, info);
1656 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1657 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1658 if (!domain->internal && old_status) {
1659 set_domain_offline(domain);
1663 !domain->internal &&
1665 centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
1667 goto do_fetch_cache;
1672 refresh_sequence_number(domain, false);
1673 if (!NT_STATUS_IS_OK(status)) {
1676 centry = centry_start(domain, status);
1679 centry_put_uint32(centry, *num_entries);
1680 for (i=0; i<(*num_entries); i++) {
1681 centry_put_string(centry, (*info)[i].acct_name);
1682 centry_put_string(centry, (*info)[i].acct_desc);
1683 centry_put_uint32(centry, (*info)[i].rid);
1685 centry_end(centry, "GL/%s/domain", domain->name);
1686 centry_free(centry);
1692 /* list all domain groups */
1693 static NTSTATUS enum_local_groups(struct winbindd_domain *domain,
1694 TALLOC_CTX *mem_ctx,
1695 uint32 *num_entries,
1696 struct wb_acct_info **info)
1698 struct winbind_cache *cache = get_cache(domain);
1699 struct cache_entry *centry = NULL;
1704 old_status = domain->online;
1708 centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
1713 *num_entries = centry_uint32(centry);
1715 if (*num_entries == 0)
1718 (*info) = talloc_array(mem_ctx, struct wb_acct_info, *num_entries);
1720 smb_panic_fn("enum_dom_groups out of memory");
1722 for (i=0; i<(*num_entries); i++) {
1723 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1724 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1725 (*info)[i].rid = centry_uint32(centry);
1730 /* If we are returning cached data and the domain controller
1731 is down then we don't know whether the data is up to date
1732 or not. Return NT_STATUS_MORE_PROCESSING_REQUIRED to
1735 if (wcache_server_down(domain)) {
1736 DEBUG(10, ("enum_local_groups: returning cached user list and server was down\n"));
1737 status = NT_STATUS_MORE_PROCESSING_REQUIRED;
1739 status = centry->status;
1741 DEBUG(10,("enum_local_groups: [Cached] - cached list for domain %s status: %s\n",
1742 domain->name, nt_errstr(status) ));
1744 centry_free(centry);
1751 /* Return status value returned by seq number check */
1753 if (!NT_STATUS_IS_OK(domain->last_status))
1754 return domain->last_status;
1756 DEBUG(10,("enum_local_groups: [Cached] - doing backend query for list for domain %s\n",
1759 status = domain->backend->enum_local_groups(domain, mem_ctx, num_entries, info);
1761 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1762 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1763 if (!domain->internal && old_status) {
1764 set_domain_offline(domain);
1767 !domain->internal &&
1770 centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
1772 goto do_fetch_cache;
1777 refresh_sequence_number(domain, false);
1778 if (!NT_STATUS_IS_OK(status)) {
1781 centry = centry_start(domain, status);
1784 centry_put_uint32(centry, *num_entries);
1785 for (i=0; i<(*num_entries); i++) {
1786 centry_put_string(centry, (*info)[i].acct_name);
1787 centry_put_string(centry, (*info)[i].acct_desc);
1788 centry_put_uint32(centry, (*info)[i].rid);
1790 centry_end(centry, "GL/%s/local", domain->name);
1791 centry_free(centry);
1797 NTSTATUS wcache_name_to_sid(struct winbindd_domain *domain,
1798 const char *domain_name,
1800 struct dom_sid *sid,
1801 enum lsa_SidType *type)
1803 struct winbind_cache *cache = get_cache(domain);
1804 struct cache_entry *centry;
1808 if (cache->tdb == NULL) {
1809 return NT_STATUS_NOT_FOUND;
1812 uname = talloc_strdup_upper(talloc_tos(), name);
1813 if (uname == NULL) {
1814 return NT_STATUS_NO_MEMORY;
1817 if ((domain_name == NULL) || (domain_name[0] == '\0')) {
1818 domain_name = domain->name;
1821 centry = wcache_fetch(cache, domain, "NS/%s/%s", domain_name, uname);
1823 if (centry == NULL) {
1824 return NT_STATUS_NOT_FOUND;
1827 status = centry->status;
1828 if (NT_STATUS_IS_OK(status)) {
1829 *type = (enum lsa_SidType)centry_uint32(centry);
1830 centry_sid(centry, sid);
1833 DEBUG(10,("name_to_sid: [Cached] - cached name for domain %s status: "
1834 "%s\n", domain->name, nt_errstr(status) ));
1836 centry_free(centry);
1840 /* convert a single name to a sid in a domain */
1841 static NTSTATUS name_to_sid(struct winbindd_domain *domain,
1842 TALLOC_CTX *mem_ctx,
1843 const char *domain_name,
1846 struct dom_sid *sid,
1847 enum lsa_SidType *type)
1852 old_status = domain->online;
1854 status = wcache_name_to_sid(domain, domain_name, name, sid, type);
1855 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
1861 /* If the seq number check indicated that there is a problem
1862 * with this DC, then return that status... except for
1863 * access_denied. This is special because the dc may be in
1864 * "restrict anonymous = 1" mode, in which case it will deny
1865 * most unauthenticated operations, but *will* allow the LSA
1866 * name-to-sid that we try as a fallback. */
1868 if (!(NT_STATUS_IS_OK(domain->last_status)
1869 || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1870 return domain->last_status;
1872 DEBUG(10,("name_to_sid: [Cached] - doing backend query for name for domain %s\n",
1875 status = domain->backend->name_to_sid(domain, mem_ctx, domain_name,
1876 name, flags, sid, type);
1878 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1879 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1880 if (!domain->internal && old_status) {
1881 set_domain_offline(domain);
1883 if (!domain->internal &&
1886 NTSTATUS cache_status;
1887 cache_status = wcache_name_to_sid(domain, domain_name, name, sid, type);
1888 return cache_status;
1892 refresh_sequence_number(domain, false);
1894 if (domain->online &&
1895 (NT_STATUS_IS_OK(status) || NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED))) {
1896 wcache_save_name_to_sid(domain, status, domain_name, name, sid, *type);
1898 /* Only save the reverse mapping if this was not a UPN */
1899 if (!strchr(name, '@')) {
1900 if (!strupper_m(discard_const_p(char, domain_name))) {
1901 return NT_STATUS_INVALID_PARAMETER;
1903 (void)strlower_m(discard_const_p(char, name));
1904 wcache_save_sid_to_name(domain, status, sid, domain_name, name, *type);
1911 NTSTATUS wcache_sid_to_name(struct winbindd_domain *domain,
1912 const struct dom_sid *sid,
1913 TALLOC_CTX *mem_ctx,
1916 enum lsa_SidType *type)
1918 struct winbind_cache *cache = get_cache(domain);
1919 struct cache_entry *centry;
1923 if (cache->tdb == NULL) {
1924 return NT_STATUS_NOT_FOUND;
1927 sid_string = sid_string_tos(sid);
1928 if (sid_string == NULL) {
1929 return NT_STATUS_NO_MEMORY;
1932 centry = wcache_fetch(cache, domain, "SN/%s", sid_string);
1933 TALLOC_FREE(sid_string);
1934 if (centry == NULL) {
1935 return NT_STATUS_NOT_FOUND;
1938 if (NT_STATUS_IS_OK(centry->status)) {
1939 *type = (enum lsa_SidType)centry_uint32(centry);
1940 *domain_name = centry_string(centry, mem_ctx);
1941 *name = centry_string(centry, mem_ctx);
1944 status = centry->status;
1945 centry_free(centry);
1947 DEBUG(10,("sid_to_name: [Cached] - cached name for domain %s status: "
1948 "%s\n", domain->name, nt_errstr(status) ));
1953 /* convert a sid to a user or group name. The sid is guaranteed to be in the domain
1955 static NTSTATUS sid_to_name(struct winbindd_domain *domain,
1956 TALLOC_CTX *mem_ctx,
1957 const struct dom_sid *sid,
1960 enum lsa_SidType *type)
1965 old_status = domain->online;
1966 status = wcache_sid_to_name(domain, sid, mem_ctx, domain_name, name,
1968 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
1973 *domain_name = NULL;
1975 /* If the seq number check indicated that there is a problem
1976 * with this DC, then return that status... except for
1977 * access_denied. This is special because the dc may be in
1978 * "restrict anonymous = 1" mode, in which case it will deny
1979 * most unauthenticated operations, but *will* allow the LSA
1980 * sid-to-name that we try as a fallback. */
1982 if (!(NT_STATUS_IS_OK(domain->last_status)
1983 || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1984 return domain->last_status;
1986 DEBUG(10,("sid_to_name: [Cached] - doing backend query for name for domain %s\n",
1989 status = domain->backend->sid_to_name(domain, mem_ctx, sid, domain_name, name, type);
1991 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1992 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1993 if (!domain->internal && old_status) {
1994 set_domain_offline(domain);
1996 if (!domain->internal &&
1999 NTSTATUS cache_status;
2000 cache_status = wcache_sid_to_name(domain, sid, mem_ctx,
2001 domain_name, name, type);
2002 return cache_status;
2006 refresh_sequence_number(domain, false);
2007 if (!NT_STATUS_IS_OK(status)) {
2010 wcache_save_sid_to_name(domain, status, sid, *domain_name, *name, *type);
2012 /* We can't save the name to sid mapping here, as with sid history a
2013 * later name2sid would give the wrong sid. */
2018 static NTSTATUS rids_to_names(struct winbindd_domain *domain,
2019 TALLOC_CTX *mem_ctx,
2020 const struct dom_sid *domain_sid,
2025 enum lsa_SidType **types)
2027 struct winbind_cache *cache = get_cache(domain);
2029 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
2034 old_status = domain->online;
2035 *domain_name = NULL;
2043 if (num_rids == 0) {
2044 return NT_STATUS_OK;
2047 *names = talloc_array(mem_ctx, char *, num_rids);
2048 *types = talloc_array(mem_ctx, enum lsa_SidType, num_rids);
2050 if ((*names == NULL) || (*types == NULL)) {
2051 result = NT_STATUS_NO_MEMORY;
2055 have_mapped = have_unmapped = false;
2057 for (i=0; i<num_rids; i++) {
2059 struct cache_entry *centry;
2062 if (!sid_compose(&sid, domain_sid, rids[i])) {
2063 result = NT_STATUS_INTERNAL_ERROR;
2067 centry = wcache_fetch(cache, domain, "SN/%s",
2068 sid_to_fstring(tmp, &sid));
2073 (*types)[i] = SID_NAME_UNKNOWN;
2074 (*names)[i] = talloc_strdup(*names, "");
2076 if (NT_STATUS_IS_OK(centry->status)) {
2079 (*types)[i] = (enum lsa_SidType)centry_uint32(centry);
2081 dom = centry_string(centry, mem_ctx);
2082 if (*domain_name == NULL) {
2088 (*names)[i] = centry_string(centry, *names);
2090 } else if (NT_STATUS_EQUAL(centry->status, NT_STATUS_NONE_MAPPED)
2091 || NT_STATUS_EQUAL(centry->status, STATUS_SOME_UNMAPPED)) {
2092 have_unmapped = true;
2095 /* something's definitely wrong */
2096 result = centry->status;
2097 centry_free(centry);
2101 centry_free(centry);
2105 return NT_STATUS_NONE_MAPPED;
2107 if (!have_unmapped) {
2108 return NT_STATUS_OK;
2110 return STATUS_SOME_UNMAPPED;
2114 TALLOC_FREE(*names);
2115 TALLOC_FREE(*types);
2117 result = domain->backend->rids_to_names(domain, mem_ctx, domain_sid,
2118 rids, num_rids, domain_name,
2121 if (NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
2122 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2123 if (!domain->internal && old_status) {
2124 set_domain_offline(domain);
2127 !domain->internal &&
2130 have_mapped = have_unmapped = false;
2132 *names = talloc_array(mem_ctx, char *, num_rids);
2133 if (*names == NULL) {
2134 result = NT_STATUS_NO_MEMORY;
2138 *types = talloc_array(mem_ctx, enum lsa_SidType,
2140 if (*types == NULL) {
2141 result = NT_STATUS_NO_MEMORY;
2145 for (i=0; i<num_rids; i++) {
2147 struct cache_entry *centry;
2150 if (!sid_compose(&sid, domain_sid, rids[i])) {
2151 result = NT_STATUS_INTERNAL_ERROR;
2155 centry = wcache_fetch(cache, domain, "SN/%s",
2156 sid_to_fstring(tmp, &sid));
2158 (*types)[i] = SID_NAME_UNKNOWN;
2159 (*names)[i] = talloc_strdup(*names, "");
2163 (*types)[i] = SID_NAME_UNKNOWN;
2164 (*names)[i] = talloc_strdup(*names, "");
2166 if (NT_STATUS_IS_OK(centry->status)) {
2169 (*types)[i] = (enum lsa_SidType)centry_uint32(centry);
2171 dom = centry_string(centry, mem_ctx);
2172 if (*domain_name == NULL) {
2178 (*names)[i] = centry_string(centry, *names);
2180 } else if (NT_STATUS_EQUAL(centry->status, NT_STATUS_NONE_MAPPED)) {
2181 have_unmapped = true;
2184 /* something's definitely wrong */
2185 result = centry->status;
2186 centry_free(centry);
2190 centry_free(centry);
2194 return NT_STATUS_NONE_MAPPED;
2196 if (!have_unmapped) {
2197 return NT_STATUS_OK;
2199 return STATUS_SOME_UNMAPPED;
2203 None of the queried rids has been found so save all negative entries
2205 if (NT_STATUS_EQUAL(result, NT_STATUS_NONE_MAPPED)) {
2206 for (i = 0; i < num_rids; i++) {
2208 const char *name = "";
2209 const enum lsa_SidType type = SID_NAME_UNKNOWN;
2210 NTSTATUS status = NT_STATUS_NONE_MAPPED;
2212 if (!sid_compose(&sid, domain_sid, rids[i])) {
2213 return NT_STATUS_INTERNAL_ERROR;
2216 wcache_save_sid_to_name(domain, status, &sid, *domain_name,
2224 Some or all of the queried rids have been found.
2226 if (!NT_STATUS_IS_OK(result) &&
2227 !NT_STATUS_EQUAL(result, STATUS_SOME_UNMAPPED)) {
2231 refresh_sequence_number(domain, false);
2233 for (i=0; i<num_rids; i++) {
2237 if (!sid_compose(&sid, domain_sid, rids[i])) {
2238 result = NT_STATUS_INTERNAL_ERROR;
2242 status = (*types)[i] == SID_NAME_UNKNOWN ?
2243 NT_STATUS_NONE_MAPPED : NT_STATUS_OK;
2245 wcache_save_sid_to_name(domain, status, &sid, *domain_name,
2246 (*names)[i], (*types)[i]);
2252 TALLOC_FREE(*names);
2253 TALLOC_FREE(*types);
2257 NTSTATUS wcache_query_user(struct winbindd_domain *domain,
2258 TALLOC_CTX *mem_ctx,
2259 const struct dom_sid *user_sid,
2260 struct wbint_userinfo *info)
2262 struct winbind_cache *cache = get_cache(domain);
2263 struct cache_entry *centry = NULL;
2267 if (cache->tdb == NULL) {
2268 return NT_STATUS_NOT_FOUND;
2271 sid_string = sid_string_tos(user_sid);
2272 if (sid_string == NULL) {
2273 return NT_STATUS_NO_MEMORY;
2276 centry = wcache_fetch(cache, domain, "U/%s", sid_string);
2277 TALLOC_FREE(sid_string);
2278 if (centry == NULL) {
2279 return NT_STATUS_NOT_FOUND;
2283 * If we have an access denied cache entry and a cached info3
2284 * in the samlogon cache then do a query. This will force the
2285 * rpc back end to return the info3 data.
2288 if (NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED) &&
2289 netsamlogon_cache_have(user_sid)) {
2290 DEBUG(10, ("query_user: cached access denied and have cached "
2292 domain->last_status = NT_STATUS_OK;
2293 centry_free(centry);
2294 return NT_STATUS_NOT_FOUND;
2297 /* if status is not ok then this is a negative hit
2298 and the rest of the data doesn't matter */
2299 status = centry->status;
2300 if (NT_STATUS_IS_OK(status)) {
2301 info->acct_name = centry_string(centry, mem_ctx);
2302 info->full_name = centry_string(centry, mem_ctx);
2303 info->homedir = centry_string(centry, mem_ctx);
2304 info->shell = centry_string(centry, mem_ctx);
2305 info->primary_gid = centry_uint32(centry);
2306 centry_sid(centry, &info->user_sid);
2307 centry_sid(centry, &info->group_sid);
2310 DEBUG(10,("query_user: [Cached] - cached info for domain %s status: "
2311 "%s\n", domain->name, nt_errstr(status) ));
2313 centry_free(centry);
2317 /* Lookup user information from a rid */
2318 static NTSTATUS query_user(struct winbindd_domain *domain,
2319 TALLOC_CTX *mem_ctx,
2320 const struct dom_sid *user_sid,
2321 struct wbint_userinfo *info)
2326 old_status = domain->online;
2327 status = wcache_query_user(domain, mem_ctx, user_sid, info);
2328 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2334 /* Return status value returned by seq number check */
2336 if (!NT_STATUS_IS_OK(domain->last_status))
2337 return domain->last_status;
2339 DEBUG(10,("query_user: [Cached] - doing backend query for info for domain %s\n",
2342 status = domain->backend->query_user(domain, mem_ctx, user_sid, info);
2344 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2345 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2346 if (!domain->internal && old_status) {
2347 set_domain_offline(domain);
2349 if (!domain->internal &&
2352 NTSTATUS cache_status;
2353 cache_status = wcache_query_user(domain, mem_ctx, user_sid, info);
2354 return cache_status;
2358 refresh_sequence_number(domain, false);
2359 if (!NT_STATUS_IS_OK(status)) {
2362 wcache_save_user(domain, status, info);
2367 NTSTATUS wcache_lookup_usergroups(struct winbindd_domain *domain,
2368 TALLOC_CTX *mem_ctx,
2369 const struct dom_sid *user_sid,
2370 uint32_t *pnum_sids,
2371 struct dom_sid **psids)
2373 struct winbind_cache *cache = get_cache(domain);
2374 struct cache_entry *centry = NULL;
2376 uint32_t i, num_sids;
2377 struct dom_sid *sids;
2380 if (cache->tdb == NULL) {
2381 return NT_STATUS_NOT_FOUND;
2384 centry = wcache_fetch(cache, domain, "UG/%s",
2385 sid_to_fstring(sid_string, user_sid));
2386 if (centry == NULL) {
2387 return NT_STATUS_NOT_FOUND;
2390 /* If we have an access denied cache entry and a cached info3 in the
2391 samlogon cache then do a query. This will force the rpc back end
2392 to return the info3 data. */
2394 if (NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)
2395 && netsamlogon_cache_have(user_sid)) {
2396 DEBUG(10, ("lookup_usergroups: cached access denied and have "
2398 domain->last_status = NT_STATUS_OK;
2399 centry_free(centry);
2400 return NT_STATUS_NOT_FOUND;
2403 num_sids = centry_uint32(centry);
2404 sids = talloc_array(mem_ctx, struct dom_sid, num_sids);
2406 centry_free(centry);
2407 return NT_STATUS_NO_MEMORY;
2410 for (i=0; i<num_sids; i++) {
2411 centry_sid(centry, &sids[i]);
2414 status = centry->status;
2416 DEBUG(10,("lookup_usergroups: [Cached] - cached info for domain %s "
2417 "status: %s\n", domain->name, nt_errstr(status)));
2419 centry_free(centry);
2421 *pnum_sids = num_sids;
2426 /* Lookup groups a user is a member of. */
2427 static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
2428 TALLOC_CTX *mem_ctx,
2429 const struct dom_sid *user_sid,
2430 uint32 *num_groups, struct dom_sid **user_gids)
2432 struct cache_entry *centry = NULL;
2438 old_status = domain->online;
2439 status = wcache_lookup_usergroups(domain, mem_ctx, user_sid,
2440 num_groups, user_gids);
2441 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2446 (*user_gids) = NULL;
2448 /* Return status value returned by seq number check */
2450 if (!NT_STATUS_IS_OK(domain->last_status))
2451 return domain->last_status;
2453 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info for domain %s\n",
2456 status = domain->backend->lookup_usergroups(domain, mem_ctx, user_sid, num_groups, user_gids);
2458 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2459 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2460 if (!domain->internal && old_status) {
2461 set_domain_offline(domain);
2463 if (!domain->internal &&
2466 NTSTATUS cache_status;
2467 cache_status = wcache_lookup_usergroups(domain, mem_ctx, user_sid,
2468 num_groups, user_gids);
2469 return cache_status;
2472 if ( NT_STATUS_EQUAL(status, NT_STATUS_SYNCHRONIZATION_REQUIRED) )
2476 refresh_sequence_number(domain, false);
2477 if (!NT_STATUS_IS_OK(status)) {
2480 centry = centry_start(domain, status);
2484 centry_put_uint32(centry, *num_groups);
2485 for (i=0; i<(*num_groups); i++) {
2486 centry_put_sid(centry, &(*user_gids)[i]);
2489 centry_end(centry, "UG/%s", sid_to_fstring(sid_string, user_sid));
2490 centry_free(centry);
2496 static char *wcache_make_sidlist(TALLOC_CTX *mem_ctx, uint32_t num_sids,
2497 const struct dom_sid *sids)
2502 sidlist = talloc_strdup(mem_ctx, "");
2503 if (sidlist == NULL) {
2506 for (i=0; i<num_sids; i++) {
2508 sidlist = talloc_asprintf_append_buffer(
2509 sidlist, "/%s", sid_to_fstring(tmp, &sids[i]));
2510 if (sidlist == NULL) {
2517 NTSTATUS wcache_lookup_useraliases(struct winbindd_domain *domain,
2518 TALLOC_CTX *mem_ctx, uint32_t num_sids,
2519 const struct dom_sid *sids,
2520 uint32_t *pnum_aliases, uint32_t **paliases)
2522 struct winbind_cache *cache = get_cache(domain);
2523 struct cache_entry *centry = NULL;
2524 uint32_t num_aliases;
2530 if (cache->tdb == NULL) {
2531 return NT_STATUS_NOT_FOUND;
2534 if (num_sids == 0) {
2537 return NT_STATUS_OK;
2540 /* We need to cache indexed by the whole list of SIDs, the aliases
2541 * resulting might come from any of the SIDs. */
2543 sidlist = wcache_make_sidlist(talloc_tos(), num_sids, sids);
2544 if (sidlist == NULL) {
2545 return NT_STATUS_NO_MEMORY;
2548 centry = wcache_fetch(cache, domain, "UA%s", sidlist);
2549 TALLOC_FREE(sidlist);
2550 if (centry == NULL) {
2551 return NT_STATUS_NOT_FOUND;
2554 num_aliases = centry_uint32(centry);
2555 aliases = talloc_array(mem_ctx, uint32_t, num_aliases);
2556 if (aliases == NULL) {
2557 centry_free(centry);
2558 return NT_STATUS_NO_MEMORY;
2561 for (i=0; i<num_aliases; i++) {
2562 aliases[i] = centry_uint32(centry);
2565 status = centry->status;
2567 DEBUG(10,("lookup_useraliases: [Cached] - cached info for domain: %s "
2568 "status %s\n", domain->name, nt_errstr(status)));
2570 centry_free(centry);
2572 *pnum_aliases = num_aliases;
2573 *paliases = aliases;
2578 static NTSTATUS lookup_useraliases(struct winbindd_domain *domain,
2579 TALLOC_CTX *mem_ctx,
2580 uint32 num_sids, const struct dom_sid *sids,
2581 uint32 *num_aliases, uint32 **alias_rids)
2583 struct cache_entry *centry = NULL;
2589 old_status = domain->online;
2590 status = wcache_lookup_useraliases(domain, mem_ctx, num_sids, sids,
2591 num_aliases, alias_rids);
2592 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2597 (*alias_rids) = NULL;
2599 if (!NT_STATUS_IS_OK(domain->last_status))
2600 return domain->last_status;
2602 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info "
2603 "for domain %s\n", domain->name ));
2605 sidlist = wcache_make_sidlist(talloc_tos(), num_sids, sids);
2606 if (sidlist == NULL) {
2607 return NT_STATUS_NO_MEMORY;
2610 status = domain->backend->lookup_useraliases(domain, mem_ctx,
2612 num_aliases, alias_rids);
2614 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2615 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2616 if (!domain->internal && old_status) {
2617 set_domain_offline(domain);
2619 if (!domain->internal &&
2622 NTSTATUS cache_status;
2623 cache_status = wcache_lookup_useraliases(domain, mem_ctx, num_sids,
2624 sids, num_aliases, alias_rids);
2625 return cache_status;
2629 refresh_sequence_number(domain, false);
2630 if (!NT_STATUS_IS_OK(status)) {
2633 centry = centry_start(domain, status);
2636 centry_put_uint32(centry, *num_aliases);
2637 for (i=0; i<(*num_aliases); i++)
2638 centry_put_uint32(centry, (*alias_rids)[i]);
2639 centry_end(centry, "UA%s", sidlist);
2640 centry_free(centry);
2646 NTSTATUS wcache_lookup_groupmem(struct winbindd_domain *domain,
2647 TALLOC_CTX *mem_ctx,
2648 const struct dom_sid *group_sid,
2649 uint32_t *num_names,
2650 struct dom_sid **sid_mem, char ***names,
2651 uint32_t **name_types)
2653 struct winbind_cache *cache = get_cache(domain);
2654 struct cache_entry *centry = NULL;
2659 if (cache->tdb == NULL) {
2660 return NT_STATUS_NOT_FOUND;
2663 sid_string = sid_string_tos(group_sid);
2664 if (sid_string == NULL) {
2665 return NT_STATUS_NO_MEMORY;
2668 centry = wcache_fetch(cache, domain, "GM/%s", sid_string);
2669 TALLOC_FREE(sid_string);
2670 if (centry == NULL) {
2671 return NT_STATUS_NOT_FOUND;
2678 *num_names = centry_uint32(centry);
2679 if (*num_names == 0) {
2680 centry_free(centry);
2681 return NT_STATUS_OK;
2684 *sid_mem = talloc_array(mem_ctx, struct dom_sid, *num_names);
2685 *names = talloc_array(mem_ctx, char *, *num_names);
2686 *name_types = talloc_array(mem_ctx, uint32, *num_names);
2688 if ((*sid_mem == NULL) || (*names == NULL) || (*name_types == NULL)) {
2689 TALLOC_FREE(*sid_mem);
2690 TALLOC_FREE(*names);
2691 TALLOC_FREE(*name_types);
2692 centry_free(centry);
2693 return NT_STATUS_NO_MEMORY;
2696 for (i=0; i<(*num_names); i++) {
2697 centry_sid(centry, &(*sid_mem)[i]);
2698 (*names)[i] = centry_string(centry, mem_ctx);
2699 (*name_types)[i] = centry_uint32(centry);
2702 status = centry->status;
2704 DEBUG(10,("lookup_groupmem: [Cached] - cached info for domain %s "
2705 "status: %s\n", domain->name, nt_errstr(status)));
2707 centry_free(centry);
2711 static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
2712 TALLOC_CTX *mem_ctx,
2713 const struct dom_sid *group_sid,
2714 enum lsa_SidType type,
2716 struct dom_sid **sid_mem, char ***names,
2717 uint32 **name_types)
2719 struct cache_entry *centry = NULL;
2725 old_status = domain->online;
2726 status = wcache_lookup_groupmem(domain, mem_ctx, group_sid, num_names,
2727 sid_mem, names, name_types);
2728 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2735 (*name_types) = NULL;
2737 /* Return status value returned by seq number check */
2739 if (!NT_STATUS_IS_OK(domain->last_status))
2740 return domain->last_status;
2742 DEBUG(10,("lookup_groupmem: [Cached] - doing backend query for info for domain %s\n",
2745 status = domain->backend->lookup_groupmem(domain, mem_ctx, group_sid,
2747 sid_mem, names, name_types);
2749 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2750 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2751 if (!domain->internal && old_status) {
2752 set_domain_offline(domain);
2754 if (!domain->internal &&
2757 NTSTATUS cache_status;
2758 cache_status = wcache_lookup_groupmem(domain, mem_ctx, group_sid,
2759 num_names, sid_mem, names,
2761 return cache_status;
2765 refresh_sequence_number(domain, false);
2766 if (!NT_STATUS_IS_OK(status)) {
2769 centry = centry_start(domain, status);
2772 centry_put_uint32(centry, *num_names);
2773 for (i=0; i<(*num_names); i++) {
2774 centry_put_sid(centry, &(*sid_mem)[i]);
2775 centry_put_string(centry, (*names)[i]);
2776 centry_put_uint32(centry, (*name_types)[i]);
2778 centry_end(centry, "GM/%s", sid_to_fstring(sid_string, group_sid));
2779 centry_free(centry);
2785 /* find the sequence number for a domain */
2786 static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
2788 refresh_sequence_number(domain, false);
2790 *seq = domain->sequence_number;
2792 return NT_STATUS_OK;
2795 /* enumerate trusted domains
2796 * (we need to have the list of trustdoms in the cache when we go offline) -
2798 static NTSTATUS trusted_domains(struct winbindd_domain *domain,
2799 TALLOC_CTX *mem_ctx,
2800 struct netr_DomainTrustList *trusts)
2803 struct winbind_cache *cache;
2804 struct winbindd_tdc_domain *dom_list = NULL;
2805 size_t num_domains = 0;
2806 bool retval = false;
2810 old_status = domain->online;
2812 trusts->array = NULL;
2814 cache = get_cache(domain);
2815 if (!cache || !cache->tdb) {
2819 if (domain->online) {
2823 retval = wcache_tdc_fetch_list(&dom_list, &num_domains);
2824 if (!retval || !num_domains || !dom_list) {
2825 TALLOC_FREE(dom_list);
2830 trusts->array = talloc_zero_array(mem_ctx, struct netr_DomainTrust, num_domains);
2831 if (!trusts->array) {
2832 TALLOC_FREE(dom_list);
2833 return NT_STATUS_NO_MEMORY;
2836 for (i = 0; i < num_domains; i++) {
2837 struct netr_DomainTrust *trust;
2838 struct dom_sid *sid;
2839 struct winbindd_domain *dom;
2841 dom = find_domain_from_name_noinit(dom_list[i].domain_name);
2842 if (dom && dom->internal) {
2846 trust = &trusts->array[trusts->count];
2847 trust->netbios_name = talloc_strdup(trusts->array, dom_list[i].domain_name);
2848 trust->dns_name = talloc_strdup(trusts->array, dom_list[i].dns_name);
2849 sid = talloc(trusts->array, struct dom_sid);
2850 if (!trust->netbios_name || !trust->dns_name ||
2852 TALLOC_FREE(dom_list);
2853 TALLOC_FREE(trusts->array);
2854 return NT_STATUS_NO_MEMORY;
2857 trust->trust_flags = dom_list[i].trust_flags;
2858 trust->trust_attributes = dom_list[i].trust_attribs;
2859 trust->trust_type = dom_list[i].trust_type;
2860 sid_copy(sid, &dom_list[i].sid);
2865 TALLOC_FREE(dom_list);
2866 return NT_STATUS_OK;
2869 /* Return status value returned by seq number check */
2871 if (!NT_STATUS_IS_OK(domain->last_status))
2872 return domain->last_status;
2874 DEBUG(10,("trusted_domains: [Cached] - doing backend query for info for domain %s\n",
2877 status = domain->backend->trusted_domains(domain, mem_ctx, trusts);
2879 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2880 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2881 if (!domain->internal && old_status) {
2882 set_domain_offline(domain);
2884 if (!domain->internal &&
2887 retval = wcache_tdc_fetch_list(&dom_list, &num_domains);
2888 if (retval && num_domains && dom_list) {
2889 TALLOC_FREE(trusts->array);
2891 goto do_fetch_cache;
2895 /* no trusts gives NT_STATUS_NO_MORE_ENTRIES resetting to NT_STATUS_OK
2896 * so that the generic centry handling still applies correctly -
2899 if (!NT_STATUS_IS_ERR(status)) {
2900 status = NT_STATUS_OK;
2905 /* get lockout policy */
2906 static NTSTATUS lockout_policy(struct winbindd_domain *domain,
2907 TALLOC_CTX *mem_ctx,
2908 struct samr_DomInfo12 *policy)
2910 struct winbind_cache *cache = get_cache(domain);
2911 struct cache_entry *centry = NULL;
2915 old_status = domain->online;
2919 centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
2925 policy->lockout_duration = centry_nttime(centry);
2926 policy->lockout_window = centry_nttime(centry);
2927 policy->lockout_threshold = centry_uint16(centry);
2929 status = centry->status;
2931 DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2932 domain->name, nt_errstr(status) ));
2934 centry_free(centry);
2938 ZERO_STRUCTP(policy);
2940 /* Return status value returned by seq number check */
2942 if (!NT_STATUS_IS_OK(domain->last_status))
2943 return domain->last_status;
2945 DEBUG(10,("lockout_policy: [Cached] - doing backend query for info for domain %s\n",
2948 status = domain->backend->lockout_policy(domain, mem_ctx, policy);
2950 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2951 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2952 if (!domain->internal && old_status) {
2953 set_domain_offline(domain);
2956 !domain->internal &&
2959 centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
2961 goto do_fetch_cache;
2966 refresh_sequence_number(domain, false);
2967 if (!NT_STATUS_IS_OK(status)) {
2970 wcache_save_lockout_policy(domain, status, policy);
2975 /* get password policy */
2976 static NTSTATUS password_policy(struct winbindd_domain *domain,
2977 TALLOC_CTX *mem_ctx,
2978 struct samr_DomInfo1 *policy)
2980 struct winbind_cache *cache = get_cache(domain);
2981 struct cache_entry *centry = NULL;
2985 old_status = domain->online;
2989 centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
2995 policy->min_password_length = centry_uint16(centry);
2996 policy->password_history_length = centry_uint16(centry);
2997 policy->password_properties = centry_uint32(centry);
2998 policy->max_password_age = centry_nttime(centry);
2999 policy->min_password_age = centry_nttime(centry);
3001 status = centry->status;
3003 DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
3004 domain->name, nt_errstr(status) ));
3006 centry_free(centry);
3010 ZERO_STRUCTP(policy);
3012 /* Return status value returned by seq number check */
3014 if (!NT_STATUS_IS_OK(domain->last_status))
3015 return domain->last_status;
3017 DEBUG(10,("password_policy: [Cached] - doing backend query for info for domain %s\n",
3020 status = domain->backend->password_policy(domain, mem_ctx, policy);
3022 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
3023 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
3024 if (!domain->internal && old_status) {
3025 set_domain_offline(domain);
3028 !domain->internal &&
3031 centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
3033 goto do_fetch_cache;
3038 refresh_sequence_number(domain, false);
3039 if (!NT_STATUS_IS_OK(status)) {
3042 wcache_save_password_policy(domain, status, policy);
3048 /* Invalidate cached user and group lists coherently */
3050 static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
3053 if (strncmp((const char *)kbuf.dptr, "UL/", 3) == 0 ||
3054 strncmp((const char *)kbuf.dptr, "GL/", 3) == 0)
3055 tdb_delete(the_tdb, kbuf);
3060 /* Invalidate the getpwnam and getgroups entries for a winbindd domain */
3062 void wcache_invalidate_samlogon(struct winbindd_domain *domain,
3063 const struct dom_sid *sid)
3065 fstring key_str, sid_string;
3066 struct winbind_cache *cache;
3068 /* dont clear cached U/SID and UG/SID entries when we want to logon
3071 if (lp_winbind_offline_logon()) {
3078 cache = get_cache(domain);
3084 /* Clear U/SID cache entry */
3085 fstr_sprintf(key_str, "U/%s", sid_to_fstring(sid_string, sid));
3086 DEBUG(10, ("wcache_invalidate_samlogon: clearing %s\n", key_str));
3087 tdb_delete(cache->tdb, string_tdb_data(key_str));
3089 /* Clear UG/SID cache entry */
3090 fstr_sprintf(key_str, "UG/%s", sid_to_fstring(sid_string, sid));
3091 DEBUG(10, ("wcache_invalidate_samlogon: clearing %s\n", key_str));
3092 tdb_delete(cache->tdb, string_tdb_data(key_str));
3094 /* Samba/winbindd never needs this. */
3095 netsamlogon_clear_cached_user(sid);
3098 bool wcache_invalidate_cache(void)
3100 struct winbindd_domain *domain;
3102 for (domain = domain_list(); domain; domain = domain->next) {
3103 struct winbind_cache *cache = get_cache(domain);
3105 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
3106 "entries for %s\n", domain->name));
3109 tdb_traverse(cache->tdb, traverse_fn, NULL);
3118 bool wcache_invalidate_cache_noinit(void)
3120 struct winbindd_domain *domain;
3122 for (domain = domain_list(); domain; domain = domain->next) {
3123 struct winbind_cache *cache;
3125 /* Skip uninitialized domains. */
3126 if (!domain->initialized && !domain->internal) {
3130 cache = get_cache(domain);
3132 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
3133 "entries for %s\n", domain->name));
3136 tdb_traverse(cache->tdb, traverse_fn, NULL);
3138 * Flushing cache has nothing to with domains.
3139 * return here if we successfully flushed once.
3140 * To avoid unnecessary traversing the cache.
3151 bool init_wcache(void)
3153 if (wcache == NULL) {
3154 wcache = SMB_XMALLOC_P(struct winbind_cache);
3155 ZERO_STRUCTP(wcache);
3158 if (wcache->tdb != NULL)
3161 /* when working offline we must not clear the cache on restart */
3162 wcache->tdb = tdb_open_log(state_path("winbindd_cache.tdb"),
3163 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
3164 TDB_INCOMPATIBLE_HASH |
3165 (lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST)),
3166 O_RDWR|O_CREAT, 0600);
3168 if (wcache->tdb == NULL) {
3169 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
3176 /************************************************************************
3177 This is called by the parent to initialize the cache file.
3178 We don't need sophisticated locking here as we know we're the
3180 ************************************************************************/
3182 bool initialize_winbindd_cache(void)
3184 bool cache_bad = true;
3187 if (!init_wcache()) {
3188 DEBUG(0,("initialize_winbindd_cache: init_wcache failed.\n"));
3192 /* Check version number. */
3193 if (tdb_fetch_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, &vers) &&
3194 vers == WINBINDD_CACHE_VERSION) {
3199 DEBUG(0,("initialize_winbindd_cache: clearing cache "
3200 "and re-creating with version number %d\n",
3201 WINBINDD_CACHE_VERSION ));
3203 tdb_close(wcache->tdb);
3206 if (unlink(state_path("winbindd_cache.tdb")) == -1) {
3207 DEBUG(0,("initialize_winbindd_cache: unlink %s failed %s ",
3208 state_path("winbindd_cache.tdb"),
3212 if (!init_wcache()) {
3213 DEBUG(0,("initialize_winbindd_cache: re-initialization "
3214 "init_wcache failed.\n"));
3218 /* Write the version. */
3219 if (!tdb_store_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, WINBINDD_CACHE_VERSION)) {
3220 DEBUG(0,("initialize_winbindd_cache: version number store failed %s\n",
3221 tdb_errorstr_compat(wcache->tdb) ));
3226 tdb_close(wcache->tdb);
3231 void close_winbindd_cache(void)
3237 tdb_close(wcache->tdb);
3242 bool lookup_cached_sid(TALLOC_CTX *mem_ctx, const struct dom_sid *sid,
3243 char **domain_name, char **name,
3244 enum lsa_SidType *type)
3246 struct winbindd_domain *domain;
3249 domain = find_lookup_domain_from_sid(sid);
3250 if (domain == NULL) {
3253 status = wcache_sid_to_name(domain, sid, mem_ctx, domain_name, name,
3255 return NT_STATUS_IS_OK(status);
3258 bool lookup_cached_name(const char *domain_name,
3260 struct dom_sid *sid,
3261 enum lsa_SidType *type)
3263 struct winbindd_domain *domain;
3265 bool original_online_state;
3267 domain = find_lookup_domain_from_name(domain_name);
3268 if (domain == NULL) {
3272 /* If we are doing a cached logon, temporarily set the domain
3273 offline so the cache won't expire the entry */
3275 original_online_state = domain->online;
3276 domain->online = false;
3277 status = wcache_name_to_sid(domain, domain_name, name, sid, type);
3278 domain->online = original_online_state;
3280 return NT_STATUS_IS_OK(status);
3283 void cache_name2sid(struct winbindd_domain *domain,
3284 const char *domain_name, const char *name,
3285 enum lsa_SidType type, const struct dom_sid *sid)
3287 refresh_sequence_number(domain, false);
3288 wcache_save_name_to_sid(domain, NT_STATUS_OK, domain_name, name,
3293 * The original idea that this cache only contains centries has
3294 * been blurred - now other stuff gets put in here. Ensure we
3295 * ignore these things on cleanup.
3298 static int traverse_fn_cleanup(TDB_CONTEXT *the_tdb, TDB_DATA kbuf,
3299 TDB_DATA dbuf, void *state)
3301 struct cache_entry *centry;
3303 if (is_non_centry_key(kbuf)) {
3307 centry = wcache_fetch_raw((char *)kbuf.dptr);
3312 if (!NT_STATUS_IS_OK(centry->status)) {
3313 DEBUG(10,("deleting centry %s\n", (const char *)kbuf.dptr));
3314 tdb_delete(the_tdb, kbuf);
3317 centry_free(centry);
3321 /* flush the cache */
3322 void wcache_flush_cache(void)
3327 tdb_close(wcache->tdb);
3330 if (!winbindd_use_cache()) {
3334 /* when working offline we must not clear the cache on restart */
3335 wcache->tdb = tdb_open_log(state_path("winbindd_cache.tdb"),
3336 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
3337 TDB_INCOMPATIBLE_HASH |
3338 (lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST)),
3339 O_RDWR|O_CREAT, 0600);
3342 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
3346 tdb_traverse(wcache->tdb, traverse_fn_cleanup, NULL);
3348 DEBUG(10,("wcache_flush_cache success\n"));
3351 /* Count cached creds */
3353 static int traverse_fn_cached_creds(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
3356 int *cred_count = (int*)state;
3358 if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
3364 NTSTATUS wcache_count_cached_creds(struct winbindd_domain *domain, int *count)
3366 struct winbind_cache *cache = get_cache(domain);
3371 return NT_STATUS_INTERNAL_DB_ERROR;
3374 tdb_traverse(cache->tdb, traverse_fn_cached_creds, (void *)count);
3376 return NT_STATUS_OK;
3380 struct cred_list *prev, *next;
3385 static struct cred_list *wcache_cred_list;
3387 static int traverse_fn_get_credlist(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
3390 struct cred_list *cred;
3392 if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
3394 cred = SMB_MALLOC_P(struct cred_list);
3396 DEBUG(0,("traverse_fn_remove_first_creds: failed to malloc new entry for list\n"));
3402 /* save a copy of the key */
3404 fstrcpy(cred->name, (const char *)kbuf.dptr);
3405 DLIST_ADD(wcache_cred_list, cred);
3411 NTSTATUS wcache_remove_oldest_cached_creds(struct winbindd_domain *domain, const struct dom_sid *sid)
3413 struct winbind_cache *cache = get_cache(domain);
3416 struct cred_list *cred, *oldest = NULL;
3419 return NT_STATUS_INTERNAL_DB_ERROR;
3422 /* we possibly already have an entry */
3423 if (sid && NT_STATUS_IS_OK(wcache_cached_creds_exist(domain, sid))) {
3425 fstring key_str, tmp;
3427 DEBUG(11,("we already have an entry, deleting that\n"));
3429 fstr_sprintf(key_str, "CRED/%s", sid_to_fstring(tmp, sid));
3431 tdb_delete(cache->tdb, string_tdb_data(key_str));
3433 return NT_STATUS_OK;
3436 ret = tdb_traverse(cache->tdb, traverse_fn_get_credlist, NULL);
3438 return NT_STATUS_OK;
3439 } else if ((ret < 0) || (wcache_cred_list == NULL)) {
3440 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
3443 ZERO_STRUCTP(oldest);
3445 for (cred = wcache_cred_list; cred; cred = cred->next) {
3450 data = tdb_fetch_compat(cache->tdb, string_tdb_data(cred->name));
3452 DEBUG(10,("wcache_remove_oldest_cached_creds: entry for [%s] not found\n",
3454 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
3458 t = IVAL(data.dptr, 0);
3459 SAFE_FREE(data.dptr);
3462 oldest = SMB_MALLOC_P(struct cred_list);
3463 if (oldest == NULL) {
3464 status = NT_STATUS_NO_MEMORY;
3468 fstrcpy(oldest->name, cred->name);
3469 oldest->created = t;
3473 if (t < oldest->created) {
3474 fstrcpy(oldest->name, cred->name);
3475 oldest->created = t;
3479 if (tdb_delete(cache->tdb, string_tdb_data(oldest->name)) == 0) {
3480 status = NT_STATUS_OK;
3482 status = NT_STATUS_UNSUCCESSFUL;
3485 SAFE_FREE(wcache_cred_list);
3491 /* Change the global online/offline state. */
3492 bool set_global_winbindd_state_offline(void)
3496 DEBUG(10,("set_global_winbindd_state_offline: offline requested.\n"));
3498 /* Only go offline if someone has created
3499 the key "WINBINDD_OFFLINE" in the cache tdb. */
3501 if (wcache == NULL || wcache->tdb == NULL) {
3502 DEBUG(10,("set_global_winbindd_state_offline: wcache not open yet.\n"));
3506 if (!lp_winbind_offline_logon()) {
3507 DEBUG(10,("set_global_winbindd_state_offline: rejecting.\n"));
3511 if (global_winbindd_offline_state) {
3512 /* Already offline. */
3516 data = tdb_fetch_bystring( wcache->tdb, "WINBINDD_OFFLINE" );
3518 if (!data.dptr || data.dsize != 4) {
3519 DEBUG(10,("set_global_winbindd_state_offline: offline state not set.\n"));
3520 SAFE_FREE(data.dptr);
3523 DEBUG(10,("set_global_winbindd_state_offline: offline state set.\n"));
3524 global_winbindd_offline_state = true;
3525 SAFE_FREE(data.dptr);
3530 void set_global_winbindd_state_online(void)
3532 DEBUG(10,("set_global_winbindd_state_online: online requested.\n"));
3534 if (!lp_winbind_offline_logon()) {
3535 DEBUG(10,("set_global_winbindd_state_online: rejecting.\n"));
3539 if (!global_winbindd_offline_state) {
3540 /* Already online. */
3543 global_winbindd_offline_state = false;
3549 /* Ensure there is no key "WINBINDD_OFFLINE" in the cache tdb. */
3550 tdb_delete_bystring(wcache->tdb, "WINBINDD_OFFLINE");
3553 bool get_global_winbindd_state_offline(void)
3555 return global_winbindd_offline_state;
3558 /***********************************************************************
3559 Validate functions for all possible cache tdb keys.
3560 ***********************************************************************/
3562 static struct cache_entry *create_centry_validate(const char *kstr, TDB_DATA data,
3563 struct tdb_validation_status *state)
3565 struct cache_entry *centry;
3567 centry = SMB_XMALLOC_P(struct cache_entry);
3568 centry->data = (unsigned char *)smb_memdup(data.dptr, data.dsize);
3569 if (!centry->data) {
3573 centry->len = data.dsize;
3576 if (centry->len < 16) {
3577 /* huh? corrupt cache? */
3578 DEBUG(0,("create_centry_validate: Corrupt cache for key %s "
3579 "(len < 16) ?\n", kstr));
3580 centry_free(centry);
3581 state->bad_entry = true;
3582 state->success = false;
3586 centry->status = NT_STATUS(centry_uint32(centry));
3587 centry->sequence_number = centry_uint32(centry);
3588 centry->timeout = centry_uint64_t(centry);
3592 static int validate_seqnum(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3593 struct tdb_validation_status *state)
3595 if (dbuf.dsize != 8) {
3596 DEBUG(0,("validate_seqnum: Corrupt cache for key %s (len %u != 8) ?\n",
3597 keystr, (unsigned int)dbuf.dsize ));
3598 state->bad_entry = true;
3604 static int validate_ns(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3605 struct tdb_validation_status *state)
3607 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3612 (void)centry_uint32(centry);
3613 if (NT_STATUS_IS_OK(centry->status)) {
3615 (void)centry_sid(centry, &sid);
3618 centry_free(centry);
3620 if (!(state->success)) {
3623 DEBUG(10,("validate_ns: %s ok\n", keystr));
3627 static int validate_sn(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3628 struct tdb_validation_status *state)
3630 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3635 if (NT_STATUS_IS_OK(centry->status)) {
3636 (void)centry_uint32(centry);
3637 (void)centry_string(centry, mem_ctx);
3638 (void)centry_string(centry, mem_ctx);
3641 centry_free(centry);
3643 if (!(state->success)) {
3646 DEBUG(10,("validate_sn: %s ok\n", keystr));
3650 static int validate_u(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3651 struct tdb_validation_status *state)
3653 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3660 (void)centry_string(centry, mem_ctx);
3661 (void)centry_string(centry, mem_ctx);
3662 (void)centry_string(centry, mem_ctx);
3663 (void)centry_string(centry, mem_ctx);
3664 (void)centry_uint32(centry);
3665 (void)centry_sid(centry, &sid);
3666 (void)centry_sid(centry, &sid);
3668 centry_free(centry);
3670 if (!(state->success)) {
3673 DEBUG(10,("validate_u: %s ok\n", keystr));
3677 static int validate_loc_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3678 struct tdb_validation_status *state)
3680 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3686 (void)centry_nttime(centry);
3687 (void)centry_nttime(centry);
3688 (void)centry_uint16(centry);
3690 centry_free(centry);
3692 if (!(state->success)) {
3695 DEBUG(10,("validate_loc_pol: %s ok\n", keystr));
3699 static int validate_pwd_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3700 struct tdb_validation_status *state)
3702 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3708 (void)centry_uint16(centry);
3709 (void)centry_uint16(centry);
3710 (void)centry_uint32(centry);
3711 (void)centry_nttime(centry);
3712 (void)centry_nttime(centry);
3714 centry_free(centry);
3716 if (!(state->success)) {
3719 DEBUG(10,("validate_pwd_pol: %s ok\n", keystr));
3723 static int validate_cred(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3724 struct tdb_validation_status *state)
3726 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3732 (void)centry_time(centry);
3733 (void)centry_hash16(centry, mem_ctx);
3735 /* We only have 17 bytes more data in the salted cred case. */
3736 if (centry->len - centry->ofs == 17) {
3737 (void)centry_hash16(centry, mem_ctx);
3740 centry_free(centry);
3742 if (!(state->success)) {
3745 DEBUG(10,("validate_cred: %s ok\n", keystr));
3749 static int validate_ul(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3750 struct tdb_validation_status *state)
3752 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3753 int32 num_entries, i;
3759 num_entries = (int32)centry_uint32(centry);
3761 for (i=0; i< num_entries; i++) {
3763 (void)centry_string(centry, mem_ctx);
3764 (void)centry_string(centry, mem_ctx);
3765 (void)centry_string(centry, mem_ctx);
3766 (void)centry_string(centry, mem_ctx);
3767 (void)centry_sid(centry, &sid);
3768 (void)centry_sid(centry, &sid);
3771 centry_free(centry);
3773 if (!(state->success)) {
3776 DEBUG(10,("validate_ul: %s ok\n", keystr));
3780 static int validate_gl(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3781 struct tdb_validation_status *state)
3783 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3784 int32 num_entries, i;
3790 num_entries = centry_uint32(centry);
3792 for (i=0; i< num_entries; i++) {
3793 (void)centry_string(centry, mem_ctx);
3794 (void)centry_string(centry, mem_ctx);
3795 (void)centry_uint32(centry);
3798 centry_free(centry);
3800 if (!(state->success)) {
3803 DEBUG(10,("validate_gl: %s ok\n", keystr));
3807 static int validate_ug(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3808 struct tdb_validation_status *state)
3810 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3811 int32 num_groups, i;
3817 num_groups = centry_uint32(centry);
3819 for (i=0; i< num_groups; i++) {
3821 centry_sid(centry, &sid);
3824 centry_free(centry);
3826 if (!(state->success)) {
3829 DEBUG(10,("validate_ug: %s ok\n", keystr));
3833 static int validate_ua(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3834 struct tdb_validation_status *state)
3836 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3837 int32 num_aliases, i;
3843 num_aliases = centry_uint32(centry);
3845 for (i=0; i < num_aliases; i++) {
3846 (void)centry_uint32(centry);
3849 centry_free(centry);
3851 if (!(state->success)) {
3854 DEBUG(10,("validate_ua: %s ok\n", keystr));
3858 static int validate_gm(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3859 struct tdb_validation_status *state)
3861 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3868 num_names = centry_uint32(centry);
3870 for (i=0; i< num_names; i++) {
3872 centry_sid(centry, &sid);
3873 (void)centry_string(centry, mem_ctx);
3874 (void)centry_uint32(centry);
3877 centry_free(centry);
3879 if (!(state->success)) {
3882 DEBUG(10,("validate_gm: %s ok\n", keystr));
3886 static int validate_dr(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3887 struct tdb_validation_status *state)
3889 /* Can't say anything about this other than must be nonzero. */
3890 if (dbuf.dsize == 0) {
3891 DEBUG(0,("validate_dr: Corrupt cache for key %s (len == 0) ?\n",
3893 state->bad_entry = true;
3894 state->success = false;
3898 DEBUG(10,("validate_dr: %s ok\n", keystr));
3902 static int validate_de(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3903 struct tdb_validation_status *state)
3905 /* Can't say anything about this other than must be nonzero. */
3906 if (dbuf.dsize == 0) {
3907 DEBUG(0,("validate_de: Corrupt cache for key %s (len == 0) ?\n",
3909 state->bad_entry = true;
3910 state->success = false;
3914 DEBUG(10,("validate_de: %s ok\n", keystr));
3918 static int validate_pwinfo(TALLOC_CTX *mem_ctx, const char *keystr,
3919 TDB_DATA dbuf, struct tdb_validation_status *state)
3921 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3927 (void)centry_string(centry, mem_ctx);
3928 (void)centry_string(centry, mem_ctx);
3929 (void)centry_string(centry, mem_ctx);
3930 (void)centry_uint32(centry);
3932 centry_free(centry);
3934 if (!(state->success)) {
3937 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3941 static int validate_nss_an(TALLOC_CTX *mem_ctx, const char *keystr,
3943 struct tdb_validation_status *state)
3945 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3951 (void)centry_string( centry, mem_ctx );
3953 centry_free(centry);
3955 if (!(state->success)) {
3958 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3962 static int validate_nss_na(TALLOC_CTX *mem_ctx, const char *keystr,
3964 struct tdb_validation_status *state)
3966 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3972 (void)centry_string( centry, mem_ctx );
3974 centry_free(centry);
3976 if (!(state->success)) {
3979 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3983 static int validate_trustdomcache(TALLOC_CTX *mem_ctx, const char *keystr,
3985 struct tdb_validation_status *state)
3987 if (dbuf.dsize == 0) {
3988 DEBUG(0, ("validate_trustdomcache: Corrupt cache for "
3989 "key %s (len ==0) ?\n", keystr));
3990 state->bad_entry = true;
3991 state->success = false;
3995 DEBUG(10, ("validate_trustdomcache: %s ok\n", keystr));
3996 DEBUGADD(10, (" Don't trust me, I am a DUMMY!\n"));
4000 static int validate_offline(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
4001 struct tdb_validation_status *state)
4003 if (dbuf.dsize != 4) {
4004 DEBUG(0,("validate_offline: Corrupt cache for key %s (len %u != 4) ?\n",
4005 keystr, (unsigned int)dbuf.dsize ));
4006 state->bad_entry = true;
4007 state->success = false;
4010 DEBUG(10,("validate_offline: %s ok\n", keystr));
4014 static int validate_ndr(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
4015 struct tdb_validation_status *state)
4018 * Ignore validation for now. The proper way to do this is with a
4019 * checksum. Just pure parsing does not really catch much.
4024 static int validate_cache_version(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
4025 struct tdb_validation_status *state)
4027 if (dbuf.dsize != 4) {
4028 DEBUG(0, ("validate_cache_version: Corrupt cache for "
4029 "key %s (len %u != 4) ?\n",
4030 keystr, (unsigned int)dbuf.dsize));
4031 state->bad_entry = true;
4032 state->success = false;
4036 DEBUG(10, ("validate_cache_version: %s ok\n", keystr));
4040 /***********************************************************************
4041 A list of all possible cache tdb keys with associated validation
4043 ***********************************************************************/
4045 struct key_val_struct {
4046 const char *keyname;
4047 int (*validate_data_fn)(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, struct tdb_validation_status* state);
4049 {"SEQNUM/", validate_seqnum},
4050 {"NS/", validate_ns},
4051 {"SN/", validate_sn},
4053 {"LOC_POL/", validate_loc_pol},
4054 {"PWD_POL/", validate_pwd_pol},
4055 {"CRED/", validate_cred},
4056 {"UL/", validate_ul},
4057 {"GL/", validate_gl},
4058 {"UG/", validate_ug},
4059 {"UA", validate_ua},
4060 {"GM/", validate_gm},
4061 {"DR/", validate_dr},
4062 {"DE/", validate_de},
4063 {"NSS/PWINFO/", validate_pwinfo},
4064 {"TRUSTDOMCACHE/", validate_trustdomcache},
4065 {"NSS/NA/", validate_nss_na},
4066 {"NSS/AN/", validate_nss_an},
4067 {"WINBINDD_OFFLINE", validate_offline},
4068 {"NDR/", validate_ndr},
4069 {WINBINDD_CACHE_VERSION_KEYSTR, validate_cache_version},
4073 /***********************************************************************
4074 Function to look at every entry in the tdb and validate it as far as
4076 ***********************************************************************/
4078 static int cache_traverse_validate_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, void *state)
4081 unsigned int max_key_len = 1024;
4082 struct tdb_validation_status *v_state = (struct tdb_validation_status *)state;
4084 /* Paranoia check. */
4085 if (strncmp("UA/", (const char *)kbuf.dptr, 3) == 0 ||
4086 strncmp("NDR/", (const char *)kbuf.dptr, 4) == 0) {
4087 max_key_len = 1024 * 1024;
4089 if (kbuf.dsize > max_key_len) {
4090 DEBUG(0, ("cache_traverse_validate_fn: key length too large: "
4092 (unsigned int)kbuf.dsize, (unsigned int)max_key_len));
4096 for (i = 0; key_val[i].keyname; i++) {
4097 size_t namelen = strlen(key_val[i].keyname);
4098 if (kbuf.dsize >= namelen && (
4099 strncmp(key_val[i].keyname, (const char *)kbuf.dptr, namelen)) == 0) {
4100 TALLOC_CTX *mem_ctx;
4104 keystr = SMB_MALLOC_ARRAY(char, kbuf.dsize+1);
4108 memcpy(keystr, kbuf.dptr, kbuf.dsize);
4109 keystr[kbuf.dsize] = '\0';
4111 mem_ctx = talloc_init("validate_ctx");
4117 ret = key_val[i].validate_data_fn(mem_ctx, keystr, dbuf,
4121 talloc_destroy(mem_ctx);
4126 DEBUG(0,("cache_traverse_validate_fn: unknown cache entry\nkey :\n"));
4127 dump_data(0, (uint8 *)kbuf.dptr, kbuf.dsize);
4128 DEBUG(0,("data :\n"));
4129 dump_data(0, (uint8 *)dbuf.dptr, dbuf.dsize);
4130 v_state->unknown_key = true;
4131 v_state->success = false;
4132 return 1; /* terminate. */
4135 static void validate_panic(const char *const why)
4137 DEBUG(0,("validating cache: would panic %s\n", why ));
4138 DEBUGADD(0, ("exiting instead (cache validation mode)\n"));
4142 static int wbcache_update_centry_fn(TDB_CONTEXT *tdb,
4150 if (is_non_centry_key(key)) {
4154 if (data.dptr == NULL || data.dsize == 0) {
4155 if (tdb_delete(tdb, key) < 0) {
4156 DEBUG(0, ("tdb_delete for [%s] failed!\n",
4162 /* add timeout to blob (uint64_t) */
4163 blob.dsize = data.dsize + 8;
4165 blob.dptr = SMB_XMALLOC_ARRAY(uint8_t, blob.dsize);
4166 if (blob.dptr == NULL) {
4169 memset(blob.dptr, 0, blob.dsize);
4171 /* copy status and seqnum */
4172 memcpy(blob.dptr, data.dptr, 8);
4175 ctimeout = lp_winbind_cache_time() + time(NULL);
4176 SBVAL(blob.dptr, 8, ctimeout);
4179 memcpy(blob.dptr + 16, data.dptr + 8, data.dsize - 8);
4181 if (tdb_store(tdb, key, blob, TDB_REPLACE) < 0) {
4182 DEBUG(0, ("tdb_store to update [%s] failed!\n",
4184 SAFE_FREE(blob.dptr);
4188 SAFE_FREE(blob.dptr);
4192 static bool wbcache_upgrade_v1_to_v2(TDB_CONTEXT *tdb)
4196 DEBUG(1, ("Upgrade to version 2 of the winbindd_cache.tdb\n"));
4198 rc = tdb_traverse(tdb, wbcache_update_centry_fn, NULL);
4206 /***********************************************************************
4207 Try and validate every entry in the winbindd cache. If we fail here,
4208 delete the cache tdb and return non-zero.
4209 ***********************************************************************/
4211 int winbindd_validate_cache(void)
4214 const char *tdb_path = state_path("winbindd_cache.tdb");
4215 TDB_CONTEXT *tdb = NULL;
4219 DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
4220 smb_panic_fn = validate_panic;
4222 tdb = tdb_open_log(tdb_path,
4223 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
4224 TDB_INCOMPATIBLE_HASH |
4225 ( lp_winbind_offline_logon()
4227 : TDB_DEFAULT | TDB_CLEAR_IF_FIRST ),
4231 DEBUG(0, ("winbindd_validate_cache: "
4232 "error opening/initializing tdb\n"));
4236 /* Version check and upgrade code. */
4237 if (!tdb_fetch_uint32(tdb, WINBINDD_CACHE_VERSION_KEYSTR, &vers_id)) {
4238 DEBUG(10, ("Fresh database\n"));
4239 tdb_store_uint32(tdb, WINBINDD_CACHE_VERSION_KEYSTR, WINBINDD_CACHE_VERSION);
4240 vers_id = WINBINDD_CACHE_VERSION;
4243 if (vers_id != WINBINDD_CACHE_VERSION) {
4244 if (vers_id == WINBINDD_CACHE_VER1) {
4245 ok = wbcache_upgrade_v1_to_v2(tdb);
4247 DEBUG(10, ("winbindd_validate_cache: upgrade to version 2 failed.\n"));
4252 tdb_store_uint32(tdb,
4253 WINBINDD_CACHE_VERSION_KEYSTR,
4254 WINBINDD_CACHE_VERSION);
4255 vers_id = WINBINDD_CACHE_VER2;
4261 ret = tdb_validate_and_backup(tdb_path, cache_traverse_validate_fn);
4264 DEBUG(10, ("winbindd_validate_cache: validation not successful.\n"));
4265 DEBUGADD(10, ("removing tdb %s.\n", tdb_path));
4270 DEBUG(10, ("winbindd_validate_cache: restoring panic function\n"));
4271 smb_panic_fn = smb_panic;
4275 /***********************************************************************
4276 Try and validate every entry in the winbindd cache.
4277 ***********************************************************************/
4279 int winbindd_validate_cache_nobackup(void)
4282 const char *tdb_path = state_path("winbindd_cache.tdb");
4284 DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
4285 smb_panic_fn = validate_panic;
4288 if (wcache == NULL || wcache->tdb == NULL) {
4289 ret = tdb_validate_open(tdb_path, cache_traverse_validate_fn);
4291 ret = tdb_validate(wcache->tdb, cache_traverse_validate_fn);
4295 DEBUG(10, ("winbindd_validate_cache_nobackup: validation not "
4299 DEBUG(10, ("winbindd_validate_cache_nobackup: restoring panic "
4301 smb_panic_fn = smb_panic;
4305 bool winbindd_cache_validate_and_initialize(void)
4307 close_winbindd_cache();
4309 if (lp_winbind_offline_logon()) {
4310 if (winbindd_validate_cache() < 0) {
4311 DEBUG(0, ("winbindd cache tdb corrupt and no backup "
4312 "could be restored.\n"));
4316 return initialize_winbindd_cache();
4319 /*********************************************************************
4320 ********************************************************************/
4322 static bool add_wbdomain_to_tdc_array( struct winbindd_domain *new_dom,
4323 struct winbindd_tdc_domain **domains,
4324 size_t *num_domains )
4326 struct winbindd_tdc_domain *list = NULL;
4329 bool set_only = false;
4331 /* don't allow duplicates */
4336 for ( i=0; i< (*num_domains); i++ ) {
4337 if ( strequal( new_dom->name, list[i].domain_name ) ) {
4338 DEBUG(10,("add_wbdomain_to_tdc_array: Found existing record for %s\n",
4349 list = talloc_array( NULL, struct winbindd_tdc_domain, 1 );
4352 list = talloc_realloc( *domains, *domains,
4353 struct winbindd_tdc_domain,
4358 ZERO_STRUCT( list[idx] );
4364 list[idx].domain_name = talloc_strdup(list, new_dom->name);
4365 if (list[idx].domain_name == NULL) {
4368 if (new_dom->alt_name != NULL) {
4369 list[idx].dns_name = talloc_strdup(list, new_dom->alt_name);
4370 if (list[idx].dns_name == NULL) {
4375 if ( !is_null_sid( &new_dom->sid ) ) {
4376 sid_copy( &list[idx].sid, &new_dom->sid );
4378 sid_copy(&list[idx].sid, &global_sid_NULL);
4381 if ( new_dom->domain_flags != 0x0 )
4382 list[idx].trust_flags = new_dom->domain_flags;
4384 if ( new_dom->domain_type != 0x0 )
4385 list[idx].trust_type = new_dom->domain_type;
4387 if ( new_dom->domain_trust_attribs != 0x0 )
4388 list[idx].trust_attribs = new_dom->domain_trust_attribs;
4392 *num_domains = idx + 1;
4398 /*********************************************************************
4399 ********************************************************************/
4401 static TDB_DATA make_tdc_key( const char *domain_name )
4403 char *keystr = NULL;
4404 TDB_DATA key = { NULL, 0 };
4406 if ( !domain_name ) {
4407 DEBUG(5,("make_tdc_key: Keyname workgroup is NULL!\n"));
4411 if (asprintf( &keystr, "TRUSTDOMCACHE/%s", domain_name ) == -1) {
4414 key = string_term_tdb_data(keystr);
4419 /*********************************************************************
4420 ********************************************************************/
4422 static int pack_tdc_domains( struct winbindd_tdc_domain *domains,
4424 unsigned char **buf )
4426 unsigned char *buffer = NULL;
4431 DEBUG(10,("pack_tdc_domains: Packing %d trusted domains\n",
4439 /* Store the number of array items first */
4440 len += tdb_pack( buffer+len, buflen-len, "d",
4443 /* now pack each domain trust record */
4444 for ( i=0; i<num_domains; i++ ) {
4449 DEBUG(10,("pack_tdc_domains: Packing domain %s (%s)\n",
4450 domains[i].domain_name,
4451 domains[i].dns_name ? domains[i].dns_name : "UNKNOWN" ));
4454 len += tdb_pack( buffer+len, buflen-len, "fffddd",
4455 domains[i].domain_name,
4456 domains[i].dns_name ? domains[i].dns_name : "",
4457 sid_to_fstring(tmp, &domains[i].sid),
4458 domains[i].trust_flags,
4459 domains[i].trust_attribs,
4460 domains[i].trust_type );
4463 if ( buflen < len ) {
4465 if ( (buffer = SMB_MALLOC_ARRAY(unsigned char, len)) == NULL ) {
4466 DEBUG(0,("pack_tdc_domains: failed to alloc buffer!\n"));
4480 /*********************************************************************
4481 ********************************************************************/
4483 static size_t unpack_tdc_domains( unsigned char *buf, int buflen,
4484 struct winbindd_tdc_domain **domains )
4486 fstring domain_name, dns_name, sid_string;
4487 uint32 type, attribs, flags;
4491 struct winbindd_tdc_domain *list = NULL;
4493 /* get the number of domains */
4494 len += tdb_unpack( buf+len, buflen-len, "d", &num_domains);
4496 DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
4500 list = talloc_array( NULL, struct winbindd_tdc_domain, num_domains );
4502 DEBUG(0,("unpack_tdc_domains: Failed to talloc() domain list!\n"));
4506 for ( i=0; i<num_domains; i++ ) {
4509 this_len = tdb_unpack( buf+len, buflen-len, "fffddd",
4517 if ( this_len == -1 ) {
4518 DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
4519 TALLOC_FREE( list );
4524 DEBUG(11,("unpack_tdc_domains: Unpacking domain %s (%s) "
4525 "SID %s, flags = 0x%x, attribs = 0x%x, type = 0x%x\n",
4526 domain_name, dns_name, sid_string,
4527 flags, attribs, type));
4529 list[i].domain_name = talloc_strdup( list, domain_name );
4530 list[i].dns_name = NULL;
4531 if (dns_name[0] != '\0') {
4532 list[i].dns_name = talloc_strdup(list, dns_name);
4534 if ( !string_to_sid( &(list[i].sid), sid_string ) ) {
4535 DEBUG(10,("unpack_tdc_domains: no SID for domain %s\n",
4538 list[i].trust_flags = flags;
4539 list[i].trust_attribs = attribs;
4540 list[i].trust_type = type;
4548 /*********************************************************************
4549 ********************************************************************/
4551 static bool wcache_tdc_store_list( struct winbindd_tdc_domain *domains, size_t num_domains )
4553 TDB_DATA key = make_tdc_key( lp_workgroup() );
4554 TDB_DATA data = { NULL, 0 };
4560 /* See if we were asked to delete the cache entry */
4563 ret = tdb_delete( wcache->tdb, key );
4567 data.dsize = pack_tdc_domains( domains, num_domains, &data.dptr );
4574 ret = tdb_store( wcache->tdb, key, data, 0 );
4577 SAFE_FREE( data.dptr );
4578 SAFE_FREE( key.dptr );
4580 return ( ret == 0 );
4583 /*********************************************************************
4584 ********************************************************************/
4586 bool wcache_tdc_fetch_list( struct winbindd_tdc_domain **domains, size_t *num_domains )
4588 TDB_DATA key = make_tdc_key( lp_workgroup() );
4589 TDB_DATA data = { NULL, 0 };
4597 data = tdb_fetch_compat( wcache->tdb, key );
4599 SAFE_FREE( key.dptr );
4604 *num_domains = unpack_tdc_domains( data.dptr, data.dsize, domains );
4606 SAFE_FREE( data.dptr );
4614 /*********************************************************************
4615 ********************************************************************/
4617 bool wcache_tdc_add_domain( struct winbindd_domain *domain )
4619 struct winbindd_tdc_domain *dom_list = NULL;
4620 size_t num_domains = 0;
4623 DEBUG(10,("wcache_tdc_add_domain: Adding domain %s (%s), SID %s, "
4624 "flags = 0x%x, attributes = 0x%x, type = 0x%x\n",
4625 domain->name, domain->alt_name,
4626 sid_string_dbg(&domain->sid),
4627 domain->domain_flags,
4628 domain->domain_trust_attribs,
4629 domain->domain_type));
4631 if ( !init_wcache() ) {
4635 /* fetch the list */
4637 wcache_tdc_fetch_list( &dom_list, &num_domains );
4639 /* add the new domain */
4641 if ( !add_wbdomain_to_tdc_array( domain, &dom_list, &num_domains ) ) {
4645 /* pack the domain */
4647 if ( !wcache_tdc_store_list( dom_list, num_domains ) ) {
4655 TALLOC_FREE( dom_list );
4660 static struct winbindd_tdc_domain *wcache_tdc_dup_domain(
4661 TALLOC_CTX *mem_ctx, const struct winbindd_tdc_domain *src)
4663 struct winbindd_tdc_domain *dst;
4665 dst = talloc(mem_ctx, struct winbindd_tdc_domain);
4669 dst->domain_name = talloc_strdup(dst, src->domain_name);
4670 if (dst->domain_name == NULL) {
4674 dst->dns_name = NULL;
4675 if (src->dns_name != NULL) {
4676 dst->dns_name = talloc_strdup(dst, src->dns_name);
4677 if (dst->dns_name == NULL) {
4682 sid_copy(&dst->sid, &src->sid);
4683 dst->trust_flags = src->trust_flags;
4684 dst->trust_type = src->trust_type;
4685 dst->trust_attribs = src->trust_attribs;
4692 /*********************************************************************
4693 ********************************************************************/
4695 struct winbindd_tdc_domain * wcache_tdc_fetch_domain( TALLOC_CTX *ctx, const char *name )
4697 struct winbindd_tdc_domain *dom_list = NULL;
4698 size_t num_domains = 0;
4700 struct winbindd_tdc_domain *d = NULL;
4702 DEBUG(10,("wcache_tdc_fetch_domain: Searching for domain %s\n", name));
4704 if ( !init_wcache() ) {
4708 /* fetch the list */
4710 wcache_tdc_fetch_list( &dom_list, &num_domains );
4712 for ( i=0; i<num_domains; i++ ) {
4713 if ( strequal(name, dom_list[i].domain_name) ||
4714 strequal(name, dom_list[i].dns_name) )
4716 DEBUG(10,("wcache_tdc_fetch_domain: Found domain %s\n",
4719 d = wcache_tdc_dup_domain(ctx, &dom_list[i]);
4724 TALLOC_FREE( dom_list );
4729 /*********************************************************************
4730 ********************************************************************/
4732 struct winbindd_tdc_domain*
4733 wcache_tdc_fetch_domainbysid(TALLOC_CTX *ctx,
4734 const struct dom_sid *sid)
4736 struct winbindd_tdc_domain *dom_list = NULL;
4737 size_t num_domains = 0;
4739 struct winbindd_tdc_domain *d = NULL;
4741 DEBUG(10,("wcache_tdc_fetch_domainbysid: Searching for domain %s\n",
4742 sid_string_dbg(sid)));
4744 if (!init_wcache()) {
4748 /* fetch the list */
4750 wcache_tdc_fetch_list(&dom_list, &num_domains);
4752 for (i = 0; i<num_domains; i++) {
4753 if (dom_sid_equal(sid, &(dom_list[i].sid))) {
4754 DEBUG(10, ("wcache_tdc_fetch_domainbysid: "
4755 "Found domain %s for SID %s\n",
4756 dom_list[i].domain_name,
4757 sid_string_dbg(sid)));
4759 d = wcache_tdc_dup_domain(ctx, &dom_list[i]);
4764 TALLOC_FREE(dom_list);
4770 /*********************************************************************
4771 ********************************************************************/
4773 void wcache_tdc_clear( void )
4775 if ( !init_wcache() )
4778 wcache_tdc_store_list( NULL, 0 );
4784 /*********************************************************************
4785 ********************************************************************/
4787 static void wcache_save_user_pwinfo(struct winbindd_domain *domain,
4789 const struct dom_sid *user_sid,
4790 const char *homedir,
4795 struct cache_entry *centry;
4798 if ( (centry = centry_start(domain, status)) == NULL )
4801 centry_put_string( centry, homedir );
4802 centry_put_string( centry, shell );
4803 centry_put_string( centry, gecos );
4804 centry_put_uint32( centry, gid );
4806 centry_end(centry, "NSS/PWINFO/%s", sid_to_fstring(tmp, user_sid) );
4808 DEBUG(10,("wcache_save_user_pwinfo: %s\n", sid_string_dbg(user_sid) ));
4810 centry_free(centry);
4815 NTSTATUS nss_get_info_cached( struct winbindd_domain *domain,
4816 const struct dom_sid *user_sid,
4818 const char **homedir, const char **shell,
4819 const char **gecos, gid_t *p_gid)
4821 struct winbind_cache *cache = get_cache(domain);
4822 struct cache_entry *centry = NULL;
4829 centry = wcache_fetch(cache, domain, "NSS/PWINFO/%s",
4830 sid_to_fstring(tmp, user_sid));
4835 *homedir = centry_string( centry, ctx );
4836 *shell = centry_string( centry, ctx );
4837 *gecos = centry_string( centry, ctx );
4838 *p_gid = centry_uint32( centry );
4840 centry_free(centry);
4842 DEBUG(10,("nss_get_info_cached: [Cached] - user_sid %s\n",
4843 sid_string_dbg(user_sid)));
4845 return NT_STATUS_OK;
4849 nt_status = nss_get_info( domain->name, user_sid, ctx,
4850 homedir, shell, gecos, p_gid );
4852 DEBUG(10, ("nss_get_info returned %s\n", nt_errstr(nt_status)));
4854 if ( NT_STATUS_IS_OK(nt_status) ) {
4855 DEBUG(10, ("result:\n\thomedir = '%s'\n", *homedir));
4856 DEBUGADD(10, ("\tshell = '%s'\n", *shell));
4857 DEBUGADD(10, ("\tgecos = '%s'\n", *gecos));
4858 DEBUGADD(10, ("\tgid = '%u'\n", (unsigned int)*p_gid));
4860 wcache_save_user_pwinfo( domain, nt_status, user_sid,
4861 *homedir, *shell, *gecos, *p_gid );
4864 if ( NT_STATUS_EQUAL( nt_status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND ) ) {
4865 DEBUG(5,("nss_get_info_cached: Setting domain %s offline\n",
4867 set_domain_offline( domain );
4875 /* the cache backend methods are exposed via this structure */
4876 struct winbindd_methods cache_methods = {
4894 static bool wcache_ndr_key(TALLOC_CTX *mem_ctx, const char *domain_name,
4895 uint32_t opnum, const DATA_BLOB *req,
4901 key = talloc_asprintf(mem_ctx, "NDR/%s/%d/", domain_name, (int)opnum);
4905 keylen = talloc_get_size(key) - 1;
4907 key = talloc_realloc(mem_ctx, key, char, keylen + req->length);
4911 memcpy(key + keylen, req->data, req->length);
4913 pkey->dptr = (uint8_t *)key;
4914 pkey->dsize = talloc_get_size(key);
4918 static bool wcache_opnum_cacheable(uint32_t opnum)
4921 case NDR_WBINT_PING:
4922 case NDR_WBINT_QUERYSEQUENCENUMBER:
4923 case NDR_WBINT_ALLOCATEUID:
4924 case NDR_WBINT_ALLOCATEGID:
4925 case NDR_WBINT_CHECKMACHINEACCOUNT:
4926 case NDR_WBINT_CHANGEMACHINEACCOUNT:
4927 case NDR_WBINT_PINGDC:
4933 bool wcache_fetch_ndr(TALLOC_CTX *mem_ctx, struct winbindd_domain *domain,
4934 uint32_t opnum, const DATA_BLOB *req, DATA_BLOB *resp)
4939 if (!wcache_opnum_cacheable(opnum) ||
4940 is_my_own_sam_domain(domain) ||
4941 is_builtin_domain(domain)) {
4945 if (wcache->tdb == NULL) {
4949 if (!wcache_ndr_key(talloc_tos(), domain->name, opnum, req, &key)) {
4952 data = tdb_fetch_compat(wcache->tdb, key);
4953 TALLOC_FREE(key.dptr);
4955 if (data.dptr == NULL) {
4958 if (data.dsize < 12) {
4962 if (!is_domain_offline(domain)) {
4963 uint32_t entry_seqnum, dom_seqnum, last_check;
4964 uint64_t entry_timeout;
4966 if (!wcache_fetch_seqnum(domain->name, &dom_seqnum,
4970 entry_seqnum = IVAL(data.dptr, 0);
4971 if (entry_seqnum != dom_seqnum) {
4972 DEBUG(10, ("Entry has wrong sequence number: %d\n",
4973 (int)entry_seqnum));
4976 entry_timeout = BVAL(data.dptr, 4);
4977 if (time(NULL) > entry_timeout) {
4978 DEBUG(10, ("Entry has timed out\n"));
4983 resp->data = (uint8_t *)talloc_memdup(mem_ctx, data.dptr + 12,
4985 if (resp->data == NULL) {
4986 DEBUG(10, ("talloc failed\n"));
4989 resp->length = data.dsize - 12;
4993 SAFE_FREE(data.dptr);
4997 void wcache_store_ndr(struct winbindd_domain *domain, uint32_t opnum,
4998 const DATA_BLOB *req, const DATA_BLOB *resp)
5001 uint32_t dom_seqnum, last_check;
5004 if (!wcache_opnum_cacheable(opnum) ||
5005 is_my_own_sam_domain(domain) ||
5006 is_builtin_domain(domain)) {
5010 if (wcache->tdb == NULL) {
5014 if (!wcache_fetch_seqnum(domain->name, &dom_seqnum, &last_check)) {
5015 DEBUG(10, ("could not fetch seqnum for domain %s\n",
5020 if (!wcache_ndr_key(talloc_tos(), domain->name, opnum, req, &key)) {
5024 timeout = time(NULL) + lp_winbind_cache_time();
5026 data.dsize = resp->length + 12;
5027 data.dptr = talloc_array(key.dptr, uint8_t, data.dsize);
5028 if (data.dptr == NULL) {
5032 SIVAL(data.dptr, 0, dom_seqnum);
5033 SBVAL(data.dptr, 4, timeout);
5034 memcpy(data.dptr + 12, resp->data, resp->length);
5036 tdb_store(wcache->tdb, key, data, 0);
5039 TALLOC_FREE(key.dptr);