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 = IVAL(centry->data, centry->ofs);
220 ret += (uint64_t)IVAL(centry->data, centry->ofs) << 32;
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 hash16 from a cache entry, using the supplied
275 static char *centry_hash16(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
280 len = centry_uint8(centry);
283 DEBUG(0,("centry corruption? hash len (%u) != 16\n",
288 if (centry->len - centry->ofs < 16) {
289 DEBUG(0,("centry corruption? needed 16 bytes, have %d\n",
290 centry->len - centry->ofs));
294 ret = TALLOC_ARRAY(mem_ctx, char, 16);
296 smb_panic("centry_hash out of memory\n");
298 memcpy(ret,centry->data + centry->ofs, 16);
303 /* pull a sid from a cache entry, using the supplied
306 static BOOL centry_sid(struct cache_entry *centry, TALLOC_CTX *mem_ctx, DOM_SID *sid)
309 sid_string = centry_string(centry, mem_ctx);
310 if ((sid_string == NULL) || (!string_to_sid(sid, sid_string))) {
316 /* the server is considered down if it can't give us a sequence number */
317 static BOOL wcache_server_down(struct winbindd_domain *domain)
324 ret = (domain->sequence_number == DOM_SEQUENCE_NONE);
327 DEBUG(10,("wcache_server_down: server for Domain %s down\n",
332 static NTSTATUS fetch_cache_seqnum( struct winbindd_domain *domain, time_t now )
339 DEBUG(10,("fetch_cache_seqnum: tdb == NULL\n"));
340 return NT_STATUS_UNSUCCESSFUL;
343 fstr_sprintf( key, "SEQNUM/%s", domain->name );
345 data = tdb_fetch_bystring( wcache->tdb, key );
346 if ( !data.dptr || data.dsize!=8 ) {
347 DEBUG(10,("fetch_cache_seqnum: invalid data size key [%s]\n", key ));
348 return NT_STATUS_UNSUCCESSFUL;
351 domain->sequence_number = IVAL(data.dptr, 0);
352 domain->last_seq_check = IVAL(data.dptr, 4);
354 SAFE_FREE(data.dptr);
356 /* have we expired? */
358 time_diff = now - domain->last_seq_check;
359 if ( time_diff > lp_winbind_cache_time() ) {
360 DEBUG(10,("fetch_cache_seqnum: timeout [%s][%u @ %u]\n",
361 domain->name, domain->sequence_number,
362 (uint32)domain->last_seq_check));
363 return NT_STATUS_UNSUCCESSFUL;
366 DEBUG(10,("fetch_cache_seqnum: success [%s][%u @ %u]\n",
367 domain->name, domain->sequence_number,
368 (uint32)domain->last_seq_check));
373 static NTSTATUS store_cache_seqnum( struct winbindd_domain *domain )
380 DEBUG(10,("store_cache_seqnum: tdb == NULL\n"));
381 return NT_STATUS_UNSUCCESSFUL;
384 fstr_sprintf( key_str, "SEQNUM/%s", domain->name );
386 key.dsize = strlen(key_str)+1;
388 SIVAL(buf, 0, domain->sequence_number);
389 SIVAL(buf, 4, domain->last_seq_check);
393 if ( tdb_store( wcache->tdb, key, data, TDB_REPLACE) == -1 ) {
394 DEBUG(10,("store_cache_seqnum: tdb_store fail key [%s]\n", key_str ));
395 return NT_STATUS_UNSUCCESSFUL;
398 DEBUG(10,("store_cache_seqnum: success [%s][%u @ %u]\n",
399 domain->name, domain->sequence_number,
400 (uint32)domain->last_seq_check));
406 refresh the domain sequence number. If force is True
407 then always refresh it, no matter how recently we fetched it
410 static void refresh_sequence_number(struct winbindd_domain *domain, BOOL force)
414 time_t t = time(NULL);
415 unsigned cache_time = lp_winbind_cache_time();
419 #if 0 /* JERRY -- disable as the default cache time is now 5 minutes */
420 /* trying to reconnect is expensive, don't do it too often */
421 if (domain->sequence_number == DOM_SEQUENCE_NONE) {
426 time_diff = t - domain->last_seq_check;
428 /* see if we have to refetch the domain sequence number */
429 if (!force && (time_diff < cache_time)) {
430 DEBUG(10, ("refresh_sequence_number: %s time ok\n", domain->name));
434 /* try to get the sequence number from the tdb cache first */
435 /* this will update the timestamp as well */
437 status = fetch_cache_seqnum( domain, t );
438 if ( NT_STATUS_IS_OK(status) )
441 /* important! make sure that we know if this is a native
442 mode domain or not */
444 status = domain->backend->sequence_number(domain, &domain->sequence_number);
446 if (!NT_STATUS_IS_OK(status)) {
447 DEBUG(10,("refresh_sequence_number: failed with %s\n", nt_errstr(status)));
448 domain->sequence_number = DOM_SEQUENCE_NONE;
451 domain->last_status = status;
452 domain->last_seq_check = time(NULL);
454 /* save the new sequence number ni the cache */
455 store_cache_seqnum( domain );
458 DEBUG(10, ("refresh_sequence_number: %s seq number is now %d\n",
459 domain->name, domain->sequence_number));
465 decide if a cache entry has expired
467 static BOOL centry_expired(struct winbindd_domain *domain, const char *keystr, struct cache_entry *centry)
469 /* If we've been told to be offline - stay in that state... */
470 if (lp_winbind_offline_logon() && global_winbindd_offline_state) {
471 DEBUG(10,("centry_expired: Key %s for domain %s valid as winbindd is globally offline.\n",
472 keystr, domain->name ));
476 /* when the domain is offline return the cached entry.
477 * This deals with transient offline states... */
479 if (!domain->online) {
480 DEBUG(10,("centry_expired: Key %s for domain %s valid as domain is offline.\n",
481 keystr, domain->name ));
485 /* if the server is OK and our cache entry came from when it was down then
486 the entry is invalid */
487 if ((domain->sequence_number != DOM_SEQUENCE_NONE) &&
488 (centry->sequence_number == DOM_SEQUENCE_NONE)) {
489 DEBUG(10,("centry_expired: Key %s for domain %s invalid sequence.\n",
490 keystr, domain->name ));
494 /* if the server is down or the cache entry is not older than the
495 current sequence number then it is OK */
496 if (wcache_server_down(domain) ||
497 centry->sequence_number == domain->sequence_number) {
498 DEBUG(10,("centry_expired: Key %s for domain %s is good.\n",
499 keystr, domain->name ));
503 DEBUG(10,("centry_expired: Key %s for domain %s expired\n",
504 keystr, domain->name ));
510 static struct cache_entry *wcache_fetch_raw(char *kstr)
513 struct cache_entry *centry;
517 key.dsize = strlen(kstr);
518 data = tdb_fetch(wcache->tdb, key);
524 centry = SMB_XMALLOC_P(struct cache_entry);
525 centry->data = (unsigned char *)data.dptr;
526 centry->len = data.dsize;
529 if (centry->len < 8) {
530 /* huh? corrupt cache? */
531 DEBUG(10,("wcache_fetch_raw: Corrupt cache for key %s (len < 8) ?\n", kstr));
536 centry->status = NT_STATUS(centry_uint32(centry));
537 centry->sequence_number = centry_uint32(centry);
543 fetch an entry from the cache, with a varargs key. auto-fetch the sequence
544 number and return status
546 static struct cache_entry *wcache_fetch(struct winbind_cache *cache,
547 struct winbindd_domain *domain,
548 const char *format, ...) PRINTF_ATTRIBUTE(3,4);
549 static struct cache_entry *wcache_fetch(struct winbind_cache *cache,
550 struct winbindd_domain *domain,
551 const char *format, ...)
555 struct cache_entry *centry;
557 extern BOOL opt_nocache;
563 refresh_sequence_number(domain, False);
565 va_start(ap, format);
566 smb_xvasprintf(&kstr, format, ap);
569 centry = wcache_fetch_raw(kstr);
570 if (centry == NULL) {
575 if (centry_expired(domain, kstr, centry)) {
577 DEBUG(10,("wcache_fetch: entry %s expired for domain %s\n",
578 kstr, domain->name ));
585 DEBUG(10,("wcache_fetch: returning entry %s for domain %s\n",
586 kstr, domain->name ));
592 static void wcache_delete(const char *format, ...) PRINTF_ATTRIBUTE(1,2);
593 static void wcache_delete(const char *format, ...)
599 va_start(ap, format);
600 smb_xvasprintf(&kstr, format, ap);
604 key.dsize = strlen(kstr);
606 tdb_delete(wcache->tdb, key);
611 make sure we have at least len bytes available in a centry
613 static void centry_expand(struct cache_entry *centry, uint32 len)
615 if (centry->len - centry->ofs >= len)
618 centry->data = SMB_REALLOC_ARRAY(centry->data, unsigned char,
621 DEBUG(0,("out of memory: needed %d bytes in centry_expand\n", centry->len));
622 smb_panic("out of memory in centry_expand");
627 push a uint32 into a centry
629 static void centry_put_uint32(struct cache_entry *centry, uint32 v)
631 centry_expand(centry, 4);
632 SIVAL(centry->data, centry->ofs, v);
637 push a uint16 into a centry
639 static void centry_put_uint16(struct cache_entry *centry, uint16 v)
641 centry_expand(centry, 2);
642 SIVAL(centry->data, centry->ofs, v);
647 push a uint8 into a centry
649 static void centry_put_uint8(struct cache_entry *centry, uint8 v)
651 centry_expand(centry, 1);
652 SCVAL(centry->data, centry->ofs, v);
657 push a string into a centry
659 static void centry_put_string(struct cache_entry *centry, const char *s)
664 /* null strings are marked as len 0xFFFF */
665 centry_put_uint8(centry, 0xFF);
670 /* can't handle more than 254 char strings. Truncating is probably best */
672 DEBUG(10,("centry_put_string: truncating len (%d) to: 254\n", len));
675 centry_put_uint8(centry, len);
676 centry_expand(centry, len);
677 memcpy(centry->data + centry->ofs, s, len);
682 push a 16 byte hash into a centry - treat as 16 byte string.
684 static void centry_put_hash16(struct cache_entry *centry, const uint8 val[16])
686 centry_put_uint8(centry, 16);
687 centry_expand(centry, 16);
688 memcpy(centry->data + centry->ofs, val, 16);
692 static void centry_put_sid(struct cache_entry *centry, const DOM_SID *sid)
695 centry_put_string(centry, sid_to_string(sid_string, sid));
699 push a NTTIME into a centry
701 static void centry_put_nttime(struct cache_entry *centry, NTTIME nt)
703 centry_expand(centry, 8);
704 SIVAL(centry->data, centry->ofs, nt & 0xFFFFFFFF);
706 SIVAL(centry->data, centry->ofs, nt >> 32);
711 push a time_t into a centry
713 static void centry_put_time(struct cache_entry *centry, time_t t)
715 centry_expand(centry, sizeof(time_t));
716 SIVAL(centry->data, centry->ofs, t); /* FIXME: is this correct ?? */
717 centry->ofs += sizeof(time_t);
721 start a centry for output. When finished, call centry_end()
723 struct cache_entry *centry_start(struct winbindd_domain *domain, NTSTATUS status)
725 struct cache_entry *centry;
730 centry = SMB_XMALLOC_P(struct cache_entry);
732 centry->len = 8192; /* reasonable default */
733 centry->data = SMB_XMALLOC_ARRAY(uint8, centry->len);
735 centry->sequence_number = domain->sequence_number;
736 centry_put_uint32(centry, NT_STATUS_V(status));
737 centry_put_uint32(centry, centry->sequence_number);
742 finish a centry and write it to the tdb
744 static void centry_end(struct cache_entry *centry, const char *format, ...) PRINTF_ATTRIBUTE(2,3);
745 static void centry_end(struct cache_entry *centry, const char *format, ...)
751 va_start(ap, format);
752 smb_xvasprintf(&kstr, format, ap);
756 key.dsize = strlen(kstr);
757 data.dptr = (char *)centry->data;
758 data.dsize = centry->ofs;
760 tdb_store(wcache->tdb, key, data, TDB_REPLACE);
764 static void wcache_save_name_to_sid(struct winbindd_domain *domain,
765 NTSTATUS status, const char *domain_name,
766 const char *name, const DOM_SID *sid,
767 enum lsa_SidType type)
769 struct cache_entry *centry;
772 centry = centry_start(domain, status);
775 centry_put_uint32(centry, type);
776 centry_put_sid(centry, sid);
777 fstrcpy(uname, name);
779 centry_end(centry, "NS/%s/%s", domain_name, uname);
780 DEBUG(10,("wcache_save_name_to_sid: %s\\%s -> %s\n", domain_name, uname,
781 sid_string_static(sid)));
785 static void wcache_save_sid_to_name(struct winbindd_domain *domain, NTSTATUS status,
786 const DOM_SID *sid, const char *domain_name, const char *name, enum lsa_SidType type)
788 struct cache_entry *centry;
791 if (is_null_sid(&sid)) {
795 centry = centry_start(domain, status);
798 if (NT_STATUS_IS_OK(status)) {
799 centry_put_uint32(centry, type);
800 centry_put_string(centry, domain_name);
801 centry_put_string(centry, name);
803 centry_end(centry, "SN/%s", sid_to_string(sid_string, sid));
804 DEBUG(10,("wcache_save_sid_to_name: %s -> %s\n", sid_string, name));
809 static void wcache_save_user(struct winbindd_domain *domain, NTSTATUS status, WINBIND_USERINFO *info)
811 struct cache_entry *centry;
814 if (is_null_sid(&info->user_sid)) {
818 centry = centry_start(domain, status);
821 centry_put_string(centry, info->acct_name);
822 centry_put_string(centry, info->full_name);
823 centry_put_string(centry, info->homedir);
824 centry_put_string(centry, info->shell);
825 centry_put_sid(centry, &info->user_sid);
826 centry_put_sid(centry, &info->group_sid);
827 centry_end(centry, "U/%s", sid_to_string(sid_string, &info->user_sid));
828 DEBUG(10,("wcache_save_user: %s (acct_name %s)\n", sid_string, info->acct_name));
832 static void wcache_save_lockout_policy(struct winbindd_domain *domain, NTSTATUS status, SAM_UNK_INFO_12 *lockout_policy)
834 struct cache_entry *centry;
836 centry = centry_start(domain, status);
840 centry_put_nttime(centry, lockout_policy->duration);
841 centry_put_nttime(centry, lockout_policy->reset_count);
842 centry_put_uint16(centry, lockout_policy->bad_attempt_lockout);
844 centry_end(centry, "LOC_POL/%s", domain->name);
846 DEBUG(10,("wcache_save_lockout_policy: %s\n", domain->name));
851 static void wcache_save_password_policy(struct winbindd_domain *domain, NTSTATUS status, SAM_UNK_INFO_1 *policy)
853 struct cache_entry *centry;
855 centry = centry_start(domain, status);
859 centry_put_uint16(centry, policy->min_length_password);
860 centry_put_uint16(centry, policy->password_history);
861 centry_put_uint32(centry, policy->password_properties);
862 centry_put_nttime(centry, policy->expire);
863 centry_put_nttime(centry, policy->min_passwordage);
865 centry_end(centry, "PWD_POL/%s", domain->name);
867 DEBUG(10,("wcache_save_password_policy: %s\n", domain->name));
872 NTSTATUS wcache_cached_creds_exist(struct winbindd_domain *domain, const DOM_SID *sid)
874 struct winbind_cache *cache = get_cache(domain);
880 return NT_STATUS_INTERNAL_DB_ERROR;
883 if (is_null_sid(sid)) {
884 return NT_STATUS_INVALID_SID;
887 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
888 return NT_STATUS_INVALID_SID;
891 fstr_sprintf(key_str, "CRED/%s", sid_string_static(sid));
893 data = tdb_fetch(cache->tdb, make_tdb_data(key_str, strlen(key_str)));
895 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
898 SAFE_FREE(data.dptr);
902 /* Lookup creds for a SID - copes with old (unsalted) creds as well
903 as new salted ones. */
905 NTSTATUS wcache_get_creds(struct winbindd_domain *domain,
908 const uint8 **cached_nt_pass,
909 const uint8 **cached_salt)
911 struct winbind_cache *cache = get_cache(domain);
912 struct cache_entry *centry = NULL;
918 return NT_STATUS_INTERNAL_DB_ERROR;
921 if (is_null_sid(sid)) {
922 return NT_STATUS_INVALID_SID;
925 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
926 return NT_STATUS_INVALID_SID;
929 /* Try and get a salted cred first. If we can't
930 fall back to an unsalted cred. */
932 centry = wcache_fetch(cache, domain, "CRED/%s", sid_string_static(sid));
934 DEBUG(10,("wcache_get_creds: entry for [CRED/%s] not found\n",
935 sid_string_static(sid)));
936 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
939 t = centry_time(centry);
941 /* In the salted case this isn't actually the nt_hash itself,
942 but the MD5 of the salt + nt_hash. Let the caller
943 sort this out. It can tell as we only return the cached_salt
944 if we are returning a salted cred. */
946 *cached_nt_pass = (const uint8 *)centry_hash16(centry, mem_ctx);
947 if (*cached_nt_pass == NULL) {
948 const char *sidstr = sid_string_static(sid);
950 /* Bad (old) cred cache. Delete and pretend we
952 DEBUG(0,("wcache_get_creds: bad entry for [CRED/%s] - deleting\n",
954 wcache_delete("CRED/%s", sidstr);
955 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
958 /* We only have 17 bytes more data in the salted cred case. */
959 if (centry->len - centry->ofs == 17) {
960 *cached_salt = (const uint8 *)centry_hash16(centry, mem_ctx);
966 dump_data(100, (const char *)*cached_nt_pass, NT_HASH_LEN);
968 dump_data(100, (const char *)*cached_salt, NT_HASH_LEN);
971 status = centry->status;
973 DEBUG(10,("wcache_get_creds: [Cached] - cached creds for user %s status: %s\n",
974 sid_string_static(sid), nt_errstr(status) ));
980 /* Store creds for a SID - only writes out new salted ones. */
982 NTSTATUS wcache_save_creds(struct winbindd_domain *domain,
985 const uint8 nt_pass[NT_HASH_LEN])
987 struct cache_entry *centry;
990 uint8 cred_salt[NT_HASH_LEN];
991 uint8 salted_hash[NT_HASH_LEN];
993 if (is_null_sid(sid)) {
994 return NT_STATUS_INVALID_SID;
997 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
998 return NT_STATUS_INVALID_SID;
1001 centry = centry_start(domain, NT_STATUS_OK);
1003 return NT_STATUS_INTERNAL_DB_ERROR;
1007 dump_data(100, (const char *)nt_pass, NT_HASH_LEN);
1010 centry_put_time(centry, time(NULL));
1012 /* Create a salt and then salt the hash. */
1013 generate_random_buffer(cred_salt, NT_HASH_LEN);
1014 E_md5hash(cred_salt, nt_pass, salted_hash);
1016 centry_put_hash16(centry, salted_hash);
1017 centry_put_hash16(centry, cred_salt);
1018 centry_end(centry, "CRED/%s", sid_to_string(sid_string, sid));
1020 DEBUG(10,("wcache_save_creds: %s\n", sid_string));
1022 centry_free(centry);
1024 return NT_STATUS_OK;
1028 /* Query display info. This is the basic user list fn */
1029 static NTSTATUS query_user_list(struct winbindd_domain *domain,
1030 TALLOC_CTX *mem_ctx,
1031 uint32 *num_entries,
1032 WINBIND_USERINFO **info)
1034 struct winbind_cache *cache = get_cache(domain);
1035 struct cache_entry *centry = NULL;
1037 unsigned int i, retry;
1042 centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
1046 *num_entries = centry_uint32(centry);
1048 if (*num_entries == 0)
1051 (*info) = TALLOC_ARRAY(mem_ctx, WINBIND_USERINFO, *num_entries);
1053 smb_panic("query_user_list out of memory");
1054 for (i=0; i<(*num_entries); i++) {
1055 (*info)[i].acct_name = centry_string(centry, mem_ctx);
1056 (*info)[i].full_name = centry_string(centry, mem_ctx);
1057 (*info)[i].homedir = centry_string(centry, mem_ctx);
1058 (*info)[i].shell = centry_string(centry, mem_ctx);
1059 centry_sid(centry, mem_ctx, &(*info)[i].user_sid);
1060 centry_sid(centry, mem_ctx, &(*info)[i].group_sid);
1064 status = centry->status;
1066 DEBUG(10,("query_user_list: [Cached] - cached list for domain %s status: %s\n",
1067 domain->name, nt_errstr(status) ));
1069 centry_free(centry);
1076 /* Return status value returned by seq number check */
1078 if (!NT_STATUS_IS_OK(domain->last_status))
1079 return domain->last_status;
1081 /* Put the query_user_list() in a retry loop. There appears to be
1082 * some bug either with Windows 2000 or Samba's handling of large
1083 * rpc replies. This manifests itself as sudden disconnection
1084 * at a random point in the enumeration of a large (60k) user list.
1085 * The retry loop simply tries the operation again. )-: It's not
1086 * pretty but an acceptable workaround until we work out what the
1087 * real problem is. */
1092 DEBUG(10,("query_user_list: [Cached] - doing backend query for list for domain %s\n",
1095 status = domain->backend->query_user_list(domain, mem_ctx, num_entries, info);
1096 if (!NT_STATUS_IS_OK(status))
1097 DEBUG(3, ("query_user_list: returned 0x%08x, "
1098 "retrying\n", NT_STATUS_V(status)));
1099 if (NT_STATUS_EQUAL(status, NT_STATUS_UNSUCCESSFUL)) {
1100 DEBUG(3, ("query_user_list: flushing "
1101 "connection cache\n"));
1102 invalidate_cm_connection(&domain->conn);
1105 } while (NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_UNSUCCESSFUL) &&
1109 refresh_sequence_number(domain, False);
1110 centry = centry_start(domain, status);
1113 centry_put_uint32(centry, *num_entries);
1114 for (i=0; i<(*num_entries); i++) {
1115 centry_put_string(centry, (*info)[i].acct_name);
1116 centry_put_string(centry, (*info)[i].full_name);
1117 centry_put_string(centry, (*info)[i].homedir);
1118 centry_put_string(centry, (*info)[i].shell);
1119 centry_put_sid(centry, &(*info)[i].user_sid);
1120 centry_put_sid(centry, &(*info)[i].group_sid);
1121 if (domain->backend->consistent) {
1122 /* when the backend is consistent we can pre-prime some mappings */
1123 wcache_save_name_to_sid(domain, NT_STATUS_OK,
1125 (*info)[i].acct_name,
1126 &(*info)[i].user_sid,
1128 wcache_save_sid_to_name(domain, NT_STATUS_OK,
1129 &(*info)[i].user_sid,
1131 (*info)[i].acct_name,
1133 wcache_save_user(domain, NT_STATUS_OK, &(*info)[i]);
1136 centry_end(centry, "UL/%s", domain->name);
1137 centry_free(centry);
1143 /* list all domain groups */
1144 static NTSTATUS enum_dom_groups(struct winbindd_domain *domain,
1145 TALLOC_CTX *mem_ctx,
1146 uint32 *num_entries,
1147 struct acct_info **info)
1149 struct winbind_cache *cache = get_cache(domain);
1150 struct cache_entry *centry = NULL;
1157 centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
1161 *num_entries = centry_uint32(centry);
1163 if (*num_entries == 0)
1166 (*info) = TALLOC_ARRAY(mem_ctx, struct acct_info, *num_entries);
1168 smb_panic("enum_dom_groups out of memory");
1169 for (i=0; i<(*num_entries); i++) {
1170 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1171 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1172 (*info)[i].rid = centry_uint32(centry);
1176 status = centry->status;
1178 DEBUG(10,("enum_dom_groups: [Cached] - cached list for domain %s status: %s\n",
1179 domain->name, nt_errstr(status) ));
1181 centry_free(centry);
1188 /* Return status value returned by seq number check */
1190 if (!NT_STATUS_IS_OK(domain->last_status))
1191 return domain->last_status;
1193 DEBUG(10,("enum_dom_groups: [Cached] - doing backend query for list for domain %s\n",
1196 status = domain->backend->enum_dom_groups(domain, mem_ctx, num_entries, info);
1199 refresh_sequence_number(domain, False);
1200 centry = centry_start(domain, status);
1203 centry_put_uint32(centry, *num_entries);
1204 for (i=0; i<(*num_entries); i++) {
1205 centry_put_string(centry, (*info)[i].acct_name);
1206 centry_put_string(centry, (*info)[i].acct_desc);
1207 centry_put_uint32(centry, (*info)[i].rid);
1209 centry_end(centry, "GL/%s/domain", domain->name);
1210 centry_free(centry);
1216 /* list all domain groups */
1217 static NTSTATUS enum_local_groups(struct winbindd_domain *domain,
1218 TALLOC_CTX *mem_ctx,
1219 uint32 *num_entries,
1220 struct acct_info **info)
1222 struct winbind_cache *cache = get_cache(domain);
1223 struct cache_entry *centry = NULL;
1230 centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
1234 *num_entries = centry_uint32(centry);
1236 if (*num_entries == 0)
1239 (*info) = TALLOC_ARRAY(mem_ctx, struct acct_info, *num_entries);
1241 smb_panic("enum_dom_groups out of memory");
1242 for (i=0; i<(*num_entries); i++) {
1243 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1244 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1245 (*info)[i].rid = centry_uint32(centry);
1250 /* If we are returning cached data and the domain controller
1251 is down then we don't know whether the data is up to date
1252 or not. Return NT_STATUS_MORE_PROCESSING_REQUIRED to
1255 if (wcache_server_down(domain)) {
1256 DEBUG(10, ("enum_local_groups: returning cached user list and server was down\n"));
1257 status = NT_STATUS_MORE_PROCESSING_REQUIRED;
1259 status = centry->status;
1261 DEBUG(10,("enum_local_groups: [Cached] - cached list for domain %s status: %s\n",
1262 domain->name, nt_errstr(status) ));
1264 centry_free(centry);
1271 /* Return status value returned by seq number check */
1273 if (!NT_STATUS_IS_OK(domain->last_status))
1274 return domain->last_status;
1276 DEBUG(10,("enum_local_groups: [Cached] - doing backend query for list for domain %s\n",
1279 status = domain->backend->enum_local_groups(domain, mem_ctx, num_entries, info);
1282 refresh_sequence_number(domain, False);
1283 centry = centry_start(domain, status);
1286 centry_put_uint32(centry, *num_entries);
1287 for (i=0; i<(*num_entries); i++) {
1288 centry_put_string(centry, (*info)[i].acct_name);
1289 centry_put_string(centry, (*info)[i].acct_desc);
1290 centry_put_uint32(centry, (*info)[i].rid);
1292 centry_end(centry, "GL/%s/local", domain->name);
1293 centry_free(centry);
1299 /* convert a single name to a sid in a domain */
1300 static NTSTATUS name_to_sid(struct winbindd_domain *domain,
1301 TALLOC_CTX *mem_ctx,
1302 const char *domain_name,
1305 enum lsa_SidType *type)
1307 struct winbind_cache *cache = get_cache(domain);
1308 struct cache_entry *centry = NULL;
1315 fstrcpy(uname, name);
1317 centry = wcache_fetch(cache, domain, "NS/%s/%s", domain_name, uname);
1320 *type = (enum lsa_SidType)centry_uint32(centry);
1321 status = centry->status;
1322 if (NT_STATUS_IS_OK(status)) {
1323 centry_sid(centry, mem_ctx, sid);
1326 DEBUG(10,("name_to_sid: [Cached] - cached name for domain %s status: %s\n",
1327 domain->name, nt_errstr(status) ));
1329 centry_free(centry);
1335 /* If the seq number check indicated that there is a problem
1336 * with this DC, then return that status... except for
1337 * access_denied. This is special because the dc may be in
1338 * "restrict anonymous = 1" mode, in which case it will deny
1339 * most unauthenticated operations, but *will* allow the LSA
1340 * name-to-sid that we try as a fallback. */
1342 if (!(NT_STATUS_IS_OK(domain->last_status)
1343 || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1344 return domain->last_status;
1346 DEBUG(10,("name_to_sid: [Cached] - doing backend query for name for domain %s\n",
1349 status = domain->backend->name_to_sid(domain, mem_ctx, domain_name, name, sid, type);
1352 if (domain->online && !is_null_sid(sid)) {
1353 wcache_save_name_to_sid(domain, status, domain_name, name, sid, *type);
1356 if (NT_STATUS_IS_OK(status)) {
1357 strupper_m(CONST_DISCARD(char *,domain_name));
1358 strlower_m(CONST_DISCARD(char *,name));
1359 wcache_save_sid_to_name(domain, status, sid, domain_name, name, *type);
1365 /* convert a sid to a user or group name. The sid is guaranteed to be in the domain
1367 static NTSTATUS sid_to_name(struct winbindd_domain *domain,
1368 TALLOC_CTX *mem_ctx,
1372 enum lsa_SidType *type)
1374 struct winbind_cache *cache = get_cache(domain);
1375 struct cache_entry *centry = NULL;
1382 centry = wcache_fetch(cache, domain, "SN/%s", sid_to_string(sid_string, sid));
1385 if (NT_STATUS_IS_OK(centry->status)) {
1386 *type = (enum lsa_SidType)centry_uint32(centry);
1387 *domain_name = centry_string(centry, mem_ctx);
1388 *name = centry_string(centry, mem_ctx);
1390 status = centry->status;
1392 DEBUG(10,("sid_to_name: [Cached] - cached name for domain %s status: %s\n",
1393 domain->name, nt_errstr(status) ));
1395 centry_free(centry);
1400 *domain_name = NULL;
1402 /* If the seq number check indicated that there is a problem
1403 * with this DC, then return that status... except for
1404 * access_denied. This is special because the dc may be in
1405 * "restrict anonymous = 1" mode, in which case it will deny
1406 * most unauthenticated operations, but *will* allow the LSA
1407 * sid-to-name that we try as a fallback. */
1409 if (!(NT_STATUS_IS_OK(domain->last_status)
1410 || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1411 return domain->last_status;
1413 DEBUG(10,("sid_to_name: [Cached] - doing backend query for name for domain %s\n",
1416 status = domain->backend->sid_to_name(domain, mem_ctx, sid, domain_name, name, type);
1419 refresh_sequence_number(domain, False);
1420 wcache_save_sid_to_name(domain, status, sid, *domain_name, *name, *type);
1422 /* We can't save the name to sid mapping here, as with sid history a
1423 * later name2sid would give the wrong sid. */
1428 static NTSTATUS rids_to_names(struct winbindd_domain *domain,
1429 TALLOC_CTX *mem_ctx,
1430 const DOM_SID *domain_sid,
1435 enum lsa_SidType **types)
1437 struct winbind_cache *cache = get_cache(domain);
1439 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
1443 *domain_name = NULL;
1451 if (num_rids == 0) {
1452 return NT_STATUS_OK;
1455 *names = TALLOC_ARRAY(mem_ctx, char *, num_rids);
1456 *types = TALLOC_ARRAY(mem_ctx, enum lsa_SidType, num_rids);
1458 if ((*names == NULL) || (*types == NULL)) {
1459 result = NT_STATUS_NO_MEMORY;
1463 have_mapped = have_unmapped = False;
1465 for (i=0; i<num_rids; i++) {
1467 struct cache_entry *centry;
1469 if (!sid_compose(&sid, domain_sid, rids[i])) {
1470 result = NT_STATUS_INTERNAL_ERROR;
1474 centry = wcache_fetch(cache, domain, "SN/%s",
1475 sid_string_static(&sid));
1480 (*types)[i] = SID_NAME_UNKNOWN;
1481 (*names)[i] = talloc_strdup(*names, "");
1483 if (NT_STATUS_IS_OK(centry->status)) {
1486 (*types)[i] = (enum lsa_SidType)centry_uint32(centry);
1487 dom = centry_string(centry, mem_ctx);
1488 if (*domain_name == NULL) {
1493 (*names)[i] = centry_string(centry, *names);
1495 have_unmapped = True;
1498 centry_free(centry);
1502 return NT_STATUS_NONE_MAPPED;
1504 if (!have_unmapped) {
1505 return NT_STATUS_OK;
1507 return STATUS_SOME_UNMAPPED;
1511 TALLOC_FREE(*names);
1512 TALLOC_FREE(*types);
1514 result = domain->backend->rids_to_names(domain, mem_ctx, domain_sid,
1515 rids, num_rids, domain_name,
1518 if (!NT_STATUS_IS_OK(result) &&
1519 !NT_STATUS_EQUAL(result, STATUS_SOME_UNMAPPED)) {
1523 refresh_sequence_number(domain, False);
1525 for (i=0; i<num_rids; i++) {
1529 if (!sid_compose(&sid, domain_sid, rids[i])) {
1530 result = NT_STATUS_INTERNAL_ERROR;
1534 status = (*types)[i] == SID_NAME_UNKNOWN ?
1535 NT_STATUS_NONE_MAPPED : NT_STATUS_OK;
1537 wcache_save_sid_to_name(domain, status, &sid, *domain_name,
1538 (*names)[i], (*types)[i]);
1545 TALLOC_FREE(*names);
1546 TALLOC_FREE(*types);
1550 /* Lookup user information from a rid */
1551 static NTSTATUS query_user(struct winbindd_domain *domain,
1552 TALLOC_CTX *mem_ctx,
1553 const DOM_SID *user_sid,
1554 WINBIND_USERINFO *info)
1556 struct winbind_cache *cache = get_cache(domain);
1557 struct cache_entry *centry = NULL;
1563 centry = wcache_fetch(cache, domain, "U/%s", sid_string_static(user_sid));
1565 /* If we have an access denied cache entry and a cached info3 in the
1566 samlogon cache then do a query. This will force the rpc back end
1567 to return the info3 data. */
1569 if (NT_STATUS_V(domain->last_status) == NT_STATUS_V(NT_STATUS_ACCESS_DENIED) &&
1570 netsamlogon_cache_have(user_sid)) {
1571 DEBUG(10, ("query_user: cached access denied and have cached info3\n"));
1572 domain->last_status = NT_STATUS_OK;
1573 centry_free(centry);
1580 info->acct_name = centry_string(centry, mem_ctx);
1581 info->full_name = centry_string(centry, mem_ctx);
1582 info->homedir = centry_string(centry, mem_ctx);
1583 info->shell = centry_string(centry, mem_ctx);
1584 centry_sid(centry, mem_ctx, &info->user_sid);
1585 centry_sid(centry, mem_ctx, &info->group_sid);
1586 status = centry->status;
1588 DEBUG(10,("query_user: [Cached] - cached info for domain %s status: %s\n",
1589 domain->name, nt_errstr(status) ));
1591 centry_free(centry);
1597 /* Return status value returned by seq number check */
1599 if (!NT_STATUS_IS_OK(domain->last_status))
1600 return domain->last_status;
1602 DEBUG(10,("sid_to_name: [Cached] - doing backend query for info for domain %s\n",
1605 status = domain->backend->query_user(domain, mem_ctx, user_sid, info);
1608 refresh_sequence_number(domain, False);
1609 wcache_save_user(domain, status, info);
1615 /* Lookup groups a user is a member of. */
1616 static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
1617 TALLOC_CTX *mem_ctx,
1618 const DOM_SID *user_sid,
1619 uint32 *num_groups, DOM_SID **user_gids)
1621 struct winbind_cache *cache = get_cache(domain);
1622 struct cache_entry *centry = NULL;
1630 centry = wcache_fetch(cache, domain, "UG/%s", sid_to_string(sid_string, user_sid));
1632 /* If we have an access denied cache entry and a cached info3 in the
1633 samlogon cache then do a query. This will force the rpc back end
1634 to return the info3 data. */
1636 if (NT_STATUS_V(domain->last_status) == NT_STATUS_V(NT_STATUS_ACCESS_DENIED) &&
1637 netsamlogon_cache_have(user_sid)) {
1638 DEBUG(10, ("lookup_usergroups: cached access denied and have cached info3\n"));
1639 domain->last_status = NT_STATUS_OK;
1640 centry_free(centry);
1647 *num_groups = centry_uint32(centry);
1649 if (*num_groups == 0)
1652 (*user_gids) = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_groups);
1654 smb_panic("lookup_usergroups out of memory");
1655 for (i=0; i<(*num_groups); i++) {
1656 centry_sid(centry, mem_ctx, &(*user_gids)[i]);
1660 status = centry->status;
1662 DEBUG(10,("lookup_usergroups: [Cached] - cached info for domain %s status: %s\n",
1663 domain->name, nt_errstr(status) ));
1665 centry_free(centry);
1670 (*user_gids) = NULL;
1672 /* Return status value returned by seq number check */
1674 if (!NT_STATUS_IS_OK(domain->last_status))
1675 return domain->last_status;
1677 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info for domain %s\n",
1680 status = domain->backend->lookup_usergroups(domain, mem_ctx, user_sid, num_groups, user_gids);
1683 refresh_sequence_number(domain, False);
1684 centry = centry_start(domain, status);
1687 centry_put_uint32(centry, *num_groups);
1688 for (i=0; i<(*num_groups); i++) {
1689 centry_put_sid(centry, &(*user_gids)[i]);
1691 centry_end(centry, "UG/%s", sid_to_string(sid_string, user_sid));
1692 centry_free(centry);
1698 static NTSTATUS lookup_useraliases(struct winbindd_domain *domain,
1699 TALLOC_CTX *mem_ctx,
1700 uint32 num_sids, const DOM_SID *sids,
1701 uint32 *num_aliases, uint32 **alias_rids)
1703 struct winbind_cache *cache = get_cache(domain);
1704 struct cache_entry *centry = NULL;
1706 char *sidlist = talloc_strdup(mem_ctx, "");
1712 if (num_sids == 0) {
1715 return NT_STATUS_OK;
1718 /* We need to cache indexed by the whole list of SIDs, the aliases
1719 * resulting might come from any of the SIDs. */
1721 for (i=0; i<num_sids; i++) {
1722 sidlist = talloc_asprintf(mem_ctx, "%s/%s", sidlist,
1723 sid_string_static(&sids[i]));
1724 if (sidlist == NULL)
1725 return NT_STATUS_NO_MEMORY;
1728 centry = wcache_fetch(cache, domain, "UA%s", sidlist);
1733 *num_aliases = centry_uint32(centry);
1736 (*alias_rids) = TALLOC_ARRAY(mem_ctx, uint32, *num_aliases);
1738 if ((*num_aliases != 0) && ((*alias_rids) == NULL)) {
1739 centry_free(centry);
1740 return NT_STATUS_NO_MEMORY;
1743 for (i=0; i<(*num_aliases); i++)
1744 (*alias_rids)[i] = centry_uint32(centry);
1746 status = centry->status;
1748 DEBUG(10,("lookup_useraliases: [Cached] - cached info for domain: %s "
1749 "status %s\n", domain->name, nt_errstr(status)));
1751 centry_free(centry);
1756 (*alias_rids) = NULL;
1758 if (!NT_STATUS_IS_OK(domain->last_status))
1759 return domain->last_status;
1761 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info "
1762 "for domain %s\n", domain->name ));
1764 status = domain->backend->lookup_useraliases(domain, mem_ctx,
1766 num_aliases, alias_rids);
1769 refresh_sequence_number(domain, False);
1770 centry = centry_start(domain, status);
1773 centry_put_uint32(centry, *num_aliases);
1774 for (i=0; i<(*num_aliases); i++)
1775 centry_put_uint32(centry, (*alias_rids)[i]);
1776 centry_end(centry, "UA%s", sidlist);
1777 centry_free(centry);
1784 static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
1785 TALLOC_CTX *mem_ctx,
1786 const DOM_SID *group_sid, uint32 *num_names,
1787 DOM_SID **sid_mem, char ***names,
1788 uint32 **name_types)
1790 struct winbind_cache *cache = get_cache(domain);
1791 struct cache_entry *centry = NULL;
1799 centry = wcache_fetch(cache, domain, "GM/%s", sid_to_string(sid_string, group_sid));
1803 *num_names = centry_uint32(centry);
1805 if (*num_names == 0)
1808 (*sid_mem) = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_names);
1809 (*names) = TALLOC_ARRAY(mem_ctx, char *, *num_names);
1810 (*name_types) = TALLOC_ARRAY(mem_ctx, uint32, *num_names);
1812 if (! (*sid_mem) || ! (*names) || ! (*name_types)) {
1813 smb_panic("lookup_groupmem out of memory");
1816 for (i=0; i<(*num_names); i++) {
1817 centry_sid(centry, mem_ctx, &(*sid_mem)[i]);
1818 (*names)[i] = centry_string(centry, mem_ctx);
1819 (*name_types)[i] = centry_uint32(centry);
1823 status = centry->status;
1825 DEBUG(10,("lookup_groupmem: [Cached] - cached info for domain %s status: %s\n",
1826 domain->name, nt_errstr(status)));
1828 centry_free(centry);
1835 (*name_types) = NULL;
1837 /* Return status value returned by seq number check */
1839 if (!NT_STATUS_IS_OK(domain->last_status))
1840 return domain->last_status;
1842 DEBUG(10,("lookup_groupmem: [Cached] - doing backend query for info for domain %s\n",
1845 status = domain->backend->lookup_groupmem(domain, mem_ctx, group_sid, num_names,
1846 sid_mem, names, name_types);
1849 refresh_sequence_number(domain, False);
1850 centry = centry_start(domain, status);
1853 centry_put_uint32(centry, *num_names);
1854 for (i=0; i<(*num_names); i++) {
1855 centry_put_sid(centry, &(*sid_mem)[i]);
1856 centry_put_string(centry, (*names)[i]);
1857 centry_put_uint32(centry, (*name_types)[i]);
1859 centry_end(centry, "GM/%s", sid_to_string(sid_string, group_sid));
1860 centry_free(centry);
1866 /* find the sequence number for a domain */
1867 static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
1869 refresh_sequence_number(domain, False);
1871 *seq = domain->sequence_number;
1873 return NT_STATUS_OK;
1876 /* enumerate trusted domains
1877 * (we need to have the list of trustdoms in the cache when we go offline) -
1879 static NTSTATUS trusted_domains(struct winbindd_domain *domain,
1880 TALLOC_CTX *mem_ctx,
1881 uint32 *num_domains,
1886 struct winbind_cache *cache = get_cache(domain);
1887 struct cache_entry *centry = NULL;
1894 centry = wcache_fetch(cache, domain, "TRUSTDOMS/%s", domain->name);
1900 *num_domains = centry_uint32(centry);
1902 (*names) = TALLOC_ARRAY(mem_ctx, char *, *num_domains);
1903 (*alt_names) = TALLOC_ARRAY(mem_ctx, char *, *num_domains);
1904 (*dom_sids) = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_domains);
1906 if (! (*dom_sids) || ! (*names) || ! (*alt_names)) {
1907 smb_panic("trusted_domains out of memory");
1910 for (i=0; i<(*num_domains); i++) {
1911 (*names)[i] = centry_string(centry, mem_ctx);
1912 (*alt_names)[i] = centry_string(centry, mem_ctx);
1913 centry_sid(centry, mem_ctx, &(*dom_sids)[i]);
1916 status = centry->status;
1918 DEBUG(10,("trusted_domains: [Cached] - cached info for domain %s (%d trusts) status: %s\n",
1919 domain->name, *num_domains, nt_errstr(status) ));
1921 centry_free(centry);
1928 (*alt_names) = NULL;
1930 /* Return status value returned by seq number check */
1932 if (!NT_STATUS_IS_OK(domain->last_status))
1933 return domain->last_status;
1935 DEBUG(10,("trusted_domains: [Cached] - doing backend query for info for domain %s\n",
1938 status = domain->backend->trusted_domains(domain, mem_ctx, num_domains,
1939 names, alt_names, dom_sids);
1941 /* no trusts gives NT_STATUS_NO_MORE_ENTRIES resetting to NT_STATUS_OK
1942 * so that the generic centry handling still applies correctly -
1945 if (!NT_STATUS_IS_ERR(status)) {
1946 status = NT_STATUS_OK;
1950 refresh_sequence_number(domain, False);
1952 centry = centry_start(domain, status);
1956 centry_put_uint32(centry, *num_domains);
1958 for (i=0; i<(*num_domains); i++) {
1959 centry_put_string(centry, (*names)[i]);
1960 centry_put_string(centry, (*alt_names)[i]);
1961 centry_put_sid(centry, &(*dom_sids)[i]);
1964 centry_end(centry, "TRUSTDOMS/%s", domain->name);
1966 centry_free(centry);
1972 /* get lockout policy */
1973 static NTSTATUS lockout_policy(struct winbindd_domain *domain,
1974 TALLOC_CTX *mem_ctx,
1975 SAM_UNK_INFO_12 *policy){
1976 struct winbind_cache *cache = get_cache(domain);
1977 struct cache_entry *centry = NULL;
1983 centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
1988 policy->duration = centry_nttime(centry);
1989 policy->reset_count = centry_nttime(centry);
1990 policy->bad_attempt_lockout = centry_uint16(centry);
1992 status = centry->status;
1994 DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
1995 domain->name, nt_errstr(status) ));
1997 centry_free(centry);
2001 ZERO_STRUCTP(policy);
2003 /* Return status value returned by seq number check */
2005 if (!NT_STATUS_IS_OK(domain->last_status))
2006 return domain->last_status;
2008 DEBUG(10,("lockout_policy: [Cached] - doing backend query for info for domain %s\n",
2011 status = domain->backend->lockout_policy(domain, mem_ctx, policy);
2014 refresh_sequence_number(domain, False);
2015 wcache_save_lockout_policy(domain, status, policy);
2020 /* get password policy */
2021 static NTSTATUS password_policy(struct winbindd_domain *domain,
2022 TALLOC_CTX *mem_ctx,
2023 SAM_UNK_INFO_1 *policy)
2025 struct winbind_cache *cache = get_cache(domain);
2026 struct cache_entry *centry = NULL;
2032 centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
2037 policy->min_length_password = centry_uint16(centry);
2038 policy->password_history = centry_uint16(centry);
2039 policy->password_properties = centry_uint32(centry);
2040 policy->expire = centry_nttime(centry);
2041 policy->min_passwordage = centry_nttime(centry);
2043 status = centry->status;
2045 DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2046 domain->name, nt_errstr(status) ));
2048 centry_free(centry);
2052 ZERO_STRUCTP(policy);
2054 /* Return status value returned by seq number check */
2056 if (!NT_STATUS_IS_OK(domain->last_status))
2057 return domain->last_status;
2059 DEBUG(10,("password_policy: [Cached] - doing backend query for info for domain %s\n",
2062 status = domain->backend->password_policy(domain, mem_ctx, policy);
2065 refresh_sequence_number(domain, False);
2066 wcache_save_password_policy(domain, status, policy);
2072 /* Invalidate cached user and group lists coherently */
2074 static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
2077 if (strncmp(kbuf.dptr, "UL/", 3) == 0 ||
2078 strncmp(kbuf.dptr, "GL/", 3) == 0)
2079 tdb_delete(the_tdb, kbuf);
2084 /* Invalidate the getpwnam and getgroups entries for a winbindd domain */
2086 void wcache_invalidate_samlogon(struct winbindd_domain *domain,
2087 NET_USER_INFO_3 *info3)
2089 struct winbind_cache *cache;
2094 cache = get_cache(domain);
2095 netsamlogon_clear_cached_user(cache->tdb, info3);
2098 void wcache_invalidate_cache(void)
2100 struct winbindd_domain *domain;
2102 for (domain = domain_list(); domain; domain = domain->next) {
2103 struct winbind_cache *cache = get_cache(domain);
2105 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
2106 "entries for %s\n", domain->name));
2108 tdb_traverse(cache->tdb, traverse_fn, NULL);
2112 static BOOL init_wcache(void)
2114 if (wcache == NULL) {
2115 wcache = SMB_XMALLOC_P(struct winbind_cache);
2116 ZERO_STRUCTP(wcache);
2119 if (wcache->tdb != NULL)
2122 /* when working offline we must not clear the cache on restart */
2123 wcache->tdb = tdb_open_log(lock_path("winbindd_cache.tdb"),
2124 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
2125 lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST),
2126 O_RDWR|O_CREAT, 0600);
2128 if (wcache->tdb == NULL) {
2129 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
2136 void cache_store_response(pid_t pid, struct winbindd_response *response)
2143 DEBUG(10, ("Storing response for pid %d, len %d\n",
2144 pid, response->length));
2146 fstr_sprintf(key_str, "DR/%d", pid);
2147 if (tdb_store(wcache->tdb, string_tdb_data(key_str),
2148 make_tdb_data((const char *)response, sizeof(*response)),
2152 if (response->length == sizeof(*response))
2155 /* There's extra data */
2157 DEBUG(10, ("Storing extra data: len=%d\n",
2158 (int)(response->length - sizeof(*response))));
2160 fstr_sprintf(key_str, "DE/%d", pid);
2161 if (tdb_store(wcache->tdb, string_tdb_data(key_str),
2162 make_tdb_data((const char *)response->extra_data.data,
2163 response->length - sizeof(*response)),
2167 /* We could not store the extra data, make sure the tdb does not
2168 * contain a main record with wrong dangling extra data */
2170 fstr_sprintf(key_str, "DR/%d", pid);
2171 tdb_delete(wcache->tdb, string_tdb_data(key_str));
2176 BOOL cache_retrieve_response(pid_t pid, struct winbindd_response * response)
2184 DEBUG(10, ("Retrieving response for pid %d\n", pid));
2186 fstr_sprintf(key_str, "DR/%d", pid);
2187 data = tdb_fetch(wcache->tdb, string_tdb_data(key_str));
2189 if (data.dptr == NULL)
2192 if (data.dsize != sizeof(*response))
2195 memcpy(response, data.dptr, data.dsize);
2196 SAFE_FREE(data.dptr);
2198 if (response->length == sizeof(*response)) {
2199 response->extra_data.data = NULL;
2203 /* There's extra data */
2205 DEBUG(10, ("Retrieving extra data length=%d\n",
2206 (int)(response->length - sizeof(*response))));
2208 fstr_sprintf(key_str, "DE/%d", pid);
2209 data = tdb_fetch(wcache->tdb, string_tdb_data(key_str));
2211 if (data.dptr == NULL) {
2212 DEBUG(0, ("Did not find extra data\n"));
2216 if (data.dsize != (response->length - sizeof(*response))) {
2217 DEBUG(0, ("Invalid extra data length: %d\n", (int)data.dsize));
2218 SAFE_FREE(data.dptr);
2222 dump_data(11, data.dptr, data.dsize);
2224 response->extra_data.data = data.dptr;
2228 void cache_cleanup_response(pid_t pid)
2235 fstr_sprintf(key_str, "DR/%d", pid);
2236 tdb_delete(wcache->tdb, string_tdb_data(key_str));
2238 fstr_sprintf(key_str, "DE/%d", pid);
2239 tdb_delete(wcache->tdb, string_tdb_data(key_str));
2245 BOOL lookup_cached_sid(TALLOC_CTX *mem_ctx, const DOM_SID *sid,
2246 const char **domain_name, const char **name,
2247 enum lsa_SidType *type)
2249 struct winbindd_domain *domain;
2250 struct winbind_cache *cache;
2251 struct cache_entry *centry = NULL;
2254 domain = find_lookup_domain_from_sid(sid);
2255 if (domain == NULL) {
2259 cache = get_cache(domain);
2261 if (cache->tdb == NULL) {
2265 centry = wcache_fetch(cache, domain, "SN/%s", sid_string_static(sid));
2266 if (centry == NULL) {
2270 if (NT_STATUS_IS_OK(centry->status)) {
2271 *type = (enum lsa_SidType)centry_uint32(centry);
2272 *domain_name = centry_string(centry, mem_ctx);
2273 *name = centry_string(centry, mem_ctx);
2276 status = centry->status;
2277 centry_free(centry);
2278 return NT_STATUS_IS_OK(status);
2281 BOOL lookup_cached_name(TALLOC_CTX *mem_ctx,
2282 const char *domain_name,
2285 enum lsa_SidType *type)
2287 struct winbindd_domain *domain;
2288 struct winbind_cache *cache;
2289 struct cache_entry *centry = NULL;
2293 domain = find_lookup_domain_from_name(domain_name);
2294 if (domain == NULL) {
2298 cache = get_cache(domain);
2300 if (cache->tdb == NULL) {
2304 fstrcpy(uname, name);
2307 centry = wcache_fetch(cache, domain, "NS/%s/%s", domain_name, uname);
2308 if (centry == NULL) {
2312 if (NT_STATUS_IS_OK(centry->status)) {
2313 *type = (enum lsa_SidType)centry_uint32(centry);
2314 centry_sid(centry, mem_ctx, sid);
2317 status = centry->status;
2318 centry_free(centry);
2320 return NT_STATUS_IS_OK(status);
2323 void cache_name2sid(struct winbindd_domain *domain,
2324 const char *domain_name, const char *name,
2325 enum lsa_SidType type, const DOM_SID *sid)
2327 wcache_save_name_to_sid(domain, NT_STATUS_OK, domain_name, name,
2331 /* delete all centries that don't have NT_STATUS_OK set */
2332 static int traverse_fn_cleanup(TDB_CONTEXT *the_tdb, TDB_DATA kbuf,
2333 TDB_DATA dbuf, void *state)
2335 struct cache_entry *centry;
2337 centry = wcache_fetch_raw(kbuf.dptr);
2342 if (!NT_STATUS_IS_OK(centry->status)) {
2343 DEBUG(10,("deleting centry %s\n", kbuf.dptr));
2344 tdb_delete(the_tdb, kbuf);
2347 centry_free(centry);
2351 /* flush the cache */
2352 void wcache_flush_cache(void)
2354 extern BOOL opt_nocache;
2359 tdb_close(wcache->tdb);
2365 /* when working offline we must not clear the cache on restart */
2366 wcache->tdb = tdb_open_log(lock_path("winbindd_cache.tdb"),
2367 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
2368 lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST),
2369 O_RDWR|O_CREAT, 0600);
2372 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
2376 tdb_traverse(wcache->tdb, traverse_fn_cleanup, NULL);
2378 DEBUG(10,("wcache_flush_cache success\n"));
2381 /* Count cached creds */
2383 static int traverse_fn_cached_creds(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
2386 int *cred_count = (int*)state;
2388 if (strncmp(kbuf.dptr, "CRED/", 5) == 0) {
2394 NTSTATUS wcache_count_cached_creds(struct winbindd_domain *domain, int *count)
2396 struct winbind_cache *cache = get_cache(domain);
2401 return NT_STATUS_INTERNAL_DB_ERROR;
2404 tdb_traverse(cache->tdb, traverse_fn_cached_creds, (void *)count);
2406 return NT_STATUS_OK;
2410 struct cred_list *prev, *next;
2415 static struct cred_list *wcache_cred_list;
2417 static int traverse_fn_get_credlist(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
2420 struct cred_list *cred;
2422 if (strncmp(kbuf.dptr, "CRED/", 5) == 0) {
2424 cred = SMB_MALLOC_P(struct cred_list);
2426 DEBUG(0,("traverse_fn_remove_first_creds: failed to malloc new entry for list\n"));
2432 /* save a copy of the key */
2434 fstrcpy(cred->name, kbuf.dptr);
2435 DLIST_ADD(wcache_cred_list, cred);
2441 NTSTATUS wcache_remove_oldest_cached_creds(struct winbindd_domain *domain, const DOM_SID *sid)
2443 struct winbind_cache *cache = get_cache(domain);
2446 struct cred_list *cred, *oldest = NULL;
2449 return NT_STATUS_INTERNAL_DB_ERROR;
2452 /* we possibly already have an entry */
2453 if (sid && NT_STATUS_IS_OK(wcache_cached_creds_exist(domain, sid))) {
2457 DEBUG(11,("we already have an entry, deleting that\n"));
2459 fstr_sprintf(key_str, "CRED/%s", sid_string_static(sid));
2461 tdb_delete(cache->tdb, string_tdb_data(key_str));
2463 return NT_STATUS_OK;
2466 ret = tdb_traverse(cache->tdb, traverse_fn_get_credlist, NULL);
2468 return NT_STATUS_OK;
2469 } else if ((ret == -1) || (wcache_cred_list == NULL)) {
2470 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
2473 ZERO_STRUCTP(oldest);
2475 for (cred = wcache_cred_list; cred; cred = cred->next) {
2480 data = tdb_fetch(cache->tdb, make_tdb_data(cred->name, strlen(cred->name)));
2482 DEBUG(10,("wcache_remove_oldest_cached_creds: entry for [%s] not found\n",
2484 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
2488 t = IVAL(data.dptr, 0);
2489 SAFE_FREE(data.dptr);
2492 oldest = SMB_MALLOC_P(struct cred_list);
2493 if (oldest == NULL) {
2494 status = NT_STATUS_NO_MEMORY;
2498 fstrcpy(oldest->name, cred->name);
2499 oldest->created = t;
2503 if (t < oldest->created) {
2504 fstrcpy(oldest->name, cred->name);
2505 oldest->created = t;
2509 if (tdb_delete(cache->tdb, string_tdb_data(oldest->name)) == 0) {
2510 status = NT_STATUS_OK;
2512 status = NT_STATUS_UNSUCCESSFUL;
2515 SAFE_FREE(wcache_cred_list);
2521 /* Change the global online/offline state. */
2522 BOOL set_global_winbindd_state_offline(void)
2527 DEBUG(10,("set_global_winbindd_state_offline: offline requested.\n"));
2529 /* Only go offline if someone has created
2530 the key "WINBINDD_OFFLINE" in the cache tdb. */
2532 if (wcache == NULL || wcache->tdb == NULL) {
2533 DEBUG(10,("set_global_winbindd_state_offline: wcache not open yet.\n"));
2537 if (!lp_winbind_offline_logon()) {
2538 DEBUG(10,("set_global_winbindd_state_offline: rejecting.\n"));
2542 if (global_winbindd_offline_state) {
2543 /* Already offline. */
2547 /* wcache->tdb->ecode = 0; */
2549 data = tdb_fetch_bystring( wcache->tdb, "WINBINDD_OFFLINE" );
2551 /* As this is a key with no data we don't need to free, we
2552 check for existence by looking at tdb_err. */
2554 err = tdb_error(wcache->tdb);
2556 if (err == TDB_ERR_NOEXIST) {
2557 DEBUG(10,("set_global_winbindd_state_offline: offline state not set.\n"));
2560 DEBUG(10,("set_global_winbindd_state_offline: offline state set.\n"));
2561 global_winbindd_offline_state = True;
2566 void set_global_winbindd_state_online(void)
2568 DEBUG(10,("set_global_winbindd_state_online: online requested.\n"));
2570 if (!lp_winbind_offline_logon()) {
2571 DEBUG(10,("set_global_winbindd_state_online: rejecting.\n"));
2575 if (!global_winbindd_offline_state) {
2576 /* Already online. */
2579 global_winbindd_offline_state = False;
2585 /* Ensure there is no key "WINBINDD_OFFLINE" in the cache tdb. */
2586 tdb_delete_bystring(wcache->tdb, "WINBINDD_OFFLINE");
2589 BOOL get_global_winbindd_state_offline(void)
2591 return global_winbindd_offline_state;
2594 /* the cache backend methods are exposed via this structure */
2595 struct winbindd_methods cache_methods = {