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);
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 static 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);
2319 * @brief Query a fullname from the username cache (for further gecos processing)
2321 * @param domain A pointer to the winbindd_domain struct.
2322 * @param mem_ctx The talloc context.
2323 * @param user_sid The user sid.
2324 * @param full_name A pointer to the full_name string.
2326 * @return NTSTATUS code
2328 NTSTATUS wcache_query_user_fullname(struct winbindd_domain *domain,
2329 TALLOC_CTX *mem_ctx,
2330 const struct dom_sid *user_sid,
2331 const char **full_name)
2334 struct wbint_userinfo info;
2336 status = wcache_query_user(domain, mem_ctx, user_sid, &info);
2337 if (!NT_STATUS_IS_OK(status)) {
2341 if (info.full_name != NULL) {
2342 *full_name = talloc_strdup(mem_ctx, info.full_name);
2343 if (*full_name == NULL) {
2344 return NT_STATUS_NO_MEMORY;
2348 return NT_STATUS_OK;
2351 /* Lookup user information from a rid */
2352 static NTSTATUS query_user(struct winbindd_domain *domain,
2353 TALLOC_CTX *mem_ctx,
2354 const struct dom_sid *user_sid,
2355 struct wbint_userinfo *info)
2360 old_status = domain->online;
2361 status = wcache_query_user(domain, mem_ctx, user_sid, info);
2362 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2368 /* Return status value returned by seq number check */
2370 if (!NT_STATUS_IS_OK(domain->last_status))
2371 return domain->last_status;
2373 DEBUG(10,("query_user: [Cached] - doing backend query for info for domain %s\n",
2376 status = domain->backend->query_user(domain, mem_ctx, user_sid, info);
2378 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2379 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2380 if (!domain->internal && old_status) {
2381 set_domain_offline(domain);
2383 if (!domain->internal &&
2386 NTSTATUS cache_status;
2387 cache_status = wcache_query_user(domain, mem_ctx, user_sid, info);
2388 return cache_status;
2392 refresh_sequence_number(domain, false);
2393 if (!NT_STATUS_IS_OK(status)) {
2396 wcache_save_user(domain, status, info);
2401 NTSTATUS wcache_lookup_usergroups(struct winbindd_domain *domain,
2402 TALLOC_CTX *mem_ctx,
2403 const struct dom_sid *user_sid,
2404 uint32_t *pnum_sids,
2405 struct dom_sid **psids)
2407 struct winbind_cache *cache = get_cache(domain);
2408 struct cache_entry *centry = NULL;
2410 uint32_t i, num_sids;
2411 struct dom_sid *sids;
2414 if (cache->tdb == NULL) {
2415 return NT_STATUS_NOT_FOUND;
2418 centry = wcache_fetch(cache, domain, "UG/%s",
2419 sid_to_fstring(sid_string, user_sid));
2420 if (centry == NULL) {
2421 return NT_STATUS_NOT_FOUND;
2424 /* If we have an access denied cache entry and a cached info3 in the
2425 samlogon cache then do a query. This will force the rpc back end
2426 to return the info3 data. */
2428 if (NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)
2429 && netsamlogon_cache_have(user_sid)) {
2430 DEBUG(10, ("lookup_usergroups: cached access denied and have "
2432 domain->last_status = NT_STATUS_OK;
2433 centry_free(centry);
2434 return NT_STATUS_NOT_FOUND;
2437 num_sids = centry_uint32(centry);
2438 sids = talloc_array(mem_ctx, struct dom_sid, num_sids);
2440 centry_free(centry);
2441 return NT_STATUS_NO_MEMORY;
2444 for (i=0; i<num_sids; i++) {
2445 centry_sid(centry, &sids[i]);
2448 status = centry->status;
2450 DEBUG(10,("lookup_usergroups: [Cached] - cached info for domain %s "
2451 "status: %s\n", domain->name, nt_errstr(status)));
2453 centry_free(centry);
2455 *pnum_sids = num_sids;
2460 /* Lookup groups a user is a member of. */
2461 static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
2462 TALLOC_CTX *mem_ctx,
2463 const struct dom_sid *user_sid,
2464 uint32 *num_groups, struct dom_sid **user_gids)
2466 struct cache_entry *centry = NULL;
2472 old_status = domain->online;
2473 status = wcache_lookup_usergroups(domain, mem_ctx, user_sid,
2474 num_groups, user_gids);
2475 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2480 (*user_gids) = NULL;
2482 /* Return status value returned by seq number check */
2484 if (!NT_STATUS_IS_OK(domain->last_status))
2485 return domain->last_status;
2487 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info for domain %s\n",
2490 status = domain->backend->lookup_usergroups(domain, mem_ctx, user_sid, num_groups, user_gids);
2492 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2493 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2494 if (!domain->internal && old_status) {
2495 set_domain_offline(domain);
2497 if (!domain->internal &&
2500 NTSTATUS cache_status;
2501 cache_status = wcache_lookup_usergroups(domain, mem_ctx, user_sid,
2502 num_groups, user_gids);
2503 return cache_status;
2506 if ( NT_STATUS_EQUAL(status, NT_STATUS_SYNCHRONIZATION_REQUIRED) )
2510 refresh_sequence_number(domain, false);
2511 if (!NT_STATUS_IS_OK(status)) {
2514 centry = centry_start(domain, status);
2518 centry_put_uint32(centry, *num_groups);
2519 for (i=0; i<(*num_groups); i++) {
2520 centry_put_sid(centry, &(*user_gids)[i]);
2523 centry_end(centry, "UG/%s", sid_to_fstring(sid_string, user_sid));
2524 centry_free(centry);
2530 static char *wcache_make_sidlist(TALLOC_CTX *mem_ctx, uint32_t num_sids,
2531 const struct dom_sid *sids)
2536 sidlist = talloc_strdup(mem_ctx, "");
2537 if (sidlist == NULL) {
2540 for (i=0; i<num_sids; i++) {
2542 sidlist = talloc_asprintf_append_buffer(
2543 sidlist, "/%s", sid_to_fstring(tmp, &sids[i]));
2544 if (sidlist == NULL) {
2551 NTSTATUS wcache_lookup_useraliases(struct winbindd_domain *domain,
2552 TALLOC_CTX *mem_ctx, uint32_t num_sids,
2553 const struct dom_sid *sids,
2554 uint32_t *pnum_aliases, uint32_t **paliases)
2556 struct winbind_cache *cache = get_cache(domain);
2557 struct cache_entry *centry = NULL;
2558 uint32_t num_aliases;
2564 if (cache->tdb == NULL) {
2565 return NT_STATUS_NOT_FOUND;
2568 if (num_sids == 0) {
2571 return NT_STATUS_OK;
2574 /* We need to cache indexed by the whole list of SIDs, the aliases
2575 * resulting might come from any of the SIDs. */
2577 sidlist = wcache_make_sidlist(talloc_tos(), num_sids, sids);
2578 if (sidlist == NULL) {
2579 return NT_STATUS_NO_MEMORY;
2582 centry = wcache_fetch(cache, domain, "UA%s", sidlist);
2583 TALLOC_FREE(sidlist);
2584 if (centry == NULL) {
2585 return NT_STATUS_NOT_FOUND;
2588 num_aliases = centry_uint32(centry);
2589 aliases = talloc_array(mem_ctx, uint32_t, num_aliases);
2590 if (aliases == NULL) {
2591 centry_free(centry);
2592 return NT_STATUS_NO_MEMORY;
2595 for (i=0; i<num_aliases; i++) {
2596 aliases[i] = centry_uint32(centry);
2599 status = centry->status;
2601 DEBUG(10,("lookup_useraliases: [Cached] - cached info for domain: %s "
2602 "status %s\n", domain->name, nt_errstr(status)));
2604 centry_free(centry);
2606 *pnum_aliases = num_aliases;
2607 *paliases = aliases;
2612 static NTSTATUS lookup_useraliases(struct winbindd_domain *domain,
2613 TALLOC_CTX *mem_ctx,
2614 uint32 num_sids, const struct dom_sid *sids,
2615 uint32 *num_aliases, uint32 **alias_rids)
2617 struct cache_entry *centry = NULL;
2623 old_status = domain->online;
2624 status = wcache_lookup_useraliases(domain, mem_ctx, num_sids, sids,
2625 num_aliases, alias_rids);
2626 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2631 (*alias_rids) = NULL;
2633 if (!NT_STATUS_IS_OK(domain->last_status))
2634 return domain->last_status;
2636 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info "
2637 "for domain %s\n", domain->name ));
2639 sidlist = wcache_make_sidlist(talloc_tos(), num_sids, sids);
2640 if (sidlist == NULL) {
2641 return NT_STATUS_NO_MEMORY;
2644 status = domain->backend->lookup_useraliases(domain, mem_ctx,
2646 num_aliases, alias_rids);
2648 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2649 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2650 if (!domain->internal && old_status) {
2651 set_domain_offline(domain);
2653 if (!domain->internal &&
2656 NTSTATUS cache_status;
2657 cache_status = wcache_lookup_useraliases(domain, mem_ctx, num_sids,
2658 sids, num_aliases, alias_rids);
2659 return cache_status;
2663 refresh_sequence_number(domain, false);
2664 if (!NT_STATUS_IS_OK(status)) {
2667 centry = centry_start(domain, status);
2670 centry_put_uint32(centry, *num_aliases);
2671 for (i=0; i<(*num_aliases); i++)
2672 centry_put_uint32(centry, (*alias_rids)[i]);
2673 centry_end(centry, "UA%s", sidlist);
2674 centry_free(centry);
2680 NTSTATUS wcache_lookup_groupmem(struct winbindd_domain *domain,
2681 TALLOC_CTX *mem_ctx,
2682 const struct dom_sid *group_sid,
2683 uint32_t *num_names,
2684 struct dom_sid **sid_mem, char ***names,
2685 uint32_t **name_types)
2687 struct winbind_cache *cache = get_cache(domain);
2688 struct cache_entry *centry = NULL;
2693 if (cache->tdb == NULL) {
2694 return NT_STATUS_NOT_FOUND;
2697 sid_string = sid_string_tos(group_sid);
2698 if (sid_string == NULL) {
2699 return NT_STATUS_NO_MEMORY;
2702 centry = wcache_fetch(cache, domain, "GM/%s", sid_string);
2703 TALLOC_FREE(sid_string);
2704 if (centry == NULL) {
2705 return NT_STATUS_NOT_FOUND;
2712 *num_names = centry_uint32(centry);
2713 if (*num_names == 0) {
2714 centry_free(centry);
2715 return NT_STATUS_OK;
2718 *sid_mem = talloc_array(mem_ctx, struct dom_sid, *num_names);
2719 *names = talloc_array(mem_ctx, char *, *num_names);
2720 *name_types = talloc_array(mem_ctx, uint32, *num_names);
2722 if ((*sid_mem == NULL) || (*names == NULL) || (*name_types == NULL)) {
2723 TALLOC_FREE(*sid_mem);
2724 TALLOC_FREE(*names);
2725 TALLOC_FREE(*name_types);
2726 centry_free(centry);
2727 return NT_STATUS_NO_MEMORY;
2730 for (i=0; i<(*num_names); i++) {
2731 centry_sid(centry, &(*sid_mem)[i]);
2732 (*names)[i] = centry_string(centry, mem_ctx);
2733 (*name_types)[i] = centry_uint32(centry);
2736 status = centry->status;
2738 DEBUG(10,("lookup_groupmem: [Cached] - cached info for domain %s "
2739 "status: %s\n", domain->name, nt_errstr(status)));
2741 centry_free(centry);
2745 static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
2746 TALLOC_CTX *mem_ctx,
2747 const struct dom_sid *group_sid,
2748 enum lsa_SidType type,
2750 struct dom_sid **sid_mem, char ***names,
2751 uint32 **name_types)
2753 struct cache_entry *centry = NULL;
2759 old_status = domain->online;
2760 status = wcache_lookup_groupmem(domain, mem_ctx, group_sid, num_names,
2761 sid_mem, names, name_types);
2762 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2769 (*name_types) = NULL;
2771 /* Return status value returned by seq number check */
2773 if (!NT_STATUS_IS_OK(domain->last_status))
2774 return domain->last_status;
2776 DEBUG(10,("lookup_groupmem: [Cached] - doing backend query for info for domain %s\n",
2779 status = domain->backend->lookup_groupmem(domain, mem_ctx, group_sid,
2781 sid_mem, names, name_types);
2783 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2784 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2785 if (!domain->internal && old_status) {
2786 set_domain_offline(domain);
2788 if (!domain->internal &&
2791 NTSTATUS cache_status;
2792 cache_status = wcache_lookup_groupmem(domain, mem_ctx, group_sid,
2793 num_names, sid_mem, names,
2795 return cache_status;
2799 refresh_sequence_number(domain, false);
2800 if (!NT_STATUS_IS_OK(status)) {
2803 centry = centry_start(domain, status);
2806 centry_put_uint32(centry, *num_names);
2807 for (i=0; i<(*num_names); i++) {
2808 centry_put_sid(centry, &(*sid_mem)[i]);
2809 centry_put_string(centry, (*names)[i]);
2810 centry_put_uint32(centry, (*name_types)[i]);
2812 centry_end(centry, "GM/%s", sid_to_fstring(sid_string, group_sid));
2813 centry_free(centry);
2819 /* find the sequence number for a domain */
2820 static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
2822 refresh_sequence_number(domain, false);
2824 *seq = domain->sequence_number;
2826 return NT_STATUS_OK;
2829 /* enumerate trusted domains
2830 * (we need to have the list of trustdoms in the cache when we go offline) -
2832 static NTSTATUS trusted_domains(struct winbindd_domain *domain,
2833 TALLOC_CTX *mem_ctx,
2834 struct netr_DomainTrustList *trusts)
2837 struct winbind_cache *cache;
2838 struct winbindd_tdc_domain *dom_list = NULL;
2839 size_t num_domains = 0;
2840 bool retval = false;
2844 old_status = domain->online;
2846 trusts->array = NULL;
2848 cache = get_cache(domain);
2849 if (!cache || !cache->tdb) {
2853 if (domain->online) {
2857 retval = wcache_tdc_fetch_list(&dom_list, &num_domains);
2858 if (!retval || !num_domains || !dom_list) {
2859 TALLOC_FREE(dom_list);
2864 trusts->array = talloc_zero_array(mem_ctx, struct netr_DomainTrust, num_domains);
2865 if (!trusts->array) {
2866 TALLOC_FREE(dom_list);
2867 return NT_STATUS_NO_MEMORY;
2870 for (i = 0; i < num_domains; i++) {
2871 struct netr_DomainTrust *trust;
2872 struct dom_sid *sid;
2873 struct winbindd_domain *dom;
2875 dom = find_domain_from_name_noinit(dom_list[i].domain_name);
2876 if (dom && dom->internal) {
2880 trust = &trusts->array[trusts->count];
2881 trust->netbios_name = talloc_strdup(trusts->array, dom_list[i].domain_name);
2882 trust->dns_name = talloc_strdup(trusts->array, dom_list[i].dns_name);
2883 sid = talloc(trusts->array, struct dom_sid);
2884 if (!trust->netbios_name || !trust->dns_name ||
2886 TALLOC_FREE(dom_list);
2887 TALLOC_FREE(trusts->array);
2888 return NT_STATUS_NO_MEMORY;
2891 trust->trust_flags = dom_list[i].trust_flags;
2892 trust->trust_attributes = dom_list[i].trust_attribs;
2893 trust->trust_type = dom_list[i].trust_type;
2894 sid_copy(sid, &dom_list[i].sid);
2899 TALLOC_FREE(dom_list);
2900 return NT_STATUS_OK;
2903 /* Return status value returned by seq number check */
2905 if (!NT_STATUS_IS_OK(domain->last_status))
2906 return domain->last_status;
2908 DEBUG(10,("trusted_domains: [Cached] - doing backend query for info for domain %s\n",
2911 status = domain->backend->trusted_domains(domain, mem_ctx, trusts);
2913 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2914 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2915 if (!domain->internal && old_status) {
2916 set_domain_offline(domain);
2918 if (!domain->internal &&
2921 retval = wcache_tdc_fetch_list(&dom_list, &num_domains);
2922 if (retval && num_domains && dom_list) {
2923 TALLOC_FREE(trusts->array);
2925 goto do_fetch_cache;
2929 /* no trusts gives NT_STATUS_NO_MORE_ENTRIES resetting to NT_STATUS_OK
2930 * so that the generic centry handling still applies correctly -
2933 if (!NT_STATUS_IS_ERR(status)) {
2934 status = NT_STATUS_OK;
2939 /* get lockout policy */
2940 static NTSTATUS lockout_policy(struct winbindd_domain *domain,
2941 TALLOC_CTX *mem_ctx,
2942 struct samr_DomInfo12 *policy)
2944 struct winbind_cache *cache = get_cache(domain);
2945 struct cache_entry *centry = NULL;
2949 old_status = domain->online;
2953 centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
2959 policy->lockout_duration = centry_nttime(centry);
2960 policy->lockout_window = centry_nttime(centry);
2961 policy->lockout_threshold = centry_uint16(centry);
2963 status = centry->status;
2965 DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2966 domain->name, nt_errstr(status) ));
2968 centry_free(centry);
2972 ZERO_STRUCTP(policy);
2974 /* Return status value returned by seq number check */
2976 if (!NT_STATUS_IS_OK(domain->last_status))
2977 return domain->last_status;
2979 DEBUG(10,("lockout_policy: [Cached] - doing backend query for info for domain %s\n",
2982 status = domain->backend->lockout_policy(domain, mem_ctx, policy);
2984 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2985 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2986 if (!domain->internal && old_status) {
2987 set_domain_offline(domain);
2990 !domain->internal &&
2993 centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
2995 goto do_fetch_cache;
3000 refresh_sequence_number(domain, false);
3001 if (!NT_STATUS_IS_OK(status)) {
3004 wcache_save_lockout_policy(domain, status, policy);
3009 /* get password policy */
3010 static NTSTATUS password_policy(struct winbindd_domain *domain,
3011 TALLOC_CTX *mem_ctx,
3012 struct samr_DomInfo1 *policy)
3014 struct winbind_cache *cache = get_cache(domain);
3015 struct cache_entry *centry = NULL;
3019 old_status = domain->online;
3023 centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
3029 policy->min_password_length = centry_uint16(centry);
3030 policy->password_history_length = centry_uint16(centry);
3031 policy->password_properties = centry_uint32(centry);
3032 policy->max_password_age = centry_nttime(centry);
3033 policy->min_password_age = centry_nttime(centry);
3035 status = centry->status;
3037 DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
3038 domain->name, nt_errstr(status) ));
3040 centry_free(centry);
3044 ZERO_STRUCTP(policy);
3046 /* Return status value returned by seq number check */
3048 if (!NT_STATUS_IS_OK(domain->last_status))
3049 return domain->last_status;
3051 DEBUG(10,("password_policy: [Cached] - doing backend query for info for domain %s\n",
3054 status = domain->backend->password_policy(domain, mem_ctx, policy);
3056 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
3057 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
3058 if (!domain->internal && old_status) {
3059 set_domain_offline(domain);
3062 !domain->internal &&
3065 centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
3067 goto do_fetch_cache;
3072 refresh_sequence_number(domain, false);
3073 if (!NT_STATUS_IS_OK(status)) {
3076 wcache_save_password_policy(domain, status, policy);
3082 /* Invalidate cached user and group lists coherently */
3084 static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
3087 if (strncmp((const char *)kbuf.dptr, "UL/", 3) == 0 ||
3088 strncmp((const char *)kbuf.dptr, "GL/", 3) == 0)
3089 tdb_delete(the_tdb, kbuf);
3094 /* Invalidate the getpwnam and getgroups entries for a winbindd domain */
3096 void wcache_invalidate_samlogon(struct winbindd_domain *domain,
3097 const struct dom_sid *sid)
3099 fstring key_str, sid_string;
3100 struct winbind_cache *cache;
3102 /* dont clear cached U/SID and UG/SID entries when we want to logon
3105 if (lp_winbind_offline_logon()) {
3112 cache = get_cache(domain);
3118 /* Clear U/SID cache entry */
3119 fstr_sprintf(key_str, "U/%s", sid_to_fstring(sid_string, sid));
3120 DEBUG(10, ("wcache_invalidate_samlogon: clearing %s\n", key_str));
3121 tdb_delete(cache->tdb, string_tdb_data(key_str));
3123 /* Clear UG/SID cache entry */
3124 fstr_sprintf(key_str, "UG/%s", sid_to_fstring(sid_string, sid));
3125 DEBUG(10, ("wcache_invalidate_samlogon: clearing %s\n", key_str));
3126 tdb_delete(cache->tdb, string_tdb_data(key_str));
3128 /* Samba/winbindd never needs this. */
3129 netsamlogon_clear_cached_user(sid);
3132 bool wcache_invalidate_cache(void)
3134 struct winbindd_domain *domain;
3136 for (domain = domain_list(); domain; domain = domain->next) {
3137 struct winbind_cache *cache = get_cache(domain);
3139 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
3140 "entries for %s\n", domain->name));
3143 tdb_traverse(cache->tdb, traverse_fn, NULL);
3152 bool wcache_invalidate_cache_noinit(void)
3154 struct winbindd_domain *domain;
3156 for (domain = domain_list(); domain; domain = domain->next) {
3157 struct winbind_cache *cache;
3159 /* Skip uninitialized domains. */
3160 if (!domain->initialized && !domain->internal) {
3164 cache = get_cache(domain);
3166 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
3167 "entries for %s\n", domain->name));
3170 tdb_traverse(cache->tdb, traverse_fn, NULL);
3172 * Flushing cache has nothing to with domains.
3173 * return here if we successfully flushed once.
3174 * To avoid unnecessary traversing the cache.
3185 bool init_wcache(void)
3187 if (wcache == NULL) {
3188 wcache = SMB_XMALLOC_P(struct winbind_cache);
3189 ZERO_STRUCTP(wcache);
3192 if (wcache->tdb != NULL)
3195 /* when working offline we must not clear the cache on restart */
3196 wcache->tdb = tdb_open_log(state_path("winbindd_cache.tdb"),
3197 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
3198 TDB_INCOMPATIBLE_HASH |
3199 (lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST)),
3200 O_RDWR|O_CREAT, 0600);
3202 if (wcache->tdb == NULL) {
3203 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
3210 /************************************************************************
3211 This is called by the parent to initialize the cache file.
3212 We don't need sophisticated locking here as we know we're the
3214 ************************************************************************/
3216 bool initialize_winbindd_cache(void)
3218 bool cache_bad = true;
3221 if (!init_wcache()) {
3222 DEBUG(0,("initialize_winbindd_cache: init_wcache failed.\n"));
3226 /* Check version number. */
3227 if (tdb_fetch_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, &vers) &&
3228 vers == WINBINDD_CACHE_VERSION) {
3233 DEBUG(0,("initialize_winbindd_cache: clearing cache "
3234 "and re-creating with version number %d\n",
3235 WINBINDD_CACHE_VERSION ));
3237 tdb_close(wcache->tdb);
3240 if (unlink(state_path("winbindd_cache.tdb")) == -1) {
3241 DEBUG(0,("initialize_winbindd_cache: unlink %s failed %s ",
3242 state_path("winbindd_cache.tdb"),
3246 if (!init_wcache()) {
3247 DEBUG(0,("initialize_winbindd_cache: re-initialization "
3248 "init_wcache failed.\n"));
3252 /* Write the version. */
3253 if (!tdb_store_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, WINBINDD_CACHE_VERSION)) {
3254 DEBUG(0,("initialize_winbindd_cache: version number store failed %s\n",
3255 tdb_errorstr_compat(wcache->tdb) ));
3260 tdb_close(wcache->tdb);
3265 void close_winbindd_cache(void)
3271 tdb_close(wcache->tdb);
3276 bool lookup_cached_sid(TALLOC_CTX *mem_ctx, const struct dom_sid *sid,
3277 char **domain_name, char **name,
3278 enum lsa_SidType *type)
3280 struct winbindd_domain *domain;
3283 domain = find_lookup_domain_from_sid(sid);
3284 if (domain == NULL) {
3287 status = wcache_sid_to_name(domain, sid, mem_ctx, domain_name, name,
3289 return NT_STATUS_IS_OK(status);
3292 bool lookup_cached_name(const char *domain_name,
3294 struct dom_sid *sid,
3295 enum lsa_SidType *type)
3297 struct winbindd_domain *domain;
3299 bool original_online_state;
3301 domain = find_lookup_domain_from_name(domain_name);
3302 if (domain == NULL) {
3306 /* If we are doing a cached logon, temporarily set the domain
3307 offline so the cache won't expire the entry */
3309 original_online_state = domain->online;
3310 domain->online = false;
3311 status = wcache_name_to_sid(domain, domain_name, name, sid, type);
3312 domain->online = original_online_state;
3314 return NT_STATUS_IS_OK(status);
3317 void cache_name2sid(struct winbindd_domain *domain,
3318 const char *domain_name, const char *name,
3319 enum lsa_SidType type, const struct dom_sid *sid)
3321 refresh_sequence_number(domain, false);
3322 wcache_save_name_to_sid(domain, NT_STATUS_OK, domain_name, name,
3327 * The original idea that this cache only contains centries has
3328 * been blurred - now other stuff gets put in here. Ensure we
3329 * ignore these things on cleanup.
3332 static int traverse_fn_cleanup(TDB_CONTEXT *the_tdb, TDB_DATA kbuf,
3333 TDB_DATA dbuf, void *state)
3335 struct cache_entry *centry;
3337 if (is_non_centry_key(kbuf)) {
3341 centry = wcache_fetch_raw((char *)kbuf.dptr);
3346 if (!NT_STATUS_IS_OK(centry->status)) {
3347 DEBUG(10,("deleting centry %s\n", (const char *)kbuf.dptr));
3348 tdb_delete(the_tdb, kbuf);
3351 centry_free(centry);
3355 /* flush the cache */
3356 void wcache_flush_cache(void)
3361 tdb_close(wcache->tdb);
3364 if (!winbindd_use_cache()) {
3368 /* when working offline we must not clear the cache on restart */
3369 wcache->tdb = tdb_open_log(state_path("winbindd_cache.tdb"),
3370 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
3371 TDB_INCOMPATIBLE_HASH |
3372 (lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST)),
3373 O_RDWR|O_CREAT, 0600);
3376 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
3380 tdb_traverse(wcache->tdb, traverse_fn_cleanup, NULL);
3382 DEBUG(10,("wcache_flush_cache success\n"));
3385 /* Count cached creds */
3387 static int traverse_fn_cached_creds(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
3390 int *cred_count = (int*)state;
3392 if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
3398 NTSTATUS wcache_count_cached_creds(struct winbindd_domain *domain, int *count)
3400 struct winbind_cache *cache = get_cache(domain);
3405 return NT_STATUS_INTERNAL_DB_ERROR;
3408 tdb_traverse(cache->tdb, traverse_fn_cached_creds, (void *)count);
3410 return NT_STATUS_OK;
3414 struct cred_list *prev, *next;
3419 static struct cred_list *wcache_cred_list;
3421 static int traverse_fn_get_credlist(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
3424 struct cred_list *cred;
3426 if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
3428 cred = SMB_MALLOC_P(struct cred_list);
3430 DEBUG(0,("traverse_fn_remove_first_creds: failed to malloc new entry for list\n"));
3436 /* save a copy of the key */
3438 fstrcpy(cred->name, (const char *)kbuf.dptr);
3439 DLIST_ADD(wcache_cred_list, cred);
3445 NTSTATUS wcache_remove_oldest_cached_creds(struct winbindd_domain *domain, const struct dom_sid *sid)
3447 struct winbind_cache *cache = get_cache(domain);
3450 struct cred_list *cred, *oldest = NULL;
3453 return NT_STATUS_INTERNAL_DB_ERROR;
3456 /* we possibly already have an entry */
3457 if (sid && NT_STATUS_IS_OK(wcache_cached_creds_exist(domain, sid))) {
3459 fstring key_str, tmp;
3461 DEBUG(11,("we already have an entry, deleting that\n"));
3463 fstr_sprintf(key_str, "CRED/%s", sid_to_fstring(tmp, sid));
3465 tdb_delete(cache->tdb, string_tdb_data(key_str));
3467 return NT_STATUS_OK;
3470 ret = tdb_traverse(cache->tdb, traverse_fn_get_credlist, NULL);
3472 return NT_STATUS_OK;
3473 } else if ((ret < 0) || (wcache_cred_list == NULL)) {
3474 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
3477 ZERO_STRUCTP(oldest);
3479 for (cred = wcache_cred_list; cred; cred = cred->next) {
3484 data = tdb_fetch_compat(cache->tdb, string_tdb_data(cred->name));
3486 DEBUG(10,("wcache_remove_oldest_cached_creds: entry for [%s] not found\n",
3488 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
3492 t = IVAL(data.dptr, 0);
3493 SAFE_FREE(data.dptr);
3496 oldest = SMB_MALLOC_P(struct cred_list);
3497 if (oldest == NULL) {
3498 status = NT_STATUS_NO_MEMORY;
3502 fstrcpy(oldest->name, cred->name);
3503 oldest->created = t;
3507 if (t < oldest->created) {
3508 fstrcpy(oldest->name, cred->name);
3509 oldest->created = t;
3513 if (tdb_delete(cache->tdb, string_tdb_data(oldest->name)) == 0) {
3514 status = NT_STATUS_OK;
3516 status = NT_STATUS_UNSUCCESSFUL;
3519 SAFE_FREE(wcache_cred_list);
3525 /* Change the global online/offline state. */
3526 bool set_global_winbindd_state_offline(void)
3530 DEBUG(10,("set_global_winbindd_state_offline: offline requested.\n"));
3532 /* Only go offline if someone has created
3533 the key "WINBINDD_OFFLINE" in the cache tdb. */
3535 if (wcache == NULL || wcache->tdb == NULL) {
3536 DEBUG(10,("set_global_winbindd_state_offline: wcache not open yet.\n"));
3540 if (!lp_winbind_offline_logon()) {
3541 DEBUG(10,("set_global_winbindd_state_offline: rejecting.\n"));
3545 if (global_winbindd_offline_state) {
3546 /* Already offline. */
3550 data = tdb_fetch_bystring( wcache->tdb, "WINBINDD_OFFLINE" );
3552 if (!data.dptr || data.dsize != 4) {
3553 DEBUG(10,("set_global_winbindd_state_offline: offline state not set.\n"));
3554 SAFE_FREE(data.dptr);
3557 DEBUG(10,("set_global_winbindd_state_offline: offline state set.\n"));
3558 global_winbindd_offline_state = true;
3559 SAFE_FREE(data.dptr);
3564 void set_global_winbindd_state_online(void)
3566 DEBUG(10,("set_global_winbindd_state_online: online requested.\n"));
3568 if (!lp_winbind_offline_logon()) {
3569 DEBUG(10,("set_global_winbindd_state_online: rejecting.\n"));
3573 if (!global_winbindd_offline_state) {
3574 /* Already online. */
3577 global_winbindd_offline_state = false;
3583 /* Ensure there is no key "WINBINDD_OFFLINE" in the cache tdb. */
3584 tdb_delete_bystring(wcache->tdb, "WINBINDD_OFFLINE");
3587 bool get_global_winbindd_state_offline(void)
3589 return global_winbindd_offline_state;
3592 /***********************************************************************
3593 Validate functions for all possible cache tdb keys.
3594 ***********************************************************************/
3596 static struct cache_entry *create_centry_validate(const char *kstr, TDB_DATA data,
3597 struct tdb_validation_status *state)
3599 struct cache_entry *centry;
3601 centry = SMB_XMALLOC_P(struct cache_entry);
3602 centry->data = (unsigned char *)smb_memdup(data.dptr, data.dsize);
3603 if (!centry->data) {
3607 centry->len = data.dsize;
3610 if (centry->len < 16) {
3611 /* huh? corrupt cache? */
3612 DEBUG(0,("create_centry_validate: Corrupt cache for key %s "
3613 "(len < 16) ?\n", kstr));
3614 centry_free(centry);
3615 state->bad_entry = true;
3616 state->success = false;
3620 centry->status = NT_STATUS(centry_uint32(centry));
3621 centry->sequence_number = centry_uint32(centry);
3622 centry->timeout = centry_uint64_t(centry);
3626 static int validate_seqnum(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3627 struct tdb_validation_status *state)
3629 if (dbuf.dsize != 8) {
3630 DEBUG(0,("validate_seqnum: Corrupt cache for key %s (len %u != 8) ?\n",
3631 keystr, (unsigned int)dbuf.dsize ));
3632 state->bad_entry = true;
3638 static int validate_ns(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3639 struct tdb_validation_status *state)
3641 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3646 (void)centry_uint32(centry);
3647 if (NT_STATUS_IS_OK(centry->status)) {
3649 (void)centry_sid(centry, &sid);
3652 centry_free(centry);
3654 if (!(state->success)) {
3657 DEBUG(10,("validate_ns: %s ok\n", keystr));
3661 static int validate_sn(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3662 struct tdb_validation_status *state)
3664 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3669 if (NT_STATUS_IS_OK(centry->status)) {
3670 (void)centry_uint32(centry);
3671 (void)centry_string(centry, mem_ctx);
3672 (void)centry_string(centry, mem_ctx);
3675 centry_free(centry);
3677 if (!(state->success)) {
3680 DEBUG(10,("validate_sn: %s ok\n", keystr));
3684 static int validate_u(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3685 struct tdb_validation_status *state)
3687 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3694 (void)centry_string(centry, mem_ctx);
3695 (void)centry_string(centry, mem_ctx);
3696 (void)centry_string(centry, mem_ctx);
3697 (void)centry_string(centry, mem_ctx);
3698 (void)centry_uint32(centry);
3699 (void)centry_sid(centry, &sid);
3700 (void)centry_sid(centry, &sid);
3702 centry_free(centry);
3704 if (!(state->success)) {
3707 DEBUG(10,("validate_u: %s ok\n", keystr));
3711 static int validate_loc_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3712 struct tdb_validation_status *state)
3714 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3720 (void)centry_nttime(centry);
3721 (void)centry_nttime(centry);
3722 (void)centry_uint16(centry);
3724 centry_free(centry);
3726 if (!(state->success)) {
3729 DEBUG(10,("validate_loc_pol: %s ok\n", keystr));
3733 static int validate_pwd_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3734 struct tdb_validation_status *state)
3736 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3742 (void)centry_uint16(centry);
3743 (void)centry_uint16(centry);
3744 (void)centry_uint32(centry);
3745 (void)centry_nttime(centry);
3746 (void)centry_nttime(centry);
3748 centry_free(centry);
3750 if (!(state->success)) {
3753 DEBUG(10,("validate_pwd_pol: %s ok\n", keystr));
3757 static int validate_cred(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3758 struct tdb_validation_status *state)
3760 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3766 (void)centry_time(centry);
3767 (void)centry_hash16(centry, mem_ctx);
3769 /* We only have 17 bytes more data in the salted cred case. */
3770 if (centry->len - centry->ofs == 17) {
3771 (void)centry_hash16(centry, mem_ctx);
3774 centry_free(centry);
3776 if (!(state->success)) {
3779 DEBUG(10,("validate_cred: %s ok\n", keystr));
3783 static int validate_ul(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3784 struct tdb_validation_status *state)
3786 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3787 int32 num_entries, i;
3793 num_entries = (int32)centry_uint32(centry);
3795 for (i=0; i< num_entries; i++) {
3797 (void)centry_string(centry, mem_ctx);
3798 (void)centry_string(centry, mem_ctx);
3799 (void)centry_string(centry, mem_ctx);
3800 (void)centry_string(centry, mem_ctx);
3801 (void)centry_sid(centry, &sid);
3802 (void)centry_sid(centry, &sid);
3805 centry_free(centry);
3807 if (!(state->success)) {
3810 DEBUG(10,("validate_ul: %s ok\n", keystr));
3814 static int validate_gl(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3815 struct tdb_validation_status *state)
3817 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3818 int32 num_entries, i;
3824 num_entries = centry_uint32(centry);
3826 for (i=0; i< num_entries; i++) {
3827 (void)centry_string(centry, mem_ctx);
3828 (void)centry_string(centry, mem_ctx);
3829 (void)centry_uint32(centry);
3832 centry_free(centry);
3834 if (!(state->success)) {
3837 DEBUG(10,("validate_gl: %s ok\n", keystr));
3841 static int validate_ug(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3842 struct tdb_validation_status *state)
3844 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3845 int32 num_groups, i;
3851 num_groups = centry_uint32(centry);
3853 for (i=0; i< num_groups; i++) {
3855 centry_sid(centry, &sid);
3858 centry_free(centry);
3860 if (!(state->success)) {
3863 DEBUG(10,("validate_ug: %s ok\n", keystr));
3867 static int validate_ua(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3868 struct tdb_validation_status *state)
3870 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3871 int32 num_aliases, i;
3877 num_aliases = centry_uint32(centry);
3879 for (i=0; i < num_aliases; i++) {
3880 (void)centry_uint32(centry);
3883 centry_free(centry);
3885 if (!(state->success)) {
3888 DEBUG(10,("validate_ua: %s ok\n", keystr));
3892 static int validate_gm(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3893 struct tdb_validation_status *state)
3895 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3902 num_names = centry_uint32(centry);
3904 for (i=0; i< num_names; i++) {
3906 centry_sid(centry, &sid);
3907 (void)centry_string(centry, mem_ctx);
3908 (void)centry_uint32(centry);
3911 centry_free(centry);
3913 if (!(state->success)) {
3916 DEBUG(10,("validate_gm: %s ok\n", keystr));
3920 static int validate_dr(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3921 struct tdb_validation_status *state)
3923 /* Can't say anything about this other than must be nonzero. */
3924 if (dbuf.dsize == 0) {
3925 DEBUG(0,("validate_dr: Corrupt cache for key %s (len == 0) ?\n",
3927 state->bad_entry = true;
3928 state->success = false;
3932 DEBUG(10,("validate_dr: %s ok\n", keystr));
3936 static int validate_de(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3937 struct tdb_validation_status *state)
3939 /* Can't say anything about this other than must be nonzero. */
3940 if (dbuf.dsize == 0) {
3941 DEBUG(0,("validate_de: Corrupt cache for key %s (len == 0) ?\n",
3943 state->bad_entry = true;
3944 state->success = false;
3948 DEBUG(10,("validate_de: %s ok\n", keystr));
3952 static int validate_pwinfo(TALLOC_CTX *mem_ctx, const char *keystr,
3953 TDB_DATA dbuf, struct tdb_validation_status *state)
3955 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3961 (void)centry_string(centry, mem_ctx);
3962 (void)centry_string(centry, mem_ctx);
3963 (void)centry_string(centry, mem_ctx);
3964 (void)centry_uint32(centry);
3966 centry_free(centry);
3968 if (!(state->success)) {
3971 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3975 static int validate_nss_an(TALLOC_CTX *mem_ctx, const char *keystr,
3977 struct tdb_validation_status *state)
3979 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3985 (void)centry_string( centry, mem_ctx );
3987 centry_free(centry);
3989 if (!(state->success)) {
3992 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3996 static int validate_nss_na(TALLOC_CTX *mem_ctx, const char *keystr,
3998 struct tdb_validation_status *state)
4000 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
4006 (void)centry_string( centry, mem_ctx );
4008 centry_free(centry);
4010 if (!(state->success)) {
4013 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
4017 static int validate_trustdomcache(TALLOC_CTX *mem_ctx, const char *keystr,
4019 struct tdb_validation_status *state)
4021 if (dbuf.dsize == 0) {
4022 DEBUG(0, ("validate_trustdomcache: Corrupt cache for "
4023 "key %s (len ==0) ?\n", keystr));
4024 state->bad_entry = true;
4025 state->success = false;
4029 DEBUG(10, ("validate_trustdomcache: %s ok\n", keystr));
4030 DEBUGADD(10, (" Don't trust me, I am a DUMMY!\n"));
4034 static int validate_offline(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
4035 struct tdb_validation_status *state)
4037 if (dbuf.dsize != 4) {
4038 DEBUG(0,("validate_offline: Corrupt cache for key %s (len %u != 4) ?\n",
4039 keystr, (unsigned int)dbuf.dsize ));
4040 state->bad_entry = true;
4041 state->success = false;
4044 DEBUG(10,("validate_offline: %s ok\n", keystr));
4048 static int validate_ndr(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
4049 struct tdb_validation_status *state)
4052 * Ignore validation for now. The proper way to do this is with a
4053 * checksum. Just pure parsing does not really catch much.
4058 static int validate_cache_version(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
4059 struct tdb_validation_status *state)
4061 if (dbuf.dsize != 4) {
4062 DEBUG(0, ("validate_cache_version: Corrupt cache for "
4063 "key %s (len %u != 4) ?\n",
4064 keystr, (unsigned int)dbuf.dsize));
4065 state->bad_entry = true;
4066 state->success = false;
4070 DEBUG(10, ("validate_cache_version: %s ok\n", keystr));
4074 /***********************************************************************
4075 A list of all possible cache tdb keys with associated validation
4077 ***********************************************************************/
4079 struct key_val_struct {
4080 const char *keyname;
4081 int (*validate_data_fn)(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, struct tdb_validation_status* state);
4083 {"SEQNUM/", validate_seqnum},
4084 {"NS/", validate_ns},
4085 {"SN/", validate_sn},
4087 {"LOC_POL/", validate_loc_pol},
4088 {"PWD_POL/", validate_pwd_pol},
4089 {"CRED/", validate_cred},
4090 {"UL/", validate_ul},
4091 {"GL/", validate_gl},
4092 {"UG/", validate_ug},
4093 {"UA", validate_ua},
4094 {"GM/", validate_gm},
4095 {"DR/", validate_dr},
4096 {"DE/", validate_de},
4097 {"NSS/PWINFO/", validate_pwinfo},
4098 {"TRUSTDOMCACHE/", validate_trustdomcache},
4099 {"NSS/NA/", validate_nss_na},
4100 {"NSS/AN/", validate_nss_an},
4101 {"WINBINDD_OFFLINE", validate_offline},
4102 {"NDR/", validate_ndr},
4103 {WINBINDD_CACHE_VERSION_KEYSTR, validate_cache_version},
4107 /***********************************************************************
4108 Function to look at every entry in the tdb and validate it as far as
4110 ***********************************************************************/
4112 static int cache_traverse_validate_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, void *state)
4115 unsigned int max_key_len = 1024;
4116 struct tdb_validation_status *v_state = (struct tdb_validation_status *)state;
4118 /* Paranoia check. */
4119 if (strncmp("UA/", (const char *)kbuf.dptr, 3) == 0 ||
4120 strncmp("NDR/", (const char *)kbuf.dptr, 4) == 0) {
4121 max_key_len = 1024 * 1024;
4123 if (kbuf.dsize > max_key_len) {
4124 DEBUG(0, ("cache_traverse_validate_fn: key length too large: "
4126 (unsigned int)kbuf.dsize, (unsigned int)max_key_len));
4130 for (i = 0; key_val[i].keyname; i++) {
4131 size_t namelen = strlen(key_val[i].keyname);
4132 if (kbuf.dsize >= namelen && (
4133 strncmp(key_val[i].keyname, (const char *)kbuf.dptr, namelen)) == 0) {
4134 TALLOC_CTX *mem_ctx;
4138 keystr = SMB_MALLOC_ARRAY(char, kbuf.dsize+1);
4142 memcpy(keystr, kbuf.dptr, kbuf.dsize);
4143 keystr[kbuf.dsize] = '\0';
4145 mem_ctx = talloc_init("validate_ctx");
4151 ret = key_val[i].validate_data_fn(mem_ctx, keystr, dbuf,
4155 talloc_destroy(mem_ctx);
4160 DEBUG(0,("cache_traverse_validate_fn: unknown cache entry\nkey :\n"));
4161 dump_data(0, (uint8 *)kbuf.dptr, kbuf.dsize);
4162 DEBUG(0,("data :\n"));
4163 dump_data(0, (uint8 *)dbuf.dptr, dbuf.dsize);
4164 v_state->unknown_key = true;
4165 v_state->success = false;
4166 return 1; /* terminate. */
4169 static void validate_panic(const char *const why)
4171 DEBUG(0,("validating cache: would panic %s\n", why ));
4172 DEBUGADD(0, ("exiting instead (cache validation mode)\n"));
4176 static int wbcache_update_centry_fn(TDB_CONTEXT *tdb,
4184 if (is_non_centry_key(key)) {
4188 if (data.dptr == NULL || data.dsize == 0) {
4189 if (tdb_delete(tdb, key) < 0) {
4190 DEBUG(0, ("tdb_delete for [%s] failed!\n",
4196 /* add timeout to blob (uint64_t) */
4197 blob.dsize = data.dsize + 8;
4199 blob.dptr = SMB_XMALLOC_ARRAY(uint8_t, blob.dsize);
4200 if (blob.dptr == NULL) {
4203 memset(blob.dptr, 0, blob.dsize);
4205 /* copy status and seqnum */
4206 memcpy(blob.dptr, data.dptr, 8);
4209 ctimeout = lp_winbind_cache_time() + time(NULL);
4210 SBVAL(blob.dptr, 8, ctimeout);
4213 memcpy(blob.dptr + 16, data.dptr + 8, data.dsize - 8);
4215 if (tdb_store(tdb, key, blob, TDB_REPLACE) < 0) {
4216 DEBUG(0, ("tdb_store to update [%s] failed!\n",
4218 SAFE_FREE(blob.dptr);
4222 SAFE_FREE(blob.dptr);
4226 static bool wbcache_upgrade_v1_to_v2(TDB_CONTEXT *tdb)
4230 DEBUG(1, ("Upgrade to version 2 of the winbindd_cache.tdb\n"));
4232 rc = tdb_traverse(tdb, wbcache_update_centry_fn, NULL);
4240 /***********************************************************************
4241 Try and validate every entry in the winbindd cache. If we fail here,
4242 delete the cache tdb and return non-zero.
4243 ***********************************************************************/
4245 int winbindd_validate_cache(void)
4248 const char *tdb_path = state_path("winbindd_cache.tdb");
4249 TDB_CONTEXT *tdb = NULL;
4253 DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
4254 smb_panic_fn = validate_panic;
4256 tdb = tdb_open_log(tdb_path,
4257 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
4258 TDB_INCOMPATIBLE_HASH |
4259 ( lp_winbind_offline_logon()
4261 : TDB_DEFAULT | TDB_CLEAR_IF_FIRST ),
4265 DEBUG(0, ("winbindd_validate_cache: "
4266 "error opening/initializing tdb\n"));
4270 /* Version check and upgrade code. */
4271 if (!tdb_fetch_uint32(tdb, WINBINDD_CACHE_VERSION_KEYSTR, &vers_id)) {
4272 DEBUG(10, ("Fresh database\n"));
4273 tdb_store_uint32(tdb, WINBINDD_CACHE_VERSION_KEYSTR, WINBINDD_CACHE_VERSION);
4274 vers_id = WINBINDD_CACHE_VERSION;
4277 if (vers_id != WINBINDD_CACHE_VERSION) {
4278 if (vers_id == WINBINDD_CACHE_VER1) {
4279 ok = wbcache_upgrade_v1_to_v2(tdb);
4281 DEBUG(10, ("winbindd_validate_cache: upgrade to version 2 failed.\n"));
4286 tdb_store_uint32(tdb,
4287 WINBINDD_CACHE_VERSION_KEYSTR,
4288 WINBINDD_CACHE_VERSION);
4289 vers_id = WINBINDD_CACHE_VER2;
4295 ret = tdb_validate_and_backup(tdb_path, cache_traverse_validate_fn);
4298 DEBUG(10, ("winbindd_validate_cache: validation not successful.\n"));
4299 DEBUGADD(10, ("removing tdb %s.\n", tdb_path));
4304 DEBUG(10, ("winbindd_validate_cache: restoring panic function\n"));
4305 smb_panic_fn = smb_panic;
4309 /***********************************************************************
4310 Try and validate every entry in the winbindd cache.
4311 ***********************************************************************/
4313 int winbindd_validate_cache_nobackup(void)
4316 const char *tdb_path = state_path("winbindd_cache.tdb");
4318 DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
4319 smb_panic_fn = validate_panic;
4322 if (wcache == NULL || wcache->tdb == NULL) {
4323 ret = tdb_validate_open(tdb_path, cache_traverse_validate_fn);
4325 ret = tdb_validate(wcache->tdb, cache_traverse_validate_fn);
4329 DEBUG(10, ("winbindd_validate_cache_nobackup: validation not "
4333 DEBUG(10, ("winbindd_validate_cache_nobackup: restoring panic "
4335 smb_panic_fn = smb_panic;
4339 bool winbindd_cache_validate_and_initialize(void)
4341 close_winbindd_cache();
4343 if (lp_winbind_offline_logon()) {
4344 if (winbindd_validate_cache() < 0) {
4345 DEBUG(0, ("winbindd cache tdb corrupt and no backup "
4346 "could be restored.\n"));
4350 return initialize_winbindd_cache();
4353 /*********************************************************************
4354 ********************************************************************/
4356 static bool add_wbdomain_to_tdc_array( struct winbindd_domain *new_dom,
4357 struct winbindd_tdc_domain **domains,
4358 size_t *num_domains )
4360 struct winbindd_tdc_domain *list = NULL;
4363 bool set_only = false;
4365 /* don't allow duplicates */
4370 for ( i=0; i< (*num_domains); i++ ) {
4371 if ( strequal( new_dom->name, list[i].domain_name ) ) {
4372 DEBUG(10,("add_wbdomain_to_tdc_array: Found existing record for %s\n",
4383 list = talloc_array( NULL, struct winbindd_tdc_domain, 1 );
4386 list = talloc_realloc( *domains, *domains,
4387 struct winbindd_tdc_domain,
4392 ZERO_STRUCT( list[idx] );
4398 list[idx].domain_name = talloc_strdup(list, new_dom->name);
4399 if (list[idx].domain_name == NULL) {
4402 if (new_dom->alt_name != NULL) {
4403 list[idx].dns_name = talloc_strdup(list, new_dom->alt_name);
4404 if (list[idx].dns_name == NULL) {
4409 if ( !is_null_sid( &new_dom->sid ) ) {
4410 sid_copy( &list[idx].sid, &new_dom->sid );
4412 sid_copy(&list[idx].sid, &global_sid_NULL);
4415 if ( new_dom->domain_flags != 0x0 )
4416 list[idx].trust_flags = new_dom->domain_flags;
4418 if ( new_dom->domain_type != 0x0 )
4419 list[idx].trust_type = new_dom->domain_type;
4421 if ( new_dom->domain_trust_attribs != 0x0 )
4422 list[idx].trust_attribs = new_dom->domain_trust_attribs;
4426 *num_domains = idx + 1;
4432 /*********************************************************************
4433 ********************************************************************/
4435 static TDB_DATA make_tdc_key( const char *domain_name )
4437 char *keystr = NULL;
4438 TDB_DATA key = { NULL, 0 };
4440 if ( !domain_name ) {
4441 DEBUG(5,("make_tdc_key: Keyname workgroup is NULL!\n"));
4445 if (asprintf( &keystr, "TRUSTDOMCACHE/%s", domain_name ) == -1) {
4448 key = string_term_tdb_data(keystr);
4453 /*********************************************************************
4454 ********************************************************************/
4456 static int pack_tdc_domains( struct winbindd_tdc_domain *domains,
4458 unsigned char **buf )
4460 unsigned char *buffer = NULL;
4465 DEBUG(10,("pack_tdc_domains: Packing %d trusted domains\n",
4473 /* Store the number of array items first */
4474 len += tdb_pack( buffer+len, buflen-len, "d",
4477 /* now pack each domain trust record */
4478 for ( i=0; i<num_domains; i++ ) {
4483 DEBUG(10,("pack_tdc_domains: Packing domain %s (%s)\n",
4484 domains[i].domain_name,
4485 domains[i].dns_name ? domains[i].dns_name : "UNKNOWN" ));
4488 len += tdb_pack( buffer+len, buflen-len, "fffddd",
4489 domains[i].domain_name,
4490 domains[i].dns_name ? domains[i].dns_name : "",
4491 sid_to_fstring(tmp, &domains[i].sid),
4492 domains[i].trust_flags,
4493 domains[i].trust_attribs,
4494 domains[i].trust_type );
4497 if ( buflen < len ) {
4499 if ( (buffer = SMB_MALLOC_ARRAY(unsigned char, len)) == NULL ) {
4500 DEBUG(0,("pack_tdc_domains: failed to alloc buffer!\n"));
4514 /*********************************************************************
4515 ********************************************************************/
4517 static size_t unpack_tdc_domains( unsigned char *buf, int buflen,
4518 struct winbindd_tdc_domain **domains )
4520 fstring domain_name, dns_name, sid_string;
4521 uint32 type, attribs, flags;
4525 struct winbindd_tdc_domain *list = NULL;
4527 /* get the number of domains */
4528 len += tdb_unpack( buf+len, buflen-len, "d", &num_domains);
4530 DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
4534 list = talloc_array( NULL, struct winbindd_tdc_domain, num_domains );
4536 DEBUG(0,("unpack_tdc_domains: Failed to talloc() domain list!\n"));
4540 for ( i=0; i<num_domains; i++ ) {
4543 this_len = tdb_unpack( buf+len, buflen-len, "fffddd",
4551 if ( this_len == -1 ) {
4552 DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
4553 TALLOC_FREE( list );
4558 DEBUG(11,("unpack_tdc_domains: Unpacking domain %s (%s) "
4559 "SID %s, flags = 0x%x, attribs = 0x%x, type = 0x%x\n",
4560 domain_name, dns_name, sid_string,
4561 flags, attribs, type));
4563 list[i].domain_name = talloc_strdup( list, domain_name );
4564 list[i].dns_name = NULL;
4565 if (dns_name[0] != '\0') {
4566 list[i].dns_name = talloc_strdup(list, dns_name);
4568 if ( !string_to_sid( &(list[i].sid), sid_string ) ) {
4569 DEBUG(10,("unpack_tdc_domains: no SID for domain %s\n",
4572 list[i].trust_flags = flags;
4573 list[i].trust_attribs = attribs;
4574 list[i].trust_type = type;
4582 /*********************************************************************
4583 ********************************************************************/
4585 static bool wcache_tdc_store_list( struct winbindd_tdc_domain *domains, size_t num_domains )
4587 TDB_DATA key = make_tdc_key( lp_workgroup() );
4588 TDB_DATA data = { NULL, 0 };
4594 /* See if we were asked to delete the cache entry */
4597 ret = tdb_delete( wcache->tdb, key );
4601 data.dsize = pack_tdc_domains( domains, num_domains, &data.dptr );
4608 ret = tdb_store( wcache->tdb, key, data, 0 );
4611 SAFE_FREE( data.dptr );
4612 SAFE_FREE( key.dptr );
4614 return ( ret == 0 );
4617 /*********************************************************************
4618 ********************************************************************/
4620 bool wcache_tdc_fetch_list( struct winbindd_tdc_domain **domains, size_t *num_domains )
4622 TDB_DATA key = make_tdc_key( lp_workgroup() );
4623 TDB_DATA data = { NULL, 0 };
4631 data = tdb_fetch_compat( wcache->tdb, key );
4633 SAFE_FREE( key.dptr );
4638 *num_domains = unpack_tdc_domains( data.dptr, data.dsize, domains );
4640 SAFE_FREE( data.dptr );
4648 /*********************************************************************
4649 ********************************************************************/
4651 bool wcache_tdc_add_domain( struct winbindd_domain *domain )
4653 struct winbindd_tdc_domain *dom_list = NULL;
4654 size_t num_domains = 0;
4657 DEBUG(10,("wcache_tdc_add_domain: Adding domain %s (%s), SID %s, "
4658 "flags = 0x%x, attributes = 0x%x, type = 0x%x\n",
4659 domain->name, domain->alt_name,
4660 sid_string_dbg(&domain->sid),
4661 domain->domain_flags,
4662 domain->domain_trust_attribs,
4663 domain->domain_type));
4665 if ( !init_wcache() ) {
4669 /* fetch the list */
4671 wcache_tdc_fetch_list( &dom_list, &num_domains );
4673 /* add the new domain */
4675 if ( !add_wbdomain_to_tdc_array( domain, &dom_list, &num_domains ) ) {
4679 /* pack the domain */
4681 if ( !wcache_tdc_store_list( dom_list, num_domains ) ) {
4689 TALLOC_FREE( dom_list );
4694 static struct winbindd_tdc_domain *wcache_tdc_dup_domain(
4695 TALLOC_CTX *mem_ctx, const struct winbindd_tdc_domain *src)
4697 struct winbindd_tdc_domain *dst;
4699 dst = talloc(mem_ctx, struct winbindd_tdc_domain);
4703 dst->domain_name = talloc_strdup(dst, src->domain_name);
4704 if (dst->domain_name == NULL) {
4708 dst->dns_name = NULL;
4709 if (src->dns_name != NULL) {
4710 dst->dns_name = talloc_strdup(dst, src->dns_name);
4711 if (dst->dns_name == NULL) {
4716 sid_copy(&dst->sid, &src->sid);
4717 dst->trust_flags = src->trust_flags;
4718 dst->trust_type = src->trust_type;
4719 dst->trust_attribs = src->trust_attribs;
4726 /*********************************************************************
4727 ********************************************************************/
4729 struct winbindd_tdc_domain * wcache_tdc_fetch_domain( TALLOC_CTX *ctx, const char *name )
4731 struct winbindd_tdc_domain *dom_list = NULL;
4732 size_t num_domains = 0;
4734 struct winbindd_tdc_domain *d = NULL;
4736 DEBUG(10,("wcache_tdc_fetch_domain: Searching for domain %s\n", name));
4738 if ( !init_wcache() ) {
4742 /* fetch the list */
4744 wcache_tdc_fetch_list( &dom_list, &num_domains );
4746 for ( i=0; i<num_domains; i++ ) {
4747 if ( strequal(name, dom_list[i].domain_name) ||
4748 strequal(name, dom_list[i].dns_name) )
4750 DEBUG(10,("wcache_tdc_fetch_domain: Found domain %s\n",
4753 d = wcache_tdc_dup_domain(ctx, &dom_list[i]);
4758 TALLOC_FREE( dom_list );
4763 /*********************************************************************
4764 ********************************************************************/
4766 struct winbindd_tdc_domain*
4767 wcache_tdc_fetch_domainbysid(TALLOC_CTX *ctx,
4768 const struct dom_sid *sid)
4770 struct winbindd_tdc_domain *dom_list = NULL;
4771 size_t num_domains = 0;
4773 struct winbindd_tdc_domain *d = NULL;
4775 DEBUG(10,("wcache_tdc_fetch_domainbysid: Searching for domain %s\n",
4776 sid_string_dbg(sid)));
4778 if (!init_wcache()) {
4782 /* fetch the list */
4784 wcache_tdc_fetch_list(&dom_list, &num_domains);
4786 for (i = 0; i<num_domains; i++) {
4787 if (dom_sid_equal(sid, &(dom_list[i].sid))) {
4788 DEBUG(10, ("wcache_tdc_fetch_domainbysid: "
4789 "Found domain %s for SID %s\n",
4790 dom_list[i].domain_name,
4791 sid_string_dbg(sid)));
4793 d = wcache_tdc_dup_domain(ctx, &dom_list[i]);
4798 TALLOC_FREE(dom_list);
4804 /*********************************************************************
4805 ********************************************************************/
4807 void wcache_tdc_clear( void )
4809 if ( !init_wcache() )
4812 wcache_tdc_store_list( NULL, 0 );
4818 /*********************************************************************
4819 ********************************************************************/
4821 static void wcache_save_user_pwinfo(struct winbindd_domain *domain,
4823 const struct dom_sid *user_sid,
4824 const char *homedir,
4829 struct cache_entry *centry;
4832 if ( (centry = centry_start(domain, status)) == NULL )
4835 centry_put_string( centry, homedir );
4836 centry_put_string( centry, shell );
4837 centry_put_string( centry, gecos );
4838 centry_put_uint32( centry, gid );
4840 centry_end(centry, "NSS/PWINFO/%s", sid_to_fstring(tmp, user_sid) );
4842 DEBUG(10,("wcache_save_user_pwinfo: %s\n", sid_string_dbg(user_sid) ));
4844 centry_free(centry);
4849 NTSTATUS nss_get_info_cached( struct winbindd_domain *domain,
4850 const struct dom_sid *user_sid,
4852 const char **homedir, const char **shell,
4853 const char **gecos, gid_t *p_gid)
4855 struct winbind_cache *cache = get_cache(domain);
4856 struct cache_entry *centry = NULL;
4863 centry = wcache_fetch(cache, domain, "NSS/PWINFO/%s",
4864 sid_to_fstring(tmp, user_sid));
4869 *homedir = centry_string( centry, ctx );
4870 *shell = centry_string( centry, ctx );
4871 *gecos = centry_string( centry, ctx );
4872 *p_gid = centry_uint32( centry );
4874 centry_free(centry);
4876 DEBUG(10,("nss_get_info_cached: [Cached] - user_sid %s\n",
4877 sid_string_dbg(user_sid)));
4879 return NT_STATUS_OK;
4883 nt_status = nss_get_info( domain->name, user_sid, ctx,
4884 homedir, shell, gecos, p_gid );
4886 DEBUG(10, ("nss_get_info returned %s\n", nt_errstr(nt_status)));
4888 if ( NT_STATUS_IS_OK(nt_status) ) {
4889 DEBUG(10, ("result:\n\thomedir = '%s'\n", *homedir));
4890 DEBUGADD(10, ("\tshell = '%s'\n", *shell));
4891 DEBUGADD(10, ("\tgecos = '%s'\n", *gecos));
4892 DEBUGADD(10, ("\tgid = '%u'\n", (unsigned int)*p_gid));
4894 wcache_save_user_pwinfo( domain, nt_status, user_sid,
4895 *homedir, *shell, *gecos, *p_gid );
4898 if ( NT_STATUS_EQUAL( nt_status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND ) ) {
4899 DEBUG(5,("nss_get_info_cached: Setting domain %s offline\n",
4901 set_domain_offline( domain );
4909 /* the cache backend methods are exposed via this structure */
4910 struct winbindd_methods cache_methods = {
4928 static bool wcache_ndr_key(TALLOC_CTX *mem_ctx, const char *domain_name,
4929 uint32_t opnum, const DATA_BLOB *req,
4935 key = talloc_asprintf(mem_ctx, "NDR/%s/%d/", domain_name, (int)opnum);
4939 keylen = talloc_get_size(key) - 1;
4941 key = talloc_realloc(mem_ctx, key, char, keylen + req->length);
4945 memcpy(key + keylen, req->data, req->length);
4947 pkey->dptr = (uint8_t *)key;
4948 pkey->dsize = talloc_get_size(key);
4952 static bool wcache_opnum_cacheable(uint32_t opnum)
4955 case NDR_WBINT_PING:
4956 case NDR_WBINT_QUERYSEQUENCENUMBER:
4957 case NDR_WBINT_ALLOCATEUID:
4958 case NDR_WBINT_ALLOCATEGID:
4959 case NDR_WBINT_CHECKMACHINEACCOUNT:
4960 case NDR_WBINT_CHANGEMACHINEACCOUNT:
4961 case NDR_WBINT_PINGDC:
4967 bool wcache_fetch_ndr(TALLOC_CTX *mem_ctx, struct winbindd_domain *domain,
4968 uint32_t opnum, const DATA_BLOB *req, DATA_BLOB *resp)
4973 if (!wcache_opnum_cacheable(opnum) ||
4974 is_my_own_sam_domain(domain) ||
4975 is_builtin_domain(domain)) {
4979 if (wcache->tdb == NULL) {
4983 if (!wcache_ndr_key(talloc_tos(), domain->name, opnum, req, &key)) {
4986 data = tdb_fetch_compat(wcache->tdb, key);
4987 TALLOC_FREE(key.dptr);
4989 if (data.dptr == NULL) {
4992 if (data.dsize < 12) {
4996 if (!is_domain_offline(domain)) {
4997 uint32_t entry_seqnum, dom_seqnum, last_check;
4998 uint64_t entry_timeout;
5000 if (!wcache_fetch_seqnum(domain->name, &dom_seqnum,
5004 entry_seqnum = IVAL(data.dptr, 0);
5005 if (entry_seqnum != dom_seqnum) {
5006 DEBUG(10, ("Entry has wrong sequence number: %d\n",
5007 (int)entry_seqnum));
5010 entry_timeout = BVAL(data.dptr, 4);
5011 if (time(NULL) > entry_timeout) {
5012 DEBUG(10, ("Entry has timed out\n"));
5017 resp->data = (uint8_t *)talloc_memdup(mem_ctx, data.dptr + 12,
5019 if (resp->data == NULL) {
5020 DEBUG(10, ("talloc failed\n"));
5023 resp->length = data.dsize - 12;
5027 SAFE_FREE(data.dptr);
5031 void wcache_store_ndr(struct winbindd_domain *domain, uint32_t opnum,
5032 const DATA_BLOB *req, const DATA_BLOB *resp)
5035 uint32_t dom_seqnum, last_check;
5038 if (!wcache_opnum_cacheable(opnum) ||
5039 is_my_own_sam_domain(domain) ||
5040 is_builtin_domain(domain)) {
5044 if (wcache->tdb == NULL) {
5048 if (!wcache_fetch_seqnum(domain->name, &dom_seqnum, &last_check)) {
5049 DEBUG(10, ("could not fetch seqnum for domain %s\n",
5054 if (!wcache_ndr_key(talloc_tos(), domain->name, opnum, req, &key)) {
5058 timeout = time(NULL) + lp_winbind_cache_time();
5060 data.dsize = resp->length + 12;
5061 data.dptr = talloc_array(key.dptr, uint8_t, data.dsize);
5062 if (data.dptr == NULL) {
5066 SIVAL(data.dptr, 0, dom_seqnum);
5067 SBVAL(data.dptr, 4, timeout);
5068 memcpy(data.dptr + 12, resp->data, resp->length);
5070 tdb_store(wcache->tdb, key, data, 0);
5073 TALLOC_FREE(key.dptr);