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 centry_sid(centry, sid);
964 status = centry->status;
966 DEBUG(10,("name_to_sid: [Cached] - cached name for domain %s status %s\n",
967 domain->name, get_friendly_nt_error_msg(status) ));
975 /* If the seq number check indicated that there is a problem
976 * with this DC, then return that status... except for
977 * access_denied. This is special because the dc may be in
978 * "restrict anonymous = 1" mode, in which case it will deny
979 * most unauthenticated operations, but *will* allow the LSA
980 * name-to-sid that we try as a fallback. */
982 if (!(NT_STATUS_IS_OK(domain->last_status)
983 || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
984 return domain->last_status;
986 DEBUG(10,("name_to_sid: [Cached] - doing backend query for name for domain %s\n",
989 status = domain->backend->name_to_sid(domain, mem_ctx, domain_name, name, sid, type);
992 wcache_save_name_to_sid(domain, status, domain_name, name, sid, *type);
994 /* We can't save the sid to name mapping as we don't know the
995 correct case of the name without looking it up */
1000 /* convert a sid to a user or group name. The sid is guaranteed to be in the domain
1002 static NTSTATUS sid_to_name(struct winbindd_domain *domain,
1003 TALLOC_CTX *mem_ctx,
1007 enum SID_NAME_USE *type)
1009 struct winbind_cache *cache = get_cache(domain);
1010 struct cache_entry *centry = NULL;
1017 centry = wcache_fetch(cache, domain, "SN/%s", sid_to_string(sid_string, sid));
1020 if (NT_STATUS_IS_OK(centry->status)) {
1021 *type = (enum SID_NAME_USE)centry_uint32(centry);
1022 *domain_name = centry_string(centry, mem_ctx);
1023 *name = centry_string(centry, mem_ctx);
1025 status = centry->status;
1027 DEBUG(10,("sid_to_name: [Cached] - cached name for domain %s status %s\n",
1028 domain->name, get_friendly_nt_error_msg(status) ));
1030 centry_free(centry);
1035 *domain_name = NULL;
1037 /* If the seq number check indicated that there is a problem
1038 * with this DC, then return that status... except for
1039 * access_denied. This is special because the dc may be in
1040 * "restrict anonymous = 1" mode, in which case it will deny
1041 * most unauthenticated operations, but *will* allow the LSA
1042 * sid-to-name that we try as a fallback. */
1044 if (!(NT_STATUS_IS_OK(domain->last_status)
1045 || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1046 return domain->last_status;
1048 DEBUG(10,("sid_to_name: [Cached] - doing backend query for name for domain %s\n",
1051 status = domain->backend->sid_to_name(domain, mem_ctx, sid, domain_name, name, type);
1054 refresh_sequence_number(domain, False);
1055 wcache_save_sid_to_name(domain, status, sid, *domain_name, *name, *type);
1057 /* We can't save the name to sid mapping here, as with sid history a
1058 * later name2sid would give the wrong sid. */
1063 /* Lookup user information from a rid */
1064 static NTSTATUS query_user(struct winbindd_domain *domain,
1065 TALLOC_CTX *mem_ctx,
1066 const DOM_SID *user_sid,
1067 WINBIND_USERINFO *info)
1069 struct winbind_cache *cache = get_cache(domain);
1070 struct cache_entry *centry = NULL;
1076 centry = wcache_fetch(cache, domain, "U/%s", sid_string_static(user_sid));
1078 /* If we have an access denied cache entry and a cached info3 in the
1079 samlogon cache then do a query. This will force the rpc back end
1080 to return the info3 data. */
1082 if (NT_STATUS_V(domain->last_status) == NT_STATUS_V(NT_STATUS_ACCESS_DENIED) &&
1083 netsamlogon_cache_have(user_sid)) {
1084 DEBUG(10, ("query_user: cached access denied and have cached info3\n"));
1085 domain->last_status = NT_STATUS_OK;
1086 centry_free(centry);
1093 info->acct_name = centry_string(centry, mem_ctx);
1094 info->full_name = centry_string(centry, mem_ctx);
1095 info->homedir = centry_string(centry, mem_ctx);
1096 info->shell = centry_string(centry, mem_ctx);
1097 centry_sid(centry, &info->user_sid);
1098 centry_sid(centry, &info->group_sid);
1099 status = centry->status;
1101 DEBUG(10,("query_user: [Cached] - cached info for domain %s status %s\n",
1102 domain->name, get_friendly_nt_error_msg(status) ));
1104 centry_free(centry);
1110 /* Return status value returned by seq number check */
1112 if (!NT_STATUS_IS_OK(domain->last_status))
1113 return domain->last_status;
1115 DEBUG(10,("sid_to_name: [Cached] - doing backend query for info for domain %s\n",
1118 status = domain->backend->query_user(domain, mem_ctx, user_sid, info);
1121 refresh_sequence_number(domain, False);
1122 wcache_save_user(domain, status, info);
1128 /* Lookup groups a user is a member of. */
1129 static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
1130 TALLOC_CTX *mem_ctx,
1131 const DOM_SID *user_sid,
1132 uint32 *num_groups, DOM_SID **user_gids)
1134 struct winbind_cache *cache = get_cache(domain);
1135 struct cache_entry *centry = NULL;
1143 centry = wcache_fetch(cache, domain, "UG/%s", sid_to_string(sid_string, user_sid));
1145 /* If we have an access denied cache entry and a cached info3 in the
1146 samlogon cache then do a query. This will force the rpc back end
1147 to return the info3 data. */
1149 if (NT_STATUS_V(domain->last_status) == NT_STATUS_V(NT_STATUS_ACCESS_DENIED) &&
1150 netsamlogon_cache_have(user_sid)) {
1151 DEBUG(10, ("lookup_usergroups: cached access denied and have cached info3\n"));
1152 domain->last_status = NT_STATUS_OK;
1153 centry_free(centry);
1160 *num_groups = centry_uint32(centry);
1162 if (*num_groups == 0)
1165 (*user_gids) = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_groups);
1167 smb_panic("lookup_usergroups out of memory");
1168 for (i=0; i<(*num_groups); i++) {
1169 centry_sid(centry, &(*user_gids)[i]);
1173 status = centry->status;
1175 DEBUG(10,("lookup_usergroups: [Cached] - cached info for domain %s status %s\n",
1176 domain->name, get_friendly_nt_error_msg(status) ));
1178 centry_free(centry);
1183 (*user_gids) = NULL;
1185 /* Return status value returned by seq number check */
1187 if (!NT_STATUS_IS_OK(domain->last_status))
1188 return domain->last_status;
1190 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info for domain %s\n",
1193 status = domain->backend->lookup_usergroups(domain, mem_ctx, user_sid, num_groups, user_gids);
1196 refresh_sequence_number(domain, False);
1197 centry = centry_start(domain, status);
1200 centry_put_uint32(centry, *num_groups);
1201 for (i=0; i<(*num_groups); i++) {
1202 centry_put_sid(centry, &(*user_gids)[i]);
1204 centry_end(centry, "UG/%s", sid_to_string(sid_string, user_sid));
1205 centry_free(centry);
1211 static NTSTATUS lookup_useraliases(struct winbindd_domain *domain,
1212 TALLOC_CTX *mem_ctx,
1213 uint32 num_sids, const DOM_SID *sids,
1214 uint32 *num_aliases, uint32 **alias_rids)
1216 struct winbind_cache *cache = get_cache(domain);
1217 struct cache_entry *centry = NULL;
1219 char *sidlist = talloc_strdup(mem_ctx, "");
1225 if (num_sids == 0) {
1228 return NT_STATUS_OK;
1231 /* We need to cache indexed by the whole list of SIDs, the aliases
1232 * resulting might come from any of the SIDs. */
1234 for (i=0; i<num_sids; i++) {
1235 sidlist = talloc_asprintf(mem_ctx, "%s/%s", sidlist,
1236 sid_string_static(&sids[i]));
1237 if (sidlist == NULL)
1238 return NT_STATUS_NO_MEMORY;
1241 centry = wcache_fetch(cache, domain, "UA%s", sidlist);
1246 *num_aliases = centry_uint32(centry);
1249 (*alias_rids) = TALLOC_ARRAY(mem_ctx, uint32, *num_aliases);
1251 if ((*num_aliases != 0) && ((*alias_rids) == NULL))
1252 return NT_STATUS_NO_MEMORY;
1254 for (i=0; i<(*num_aliases); i++)
1255 (*alias_rids)[i] = centry_uint32(centry);
1257 status = centry->status;
1259 DEBUG(10,("lookup_useraliases: [Cached] - cached info for domain %s "
1260 "status %s\n", domain->name,
1261 get_friendly_nt_error_msg(status)));
1263 centry_free(centry);
1268 (*alias_rids) = NULL;
1270 if (!NT_STATUS_IS_OK(domain->last_status))
1271 return domain->last_status;
1273 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info "
1274 "for domain %s\n", domain->name ));
1276 status = domain->backend->lookup_useraliases(domain, mem_ctx,
1278 num_aliases, alias_rids);
1281 refresh_sequence_number(domain, False);
1282 centry = centry_start(domain, status);
1285 centry_put_uint32(centry, *num_aliases);
1286 for (i=0; i<(*num_aliases); i++)
1287 centry_put_uint32(centry, (*alias_rids)[i]);
1288 centry_end(centry, "UA%s", sidlist);
1289 centry_free(centry);
1296 static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
1297 TALLOC_CTX *mem_ctx,
1298 const DOM_SID *group_sid, uint32 *num_names,
1299 DOM_SID **sid_mem, char ***names,
1300 uint32 **name_types)
1302 struct winbind_cache *cache = get_cache(domain);
1303 struct cache_entry *centry = NULL;
1311 centry = wcache_fetch(cache, domain, "GM/%s", sid_to_string(sid_string, group_sid));
1315 *num_names = centry_uint32(centry);
1317 if (*num_names == 0)
1320 (*sid_mem) = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_names);
1321 (*names) = TALLOC_ARRAY(mem_ctx, char *, *num_names);
1322 (*name_types) = TALLOC_ARRAY(mem_ctx, uint32, *num_names);
1324 if (! (*sid_mem) || ! (*names) || ! (*name_types)) {
1325 smb_panic("lookup_groupmem out of memory");
1328 for (i=0; i<(*num_names); i++) {
1329 centry_sid(centry, &(*sid_mem)[i]);
1330 (*names)[i] = centry_string(centry, mem_ctx);
1331 (*name_types)[i] = centry_uint32(centry);
1335 status = centry->status;
1337 DEBUG(10,("lookup_groupmem: [Cached] - cached info for domain %s status %s\n",
1338 domain->name, get_friendly_nt_error_msg(status) ));
1340 centry_free(centry);
1347 (*name_types) = NULL;
1349 /* Return status value returned by seq number check */
1351 if (!NT_STATUS_IS_OK(domain->last_status))
1352 return domain->last_status;
1354 DEBUG(10,("lookup_groupmem: [Cached] - doing backend query for info for domain %s\n",
1357 status = domain->backend->lookup_groupmem(domain, mem_ctx, group_sid, num_names,
1358 sid_mem, names, name_types);
1361 refresh_sequence_number(domain, False);
1362 centry = centry_start(domain, status);
1365 centry_put_uint32(centry, *num_names);
1366 for (i=0; i<(*num_names); i++) {
1367 centry_put_sid(centry, &(*sid_mem)[i]);
1368 centry_put_string(centry, (*names)[i]);
1369 centry_put_uint32(centry, (*name_types)[i]);
1371 centry_end(centry, "GM/%s", sid_to_string(sid_string, group_sid));
1372 centry_free(centry);
1378 /* find the sequence number for a domain */
1379 static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
1381 refresh_sequence_number(domain, False);
1383 *seq = domain->sequence_number;
1385 return NT_STATUS_OK;
1388 /* enumerate trusted domains */
1389 static NTSTATUS trusted_domains(struct winbindd_domain *domain,
1390 TALLOC_CTX *mem_ctx,
1391 uint32 *num_domains,
1398 DEBUG(10,("trusted_domains: [Cached] - doing backend query for info for domain %s\n",
1401 /* we don't cache this call */
1402 return domain->backend->trusted_domains(domain, mem_ctx, num_domains,
1403 names, alt_names, dom_sids);
1406 /* Invalidate cached user and group lists coherently */
1408 static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
1411 if (strncmp(kbuf.dptr, "UL/", 3) == 0 ||
1412 strncmp(kbuf.dptr, "GL/", 3) == 0)
1413 tdb_delete(the_tdb, kbuf);
1418 /* Invalidate the getpwnam and getgroups entries for a winbindd domain */
1420 void wcache_invalidate_samlogon(struct winbindd_domain *domain,
1421 NET_USER_INFO_3 *info3)
1423 struct winbind_cache *cache;
1428 cache = get_cache(domain);
1429 netsamlogon_clear_cached_user(cache->tdb, info3);
1432 void wcache_invalidate_cache(void)
1434 struct winbindd_domain *domain;
1436 for (domain = domain_list(); domain; domain = domain->next) {
1437 struct winbind_cache *cache = get_cache(domain);
1439 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
1440 "entries for %s\n", domain->name));
1442 tdb_traverse(cache->tdb, traverse_fn, NULL);
1446 /* the ADS backend methods are exposed via this structure */
1447 struct winbindd_methods cache_methods = {
1462 static BOOL init_wcache(void)
1464 if (wcache == NULL) {
1465 wcache = SMB_XMALLOC_P(struct winbind_cache);
1466 ZERO_STRUCTP(wcache);
1469 if (wcache->tdb != NULL)
1472 wcache->tdb = tdb_open_log(lock_path("winbindd_cache.tdb"), 5000,
1473 TDB_CLEAR_IF_FIRST, O_RDWR|O_CREAT, 0600);
1475 if (wcache->tdb == NULL) {
1476 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
1483 void cache_store_response(pid_t pid, struct winbindd_response *response)
1490 DEBUG(10, ("Storing response for pid %d, len %d\n",
1491 pid, response->length));
1493 fstr_sprintf(key_str, "DR/%d", pid);
1494 if (tdb_store(wcache->tdb, string_tdb_data(key_str),
1495 make_tdb_data((void *)response, sizeof(*response)),
1499 if (response->length == sizeof(*response))
1502 /* There's extra data */
1504 DEBUG(10, ("Storing extra data: len=%d\n",
1505 (int)(response->length - sizeof(*response))));
1507 fstr_sprintf(key_str, "DE/%d", pid);
1508 if (tdb_store(wcache->tdb, string_tdb_data(key_str),
1509 make_tdb_data(response->extra_data,
1510 response->length - sizeof(*response)),
1514 /* We could not store the extra data, make sure the tdb does not
1515 * contain a main record with wrong dangling extra data */
1517 fstr_sprintf(key_str, "DR/%d", pid);
1518 tdb_delete(wcache->tdb, string_tdb_data(key_str));
1523 BOOL cache_retrieve_response(pid_t pid, struct winbindd_response * response)
1531 DEBUG(10, ("Retrieving response for pid %d\n", pid));
1533 fstr_sprintf(key_str, "DR/%d", pid);
1534 data = tdb_fetch(wcache->tdb, string_tdb_data(key_str));
1536 if (data.dptr == NULL)
1539 if (data.dsize != sizeof(*response))
1542 memcpy(response, data.dptr, data.dsize);
1543 SAFE_FREE(data.dptr);
1545 if (response->length == sizeof(*response)) {
1546 response->extra_data = NULL;
1550 /* There's extra data */
1552 DEBUG(10, ("Retrieving extra data length=%d\n",
1553 (int)(response->length - sizeof(*response))));
1555 fstr_sprintf(key_str, "DE/%d", pid);
1556 data = tdb_fetch(wcache->tdb, string_tdb_data(key_str));
1558 if (data.dptr == NULL) {
1559 DEBUG(0, ("Did not find extra data\n"));
1563 if (data.dsize != (response->length - sizeof(*response))) {
1564 DEBUG(0, ("Invalid extra data length: %d\n", (int)data.dsize));
1565 SAFE_FREE(data.dptr);
1569 dump_data(11, data.dptr, data.dsize);
1571 response->extra_data = data.dptr;
1575 BOOL lookup_cached_sid(TALLOC_CTX *mem_ctx, const DOM_SID *sid,
1576 const char **domain_name, const char **name,
1577 enum SID_NAME_USE *type)
1579 struct winbindd_domain *domain;
1580 struct winbind_cache *cache;
1581 struct cache_entry *centry = NULL;
1584 domain = find_lookup_domain_from_sid(sid);
1585 if (domain == NULL) {
1589 cache = get_cache(domain);
1591 if (cache->tdb == NULL) {
1595 centry = wcache_fetch(cache, domain, "SN/%s", sid_string_static(sid));
1596 if (centry == NULL) {
1600 if (NT_STATUS_IS_OK(centry->status)) {
1601 *type = (enum SID_NAME_USE)centry_uint32(centry);
1602 *domain_name = centry_string(centry, mem_ctx);
1603 *name = centry_string(centry, mem_ctx);
1606 status = centry->status;
1607 centry_free(centry);
1608 return NT_STATUS_IS_OK(status);
1611 void cache_sid2name(struct winbindd_domain *domain, const DOM_SID *sid,
1612 const char *domain_name, const char *name,
1613 enum SID_NAME_USE type)
1615 wcache_save_sid_to_name(domain, NT_STATUS_OK, sid, domain_name,