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 if (NT_STATUS_IS_OK(status)) {
995 strupper_m(CONST_DISCARD(char *,domain_name));
996 strlower_m(CONST_DISCARD(char *,name));
997 wcache_save_sid_to_name(domain, status, sid, domain_name, name, *type);
1003 /* convert a sid to a user or group name. The sid is guaranteed to be in the domain
1005 static NTSTATUS sid_to_name(struct winbindd_domain *domain,
1006 TALLOC_CTX *mem_ctx,
1010 enum SID_NAME_USE *type)
1012 struct winbind_cache *cache = get_cache(domain);
1013 struct cache_entry *centry = NULL;
1020 centry = wcache_fetch(cache, domain, "SN/%s", sid_to_string(sid_string, sid));
1023 if (NT_STATUS_IS_OK(centry->status)) {
1024 *type = (enum SID_NAME_USE)centry_uint32(centry);
1025 *domain_name = centry_string(centry, mem_ctx);
1026 *name = centry_string(centry, mem_ctx);
1028 status = centry->status;
1030 DEBUG(10,("sid_to_name: [Cached] - cached name for domain %s status %s\n",
1031 domain->name, get_friendly_nt_error_msg(status) ));
1033 centry_free(centry);
1038 *domain_name = NULL;
1040 /* If the seq number check indicated that there is a problem
1041 * with this DC, then return that status... except for
1042 * access_denied. This is special because the dc may be in
1043 * "restrict anonymous = 1" mode, in which case it will deny
1044 * most unauthenticated operations, but *will* allow the LSA
1045 * sid-to-name that we try as a fallback. */
1047 if (!(NT_STATUS_IS_OK(domain->last_status)
1048 || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1049 return domain->last_status;
1051 DEBUG(10,("sid_to_name: [Cached] - doing backend query for name for domain %s\n",
1054 status = domain->backend->sid_to_name(domain, mem_ctx, sid, domain_name, name, type);
1057 refresh_sequence_number(domain, False);
1058 wcache_save_sid_to_name(domain, status, sid, *domain_name, *name, *type);
1060 /* We can't save the name to sid mapping here, as with sid history a
1061 * later name2sid would give the wrong sid. */
1066 /* Lookup user information from a rid */
1067 static NTSTATUS query_user(struct winbindd_domain *domain,
1068 TALLOC_CTX *mem_ctx,
1069 const DOM_SID *user_sid,
1070 WINBIND_USERINFO *info)
1072 struct winbind_cache *cache = get_cache(domain);
1073 struct cache_entry *centry = NULL;
1079 centry = wcache_fetch(cache, domain, "U/%s", sid_string_static(user_sid));
1081 /* If we have an access denied cache entry and a cached info3 in the
1082 samlogon cache then do a query. This will force the rpc back end
1083 to return the info3 data. */
1085 if (NT_STATUS_V(domain->last_status) == NT_STATUS_V(NT_STATUS_ACCESS_DENIED) &&
1086 netsamlogon_cache_have(user_sid)) {
1087 DEBUG(10, ("query_user: cached access denied and have cached info3\n"));
1088 domain->last_status = NT_STATUS_OK;
1089 centry_free(centry);
1096 info->acct_name = centry_string(centry, mem_ctx);
1097 info->full_name = centry_string(centry, mem_ctx);
1098 info->homedir = centry_string(centry, mem_ctx);
1099 info->shell = centry_string(centry, mem_ctx);
1100 centry_sid(centry, &info->user_sid);
1101 centry_sid(centry, &info->group_sid);
1102 status = centry->status;
1104 DEBUG(10,("query_user: [Cached] - cached info for domain %s status %s\n",
1105 domain->name, get_friendly_nt_error_msg(status) ));
1107 centry_free(centry);
1113 /* Return status value returned by seq number check */
1115 if (!NT_STATUS_IS_OK(domain->last_status))
1116 return domain->last_status;
1118 DEBUG(10,("sid_to_name: [Cached] - doing backend query for info for domain %s\n",
1121 status = domain->backend->query_user(domain, mem_ctx, user_sid, info);
1124 refresh_sequence_number(domain, False);
1125 wcache_save_user(domain, status, info);
1131 /* Lookup groups a user is a member of. */
1132 static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
1133 TALLOC_CTX *mem_ctx,
1134 const DOM_SID *user_sid,
1135 uint32 *num_groups, DOM_SID **user_gids)
1137 struct winbind_cache *cache = get_cache(domain);
1138 struct cache_entry *centry = NULL;
1146 centry = wcache_fetch(cache, domain, "UG/%s", sid_to_string(sid_string, user_sid));
1148 /* If we have an access denied cache entry and a cached info3 in the
1149 samlogon cache then do a query. This will force the rpc back end
1150 to return the info3 data. */
1152 if (NT_STATUS_V(domain->last_status) == NT_STATUS_V(NT_STATUS_ACCESS_DENIED) &&
1153 netsamlogon_cache_have(user_sid)) {
1154 DEBUG(10, ("lookup_usergroups: cached access denied and have cached info3\n"));
1155 domain->last_status = NT_STATUS_OK;
1156 centry_free(centry);
1163 *num_groups = centry_uint32(centry);
1165 if (*num_groups == 0)
1168 (*user_gids) = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_groups);
1170 smb_panic("lookup_usergroups out of memory");
1171 for (i=0; i<(*num_groups); i++) {
1172 centry_sid(centry, &(*user_gids)[i]);
1176 status = centry->status;
1178 DEBUG(10,("lookup_usergroups: [Cached] - cached info for domain %s status %s\n",
1179 domain->name, get_friendly_nt_error_msg(status) ));
1181 centry_free(centry);
1186 (*user_gids) = NULL;
1188 /* Return status value returned by seq number check */
1190 if (!NT_STATUS_IS_OK(domain->last_status))
1191 return domain->last_status;
1193 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info for domain %s\n",
1196 status = domain->backend->lookup_usergroups(domain, mem_ctx, user_sid, num_groups, user_gids);
1199 refresh_sequence_number(domain, False);
1200 centry = centry_start(domain, status);
1203 centry_put_uint32(centry, *num_groups);
1204 for (i=0; i<(*num_groups); i++) {
1205 centry_put_sid(centry, &(*user_gids)[i]);
1207 centry_end(centry, "UG/%s", sid_to_string(sid_string, user_sid));
1208 centry_free(centry);
1214 static NTSTATUS lookup_useraliases(struct winbindd_domain *domain,
1215 TALLOC_CTX *mem_ctx,
1216 uint32 num_sids, const DOM_SID *sids,
1217 uint32 *num_aliases, uint32 **alias_rids)
1219 struct winbind_cache *cache = get_cache(domain);
1220 struct cache_entry *centry = NULL;
1222 char *sidlist = talloc_strdup(mem_ctx, "");
1228 if (num_sids == 0) {
1231 return NT_STATUS_OK;
1234 /* We need to cache indexed by the whole list of SIDs, the aliases
1235 * resulting might come from any of the SIDs. */
1237 for (i=0; i<num_sids; i++) {
1238 sidlist = talloc_asprintf(mem_ctx, "%s/%s", sidlist,
1239 sid_string_static(&sids[i]));
1240 if (sidlist == NULL)
1241 return NT_STATUS_NO_MEMORY;
1244 centry = wcache_fetch(cache, domain, "UA%s", sidlist);
1249 *num_aliases = centry_uint32(centry);
1252 (*alias_rids) = TALLOC_ARRAY(mem_ctx, uint32, *num_aliases);
1254 if ((*num_aliases != 0) && ((*alias_rids) == NULL))
1255 return NT_STATUS_NO_MEMORY;
1257 for (i=0; i<(*num_aliases); i++)
1258 (*alias_rids)[i] = centry_uint32(centry);
1260 status = centry->status;
1262 DEBUG(10,("lookup_useraliases: [Cached] - cached info for domain %s "
1263 "status %s\n", domain->name,
1264 get_friendly_nt_error_msg(status)));
1266 centry_free(centry);
1271 (*alias_rids) = NULL;
1273 if (!NT_STATUS_IS_OK(domain->last_status))
1274 return domain->last_status;
1276 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info "
1277 "for domain %s\n", domain->name ));
1279 status = domain->backend->lookup_useraliases(domain, mem_ctx,
1281 num_aliases, alias_rids);
1284 refresh_sequence_number(domain, False);
1285 centry = centry_start(domain, status);
1288 centry_put_uint32(centry, *num_aliases);
1289 for (i=0; i<(*num_aliases); i++)
1290 centry_put_uint32(centry, (*alias_rids)[i]);
1291 centry_end(centry, "UA%s", sidlist);
1292 centry_free(centry);
1299 static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
1300 TALLOC_CTX *mem_ctx,
1301 const DOM_SID *group_sid, uint32 *num_names,
1302 DOM_SID **sid_mem, char ***names,
1303 uint32 **name_types)
1305 struct winbind_cache *cache = get_cache(domain);
1306 struct cache_entry *centry = NULL;
1314 centry = wcache_fetch(cache, domain, "GM/%s", sid_to_string(sid_string, group_sid));
1318 *num_names = centry_uint32(centry);
1320 if (*num_names == 0)
1323 (*sid_mem) = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_names);
1324 (*names) = TALLOC_ARRAY(mem_ctx, char *, *num_names);
1325 (*name_types) = TALLOC_ARRAY(mem_ctx, uint32, *num_names);
1327 if (! (*sid_mem) || ! (*names) || ! (*name_types)) {
1328 smb_panic("lookup_groupmem out of memory");
1331 for (i=0; i<(*num_names); i++) {
1332 centry_sid(centry, &(*sid_mem)[i]);
1333 (*names)[i] = centry_string(centry, mem_ctx);
1334 (*name_types)[i] = centry_uint32(centry);
1338 status = centry->status;
1340 DEBUG(10,("lookup_groupmem: [Cached] - cached info for domain %s status %s\n",
1341 domain->name, get_friendly_nt_error_msg(status) ));
1343 centry_free(centry);
1350 (*name_types) = NULL;
1352 /* Return status value returned by seq number check */
1354 if (!NT_STATUS_IS_OK(domain->last_status))
1355 return domain->last_status;
1357 DEBUG(10,("lookup_groupmem: [Cached] - doing backend query for info for domain %s\n",
1360 status = domain->backend->lookup_groupmem(domain, mem_ctx, group_sid, num_names,
1361 sid_mem, names, name_types);
1364 refresh_sequence_number(domain, False);
1365 centry = centry_start(domain, status);
1368 centry_put_uint32(centry, *num_names);
1369 for (i=0; i<(*num_names); i++) {
1370 centry_put_sid(centry, &(*sid_mem)[i]);
1371 centry_put_string(centry, (*names)[i]);
1372 centry_put_uint32(centry, (*name_types)[i]);
1374 centry_end(centry, "GM/%s", sid_to_string(sid_string, group_sid));
1375 centry_free(centry);
1381 /* find the sequence number for a domain */
1382 static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
1384 refresh_sequence_number(domain, False);
1386 *seq = domain->sequence_number;
1388 return NT_STATUS_OK;
1391 /* enumerate trusted domains */
1392 static NTSTATUS trusted_domains(struct winbindd_domain *domain,
1393 TALLOC_CTX *mem_ctx,
1394 uint32 *num_domains,
1401 DEBUG(10,("trusted_domains: [Cached] - doing backend query for info for domain %s\n",
1404 /* we don't cache this call */
1405 return domain->backend->trusted_domains(domain, mem_ctx, num_domains,
1406 names, alt_names, dom_sids);
1409 /* Invalidate cached user and group lists coherently */
1411 static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
1414 if (strncmp(kbuf.dptr, "UL/", 3) == 0 ||
1415 strncmp(kbuf.dptr, "GL/", 3) == 0)
1416 tdb_delete(the_tdb, kbuf);
1421 /* Invalidate the getpwnam and getgroups entries for a winbindd domain */
1423 void wcache_invalidate_samlogon(struct winbindd_domain *domain,
1424 NET_USER_INFO_3 *info3)
1426 struct winbind_cache *cache;
1431 cache = get_cache(domain);
1432 netsamlogon_clear_cached_user(cache->tdb, info3);
1435 void wcache_invalidate_cache(void)
1437 struct winbindd_domain *domain;
1439 for (domain = domain_list(); domain; domain = domain->next) {
1440 struct winbind_cache *cache = get_cache(domain);
1442 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
1443 "entries for %s\n", domain->name));
1445 tdb_traverse(cache->tdb, traverse_fn, NULL);
1449 /* the ADS backend methods are exposed via this structure */
1450 struct winbindd_methods cache_methods = {
1465 static BOOL init_wcache(void)
1467 if (wcache == NULL) {
1468 wcache = SMB_XMALLOC_P(struct winbind_cache);
1469 ZERO_STRUCTP(wcache);
1472 if (wcache->tdb != NULL)
1475 wcache->tdb = tdb_open_log(lock_path("winbindd_cache.tdb"), 5000,
1476 TDB_CLEAR_IF_FIRST, O_RDWR|O_CREAT, 0600);
1478 if (wcache->tdb == NULL) {
1479 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
1486 void cache_store_response(pid_t pid, struct winbindd_response *response)
1493 DEBUG(10, ("Storing response for pid %d, len %d\n",
1494 pid, response->length));
1496 fstr_sprintf(key_str, "DR/%d", pid);
1497 if (tdb_store(wcache->tdb, string_tdb_data(key_str),
1498 make_tdb_data((void *)response, sizeof(*response)),
1502 if (response->length == sizeof(*response))
1505 /* There's extra data */
1507 DEBUG(10, ("Storing extra data: len=%d\n",
1508 (int)(response->length - sizeof(*response))));
1510 fstr_sprintf(key_str, "DE/%d", pid);
1511 if (tdb_store(wcache->tdb, string_tdb_data(key_str),
1512 make_tdb_data(response->extra_data,
1513 response->length - sizeof(*response)),
1517 /* We could not store the extra data, make sure the tdb does not
1518 * contain a main record with wrong dangling extra data */
1520 fstr_sprintf(key_str, "DR/%d", pid);
1521 tdb_delete(wcache->tdb, string_tdb_data(key_str));
1526 BOOL cache_retrieve_response(pid_t pid, struct winbindd_response * response)
1534 DEBUG(10, ("Retrieving response for pid %d\n", pid));
1536 fstr_sprintf(key_str, "DR/%d", pid);
1537 data = tdb_fetch(wcache->tdb, string_tdb_data(key_str));
1539 if (data.dptr == NULL)
1542 if (data.dsize != sizeof(*response))
1545 memcpy(response, data.dptr, data.dsize);
1546 SAFE_FREE(data.dptr);
1548 if (response->length == sizeof(*response)) {
1549 response->extra_data = NULL;
1553 /* There's extra data */
1555 DEBUG(10, ("Retrieving extra data length=%d\n",
1556 (int)(response->length - sizeof(*response))));
1558 fstr_sprintf(key_str, "DE/%d", pid);
1559 data = tdb_fetch(wcache->tdb, string_tdb_data(key_str));
1561 if (data.dptr == NULL) {
1562 DEBUG(0, ("Did not find extra data\n"));
1566 if (data.dsize != (response->length - sizeof(*response))) {
1567 DEBUG(0, ("Invalid extra data length: %d\n", (int)data.dsize));
1568 SAFE_FREE(data.dptr);
1572 dump_data(11, data.dptr, data.dsize);
1574 response->extra_data = data.dptr;
1578 BOOL lookup_cached_sid(TALLOC_CTX *mem_ctx, const DOM_SID *sid,
1579 const char **domain_name, const char **name,
1580 enum SID_NAME_USE *type)
1582 struct winbindd_domain *domain;
1583 struct winbind_cache *cache;
1584 struct cache_entry *centry = NULL;
1587 domain = find_lookup_domain_from_sid(sid);
1588 if (domain == NULL) {
1592 cache = get_cache(domain);
1594 if (cache->tdb == NULL) {
1598 centry = wcache_fetch(cache, domain, "SN/%s", sid_string_static(sid));
1599 if (centry == NULL) {
1603 if (NT_STATUS_IS_OK(centry->status)) {
1604 *type = (enum SID_NAME_USE)centry_uint32(centry);
1605 *domain_name = centry_string(centry, mem_ctx);
1606 *name = centry_string(centry, mem_ctx);
1609 status = centry->status;
1610 centry_free(centry);
1611 return NT_STATUS_IS_OK(status);
1614 void cache_sid2name(struct winbindd_domain *domain, const DOM_SID *sid,
1615 const char *domain_name, const char *name,
1616 enum SID_NAME_USE type)
1618 wcache_save_sid_to_name(domain, NT_STATUS_OK, sid, domain_name,