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(wcache->tdb->fd, &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 %d bytes, have %d\n",
233 sizeof(time_t), 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(mem_ctx, 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 (!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 refresh_sequence_number(domain, False);
529 va_start(ap, format);
530 smb_xvasprintf(&kstr, format, ap);
533 centry = wcache_fetch_raw(kstr);
534 if (centry == NULL) {
539 if (centry_expired(domain, kstr, centry)) {
541 DEBUG(10,("wcache_fetch: entry %s expired for domain %s\n",
542 kstr, domain->name ));
549 DEBUG(10,("wcache_fetch: returning entry %s for domain %s\n",
550 kstr, domain->name ));
557 make sure we have at least len bytes available in a centry
559 static void centry_expand(struct cache_entry *centry, uint32 len)
561 if (centry->len - centry->ofs >= len)
564 centry->data = SMB_REALLOC(centry->data, centry->len);
566 DEBUG(0,("out of memory: needed %d bytes in centry_expand\n", centry->len));
567 smb_panic("out of memory in centry_expand");
572 push a uint32 into a centry
574 static void centry_put_uint32(struct cache_entry *centry, uint32 v)
576 centry_expand(centry, 4);
577 SIVAL(centry->data, centry->ofs, v);
582 push a uint16 into a centry
584 static void centry_put_uint16(struct cache_entry *centry, uint16 v)
586 centry_expand(centry, 2);
587 SIVAL(centry->data, centry->ofs, v);
592 push a uint8 into a centry
594 static void centry_put_uint8(struct cache_entry *centry, uint8 v)
596 centry_expand(centry, 1);
597 SCVAL(centry->data, centry->ofs, v);
602 push a string into a centry
604 static void centry_put_string(struct cache_entry *centry, const char *s)
609 /* null strings are marked as len 0xFFFF */
610 centry_put_uint8(centry, 0xFF);
615 /* can't handle more than 254 char strings. Truncating is probably best */
617 DEBUG(10,("centry_put_string: truncating len (%d) to: 254\n", len));
620 centry_put_uint8(centry, len);
621 centry_expand(centry, len);
622 memcpy(centry->data + centry->ofs, s, len);
626 static void centry_put_sid(struct cache_entry *centry, const DOM_SID *sid)
629 centry_put_string(centry, sid_to_string(sid_string, sid));
633 push a NTTIME into a centry
635 static void centry_put_nttime(struct cache_entry *centry, NTTIME nt)
637 centry_expand(centry, 8);
638 SIVAL(centry->data, centry->ofs, nt.low);
640 SIVAL(centry->data, centry->ofs, nt.high);
645 push a time_t into a centry
647 static void centry_put_time(struct cache_entry *centry, time_t t)
649 centry_expand(centry, sizeof(time_t));
650 SIVAL(centry->data, centry->ofs, t); /* FIXME: is this correct ?? */
651 centry->ofs += sizeof(time_t);
655 start a centry for output. When finished, call centry_end()
657 struct cache_entry *centry_start(struct winbindd_domain *domain, NTSTATUS status)
659 struct cache_entry *centry;
664 centry = SMB_XMALLOC_P(struct cache_entry);
666 centry->len = 8192; /* reasonable default */
667 centry->data = SMB_XMALLOC_ARRAY(uint8, centry->len);
669 centry->sequence_number = domain->sequence_number;
670 centry_put_uint32(centry, NT_STATUS_V(status));
671 centry_put_uint32(centry, centry->sequence_number);
676 finish a centry and write it to the tdb
678 static void centry_end(struct cache_entry *centry, const char *format, ...) PRINTF_ATTRIBUTE(2,3);
679 static void centry_end(struct cache_entry *centry, const char *format, ...)
685 va_start(ap, format);
686 smb_xvasprintf(&kstr, format, ap);
690 key.dsize = strlen(kstr);
691 data.dptr = (char *)centry->data;
692 data.dsize = centry->ofs;
694 tdb_store(wcache->tdb, key, data, TDB_REPLACE);
698 static void wcache_save_name_to_sid(struct winbindd_domain *domain,
699 NTSTATUS status, const char *domain_name,
700 const char *name, const DOM_SID *sid,
701 enum SID_NAME_USE type)
703 struct cache_entry *centry;
706 centry = centry_start(domain, status);
709 centry_put_uint32(centry, type);
710 centry_put_sid(centry, sid);
711 fstrcpy(uname, name);
713 centry_end(centry, "NS/%s/%s", domain_name, uname);
714 DEBUG(10,("wcache_save_name_to_sid: %s -> %s\n", uname,
715 sid_string_static(sid)));
719 static void wcache_save_sid_to_name(struct winbindd_domain *domain, NTSTATUS status,
720 const DOM_SID *sid, const char *domain_name, const char *name, enum SID_NAME_USE type)
722 struct cache_entry *centry;
725 centry = centry_start(domain, status);
728 if (NT_STATUS_IS_OK(status)) {
729 centry_put_uint32(centry, type);
730 centry_put_string(centry, domain_name);
731 centry_put_string(centry, name);
733 centry_end(centry, "SN/%s", sid_to_string(sid_string, sid));
734 DEBUG(10,("wcache_save_sid_to_name: %s -> %s\n", sid_string, name));
739 static void wcache_save_user(struct winbindd_domain *domain, NTSTATUS status, WINBIND_USERINFO *info)
741 struct cache_entry *centry;
744 centry = centry_start(domain, status);
747 centry_put_string(centry, info->acct_name);
748 centry_put_string(centry, info->full_name);
749 centry_put_string(centry, info->homedir);
750 centry_put_string(centry, info->shell);
751 centry_put_sid(centry, &info->user_sid);
752 centry_put_sid(centry, &info->group_sid);
753 centry_end(centry, "U/%s", sid_to_string(sid_string, &info->user_sid));
754 DEBUG(10,("wcache_save_user: %s (acct_name %s)\n", sid_string, info->acct_name));
758 static void wcache_save_lockout_policy(struct winbindd_domain *domain, NTSTATUS status, SAM_UNK_INFO_12 *lockout_policy)
760 struct cache_entry *centry;
762 centry = centry_start(domain, status);
766 centry_put_nttime(centry, lockout_policy->duration);
767 centry_put_nttime(centry, lockout_policy->reset_count);
768 centry_put_uint16(centry, lockout_policy->bad_attempt_lockout);
770 centry_end(centry, "LOC_POL/%s", domain->name);
772 DEBUG(10,("wcache_save_lockout_policy: %s\n", domain->name));
777 static void wcache_save_password_policy(struct winbindd_domain *domain, NTSTATUS status, SAM_UNK_INFO_1 *password_policy)
779 struct cache_entry *centry;
781 centry = centry_start(domain, status);
785 centry_put_uint16(centry, password_policy->min_length_password);
786 centry_put_uint16(centry, password_policy->password_history);
787 centry_put_uint32(centry, password_policy->password_properties);
788 centry_put_nttime(centry, password_policy->expire);
789 centry_put_nttime(centry, password_policy->min_passwordage);
791 centry_end(centry, "PWD_POL/%s", domain->name);
793 DEBUG(10,("wcache_save_password_policy: %s\n", domain->name));
798 NTSTATUS wcache_cached_creds_exist(struct winbindd_domain *domain, const DOM_SID *sid)
800 struct winbind_cache *cache = get_cache(domain);
805 return NT_STATUS_INTERNAL_DB_ERROR;
808 fstr_sprintf(key_str, "CRED/%s", sid_string_static(sid));
810 data = tdb_fetch(cache->tdb, make_tdb_data(key_str, strlen(key_str)));
812 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
818 /* Lookup creds for a SID */
819 NTSTATUS wcache_get_creds(struct winbindd_domain *domain,
822 const uint8 **cached_nt_pass)
824 struct winbind_cache *cache = get_cache(domain);
825 struct cache_entry *centry = NULL;
830 return NT_STATUS_INTERNAL_DB_ERROR;
833 centry = wcache_fetch(cache, domain, "CRED/%s", sid_string_static(sid));
836 DEBUG(10,("wcache_get_creds: entry for [CRED/%s] not found\n",
837 sid_string_static(sid)));
838 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
841 t = centry_time(centry);
842 *cached_nt_pass = (const uint8 *)centry_string(centry, mem_ctx);
845 dump_data(100, (const char *)cached_nt_pass, NT_HASH_LEN);
847 status = centry->status;
849 DEBUG(10,("wcache_get_creds: [Cached] - cached creds for user %s status %s\n",
850 sid_string_static(sid), get_friendly_nt_error_msg(status) ));
856 NTSTATUS wcache_save_creds(struct winbindd_domain *domain,
859 const uint8 nt_pass[NT_HASH_LEN])
861 struct cache_entry *centry;
863 NTSTATUS status = NT_STATUS_OK; /* ??? */
865 centry = centry_start(domain, status);
867 return NT_STATUS_INTERNAL_DB_ERROR;
871 dump_data(100, (const char *)nt_pass, NT_HASH_LEN);
874 centry_put_time(centry, time(NULL));
875 centry_put_string(centry, (const char *)nt_pass);
876 centry_end(centry, "CRED/%s", sid_to_string(sid_string, sid));
878 DEBUG(10,("wcache_save_creds: %s\n", sid_string));
886 /* Query display info. This is the basic user list fn */
887 static NTSTATUS query_user_list(struct winbindd_domain *domain,
890 WINBIND_USERINFO **info)
892 struct winbind_cache *cache = get_cache(domain);
893 struct cache_entry *centry = NULL;
895 unsigned int i, retry;
900 centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
904 *num_entries = centry_uint32(centry);
906 if (*num_entries == 0)
909 (*info) = TALLOC_ARRAY(mem_ctx, WINBIND_USERINFO, *num_entries);
911 smb_panic("query_user_list out of memory");
912 for (i=0; i<(*num_entries); i++) {
913 (*info)[i].acct_name = centry_string(centry, mem_ctx);
914 (*info)[i].full_name = centry_string(centry, mem_ctx);
915 (*info)[i].homedir = centry_string(centry, mem_ctx);
916 (*info)[i].shell = centry_string(centry, mem_ctx);
917 centry_sid(centry, mem_ctx, &(*info)[i].user_sid);
918 centry_sid(centry, mem_ctx, &(*info)[i].group_sid);
922 status = centry->status;
924 DEBUG(10,("query_user_list: [Cached] - cached list for domain %s status %s\n",
925 domain->name, get_friendly_nt_error_msg(status) ));
934 /* Return status value returned by seq number check */
936 if (!NT_STATUS_IS_OK(domain->last_status))
937 return domain->last_status;
939 /* Put the query_user_list() in a retry loop. There appears to be
940 * some bug either with Windows 2000 or Samba's handling of large
941 * rpc replies. This manifests itself as sudden disconnection
942 * at a random point in the enumeration of a large (60k) user list.
943 * The retry loop simply tries the operation again. )-: It's not
944 * pretty but an acceptable workaround until we work out what the
945 * real problem is. */
950 DEBUG(10,("query_user_list: [Cached] - doing backend query for list for domain %s\n",
953 status = domain->backend->query_user_list(domain, mem_ctx, num_entries, info);
954 if (!NT_STATUS_IS_OK(status))
955 DEBUG(3, ("query_user_list: returned 0x%08x, "
956 "retrying\n", NT_STATUS_V(status)));
957 if (NT_STATUS_EQUAL(status, NT_STATUS_UNSUCCESSFUL)) {
958 DEBUG(3, ("query_user_list: flushing "
959 "connection cache\n"));
960 invalidate_cm_connection(&domain->conn);
963 } while (NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_UNSUCCESSFUL) &&
967 refresh_sequence_number(domain, False);
968 centry = centry_start(domain, status);
971 centry_put_uint32(centry, *num_entries);
972 for (i=0; i<(*num_entries); i++) {
973 centry_put_string(centry, (*info)[i].acct_name);
974 centry_put_string(centry, (*info)[i].full_name);
975 centry_put_string(centry, (*info)[i].homedir);
976 centry_put_string(centry, (*info)[i].shell);
977 centry_put_sid(centry, &(*info)[i].user_sid);
978 centry_put_sid(centry, &(*info)[i].group_sid);
979 if (domain->backend->consistent) {
980 /* when the backend is consistent we can pre-prime some mappings */
981 wcache_save_name_to_sid(domain, NT_STATUS_OK,
983 (*info)[i].acct_name,
984 &(*info)[i].user_sid,
986 wcache_save_sid_to_name(domain, NT_STATUS_OK,
987 &(*info)[i].user_sid,
989 (*info)[i].acct_name,
991 wcache_save_user(domain, NT_STATUS_OK, &(*info)[i]);
994 centry_end(centry, "UL/%s", domain->name);
1001 /* list all domain groups */
1002 static NTSTATUS enum_dom_groups(struct winbindd_domain *domain,
1003 TALLOC_CTX *mem_ctx,
1004 uint32 *num_entries,
1005 struct acct_info **info)
1007 struct winbind_cache *cache = get_cache(domain);
1008 struct cache_entry *centry = NULL;
1015 centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
1019 *num_entries = centry_uint32(centry);
1021 if (*num_entries == 0)
1024 (*info) = TALLOC_ARRAY(mem_ctx, struct acct_info, *num_entries);
1026 smb_panic("enum_dom_groups out of memory");
1027 for (i=0; i<(*num_entries); i++) {
1028 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1029 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1030 (*info)[i].rid = centry_uint32(centry);
1034 status = centry->status;
1036 DEBUG(10,("enum_dom_groups: [Cached] - cached list for domain %s status %s\n",
1037 domain->name, get_friendly_nt_error_msg(status) ));
1039 centry_free(centry);
1046 /* Return status value returned by seq number check */
1048 if (!NT_STATUS_IS_OK(domain->last_status))
1049 return domain->last_status;
1051 DEBUG(10,("enum_dom_groups: [Cached] - doing backend query for list for domain %s\n",
1054 status = domain->backend->enum_dom_groups(domain, mem_ctx, num_entries, info);
1057 refresh_sequence_number(domain, False);
1058 centry = centry_start(domain, status);
1061 centry_put_uint32(centry, *num_entries);
1062 for (i=0; i<(*num_entries); i++) {
1063 centry_put_string(centry, (*info)[i].acct_name);
1064 centry_put_string(centry, (*info)[i].acct_desc);
1065 centry_put_uint32(centry, (*info)[i].rid);
1067 centry_end(centry, "GL/%s/domain", domain->name);
1068 centry_free(centry);
1074 /* list all domain groups */
1075 static NTSTATUS enum_local_groups(struct winbindd_domain *domain,
1076 TALLOC_CTX *mem_ctx,
1077 uint32 *num_entries,
1078 struct acct_info **info)
1080 struct winbind_cache *cache = get_cache(domain);
1081 struct cache_entry *centry = NULL;
1088 centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
1092 *num_entries = centry_uint32(centry);
1094 if (*num_entries == 0)
1097 (*info) = TALLOC_ARRAY(mem_ctx, struct acct_info, *num_entries);
1099 smb_panic("enum_dom_groups out of memory");
1100 for (i=0; i<(*num_entries); i++) {
1101 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1102 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1103 (*info)[i].rid = centry_uint32(centry);
1108 /* If we are returning cached data and the domain controller
1109 is down then we don't know whether the data is up to date
1110 or not. Return NT_STATUS_MORE_PROCESSING_REQUIRED to
1113 if (wcache_server_down(domain)) {
1114 DEBUG(10, ("enum_local_groups: returning cached user list and server was down\n"));
1115 status = NT_STATUS_MORE_PROCESSING_REQUIRED;
1117 status = centry->status;
1119 DEBUG(10,("enum_local_groups: [Cached] - cached list for domain %s status %s\n",
1120 domain->name, get_friendly_nt_error_msg(status) ));
1122 centry_free(centry);
1129 /* Return status value returned by seq number check */
1131 if (!NT_STATUS_IS_OK(domain->last_status))
1132 return domain->last_status;
1134 DEBUG(10,("enum_local_groups: [Cached] - doing backend query for list for domain %s\n",
1137 status = domain->backend->enum_local_groups(domain, mem_ctx, num_entries, info);
1140 refresh_sequence_number(domain, False);
1141 centry = centry_start(domain, status);
1144 centry_put_uint32(centry, *num_entries);
1145 for (i=0; i<(*num_entries); i++) {
1146 centry_put_string(centry, (*info)[i].acct_name);
1147 centry_put_string(centry, (*info)[i].acct_desc);
1148 centry_put_uint32(centry, (*info)[i].rid);
1150 centry_end(centry, "GL/%s/local", domain->name);
1151 centry_free(centry);
1157 /* convert a single name to a sid in a domain */
1158 static NTSTATUS name_to_sid(struct winbindd_domain *domain,
1159 TALLOC_CTX *mem_ctx,
1160 const char *domain_name,
1163 enum SID_NAME_USE *type)
1165 struct winbind_cache *cache = get_cache(domain);
1166 struct cache_entry *centry = NULL;
1173 fstrcpy(uname, name);
1175 centry = wcache_fetch(cache, domain, "NS/%s/%s", domain_name, uname);
1178 *type = (enum SID_NAME_USE)centry_uint32(centry);
1179 status = centry->status;
1180 if (NT_STATUS_IS_OK(status)) {
1181 centry_sid(centry, mem_ctx, sid);
1184 DEBUG(10,("name_to_sid: [Cached] - cached name for domain %s status %s\n",
1185 domain->name, get_friendly_nt_error_msg(status) ));
1187 centry_free(centry);
1193 /* If the seq number check indicated that there is a problem
1194 * with this DC, then return that status... except for
1195 * access_denied. This is special because the dc may be in
1196 * "restrict anonymous = 1" mode, in which case it will deny
1197 * most unauthenticated operations, but *will* allow the LSA
1198 * name-to-sid that we try as a fallback. */
1200 if (!(NT_STATUS_IS_OK(domain->last_status)
1201 || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1202 return domain->last_status;
1204 DEBUG(10,("name_to_sid: [Cached] - doing backend query for name for domain %s\n",
1207 status = domain->backend->name_to_sid(domain, mem_ctx, domain_name, name, sid, type);
1210 if (domain->online || !is_null_sid(sid)) {
1211 wcache_save_name_to_sid(domain, status, domain_name, name, sid, *type);
1214 if (NT_STATUS_IS_OK(status)) {
1215 strupper_m(CONST_DISCARD(char *,domain_name));
1216 strlower_m(CONST_DISCARD(char *,name));
1217 wcache_save_sid_to_name(domain, status, sid, domain_name, name, *type);
1223 /* convert a sid to a user or group name. The sid is guaranteed to be in the domain
1225 static NTSTATUS sid_to_name(struct winbindd_domain *domain,
1226 TALLOC_CTX *mem_ctx,
1230 enum SID_NAME_USE *type)
1232 struct winbind_cache *cache = get_cache(domain);
1233 struct cache_entry *centry = NULL;
1240 centry = wcache_fetch(cache, domain, "SN/%s", sid_to_string(sid_string, sid));
1243 if (NT_STATUS_IS_OK(centry->status)) {
1244 *type = (enum SID_NAME_USE)centry_uint32(centry);
1245 *domain_name = centry_string(centry, mem_ctx);
1246 *name = centry_string(centry, mem_ctx);
1248 status = centry->status;
1250 DEBUG(10,("sid_to_name: [Cached] - cached name for domain %s status %s\n",
1251 domain->name, get_friendly_nt_error_msg(status) ));
1253 centry_free(centry);
1258 *domain_name = NULL;
1260 /* If the seq number check indicated that there is a problem
1261 * with this DC, then return that status... except for
1262 * access_denied. This is special because the dc may be in
1263 * "restrict anonymous = 1" mode, in which case it will deny
1264 * most unauthenticated operations, but *will* allow the LSA
1265 * sid-to-name that we try as a fallback. */
1267 if (!(NT_STATUS_IS_OK(domain->last_status)
1268 || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1269 return domain->last_status;
1271 DEBUG(10,("sid_to_name: [Cached] - doing backend query for name for domain %s\n",
1274 status = domain->backend->sid_to_name(domain, mem_ctx, sid, domain_name, name, type);
1277 refresh_sequence_number(domain, False);
1278 wcache_save_sid_to_name(domain, status, sid, *domain_name, *name, *type);
1280 /* We can't save the name to sid mapping here, as with sid history a
1281 * later name2sid would give the wrong sid. */
1286 /* Lookup user information from a rid */
1287 static NTSTATUS query_user(struct winbindd_domain *domain,
1288 TALLOC_CTX *mem_ctx,
1289 const DOM_SID *user_sid,
1290 WINBIND_USERINFO *info)
1292 struct winbind_cache *cache = get_cache(domain);
1293 struct cache_entry *centry = NULL;
1299 centry = wcache_fetch(cache, domain, "U/%s", sid_string_static(user_sid));
1301 /* If we have an access denied cache entry and a cached info3 in the
1302 samlogon cache then do a query. This will force the rpc back end
1303 to return the info3 data. */
1305 if (NT_STATUS_V(domain->last_status) == NT_STATUS_V(NT_STATUS_ACCESS_DENIED) &&
1306 netsamlogon_cache_have(user_sid)) {
1307 DEBUG(10, ("query_user: cached access denied and have cached info3\n"));
1308 domain->last_status = NT_STATUS_OK;
1309 centry_free(centry);
1316 info->acct_name = centry_string(centry, mem_ctx);
1317 info->full_name = centry_string(centry, mem_ctx);
1318 info->homedir = centry_string(centry, mem_ctx);
1319 info->shell = centry_string(centry, mem_ctx);
1320 centry_sid(centry, mem_ctx, &info->user_sid);
1321 centry_sid(centry, mem_ctx, &info->group_sid);
1322 status = centry->status;
1324 DEBUG(10,("query_user: [Cached] - cached info for domain %s status %s\n",
1325 domain->name, get_friendly_nt_error_msg(status) ));
1327 centry_free(centry);
1333 /* Return status value returned by seq number check */
1335 if (!NT_STATUS_IS_OK(domain->last_status))
1336 return domain->last_status;
1338 DEBUG(10,("sid_to_name: [Cached] - doing backend query for info for domain %s\n",
1341 status = domain->backend->query_user(domain, mem_ctx, user_sid, info);
1344 refresh_sequence_number(domain, False);
1345 wcache_save_user(domain, status, info);
1351 /* Lookup groups a user is a member of. */
1352 static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
1353 TALLOC_CTX *mem_ctx,
1354 const DOM_SID *user_sid,
1355 uint32 *num_groups, DOM_SID **user_gids)
1357 struct winbind_cache *cache = get_cache(domain);
1358 struct cache_entry *centry = NULL;
1366 centry = wcache_fetch(cache, domain, "UG/%s", sid_to_string(sid_string, user_sid));
1368 /* If we have an access denied cache entry and a cached info3 in the
1369 samlogon cache then do a query. This will force the rpc back end
1370 to return the info3 data. */
1372 if (NT_STATUS_V(domain->last_status) == NT_STATUS_V(NT_STATUS_ACCESS_DENIED) &&
1373 netsamlogon_cache_have(user_sid)) {
1374 DEBUG(10, ("lookup_usergroups: cached access denied and have cached info3\n"));
1375 domain->last_status = NT_STATUS_OK;
1376 centry_free(centry);
1383 *num_groups = centry_uint32(centry);
1385 if (*num_groups == 0)
1388 (*user_gids) = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_groups);
1390 smb_panic("lookup_usergroups out of memory");
1391 for (i=0; i<(*num_groups); i++) {
1392 centry_sid(centry, mem_ctx, &(*user_gids)[i]);
1396 status = centry->status;
1398 DEBUG(10,("lookup_usergroups: [Cached] - cached info for domain %s status %s\n",
1399 domain->name, get_friendly_nt_error_msg(status) ));
1401 centry_free(centry);
1406 (*user_gids) = NULL;
1408 /* Return status value returned by seq number check */
1410 if (!NT_STATUS_IS_OK(domain->last_status))
1411 return domain->last_status;
1413 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info for domain %s\n",
1416 status = domain->backend->lookup_usergroups(domain, mem_ctx, user_sid, num_groups, user_gids);
1419 refresh_sequence_number(domain, False);
1420 centry = centry_start(domain, status);
1423 centry_put_uint32(centry, *num_groups);
1424 for (i=0; i<(*num_groups); i++) {
1425 centry_put_sid(centry, &(*user_gids)[i]);
1427 centry_end(centry, "UG/%s", sid_to_string(sid_string, user_sid));
1428 centry_free(centry);
1434 static NTSTATUS lookup_useraliases(struct winbindd_domain *domain,
1435 TALLOC_CTX *mem_ctx,
1436 uint32 num_sids, const DOM_SID *sids,
1437 uint32 *num_aliases, uint32 **alias_rids)
1439 struct winbind_cache *cache = get_cache(domain);
1440 struct cache_entry *centry = NULL;
1442 char *sidlist = talloc_strdup(mem_ctx, "");
1448 if (num_sids == 0) {
1451 return NT_STATUS_OK;
1454 /* We need to cache indexed by the whole list of SIDs, the aliases
1455 * resulting might come from any of the SIDs. */
1457 for (i=0; i<num_sids; i++) {
1458 sidlist = talloc_asprintf(mem_ctx, "%s/%s", sidlist,
1459 sid_string_static(&sids[i]));
1460 if (sidlist == NULL)
1461 return NT_STATUS_NO_MEMORY;
1464 centry = wcache_fetch(cache, domain, "UA%s", sidlist);
1469 *num_aliases = centry_uint32(centry);
1472 (*alias_rids) = TALLOC_ARRAY(mem_ctx, uint32, *num_aliases);
1474 if ((*num_aliases != 0) && ((*alias_rids) == NULL)) {
1475 centry_free(centry);
1476 return NT_STATUS_NO_MEMORY;
1479 for (i=0; i<(*num_aliases); i++)
1480 (*alias_rids)[i] = centry_uint32(centry);
1482 status = centry->status;
1484 DEBUG(10,("lookup_useraliases: [Cached] - cached info for domain %s "
1485 "status %s\n", domain->name,
1486 get_friendly_nt_error_msg(status)));
1488 centry_free(centry);
1493 (*alias_rids) = NULL;
1495 if (!NT_STATUS_IS_OK(domain->last_status))
1496 return domain->last_status;
1498 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info "
1499 "for domain %s\n", domain->name ));
1501 status = domain->backend->lookup_useraliases(domain, mem_ctx,
1503 num_aliases, alias_rids);
1506 refresh_sequence_number(domain, False);
1507 centry = centry_start(domain, status);
1510 centry_put_uint32(centry, *num_aliases);
1511 for (i=0; i<(*num_aliases); i++)
1512 centry_put_uint32(centry, (*alias_rids)[i]);
1513 centry_end(centry, "UA%s", sidlist);
1514 centry_free(centry);
1521 static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
1522 TALLOC_CTX *mem_ctx,
1523 const DOM_SID *group_sid, uint32 *num_names,
1524 DOM_SID **sid_mem, char ***names,
1525 uint32 **name_types)
1527 struct winbind_cache *cache = get_cache(domain);
1528 struct cache_entry *centry = NULL;
1536 centry = wcache_fetch(cache, domain, "GM/%s", sid_to_string(sid_string, group_sid));
1540 *num_names = centry_uint32(centry);
1542 if (*num_names == 0)
1545 (*sid_mem) = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_names);
1546 (*names) = TALLOC_ARRAY(mem_ctx, char *, *num_names);
1547 (*name_types) = TALLOC_ARRAY(mem_ctx, uint32, *num_names);
1549 if (! (*sid_mem) || ! (*names) || ! (*name_types)) {
1550 smb_panic("lookup_groupmem out of memory");
1553 for (i=0; i<(*num_names); i++) {
1554 centry_sid(centry, mem_ctx, &(*sid_mem)[i]);
1555 (*names)[i] = centry_string(centry, mem_ctx);
1556 (*name_types)[i] = centry_uint32(centry);
1560 status = centry->status;
1562 DEBUG(10,("lookup_groupmem: [Cached] - cached info for domain %s status %s\n",
1563 domain->name, get_friendly_nt_error_msg(status) ));
1565 centry_free(centry);
1572 (*name_types) = NULL;
1574 /* Return status value returned by seq number check */
1576 if (!NT_STATUS_IS_OK(domain->last_status))
1577 return domain->last_status;
1579 DEBUG(10,("lookup_groupmem: [Cached] - doing backend query for info for domain %s\n",
1582 status = domain->backend->lookup_groupmem(domain, mem_ctx, group_sid, num_names,
1583 sid_mem, names, name_types);
1586 refresh_sequence_number(domain, False);
1587 centry = centry_start(domain, status);
1590 centry_put_uint32(centry, *num_names);
1591 for (i=0; i<(*num_names); i++) {
1592 centry_put_sid(centry, &(*sid_mem)[i]);
1593 centry_put_string(centry, (*names)[i]);
1594 centry_put_uint32(centry, (*name_types)[i]);
1596 centry_end(centry, "GM/%s", sid_to_string(sid_string, group_sid));
1597 centry_free(centry);
1603 /* find the sequence number for a domain */
1604 static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
1606 refresh_sequence_number(domain, False);
1608 *seq = domain->sequence_number;
1610 return NT_STATUS_OK;
1613 /* enumerate trusted domains
1614 * (we need to have the list of trustdoms in the cache when we go offline) -
1616 static NTSTATUS trusted_domains(struct winbindd_domain *domain,
1617 TALLOC_CTX *mem_ctx,
1618 uint32 *num_domains,
1623 struct winbind_cache *cache = get_cache(domain);
1624 struct cache_entry *centry = NULL;
1631 centry = wcache_fetch(cache, domain, "TRUSTDOMS/%s", domain->name);
1637 *num_domains = centry_uint32(centry);
1639 (*names) = TALLOC_ARRAY(mem_ctx, char *, *num_domains);
1640 (*alt_names) = TALLOC_ARRAY(mem_ctx, char *, *num_domains);
1641 (*dom_sids) = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_domains);
1643 if (! (*dom_sids) || ! (*names) || ! (*alt_names)) {
1644 smb_panic("trusted_domains out of memory");
1647 for (i=0; i<(*num_domains); i++) {
1648 (*names)[i] = centry_string(centry, mem_ctx);
1649 (*alt_names)[i] = centry_string(centry, mem_ctx);
1650 centry_sid(centry, mem_ctx, &(*dom_sids)[i]);
1653 status = centry->status;
1655 DEBUG(10,("trusted_domains: [Cached] - cached info for domain %s (%d trusts) status %s\n",
1656 domain->name, *num_domains, get_friendly_nt_error_msg(status) ));
1658 centry_free(centry);
1665 (*alt_names) = NULL;
1667 /* Return status value returned by seq number check */
1669 if (!NT_STATUS_IS_OK(domain->last_status))
1670 return domain->last_status;
1672 DEBUG(10,("trusted_domains: [Cached] - doing backend query for info for domain %s\n",
1675 status = domain->backend->trusted_domains(domain, mem_ctx, num_domains,
1676 names, alt_names, dom_sids);
1678 /* no trusts gives NT_STATUS_NO_MORE_ENTRIES resetting to NT_STATUS_OK
1679 * so that the generic centry handling still applies correctly -
1682 if (!NT_STATUS_IS_ERR(status)) {
1683 status = NT_STATUS_OK;
1687 refresh_sequence_number(domain, False);
1689 centry = centry_start(domain, status);
1693 centry_put_uint32(centry, *num_domains);
1695 for (i=0; i<(*num_domains); i++) {
1696 centry_put_string(centry, (*names)[i]);
1697 centry_put_string(centry, (*alt_names)[i]);
1698 centry_put_sid(centry, &(*dom_sids)[i]);
1701 centry_end(centry, "TRUSTDOMS/%s", domain->name);
1703 centry_free(centry);
1709 /* get lockout policy */
1710 static NTSTATUS lockout_policy(struct winbindd_domain *domain,
1711 TALLOC_CTX *mem_ctx,
1712 SAM_UNK_INFO_12 *lockout_policy){
1713 struct winbind_cache *cache = get_cache(domain);
1714 struct cache_entry *centry = NULL;
1720 centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
1725 lockout_policy->duration = centry_nttime(centry);
1726 lockout_policy->reset_count = centry_nttime(centry);
1727 lockout_policy->bad_attempt_lockout = centry_uint16(centry);
1729 status = centry->status;
1731 DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status %s\n",
1732 domain->name, get_friendly_nt_error_msg(status) ));
1734 centry_free(centry);
1738 ZERO_STRUCTP(lockout_policy);
1740 /* Return status value returned by seq number check */
1742 if (!NT_STATUS_IS_OK(domain->last_status))
1743 return domain->last_status;
1745 DEBUG(10,("lockout_policy: [Cached] - doing backend query for info for domain %s\n",
1748 status = domain->backend->lockout_policy(domain, mem_ctx, lockout_policy);
1751 refresh_sequence_number(domain, False);
1752 wcache_save_lockout_policy(domain, status, lockout_policy);
1757 /* get password policy */
1758 static NTSTATUS password_policy(struct winbindd_domain *domain,
1759 TALLOC_CTX *mem_ctx,
1760 SAM_UNK_INFO_1 *password_policy)
1762 struct winbind_cache *cache = get_cache(domain);
1763 struct cache_entry *centry = NULL;
1769 centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
1774 password_policy->min_length_password = centry_uint16(centry);
1775 password_policy->password_history = centry_uint16(centry);
1776 password_policy->password_properties = centry_uint32(centry);
1777 password_policy->expire = centry_nttime(centry);
1778 password_policy->min_passwordage = centry_nttime(centry);
1780 status = centry->status;
1782 DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status %s\n",
1783 domain->name, get_friendly_nt_error_msg(status) ));
1785 centry_free(centry);
1789 ZERO_STRUCTP(password_policy);
1791 /* Return status value returned by seq number check */
1793 if (!NT_STATUS_IS_OK(domain->last_status))
1794 return domain->last_status;
1796 DEBUG(10,("password_policy: [Cached] - doing backend query for info for domain %s\n",
1799 status = domain->backend->password_policy(domain, mem_ctx, password_policy);
1802 refresh_sequence_number(domain, False);
1803 wcache_save_password_policy(domain, status, password_policy);
1809 /* Invalidate cached user and group lists coherently */
1811 static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
1814 if (strncmp(kbuf.dptr, "UL/", 3) == 0 ||
1815 strncmp(kbuf.dptr, "GL/", 3) == 0)
1816 tdb_delete(the_tdb, kbuf);
1821 /* Invalidate the getpwnam and getgroups entries for a winbindd domain */
1823 void wcache_invalidate_samlogon(struct winbindd_domain *domain,
1824 NET_USER_INFO_3 *info3)
1826 struct winbind_cache *cache;
1831 cache = get_cache(domain);
1832 netsamlogon_clear_cached_user(cache->tdb, info3);
1835 void wcache_invalidate_cache(void)
1837 struct winbindd_domain *domain;
1839 for (domain = domain_list(); domain; domain = domain->next) {
1840 struct winbind_cache *cache = get_cache(domain);
1842 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
1843 "entries for %s\n", domain->name));
1845 tdb_traverse(cache->tdb, traverse_fn, NULL);
1849 static BOOL init_wcache(void)
1851 if (wcache == NULL) {
1852 wcache = SMB_XMALLOC_P(struct winbind_cache);
1853 ZERO_STRUCTP(wcache);
1856 if (wcache->tdb != NULL)
1859 /* when working offline we must not clear the cache on restart */
1860 wcache->tdb = tdb_open_log(lock_path("winbindd_cache.tdb"),
1861 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
1862 TDB_DEFAULT /*TDB_CLEAR_IF_FIRST*/, O_RDWR|O_CREAT, 0600);
1864 if (wcache->tdb == NULL) {
1865 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
1872 void cache_store_response(pid_t pid, struct winbindd_response *response)
1879 DEBUG(10, ("Storing response for pid %d, len %d\n",
1880 pid, response->length));
1882 fstr_sprintf(key_str, "DR/%d", pid);
1883 if (tdb_store(wcache->tdb, string_tdb_data(key_str),
1884 make_tdb_data((void *)response, sizeof(*response)),
1888 if (response->length == sizeof(*response))
1891 /* There's extra data */
1893 DEBUG(10, ("Storing extra data: len=%d\n",
1894 (int)(response->length - sizeof(*response))));
1896 fstr_sprintf(key_str, "DE/%d", pid);
1897 if (tdb_store(wcache->tdb, string_tdb_data(key_str),
1898 make_tdb_data(response->extra_data,
1899 response->length - sizeof(*response)),
1903 /* We could not store the extra data, make sure the tdb does not
1904 * contain a main record with wrong dangling extra data */
1906 fstr_sprintf(key_str, "DR/%d", pid);
1907 tdb_delete(wcache->tdb, string_tdb_data(key_str));
1912 BOOL cache_retrieve_response(pid_t pid, struct winbindd_response * response)
1920 DEBUG(10, ("Retrieving response for pid %d\n", pid));
1922 fstr_sprintf(key_str, "DR/%d", pid);
1923 data = tdb_fetch(wcache->tdb, string_tdb_data(key_str));
1925 if (data.dptr == NULL)
1928 if (data.dsize != sizeof(*response))
1931 memcpy(response, data.dptr, data.dsize);
1932 SAFE_FREE(data.dptr);
1934 if (response->length == sizeof(*response)) {
1935 response->extra_data = NULL;
1939 /* There's extra data */
1941 DEBUG(10, ("Retrieving extra data length=%d\n",
1942 (int)(response->length - sizeof(*response))));
1944 fstr_sprintf(key_str, "DE/%d", pid);
1945 data = tdb_fetch(wcache->tdb, string_tdb_data(key_str));
1947 if (data.dptr == NULL) {
1948 DEBUG(0, ("Did not find extra data\n"));
1952 if (data.dsize != (response->length - sizeof(*response))) {
1953 DEBUG(0, ("Invalid extra data length: %d\n", (int)data.dsize));
1954 SAFE_FREE(data.dptr);
1958 dump_data(11, data.dptr, data.dsize);
1960 response->extra_data = data.dptr;
1964 void cache_cleanup_response(pid_t pid)
1971 fstr_sprintf(key_str, "DR/%d", pid);
1972 tdb_delete(wcache->tdb, string_tdb_data(key_str));
1974 fstr_sprintf(key_str, "DE/%d", pid);
1975 tdb_delete(wcache->tdb, string_tdb_data(key_str));
1981 BOOL lookup_cached_sid(TALLOC_CTX *mem_ctx, const DOM_SID *sid,
1982 const char **domain_name, const char **name,
1983 enum SID_NAME_USE *type)
1985 struct winbindd_domain *domain;
1986 struct winbind_cache *cache;
1987 struct cache_entry *centry = NULL;
1990 domain = find_lookup_domain_from_sid(sid);
1991 if (domain == NULL) {
1995 cache = get_cache(domain);
1997 if (cache->tdb == NULL) {
2001 centry = wcache_fetch(cache, domain, "SN/%s", sid_string_static(sid));
2002 if (centry == NULL) {
2006 if (NT_STATUS_IS_OK(centry->status)) {
2007 *type = (enum SID_NAME_USE)centry_uint32(centry);
2008 *domain_name = centry_string(centry, mem_ctx);
2009 *name = centry_string(centry, mem_ctx);
2012 status = centry->status;
2013 centry_free(centry);
2014 return NT_STATUS_IS_OK(status);
2017 BOOL lookup_cached_name(TALLOC_CTX *mem_ctx,
2018 const char *domain_name,
2021 enum SID_NAME_USE *type)
2023 struct winbindd_domain *domain;
2024 struct winbind_cache *cache;
2025 struct cache_entry *centry = NULL;
2029 domain = find_lookup_domain_from_name(domain_name);
2030 if (domain == NULL) {
2034 cache = get_cache(domain);
2036 if (cache->tdb == NULL) {
2040 fstrcpy(uname, name);
2043 centry = wcache_fetch(cache, domain, "NS/%s/%s", domain_name, uname);
2044 if (centry == NULL) {
2048 if (NT_STATUS_IS_OK(centry->status)) {
2049 *type = (enum SID_NAME_USE)centry_uint32(centry);
2050 centry_sid(centry, mem_ctx, sid);
2053 status = centry->status;
2054 centry_free(centry);
2056 return NT_STATUS_IS_OK(status);
2059 void cache_name2sid(struct winbindd_domain *domain,
2060 const char *domain_name, const char *name,
2061 enum SID_NAME_USE type, const DOM_SID *sid)
2063 wcache_save_name_to_sid(domain, NT_STATUS_OK, domain_name, name,
2067 /* delete all centries that don't have NT_STATUS_OK set */
2068 static int traverse_fn_cleanup(TDB_CONTEXT *the_tdb, TDB_DATA kbuf,
2069 TDB_DATA dbuf, void *state)
2071 struct cache_entry *centry;
2074 if (!snprintf(buf, kbuf.dsize + 1, "%s", kbuf.dptr)) {
2078 centry = wcache_fetch_raw(buf);
2083 if (!NT_STATUS_IS_OK(centry->status)) {
2084 DEBUG(10,("deleting centry %s\n", buf));
2085 tdb_delete(the_tdb, kbuf);
2088 centry_free(centry);
2092 /* flush the cache */
2093 void wcache_flush_cache(void)
2095 extern BOOL opt_nocache;
2100 tdb_close(wcache->tdb);
2106 /* when working offline we must not clear the cache on restart */
2107 wcache->tdb = tdb_open_log(lock_path("winbindd_cache.tdb"),
2108 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
2109 TDB_DEFAULT /* TDB_CLEAR_IF_FIRST */, O_RDWR|O_CREAT, 0600);
2112 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
2115 tdb_traverse(wcache->tdb, traverse_fn_cleanup, NULL);
2117 DEBUG(10,("wcache_flush_cache success\n"));
2120 /* Count cached creds */
2122 static int traverse_fn_cached_creds(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
2125 int *cred_count = (int*)state;
2127 if (strncmp(kbuf.dptr, "CRED/", 5) == 0) {
2133 NTSTATUS wcache_count_cached_creds(struct winbindd_domain *domain, int *count)
2135 struct winbind_cache *cache = get_cache(domain);
2140 return NT_STATUS_INTERNAL_DB_ERROR;
2143 tdb_traverse(cache->tdb, traverse_fn_cached_creds, (void *)count);
2145 return NT_STATUS_OK;
2149 struct cred_list *prev, *next;
2154 static struct cred_list *wcache_cred_list;
2156 static int traverse_fn_get_credlist(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
2159 struct cred_list *cred;
2161 if (strncmp(kbuf.dptr, "CRED/", 5) == 0) {
2163 cred = SMB_MALLOC_P(struct cred_list);
2165 DEBUG(0,("traverse_fn_remove_first_creds: failed to malloc new entry for list\n"));
2171 /* save a copy of the key */
2173 fstrcpy(cred->name, kbuf.dptr);
2174 DLIST_ADD(wcache_cred_list, cred);
2180 NTSTATUS wcache_remove_oldest_cached_creds(struct winbindd_domain *domain, const DOM_SID *sid)
2182 struct winbind_cache *cache = get_cache(domain);
2185 struct cred_list *cred, *oldest = NULL;
2188 return NT_STATUS_INTERNAL_DB_ERROR;
2191 /* we possibly already have an entry */
2192 if (sid && NT_STATUS_IS_OK(wcache_cached_creds_exist(domain, sid))) {
2196 DEBUG(11,("we already have an entry, deleting that\n"));
2198 fstr_sprintf(key_str, "CRED/%s", sid_string_static(sid));
2200 tdb_delete(cache->tdb, string_tdb_data(key_str));
2202 return NT_STATUS_OK;
2205 ret = tdb_traverse(cache->tdb, traverse_fn_get_credlist, NULL);
2207 return NT_STATUS_OK;
2208 } else if (ret == -1) {
2209 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
2212 ZERO_STRUCTP(oldest);
2214 for (cred = wcache_cred_list; cred; cred = cred->next) {
2219 data = tdb_fetch(cache->tdb, make_tdb_data(cred->name, strlen(cred->name)));
2221 DEBUG(10,("wcache_remove_oldest_cached_creds: entry for [%s] not found\n",
2223 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
2227 t = IVAL(data.dptr, 0);
2228 SAFE_FREE(data.dptr);
2231 oldest = SMB_MALLOC_P(struct cred_list);
2232 if (oldest == NULL) {
2233 status = NT_STATUS_NO_MEMORY;
2237 fstrcpy(oldest->name, cred->name);
2238 oldest->created = t;
2242 if (t < oldest->created) {
2243 fstrcpy(oldest->name, cred->name);
2244 oldest->created = t;
2248 if (tdb_delete(cache->tdb, string_tdb_data(oldest->name)) == 0) {
2249 status = NT_STATUS_OK;
2251 status = NT_STATUS_UNSUCCESSFUL;
2254 SAFE_FREE(wcache_cred_list);
2260 /* Change the global online/offline state. */
2261 BOOL set_global_winbindd_state_offline(void)
2266 DEBUG(10,("set_global_winbindd_state_offline: offline requested.\n"));
2268 /* Only go offline if someone has created
2269 the key "WINBINDD_OFFLINE" in the cache tdb. */
2271 if (wcache == NULL || wcache->tdb == NULL) {
2272 DEBUG(10,("set_global_winbindd_state_offline: wcache not open yet.\n"));
2276 if (!lp_winbind_offline_logon()) {
2277 DEBUG(10,("set_global_winbindd_state_offline: rejecting.\n"));
2281 if (global_winbindd_offline_state) {
2282 /* Already offline. */
2286 wcache->tdb->ecode = 0;
2288 data = tdb_fetch_bystring( wcache->tdb, "WINBINDD_OFFLINE" );
2290 /* As this is a key with no data we don't need to free, we
2291 check for existence by looking at tdb_err. */
2293 err = tdb_error(wcache->tdb);
2295 if (err == TDB_ERR_NOEXIST) {
2296 DEBUG(10,("set_global_winbindd_state_offline: offline state not set.\n"));
2299 DEBUG(10,("set_global_winbindd_state_offline: offline state set.\n"));
2300 global_winbindd_offline_state = True;
2305 void set_global_winbindd_state_online(void)
2307 DEBUG(10,("set_global_winbindd_state_online: online requested.\n"));
2309 if (!lp_winbind_offline_logon()) {
2310 DEBUG(10,("set_global_winbindd_state_online: rejecting.\n"));
2314 if (!global_winbindd_offline_state) {
2315 /* Already online. */
2318 global_winbindd_offline_state = False;
2324 /* Ensure there is no key "WINBINDD_OFFLINE" in the cache tdb. */
2325 tdb_delete_bystring(wcache->tdb, "WINBINDD_OFFLINE");
2328 /* the cache backend methods are exposed via this structure */
2329 struct winbindd_methods cache_methods = {