2 Unix SMB/CIFS implementation.
4 Winbind cache backend functions
6 Copyright (C) Andrew Tridgell 2001
7 Copyright (C) Gerald Carter 2003
8 Copyright (C) Volker Lendecke 2005
11 This program is free software; you can redistribute it and/or modify
12 it under the terms of the GNU General Public License as published by
13 the Free Software Foundation; either version 2 of the License, or
14 (at your option) any later version.
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 GNU General Public License for more details.
21 You should have received a copy of the GNU General Public License
22 along with this program; if not, write to the Free Software
23 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
30 #define DBGC_CLASS DBGC_WINBIND
32 struct winbind_cache {
38 uint32 sequence_number;
43 #define WINBINDD_MAX_CACHE_SIZE (50*1024*1024)
45 static struct winbind_cache *wcache;
48 void wcache_flush_cache(void)
50 extern BOOL opt_nocache;
55 tdb_close(wcache->tdb);
61 wcache->tdb = tdb_open_log(lock_path("winbindd_cache.tdb"), 5000,
62 TDB_CLEAR_IF_FIRST, O_RDWR|O_CREAT, 0600);
65 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
67 DEBUG(10,("wcache_flush_cache success\n"));
70 void winbindd_check_cache_size(time_t t)
72 static time_t last_check_time;
75 if (last_check_time == (time_t)0)
78 if (t - last_check_time < 60 && t - last_check_time > 0)
81 if (wcache == NULL || wcache->tdb == NULL) {
82 DEBUG(0, ("Unable to check size of tdb cache - cache not open !\n"));
86 if (fstat(wcache->tdb->fd, &st) == -1) {
87 DEBUG(0, ("Unable to check size of tdb cache %s!\n", strerror(errno) ));
91 if (st.st_size > WINBINDD_MAX_CACHE_SIZE) {
92 DEBUG(10,("flushing cache due to size (%lu) > (%lu)\n",
93 (unsigned long)st.st_size,
94 (unsigned long)WINBINDD_MAX_CACHE_SIZE));
99 /* get the winbind_cache structure */
100 static struct winbind_cache *get_cache(struct winbindd_domain *domain)
102 struct winbind_cache *ret = wcache;
103 struct winbindd_domain *our_domain = domain;
105 /* we have to know what type of domain we are dealing with first */
107 if ( !domain->initialized )
108 set_dc_type_and_flags( domain );
111 OK. listen up becasue I'm only going to say this once.
112 We have the following scenarios to consider
113 (a) trusted AD domains on a Samba DC,
114 (b) trusted AD domains and we are joined to a non-kerberos domain
115 (c) trusted AD domains and we are joined to a kerberos (AD) domain
117 For (a) we can always contact the trusted domain using krb5
118 since we have the domain trust account password
120 For (b) we can only use RPC since we have no way of
121 getting a krb5 ticket in our own domain
123 For (c) we can always use krb5 since we have a kerberos trust
128 if (!domain->backend) {
129 extern struct winbindd_methods reconnect_methods;
131 extern struct winbindd_methods ads_methods;
133 /* find our domain first so we can figure out if we
134 are joined to a kerberized domain */
136 if ( !domain->primary )
137 our_domain = find_our_domain();
139 if ( (our_domain->active_directory || IS_DC) && domain->active_directory ) {
140 DEBUG(5,("get_cache: Setting ADS methods for domain %s\n", domain->name));
141 domain->backend = &ads_methods;
143 #endif /* HAVE_ADS */
144 DEBUG(5,("get_cache: Setting MS-RPC methods for domain %s\n", domain->name));
145 domain->backend = &reconnect_methods;
148 #endif /* HAVE_ADS */
154 ret = SMB_XMALLOC_P(struct winbind_cache);
158 wcache_flush_cache();
164 free a centry structure
166 static void centry_free(struct cache_entry *centry)
170 SAFE_FREE(centry->data);
175 pull a uint32 from a cache entry
177 static uint32 centry_uint32(struct cache_entry *centry)
180 if (centry->len - centry->ofs < 4) {
181 DEBUG(0,("centry corruption? needed 4 bytes, have %d\n",
182 centry->len - centry->ofs));
183 smb_panic("centry_uint32");
185 ret = IVAL(centry->data, centry->ofs);
191 pull a uint8 from a cache entry
193 static uint8 centry_uint8(struct cache_entry *centry)
196 if (centry->len - centry->ofs < 1) {
197 DEBUG(0,("centry corruption? needed 1 bytes, have %d\n",
198 centry->len - centry->ofs));
199 smb_panic("centry_uint32");
201 ret = CVAL(centry->data, centry->ofs);
206 /* pull a string from a cache entry, using the supplied
209 static char *centry_string(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
214 len = centry_uint8(centry);
217 /* a deliberate NULL string */
221 if (centry->len - centry->ofs < len) {
222 DEBUG(0,("centry corruption? needed %d bytes, have %d\n",
223 len, centry->len - centry->ofs));
224 smb_panic("centry_string");
228 ret = TALLOC(mem_ctx, len+1);
230 ret = SMB_MALLOC(len+1);
232 smb_panic("centry_string out of memory\n");
234 memcpy(ret,centry->data + centry->ofs, len);
240 /* pull a string from a cache entry, using the supplied
243 static BOOL centry_sid(struct cache_entry *centry, DOM_SID *sid)
246 sid_string = centry_string(centry, NULL);
247 if (!string_to_sid(sid, sid_string)) {
250 SAFE_FREE(sid_string);
254 /* the server is considered down if it can't give us a sequence number */
255 static BOOL wcache_server_down(struct winbindd_domain *domain)
262 ret = (domain->sequence_number == DOM_SEQUENCE_NONE);
265 DEBUG(10,("wcache_server_down: server for Domain %s down\n",
270 static NTSTATUS fetch_cache_seqnum( struct winbindd_domain *domain, time_t now )
277 DEBUG(10,("fetch_cache_seqnum: tdb == NULL\n"));
278 return NT_STATUS_UNSUCCESSFUL;
281 fstr_sprintf( key, "SEQNUM/%s", domain->name );
283 data = tdb_fetch_bystring( wcache->tdb, key );
284 if ( !data.dptr || data.dsize!=8 ) {
285 DEBUG(10,("fetch_cache_seqnum: invalid data size key [%s]\n", key ));
286 return NT_STATUS_UNSUCCESSFUL;
289 domain->sequence_number = IVAL(data.dptr, 0);
290 domain->last_seq_check = IVAL(data.dptr, 4);
292 SAFE_FREE(data.dptr);
294 /* have we expired? */
296 time_diff = now - domain->last_seq_check;
297 if ( time_diff > lp_winbind_cache_time() ) {
298 DEBUG(10,("fetch_cache_seqnum: timeout [%s][%u @ %u]\n",
299 domain->name, domain->sequence_number,
300 (uint32)domain->last_seq_check));
301 return NT_STATUS_UNSUCCESSFUL;
304 DEBUG(10,("fetch_cache_seqnum: success [%s][%u @ %u]\n",
305 domain->name, domain->sequence_number,
306 (uint32)domain->last_seq_check));
311 static NTSTATUS store_cache_seqnum( struct winbindd_domain *domain )
318 DEBUG(10,("store_cache_seqnum: tdb == NULL\n"));
319 return NT_STATUS_UNSUCCESSFUL;
322 fstr_sprintf( key_str, "SEQNUM/%s", domain->name );
324 key.dsize = strlen(key_str)+1;
326 SIVAL(buf, 0, domain->sequence_number);
327 SIVAL(buf, 4, domain->last_seq_check);
331 if ( tdb_store( wcache->tdb, key, data, TDB_REPLACE) == -1 ) {
332 DEBUG(10,("store_cache_seqnum: tdb_store fail key [%s]\n", key_str ));
333 return NT_STATUS_UNSUCCESSFUL;
336 DEBUG(10,("store_cache_seqnum: success [%s][%u @ %u]\n",
337 domain->name, domain->sequence_number,
338 (uint32)domain->last_seq_check));
344 refresh the domain sequence number. If force is True
345 then always refresh it, no matter how recently we fetched it
348 static void refresh_sequence_number(struct winbindd_domain *domain, BOOL force)
352 time_t t = time(NULL);
353 unsigned cache_time = lp_winbind_cache_time();
357 #if 0 /* JERRY -- disable as the default cache time is now 5 minutes */
358 /* trying to reconnect is expensive, don't do it too often */
359 if (domain->sequence_number == DOM_SEQUENCE_NONE) {
364 time_diff = t - domain->last_seq_check;
366 /* see if we have to refetch the domain sequence number */
367 if (!force && (time_diff < cache_time)) {
368 DEBUG(10, ("refresh_sequence_number: %s time ok\n", domain->name));
372 /* try to get the sequence number from the tdb cache first */
373 /* this will update the timestamp as well */
375 status = fetch_cache_seqnum( domain, t );
376 if ( NT_STATUS_IS_OK(status) )
379 /* important! make sure that we know if this is a native
380 mode domain or not */
382 status = domain->backend->sequence_number(domain, &domain->sequence_number);
384 if (!NT_STATUS_IS_OK(status)) {
385 domain->sequence_number = DOM_SEQUENCE_NONE;
388 domain->last_status = status;
389 domain->last_seq_check = time(NULL);
391 /* save the new sequence number ni the cache */
392 store_cache_seqnum( domain );
395 DEBUG(10, ("refresh_sequence_number: %s seq number is now %d\n",
396 domain->name, domain->sequence_number));
402 decide if a cache entry has expired
404 static BOOL centry_expired(struct winbindd_domain *domain, const char *keystr, struct cache_entry *centry)
406 /* if the server is OK and our cache entry came from when it was down then
407 the entry is invalid */
408 if (domain->sequence_number != DOM_SEQUENCE_NONE &&
409 centry->sequence_number == DOM_SEQUENCE_NONE) {
410 DEBUG(10,("centry_expired: Key %s for domain %s invalid sequence.\n",
411 keystr, domain->name ));
415 /* if the server is down or the cache entry is not older than the
416 current sequence number then it is OK */
417 if (wcache_server_down(domain) ||
418 centry->sequence_number == domain->sequence_number) {
419 DEBUG(10,("centry_expired: Key %s for domain %s is good.\n",
420 keystr, domain->name ));
424 DEBUG(10,("centry_expired: Key %s for domain %s expired\n",
425 keystr, domain->name ));
432 fetch an entry from the cache, with a varargs key. auto-fetch the sequence
433 number and return status
435 static struct cache_entry *wcache_fetch(struct winbind_cache *cache,
436 struct winbindd_domain *domain,
437 const char *format, ...) PRINTF_ATTRIBUTE(3,4);
438 static struct cache_entry *wcache_fetch(struct winbind_cache *cache,
439 struct winbindd_domain *domain,
440 const char *format, ...)
445 struct cache_entry *centry;
448 refresh_sequence_number(domain, False);
450 va_start(ap, format);
451 smb_xvasprintf(&kstr, format, ap);
455 key.dsize = strlen(kstr);
456 data = tdb_fetch(wcache->tdb, key);
463 centry = SMB_XMALLOC_P(struct cache_entry);
464 centry->data = (unsigned char *)data.dptr;
465 centry->len = data.dsize;
468 if (centry->len < 8) {
469 /* huh? corrupt cache? */
470 DEBUG(10,("wcache_fetch: Corrupt cache for key %s domain %s (len < 8) ?\n",
471 kstr, domain->name ));
477 centry->status = NT_STATUS(centry_uint32(centry));
478 centry->sequence_number = centry_uint32(centry);
480 if (centry_expired(domain, kstr, centry)) {
482 DEBUG(10,("wcache_fetch: entry %s expired for domain %s\n",
483 kstr, domain->name ));
490 DEBUG(10,("wcache_fetch: returning entry %s for domain %s\n",
491 kstr, domain->name ));
498 make sure we have at least len bytes available in a centry
500 static void centry_expand(struct cache_entry *centry, uint32 len)
503 if (centry->len - centry->ofs >= len)
506 p = SMB_REALLOC(centry->data, centry->len);
508 DEBUG(0,("out of memory: needed %d bytes in centry_expand\n", centry->len));
509 smb_panic("out of memory in centry_expand");
515 push a uint32 into a centry
517 static void centry_put_uint32(struct cache_entry *centry, uint32 v)
519 centry_expand(centry, 4);
520 SIVAL(centry->data, centry->ofs, v);
525 push a uint8 into a centry
527 static void centry_put_uint8(struct cache_entry *centry, uint8 v)
529 centry_expand(centry, 1);
530 SCVAL(centry->data, centry->ofs, v);
535 push a string into a centry
537 static void centry_put_string(struct cache_entry *centry, const char *s)
542 /* null strings are marked as len 0xFFFF */
543 centry_put_uint8(centry, 0xFF);
548 /* can't handle more than 254 char strings. Truncating is probably best */
550 DEBUG(10,("centry_put_string: truncating len (%d) to: 254\n", len));
553 centry_put_uint8(centry, len);
554 centry_expand(centry, len);
555 memcpy(centry->data + centry->ofs, s, len);
559 static void centry_put_sid(struct cache_entry *centry, const DOM_SID *sid)
562 centry_put_string(centry, sid_to_string(sid_string, sid));
566 start a centry for output. When finished, call centry_end()
568 struct cache_entry *centry_start(struct winbindd_domain *domain, NTSTATUS status)
570 struct cache_entry *centry;
575 centry = SMB_XMALLOC_P(struct cache_entry);
577 centry->len = 8192; /* reasonable default */
578 centry->data = SMB_XMALLOC_ARRAY(uint8, centry->len);
580 centry->sequence_number = domain->sequence_number;
581 centry_put_uint32(centry, NT_STATUS_V(status));
582 centry_put_uint32(centry, centry->sequence_number);
587 finish a centry and write it to the tdb
589 static void centry_end(struct cache_entry *centry, const char *format, ...) PRINTF_ATTRIBUTE(2,3);
590 static void centry_end(struct cache_entry *centry, const char *format, ...)
596 va_start(ap, format);
597 smb_xvasprintf(&kstr, format, ap);
601 key.dsize = strlen(kstr);
602 data.dptr = (char *)centry->data;
603 data.dsize = centry->ofs;
605 tdb_store(wcache->tdb, key, data, TDB_REPLACE);
609 static void wcache_save_name_to_sid(struct winbindd_domain *domain,
610 NTSTATUS status, const char *domain_name,
611 const char *name, const DOM_SID *sid,
612 enum SID_NAME_USE type)
614 struct cache_entry *centry;
617 centry = centry_start(domain, status);
620 centry_put_uint32(centry, type);
621 centry_put_sid(centry, sid);
622 fstrcpy(uname, name);
624 centry_end(centry, "NS/%s/%s", domain_name, uname);
625 DEBUG(10,("wcache_save_name_to_sid: %s -> %s\n", uname,
626 sid_string_static(sid)));
630 static void wcache_save_sid_to_name(struct winbindd_domain *domain, NTSTATUS status,
631 const DOM_SID *sid, const char *domain_name, const char *name, enum SID_NAME_USE type)
633 struct cache_entry *centry;
636 centry = centry_start(domain, status);
639 if (NT_STATUS_IS_OK(status)) {
640 centry_put_uint32(centry, type);
641 centry_put_string(centry, domain_name);
642 centry_put_string(centry, name);
644 centry_end(centry, "SN/%s", sid_to_string(sid_string, sid));
645 DEBUG(10,("wcache_save_sid_to_name: %s -> %s\n", sid_string, name));
650 static void wcache_save_user(struct winbindd_domain *domain, NTSTATUS status, WINBIND_USERINFO *info)
652 struct cache_entry *centry;
655 centry = centry_start(domain, status);
658 centry_put_string(centry, info->acct_name);
659 centry_put_string(centry, info->full_name);
660 centry_put_string(centry, info->homedir);
661 centry_put_string(centry, info->shell);
662 centry_put_sid(centry, &info->user_sid);
663 centry_put_sid(centry, &info->group_sid);
664 centry_end(centry, "U/%s", sid_to_string(sid_string, &info->user_sid));
665 DEBUG(10,("wcache_save_user: %s (acct_name %s)\n", sid_string, info->acct_name));
670 /* Query display info. This is the basic user list fn */
671 static NTSTATUS query_user_list(struct winbindd_domain *domain,
674 WINBIND_USERINFO **info)
676 struct winbind_cache *cache = get_cache(domain);
677 struct cache_entry *centry = NULL;
679 unsigned int i, retry;
684 centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
688 *num_entries = centry_uint32(centry);
690 if (*num_entries == 0)
693 (*info) = TALLOC_ARRAY(mem_ctx, WINBIND_USERINFO, *num_entries);
695 smb_panic("query_user_list out of memory");
696 for (i=0; i<(*num_entries); i++) {
697 (*info)[i].acct_name = centry_string(centry, mem_ctx);
698 (*info)[i].full_name = centry_string(centry, mem_ctx);
699 (*info)[i].homedir = centry_string(centry, mem_ctx);
700 (*info)[i].shell = centry_string(centry, mem_ctx);
701 centry_sid(centry, &(*info)[i].user_sid);
702 centry_sid(centry, &(*info)[i].group_sid);
706 status = centry->status;
708 DEBUG(10,("query_user_list: [Cached] - cached list for domain %s status %s\n",
709 domain->name, get_friendly_nt_error_msg(status) ));
718 /* Return status value returned by seq number check */
720 if (!NT_STATUS_IS_OK(domain->last_status))
721 return domain->last_status;
723 /* Put the query_user_list() in a retry loop. There appears to be
724 * some bug either with Windows 2000 or Samba's handling of large
725 * rpc replies. This manifests itself as sudden disconnection
726 * at a random point in the enumeration of a large (60k) user list.
727 * The retry loop simply tries the operation again. )-: It's not
728 * pretty but an acceptable workaround until we work out what the
729 * real problem is. */
734 DEBUG(10,("query_user_list: [Cached] - doing backend query for list for domain %s\n",
737 status = domain->backend->query_user_list(domain, mem_ctx, num_entries, info);
738 if (!NT_STATUS_IS_OK(status))
739 DEBUG(3, ("query_user_list: returned 0x%08x, "
740 "retrying\n", NT_STATUS_V(status)));
741 if (NT_STATUS_EQUAL(status, NT_STATUS_UNSUCCESSFUL)) {
742 DEBUG(3, ("query_user_list: flushing "
743 "connection cache\n"));
744 invalidate_cm_connection(&domain->conn);
747 } while (NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_UNSUCCESSFUL) &&
751 refresh_sequence_number(domain, False);
752 centry = centry_start(domain, status);
755 centry_put_uint32(centry, *num_entries);
756 for (i=0; i<(*num_entries); i++) {
757 centry_put_string(centry, (*info)[i].acct_name);
758 centry_put_string(centry, (*info)[i].full_name);
759 centry_put_string(centry, (*info)[i].homedir);
760 centry_put_string(centry, (*info)[i].shell);
761 centry_put_sid(centry, &(*info)[i].user_sid);
762 centry_put_sid(centry, &(*info)[i].group_sid);
763 if (domain->backend->consistent) {
764 /* when the backend is consistent we can pre-prime some mappings */
765 wcache_save_name_to_sid(domain, NT_STATUS_OK,
767 (*info)[i].acct_name,
768 &(*info)[i].user_sid,
770 wcache_save_sid_to_name(domain, NT_STATUS_OK,
771 &(*info)[i].user_sid,
773 (*info)[i].acct_name,
775 wcache_save_user(domain, NT_STATUS_OK, &(*info)[i]);
778 centry_end(centry, "UL/%s", domain->name);
785 /* list all domain groups */
786 static NTSTATUS enum_dom_groups(struct winbindd_domain *domain,
789 struct acct_info **info)
791 struct winbind_cache *cache = get_cache(domain);
792 struct cache_entry *centry = NULL;
799 centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
803 *num_entries = centry_uint32(centry);
805 if (*num_entries == 0)
808 (*info) = TALLOC_ARRAY(mem_ctx, struct acct_info, *num_entries);
810 smb_panic("enum_dom_groups out of memory");
811 for (i=0; i<(*num_entries); i++) {
812 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
813 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
814 (*info)[i].rid = centry_uint32(centry);
818 status = centry->status;
820 DEBUG(10,("enum_dom_groups: [Cached] - cached list for domain %s status %s\n",
821 domain->name, get_friendly_nt_error_msg(status) ));
830 /* Return status value returned by seq number check */
832 if (!NT_STATUS_IS_OK(domain->last_status))
833 return domain->last_status;
835 DEBUG(10,("enum_dom_groups: [Cached] - doing backend query for list for domain %s\n",
838 status = domain->backend->enum_dom_groups(domain, mem_ctx, num_entries, info);
841 refresh_sequence_number(domain, False);
842 centry = centry_start(domain, status);
845 centry_put_uint32(centry, *num_entries);
846 for (i=0; i<(*num_entries); i++) {
847 centry_put_string(centry, (*info)[i].acct_name);
848 centry_put_string(centry, (*info)[i].acct_desc);
849 centry_put_uint32(centry, (*info)[i].rid);
851 centry_end(centry, "GL/%s/domain", domain->name);
858 /* list all domain groups */
859 static NTSTATUS enum_local_groups(struct winbindd_domain *domain,
862 struct acct_info **info)
864 struct winbind_cache *cache = get_cache(domain);
865 struct cache_entry *centry = NULL;
872 centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
876 *num_entries = centry_uint32(centry);
878 if (*num_entries == 0)
881 (*info) = TALLOC_ARRAY(mem_ctx, struct acct_info, *num_entries);
883 smb_panic("enum_dom_groups out of memory");
884 for (i=0; i<(*num_entries); i++) {
885 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
886 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
887 (*info)[i].rid = centry_uint32(centry);
892 /* If we are returning cached data and the domain controller
893 is down then we don't know whether the data is up to date
894 or not. Return NT_STATUS_MORE_PROCESSING_REQUIRED to
897 if (wcache_server_down(domain)) {
898 DEBUG(10, ("enum_local_groups: returning cached user list and server was down\n"));
899 status = NT_STATUS_MORE_PROCESSING_REQUIRED;
901 status = centry->status;
903 DEBUG(10,("enum_local_groups: [Cached] - cached list for domain %s status %s\n",
904 domain->name, get_friendly_nt_error_msg(status) ));
913 /* Return status value returned by seq number check */
915 if (!NT_STATUS_IS_OK(domain->last_status))
916 return domain->last_status;
918 DEBUG(10,("enum_local_groups: [Cached] - doing backend query for list for domain %s\n",
921 status = domain->backend->enum_local_groups(domain, mem_ctx, num_entries, info);
924 refresh_sequence_number(domain, False);
925 centry = centry_start(domain, status);
928 centry_put_uint32(centry, *num_entries);
929 for (i=0; i<(*num_entries); i++) {
930 centry_put_string(centry, (*info)[i].acct_name);
931 centry_put_string(centry, (*info)[i].acct_desc);
932 centry_put_uint32(centry, (*info)[i].rid);
934 centry_end(centry, "GL/%s/local", domain->name);
941 /* convert a single name to a sid in a domain */
942 static NTSTATUS name_to_sid(struct winbindd_domain *domain,
944 const char *domain_name,
947 enum SID_NAME_USE *type)
949 struct winbind_cache *cache = get_cache(domain);
950 struct cache_entry *centry = NULL;
957 fstrcpy(uname, name);
959 centry = wcache_fetch(cache, domain, "NS/%s/%s", domain_name, uname);
962 *type = (enum SID_NAME_USE)centry_uint32(centry);
963 status = centry->status;
964 if (NT_STATUS_IS_OK(status)) {
965 centry_sid(centry, sid);
968 DEBUG(10,("name_to_sid: [Cached] - cached name for domain %s status %s\n",
969 domain->name, get_friendly_nt_error_msg(status) ));
977 /* If the seq number check indicated that there is a problem
978 * with this DC, then return that status... except for
979 * access_denied. This is special because the dc may be in
980 * "restrict anonymous = 1" mode, in which case it will deny
981 * most unauthenticated operations, but *will* allow the LSA
982 * name-to-sid that we try as a fallback. */
984 if (!(NT_STATUS_IS_OK(domain->last_status)
985 || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
986 return domain->last_status;
988 DEBUG(10,("name_to_sid: [Cached] - doing backend query for name for domain %s\n",
991 status = domain->backend->name_to_sid(domain, mem_ctx, domain_name, name, sid, type);
994 wcache_save_name_to_sid(domain, status, domain_name, name, sid, *type);
996 if (NT_STATUS_IS_OK(status)) {
997 strupper_m(CONST_DISCARD(char *,domain_name));
998 strlower_m(CONST_DISCARD(char *,name));
999 wcache_save_sid_to_name(domain, status, sid, domain_name, name, *type);
1005 /* convert a sid to a user or group name. The sid is guaranteed to be in the domain
1007 static NTSTATUS sid_to_name(struct winbindd_domain *domain,
1008 TALLOC_CTX *mem_ctx,
1012 enum SID_NAME_USE *type)
1014 struct winbind_cache *cache = get_cache(domain);
1015 struct cache_entry *centry = NULL;
1022 centry = wcache_fetch(cache, domain, "SN/%s", sid_to_string(sid_string, sid));
1025 if (NT_STATUS_IS_OK(centry->status)) {
1026 *type = (enum SID_NAME_USE)centry_uint32(centry);
1027 *domain_name = centry_string(centry, mem_ctx);
1028 *name = centry_string(centry, mem_ctx);
1030 status = centry->status;
1032 DEBUG(10,("sid_to_name: [Cached] - cached name for domain %s status %s\n",
1033 domain->name, get_friendly_nt_error_msg(status) ));
1035 centry_free(centry);
1040 *domain_name = NULL;
1042 /* If the seq number check indicated that there is a problem
1043 * with this DC, then return that status... except for
1044 * access_denied. This is special because the dc may be in
1045 * "restrict anonymous = 1" mode, in which case it will deny
1046 * most unauthenticated operations, but *will* allow the LSA
1047 * sid-to-name that we try as a fallback. */
1049 if (!(NT_STATUS_IS_OK(domain->last_status)
1050 || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1051 return domain->last_status;
1053 DEBUG(10,("sid_to_name: [Cached] - doing backend query for name for domain %s\n",
1056 status = domain->backend->sid_to_name(domain, mem_ctx, sid, domain_name, name, type);
1059 refresh_sequence_number(domain, False);
1060 wcache_save_sid_to_name(domain, status, sid, *domain_name, *name, *type);
1062 /* We can't save the name to sid mapping here, as with sid history a
1063 * later name2sid would give the wrong sid. */
1068 /* Lookup user information from a rid */
1069 static NTSTATUS query_user(struct winbindd_domain *domain,
1070 TALLOC_CTX *mem_ctx,
1071 const DOM_SID *user_sid,
1072 WINBIND_USERINFO *info)
1074 struct winbind_cache *cache = get_cache(domain);
1075 struct cache_entry *centry = NULL;
1081 centry = wcache_fetch(cache, domain, "U/%s", sid_string_static(user_sid));
1083 /* If we have an access denied cache entry and a cached info3 in the
1084 samlogon cache then do a query. This will force the rpc back end
1085 to return the info3 data. */
1087 if (NT_STATUS_V(domain->last_status) == NT_STATUS_V(NT_STATUS_ACCESS_DENIED) &&
1088 netsamlogon_cache_have(user_sid)) {
1089 DEBUG(10, ("query_user: cached access denied and have cached info3\n"));
1090 domain->last_status = NT_STATUS_OK;
1091 centry_free(centry);
1098 info->acct_name = centry_string(centry, mem_ctx);
1099 info->full_name = centry_string(centry, mem_ctx);
1100 info->homedir = centry_string(centry, mem_ctx);
1101 info->shell = centry_string(centry, mem_ctx);
1102 centry_sid(centry, &info->user_sid);
1103 centry_sid(centry, &info->group_sid);
1104 status = centry->status;
1106 DEBUG(10,("query_user: [Cached] - cached info for domain %s status %s\n",
1107 domain->name, get_friendly_nt_error_msg(status) ));
1109 centry_free(centry);
1115 /* Return status value returned by seq number check */
1117 if (!NT_STATUS_IS_OK(domain->last_status))
1118 return domain->last_status;
1120 DEBUG(10,("sid_to_name: [Cached] - doing backend query for info for domain %s\n",
1123 status = domain->backend->query_user(domain, mem_ctx, user_sid, info);
1126 refresh_sequence_number(domain, False);
1127 wcache_save_user(domain, status, info);
1133 /* Lookup groups a user is a member of. */
1134 static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
1135 TALLOC_CTX *mem_ctx,
1136 const DOM_SID *user_sid,
1137 uint32 *num_groups, DOM_SID **user_gids)
1139 struct winbind_cache *cache = get_cache(domain);
1140 struct cache_entry *centry = NULL;
1148 centry = wcache_fetch(cache, domain, "UG/%s", sid_to_string(sid_string, user_sid));
1150 /* If we have an access denied cache entry and a cached info3 in the
1151 samlogon cache then do a query. This will force the rpc back end
1152 to return the info3 data. */
1154 if (NT_STATUS_V(domain->last_status) == NT_STATUS_V(NT_STATUS_ACCESS_DENIED) &&
1155 netsamlogon_cache_have(user_sid)) {
1156 DEBUG(10, ("lookup_usergroups: cached access denied and have cached info3\n"));
1157 domain->last_status = NT_STATUS_OK;
1158 centry_free(centry);
1165 *num_groups = centry_uint32(centry);
1167 if (*num_groups == 0)
1170 (*user_gids) = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_groups);
1172 smb_panic("lookup_usergroups out of memory");
1173 for (i=0; i<(*num_groups); i++) {
1174 centry_sid(centry, &(*user_gids)[i]);
1178 status = centry->status;
1180 DEBUG(10,("lookup_usergroups: [Cached] - cached info for domain %s status %s\n",
1181 domain->name, get_friendly_nt_error_msg(status) ));
1183 centry_free(centry);
1188 (*user_gids) = NULL;
1190 /* Return status value returned by seq number check */
1192 if (!NT_STATUS_IS_OK(domain->last_status))
1193 return domain->last_status;
1195 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info for domain %s\n",
1198 status = domain->backend->lookup_usergroups(domain, mem_ctx, user_sid, num_groups, user_gids);
1201 refresh_sequence_number(domain, False);
1202 centry = centry_start(domain, status);
1205 centry_put_uint32(centry, *num_groups);
1206 for (i=0; i<(*num_groups); i++) {
1207 centry_put_sid(centry, &(*user_gids)[i]);
1209 centry_end(centry, "UG/%s", sid_to_string(sid_string, user_sid));
1210 centry_free(centry);
1216 static NTSTATUS lookup_useraliases(struct winbindd_domain *domain,
1217 TALLOC_CTX *mem_ctx,
1218 uint32 num_sids, const DOM_SID *sids,
1219 uint32 *num_aliases, uint32 **alias_rids)
1221 struct winbind_cache *cache = get_cache(domain);
1222 struct cache_entry *centry = NULL;
1224 char *sidlist = talloc_strdup(mem_ctx, "");
1230 if (num_sids == 0) {
1233 return NT_STATUS_OK;
1236 /* We need to cache indexed by the whole list of SIDs, the aliases
1237 * resulting might come from any of the SIDs. */
1239 for (i=0; i<num_sids; i++) {
1240 sidlist = talloc_asprintf(mem_ctx, "%s/%s", sidlist,
1241 sid_string_static(&sids[i]));
1242 if (sidlist == NULL)
1243 return NT_STATUS_NO_MEMORY;
1246 centry = wcache_fetch(cache, domain, "UA%s", sidlist);
1251 *num_aliases = centry_uint32(centry);
1254 (*alias_rids) = TALLOC_ARRAY(mem_ctx, uint32, *num_aliases);
1256 if ((*num_aliases != 0) && ((*alias_rids) == NULL))
1257 return NT_STATUS_NO_MEMORY;
1259 for (i=0; i<(*num_aliases); i++)
1260 (*alias_rids)[i] = centry_uint32(centry);
1262 status = centry->status;
1264 DEBUG(10,("lookup_useraliases: [Cached] - cached info for domain %s "
1265 "status %s\n", domain->name,
1266 get_friendly_nt_error_msg(status)));
1268 centry_free(centry);
1273 (*alias_rids) = NULL;
1275 if (!NT_STATUS_IS_OK(domain->last_status))
1276 return domain->last_status;
1278 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info "
1279 "for domain %s\n", domain->name ));
1281 status = domain->backend->lookup_useraliases(domain, mem_ctx,
1283 num_aliases, alias_rids);
1286 refresh_sequence_number(domain, False);
1287 centry = centry_start(domain, status);
1290 centry_put_uint32(centry, *num_aliases);
1291 for (i=0; i<(*num_aliases); i++)
1292 centry_put_uint32(centry, (*alias_rids)[i]);
1293 centry_end(centry, "UA%s", sidlist);
1294 centry_free(centry);
1301 static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
1302 TALLOC_CTX *mem_ctx,
1303 const DOM_SID *group_sid, uint32 *num_names,
1304 DOM_SID **sid_mem, char ***names,
1305 uint32 **name_types)
1307 struct winbind_cache *cache = get_cache(domain);
1308 struct cache_entry *centry = NULL;
1316 centry = wcache_fetch(cache, domain, "GM/%s", sid_to_string(sid_string, group_sid));
1320 *num_names = centry_uint32(centry);
1322 if (*num_names == 0)
1325 (*sid_mem) = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_names);
1326 (*names) = TALLOC_ARRAY(mem_ctx, char *, *num_names);
1327 (*name_types) = TALLOC_ARRAY(mem_ctx, uint32, *num_names);
1329 if (! (*sid_mem) || ! (*names) || ! (*name_types)) {
1330 smb_panic("lookup_groupmem out of memory");
1333 for (i=0; i<(*num_names); i++) {
1334 centry_sid(centry, &(*sid_mem)[i]);
1335 (*names)[i] = centry_string(centry, mem_ctx);
1336 (*name_types)[i] = centry_uint32(centry);
1340 status = centry->status;
1342 DEBUG(10,("lookup_groupmem: [Cached] - cached info for domain %s status %s\n",
1343 domain->name, get_friendly_nt_error_msg(status) ));
1345 centry_free(centry);
1352 (*name_types) = NULL;
1354 /* Return status value returned by seq number check */
1356 if (!NT_STATUS_IS_OK(domain->last_status))
1357 return domain->last_status;
1359 DEBUG(10,("lookup_groupmem: [Cached] - doing backend query for info for domain %s\n",
1362 status = domain->backend->lookup_groupmem(domain, mem_ctx, group_sid, num_names,
1363 sid_mem, names, name_types);
1366 refresh_sequence_number(domain, False);
1367 centry = centry_start(domain, status);
1370 centry_put_uint32(centry, *num_names);
1371 for (i=0; i<(*num_names); i++) {
1372 centry_put_sid(centry, &(*sid_mem)[i]);
1373 centry_put_string(centry, (*names)[i]);
1374 centry_put_uint32(centry, (*name_types)[i]);
1376 centry_end(centry, "GM/%s", sid_to_string(sid_string, group_sid));
1377 centry_free(centry);
1383 /* find the sequence number for a domain */
1384 static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
1386 refresh_sequence_number(domain, False);
1388 *seq = domain->sequence_number;
1390 return NT_STATUS_OK;
1393 /* enumerate trusted domains */
1394 static NTSTATUS trusted_domains(struct winbindd_domain *domain,
1395 TALLOC_CTX *mem_ctx,
1396 uint32 *num_domains,
1403 DEBUG(10,("trusted_domains: [Cached] - doing backend query for info for domain %s\n",
1406 /* we don't cache this call */
1407 return domain->backend->trusted_domains(domain, mem_ctx, num_domains,
1408 names, alt_names, dom_sids);
1411 /* Invalidate cached user and group lists coherently */
1413 static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
1416 if (strncmp(kbuf.dptr, "UL/", 3) == 0 ||
1417 strncmp(kbuf.dptr, "GL/", 3) == 0)
1418 tdb_delete(the_tdb, kbuf);
1423 /* Invalidate the getpwnam and getgroups entries for a winbindd domain */
1425 void wcache_invalidate_samlogon(struct winbindd_domain *domain,
1426 NET_USER_INFO_3 *info3)
1428 struct winbind_cache *cache;
1433 cache = get_cache(domain);
1434 netsamlogon_clear_cached_user(cache->tdb, info3);
1437 void wcache_invalidate_cache(void)
1439 struct winbindd_domain *domain;
1441 for (domain = domain_list(); domain; domain = domain->next) {
1442 struct winbind_cache *cache = get_cache(domain);
1444 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
1445 "entries for %s\n", domain->name));
1447 tdb_traverse(cache->tdb, traverse_fn, NULL);
1451 /* the ADS backend methods are exposed via this structure */
1452 struct winbindd_methods cache_methods = {
1467 static BOOL init_wcache(void)
1469 if (wcache == NULL) {
1470 wcache = SMB_XMALLOC_P(struct winbind_cache);
1471 ZERO_STRUCTP(wcache);
1474 if (wcache->tdb != NULL)
1477 wcache->tdb = tdb_open_log(lock_path("winbindd_cache.tdb"), 5000,
1478 TDB_CLEAR_IF_FIRST, O_RDWR|O_CREAT, 0600);
1480 if (wcache->tdb == NULL) {
1481 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
1488 void cache_store_response(pid_t pid, struct winbindd_response *response)
1495 DEBUG(10, ("Storing response for pid %d, len %d\n",
1496 pid, response->length));
1498 fstr_sprintf(key_str, "DR/%d", pid);
1499 if (tdb_store(wcache->tdb, string_tdb_data(key_str),
1500 make_tdb_data((void *)response, sizeof(*response)),
1504 if (response->length == sizeof(*response))
1507 /* There's extra data */
1509 DEBUG(10, ("Storing extra data: len=%d\n",
1510 (int)(response->length - sizeof(*response))));
1512 fstr_sprintf(key_str, "DE/%d", pid);
1513 if (tdb_store(wcache->tdb, string_tdb_data(key_str),
1514 make_tdb_data(response->extra_data,
1515 response->length - sizeof(*response)),
1519 /* We could not store the extra data, make sure the tdb does not
1520 * contain a main record with wrong dangling extra data */
1522 fstr_sprintf(key_str, "DR/%d", pid);
1523 tdb_delete(wcache->tdb, string_tdb_data(key_str));
1528 BOOL cache_retrieve_response(pid_t pid, struct winbindd_response * response)
1536 DEBUG(10, ("Retrieving response for pid %d\n", pid));
1538 fstr_sprintf(key_str, "DR/%d", pid);
1539 data = tdb_fetch(wcache->tdb, string_tdb_data(key_str));
1541 if (data.dptr == NULL)
1544 if (data.dsize != sizeof(*response))
1547 memcpy(response, data.dptr, data.dsize);
1548 SAFE_FREE(data.dptr);
1550 if (response->length == sizeof(*response)) {
1551 response->extra_data = NULL;
1555 /* There's extra data */
1557 DEBUG(10, ("Retrieving extra data length=%d\n",
1558 (int)(response->length - sizeof(*response))));
1560 fstr_sprintf(key_str, "DE/%d", pid);
1561 data = tdb_fetch(wcache->tdb, string_tdb_data(key_str));
1563 if (data.dptr == NULL) {
1564 DEBUG(0, ("Did not find extra data\n"));
1568 if (data.dsize != (response->length - sizeof(*response))) {
1569 DEBUG(0, ("Invalid extra data length: %d\n", (int)data.dsize));
1570 SAFE_FREE(data.dptr);
1574 dump_data(11, data.dptr, data.dsize);
1576 response->extra_data = data.dptr;
1580 BOOL lookup_cached_sid(TALLOC_CTX *mem_ctx, const DOM_SID *sid,
1581 const char **domain_name, const char **name,
1582 enum SID_NAME_USE *type)
1584 struct winbindd_domain *domain;
1585 struct winbind_cache *cache;
1586 struct cache_entry *centry = NULL;
1589 domain = find_lookup_domain_from_sid(sid);
1590 if (domain == NULL) {
1594 cache = get_cache(domain);
1596 if (cache->tdb == NULL) {
1600 centry = wcache_fetch(cache, domain, "SN/%s", sid_string_static(sid));
1601 if (centry == NULL) {
1605 if (NT_STATUS_IS_OK(centry->status)) {
1606 *type = (enum SID_NAME_USE)centry_uint32(centry);
1607 *domain_name = centry_string(centry, mem_ctx);
1608 *name = centry_string(centry, mem_ctx);
1611 status = centry->status;
1612 centry_free(centry);
1613 return NT_STATUS_IS_OK(status);
1616 void cache_sid2name(struct winbindd_domain *domain, const DOM_SID *sid,
1617 const char *domain_name, const char *name,
1618 enum SID_NAME_USE type)
1620 wcache_save_sid_to_name(domain, NT_STATUS_OK, sid, domain_name,