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 reconnect_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_t 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 static char *wcache_path(void)
117 * Data needs to be kept persistent in state directory for
118 * running with "winbindd offline logon".
120 return state_path("winbindd_cache.tdb");
123 /* get the winbind_cache structure */
124 static struct winbind_cache *get_cache(struct winbindd_domain *domain)
126 struct winbind_cache *ret = wcache;
128 /* We have to know what type of domain we are dealing with first. */
130 if (domain->internal) {
131 domain->backend = &builtin_passdb_methods;
134 if (dom_sid_equal(&domain->sid, &global_sid_Builtin)) {
135 domain->initialized = true;
138 if (strequal(domain->name, get_global_sam_name()) &&
139 sid_check_is_our_sam(&domain->sid)) {
140 domain->backend = &sam_passdb_methods;
143 if ( !domain->initialized ) {
144 /* We do not need a connection to an RW DC for cache operation */
145 init_dc_connection(domain, false);
149 OK. Listen up because I'm only going to say this once.
150 We have the following scenarios to consider
151 (a) trusted AD domains on a Samba DC,
152 (b) trusted AD domains and we are joined to a non-kerberos domain
153 (c) trusted AD domains and we are joined to a kerberos (AD) domain
155 For (a) we can always contact the trusted domain using krb5
156 since we have the domain trust account password
158 For (b) we can only use RPC since we have no way of
159 getting a krb5 ticket in our own domain
161 For (c) we can always use krb5 since we have a kerberos trust
166 if (!domain->backend) {
168 struct winbindd_domain *our_domain = domain;
170 /* find our domain first so we can figure out if we
171 are joined to a kerberized domain */
173 if ( !domain->primary )
174 our_domain = find_our_domain();
176 if ((our_domain->active_directory || IS_DC)
177 && domain->active_directory
178 && !lp_winbind_rpc_only()) {
179 DEBUG(5,("get_cache: Setting ADS methods for domain %s\n", domain->name));
180 domain->backend = &reconnect_ads_methods;
182 #endif /* HAVE_ADS */
183 DEBUG(5,("get_cache: Setting MS-RPC methods for domain %s\n", domain->name));
184 domain->backend = &reconnect_methods;
187 #endif /* HAVE_ADS */
193 ret = SMB_XMALLOC_P(struct winbind_cache);
197 wcache_flush_cache();
203 free a centry structure
205 static void centry_free(struct cache_entry *centry)
209 SAFE_FREE(centry->data);
213 static bool centry_check_bytes(struct cache_entry *centry, size_t nbytes)
215 if (centry->len - centry->ofs < nbytes) {
216 DEBUG(0,("centry corruption? needed %u bytes, have %d\n",
217 (unsigned int)nbytes,
218 centry->len - centry->ofs));
225 pull a uint64_t from a cache entry
227 static uint64_t centry_uint64_t(struct cache_entry *centry)
231 if (!centry_check_bytes(centry, 8)) {
232 smb_panic_fn("centry_uint64_t");
234 ret = BVAL(centry->data, centry->ofs);
240 pull a uint32_t from a cache entry
242 static uint32_t centry_uint32(struct cache_entry *centry)
246 if (!centry_check_bytes(centry, 4)) {
247 smb_panic_fn("centry_uint32");
249 ret = IVAL(centry->data, centry->ofs);
255 pull a uint16_t from a cache entry
257 static uint16_t centry_uint16(struct cache_entry *centry)
260 if (!centry_check_bytes(centry, 2)) {
261 smb_panic_fn("centry_uint16");
263 ret = SVAL(centry->data, centry->ofs);
269 pull a uint8_t from a cache entry
271 static uint8_t centry_uint8(struct cache_entry *centry)
274 if (!centry_check_bytes(centry, 1)) {
275 smb_panic_fn("centry_uint8");
277 ret = CVAL(centry->data, centry->ofs);
283 pull a NTTIME from a cache entry
285 static NTTIME centry_nttime(struct cache_entry *centry)
288 if (!centry_check_bytes(centry, 8)) {
289 smb_panic_fn("centry_nttime");
291 ret = IVAL(centry->data, centry->ofs);
293 ret += (uint64_t)IVAL(centry->data, centry->ofs) << 32;
299 pull a time_t from a cache entry. time_t stored portably as a 64-bit time.
301 static time_t centry_time(struct cache_entry *centry)
303 return (time_t)centry_nttime(centry);
306 /* pull a string from a cache entry, using the supplied
309 static char *centry_string(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
314 len = centry_uint8(centry);
317 /* a deliberate NULL string */
321 if (!centry_check_bytes(centry, (size_t)len)) {
322 smb_panic_fn("centry_string");
325 ret = talloc_array(mem_ctx, char, len+1);
327 smb_panic_fn("centry_string out of memory\n");
329 memcpy(ret,centry->data + centry->ofs, len);
335 /* pull a hash16 from a cache entry, using the supplied
338 static char *centry_hash16(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
343 len = centry_uint8(centry);
346 DEBUG(0,("centry corruption? hash len (%u) != 16\n",
351 if (!centry_check_bytes(centry, 16)) {
355 ret = talloc_array(mem_ctx, char, 16);
357 smb_panic_fn("centry_hash out of memory\n");
359 memcpy(ret,centry->data + centry->ofs, 16);
364 /* pull a sid from a cache entry, using the supplied
367 static bool centry_sid(struct cache_entry *centry, struct dom_sid *sid)
372 sid_string = centry_string(centry, talloc_tos());
373 if (sid_string == NULL) {
376 ret = string_to_sid(sid, sid_string);
377 TALLOC_FREE(sid_string);
383 pull a NTSTATUS from a cache entry
385 static NTSTATUS centry_ntstatus(struct cache_entry *centry)
389 status = NT_STATUS(centry_uint32(centry));
394 /* the server is considered down if it can't give us a sequence number */
395 static bool wcache_server_down(struct winbindd_domain *domain)
402 ret = (domain->sequence_number == DOM_SEQUENCE_NONE);
405 DEBUG(10,("wcache_server_down: server for Domain %s down\n",
410 struct wcache_seqnum_state {
412 uint32_t *last_seq_check;
415 static int wcache_seqnum_parser(TDB_DATA key, TDB_DATA data,
418 struct wcache_seqnum_state *state = private_data;
420 if (data.dsize != 8) {
421 DEBUG(10, ("wcache_fetch_seqnum: invalid data size %d\n",
426 *state->seqnum = IVAL(data.dptr, 0);
427 *state->last_seq_check = IVAL(data.dptr, 4);
431 static bool wcache_fetch_seqnum(const char *domain_name, uint32_t *seqnum,
432 uint32_t *last_seq_check)
434 struct wcache_seqnum_state state = {
435 .seqnum = seqnum, .last_seq_check = last_seq_check
437 size_t len = strlen(domain_name);
439 TDB_DATA key = { .dptr = (uint8_t *)keystr, .dsize = sizeof(keystr) };
442 if (wcache->tdb == NULL) {
443 DEBUG(10,("wcache_fetch_seqnum: tdb == NULL\n"));
447 snprintf(keystr, sizeof(keystr), "SEQNUM/%s", domain_name);
449 ret = tdb_parse_record(wcache->tdb, key, wcache_seqnum_parser,
454 static NTSTATUS fetch_cache_seqnum( struct winbindd_domain *domain, time_t now )
456 uint32_t last_check, time_diff;
458 if (!wcache_fetch_seqnum(domain->name, &domain->sequence_number,
460 return NT_STATUS_UNSUCCESSFUL;
462 domain->last_seq_check = last_check;
464 /* have we expired? */
466 time_diff = now - domain->last_seq_check;
467 if ( time_diff > lp_winbind_cache_time() ) {
468 DEBUG(10,("fetch_cache_seqnum: timeout [%s][%u @ %u]\n",
469 domain->name, domain->sequence_number,
470 (uint32_t)domain->last_seq_check));
471 return NT_STATUS_UNSUCCESSFUL;
474 DEBUG(10,("fetch_cache_seqnum: success [%s][%u @ %u]\n",
475 domain->name, domain->sequence_number,
476 (uint32_t)domain->last_seq_check));
481 bool wcache_store_seqnum(const char *domain_name, uint32_t seqnum,
482 time_t last_seq_check)
484 size_t len = strlen(domain_name);
486 TDB_DATA key = { .dptr = (uint8_t *)keystr, .dsize = sizeof(keystr) };
490 if (wcache->tdb == NULL) {
491 DEBUG(10, ("wcache_store_seqnum: wcache->tdb == NULL\n"));
495 snprintf(keystr, sizeof(keystr), "SEQNUM/%s", domain_name);
497 SIVAL(buf, 0, seqnum);
498 SIVAL(buf, 4, last_seq_check);
500 ret = tdb_store(wcache->tdb, key, make_tdb_data(buf, sizeof(buf)),
503 DEBUG(10, ("tdb_store_bystring failed: %s\n",
504 tdb_errorstr(wcache->tdb)));
508 DEBUG(10, ("wcache_store_seqnum: success [%s][%u @ %u]\n",
509 domain_name, seqnum, (unsigned)last_seq_check));
514 static bool store_cache_seqnum( struct winbindd_domain *domain )
516 return wcache_store_seqnum(domain->name, domain->sequence_number,
517 domain->last_seq_check);
521 refresh the domain sequence number on timeout.
524 static void refresh_sequence_number(struct winbindd_domain *domain)
528 time_t t = time(NULL);
529 unsigned cache_time = lp_winbind_cache_time();
531 if (is_domain_offline(domain)) {
537 #if 0 /* JERRY -- disable as the default cache time is now 5 minutes */
538 /* trying to reconnect is expensive, don't do it too often */
539 if (domain->sequence_number == DOM_SEQUENCE_NONE) {
544 time_diff = t - domain->last_seq_check;
546 /* see if we have to refetch the domain sequence number */
547 if ((time_diff < cache_time) &&
548 (domain->sequence_number != DOM_SEQUENCE_NONE) &&
549 NT_STATUS_IS_OK(domain->last_status)) {
550 DEBUG(10, ("refresh_sequence_number: %s time ok\n", domain->name));
554 /* try to get the sequence number from the tdb cache first */
555 /* this will update the timestamp as well */
557 status = fetch_cache_seqnum( domain, t );
558 if (NT_STATUS_IS_OK(status) &&
559 (domain->sequence_number != DOM_SEQUENCE_NONE) &&
560 NT_STATUS_IS_OK(domain->last_status)) {
564 /* important! make sure that we know if this is a native
565 mode domain or not. And that we can contact it. */
567 if ( winbindd_can_contact_domain( domain ) ) {
568 status = domain->backend->sequence_number(domain,
569 &domain->sequence_number);
571 /* just use the current time */
572 status = NT_STATUS_OK;
573 domain->sequence_number = time(NULL);
577 /* the above call could have set our domain->backend to NULL when
578 * coming from offline to online mode, make sure to reinitialize the
579 * backend - Guenther */
582 if (!NT_STATUS_IS_OK(status)) {
583 DEBUG(10,("refresh_sequence_number: failed with %s\n", nt_errstr(status)));
584 domain->sequence_number = DOM_SEQUENCE_NONE;
587 domain->last_status = status;
588 domain->last_seq_check = time(NULL);
590 /* save the new sequence number in the cache */
591 store_cache_seqnum( domain );
594 DEBUG(10, ("refresh_sequence_number: %s seq number is now %d\n",
595 domain->name, domain->sequence_number));
601 decide if a cache entry has expired
603 static bool centry_expired(struct winbindd_domain *domain, const char *keystr, struct cache_entry *centry)
605 /* If we've been told to be offline - stay in that state... */
606 if (lp_winbind_offline_logon() && global_winbindd_offline_state) {
607 DEBUG(10,("centry_expired: Key %s for domain %s valid as winbindd is globally offline.\n",
608 keystr, domain->name ));
612 /* when the domain is offline return the cached entry.
613 * This deals with transient offline states... */
615 if (!domain->online) {
616 DEBUG(10,("centry_expired: Key %s for domain %s valid as domain is offline.\n",
617 keystr, domain->name ));
621 /* if the server is OK and our cache entry came from when it was down then
622 the entry is invalid */
623 if ((domain->sequence_number != DOM_SEQUENCE_NONE) &&
624 (centry->sequence_number == DOM_SEQUENCE_NONE)) {
625 DEBUG(10,("centry_expired: Key %s for domain %s invalid sequence.\n",
626 keystr, domain->name ));
630 /* if the server is down or the cache entry is not older than the
631 current sequence number or it did not timeout then it is OK */
632 if (wcache_server_down(domain)
633 || ((centry->sequence_number == domain->sequence_number)
634 && (centry->timeout > time(NULL)))) {
635 DEBUG(10,("centry_expired: Key %s for domain %s is good.\n",
636 keystr, domain->name ));
640 DEBUG(10,("centry_expired: Key %s for domain %s expired\n",
641 keystr, domain->name ));
647 static struct cache_entry *wcache_fetch_raw(char *kstr)
650 struct cache_entry *centry;
653 key = string_tdb_data(kstr);
654 data = tdb_fetch(wcache->tdb, key);
660 centry = SMB_XMALLOC_P(struct cache_entry);
661 centry->data = (unsigned char *)data.dptr;
662 centry->len = data.dsize;
665 if (centry->len < 16) {
666 /* huh? corrupt cache? */
667 DEBUG(10,("wcache_fetch_raw: Corrupt cache for key %s "
668 "(len < 16)?\n", kstr));
673 centry->status = centry_ntstatus(centry);
674 centry->sequence_number = centry_uint32(centry);
675 centry->timeout = centry_uint64_t(centry);
680 static bool is_my_own_sam_domain(struct winbindd_domain *domain)
682 if (strequal(domain->name, get_global_sam_name()) &&
683 sid_check_is_our_sam(&domain->sid)) {
690 static bool is_builtin_domain(struct winbindd_domain *domain)
692 if (strequal(domain->name, "BUILTIN") &&
693 sid_check_is_builtin(&domain->sid)) {
701 fetch an entry from the cache, with a varargs key. auto-fetch the sequence
702 number and return status
704 static struct cache_entry *wcache_fetch(struct winbind_cache *cache,
705 struct winbindd_domain *domain,
706 const char *format, ...) PRINTF_ATTRIBUTE(3,4);
707 static struct cache_entry *wcache_fetch(struct winbind_cache *cache,
708 struct winbindd_domain *domain,
709 const char *format, ...)
713 struct cache_entry *centry;
715 if (!winbindd_use_cache() ||
716 is_my_own_sam_domain(domain) ||
717 is_builtin_domain(domain)) {
721 refresh_sequence_number(domain);
723 va_start(ap, format);
724 smb_xvasprintf(&kstr, format, ap);
727 centry = wcache_fetch_raw(kstr);
728 if (centry == NULL) {
733 if (centry_expired(domain, kstr, centry)) {
735 DEBUG(10,("wcache_fetch: entry %s expired for domain %s\n",
736 kstr, domain->name ));
743 DEBUG(10,("wcache_fetch: returning entry %s for domain %s\n",
744 kstr, domain->name ));
750 static void wcache_delete(const char *format, ...) PRINTF_ATTRIBUTE(1,2);
751 static void wcache_delete(const char *format, ...)
757 va_start(ap, format);
758 smb_xvasprintf(&kstr, format, ap);
761 key = string_tdb_data(kstr);
763 tdb_delete(wcache->tdb, key);
768 make sure we have at least len bytes available in a centry
770 static void centry_expand(struct cache_entry *centry, uint32_t len)
772 if (centry->len - centry->ofs >= len)
775 centry->data = SMB_REALLOC_ARRAY(centry->data, unsigned char,
778 DEBUG(0,("out of memory: needed %d bytes in centry_expand\n", centry->len));
779 smb_panic_fn("out of memory in centry_expand");
784 push a uint64_t into a centry
786 static void centry_put_uint64_t(struct cache_entry *centry, uint64_t v)
788 centry_expand(centry, 8);
789 SBVAL(centry->data, centry->ofs, v);
794 push a uint32_t into a centry
796 static void centry_put_uint32(struct cache_entry *centry, uint32_t v)
798 centry_expand(centry, 4);
799 SIVAL(centry->data, centry->ofs, v);
804 push a uint16_t into a centry
806 static void centry_put_uint16(struct cache_entry *centry, uint16_t v)
808 centry_expand(centry, 2);
809 SSVAL(centry->data, centry->ofs, v);
814 push a uint8_t into a centry
816 static void centry_put_uint8(struct cache_entry *centry, uint8_t v)
818 centry_expand(centry, 1);
819 SCVAL(centry->data, centry->ofs, v);
824 push a string into a centry
826 static void centry_put_string(struct cache_entry *centry, const char *s)
831 /* null strings are marked as len 0xFFFF */
832 centry_put_uint8(centry, 0xFF);
837 /* can't handle more than 254 char strings. Truncating is probably best */
839 DEBUG(10,("centry_put_string: truncating len (%d) to: 254\n", len));
842 centry_put_uint8(centry, len);
843 centry_expand(centry, len);
844 memcpy(centry->data + centry->ofs, s, len);
849 push a 16 byte hash into a centry - treat as 16 byte string.
851 static void centry_put_hash16(struct cache_entry *centry, const uint8_t val[16])
853 centry_put_uint8(centry, 16);
854 centry_expand(centry, 16);
855 memcpy(centry->data + centry->ofs, val, 16);
859 static void centry_put_sid(struct cache_entry *centry, const struct dom_sid *sid)
862 centry_put_string(centry, sid_to_fstring(sid_string, sid));
867 put NTSTATUS into a centry
869 static void centry_put_ntstatus(struct cache_entry *centry, NTSTATUS status)
871 uint32_t status_value = NT_STATUS_V(status);
872 centry_put_uint32(centry, status_value);
877 push a NTTIME into a centry
879 static void centry_put_nttime(struct cache_entry *centry, NTTIME nt)
881 centry_expand(centry, 8);
882 SIVAL(centry->data, centry->ofs, nt & 0xFFFFFFFF);
884 SIVAL(centry->data, centry->ofs, nt >> 32);
889 push a time_t into a centry - use a 64 bit size.
890 NTTIME here is being used as a convenient 64-bit size.
892 static void centry_put_time(struct cache_entry *centry, time_t t)
894 NTTIME nt = (NTTIME)t;
895 centry_put_nttime(centry, nt);
899 start a centry for output. When finished, call centry_end()
901 static struct cache_entry *centry_start(struct winbindd_domain *domain,
904 struct cache_entry *centry;
909 centry = SMB_XMALLOC_P(struct cache_entry);
911 centry->len = 8192; /* reasonable default */
912 centry->data = SMB_XMALLOC_ARRAY(uint8_t, centry->len);
914 centry->sequence_number = domain->sequence_number;
915 centry->timeout = lp_winbind_cache_time() + time(NULL);
916 centry_put_ntstatus(centry, status);
917 centry_put_uint32(centry, centry->sequence_number);
918 centry_put_uint64_t(centry, centry->timeout);
923 finish a centry and write it to the tdb
925 static void centry_end(struct cache_entry *centry, const char *format, ...) PRINTF_ATTRIBUTE(2,3);
926 static void centry_end(struct cache_entry *centry, const char *format, ...)
932 if (!winbindd_use_cache()) {
936 va_start(ap, format);
937 smb_xvasprintf(&kstr, format, ap);
940 key = string_tdb_data(kstr);
941 data.dptr = centry->data;
942 data.dsize = centry->ofs;
944 tdb_store(wcache->tdb, key, data, TDB_REPLACE);
948 static void wcache_save_name_to_sid(struct winbindd_domain *domain,
949 NTSTATUS status, const char *domain_name,
950 const char *name, const struct dom_sid *sid,
951 enum lsa_SidType type)
953 struct cache_entry *centry;
956 centry = centry_start(domain, status);
960 if ((domain_name == NULL) || (domain_name[0] == '\0')) {
961 struct winbindd_domain *mydomain =
962 find_domain_from_sid_noinit(sid);
963 if (mydomain != NULL) {
964 domain_name = mydomain->name;
968 centry_put_uint32(centry, type);
969 centry_put_sid(centry, sid);
970 fstrcpy(uname, name);
971 (void)strupper_m(uname);
972 centry_end(centry, "NS/%s/%s", domain_name, uname);
973 DEBUG(10,("wcache_save_name_to_sid: %s\\%s -> %s (%s)\n", domain_name,
974 uname, sid_string_dbg(sid), nt_errstr(status)));
978 static void wcache_save_sid_to_name(struct winbindd_domain *domain, NTSTATUS status,
979 const struct dom_sid *sid, const char *domain_name, const char *name, enum lsa_SidType type)
981 struct cache_entry *centry;
984 centry = centry_start(domain, status);
988 if ((domain_name == NULL) || (domain_name[0] == '\0')) {
989 struct winbindd_domain *mydomain =
990 find_domain_from_sid_noinit(sid);
991 if (mydomain != NULL) {
992 domain_name = mydomain->name;
996 if (NT_STATUS_IS_OK(status)) {
997 centry_put_uint32(centry, type);
998 centry_put_string(centry, domain_name);
999 centry_put_string(centry, name);
1002 centry_end(centry, "SN/%s", sid_to_fstring(sid_string, sid));
1003 DEBUG(10,("wcache_save_sid_to_name: %s -> %s\\%s (%s)\n", sid_string,
1004 domain_name, name, nt_errstr(status)));
1005 centry_free(centry);
1009 static void wcache_save_user(struct winbindd_domain *domain, NTSTATUS status,
1010 struct wbint_userinfo *info)
1012 struct cache_entry *centry;
1015 if (is_null_sid(&info->user_sid)) {
1019 centry = centry_start(domain, status);
1022 centry_put_string(centry, info->acct_name);
1023 centry_put_string(centry, info->full_name);
1024 centry_put_string(centry, info->homedir);
1025 centry_put_string(centry, info->shell);
1026 centry_put_uint32(centry, info->primary_gid);
1027 centry_put_sid(centry, &info->user_sid);
1028 centry_put_sid(centry, &info->group_sid);
1029 centry_end(centry, "U/%s", sid_to_fstring(sid_string,
1031 DEBUG(10,("wcache_save_user: %s (acct_name %s)\n", sid_string, info->acct_name));
1032 centry_free(centry);
1035 static void wcache_save_lockout_policy(struct winbindd_domain *domain,
1037 struct samr_DomInfo12 *lockout_policy)
1039 struct cache_entry *centry;
1041 centry = centry_start(domain, status);
1045 centry_put_nttime(centry, lockout_policy->lockout_duration);
1046 centry_put_nttime(centry, lockout_policy->lockout_window);
1047 centry_put_uint16(centry, lockout_policy->lockout_threshold);
1049 centry_end(centry, "LOC_POL/%s", domain->name);
1051 DEBUG(10,("wcache_save_lockout_policy: %s\n", domain->name));
1053 centry_free(centry);
1058 static void wcache_save_password_policy(struct winbindd_domain *domain,
1060 struct samr_DomInfo1 *policy)
1062 struct cache_entry *centry;
1064 centry = centry_start(domain, status);
1068 centry_put_uint16(centry, policy->min_password_length);
1069 centry_put_uint16(centry, policy->password_history_length);
1070 centry_put_uint32(centry, policy->password_properties);
1071 centry_put_nttime(centry, policy->max_password_age);
1072 centry_put_nttime(centry, policy->min_password_age);
1074 centry_end(centry, "PWD_POL/%s", domain->name);
1076 DEBUG(10,("wcache_save_password_policy: %s\n", domain->name));
1078 centry_free(centry);
1081 /***************************************************************************
1082 ***************************************************************************/
1084 static void wcache_save_username_alias(struct winbindd_domain *domain,
1086 const char *name, const char *alias)
1088 struct cache_entry *centry;
1091 if ( (centry = centry_start(domain, status)) == NULL )
1094 centry_put_string( centry, alias );
1096 fstrcpy(uname, name);
1097 (void)strupper_m(uname);
1098 centry_end(centry, "NSS/NA/%s", uname);
1100 DEBUG(10,("wcache_save_username_alias: %s -> %s\n", name, alias ));
1102 centry_free(centry);
1105 static void wcache_save_alias_username(struct winbindd_domain *domain,
1107 const char *alias, const char *name)
1109 struct cache_entry *centry;
1112 if ( (centry = centry_start(domain, status)) == NULL )
1115 centry_put_string( centry, name );
1117 fstrcpy(uname, alias);
1118 (void)strupper_m(uname);
1119 centry_end(centry, "NSS/AN/%s", uname);
1121 DEBUG(10,("wcache_save_alias_username: %s -> %s\n", alias, name ));
1123 centry_free(centry);
1126 /***************************************************************************
1127 ***************************************************************************/
1129 NTSTATUS resolve_username_to_alias( TALLOC_CTX *mem_ctx,
1130 struct winbindd_domain *domain,
1131 const char *name, char **alias )
1133 struct winbind_cache *cache = get_cache(domain);
1134 struct cache_entry *centry = NULL;
1138 if ( domain->internal )
1139 return NT_STATUS_NOT_SUPPORTED;
1144 upper_name = talloc_strdup(mem_ctx, name);
1145 if (upper_name == NULL) {
1146 return NT_STATUS_NO_MEMORY;
1148 if (!strupper_m(upper_name)) {
1149 talloc_free(upper_name);
1150 return NT_STATUS_INVALID_PARAMETER;
1153 centry = wcache_fetch(cache, domain, "NSS/NA/%s", upper_name);
1155 talloc_free(upper_name);
1160 status = centry->status;
1162 if (!NT_STATUS_IS_OK(status)) {
1163 centry_free(centry);
1167 *alias = centry_string( centry, mem_ctx );
1169 centry_free(centry);
1171 DEBUG(10,("resolve_username_to_alias: [Cached] - mapped %s to %s\n",
1172 name, *alias ? *alias : "(none)"));
1174 return (*alias) ? NT_STATUS_OK : NT_STATUS_OBJECT_NAME_NOT_FOUND;
1178 /* If its not in cache and we are offline, then fail */
1180 if ( get_global_winbindd_state_offline() || !domain->online ) {
1181 DEBUG(8,("resolve_username_to_alias: rejecting query "
1182 "in offline mode\n"));
1183 return NT_STATUS_NOT_FOUND;
1186 status = nss_map_to_alias( mem_ctx, domain->name, name, alias );
1188 if ( NT_STATUS_IS_OK( status ) ) {
1189 wcache_save_username_alias(domain, status, name, *alias);
1192 if ( NT_STATUS_EQUAL( status, NT_STATUS_NONE_MAPPED ) ) {
1193 wcache_save_username_alias(domain, status, name, "(NULL)");
1196 DEBUG(5,("resolve_username_to_alias: backend query returned %s\n",
1197 nt_errstr(status)));
1199 if ( NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ) {
1200 set_domain_offline( domain );
1206 /***************************************************************************
1207 ***************************************************************************/
1209 NTSTATUS resolve_alias_to_username( TALLOC_CTX *mem_ctx,
1210 struct winbindd_domain *domain,
1211 const char *alias, char **name )
1213 struct winbind_cache *cache = get_cache(domain);
1214 struct cache_entry *centry = NULL;
1218 if ( domain->internal )
1219 return NT_STATUS_NOT_SUPPORTED;
1224 upper_name = talloc_strdup(mem_ctx, alias);
1225 if (upper_name == NULL) {
1226 return NT_STATUS_NO_MEMORY;
1228 if (!strupper_m(upper_name)) {
1229 talloc_free(upper_name);
1230 return NT_STATUS_INVALID_PARAMETER;
1233 centry = wcache_fetch(cache, domain, "NSS/AN/%s", upper_name);
1235 talloc_free(upper_name);
1240 status = centry->status;
1242 if (!NT_STATUS_IS_OK(status)) {
1243 centry_free(centry);
1247 *name = centry_string( centry, mem_ctx );
1249 centry_free(centry);
1251 DEBUG(10,("resolve_alias_to_username: [Cached] - mapped %s to %s\n",
1252 alias, *name ? *name : "(none)"));
1254 return (*name) ? NT_STATUS_OK : NT_STATUS_OBJECT_NAME_NOT_FOUND;
1258 /* If its not in cache and we are offline, then fail */
1260 if ( get_global_winbindd_state_offline() || !domain->online ) {
1261 DEBUG(8,("resolve_alias_to_username: rejecting query "
1262 "in offline mode\n"));
1263 return NT_STATUS_NOT_FOUND;
1266 /* an alias cannot contain a domain prefix or '@' */
1268 if (strchr(alias, '\\') || strchr(alias, '@')) {
1269 DEBUG(10,("resolve_alias_to_username: skipping fully "
1270 "qualified name %s\n", alias));
1271 return NT_STATUS_OBJECT_NAME_INVALID;
1274 status = nss_map_from_alias( mem_ctx, domain->name, alias, name );
1276 if ( NT_STATUS_IS_OK( status ) ) {
1277 wcache_save_alias_username( domain, status, alias, *name );
1280 if (NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) {
1281 wcache_save_alias_username(domain, status, alias, "(NULL)");
1284 DEBUG(5,("resolve_alias_to_username: backend query returned %s\n",
1285 nt_errstr(status)));
1287 if ( NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ) {
1288 set_domain_offline( domain );
1294 NTSTATUS wcache_cached_creds_exist(struct winbindd_domain *domain, const struct dom_sid *sid)
1296 struct winbind_cache *cache = get_cache(domain);
1298 fstring key_str, tmp;
1302 return NT_STATUS_INTERNAL_DB_ERROR;
1305 if (is_null_sid(sid)) {
1306 return NT_STATUS_INVALID_SID;
1309 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1310 return NT_STATUS_INVALID_SID;
1313 fstr_sprintf(key_str, "CRED/%s", sid_to_fstring(tmp, sid));
1315 data = tdb_fetch(cache->tdb, string_tdb_data(key_str));
1317 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1320 SAFE_FREE(data.dptr);
1321 return NT_STATUS_OK;
1324 /* Lookup creds for a SID - copes with old (unsalted) creds as well
1325 as new salted ones. */
1327 NTSTATUS wcache_get_creds(struct winbindd_domain *domain,
1328 TALLOC_CTX *mem_ctx,
1329 const struct dom_sid *sid,
1330 const uint8_t **cached_nt_pass,
1331 const uint8_t **cached_salt)
1333 struct winbind_cache *cache = get_cache(domain);
1334 struct cache_entry *centry = NULL;
1340 return NT_STATUS_INTERNAL_DB_ERROR;
1343 if (is_null_sid(sid)) {
1344 return NT_STATUS_INVALID_SID;
1347 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1348 return NT_STATUS_INVALID_SID;
1351 /* Try and get a salted cred first. If we can't
1352 fall back to an unsalted cred. */
1354 centry = wcache_fetch(cache, domain, "CRED/%s",
1355 sid_to_fstring(tmp, sid));
1357 DEBUG(10,("wcache_get_creds: entry for [CRED/%s] not found\n",
1358 sid_string_dbg(sid)));
1359 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1363 * We don't use the time element at this moment,
1364 * but we have to consume it, so that we don't
1365 * neet to change the disk format of the cache.
1367 (void)centry_time(centry);
1369 /* In the salted case this isn't actually the nt_hash itself,
1370 but the MD5 of the salt + nt_hash. Let the caller
1371 sort this out. It can tell as we only return the cached_salt
1372 if we are returning a salted cred. */
1374 *cached_nt_pass = (const uint8_t *)centry_hash16(centry, mem_ctx);
1375 if (*cached_nt_pass == NULL) {
1378 sid_to_fstring(sidstr, sid);
1380 /* Bad (old) cred cache. Delete and pretend we
1382 DEBUG(0,("wcache_get_creds: bad entry for [CRED/%s] - deleting\n",
1384 wcache_delete("CRED/%s", sidstr);
1385 centry_free(centry);
1386 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1389 /* We only have 17 bytes more data in the salted cred case. */
1390 if (centry->len - centry->ofs == 17) {
1391 *cached_salt = (const uint8_t *)centry_hash16(centry, mem_ctx);
1393 *cached_salt = NULL;
1396 dump_data_pw("cached_nt_pass", *cached_nt_pass, NT_HASH_LEN);
1398 dump_data_pw("cached_salt", *cached_salt, NT_HASH_LEN);
1401 status = centry->status;
1403 DEBUG(10,("wcache_get_creds: [Cached] - cached creds for user %s status: %s\n",
1404 sid_string_dbg(sid), nt_errstr(status) ));
1406 centry_free(centry);
1410 /* Store creds for a SID - only writes out new salted ones. */
1412 NTSTATUS wcache_save_creds(struct winbindd_domain *domain,
1413 const struct dom_sid *sid,
1414 const uint8_t nt_pass[NT_HASH_LEN])
1416 struct cache_entry *centry;
1419 uint8_t cred_salt[NT_HASH_LEN];
1420 uint8_t salted_hash[NT_HASH_LEN];
1422 if (is_null_sid(sid)) {
1423 return NT_STATUS_INVALID_SID;
1426 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1427 return NT_STATUS_INVALID_SID;
1430 centry = centry_start(domain, NT_STATUS_OK);
1432 return NT_STATUS_INTERNAL_DB_ERROR;
1435 dump_data_pw("nt_pass", nt_pass, NT_HASH_LEN);
1437 centry_put_time(centry, time(NULL));
1439 /* Create a salt and then salt the hash. */
1440 generate_random_buffer(cred_salt, NT_HASH_LEN);
1441 E_md5hash(cred_salt, nt_pass, salted_hash);
1443 centry_put_hash16(centry, salted_hash);
1444 centry_put_hash16(centry, cred_salt);
1445 centry_end(centry, "CRED/%s", sid_to_fstring(sid_string, sid));
1447 DEBUG(10,("wcache_save_creds: %s\n", sid_string));
1449 centry_free(centry);
1451 return NT_STATUS_OK;
1455 /* Query display info. This is the basic user list fn */
1456 static NTSTATUS query_user_list(struct winbindd_domain *domain,
1457 TALLOC_CTX *mem_ctx,
1458 uint32_t *num_entries,
1459 struct wbint_userinfo **info)
1461 struct winbind_cache *cache = get_cache(domain);
1462 struct cache_entry *centry = NULL;
1464 unsigned int i, retry;
1465 bool old_status = domain->online;
1470 centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
1475 *num_entries = centry_uint32(centry);
1477 if (*num_entries == 0)
1480 (*info) = talloc_array(mem_ctx, struct wbint_userinfo, *num_entries);
1482 smb_panic_fn("query_user_list out of memory");
1484 for (i=0; i<(*num_entries); i++) {
1485 (*info)[i].acct_name = centry_string(centry, mem_ctx);
1486 (*info)[i].full_name = centry_string(centry, mem_ctx);
1487 (*info)[i].homedir = centry_string(centry, mem_ctx);
1488 (*info)[i].shell = centry_string(centry, mem_ctx);
1489 centry_sid(centry, &(*info)[i].user_sid);
1490 centry_sid(centry, &(*info)[i].group_sid);
1494 status = centry->status;
1496 DEBUG(10,("query_user_list: [Cached] - cached list for domain %s status: %s\n",
1497 domain->name, nt_errstr(status) ));
1499 centry_free(centry);
1506 /* Return status value returned by seq number check */
1508 if (!NT_STATUS_IS_OK(domain->last_status))
1509 return domain->last_status;
1511 /* Put the query_user_list() in a retry loop. There appears to be
1512 * some bug either with Windows 2000 or Samba's handling of large
1513 * rpc replies. This manifests itself as sudden disconnection
1514 * at a random point in the enumeration of a large (60k) user list.
1515 * The retry loop simply tries the operation again. )-: It's not
1516 * pretty but an acceptable workaround until we work out what the
1517 * real problem is. */
1522 DEBUG(10,("query_user_list: [Cached] - doing backend query for list for domain %s\n",
1525 status = domain->backend->query_user_list(domain, mem_ctx, num_entries, info);
1526 if (!NT_STATUS_IS_OK(status)) {
1527 DEBUG(3, ("query_user_list: returned 0x%08x, "
1528 "retrying\n", NT_STATUS_V(status)));
1530 if (NT_STATUS_EQUAL(status, NT_STATUS_UNSUCCESSFUL)) {
1531 DEBUG(3, ("query_user_list: flushing "
1532 "connection cache\n"));
1533 invalidate_cm_connection(domain);
1535 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1536 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1537 if (!domain->internal && old_status) {
1538 set_domain_offline(domain);
1540 /* store partial response. */
1541 if (*num_entries > 0) {
1543 * humm, what about the status used for cache?
1544 * Should it be NT_STATUS_OK?
1549 * domain is offline now, and there is no user entries,
1550 * try to fetch from cache again.
1552 if (cache->tdb && !domain->online && !domain->internal && old_status) {
1553 centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
1554 /* partial response... */
1558 goto do_fetch_cache;
1565 } while (NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_UNSUCCESSFUL) &&
1569 refresh_sequence_number(domain);
1570 if (!NT_STATUS_IS_OK(status)) {
1573 centry = centry_start(domain, status);
1576 centry_put_uint32(centry, *num_entries);
1577 for (i=0; i<(*num_entries); i++) {
1578 centry_put_string(centry, (*info)[i].acct_name);
1579 centry_put_string(centry, (*info)[i].full_name);
1580 centry_put_string(centry, (*info)[i].homedir);
1581 centry_put_string(centry, (*info)[i].shell);
1582 centry_put_sid(centry, &(*info)[i].user_sid);
1583 centry_put_sid(centry, &(*info)[i].group_sid);
1584 if (domain->backend && domain->backend->consistent) {
1585 /* when the backend is consistent we can pre-prime some mappings */
1586 wcache_save_name_to_sid(domain, NT_STATUS_OK,
1588 (*info)[i].acct_name,
1589 &(*info)[i].user_sid,
1591 wcache_save_sid_to_name(domain, NT_STATUS_OK,
1592 &(*info)[i].user_sid,
1594 (*info)[i].acct_name,
1596 wcache_save_user(domain, NT_STATUS_OK, &(*info)[i]);
1599 centry_end(centry, "UL/%s", domain->name);
1600 centry_free(centry);
1606 /* list all domain groups */
1607 static NTSTATUS enum_dom_groups(struct winbindd_domain *domain,
1608 TALLOC_CTX *mem_ctx,
1609 uint32_t *num_entries,
1610 struct wb_acct_info **info)
1612 struct winbind_cache *cache = get_cache(domain);
1613 struct cache_entry *centry = NULL;
1618 old_status = domain->online;
1622 centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
1627 *num_entries = centry_uint32(centry);
1629 if (*num_entries == 0)
1632 (*info) = talloc_array(mem_ctx, struct wb_acct_info, *num_entries);
1634 smb_panic_fn("enum_dom_groups out of memory");
1636 for (i=0; i<(*num_entries); i++) {
1637 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1638 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1639 (*info)[i].rid = centry_uint32(centry);
1643 status = centry->status;
1645 DEBUG(10,("enum_dom_groups: [Cached] - cached list for domain %s status: %s\n",
1646 domain->name, nt_errstr(status) ));
1648 centry_free(centry);
1655 /* Return status value returned by seq number check */
1657 if (!NT_STATUS_IS_OK(domain->last_status))
1658 return domain->last_status;
1660 DEBUG(10,("enum_dom_groups: [Cached] - doing backend query for list for domain %s\n",
1663 status = domain->backend->enum_dom_groups(domain, mem_ctx, num_entries, info);
1665 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1666 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1667 if (!domain->internal && old_status) {
1668 set_domain_offline(domain);
1672 !domain->internal &&
1674 centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
1676 goto do_fetch_cache;
1681 refresh_sequence_number(domain);
1682 if (!NT_STATUS_IS_OK(status)) {
1685 centry = centry_start(domain, status);
1688 centry_put_uint32(centry, *num_entries);
1689 for (i=0; i<(*num_entries); i++) {
1690 centry_put_string(centry, (*info)[i].acct_name);
1691 centry_put_string(centry, (*info)[i].acct_desc);
1692 centry_put_uint32(centry, (*info)[i].rid);
1694 centry_end(centry, "GL/%s/domain", domain->name);
1695 centry_free(centry);
1701 /* list all domain groups */
1702 static NTSTATUS enum_local_groups(struct winbindd_domain *domain,
1703 TALLOC_CTX *mem_ctx,
1704 uint32_t *num_entries,
1705 struct wb_acct_info **info)
1707 struct winbind_cache *cache = get_cache(domain);
1708 struct cache_entry *centry = NULL;
1713 old_status = domain->online;
1717 centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
1722 *num_entries = centry_uint32(centry);
1724 if (*num_entries == 0)
1727 (*info) = talloc_array(mem_ctx, struct wb_acct_info, *num_entries);
1729 smb_panic_fn("enum_dom_groups out of memory");
1731 for (i=0; i<(*num_entries); i++) {
1732 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1733 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1734 (*info)[i].rid = centry_uint32(centry);
1739 /* If we are returning cached data and the domain controller
1740 is down then we don't know whether the data is up to date
1741 or not. Return NT_STATUS_MORE_PROCESSING_REQUIRED to
1744 if (wcache_server_down(domain)) {
1745 DEBUG(10, ("enum_local_groups: returning cached user list and server was down\n"));
1746 status = NT_STATUS_MORE_PROCESSING_REQUIRED;
1748 status = centry->status;
1750 DEBUG(10,("enum_local_groups: [Cached] - cached list for domain %s status: %s\n",
1751 domain->name, nt_errstr(status) ));
1753 centry_free(centry);
1760 /* Return status value returned by seq number check */
1762 if (!NT_STATUS_IS_OK(domain->last_status))
1763 return domain->last_status;
1765 DEBUG(10,("enum_local_groups: [Cached] - doing backend query for list for domain %s\n",
1768 status = domain->backend->enum_local_groups(domain, mem_ctx, num_entries, info);
1770 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1771 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1772 if (!domain->internal && old_status) {
1773 set_domain_offline(domain);
1776 !domain->internal &&
1779 centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
1781 goto do_fetch_cache;
1786 refresh_sequence_number(domain);
1787 if (!NT_STATUS_IS_OK(status)) {
1790 centry = centry_start(domain, status);
1793 centry_put_uint32(centry, *num_entries);
1794 for (i=0; i<(*num_entries); i++) {
1795 centry_put_string(centry, (*info)[i].acct_name);
1796 centry_put_string(centry, (*info)[i].acct_desc);
1797 centry_put_uint32(centry, (*info)[i].rid);
1799 centry_end(centry, "GL/%s/local", domain->name);
1800 centry_free(centry);
1806 NTSTATUS wcache_name_to_sid(struct winbindd_domain *domain,
1807 const char *domain_name,
1809 struct dom_sid *sid,
1810 enum lsa_SidType *type)
1812 struct winbind_cache *cache = get_cache(domain);
1813 struct cache_entry *centry;
1817 if (cache->tdb == NULL) {
1818 return NT_STATUS_NOT_FOUND;
1821 uname = talloc_strdup_upper(talloc_tos(), name);
1822 if (uname == NULL) {
1823 return NT_STATUS_NO_MEMORY;
1826 if ((domain_name == NULL) || (domain_name[0] == '\0')) {
1827 domain_name = domain->name;
1830 centry = wcache_fetch(cache, domain, "NS/%s/%s", domain_name, uname);
1832 if (centry == NULL) {
1833 return NT_STATUS_NOT_FOUND;
1836 status = centry->status;
1837 if (NT_STATUS_IS_OK(status)) {
1838 *type = (enum lsa_SidType)centry_uint32(centry);
1839 centry_sid(centry, sid);
1842 DEBUG(10,("name_to_sid: [Cached] - cached name for domain %s status: "
1843 "%s\n", domain->name, nt_errstr(status) ));
1845 centry_free(centry);
1849 /* convert a single name to a sid in a domain */
1850 static NTSTATUS name_to_sid(struct winbindd_domain *domain,
1851 TALLOC_CTX *mem_ctx,
1852 const char *domain_name,
1855 struct dom_sid *sid,
1856 enum lsa_SidType *type)
1861 old_status = domain->online;
1863 status = wcache_name_to_sid(domain, domain_name, name, sid, type);
1864 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
1870 /* If the seq number check indicated that there is a problem
1871 * with this DC, then return that status... except for
1872 * access_denied. This is special because the dc may be in
1873 * "restrict anonymous = 1" mode, in which case it will deny
1874 * most unauthenticated operations, but *will* allow the LSA
1875 * name-to-sid that we try as a fallback. */
1877 if (!(NT_STATUS_IS_OK(domain->last_status)
1878 || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1879 return domain->last_status;
1881 DEBUG(10,("name_to_sid: [Cached] - doing backend query for name for domain %s\n",
1884 status = domain->backend->name_to_sid(domain, mem_ctx, domain_name,
1885 name, flags, sid, type);
1887 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1888 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1889 if (!domain->internal && old_status) {
1890 set_domain_offline(domain);
1892 if (!domain->internal &&
1895 NTSTATUS cache_status;
1896 cache_status = wcache_name_to_sid(domain, domain_name, name, sid, type);
1897 return cache_status;
1901 refresh_sequence_number(domain);
1903 if (domain->online &&
1904 (NT_STATUS_IS_OK(status) || NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED))) {
1905 wcache_save_name_to_sid(domain, status, domain_name, name, sid, *type);
1907 /* Only save the reverse mapping if this was not a UPN */
1908 if (!strchr(name, '@')) {
1909 if (!strupper_m(discard_const_p(char, domain_name))) {
1910 return NT_STATUS_INVALID_PARAMETER;
1912 (void)strlower_m(discard_const_p(char, name));
1913 wcache_save_sid_to_name(domain, status, sid, domain_name, name, *type);
1920 static NTSTATUS wcache_sid_to_name(struct winbindd_domain *domain,
1921 const struct dom_sid *sid,
1922 TALLOC_CTX *mem_ctx,
1925 enum lsa_SidType *type)
1927 struct winbind_cache *cache = get_cache(domain);
1928 struct cache_entry *centry;
1932 if (cache->tdb == NULL) {
1933 return NT_STATUS_NOT_FOUND;
1936 sid_string = sid_string_tos(sid);
1937 if (sid_string == NULL) {
1938 return NT_STATUS_NO_MEMORY;
1941 centry = wcache_fetch(cache, domain, "SN/%s", sid_string);
1942 TALLOC_FREE(sid_string);
1943 if (centry == NULL) {
1944 return NT_STATUS_NOT_FOUND;
1947 if (NT_STATUS_IS_OK(centry->status)) {
1948 *type = (enum lsa_SidType)centry_uint32(centry);
1949 *domain_name = centry_string(centry, mem_ctx);
1950 *name = centry_string(centry, mem_ctx);
1953 status = centry->status;
1954 centry_free(centry);
1956 DEBUG(10,("sid_to_name: [Cached] - cached name for domain %s status: "
1957 "%s\n", domain->name, nt_errstr(status) ));
1962 /* convert a sid to a user or group name. The sid is guaranteed to be in the domain
1964 static NTSTATUS sid_to_name(struct winbindd_domain *domain,
1965 TALLOC_CTX *mem_ctx,
1966 const struct dom_sid *sid,
1969 enum lsa_SidType *type)
1974 old_status = domain->online;
1975 status = wcache_sid_to_name(domain, sid, mem_ctx, domain_name, name,
1977 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
1982 *domain_name = NULL;
1984 /* If the seq number check indicated that there is a problem
1985 * with this DC, then return that status... except for
1986 * access_denied. This is special because the dc may be in
1987 * "restrict anonymous = 1" mode, in which case it will deny
1988 * most unauthenticated operations, but *will* allow the LSA
1989 * sid-to-name that we try as a fallback. */
1991 if (!(NT_STATUS_IS_OK(domain->last_status)
1992 || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1993 return domain->last_status;
1995 DEBUG(10,("sid_to_name: [Cached] - doing backend query for name for domain %s\n",
1998 status = domain->backend->sid_to_name(domain, mem_ctx, sid, domain_name, name, type);
2000 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2001 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2002 if (!domain->internal && old_status) {
2003 set_domain_offline(domain);
2005 if (!domain->internal &&
2008 NTSTATUS cache_status;
2009 cache_status = wcache_sid_to_name(domain, sid, mem_ctx,
2010 domain_name, name, type);
2011 return cache_status;
2015 refresh_sequence_number(domain);
2016 if (!NT_STATUS_IS_OK(status)) {
2019 wcache_save_sid_to_name(domain, status, sid, *domain_name, *name, *type);
2021 /* We can't save the name to sid mapping here, as with sid history a
2022 * later name2sid would give the wrong sid. */
2027 static NTSTATUS rids_to_names(struct winbindd_domain *domain,
2028 TALLOC_CTX *mem_ctx,
2029 const struct dom_sid *domain_sid,
2034 enum lsa_SidType **types)
2036 struct winbind_cache *cache = get_cache(domain);
2038 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
2043 old_status = domain->online;
2044 *domain_name = NULL;
2052 if (num_rids == 0) {
2053 return NT_STATUS_OK;
2056 *names = talloc_array(mem_ctx, char *, num_rids);
2057 *types = talloc_array(mem_ctx, enum lsa_SidType, num_rids);
2059 if ((*names == NULL) || (*types == NULL)) {
2060 result = NT_STATUS_NO_MEMORY;
2064 have_mapped = have_unmapped = false;
2066 for (i=0; i<num_rids; i++) {
2068 struct cache_entry *centry;
2071 if (!sid_compose(&sid, domain_sid, rids[i])) {
2072 result = NT_STATUS_INTERNAL_ERROR;
2076 centry = wcache_fetch(cache, domain, "SN/%s",
2077 sid_to_fstring(tmp, &sid));
2082 (*types)[i] = SID_NAME_UNKNOWN;
2083 (*names)[i] = talloc_strdup(*names, "");
2085 if (NT_STATUS_IS_OK(centry->status)) {
2088 (*types)[i] = (enum lsa_SidType)centry_uint32(centry);
2090 dom = centry_string(centry, mem_ctx);
2091 if (*domain_name == NULL) {
2097 (*names)[i] = centry_string(centry, *names);
2099 } else if (NT_STATUS_EQUAL(centry->status, NT_STATUS_NONE_MAPPED)
2100 || NT_STATUS_EQUAL(centry->status, STATUS_SOME_UNMAPPED)) {
2101 have_unmapped = true;
2104 /* something's definitely wrong */
2105 result = centry->status;
2106 centry_free(centry);
2110 centry_free(centry);
2114 return NT_STATUS_NONE_MAPPED;
2116 if (!have_unmapped) {
2117 return NT_STATUS_OK;
2119 return STATUS_SOME_UNMAPPED;
2123 TALLOC_FREE(*names);
2124 TALLOC_FREE(*types);
2126 result = domain->backend->rids_to_names(domain, mem_ctx, domain_sid,
2127 rids, num_rids, domain_name,
2130 if (NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
2131 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2132 if (!domain->internal && old_status) {
2133 set_domain_offline(domain);
2136 !domain->internal &&
2139 have_mapped = have_unmapped = false;
2141 *names = talloc_array(mem_ctx, char *, num_rids);
2142 if (*names == NULL) {
2143 result = NT_STATUS_NO_MEMORY;
2147 *types = talloc_array(mem_ctx, enum lsa_SidType,
2149 if (*types == NULL) {
2150 result = NT_STATUS_NO_MEMORY;
2154 for (i=0; i<num_rids; i++) {
2156 struct cache_entry *centry;
2159 if (!sid_compose(&sid, domain_sid, rids[i])) {
2160 result = NT_STATUS_INTERNAL_ERROR;
2164 centry = wcache_fetch(cache, domain, "SN/%s",
2165 sid_to_fstring(tmp, &sid));
2167 (*types)[i] = SID_NAME_UNKNOWN;
2168 (*names)[i] = talloc_strdup(*names, "");
2172 (*types)[i] = SID_NAME_UNKNOWN;
2173 (*names)[i] = talloc_strdup(*names, "");
2175 if (NT_STATUS_IS_OK(centry->status)) {
2178 (*types)[i] = (enum lsa_SidType)centry_uint32(centry);
2180 dom = centry_string(centry, mem_ctx);
2181 if (*domain_name == NULL) {
2187 (*names)[i] = centry_string(centry, *names);
2189 } else if (NT_STATUS_EQUAL(centry->status, NT_STATUS_NONE_MAPPED)) {
2190 have_unmapped = true;
2193 /* something's definitely wrong */
2194 result = centry->status;
2195 centry_free(centry);
2199 centry_free(centry);
2203 return NT_STATUS_NONE_MAPPED;
2205 if (!have_unmapped) {
2206 return NT_STATUS_OK;
2208 return STATUS_SOME_UNMAPPED;
2212 None of the queried rids has been found so save all negative entries
2214 if (NT_STATUS_EQUAL(result, NT_STATUS_NONE_MAPPED)) {
2215 for (i = 0; i < num_rids; i++) {
2217 const char *name = "";
2218 const enum lsa_SidType type = SID_NAME_UNKNOWN;
2219 NTSTATUS status = NT_STATUS_NONE_MAPPED;
2221 if (!sid_compose(&sid, domain_sid, rids[i])) {
2222 return NT_STATUS_INTERNAL_ERROR;
2225 wcache_save_sid_to_name(domain, status, &sid, *domain_name,
2233 Some or all of the queried rids have been found.
2235 if (!NT_STATUS_IS_OK(result) &&
2236 !NT_STATUS_EQUAL(result, STATUS_SOME_UNMAPPED)) {
2240 refresh_sequence_number(domain);
2242 for (i=0; i<num_rids; i++) {
2246 if (!sid_compose(&sid, domain_sid, rids[i])) {
2247 result = NT_STATUS_INTERNAL_ERROR;
2251 status = (*types)[i] == SID_NAME_UNKNOWN ?
2252 NT_STATUS_NONE_MAPPED : NT_STATUS_OK;
2254 wcache_save_sid_to_name(domain, status, &sid, *domain_name,
2255 (*names)[i], (*types)[i]);
2261 TALLOC_FREE(*names);
2262 TALLOC_FREE(*types);
2266 NTSTATUS wcache_query_user(struct winbindd_domain *domain,
2267 TALLOC_CTX *mem_ctx,
2268 const struct dom_sid *user_sid,
2269 struct wbint_userinfo *info)
2271 struct winbind_cache *cache = get_cache(domain);
2272 struct cache_entry *centry = NULL;
2276 if (cache->tdb == NULL) {
2277 return NT_STATUS_NOT_FOUND;
2280 sid_string = sid_string_tos(user_sid);
2281 if (sid_string == NULL) {
2282 return NT_STATUS_NO_MEMORY;
2285 centry = wcache_fetch(cache, domain, "U/%s", sid_string);
2286 TALLOC_FREE(sid_string);
2287 if (centry == NULL) {
2288 return NT_STATUS_NOT_FOUND;
2292 * If we have an access denied cache entry and a cached info3
2293 * in the samlogon cache then do a query. This will force the
2294 * rpc back end to return the info3 data.
2297 if (NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED) &&
2298 netsamlogon_cache_have(user_sid)) {
2299 DEBUG(10, ("query_user: cached access denied and have cached "
2301 domain->last_status = NT_STATUS_OK;
2302 centry_free(centry);
2303 return NT_STATUS_NOT_FOUND;
2306 /* if status is not ok then this is a negative hit
2307 and the rest of the data doesn't matter */
2308 status = centry->status;
2309 if (NT_STATUS_IS_OK(status)) {
2310 info->acct_name = centry_string(centry, mem_ctx);
2311 info->full_name = centry_string(centry, mem_ctx);
2312 info->homedir = centry_string(centry, mem_ctx);
2313 info->shell = centry_string(centry, mem_ctx);
2314 info->primary_gid = centry_uint32(centry);
2315 centry_sid(centry, &info->user_sid);
2316 centry_sid(centry, &info->group_sid);
2319 DEBUG(10,("query_user: [Cached] - cached info for domain %s status: "
2320 "%s\n", domain->name, nt_errstr(status) ));
2322 centry_free(centry);
2328 * @brief Query a fullname from the username cache (for further gecos processing)
2330 * @param domain A pointer to the winbindd_domain struct.
2331 * @param mem_ctx The talloc context.
2332 * @param user_sid The user sid.
2333 * @param full_name A pointer to the full_name string.
2335 * @return NTSTATUS code
2337 NTSTATUS wcache_query_user_fullname(struct winbindd_domain *domain,
2338 TALLOC_CTX *mem_ctx,
2339 const struct dom_sid *user_sid,
2340 const char **full_name)
2343 struct wbint_userinfo info;
2345 status = wcache_query_user(domain, mem_ctx, user_sid, &info);
2346 if (!NT_STATUS_IS_OK(status)) {
2350 if (info.full_name != NULL) {
2351 *full_name = talloc_strdup(mem_ctx, info.full_name);
2352 if (*full_name == NULL) {
2353 return NT_STATUS_NO_MEMORY;
2357 return NT_STATUS_OK;
2360 /* Lookup user information from a rid */
2361 static NTSTATUS query_user(struct winbindd_domain *domain,
2362 TALLOC_CTX *mem_ctx,
2363 const struct dom_sid *user_sid,
2364 struct wbint_userinfo *info)
2369 old_status = domain->online;
2370 status = wcache_query_user(domain, mem_ctx, user_sid, info);
2371 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2377 /* Return status value returned by seq number check */
2379 if (!NT_STATUS_IS_OK(domain->last_status))
2380 return domain->last_status;
2382 DEBUG(10,("query_user: [Cached] - doing backend query for info for domain %s\n",
2385 status = domain->backend->query_user(domain, mem_ctx, user_sid, info);
2387 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2388 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2389 if (!domain->internal && old_status) {
2390 set_domain_offline(domain);
2392 if (!domain->internal &&
2395 NTSTATUS cache_status;
2396 cache_status = wcache_query_user(domain, mem_ctx, user_sid, info);
2397 return cache_status;
2401 refresh_sequence_number(domain);
2402 if (!NT_STATUS_IS_OK(status)) {
2405 wcache_save_user(domain, status, info);
2410 NTSTATUS wcache_lookup_usergroups(struct winbindd_domain *domain,
2411 TALLOC_CTX *mem_ctx,
2412 const struct dom_sid *user_sid,
2413 uint32_t *pnum_sids,
2414 struct dom_sid **psids)
2416 struct winbind_cache *cache = get_cache(domain);
2417 struct cache_entry *centry = NULL;
2419 uint32_t i, num_sids;
2420 struct dom_sid *sids;
2423 if (cache->tdb == NULL) {
2424 return NT_STATUS_NOT_FOUND;
2427 centry = wcache_fetch(cache, domain, "UG/%s",
2428 sid_to_fstring(sid_string, user_sid));
2429 if (centry == NULL) {
2430 return NT_STATUS_NOT_FOUND;
2433 /* If we have an access denied cache entry and a cached info3 in the
2434 samlogon cache then do a query. This will force the rpc back end
2435 to return the info3 data. */
2437 if (NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)
2438 && netsamlogon_cache_have(user_sid)) {
2439 DEBUG(10, ("lookup_usergroups: cached access denied and have "
2441 domain->last_status = NT_STATUS_OK;
2442 centry_free(centry);
2443 return NT_STATUS_NOT_FOUND;
2446 num_sids = centry_uint32(centry);
2447 sids = talloc_array(mem_ctx, struct dom_sid, num_sids);
2449 centry_free(centry);
2450 return NT_STATUS_NO_MEMORY;
2453 for (i=0; i<num_sids; i++) {
2454 centry_sid(centry, &sids[i]);
2457 status = centry->status;
2459 DEBUG(10,("lookup_usergroups: [Cached] - cached info for domain %s "
2460 "status: %s\n", domain->name, nt_errstr(status)));
2462 centry_free(centry);
2464 *pnum_sids = num_sids;
2469 /* Lookup groups a user is a member of. */
2470 static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
2471 TALLOC_CTX *mem_ctx,
2472 const struct dom_sid *user_sid,
2473 uint32_t *num_groups, struct dom_sid **user_gids)
2475 struct cache_entry *centry = NULL;
2481 old_status = domain->online;
2482 status = wcache_lookup_usergroups(domain, mem_ctx, user_sid,
2483 num_groups, user_gids);
2484 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2489 (*user_gids) = NULL;
2491 /* Return status value returned by seq number check */
2493 if (!NT_STATUS_IS_OK(domain->last_status))
2494 return domain->last_status;
2496 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info for domain %s\n",
2499 status = domain->backend->lookup_usergroups(domain, mem_ctx, user_sid, num_groups, user_gids);
2501 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2502 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2503 if (!domain->internal && old_status) {
2504 set_domain_offline(domain);
2506 if (!domain->internal &&
2509 NTSTATUS cache_status;
2510 cache_status = wcache_lookup_usergroups(domain, mem_ctx, user_sid,
2511 num_groups, user_gids);
2512 return cache_status;
2515 if ( NT_STATUS_EQUAL(status, NT_STATUS_SYNCHRONIZATION_REQUIRED) )
2519 refresh_sequence_number(domain);
2520 if (!NT_STATUS_IS_OK(status)) {
2523 centry = centry_start(domain, status);
2527 centry_put_uint32(centry, *num_groups);
2528 for (i=0; i<(*num_groups); i++) {
2529 centry_put_sid(centry, &(*user_gids)[i]);
2532 centry_end(centry, "UG/%s", sid_to_fstring(sid_string, user_sid));
2533 centry_free(centry);
2539 static char *wcache_make_sidlist(TALLOC_CTX *mem_ctx, uint32_t num_sids,
2540 const struct dom_sid *sids)
2545 sidlist = talloc_strdup(mem_ctx, "");
2546 if (sidlist == NULL) {
2549 for (i=0; i<num_sids; i++) {
2551 sidlist = talloc_asprintf_append_buffer(
2552 sidlist, "/%s", sid_to_fstring(tmp, &sids[i]));
2553 if (sidlist == NULL) {
2560 NTSTATUS wcache_lookup_useraliases(struct winbindd_domain *domain,
2561 TALLOC_CTX *mem_ctx, uint32_t num_sids,
2562 const struct dom_sid *sids,
2563 uint32_t *pnum_aliases, uint32_t **paliases)
2565 struct winbind_cache *cache = get_cache(domain);
2566 struct cache_entry *centry = NULL;
2567 uint32_t num_aliases;
2573 if (cache->tdb == NULL) {
2574 return NT_STATUS_NOT_FOUND;
2577 if (num_sids == 0) {
2580 return NT_STATUS_OK;
2583 /* We need to cache indexed by the whole list of SIDs, the aliases
2584 * resulting might come from any of the SIDs. */
2586 sidlist = wcache_make_sidlist(talloc_tos(), num_sids, sids);
2587 if (sidlist == NULL) {
2588 return NT_STATUS_NO_MEMORY;
2591 centry = wcache_fetch(cache, domain, "UA%s", sidlist);
2592 TALLOC_FREE(sidlist);
2593 if (centry == NULL) {
2594 return NT_STATUS_NOT_FOUND;
2597 num_aliases = centry_uint32(centry);
2598 aliases = talloc_array(mem_ctx, uint32_t, num_aliases);
2599 if (aliases == NULL) {
2600 centry_free(centry);
2601 return NT_STATUS_NO_MEMORY;
2604 for (i=0; i<num_aliases; i++) {
2605 aliases[i] = centry_uint32(centry);
2608 status = centry->status;
2610 DEBUG(10,("lookup_useraliases: [Cached] - cached info for domain: %s "
2611 "status %s\n", domain->name, nt_errstr(status)));
2613 centry_free(centry);
2615 *pnum_aliases = num_aliases;
2616 *paliases = aliases;
2621 static NTSTATUS lookup_useraliases(struct winbindd_domain *domain,
2622 TALLOC_CTX *mem_ctx,
2623 uint32_t num_sids, const struct dom_sid *sids,
2624 uint32_t *num_aliases, uint32_t **alias_rids)
2626 struct cache_entry *centry = NULL;
2632 old_status = domain->online;
2633 status = wcache_lookup_useraliases(domain, mem_ctx, num_sids, sids,
2634 num_aliases, alias_rids);
2635 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2640 (*alias_rids) = NULL;
2642 if (!NT_STATUS_IS_OK(domain->last_status))
2643 return domain->last_status;
2645 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info "
2646 "for domain %s\n", domain->name ));
2648 sidlist = wcache_make_sidlist(talloc_tos(), num_sids, sids);
2649 if (sidlist == NULL) {
2650 return NT_STATUS_NO_MEMORY;
2653 status = domain->backend->lookup_useraliases(domain, mem_ctx,
2655 num_aliases, alias_rids);
2657 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2658 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2659 if (!domain->internal && old_status) {
2660 set_domain_offline(domain);
2662 if (!domain->internal &&
2665 NTSTATUS cache_status;
2666 cache_status = wcache_lookup_useraliases(domain, mem_ctx, num_sids,
2667 sids, num_aliases, alias_rids);
2668 return cache_status;
2672 refresh_sequence_number(domain);
2673 if (!NT_STATUS_IS_OK(status)) {
2676 centry = centry_start(domain, status);
2679 centry_put_uint32(centry, *num_aliases);
2680 for (i=0; i<(*num_aliases); i++)
2681 centry_put_uint32(centry, (*alias_rids)[i]);
2682 centry_end(centry, "UA%s", sidlist);
2683 centry_free(centry);
2689 NTSTATUS wcache_lookup_groupmem(struct winbindd_domain *domain,
2690 TALLOC_CTX *mem_ctx,
2691 const struct dom_sid *group_sid,
2692 uint32_t *num_names,
2693 struct dom_sid **sid_mem, char ***names,
2694 uint32_t **name_types)
2696 struct winbind_cache *cache = get_cache(domain);
2697 struct cache_entry *centry = NULL;
2702 if (cache->tdb == NULL) {
2703 return NT_STATUS_NOT_FOUND;
2706 sid_string = sid_string_tos(group_sid);
2707 if (sid_string == NULL) {
2708 return NT_STATUS_NO_MEMORY;
2711 centry = wcache_fetch(cache, domain, "GM/%s", sid_string);
2712 TALLOC_FREE(sid_string);
2713 if (centry == NULL) {
2714 return NT_STATUS_NOT_FOUND;
2721 *num_names = centry_uint32(centry);
2722 if (*num_names == 0) {
2723 centry_free(centry);
2724 return NT_STATUS_OK;
2727 *sid_mem = talloc_array(mem_ctx, struct dom_sid, *num_names);
2728 *names = talloc_array(mem_ctx, char *, *num_names);
2729 *name_types = talloc_array(mem_ctx, uint32_t, *num_names);
2731 if ((*sid_mem == NULL) || (*names == NULL) || (*name_types == NULL)) {
2732 TALLOC_FREE(*sid_mem);
2733 TALLOC_FREE(*names);
2734 TALLOC_FREE(*name_types);
2735 centry_free(centry);
2736 return NT_STATUS_NO_MEMORY;
2739 for (i=0; i<(*num_names); i++) {
2740 centry_sid(centry, &(*sid_mem)[i]);
2741 (*names)[i] = centry_string(centry, mem_ctx);
2742 (*name_types)[i] = centry_uint32(centry);
2745 status = centry->status;
2747 DEBUG(10,("lookup_groupmem: [Cached] - cached info for domain %s "
2748 "status: %s\n", domain->name, nt_errstr(status)));
2750 centry_free(centry);
2754 static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
2755 TALLOC_CTX *mem_ctx,
2756 const struct dom_sid *group_sid,
2757 enum lsa_SidType type,
2758 uint32_t *num_names,
2759 struct dom_sid **sid_mem, char ***names,
2760 uint32_t **name_types)
2762 struct cache_entry *centry = NULL;
2768 old_status = domain->online;
2769 status = wcache_lookup_groupmem(domain, mem_ctx, group_sid, num_names,
2770 sid_mem, names, name_types);
2771 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2778 (*name_types) = NULL;
2780 /* Return status value returned by seq number check */
2782 if (!NT_STATUS_IS_OK(domain->last_status))
2783 return domain->last_status;
2785 DEBUG(10,("lookup_groupmem: [Cached] - doing backend query for info for domain %s\n",
2788 status = domain->backend->lookup_groupmem(domain, mem_ctx, group_sid,
2790 sid_mem, names, name_types);
2792 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2793 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2794 if (!domain->internal && old_status) {
2795 set_domain_offline(domain);
2797 if (!domain->internal &&
2800 NTSTATUS cache_status;
2801 cache_status = wcache_lookup_groupmem(domain, mem_ctx, group_sid,
2802 num_names, sid_mem, names,
2804 return cache_status;
2808 refresh_sequence_number(domain);
2809 if (!NT_STATUS_IS_OK(status)) {
2812 centry = centry_start(domain, status);
2815 centry_put_uint32(centry, *num_names);
2816 for (i=0; i<(*num_names); i++) {
2817 centry_put_sid(centry, &(*sid_mem)[i]);
2818 centry_put_string(centry, (*names)[i]);
2819 centry_put_uint32(centry, (*name_types)[i]);
2821 centry_end(centry, "GM/%s", sid_to_fstring(sid_string, group_sid));
2822 centry_free(centry);
2828 /* find the sequence number for a domain */
2829 static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32_t *seq)
2831 refresh_sequence_number(domain);
2833 *seq = domain->sequence_number;
2835 return NT_STATUS_OK;
2838 /* enumerate trusted domains
2839 * (we need to have the list of trustdoms in the cache when we go offline) -
2841 static NTSTATUS trusted_domains(struct winbindd_domain *domain,
2842 TALLOC_CTX *mem_ctx,
2843 struct netr_DomainTrustList *trusts)
2846 struct winbind_cache *cache;
2847 struct winbindd_tdc_domain *dom_list = NULL;
2848 size_t num_domains = 0;
2849 bool retval = false;
2853 old_status = domain->online;
2855 trusts->array = NULL;
2857 cache = get_cache(domain);
2858 if (!cache || !cache->tdb) {
2862 if (domain->online) {
2866 retval = wcache_tdc_fetch_list(&dom_list, &num_domains);
2867 if (!retval || !num_domains || !dom_list) {
2868 TALLOC_FREE(dom_list);
2873 trusts->array = talloc_zero_array(mem_ctx, struct netr_DomainTrust, num_domains);
2874 if (!trusts->array) {
2875 TALLOC_FREE(dom_list);
2876 return NT_STATUS_NO_MEMORY;
2879 for (i = 0; i < num_domains; i++) {
2880 struct netr_DomainTrust *trust;
2881 struct dom_sid *sid;
2882 struct winbindd_domain *dom;
2884 dom = find_domain_from_name_noinit(dom_list[i].domain_name);
2885 if (dom && dom->internal) {
2889 trust = &trusts->array[trusts->count];
2890 trust->netbios_name = talloc_strdup(trusts->array, dom_list[i].domain_name);
2891 trust->dns_name = talloc_strdup(trusts->array, dom_list[i].dns_name);
2892 sid = talloc(trusts->array, struct dom_sid);
2893 if (!trust->netbios_name || !trust->dns_name ||
2895 TALLOC_FREE(dom_list);
2896 TALLOC_FREE(trusts->array);
2897 return NT_STATUS_NO_MEMORY;
2900 trust->trust_flags = dom_list[i].trust_flags;
2901 trust->trust_attributes = dom_list[i].trust_attribs;
2902 trust->trust_type = dom_list[i].trust_type;
2903 sid_copy(sid, &dom_list[i].sid);
2908 TALLOC_FREE(dom_list);
2909 return NT_STATUS_OK;
2912 /* Return status value returned by seq number check */
2914 if (!NT_STATUS_IS_OK(domain->last_status))
2915 return domain->last_status;
2917 DEBUG(10,("trusted_domains: [Cached] - doing backend query for info for domain %s\n",
2920 status = domain->backend->trusted_domains(domain, mem_ctx, trusts);
2922 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2923 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2924 if (!domain->internal && old_status) {
2925 set_domain_offline(domain);
2927 if (!domain->internal &&
2930 retval = wcache_tdc_fetch_list(&dom_list, &num_domains);
2931 if (retval && num_domains && dom_list) {
2932 TALLOC_FREE(trusts->array);
2934 goto do_fetch_cache;
2938 /* no trusts gives NT_STATUS_NO_MORE_ENTRIES resetting to NT_STATUS_OK
2939 * so that the generic centry handling still applies correctly -
2942 if (!NT_STATUS_IS_ERR(status)) {
2943 status = NT_STATUS_OK;
2948 /* get lockout policy */
2949 static NTSTATUS lockout_policy(struct winbindd_domain *domain,
2950 TALLOC_CTX *mem_ctx,
2951 struct samr_DomInfo12 *policy)
2953 struct winbind_cache *cache = get_cache(domain);
2954 struct cache_entry *centry = NULL;
2958 old_status = domain->online;
2962 centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
2968 policy->lockout_duration = centry_nttime(centry);
2969 policy->lockout_window = centry_nttime(centry);
2970 policy->lockout_threshold = centry_uint16(centry);
2972 status = centry->status;
2974 DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2975 domain->name, nt_errstr(status) ));
2977 centry_free(centry);
2981 ZERO_STRUCTP(policy);
2983 /* Return status value returned by seq number check */
2985 if (!NT_STATUS_IS_OK(domain->last_status))
2986 return domain->last_status;
2988 DEBUG(10,("lockout_policy: [Cached] - doing backend query for info for domain %s\n",
2991 status = domain->backend->lockout_policy(domain, mem_ctx, policy);
2993 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2994 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2995 if (!domain->internal && old_status) {
2996 set_domain_offline(domain);
2999 !domain->internal &&
3002 centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
3004 goto do_fetch_cache;
3009 refresh_sequence_number(domain);
3010 if (!NT_STATUS_IS_OK(status)) {
3013 wcache_save_lockout_policy(domain, status, policy);
3018 /* get password policy */
3019 static NTSTATUS password_policy(struct winbindd_domain *domain,
3020 TALLOC_CTX *mem_ctx,
3021 struct samr_DomInfo1 *policy)
3023 struct winbind_cache *cache = get_cache(domain);
3024 struct cache_entry *centry = NULL;
3028 old_status = domain->online;
3032 centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
3038 policy->min_password_length = centry_uint16(centry);
3039 policy->password_history_length = centry_uint16(centry);
3040 policy->password_properties = centry_uint32(centry);
3041 policy->max_password_age = centry_nttime(centry);
3042 policy->min_password_age = centry_nttime(centry);
3044 status = centry->status;
3046 DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
3047 domain->name, nt_errstr(status) ));
3049 centry_free(centry);
3053 ZERO_STRUCTP(policy);
3055 /* Return status value returned by seq number check */
3057 if (!NT_STATUS_IS_OK(domain->last_status))
3058 return domain->last_status;
3060 DEBUG(10,("password_policy: [Cached] - doing backend query for info for domain %s\n",
3063 status = domain->backend->password_policy(domain, mem_ctx, policy);
3065 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
3066 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
3067 if (!domain->internal && old_status) {
3068 set_domain_offline(domain);
3071 !domain->internal &&
3074 centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
3076 goto do_fetch_cache;
3081 refresh_sequence_number(domain);
3082 if (!NT_STATUS_IS_OK(status)) {
3085 wcache_save_password_policy(domain, status, policy);
3091 /* Invalidate cached user and group lists coherently */
3093 static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
3096 if (strncmp((const char *)kbuf.dptr, "UL/", 3) == 0 ||
3097 strncmp((const char *)kbuf.dptr, "GL/", 3) == 0)
3098 tdb_delete(the_tdb, kbuf);
3103 /* Invalidate the getpwnam and getgroups entries for a winbindd domain */
3105 void wcache_invalidate_samlogon(struct winbindd_domain *domain,
3106 const struct dom_sid *sid)
3108 fstring key_str, sid_string;
3109 struct winbind_cache *cache;
3111 /* don't clear cached U/SID and UG/SID entries when we want to logon
3114 if (lp_winbind_offline_logon()) {
3121 cache = get_cache(domain);
3127 /* Clear U/SID cache entry */
3128 fstr_sprintf(key_str, "U/%s", sid_to_fstring(sid_string, sid));
3129 DEBUG(10, ("wcache_invalidate_samlogon: clearing %s\n", key_str));
3130 tdb_delete(cache->tdb, string_tdb_data(key_str));
3132 /* Clear UG/SID cache entry */
3133 fstr_sprintf(key_str, "UG/%s", sid_to_fstring(sid_string, sid));
3134 DEBUG(10, ("wcache_invalidate_samlogon: clearing %s\n", key_str));
3135 tdb_delete(cache->tdb, string_tdb_data(key_str));
3137 /* Samba/winbindd never needs this. */
3138 netsamlogon_clear_cached_user(sid);
3141 bool wcache_invalidate_cache(void)
3143 struct winbindd_domain *domain;
3145 for (domain = domain_list(); domain; domain = domain->next) {
3146 struct winbind_cache *cache = get_cache(domain);
3148 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
3149 "entries for %s\n", domain->name));
3152 tdb_traverse(cache->tdb, traverse_fn, NULL);
3161 bool wcache_invalidate_cache_noinit(void)
3163 struct winbindd_domain *domain;
3165 for (domain = domain_list(); domain; domain = domain->next) {
3166 struct winbind_cache *cache;
3168 /* Skip uninitialized domains. */
3169 if (!domain->initialized && !domain->internal) {
3173 cache = get_cache(domain);
3175 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
3176 "entries for %s\n", domain->name));
3179 tdb_traverse(cache->tdb, traverse_fn, NULL);
3181 * Flushing cache has nothing to with domains.
3182 * return here if we successfully flushed once.
3183 * To avoid unnecessary traversing the cache.
3194 bool init_wcache(void)
3198 if (wcache == NULL) {
3199 wcache = SMB_XMALLOC_P(struct winbind_cache);
3200 ZERO_STRUCTP(wcache);
3203 if (wcache->tdb != NULL)
3206 db_path = wcache_path();
3207 if (db_path == NULL) {
3211 /* when working offline we must not clear the cache on restart */
3212 wcache->tdb = tdb_open_log(db_path,
3213 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
3214 TDB_INCOMPATIBLE_HASH |
3215 (lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST)),
3216 O_RDWR|O_CREAT, 0600);
3217 TALLOC_FREE(db_path);
3218 if (wcache->tdb == NULL) {
3219 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
3226 /************************************************************************
3227 This is called by the parent to initialize the cache file.
3228 We don't need sophisticated locking here as we know we're the
3230 ************************************************************************/
3232 bool initialize_winbindd_cache(void)
3234 bool cache_bad = true;
3237 if (!init_wcache()) {
3238 DEBUG(0,("initialize_winbindd_cache: init_wcache failed.\n"));
3242 /* Check version number. */
3243 if (tdb_fetch_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, &vers) &&
3244 vers == WINBINDD_CACHE_VERSION) {
3251 DEBUG(0,("initialize_winbindd_cache: clearing cache "
3252 "and re-creating with version number %d\n",
3253 WINBINDD_CACHE_VERSION ));
3255 tdb_close(wcache->tdb);
3258 db_path = wcache_path();
3259 if (db_path == NULL) {
3263 if (unlink(db_path) == -1) {
3264 DEBUG(0,("initialize_winbindd_cache: unlink %s failed %s ",
3267 TALLOC_FREE(db_path);
3270 TALLOC_FREE(db_path);
3271 if (!init_wcache()) {
3272 DEBUG(0,("initialize_winbindd_cache: re-initialization "
3273 "init_wcache failed.\n"));
3277 /* Write the version. */
3278 if (!tdb_store_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, WINBINDD_CACHE_VERSION)) {
3279 DEBUG(0,("initialize_winbindd_cache: version number store failed %s\n",
3280 tdb_errorstr(wcache->tdb) ));
3285 tdb_close(wcache->tdb);
3290 void close_winbindd_cache(void)
3296 tdb_close(wcache->tdb);
3301 bool lookup_cached_sid(TALLOC_CTX *mem_ctx, const struct dom_sid *sid,
3302 char **domain_name, char **name,
3303 enum lsa_SidType *type)
3305 struct winbindd_domain *domain;
3308 domain = find_lookup_domain_from_sid(sid);
3309 if (domain == NULL) {
3312 status = wcache_sid_to_name(domain, sid, mem_ctx, domain_name, name,
3314 return NT_STATUS_IS_OK(status);
3317 bool lookup_cached_name(const char *domain_name,
3319 struct dom_sid *sid,
3320 enum lsa_SidType *type)
3322 struct winbindd_domain *domain;
3324 bool original_online_state;
3326 domain = find_lookup_domain_from_name(domain_name);
3327 if (domain == NULL) {
3331 /* If we are doing a cached logon, temporarily set the domain
3332 offline so the cache won't expire the entry */
3334 original_online_state = domain->online;
3335 domain->online = false;
3336 status = wcache_name_to_sid(domain, domain_name, name, sid, type);
3337 domain->online = original_online_state;
3339 return NT_STATUS_IS_OK(status);
3343 * Cache a name to sid without checking the sequence number.
3344 * Used when caching from a trusted PAC.
3347 void cache_name2sid_trusted(struct winbindd_domain *domain,
3348 const char *domain_name,
3350 enum lsa_SidType type,
3351 const struct dom_sid *sid)
3353 wcache_save_name_to_sid(domain,
3361 void cache_name2sid(struct winbindd_domain *domain,
3362 const char *domain_name, const char *name,
3363 enum lsa_SidType type, const struct dom_sid *sid)
3365 refresh_sequence_number(domain);
3366 wcache_save_name_to_sid(domain, NT_STATUS_OK, domain_name, name,
3371 * The original idea that this cache only contains centries has
3372 * been blurred - now other stuff gets put in here. Ensure we
3373 * ignore these things on cleanup.
3376 static int traverse_fn_cleanup(TDB_CONTEXT *the_tdb, TDB_DATA kbuf,
3377 TDB_DATA dbuf, void *state)
3379 struct cache_entry *centry;
3381 if (is_non_centry_key(kbuf)) {
3385 centry = wcache_fetch_raw((char *)kbuf.dptr);
3390 if (!NT_STATUS_IS_OK(centry->status)) {
3391 DEBUG(10,("deleting centry %s\n", (const char *)kbuf.dptr));
3392 tdb_delete(the_tdb, kbuf);
3395 centry_free(centry);
3399 /* flush the cache */
3400 void wcache_flush_cache(void)
3407 tdb_close(wcache->tdb);
3410 if (!winbindd_use_cache()) {
3414 db_path = wcache_path();
3415 if (db_path == NULL) {
3419 /* when working offline we must not clear the cache on restart */
3420 wcache->tdb = tdb_open_log(db_path,
3421 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
3422 TDB_INCOMPATIBLE_HASH |
3423 (lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST)),
3424 O_RDWR|O_CREAT, 0600);
3425 TALLOC_FREE(db_path);
3427 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
3431 tdb_traverse(wcache->tdb, traverse_fn_cleanup, NULL);
3433 DEBUG(10,("wcache_flush_cache success\n"));
3436 /* Count cached creds */
3438 static int traverse_fn_cached_creds(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
3441 int *cred_count = (int*)state;
3443 if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
3449 NTSTATUS wcache_count_cached_creds(struct winbindd_domain *domain, int *count)
3451 struct winbind_cache *cache = get_cache(domain);
3456 return NT_STATUS_INTERNAL_DB_ERROR;
3459 tdb_traverse(cache->tdb, traverse_fn_cached_creds, (void *)count);
3461 return NT_STATUS_OK;
3465 struct cred_list *prev, *next;
3470 static struct cred_list *wcache_cred_list;
3472 static int traverse_fn_get_credlist(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
3475 struct cred_list *cred;
3477 if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
3479 cred = SMB_MALLOC_P(struct cred_list);
3481 DEBUG(0,("traverse_fn_remove_first_creds: failed to malloc new entry for list\n"));
3487 /* save a copy of the key */
3489 fstrcpy(cred->name, (const char *)kbuf.dptr);
3490 DLIST_ADD(wcache_cred_list, cred);
3496 NTSTATUS wcache_remove_oldest_cached_creds(struct winbindd_domain *domain, const struct dom_sid *sid)
3498 struct winbind_cache *cache = get_cache(domain);
3501 struct cred_list *cred, *next, *oldest = NULL;
3504 return NT_STATUS_INTERNAL_DB_ERROR;
3507 /* we possibly already have an entry */
3508 if (sid && NT_STATUS_IS_OK(wcache_cached_creds_exist(domain, sid))) {
3510 fstring key_str, tmp;
3512 DEBUG(11,("we already have an entry, deleting that\n"));
3514 fstr_sprintf(key_str, "CRED/%s", sid_to_fstring(tmp, sid));
3516 tdb_delete(cache->tdb, string_tdb_data(key_str));
3518 return NT_STATUS_OK;
3521 ret = tdb_traverse(cache->tdb, traverse_fn_get_credlist, NULL);
3523 return NT_STATUS_OK;
3524 } else if ((ret < 0) || (wcache_cred_list == NULL)) {
3525 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
3528 ZERO_STRUCTP(oldest);
3530 for (cred = wcache_cred_list; cred; cred = cred->next) {
3535 data = tdb_fetch(cache->tdb, string_tdb_data(cred->name));
3537 DEBUG(10,("wcache_remove_oldest_cached_creds: entry for [%s] not found\n",
3539 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
3543 t = IVAL(data.dptr, 0);
3544 SAFE_FREE(data.dptr);
3547 oldest = SMB_MALLOC_P(struct cred_list);
3548 if (oldest == NULL) {
3549 status = NT_STATUS_NO_MEMORY;
3553 fstrcpy(oldest->name, cred->name);
3554 oldest->created = t;
3558 if (t < oldest->created) {
3559 fstrcpy(oldest->name, cred->name);
3560 oldest->created = t;
3564 if (tdb_delete(cache->tdb, string_tdb_data(oldest->name)) == 0) {
3565 status = NT_STATUS_OK;
3567 status = NT_STATUS_UNSUCCESSFUL;
3570 for (cred = wcache_cred_list; cred; cred = next) {
3572 DLIST_REMOVE(wcache_cred_list, cred);
3580 /* Change the global online/offline state. */
3581 bool set_global_winbindd_state_offline(void)
3585 DEBUG(10,("set_global_winbindd_state_offline: offline requested.\n"));
3587 /* Only go offline if someone has created
3588 the key "WINBINDD_OFFLINE" in the cache tdb. */
3590 if (wcache == NULL || wcache->tdb == NULL) {
3591 DEBUG(10,("set_global_winbindd_state_offline: wcache not open yet.\n"));
3595 if (!lp_winbind_offline_logon()) {
3596 DEBUG(10,("set_global_winbindd_state_offline: rejecting.\n"));
3600 if (global_winbindd_offline_state) {
3601 /* Already offline. */
3605 data = tdb_fetch_bystring( wcache->tdb, "WINBINDD_OFFLINE" );
3607 if (!data.dptr || data.dsize != 4) {
3608 DEBUG(10,("set_global_winbindd_state_offline: offline state not set.\n"));
3609 SAFE_FREE(data.dptr);
3612 DEBUG(10,("set_global_winbindd_state_offline: offline state set.\n"));
3613 global_winbindd_offline_state = true;
3614 SAFE_FREE(data.dptr);
3619 void set_global_winbindd_state_online(void)
3621 DEBUG(10,("set_global_winbindd_state_online: online requested.\n"));
3623 if (!lp_winbind_offline_logon()) {
3624 DEBUG(10,("set_global_winbindd_state_online: rejecting.\n"));
3628 if (!global_winbindd_offline_state) {
3629 /* Already online. */
3632 global_winbindd_offline_state = false;
3638 /* Ensure there is no key "WINBINDD_OFFLINE" in the cache tdb. */
3639 tdb_delete_bystring(wcache->tdb, "WINBINDD_OFFLINE");
3642 bool get_global_winbindd_state_offline(void)
3644 return global_winbindd_offline_state;
3647 /***********************************************************************
3648 Validate functions for all possible cache tdb keys.
3649 ***********************************************************************/
3651 static struct cache_entry *create_centry_validate(const char *kstr, TDB_DATA data,
3652 struct tdb_validation_status *state)
3654 struct cache_entry *centry;
3656 centry = SMB_XMALLOC_P(struct cache_entry);
3657 centry->data = (unsigned char *)smb_memdup(data.dptr, data.dsize);
3658 if (!centry->data) {
3662 centry->len = data.dsize;
3665 if (centry->len < 16) {
3666 /* huh? corrupt cache? */
3667 DEBUG(0,("create_centry_validate: Corrupt cache for key %s "
3668 "(len < 16) ?\n", kstr));
3669 centry_free(centry);
3670 state->bad_entry = true;
3671 state->success = false;
3675 centry->status = NT_STATUS(centry_uint32(centry));
3676 centry->sequence_number = centry_uint32(centry);
3677 centry->timeout = centry_uint64_t(centry);
3681 static int validate_seqnum(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3682 struct tdb_validation_status *state)
3684 if (dbuf.dsize != 8) {
3685 DEBUG(0,("validate_seqnum: Corrupt cache for key %s (len %u != 8) ?\n",
3686 keystr, (unsigned int)dbuf.dsize ));
3687 state->bad_entry = true;
3693 static int validate_ns(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3694 struct tdb_validation_status *state)
3696 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3701 (void)centry_uint32(centry);
3702 if (NT_STATUS_IS_OK(centry->status)) {
3704 (void)centry_sid(centry, &sid);
3707 centry_free(centry);
3709 if (!(state->success)) {
3712 DEBUG(10,("validate_ns: %s ok\n", keystr));
3716 static int validate_sn(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3717 struct tdb_validation_status *state)
3719 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3724 if (NT_STATUS_IS_OK(centry->status)) {
3725 (void)centry_uint32(centry);
3726 (void)centry_string(centry, mem_ctx);
3727 (void)centry_string(centry, mem_ctx);
3730 centry_free(centry);
3732 if (!(state->success)) {
3735 DEBUG(10,("validate_sn: %s ok\n", keystr));
3739 static int validate_u(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3740 struct tdb_validation_status *state)
3742 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3749 (void)centry_string(centry, mem_ctx);
3750 (void)centry_string(centry, mem_ctx);
3751 (void)centry_string(centry, mem_ctx);
3752 (void)centry_string(centry, mem_ctx);
3753 (void)centry_uint32(centry);
3754 (void)centry_sid(centry, &sid);
3755 (void)centry_sid(centry, &sid);
3757 centry_free(centry);
3759 if (!(state->success)) {
3762 DEBUG(10,("validate_u: %s ok\n", keystr));
3766 static int validate_loc_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3767 struct tdb_validation_status *state)
3769 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3775 (void)centry_nttime(centry);
3776 (void)centry_nttime(centry);
3777 (void)centry_uint16(centry);
3779 centry_free(centry);
3781 if (!(state->success)) {
3784 DEBUG(10,("validate_loc_pol: %s ok\n", keystr));
3788 static int validate_pwd_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3789 struct tdb_validation_status *state)
3791 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3797 (void)centry_uint16(centry);
3798 (void)centry_uint16(centry);
3799 (void)centry_uint32(centry);
3800 (void)centry_nttime(centry);
3801 (void)centry_nttime(centry);
3803 centry_free(centry);
3805 if (!(state->success)) {
3808 DEBUG(10,("validate_pwd_pol: %s ok\n", keystr));
3812 static int validate_cred(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3813 struct tdb_validation_status *state)
3815 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3821 (void)centry_time(centry);
3822 (void)centry_hash16(centry, mem_ctx);
3824 /* We only have 17 bytes more data in the salted cred case. */
3825 if (centry->len - centry->ofs == 17) {
3826 (void)centry_hash16(centry, mem_ctx);
3829 centry_free(centry);
3831 if (!(state->success)) {
3834 DEBUG(10,("validate_cred: %s ok\n", keystr));
3838 static int validate_ul(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3839 struct tdb_validation_status *state)
3841 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3842 int32_t num_entries, i;
3848 num_entries = (int32_t)centry_uint32(centry);
3850 for (i=0; i< num_entries; i++) {
3852 (void)centry_string(centry, mem_ctx);
3853 (void)centry_string(centry, mem_ctx);
3854 (void)centry_string(centry, mem_ctx);
3855 (void)centry_string(centry, mem_ctx);
3856 (void)centry_sid(centry, &sid);
3857 (void)centry_sid(centry, &sid);
3860 centry_free(centry);
3862 if (!(state->success)) {
3865 DEBUG(10,("validate_ul: %s ok\n", keystr));
3869 static int validate_gl(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3870 struct tdb_validation_status *state)
3872 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3873 int32_t num_entries, i;
3879 num_entries = centry_uint32(centry);
3881 for (i=0; i< num_entries; i++) {
3882 (void)centry_string(centry, mem_ctx);
3883 (void)centry_string(centry, mem_ctx);
3884 (void)centry_uint32(centry);
3887 centry_free(centry);
3889 if (!(state->success)) {
3892 DEBUG(10,("validate_gl: %s ok\n", keystr));
3896 static int validate_ug(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3897 struct tdb_validation_status *state)
3899 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3900 int32_t num_groups, i;
3906 num_groups = centry_uint32(centry);
3908 for (i=0; i< num_groups; i++) {
3910 centry_sid(centry, &sid);
3913 centry_free(centry);
3915 if (!(state->success)) {
3918 DEBUG(10,("validate_ug: %s ok\n", keystr));
3922 static int validate_ua(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3923 struct tdb_validation_status *state)
3925 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3926 int32_t num_aliases, i;
3932 num_aliases = centry_uint32(centry);
3934 for (i=0; i < num_aliases; i++) {
3935 (void)centry_uint32(centry);
3938 centry_free(centry);
3940 if (!(state->success)) {
3943 DEBUG(10,("validate_ua: %s ok\n", keystr));
3947 static int validate_gm(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3948 struct tdb_validation_status *state)
3950 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3951 int32_t num_names, i;
3957 num_names = centry_uint32(centry);
3959 for (i=0; i< num_names; i++) {
3961 centry_sid(centry, &sid);
3962 (void)centry_string(centry, mem_ctx);
3963 (void)centry_uint32(centry);
3966 centry_free(centry);
3968 if (!(state->success)) {
3971 DEBUG(10,("validate_gm: %s ok\n", keystr));
3975 static int validate_dr(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3976 struct tdb_validation_status *state)
3978 /* Can't say anything about this other than must be nonzero. */
3979 if (dbuf.dsize == 0) {
3980 DEBUG(0,("validate_dr: Corrupt cache for key %s (len == 0) ?\n",
3982 state->bad_entry = true;
3983 state->success = false;
3987 DEBUG(10,("validate_dr: %s ok\n", keystr));
3991 static int validate_de(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3992 struct tdb_validation_status *state)
3994 /* Can't say anything about this other than must be nonzero. */
3995 if (dbuf.dsize == 0) {
3996 DEBUG(0,("validate_de: Corrupt cache for key %s (len == 0) ?\n",
3998 state->bad_entry = true;
3999 state->success = false;
4003 DEBUG(10,("validate_de: %s ok\n", keystr));
4007 static int validate_pwinfo(TALLOC_CTX *mem_ctx, const char *keystr,
4008 TDB_DATA dbuf, struct tdb_validation_status *state)
4010 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
4016 (void)centry_string(centry, mem_ctx);
4017 (void)centry_string(centry, mem_ctx);
4018 (void)centry_string(centry, mem_ctx);
4019 (void)centry_uint32(centry);
4021 centry_free(centry);
4023 if (!(state->success)) {
4026 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
4030 static int validate_nss_an(TALLOC_CTX *mem_ctx, const char *keystr,
4032 struct tdb_validation_status *state)
4034 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
4040 (void)centry_string( centry, mem_ctx );
4042 centry_free(centry);
4044 if (!(state->success)) {
4047 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
4051 static int validate_nss_na(TALLOC_CTX *mem_ctx, const char *keystr,
4053 struct tdb_validation_status *state)
4055 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
4061 (void)centry_string( centry, mem_ctx );
4063 centry_free(centry);
4065 if (!(state->success)) {
4068 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
4072 static int validate_trustdomcache(TALLOC_CTX *mem_ctx, const char *keystr,
4074 struct tdb_validation_status *state)
4076 if (dbuf.dsize == 0) {
4077 DEBUG(0, ("validate_trustdomcache: Corrupt cache for "
4078 "key %s (len ==0) ?\n", keystr));
4079 state->bad_entry = true;
4080 state->success = false;
4084 DEBUG(10, ("validate_trustdomcache: %s ok\n", keystr));
4085 DEBUGADD(10, (" Don't trust me, I am a DUMMY!\n"));
4089 static int validate_offline(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
4090 struct tdb_validation_status *state)
4092 if (dbuf.dsize != 4) {
4093 DEBUG(0,("validate_offline: Corrupt cache for key %s (len %u != 4) ?\n",
4094 keystr, (unsigned int)dbuf.dsize ));
4095 state->bad_entry = true;
4096 state->success = false;
4099 DEBUG(10,("validate_offline: %s ok\n", keystr));
4103 static int validate_ndr(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
4104 struct tdb_validation_status *state)
4107 * Ignore validation for now. The proper way to do this is with a
4108 * checksum. Just pure parsing does not really catch much.
4113 static int validate_cache_version(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
4114 struct tdb_validation_status *state)
4116 if (dbuf.dsize != 4) {
4117 DEBUG(0, ("validate_cache_version: Corrupt cache for "
4118 "key %s (len %u != 4) ?\n",
4119 keystr, (unsigned int)dbuf.dsize));
4120 state->bad_entry = true;
4121 state->success = false;
4125 DEBUG(10, ("validate_cache_version: %s ok\n", keystr));
4129 /***********************************************************************
4130 A list of all possible cache tdb keys with associated validation
4132 ***********************************************************************/
4134 struct key_val_struct {
4135 const char *keyname;
4136 int (*validate_data_fn)(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, struct tdb_validation_status* state);
4138 {"SEQNUM/", validate_seqnum},
4139 {"NS/", validate_ns},
4140 {"SN/", validate_sn},
4142 {"LOC_POL/", validate_loc_pol},
4143 {"PWD_POL/", validate_pwd_pol},
4144 {"CRED/", validate_cred},
4145 {"UL/", validate_ul},
4146 {"GL/", validate_gl},
4147 {"UG/", validate_ug},
4148 {"UA", validate_ua},
4149 {"GM/", validate_gm},
4150 {"DR/", validate_dr},
4151 {"DE/", validate_de},
4152 {"NSS/PWINFO/", validate_pwinfo},
4153 {"TRUSTDOMCACHE/", validate_trustdomcache},
4154 {"NSS/NA/", validate_nss_na},
4155 {"NSS/AN/", validate_nss_an},
4156 {"WINBINDD_OFFLINE", validate_offline},
4157 {"NDR/", validate_ndr},
4158 {WINBINDD_CACHE_VERSION_KEYSTR, validate_cache_version},
4162 /***********************************************************************
4163 Function to look at every entry in the tdb and validate it as far as
4165 ***********************************************************************/
4167 static int cache_traverse_validate_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, void *state)
4170 unsigned int max_key_len = 1024;
4171 struct tdb_validation_status *v_state = (struct tdb_validation_status *)state;
4173 /* Paranoia check. */
4174 if (strncmp("UA/", (const char *)kbuf.dptr, 3) == 0 ||
4175 strncmp("NDR/", (const char *)kbuf.dptr, 4) == 0) {
4176 max_key_len = 1024 * 1024;
4178 if (kbuf.dsize > max_key_len) {
4179 DEBUG(0, ("cache_traverse_validate_fn: key length too large: "
4181 (unsigned int)kbuf.dsize, (unsigned int)max_key_len));
4185 for (i = 0; key_val[i].keyname; i++) {
4186 size_t namelen = strlen(key_val[i].keyname);
4187 if (kbuf.dsize >= namelen && (
4188 strncmp(key_val[i].keyname, (const char *)kbuf.dptr, namelen)) == 0) {
4189 TALLOC_CTX *mem_ctx;
4193 keystr = SMB_MALLOC_ARRAY(char, kbuf.dsize+1);
4197 memcpy(keystr, kbuf.dptr, kbuf.dsize);
4198 keystr[kbuf.dsize] = '\0';
4200 mem_ctx = talloc_init("validate_ctx");
4206 ret = key_val[i].validate_data_fn(mem_ctx, keystr, dbuf,
4210 talloc_destroy(mem_ctx);
4215 DEBUG(0,("cache_traverse_validate_fn: unknown cache entry\nkey :\n"));
4216 dump_data(0, (uint8_t *)kbuf.dptr, kbuf.dsize);
4217 DEBUG(0,("data :\n"));
4218 dump_data(0, (uint8_t *)dbuf.dptr, dbuf.dsize);
4219 v_state->unknown_key = true;
4220 v_state->success = false;
4221 return 1; /* terminate. */
4224 static void validate_panic(const char *const why)
4226 DEBUG(0,("validating cache: would panic %s\n", why ));
4227 DEBUGADD(0, ("exiting instead (cache validation mode)\n"));
4231 static int wbcache_update_centry_fn(TDB_CONTEXT *tdb,
4239 if (is_non_centry_key(key)) {
4243 if (data.dptr == NULL || data.dsize == 0) {
4244 if (tdb_delete(tdb, key) < 0) {
4245 DEBUG(0, ("tdb_delete for [%s] failed!\n",
4251 /* add timeout to blob (uint64_t) */
4252 blob.dsize = data.dsize + 8;
4254 blob.dptr = SMB_XMALLOC_ARRAY(uint8_t, blob.dsize);
4255 if (blob.dptr == NULL) {
4258 memset(blob.dptr, 0, blob.dsize);
4260 /* copy status and seqnum */
4261 memcpy(blob.dptr, data.dptr, 8);
4264 ctimeout = lp_winbind_cache_time() + time(NULL);
4265 SBVAL(blob.dptr, 8, ctimeout);
4268 memcpy(blob.dptr + 16, data.dptr + 8, data.dsize - 8);
4270 if (tdb_store(tdb, key, blob, TDB_REPLACE) < 0) {
4271 DEBUG(0, ("tdb_store to update [%s] failed!\n",
4273 SAFE_FREE(blob.dptr);
4277 SAFE_FREE(blob.dptr);
4281 static bool wbcache_upgrade_v1_to_v2(TDB_CONTEXT *tdb)
4285 DEBUG(1, ("Upgrade to version 2 of the winbindd_cache.tdb\n"));
4287 rc = tdb_traverse(tdb, wbcache_update_centry_fn, NULL);
4295 /***********************************************************************
4296 Try and validate every entry in the winbindd cache. If we fail here,
4297 delete the cache tdb and return non-zero.
4298 ***********************************************************************/
4300 int winbindd_validate_cache(void)
4303 char *tdb_path = NULL;
4304 TDB_CONTEXT *tdb = NULL;
4308 DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
4309 smb_panic_fn = validate_panic;
4311 tdb_path = wcache_path();
4312 if (tdb_path == NULL) {
4316 tdb = tdb_open_log(tdb_path,
4317 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
4318 TDB_INCOMPATIBLE_HASH |
4319 ( lp_winbind_offline_logon()
4321 : TDB_DEFAULT | TDB_CLEAR_IF_FIRST ),
4325 DEBUG(0, ("winbindd_validate_cache: "
4326 "error opening/initializing tdb\n"));
4330 /* Version check and upgrade code. */
4331 if (!tdb_fetch_uint32(tdb, WINBINDD_CACHE_VERSION_KEYSTR, &vers_id)) {
4332 DEBUG(10, ("Fresh database\n"));
4333 tdb_store_uint32(tdb, WINBINDD_CACHE_VERSION_KEYSTR, WINBINDD_CACHE_VERSION);
4334 vers_id = WINBINDD_CACHE_VERSION;
4337 if (vers_id != WINBINDD_CACHE_VERSION) {
4338 if (vers_id == WINBINDD_CACHE_VER1) {
4339 ok = wbcache_upgrade_v1_to_v2(tdb);
4341 DEBUG(10, ("winbindd_validate_cache: upgrade to version 2 failed.\n"));
4346 tdb_store_uint32(tdb,
4347 WINBINDD_CACHE_VERSION_KEYSTR,
4348 WINBINDD_CACHE_VERSION);
4349 vers_id = WINBINDD_CACHE_VER2;
4355 ret = tdb_validate_and_backup(tdb_path, cache_traverse_validate_fn);
4358 DEBUG(10, ("winbindd_validate_cache: validation not successful.\n"));
4359 DEBUGADD(10, ("removing tdb %s.\n", tdb_path));
4364 TALLOC_FREE(tdb_path);
4365 DEBUG(10, ("winbindd_validate_cache: restoring panic function\n"));
4366 smb_panic_fn = smb_panic;
4370 /***********************************************************************
4371 Try and validate every entry in the winbindd cache.
4372 ***********************************************************************/
4374 int winbindd_validate_cache_nobackup(void)
4379 DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
4380 smb_panic_fn = validate_panic;
4382 tdb_path = wcache_path();
4383 if (tdb_path == NULL) {
4384 goto err_panic_restore;
4387 if (wcache == NULL || wcache->tdb == NULL) {
4388 ret = tdb_validate_open(tdb_path, cache_traverse_validate_fn);
4390 ret = tdb_validate(wcache->tdb, cache_traverse_validate_fn);
4394 DEBUG(10, ("winbindd_validate_cache_nobackup: validation not "
4398 TALLOC_FREE(tdb_path);
4400 DEBUG(10, ("winbindd_validate_cache_nobackup: restoring panic "
4402 smb_panic_fn = smb_panic;
4406 bool winbindd_cache_validate_and_initialize(void)
4408 close_winbindd_cache();
4410 if (lp_winbind_offline_logon()) {
4411 if (winbindd_validate_cache() < 0) {
4412 DEBUG(0, ("winbindd cache tdb corrupt and no backup "
4413 "could be restored.\n"));
4417 return initialize_winbindd_cache();
4420 /*********************************************************************
4421 ********************************************************************/
4423 static bool add_wbdomain_to_tdc_array( struct winbindd_domain *new_dom,
4424 struct winbindd_tdc_domain **domains,
4425 size_t *num_domains )
4427 struct winbindd_tdc_domain *list = NULL;
4430 bool set_only = false;
4432 /* don't allow duplicates */
4437 for ( i=0; i< (*num_domains); i++ ) {
4438 if ( strequal( new_dom->name, list[i].domain_name ) ) {
4439 DEBUG(10,("add_wbdomain_to_tdc_array: Found existing record for %s\n",
4450 list = talloc_array( NULL, struct winbindd_tdc_domain, 1 );
4453 list = talloc_realloc( *domains, *domains,
4454 struct winbindd_tdc_domain,
4459 ZERO_STRUCT( list[idx] );
4465 list[idx].domain_name = talloc_strdup(list, new_dom->name);
4466 if (list[idx].domain_name == NULL) {
4469 if (new_dom->alt_name != NULL) {
4470 list[idx].dns_name = talloc_strdup(list, new_dom->alt_name);
4471 if (list[idx].dns_name == NULL) {
4476 if ( !is_null_sid( &new_dom->sid ) ) {
4477 sid_copy( &list[idx].sid, &new_dom->sid );
4479 sid_copy(&list[idx].sid, &global_sid_NULL);
4482 if ( new_dom->domain_flags != 0x0 )
4483 list[idx].trust_flags = new_dom->domain_flags;
4485 if ( new_dom->domain_type != 0x0 )
4486 list[idx].trust_type = new_dom->domain_type;
4488 if ( new_dom->domain_trust_attribs != 0x0 )
4489 list[idx].trust_attribs = new_dom->domain_trust_attribs;
4493 *num_domains = idx + 1;
4499 /*********************************************************************
4500 ********************************************************************/
4502 static TDB_DATA make_tdc_key( const char *domain_name )
4504 char *keystr = NULL;
4505 TDB_DATA key = { NULL, 0 };
4507 if ( !domain_name ) {
4508 DEBUG(5,("make_tdc_key: Keyname workgroup is NULL!\n"));
4512 if (asprintf( &keystr, "TRUSTDOMCACHE/%s", domain_name ) == -1) {
4515 key = string_term_tdb_data(keystr);
4520 /*********************************************************************
4521 ********************************************************************/
4523 static int pack_tdc_domains( struct winbindd_tdc_domain *domains,
4525 unsigned char **buf )
4527 unsigned char *buffer = NULL;
4532 DEBUG(10,("pack_tdc_domains: Packing %d trusted domains\n",
4540 /* Store the number of array items first */
4541 len += tdb_pack( buffer+len, buflen-len, "d",
4544 /* now pack each domain trust record */
4545 for ( i=0; i<num_domains; i++ ) {
4550 DEBUG(10,("pack_tdc_domains: Packing domain %s (%s)\n",
4551 domains[i].domain_name,
4552 domains[i].dns_name ? domains[i].dns_name : "UNKNOWN" ));
4555 len += tdb_pack( buffer+len, buflen-len, "fffddd",
4556 domains[i].domain_name,
4557 domains[i].dns_name ? domains[i].dns_name : "",
4558 sid_to_fstring(tmp, &domains[i].sid),
4559 domains[i].trust_flags,
4560 domains[i].trust_attribs,
4561 domains[i].trust_type );
4564 if ( buflen < len ) {
4566 if ( (buffer = SMB_MALLOC_ARRAY(unsigned char, len)) == NULL ) {
4567 DEBUG(0,("pack_tdc_domains: failed to alloc buffer!\n"));
4581 /*********************************************************************
4582 ********************************************************************/
4584 static size_t unpack_tdc_domains( unsigned char *buf, int buflen,
4585 struct winbindd_tdc_domain **domains )
4587 fstring domain_name, dns_name, sid_string;
4588 uint32_t type, attribs, flags;
4592 struct winbindd_tdc_domain *list = NULL;
4594 /* get the number of domains */
4595 len += tdb_unpack( buf+len, buflen-len, "d", &num_domains);
4597 DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
4601 list = talloc_array( NULL, struct winbindd_tdc_domain, num_domains );
4603 DEBUG(0,("unpack_tdc_domains: Failed to talloc() domain list!\n"));
4607 for ( i=0; i<num_domains; i++ ) {
4610 this_len = tdb_unpack( buf+len, buflen-len, "fffddd",
4618 if ( this_len == -1 ) {
4619 DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
4620 TALLOC_FREE( list );
4625 DEBUG(11,("unpack_tdc_domains: Unpacking domain %s (%s) "
4626 "SID %s, flags = 0x%x, attribs = 0x%x, type = 0x%x\n",
4627 domain_name, dns_name, sid_string,
4628 flags, attribs, type));
4630 list[i].domain_name = talloc_strdup( list, domain_name );
4631 list[i].dns_name = NULL;
4632 if (dns_name[0] != '\0') {
4633 list[i].dns_name = talloc_strdup(list, dns_name);
4635 if ( !string_to_sid( &(list[i].sid), sid_string ) ) {
4636 DEBUG(10,("unpack_tdc_domains: no SID for domain %s\n",
4639 list[i].trust_flags = flags;
4640 list[i].trust_attribs = attribs;
4641 list[i].trust_type = type;
4649 /*********************************************************************
4650 ********************************************************************/
4652 static bool wcache_tdc_store_list( struct winbindd_tdc_domain *domains, size_t num_domains )
4654 TDB_DATA key = make_tdc_key( lp_workgroup() );
4655 TDB_DATA data = { NULL, 0 };
4661 /* See if we were asked to delete the cache entry */
4664 ret = tdb_delete( wcache->tdb, key );
4668 data.dsize = pack_tdc_domains( domains, num_domains, &data.dptr );
4675 ret = tdb_store( wcache->tdb, key, data, 0 );
4678 SAFE_FREE( data.dptr );
4679 SAFE_FREE( key.dptr );
4681 return ( ret == 0 );
4684 /*********************************************************************
4685 ********************************************************************/
4687 bool wcache_tdc_fetch_list( struct winbindd_tdc_domain **domains, size_t *num_domains )
4689 TDB_DATA key = make_tdc_key( lp_workgroup() );
4690 TDB_DATA data = { NULL, 0 };
4698 data = tdb_fetch( wcache->tdb, key );
4700 SAFE_FREE( key.dptr );
4705 *num_domains = unpack_tdc_domains( data.dptr, data.dsize, domains );
4707 SAFE_FREE( data.dptr );
4715 /*********************************************************************
4716 ********************************************************************/
4718 bool wcache_tdc_add_domain( struct winbindd_domain *domain )
4720 struct winbindd_tdc_domain *dom_list = NULL;
4721 size_t num_domains = 0;
4724 DEBUG(10,("wcache_tdc_add_domain: Adding domain %s (%s), SID %s, "
4725 "flags = 0x%x, attributes = 0x%x, type = 0x%x\n",
4726 domain->name, domain->alt_name,
4727 sid_string_dbg(&domain->sid),
4728 domain->domain_flags,
4729 domain->domain_trust_attribs,
4730 domain->domain_type));
4732 if ( !init_wcache() ) {
4736 /* fetch the list */
4738 wcache_tdc_fetch_list( &dom_list, &num_domains );
4740 /* add the new domain */
4742 if ( !add_wbdomain_to_tdc_array( domain, &dom_list, &num_domains ) ) {
4746 /* pack the domain */
4748 if ( !wcache_tdc_store_list( dom_list, num_domains ) ) {
4756 TALLOC_FREE( dom_list );
4761 static struct winbindd_tdc_domain *wcache_tdc_dup_domain(
4762 TALLOC_CTX *mem_ctx, const struct winbindd_tdc_domain *src)
4764 struct winbindd_tdc_domain *dst;
4766 dst = talloc(mem_ctx, struct winbindd_tdc_domain);
4770 dst->domain_name = talloc_strdup(dst, src->domain_name);
4771 if (dst->domain_name == NULL) {
4775 dst->dns_name = NULL;
4776 if (src->dns_name != NULL) {
4777 dst->dns_name = talloc_strdup(dst, src->dns_name);
4778 if (dst->dns_name == NULL) {
4783 sid_copy(&dst->sid, &src->sid);
4784 dst->trust_flags = src->trust_flags;
4785 dst->trust_type = src->trust_type;
4786 dst->trust_attribs = src->trust_attribs;
4793 /*********************************************************************
4794 ********************************************************************/
4796 struct winbindd_tdc_domain * wcache_tdc_fetch_domain( TALLOC_CTX *ctx, const char *name )
4798 struct winbindd_tdc_domain *dom_list = NULL;
4799 size_t num_domains = 0;
4801 struct winbindd_tdc_domain *d = NULL;
4803 DEBUG(10,("wcache_tdc_fetch_domain: Searching for domain %s\n", name));
4805 if ( !init_wcache() ) {
4809 /* fetch the list */
4811 wcache_tdc_fetch_list( &dom_list, &num_domains );
4813 for ( i=0; i<num_domains; i++ ) {
4814 if ( strequal(name, dom_list[i].domain_name) ||
4815 strequal(name, dom_list[i].dns_name) )
4817 DEBUG(10,("wcache_tdc_fetch_domain: Found domain %s\n",
4820 d = wcache_tdc_dup_domain(ctx, &dom_list[i]);
4825 TALLOC_FREE( dom_list );
4830 /*********************************************************************
4831 ********************************************************************/
4833 struct winbindd_tdc_domain*
4834 wcache_tdc_fetch_domainbysid(TALLOC_CTX *ctx,
4835 const struct dom_sid *sid)
4837 struct winbindd_tdc_domain *dom_list = NULL;
4838 size_t num_domains = 0;
4840 struct winbindd_tdc_domain *d = NULL;
4842 DEBUG(10,("wcache_tdc_fetch_domainbysid: Searching for domain %s\n",
4843 sid_string_dbg(sid)));
4845 if (!init_wcache()) {
4849 /* fetch the list */
4851 wcache_tdc_fetch_list(&dom_list, &num_domains);
4853 for (i = 0; i<num_domains; i++) {
4854 if (dom_sid_equal(sid, &(dom_list[i].sid))) {
4855 DEBUG(10, ("wcache_tdc_fetch_domainbysid: "
4856 "Found domain %s for SID %s\n",
4857 dom_list[i].domain_name,
4858 sid_string_dbg(sid)));
4860 d = wcache_tdc_dup_domain(ctx, &dom_list[i]);
4865 TALLOC_FREE(dom_list);
4871 /*********************************************************************
4872 ********************************************************************/
4874 void wcache_tdc_clear( void )
4876 if ( !init_wcache() )
4879 wcache_tdc_store_list( NULL, 0 );
4885 /*********************************************************************
4886 ********************************************************************/
4888 static void wcache_save_user_pwinfo(struct winbindd_domain *domain,
4890 const struct dom_sid *user_sid,
4891 const char *homedir,
4896 struct cache_entry *centry;
4899 if ( (centry = centry_start(domain, status)) == NULL )
4902 centry_put_string( centry, homedir );
4903 centry_put_string( centry, shell );
4904 centry_put_string( centry, gecos );
4905 centry_put_uint32( centry, gid );
4907 centry_end(centry, "NSS/PWINFO/%s", sid_to_fstring(tmp, user_sid) );
4909 DEBUG(10,("wcache_save_user_pwinfo: %s\n", sid_string_dbg(user_sid) ));
4911 centry_free(centry);
4916 NTSTATUS nss_get_info_cached( struct winbindd_domain *domain,
4917 const struct dom_sid *user_sid,
4919 const char **homedir, const char **shell,
4920 const char **gecos, gid_t *p_gid)
4922 struct winbind_cache *cache = get_cache(domain);
4923 struct cache_entry *centry = NULL;
4930 centry = wcache_fetch(cache, domain, "NSS/PWINFO/%s",
4931 sid_to_fstring(tmp, user_sid));
4936 *homedir = centry_string( centry, ctx );
4937 *shell = centry_string( centry, ctx );
4938 *gecos = centry_string( centry, ctx );
4939 *p_gid = centry_uint32( centry );
4941 centry_free(centry);
4943 DEBUG(10,("nss_get_info_cached: [Cached] - user_sid %s\n",
4944 sid_string_dbg(user_sid)));
4946 return NT_STATUS_OK;
4950 nt_status = nss_get_info( domain->name, user_sid, ctx,
4951 homedir, shell, gecos, p_gid );
4953 DEBUG(10, ("nss_get_info returned %s\n", nt_errstr(nt_status)));
4955 if ( NT_STATUS_IS_OK(nt_status) ) {
4956 DEBUG(10, ("result:\n\thomedir = '%s'\n", *homedir));
4957 DEBUGADD(10, ("\tshell = '%s'\n", *shell));
4958 DEBUGADD(10, ("\tgecos = '%s'\n", *gecos));
4959 DEBUGADD(10, ("\tgid = '%u'\n", (unsigned int)*p_gid));
4961 wcache_save_user_pwinfo( domain, nt_status, user_sid,
4962 *homedir, *shell, *gecos, *p_gid );
4965 if ( NT_STATUS_EQUAL( nt_status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND ) ) {
4966 DEBUG(5,("nss_get_info_cached: Setting domain %s offline\n",
4968 set_domain_offline( domain );
4976 /* the cache backend methods are exposed via this structure */
4977 struct winbindd_methods cache_methods = {
4995 static bool wcache_ndr_key(TALLOC_CTX *mem_ctx, const char *domain_name,
4996 uint32_t opnum, const DATA_BLOB *req,
5002 key = talloc_asprintf(mem_ctx, "NDR/%s/%d/", domain_name, (int)opnum);
5006 keylen = talloc_get_size(key) - 1;
5008 key = talloc_realloc(mem_ctx, key, char, keylen + req->length);
5012 memcpy(key + keylen, req->data, req->length);
5014 pkey->dptr = (uint8_t *)key;
5015 pkey->dsize = talloc_get_size(key);
5019 static bool wcache_opnum_cacheable(uint32_t opnum)
5022 case NDR_WBINT_PING:
5023 case NDR_WBINT_QUERYSEQUENCENUMBER:
5024 case NDR_WBINT_ALLOCATEUID:
5025 case NDR_WBINT_ALLOCATEGID:
5026 case NDR_WBINT_CHECKMACHINEACCOUNT:
5027 case NDR_WBINT_CHANGEMACHINEACCOUNT:
5028 case NDR_WBINT_PINGDC:
5034 bool wcache_fetch_ndr(TALLOC_CTX *mem_ctx, struct winbindd_domain *domain,
5035 uint32_t opnum, const DATA_BLOB *req, DATA_BLOB *resp)
5040 if (!wcache_opnum_cacheable(opnum) ||
5041 is_my_own_sam_domain(domain) ||
5042 is_builtin_domain(domain)) {
5046 if (wcache->tdb == NULL) {
5050 if (!wcache_ndr_key(talloc_tos(), domain->name, opnum, req, &key)) {
5053 data = tdb_fetch(wcache->tdb, key);
5054 TALLOC_FREE(key.dptr);
5056 if (data.dptr == NULL) {
5059 if (data.dsize < 12) {
5063 if (!is_domain_offline(domain)) {
5064 uint32_t entry_seqnum, dom_seqnum, last_check;
5065 uint64_t entry_timeout;
5067 if (!wcache_fetch_seqnum(domain->name, &dom_seqnum,
5071 entry_seqnum = IVAL(data.dptr, 0);
5072 if (entry_seqnum != dom_seqnum) {
5073 DEBUG(10, ("Entry has wrong sequence number: %d\n",
5074 (int)entry_seqnum));
5077 entry_timeout = BVAL(data.dptr, 4);
5078 if (time(NULL) > entry_timeout) {
5079 DEBUG(10, ("Entry has timed out\n"));
5084 resp->data = (uint8_t *)talloc_memdup(mem_ctx, data.dptr + 12,
5086 if (resp->data == NULL) {
5087 DEBUG(10, ("talloc failed\n"));
5090 resp->length = data.dsize - 12;
5094 SAFE_FREE(data.dptr);
5098 void wcache_store_ndr(struct winbindd_domain *domain, uint32_t opnum,
5099 const DATA_BLOB *req, const DATA_BLOB *resp)
5102 uint32_t dom_seqnum, last_check;
5105 if (!wcache_opnum_cacheable(opnum) ||
5106 is_my_own_sam_domain(domain) ||
5107 is_builtin_domain(domain)) {
5111 if (wcache->tdb == NULL) {
5115 if (!wcache_fetch_seqnum(domain->name, &dom_seqnum, &last_check)) {
5116 DEBUG(10, ("could not fetch seqnum for domain %s\n",
5121 if (!wcache_ndr_key(talloc_tos(), domain->name, opnum, req, &key)) {
5125 timeout = time(NULL) + lp_winbind_cache_time();
5127 data.dsize = resp->length + 12;
5128 data.dptr = talloc_array(key.dptr, uint8_t, data.dsize);
5129 if (data.dptr == NULL) {
5133 SIVAL(data.dptr, 0, dom_seqnum);
5134 SBVAL(data.dptr, 4, timeout);
5135 memcpy(data.dptr + 12, resp->data, resp->length);
5137 tdb_store(wcache->tdb, key, data, 0);
5140 TALLOC_FREE(key.dptr);