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
9 Copyright (C) Guenther Deschner 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 /* Global online/offline state - False when online. winbindd starts up online
33 and sets this to true if the first query fails and there's an entry in
34 the cache tdb telling us to stay offline. */
36 static BOOL global_winbindd_offline_state;
38 struct winbind_cache {
44 uint32 sequence_number;
49 #define WINBINDD_MAX_CACHE_SIZE (50*1024*1024)
51 static struct winbind_cache *wcache;
53 void winbindd_check_cache_size(time_t t)
55 static time_t last_check_time;
58 if (last_check_time == (time_t)0)
61 if (t - last_check_time < 60 && t - last_check_time > 0)
64 if (wcache == NULL || wcache->tdb == NULL) {
65 DEBUG(0, ("Unable to check size of tdb cache - cache not open !\n"));
69 if (fstat(tdb_fd(wcache->tdb), &st) == -1) {
70 DEBUG(0, ("Unable to check size of tdb cache %s!\n", strerror(errno) ));
74 if (st.st_size > WINBINDD_MAX_CACHE_SIZE) {
75 DEBUG(10,("flushing cache due to size (%lu) > (%lu)\n",
76 (unsigned long)st.st_size,
77 (unsigned long)WINBINDD_MAX_CACHE_SIZE));
82 /* get the winbind_cache structure */
83 static struct winbind_cache *get_cache(struct winbindd_domain *domain)
85 struct winbind_cache *ret = wcache;
87 struct winbindd_domain *our_domain = domain;
90 /* we have to know what type of domain we are dealing with first */
92 if ( !domain->initialized )
93 set_dc_type_and_flags( domain );
96 OK. listen up becasue I'm only going to say this once.
97 We have the following scenarios to consider
98 (a) trusted AD domains on a Samba DC,
99 (b) trusted AD domains and we are joined to a non-kerberos domain
100 (c) trusted AD domains and we are joined to a kerberos (AD) domain
102 For (a) we can always contact the trusted domain using krb5
103 since we have the domain trust account password
105 For (b) we can only use RPC since we have no way of
106 getting a krb5 ticket in our own domain
108 For (c) we can always use krb5 since we have a kerberos trust
113 if (!domain->backend) {
114 extern struct winbindd_methods reconnect_methods;
116 extern struct winbindd_methods ads_methods;
118 /* find our domain first so we can figure out if we
119 are joined to a kerberized domain */
121 if ( !domain->primary )
122 our_domain = find_our_domain();
124 if ( (our_domain->active_directory || IS_DC) && domain->active_directory ) {
125 DEBUG(5,("get_cache: Setting ADS methods for domain %s\n", domain->name));
126 domain->backend = &ads_methods;
128 #endif /* HAVE_ADS */
129 DEBUG(5,("get_cache: Setting MS-RPC methods for domain %s\n", domain->name));
130 domain->backend = &reconnect_methods;
133 #endif /* HAVE_ADS */
139 ret = SMB_XMALLOC_P(struct winbind_cache);
143 wcache_flush_cache();
149 free a centry structure
151 static void centry_free(struct cache_entry *centry)
155 SAFE_FREE(centry->data);
160 pull a uint32 from a cache entry
162 static uint32 centry_uint32(struct cache_entry *centry)
165 if (centry->len - centry->ofs < 4) {
166 DEBUG(0,("centry corruption? needed 4 bytes, have %d\n",
167 centry->len - centry->ofs));
168 smb_panic("centry_uint32");
170 ret = IVAL(centry->data, centry->ofs);
176 pull a uint16 from a cache entry
178 static uint16 centry_uint16(struct cache_entry *centry)
181 if (centry->len - centry->ofs < 2) {
182 DEBUG(0,("centry corruption? needed 2 bytes, have %d\n",
183 centry->len - centry->ofs));
184 smb_panic("centry_uint16");
186 ret = CVAL(centry->data, centry->ofs);
192 pull a uint8 from a cache entry
194 static uint8 centry_uint8(struct cache_entry *centry)
197 if (centry->len - centry->ofs < 1) {
198 DEBUG(0,("centry corruption? needed 1 bytes, have %d\n",
199 centry->len - centry->ofs));
200 smb_panic("centry_uint32");
202 ret = CVAL(centry->data, centry->ofs);
208 pull a NTTIME from a cache entry
210 static NTTIME centry_nttime(struct cache_entry *centry)
213 if (centry->len - centry->ofs < 8) {
214 DEBUG(0,("centry corruption? needed 8 bytes, have %d\n",
215 centry->len - centry->ofs));
216 smb_panic("centry_nttime");
218 ret.low = IVAL(centry->data, centry->ofs);
220 ret.high = IVAL(centry->data, centry->ofs);
226 pull a time_t from a cache entry
228 static time_t centry_time(struct cache_entry *centry)
231 if (centry->len - centry->ofs < sizeof(time_t)) {
232 DEBUG(0,("centry corruption? needed %u bytes, have %u\n",
233 (unsigned int)sizeof(time_t), (unsigned int)(centry->len - centry->ofs)));
234 smb_panic("centry_time");
236 ret = IVAL(centry->data, centry->ofs); /* FIXME: correct ? */
237 centry->ofs += sizeof(time_t);
241 /* pull a string from a cache entry, using the supplied
244 static char *centry_string(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
249 len = centry_uint8(centry);
252 /* a deliberate NULL string */
256 if (centry->len - centry->ofs < len) {
257 DEBUG(0,("centry corruption? needed %d bytes, have %d\n",
258 len, centry->len - centry->ofs));
259 smb_panic("centry_string");
262 ret = TALLOC_ARRAY(mem_ctx, char, len+1);
264 smb_panic("centry_string out of memory\n");
266 memcpy(ret,centry->data + centry->ofs, len);
272 /* pull a string from a cache entry, using the supplied
275 static BOOL centry_sid(struct cache_entry *centry, TALLOC_CTX *mem_ctx, DOM_SID *sid)
278 sid_string = centry_string(centry, mem_ctx);
279 if ((sid_string == NULL) || (!string_to_sid(sid, sid_string))) {
285 /* the server is considered down if it can't give us a sequence number */
286 static BOOL wcache_server_down(struct winbindd_domain *domain)
293 ret = (domain->sequence_number == DOM_SEQUENCE_NONE);
296 DEBUG(10,("wcache_server_down: server for Domain %s down\n",
301 static NTSTATUS fetch_cache_seqnum( struct winbindd_domain *domain, time_t now )
308 DEBUG(10,("fetch_cache_seqnum: tdb == NULL\n"));
309 return NT_STATUS_UNSUCCESSFUL;
312 fstr_sprintf( key, "SEQNUM/%s", domain->name );
314 data = tdb_fetch_bystring( wcache->tdb, key );
315 if ( !data.dptr || data.dsize!=8 ) {
316 DEBUG(10,("fetch_cache_seqnum: invalid data size key [%s]\n", key ));
317 return NT_STATUS_UNSUCCESSFUL;
320 domain->sequence_number = IVAL(data.dptr, 0);
321 domain->last_seq_check = IVAL(data.dptr, 4);
323 SAFE_FREE(data.dptr);
325 /* have we expired? */
327 time_diff = now - domain->last_seq_check;
328 if ( time_diff > lp_winbind_cache_time() ) {
329 DEBUG(10,("fetch_cache_seqnum: timeout [%s][%u @ %u]\n",
330 domain->name, domain->sequence_number,
331 (uint32)domain->last_seq_check));
332 return NT_STATUS_UNSUCCESSFUL;
335 DEBUG(10,("fetch_cache_seqnum: success [%s][%u @ %u]\n",
336 domain->name, domain->sequence_number,
337 (uint32)domain->last_seq_check));
342 static NTSTATUS store_cache_seqnum( struct winbindd_domain *domain )
349 DEBUG(10,("store_cache_seqnum: tdb == NULL\n"));
350 return NT_STATUS_UNSUCCESSFUL;
353 fstr_sprintf( key_str, "SEQNUM/%s", domain->name );
355 key.dsize = strlen(key_str)+1;
357 SIVAL(buf, 0, domain->sequence_number);
358 SIVAL(buf, 4, domain->last_seq_check);
362 if ( tdb_store( wcache->tdb, key, data, TDB_REPLACE) == -1 ) {
363 DEBUG(10,("store_cache_seqnum: tdb_store fail key [%s]\n", key_str ));
364 return NT_STATUS_UNSUCCESSFUL;
367 DEBUG(10,("store_cache_seqnum: success [%s][%u @ %u]\n",
368 domain->name, domain->sequence_number,
369 (uint32)domain->last_seq_check));
375 refresh the domain sequence number. If force is True
376 then always refresh it, no matter how recently we fetched it
379 static void refresh_sequence_number(struct winbindd_domain *domain, BOOL force)
383 time_t t = time(NULL);
384 unsigned cache_time = lp_winbind_cache_time();
388 #if 0 /* JERRY -- disable as the default cache time is now 5 minutes */
389 /* trying to reconnect is expensive, don't do it too often */
390 if (domain->sequence_number == DOM_SEQUENCE_NONE) {
395 time_diff = t - domain->last_seq_check;
397 /* see if we have to refetch the domain sequence number */
398 if (!force && (time_diff < cache_time)) {
399 DEBUG(10, ("refresh_sequence_number: %s time ok\n", domain->name));
403 /* try to get the sequence number from the tdb cache first */
404 /* this will update the timestamp as well */
406 status = fetch_cache_seqnum( domain, t );
407 if ( NT_STATUS_IS_OK(status) )
410 /* important! make sure that we know if this is a native
411 mode domain or not */
413 status = domain->backend->sequence_number(domain, &domain->sequence_number);
415 if (!NT_STATUS_IS_OK(status)) {
416 domain->sequence_number = DOM_SEQUENCE_NONE;
419 domain->last_status = status;
420 domain->last_seq_check = time(NULL);
422 /* save the new sequence number ni the cache */
423 store_cache_seqnum( domain );
426 DEBUG(10, ("refresh_sequence_number: %s seq number is now %d\n",
427 domain->name, domain->sequence_number));
433 decide if a cache entry has expired
435 static BOOL centry_expired(struct winbindd_domain *domain, const char *keystr, struct cache_entry *centry)
437 /* If we've been told to be offline - stay in that state... */
438 if (lp_winbind_offline_logon() && global_winbindd_offline_state) {
439 DEBUG(10,("centry_expired: Key %s for domain %s valid as winbindd is globally offline.\n",
440 keystr, domain->name ));
444 /* when the domain is offline and we havent checked in the last 30
445 * seconds if it has become online again, return the cached entry.
446 * This deals with transient offline states... */
448 if (!domain->online &&
449 !NT_STATUS_IS_OK(check_negative_conn_cache(domain->name, domain->dcname))) {
450 DEBUG(10,("centry_expired: Key %s for domain %s valid as domain is offline.\n",
451 keystr, domain->name ));
455 /* if the server is OK and our cache entry came from when it was down then
456 the entry is invalid */
457 if ((domain->sequence_number != DOM_SEQUENCE_NONE) &&
458 (centry->sequence_number == DOM_SEQUENCE_NONE)) {
459 DEBUG(10,("centry_expired: Key %s for domain %s invalid sequence.\n",
460 keystr, domain->name ));
464 /* if the server is down or the cache entry is not older than the
465 current sequence number then it is OK */
466 if (wcache_server_down(domain) ||
467 centry->sequence_number == domain->sequence_number) {
468 DEBUG(10,("centry_expired: Key %s for domain %s is good.\n",
469 keystr, domain->name ));
473 DEBUG(10,("centry_expired: Key %s for domain %s expired\n",
474 keystr, domain->name ));
480 static struct cache_entry *wcache_fetch_raw(char *kstr)
483 struct cache_entry *centry;
487 key.dsize = strlen(kstr);
488 data = tdb_fetch(wcache->tdb, key);
494 centry = SMB_XMALLOC_P(struct cache_entry);
495 centry->data = (unsigned char *)data.dptr;
496 centry->len = data.dsize;
499 if (centry->len < 8) {
500 /* huh? corrupt cache? */
501 DEBUG(10,("wcache_fetch_raw: Corrupt cache for key %s (len < 8) ?\n", kstr));
506 centry->status = NT_STATUS(centry_uint32(centry));
507 centry->sequence_number = centry_uint32(centry);
513 fetch an entry from the cache, with a varargs key. auto-fetch the sequence
514 number and return status
516 static struct cache_entry *wcache_fetch(struct winbind_cache *cache,
517 struct winbindd_domain *domain,
518 const char *format, ...) PRINTF_ATTRIBUTE(3,4);
519 static struct cache_entry *wcache_fetch(struct winbind_cache *cache,
520 struct winbindd_domain *domain,
521 const char *format, ...)
525 struct cache_entry *centry;
527 extern BOOL opt_nocache;
533 refresh_sequence_number(domain, False);
535 va_start(ap, format);
536 smb_xvasprintf(&kstr, format, ap);
539 centry = wcache_fetch_raw(kstr);
540 if (centry == NULL) {
545 if (centry_expired(domain, kstr, centry)) {
547 DEBUG(10,("wcache_fetch: entry %s expired for domain %s\n",
548 kstr, domain->name ));
555 DEBUG(10,("wcache_fetch: returning entry %s for domain %s\n",
556 kstr, domain->name ));
563 make sure we have at least len bytes available in a centry
565 static void centry_expand(struct cache_entry *centry, uint32 len)
567 if (centry->len - centry->ofs >= len)
570 centry->data = SMB_REALLOC_ARRAY(centry->data, unsigned char,
573 DEBUG(0,("out of memory: needed %d bytes in centry_expand\n", centry->len));
574 smb_panic("out of memory in centry_expand");
579 push a uint32 into a centry
581 static void centry_put_uint32(struct cache_entry *centry, uint32 v)
583 centry_expand(centry, 4);
584 SIVAL(centry->data, centry->ofs, v);
589 push a uint16 into a centry
591 static void centry_put_uint16(struct cache_entry *centry, uint16 v)
593 centry_expand(centry, 2);
594 SIVAL(centry->data, centry->ofs, v);
599 push a uint8 into a centry
601 static void centry_put_uint8(struct cache_entry *centry, uint8 v)
603 centry_expand(centry, 1);
604 SCVAL(centry->data, centry->ofs, v);
609 push a string into a centry
611 static void centry_put_string(struct cache_entry *centry, const char *s)
616 /* null strings are marked as len 0xFFFF */
617 centry_put_uint8(centry, 0xFF);
622 /* can't handle more than 254 char strings. Truncating is probably best */
624 DEBUG(10,("centry_put_string: truncating len (%d) to: 254\n", len));
627 centry_put_uint8(centry, len);
628 centry_expand(centry, len);
629 memcpy(centry->data + centry->ofs, s, len);
633 static void centry_put_sid(struct cache_entry *centry, const DOM_SID *sid)
636 centry_put_string(centry, sid_to_string(sid_string, sid));
640 push a NTTIME into a centry
642 static void centry_put_nttime(struct cache_entry *centry, NTTIME nt)
644 centry_expand(centry, 8);
645 SIVAL(centry->data, centry->ofs, nt.low);
647 SIVAL(centry->data, centry->ofs, nt.high);
652 push a time_t into a centry
654 static void centry_put_time(struct cache_entry *centry, time_t t)
656 centry_expand(centry, sizeof(time_t));
657 SIVAL(centry->data, centry->ofs, t); /* FIXME: is this correct ?? */
658 centry->ofs += sizeof(time_t);
662 start a centry for output. When finished, call centry_end()
664 struct cache_entry *centry_start(struct winbindd_domain *domain, NTSTATUS status)
666 struct cache_entry *centry;
671 centry = SMB_XMALLOC_P(struct cache_entry);
673 centry->len = 8192; /* reasonable default */
674 centry->data = SMB_XMALLOC_ARRAY(uint8, centry->len);
676 centry->sequence_number = domain->sequence_number;
677 centry_put_uint32(centry, NT_STATUS_V(status));
678 centry_put_uint32(centry, centry->sequence_number);
683 finish a centry and write it to the tdb
685 static void centry_end(struct cache_entry *centry, const char *format, ...) PRINTF_ATTRIBUTE(2,3);
686 static void centry_end(struct cache_entry *centry, const char *format, ...)
692 va_start(ap, format);
693 smb_xvasprintf(&kstr, format, ap);
697 key.dsize = strlen(kstr);
698 data.dptr = (char *)centry->data;
699 data.dsize = centry->ofs;
701 tdb_store(wcache->tdb, key, data, TDB_REPLACE);
705 static void wcache_save_name_to_sid(struct winbindd_domain *domain,
706 NTSTATUS status, const char *domain_name,
707 const char *name, const DOM_SID *sid,
708 enum SID_NAME_USE type)
710 struct cache_entry *centry;
713 centry = centry_start(domain, status);
716 centry_put_uint32(centry, type);
717 centry_put_sid(centry, sid);
718 fstrcpy(uname, name);
720 centry_end(centry, "NS/%s/%s", domain_name, uname);
721 DEBUG(10,("wcache_save_name_to_sid: %s\\%s -> %s\n", domain_name, uname,
722 sid_string_static(sid)));
726 static void wcache_save_sid_to_name(struct winbindd_domain *domain, NTSTATUS status,
727 const DOM_SID *sid, const char *domain_name, const char *name, enum SID_NAME_USE type)
729 struct cache_entry *centry;
732 centry = centry_start(domain, status);
735 if (NT_STATUS_IS_OK(status)) {
736 centry_put_uint32(centry, type);
737 centry_put_string(centry, domain_name);
738 centry_put_string(centry, name);
740 centry_end(centry, "SN/%s", sid_to_string(sid_string, sid));
741 DEBUG(10,("wcache_save_sid_to_name: %s -> %s\n", sid_string, name));
746 static void wcache_save_user(struct winbindd_domain *domain, NTSTATUS status, WINBIND_USERINFO *info)
748 struct cache_entry *centry;
751 centry = centry_start(domain, status);
754 centry_put_string(centry, info->acct_name);
755 centry_put_string(centry, info->full_name);
756 centry_put_string(centry, info->homedir);
757 centry_put_string(centry, info->shell);
758 centry_put_sid(centry, &info->user_sid);
759 centry_put_sid(centry, &info->group_sid);
760 centry_end(centry, "U/%s", sid_to_string(sid_string, &info->user_sid));
761 DEBUG(10,("wcache_save_user: %s (acct_name %s)\n", sid_string, info->acct_name));
765 static void wcache_save_lockout_policy(struct winbindd_domain *domain, NTSTATUS status, SAM_UNK_INFO_12 *lockout_policy)
767 struct cache_entry *centry;
769 centry = centry_start(domain, status);
773 centry_put_nttime(centry, lockout_policy->duration);
774 centry_put_nttime(centry, lockout_policy->reset_count);
775 centry_put_uint16(centry, lockout_policy->bad_attempt_lockout);
777 centry_end(centry, "LOC_POL/%s", domain->name);
779 DEBUG(10,("wcache_save_lockout_policy: %s\n", domain->name));
784 static void wcache_save_password_policy(struct winbindd_domain *domain, NTSTATUS status, SAM_UNK_INFO_1 *policy)
786 struct cache_entry *centry;
788 centry = centry_start(domain, status);
792 centry_put_uint16(centry, policy->min_length_password);
793 centry_put_uint16(centry, policy->password_history);
794 centry_put_uint32(centry, policy->password_properties);
795 centry_put_nttime(centry, policy->expire);
796 centry_put_nttime(centry, policy->min_passwordage);
798 centry_end(centry, "PWD_POL/%s", domain->name);
800 DEBUG(10,("wcache_save_password_policy: %s\n", domain->name));
805 NTSTATUS wcache_cached_creds_exist(struct winbindd_domain *domain, const DOM_SID *sid)
807 struct winbind_cache *cache = get_cache(domain);
813 return NT_STATUS_INTERNAL_DB_ERROR;
816 if (is_null_sid(sid)) {
817 return NT_STATUS_INVALID_SID;
820 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
821 return NT_STATUS_INVALID_SID;
824 fstr_sprintf(key_str, "CRED/%s", sid_string_static(sid));
826 data = tdb_fetch(cache->tdb, make_tdb_data(key_str, strlen(key_str)));
828 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
831 SAFE_FREE(data.dptr);
835 /* Lookup creds for a SID */
836 NTSTATUS wcache_get_creds(struct winbindd_domain *domain,
839 const uint8 **cached_nt_pass)
841 struct winbind_cache *cache = get_cache(domain);
842 struct cache_entry *centry = NULL;
848 return NT_STATUS_INTERNAL_DB_ERROR;
851 if (is_null_sid(sid)) {
852 return NT_STATUS_INVALID_SID;
855 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
856 return NT_STATUS_INVALID_SID;
859 centry = wcache_fetch(cache, domain, "CRED/%s", sid_string_static(sid));
862 DEBUG(10,("wcache_get_creds: entry for [CRED/%s] not found\n",
863 sid_string_static(sid)));
864 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
867 t = centry_time(centry);
868 *cached_nt_pass = (const uint8 *)centry_string(centry, mem_ctx);
871 dump_data(100, (const char *)cached_nt_pass, NT_HASH_LEN);
873 status = centry->status;
875 DEBUG(10,("wcache_get_creds: [Cached] - cached creds for user %s status: %s\n",
876 sid_string_static(sid), nt_errstr(status) ));
882 NTSTATUS wcache_save_creds(struct winbindd_domain *domain,
885 const uint8 nt_pass[NT_HASH_LEN])
887 struct cache_entry *centry;
891 if (is_null_sid(sid)) {
892 return NT_STATUS_INVALID_SID;
895 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
896 return NT_STATUS_INVALID_SID;
899 centry = centry_start(domain, NT_STATUS_OK);
901 return NT_STATUS_INTERNAL_DB_ERROR;
905 dump_data(100, (const char *)nt_pass, NT_HASH_LEN);
908 centry_put_time(centry, time(NULL));
909 centry_put_string(centry, (const char *)nt_pass);
910 centry_end(centry, "CRED/%s", sid_to_string(sid_string, sid));
912 DEBUG(10,("wcache_save_creds: %s\n", sid_string));
920 /* Query display info. This is the basic user list fn */
921 static NTSTATUS query_user_list(struct winbindd_domain *domain,
924 WINBIND_USERINFO **info)
926 struct winbind_cache *cache = get_cache(domain);
927 struct cache_entry *centry = NULL;
929 unsigned int i, retry;
934 centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
938 *num_entries = centry_uint32(centry);
940 if (*num_entries == 0)
943 (*info) = TALLOC_ARRAY(mem_ctx, WINBIND_USERINFO, *num_entries);
945 smb_panic("query_user_list out of memory");
946 for (i=0; i<(*num_entries); i++) {
947 (*info)[i].acct_name = centry_string(centry, mem_ctx);
948 (*info)[i].full_name = centry_string(centry, mem_ctx);
949 (*info)[i].homedir = centry_string(centry, mem_ctx);
950 (*info)[i].shell = centry_string(centry, mem_ctx);
951 centry_sid(centry, mem_ctx, &(*info)[i].user_sid);
952 centry_sid(centry, mem_ctx, &(*info)[i].group_sid);
956 status = centry->status;
958 DEBUG(10,("query_user_list: [Cached] - cached list for domain %s status: %s\n",
959 domain->name, nt_errstr(status) ));
968 /* Return status value returned by seq number check */
970 if (!NT_STATUS_IS_OK(domain->last_status))
971 return domain->last_status;
973 /* Put the query_user_list() in a retry loop. There appears to be
974 * some bug either with Windows 2000 or Samba's handling of large
975 * rpc replies. This manifests itself as sudden disconnection
976 * at a random point in the enumeration of a large (60k) user list.
977 * The retry loop simply tries the operation again. )-: It's not
978 * pretty but an acceptable workaround until we work out what the
979 * real problem is. */
984 DEBUG(10,("query_user_list: [Cached] - doing backend query for list for domain %s\n",
987 status = domain->backend->query_user_list(domain, mem_ctx, num_entries, info);
988 if (!NT_STATUS_IS_OK(status))
989 DEBUG(3, ("query_user_list: returned 0x%08x, "
990 "retrying\n", NT_STATUS_V(status)));
991 if (NT_STATUS_EQUAL(status, NT_STATUS_UNSUCCESSFUL)) {
992 DEBUG(3, ("query_user_list: flushing "
993 "connection cache\n"));
994 invalidate_cm_connection(&domain->conn);
997 } while (NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_UNSUCCESSFUL) &&
1001 refresh_sequence_number(domain, False);
1002 centry = centry_start(domain, status);
1005 centry_put_uint32(centry, *num_entries);
1006 for (i=0; i<(*num_entries); i++) {
1007 centry_put_string(centry, (*info)[i].acct_name);
1008 centry_put_string(centry, (*info)[i].full_name);
1009 centry_put_string(centry, (*info)[i].homedir);
1010 centry_put_string(centry, (*info)[i].shell);
1011 centry_put_sid(centry, &(*info)[i].user_sid);
1012 centry_put_sid(centry, &(*info)[i].group_sid);
1013 if (domain->backend->consistent) {
1014 /* when the backend is consistent we can pre-prime some mappings */
1015 wcache_save_name_to_sid(domain, NT_STATUS_OK,
1017 (*info)[i].acct_name,
1018 &(*info)[i].user_sid,
1020 wcache_save_sid_to_name(domain, NT_STATUS_OK,
1021 &(*info)[i].user_sid,
1023 (*info)[i].acct_name,
1025 wcache_save_user(domain, NT_STATUS_OK, &(*info)[i]);
1028 centry_end(centry, "UL/%s", domain->name);
1029 centry_free(centry);
1035 /* list all domain groups */
1036 static NTSTATUS enum_dom_groups(struct winbindd_domain *domain,
1037 TALLOC_CTX *mem_ctx,
1038 uint32 *num_entries,
1039 struct acct_info **info)
1041 struct winbind_cache *cache = get_cache(domain);
1042 struct cache_entry *centry = NULL;
1049 centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
1053 *num_entries = centry_uint32(centry);
1055 if (*num_entries == 0)
1058 (*info) = TALLOC_ARRAY(mem_ctx, struct acct_info, *num_entries);
1060 smb_panic("enum_dom_groups out of memory");
1061 for (i=0; i<(*num_entries); i++) {
1062 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1063 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1064 (*info)[i].rid = centry_uint32(centry);
1068 status = centry->status;
1070 DEBUG(10,("enum_dom_groups: [Cached] - cached list for domain %s status: %s\n",
1071 domain->name, nt_errstr(status) ));
1073 centry_free(centry);
1080 /* Return status value returned by seq number check */
1082 if (!NT_STATUS_IS_OK(domain->last_status))
1083 return domain->last_status;
1085 DEBUG(10,("enum_dom_groups: [Cached] - doing backend query for list for domain %s\n",
1088 status = domain->backend->enum_dom_groups(domain, mem_ctx, num_entries, info);
1091 refresh_sequence_number(domain, False);
1092 centry = centry_start(domain, status);
1095 centry_put_uint32(centry, *num_entries);
1096 for (i=0; i<(*num_entries); i++) {
1097 centry_put_string(centry, (*info)[i].acct_name);
1098 centry_put_string(centry, (*info)[i].acct_desc);
1099 centry_put_uint32(centry, (*info)[i].rid);
1101 centry_end(centry, "GL/%s/domain", domain->name);
1102 centry_free(centry);
1108 /* list all domain groups */
1109 static NTSTATUS enum_local_groups(struct winbindd_domain *domain,
1110 TALLOC_CTX *mem_ctx,
1111 uint32 *num_entries,
1112 struct acct_info **info)
1114 struct winbind_cache *cache = get_cache(domain);
1115 struct cache_entry *centry = NULL;
1122 centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
1126 *num_entries = centry_uint32(centry);
1128 if (*num_entries == 0)
1131 (*info) = TALLOC_ARRAY(mem_ctx, struct acct_info, *num_entries);
1133 smb_panic("enum_dom_groups out of memory");
1134 for (i=0; i<(*num_entries); i++) {
1135 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1136 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1137 (*info)[i].rid = centry_uint32(centry);
1142 /* If we are returning cached data and the domain controller
1143 is down then we don't know whether the data is up to date
1144 or not. Return NT_STATUS_MORE_PROCESSING_REQUIRED to
1147 if (wcache_server_down(domain)) {
1148 DEBUG(10, ("enum_local_groups: returning cached user list and server was down\n"));
1149 status = NT_STATUS_MORE_PROCESSING_REQUIRED;
1151 status = centry->status;
1153 DEBUG(10,("enum_local_groups: [Cached] - cached list for domain %s status: %s\n",
1154 domain->name, nt_errstr(status) ));
1156 centry_free(centry);
1163 /* Return status value returned by seq number check */
1165 if (!NT_STATUS_IS_OK(domain->last_status))
1166 return domain->last_status;
1168 DEBUG(10,("enum_local_groups: [Cached] - doing backend query for list for domain %s\n",
1171 status = domain->backend->enum_local_groups(domain, mem_ctx, num_entries, info);
1174 refresh_sequence_number(domain, False);
1175 centry = centry_start(domain, status);
1178 centry_put_uint32(centry, *num_entries);
1179 for (i=0; i<(*num_entries); i++) {
1180 centry_put_string(centry, (*info)[i].acct_name);
1181 centry_put_string(centry, (*info)[i].acct_desc);
1182 centry_put_uint32(centry, (*info)[i].rid);
1184 centry_end(centry, "GL/%s/local", domain->name);
1185 centry_free(centry);
1191 /* convert a single name to a sid in a domain */
1192 static NTSTATUS name_to_sid(struct winbindd_domain *domain,
1193 TALLOC_CTX *mem_ctx,
1194 const char *domain_name,
1197 enum SID_NAME_USE *type)
1199 struct winbind_cache *cache = get_cache(domain);
1200 struct cache_entry *centry = NULL;
1207 fstrcpy(uname, name);
1209 centry = wcache_fetch(cache, domain, "NS/%s/%s", domain_name, uname);
1212 *type = (enum SID_NAME_USE)centry_uint32(centry);
1213 status = centry->status;
1214 if (NT_STATUS_IS_OK(status)) {
1215 centry_sid(centry, mem_ctx, sid);
1218 DEBUG(10,("name_to_sid: [Cached] - cached name for domain %s status: %s\n",
1219 domain->name, nt_errstr(status) ));
1221 centry_free(centry);
1227 /* If the seq number check indicated that there is a problem
1228 * with this DC, then return that status... except for
1229 * access_denied. This is special because the dc may be in
1230 * "restrict anonymous = 1" mode, in which case it will deny
1231 * most unauthenticated operations, but *will* allow the LSA
1232 * name-to-sid that we try as a fallback. */
1234 if (!(NT_STATUS_IS_OK(domain->last_status)
1235 || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1236 return domain->last_status;
1238 DEBUG(10,("name_to_sid: [Cached] - doing backend query for name for domain %s\n",
1241 status = domain->backend->name_to_sid(domain, mem_ctx, domain_name, name, sid, type);
1244 if (domain->online || !is_null_sid(sid)) {
1245 wcache_save_name_to_sid(domain, status, domain_name, name, sid, *type);
1248 if (NT_STATUS_IS_OK(status)) {
1249 strupper_m(CONST_DISCARD(char *,domain_name));
1250 strlower_m(CONST_DISCARD(char *,name));
1251 wcache_save_sid_to_name(domain, status, sid, domain_name, name, *type);
1257 /* convert a sid to a user or group name. The sid is guaranteed to be in the domain
1259 static NTSTATUS sid_to_name(struct winbindd_domain *domain,
1260 TALLOC_CTX *mem_ctx,
1264 enum SID_NAME_USE *type)
1266 struct winbind_cache *cache = get_cache(domain);
1267 struct cache_entry *centry = NULL;
1274 centry = wcache_fetch(cache, domain, "SN/%s", sid_to_string(sid_string, sid));
1277 if (NT_STATUS_IS_OK(centry->status)) {
1278 *type = (enum SID_NAME_USE)centry_uint32(centry);
1279 *domain_name = centry_string(centry, mem_ctx);
1280 *name = centry_string(centry, mem_ctx);
1282 status = centry->status;
1284 DEBUG(10,("sid_to_name: [Cached] - cached name for domain %s status: %s\n",
1285 domain->name, nt_errstr(status) ));
1287 centry_free(centry);
1292 *domain_name = NULL;
1294 /* If the seq number check indicated that there is a problem
1295 * with this DC, then return that status... except for
1296 * access_denied. This is special because the dc may be in
1297 * "restrict anonymous = 1" mode, in which case it will deny
1298 * most unauthenticated operations, but *will* allow the LSA
1299 * sid-to-name that we try as a fallback. */
1301 if (!(NT_STATUS_IS_OK(domain->last_status)
1302 || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1303 return domain->last_status;
1305 DEBUG(10,("sid_to_name: [Cached] - doing backend query for name for domain %s\n",
1308 status = domain->backend->sid_to_name(domain, mem_ctx, sid, domain_name, name, type);
1311 refresh_sequence_number(domain, False);
1312 wcache_save_sid_to_name(domain, status, sid, *domain_name, *name, *type);
1314 /* We can't save the name to sid mapping here, as with sid history a
1315 * later name2sid would give the wrong sid. */
1320 static NTSTATUS rids_to_names(struct winbindd_domain *domain,
1321 TALLOC_CTX *mem_ctx,
1322 const DOM_SID *domain_sid,
1327 enum SID_NAME_USE **types)
1329 struct winbind_cache *cache = get_cache(domain);
1331 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
1335 *domain_name = NULL;
1343 if (num_rids == 0) {
1344 return NT_STATUS_OK;
1347 *names = TALLOC_ARRAY(mem_ctx, char *, num_rids);
1348 *types = TALLOC_ARRAY(mem_ctx, enum SID_NAME_USE, num_rids);
1350 if ((*names == NULL) || (*types == NULL)) {
1351 result = NT_STATUS_NO_MEMORY;
1355 have_mapped = have_unmapped = False;
1357 for (i=0; i<num_rids; i++) {
1359 struct cache_entry *centry;
1361 if (!sid_compose(&sid, domain_sid, rids[i])) {
1362 result = NT_STATUS_INTERNAL_ERROR;
1366 centry = wcache_fetch(cache, domain, "SN/%s",
1367 sid_string_static(&sid));
1372 (*types)[i] = SID_NAME_UNKNOWN;
1373 (*names)[i] = talloc_strdup(*names, "");
1375 if (NT_STATUS_IS_OK(centry->status)) {
1378 (*types)[i] = (enum SID_NAME_USE)centry_uint32(centry);
1379 dom = centry_string(centry, mem_ctx);
1380 if (*domain_name == NULL) {
1385 (*names)[i] = centry_string(centry, *names);
1387 have_unmapped = True;
1390 centry_free(centry);
1394 return NT_STATUS_NONE_MAPPED;
1396 if (!have_unmapped) {
1397 return NT_STATUS_OK;
1399 return STATUS_SOME_UNMAPPED;
1403 TALLOC_FREE(*names);
1404 TALLOC_FREE(*types);
1406 result = domain->backend->rids_to_names(domain, mem_ctx, domain_sid,
1407 rids, num_rids, domain_name,
1410 if (!NT_STATUS_IS_OK(result) &&
1411 !NT_STATUS_EQUAL(result, STATUS_SOME_UNMAPPED)) {
1415 refresh_sequence_number(domain, False);
1417 for (i=0; i<num_rids; i++) {
1421 if (!sid_compose(&sid, domain_sid, rids[i])) {
1422 result = NT_STATUS_INTERNAL_ERROR;
1426 status = (*types)[i] == SID_NAME_UNKNOWN ?
1427 NT_STATUS_NONE_MAPPED : NT_STATUS_OK;
1429 wcache_save_sid_to_name(domain, status, &sid, *domain_name,
1430 (*names)[i], (*types)[i]);
1437 TALLOC_FREE(*names);
1438 TALLOC_FREE(*types);
1442 /* Lookup user information from a rid */
1443 static NTSTATUS query_user(struct winbindd_domain *domain,
1444 TALLOC_CTX *mem_ctx,
1445 const DOM_SID *user_sid,
1446 WINBIND_USERINFO *info)
1448 struct winbind_cache *cache = get_cache(domain);
1449 struct cache_entry *centry = NULL;
1455 centry = wcache_fetch(cache, domain, "U/%s", sid_string_static(user_sid));
1457 /* If we have an access denied cache entry and a cached info3 in the
1458 samlogon cache then do a query. This will force the rpc back end
1459 to return the info3 data. */
1461 if (NT_STATUS_V(domain->last_status) == NT_STATUS_V(NT_STATUS_ACCESS_DENIED) &&
1462 netsamlogon_cache_have(user_sid)) {
1463 DEBUG(10, ("query_user: cached access denied and have cached info3\n"));
1464 domain->last_status = NT_STATUS_OK;
1465 centry_free(centry);
1472 info->acct_name = centry_string(centry, mem_ctx);
1473 info->full_name = centry_string(centry, mem_ctx);
1474 info->homedir = centry_string(centry, mem_ctx);
1475 info->shell = centry_string(centry, mem_ctx);
1476 centry_sid(centry, mem_ctx, &info->user_sid);
1477 centry_sid(centry, mem_ctx, &info->group_sid);
1478 status = centry->status;
1480 DEBUG(10,("query_user: [Cached] - cached info for domain %s status: %s\n",
1481 domain->name, nt_errstr(status) ));
1483 centry_free(centry);
1489 /* Return status value returned by seq number check */
1491 if (!NT_STATUS_IS_OK(domain->last_status))
1492 return domain->last_status;
1494 DEBUG(10,("sid_to_name: [Cached] - doing backend query for info for domain %s\n",
1497 status = domain->backend->query_user(domain, mem_ctx, user_sid, info);
1500 refresh_sequence_number(domain, False);
1501 wcache_save_user(domain, status, info);
1507 /* Lookup groups a user is a member of. */
1508 static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
1509 TALLOC_CTX *mem_ctx,
1510 const DOM_SID *user_sid,
1511 uint32 *num_groups, DOM_SID **user_gids)
1513 struct winbind_cache *cache = get_cache(domain);
1514 struct cache_entry *centry = NULL;
1522 centry = wcache_fetch(cache, domain, "UG/%s", sid_to_string(sid_string, user_sid));
1524 /* If we have an access denied cache entry and a cached info3 in the
1525 samlogon cache then do a query. This will force the rpc back end
1526 to return the info3 data. */
1528 if (NT_STATUS_V(domain->last_status) == NT_STATUS_V(NT_STATUS_ACCESS_DENIED) &&
1529 netsamlogon_cache_have(user_sid)) {
1530 DEBUG(10, ("lookup_usergroups: cached access denied and have cached info3\n"));
1531 domain->last_status = NT_STATUS_OK;
1532 centry_free(centry);
1539 *num_groups = centry_uint32(centry);
1541 if (*num_groups == 0)
1544 (*user_gids) = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_groups);
1546 smb_panic("lookup_usergroups out of memory");
1547 for (i=0; i<(*num_groups); i++) {
1548 centry_sid(centry, mem_ctx, &(*user_gids)[i]);
1552 status = centry->status;
1554 DEBUG(10,("lookup_usergroups: [Cached] - cached info for domain %s status: %s\n",
1555 domain->name, nt_errstr(status) ));
1557 centry_free(centry);
1562 (*user_gids) = NULL;
1564 /* Return status value returned by seq number check */
1566 if (!NT_STATUS_IS_OK(domain->last_status))
1567 return domain->last_status;
1569 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info for domain %s\n",
1572 status = domain->backend->lookup_usergroups(domain, mem_ctx, user_sid, num_groups, user_gids);
1575 refresh_sequence_number(domain, False);
1576 centry = centry_start(domain, status);
1579 centry_put_uint32(centry, *num_groups);
1580 for (i=0; i<(*num_groups); i++) {
1581 centry_put_sid(centry, &(*user_gids)[i]);
1583 centry_end(centry, "UG/%s", sid_to_string(sid_string, user_sid));
1584 centry_free(centry);
1590 static NTSTATUS lookup_useraliases(struct winbindd_domain *domain,
1591 TALLOC_CTX *mem_ctx,
1592 uint32 num_sids, const DOM_SID *sids,
1593 uint32 *num_aliases, uint32 **alias_rids)
1595 struct winbind_cache *cache = get_cache(domain);
1596 struct cache_entry *centry = NULL;
1598 char *sidlist = talloc_strdup(mem_ctx, "");
1604 if (num_sids == 0) {
1607 return NT_STATUS_OK;
1610 /* We need to cache indexed by the whole list of SIDs, the aliases
1611 * resulting might come from any of the SIDs. */
1613 for (i=0; i<num_sids; i++) {
1614 sidlist = talloc_asprintf(mem_ctx, "%s/%s", sidlist,
1615 sid_string_static(&sids[i]));
1616 if (sidlist == NULL)
1617 return NT_STATUS_NO_MEMORY;
1620 centry = wcache_fetch(cache, domain, "UA%s", sidlist);
1625 *num_aliases = centry_uint32(centry);
1628 (*alias_rids) = TALLOC_ARRAY(mem_ctx, uint32, *num_aliases);
1630 if ((*num_aliases != 0) && ((*alias_rids) == NULL)) {
1631 centry_free(centry);
1632 return NT_STATUS_NO_MEMORY;
1635 for (i=0; i<(*num_aliases); i++)
1636 (*alias_rids)[i] = centry_uint32(centry);
1638 status = centry->status;
1640 DEBUG(10,("lookup_useraliases: [Cached] - cached info for domain: %s "
1641 "status %s\n", domain->name, nt_errstr(status)));
1643 centry_free(centry);
1648 (*alias_rids) = NULL;
1650 if (!NT_STATUS_IS_OK(domain->last_status))
1651 return domain->last_status;
1653 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info "
1654 "for domain %s\n", domain->name ));
1656 status = domain->backend->lookup_useraliases(domain, mem_ctx,
1658 num_aliases, alias_rids);
1661 refresh_sequence_number(domain, False);
1662 centry = centry_start(domain, status);
1665 centry_put_uint32(centry, *num_aliases);
1666 for (i=0; i<(*num_aliases); i++)
1667 centry_put_uint32(centry, (*alias_rids)[i]);
1668 centry_end(centry, "UA%s", sidlist);
1669 centry_free(centry);
1676 static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
1677 TALLOC_CTX *mem_ctx,
1678 const DOM_SID *group_sid, uint32 *num_names,
1679 DOM_SID **sid_mem, char ***names,
1680 uint32 **name_types)
1682 struct winbind_cache *cache = get_cache(domain);
1683 struct cache_entry *centry = NULL;
1691 centry = wcache_fetch(cache, domain, "GM/%s", sid_to_string(sid_string, group_sid));
1695 *num_names = centry_uint32(centry);
1697 if (*num_names == 0)
1700 (*sid_mem) = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_names);
1701 (*names) = TALLOC_ARRAY(mem_ctx, char *, *num_names);
1702 (*name_types) = TALLOC_ARRAY(mem_ctx, uint32, *num_names);
1704 if (! (*sid_mem) || ! (*names) || ! (*name_types)) {
1705 smb_panic("lookup_groupmem out of memory");
1708 for (i=0; i<(*num_names); i++) {
1709 centry_sid(centry, mem_ctx, &(*sid_mem)[i]);
1710 (*names)[i] = centry_string(centry, mem_ctx);
1711 (*name_types)[i] = centry_uint32(centry);
1715 status = centry->status;
1717 DEBUG(10,("lookup_groupmem: [Cached] - cached info for domain %s status: %s\n",
1718 domain->name, nt_errstr(status)));
1720 centry_free(centry);
1727 (*name_types) = NULL;
1729 /* Return status value returned by seq number check */
1731 if (!NT_STATUS_IS_OK(domain->last_status))
1732 return domain->last_status;
1734 DEBUG(10,("lookup_groupmem: [Cached] - doing backend query for info for domain %s\n",
1737 status = domain->backend->lookup_groupmem(domain, mem_ctx, group_sid, num_names,
1738 sid_mem, names, name_types);
1741 refresh_sequence_number(domain, False);
1742 centry = centry_start(domain, status);
1745 centry_put_uint32(centry, *num_names);
1746 for (i=0; i<(*num_names); i++) {
1747 centry_put_sid(centry, &(*sid_mem)[i]);
1748 centry_put_string(centry, (*names)[i]);
1749 centry_put_uint32(centry, (*name_types)[i]);
1751 centry_end(centry, "GM/%s", sid_to_string(sid_string, group_sid));
1752 centry_free(centry);
1758 /* find the sequence number for a domain */
1759 static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
1761 refresh_sequence_number(domain, False);
1763 *seq = domain->sequence_number;
1765 return NT_STATUS_OK;
1768 /* enumerate trusted domains
1769 * (we need to have the list of trustdoms in the cache when we go offline) -
1771 static NTSTATUS trusted_domains(struct winbindd_domain *domain,
1772 TALLOC_CTX *mem_ctx,
1773 uint32 *num_domains,
1778 struct winbind_cache *cache = get_cache(domain);
1779 struct cache_entry *centry = NULL;
1786 centry = wcache_fetch(cache, domain, "TRUSTDOMS/%s", domain->name);
1792 *num_domains = centry_uint32(centry);
1794 (*names) = TALLOC_ARRAY(mem_ctx, char *, *num_domains);
1795 (*alt_names) = TALLOC_ARRAY(mem_ctx, char *, *num_domains);
1796 (*dom_sids) = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_domains);
1798 if (! (*dom_sids) || ! (*names) || ! (*alt_names)) {
1799 smb_panic("trusted_domains out of memory");
1802 for (i=0; i<(*num_domains); i++) {
1803 (*names)[i] = centry_string(centry, mem_ctx);
1804 (*alt_names)[i] = centry_string(centry, mem_ctx);
1805 centry_sid(centry, mem_ctx, &(*dom_sids)[i]);
1808 status = centry->status;
1810 DEBUG(10,("trusted_domains: [Cached] - cached info for domain %s (%d trusts) status: %s\n",
1811 domain->name, *num_domains, nt_errstr(status) ));
1813 centry_free(centry);
1820 (*alt_names) = NULL;
1822 /* Return status value returned by seq number check */
1824 if (!NT_STATUS_IS_OK(domain->last_status))
1825 return domain->last_status;
1827 DEBUG(10,("trusted_domains: [Cached] - doing backend query for info for domain %s\n",
1830 status = domain->backend->trusted_domains(domain, mem_ctx, num_domains,
1831 names, alt_names, dom_sids);
1833 /* no trusts gives NT_STATUS_NO_MORE_ENTRIES resetting to NT_STATUS_OK
1834 * so that the generic centry handling still applies correctly -
1837 if (!NT_STATUS_IS_ERR(status)) {
1838 status = NT_STATUS_OK;
1842 refresh_sequence_number(domain, False);
1844 centry = centry_start(domain, status);
1848 centry_put_uint32(centry, *num_domains);
1850 for (i=0; i<(*num_domains); i++) {
1851 centry_put_string(centry, (*names)[i]);
1852 centry_put_string(centry, (*alt_names)[i]);
1853 centry_put_sid(centry, &(*dom_sids)[i]);
1856 centry_end(centry, "TRUSTDOMS/%s", domain->name);
1858 centry_free(centry);
1864 /* get lockout policy */
1865 static NTSTATUS lockout_policy(struct winbindd_domain *domain,
1866 TALLOC_CTX *mem_ctx,
1867 SAM_UNK_INFO_12 *policy){
1868 struct winbind_cache *cache = get_cache(domain);
1869 struct cache_entry *centry = NULL;
1875 centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
1880 policy->duration = centry_nttime(centry);
1881 policy->reset_count = centry_nttime(centry);
1882 policy->bad_attempt_lockout = centry_uint16(centry);
1884 status = centry->status;
1886 DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
1887 domain->name, nt_errstr(status) ));
1889 centry_free(centry);
1893 ZERO_STRUCTP(policy);
1895 /* Return status value returned by seq number check */
1897 if (!NT_STATUS_IS_OK(domain->last_status))
1898 return domain->last_status;
1900 DEBUG(10,("lockout_policy: [Cached] - doing backend query for info for domain %s\n",
1903 status = domain->backend->lockout_policy(domain, mem_ctx, policy);
1906 refresh_sequence_number(domain, False);
1907 wcache_save_lockout_policy(domain, status, policy);
1912 /* get password policy */
1913 static NTSTATUS password_policy(struct winbindd_domain *domain,
1914 TALLOC_CTX *mem_ctx,
1915 SAM_UNK_INFO_1 *policy)
1917 struct winbind_cache *cache = get_cache(domain);
1918 struct cache_entry *centry = NULL;
1924 centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
1929 policy->min_length_password = centry_uint16(centry);
1930 policy->password_history = centry_uint16(centry);
1931 policy->password_properties = centry_uint32(centry);
1932 policy->expire = centry_nttime(centry);
1933 policy->min_passwordage = centry_nttime(centry);
1935 status = centry->status;
1937 DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
1938 domain->name, nt_errstr(status) ));
1940 centry_free(centry);
1944 ZERO_STRUCTP(policy);
1946 /* Return status value returned by seq number check */
1948 if (!NT_STATUS_IS_OK(domain->last_status))
1949 return domain->last_status;
1951 DEBUG(10,("password_policy: [Cached] - doing backend query for info for domain %s\n",
1954 status = domain->backend->password_policy(domain, mem_ctx, policy);
1957 refresh_sequence_number(domain, False);
1958 wcache_save_password_policy(domain, status, policy);
1964 /* Invalidate cached user and group lists coherently */
1966 static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
1969 if (strncmp(kbuf.dptr, "UL/", 3) == 0 ||
1970 strncmp(kbuf.dptr, "GL/", 3) == 0)
1971 tdb_delete(the_tdb, kbuf);
1976 /* Invalidate the getpwnam and getgroups entries for a winbindd domain */
1978 void wcache_invalidate_samlogon(struct winbindd_domain *domain,
1979 NET_USER_INFO_3 *info3)
1981 struct winbind_cache *cache;
1986 cache = get_cache(domain);
1987 netsamlogon_clear_cached_user(cache->tdb, info3);
1990 void wcache_invalidate_cache(void)
1992 struct winbindd_domain *domain;
1994 for (domain = domain_list(); domain; domain = domain->next) {
1995 struct winbind_cache *cache = get_cache(domain);
1997 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
1998 "entries for %s\n", domain->name));
2000 tdb_traverse(cache->tdb, traverse_fn, NULL);
2004 static BOOL init_wcache(void)
2006 if (wcache == NULL) {
2007 wcache = SMB_XMALLOC_P(struct winbind_cache);
2008 ZERO_STRUCTP(wcache);
2011 if (wcache->tdb != NULL)
2014 /* when working offline we must not clear the cache on restart */
2015 wcache->tdb = tdb_open_log(lock_path("winbindd_cache.tdb"),
2016 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
2017 lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST),
2018 O_RDWR|O_CREAT, 0600);
2020 if (wcache->tdb == NULL) {
2021 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
2028 void cache_store_response(pid_t pid, struct winbindd_response *response)
2035 DEBUG(10, ("Storing response for pid %d, len %d\n",
2036 pid, response->length));
2038 fstr_sprintf(key_str, "DR/%d", pid);
2039 if (tdb_store(wcache->tdb, string_tdb_data(key_str),
2040 make_tdb_data((const char *)response, sizeof(*response)),
2044 if (response->length == sizeof(*response))
2047 /* There's extra data */
2049 DEBUG(10, ("Storing extra data: len=%d\n",
2050 (int)(response->length - sizeof(*response))));
2052 fstr_sprintf(key_str, "DE/%d", pid);
2053 if (tdb_store(wcache->tdb, string_tdb_data(key_str),
2054 make_tdb_data((const char *)response->extra_data.data,
2055 response->length - sizeof(*response)),
2059 /* We could not store the extra data, make sure the tdb does not
2060 * contain a main record with wrong dangling extra data */
2062 fstr_sprintf(key_str, "DR/%d", pid);
2063 tdb_delete(wcache->tdb, string_tdb_data(key_str));
2068 BOOL cache_retrieve_response(pid_t pid, struct winbindd_response * response)
2076 DEBUG(10, ("Retrieving response for pid %d\n", pid));
2078 fstr_sprintf(key_str, "DR/%d", pid);
2079 data = tdb_fetch(wcache->tdb, string_tdb_data(key_str));
2081 if (data.dptr == NULL)
2084 if (data.dsize != sizeof(*response))
2087 memcpy(response, data.dptr, data.dsize);
2088 SAFE_FREE(data.dptr);
2090 if (response->length == sizeof(*response)) {
2091 response->extra_data.data = NULL;
2095 /* There's extra data */
2097 DEBUG(10, ("Retrieving extra data length=%d\n",
2098 (int)(response->length - sizeof(*response))));
2100 fstr_sprintf(key_str, "DE/%d", pid);
2101 data = tdb_fetch(wcache->tdb, string_tdb_data(key_str));
2103 if (data.dptr == NULL) {
2104 DEBUG(0, ("Did not find extra data\n"));
2108 if (data.dsize != (response->length - sizeof(*response))) {
2109 DEBUG(0, ("Invalid extra data length: %d\n", (int)data.dsize));
2110 SAFE_FREE(data.dptr);
2114 dump_data(11, data.dptr, data.dsize);
2116 response->extra_data.data = data.dptr;
2120 void cache_cleanup_response(pid_t pid)
2127 fstr_sprintf(key_str, "DR/%d", pid);
2128 tdb_delete(wcache->tdb, string_tdb_data(key_str));
2130 fstr_sprintf(key_str, "DE/%d", pid);
2131 tdb_delete(wcache->tdb, string_tdb_data(key_str));
2137 BOOL lookup_cached_sid(TALLOC_CTX *mem_ctx, const DOM_SID *sid,
2138 const char **domain_name, const char **name,
2139 enum SID_NAME_USE *type)
2141 struct winbindd_domain *domain;
2142 struct winbind_cache *cache;
2143 struct cache_entry *centry = NULL;
2146 domain = find_lookup_domain_from_sid(sid);
2147 if (domain == NULL) {
2151 cache = get_cache(domain);
2153 if (cache->tdb == NULL) {
2157 centry = wcache_fetch(cache, domain, "SN/%s", sid_string_static(sid));
2158 if (centry == NULL) {
2162 if (NT_STATUS_IS_OK(centry->status)) {
2163 *type = (enum SID_NAME_USE)centry_uint32(centry);
2164 *domain_name = centry_string(centry, mem_ctx);
2165 *name = centry_string(centry, mem_ctx);
2168 status = centry->status;
2169 centry_free(centry);
2170 return NT_STATUS_IS_OK(status);
2173 BOOL lookup_cached_name(TALLOC_CTX *mem_ctx,
2174 const char *domain_name,
2177 enum SID_NAME_USE *type)
2179 struct winbindd_domain *domain;
2180 struct winbind_cache *cache;
2181 struct cache_entry *centry = NULL;
2185 domain = find_lookup_domain_from_name(domain_name);
2186 if (domain == NULL) {
2190 cache = get_cache(domain);
2192 if (cache->tdb == NULL) {
2196 fstrcpy(uname, name);
2199 centry = wcache_fetch(cache, domain, "NS/%s/%s", domain_name, uname);
2200 if (centry == NULL) {
2204 if (NT_STATUS_IS_OK(centry->status)) {
2205 *type = (enum SID_NAME_USE)centry_uint32(centry);
2206 centry_sid(centry, mem_ctx, sid);
2209 status = centry->status;
2210 centry_free(centry);
2212 return NT_STATUS_IS_OK(status);
2215 void cache_name2sid(struct winbindd_domain *domain,
2216 const char *domain_name, const char *name,
2217 enum SID_NAME_USE type, const DOM_SID *sid)
2219 wcache_save_name_to_sid(domain, NT_STATUS_OK, domain_name, name,
2223 /* delete all centries that don't have NT_STATUS_OK set */
2224 static int traverse_fn_cleanup(TDB_CONTEXT *the_tdb, TDB_DATA kbuf,
2225 TDB_DATA dbuf, void *state)
2227 struct cache_entry *centry;
2229 centry = wcache_fetch_raw(kbuf.dptr);
2234 if (!NT_STATUS_IS_OK(centry->status)) {
2235 DEBUG(10,("deleting centry %s\n", kbuf.dptr));
2236 tdb_delete(the_tdb, kbuf);
2239 centry_free(centry);
2243 /* flush the cache */
2244 void wcache_flush_cache(void)
2246 extern BOOL opt_nocache;
2251 tdb_close(wcache->tdb);
2257 /* when working offline we must not clear the cache on restart */
2258 wcache->tdb = tdb_open_log(lock_path("winbindd_cache.tdb"),
2259 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
2260 lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST),
2261 O_RDWR|O_CREAT, 0600);
2264 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
2268 tdb_traverse(wcache->tdb, traverse_fn_cleanup, NULL);
2270 DEBUG(10,("wcache_flush_cache success\n"));
2273 /* Count cached creds */
2275 static int traverse_fn_cached_creds(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
2278 int *cred_count = (int*)state;
2280 if (strncmp(kbuf.dptr, "CRED/", 5) == 0) {
2286 NTSTATUS wcache_count_cached_creds(struct winbindd_domain *domain, int *count)
2288 struct winbind_cache *cache = get_cache(domain);
2293 return NT_STATUS_INTERNAL_DB_ERROR;
2296 tdb_traverse(cache->tdb, traverse_fn_cached_creds, (void *)count);
2298 return NT_STATUS_OK;
2302 struct cred_list *prev, *next;
2307 static struct cred_list *wcache_cred_list;
2309 static int traverse_fn_get_credlist(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
2312 struct cred_list *cred;
2314 if (strncmp(kbuf.dptr, "CRED/", 5) == 0) {
2316 cred = SMB_MALLOC_P(struct cred_list);
2318 DEBUG(0,("traverse_fn_remove_first_creds: failed to malloc new entry for list\n"));
2324 /* save a copy of the key */
2326 fstrcpy(cred->name, kbuf.dptr);
2327 DLIST_ADD(wcache_cred_list, cred);
2333 NTSTATUS wcache_remove_oldest_cached_creds(struct winbindd_domain *domain, const DOM_SID *sid)
2335 struct winbind_cache *cache = get_cache(domain);
2338 struct cred_list *cred, *oldest = NULL;
2341 return NT_STATUS_INTERNAL_DB_ERROR;
2344 /* we possibly already have an entry */
2345 if (sid && NT_STATUS_IS_OK(wcache_cached_creds_exist(domain, sid))) {
2349 DEBUG(11,("we already have an entry, deleting that\n"));
2351 fstr_sprintf(key_str, "CRED/%s", sid_string_static(sid));
2353 tdb_delete(cache->tdb, string_tdb_data(key_str));
2355 return NT_STATUS_OK;
2358 ret = tdb_traverse(cache->tdb, traverse_fn_get_credlist, NULL);
2360 return NT_STATUS_OK;
2361 } else if ((ret == -1) || (wcache_cred_list == NULL)) {
2362 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
2365 ZERO_STRUCTP(oldest);
2367 for (cred = wcache_cred_list; cred; cred = cred->next) {
2372 data = tdb_fetch(cache->tdb, make_tdb_data(cred->name, strlen(cred->name)));
2374 DEBUG(10,("wcache_remove_oldest_cached_creds: entry for [%s] not found\n",
2376 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
2380 t = IVAL(data.dptr, 0);
2381 SAFE_FREE(data.dptr);
2384 oldest = SMB_MALLOC_P(struct cred_list);
2385 if (oldest == NULL) {
2386 status = NT_STATUS_NO_MEMORY;
2390 fstrcpy(oldest->name, cred->name);
2391 oldest->created = t;
2395 if (t < oldest->created) {
2396 fstrcpy(oldest->name, cred->name);
2397 oldest->created = t;
2401 if (tdb_delete(cache->tdb, string_tdb_data(oldest->name)) == 0) {
2402 status = NT_STATUS_OK;
2404 status = NT_STATUS_UNSUCCESSFUL;
2407 SAFE_FREE(wcache_cred_list);
2413 /* Change the global online/offline state. */
2414 BOOL set_global_winbindd_state_offline(void)
2419 DEBUG(10,("set_global_winbindd_state_offline: offline requested.\n"));
2421 /* Only go offline if someone has created
2422 the key "WINBINDD_OFFLINE" in the cache tdb. */
2424 if (wcache == NULL || wcache->tdb == NULL) {
2425 DEBUG(10,("set_global_winbindd_state_offline: wcache not open yet.\n"));
2429 if (!lp_winbind_offline_logon()) {
2430 DEBUG(10,("set_global_winbindd_state_offline: rejecting.\n"));
2434 if (global_winbindd_offline_state) {
2435 /* Already offline. */
2439 /* wcache->tdb->ecode = 0; */
2441 data = tdb_fetch_bystring( wcache->tdb, "WINBINDD_OFFLINE" );
2443 /* As this is a key with no data we don't need to free, we
2444 check for existence by looking at tdb_err. */
2446 err = tdb_error(wcache->tdb);
2448 if (err == TDB_ERR_NOEXIST) {
2449 DEBUG(10,("set_global_winbindd_state_offline: offline state not set.\n"));
2452 DEBUG(10,("set_global_winbindd_state_offline: offline state set.\n"));
2453 global_winbindd_offline_state = True;
2458 void set_global_winbindd_state_online(void)
2460 DEBUG(10,("set_global_winbindd_state_online: online requested.\n"));
2462 if (!lp_winbind_offline_logon()) {
2463 DEBUG(10,("set_global_winbindd_state_online: rejecting.\n"));
2467 if (!global_winbindd_offline_state) {
2468 /* Already online. */
2471 global_winbindd_offline_state = False;
2477 /* Ensure there is no key "WINBINDD_OFFLINE" in the cache tdb. */
2478 tdb_delete_bystring(wcache->tdb, "WINBINDD_OFFLINE");
2481 BOOL get_global_winbindd_state_online(void)
2483 return global_winbindd_offline_state;
2486 /* the cache backend methods are exposed via this structure */
2487 struct winbindd_methods cache_methods = {