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"
37 #include "libsmb/samlogon_cache.h"
40 #define DBGC_CLASS DBGC_WINBIND
42 #define WINBINDD_CACHE_VER1 1 /* initial db version */
43 #define WINBINDD_CACHE_VER2 2 /* second version with timeouts for NDR entries */
45 #define WINBINDD_CACHE_VERSION WINBINDD_CACHE_VER2
46 #define WINBINDD_CACHE_VERSION_KEYSTR "WINBINDD_CACHE_VERSION"
48 extern struct winbindd_methods reconnect_methods;
50 extern struct winbindd_methods reconnect_ads_methods;
52 extern struct winbindd_methods builtin_passdb_methods;
53 extern struct winbindd_methods sam_passdb_methods;
56 * JRA. KEEP THIS LIST UP TO DATE IF YOU ADD CACHE ENTRIES.
57 * Here are the list of entry types that are *not* stored
58 * as form struct cache_entry in the cache.
61 static const char *non_centry_keys[] = {
64 WINBINDD_CACHE_VERSION_KEYSTR,
68 /************************************************************************
69 Is this key a non-centry type ?
70 ************************************************************************/
72 static bool is_non_centry_key(TDB_DATA kbuf)
76 if (kbuf.dptr == NULL || kbuf.dsize == 0) {
79 for (i = 0; non_centry_keys[i] != NULL; i++) {
80 size_t namelen = strlen(non_centry_keys[i]);
81 if (kbuf.dsize < namelen) {
84 if (strncmp(non_centry_keys[i], (const char *)kbuf.dptr, namelen) == 0) {
91 /* Global online/offline state - False when online. winbindd starts up online
92 and sets this to true if the first query fails and there's an entry in
93 the cache tdb telling us to stay offline. */
95 static bool global_winbindd_offline_state;
97 struct winbind_cache {
103 uint32_t sequence_number;
109 void (*smb_panic_fn)(const char *const why) = smb_panic;
111 #define WINBINDD_MAX_CACHE_SIZE (50*1024*1024)
113 static struct winbind_cache *wcache;
115 static char *wcache_path(void)
118 * Data needs to be kept persistent in state directory for
119 * running with "winbindd offline logon".
121 return state_path("winbindd_cache.tdb");
124 /* get the winbind_cache structure */
125 static struct winbind_cache *get_cache(struct winbindd_domain *domain)
127 struct winbind_cache *ret = wcache;
129 /* We have to know what type of domain we are dealing with first. */
131 if (domain->internal) {
132 domain->backend = &builtin_passdb_methods;
135 if (dom_sid_equal(&domain->sid, &global_sid_Builtin)) {
136 domain->initialized = true;
139 if (strequal(domain->name, get_global_sam_name()) &&
140 sid_check_is_our_sam(&domain->sid)) {
141 domain->backend = &sam_passdb_methods;
144 if ( !domain->initialized ) {
145 /* We do not need a connection to an RW DC for cache operation */
146 init_dc_connection(domain, false);
150 OK. Listen up because I'm only going to say this once.
151 We have the following scenarios to consider
152 (a) trusted AD domains on a Samba DC,
153 (b) trusted AD domains and we are joined to a non-kerberos domain
154 (c) trusted AD domains and we are joined to a kerberos (AD) domain
156 For (a) we can always contact the trusted domain using krb5
157 since we have the domain trust account password
159 For (b) we can only use RPC since we have no way of
160 getting a krb5 ticket in our own domain
162 For (c) we can always use krb5 since we have a kerberos trust
167 if (!domain->backend) {
169 struct winbindd_domain *our_domain = domain;
171 /* find our domain first so we can figure out if we
172 are joined to a kerberized domain */
174 if ( !domain->primary )
175 our_domain = find_our_domain();
177 if ((our_domain->active_directory || IS_DC)
178 && domain->active_directory
179 && !lp_winbind_rpc_only()) {
180 DEBUG(5,("get_cache: Setting ADS methods for domain %s\n", domain->name));
181 domain->backend = &reconnect_ads_methods;
183 #endif /* HAVE_ADS */
184 DEBUG(5,("get_cache: Setting MS-RPC methods for domain %s\n", domain->name));
185 domain->backend = &reconnect_methods;
188 #endif /* HAVE_ADS */
194 ret = SMB_XMALLOC_P(struct winbind_cache);
198 wcache_flush_cache();
204 free a centry structure
206 static void centry_free(struct cache_entry *centry)
210 SAFE_FREE(centry->data);
214 static bool centry_check_bytes(struct cache_entry *centry, size_t nbytes)
216 if (centry->len - centry->ofs < nbytes) {
217 DEBUG(0,("centry corruption? needed %u bytes, have %d\n",
218 (unsigned int)nbytes,
219 centry->len - centry->ofs));
226 pull a uint64_t from a cache entry
228 static uint64_t centry_uint64_t(struct cache_entry *centry)
232 if (!centry_check_bytes(centry, 8)) {
233 smb_panic_fn("centry_uint64_t");
235 ret = BVAL(centry->data, centry->ofs);
241 pull a uint32_t from a cache entry
243 static uint32_t centry_uint32(struct cache_entry *centry)
247 if (!centry_check_bytes(centry, 4)) {
248 smb_panic_fn("centry_uint32");
250 ret = IVAL(centry->data, centry->ofs);
256 pull a uint16_t from a cache entry
258 static uint16_t centry_uint16(struct cache_entry *centry)
261 if (!centry_check_bytes(centry, 2)) {
262 smb_panic_fn("centry_uint16");
264 ret = SVAL(centry->data, centry->ofs);
270 pull a uint8_t from a cache entry
272 static uint8_t centry_uint8(struct cache_entry *centry)
275 if (!centry_check_bytes(centry, 1)) {
276 smb_panic_fn("centry_uint8");
278 ret = CVAL(centry->data, centry->ofs);
284 pull a NTTIME from a cache entry
286 static NTTIME centry_nttime(struct cache_entry *centry)
289 if (!centry_check_bytes(centry, 8)) {
290 smb_panic_fn("centry_nttime");
292 ret = IVAL(centry->data, centry->ofs);
294 ret += (uint64_t)IVAL(centry->data, centry->ofs) << 32;
300 pull a time_t from a cache entry. time_t stored portably as a 64-bit time.
302 static time_t centry_time(struct cache_entry *centry)
304 return (time_t)centry_nttime(centry);
307 /* pull a string from a cache entry, using the supplied
310 static char *centry_string(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
315 len = centry_uint8(centry);
318 /* a deliberate NULL string */
322 if (!centry_check_bytes(centry, (size_t)len)) {
323 smb_panic_fn("centry_string");
326 ret = talloc_array(mem_ctx, char, len+1);
328 smb_panic_fn("centry_string out of memory\n");
330 memcpy(ret,centry->data + centry->ofs, len);
336 /* pull a hash16 from a cache entry, using the supplied
339 static char *centry_hash16(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
344 len = centry_uint8(centry);
347 DEBUG(0,("centry corruption? hash len (%u) != 16\n",
352 if (!centry_check_bytes(centry, 16)) {
356 ret = talloc_array(mem_ctx, char, 16);
358 smb_panic_fn("centry_hash out of memory\n");
360 memcpy(ret,centry->data + centry->ofs, 16);
365 /* pull a sid from a cache entry, using the supplied
368 static bool centry_sid(struct cache_entry *centry, struct dom_sid *sid)
373 sid_string = centry_string(centry, talloc_tos());
374 if (sid_string == NULL) {
377 ret = string_to_sid(sid, sid_string);
378 TALLOC_FREE(sid_string);
384 pull a NTSTATUS from a cache entry
386 static NTSTATUS centry_ntstatus(struct cache_entry *centry)
390 status = NT_STATUS(centry_uint32(centry));
395 /* the server is considered down if it can't give us a sequence number */
396 static bool wcache_server_down(struct winbindd_domain *domain)
403 ret = (domain->sequence_number == DOM_SEQUENCE_NONE);
406 DEBUG(10,("wcache_server_down: server for Domain %s down\n",
411 struct wcache_seqnum_state {
413 uint32_t *last_seq_check;
416 static int wcache_seqnum_parser(TDB_DATA key, TDB_DATA data,
419 struct wcache_seqnum_state *state = private_data;
421 if (data.dsize != 8) {
422 DEBUG(10, ("wcache_fetch_seqnum: invalid data size %d\n",
427 *state->seqnum = IVAL(data.dptr, 0);
428 *state->last_seq_check = IVAL(data.dptr, 4);
432 static bool wcache_fetch_seqnum(const char *domain_name, uint32_t *seqnum,
433 uint32_t *last_seq_check)
435 struct wcache_seqnum_state state = {
436 .seqnum = seqnum, .last_seq_check = last_seq_check
438 size_t len = strlen(domain_name);
440 TDB_DATA key = { .dptr = (uint8_t *)keystr, .dsize = sizeof(keystr) };
443 if (wcache->tdb == NULL) {
444 DEBUG(10,("wcache_fetch_seqnum: tdb == NULL\n"));
448 snprintf(keystr, sizeof(keystr), "SEQNUM/%s", domain_name);
450 ret = tdb_parse_record(wcache->tdb, key, wcache_seqnum_parser,
455 static NTSTATUS fetch_cache_seqnum( struct winbindd_domain *domain, time_t now )
457 uint32_t last_check, time_diff;
459 if (!wcache_fetch_seqnum(domain->name, &domain->sequence_number,
461 return NT_STATUS_UNSUCCESSFUL;
463 domain->last_seq_check = last_check;
465 /* have we expired? */
467 time_diff = now - domain->last_seq_check;
468 if ( time_diff > lp_winbind_cache_time() ) {
469 DEBUG(10,("fetch_cache_seqnum: timeout [%s][%u @ %u]\n",
470 domain->name, domain->sequence_number,
471 (uint32_t)domain->last_seq_check));
472 return NT_STATUS_UNSUCCESSFUL;
475 DEBUG(10,("fetch_cache_seqnum: success [%s][%u @ %u]\n",
476 domain->name, domain->sequence_number,
477 (uint32_t)domain->last_seq_check));
482 bool wcache_store_seqnum(const char *domain_name, uint32_t seqnum,
483 time_t last_seq_check)
485 size_t len = strlen(domain_name);
487 TDB_DATA key = { .dptr = (uint8_t *)keystr, .dsize = sizeof(keystr) };
491 if (wcache->tdb == NULL) {
492 DEBUG(10, ("wcache_store_seqnum: wcache->tdb == NULL\n"));
496 snprintf(keystr, sizeof(keystr), "SEQNUM/%s", domain_name);
498 SIVAL(buf, 0, seqnum);
499 SIVAL(buf, 4, last_seq_check);
501 ret = tdb_store(wcache->tdb, key, make_tdb_data(buf, sizeof(buf)),
504 DEBUG(10, ("tdb_store_bystring failed: %s\n",
505 tdb_errorstr(wcache->tdb)));
509 DEBUG(10, ("wcache_store_seqnum: success [%s][%u @ %u]\n",
510 domain_name, seqnum, (unsigned)last_seq_check));
515 static bool store_cache_seqnum( struct winbindd_domain *domain )
517 return wcache_store_seqnum(domain->name, domain->sequence_number,
518 domain->last_seq_check);
522 refresh the domain sequence number on timeout.
525 static void refresh_sequence_number(struct winbindd_domain *domain)
529 time_t t = time(NULL);
530 unsigned cache_time = lp_winbind_cache_time();
532 if (is_domain_offline(domain)) {
538 #if 0 /* JERRY -- disable as the default cache time is now 5 minutes */
539 /* trying to reconnect is expensive, don't do it too often */
540 if (domain->sequence_number == DOM_SEQUENCE_NONE) {
545 time_diff = t - domain->last_seq_check;
547 /* see if we have to refetch the domain sequence number */
548 if ((time_diff < cache_time) &&
549 (domain->sequence_number != DOM_SEQUENCE_NONE) &&
550 NT_STATUS_IS_OK(domain->last_status)) {
551 DEBUG(10, ("refresh_sequence_number: %s time ok\n", domain->name));
555 /* try to get the sequence number from the tdb cache first */
556 /* this will update the timestamp as well */
558 status = fetch_cache_seqnum( domain, t );
559 if (NT_STATUS_IS_OK(status) &&
560 (domain->sequence_number != DOM_SEQUENCE_NONE) &&
561 NT_STATUS_IS_OK(domain->last_status)) {
565 /* important! make sure that we know if this is a native
566 mode domain or not. And that we can contact it. */
568 if ( winbindd_can_contact_domain( domain ) ) {
569 status = domain->backend->sequence_number(domain,
570 &domain->sequence_number);
572 /* just use the current time */
573 status = NT_STATUS_OK;
574 domain->sequence_number = time(NULL);
578 /* the above call could have set our domain->backend to NULL when
579 * coming from offline to online mode, make sure to reinitialize the
580 * backend - Guenther */
583 if (!NT_STATUS_IS_OK(status)) {
584 DEBUG(10,("refresh_sequence_number: failed with %s\n", nt_errstr(status)));
585 domain->sequence_number = DOM_SEQUENCE_NONE;
588 domain->last_status = status;
589 domain->last_seq_check = time(NULL);
591 /* save the new sequence number in the cache */
592 store_cache_seqnum( domain );
595 DEBUG(10, ("refresh_sequence_number: %s seq number is now %d\n",
596 domain->name, domain->sequence_number));
602 decide if a cache entry has expired
604 static bool centry_expired(struct winbindd_domain *domain, const char *keystr, struct cache_entry *centry)
606 /* If we've been told to be offline - stay in that state... */
607 if (lp_winbind_offline_logon() && global_winbindd_offline_state) {
608 DEBUG(10,("centry_expired: Key %s for domain %s valid as winbindd is globally offline.\n",
609 keystr, domain->name ));
613 /* when the domain is offline return the cached entry.
614 * This deals with transient offline states... */
616 if (!domain->online) {
617 DEBUG(10,("centry_expired: Key %s for domain %s valid as domain is offline.\n",
618 keystr, domain->name ));
622 /* if the server is OK and our cache entry came from when it was down then
623 the entry is invalid */
624 if ((domain->sequence_number != DOM_SEQUENCE_NONE) &&
625 (centry->sequence_number == DOM_SEQUENCE_NONE)) {
626 DEBUG(10,("centry_expired: Key %s for domain %s invalid sequence.\n",
627 keystr, domain->name ));
631 /* if the server is down or the cache entry is not older than the
632 current sequence number or it did not timeout then it is OK */
633 if (wcache_server_down(domain)
634 || ((centry->sequence_number == domain->sequence_number)
635 && (centry->timeout > time(NULL)))) {
636 DEBUG(10,("centry_expired: Key %s for domain %s is good.\n",
637 keystr, domain->name ));
641 DEBUG(10,("centry_expired: Key %s for domain %s expired\n",
642 keystr, domain->name ));
648 static struct cache_entry *wcache_fetch_raw(char *kstr)
651 struct cache_entry *centry;
654 key = string_tdb_data(kstr);
655 data = tdb_fetch(wcache->tdb, key);
661 centry = SMB_XMALLOC_P(struct cache_entry);
662 centry->data = (unsigned char *)data.dptr;
663 centry->len = data.dsize;
666 if (centry->len < 16) {
667 /* huh? corrupt cache? */
668 DEBUG(10,("wcache_fetch_raw: Corrupt cache for key %s "
669 "(len < 16)?\n", kstr));
674 centry->status = centry_ntstatus(centry);
675 centry->sequence_number = centry_uint32(centry);
676 centry->timeout = centry_uint64_t(centry);
681 static bool is_my_own_sam_domain(struct winbindd_domain *domain)
683 if (strequal(domain->name, get_global_sam_name()) &&
684 sid_check_is_our_sam(&domain->sid)) {
691 static bool is_builtin_domain(struct winbindd_domain *domain)
693 if (strequal(domain->name, "BUILTIN") &&
694 sid_check_is_builtin(&domain->sid)) {
702 fetch an entry from the cache, with a varargs key. auto-fetch the sequence
703 number and return status
705 static struct cache_entry *wcache_fetch(struct winbind_cache *cache,
706 struct winbindd_domain *domain,
707 const char *format, ...) PRINTF_ATTRIBUTE(3,4);
708 static struct cache_entry *wcache_fetch(struct winbind_cache *cache,
709 struct winbindd_domain *domain,
710 const char *format, ...)
714 struct cache_entry *centry;
716 if (!winbindd_use_cache() ||
717 is_my_own_sam_domain(domain) ||
718 is_builtin_domain(domain)) {
722 refresh_sequence_number(domain);
724 va_start(ap, format);
725 smb_xvasprintf(&kstr, format, ap);
728 centry = wcache_fetch_raw(kstr);
729 if (centry == NULL) {
734 if (centry_expired(domain, kstr, centry)) {
736 DEBUG(10,("wcache_fetch: entry %s expired for domain %s\n",
737 kstr, domain->name ));
744 DEBUG(10,("wcache_fetch: returning entry %s for domain %s\n",
745 kstr, domain->name ));
751 static void wcache_delete(const char *format, ...) PRINTF_ATTRIBUTE(1,2);
752 static void wcache_delete(const char *format, ...)
758 va_start(ap, format);
759 smb_xvasprintf(&kstr, format, ap);
762 key = string_tdb_data(kstr);
764 tdb_delete(wcache->tdb, key);
769 make sure we have at least len bytes available in a centry
771 static void centry_expand(struct cache_entry *centry, uint32_t len)
773 if (centry->len - centry->ofs >= len)
776 centry->data = SMB_REALLOC_ARRAY(centry->data, unsigned char,
779 DEBUG(0,("out of memory: needed %d bytes in centry_expand\n", centry->len));
780 smb_panic_fn("out of memory in centry_expand");
785 push a uint64_t into a centry
787 static void centry_put_uint64_t(struct cache_entry *centry, uint64_t v)
789 centry_expand(centry, 8);
790 SBVAL(centry->data, centry->ofs, v);
795 push a uint32_t into a centry
797 static void centry_put_uint32(struct cache_entry *centry, uint32_t v)
799 centry_expand(centry, 4);
800 SIVAL(centry->data, centry->ofs, v);
805 push a uint16_t into a centry
807 static void centry_put_uint16(struct cache_entry *centry, uint16_t v)
809 centry_expand(centry, 2);
810 SSVAL(centry->data, centry->ofs, v);
815 push a uint8_t into a centry
817 static void centry_put_uint8(struct cache_entry *centry, uint8_t v)
819 centry_expand(centry, 1);
820 SCVAL(centry->data, centry->ofs, v);
825 push a string into a centry
827 static void centry_put_string(struct cache_entry *centry, const char *s)
832 /* null strings are marked as len 0xFFFF */
833 centry_put_uint8(centry, 0xFF);
838 /* can't handle more than 254 char strings. Truncating is probably best */
840 DEBUG(10,("centry_put_string: truncating len (%d) to: 254\n", len));
843 centry_put_uint8(centry, len);
844 centry_expand(centry, len);
845 memcpy(centry->data + centry->ofs, s, len);
850 push a 16 byte hash into a centry - treat as 16 byte string.
852 static void centry_put_hash16(struct cache_entry *centry, const uint8_t val[16])
854 centry_put_uint8(centry, 16);
855 centry_expand(centry, 16);
856 memcpy(centry->data + centry->ofs, val, 16);
860 static void centry_put_sid(struct cache_entry *centry, const struct dom_sid *sid)
863 centry_put_string(centry, sid_to_fstring(sid_string, sid));
868 put NTSTATUS into a centry
870 static void centry_put_ntstatus(struct cache_entry *centry, NTSTATUS status)
872 uint32_t status_value = NT_STATUS_V(status);
873 centry_put_uint32(centry, status_value);
878 push a NTTIME into a centry
880 static void centry_put_nttime(struct cache_entry *centry, NTTIME nt)
882 centry_expand(centry, 8);
883 SIVAL(centry->data, centry->ofs, nt & 0xFFFFFFFF);
885 SIVAL(centry->data, centry->ofs, nt >> 32);
890 push a time_t into a centry - use a 64 bit size.
891 NTTIME here is being used as a convenient 64-bit size.
893 static void centry_put_time(struct cache_entry *centry, time_t t)
895 NTTIME nt = (NTTIME)t;
896 centry_put_nttime(centry, nt);
900 start a centry for output. When finished, call centry_end()
902 static struct cache_entry *centry_start(struct winbindd_domain *domain,
905 struct cache_entry *centry;
910 centry = SMB_XMALLOC_P(struct cache_entry);
912 centry->len = 8192; /* reasonable default */
913 centry->data = SMB_XMALLOC_ARRAY(uint8_t, centry->len);
915 centry->sequence_number = domain->sequence_number;
916 centry->timeout = lp_winbind_cache_time() + time(NULL);
917 centry_put_ntstatus(centry, status);
918 centry_put_uint32(centry, centry->sequence_number);
919 centry_put_uint64_t(centry, centry->timeout);
924 finish a centry and write it to the tdb
926 static void centry_end(struct cache_entry *centry, const char *format, ...) PRINTF_ATTRIBUTE(2,3);
927 static void centry_end(struct cache_entry *centry, const char *format, ...)
933 if (!winbindd_use_cache()) {
937 va_start(ap, format);
938 smb_xvasprintf(&kstr, format, ap);
941 key = string_tdb_data(kstr);
942 data.dptr = centry->data;
943 data.dsize = centry->ofs;
945 tdb_store(wcache->tdb, key, data, TDB_REPLACE);
949 static void wcache_save_name_to_sid(struct winbindd_domain *domain,
950 NTSTATUS status, const char *domain_name,
951 const char *name, const struct dom_sid *sid,
952 enum lsa_SidType type)
954 struct cache_entry *centry;
957 centry = centry_start(domain, status);
961 if ((domain_name == NULL) || (domain_name[0] == '\0')) {
962 struct winbindd_domain *mydomain =
963 find_domain_from_sid_noinit(sid);
964 if (mydomain != NULL) {
965 domain_name = mydomain->name;
969 centry_put_uint32(centry, type);
970 centry_put_sid(centry, sid);
971 fstrcpy(uname, name);
972 (void)strupper_m(uname);
973 centry_end(centry, "NS/%s/%s", domain_name, uname);
974 DEBUG(10,("wcache_save_name_to_sid: %s\\%s -> %s (%s)\n", domain_name,
975 uname, sid_string_dbg(sid), nt_errstr(status)));
979 static void wcache_save_sid_to_name(struct winbindd_domain *domain, NTSTATUS status,
980 const struct dom_sid *sid, const char *domain_name, const char *name, enum lsa_SidType type)
982 struct cache_entry *centry;
985 centry = centry_start(domain, status);
989 if ((domain_name == NULL) || (domain_name[0] == '\0')) {
990 struct winbindd_domain *mydomain =
991 find_domain_from_sid_noinit(sid);
992 if (mydomain != NULL) {
993 domain_name = mydomain->name;
997 if (NT_STATUS_IS_OK(status)) {
998 centry_put_uint32(centry, type);
999 centry_put_string(centry, domain_name);
1000 centry_put_string(centry, name);
1003 centry_end(centry, "SN/%s", sid_to_fstring(sid_string, sid));
1004 DEBUG(10,("wcache_save_sid_to_name: %s -> %s\\%s (%s)\n", sid_string,
1005 domain_name, name, nt_errstr(status)));
1006 centry_free(centry);
1010 static void wcache_save_user(struct winbindd_domain *domain, NTSTATUS status,
1011 struct wbint_userinfo *info)
1013 struct cache_entry *centry;
1016 if (is_null_sid(&info->user_sid)) {
1020 centry = centry_start(domain, status);
1023 centry_put_string(centry, info->domain_name);
1024 centry_put_string(centry, info->acct_name);
1025 centry_put_string(centry, info->full_name);
1026 centry_put_string(centry, info->homedir);
1027 centry_put_string(centry, info->shell);
1028 centry_put_uint32(centry, info->uid);
1029 centry_put_uint32(centry, info->primary_gid);
1030 centry_put_string(centry, info->primary_group_name);
1031 centry_put_sid(centry, &info->user_sid);
1032 centry_put_sid(centry, &info->group_sid);
1033 centry_end(centry, "U/%s", sid_to_fstring(sid_string,
1035 DEBUG(10,("wcache_save_user: %s (acct_name %s)\n", sid_string, info->acct_name));
1036 centry_free(centry);
1039 static void wcache_save_lockout_policy(struct winbindd_domain *domain,
1041 struct samr_DomInfo12 *lockout_policy)
1043 struct cache_entry *centry;
1045 centry = centry_start(domain, status);
1049 centry_put_nttime(centry, lockout_policy->lockout_duration);
1050 centry_put_nttime(centry, lockout_policy->lockout_window);
1051 centry_put_uint16(centry, lockout_policy->lockout_threshold);
1053 centry_end(centry, "LOC_POL/%s", domain->name);
1055 DEBUG(10,("wcache_save_lockout_policy: %s\n", domain->name));
1057 centry_free(centry);
1062 static void wcache_save_password_policy(struct winbindd_domain *domain,
1064 struct samr_DomInfo1 *policy)
1066 struct cache_entry *centry;
1068 centry = centry_start(domain, status);
1072 centry_put_uint16(centry, policy->min_password_length);
1073 centry_put_uint16(centry, policy->password_history_length);
1074 centry_put_uint32(centry, policy->password_properties);
1075 centry_put_nttime(centry, policy->max_password_age);
1076 centry_put_nttime(centry, policy->min_password_age);
1078 centry_end(centry, "PWD_POL/%s", domain->name);
1080 DEBUG(10,("wcache_save_password_policy: %s\n", domain->name));
1082 centry_free(centry);
1085 /***************************************************************************
1086 ***************************************************************************/
1088 static void wcache_save_username_alias(struct winbindd_domain *domain,
1090 const char *name, const char *alias)
1092 struct cache_entry *centry;
1095 if ( (centry = centry_start(domain, status)) == NULL )
1098 centry_put_string( centry, alias );
1100 fstrcpy(uname, name);
1101 (void)strupper_m(uname);
1102 centry_end(centry, "NSS/NA/%s", uname);
1104 DEBUG(10,("wcache_save_username_alias: %s -> %s\n", name, alias ));
1106 centry_free(centry);
1109 static void wcache_save_alias_username(struct winbindd_domain *domain,
1111 const char *alias, const char *name)
1113 struct cache_entry *centry;
1116 if ( (centry = centry_start(domain, status)) == NULL )
1119 centry_put_string( centry, name );
1121 fstrcpy(uname, alias);
1122 (void)strupper_m(uname);
1123 centry_end(centry, "NSS/AN/%s", uname);
1125 DEBUG(10,("wcache_save_alias_username: %s -> %s\n", alias, name ));
1127 centry_free(centry);
1130 /***************************************************************************
1131 ***************************************************************************/
1133 NTSTATUS resolve_username_to_alias( TALLOC_CTX *mem_ctx,
1134 struct winbindd_domain *domain,
1135 const char *name, char **alias )
1137 struct winbind_cache *cache = get_cache(domain);
1138 struct cache_entry *centry = NULL;
1142 if ( domain->internal )
1143 return NT_STATUS_NOT_SUPPORTED;
1148 upper_name = talloc_strdup(mem_ctx, name);
1149 if (upper_name == NULL) {
1150 return NT_STATUS_NO_MEMORY;
1152 if (!strupper_m(upper_name)) {
1153 talloc_free(upper_name);
1154 return NT_STATUS_INVALID_PARAMETER;
1157 centry = wcache_fetch(cache, domain, "NSS/NA/%s", upper_name);
1159 talloc_free(upper_name);
1164 status = centry->status;
1166 if (!NT_STATUS_IS_OK(status)) {
1167 centry_free(centry);
1171 *alias = centry_string( centry, mem_ctx );
1173 centry_free(centry);
1175 DEBUG(10,("resolve_username_to_alias: [Cached] - mapped %s to %s\n",
1176 name, *alias ? *alias : "(none)"));
1178 return (*alias) ? NT_STATUS_OK : NT_STATUS_OBJECT_NAME_NOT_FOUND;
1182 /* If its not in cache and we are offline, then fail */
1184 if ( get_global_winbindd_state_offline() || !domain->online ) {
1185 DEBUG(8,("resolve_username_to_alias: rejecting query "
1186 "in offline mode\n"));
1187 return NT_STATUS_NOT_FOUND;
1190 status = nss_map_to_alias( mem_ctx, domain->name, name, alias );
1192 if ( NT_STATUS_IS_OK( status ) ) {
1193 wcache_save_username_alias(domain, status, name, *alias);
1196 if ( NT_STATUS_EQUAL( status, NT_STATUS_NONE_MAPPED ) ) {
1197 wcache_save_username_alias(domain, status, name, "(NULL)");
1200 DEBUG(5,("resolve_username_to_alias: backend query returned %s\n",
1201 nt_errstr(status)));
1203 if ( NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ) {
1204 set_domain_offline( domain );
1210 /***************************************************************************
1211 ***************************************************************************/
1213 NTSTATUS resolve_alias_to_username( TALLOC_CTX *mem_ctx,
1214 struct winbindd_domain *domain,
1215 const char *alias, char **name )
1217 struct winbind_cache *cache = get_cache(domain);
1218 struct cache_entry *centry = NULL;
1222 if ( domain->internal )
1223 return NT_STATUS_NOT_SUPPORTED;
1228 upper_name = talloc_strdup(mem_ctx, alias);
1229 if (upper_name == NULL) {
1230 return NT_STATUS_NO_MEMORY;
1232 if (!strupper_m(upper_name)) {
1233 talloc_free(upper_name);
1234 return NT_STATUS_INVALID_PARAMETER;
1237 centry = wcache_fetch(cache, domain, "NSS/AN/%s", upper_name);
1239 talloc_free(upper_name);
1244 status = centry->status;
1246 if (!NT_STATUS_IS_OK(status)) {
1247 centry_free(centry);
1251 *name = centry_string( centry, mem_ctx );
1253 centry_free(centry);
1255 DEBUG(10,("resolve_alias_to_username: [Cached] - mapped %s to %s\n",
1256 alias, *name ? *name : "(none)"));
1258 return (*name) ? NT_STATUS_OK : NT_STATUS_OBJECT_NAME_NOT_FOUND;
1262 /* If its not in cache and we are offline, then fail */
1264 if ( get_global_winbindd_state_offline() || !domain->online ) {
1265 DEBUG(8,("resolve_alias_to_username: rejecting query "
1266 "in offline mode\n"));
1267 return NT_STATUS_NOT_FOUND;
1270 /* an alias cannot contain a domain prefix or '@' */
1272 if (strchr(alias, '\\') || strchr(alias, '@')) {
1273 DEBUG(10,("resolve_alias_to_username: skipping fully "
1274 "qualified name %s\n", alias));
1275 return NT_STATUS_OBJECT_NAME_INVALID;
1278 status = nss_map_from_alias( mem_ctx, domain->name, alias, name );
1280 if ( NT_STATUS_IS_OK( status ) ) {
1281 wcache_save_alias_username( domain, status, alias, *name );
1284 if (NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) {
1285 wcache_save_alias_username(domain, status, alias, "(NULL)");
1288 DEBUG(5,("resolve_alias_to_username: backend query returned %s\n",
1289 nt_errstr(status)));
1291 if ( NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ) {
1292 set_domain_offline( domain );
1298 NTSTATUS wcache_cached_creds_exist(struct winbindd_domain *domain, const struct dom_sid *sid)
1300 struct winbind_cache *cache = get_cache(domain);
1302 fstring key_str, tmp;
1306 return NT_STATUS_INTERNAL_DB_ERROR;
1309 if (is_null_sid(sid)) {
1310 return NT_STATUS_INVALID_SID;
1313 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1314 return NT_STATUS_INVALID_SID;
1317 fstr_sprintf(key_str, "CRED/%s", sid_to_fstring(tmp, sid));
1319 data = tdb_fetch(cache->tdb, string_tdb_data(key_str));
1321 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1324 SAFE_FREE(data.dptr);
1325 return NT_STATUS_OK;
1328 /* Lookup creds for a SID - copes with old (unsalted) creds as well
1329 as new salted ones. */
1331 NTSTATUS wcache_get_creds(struct winbindd_domain *domain,
1332 TALLOC_CTX *mem_ctx,
1333 const struct dom_sid *sid,
1334 const uint8_t **cached_nt_pass,
1335 const uint8_t **cached_salt)
1337 struct winbind_cache *cache = get_cache(domain);
1338 struct cache_entry *centry = NULL;
1344 return NT_STATUS_INTERNAL_DB_ERROR;
1347 if (is_null_sid(sid)) {
1348 return NT_STATUS_INVALID_SID;
1351 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1352 return NT_STATUS_INVALID_SID;
1355 /* Try and get a salted cred first. If we can't
1356 fall back to an unsalted cred. */
1358 centry = wcache_fetch(cache, domain, "CRED/%s",
1359 sid_to_fstring(tmp, sid));
1361 DEBUG(10,("wcache_get_creds: entry for [CRED/%s] not found\n",
1362 sid_string_dbg(sid)));
1363 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1367 * We don't use the time element at this moment,
1368 * but we have to consume it, so that we don't
1369 * neet to change the disk format of the cache.
1371 (void)centry_time(centry);
1373 /* In the salted case this isn't actually the nt_hash itself,
1374 but the MD5 of the salt + nt_hash. Let the caller
1375 sort this out. It can tell as we only return the cached_salt
1376 if we are returning a salted cred. */
1378 *cached_nt_pass = (const uint8_t *)centry_hash16(centry, mem_ctx);
1379 if (*cached_nt_pass == NULL) {
1382 sid_to_fstring(sidstr, sid);
1384 /* Bad (old) cred cache. Delete and pretend we
1386 DEBUG(0,("wcache_get_creds: bad entry for [CRED/%s] - deleting\n",
1388 wcache_delete("CRED/%s", sidstr);
1389 centry_free(centry);
1390 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1393 /* We only have 17 bytes more data in the salted cred case. */
1394 if (centry->len - centry->ofs == 17) {
1395 *cached_salt = (const uint8_t *)centry_hash16(centry, mem_ctx);
1397 *cached_salt = NULL;
1400 dump_data_pw("cached_nt_pass", *cached_nt_pass, NT_HASH_LEN);
1402 dump_data_pw("cached_salt", *cached_salt, NT_HASH_LEN);
1405 status = centry->status;
1407 DEBUG(10,("wcache_get_creds: [Cached] - cached creds for user %s status: %s\n",
1408 sid_string_dbg(sid), nt_errstr(status) ));
1410 centry_free(centry);
1414 /* Store creds for a SID - only writes out new salted ones. */
1416 NTSTATUS wcache_save_creds(struct winbindd_domain *domain,
1417 const struct dom_sid *sid,
1418 const uint8_t nt_pass[NT_HASH_LEN])
1420 struct cache_entry *centry;
1423 uint8_t cred_salt[NT_HASH_LEN];
1424 uint8_t salted_hash[NT_HASH_LEN];
1426 if (is_null_sid(sid)) {
1427 return NT_STATUS_INVALID_SID;
1430 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1431 return NT_STATUS_INVALID_SID;
1434 centry = centry_start(domain, NT_STATUS_OK);
1436 return NT_STATUS_INTERNAL_DB_ERROR;
1439 dump_data_pw("nt_pass", nt_pass, NT_HASH_LEN);
1441 centry_put_time(centry, time(NULL));
1443 /* Create a salt and then salt the hash. */
1444 generate_random_buffer(cred_salt, NT_HASH_LEN);
1445 E_md5hash(cred_salt, nt_pass, salted_hash);
1447 centry_put_hash16(centry, salted_hash);
1448 centry_put_hash16(centry, cred_salt);
1449 centry_end(centry, "CRED/%s", sid_to_fstring(sid_string, sid));
1451 DEBUG(10,("wcache_save_creds: %s\n", sid_string));
1453 centry_free(centry);
1455 return NT_STATUS_OK;
1459 /* Query display info. This is the basic user list fn */
1460 NTSTATUS wb_cache_query_user_list(struct winbindd_domain *domain,
1461 TALLOC_CTX *mem_ctx,
1462 uint32_t *num_entries,
1463 struct wbint_userinfo **info)
1465 struct winbind_cache *cache = get_cache(domain);
1466 struct cache_entry *centry = NULL;
1468 unsigned int i, retry;
1469 bool old_status = domain->online;
1474 centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
1479 *num_entries = centry_uint32(centry);
1481 if (*num_entries == 0)
1484 (*info) = talloc_array(mem_ctx, struct wbint_userinfo, *num_entries);
1486 smb_panic_fn("query_user_list out of memory");
1488 for (i=0; i<(*num_entries); i++) {
1489 (*info)[i].domain_name = centry_string(centry, mem_ctx);
1490 (*info)[i].acct_name = centry_string(centry, mem_ctx);
1491 (*info)[i].full_name = centry_string(centry, mem_ctx);
1492 (*info)[i].homedir = centry_string(centry, mem_ctx);
1493 (*info)[i].shell = centry_string(centry, mem_ctx);
1494 (*info)[i].uid = centry_uint32(centry);
1495 (*info)[i].primary_gid = centry_uint32(centry);
1496 (*info)[i].primary_group_name = centry_string(centry, mem_ctx);
1497 centry_sid(centry, &(*info)[i].user_sid);
1498 centry_sid(centry, &(*info)[i].group_sid);
1502 status = centry->status;
1504 DEBUG(10,("query_user_list: [Cached] - cached list for domain %s status: %s\n",
1505 domain->name, nt_errstr(status) ));
1507 centry_free(centry);
1514 /* Return status value returned by seq number check */
1516 if (!NT_STATUS_IS_OK(domain->last_status))
1517 return domain->last_status;
1519 /* Put the query_user_list() in a retry loop. There appears to be
1520 * some bug either with Windows 2000 or Samba's handling of large
1521 * rpc replies. This manifests itself as sudden disconnection
1522 * at a random point in the enumeration of a large (60k) user list.
1523 * The retry loop simply tries the operation again. )-: It's not
1524 * pretty but an acceptable workaround until we work out what the
1525 * real problem is. */
1530 DEBUG(10,("query_user_list: [Cached] - doing backend query for list for domain %s\n",
1533 status = domain->backend->query_user_list(domain, mem_ctx, num_entries, info);
1534 if (!NT_STATUS_IS_OK(status)) {
1535 DEBUG(3, ("query_user_list: returned 0x%08x, "
1536 "retrying\n", NT_STATUS_V(status)));
1538 if (NT_STATUS_EQUAL(status, NT_STATUS_UNSUCCESSFUL)) {
1539 DEBUG(3, ("query_user_list: flushing "
1540 "connection cache\n"));
1541 invalidate_cm_connection(domain);
1543 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1544 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1545 if (!domain->internal && old_status) {
1546 set_domain_offline(domain);
1548 /* store partial response. */
1549 if (*num_entries > 0) {
1551 * humm, what about the status used for cache?
1552 * Should it be NT_STATUS_OK?
1557 * domain is offline now, and there is no user entries,
1558 * try to fetch from cache again.
1560 if (cache->tdb && !domain->online && !domain->internal && old_status) {
1561 centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
1562 /* partial response... */
1566 goto do_fetch_cache;
1573 } while (NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_UNSUCCESSFUL) &&
1577 refresh_sequence_number(domain);
1578 if (!NT_STATUS_IS_OK(status)) {
1581 centry = centry_start(domain, status);
1584 centry_put_uint32(centry, *num_entries);
1585 for (i=0; i<(*num_entries); i++) {
1586 centry_put_string(centry, (*info)[i].domain_name);
1587 centry_put_string(centry, (*info)[i].acct_name);
1588 centry_put_string(centry, (*info)[i].full_name);
1589 centry_put_string(centry, (*info)[i].homedir);
1590 centry_put_string(centry, (*info)[i].shell);
1591 centry_put_uint32(centry, (*info)[i].uid);
1592 centry_put_uint32(centry, (*info)[i].primary_gid);
1593 centry_put_string(centry, (*info)[i].primary_group_name);
1594 centry_put_sid(centry, &(*info)[i].user_sid);
1595 centry_put_sid(centry, &(*info)[i].group_sid);
1596 if (domain->backend && domain->backend->consistent) {
1597 /* when the backend is consistent we can pre-prime some mappings */
1598 wcache_save_name_to_sid(domain, NT_STATUS_OK,
1600 (*info)[i].acct_name,
1601 &(*info)[i].user_sid,
1603 wcache_save_sid_to_name(domain, NT_STATUS_OK,
1604 &(*info)[i].user_sid,
1606 (*info)[i].acct_name,
1608 wcache_save_user(domain, NT_STATUS_OK, &(*info)[i]);
1611 centry_end(centry, "UL/%s", domain->name);
1612 centry_free(centry);
1618 /* list all domain groups */
1619 NTSTATUS wb_cache_enum_dom_groups(struct winbindd_domain *domain,
1620 TALLOC_CTX *mem_ctx,
1621 uint32_t *num_entries,
1622 struct wb_acct_info **info)
1624 struct winbind_cache *cache = get_cache(domain);
1625 struct cache_entry *centry = NULL;
1630 old_status = domain->online;
1634 centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
1639 *num_entries = centry_uint32(centry);
1641 if (*num_entries == 0)
1644 (*info) = talloc_array(mem_ctx, struct wb_acct_info, *num_entries);
1646 smb_panic_fn("enum_dom_groups out of memory");
1648 for (i=0; i<(*num_entries); i++) {
1649 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1650 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1651 (*info)[i].rid = centry_uint32(centry);
1655 status = centry->status;
1657 DEBUG(10,("enum_dom_groups: [Cached] - cached list for domain %s status: %s\n",
1658 domain->name, nt_errstr(status) ));
1660 centry_free(centry);
1667 /* Return status value returned by seq number check */
1669 if (!NT_STATUS_IS_OK(domain->last_status))
1670 return domain->last_status;
1672 DEBUG(10,("enum_dom_groups: [Cached] - doing backend query for list for domain %s\n",
1675 status = domain->backend->enum_dom_groups(domain, mem_ctx, num_entries, info);
1677 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1678 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1679 if (!domain->internal && old_status) {
1680 set_domain_offline(domain);
1684 !domain->internal &&
1686 centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
1688 goto do_fetch_cache;
1693 refresh_sequence_number(domain);
1694 if (!NT_STATUS_IS_OK(status)) {
1697 centry = centry_start(domain, status);
1700 centry_put_uint32(centry, *num_entries);
1701 for (i=0; i<(*num_entries); i++) {
1702 centry_put_string(centry, (*info)[i].acct_name);
1703 centry_put_string(centry, (*info)[i].acct_desc);
1704 centry_put_uint32(centry, (*info)[i].rid);
1706 centry_end(centry, "GL/%s/domain", domain->name);
1707 centry_free(centry);
1713 /* list all domain groups */
1714 NTSTATUS wb_cache_enum_local_groups(struct winbindd_domain *domain,
1715 TALLOC_CTX *mem_ctx,
1716 uint32_t *num_entries,
1717 struct wb_acct_info **info)
1719 struct winbind_cache *cache = get_cache(domain);
1720 struct cache_entry *centry = NULL;
1725 old_status = domain->online;
1729 centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
1734 *num_entries = centry_uint32(centry);
1736 if (*num_entries == 0)
1739 (*info) = talloc_array(mem_ctx, struct wb_acct_info, *num_entries);
1741 smb_panic_fn("enum_dom_groups out of memory");
1743 for (i=0; i<(*num_entries); i++) {
1744 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1745 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1746 (*info)[i].rid = centry_uint32(centry);
1751 /* If we are returning cached data and the domain controller
1752 is down then we don't know whether the data is up to date
1753 or not. Return NT_STATUS_MORE_PROCESSING_REQUIRED to
1756 if (wcache_server_down(domain)) {
1757 DEBUG(10, ("enum_local_groups: returning cached user list and server was down\n"));
1758 status = NT_STATUS_MORE_PROCESSING_REQUIRED;
1760 status = centry->status;
1762 DEBUG(10,("enum_local_groups: [Cached] - cached list for domain %s status: %s\n",
1763 domain->name, nt_errstr(status) ));
1765 centry_free(centry);
1772 /* Return status value returned by seq number check */
1774 if (!NT_STATUS_IS_OK(domain->last_status))
1775 return domain->last_status;
1777 DEBUG(10,("enum_local_groups: [Cached] - doing backend query for list for domain %s\n",
1780 status = domain->backend->enum_local_groups(domain, mem_ctx, num_entries, info);
1782 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1783 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1784 if (!domain->internal && old_status) {
1785 set_domain_offline(domain);
1788 !domain->internal &&
1791 centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
1793 goto do_fetch_cache;
1798 refresh_sequence_number(domain);
1799 if (!NT_STATUS_IS_OK(status)) {
1802 centry = centry_start(domain, status);
1805 centry_put_uint32(centry, *num_entries);
1806 for (i=0; i<(*num_entries); i++) {
1807 centry_put_string(centry, (*info)[i].acct_name);
1808 centry_put_string(centry, (*info)[i].acct_desc);
1809 centry_put_uint32(centry, (*info)[i].rid);
1811 centry_end(centry, "GL/%s/local", domain->name);
1812 centry_free(centry);
1818 NTSTATUS wcache_name_to_sid(struct winbindd_domain *domain,
1819 const char *domain_name,
1821 struct dom_sid *sid,
1822 enum lsa_SidType *type)
1824 struct winbind_cache *cache = get_cache(domain);
1825 struct cache_entry *centry;
1829 if (cache->tdb == NULL) {
1830 return NT_STATUS_NOT_FOUND;
1833 uname = talloc_strdup_upper(talloc_tos(), name);
1834 if (uname == NULL) {
1835 return NT_STATUS_NO_MEMORY;
1838 if ((domain_name == NULL) || (domain_name[0] == '\0')) {
1839 domain_name = domain->name;
1842 centry = wcache_fetch(cache, domain, "NS/%s/%s", domain_name, uname);
1844 if (centry == NULL) {
1845 return NT_STATUS_NOT_FOUND;
1848 status = centry->status;
1849 if (NT_STATUS_IS_OK(status)) {
1850 *type = (enum lsa_SidType)centry_uint32(centry);
1851 centry_sid(centry, sid);
1854 DEBUG(10,("name_to_sid: [Cached] - cached name for domain %s status: "
1855 "%s\n", domain->name, nt_errstr(status) ));
1857 centry_free(centry);
1861 /* convert a single name to a sid in a domain */
1862 NTSTATUS wb_cache_name_to_sid(struct winbindd_domain *domain,
1863 TALLOC_CTX *mem_ctx,
1864 const char *domain_name,
1867 struct dom_sid *sid,
1868 enum lsa_SidType *type)
1873 old_status = domain->online;
1875 status = wcache_name_to_sid(domain, domain_name, name, sid, type);
1876 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
1882 /* If the seq number check indicated that there is a problem
1883 * with this DC, then return that status... except for
1884 * access_denied. This is special because the dc may be in
1885 * "restrict anonymous = 1" mode, in which case it will deny
1886 * most unauthenticated operations, but *will* allow the LSA
1887 * name-to-sid that we try as a fallback. */
1889 if (!(NT_STATUS_IS_OK(domain->last_status)
1890 || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1891 return domain->last_status;
1893 DEBUG(10,("name_to_sid: [Cached] - doing backend query for name for domain %s\n",
1896 status = domain->backend->name_to_sid(domain, mem_ctx, domain_name,
1897 name, flags, sid, type);
1899 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1900 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1901 if (!domain->internal && old_status) {
1902 set_domain_offline(domain);
1904 if (!domain->internal &&
1907 NTSTATUS cache_status;
1908 cache_status = wcache_name_to_sid(domain, domain_name, name, sid, type);
1909 return cache_status;
1913 refresh_sequence_number(domain);
1915 if (domain->online &&
1916 (NT_STATUS_IS_OK(status) || NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED))) {
1917 wcache_save_name_to_sid(domain, status, domain_name, name, sid, *type);
1919 /* Only save the reverse mapping if this was not a UPN */
1920 if (!strchr(name, '@')) {
1921 if (!strupper_m(discard_const_p(char, domain_name))) {
1922 return NT_STATUS_INVALID_PARAMETER;
1924 (void)strlower_m(discard_const_p(char, name));
1925 wcache_save_sid_to_name(domain, status, sid, domain_name, name, *type);
1932 static NTSTATUS wcache_sid_to_name(struct winbindd_domain *domain,
1933 const struct dom_sid *sid,
1934 TALLOC_CTX *mem_ctx,
1937 enum lsa_SidType *type)
1939 struct winbind_cache *cache = get_cache(domain);
1940 struct cache_entry *centry;
1944 if (cache->tdb == NULL) {
1945 return NT_STATUS_NOT_FOUND;
1948 sid_string = sid_string_tos(sid);
1949 if (sid_string == NULL) {
1950 return NT_STATUS_NO_MEMORY;
1953 centry = wcache_fetch(cache, domain, "SN/%s", sid_string);
1954 TALLOC_FREE(sid_string);
1955 if (centry == NULL) {
1956 return NT_STATUS_NOT_FOUND;
1959 if (NT_STATUS_IS_OK(centry->status)) {
1960 *type = (enum lsa_SidType)centry_uint32(centry);
1961 *domain_name = centry_string(centry, mem_ctx);
1962 *name = centry_string(centry, mem_ctx);
1965 status = centry->status;
1966 centry_free(centry);
1968 DEBUG(10,("sid_to_name: [Cached] - cached name for domain %s status: "
1969 "%s\n", domain->name, nt_errstr(status) ));
1974 /* convert a sid to a user or group name. The sid is guaranteed to be in the domain
1976 NTSTATUS wb_cache_sid_to_name(struct winbindd_domain *domain,
1977 TALLOC_CTX *mem_ctx,
1978 const struct dom_sid *sid,
1981 enum lsa_SidType *type)
1986 old_status = domain->online;
1987 status = wcache_sid_to_name(domain, sid, mem_ctx, domain_name, name,
1989 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
1994 *domain_name = NULL;
1996 /* If the seq number check indicated that there is a problem
1997 * with this DC, then return that status... except for
1998 * access_denied. This is special because the dc may be in
1999 * "restrict anonymous = 1" mode, in which case it will deny
2000 * most unauthenticated operations, but *will* allow the LSA
2001 * sid-to-name that we try as a fallback. */
2003 if (!(NT_STATUS_IS_OK(domain->last_status)
2004 || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
2005 return domain->last_status;
2007 DEBUG(10,("sid_to_name: [Cached] - doing backend query for name for domain %s\n",
2010 status = domain->backend->sid_to_name(domain, mem_ctx, sid, domain_name, name, type);
2012 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2013 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2014 if (!domain->internal && old_status) {
2015 set_domain_offline(domain);
2017 if (!domain->internal &&
2020 NTSTATUS cache_status;
2021 cache_status = wcache_sid_to_name(domain, sid, mem_ctx,
2022 domain_name, name, type);
2023 return cache_status;
2027 refresh_sequence_number(domain);
2028 if (!NT_STATUS_IS_OK(status)) {
2031 wcache_save_sid_to_name(domain, status, sid, *domain_name, *name, *type);
2033 /* We can't save the name to sid mapping here, as with sid history a
2034 * later name2sid would give the wrong sid. */
2039 NTSTATUS wb_cache_rids_to_names(struct winbindd_domain *domain,
2040 TALLOC_CTX *mem_ctx,
2041 const struct dom_sid *domain_sid,
2046 enum lsa_SidType **types)
2048 struct winbind_cache *cache = get_cache(domain);
2050 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
2055 old_status = domain->online;
2056 *domain_name = NULL;
2064 if (num_rids == 0) {
2065 return NT_STATUS_OK;
2068 *names = talloc_array(mem_ctx, char *, num_rids);
2069 *types = talloc_array(mem_ctx, enum lsa_SidType, num_rids);
2071 if ((*names == NULL) || (*types == NULL)) {
2072 result = NT_STATUS_NO_MEMORY;
2076 have_mapped = have_unmapped = false;
2078 for (i=0; i<num_rids; i++) {
2080 struct cache_entry *centry;
2083 if (!sid_compose(&sid, domain_sid, rids[i])) {
2084 result = NT_STATUS_INTERNAL_ERROR;
2088 centry = wcache_fetch(cache, domain, "SN/%s",
2089 sid_to_fstring(tmp, &sid));
2094 (*types)[i] = SID_NAME_UNKNOWN;
2095 (*names)[i] = talloc_strdup(*names, "");
2097 if (NT_STATUS_IS_OK(centry->status)) {
2100 (*types)[i] = (enum lsa_SidType)centry_uint32(centry);
2102 dom = centry_string(centry, mem_ctx);
2103 if (*domain_name == NULL) {
2109 (*names)[i] = centry_string(centry, *names);
2111 } else if (NT_STATUS_EQUAL(centry->status, NT_STATUS_NONE_MAPPED)
2112 || NT_STATUS_EQUAL(centry->status, STATUS_SOME_UNMAPPED)) {
2113 have_unmapped = true;
2116 /* something's definitely wrong */
2117 result = centry->status;
2118 centry_free(centry);
2122 centry_free(centry);
2126 return NT_STATUS_NONE_MAPPED;
2128 if (!have_unmapped) {
2129 return NT_STATUS_OK;
2131 return STATUS_SOME_UNMAPPED;
2135 TALLOC_FREE(*names);
2136 TALLOC_FREE(*types);
2138 result = domain->backend->rids_to_names(domain, mem_ctx, domain_sid,
2139 rids, num_rids, domain_name,
2142 if (NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
2143 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2144 if (!domain->internal && old_status) {
2145 set_domain_offline(domain);
2148 !domain->internal &&
2151 have_mapped = have_unmapped = false;
2153 *names = talloc_array(mem_ctx, char *, num_rids);
2154 if (*names == NULL) {
2155 result = NT_STATUS_NO_MEMORY;
2159 *types = talloc_array(mem_ctx, enum lsa_SidType,
2161 if (*types == NULL) {
2162 result = NT_STATUS_NO_MEMORY;
2166 for (i=0; i<num_rids; i++) {
2168 struct cache_entry *centry;
2171 if (!sid_compose(&sid, domain_sid, rids[i])) {
2172 result = NT_STATUS_INTERNAL_ERROR;
2176 centry = wcache_fetch(cache, domain, "SN/%s",
2177 sid_to_fstring(tmp, &sid));
2179 (*types)[i] = SID_NAME_UNKNOWN;
2180 (*names)[i] = talloc_strdup(*names, "");
2184 (*types)[i] = SID_NAME_UNKNOWN;
2185 (*names)[i] = talloc_strdup(*names, "");
2187 if (NT_STATUS_IS_OK(centry->status)) {
2190 (*types)[i] = (enum lsa_SidType)centry_uint32(centry);
2192 dom = centry_string(centry, mem_ctx);
2193 if (*domain_name == NULL) {
2199 (*names)[i] = centry_string(centry, *names);
2201 } else if (NT_STATUS_EQUAL(centry->status, NT_STATUS_NONE_MAPPED)) {
2202 have_unmapped = true;
2205 /* something's definitely wrong */
2206 result = centry->status;
2207 centry_free(centry);
2211 centry_free(centry);
2215 return NT_STATUS_NONE_MAPPED;
2217 if (!have_unmapped) {
2218 return NT_STATUS_OK;
2220 return STATUS_SOME_UNMAPPED;
2224 None of the queried rids has been found so save all negative entries
2226 if (NT_STATUS_EQUAL(result, NT_STATUS_NONE_MAPPED)) {
2227 for (i = 0; i < num_rids; i++) {
2229 const char *name = "";
2230 const enum lsa_SidType type = SID_NAME_UNKNOWN;
2231 NTSTATUS status = NT_STATUS_NONE_MAPPED;
2233 if (!sid_compose(&sid, domain_sid, rids[i])) {
2234 return NT_STATUS_INTERNAL_ERROR;
2237 wcache_save_sid_to_name(domain, status, &sid, *domain_name,
2245 Some or all of the queried rids have been found.
2247 if (!NT_STATUS_IS_OK(result) &&
2248 !NT_STATUS_EQUAL(result, STATUS_SOME_UNMAPPED)) {
2252 refresh_sequence_number(domain);
2254 for (i=0; i<num_rids; i++) {
2258 if (!sid_compose(&sid, domain_sid, rids[i])) {
2259 result = NT_STATUS_INTERNAL_ERROR;
2263 status = (*types)[i] == SID_NAME_UNKNOWN ?
2264 NT_STATUS_NONE_MAPPED : NT_STATUS_OK;
2266 wcache_save_sid_to_name(domain, status, &sid, *domain_name,
2267 (*names)[i], (*types)[i]);
2273 TALLOC_FREE(*names);
2274 TALLOC_FREE(*types);
2278 NTSTATUS wcache_query_user(struct winbindd_domain *domain,
2279 TALLOC_CTX *mem_ctx,
2280 const struct dom_sid *user_sid,
2281 struct wbint_userinfo *info)
2283 struct winbind_cache *cache = get_cache(domain);
2284 struct cache_entry *centry = NULL;
2288 if (cache->tdb == NULL) {
2289 return NT_STATUS_NOT_FOUND;
2292 sid_string = sid_string_tos(user_sid);
2293 if (sid_string == NULL) {
2294 return NT_STATUS_NO_MEMORY;
2297 centry = wcache_fetch(cache, domain, "U/%s", sid_string);
2298 TALLOC_FREE(sid_string);
2299 if (centry == NULL) {
2300 return NT_STATUS_NOT_FOUND;
2304 * If we have an access denied cache entry and a cached info3
2305 * in the samlogon cache then do a query. This will force the
2306 * rpc back end to return the info3 data.
2309 if (NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED) &&
2310 netsamlogon_cache_have(user_sid)) {
2311 DEBUG(10, ("query_user: cached access denied and have cached "
2313 domain->last_status = NT_STATUS_OK;
2314 centry_free(centry);
2315 return NT_STATUS_NOT_FOUND;
2318 /* if status is not ok then this is a negative hit
2319 and the rest of the data doesn't matter */
2320 status = centry->status;
2321 if (NT_STATUS_IS_OK(status)) {
2322 info->domain_name = centry_string(centry, mem_ctx);
2323 info->acct_name = centry_string(centry, mem_ctx);
2324 info->full_name = centry_string(centry, mem_ctx);
2325 info->homedir = centry_string(centry, mem_ctx);
2326 info->shell = centry_string(centry, mem_ctx);
2327 info->uid = centry_uint32(centry);
2328 info->primary_gid = centry_uint32(centry);
2329 info->primary_group_name = centry_string(centry, mem_ctx);
2330 centry_sid(centry, &info->user_sid);
2331 centry_sid(centry, &info->group_sid);
2334 DEBUG(10,("query_user: [Cached] - cached info for domain %s status: "
2335 "%s\n", domain->name, nt_errstr(status) ));
2337 centry_free(centry);
2343 * @brief Query a fullname from the username cache (for further gecos processing)
2345 * @param domain A pointer to the winbindd_domain struct.
2346 * @param mem_ctx The talloc context.
2347 * @param user_sid The user sid.
2348 * @param full_name A pointer to the full_name string.
2350 * @return NTSTATUS code
2352 NTSTATUS wcache_query_user_fullname(struct winbindd_domain *domain,
2353 TALLOC_CTX *mem_ctx,
2354 const struct dom_sid *user_sid,
2355 const char **full_name)
2358 struct wbint_userinfo info;
2360 status = wcache_query_user(domain, mem_ctx, user_sid, &info);
2361 if (!NT_STATUS_IS_OK(status)) {
2365 if (info.full_name != NULL) {
2366 *full_name = talloc_strdup(mem_ctx, info.full_name);
2367 if (*full_name == NULL) {
2368 return NT_STATUS_NO_MEMORY;
2372 return NT_STATUS_OK;
2375 /* Lookup user information from a rid */
2376 NTSTATUS wb_cache_query_user(struct winbindd_domain *domain,
2377 TALLOC_CTX *mem_ctx,
2378 const struct dom_sid *user_sid,
2379 struct wbint_userinfo *info)
2384 old_status = domain->online;
2385 status = wcache_query_user(domain, mem_ctx, user_sid, info);
2386 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2392 /* Return status value returned by seq number check */
2394 if (!NT_STATUS_IS_OK(domain->last_status))
2395 return domain->last_status;
2397 DEBUG(10,("query_user: [Cached] - doing backend query for info for domain %s\n",
2400 status = domain->backend->query_user(domain, mem_ctx, user_sid, info);
2402 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2403 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2404 if (!domain->internal && old_status) {
2405 set_domain_offline(domain);
2407 if (!domain->internal &&
2410 NTSTATUS cache_status;
2411 cache_status = wcache_query_user(domain, mem_ctx, user_sid, info);
2412 return cache_status;
2416 refresh_sequence_number(domain);
2417 if (!NT_STATUS_IS_OK(status)) {
2420 wcache_save_user(domain, status, info);
2425 NTSTATUS wcache_lookup_usergroups(struct winbindd_domain *domain,
2426 TALLOC_CTX *mem_ctx,
2427 const struct dom_sid *user_sid,
2428 uint32_t *pnum_sids,
2429 struct dom_sid **psids)
2431 struct winbind_cache *cache = get_cache(domain);
2432 struct cache_entry *centry = NULL;
2434 uint32_t i, num_sids;
2435 struct dom_sid *sids;
2438 if (cache->tdb == NULL) {
2439 return NT_STATUS_NOT_FOUND;
2442 centry = wcache_fetch(cache, domain, "UG/%s",
2443 sid_to_fstring(sid_string, user_sid));
2444 if (centry == NULL) {
2445 return NT_STATUS_NOT_FOUND;
2448 /* If we have an access denied cache entry and a cached info3 in the
2449 samlogon cache then do a query. This will force the rpc back end
2450 to return the info3 data. */
2452 if (NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)
2453 && netsamlogon_cache_have(user_sid)) {
2454 DEBUG(10, ("lookup_usergroups: cached access denied and have "
2456 domain->last_status = NT_STATUS_OK;
2457 centry_free(centry);
2458 return NT_STATUS_NOT_FOUND;
2461 num_sids = centry_uint32(centry);
2462 sids = talloc_array(mem_ctx, struct dom_sid, num_sids);
2464 centry_free(centry);
2465 return NT_STATUS_NO_MEMORY;
2468 for (i=0; i<num_sids; i++) {
2469 centry_sid(centry, &sids[i]);
2472 status = centry->status;
2474 DEBUG(10,("lookup_usergroups: [Cached] - cached info for domain %s "
2475 "status: %s\n", domain->name, nt_errstr(status)));
2477 centry_free(centry);
2479 *pnum_sids = num_sids;
2484 /* Lookup groups a user is a member of. */
2485 NTSTATUS wb_cache_lookup_usergroups(struct winbindd_domain *domain,
2486 TALLOC_CTX *mem_ctx,
2487 const struct dom_sid *user_sid,
2488 uint32_t *num_groups,
2489 struct dom_sid **user_gids)
2491 struct cache_entry *centry = NULL;
2497 old_status = domain->online;
2498 status = wcache_lookup_usergroups(domain, mem_ctx, user_sid,
2499 num_groups, user_gids);
2500 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2505 (*user_gids) = NULL;
2507 /* Return status value returned by seq number check */
2509 if (!NT_STATUS_IS_OK(domain->last_status))
2510 return domain->last_status;
2512 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info for domain %s\n",
2515 status = domain->backend->lookup_usergroups(domain, mem_ctx, user_sid, num_groups, user_gids);
2517 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2518 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2519 if (!domain->internal && old_status) {
2520 set_domain_offline(domain);
2522 if (!domain->internal &&
2525 NTSTATUS cache_status;
2526 cache_status = wcache_lookup_usergroups(domain, mem_ctx, user_sid,
2527 num_groups, user_gids);
2528 return cache_status;
2531 if ( NT_STATUS_EQUAL(status, NT_STATUS_SYNCHRONIZATION_REQUIRED) )
2535 refresh_sequence_number(domain);
2536 if (!NT_STATUS_IS_OK(status)) {
2539 centry = centry_start(domain, status);
2543 centry_put_uint32(centry, *num_groups);
2544 for (i=0; i<(*num_groups); i++) {
2545 centry_put_sid(centry, &(*user_gids)[i]);
2548 centry_end(centry, "UG/%s", sid_to_fstring(sid_string, user_sid));
2549 centry_free(centry);
2555 static char *wcache_make_sidlist(TALLOC_CTX *mem_ctx, uint32_t num_sids,
2556 const struct dom_sid *sids)
2561 sidlist = talloc_strdup(mem_ctx, "");
2562 if (sidlist == NULL) {
2565 for (i=0; i<num_sids; i++) {
2567 sidlist = talloc_asprintf_append_buffer(
2568 sidlist, "/%s", sid_to_fstring(tmp, &sids[i]));
2569 if (sidlist == NULL) {
2576 NTSTATUS wcache_lookup_useraliases(struct winbindd_domain *domain,
2577 TALLOC_CTX *mem_ctx, uint32_t num_sids,
2578 const struct dom_sid *sids,
2579 uint32_t *pnum_aliases, uint32_t **paliases)
2581 struct winbind_cache *cache = get_cache(domain);
2582 struct cache_entry *centry = NULL;
2583 uint32_t num_aliases;
2589 if (cache->tdb == NULL) {
2590 return NT_STATUS_NOT_FOUND;
2593 if (num_sids == 0) {
2596 return NT_STATUS_OK;
2599 /* We need to cache indexed by the whole list of SIDs, the aliases
2600 * resulting might come from any of the SIDs. */
2602 sidlist = wcache_make_sidlist(talloc_tos(), num_sids, sids);
2603 if (sidlist == NULL) {
2604 return NT_STATUS_NO_MEMORY;
2607 centry = wcache_fetch(cache, domain, "UA%s", sidlist);
2608 TALLOC_FREE(sidlist);
2609 if (centry == NULL) {
2610 return NT_STATUS_NOT_FOUND;
2613 num_aliases = centry_uint32(centry);
2614 aliases = talloc_array(mem_ctx, uint32_t, num_aliases);
2615 if (aliases == NULL) {
2616 centry_free(centry);
2617 return NT_STATUS_NO_MEMORY;
2620 for (i=0; i<num_aliases; i++) {
2621 aliases[i] = centry_uint32(centry);
2624 status = centry->status;
2626 DEBUG(10,("lookup_useraliases: [Cached] - cached info for domain: %s "
2627 "status %s\n", domain->name, nt_errstr(status)));
2629 centry_free(centry);
2631 *pnum_aliases = num_aliases;
2632 *paliases = aliases;
2637 NTSTATUS wb_cache_lookup_useraliases(struct winbindd_domain *domain,
2638 TALLOC_CTX *mem_ctx,
2640 const struct dom_sid *sids,
2641 uint32_t *num_aliases,
2642 uint32_t **alias_rids)
2644 struct cache_entry *centry = NULL;
2650 old_status = domain->online;
2651 status = wcache_lookup_useraliases(domain, mem_ctx, num_sids, sids,
2652 num_aliases, alias_rids);
2653 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2658 (*alias_rids) = NULL;
2660 if (!NT_STATUS_IS_OK(domain->last_status))
2661 return domain->last_status;
2663 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info "
2664 "for domain %s\n", domain->name ));
2666 sidlist = wcache_make_sidlist(talloc_tos(), num_sids, sids);
2667 if (sidlist == NULL) {
2668 return NT_STATUS_NO_MEMORY;
2671 status = domain->backend->lookup_useraliases(domain, mem_ctx,
2673 num_aliases, alias_rids);
2675 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2676 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2677 if (!domain->internal && old_status) {
2678 set_domain_offline(domain);
2680 if (!domain->internal &&
2683 NTSTATUS cache_status;
2684 cache_status = wcache_lookup_useraliases(domain, mem_ctx, num_sids,
2685 sids, num_aliases, alias_rids);
2686 return cache_status;
2690 refresh_sequence_number(domain);
2691 if (!NT_STATUS_IS_OK(status)) {
2694 centry = centry_start(domain, status);
2697 centry_put_uint32(centry, *num_aliases);
2698 for (i=0; i<(*num_aliases); i++)
2699 centry_put_uint32(centry, (*alias_rids)[i]);
2700 centry_end(centry, "UA%s", sidlist);
2701 centry_free(centry);
2707 NTSTATUS wcache_lookup_groupmem(struct winbindd_domain *domain,
2708 TALLOC_CTX *mem_ctx,
2709 const struct dom_sid *group_sid,
2710 uint32_t *num_names,
2711 struct dom_sid **sid_mem, char ***names,
2712 uint32_t **name_types)
2714 struct winbind_cache *cache = get_cache(domain);
2715 struct cache_entry *centry = NULL;
2720 if (cache->tdb == NULL) {
2721 return NT_STATUS_NOT_FOUND;
2724 sid_string = sid_string_tos(group_sid);
2725 if (sid_string == NULL) {
2726 return NT_STATUS_NO_MEMORY;
2729 centry = wcache_fetch(cache, domain, "GM/%s", sid_string);
2730 TALLOC_FREE(sid_string);
2731 if (centry == NULL) {
2732 return NT_STATUS_NOT_FOUND;
2739 *num_names = centry_uint32(centry);
2740 if (*num_names == 0) {
2741 centry_free(centry);
2742 return NT_STATUS_OK;
2745 *sid_mem = talloc_array(mem_ctx, struct dom_sid, *num_names);
2746 *names = talloc_array(mem_ctx, char *, *num_names);
2747 *name_types = talloc_array(mem_ctx, uint32_t, *num_names);
2749 if ((*sid_mem == NULL) || (*names == NULL) || (*name_types == NULL)) {
2750 TALLOC_FREE(*sid_mem);
2751 TALLOC_FREE(*names);
2752 TALLOC_FREE(*name_types);
2753 centry_free(centry);
2754 return NT_STATUS_NO_MEMORY;
2757 for (i=0; i<(*num_names); i++) {
2758 centry_sid(centry, &(*sid_mem)[i]);
2759 (*names)[i] = centry_string(centry, mem_ctx);
2760 (*name_types)[i] = centry_uint32(centry);
2763 status = centry->status;
2765 DEBUG(10,("lookup_groupmem: [Cached] - cached info for domain %s "
2766 "status: %s\n", domain->name, nt_errstr(status)));
2768 centry_free(centry);
2772 NTSTATUS wb_cache_lookup_groupmem(struct winbindd_domain *domain,
2773 TALLOC_CTX *mem_ctx,
2774 const struct dom_sid *group_sid,
2775 enum lsa_SidType type,
2776 uint32_t *num_names,
2777 struct dom_sid **sid_mem,
2779 uint32_t **name_types)
2781 struct cache_entry *centry = NULL;
2787 old_status = domain->online;
2788 status = wcache_lookup_groupmem(domain, mem_ctx, group_sid, num_names,
2789 sid_mem, names, name_types);
2790 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2797 (*name_types) = NULL;
2799 /* Return status value returned by seq number check */
2801 if (!NT_STATUS_IS_OK(domain->last_status))
2802 return domain->last_status;
2804 DEBUG(10,("lookup_groupmem: [Cached] - doing backend query for info for domain %s\n",
2807 status = domain->backend->lookup_groupmem(domain, mem_ctx, group_sid,
2809 sid_mem, names, name_types);
2811 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2812 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2813 if (!domain->internal && old_status) {
2814 set_domain_offline(domain);
2816 if (!domain->internal &&
2819 NTSTATUS cache_status;
2820 cache_status = wcache_lookup_groupmem(domain, mem_ctx, group_sid,
2821 num_names, sid_mem, names,
2823 return cache_status;
2827 refresh_sequence_number(domain);
2828 if (!NT_STATUS_IS_OK(status)) {
2831 centry = centry_start(domain, status);
2834 centry_put_uint32(centry, *num_names);
2835 for (i=0; i<(*num_names); i++) {
2836 centry_put_sid(centry, &(*sid_mem)[i]);
2837 centry_put_string(centry, (*names)[i]);
2838 centry_put_uint32(centry, (*name_types)[i]);
2840 centry_end(centry, "GM/%s", sid_to_fstring(sid_string, group_sid));
2841 centry_free(centry);
2847 /* find the sequence number for a domain */
2848 NTSTATUS wb_cache_sequence_number(struct winbindd_domain *domain,
2851 refresh_sequence_number(domain);
2853 *seq = domain->sequence_number;
2855 return NT_STATUS_OK;
2858 /* enumerate trusted domains
2859 * (we need to have the list of trustdoms in the cache when we go offline) -
2861 NTSTATUS wb_cache_trusted_domains(struct winbindd_domain *domain,
2862 TALLOC_CTX *mem_ctx,
2863 struct netr_DomainTrustList *trusts)
2866 struct winbind_cache *cache;
2867 struct winbindd_tdc_domain *dom_list = NULL;
2868 size_t num_domains = 0;
2869 bool retval = false;
2873 old_status = domain->online;
2875 trusts->array = NULL;
2877 cache = get_cache(domain);
2878 if (!cache || !cache->tdb) {
2882 if (domain->online) {
2886 retval = wcache_tdc_fetch_list(&dom_list, &num_domains);
2887 if (!retval || !num_domains || !dom_list) {
2888 TALLOC_FREE(dom_list);
2893 trusts->array = talloc_zero_array(mem_ctx, struct netr_DomainTrust, num_domains);
2894 if (!trusts->array) {
2895 TALLOC_FREE(dom_list);
2896 return NT_STATUS_NO_MEMORY;
2899 for (i = 0; i < num_domains; i++) {
2900 struct netr_DomainTrust *trust;
2901 struct dom_sid *sid;
2902 struct winbindd_domain *dom;
2904 dom = find_domain_from_name_noinit(dom_list[i].domain_name);
2905 if (dom && dom->internal) {
2909 trust = &trusts->array[trusts->count];
2910 trust->netbios_name = talloc_strdup(trusts->array, dom_list[i].domain_name);
2911 trust->dns_name = talloc_strdup(trusts->array, dom_list[i].dns_name);
2912 sid = talloc(trusts->array, struct dom_sid);
2913 if (!trust->netbios_name || !trust->dns_name ||
2915 TALLOC_FREE(dom_list);
2916 TALLOC_FREE(trusts->array);
2917 return NT_STATUS_NO_MEMORY;
2920 trust->trust_flags = dom_list[i].trust_flags;
2921 trust->trust_attributes = dom_list[i].trust_attribs;
2922 trust->trust_type = dom_list[i].trust_type;
2923 sid_copy(sid, &dom_list[i].sid);
2928 TALLOC_FREE(dom_list);
2929 return NT_STATUS_OK;
2932 /* Return status value returned by seq number check */
2934 if (!NT_STATUS_IS_OK(domain->last_status))
2935 return domain->last_status;
2937 DEBUG(10,("trusted_domains: [Cached] - doing backend query for info for domain %s\n",
2940 status = domain->backend->trusted_domains(domain, mem_ctx, trusts);
2942 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2943 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2944 if (!domain->internal && old_status) {
2945 set_domain_offline(domain);
2947 if (!domain->internal &&
2950 retval = wcache_tdc_fetch_list(&dom_list, &num_domains);
2951 if (retval && num_domains && dom_list) {
2952 TALLOC_FREE(trusts->array);
2954 goto do_fetch_cache;
2958 /* no trusts gives NT_STATUS_NO_MORE_ENTRIES resetting to NT_STATUS_OK
2959 * so that the generic centry handling still applies correctly -
2962 if (!NT_STATUS_IS_ERR(status)) {
2963 status = NT_STATUS_OK;
2968 /* get lockout policy */
2969 NTSTATUS wb_cache_lockout_policy(struct winbindd_domain *domain,
2970 TALLOC_CTX *mem_ctx,
2971 struct samr_DomInfo12 *policy)
2973 struct winbind_cache *cache = get_cache(domain);
2974 struct cache_entry *centry = NULL;
2978 old_status = domain->online;
2982 centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
2988 policy->lockout_duration = centry_nttime(centry);
2989 policy->lockout_window = centry_nttime(centry);
2990 policy->lockout_threshold = centry_uint16(centry);
2992 status = centry->status;
2994 DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2995 domain->name, nt_errstr(status) ));
2997 centry_free(centry);
3001 ZERO_STRUCTP(policy);
3003 /* Return status value returned by seq number check */
3005 if (!NT_STATUS_IS_OK(domain->last_status))
3006 return domain->last_status;
3008 DEBUG(10,("lockout_policy: [Cached] - doing backend query for info for domain %s\n",
3011 status = domain->backend->lockout_policy(domain, mem_ctx, policy);
3013 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
3014 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
3015 if (!domain->internal && old_status) {
3016 set_domain_offline(domain);
3019 !domain->internal &&
3022 centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
3024 goto do_fetch_cache;
3029 refresh_sequence_number(domain);
3030 if (!NT_STATUS_IS_OK(status)) {
3033 wcache_save_lockout_policy(domain, status, policy);
3038 /* get password policy */
3039 NTSTATUS wb_cache_password_policy(struct winbindd_domain *domain,
3040 TALLOC_CTX *mem_ctx,
3041 struct samr_DomInfo1 *policy)
3043 struct winbind_cache *cache = get_cache(domain);
3044 struct cache_entry *centry = NULL;
3048 old_status = domain->online;
3052 centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
3058 policy->min_password_length = centry_uint16(centry);
3059 policy->password_history_length = centry_uint16(centry);
3060 policy->password_properties = centry_uint32(centry);
3061 policy->max_password_age = centry_nttime(centry);
3062 policy->min_password_age = centry_nttime(centry);
3064 status = centry->status;
3066 DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
3067 domain->name, nt_errstr(status) ));
3069 centry_free(centry);
3073 ZERO_STRUCTP(policy);
3075 /* Return status value returned by seq number check */
3077 if (!NT_STATUS_IS_OK(domain->last_status))
3078 return domain->last_status;
3080 DEBUG(10,("password_policy: [Cached] - doing backend query for info for domain %s\n",
3083 status = domain->backend->password_policy(domain, mem_ctx, policy);
3085 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
3086 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
3087 if (!domain->internal && old_status) {
3088 set_domain_offline(domain);
3091 !domain->internal &&
3094 centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
3096 goto do_fetch_cache;
3101 refresh_sequence_number(domain);
3102 if (!NT_STATUS_IS_OK(status)) {
3105 wcache_save_password_policy(domain, status, policy);
3111 /* Invalidate cached user and group lists coherently */
3113 static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
3116 if (strncmp((const char *)kbuf.dptr, "UL/", 3) == 0 ||
3117 strncmp((const char *)kbuf.dptr, "GL/", 3) == 0)
3118 tdb_delete(the_tdb, kbuf);
3123 /* Invalidate the getpwnam and getgroups entries for a winbindd domain */
3125 void wcache_invalidate_samlogon(struct winbindd_domain *domain,
3126 const struct dom_sid *sid)
3128 fstring key_str, sid_string;
3129 struct winbind_cache *cache;
3131 /* don't clear cached U/SID and UG/SID entries when we want to logon
3134 if (lp_winbind_offline_logon()) {
3141 cache = get_cache(domain);
3147 /* Clear U/SID cache entry */
3148 fstr_sprintf(key_str, "U/%s", sid_to_fstring(sid_string, sid));
3149 DEBUG(10, ("wcache_invalidate_samlogon: clearing %s\n", key_str));
3150 tdb_delete(cache->tdb, string_tdb_data(key_str));
3152 /* Clear UG/SID cache entry */
3153 fstr_sprintf(key_str, "UG/%s", sid_to_fstring(sid_string, sid));
3154 DEBUG(10, ("wcache_invalidate_samlogon: clearing %s\n", key_str));
3155 tdb_delete(cache->tdb, string_tdb_data(key_str));
3157 /* Samba/winbindd never needs this. */
3158 netsamlogon_clear_cached_user(sid);
3161 bool wcache_invalidate_cache(void)
3163 struct winbindd_domain *domain;
3165 for (domain = domain_list(); domain; domain = domain->next) {
3166 struct winbind_cache *cache = get_cache(domain);
3168 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
3169 "entries for %s\n", domain->name));
3172 tdb_traverse(cache->tdb, traverse_fn, NULL);
3181 bool wcache_invalidate_cache_noinit(void)
3183 struct winbindd_domain *domain;
3185 for (domain = domain_list(); domain; domain = domain->next) {
3186 struct winbind_cache *cache;
3188 /* Skip uninitialized domains. */
3189 if (!domain->initialized && !domain->internal) {
3193 cache = get_cache(domain);
3195 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
3196 "entries for %s\n", domain->name));
3199 tdb_traverse(cache->tdb, traverse_fn, NULL);
3201 * Flushing cache has nothing to with domains.
3202 * return here if we successfully flushed once.
3203 * To avoid unnecessary traversing the cache.
3214 bool init_wcache(void)
3218 if (wcache == NULL) {
3219 wcache = SMB_XMALLOC_P(struct winbind_cache);
3220 ZERO_STRUCTP(wcache);
3223 if (wcache->tdb != NULL)
3226 db_path = wcache_path();
3227 if (db_path == NULL) {
3231 /* when working offline we must not clear the cache on restart */
3232 wcache->tdb = tdb_open_log(db_path,
3233 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
3234 TDB_INCOMPATIBLE_HASH |
3235 (lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST)),
3236 O_RDWR|O_CREAT, 0600);
3237 TALLOC_FREE(db_path);
3238 if (wcache->tdb == NULL) {
3239 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
3246 /************************************************************************
3247 This is called by the parent to initialize the cache file.
3248 We don't need sophisticated locking here as we know we're the
3250 ************************************************************************/
3252 bool initialize_winbindd_cache(void)
3254 bool cache_bad = true;
3257 if (!init_wcache()) {
3258 DEBUG(0,("initialize_winbindd_cache: init_wcache failed.\n"));
3262 /* Check version number. */
3263 if (tdb_fetch_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, &vers) &&
3264 vers == WINBINDD_CACHE_VERSION) {
3271 DEBUG(0,("initialize_winbindd_cache: clearing cache "
3272 "and re-creating with version number %d\n",
3273 WINBINDD_CACHE_VERSION ));
3275 tdb_close(wcache->tdb);
3278 db_path = wcache_path();
3279 if (db_path == NULL) {
3283 if (unlink(db_path) == -1) {
3284 DEBUG(0,("initialize_winbindd_cache: unlink %s failed %s ",
3287 TALLOC_FREE(db_path);
3290 TALLOC_FREE(db_path);
3291 if (!init_wcache()) {
3292 DEBUG(0,("initialize_winbindd_cache: re-initialization "
3293 "init_wcache failed.\n"));
3297 /* Write the version. */
3298 if (!tdb_store_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, WINBINDD_CACHE_VERSION)) {
3299 DEBUG(0,("initialize_winbindd_cache: version number store failed %s\n",
3300 tdb_errorstr(wcache->tdb) ));
3305 tdb_close(wcache->tdb);
3310 void close_winbindd_cache(void)
3316 tdb_close(wcache->tdb);
3321 bool lookup_cached_sid(TALLOC_CTX *mem_ctx, const struct dom_sid *sid,
3322 char **domain_name, char **name,
3323 enum lsa_SidType *type)
3325 struct winbindd_domain *domain;
3328 domain = find_lookup_domain_from_sid(sid);
3329 if (domain == NULL) {
3332 status = wcache_sid_to_name(domain, sid, mem_ctx, domain_name, name,
3334 return NT_STATUS_IS_OK(status);
3337 bool lookup_cached_name(const char *domain_name,
3339 struct dom_sid *sid,
3340 enum lsa_SidType *type)
3342 struct winbindd_domain *domain;
3344 bool original_online_state;
3346 domain = find_lookup_domain_from_name(domain_name);
3347 if (domain == NULL) {
3351 /* If we are doing a cached logon, temporarily set the domain
3352 offline so the cache won't expire the entry */
3354 original_online_state = domain->online;
3355 domain->online = false;
3356 status = wcache_name_to_sid(domain, domain_name, name, sid, type);
3357 domain->online = original_online_state;
3359 return NT_STATUS_IS_OK(status);
3363 * Cache a name to sid without checking the sequence number.
3364 * Used when caching from a trusted PAC.
3367 void cache_name2sid_trusted(struct winbindd_domain *domain,
3368 const char *domain_name,
3370 enum lsa_SidType type,
3371 const struct dom_sid *sid)
3374 * Ensure we store the mapping with the
3375 * existing sequence number from the cache.
3378 (void)fetch_cache_seqnum(domain, time(NULL));
3379 wcache_save_name_to_sid(domain,
3387 void cache_name2sid(struct winbindd_domain *domain,
3388 const char *domain_name, const char *name,
3389 enum lsa_SidType type, const struct dom_sid *sid)
3391 refresh_sequence_number(domain);
3392 wcache_save_name_to_sid(domain, NT_STATUS_OK, domain_name, name,
3397 * The original idea that this cache only contains centries has
3398 * been blurred - now other stuff gets put in here. Ensure we
3399 * ignore these things on cleanup.
3402 static int traverse_fn_cleanup(TDB_CONTEXT *the_tdb, TDB_DATA kbuf,
3403 TDB_DATA dbuf, void *state)
3405 struct cache_entry *centry;
3407 if (is_non_centry_key(kbuf)) {
3411 centry = wcache_fetch_raw((char *)kbuf.dptr);
3416 if (!NT_STATUS_IS_OK(centry->status)) {
3417 DEBUG(10,("deleting centry %s\n", (const char *)kbuf.dptr));
3418 tdb_delete(the_tdb, kbuf);
3421 centry_free(centry);
3425 /* flush the cache */
3426 void wcache_flush_cache(void)
3433 tdb_close(wcache->tdb);
3436 if (!winbindd_use_cache()) {
3440 db_path = wcache_path();
3441 if (db_path == NULL) {
3445 /* when working offline we must not clear the cache on restart */
3446 wcache->tdb = tdb_open_log(db_path,
3447 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
3448 TDB_INCOMPATIBLE_HASH |
3449 (lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST)),
3450 O_RDWR|O_CREAT, 0600);
3451 TALLOC_FREE(db_path);
3453 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
3457 tdb_traverse(wcache->tdb, traverse_fn_cleanup, NULL);
3459 DEBUG(10,("wcache_flush_cache success\n"));
3462 /* Count cached creds */
3464 static int traverse_fn_cached_creds(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
3467 int *cred_count = (int*)state;
3469 if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
3475 NTSTATUS wcache_count_cached_creds(struct winbindd_domain *domain, int *count)
3477 struct winbind_cache *cache = get_cache(domain);
3482 return NT_STATUS_INTERNAL_DB_ERROR;
3485 tdb_traverse(cache->tdb, traverse_fn_cached_creds, (void *)count);
3487 return NT_STATUS_OK;
3491 struct cred_list *prev, *next;
3496 static struct cred_list *wcache_cred_list;
3498 static int traverse_fn_get_credlist(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
3501 struct cred_list *cred;
3503 if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
3505 cred = SMB_MALLOC_P(struct cred_list);
3507 DEBUG(0,("traverse_fn_remove_first_creds: failed to malloc new entry for list\n"));
3513 /* save a copy of the key */
3515 fstrcpy(cred->name, (const char *)kbuf.dptr);
3516 DLIST_ADD(wcache_cred_list, cred);
3522 NTSTATUS wcache_remove_oldest_cached_creds(struct winbindd_domain *domain, const struct dom_sid *sid)
3524 struct winbind_cache *cache = get_cache(domain);
3527 struct cred_list *cred, *next, *oldest = NULL;
3530 return NT_STATUS_INTERNAL_DB_ERROR;
3533 /* we possibly already have an entry */
3534 if (sid && NT_STATUS_IS_OK(wcache_cached_creds_exist(domain, sid))) {
3536 fstring key_str, tmp;
3538 DEBUG(11,("we already have an entry, deleting that\n"));
3540 fstr_sprintf(key_str, "CRED/%s", sid_to_fstring(tmp, sid));
3542 tdb_delete(cache->tdb, string_tdb_data(key_str));
3544 return NT_STATUS_OK;
3547 ret = tdb_traverse(cache->tdb, traverse_fn_get_credlist, NULL);
3549 return NT_STATUS_OK;
3550 } else if ((ret < 0) || (wcache_cred_list == NULL)) {
3551 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
3554 ZERO_STRUCTP(oldest);
3556 for (cred = wcache_cred_list; cred; cred = cred->next) {
3561 data = tdb_fetch(cache->tdb, string_tdb_data(cred->name));
3563 DEBUG(10,("wcache_remove_oldest_cached_creds: entry for [%s] not found\n",
3565 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
3569 t = IVAL(data.dptr, 0);
3570 SAFE_FREE(data.dptr);
3573 oldest = SMB_MALLOC_P(struct cred_list);
3574 if (oldest == NULL) {
3575 status = NT_STATUS_NO_MEMORY;
3579 fstrcpy(oldest->name, cred->name);
3580 oldest->created = t;
3584 if (t < oldest->created) {
3585 fstrcpy(oldest->name, cred->name);
3586 oldest->created = t;
3590 if (tdb_delete(cache->tdb, string_tdb_data(oldest->name)) == 0) {
3591 status = NT_STATUS_OK;
3593 status = NT_STATUS_UNSUCCESSFUL;
3596 for (cred = wcache_cred_list; cred; cred = next) {
3598 DLIST_REMOVE(wcache_cred_list, cred);
3606 /* Change the global online/offline state. */
3607 bool set_global_winbindd_state_offline(void)
3611 DEBUG(10,("set_global_winbindd_state_offline: offline requested.\n"));
3613 /* Only go offline if someone has created
3614 the key "WINBINDD_OFFLINE" in the cache tdb. */
3616 if (wcache == NULL || wcache->tdb == NULL) {
3617 DEBUG(10,("set_global_winbindd_state_offline: wcache not open yet.\n"));
3621 if (!lp_winbind_offline_logon()) {
3622 DEBUG(10,("set_global_winbindd_state_offline: rejecting.\n"));
3626 if (global_winbindd_offline_state) {
3627 /* Already offline. */
3631 data = tdb_fetch_bystring( wcache->tdb, "WINBINDD_OFFLINE" );
3633 if (!data.dptr || data.dsize != 4) {
3634 DEBUG(10,("set_global_winbindd_state_offline: offline state not set.\n"));
3635 SAFE_FREE(data.dptr);
3638 DEBUG(10,("set_global_winbindd_state_offline: offline state set.\n"));
3639 global_winbindd_offline_state = true;
3640 SAFE_FREE(data.dptr);
3645 void set_global_winbindd_state_online(void)
3647 DEBUG(10,("set_global_winbindd_state_online: online requested.\n"));
3649 if (!lp_winbind_offline_logon()) {
3650 DEBUG(10,("set_global_winbindd_state_online: rejecting.\n"));
3654 if (!global_winbindd_offline_state) {
3655 /* Already online. */
3658 global_winbindd_offline_state = false;
3664 /* Ensure there is no key "WINBINDD_OFFLINE" in the cache tdb. */
3665 tdb_delete_bystring(wcache->tdb, "WINBINDD_OFFLINE");
3668 bool get_global_winbindd_state_offline(void)
3670 return global_winbindd_offline_state;
3673 /***********************************************************************
3674 Validate functions for all possible cache tdb keys.
3675 ***********************************************************************/
3677 static struct cache_entry *create_centry_validate(const char *kstr, TDB_DATA data,
3678 struct tdb_validation_status *state)
3680 struct cache_entry *centry;
3682 centry = SMB_XMALLOC_P(struct cache_entry);
3683 centry->data = (unsigned char *)smb_memdup(data.dptr, data.dsize);
3684 if (!centry->data) {
3688 centry->len = data.dsize;
3691 if (centry->len < 16) {
3692 /* huh? corrupt cache? */
3693 DEBUG(0,("create_centry_validate: Corrupt cache for key %s "
3694 "(len < 16) ?\n", kstr));
3695 centry_free(centry);
3696 state->bad_entry = true;
3697 state->success = false;
3701 centry->status = NT_STATUS(centry_uint32(centry));
3702 centry->sequence_number = centry_uint32(centry);
3703 centry->timeout = centry_uint64_t(centry);
3707 static int validate_seqnum(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3708 struct tdb_validation_status *state)
3710 if (dbuf.dsize != 8) {
3711 DEBUG(0,("validate_seqnum: Corrupt cache for key %s (len %u != 8) ?\n",
3712 keystr, (unsigned int)dbuf.dsize ));
3713 state->bad_entry = true;
3719 static int validate_ns(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3720 struct tdb_validation_status *state)
3722 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3727 (void)centry_uint32(centry);
3728 if (NT_STATUS_IS_OK(centry->status)) {
3730 (void)centry_sid(centry, &sid);
3733 centry_free(centry);
3735 if (!(state->success)) {
3738 DEBUG(10,("validate_ns: %s ok\n", keystr));
3742 static int validate_sn(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3743 struct tdb_validation_status *state)
3745 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3750 if (NT_STATUS_IS_OK(centry->status)) {
3751 (void)centry_uint32(centry);
3752 (void)centry_string(centry, mem_ctx);
3753 (void)centry_string(centry, mem_ctx);
3756 centry_free(centry);
3758 if (!(state->success)) {
3761 DEBUG(10,("validate_sn: %s ok\n", keystr));
3765 static int validate_u(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3766 struct tdb_validation_status *state)
3768 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3775 (void)centry_string(centry, mem_ctx);
3776 (void)centry_string(centry, mem_ctx);
3777 (void)centry_string(centry, mem_ctx);
3778 (void)centry_string(centry, mem_ctx);
3779 (void)centry_string(centry, mem_ctx);
3780 (void)centry_uint32(centry);
3781 (void)centry_uint32(centry);
3782 (void)centry_string(centry, mem_ctx);
3783 (void)centry_sid(centry, &sid);
3784 (void)centry_sid(centry, &sid);
3786 centry_free(centry);
3788 if (!(state->success)) {
3791 DEBUG(10,("validate_u: %s ok\n", keystr));
3795 static int validate_loc_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3796 struct tdb_validation_status *state)
3798 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3804 (void)centry_nttime(centry);
3805 (void)centry_nttime(centry);
3806 (void)centry_uint16(centry);
3808 centry_free(centry);
3810 if (!(state->success)) {
3813 DEBUG(10,("validate_loc_pol: %s ok\n", keystr));
3817 static int validate_pwd_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3818 struct tdb_validation_status *state)
3820 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3826 (void)centry_uint16(centry);
3827 (void)centry_uint16(centry);
3828 (void)centry_uint32(centry);
3829 (void)centry_nttime(centry);
3830 (void)centry_nttime(centry);
3832 centry_free(centry);
3834 if (!(state->success)) {
3837 DEBUG(10,("validate_pwd_pol: %s ok\n", keystr));
3841 static int validate_cred(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3842 struct tdb_validation_status *state)
3844 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3850 (void)centry_time(centry);
3851 (void)centry_hash16(centry, mem_ctx);
3853 /* We only have 17 bytes more data in the salted cred case. */
3854 if (centry->len - centry->ofs == 17) {
3855 (void)centry_hash16(centry, mem_ctx);
3858 centry_free(centry);
3860 if (!(state->success)) {
3863 DEBUG(10,("validate_cred: %s ok\n", keystr));
3867 static int validate_ul(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3868 struct tdb_validation_status *state)
3870 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3871 int32_t num_entries, i;
3877 num_entries = (int32_t)centry_uint32(centry);
3879 for (i=0; i< num_entries; i++) {
3881 (void)centry_string(centry, mem_ctx);
3882 (void)centry_string(centry, mem_ctx);
3883 (void)centry_string(centry, mem_ctx);
3884 (void)centry_string(centry, mem_ctx);
3885 (void)centry_string(centry, mem_ctx);
3886 (void)centry_uint32(centry);
3887 (void)centry_uint32(centry);
3888 (void)centry_string(centry, mem_ctx);
3889 (void)centry_sid(centry, &sid);
3890 (void)centry_sid(centry, &sid);
3893 centry_free(centry);
3895 if (!(state->success)) {
3898 DEBUG(10,("validate_ul: %s ok\n", keystr));
3902 static int validate_gl(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3903 struct tdb_validation_status *state)
3905 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3906 int32_t num_entries, i;
3912 num_entries = centry_uint32(centry);
3914 for (i=0; i< num_entries; i++) {
3915 (void)centry_string(centry, mem_ctx);
3916 (void)centry_string(centry, mem_ctx);
3917 (void)centry_uint32(centry);
3920 centry_free(centry);
3922 if (!(state->success)) {
3925 DEBUG(10,("validate_gl: %s ok\n", keystr));
3929 static int validate_ug(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3930 struct tdb_validation_status *state)
3932 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3933 int32_t num_groups, i;
3939 num_groups = centry_uint32(centry);
3941 for (i=0; i< num_groups; i++) {
3943 centry_sid(centry, &sid);
3946 centry_free(centry);
3948 if (!(state->success)) {
3951 DEBUG(10,("validate_ug: %s ok\n", keystr));
3955 static int validate_ua(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3956 struct tdb_validation_status *state)
3958 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3959 int32_t num_aliases, i;
3965 num_aliases = centry_uint32(centry);
3967 for (i=0; i < num_aliases; i++) {
3968 (void)centry_uint32(centry);
3971 centry_free(centry);
3973 if (!(state->success)) {
3976 DEBUG(10,("validate_ua: %s ok\n", keystr));
3980 static int validate_gm(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3981 struct tdb_validation_status *state)
3983 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3984 int32_t num_names, i;
3990 num_names = centry_uint32(centry);
3992 for (i=0; i< num_names; i++) {
3994 centry_sid(centry, &sid);
3995 (void)centry_string(centry, mem_ctx);
3996 (void)centry_uint32(centry);
3999 centry_free(centry);
4001 if (!(state->success)) {
4004 DEBUG(10,("validate_gm: %s ok\n", keystr));
4008 static int validate_dr(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
4009 struct tdb_validation_status *state)
4011 /* Can't say anything about this other than must be nonzero. */
4012 if (dbuf.dsize == 0) {
4013 DEBUG(0,("validate_dr: Corrupt cache for key %s (len == 0) ?\n",
4015 state->bad_entry = true;
4016 state->success = false;
4020 DEBUG(10,("validate_dr: %s ok\n", keystr));
4024 static int validate_de(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
4025 struct tdb_validation_status *state)
4027 /* Can't say anything about this other than must be nonzero. */
4028 if (dbuf.dsize == 0) {
4029 DEBUG(0,("validate_de: Corrupt cache for key %s (len == 0) ?\n",
4031 state->bad_entry = true;
4032 state->success = false;
4036 DEBUG(10,("validate_de: %s ok\n", keystr));
4040 static int validate_pwinfo(TALLOC_CTX *mem_ctx, const char *keystr,
4041 TDB_DATA dbuf, struct tdb_validation_status *state)
4043 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
4049 (void)centry_string(centry, mem_ctx);
4050 (void)centry_string(centry, mem_ctx);
4051 (void)centry_string(centry, mem_ctx);
4052 (void)centry_uint32(centry);
4054 centry_free(centry);
4056 if (!(state->success)) {
4059 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
4063 static int validate_nss_an(TALLOC_CTX *mem_ctx, const char *keystr,
4065 struct tdb_validation_status *state)
4067 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
4073 (void)centry_string( centry, mem_ctx );
4075 centry_free(centry);
4077 if (!(state->success)) {
4080 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
4084 static int validate_nss_na(TALLOC_CTX *mem_ctx, const char *keystr,
4086 struct tdb_validation_status *state)
4088 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
4094 (void)centry_string( centry, mem_ctx );
4096 centry_free(centry);
4098 if (!(state->success)) {
4101 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
4105 static int validate_trustdomcache(TALLOC_CTX *mem_ctx, const char *keystr,
4107 struct tdb_validation_status *state)
4109 if (dbuf.dsize == 0) {
4110 DEBUG(0, ("validate_trustdomcache: Corrupt cache for "
4111 "key %s (len ==0) ?\n", keystr));
4112 state->bad_entry = true;
4113 state->success = false;
4117 DEBUG(10, ("validate_trustdomcache: %s ok\n", keystr));
4118 DEBUGADD(10, (" Don't trust me, I am a DUMMY!\n"));
4122 static int validate_offline(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
4123 struct tdb_validation_status *state)
4125 if (dbuf.dsize != 4) {
4126 DEBUG(0,("validate_offline: Corrupt cache for key %s (len %u != 4) ?\n",
4127 keystr, (unsigned int)dbuf.dsize ));
4128 state->bad_entry = true;
4129 state->success = false;
4132 DEBUG(10,("validate_offline: %s ok\n", keystr));
4136 static int validate_ndr(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
4137 struct tdb_validation_status *state)
4140 * Ignore validation for now. The proper way to do this is with a
4141 * checksum. Just pure parsing does not really catch much.
4146 static int validate_cache_version(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
4147 struct tdb_validation_status *state)
4149 if (dbuf.dsize != 4) {
4150 DEBUG(0, ("validate_cache_version: Corrupt cache for "
4151 "key %s (len %u != 4) ?\n",
4152 keystr, (unsigned int)dbuf.dsize));
4153 state->bad_entry = true;
4154 state->success = false;
4158 DEBUG(10, ("validate_cache_version: %s ok\n", keystr));
4162 /***********************************************************************
4163 A list of all possible cache tdb keys with associated validation
4165 ***********************************************************************/
4167 struct key_val_struct {
4168 const char *keyname;
4169 int (*validate_data_fn)(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, struct tdb_validation_status* state);
4171 {"SEQNUM/", validate_seqnum},
4172 {"NS/", validate_ns},
4173 {"SN/", validate_sn},
4175 {"LOC_POL/", validate_loc_pol},
4176 {"PWD_POL/", validate_pwd_pol},
4177 {"CRED/", validate_cred},
4178 {"UL/", validate_ul},
4179 {"GL/", validate_gl},
4180 {"UG/", validate_ug},
4181 {"UA", validate_ua},
4182 {"GM/", validate_gm},
4183 {"DR/", validate_dr},
4184 {"DE/", validate_de},
4185 {"NSS/PWINFO/", validate_pwinfo},
4186 {"TRUSTDOMCACHE/", validate_trustdomcache},
4187 {"NSS/NA/", validate_nss_na},
4188 {"NSS/AN/", validate_nss_an},
4189 {"WINBINDD_OFFLINE", validate_offline},
4190 {"NDR/", validate_ndr},
4191 {WINBINDD_CACHE_VERSION_KEYSTR, validate_cache_version},
4195 /***********************************************************************
4196 Function to look at every entry in the tdb and validate it as far as
4198 ***********************************************************************/
4200 static int cache_traverse_validate_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, void *state)
4203 unsigned int max_key_len = 1024;
4204 struct tdb_validation_status *v_state = (struct tdb_validation_status *)state;
4206 /* Paranoia check. */
4207 if (strncmp("UA/", (const char *)kbuf.dptr, 3) == 0 ||
4208 strncmp("NDR/", (const char *)kbuf.dptr, 4) == 0) {
4209 max_key_len = 1024 * 1024;
4211 if (kbuf.dsize > max_key_len) {
4212 DEBUG(0, ("cache_traverse_validate_fn: key length too large: "
4214 (unsigned int)kbuf.dsize, (unsigned int)max_key_len));
4218 for (i = 0; key_val[i].keyname; i++) {
4219 size_t namelen = strlen(key_val[i].keyname);
4220 if (kbuf.dsize >= namelen && (
4221 strncmp(key_val[i].keyname, (const char *)kbuf.dptr, namelen)) == 0) {
4222 TALLOC_CTX *mem_ctx;
4226 keystr = SMB_MALLOC_ARRAY(char, kbuf.dsize+1);
4230 memcpy(keystr, kbuf.dptr, kbuf.dsize);
4231 keystr[kbuf.dsize] = '\0';
4233 mem_ctx = talloc_init("validate_ctx");
4239 ret = key_val[i].validate_data_fn(mem_ctx, keystr, dbuf,
4243 talloc_destroy(mem_ctx);
4248 DEBUG(0,("cache_traverse_validate_fn: unknown cache entry\nkey :\n"));
4249 dump_data(0, (uint8_t *)kbuf.dptr, kbuf.dsize);
4250 DEBUG(0,("data :\n"));
4251 dump_data(0, (uint8_t *)dbuf.dptr, dbuf.dsize);
4252 v_state->unknown_key = true;
4253 v_state->success = false;
4254 return 1; /* terminate. */
4257 static void validate_panic(const char *const why)
4259 DEBUG(0,("validating cache: would panic %s\n", why ));
4260 DEBUGADD(0, ("exiting instead (cache validation mode)\n"));
4264 static int wbcache_update_centry_fn(TDB_CONTEXT *tdb,
4272 if (is_non_centry_key(key)) {
4276 if (data.dptr == NULL || data.dsize == 0) {
4277 if (tdb_delete(tdb, key) < 0) {
4278 DEBUG(0, ("tdb_delete for [%s] failed!\n",
4284 /* add timeout to blob (uint64_t) */
4285 blob.dsize = data.dsize + 8;
4287 blob.dptr = SMB_XMALLOC_ARRAY(uint8_t, blob.dsize);
4288 if (blob.dptr == NULL) {
4291 memset(blob.dptr, 0, blob.dsize);
4293 /* copy status and seqnum */
4294 memcpy(blob.dptr, data.dptr, 8);
4297 ctimeout = lp_winbind_cache_time() + time(NULL);
4298 SBVAL(blob.dptr, 8, ctimeout);
4301 memcpy(blob.dptr + 16, data.dptr + 8, data.dsize - 8);
4303 if (tdb_store(tdb, key, blob, TDB_REPLACE) < 0) {
4304 DEBUG(0, ("tdb_store to update [%s] failed!\n",
4306 SAFE_FREE(blob.dptr);
4310 SAFE_FREE(blob.dptr);
4314 static bool wbcache_upgrade_v1_to_v2(TDB_CONTEXT *tdb)
4318 DEBUG(1, ("Upgrade to version 2 of the winbindd_cache.tdb\n"));
4320 rc = tdb_traverse(tdb, wbcache_update_centry_fn, NULL);
4328 /***********************************************************************
4329 Try and validate every entry in the winbindd cache. If we fail here,
4330 delete the cache tdb and return non-zero.
4331 ***********************************************************************/
4333 int winbindd_validate_cache(void)
4336 char *tdb_path = NULL;
4337 TDB_CONTEXT *tdb = NULL;
4341 DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
4342 smb_panic_fn = validate_panic;
4344 tdb_path = wcache_path();
4345 if (tdb_path == NULL) {
4349 tdb = tdb_open_log(tdb_path,
4350 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
4351 TDB_INCOMPATIBLE_HASH |
4352 ( lp_winbind_offline_logon()
4354 : TDB_DEFAULT | TDB_CLEAR_IF_FIRST ),
4358 DEBUG(0, ("winbindd_validate_cache: "
4359 "error opening/initializing tdb\n"));
4363 /* Version check and upgrade code. */
4364 if (!tdb_fetch_uint32(tdb, WINBINDD_CACHE_VERSION_KEYSTR, &vers_id)) {
4365 DEBUG(10, ("Fresh database\n"));
4366 tdb_store_uint32(tdb, WINBINDD_CACHE_VERSION_KEYSTR, WINBINDD_CACHE_VERSION);
4367 vers_id = WINBINDD_CACHE_VERSION;
4370 if (vers_id != WINBINDD_CACHE_VERSION) {
4371 if (vers_id == WINBINDD_CACHE_VER1) {
4372 ok = wbcache_upgrade_v1_to_v2(tdb);
4374 DEBUG(10, ("winbindd_validate_cache: upgrade to version 2 failed.\n"));
4379 tdb_store_uint32(tdb,
4380 WINBINDD_CACHE_VERSION_KEYSTR,
4381 WINBINDD_CACHE_VERSION);
4382 vers_id = WINBINDD_CACHE_VER2;
4388 ret = tdb_validate_and_backup(tdb_path, cache_traverse_validate_fn);
4391 DEBUG(10, ("winbindd_validate_cache: validation not successful.\n"));
4392 DEBUGADD(10, ("removing tdb %s.\n", tdb_path));
4397 TALLOC_FREE(tdb_path);
4398 DEBUG(10, ("winbindd_validate_cache: restoring panic function\n"));
4399 smb_panic_fn = smb_panic;
4403 /***********************************************************************
4404 Try and validate every entry in the winbindd cache.
4405 ***********************************************************************/
4407 int winbindd_validate_cache_nobackup(void)
4412 DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
4413 smb_panic_fn = validate_panic;
4415 tdb_path = wcache_path();
4416 if (tdb_path == NULL) {
4417 goto err_panic_restore;
4420 if (wcache == NULL || wcache->tdb == NULL) {
4421 ret = tdb_validate_open(tdb_path, cache_traverse_validate_fn);
4423 ret = tdb_validate(wcache->tdb, cache_traverse_validate_fn);
4427 DEBUG(10, ("winbindd_validate_cache_nobackup: validation not "
4431 TALLOC_FREE(tdb_path);
4433 DEBUG(10, ("winbindd_validate_cache_nobackup: restoring panic "
4435 smb_panic_fn = smb_panic;
4439 bool winbindd_cache_validate_and_initialize(void)
4441 close_winbindd_cache();
4443 if (lp_winbind_offline_logon()) {
4444 if (winbindd_validate_cache() < 0) {
4445 DEBUG(0, ("winbindd cache tdb corrupt and no backup "
4446 "could be restored.\n"));
4450 return initialize_winbindd_cache();
4453 /*********************************************************************
4454 ********************************************************************/
4456 static bool add_wbdomain_to_tdc_array( struct winbindd_domain *new_dom,
4457 struct winbindd_tdc_domain **domains,
4458 size_t *num_domains )
4460 struct winbindd_tdc_domain *list = NULL;
4463 bool set_only = false;
4465 /* don't allow duplicates */
4470 for ( i=0; i< (*num_domains); i++ ) {
4471 if ( strequal( new_dom->name, list[i].domain_name ) ) {
4472 DEBUG(10,("add_wbdomain_to_tdc_array: Found existing record for %s\n",
4483 list = talloc_array( NULL, struct winbindd_tdc_domain, 1 );
4486 list = talloc_realloc( *domains, *domains,
4487 struct winbindd_tdc_domain,
4492 ZERO_STRUCT( list[idx] );
4498 list[idx].domain_name = talloc_strdup(list, new_dom->name);
4499 if (list[idx].domain_name == NULL) {
4502 if (new_dom->alt_name != NULL) {
4503 list[idx].dns_name = talloc_strdup(list, new_dom->alt_name);
4504 if (list[idx].dns_name == NULL) {
4509 if ( !is_null_sid( &new_dom->sid ) ) {
4510 sid_copy( &list[idx].sid, &new_dom->sid );
4512 sid_copy(&list[idx].sid, &global_sid_NULL);
4515 if ( new_dom->domain_flags != 0x0 )
4516 list[idx].trust_flags = new_dom->domain_flags;
4518 if ( new_dom->domain_type != 0x0 )
4519 list[idx].trust_type = new_dom->domain_type;
4521 if ( new_dom->domain_trust_attribs != 0x0 )
4522 list[idx].trust_attribs = new_dom->domain_trust_attribs;
4526 *num_domains = idx + 1;
4532 /*********************************************************************
4533 ********************************************************************/
4535 static TDB_DATA make_tdc_key( const char *domain_name )
4537 char *keystr = NULL;
4538 TDB_DATA key = { NULL, 0 };
4540 if ( !domain_name ) {
4541 DEBUG(5,("make_tdc_key: Keyname workgroup is NULL!\n"));
4545 if (asprintf( &keystr, "TRUSTDOMCACHE/%s", domain_name ) == -1) {
4548 key = string_term_tdb_data(keystr);
4553 /*********************************************************************
4554 ********************************************************************/
4556 static int pack_tdc_domains( struct winbindd_tdc_domain *domains,
4558 unsigned char **buf )
4560 unsigned char *buffer = NULL;
4565 DEBUG(10,("pack_tdc_domains: Packing %d trusted domains\n",
4573 /* Store the number of array items first */
4574 len += tdb_pack( buffer+len, buflen-len, "d",
4577 /* now pack each domain trust record */
4578 for ( i=0; i<num_domains; i++ ) {
4583 DEBUG(10,("pack_tdc_domains: Packing domain %s (%s)\n",
4584 domains[i].domain_name,
4585 domains[i].dns_name ? domains[i].dns_name : "UNKNOWN" ));
4588 len += tdb_pack( buffer+len, buflen-len, "fffddd",
4589 domains[i].domain_name,
4590 domains[i].dns_name ? domains[i].dns_name : "",
4591 sid_to_fstring(tmp, &domains[i].sid),
4592 domains[i].trust_flags,
4593 domains[i].trust_attribs,
4594 domains[i].trust_type );
4597 if ( buflen < len ) {
4599 if ( (buffer = SMB_MALLOC_ARRAY(unsigned char, len)) == NULL ) {
4600 DEBUG(0,("pack_tdc_domains: failed to alloc buffer!\n"));
4614 /*********************************************************************
4615 ********************************************************************/
4617 static size_t unpack_tdc_domains( unsigned char *buf, int buflen,
4618 struct winbindd_tdc_domain **domains )
4620 fstring domain_name, dns_name, sid_string;
4621 uint32_t type, attribs, flags;
4625 struct winbindd_tdc_domain *list = NULL;
4627 /* get the number of domains */
4628 len += tdb_unpack( buf+len, buflen-len, "d", &num_domains);
4630 DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
4634 list = talloc_array( NULL, struct winbindd_tdc_domain, num_domains );
4636 DEBUG(0,("unpack_tdc_domains: Failed to talloc() domain list!\n"));
4640 for ( i=0; i<num_domains; i++ ) {
4643 this_len = tdb_unpack( buf+len, buflen-len, "fffddd",
4651 if ( this_len == -1 ) {
4652 DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
4653 TALLOC_FREE( list );
4658 DEBUG(11,("unpack_tdc_domains: Unpacking domain %s (%s) "
4659 "SID %s, flags = 0x%x, attribs = 0x%x, type = 0x%x\n",
4660 domain_name, dns_name, sid_string,
4661 flags, attribs, type));
4663 list[i].domain_name = talloc_strdup( list, domain_name );
4664 list[i].dns_name = NULL;
4665 if (dns_name[0] != '\0') {
4666 list[i].dns_name = talloc_strdup(list, dns_name);
4668 if ( !string_to_sid( &(list[i].sid), sid_string ) ) {
4669 DEBUG(10,("unpack_tdc_domains: no SID for domain %s\n",
4672 list[i].trust_flags = flags;
4673 list[i].trust_attribs = attribs;
4674 list[i].trust_type = type;
4682 /*********************************************************************
4683 ********************************************************************/
4685 static bool wcache_tdc_store_list( struct winbindd_tdc_domain *domains, size_t num_domains )
4687 TDB_DATA key = make_tdc_key( lp_workgroup() );
4688 TDB_DATA data = { NULL, 0 };
4694 /* See if we were asked to delete the cache entry */
4697 ret = tdb_delete( wcache->tdb, key );
4701 data.dsize = pack_tdc_domains( domains, num_domains, &data.dptr );
4708 ret = tdb_store( wcache->tdb, key, data, 0 );
4711 SAFE_FREE( data.dptr );
4712 SAFE_FREE( key.dptr );
4714 return ( ret == 0 );
4717 /*********************************************************************
4718 ********************************************************************/
4720 bool wcache_tdc_fetch_list( struct winbindd_tdc_domain **domains, size_t *num_domains )
4722 TDB_DATA key = make_tdc_key( lp_workgroup() );
4723 TDB_DATA data = { NULL, 0 };
4731 data = tdb_fetch( wcache->tdb, key );
4733 SAFE_FREE( key.dptr );
4738 *num_domains = unpack_tdc_domains( data.dptr, data.dsize, domains );
4740 SAFE_FREE( data.dptr );
4748 /*********************************************************************
4749 ********************************************************************/
4751 bool wcache_tdc_add_domain( struct winbindd_domain *domain )
4753 struct winbindd_tdc_domain *dom_list = NULL;
4754 size_t num_domains = 0;
4757 DEBUG(10,("wcache_tdc_add_domain: Adding domain %s (%s), SID %s, "
4758 "flags = 0x%x, attributes = 0x%x, type = 0x%x\n",
4759 domain->name, domain->alt_name,
4760 sid_string_dbg(&domain->sid),
4761 domain->domain_flags,
4762 domain->domain_trust_attribs,
4763 domain->domain_type));
4765 if ( !init_wcache() ) {
4769 /* fetch the list */
4771 wcache_tdc_fetch_list( &dom_list, &num_domains );
4773 /* add the new domain */
4775 if ( !add_wbdomain_to_tdc_array( domain, &dom_list, &num_domains ) ) {
4779 /* pack the domain */
4781 if ( !wcache_tdc_store_list( dom_list, num_domains ) ) {
4789 TALLOC_FREE( dom_list );
4794 static struct winbindd_tdc_domain *wcache_tdc_dup_domain(
4795 TALLOC_CTX *mem_ctx, const struct winbindd_tdc_domain *src)
4797 struct winbindd_tdc_domain *dst;
4799 dst = talloc(mem_ctx, struct winbindd_tdc_domain);
4803 dst->domain_name = talloc_strdup(dst, src->domain_name);
4804 if (dst->domain_name == NULL) {
4808 dst->dns_name = NULL;
4809 if (src->dns_name != NULL) {
4810 dst->dns_name = talloc_strdup(dst, src->dns_name);
4811 if (dst->dns_name == NULL) {
4816 sid_copy(&dst->sid, &src->sid);
4817 dst->trust_flags = src->trust_flags;
4818 dst->trust_type = src->trust_type;
4819 dst->trust_attribs = src->trust_attribs;
4826 /*********************************************************************
4827 ********************************************************************/
4829 struct winbindd_tdc_domain * wcache_tdc_fetch_domain( TALLOC_CTX *ctx, const char *name )
4831 struct winbindd_tdc_domain *dom_list = NULL;
4832 size_t num_domains = 0;
4834 struct winbindd_tdc_domain *d = NULL;
4836 DEBUG(10,("wcache_tdc_fetch_domain: Searching for domain %s\n", name));
4838 if ( !init_wcache() ) {
4842 /* fetch the list */
4844 wcache_tdc_fetch_list( &dom_list, &num_domains );
4846 for ( i=0; i<num_domains; i++ ) {
4847 if ( strequal(name, dom_list[i].domain_name) ||
4848 strequal(name, dom_list[i].dns_name) )
4850 DEBUG(10,("wcache_tdc_fetch_domain: Found domain %s\n",
4853 d = wcache_tdc_dup_domain(ctx, &dom_list[i]);
4858 TALLOC_FREE( dom_list );
4863 /*********************************************************************
4864 ********************************************************************/
4866 struct winbindd_tdc_domain*
4867 wcache_tdc_fetch_domainbysid(TALLOC_CTX *ctx,
4868 const struct dom_sid *sid)
4870 struct winbindd_tdc_domain *dom_list = NULL;
4871 size_t num_domains = 0;
4873 struct winbindd_tdc_domain *d = NULL;
4875 DEBUG(10,("wcache_tdc_fetch_domainbysid: Searching for domain %s\n",
4876 sid_string_dbg(sid)));
4878 if (!init_wcache()) {
4882 /* fetch the list */
4884 wcache_tdc_fetch_list(&dom_list, &num_domains);
4886 for (i = 0; i<num_domains; i++) {
4887 if (dom_sid_equal(sid, &(dom_list[i].sid))) {
4888 DEBUG(10, ("wcache_tdc_fetch_domainbysid: "
4889 "Found domain %s for SID %s\n",
4890 dom_list[i].domain_name,
4891 sid_string_dbg(sid)));
4893 d = wcache_tdc_dup_domain(ctx, &dom_list[i]);
4898 TALLOC_FREE(dom_list);
4904 /*********************************************************************
4905 ********************************************************************/
4907 void wcache_tdc_clear( void )
4909 if ( !init_wcache() )
4912 wcache_tdc_store_list( NULL, 0 );
4918 /*********************************************************************
4919 ********************************************************************/
4921 static void wcache_save_user_pwinfo(struct winbindd_domain *domain,
4923 const struct dom_sid *user_sid,
4924 const char *homedir,
4929 struct cache_entry *centry;
4932 if ( (centry = centry_start(domain, status)) == NULL )
4935 centry_put_string( centry, homedir );
4936 centry_put_string( centry, shell );
4937 centry_put_string( centry, gecos );
4938 centry_put_uint32( centry, gid );
4940 centry_end(centry, "NSS/PWINFO/%s", sid_to_fstring(tmp, user_sid) );
4942 DEBUG(10,("wcache_save_user_pwinfo: %s\n", sid_string_dbg(user_sid) ));
4944 centry_free(centry);
4949 NTSTATUS nss_get_info_cached( struct winbindd_domain *domain,
4950 const struct dom_sid *user_sid,
4952 const char **homedir, const char **shell,
4953 const char **gecos, gid_t *p_gid)
4955 struct winbind_cache *cache = get_cache(domain);
4956 struct cache_entry *centry = NULL;
4963 centry = wcache_fetch(cache, domain, "NSS/PWINFO/%s",
4964 sid_to_fstring(tmp, user_sid));
4969 *homedir = centry_string( centry, ctx );
4970 *shell = centry_string( centry, ctx );
4971 *gecos = centry_string( centry, ctx );
4972 *p_gid = centry_uint32( centry );
4974 centry_free(centry);
4976 DEBUG(10,("nss_get_info_cached: [Cached] - user_sid %s\n",
4977 sid_string_dbg(user_sid)));
4979 return NT_STATUS_OK;
4983 nt_status = nss_get_info( domain->name, user_sid, ctx,
4984 homedir, shell, gecos, p_gid );
4986 DEBUG(10, ("nss_get_info returned %s\n", nt_errstr(nt_status)));
4988 if ( NT_STATUS_IS_OK(nt_status) ) {
4989 DEBUG(10, ("result:\n\thomedir = '%s'\n", *homedir));
4990 DEBUGADD(10, ("\tshell = '%s'\n", *shell));
4991 DEBUGADD(10, ("\tgecos = '%s'\n", *gecos));
4992 DEBUGADD(10, ("\tgid = '%u'\n", (unsigned int)*p_gid));
4994 wcache_save_user_pwinfo( domain, nt_status, user_sid,
4995 *homedir, *shell, *gecos, *p_gid );
4998 if ( NT_STATUS_EQUAL( nt_status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND ) ) {
4999 DEBUG(5,("nss_get_info_cached: Setting domain %s offline\n",
5001 set_domain_offline( domain );
5009 static bool wcache_ndr_key(TALLOC_CTX *mem_ctx, const char *domain_name,
5010 uint32_t opnum, const DATA_BLOB *req,
5016 key = talloc_asprintf(mem_ctx, "NDR/%s/%d/", domain_name, (int)opnum);
5020 keylen = talloc_get_size(key) - 1;
5022 key = talloc_realloc(mem_ctx, key, char, keylen + req->length);
5026 memcpy(key + keylen, req->data, req->length);
5028 pkey->dptr = (uint8_t *)key;
5029 pkey->dsize = talloc_get_size(key);
5033 static bool wcache_opnum_cacheable(uint32_t opnum)
5036 case NDR_WBINT_PING:
5037 case NDR_WBINT_QUERYSEQUENCENUMBER:
5038 case NDR_WBINT_ALLOCATEUID:
5039 case NDR_WBINT_ALLOCATEGID:
5040 case NDR_WBINT_CHECKMACHINEACCOUNT:
5041 case NDR_WBINT_CHANGEMACHINEACCOUNT:
5042 case NDR_WBINT_PINGDC:
5048 bool wcache_fetch_ndr(TALLOC_CTX *mem_ctx, struct winbindd_domain *domain,
5049 uint32_t opnum, const DATA_BLOB *req, DATA_BLOB *resp)
5054 if (!wcache_opnum_cacheable(opnum) ||
5055 is_my_own_sam_domain(domain) ||
5056 is_builtin_domain(domain)) {
5060 if (wcache->tdb == NULL) {
5064 if (!wcache_ndr_key(talloc_tos(), domain->name, opnum, req, &key)) {
5067 data = tdb_fetch(wcache->tdb, key);
5068 TALLOC_FREE(key.dptr);
5070 if (data.dptr == NULL) {
5073 if (data.dsize < 12) {
5077 if (!is_domain_offline(domain)) {
5078 uint32_t entry_seqnum, dom_seqnum, last_check;
5079 uint64_t entry_timeout;
5081 if (!wcache_fetch_seqnum(domain->name, &dom_seqnum,
5085 entry_seqnum = IVAL(data.dptr, 0);
5086 if (entry_seqnum != dom_seqnum) {
5087 DEBUG(10, ("Entry has wrong sequence number: %d\n",
5088 (int)entry_seqnum));
5091 entry_timeout = BVAL(data.dptr, 4);
5092 if (time(NULL) > entry_timeout) {
5093 DEBUG(10, ("Entry has timed out\n"));
5098 resp->data = (uint8_t *)talloc_memdup(mem_ctx, data.dptr + 12,
5100 if (resp->data == NULL) {
5101 DEBUG(10, ("talloc failed\n"));
5104 resp->length = data.dsize - 12;
5108 SAFE_FREE(data.dptr);
5112 void wcache_store_ndr(struct winbindd_domain *domain, uint32_t opnum,
5113 const DATA_BLOB *req, const DATA_BLOB *resp)
5116 uint32_t dom_seqnum, last_check;
5119 if (!wcache_opnum_cacheable(opnum) ||
5120 is_my_own_sam_domain(domain) ||
5121 is_builtin_domain(domain)) {
5125 if (wcache->tdb == NULL) {
5129 if (!wcache_fetch_seqnum(domain->name, &dom_seqnum, &last_check)) {
5130 DEBUG(10, ("could not fetch seqnum for domain %s\n",
5135 if (!wcache_ndr_key(talloc_tos(), domain->name, opnum, req, &key)) {
5139 timeout = time(NULL) + lp_winbind_cache_time();
5141 data.dsize = resp->length + 12;
5142 data.dptr = talloc_array(key.dptr, uint8_t, data.dsize);
5143 if (data.dptr == NULL) {
5147 SIVAL(data.dptr, 0, dom_seqnum);
5148 SBVAL(data.dptr, 4, timeout);
5149 memcpy(data.dptr + 12, resp->data, resp->length);
5151 tdb_store(wcache->tdb, key, data, 0);
5154 TALLOC_FREE(key.dptr);