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 */
551 centry_put_uint8(centry
, len
);
552 centry_expand(centry
, len
);
553 memcpy(centry
->data
+ centry
->ofs
, s
, len
);
557 static void centry_put_sid(struct cache_entry
*centry
, const DOM_SID
*sid
)
560 centry_put_string(centry
, sid_to_string(sid_string
, sid
));
564 start a centry for output. When finished, call centry_end()
566 struct cache_entry
*centry_start(struct winbindd_domain
*domain
, NTSTATUS status
)
568 struct cache_entry
*centry
;
573 centry
= SMB_XMALLOC_P(struct cache_entry
);
575 centry
->len
= 8192; /* reasonable default */
576 centry
->data
= SMB_XMALLOC_ARRAY(uint8
, centry
->len
);
578 centry
->sequence_number
= domain
->sequence_number
;
579 centry_put_uint32(centry
, NT_STATUS_V(status
));
580 centry_put_uint32(centry
, centry
->sequence_number
);
585 finish a centry and write it to the tdb
587 static void centry_end(struct cache_entry
*centry
, const char *format
, ...) PRINTF_ATTRIBUTE(2,3);
588 static void centry_end(struct cache_entry
*centry
, const char *format
, ...)
594 va_start(ap
, format
);
595 smb_xvasprintf(&kstr
, format
, ap
);
599 key
.dsize
= strlen(kstr
);
600 data
.dptr
= (char *)centry
->data
;
601 data
.dsize
= centry
->ofs
;
603 tdb_store(wcache
->tdb
, key
, data
, TDB_REPLACE
);
607 static void wcache_save_name_to_sid(struct winbindd_domain
*domain
,
608 NTSTATUS status
, const char *domain_name
,
609 const char *name
, const DOM_SID
*sid
,
610 enum SID_NAME_USE type
)
612 struct cache_entry
*centry
;
615 centry
= centry_start(domain
, status
);
618 centry_put_uint32(centry
, type
);
619 centry_put_sid(centry
, sid
);
620 fstrcpy(uname
, name
);
622 centry_end(centry
, "NS/%s/%s", domain_name
, uname
);
623 DEBUG(10,("wcache_save_name_to_sid: %s -> %s\n", uname
,
624 sid_string_static(sid
)));
628 static void wcache_save_sid_to_name(struct winbindd_domain
*domain
, NTSTATUS status
,
629 const DOM_SID
*sid
, const char *domain_name
, const char *name
, enum SID_NAME_USE type
)
631 struct cache_entry
*centry
;
634 centry
= centry_start(domain
, status
);
637 if (NT_STATUS_IS_OK(status
)) {
638 centry_put_uint32(centry
, type
);
639 centry_put_string(centry
, domain_name
);
640 centry_put_string(centry
, name
);
642 centry_end(centry
, "SN/%s", sid_to_string(sid_string
, sid
));
643 DEBUG(10,("wcache_save_sid_to_name: %s -> %s\n", sid_string
, name
));
648 static void wcache_save_user(struct winbindd_domain
*domain
, NTSTATUS status
, WINBIND_USERINFO
*info
)
650 struct cache_entry
*centry
;
653 centry
= centry_start(domain
, status
);
656 centry_put_string(centry
, info
->acct_name
);
657 centry_put_string(centry
, info
->full_name
);
658 centry_put_string(centry
, info
->homedir
);
659 centry_put_string(centry
, info
->shell
);
660 centry_put_sid(centry
, &info
->user_sid
);
661 centry_put_sid(centry
, &info
->group_sid
);
662 centry_end(centry
, "U/%s", sid_to_string(sid_string
, &info
->user_sid
));
663 DEBUG(10,("wcache_save_user: %s (acct_name %s)\n", sid_string
, info
->acct_name
));
668 /* Query display info. This is the basic user list fn */
669 static NTSTATUS
query_user_list(struct winbindd_domain
*domain
,
672 WINBIND_USERINFO
**info
)
674 struct winbind_cache
*cache
= get_cache(domain
);
675 struct cache_entry
*centry
= NULL
;
677 unsigned int i
, retry
;
682 centry
= wcache_fetch(cache
, domain
, "UL/%s", domain
->name
);
686 *num_entries
= centry_uint32(centry
);
688 if (*num_entries
== 0)
691 (*info
) = TALLOC_ARRAY(mem_ctx
, WINBIND_USERINFO
, *num_entries
);
693 smb_panic("query_user_list out of memory");
694 for (i
=0; i
<(*num_entries
); i
++) {
695 (*info
)[i
].acct_name
= centry_string(centry
, mem_ctx
);
696 (*info
)[i
].full_name
= centry_string(centry
, mem_ctx
);
697 (*info
)[i
].homedir
= centry_string(centry
, mem_ctx
);
698 (*info
)[i
].shell
= centry_string(centry
, mem_ctx
);
699 centry_sid(centry
, &(*info
)[i
].user_sid
);
700 centry_sid(centry
, &(*info
)[i
].group_sid
);
704 status
= centry
->status
;
706 DEBUG(10,("query_user_list: [Cached] - cached list for domain %s status %s\n",
707 domain
->name
, get_friendly_nt_error_msg(status
) ));
716 /* Return status value returned by seq number check */
718 if (!NT_STATUS_IS_OK(domain
->last_status
))
719 return domain
->last_status
;
721 /* Put the query_user_list() in a retry loop. There appears to be
722 * some bug either with Windows 2000 or Samba's handling of large
723 * rpc replies. This manifests itself as sudden disconnection
724 * at a random point in the enumeration of a large (60k) user list.
725 * The retry loop simply tries the operation again. )-: It's not
726 * pretty but an acceptable workaround until we work out what the
727 * real problem is. */
732 DEBUG(10,("query_user_list: [Cached] - doing backend query for list for domain %s\n",
735 status
= domain
->backend
->query_user_list(domain
, mem_ctx
, num_entries
, info
);
736 if (!NT_STATUS_IS_OK(status
))
737 DEBUG(3, ("query_user_list: returned 0x%08x, "
738 "retrying\n", NT_STATUS_V(status
)));
739 if (NT_STATUS_EQUAL(status
, NT_STATUS_UNSUCCESSFUL
)) {
740 DEBUG(3, ("query_user_list: flushing "
741 "connection cache\n"));
742 invalidate_cm_connection(&domain
->conn
);
745 } while (NT_STATUS_V(status
) == NT_STATUS_V(NT_STATUS_UNSUCCESSFUL
) &&
749 refresh_sequence_number(domain
, False
);
750 centry
= centry_start(domain
, status
);
753 centry_put_uint32(centry
, *num_entries
);
754 for (i
=0; i
<(*num_entries
); i
++) {
755 centry_put_string(centry
, (*info
)[i
].acct_name
);
756 centry_put_string(centry
, (*info
)[i
].full_name
);
757 centry_put_string(centry
, (*info
)[i
].homedir
);
758 centry_put_string(centry
, (*info
)[i
].shell
);
759 centry_put_sid(centry
, &(*info
)[i
].user_sid
);
760 centry_put_sid(centry
, &(*info
)[i
].group_sid
);
761 if (domain
->backend
->consistent
) {
762 /* when the backend is consistent we can pre-prime some mappings */
763 wcache_save_name_to_sid(domain
, NT_STATUS_OK
,
765 (*info
)[i
].acct_name
,
766 &(*info
)[i
].user_sid
,
768 wcache_save_sid_to_name(domain
, NT_STATUS_OK
,
769 &(*info
)[i
].user_sid
,
771 (*info
)[i
].acct_name
,
773 wcache_save_user(domain
, NT_STATUS_OK
, &(*info
)[i
]);
776 centry_end(centry
, "UL/%s", domain
->name
);
783 /* list all domain groups */
784 static NTSTATUS
enum_dom_groups(struct winbindd_domain
*domain
,
787 struct acct_info
**info
)
789 struct winbind_cache
*cache
= get_cache(domain
);
790 struct cache_entry
*centry
= NULL
;
797 centry
= wcache_fetch(cache
, domain
, "GL/%s/domain", domain
->name
);
801 *num_entries
= centry_uint32(centry
);
803 if (*num_entries
== 0)
806 (*info
) = TALLOC_ARRAY(mem_ctx
, struct acct_info
, *num_entries
);
808 smb_panic("enum_dom_groups out of memory");
809 for (i
=0; i
<(*num_entries
); i
++) {
810 fstrcpy((*info
)[i
].acct_name
, centry_string(centry
, mem_ctx
));
811 fstrcpy((*info
)[i
].acct_desc
, centry_string(centry
, mem_ctx
));
812 (*info
)[i
].rid
= centry_uint32(centry
);
816 status
= centry
->status
;
818 DEBUG(10,("enum_dom_groups: [Cached] - cached list for domain %s status %s\n",
819 domain
->name
, get_friendly_nt_error_msg(status
) ));
828 /* Return status value returned by seq number check */
830 if (!NT_STATUS_IS_OK(domain
->last_status
))
831 return domain
->last_status
;
833 DEBUG(10,("enum_dom_groups: [Cached] - doing backend query for list for domain %s\n",
836 status
= domain
->backend
->enum_dom_groups(domain
, mem_ctx
, num_entries
, info
);
839 refresh_sequence_number(domain
, False
);
840 centry
= centry_start(domain
, status
);
843 centry_put_uint32(centry
, *num_entries
);
844 for (i
=0; i
<(*num_entries
); i
++) {
845 centry_put_string(centry
, (*info
)[i
].acct_name
);
846 centry_put_string(centry
, (*info
)[i
].acct_desc
);
847 centry_put_uint32(centry
, (*info
)[i
].rid
);
849 centry_end(centry
, "GL/%s/domain", domain
->name
);
856 /* list all domain groups */
857 static NTSTATUS
enum_local_groups(struct winbindd_domain
*domain
,
860 struct acct_info
**info
)
862 struct winbind_cache
*cache
= get_cache(domain
);
863 struct cache_entry
*centry
= NULL
;
870 centry
= wcache_fetch(cache
, domain
, "GL/%s/local", domain
->name
);
874 *num_entries
= centry_uint32(centry
);
876 if (*num_entries
== 0)
879 (*info
) = TALLOC_ARRAY(mem_ctx
, struct acct_info
, *num_entries
);
881 smb_panic("enum_dom_groups out of memory");
882 for (i
=0; i
<(*num_entries
); i
++) {
883 fstrcpy((*info
)[i
].acct_name
, centry_string(centry
, mem_ctx
));
884 fstrcpy((*info
)[i
].acct_desc
, centry_string(centry
, mem_ctx
));
885 (*info
)[i
].rid
= centry_uint32(centry
);
890 /* If we are returning cached data and the domain controller
891 is down then we don't know whether the data is up to date
892 or not. Return NT_STATUS_MORE_PROCESSING_REQUIRED to
895 if (wcache_server_down(domain
)) {
896 DEBUG(10, ("enum_local_groups: returning cached user list and server was down\n"));
897 status
= NT_STATUS_MORE_PROCESSING_REQUIRED
;
899 status
= centry
->status
;
901 DEBUG(10,("enum_local_groups: [Cached] - cached list for domain %s status %s\n",
902 domain
->name
, get_friendly_nt_error_msg(status
) ));
911 /* Return status value returned by seq number check */
913 if (!NT_STATUS_IS_OK(domain
->last_status
))
914 return domain
->last_status
;
916 DEBUG(10,("enum_local_groups: [Cached] - doing backend query for list for domain %s\n",
919 status
= domain
->backend
->enum_local_groups(domain
, mem_ctx
, num_entries
, info
);
922 refresh_sequence_number(domain
, False
);
923 centry
= centry_start(domain
, status
);
926 centry_put_uint32(centry
, *num_entries
);
927 for (i
=0; i
<(*num_entries
); i
++) {
928 centry_put_string(centry
, (*info
)[i
].acct_name
);
929 centry_put_string(centry
, (*info
)[i
].acct_desc
);
930 centry_put_uint32(centry
, (*info
)[i
].rid
);
932 centry_end(centry
, "GL/%s/local", domain
->name
);
939 /* convert a single name to a sid in a domain */
940 static NTSTATUS
name_to_sid(struct winbindd_domain
*domain
,
942 const char *domain_name
,
945 enum SID_NAME_USE
*type
)
947 struct winbind_cache
*cache
= get_cache(domain
);
948 struct cache_entry
*centry
= NULL
;
955 fstrcpy(uname
, name
);
957 centry
= wcache_fetch(cache
, domain
, "NS/%s/%s", domain_name
, uname
);
960 *type
= (enum SID_NAME_USE
)centry_uint32(centry
);
961 centry_sid(centry
, sid
);
962 status
= centry
->status
;
964 DEBUG(10,("name_to_sid: [Cached] - cached name for domain %s status %s\n",
965 domain
->name
, get_friendly_nt_error_msg(status
) ));
973 /* If the seq number check indicated that there is a problem
974 * with this DC, then return that status... except for
975 * access_denied. This is special because the dc may be in
976 * "restrict anonymous = 1" mode, in which case it will deny
977 * most unauthenticated operations, but *will* allow the LSA
978 * name-to-sid that we try as a fallback. */
980 if (!(NT_STATUS_IS_OK(domain
->last_status
)
981 || NT_STATUS_EQUAL(domain
->last_status
, NT_STATUS_ACCESS_DENIED
)))
982 return domain
->last_status
;
984 DEBUG(10,("name_to_sid: [Cached] - doing backend query for name for domain %s\n",
987 status
= domain
->backend
->name_to_sid(domain
, mem_ctx
, domain_name
, name
, sid
, type
);
990 wcache_save_name_to_sid(domain
, status
, domain_name
, name
, sid
, *type
);
992 /* We can't save the sid to name mapping as we don't know the
993 correct case of the name without looking it up */
998 /* convert a sid to a user or group name. The sid is guaranteed to be in the domain
1000 static NTSTATUS
sid_to_name(struct winbindd_domain
*domain
,
1001 TALLOC_CTX
*mem_ctx
,
1005 enum SID_NAME_USE
*type
)
1007 struct winbind_cache
*cache
= get_cache(domain
);
1008 struct cache_entry
*centry
= NULL
;
1015 centry
= wcache_fetch(cache
, domain
, "SN/%s", sid_to_string(sid_string
, sid
));
1018 if (NT_STATUS_IS_OK(centry
->status
)) {
1019 *type
= (enum SID_NAME_USE
)centry_uint32(centry
);
1020 *domain_name
= centry_string(centry
, mem_ctx
);
1021 *name
= centry_string(centry
, mem_ctx
);
1023 status
= centry
->status
;
1025 DEBUG(10,("sid_to_name: [Cached] - cached name for domain %s status %s\n",
1026 domain
->name
, get_friendly_nt_error_msg(status
) ));
1028 centry_free(centry
);
1033 *domain_name
= NULL
;
1035 /* If the seq number check indicated that there is a problem
1036 * with this DC, then return that status... except for
1037 * access_denied. This is special because the dc may be in
1038 * "restrict anonymous = 1" mode, in which case it will deny
1039 * most unauthenticated operations, but *will* allow the LSA
1040 * sid-to-name that we try as a fallback. */
1042 if (!(NT_STATUS_IS_OK(domain
->last_status
)
1043 || NT_STATUS_EQUAL(domain
->last_status
, NT_STATUS_ACCESS_DENIED
)))
1044 return domain
->last_status
;
1046 DEBUG(10,("sid_to_name: [Cached] - doing backend query for name for domain %s\n",
1049 status
= domain
->backend
->sid_to_name(domain
, mem_ctx
, sid
, domain_name
, name
, type
);
1052 refresh_sequence_number(domain
, False
);
1053 wcache_save_sid_to_name(domain
, status
, sid
, *domain_name
, *name
, *type
);
1055 /* We can't save the name to sid mapping here, as with sid history a
1056 * later name2sid would give the wrong sid. */
1061 /* Lookup user information from a rid */
1062 static NTSTATUS
query_user(struct winbindd_domain
*domain
,
1063 TALLOC_CTX
*mem_ctx
,
1064 const DOM_SID
*user_sid
,
1065 WINBIND_USERINFO
*info
)
1067 struct winbind_cache
*cache
= get_cache(domain
);
1068 struct cache_entry
*centry
= NULL
;
1074 centry
= wcache_fetch(cache
, domain
, "U/%s", sid_string_static(user_sid
));
1076 /* If we have an access denied cache entry and a cached info3 in the
1077 samlogon cache then do a query. This will force the rpc back end
1078 to return the info3 data. */
1080 if (NT_STATUS_V(domain
->last_status
) == NT_STATUS_V(NT_STATUS_ACCESS_DENIED
) &&
1081 netsamlogon_cache_have(user_sid
)) {
1082 DEBUG(10, ("query_user: cached access denied and have cached info3\n"));
1083 domain
->last_status
= NT_STATUS_OK
;
1084 centry_free(centry
);
1091 info
->acct_name
= centry_string(centry
, mem_ctx
);
1092 info
->full_name
= centry_string(centry
, mem_ctx
);
1093 info
->homedir
= centry_string(centry
, mem_ctx
);
1094 info
->shell
= centry_string(centry
, mem_ctx
);
1095 centry_sid(centry
, &info
->user_sid
);
1096 centry_sid(centry
, &info
->group_sid
);
1097 status
= centry
->status
;
1099 DEBUG(10,("query_user: [Cached] - cached info for domain %s status %s\n",
1100 domain
->name
, get_friendly_nt_error_msg(status
) ));
1102 centry_free(centry
);
1108 /* Return status value returned by seq number check */
1110 if (!NT_STATUS_IS_OK(domain
->last_status
))
1111 return domain
->last_status
;
1113 DEBUG(10,("sid_to_name: [Cached] - doing backend query for info for domain %s\n",
1116 status
= domain
->backend
->query_user(domain
, mem_ctx
, user_sid
, info
);
1119 refresh_sequence_number(domain
, False
);
1120 wcache_save_user(domain
, status
, info
);
1126 /* Lookup groups a user is a member of. */
1127 static NTSTATUS
lookup_usergroups(struct winbindd_domain
*domain
,
1128 TALLOC_CTX
*mem_ctx
,
1129 const DOM_SID
*user_sid
,
1130 uint32
*num_groups
, DOM_SID
**user_gids
)
1132 struct winbind_cache
*cache
= get_cache(domain
);
1133 struct cache_entry
*centry
= NULL
;
1141 centry
= wcache_fetch(cache
, domain
, "UG/%s", sid_to_string(sid_string
, user_sid
));
1143 /* If we have an access denied cache entry and a cached info3 in the
1144 samlogon cache then do a query. This will force the rpc back end
1145 to return the info3 data. */
1147 if (NT_STATUS_V(domain
->last_status
) == NT_STATUS_V(NT_STATUS_ACCESS_DENIED
) &&
1148 netsamlogon_cache_have(user_sid
)) {
1149 DEBUG(10, ("query_user: cached access denied and have cached info3\n"));
1150 domain
->last_status
= NT_STATUS_OK
;
1151 centry_free(centry
);
1158 *num_groups
= centry_uint32(centry
);
1160 if (*num_groups
== 0)
1163 (*user_gids
) = TALLOC_ARRAY(mem_ctx
, DOM_SID
, *num_groups
);
1165 smb_panic("lookup_usergroups out of memory");
1166 for (i
=0; i
<(*num_groups
); i
++) {
1167 centry_sid(centry
, &(*user_gids
)[i
]);
1171 status
= centry
->status
;
1173 DEBUG(10,("lookup_usergroups: [Cached] - cached info for domain %s status %s\n",
1174 domain
->name
, get_friendly_nt_error_msg(status
) ));
1176 centry_free(centry
);
1181 (*user_gids
) = NULL
;
1183 /* Return status value returned by seq number check */
1185 if (!NT_STATUS_IS_OK(domain
->last_status
))
1186 return domain
->last_status
;
1188 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info for domain %s\n",
1191 status
= domain
->backend
->lookup_usergroups(domain
, mem_ctx
, user_sid
, num_groups
, user_gids
);
1194 refresh_sequence_number(domain
, False
);
1195 centry
= centry_start(domain
, status
);
1198 centry_put_uint32(centry
, *num_groups
);
1199 for (i
=0; i
<(*num_groups
); i
++) {
1200 centry_put_sid(centry
, &(*user_gids
)[i
]);
1202 centry_end(centry
, "UG/%s", sid_to_string(sid_string
, user_sid
));
1203 centry_free(centry
);
1209 static NTSTATUS
lookup_useraliases(struct winbindd_domain
*domain
,
1210 TALLOC_CTX
*mem_ctx
,
1211 uint32 num_sids
, const DOM_SID
*sids
,
1212 uint32
*num_aliases
, uint32
**alias_rids
)
1214 struct winbind_cache
*cache
= get_cache(domain
);
1215 struct cache_entry
*centry
= NULL
;
1217 char *sidlist
= talloc_strdup(mem_ctx
, "");
1223 if (num_sids
== 0) {
1226 return NT_STATUS_OK
;
1229 /* We need to cache indexed by the whole list of SIDs, the aliases
1230 * resulting might come from any of the SIDs. */
1232 for (i
=0; i
<num_sids
; i
++) {
1233 sidlist
= talloc_asprintf(mem_ctx
, "%s/%s", sidlist
,
1234 sid_string_static(&sids
[i
]));
1235 if (sidlist
== NULL
)
1236 return NT_STATUS_NO_MEMORY
;
1239 centry
= wcache_fetch(cache
, domain
, "UA%s", sidlist
);
1244 *num_aliases
= centry_uint32(centry
);
1247 (*alias_rids
) = TALLOC_ARRAY(mem_ctx
, uint32
, *num_aliases
);
1249 if ((*num_aliases
!= 0) && ((*alias_rids
) == NULL
))
1250 return NT_STATUS_NO_MEMORY
;
1252 for (i
=0; i
<(*num_aliases
); i
++)
1253 (*alias_rids
)[i
] = centry_uint32(centry
);
1255 status
= centry
->status
;
1257 DEBUG(10,("lookup_useraliases: [Cached] - cached info for domain %s "
1258 "status %s\n", domain
->name
,
1259 get_friendly_nt_error_msg(status
)));
1261 centry_free(centry
);
1266 (*alias_rids
) = NULL
;
1268 if (!NT_STATUS_IS_OK(domain
->last_status
))
1269 return domain
->last_status
;
1271 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info "
1272 "for domain %s\n", domain
->name
));
1274 status
= domain
->backend
->lookup_useraliases(domain
, mem_ctx
,
1276 num_aliases
, alias_rids
);
1279 refresh_sequence_number(domain
, False
);
1280 centry
= centry_start(domain
, status
);
1283 centry_put_uint32(centry
, *num_aliases
);
1284 for (i
=0; i
<(*num_aliases
); i
++)
1285 centry_put_uint32(centry
, (*alias_rids
)[i
]);
1286 centry_end(centry
, "UA%s", sidlist
);
1287 centry_free(centry
);
1294 static NTSTATUS
lookup_groupmem(struct winbindd_domain
*domain
,
1295 TALLOC_CTX
*mem_ctx
,
1296 const DOM_SID
*group_sid
, uint32
*num_names
,
1297 DOM_SID
**sid_mem
, char ***names
,
1298 uint32
**name_types
)
1300 struct winbind_cache
*cache
= get_cache(domain
);
1301 struct cache_entry
*centry
= NULL
;
1309 centry
= wcache_fetch(cache
, domain
, "GM/%s", sid_to_string(sid_string
, group_sid
));
1313 *num_names
= centry_uint32(centry
);
1315 if (*num_names
== 0)
1318 (*sid_mem
) = TALLOC_ARRAY(mem_ctx
, DOM_SID
, *num_names
);
1319 (*names
) = TALLOC_ARRAY(mem_ctx
, char *, *num_names
);
1320 (*name_types
) = TALLOC_ARRAY(mem_ctx
, uint32
, *num_names
);
1322 if (! (*sid_mem
) || ! (*names
) || ! (*name_types
)) {
1323 smb_panic("lookup_groupmem out of memory");
1326 for (i
=0; i
<(*num_names
); i
++) {
1327 centry_sid(centry
, &(*sid_mem
)[i
]);
1328 (*names
)[i
] = centry_string(centry
, mem_ctx
);
1329 (*name_types
)[i
] = centry_uint32(centry
);
1333 status
= centry
->status
;
1335 DEBUG(10,("lookup_groupmem: [Cached] - cached info for domain %s status %s\n",
1336 domain
->name
, get_friendly_nt_error_msg(status
) ));
1338 centry_free(centry
);
1345 (*name_types
) = NULL
;
1347 /* Return status value returned by seq number check */
1349 if (!NT_STATUS_IS_OK(domain
->last_status
))
1350 return domain
->last_status
;
1352 DEBUG(10,("lookup_groupmem: [Cached] - doing backend query for info for domain %s\n",
1355 status
= domain
->backend
->lookup_groupmem(domain
, mem_ctx
, group_sid
, num_names
,
1356 sid_mem
, names
, name_types
);
1359 refresh_sequence_number(domain
, False
);
1360 centry
= centry_start(domain
, status
);
1363 centry_put_uint32(centry
, *num_names
);
1364 for (i
=0; i
<(*num_names
); i
++) {
1365 centry_put_sid(centry
, &(*sid_mem
)[i
]);
1366 centry_put_string(centry
, (*names
)[i
]);
1367 centry_put_uint32(centry
, (*name_types
)[i
]);
1369 centry_end(centry
, "GM/%s", sid_to_string(sid_string
, group_sid
));
1370 centry_free(centry
);
1376 /* find the sequence number for a domain */
1377 static NTSTATUS
sequence_number(struct winbindd_domain
*domain
, uint32
*seq
)
1379 refresh_sequence_number(domain
, False
);
1381 *seq
= domain
->sequence_number
;
1383 return NT_STATUS_OK
;
1386 /* enumerate trusted domains */
1387 static NTSTATUS
trusted_domains(struct winbindd_domain
*domain
,
1388 TALLOC_CTX
*mem_ctx
,
1389 uint32
*num_domains
,
1396 DEBUG(10,("trusted_domains: [Cached] - doing backend query for info for domain %s\n",
1399 /* we don't cache this call */
1400 return domain
->backend
->trusted_domains(domain
, mem_ctx
, num_domains
,
1401 names
, alt_names
, dom_sids
);
1404 /* find the alternate names for the domain, if any */
1405 static NTSTATUS
alternate_name(struct winbindd_domain
*domain
)
1409 DEBUG(10,("alternate_name: [Cached] - doing backend query for info for domain %s\n",
1412 /* we don't cache this call */
1413 return domain
->backend
->alternate_name(domain
);
1416 /* Invalidate cached user and group lists coherently */
1418 static int traverse_fn(TDB_CONTEXT
*the_tdb
, TDB_DATA kbuf
, TDB_DATA dbuf
,
1421 if (strncmp(kbuf
.dptr
, "UL/", 3) == 0 ||
1422 strncmp(kbuf
.dptr
, "GL/", 3) == 0)
1423 tdb_delete(the_tdb
, kbuf
);
1428 /* Invalidate the getpwnam and getgroups entries for a winbindd domain */
1430 void wcache_invalidate_samlogon(struct winbindd_domain
*domain
,
1431 NET_USER_INFO_3
*info3
)
1433 struct winbind_cache
*cache
;
1438 cache
= get_cache(domain
);
1439 netsamlogon_clear_cached_user(cache
->tdb
, info3
);
1442 void wcache_invalidate_cache(void)
1444 struct winbindd_domain
*domain
;
1446 for (domain
= domain_list(); domain
; domain
= domain
->next
) {
1447 struct winbind_cache
*cache
= get_cache(domain
);
1449 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
1450 "entries for %s\n", domain
->name
));
1452 tdb_traverse(cache
->tdb
, traverse_fn
, NULL
);
1456 /* the ADS backend methods are exposed via this structure */
1457 struct winbindd_methods cache_methods
= {
1473 static BOOL
init_wcache(void)
1475 if (wcache
== NULL
) {
1476 wcache
= SMB_XMALLOC_P(struct winbind_cache
);
1477 ZERO_STRUCTP(wcache
);
1480 if (wcache
->tdb
!= NULL
)
1483 wcache
->tdb
= tdb_open_log(lock_path("winbindd_cache.tdb"), 5000,
1484 TDB_CLEAR_IF_FIRST
, O_RDWR
|O_CREAT
, 0600);
1486 if (wcache
->tdb
== NULL
) {
1487 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
1494 void cache_store_response(pid_t pid
, struct winbindd_response
*response
)
1501 DEBUG(10, ("Storing response for pid %d, len %d\n",
1502 pid
, response
->length
));
1504 fstr_sprintf(key_str
, "DR/%d", pid
);
1505 if (tdb_store(wcache
->tdb
, string_tdb_data(key_str
),
1506 make_tdb_data((void *)response
, sizeof(*response
)),
1510 if (response
->length
== sizeof(*response
))
1513 /* There's extra data */
1515 DEBUG(10, ("Storing extra data: len=%d\n",
1516 (int)(response
->length
- sizeof(*response
))));
1518 fstr_sprintf(key_str
, "DE/%d", pid
);
1519 if (tdb_store(wcache
->tdb
, string_tdb_data(key_str
),
1520 make_tdb_data(response
->extra_data
,
1521 response
->length
- sizeof(*response
)),
1525 /* We could not store the extra data, make sure the tdb does not
1526 * contain a main record with wrong dangling extra data */
1528 fstr_sprintf(key_str
, "DR/%d", pid
);
1529 tdb_delete(wcache
->tdb
, string_tdb_data(key_str
));
1534 BOOL
cache_retrieve_response(pid_t pid
, struct winbindd_response
* response
)
1542 DEBUG(10, ("Retrieving response for pid %d\n", pid
));
1544 fstr_sprintf(key_str
, "DR/%d", pid
);
1545 data
= tdb_fetch(wcache
->tdb
, string_tdb_data(key_str
));
1547 if (data
.dptr
== NULL
)
1550 if (data
.dsize
!= sizeof(*response
))
1553 memcpy(response
, data
.dptr
, data
.dsize
);
1554 SAFE_FREE(data
.dptr
);
1556 if (response
->length
== sizeof(*response
)) {
1557 response
->extra_data
= NULL
;
1561 /* There's extra data */
1563 DEBUG(10, ("Retrieving extra data length=%d\n",
1564 (int)(response
->length
- sizeof(*response
))));
1566 fstr_sprintf(key_str
, "DE/%d", pid
);
1567 data
= tdb_fetch(wcache
->tdb
, string_tdb_data(key_str
));
1569 if (data
.dptr
== NULL
) {
1570 DEBUG(0, ("Did not find extra data\n"));
1574 if (data
.dsize
!= (response
->length
- sizeof(*response
))) {
1575 DEBUG(0, ("Invalid extra data length: %d\n", (int)data
.dsize
));
1576 SAFE_FREE(data
.dptr
);
1580 dump_data(11, data
.dptr
, data
.dsize
);
1582 response
->extra_data
= data
.dptr
;
1586 BOOL
lookup_cached_sid(TALLOC_CTX
*mem_ctx
, const DOM_SID
*sid
,
1587 const char **domain_name
, const char **name
,
1588 enum SID_NAME_USE
*type
)
1590 struct winbindd_domain
*domain
;
1591 struct winbind_cache
*cache
;
1592 struct cache_entry
*centry
= NULL
;
1595 domain
= find_lookup_domain_from_sid(sid
);
1596 if (domain
== NULL
) {
1600 cache
= get_cache(domain
);
1602 if (cache
->tdb
== NULL
) {
1606 centry
= wcache_fetch(cache
, domain
, "SN/%s", sid_string_static(sid
));
1607 if (centry
== NULL
) {
1611 if (NT_STATUS_IS_OK(centry
->status
)) {
1612 *type
= (enum SID_NAME_USE
)centry_uint32(centry
);
1613 *domain_name
= centry_string(centry
, mem_ctx
);
1614 *name
= centry_string(centry
, mem_ctx
);
1617 status
= centry
->status
;
1618 centry_free(centry
);
1619 return NT_STATUS_IS_OK(status
);
1622 void cache_sid2name(struct winbindd_domain
*domain
, const DOM_SID
*sid
,
1623 const char *domain_name
, const char *name
,
1624 enum SID_NAME_USE type
)
1626 wcache_save_sid_to_name(domain
, NT_STATUS_OK
, sid
, domain_name
,