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);
3342 void cache_name2sid(struct winbindd_domain *domain,
3343 const char *domain_name, const char *name,
3344 enum lsa_SidType type, const struct dom_sid *sid)
3346 refresh_sequence_number(domain);
3347 wcache_save_name_to_sid(domain, NT_STATUS_OK, domain_name, name,
3352 * The original idea that this cache only contains centries has
3353 * been blurred - now other stuff gets put in here. Ensure we
3354 * ignore these things on cleanup.
3357 static int traverse_fn_cleanup(TDB_CONTEXT *the_tdb, TDB_DATA kbuf,
3358 TDB_DATA dbuf, void *state)
3360 struct cache_entry *centry;
3362 if (is_non_centry_key(kbuf)) {
3366 centry = wcache_fetch_raw((char *)kbuf.dptr);
3371 if (!NT_STATUS_IS_OK(centry->status)) {
3372 DEBUG(10,("deleting centry %s\n", (const char *)kbuf.dptr));
3373 tdb_delete(the_tdb, kbuf);
3376 centry_free(centry);
3380 /* flush the cache */
3381 void wcache_flush_cache(void)
3388 tdb_close(wcache->tdb);
3391 if (!winbindd_use_cache()) {
3395 db_path = wcache_path();
3396 if (db_path == NULL) {
3400 /* when working offline we must not clear the cache on restart */
3401 wcache->tdb = tdb_open_log(db_path,
3402 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
3403 TDB_INCOMPATIBLE_HASH |
3404 (lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST)),
3405 O_RDWR|O_CREAT, 0600);
3406 TALLOC_FREE(db_path);
3408 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
3412 tdb_traverse(wcache->tdb, traverse_fn_cleanup, NULL);
3414 DEBUG(10,("wcache_flush_cache success\n"));
3417 /* Count cached creds */
3419 static int traverse_fn_cached_creds(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
3422 int *cred_count = (int*)state;
3424 if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
3430 NTSTATUS wcache_count_cached_creds(struct winbindd_domain *domain, int *count)
3432 struct winbind_cache *cache = get_cache(domain);
3437 return NT_STATUS_INTERNAL_DB_ERROR;
3440 tdb_traverse(cache->tdb, traverse_fn_cached_creds, (void *)count);
3442 return NT_STATUS_OK;
3446 struct cred_list *prev, *next;
3451 static struct cred_list *wcache_cred_list;
3453 static int traverse_fn_get_credlist(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
3456 struct cred_list *cred;
3458 if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
3460 cred = SMB_MALLOC_P(struct cred_list);
3462 DEBUG(0,("traverse_fn_remove_first_creds: failed to malloc new entry for list\n"));
3468 /* save a copy of the key */
3470 fstrcpy(cred->name, (const char *)kbuf.dptr);
3471 DLIST_ADD(wcache_cred_list, cred);
3477 NTSTATUS wcache_remove_oldest_cached_creds(struct winbindd_domain *domain, const struct dom_sid *sid)
3479 struct winbind_cache *cache = get_cache(domain);
3482 struct cred_list *cred, *next, *oldest = NULL;
3485 return NT_STATUS_INTERNAL_DB_ERROR;
3488 /* we possibly already have an entry */
3489 if (sid && NT_STATUS_IS_OK(wcache_cached_creds_exist(domain, sid))) {
3491 fstring key_str, tmp;
3493 DEBUG(11,("we already have an entry, deleting that\n"));
3495 fstr_sprintf(key_str, "CRED/%s", sid_to_fstring(tmp, sid));
3497 tdb_delete(cache->tdb, string_tdb_data(key_str));
3499 return NT_STATUS_OK;
3502 ret = tdb_traverse(cache->tdb, traverse_fn_get_credlist, NULL);
3504 return NT_STATUS_OK;
3505 } else if ((ret < 0) || (wcache_cred_list == NULL)) {
3506 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
3509 ZERO_STRUCTP(oldest);
3511 for (cred = wcache_cred_list; cred; cred = cred->next) {
3516 data = tdb_fetch(cache->tdb, string_tdb_data(cred->name));
3518 DEBUG(10,("wcache_remove_oldest_cached_creds: entry for [%s] not found\n",
3520 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
3524 t = IVAL(data.dptr, 0);
3525 SAFE_FREE(data.dptr);
3528 oldest = SMB_MALLOC_P(struct cred_list);
3529 if (oldest == NULL) {
3530 status = NT_STATUS_NO_MEMORY;
3534 fstrcpy(oldest->name, cred->name);
3535 oldest->created = t;
3539 if (t < oldest->created) {
3540 fstrcpy(oldest->name, cred->name);
3541 oldest->created = t;
3545 if (tdb_delete(cache->tdb, string_tdb_data(oldest->name)) == 0) {
3546 status = NT_STATUS_OK;
3548 status = NT_STATUS_UNSUCCESSFUL;
3551 for (cred = wcache_cred_list; cred; cred = next) {
3553 DLIST_REMOVE(wcache_cred_list, cred);
3561 /* Change the global online/offline state. */
3562 bool set_global_winbindd_state_offline(void)
3566 DEBUG(10,("set_global_winbindd_state_offline: offline requested.\n"));
3568 /* Only go offline if someone has created
3569 the key "WINBINDD_OFFLINE" in the cache tdb. */
3571 if (wcache == NULL || wcache->tdb == NULL) {
3572 DEBUG(10,("set_global_winbindd_state_offline: wcache not open yet.\n"));
3576 if (!lp_winbind_offline_logon()) {
3577 DEBUG(10,("set_global_winbindd_state_offline: rejecting.\n"));
3581 if (global_winbindd_offline_state) {
3582 /* Already offline. */
3586 data = tdb_fetch_bystring( wcache->tdb, "WINBINDD_OFFLINE" );
3588 if (!data.dptr || data.dsize != 4) {
3589 DEBUG(10,("set_global_winbindd_state_offline: offline state not set.\n"));
3590 SAFE_FREE(data.dptr);
3593 DEBUG(10,("set_global_winbindd_state_offline: offline state set.\n"));
3594 global_winbindd_offline_state = true;
3595 SAFE_FREE(data.dptr);
3600 void set_global_winbindd_state_online(void)
3602 DEBUG(10,("set_global_winbindd_state_online: online requested.\n"));
3604 if (!lp_winbind_offline_logon()) {
3605 DEBUG(10,("set_global_winbindd_state_online: rejecting.\n"));
3609 if (!global_winbindd_offline_state) {
3610 /* Already online. */
3613 global_winbindd_offline_state = false;
3619 /* Ensure there is no key "WINBINDD_OFFLINE" in the cache tdb. */
3620 tdb_delete_bystring(wcache->tdb, "WINBINDD_OFFLINE");
3623 bool get_global_winbindd_state_offline(void)
3625 return global_winbindd_offline_state;
3628 /***********************************************************************
3629 Validate functions for all possible cache tdb keys.
3630 ***********************************************************************/
3632 static struct cache_entry *create_centry_validate(const char *kstr, TDB_DATA data,
3633 struct tdb_validation_status *state)
3635 struct cache_entry *centry;
3637 centry = SMB_XMALLOC_P(struct cache_entry);
3638 centry->data = (unsigned char *)smb_memdup(data.dptr, data.dsize);
3639 if (!centry->data) {
3643 centry->len = data.dsize;
3646 if (centry->len < 16) {
3647 /* huh? corrupt cache? */
3648 DEBUG(0,("create_centry_validate: Corrupt cache for key %s "
3649 "(len < 16) ?\n", kstr));
3650 centry_free(centry);
3651 state->bad_entry = true;
3652 state->success = false;
3656 centry->status = NT_STATUS(centry_uint32(centry));
3657 centry->sequence_number = centry_uint32(centry);
3658 centry->timeout = centry_uint64_t(centry);
3662 static int validate_seqnum(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3663 struct tdb_validation_status *state)
3665 if (dbuf.dsize != 8) {
3666 DEBUG(0,("validate_seqnum: Corrupt cache for key %s (len %u != 8) ?\n",
3667 keystr, (unsigned int)dbuf.dsize ));
3668 state->bad_entry = true;
3674 static int validate_ns(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3675 struct tdb_validation_status *state)
3677 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3682 (void)centry_uint32(centry);
3683 if (NT_STATUS_IS_OK(centry->status)) {
3685 (void)centry_sid(centry, &sid);
3688 centry_free(centry);
3690 if (!(state->success)) {
3693 DEBUG(10,("validate_ns: %s ok\n", keystr));
3697 static int validate_sn(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3698 struct tdb_validation_status *state)
3700 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3705 if (NT_STATUS_IS_OK(centry->status)) {
3706 (void)centry_uint32(centry);
3707 (void)centry_string(centry, mem_ctx);
3708 (void)centry_string(centry, mem_ctx);
3711 centry_free(centry);
3713 if (!(state->success)) {
3716 DEBUG(10,("validate_sn: %s ok\n", keystr));
3720 static int validate_u(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3721 struct tdb_validation_status *state)
3723 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3730 (void)centry_string(centry, mem_ctx);
3731 (void)centry_string(centry, mem_ctx);
3732 (void)centry_string(centry, mem_ctx);
3733 (void)centry_string(centry, mem_ctx);
3734 (void)centry_uint32(centry);
3735 (void)centry_sid(centry, &sid);
3736 (void)centry_sid(centry, &sid);
3738 centry_free(centry);
3740 if (!(state->success)) {
3743 DEBUG(10,("validate_u: %s ok\n", keystr));
3747 static int validate_loc_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3748 struct tdb_validation_status *state)
3750 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3756 (void)centry_nttime(centry);
3757 (void)centry_nttime(centry);
3758 (void)centry_uint16(centry);
3760 centry_free(centry);
3762 if (!(state->success)) {
3765 DEBUG(10,("validate_loc_pol: %s ok\n", keystr));
3769 static int validate_pwd_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3770 struct tdb_validation_status *state)
3772 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3778 (void)centry_uint16(centry);
3779 (void)centry_uint16(centry);
3780 (void)centry_uint32(centry);
3781 (void)centry_nttime(centry);
3782 (void)centry_nttime(centry);
3784 centry_free(centry);
3786 if (!(state->success)) {
3789 DEBUG(10,("validate_pwd_pol: %s ok\n", keystr));
3793 static int validate_cred(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3794 struct tdb_validation_status *state)
3796 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3802 (void)centry_time(centry);
3803 (void)centry_hash16(centry, mem_ctx);
3805 /* We only have 17 bytes more data in the salted cred case. */
3806 if (centry->len - centry->ofs == 17) {
3807 (void)centry_hash16(centry, mem_ctx);
3810 centry_free(centry);
3812 if (!(state->success)) {
3815 DEBUG(10,("validate_cred: %s ok\n", keystr));
3819 static int validate_ul(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3820 struct tdb_validation_status *state)
3822 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3823 int32_t num_entries, i;
3829 num_entries = (int32_t)centry_uint32(centry);
3831 for (i=0; i< num_entries; i++) {
3833 (void)centry_string(centry, mem_ctx);
3834 (void)centry_string(centry, mem_ctx);
3835 (void)centry_string(centry, mem_ctx);
3836 (void)centry_string(centry, mem_ctx);
3837 (void)centry_sid(centry, &sid);
3838 (void)centry_sid(centry, &sid);
3841 centry_free(centry);
3843 if (!(state->success)) {
3846 DEBUG(10,("validate_ul: %s ok\n", keystr));
3850 static int validate_gl(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3851 struct tdb_validation_status *state)
3853 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3854 int32_t num_entries, i;
3860 num_entries = centry_uint32(centry);
3862 for (i=0; i< num_entries; i++) {
3863 (void)centry_string(centry, mem_ctx);
3864 (void)centry_string(centry, mem_ctx);
3865 (void)centry_uint32(centry);
3868 centry_free(centry);
3870 if (!(state->success)) {
3873 DEBUG(10,("validate_gl: %s ok\n", keystr));
3877 static int validate_ug(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3878 struct tdb_validation_status *state)
3880 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3881 int32_t num_groups, i;
3887 num_groups = centry_uint32(centry);
3889 for (i=0; i< num_groups; i++) {
3891 centry_sid(centry, &sid);
3894 centry_free(centry);
3896 if (!(state->success)) {
3899 DEBUG(10,("validate_ug: %s ok\n", keystr));
3903 static int validate_ua(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3904 struct tdb_validation_status *state)
3906 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3907 int32_t num_aliases, i;
3913 num_aliases = centry_uint32(centry);
3915 for (i=0; i < num_aliases; i++) {
3916 (void)centry_uint32(centry);
3919 centry_free(centry);
3921 if (!(state->success)) {
3924 DEBUG(10,("validate_ua: %s ok\n", keystr));
3928 static int validate_gm(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3929 struct tdb_validation_status *state)
3931 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3932 int32_t num_names, i;
3938 num_names = centry_uint32(centry);
3940 for (i=0; i< num_names; i++) {
3942 centry_sid(centry, &sid);
3943 (void)centry_string(centry, mem_ctx);
3944 (void)centry_uint32(centry);
3947 centry_free(centry);
3949 if (!(state->success)) {
3952 DEBUG(10,("validate_gm: %s ok\n", keystr));
3956 static int validate_dr(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3957 struct tdb_validation_status *state)
3959 /* Can't say anything about this other than must be nonzero. */
3960 if (dbuf.dsize == 0) {
3961 DEBUG(0,("validate_dr: Corrupt cache for key %s (len == 0) ?\n",
3963 state->bad_entry = true;
3964 state->success = false;
3968 DEBUG(10,("validate_dr: %s ok\n", keystr));
3972 static int validate_de(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3973 struct tdb_validation_status *state)
3975 /* Can't say anything about this other than must be nonzero. */
3976 if (dbuf.dsize == 0) {
3977 DEBUG(0,("validate_de: Corrupt cache for key %s (len == 0) ?\n",
3979 state->bad_entry = true;
3980 state->success = false;
3984 DEBUG(10,("validate_de: %s ok\n", keystr));
3988 static int validate_pwinfo(TALLOC_CTX *mem_ctx, const char *keystr,
3989 TDB_DATA dbuf, struct tdb_validation_status *state)
3991 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3997 (void)centry_string(centry, mem_ctx);
3998 (void)centry_string(centry, mem_ctx);
3999 (void)centry_string(centry, mem_ctx);
4000 (void)centry_uint32(centry);
4002 centry_free(centry);
4004 if (!(state->success)) {
4007 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
4011 static int validate_nss_an(TALLOC_CTX *mem_ctx, const char *keystr,
4013 struct tdb_validation_status *state)
4015 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
4021 (void)centry_string( centry, mem_ctx );
4023 centry_free(centry);
4025 if (!(state->success)) {
4028 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
4032 static int validate_nss_na(TALLOC_CTX *mem_ctx, const char *keystr,
4034 struct tdb_validation_status *state)
4036 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
4042 (void)centry_string( centry, mem_ctx );
4044 centry_free(centry);
4046 if (!(state->success)) {
4049 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
4053 static int validate_trustdomcache(TALLOC_CTX *mem_ctx, const char *keystr,
4055 struct tdb_validation_status *state)
4057 if (dbuf.dsize == 0) {
4058 DEBUG(0, ("validate_trustdomcache: Corrupt cache for "
4059 "key %s (len ==0) ?\n", keystr));
4060 state->bad_entry = true;
4061 state->success = false;
4065 DEBUG(10, ("validate_trustdomcache: %s ok\n", keystr));
4066 DEBUGADD(10, (" Don't trust me, I am a DUMMY!\n"));
4070 static int validate_offline(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
4071 struct tdb_validation_status *state)
4073 if (dbuf.dsize != 4) {
4074 DEBUG(0,("validate_offline: Corrupt cache for key %s (len %u != 4) ?\n",
4075 keystr, (unsigned int)dbuf.dsize ));
4076 state->bad_entry = true;
4077 state->success = false;
4080 DEBUG(10,("validate_offline: %s ok\n", keystr));
4084 static int validate_ndr(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
4085 struct tdb_validation_status *state)
4088 * Ignore validation for now. The proper way to do this is with a
4089 * checksum. Just pure parsing does not really catch much.
4094 static int validate_cache_version(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
4095 struct tdb_validation_status *state)
4097 if (dbuf.dsize != 4) {
4098 DEBUG(0, ("validate_cache_version: Corrupt cache for "
4099 "key %s (len %u != 4) ?\n",
4100 keystr, (unsigned int)dbuf.dsize));
4101 state->bad_entry = true;
4102 state->success = false;
4106 DEBUG(10, ("validate_cache_version: %s ok\n", keystr));
4110 /***********************************************************************
4111 A list of all possible cache tdb keys with associated validation
4113 ***********************************************************************/
4115 struct key_val_struct {
4116 const char *keyname;
4117 int (*validate_data_fn)(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, struct tdb_validation_status* state);
4119 {"SEQNUM/", validate_seqnum},
4120 {"NS/", validate_ns},
4121 {"SN/", validate_sn},
4123 {"LOC_POL/", validate_loc_pol},
4124 {"PWD_POL/", validate_pwd_pol},
4125 {"CRED/", validate_cred},
4126 {"UL/", validate_ul},
4127 {"GL/", validate_gl},
4128 {"UG/", validate_ug},
4129 {"UA", validate_ua},
4130 {"GM/", validate_gm},
4131 {"DR/", validate_dr},
4132 {"DE/", validate_de},
4133 {"NSS/PWINFO/", validate_pwinfo},
4134 {"TRUSTDOMCACHE/", validate_trustdomcache},
4135 {"NSS/NA/", validate_nss_na},
4136 {"NSS/AN/", validate_nss_an},
4137 {"WINBINDD_OFFLINE", validate_offline},
4138 {"NDR/", validate_ndr},
4139 {WINBINDD_CACHE_VERSION_KEYSTR, validate_cache_version},
4143 /***********************************************************************
4144 Function to look at every entry in the tdb and validate it as far as
4146 ***********************************************************************/
4148 static int cache_traverse_validate_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, void *state)
4151 unsigned int max_key_len = 1024;
4152 struct tdb_validation_status *v_state = (struct tdb_validation_status *)state;
4154 /* Paranoia check. */
4155 if (strncmp("UA/", (const char *)kbuf.dptr, 3) == 0 ||
4156 strncmp("NDR/", (const char *)kbuf.dptr, 4) == 0) {
4157 max_key_len = 1024 * 1024;
4159 if (kbuf.dsize > max_key_len) {
4160 DEBUG(0, ("cache_traverse_validate_fn: key length too large: "
4162 (unsigned int)kbuf.dsize, (unsigned int)max_key_len));
4166 for (i = 0; key_val[i].keyname; i++) {
4167 size_t namelen = strlen(key_val[i].keyname);
4168 if (kbuf.dsize >= namelen && (
4169 strncmp(key_val[i].keyname, (const char *)kbuf.dptr, namelen)) == 0) {
4170 TALLOC_CTX *mem_ctx;
4174 keystr = SMB_MALLOC_ARRAY(char, kbuf.dsize+1);
4178 memcpy(keystr, kbuf.dptr, kbuf.dsize);
4179 keystr[kbuf.dsize] = '\0';
4181 mem_ctx = talloc_init("validate_ctx");
4187 ret = key_val[i].validate_data_fn(mem_ctx, keystr, dbuf,
4191 talloc_destroy(mem_ctx);
4196 DEBUG(0,("cache_traverse_validate_fn: unknown cache entry\nkey :\n"));
4197 dump_data(0, (uint8_t *)kbuf.dptr, kbuf.dsize);
4198 DEBUG(0,("data :\n"));
4199 dump_data(0, (uint8_t *)dbuf.dptr, dbuf.dsize);
4200 v_state->unknown_key = true;
4201 v_state->success = false;
4202 return 1; /* terminate. */
4205 static void validate_panic(const char *const why)
4207 DEBUG(0,("validating cache: would panic %s\n", why ));
4208 DEBUGADD(0, ("exiting instead (cache validation mode)\n"));
4212 static int wbcache_update_centry_fn(TDB_CONTEXT *tdb,
4220 if (is_non_centry_key(key)) {
4224 if (data.dptr == NULL || data.dsize == 0) {
4225 if (tdb_delete(tdb, key) < 0) {
4226 DEBUG(0, ("tdb_delete for [%s] failed!\n",
4232 /* add timeout to blob (uint64_t) */
4233 blob.dsize = data.dsize + 8;
4235 blob.dptr = SMB_XMALLOC_ARRAY(uint8_t, blob.dsize);
4236 if (blob.dptr == NULL) {
4239 memset(blob.dptr, 0, blob.dsize);
4241 /* copy status and seqnum */
4242 memcpy(blob.dptr, data.dptr, 8);
4245 ctimeout = lp_winbind_cache_time() + time(NULL);
4246 SBVAL(blob.dptr, 8, ctimeout);
4249 memcpy(blob.dptr + 16, data.dptr + 8, data.dsize - 8);
4251 if (tdb_store(tdb, key, blob, TDB_REPLACE) < 0) {
4252 DEBUG(0, ("tdb_store to update [%s] failed!\n",
4254 SAFE_FREE(blob.dptr);
4258 SAFE_FREE(blob.dptr);
4262 static bool wbcache_upgrade_v1_to_v2(TDB_CONTEXT *tdb)
4266 DEBUG(1, ("Upgrade to version 2 of the winbindd_cache.tdb\n"));
4268 rc = tdb_traverse(tdb, wbcache_update_centry_fn, NULL);
4276 /***********************************************************************
4277 Try and validate every entry in the winbindd cache. If we fail here,
4278 delete the cache tdb and return non-zero.
4279 ***********************************************************************/
4281 int winbindd_validate_cache(void)
4284 char *tdb_path = NULL;
4285 TDB_CONTEXT *tdb = NULL;
4289 DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
4290 smb_panic_fn = validate_panic;
4292 tdb_path = wcache_path();
4293 if (tdb_path == NULL) {
4297 tdb = tdb_open_log(tdb_path,
4298 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
4299 TDB_INCOMPATIBLE_HASH |
4300 ( lp_winbind_offline_logon()
4302 : TDB_DEFAULT | TDB_CLEAR_IF_FIRST ),
4306 DEBUG(0, ("winbindd_validate_cache: "
4307 "error opening/initializing tdb\n"));
4311 /* Version check and upgrade code. */
4312 if (!tdb_fetch_uint32(tdb, WINBINDD_CACHE_VERSION_KEYSTR, &vers_id)) {
4313 DEBUG(10, ("Fresh database\n"));
4314 tdb_store_uint32(tdb, WINBINDD_CACHE_VERSION_KEYSTR, WINBINDD_CACHE_VERSION);
4315 vers_id = WINBINDD_CACHE_VERSION;
4318 if (vers_id != WINBINDD_CACHE_VERSION) {
4319 if (vers_id == WINBINDD_CACHE_VER1) {
4320 ok = wbcache_upgrade_v1_to_v2(tdb);
4322 DEBUG(10, ("winbindd_validate_cache: upgrade to version 2 failed.\n"));
4327 tdb_store_uint32(tdb,
4328 WINBINDD_CACHE_VERSION_KEYSTR,
4329 WINBINDD_CACHE_VERSION);
4330 vers_id = WINBINDD_CACHE_VER2;
4336 ret = tdb_validate_and_backup(tdb_path, cache_traverse_validate_fn);
4339 DEBUG(10, ("winbindd_validate_cache: validation not successful.\n"));
4340 DEBUGADD(10, ("removing tdb %s.\n", tdb_path));
4345 TALLOC_FREE(tdb_path);
4346 DEBUG(10, ("winbindd_validate_cache: restoring panic function\n"));
4347 smb_panic_fn = smb_panic;
4351 /***********************************************************************
4352 Try and validate every entry in the winbindd cache.
4353 ***********************************************************************/
4355 int winbindd_validate_cache_nobackup(void)
4360 DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
4361 smb_panic_fn = validate_panic;
4363 tdb_path = wcache_path();
4364 if (tdb_path == NULL) {
4365 goto err_panic_restore;
4368 if (wcache == NULL || wcache->tdb == NULL) {
4369 ret = tdb_validate_open(tdb_path, cache_traverse_validate_fn);
4371 ret = tdb_validate(wcache->tdb, cache_traverse_validate_fn);
4375 DEBUG(10, ("winbindd_validate_cache_nobackup: validation not "
4379 TALLOC_FREE(tdb_path);
4381 DEBUG(10, ("winbindd_validate_cache_nobackup: restoring panic "
4383 smb_panic_fn = smb_panic;
4387 bool winbindd_cache_validate_and_initialize(void)
4389 close_winbindd_cache();
4391 if (lp_winbind_offline_logon()) {
4392 if (winbindd_validate_cache() < 0) {
4393 DEBUG(0, ("winbindd cache tdb corrupt and no backup "
4394 "could be restored.\n"));
4398 return initialize_winbindd_cache();
4401 /*********************************************************************
4402 ********************************************************************/
4404 static bool add_wbdomain_to_tdc_array( struct winbindd_domain *new_dom,
4405 struct winbindd_tdc_domain **domains,
4406 size_t *num_domains )
4408 struct winbindd_tdc_domain *list = NULL;
4411 bool set_only = false;
4413 /* don't allow duplicates */
4418 for ( i=0; i< (*num_domains); i++ ) {
4419 if ( strequal( new_dom->name, list[i].domain_name ) ) {
4420 DEBUG(10,("add_wbdomain_to_tdc_array: Found existing record for %s\n",
4431 list = talloc_array( NULL, struct winbindd_tdc_domain, 1 );
4434 list = talloc_realloc( *domains, *domains,
4435 struct winbindd_tdc_domain,
4440 ZERO_STRUCT( list[idx] );
4446 list[idx].domain_name = talloc_strdup(list, new_dom->name);
4447 if (list[idx].domain_name == NULL) {
4450 if (new_dom->alt_name != NULL) {
4451 list[idx].dns_name = talloc_strdup(list, new_dom->alt_name);
4452 if (list[idx].dns_name == NULL) {
4457 if ( !is_null_sid( &new_dom->sid ) ) {
4458 sid_copy( &list[idx].sid, &new_dom->sid );
4460 sid_copy(&list[idx].sid, &global_sid_NULL);
4463 if ( new_dom->domain_flags != 0x0 )
4464 list[idx].trust_flags = new_dom->domain_flags;
4466 if ( new_dom->domain_type != 0x0 )
4467 list[idx].trust_type = new_dom->domain_type;
4469 if ( new_dom->domain_trust_attribs != 0x0 )
4470 list[idx].trust_attribs = new_dom->domain_trust_attribs;
4474 *num_domains = idx + 1;
4480 /*********************************************************************
4481 ********************************************************************/
4483 static TDB_DATA make_tdc_key( const char *domain_name )
4485 char *keystr = NULL;
4486 TDB_DATA key = { NULL, 0 };
4488 if ( !domain_name ) {
4489 DEBUG(5,("make_tdc_key: Keyname workgroup is NULL!\n"));
4493 if (asprintf( &keystr, "TRUSTDOMCACHE/%s", domain_name ) == -1) {
4496 key = string_term_tdb_data(keystr);
4501 /*********************************************************************
4502 ********************************************************************/
4504 static int pack_tdc_domains( struct winbindd_tdc_domain *domains,
4506 unsigned char **buf )
4508 unsigned char *buffer = NULL;
4513 DEBUG(10,("pack_tdc_domains: Packing %d trusted domains\n",
4521 /* Store the number of array items first */
4522 len += tdb_pack( buffer+len, buflen-len, "d",
4525 /* now pack each domain trust record */
4526 for ( i=0; i<num_domains; i++ ) {
4531 DEBUG(10,("pack_tdc_domains: Packing domain %s (%s)\n",
4532 domains[i].domain_name,
4533 domains[i].dns_name ? domains[i].dns_name : "UNKNOWN" ));
4536 len += tdb_pack( buffer+len, buflen-len, "fffddd",
4537 domains[i].domain_name,
4538 domains[i].dns_name ? domains[i].dns_name : "",
4539 sid_to_fstring(tmp, &domains[i].sid),
4540 domains[i].trust_flags,
4541 domains[i].trust_attribs,
4542 domains[i].trust_type );
4545 if ( buflen < len ) {
4547 if ( (buffer = SMB_MALLOC_ARRAY(unsigned char, len)) == NULL ) {
4548 DEBUG(0,("pack_tdc_domains: failed to alloc buffer!\n"));
4562 /*********************************************************************
4563 ********************************************************************/
4565 static size_t unpack_tdc_domains( unsigned char *buf, int buflen,
4566 struct winbindd_tdc_domain **domains )
4568 fstring domain_name, dns_name, sid_string;
4569 uint32_t type, attribs, flags;
4573 struct winbindd_tdc_domain *list = NULL;
4575 /* get the number of domains */
4576 len += tdb_unpack( buf+len, buflen-len, "d", &num_domains);
4578 DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
4582 list = talloc_array( NULL, struct winbindd_tdc_domain, num_domains );
4584 DEBUG(0,("unpack_tdc_domains: Failed to talloc() domain list!\n"));
4588 for ( i=0; i<num_domains; i++ ) {
4591 this_len = tdb_unpack( buf+len, buflen-len, "fffddd",
4599 if ( this_len == -1 ) {
4600 DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
4601 TALLOC_FREE( list );
4606 DEBUG(11,("unpack_tdc_domains: Unpacking domain %s (%s) "
4607 "SID %s, flags = 0x%x, attribs = 0x%x, type = 0x%x\n",
4608 domain_name, dns_name, sid_string,
4609 flags, attribs, type));
4611 list[i].domain_name = talloc_strdup( list, domain_name );
4612 list[i].dns_name = NULL;
4613 if (dns_name[0] != '\0') {
4614 list[i].dns_name = talloc_strdup(list, dns_name);
4616 if ( !string_to_sid( &(list[i].sid), sid_string ) ) {
4617 DEBUG(10,("unpack_tdc_domains: no SID for domain %s\n",
4620 list[i].trust_flags = flags;
4621 list[i].trust_attribs = attribs;
4622 list[i].trust_type = type;
4630 /*********************************************************************
4631 ********************************************************************/
4633 static bool wcache_tdc_store_list( struct winbindd_tdc_domain *domains, size_t num_domains )
4635 TDB_DATA key = make_tdc_key( lp_workgroup() );
4636 TDB_DATA data = { NULL, 0 };
4642 /* See if we were asked to delete the cache entry */
4645 ret = tdb_delete( wcache->tdb, key );
4649 data.dsize = pack_tdc_domains( domains, num_domains, &data.dptr );
4656 ret = tdb_store( wcache->tdb, key, data, 0 );
4659 SAFE_FREE( data.dptr );
4660 SAFE_FREE( key.dptr );
4662 return ( ret == 0 );
4665 /*********************************************************************
4666 ********************************************************************/
4668 bool wcache_tdc_fetch_list( struct winbindd_tdc_domain **domains, size_t *num_domains )
4670 TDB_DATA key = make_tdc_key( lp_workgroup() );
4671 TDB_DATA data = { NULL, 0 };
4679 data = tdb_fetch( wcache->tdb, key );
4681 SAFE_FREE( key.dptr );
4686 *num_domains = unpack_tdc_domains( data.dptr, data.dsize, domains );
4688 SAFE_FREE( data.dptr );
4696 /*********************************************************************
4697 ********************************************************************/
4699 bool wcache_tdc_add_domain( struct winbindd_domain *domain )
4701 struct winbindd_tdc_domain *dom_list = NULL;
4702 size_t num_domains = 0;
4705 DEBUG(10,("wcache_tdc_add_domain: Adding domain %s (%s), SID %s, "
4706 "flags = 0x%x, attributes = 0x%x, type = 0x%x\n",
4707 domain->name, domain->alt_name,
4708 sid_string_dbg(&domain->sid),
4709 domain->domain_flags,
4710 domain->domain_trust_attribs,
4711 domain->domain_type));
4713 if ( !init_wcache() ) {
4717 /* fetch the list */
4719 wcache_tdc_fetch_list( &dom_list, &num_domains );
4721 /* add the new domain */
4723 if ( !add_wbdomain_to_tdc_array( domain, &dom_list, &num_domains ) ) {
4727 /* pack the domain */
4729 if ( !wcache_tdc_store_list( dom_list, num_domains ) ) {
4737 TALLOC_FREE( dom_list );
4742 static struct winbindd_tdc_domain *wcache_tdc_dup_domain(
4743 TALLOC_CTX *mem_ctx, const struct winbindd_tdc_domain *src)
4745 struct winbindd_tdc_domain *dst;
4747 dst = talloc(mem_ctx, struct winbindd_tdc_domain);
4751 dst->domain_name = talloc_strdup(dst, src->domain_name);
4752 if (dst->domain_name == NULL) {
4756 dst->dns_name = NULL;
4757 if (src->dns_name != NULL) {
4758 dst->dns_name = talloc_strdup(dst, src->dns_name);
4759 if (dst->dns_name == NULL) {
4764 sid_copy(&dst->sid, &src->sid);
4765 dst->trust_flags = src->trust_flags;
4766 dst->trust_type = src->trust_type;
4767 dst->trust_attribs = src->trust_attribs;
4774 /*********************************************************************
4775 ********************************************************************/
4777 struct winbindd_tdc_domain * wcache_tdc_fetch_domain( TALLOC_CTX *ctx, const char *name )
4779 struct winbindd_tdc_domain *dom_list = NULL;
4780 size_t num_domains = 0;
4782 struct winbindd_tdc_domain *d = NULL;
4784 DEBUG(10,("wcache_tdc_fetch_domain: Searching for domain %s\n", name));
4786 if ( !init_wcache() ) {
4790 /* fetch the list */
4792 wcache_tdc_fetch_list( &dom_list, &num_domains );
4794 for ( i=0; i<num_domains; i++ ) {
4795 if ( strequal(name, dom_list[i].domain_name) ||
4796 strequal(name, dom_list[i].dns_name) )
4798 DEBUG(10,("wcache_tdc_fetch_domain: Found domain %s\n",
4801 d = wcache_tdc_dup_domain(ctx, &dom_list[i]);
4806 TALLOC_FREE( dom_list );
4811 /*********************************************************************
4812 ********************************************************************/
4814 struct winbindd_tdc_domain*
4815 wcache_tdc_fetch_domainbysid(TALLOC_CTX *ctx,
4816 const struct dom_sid *sid)
4818 struct winbindd_tdc_domain *dom_list = NULL;
4819 size_t num_domains = 0;
4821 struct winbindd_tdc_domain *d = NULL;
4823 DEBUG(10,("wcache_tdc_fetch_domainbysid: Searching for domain %s\n",
4824 sid_string_dbg(sid)));
4826 if (!init_wcache()) {
4830 /* fetch the list */
4832 wcache_tdc_fetch_list(&dom_list, &num_domains);
4834 for (i = 0; i<num_domains; i++) {
4835 if (dom_sid_equal(sid, &(dom_list[i].sid))) {
4836 DEBUG(10, ("wcache_tdc_fetch_domainbysid: "
4837 "Found domain %s for SID %s\n",
4838 dom_list[i].domain_name,
4839 sid_string_dbg(sid)));
4841 d = wcache_tdc_dup_domain(ctx, &dom_list[i]);
4846 TALLOC_FREE(dom_list);
4852 /*********************************************************************
4853 ********************************************************************/
4855 void wcache_tdc_clear( void )
4857 if ( !init_wcache() )
4860 wcache_tdc_store_list( NULL, 0 );
4866 /*********************************************************************
4867 ********************************************************************/
4869 static void wcache_save_user_pwinfo(struct winbindd_domain *domain,
4871 const struct dom_sid *user_sid,
4872 const char *homedir,
4877 struct cache_entry *centry;
4880 if ( (centry = centry_start(domain, status)) == NULL )
4883 centry_put_string( centry, homedir );
4884 centry_put_string( centry, shell );
4885 centry_put_string( centry, gecos );
4886 centry_put_uint32( centry, gid );
4888 centry_end(centry, "NSS/PWINFO/%s", sid_to_fstring(tmp, user_sid) );
4890 DEBUG(10,("wcache_save_user_pwinfo: %s\n", sid_string_dbg(user_sid) ));
4892 centry_free(centry);
4897 NTSTATUS nss_get_info_cached( struct winbindd_domain *domain,
4898 const struct dom_sid *user_sid,
4900 const char **homedir, const char **shell,
4901 const char **gecos, gid_t *p_gid)
4903 struct winbind_cache *cache = get_cache(domain);
4904 struct cache_entry *centry = NULL;
4911 centry = wcache_fetch(cache, domain, "NSS/PWINFO/%s",
4912 sid_to_fstring(tmp, user_sid));
4917 *homedir = centry_string( centry, ctx );
4918 *shell = centry_string( centry, ctx );
4919 *gecos = centry_string( centry, ctx );
4920 *p_gid = centry_uint32( centry );
4922 centry_free(centry);
4924 DEBUG(10,("nss_get_info_cached: [Cached] - user_sid %s\n",
4925 sid_string_dbg(user_sid)));
4927 return NT_STATUS_OK;
4931 nt_status = nss_get_info( domain->name, user_sid, ctx,
4932 homedir, shell, gecos, p_gid );
4934 DEBUG(10, ("nss_get_info returned %s\n", nt_errstr(nt_status)));
4936 if ( NT_STATUS_IS_OK(nt_status) ) {
4937 DEBUG(10, ("result:\n\thomedir = '%s'\n", *homedir));
4938 DEBUGADD(10, ("\tshell = '%s'\n", *shell));
4939 DEBUGADD(10, ("\tgecos = '%s'\n", *gecos));
4940 DEBUGADD(10, ("\tgid = '%u'\n", (unsigned int)*p_gid));
4942 wcache_save_user_pwinfo( domain, nt_status, user_sid,
4943 *homedir, *shell, *gecos, *p_gid );
4946 if ( NT_STATUS_EQUAL( nt_status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND ) ) {
4947 DEBUG(5,("nss_get_info_cached: Setting domain %s offline\n",
4949 set_domain_offline( domain );
4957 /* the cache backend methods are exposed via this structure */
4958 struct winbindd_methods cache_methods = {
4976 static bool wcache_ndr_key(TALLOC_CTX *mem_ctx, const char *domain_name,
4977 uint32_t opnum, const DATA_BLOB *req,
4983 key = talloc_asprintf(mem_ctx, "NDR/%s/%d/", domain_name, (int)opnum);
4987 keylen = talloc_get_size(key) - 1;
4989 key = talloc_realloc(mem_ctx, key, char, keylen + req->length);
4993 memcpy(key + keylen, req->data, req->length);
4995 pkey->dptr = (uint8_t *)key;
4996 pkey->dsize = talloc_get_size(key);
5000 static bool wcache_opnum_cacheable(uint32_t opnum)
5003 case NDR_WBINT_PING:
5004 case NDR_WBINT_QUERYSEQUENCENUMBER:
5005 case NDR_WBINT_ALLOCATEUID:
5006 case NDR_WBINT_ALLOCATEGID:
5007 case NDR_WBINT_CHECKMACHINEACCOUNT:
5008 case NDR_WBINT_CHANGEMACHINEACCOUNT:
5009 case NDR_WBINT_PINGDC:
5015 bool wcache_fetch_ndr(TALLOC_CTX *mem_ctx, struct winbindd_domain *domain,
5016 uint32_t opnum, const DATA_BLOB *req, DATA_BLOB *resp)
5021 if (!wcache_opnum_cacheable(opnum) ||
5022 is_my_own_sam_domain(domain) ||
5023 is_builtin_domain(domain)) {
5027 if (wcache->tdb == NULL) {
5031 if (!wcache_ndr_key(talloc_tos(), domain->name, opnum, req, &key)) {
5034 data = tdb_fetch(wcache->tdb, key);
5035 TALLOC_FREE(key.dptr);
5037 if (data.dptr == NULL) {
5040 if (data.dsize < 12) {
5044 if (!is_domain_offline(domain)) {
5045 uint32_t entry_seqnum, dom_seqnum, last_check;
5046 uint64_t entry_timeout;
5048 if (!wcache_fetch_seqnum(domain->name, &dom_seqnum,
5052 entry_seqnum = IVAL(data.dptr, 0);
5053 if (entry_seqnum != dom_seqnum) {
5054 DEBUG(10, ("Entry has wrong sequence number: %d\n",
5055 (int)entry_seqnum));
5058 entry_timeout = BVAL(data.dptr, 4);
5059 if (time(NULL) > entry_timeout) {
5060 DEBUG(10, ("Entry has timed out\n"));
5065 resp->data = (uint8_t *)talloc_memdup(mem_ctx, data.dptr + 12,
5067 if (resp->data == NULL) {
5068 DEBUG(10, ("talloc failed\n"));
5071 resp->length = data.dsize - 12;
5075 SAFE_FREE(data.dptr);
5079 void wcache_store_ndr(struct winbindd_domain *domain, uint32_t opnum,
5080 const DATA_BLOB *req, const DATA_BLOB *resp)
5083 uint32_t dom_seqnum, last_check;
5086 if (!wcache_opnum_cacheable(opnum) ||
5087 is_my_own_sam_domain(domain) ||
5088 is_builtin_domain(domain)) {
5092 if (wcache->tdb == NULL) {
5096 if (!wcache_fetch_seqnum(domain->name, &dom_seqnum, &last_check)) {
5097 DEBUG(10, ("could not fetch seqnum for domain %s\n",
5102 if (!wcache_ndr_key(talloc_tos(), domain->name, opnum, req, &key)) {
5106 timeout = time(NULL) + lp_winbind_cache_time();
5108 data.dsize = resp->length + 12;
5109 data.dptr = talloc_array(key.dptr, uint8_t, data.dsize);
5110 if (data.dptr == NULL) {
5114 SIVAL(data.dptr, 0, dom_seqnum);
5115 SBVAL(data.dptr, 4, timeout);
5116 memcpy(data.dptr + 12, resp->data, resp->length);
5118 tdb_store(wcache->tdb, key, data, 0);
5121 TALLOC_FREE(key.dptr);