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;
104 /* we have to know what type of domain we are dealing with first */
106 if ( !domain->initialized )
107 set_dc_type_and_flags( domain );
109 if (!domain->backend) {
110 extern struct winbindd_methods reconnect_methods;
111 switch (lp_security()) {
114 extern struct winbindd_methods ads_methods;
115 /* always obey the lp_security parameter for our domain */
116 if (domain->primary) {
117 domain->backend = &ads_methods;
121 /* only use ADS for native modes at the momment.
122 The problem is the correct detection of mixed
123 mode domains from NT4 BDC's --jerry */
125 if ( domain->native_mode ) {
126 DEBUG(5,("get_cache: Setting ADS methods for domain %s\n",
128 domain->backend = &ads_methods;
136 DEBUG(5,("get_cache: Setting MS-RPC methods for domain %s\n",
138 domain->backend = &reconnect_methods;
145 ret = SMB_XMALLOC_P(struct winbind_cache);
149 wcache_flush_cache();
155 free a centry structure
157 static void centry_free(struct cache_entry *centry)
161 SAFE_FREE(centry->data);
166 pull a uint32 from a cache entry
168 static uint32 centry_uint32(struct cache_entry *centry)
171 if (centry->len - centry->ofs < 4) {
172 DEBUG(0,("centry corruption? needed 4 bytes, have %d\n",
173 centry->len - centry->ofs));
174 smb_panic("centry_uint32");
176 ret = IVAL(centry->data, centry->ofs);
182 pull a uint8 from a cache entry
184 static uint8 centry_uint8(struct cache_entry *centry)
187 if (centry->len - centry->ofs < 1) {
188 DEBUG(0,("centry corruption? needed 1 bytes, have %d\n",
189 centry->len - centry->ofs));
190 smb_panic("centry_uint32");
192 ret = CVAL(centry->data, centry->ofs);
197 /* pull a string from a cache entry, using the supplied
200 static char *centry_string(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
205 len = centry_uint8(centry);
208 /* a deliberate NULL string */
212 if (centry->len - centry->ofs < len) {
213 DEBUG(0,("centry corruption? needed %d bytes, have %d\n",
214 len, centry->len - centry->ofs));
215 smb_panic("centry_string");
219 ret = TALLOC(mem_ctx, len+1);
221 ret = SMB_MALLOC(len+1);
223 smb_panic("centry_string out of memory\n");
225 memcpy(ret,centry->data + centry->ofs, len);
231 /* pull a string from a cache entry, using the supplied
234 static BOOL centry_sid(struct cache_entry *centry, DOM_SID *sid)
237 sid_string = centry_string(centry, NULL);
238 if (!string_to_sid(sid, sid_string)) {
241 SAFE_FREE(sid_string);
245 /* the server is considered down if it can't give us a sequence number */
246 static BOOL wcache_server_down(struct winbindd_domain *domain)
253 ret = (domain->sequence_number == DOM_SEQUENCE_NONE);
256 DEBUG(10,("wcache_server_down: server for Domain %s down\n",
261 static NTSTATUS fetch_cache_seqnum( struct winbindd_domain *domain, time_t now )
268 DEBUG(10,("fetch_cache_seqnum: tdb == NULL\n"));
269 return NT_STATUS_UNSUCCESSFUL;
272 fstr_sprintf( key, "SEQNUM/%s", domain->name );
274 data = tdb_fetch_bystring( wcache->tdb, key );
275 if ( !data.dptr || data.dsize!=8 ) {
276 DEBUG(10,("fetch_cache_seqnum: invalid data size key [%s]\n", key ));
277 return NT_STATUS_UNSUCCESSFUL;
280 domain->sequence_number = IVAL(data.dptr, 0);
281 domain->last_seq_check = IVAL(data.dptr, 4);
283 SAFE_FREE(data.dptr);
285 /* have we expired? */
287 time_diff = now - domain->last_seq_check;
288 if ( time_diff > lp_winbind_cache_time() ) {
289 DEBUG(10,("fetch_cache_seqnum: timeout [%s][%u @ %u]\n",
290 domain->name, domain->sequence_number,
291 (uint32)domain->last_seq_check));
292 return NT_STATUS_UNSUCCESSFUL;
295 DEBUG(10,("fetch_cache_seqnum: success [%s][%u @ %u]\n",
296 domain->name, domain->sequence_number,
297 (uint32)domain->last_seq_check));
302 static NTSTATUS store_cache_seqnum( struct winbindd_domain *domain )
309 DEBUG(10,("store_cache_seqnum: tdb == NULL\n"));
310 return NT_STATUS_UNSUCCESSFUL;
313 fstr_sprintf( key_str, "SEQNUM/%s", domain->name );
315 key.dsize = strlen(key_str)+1;
317 SIVAL(buf, 0, domain->sequence_number);
318 SIVAL(buf, 4, domain->last_seq_check);
322 if ( tdb_store( wcache->tdb, key, data, TDB_REPLACE) == -1 ) {
323 DEBUG(10,("store_cache_seqnum: tdb_store fail key [%s]\n", key_str ));
324 return NT_STATUS_UNSUCCESSFUL;
327 DEBUG(10,("store_cache_seqnum: success [%s][%u @ %u]\n",
328 domain->name, domain->sequence_number,
329 (uint32)domain->last_seq_check));
335 refresh the domain sequence number. If force is True
336 then always refresh it, no matter how recently we fetched it
339 static void refresh_sequence_number(struct winbindd_domain *domain, BOOL force)
343 time_t t = time(NULL);
344 unsigned cache_time = lp_winbind_cache_time();
348 #if 0 /* JERRY -- disable as the default cache time is now 5 minutes */
349 /* trying to reconnect is expensive, don't do it too often */
350 if (domain->sequence_number == DOM_SEQUENCE_NONE) {
355 time_diff = t - domain->last_seq_check;
357 /* see if we have to refetch the domain sequence number */
358 if (!force && (time_diff < cache_time)) {
359 DEBUG(10, ("refresh_sequence_number: %s time ok\n", domain->name));
363 /* try to get the sequence number from the tdb cache first */
364 /* this will update the timestamp as well */
366 status = fetch_cache_seqnum( domain, t );
367 if ( NT_STATUS_IS_OK(status) )
370 /* important! make sure that we know if this is a native
371 mode domain or not */
373 status = domain->backend->sequence_number(domain, &domain->sequence_number);
375 if (!NT_STATUS_IS_OK(status)) {
376 domain->sequence_number = DOM_SEQUENCE_NONE;
379 domain->last_status = status;
380 domain->last_seq_check = time(NULL);
382 /* save the new sequence number ni the cache */
383 store_cache_seqnum( domain );
386 DEBUG(10, ("refresh_sequence_number: %s seq number is now %d\n",
387 domain->name, domain->sequence_number));
393 decide if a cache entry has expired
395 static BOOL centry_expired(struct winbindd_domain *domain, const char *keystr, struct cache_entry *centry)
397 /* if the server is OK and our cache entry came from when it was down then
398 the entry is invalid */
399 if (domain->sequence_number != DOM_SEQUENCE_NONE &&
400 centry->sequence_number == DOM_SEQUENCE_NONE) {
401 DEBUG(10,("centry_expired: Key %s for domain %s invalid sequence.\n",
402 keystr, domain->name ));
406 /* if the server is down or the cache entry is not older than the
407 current sequence number then it is OK */
408 if (wcache_server_down(domain) ||
409 centry->sequence_number == domain->sequence_number) {
410 DEBUG(10,("centry_expired: Key %s for domain %s is good.\n",
411 keystr, domain->name ));
415 DEBUG(10,("centry_expired: Key %s for domain %s expired\n",
416 keystr, domain->name ));
423 fetch an entry from the cache, with a varargs key. auto-fetch the sequence
424 number and return status
426 static struct cache_entry *wcache_fetch(struct winbind_cache *cache,
427 struct winbindd_domain *domain,
428 const char *format, ...) PRINTF_ATTRIBUTE(3,4);
429 static struct cache_entry *wcache_fetch(struct winbind_cache *cache,
430 struct winbindd_domain *domain,
431 const char *format, ...)
436 struct cache_entry *centry;
439 refresh_sequence_number(domain, False);
441 va_start(ap, format);
442 smb_xvasprintf(&kstr, format, ap);
446 key.dsize = strlen(kstr);
447 data = tdb_fetch(wcache->tdb, key);
454 centry = SMB_XMALLOC_P(struct cache_entry);
455 centry->data = (unsigned char *)data.dptr;
456 centry->len = data.dsize;
459 if (centry->len < 8) {
460 /* huh? corrupt cache? */
461 DEBUG(10,("wcache_fetch: Corrupt cache for key %s domain %s (len < 8) ?\n",
462 kstr, domain->name ));
468 centry->status = NT_STATUS(centry_uint32(centry));
469 centry->sequence_number = centry_uint32(centry);
471 if (centry_expired(domain, kstr, centry)) {
473 DEBUG(10,("wcache_fetch: entry %s expired for domain %s\n",
474 kstr, domain->name ));
481 DEBUG(10,("wcache_fetch: returning entry %s for domain %s\n",
482 kstr, domain->name ));
489 make sure we have at least len bytes available in a centry
491 static void centry_expand(struct cache_entry *centry, uint32 len)
494 if (centry->len - centry->ofs >= len)
497 p = SMB_REALLOC(centry->data, centry->len);
499 DEBUG(0,("out of memory: needed %d bytes in centry_expand\n", centry->len));
500 smb_panic("out of memory in centry_expand");
506 push a uint32 into a centry
508 static void centry_put_uint32(struct cache_entry *centry, uint32 v)
510 centry_expand(centry, 4);
511 SIVAL(centry->data, centry->ofs, v);
516 push a uint8 into a centry
518 static void centry_put_uint8(struct cache_entry *centry, uint8 v)
520 centry_expand(centry, 1);
521 SCVAL(centry->data, centry->ofs, v);
526 push a string into a centry
528 static void centry_put_string(struct cache_entry *centry, const char *s)
533 /* null strings are marked as len 0xFFFF */
534 centry_put_uint8(centry, 0xFF);
539 /* can't handle more than 254 char strings. Truncating is probably best */
542 centry_put_uint8(centry, len);
543 centry_expand(centry, len);
544 memcpy(centry->data + centry->ofs, s, len);
548 static void centry_put_sid(struct cache_entry *centry, const DOM_SID *sid)
551 centry_put_string(centry, sid_to_string(sid_string, sid));
555 start a centry for output. When finished, call centry_end()
557 struct cache_entry *centry_start(struct winbindd_domain *domain, NTSTATUS status)
559 struct cache_entry *centry;
564 centry = SMB_XMALLOC_P(struct cache_entry);
566 centry->len = 8192; /* reasonable default */
567 centry->data = SMB_XMALLOC_ARRAY(char, centry->len);
569 centry->sequence_number = domain->sequence_number;
570 centry_put_uint32(centry, NT_STATUS_V(status));
571 centry_put_uint32(centry, centry->sequence_number);
576 finish a centry and write it to the tdb
578 static void centry_end(struct cache_entry *centry, const char *format, ...) PRINTF_ATTRIBUTE(2,3);
579 static void centry_end(struct cache_entry *centry, const char *format, ...)
585 va_start(ap, format);
586 smb_xvasprintf(&kstr, format, ap);
590 key.dsize = strlen(kstr);
591 data.dptr = (char *)centry->data;
592 data.dsize = centry->ofs;
594 tdb_store(wcache->tdb, key, data, TDB_REPLACE);
598 static void wcache_save_name_to_sid(struct winbindd_domain *domain,
599 NTSTATUS status, const char *domain_name,
600 const char *name, const DOM_SID *sid,
601 enum SID_NAME_USE type)
603 struct cache_entry *centry;
606 centry = centry_start(domain, status);
609 centry_put_uint32(centry, type);
610 centry_put_sid(centry, sid);
611 fstrcpy(uname, name);
613 centry_end(centry, "NS/%s/%s", domain_name, uname);
614 DEBUG(10,("wcache_save_name_to_sid: %s -> %s\n", uname,
615 sid_string_static(sid)));
619 static void wcache_save_sid_to_name(struct winbindd_domain *domain, NTSTATUS status,
620 const DOM_SID *sid, const char *domain_name, const char *name, enum SID_NAME_USE type)
622 struct cache_entry *centry;
625 centry = centry_start(domain, status);
628 if (NT_STATUS_IS_OK(status)) {
629 centry_put_uint32(centry, type);
630 centry_put_string(centry, domain_name);
631 centry_put_string(centry, name);
633 centry_end(centry, "SN/%s", sid_to_string(sid_string, sid));
634 DEBUG(10,("wcache_save_sid_to_name: %s -> %s\n", sid_string, name));
639 static void wcache_save_user(struct winbindd_domain *domain, NTSTATUS status, WINBIND_USERINFO *info)
641 struct cache_entry *centry;
644 centry = centry_start(domain, status);
647 centry_put_string(centry, info->acct_name);
648 centry_put_string(centry, info->full_name);
649 centry_put_string(centry, info->homedir);
650 centry_put_string(centry, info->shell);
651 centry_put_sid(centry, &info->user_sid);
652 centry_put_sid(centry, &info->group_sid);
653 centry_end(centry, "U/%s", sid_to_string(sid_string, &info->user_sid));
654 DEBUG(10,("wcache_save_user: %s (acct_name %s)\n", sid_string, info->acct_name));
659 /* Query display info. This is the basic user list fn */
660 static NTSTATUS query_user_list(struct winbindd_domain *domain,
663 WINBIND_USERINFO **info)
665 struct winbind_cache *cache = get_cache(domain);
666 struct cache_entry *centry = NULL;
668 unsigned int i, retry;
673 centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
677 *num_entries = centry_uint32(centry);
679 if (*num_entries == 0)
682 (*info) = TALLOC_ARRAY(mem_ctx, WINBIND_USERINFO, *num_entries);
684 smb_panic("query_user_list out of memory");
685 for (i=0; i<(*num_entries); i++) {
686 (*info)[i].acct_name = centry_string(centry, mem_ctx);
687 (*info)[i].full_name = centry_string(centry, mem_ctx);
688 (*info)[i].homedir = centry_string(centry, mem_ctx);
689 (*info)[i].shell = centry_string(centry, mem_ctx);
690 centry_sid(centry, &(*info)[i].user_sid);
691 centry_sid(centry, &(*info)[i].group_sid);
695 status = centry->status;
697 DEBUG(10,("query_user_list: [Cached] - cached list for domain %s status %s\n",
698 domain->name, get_friendly_nt_error_msg(status) ));
707 /* Return status value returned by seq number check */
709 if (!NT_STATUS_IS_OK(domain->last_status))
710 return domain->last_status;
712 /* Put the query_user_list() in a retry loop. There appears to be
713 * some bug either with Windows 2000 or Samba's handling of large
714 * rpc replies. This manifests itself as sudden disconnection
715 * at a random point in the enumeration of a large (60k) user list.
716 * The retry loop simply tries the operation again. )-: It's not
717 * pretty but an acceptable workaround until we work out what the
718 * real problem is. */
723 DEBUG(10,("query_user_list: [Cached] - doing backend query for list for domain %s\n",
726 status = domain->backend->query_user_list(domain, mem_ctx, num_entries, info);
727 if (!NT_STATUS_IS_OK(status))
728 DEBUG(3, ("query_user_list: returned 0x%08x, "
729 "retrying\n", NT_STATUS_V(status)));
730 if (NT_STATUS_EQUAL(status, NT_STATUS_UNSUCCESSFUL)) {
731 DEBUG(3, ("query_user_list: flushing "
732 "connection cache\n"));
733 invalidate_cm_connection(&domain->conn);
736 } while (NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_UNSUCCESSFUL) &&
740 refresh_sequence_number(domain, False);
741 centry = centry_start(domain, status);
744 centry_put_uint32(centry, *num_entries);
745 for (i=0; i<(*num_entries); i++) {
746 centry_put_string(centry, (*info)[i].acct_name);
747 centry_put_string(centry, (*info)[i].full_name);
748 centry_put_string(centry, (*info)[i].homedir);
749 centry_put_string(centry, (*info)[i].shell);
750 centry_put_sid(centry, &(*info)[i].user_sid);
751 centry_put_sid(centry, &(*info)[i].group_sid);
752 if (domain->backend->consistent) {
753 /* when the backend is consistent we can pre-prime some mappings */
754 wcache_save_name_to_sid(domain, NT_STATUS_OK,
756 (*info)[i].acct_name,
757 &(*info)[i].user_sid,
759 wcache_save_sid_to_name(domain, NT_STATUS_OK,
760 &(*info)[i].user_sid,
762 (*info)[i].acct_name,
764 wcache_save_user(domain, NT_STATUS_OK, &(*info)[i]);
767 centry_end(centry, "UL/%s", domain->name);
774 /* list all domain groups */
775 static NTSTATUS enum_dom_groups(struct winbindd_domain *domain,
778 struct acct_info **info)
780 struct winbind_cache *cache = get_cache(domain);
781 struct cache_entry *centry = NULL;
788 centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
792 *num_entries = centry_uint32(centry);
794 if (*num_entries == 0)
797 (*info) = TALLOC_ARRAY(mem_ctx, struct acct_info, *num_entries);
799 smb_panic("enum_dom_groups out of memory");
800 for (i=0; i<(*num_entries); i++) {
801 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
802 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
803 (*info)[i].rid = centry_uint32(centry);
807 status = centry->status;
809 DEBUG(10,("enum_dom_groups: [Cached] - cached list for domain %s status %s\n",
810 domain->name, get_friendly_nt_error_msg(status) ));
819 /* Return status value returned by seq number check */
821 if (!NT_STATUS_IS_OK(domain->last_status))
822 return domain->last_status;
824 DEBUG(10,("enum_dom_groups: [Cached] - doing backend query for list for domain %s\n",
827 status = domain->backend->enum_dom_groups(domain, mem_ctx, num_entries, info);
830 refresh_sequence_number(domain, False);
831 centry = centry_start(domain, status);
834 centry_put_uint32(centry, *num_entries);
835 for (i=0; i<(*num_entries); i++) {
836 centry_put_string(centry, (*info)[i].acct_name);
837 centry_put_string(centry, (*info)[i].acct_desc);
838 centry_put_uint32(centry, (*info)[i].rid);
840 centry_end(centry, "GL/%s/domain", domain->name);
847 /* list all domain groups */
848 static NTSTATUS enum_local_groups(struct winbindd_domain *domain,
851 struct acct_info **info)
853 struct winbind_cache *cache = get_cache(domain);
854 struct cache_entry *centry = NULL;
861 centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
865 *num_entries = centry_uint32(centry);
867 if (*num_entries == 0)
870 (*info) = TALLOC_ARRAY(mem_ctx, struct acct_info, *num_entries);
872 smb_panic("enum_dom_groups out of memory");
873 for (i=0; i<(*num_entries); i++) {
874 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
875 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
876 (*info)[i].rid = centry_uint32(centry);
881 /* If we are returning cached data and the domain controller
882 is down then we don't know whether the data is up to date
883 or not. Return NT_STATUS_MORE_PROCESSING_REQUIRED to
886 if (wcache_server_down(domain)) {
887 DEBUG(10, ("enum_local_groups: returning cached user list and server was down\n"));
888 status = NT_STATUS_MORE_PROCESSING_REQUIRED;
890 status = centry->status;
892 DEBUG(10,("enum_local_groups: [Cached] - cached list for domain %s status %s\n",
893 domain->name, get_friendly_nt_error_msg(status) ));
902 /* Return status value returned by seq number check */
904 if (!NT_STATUS_IS_OK(domain->last_status))
905 return domain->last_status;
907 DEBUG(10,("enum_local_groups: [Cached] - doing backend query for list for domain %s\n",
910 status = domain->backend->enum_local_groups(domain, mem_ctx, num_entries, info);
913 refresh_sequence_number(domain, False);
914 centry = centry_start(domain, status);
917 centry_put_uint32(centry, *num_entries);
918 for (i=0; i<(*num_entries); i++) {
919 centry_put_string(centry, (*info)[i].acct_name);
920 centry_put_string(centry, (*info)[i].acct_desc);
921 centry_put_uint32(centry, (*info)[i].rid);
923 centry_end(centry, "GL/%s/local", domain->name);
930 /* convert a single name to a sid in a domain */
931 static NTSTATUS name_to_sid(struct winbindd_domain *domain,
933 const char *domain_name,
936 enum SID_NAME_USE *type)
938 struct winbind_cache *cache = get_cache(domain);
939 struct cache_entry *centry = NULL;
946 fstrcpy(uname, name);
948 centry = wcache_fetch(cache, domain, "NS/%s/%s", domain_name, uname);
951 *type = (enum SID_NAME_USE)centry_uint32(centry);
952 centry_sid(centry, sid);
953 status = centry->status;
955 DEBUG(10,("name_to_sid: [Cached] - cached name for domain %s status %s\n",
956 domain->name, get_friendly_nt_error_msg(status) ));
964 /* If the seq number check indicated that there is a problem
965 * with this DC, then return that status... except for
966 * access_denied. This is special because the dc may be in
967 * "restrict anonymous = 1" mode, in which case it will deny
968 * most unauthenticated operations, but *will* allow the LSA
969 * name-to-sid that we try as a fallback. */
971 if (!(NT_STATUS_IS_OK(domain->last_status)
972 || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
973 return domain->last_status;
975 DEBUG(10,("name_to_sid: [Cached] - doing backend query for name for domain %s\n",
978 status = domain->backend->name_to_sid(domain, mem_ctx, domain_name, name, sid, type);
981 wcache_save_name_to_sid(domain, status, domain_name, name, sid, *type);
983 /* We can't save the sid to name mapping as we don't know the
984 correct case of the name without looking it up */
989 /* convert a sid to a user or group name. The sid is guaranteed to be in the domain
991 static NTSTATUS sid_to_name(struct winbindd_domain *domain,
996 enum SID_NAME_USE *type)
998 struct winbind_cache *cache = get_cache(domain);
999 struct cache_entry *centry = NULL;
1006 centry = wcache_fetch(cache, domain, "SN/%s", sid_to_string(sid_string, sid));
1009 if (NT_STATUS_IS_OK(centry->status)) {
1010 *type = (enum SID_NAME_USE)centry_uint32(centry);
1011 *domain_name = centry_string(centry, mem_ctx);
1012 *name = centry_string(centry, mem_ctx);
1014 status = centry->status;
1016 DEBUG(10,("sid_to_name: [Cached] - cached name for domain %s status %s\n",
1017 domain->name, get_friendly_nt_error_msg(status) ));
1019 centry_free(centry);
1024 *domain_name = NULL;
1026 /* If the seq number check indicated that there is a problem
1027 * with this DC, then return that status... except for
1028 * access_denied. This is special because the dc may be in
1029 * "restrict anonymous = 1" mode, in which case it will deny
1030 * most unauthenticated operations, but *will* allow the LSA
1031 * sid-to-name that we try as a fallback. */
1033 if (!(NT_STATUS_IS_OK(domain->last_status)
1034 || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1035 return domain->last_status;
1037 DEBUG(10,("sid_to_name: [Cached] - doing backend query for name for domain %s\n",
1040 status = domain->backend->sid_to_name(domain, mem_ctx, sid, domain_name, name, type);
1043 refresh_sequence_number(domain, False);
1044 wcache_save_sid_to_name(domain, status, sid, *domain_name, *name, *type);
1046 /* We can't save the name to sid mapping here, as with sid history a
1047 * later name2sid would give the wrong sid. */
1052 /* Lookup user information from a rid */
1053 static NTSTATUS query_user(struct winbindd_domain *domain,
1054 TALLOC_CTX *mem_ctx,
1055 const DOM_SID *user_sid,
1056 WINBIND_USERINFO *info)
1058 struct winbind_cache *cache = get_cache(domain);
1059 struct cache_entry *centry = NULL;
1065 centry = wcache_fetch(cache, domain, "U/%s", sid_string_static(user_sid));
1070 info->acct_name = centry_string(centry, mem_ctx);
1071 info->full_name = centry_string(centry, mem_ctx);
1072 info->homedir = centry_string(centry, mem_ctx);
1073 info->shell = centry_string(centry, mem_ctx);
1074 centry_sid(centry, &info->user_sid);
1075 centry_sid(centry, &info->group_sid);
1076 status = centry->status;
1078 DEBUG(10,("query_user: [Cached] - cached info for domain %s status %s\n",
1079 domain->name, get_friendly_nt_error_msg(status) ));
1081 centry_free(centry);
1087 /* Return status value returned by seq number check */
1089 if (!NT_STATUS_IS_OK(domain->last_status))
1090 return domain->last_status;
1092 DEBUG(10,("sid_to_name: [Cached] - doing backend query for info for domain %s\n",
1095 status = domain->backend->query_user(domain, mem_ctx, user_sid, info);
1098 refresh_sequence_number(domain, False);
1099 wcache_save_user(domain, status, info);
1105 /* Lookup groups a user is a member of. */
1106 static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
1107 TALLOC_CTX *mem_ctx,
1108 const DOM_SID *user_sid,
1109 uint32 *num_groups, DOM_SID **user_gids)
1111 struct winbind_cache *cache = get_cache(domain);
1112 struct cache_entry *centry = NULL;
1120 centry = wcache_fetch(cache, domain, "UG/%s", sid_to_string(sid_string, user_sid));
1125 *num_groups = centry_uint32(centry);
1127 if (*num_groups == 0)
1130 (*user_gids) = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_groups);
1132 smb_panic("lookup_usergroups out of memory");
1133 for (i=0; i<(*num_groups); i++) {
1134 centry_sid(centry, &(*user_gids)[i]);
1138 status = centry->status;
1140 DEBUG(10,("lookup_usergroups: [Cached] - cached info for domain %s status %s\n",
1141 domain->name, get_friendly_nt_error_msg(status) ));
1143 centry_free(centry);
1148 (*user_gids) = NULL;
1150 /* Return status value returned by seq number check */
1152 if (!NT_STATUS_IS_OK(domain->last_status))
1153 return domain->last_status;
1155 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info for domain %s\n",
1158 status = domain->backend->lookup_usergroups(domain, mem_ctx, user_sid, num_groups, user_gids);
1161 refresh_sequence_number(domain, False);
1162 centry = centry_start(domain, status);
1165 centry_put_uint32(centry, *num_groups);
1166 for (i=0; i<(*num_groups); i++) {
1167 centry_put_sid(centry, &(*user_gids)[i]);
1169 centry_end(centry, "UG/%s", sid_to_string(sid_string, user_sid));
1170 centry_free(centry);
1176 static NTSTATUS lookup_useraliases(struct winbindd_domain *domain,
1177 TALLOC_CTX *mem_ctx,
1178 uint32 num_sids, const DOM_SID *sids,
1179 uint32 *num_aliases, uint32 **alias_rids)
1181 struct winbind_cache *cache = get_cache(domain);
1182 struct cache_entry *centry = NULL;
1184 char *sidlist = talloc_strdup(mem_ctx, "");
1190 if (num_sids == 0) {
1193 return NT_STATUS_OK;
1196 /* We need to cache indexed by the whole list of SIDs, the aliases
1197 * resulting might come from any of the SIDs. */
1199 for (i=0; i<num_sids; i++) {
1200 sidlist = talloc_asprintf(mem_ctx, "%s/%s", sidlist,
1201 sid_string_static(&sids[i]));
1202 if (sidlist == NULL)
1203 return NT_STATUS_NO_MEMORY;
1206 centry = wcache_fetch(cache, domain, "UA%s", sidlist);
1211 *num_aliases = centry_uint32(centry);
1214 (*alias_rids) = TALLOC_ARRAY(mem_ctx, uint32, *num_aliases);
1216 if ((*num_aliases != 0) && ((*alias_rids) == NULL))
1217 return NT_STATUS_NO_MEMORY;
1219 for (i=0; i<(*num_aliases); i++)
1220 (*alias_rids)[i] = centry_uint32(centry);
1222 status = centry->status;
1224 DEBUG(10,("lookup_useraliases: [Cached] - cached info for domain %s "
1225 "status %s\n", domain->name,
1226 get_friendly_nt_error_msg(status)));
1228 centry_free(centry);
1233 (*alias_rids) = NULL;
1235 if (!NT_STATUS_IS_OK(domain->last_status))
1236 return domain->last_status;
1238 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info "
1239 "for domain %s\n", domain->name ));
1241 status = domain->backend->lookup_useraliases(domain, mem_ctx,
1243 num_aliases, alias_rids);
1246 refresh_sequence_number(domain, False);
1247 centry = centry_start(domain, status);
1250 centry_put_uint32(centry, *num_aliases);
1251 for (i=0; i<(*num_aliases); i++)
1252 centry_put_uint32(centry, (*alias_rids)[i]);
1253 centry_end(centry, "UA%s", sidlist);
1254 centry_free(centry);
1261 static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
1262 TALLOC_CTX *mem_ctx,
1263 const DOM_SID *group_sid, uint32 *num_names,
1264 DOM_SID **sid_mem, char ***names,
1265 uint32 **name_types)
1267 struct winbind_cache *cache = get_cache(domain);
1268 struct cache_entry *centry = NULL;
1276 centry = wcache_fetch(cache, domain, "GM/%s", sid_to_string(sid_string, group_sid));
1280 *num_names = centry_uint32(centry);
1282 if (*num_names == 0)
1285 (*sid_mem) = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_names);
1286 (*names) = TALLOC_ARRAY(mem_ctx, char *, *num_names);
1287 (*name_types) = TALLOC_ARRAY(mem_ctx, uint32, *num_names);
1289 if (! (*sid_mem) || ! (*names) || ! (*name_types)) {
1290 smb_panic("lookup_groupmem out of memory");
1293 for (i=0; i<(*num_names); i++) {
1294 centry_sid(centry, &(*sid_mem)[i]);
1295 (*names)[i] = centry_string(centry, mem_ctx);
1296 (*name_types)[i] = centry_uint32(centry);
1300 status = centry->status;
1302 DEBUG(10,("lookup_groupmem: [Cached] - cached info for domain %s status %s\n",
1303 domain->name, get_friendly_nt_error_msg(status) ));
1305 centry_free(centry);
1312 (*name_types) = NULL;
1314 /* Return status value returned by seq number check */
1316 if (!NT_STATUS_IS_OK(domain->last_status))
1317 return domain->last_status;
1319 DEBUG(10,("lookup_groupmem: [Cached] - doing backend query for info for domain %s\n",
1322 status = domain->backend->lookup_groupmem(domain, mem_ctx, group_sid, num_names,
1323 sid_mem, names, name_types);
1326 refresh_sequence_number(domain, False);
1327 centry = centry_start(domain, status);
1330 centry_put_uint32(centry, *num_names);
1331 for (i=0; i<(*num_names); i++) {
1332 centry_put_sid(centry, &(*sid_mem)[i]);
1333 centry_put_string(centry, (*names)[i]);
1334 centry_put_uint32(centry, (*name_types)[i]);
1336 centry_end(centry, "GM/%s", sid_to_string(sid_string, group_sid));
1337 centry_free(centry);
1343 /* find the sequence number for a domain */
1344 static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
1346 refresh_sequence_number(domain, False);
1348 *seq = domain->sequence_number;
1350 return NT_STATUS_OK;
1353 /* enumerate trusted domains */
1354 static NTSTATUS trusted_domains(struct winbindd_domain *domain,
1355 TALLOC_CTX *mem_ctx,
1356 uint32 *num_domains,
1363 DEBUG(10,("trusted_domains: [Cached] - doing backend query for info for domain %s\n",
1366 /* we don't cache this call */
1367 return domain->backend->trusted_domains(domain, mem_ctx, num_domains,
1368 names, alt_names, dom_sids);
1371 /* find the alternate names for the domain, if any */
1372 static NTSTATUS alternate_name(struct winbindd_domain *domain)
1376 DEBUG(10,("alternate_name: [Cached] - doing backend query for info for domain %s\n",
1379 /* we don't cache this call */
1380 return domain->backend->alternate_name(domain);
1383 /* Invalidate cached user and group lists coherently */
1385 static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
1388 if (strncmp(kbuf.dptr, "UL/", 3) == 0 ||
1389 strncmp(kbuf.dptr, "GL/", 3) == 0)
1390 tdb_delete(the_tdb, kbuf);
1395 void wcache_invalidate_cache(void)
1397 struct winbindd_domain *domain;
1399 for (domain = domain_list(); domain; domain = domain->next) {
1400 struct winbind_cache *cache = get_cache(domain);
1402 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
1403 "entries for %s\n", domain->name));
1405 tdb_traverse(cache->tdb, traverse_fn, NULL);
1409 /* the ADS backend methods are exposed via this structure */
1410 struct winbindd_methods cache_methods = {
1426 static BOOL init_wcache(void)
1428 if (wcache == NULL) {
1429 wcache = SMB_XMALLOC_P(struct winbind_cache);
1430 ZERO_STRUCTP(wcache);
1433 if (wcache->tdb != NULL)
1436 wcache->tdb = tdb_open_log(lock_path("winbindd_cache.tdb"), 5000,
1437 TDB_CLEAR_IF_FIRST, O_RDWR|O_CREAT, 0600);
1439 if (wcache->tdb == NULL) {
1440 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
1447 void cache_store_response(pid_t pid, struct winbindd_response *response)
1454 DEBUG(10, ("Storing response for pid %d, len %d\n",
1455 pid, response->length));
1457 fstr_sprintf(key_str, "DR/%d", pid);
1458 if (tdb_store(wcache->tdb, string_tdb_data(key_str),
1459 make_tdb_data((void *)response, sizeof(*response)),
1463 if (response->length == sizeof(*response))
1466 /* There's extra data */
1468 DEBUG(10, ("Storing extra data: len=%d\n",
1469 (int)(response->length - sizeof(*response))));
1471 fstr_sprintf(key_str, "DE/%d", pid);
1472 if (tdb_store(wcache->tdb, string_tdb_data(key_str),
1473 make_tdb_data(response->extra_data,
1474 response->length - sizeof(*response)),
1478 /* We could not store the extra data, make sure the tdb does not
1479 * contain a main record with wrong dangling extra data */
1481 fstr_sprintf(key_str, "DR/%d", pid);
1482 tdb_delete(wcache->tdb, string_tdb_data(key_str));
1487 BOOL cache_retrieve_response(pid_t pid, struct winbindd_response * response)
1495 DEBUG(10, ("Retrieving response for pid %d\n", pid));
1497 fstr_sprintf(key_str, "DR/%d", pid);
1498 data = tdb_fetch(wcache->tdb, string_tdb_data(key_str));
1500 if (data.dptr == NULL)
1503 if (data.dsize != sizeof(*response))
1506 memcpy(response, data.dptr, data.dsize);
1507 SAFE_FREE(data.dptr);
1509 if (response->length == sizeof(*response)) {
1510 response->extra_data = NULL;
1514 /* There's extra data */
1516 DEBUG(10, ("Retrieving extra data length=%d\n",
1517 (int)(response->length - sizeof(*response))));
1519 fstr_sprintf(key_str, "DE/%d", pid);
1520 data = tdb_fetch(wcache->tdb, string_tdb_data(key_str));
1522 if (data.dptr == NULL) {
1523 DEBUG(0, ("Did not find extra data\n"));
1527 if (data.dsize != (response->length - sizeof(*response))) {
1528 DEBUG(0, ("Invalid extra data length: %d\n", (int)data.dsize));
1529 SAFE_FREE(data.dptr);
1533 dump_data(11, data.dptr, data.dsize);
1535 response->extra_data = data.dptr;
1539 BOOL lookup_cached_sid(TALLOC_CTX *mem_ctx, const DOM_SID *sid,
1540 const char **domain_name, const char **name,
1541 enum SID_NAME_USE *type)
1543 struct winbindd_domain *domain;
1544 struct winbind_cache *cache;
1545 struct cache_entry *centry = NULL;
1548 domain = find_lookup_domain_from_sid(sid);
1549 if (domain == NULL) {
1553 cache = get_cache(domain);
1555 if (cache->tdb == NULL) {
1559 centry = wcache_fetch(cache, domain, "SN/%s", sid_string_static(sid));
1560 if (centry == NULL) {
1564 if (NT_STATUS_IS_OK(centry->status)) {
1565 *type = (enum SID_NAME_USE)centry_uint32(centry);
1566 *domain_name = centry_string(centry, mem_ctx);
1567 *name = centry_string(centry, mem_ctx);
1570 status = centry->status;
1571 centry_free(centry);
1572 return NT_STATUS_IS_OK(status);
1575 void cache_sid2name(struct winbindd_domain *domain, const DOM_SID *sid,
1576 const char *domain_name, const char *name,
1577 enum SID_NAME_USE type)
1579 wcache_save_sid_to_name(domain, NT_STATUS_OK, sid, domain_name,