2 Unix SMB/CIFS implementation.
4 Winbind cache backend functions
6 Copyright (C) Andrew Tridgell 2001
7 Copyright (C) Gerald Carter 2003-2007
8 Copyright (C) Volker Lendecke 2005
9 Copyright (C) Guenther Deschner 2005
10 Copyright (C) Michael Adam 2007
12 This program is free software; you can redistribute it and/or modify
13 it under the terms of the GNU General Public License as published by
14 the Free Software Foundation; either version 3 of the License, or
15 (at your option) any later version.
17 This program is distributed in the hope that it will be useful,
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 GNU General Public License for more details.
22 You should have received a copy of the GNU General Public License
23 along with this program. If not, see <http://www.gnu.org/licenses/>.
30 #define DBGC_CLASS DBGC_WINBIND
32 #define WINBINDD_CACHE_VERSION 1
33 #define WINBINDD_CACHE_VERSION_KEYSTR "WINBINDD_CACHE_VERSION"
35 extern struct winbindd_methods reconnect_methods;
36 extern BOOL opt_nocache;
38 extern struct winbindd_methods ads_methods;
42 * JRA. KEEP THIS LIST UP TO DATE IF YOU ADD CACHE ENTRIES.
43 * Here are the list of entry types that are *not* stored
44 * as form struct cache_entry in the cache.
47 static const char *non_centry_keys[] = {
52 WINBINDD_CACHE_VERSION_KEYSTR,
56 /************************************************************************
57 Is this key a non-centry type ?
58 ************************************************************************/
60 static BOOL is_non_centry_key(TDB_DATA kbuf)
64 if (kbuf.dptr == NULL || kbuf.dsize == 0) {
67 for (i = 0; non_centry_keys[i] != NULL; i++) {
68 size_t namelen = strlen(non_centry_keys[i]);
69 if (kbuf.dsize < namelen) {
72 if (strncmp(non_centry_keys[i], (const char *)kbuf.dptr, namelen) == 0) {
79 /* Global online/offline state - False when online. winbindd starts up online
80 and sets this to true if the first query fails and there's an entry in
81 the cache tdb telling us to stay offline. */
83 static BOOL global_winbindd_offline_state;
85 struct winbind_cache {
91 uint32 sequence_number;
96 void (*smb_panic_fn)(const char *const why) = smb_panic;
98 #define WINBINDD_MAX_CACHE_SIZE (50*1024*1024)
100 static struct winbind_cache *wcache;
102 void winbindd_check_cache_size(time_t t)
104 static time_t last_check_time;
107 if (last_check_time == (time_t)0)
110 if (t - last_check_time < 60 && t - last_check_time > 0)
113 if (wcache == NULL || wcache->tdb == NULL) {
114 DEBUG(0, ("Unable to check size of tdb cache - cache not open !\n"));
118 if (fstat(tdb_fd(wcache->tdb), &st) == -1) {
119 DEBUG(0, ("Unable to check size of tdb cache %s!\n", strerror(errno) ));
123 if (st.st_size > WINBINDD_MAX_CACHE_SIZE) {
124 DEBUG(10,("flushing cache due to size (%lu) > (%lu)\n",
125 (unsigned long)st.st_size,
126 (unsigned long)WINBINDD_MAX_CACHE_SIZE));
127 wcache_flush_cache();
131 /* get the winbind_cache structure */
132 static struct winbind_cache *get_cache(struct winbindd_domain *domain)
134 struct winbind_cache *ret = wcache;
136 /* We have to know what type of domain we are dealing with first. */
138 if ( !domain->initialized ) {
139 init_dc_connection( domain );
143 OK. listen up becasue I'm only going to say this once.
144 We have the following scenarios to consider
145 (a) trusted AD domains on a Samba DC,
146 (b) trusted AD domains and we are joined to a non-kerberos domain
147 (c) trusted AD domains and we are joined to a kerberos (AD) domain
149 For (a) we can always contact the trusted domain using krb5
150 since we have the domain trust account password
152 For (b) we can only use RPC since we have no way of
153 getting a krb5 ticket in our own domain
155 For (c) we can always use krb5 since we have a kerberos trust
160 if (!domain->backend) {
162 struct winbindd_domain *our_domain = domain;
164 /* find our domain first so we can figure out if we
165 are joined to a kerberized domain */
167 if ( !domain->primary )
168 our_domain = find_our_domain();
170 if ((our_domain->active_directory || IS_DC)
171 && domain->active_directory
172 && !lp_winbind_rpc_only()) {
173 DEBUG(5,("get_cache: Setting ADS methods for domain %s\n", domain->name));
174 domain->backend = &ads_methods;
176 #endif /* HAVE_ADS */
177 DEBUG(5,("get_cache: Setting MS-RPC methods for domain %s\n", domain->name));
178 domain->backend = &reconnect_methods;
181 #endif /* HAVE_ADS */
187 ret = SMB_XMALLOC_P(struct winbind_cache);
191 wcache_flush_cache();
197 free a centry structure
199 static void centry_free(struct cache_entry *centry)
203 SAFE_FREE(centry->data);
207 static BOOL centry_check_bytes(struct cache_entry *centry, size_t nbytes)
209 if (centry->len - centry->ofs < nbytes) {
210 DEBUG(0,("centry corruption? needed %u bytes, have %d\n",
211 (unsigned int)nbytes,
212 centry->len - centry->ofs));
219 pull a uint32 from a cache entry
221 static uint32 centry_uint32(struct cache_entry *centry)
225 if (!centry_check_bytes(centry, 4)) {
226 smb_panic_fn("centry_uint32");
228 ret = IVAL(centry->data, centry->ofs);
234 pull a uint16 from a cache entry
236 static uint16 centry_uint16(struct cache_entry *centry)
239 if (!centry_check_bytes(centry, 2)) {
240 smb_panic_fn("centry_uint16");
242 ret = CVAL(centry->data, centry->ofs);
248 pull a uint8 from a cache entry
250 static uint8 centry_uint8(struct cache_entry *centry)
253 if (!centry_check_bytes(centry, 1)) {
254 smb_panic_fn("centry_uint8");
256 ret = CVAL(centry->data, centry->ofs);
262 pull a NTTIME from a cache entry
264 static NTTIME centry_nttime(struct cache_entry *centry)
267 if (!centry_check_bytes(centry, 8)) {
268 smb_panic_fn("centry_nttime");
270 ret = IVAL(centry->data, centry->ofs);
272 ret += (uint64_t)IVAL(centry->data, centry->ofs) << 32;
278 pull a time_t from a cache entry. time_t stored portably as a 64-bit time.
280 static time_t centry_time(struct cache_entry *centry)
282 return (time_t)centry_nttime(centry);
285 /* pull a string from a cache entry, using the supplied
288 static char *centry_string(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
293 len = centry_uint8(centry);
296 /* a deliberate NULL string */
300 if (!centry_check_bytes(centry, (size_t)len)) {
301 smb_panic_fn("centry_string");
304 ret = TALLOC_ARRAY(mem_ctx, char, len+1);
306 smb_panic_fn("centry_string out of memory\n");
308 memcpy(ret,centry->data + centry->ofs, len);
314 /* pull a hash16 from a cache entry, using the supplied
317 static char *centry_hash16(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
322 len = centry_uint8(centry);
325 DEBUG(0,("centry corruption? hash len (%u) != 16\n",
330 if (!centry_check_bytes(centry, 16)) {
334 ret = TALLOC_ARRAY(mem_ctx, char, 16);
336 smb_panic_fn("centry_hash out of memory\n");
338 memcpy(ret,centry->data + centry->ofs, 16);
343 /* pull a sid from a cache entry, using the supplied
346 static BOOL centry_sid(struct cache_entry *centry, TALLOC_CTX *mem_ctx, DOM_SID *sid)
349 sid_string = centry_string(centry, mem_ctx);
350 if ((sid_string == NULL) || (!string_to_sid(sid, sid_string))) {
356 /* the server is considered down if it can't give us a sequence number */
357 static BOOL wcache_server_down(struct winbindd_domain *domain)
364 ret = (domain->sequence_number == DOM_SEQUENCE_NONE);
367 DEBUG(10,("wcache_server_down: server for Domain %s down\n",
372 static NTSTATUS fetch_cache_seqnum( struct winbindd_domain *domain, time_t now )
379 DEBUG(10,("fetch_cache_seqnum: tdb == NULL\n"));
380 return NT_STATUS_UNSUCCESSFUL;
383 fstr_sprintf( key, "SEQNUM/%s", domain->name );
385 data = tdb_fetch_bystring( wcache->tdb, key );
386 if ( !data.dptr || data.dsize!=8 ) {
387 DEBUG(10,("fetch_cache_seqnum: invalid data size key [%s]\n", key ));
388 return NT_STATUS_UNSUCCESSFUL;
391 domain->sequence_number = IVAL(data.dptr, 0);
392 domain->last_seq_check = IVAL(data.dptr, 4);
394 SAFE_FREE(data.dptr);
396 /* have we expired? */
398 time_diff = now - domain->last_seq_check;
399 if ( time_diff > lp_winbind_cache_time() ) {
400 DEBUG(10,("fetch_cache_seqnum: timeout [%s][%u @ %u]\n",
401 domain->name, domain->sequence_number,
402 (uint32)domain->last_seq_check));
403 return NT_STATUS_UNSUCCESSFUL;
406 DEBUG(10,("fetch_cache_seqnum: success [%s][%u @ %u]\n",
407 domain->name, domain->sequence_number,
408 (uint32)domain->last_seq_check));
413 static NTSTATUS store_cache_seqnum( struct winbindd_domain *domain )
420 DEBUG(10,("store_cache_seqnum: tdb == NULL\n"));
421 return NT_STATUS_UNSUCCESSFUL;
424 fstr_sprintf( key_str, "SEQNUM/%s", domain->name );
426 SIVAL(buf, 0, domain->sequence_number);
427 SIVAL(buf, 4, domain->last_seq_check);
431 if ( tdb_store_bystring( wcache->tdb, key_str, data, TDB_REPLACE) == -1 ) {
432 DEBUG(10,("store_cache_seqnum: tdb_store fail key [%s]\n", key_str ));
433 return NT_STATUS_UNSUCCESSFUL;
436 DEBUG(10,("store_cache_seqnum: success [%s][%u @ %u]\n",
437 domain->name, domain->sequence_number,
438 (uint32)domain->last_seq_check));
444 refresh the domain sequence number. If force is True
445 then always refresh it, no matter how recently we fetched it
448 static void refresh_sequence_number(struct winbindd_domain *domain, BOOL force)
452 time_t t = time(NULL);
453 unsigned cache_time = lp_winbind_cache_time();
455 if ( IS_DOMAIN_OFFLINE(domain) ) {
461 #if 0 /* JERRY -- disable as the default cache time is now 5 minutes */
462 /* trying to reconnect is expensive, don't do it too often */
463 if (domain->sequence_number == DOM_SEQUENCE_NONE) {
468 time_diff = t - domain->last_seq_check;
470 /* see if we have to refetch the domain sequence number */
471 if (!force && (time_diff < cache_time)) {
472 DEBUG(10, ("refresh_sequence_number: %s time ok\n", domain->name));
476 /* try to get the sequence number from the tdb cache first */
477 /* this will update the timestamp as well */
479 status = fetch_cache_seqnum( domain, t );
480 if ( NT_STATUS_IS_OK(status) )
483 /* important! make sure that we know if this is a native
484 mode domain or not. And that we can contact it. */
486 if ( winbindd_can_contact_domain( domain ) ) {
487 status = domain->backend->sequence_number(domain,
488 &domain->sequence_number);
490 /* just use the current time */
491 status = NT_STATUS_OK;
492 domain->sequence_number = time(NULL);
496 /* the above call could have set our domain->backend to NULL when
497 * coming from offline to online mode, make sure to reinitialize the
498 * backend - Guenther */
501 if (!NT_STATUS_IS_OK(status)) {
502 DEBUG(10,("refresh_sequence_number: failed with %s\n", nt_errstr(status)));
503 domain->sequence_number = DOM_SEQUENCE_NONE;
506 domain->last_status = status;
507 domain->last_seq_check = time(NULL);
509 /* save the new sequence number ni the cache */
510 store_cache_seqnum( domain );
513 DEBUG(10, ("refresh_sequence_number: %s seq number is now %d\n",
514 domain->name, domain->sequence_number));
520 decide if a cache entry has expired
522 static BOOL centry_expired(struct winbindd_domain *domain, const char *keystr, struct cache_entry *centry)
524 /* If we've been told to be offline - stay in that state... */
525 if (lp_winbind_offline_logon() && global_winbindd_offline_state) {
526 DEBUG(10,("centry_expired: Key %s for domain %s valid as winbindd is globally offline.\n",
527 keystr, domain->name ));
531 /* when the domain is offline return the cached entry.
532 * This deals with transient offline states... */
534 if (!domain->online) {
535 DEBUG(10,("centry_expired: Key %s for domain %s valid as domain is offline.\n",
536 keystr, domain->name ));
540 /* if the server is OK and our cache entry came from when it was down then
541 the entry is invalid */
542 if ((domain->sequence_number != DOM_SEQUENCE_NONE) &&
543 (centry->sequence_number == DOM_SEQUENCE_NONE)) {
544 DEBUG(10,("centry_expired: Key %s for domain %s invalid sequence.\n",
545 keystr, domain->name ));
549 /* if the server is down or the cache entry is not older than the
550 current sequence number then it is OK */
551 if (wcache_server_down(domain) ||
552 centry->sequence_number == domain->sequence_number) {
553 DEBUG(10,("centry_expired: Key %s for domain %s is good.\n",
554 keystr, domain->name ));
558 DEBUG(10,("centry_expired: Key %s for domain %s expired\n",
559 keystr, domain->name ));
565 static struct cache_entry *wcache_fetch_raw(char *kstr)
568 struct cache_entry *centry;
571 key = string_tdb_data(kstr);
572 data = tdb_fetch(wcache->tdb, key);
578 centry = SMB_XMALLOC_P(struct cache_entry);
579 centry->data = (unsigned char *)data.dptr;
580 centry->len = data.dsize;
583 if (centry->len < 8) {
584 /* huh? corrupt cache? */
585 DEBUG(10,("wcache_fetch_raw: Corrupt cache for key %s (len < 8) ?\n", kstr));
590 centry->status = NT_STATUS(centry_uint32(centry));
591 centry->sequence_number = centry_uint32(centry);
597 fetch an entry from the cache, with a varargs key. auto-fetch the sequence
598 number and return status
600 static struct cache_entry *wcache_fetch(struct winbind_cache *cache,
601 struct winbindd_domain *domain,
602 const char *format, ...) PRINTF_ATTRIBUTE(3,4);
603 static struct cache_entry *wcache_fetch(struct winbind_cache *cache,
604 struct winbindd_domain *domain,
605 const char *format, ...)
609 struct cache_entry *centry;
615 refresh_sequence_number(domain, False);
617 va_start(ap, format);
618 smb_xvasprintf(&kstr, format, ap);
621 centry = wcache_fetch_raw(kstr);
622 if (centry == NULL) {
627 if (centry_expired(domain, kstr, centry)) {
629 DEBUG(10,("wcache_fetch: entry %s expired for domain %s\n",
630 kstr, domain->name ));
637 DEBUG(10,("wcache_fetch: returning entry %s for domain %s\n",
638 kstr, domain->name ));
644 static void wcache_delete(const char *format, ...) PRINTF_ATTRIBUTE(1,2);
645 static void wcache_delete(const char *format, ...)
651 va_start(ap, format);
652 smb_xvasprintf(&kstr, format, ap);
655 key = string_tdb_data(kstr);
657 tdb_delete(wcache->tdb, key);
662 make sure we have at least len bytes available in a centry
664 static void centry_expand(struct cache_entry *centry, uint32 len)
666 if (centry->len - centry->ofs >= len)
669 centry->data = SMB_REALLOC_ARRAY(centry->data, unsigned char,
672 DEBUG(0,("out of memory: needed %d bytes in centry_expand\n", centry->len));
673 smb_panic_fn("out of memory in centry_expand");
678 push a uint32 into a centry
680 static void centry_put_uint32(struct cache_entry *centry, uint32 v)
682 centry_expand(centry, 4);
683 SIVAL(centry->data, centry->ofs, v);
688 push a uint16 into a centry
690 static void centry_put_uint16(struct cache_entry *centry, uint16 v)
692 centry_expand(centry, 2);
693 SIVAL(centry->data, centry->ofs, v);
698 push a uint8 into a centry
700 static void centry_put_uint8(struct cache_entry *centry, uint8 v)
702 centry_expand(centry, 1);
703 SCVAL(centry->data, centry->ofs, v);
708 push a string into a centry
710 static void centry_put_string(struct cache_entry *centry, const char *s)
715 /* null strings are marked as len 0xFFFF */
716 centry_put_uint8(centry, 0xFF);
721 /* can't handle more than 254 char strings. Truncating is probably best */
723 DEBUG(10,("centry_put_string: truncating len (%d) to: 254\n", len));
726 centry_put_uint8(centry, len);
727 centry_expand(centry, len);
728 memcpy(centry->data + centry->ofs, s, len);
733 push a 16 byte hash into a centry - treat as 16 byte string.
735 static void centry_put_hash16(struct cache_entry *centry, const uint8 val[16])
737 centry_put_uint8(centry, 16);
738 centry_expand(centry, 16);
739 memcpy(centry->data + centry->ofs, val, 16);
743 static void centry_put_sid(struct cache_entry *centry, const DOM_SID *sid)
746 centry_put_string(centry, sid_to_string(sid_string, sid));
750 push a NTTIME into a centry
752 static void centry_put_nttime(struct cache_entry *centry, NTTIME nt)
754 centry_expand(centry, 8);
755 SIVAL(centry->data, centry->ofs, nt & 0xFFFFFFFF);
757 SIVAL(centry->data, centry->ofs, nt >> 32);
762 push a time_t into a centry - use a 64 bit size.
763 NTTIME here is being used as a convenient 64-bit size.
765 static void centry_put_time(struct cache_entry *centry, time_t t)
767 NTTIME nt = (NTTIME)t;
768 centry_put_nttime(centry, nt);
772 start a centry for output. When finished, call centry_end()
774 struct cache_entry *centry_start(struct winbindd_domain *domain, NTSTATUS status)
776 struct cache_entry *centry;
781 centry = SMB_XMALLOC_P(struct cache_entry);
783 centry->len = 8192; /* reasonable default */
784 centry->data = SMB_XMALLOC_ARRAY(uint8, centry->len);
786 centry->sequence_number = domain->sequence_number;
787 centry_put_uint32(centry, NT_STATUS_V(status));
788 centry_put_uint32(centry, centry->sequence_number);
793 finish a centry and write it to the tdb
795 static void centry_end(struct cache_entry *centry, const char *format, ...) PRINTF_ATTRIBUTE(2,3);
796 static void centry_end(struct cache_entry *centry, const char *format, ...)
802 va_start(ap, format);
803 smb_xvasprintf(&kstr, format, ap);
806 key = string_tdb_data(kstr);
807 data.dptr = centry->data;
808 data.dsize = centry->ofs;
810 tdb_store(wcache->tdb, key, data, TDB_REPLACE);
814 static void wcache_save_name_to_sid(struct winbindd_domain *domain,
815 NTSTATUS status, const char *domain_name,
816 const char *name, const DOM_SID *sid,
817 enum lsa_SidType type)
819 struct cache_entry *centry;
822 centry = centry_start(domain, status);
825 centry_put_uint32(centry, type);
826 centry_put_sid(centry, sid);
827 fstrcpy(uname, name);
829 centry_end(centry, "NS/%s/%s", domain_name, uname);
830 DEBUG(10,("wcache_save_name_to_sid: %s\\%s -> %s (%s)\n", domain_name, uname,
831 sid_string_static(sid), nt_errstr(status)));
835 static void wcache_save_sid_to_name(struct winbindd_domain *domain, NTSTATUS status,
836 const DOM_SID *sid, const char *domain_name, const char *name, enum lsa_SidType type)
838 struct cache_entry *centry;
841 if (is_null_sid(sid)) {
845 centry = centry_start(domain, status);
848 if (NT_STATUS_IS_OK(status)) {
849 centry_put_uint32(centry, type);
850 centry_put_string(centry, domain_name);
851 centry_put_string(centry, name);
853 centry_end(centry, "SN/%s", sid_to_string(sid_string, sid));
854 DEBUG(10,("wcache_save_sid_to_name: %s -> %s (%s)\n", sid_string,
855 name, nt_errstr(status)));
860 static void wcache_save_user(struct winbindd_domain *domain, NTSTATUS status, WINBIND_USERINFO *info)
862 struct cache_entry *centry;
865 if (is_null_sid(&info->user_sid)) {
869 centry = centry_start(domain, status);
872 centry_put_string(centry, info->acct_name);
873 centry_put_string(centry, info->full_name);
874 centry_put_string(centry, info->homedir);
875 centry_put_string(centry, info->shell);
876 centry_put_uint32(centry, info->primary_gid);
877 centry_put_sid(centry, &info->user_sid);
878 centry_put_sid(centry, &info->group_sid);
879 centry_end(centry, "U/%s", sid_to_string(sid_string, &info->user_sid));
880 DEBUG(10,("wcache_save_user: %s (acct_name %s)\n", sid_string, info->acct_name));
884 static void wcache_save_lockout_policy(struct winbindd_domain *domain, NTSTATUS status, SAM_UNK_INFO_12 *lockout_policy)
886 struct cache_entry *centry;
888 centry = centry_start(domain, status);
892 centry_put_nttime(centry, lockout_policy->duration);
893 centry_put_nttime(centry, lockout_policy->reset_count);
894 centry_put_uint16(centry, lockout_policy->bad_attempt_lockout);
896 centry_end(centry, "LOC_POL/%s", domain->name);
898 DEBUG(10,("wcache_save_lockout_policy: %s\n", domain->name));
903 static void wcache_save_password_policy(struct winbindd_domain *domain, NTSTATUS status, SAM_UNK_INFO_1 *policy)
905 struct cache_entry *centry;
907 centry = centry_start(domain, status);
911 centry_put_uint16(centry, policy->min_length_password);
912 centry_put_uint16(centry, policy->password_history);
913 centry_put_uint32(centry, policy->password_properties);
914 centry_put_nttime(centry, policy->expire);
915 centry_put_nttime(centry, policy->min_passwordage);
917 centry_end(centry, "PWD_POL/%s", domain->name);
919 DEBUG(10,("wcache_save_password_policy: %s\n", domain->name));
924 NTSTATUS wcache_cached_creds_exist(struct winbindd_domain *domain, const DOM_SID *sid)
926 struct winbind_cache *cache = get_cache(domain);
932 return NT_STATUS_INTERNAL_DB_ERROR;
935 if (is_null_sid(sid)) {
936 return NT_STATUS_INVALID_SID;
939 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
940 return NT_STATUS_INVALID_SID;
943 fstr_sprintf(key_str, "CRED/%s", sid_string_static(sid));
945 data = tdb_fetch(cache->tdb, string_tdb_data(key_str));
947 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
950 SAFE_FREE(data.dptr);
954 /* Lookup creds for a SID - copes with old (unsalted) creds as well
955 as new salted ones. */
957 NTSTATUS wcache_get_creds(struct winbindd_domain *domain,
960 const uint8 **cached_nt_pass,
961 const uint8 **cached_salt)
963 struct winbind_cache *cache = get_cache(domain);
964 struct cache_entry *centry = NULL;
970 return NT_STATUS_INTERNAL_DB_ERROR;
973 if (is_null_sid(sid)) {
974 return NT_STATUS_INVALID_SID;
977 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
978 return NT_STATUS_INVALID_SID;
981 /* Try and get a salted cred first. If we can't
982 fall back to an unsalted cred. */
984 centry = wcache_fetch(cache, domain, "CRED/%s", sid_string_static(sid));
986 DEBUG(10,("wcache_get_creds: entry for [CRED/%s] not found\n",
987 sid_string_static(sid)));
988 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
991 t = centry_time(centry);
993 /* In the salted case this isn't actually the nt_hash itself,
994 but the MD5 of the salt + nt_hash. Let the caller
995 sort this out. It can tell as we only return the cached_salt
996 if we are returning a salted cred. */
998 *cached_nt_pass = (const uint8 *)centry_hash16(centry, mem_ctx);
999 if (*cached_nt_pass == NULL) {
1000 const char *sidstr = sid_string_static(sid);
1002 /* Bad (old) cred cache. Delete and pretend we
1004 DEBUG(0,("wcache_get_creds: bad entry for [CRED/%s] - deleting\n",
1006 wcache_delete("CRED/%s", sidstr);
1007 centry_free(centry);
1008 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1011 /* We only have 17 bytes more data in the salted cred case. */
1012 if (centry->len - centry->ofs == 17) {
1013 *cached_salt = (const uint8 *)centry_hash16(centry, mem_ctx);
1015 *cached_salt = NULL;
1018 dump_data_pw("cached_nt_pass", *cached_nt_pass, NT_HASH_LEN);
1020 dump_data_pw("cached_salt", *cached_salt, NT_HASH_LEN);
1023 status = centry->status;
1025 DEBUG(10,("wcache_get_creds: [Cached] - cached creds for user %s status: %s\n",
1026 sid_string_static(sid), nt_errstr(status) ));
1028 centry_free(centry);
1032 /* Store creds for a SID - only writes out new salted ones. */
1034 NTSTATUS wcache_save_creds(struct winbindd_domain *domain,
1035 TALLOC_CTX *mem_ctx,
1037 const uint8 nt_pass[NT_HASH_LEN])
1039 struct cache_entry *centry;
1042 uint8 cred_salt[NT_HASH_LEN];
1043 uint8 salted_hash[NT_HASH_LEN];
1045 if (is_null_sid(sid)) {
1046 return NT_STATUS_INVALID_SID;
1049 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1050 return NT_STATUS_INVALID_SID;
1053 centry = centry_start(domain, NT_STATUS_OK);
1055 return NT_STATUS_INTERNAL_DB_ERROR;
1058 dump_data_pw("nt_pass", nt_pass, NT_HASH_LEN);
1060 centry_put_time(centry, time(NULL));
1062 /* Create a salt and then salt the hash. */
1063 generate_random_buffer(cred_salt, NT_HASH_LEN);
1064 E_md5hash(cred_salt, nt_pass, salted_hash);
1066 centry_put_hash16(centry, salted_hash);
1067 centry_put_hash16(centry, cred_salt);
1068 centry_end(centry, "CRED/%s", sid_to_string(sid_string, sid));
1070 DEBUG(10,("wcache_save_creds: %s\n", sid_string));
1072 centry_free(centry);
1074 return NT_STATUS_OK;
1078 /* Query display info. This is the basic user list fn */
1079 static NTSTATUS query_user_list(struct winbindd_domain *domain,
1080 TALLOC_CTX *mem_ctx,
1081 uint32 *num_entries,
1082 WINBIND_USERINFO **info)
1084 struct winbind_cache *cache = get_cache(domain);
1085 struct cache_entry *centry = NULL;
1087 unsigned int i, retry;
1092 centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
1096 *num_entries = centry_uint32(centry);
1098 if (*num_entries == 0)
1101 (*info) = TALLOC_ARRAY(mem_ctx, WINBIND_USERINFO, *num_entries);
1103 smb_panic_fn("query_user_list out of memory");
1105 for (i=0; i<(*num_entries); i++) {
1106 (*info)[i].acct_name = centry_string(centry, mem_ctx);
1107 (*info)[i].full_name = centry_string(centry, mem_ctx);
1108 (*info)[i].homedir = centry_string(centry, mem_ctx);
1109 (*info)[i].shell = centry_string(centry, mem_ctx);
1110 centry_sid(centry, mem_ctx, &(*info)[i].user_sid);
1111 centry_sid(centry, mem_ctx, &(*info)[i].group_sid);
1115 status = centry->status;
1117 DEBUG(10,("query_user_list: [Cached] - cached list for domain %s status: %s\n",
1118 domain->name, nt_errstr(status) ));
1120 centry_free(centry);
1127 /* Return status value returned by seq number check */
1129 if (!NT_STATUS_IS_OK(domain->last_status))
1130 return domain->last_status;
1132 /* Put the query_user_list() in a retry loop. There appears to be
1133 * some bug either with Windows 2000 or Samba's handling of large
1134 * rpc replies. This manifests itself as sudden disconnection
1135 * at a random point in the enumeration of a large (60k) user list.
1136 * The retry loop simply tries the operation again. )-: It's not
1137 * pretty but an acceptable workaround until we work out what the
1138 * real problem is. */
1143 DEBUG(10,("query_user_list: [Cached] - doing backend query for list for domain %s\n",
1146 status = domain->backend->query_user_list(domain, mem_ctx, num_entries, info);
1147 if (!NT_STATUS_IS_OK(status)) {
1148 DEBUG(3, ("query_user_list: returned 0x%08x, "
1149 "retrying\n", NT_STATUS_V(status)));
1151 if (NT_STATUS_EQUAL(status, NT_STATUS_UNSUCCESSFUL)) {
1152 DEBUG(3, ("query_user_list: flushing "
1153 "connection cache\n"));
1154 invalidate_cm_connection(&domain->conn);
1157 } while (NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_UNSUCCESSFUL) &&
1161 refresh_sequence_number(domain, False);
1162 centry = centry_start(domain, status);
1165 centry_put_uint32(centry, *num_entries);
1166 for (i=0; i<(*num_entries); i++) {
1167 centry_put_string(centry, (*info)[i].acct_name);
1168 centry_put_string(centry, (*info)[i].full_name);
1169 centry_put_string(centry, (*info)[i].homedir);
1170 centry_put_string(centry, (*info)[i].shell);
1171 centry_put_sid(centry, &(*info)[i].user_sid);
1172 centry_put_sid(centry, &(*info)[i].group_sid);
1173 if (domain->backend && domain->backend->consistent) {
1174 /* when the backend is consistent we can pre-prime some mappings */
1175 wcache_save_name_to_sid(domain, NT_STATUS_OK,
1177 (*info)[i].acct_name,
1178 &(*info)[i].user_sid,
1180 wcache_save_sid_to_name(domain, NT_STATUS_OK,
1181 &(*info)[i].user_sid,
1183 (*info)[i].acct_name,
1185 wcache_save_user(domain, NT_STATUS_OK, &(*info)[i]);
1188 centry_end(centry, "UL/%s", domain->name);
1189 centry_free(centry);
1195 /* list all domain groups */
1196 static NTSTATUS enum_dom_groups(struct winbindd_domain *domain,
1197 TALLOC_CTX *mem_ctx,
1198 uint32 *num_entries,
1199 struct acct_info **info)
1201 struct winbind_cache *cache = get_cache(domain);
1202 struct cache_entry *centry = NULL;
1209 centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
1213 *num_entries = centry_uint32(centry);
1215 if (*num_entries == 0)
1218 (*info) = TALLOC_ARRAY(mem_ctx, struct acct_info, *num_entries);
1220 smb_panic_fn("enum_dom_groups out of memory");
1222 for (i=0; i<(*num_entries); i++) {
1223 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1224 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1225 (*info)[i].rid = centry_uint32(centry);
1229 status = centry->status;
1231 DEBUG(10,("enum_dom_groups: [Cached] - cached list for domain %s status: %s\n",
1232 domain->name, nt_errstr(status) ));
1234 centry_free(centry);
1241 /* Return status value returned by seq number check */
1243 if (!NT_STATUS_IS_OK(domain->last_status))
1244 return domain->last_status;
1246 DEBUG(10,("enum_dom_groups: [Cached] - doing backend query for list for domain %s\n",
1249 status = domain->backend->enum_dom_groups(domain, mem_ctx, num_entries, info);
1252 refresh_sequence_number(domain, False);
1253 centry = centry_start(domain, status);
1256 centry_put_uint32(centry, *num_entries);
1257 for (i=0; i<(*num_entries); i++) {
1258 centry_put_string(centry, (*info)[i].acct_name);
1259 centry_put_string(centry, (*info)[i].acct_desc);
1260 centry_put_uint32(centry, (*info)[i].rid);
1262 centry_end(centry, "GL/%s/domain", domain->name);
1263 centry_free(centry);
1269 /* list all domain groups */
1270 static NTSTATUS enum_local_groups(struct winbindd_domain *domain,
1271 TALLOC_CTX *mem_ctx,
1272 uint32 *num_entries,
1273 struct acct_info **info)
1275 struct winbind_cache *cache = get_cache(domain);
1276 struct cache_entry *centry = NULL;
1283 centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
1287 *num_entries = centry_uint32(centry);
1289 if (*num_entries == 0)
1292 (*info) = TALLOC_ARRAY(mem_ctx, struct acct_info, *num_entries);
1294 smb_panic_fn("enum_dom_groups out of memory");
1296 for (i=0; i<(*num_entries); i++) {
1297 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1298 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1299 (*info)[i].rid = centry_uint32(centry);
1304 /* If we are returning cached data and the domain controller
1305 is down then we don't know whether the data is up to date
1306 or not. Return NT_STATUS_MORE_PROCESSING_REQUIRED to
1309 if (wcache_server_down(domain)) {
1310 DEBUG(10, ("enum_local_groups: returning cached user list and server was down\n"));
1311 status = NT_STATUS_MORE_PROCESSING_REQUIRED;
1313 status = centry->status;
1315 DEBUG(10,("enum_local_groups: [Cached] - cached list for domain %s status: %s\n",
1316 domain->name, nt_errstr(status) ));
1318 centry_free(centry);
1325 /* Return status value returned by seq number check */
1327 if (!NT_STATUS_IS_OK(domain->last_status))
1328 return domain->last_status;
1330 DEBUG(10,("enum_local_groups: [Cached] - doing backend query for list for domain %s\n",
1333 status = domain->backend->enum_local_groups(domain, mem_ctx, num_entries, info);
1336 refresh_sequence_number(domain, False);
1337 centry = centry_start(domain, status);
1340 centry_put_uint32(centry, *num_entries);
1341 for (i=0; i<(*num_entries); i++) {
1342 centry_put_string(centry, (*info)[i].acct_name);
1343 centry_put_string(centry, (*info)[i].acct_desc);
1344 centry_put_uint32(centry, (*info)[i].rid);
1346 centry_end(centry, "GL/%s/local", domain->name);
1347 centry_free(centry);
1353 /* convert a single name to a sid in a domain */
1354 static NTSTATUS name_to_sid(struct winbindd_domain *domain,
1355 TALLOC_CTX *mem_ctx,
1356 enum winbindd_cmd orig_cmd,
1357 const char *domain_name,
1360 enum lsa_SidType *type)
1362 struct winbind_cache *cache = get_cache(domain);
1363 struct cache_entry *centry = NULL;
1370 fstrcpy(uname, name);
1372 centry = wcache_fetch(cache, domain, "NS/%s/%s", domain_name, uname);
1375 *type = (enum lsa_SidType)centry_uint32(centry);
1376 status = centry->status;
1377 if (NT_STATUS_IS_OK(status)) {
1378 centry_sid(centry, mem_ctx, sid);
1381 DEBUG(10,("name_to_sid: [Cached] - cached name for domain %s status: %s\n",
1382 domain->name, nt_errstr(status) ));
1384 centry_free(centry);
1390 /* If the seq number check indicated that there is a problem
1391 * with this DC, then return that status... except for
1392 * access_denied. This is special because the dc may be in
1393 * "restrict anonymous = 1" mode, in which case it will deny
1394 * most unauthenticated operations, but *will* allow the LSA
1395 * name-to-sid that we try as a fallback. */
1397 if (!(NT_STATUS_IS_OK(domain->last_status)
1398 || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1399 return domain->last_status;
1401 DEBUG(10,("name_to_sid: [Cached] - doing backend query for name for domain %s\n",
1404 status = domain->backend->name_to_sid(domain, mem_ctx, orig_cmd,
1405 domain_name, name, sid, type);
1408 refresh_sequence_number(domain, False);
1410 if (domain->online && !is_null_sid(sid)) {
1411 wcache_save_name_to_sid(domain, status, domain_name, name, sid, *type);
1414 /* Only save the reverse mapping if this was not a UPN */
1415 if (NT_STATUS_IS_OK(status) && !strchr(name, '@')) {
1416 strupper_m(CONST_DISCARD(char *,domain_name));
1417 strlower_m(CONST_DISCARD(char *,name));
1418 wcache_save_sid_to_name(domain, status, sid, domain_name, name, *type);
1424 /* convert a sid to a user or group name. The sid is guaranteed to be in the domain
1426 static NTSTATUS sid_to_name(struct winbindd_domain *domain,
1427 TALLOC_CTX *mem_ctx,
1431 enum lsa_SidType *type)
1433 struct winbind_cache *cache = get_cache(domain);
1434 struct cache_entry *centry = NULL;
1441 centry = wcache_fetch(cache, domain, "SN/%s", sid_to_string(sid_string, sid));
1444 if (NT_STATUS_IS_OK(centry->status)) {
1445 *type = (enum lsa_SidType)centry_uint32(centry);
1446 *domain_name = centry_string(centry, mem_ctx);
1447 *name = centry_string(centry, mem_ctx);
1449 status = centry->status;
1451 DEBUG(10,("sid_to_name: [Cached] - cached name for domain %s status: %s\n",
1452 domain->name, nt_errstr(status) ));
1454 centry_free(centry);
1459 *domain_name = NULL;
1461 /* If the seq number check indicated that there is a problem
1462 * with this DC, then return that status... except for
1463 * access_denied. This is special because the dc may be in
1464 * "restrict anonymous = 1" mode, in which case it will deny
1465 * most unauthenticated operations, but *will* allow the LSA
1466 * sid-to-name that we try as a fallback. */
1468 if (!(NT_STATUS_IS_OK(domain->last_status)
1469 || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1470 return domain->last_status;
1472 DEBUG(10,("sid_to_name: [Cached] - doing backend query for name for domain %s\n",
1475 status = domain->backend->sid_to_name(domain, mem_ctx, sid, domain_name, name, type);
1478 refresh_sequence_number(domain, False);
1479 wcache_save_sid_to_name(domain, status, sid, *domain_name, *name, *type);
1481 /* We can't save the name to sid mapping here, as with sid history a
1482 * later name2sid would give the wrong sid. */
1487 static NTSTATUS rids_to_names(struct winbindd_domain *domain,
1488 TALLOC_CTX *mem_ctx,
1489 const DOM_SID *domain_sid,
1494 enum lsa_SidType **types)
1496 struct winbind_cache *cache = get_cache(domain);
1498 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
1502 *domain_name = NULL;
1510 if (num_rids == 0) {
1511 return NT_STATUS_OK;
1514 *names = TALLOC_ARRAY(mem_ctx, char *, num_rids);
1515 *types = TALLOC_ARRAY(mem_ctx, enum lsa_SidType, num_rids);
1517 if ((*names == NULL) || (*types == NULL)) {
1518 result = NT_STATUS_NO_MEMORY;
1522 have_mapped = have_unmapped = False;
1524 for (i=0; i<num_rids; i++) {
1526 struct cache_entry *centry;
1528 if (!sid_compose(&sid, domain_sid, rids[i])) {
1529 result = NT_STATUS_INTERNAL_ERROR;
1533 centry = wcache_fetch(cache, domain, "SN/%s",
1534 sid_string_static(&sid));
1539 (*types)[i] = SID_NAME_UNKNOWN;
1540 (*names)[i] = talloc_strdup(*names, "");
1542 if (NT_STATUS_IS_OK(centry->status)) {
1545 (*types)[i] = (enum lsa_SidType)centry_uint32(centry);
1546 dom = centry_string(centry, mem_ctx);
1547 if (*domain_name == NULL) {
1552 (*names)[i] = centry_string(centry, *names);
1554 have_unmapped = True;
1557 centry_free(centry);
1561 return NT_STATUS_NONE_MAPPED;
1563 if (!have_unmapped) {
1564 return NT_STATUS_OK;
1566 return STATUS_SOME_UNMAPPED;
1570 TALLOC_FREE(*names);
1571 TALLOC_FREE(*types);
1573 result = domain->backend->rids_to_names(domain, mem_ctx, domain_sid,
1574 rids, num_rids, domain_name,
1577 if (!NT_STATUS_IS_OK(result) &&
1578 !NT_STATUS_EQUAL(result, STATUS_SOME_UNMAPPED)) {
1582 refresh_sequence_number(domain, False);
1584 for (i=0; i<num_rids; i++) {
1588 if (!sid_compose(&sid, domain_sid, rids[i])) {
1589 result = NT_STATUS_INTERNAL_ERROR;
1593 status = (*types)[i] == SID_NAME_UNKNOWN ?
1594 NT_STATUS_NONE_MAPPED : NT_STATUS_OK;
1596 wcache_save_sid_to_name(domain, status, &sid, *domain_name,
1597 (*names)[i], (*types)[i]);
1604 TALLOC_FREE(*names);
1605 TALLOC_FREE(*types);
1609 /* Lookup user information from a rid */
1610 static NTSTATUS query_user(struct winbindd_domain *domain,
1611 TALLOC_CTX *mem_ctx,
1612 const DOM_SID *user_sid,
1613 WINBIND_USERINFO *info)
1615 struct winbind_cache *cache = get_cache(domain);
1616 struct cache_entry *centry = NULL;
1622 centry = wcache_fetch(cache, domain, "U/%s", sid_string_static(user_sid));
1624 /* If we have an access denied cache entry and a cached info3 in the
1625 samlogon cache then do a query. This will force the rpc back end
1626 to return the info3 data. */
1628 if (NT_STATUS_V(domain->last_status) == NT_STATUS_V(NT_STATUS_ACCESS_DENIED) &&
1629 netsamlogon_cache_have(user_sid)) {
1630 DEBUG(10, ("query_user: cached access denied and have cached info3\n"));
1631 domain->last_status = NT_STATUS_OK;
1632 centry_free(centry);
1639 info->acct_name = centry_string(centry, mem_ctx);
1640 info->full_name = centry_string(centry, mem_ctx);
1641 info->homedir = centry_string(centry, mem_ctx);
1642 info->shell = centry_string(centry, mem_ctx);
1643 info->primary_gid = centry_uint32(centry);
1644 centry_sid(centry, mem_ctx, &info->user_sid);
1645 centry_sid(centry, mem_ctx, &info->group_sid);
1646 status = centry->status;
1648 DEBUG(10,("query_user: [Cached] - cached info for domain %s status: %s\n",
1649 domain->name, nt_errstr(status) ));
1651 centry_free(centry);
1657 /* Return status value returned by seq number check */
1659 if (!NT_STATUS_IS_OK(domain->last_status))
1660 return domain->last_status;
1662 DEBUG(10,("query_user: [Cached] - doing backend query for info for domain %s\n",
1665 status = domain->backend->query_user(domain, mem_ctx, user_sid, info);
1668 refresh_sequence_number(domain, False);
1669 wcache_save_user(domain, status, info);
1675 /* Lookup groups a user is a member of. */
1676 static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
1677 TALLOC_CTX *mem_ctx,
1678 const DOM_SID *user_sid,
1679 uint32 *num_groups, DOM_SID **user_gids)
1681 struct winbind_cache *cache = get_cache(domain);
1682 struct cache_entry *centry = NULL;
1690 centry = wcache_fetch(cache, domain, "UG/%s", sid_to_string(sid_string, user_sid));
1692 /* If we have an access denied cache entry and a cached info3 in the
1693 samlogon cache then do a query. This will force the rpc back end
1694 to return the info3 data. */
1696 if (NT_STATUS_V(domain->last_status) == NT_STATUS_V(NT_STATUS_ACCESS_DENIED) &&
1697 netsamlogon_cache_have(user_sid)) {
1698 DEBUG(10, ("lookup_usergroups: cached access denied and have cached info3\n"));
1699 domain->last_status = NT_STATUS_OK;
1700 centry_free(centry);
1707 *num_groups = centry_uint32(centry);
1709 if (*num_groups == 0)
1712 (*user_gids) = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_groups);
1713 if (! (*user_gids)) {
1714 smb_panic_fn("lookup_usergroups out of memory");
1716 for (i=0; i<(*num_groups); i++) {
1717 centry_sid(centry, mem_ctx, &(*user_gids)[i]);
1721 status = centry->status;
1723 DEBUG(10,("lookup_usergroups: [Cached] - cached info for domain %s status: %s\n",
1724 domain->name, nt_errstr(status) ));
1726 centry_free(centry);
1731 (*user_gids) = NULL;
1733 /* Return status value returned by seq number check */
1735 if (!NT_STATUS_IS_OK(domain->last_status))
1736 return domain->last_status;
1738 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info for domain %s\n",
1741 status = domain->backend->lookup_usergroups(domain, mem_ctx, user_sid, num_groups, user_gids);
1743 if ( NT_STATUS_EQUAL(status, NT_STATUS_SYNCHRONIZATION_REQUIRED) )
1747 refresh_sequence_number(domain, False);
1748 centry = centry_start(domain, status);
1751 centry_put_uint32(centry, *num_groups);
1752 for (i=0; i<(*num_groups); i++) {
1753 centry_put_sid(centry, &(*user_gids)[i]);
1755 centry_end(centry, "UG/%s", sid_to_string(sid_string, user_sid));
1756 centry_free(centry);
1762 static NTSTATUS lookup_useraliases(struct winbindd_domain *domain,
1763 TALLOC_CTX *mem_ctx,
1764 uint32 num_sids, const DOM_SID *sids,
1765 uint32 *num_aliases, uint32 **alias_rids)
1767 struct winbind_cache *cache = get_cache(domain);
1768 struct cache_entry *centry = NULL;
1770 char *sidlist = talloc_strdup(mem_ctx, "");
1776 if (num_sids == 0) {
1779 return NT_STATUS_OK;
1782 /* We need to cache indexed by the whole list of SIDs, the aliases
1783 * resulting might come from any of the SIDs. */
1785 for (i=0; i<num_sids; i++) {
1786 sidlist = talloc_asprintf(mem_ctx, "%s/%s", sidlist,
1787 sid_string_static(&sids[i]));
1788 if (sidlist == NULL)
1789 return NT_STATUS_NO_MEMORY;
1792 centry = wcache_fetch(cache, domain, "UA%s", sidlist);
1797 *num_aliases = centry_uint32(centry);
1801 (*alias_rids) = TALLOC_ARRAY(mem_ctx, uint32, *num_aliases);
1803 if ((*alias_rids) == NULL) {
1804 centry_free(centry);
1805 return NT_STATUS_NO_MEMORY;
1808 (*alias_rids) = NULL;
1811 for (i=0; i<(*num_aliases); i++)
1812 (*alias_rids)[i] = centry_uint32(centry);
1814 status = centry->status;
1816 DEBUG(10,("lookup_useraliases: [Cached] - cached info for domain: %s "
1817 "status %s\n", domain->name, nt_errstr(status)));
1819 centry_free(centry);
1824 (*alias_rids) = NULL;
1826 if (!NT_STATUS_IS_OK(domain->last_status))
1827 return domain->last_status;
1829 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info "
1830 "for domain %s\n", domain->name ));
1832 status = domain->backend->lookup_useraliases(domain, mem_ctx,
1834 num_aliases, alias_rids);
1837 refresh_sequence_number(domain, False);
1838 centry = centry_start(domain, status);
1841 centry_put_uint32(centry, *num_aliases);
1842 for (i=0; i<(*num_aliases); i++)
1843 centry_put_uint32(centry, (*alias_rids)[i]);
1844 centry_end(centry, "UA%s", sidlist);
1845 centry_free(centry);
1852 static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
1853 TALLOC_CTX *mem_ctx,
1854 const DOM_SID *group_sid, uint32 *num_names,
1855 DOM_SID **sid_mem, char ***names,
1856 uint32 **name_types)
1858 struct winbind_cache *cache = get_cache(domain);
1859 struct cache_entry *centry = NULL;
1867 centry = wcache_fetch(cache, domain, "GM/%s", sid_to_string(sid_string, group_sid));
1871 *num_names = centry_uint32(centry);
1873 if (*num_names == 0)
1876 (*sid_mem) = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_names);
1877 (*names) = TALLOC_ARRAY(mem_ctx, char *, *num_names);
1878 (*name_types) = TALLOC_ARRAY(mem_ctx, uint32, *num_names);
1880 if (! (*sid_mem) || ! (*names) || ! (*name_types)) {
1881 smb_panic_fn("lookup_groupmem out of memory");
1884 for (i=0; i<(*num_names); i++) {
1885 centry_sid(centry, mem_ctx, &(*sid_mem)[i]);
1886 (*names)[i] = centry_string(centry, mem_ctx);
1887 (*name_types)[i] = centry_uint32(centry);
1891 status = centry->status;
1893 DEBUG(10,("lookup_groupmem: [Cached] - cached info for domain %s status: %s\n",
1894 domain->name, nt_errstr(status)));
1896 centry_free(centry);
1903 (*name_types) = NULL;
1905 /* Return status value returned by seq number check */
1907 if (!NT_STATUS_IS_OK(domain->last_status))
1908 return domain->last_status;
1910 DEBUG(10,("lookup_groupmem: [Cached] - doing backend query for info for domain %s\n",
1913 status = domain->backend->lookup_groupmem(domain, mem_ctx, group_sid, num_names,
1914 sid_mem, names, name_types);
1917 refresh_sequence_number(domain, False);
1918 centry = centry_start(domain, status);
1921 centry_put_uint32(centry, *num_names);
1922 for (i=0; i<(*num_names); i++) {
1923 centry_put_sid(centry, &(*sid_mem)[i]);
1924 centry_put_string(centry, (*names)[i]);
1925 centry_put_uint32(centry, (*name_types)[i]);
1927 centry_end(centry, "GM/%s", sid_to_string(sid_string, group_sid));
1928 centry_free(centry);
1934 /* find the sequence number for a domain */
1935 static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
1937 refresh_sequence_number(domain, False);
1939 *seq = domain->sequence_number;
1941 return NT_STATUS_OK;
1944 /* enumerate trusted domains
1945 * (we need to have the list of trustdoms in the cache when we go offline) -
1947 static NTSTATUS trusted_domains(struct winbindd_domain *domain,
1948 TALLOC_CTX *mem_ctx,
1949 uint32 *num_domains,
1954 struct winbind_cache *cache = get_cache(domain);
1955 struct cache_entry *centry = NULL;
1962 centry = wcache_fetch(cache, domain, "TRUSTDOMS/%s", domain->name);
1968 *num_domains = centry_uint32(centry);
1971 (*names) = TALLOC_ARRAY(mem_ctx, char *, *num_domains);
1972 (*alt_names) = TALLOC_ARRAY(mem_ctx, char *, *num_domains);
1973 (*dom_sids) = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_domains);
1975 if (! (*dom_sids) || ! (*names) || ! (*alt_names)) {
1976 smb_panic_fn("trusted_domains out of memory");
1980 (*alt_names) = NULL;
1984 for (i=0; i<(*num_domains); i++) {
1985 (*names)[i] = centry_string(centry, mem_ctx);
1986 (*alt_names)[i] = centry_string(centry, mem_ctx);
1987 centry_sid(centry, mem_ctx, &(*dom_sids)[i]);
1990 status = centry->status;
1992 DEBUG(10,("trusted_domains: [Cached] - cached info for domain %s (%d trusts) status: %s\n",
1993 domain->name, *num_domains, nt_errstr(status) ));
1995 centry_free(centry);
2002 (*alt_names) = NULL;
2004 /* Return status value returned by seq number check */
2006 if (!NT_STATUS_IS_OK(domain->last_status))
2007 return domain->last_status;
2009 DEBUG(10,("trusted_domains: [Cached] - doing backend query for info for domain %s\n",
2012 status = domain->backend->trusted_domains(domain, mem_ctx, num_domains,
2013 names, alt_names, dom_sids);
2015 /* no trusts gives NT_STATUS_NO_MORE_ENTRIES resetting to NT_STATUS_OK
2016 * so that the generic centry handling still applies correctly -
2019 if (!NT_STATUS_IS_ERR(status)) {
2020 status = NT_STATUS_OK;
2024 #if 0 /* Disabled as we want the trust dom list to be managed by
2025 the main parent and always to make the query. --jerry */
2028 refresh_sequence_number(domain, False);
2030 centry = centry_start(domain, status);
2034 centry_put_uint32(centry, *num_domains);
2036 for (i=0; i<(*num_domains); i++) {
2037 centry_put_string(centry, (*names)[i]);
2038 centry_put_string(centry, (*alt_names)[i]);
2039 centry_put_sid(centry, &(*dom_sids)[i]);
2042 centry_end(centry, "TRUSTDOMS/%s", domain->name);
2044 centry_free(centry);
2052 /* get lockout policy */
2053 static NTSTATUS lockout_policy(struct winbindd_domain *domain,
2054 TALLOC_CTX *mem_ctx,
2055 SAM_UNK_INFO_12 *policy){
2056 struct winbind_cache *cache = get_cache(domain);
2057 struct cache_entry *centry = NULL;
2063 centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
2068 policy->duration = centry_nttime(centry);
2069 policy->reset_count = centry_nttime(centry);
2070 policy->bad_attempt_lockout = centry_uint16(centry);
2072 status = centry->status;
2074 DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2075 domain->name, nt_errstr(status) ));
2077 centry_free(centry);
2081 ZERO_STRUCTP(policy);
2083 /* Return status value returned by seq number check */
2085 if (!NT_STATUS_IS_OK(domain->last_status))
2086 return domain->last_status;
2088 DEBUG(10,("lockout_policy: [Cached] - doing backend query for info for domain %s\n",
2091 status = domain->backend->lockout_policy(domain, mem_ctx, policy);
2094 refresh_sequence_number(domain, False);
2095 wcache_save_lockout_policy(domain, status, policy);
2100 /* get password policy */
2101 static NTSTATUS password_policy(struct winbindd_domain *domain,
2102 TALLOC_CTX *mem_ctx,
2103 SAM_UNK_INFO_1 *policy)
2105 struct winbind_cache *cache = get_cache(domain);
2106 struct cache_entry *centry = NULL;
2112 centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
2117 policy->min_length_password = centry_uint16(centry);
2118 policy->password_history = centry_uint16(centry);
2119 policy->password_properties = centry_uint32(centry);
2120 policy->expire = centry_nttime(centry);
2121 policy->min_passwordage = centry_nttime(centry);
2123 status = centry->status;
2125 DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2126 domain->name, nt_errstr(status) ));
2128 centry_free(centry);
2132 ZERO_STRUCTP(policy);
2134 /* Return status value returned by seq number check */
2136 if (!NT_STATUS_IS_OK(domain->last_status))
2137 return domain->last_status;
2139 DEBUG(10,("password_policy: [Cached] - doing backend query for info for domain %s\n",
2142 status = domain->backend->password_policy(domain, mem_ctx, policy);
2145 refresh_sequence_number(domain, False);
2146 wcache_save_password_policy(domain, status, policy);
2152 /* Invalidate cached user and group lists coherently */
2154 static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
2157 if (strncmp((const char *)kbuf.dptr, "UL/", 3) == 0 ||
2158 strncmp((const char *)kbuf.dptr, "GL/", 3) == 0)
2159 tdb_delete(the_tdb, kbuf);
2164 /* Invalidate the getpwnam and getgroups entries for a winbindd domain */
2166 void wcache_invalidate_samlogon(struct winbindd_domain *domain,
2167 NET_USER_INFO_3 *info3)
2169 struct winbind_cache *cache;
2171 /* dont clear cached U/SID and UG/SID entries when we want to logon
2174 if (lp_winbind_offline_logon()) {
2181 cache = get_cache(domain);
2182 netsamlogon_clear_cached_user(cache->tdb, info3);
2185 void wcache_invalidate_cache(void)
2187 struct winbindd_domain *domain;
2189 for (domain = domain_list(); domain; domain = domain->next) {
2190 struct winbind_cache *cache = get_cache(domain);
2192 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
2193 "entries for %s\n", domain->name));
2195 tdb_traverse(cache->tdb, traverse_fn, NULL);
2199 BOOL init_wcache(void)
2201 if (wcache == NULL) {
2202 wcache = SMB_XMALLOC_P(struct winbind_cache);
2203 ZERO_STRUCTP(wcache);
2206 if (wcache->tdb != NULL)
2209 /* when working offline we must not clear the cache on restart */
2210 wcache->tdb = tdb_open_log(lock_path("winbindd_cache.tdb"),
2211 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
2212 lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST),
2213 O_RDWR|O_CREAT, 0600);
2215 if (wcache->tdb == NULL) {
2216 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
2223 /************************************************************************
2224 This is called by the parent to initialize the cache file.
2225 We don't need sophisticated locking here as we know we're the
2227 ************************************************************************/
2229 BOOL initialize_winbindd_cache(void)
2231 BOOL cache_bad = True;
2234 if (!init_wcache()) {
2235 DEBUG(0,("initialize_winbindd_cache: init_wcache failed.\n"));
2239 /* Check version number. */
2240 if (tdb_fetch_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, &vers) &&
2241 vers == WINBINDD_CACHE_VERSION) {
2246 DEBUG(0,("initialize_winbindd_cache: clearing cache "
2247 "and re-creating with version number %d\n",
2248 WINBINDD_CACHE_VERSION ));
2250 tdb_close(wcache->tdb);
2253 if (unlink(lock_path("winbindd_cache.tdb")) == -1) {
2254 DEBUG(0,("initialize_winbindd_cache: unlink %s failed %s ",
2255 lock_path("winbindd_cache.tdb"),
2259 if (!init_wcache()) {
2260 DEBUG(0,("initialize_winbindd_cache: re-initialization "
2261 "init_wcache failed.\n"));
2265 /* Write the version. */
2266 if (!tdb_store_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, WINBINDD_CACHE_VERSION)) {
2267 DEBUG(0,("initialize_winbindd_cache: version number store failed %s\n",
2268 tdb_errorstr(wcache->tdb) ));
2273 tdb_close(wcache->tdb);
2278 void cache_store_response(pid_t pid, struct winbindd_response *response)
2285 DEBUG(10, ("Storing response for pid %d, len %d\n",
2286 pid, response->length));
2288 fstr_sprintf(key_str, "DR/%d", pid);
2289 if (tdb_store(wcache->tdb, string_tdb_data(key_str),
2290 make_tdb_data((uint8 *)response, sizeof(*response)),
2294 if (response->length == sizeof(*response))
2297 /* There's extra data */
2299 DEBUG(10, ("Storing extra data: len=%d\n",
2300 (int)(response->length - sizeof(*response))));
2302 fstr_sprintf(key_str, "DE/%d", pid);
2303 if (tdb_store(wcache->tdb, string_tdb_data(key_str),
2304 make_tdb_data((uint8 *)response->extra_data.data,
2305 response->length - sizeof(*response)),
2309 /* We could not store the extra data, make sure the tdb does not
2310 * contain a main record with wrong dangling extra data */
2312 fstr_sprintf(key_str, "DR/%d", pid);
2313 tdb_delete(wcache->tdb, string_tdb_data(key_str));
2318 BOOL cache_retrieve_response(pid_t pid, struct winbindd_response * response)
2326 DEBUG(10, ("Retrieving response for pid %d\n", pid));
2328 fstr_sprintf(key_str, "DR/%d", pid);
2329 data = tdb_fetch(wcache->tdb, string_tdb_data(key_str));
2331 if (data.dptr == NULL)
2334 if (data.dsize != sizeof(*response))
2337 memcpy(response, data.dptr, data.dsize);
2338 SAFE_FREE(data.dptr);
2340 if (response->length == sizeof(*response)) {
2341 response->extra_data.data = NULL;
2345 /* There's extra data */
2347 DEBUG(10, ("Retrieving extra data length=%d\n",
2348 (int)(response->length - sizeof(*response))));
2350 fstr_sprintf(key_str, "DE/%d", pid);
2351 data = tdb_fetch(wcache->tdb, string_tdb_data(key_str));
2353 if (data.dptr == NULL) {
2354 DEBUG(0, ("Did not find extra data\n"));
2358 if (data.dsize != (response->length - sizeof(*response))) {
2359 DEBUG(0, ("Invalid extra data length: %d\n", (int)data.dsize));
2360 SAFE_FREE(data.dptr);
2364 dump_data(11, (uint8 *)data.dptr, data.dsize);
2366 response->extra_data.data = data.dptr;
2370 void cache_cleanup_response(pid_t pid)
2377 fstr_sprintf(key_str, "DR/%d", pid);
2378 tdb_delete(wcache->tdb, string_tdb_data(key_str));
2380 fstr_sprintf(key_str, "DE/%d", pid);
2381 tdb_delete(wcache->tdb, string_tdb_data(key_str));
2387 BOOL lookup_cached_sid(TALLOC_CTX *mem_ctx, const DOM_SID *sid,
2388 char **domain_name, char **name,
2389 enum lsa_SidType *type)
2391 struct winbindd_domain *domain;
2392 struct winbind_cache *cache;
2393 struct cache_entry *centry = NULL;
2396 domain = find_lookup_domain_from_sid(sid);
2397 if (domain == NULL) {
2401 cache = get_cache(domain);
2403 if (cache->tdb == NULL) {
2407 centry = wcache_fetch(cache, domain, "SN/%s", sid_string_static(sid));
2408 if (centry == NULL) {
2412 if (NT_STATUS_IS_OK(centry->status)) {
2413 *type = (enum lsa_SidType)centry_uint32(centry);
2414 *domain_name = centry_string(centry, mem_ctx);
2415 *name = centry_string(centry, mem_ctx);
2418 status = centry->status;
2419 centry_free(centry);
2420 return NT_STATUS_IS_OK(status);
2423 BOOL lookup_cached_name(TALLOC_CTX *mem_ctx,
2424 const char *domain_name,
2427 enum lsa_SidType *type)
2429 struct winbindd_domain *domain;
2430 struct winbind_cache *cache;
2431 struct cache_entry *centry = NULL;
2434 BOOL original_online_state;
2436 domain = find_lookup_domain_from_name(domain_name);
2437 if (domain == NULL) {
2441 cache = get_cache(domain);
2443 if (cache->tdb == NULL) {
2447 fstrcpy(uname, name);
2450 /* If we are doing a cached logon, temporarily set the domain
2451 offline so the cache won't expire the entry */
2453 original_online_state = domain->online;
2454 domain->online = False;
2455 centry = wcache_fetch(cache, domain, "NS/%s/%s", domain_name, uname);
2456 domain->online = original_online_state;
2458 if (centry == NULL) {
2462 if (NT_STATUS_IS_OK(centry->status)) {
2463 *type = (enum lsa_SidType)centry_uint32(centry);
2464 centry_sid(centry, mem_ctx, sid);
2467 status = centry->status;
2468 centry_free(centry);
2470 return NT_STATUS_IS_OK(status);
2473 void cache_name2sid(struct winbindd_domain *domain,
2474 const char *domain_name, const char *name,
2475 enum lsa_SidType type, const DOM_SID *sid)
2477 refresh_sequence_number(domain, False);
2478 wcache_save_name_to_sid(domain, NT_STATUS_OK, domain_name, name,
2483 * The original idea that this cache only contains centries has
2484 * been blurred - now other stuff gets put in here. Ensure we
2485 * ignore these things on cleanup.
2488 static int traverse_fn_cleanup(TDB_CONTEXT *the_tdb, TDB_DATA kbuf,
2489 TDB_DATA dbuf, void *state)
2491 struct cache_entry *centry;
2493 if (is_non_centry_key(kbuf)) {
2497 centry = wcache_fetch_raw((char *)kbuf.dptr);
2502 if (!NT_STATUS_IS_OK(centry->status)) {
2503 DEBUG(10,("deleting centry %s\n", (const char *)kbuf.dptr));
2504 tdb_delete(the_tdb, kbuf);
2507 centry_free(centry);
2511 /* flush the cache */
2512 void wcache_flush_cache(void)
2517 tdb_close(wcache->tdb);
2523 /* when working offline we must not clear the cache on restart */
2524 wcache->tdb = tdb_open_log(lock_path("winbindd_cache.tdb"),
2525 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
2526 lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST),
2527 O_RDWR|O_CREAT, 0600);
2530 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
2534 tdb_traverse(wcache->tdb, traverse_fn_cleanup, NULL);
2536 DEBUG(10,("wcache_flush_cache success\n"));
2539 /* Count cached creds */
2541 static int traverse_fn_cached_creds(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
2544 int *cred_count = (int*)state;
2546 if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
2552 NTSTATUS wcache_count_cached_creds(struct winbindd_domain *domain, int *count)
2554 struct winbind_cache *cache = get_cache(domain);
2559 return NT_STATUS_INTERNAL_DB_ERROR;
2562 tdb_traverse(cache->tdb, traverse_fn_cached_creds, (void *)count);
2564 return NT_STATUS_OK;
2568 struct cred_list *prev, *next;
2573 static struct cred_list *wcache_cred_list;
2575 static int traverse_fn_get_credlist(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
2578 struct cred_list *cred;
2580 if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
2582 cred = SMB_MALLOC_P(struct cred_list);
2584 DEBUG(0,("traverse_fn_remove_first_creds: failed to malloc new entry for list\n"));
2590 /* save a copy of the key */
2592 fstrcpy(cred->name, (const char *)kbuf.dptr);
2593 DLIST_ADD(wcache_cred_list, cred);
2599 NTSTATUS wcache_remove_oldest_cached_creds(struct winbindd_domain *domain, const DOM_SID *sid)
2601 struct winbind_cache *cache = get_cache(domain);
2604 struct cred_list *cred, *oldest = NULL;
2607 return NT_STATUS_INTERNAL_DB_ERROR;
2610 /* we possibly already have an entry */
2611 if (sid && NT_STATUS_IS_OK(wcache_cached_creds_exist(domain, sid))) {
2615 DEBUG(11,("we already have an entry, deleting that\n"));
2617 fstr_sprintf(key_str, "CRED/%s", sid_string_static(sid));
2619 tdb_delete(cache->tdb, string_tdb_data(key_str));
2621 return NT_STATUS_OK;
2624 ret = tdb_traverse(cache->tdb, traverse_fn_get_credlist, NULL);
2626 return NT_STATUS_OK;
2627 } else if ((ret == -1) || (wcache_cred_list == NULL)) {
2628 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
2631 ZERO_STRUCTP(oldest);
2633 for (cred = wcache_cred_list; cred; cred = cred->next) {
2638 data = tdb_fetch(cache->tdb, string_tdb_data(cred->name));
2640 DEBUG(10,("wcache_remove_oldest_cached_creds: entry for [%s] not found\n",
2642 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
2646 t = IVAL(data.dptr, 0);
2647 SAFE_FREE(data.dptr);
2650 oldest = SMB_MALLOC_P(struct cred_list);
2651 if (oldest == NULL) {
2652 status = NT_STATUS_NO_MEMORY;
2656 fstrcpy(oldest->name, cred->name);
2657 oldest->created = t;
2661 if (t < oldest->created) {
2662 fstrcpy(oldest->name, cred->name);
2663 oldest->created = t;
2667 if (tdb_delete(cache->tdb, string_tdb_data(oldest->name)) == 0) {
2668 status = NT_STATUS_OK;
2670 status = NT_STATUS_UNSUCCESSFUL;
2673 SAFE_FREE(wcache_cred_list);
2679 /* Change the global online/offline state. */
2680 BOOL set_global_winbindd_state_offline(void)
2684 DEBUG(10,("set_global_winbindd_state_offline: offline requested.\n"));
2686 /* Only go offline if someone has created
2687 the key "WINBINDD_OFFLINE" in the cache tdb. */
2689 if (wcache == NULL || wcache->tdb == NULL) {
2690 DEBUG(10,("set_global_winbindd_state_offline: wcache not open yet.\n"));
2694 if (!lp_winbind_offline_logon()) {
2695 DEBUG(10,("set_global_winbindd_state_offline: rejecting.\n"));
2699 if (global_winbindd_offline_state) {
2700 /* Already offline. */
2704 data = tdb_fetch_bystring( wcache->tdb, "WINBINDD_OFFLINE" );
2706 if (!data.dptr || data.dsize != 4) {
2707 DEBUG(10,("set_global_winbindd_state_offline: offline state not set.\n"));
2708 SAFE_FREE(data.dptr);
2711 DEBUG(10,("set_global_winbindd_state_offline: offline state set.\n"));
2712 global_winbindd_offline_state = True;
2713 SAFE_FREE(data.dptr);
2718 void set_global_winbindd_state_online(void)
2720 DEBUG(10,("set_global_winbindd_state_online: online requested.\n"));
2722 if (!lp_winbind_offline_logon()) {
2723 DEBUG(10,("set_global_winbindd_state_online: rejecting.\n"));
2727 if (!global_winbindd_offline_state) {
2728 /* Already online. */
2731 global_winbindd_offline_state = False;
2737 /* Ensure there is no key "WINBINDD_OFFLINE" in the cache tdb. */
2738 tdb_delete_bystring(wcache->tdb, "WINBINDD_OFFLINE");
2741 BOOL get_global_winbindd_state_offline(void)
2743 return global_winbindd_offline_state;
2746 /***********************************************************************
2747 Validate functions for all possible cache tdb keys.
2748 ***********************************************************************/
2750 static struct cache_entry *create_centry_validate(const char *kstr, TDB_DATA data,
2751 struct tdb_validation_status *state)
2753 struct cache_entry *centry;
2755 centry = SMB_XMALLOC_P(struct cache_entry);
2756 centry->data = (unsigned char *)memdup(data.dptr, data.dsize);
2757 if (!centry->data) {
2761 centry->len = data.dsize;
2764 if (centry->len < 8) {
2765 /* huh? corrupt cache? */
2766 DEBUG(0,("create_centry_validate: Corrupt cache for key %s (len < 8) ?\n", kstr));
2767 centry_free(centry);
2768 state->bad_entry = True;
2769 state->success = False;
2773 centry->status = NT_STATUS(centry_uint32(centry));
2774 centry->sequence_number = centry_uint32(centry);
2778 static int validate_seqnum(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
2779 struct tdb_validation_status *state)
2781 if (dbuf.dsize != 8) {
2782 DEBUG(0,("validate_seqnum: Corrupt cache for key %s (len %u != 8) ?\n",
2783 keystr, (unsigned int)dbuf.dsize ));
2784 state->bad_entry = True;
2790 static int validate_ns(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
2791 struct tdb_validation_status *state)
2793 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
2798 (void)centry_uint32(centry);
2799 if (NT_STATUS_IS_OK(centry->status)) {
2801 (void)centry_sid(centry, mem_ctx, &sid);
2804 centry_free(centry);
2806 if (!(state->success)) {
2809 DEBUG(10,("validate_ns: %s ok\n", keystr));
2813 static int validate_sn(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
2814 struct tdb_validation_status *state)
2816 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
2821 if (NT_STATUS_IS_OK(centry->status)) {
2822 (void)centry_uint32(centry);
2823 (void)centry_string(centry, mem_ctx);
2824 (void)centry_string(centry, mem_ctx);
2827 centry_free(centry);
2829 if (!(state->success)) {
2832 DEBUG(10,("validate_sn: %s ok\n", keystr));
2836 static int validate_u(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
2837 struct tdb_validation_status *state)
2839 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
2846 (void)centry_string(centry, mem_ctx);
2847 (void)centry_string(centry, mem_ctx);
2848 (void)centry_string(centry, mem_ctx);
2849 (void)centry_string(centry, mem_ctx);
2850 (void)centry_uint32(centry);
2851 (void)centry_sid(centry, mem_ctx, &sid);
2852 (void)centry_sid(centry, mem_ctx, &sid);
2854 centry_free(centry);
2856 if (!(state->success)) {
2859 DEBUG(10,("validate_u: %s ok\n", keystr));
2863 static int validate_loc_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
2864 struct tdb_validation_status *state)
2866 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
2872 (void)centry_nttime(centry);
2873 (void)centry_nttime(centry);
2874 (void)centry_uint16(centry);
2876 centry_free(centry);
2878 if (!(state->success)) {
2881 DEBUG(10,("validate_loc_pol: %s ok\n", keystr));
2885 static int validate_pwd_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
2886 struct tdb_validation_status *state)
2888 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
2894 (void)centry_uint16(centry);
2895 (void)centry_uint16(centry);
2896 (void)centry_uint32(centry);
2897 (void)centry_nttime(centry);
2898 (void)centry_nttime(centry);
2900 centry_free(centry);
2902 if (!(state->success)) {
2905 DEBUG(10,("validate_pwd_pol: %s ok\n", keystr));
2909 static int validate_cred(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
2910 struct tdb_validation_status *state)
2912 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
2918 (void)centry_time(centry);
2919 (void)centry_hash16(centry, mem_ctx);
2921 /* We only have 17 bytes more data in the salted cred case. */
2922 if (centry->len - centry->ofs == 17) {
2923 (void)centry_hash16(centry, mem_ctx);
2926 centry_free(centry);
2928 if (!(state->success)) {
2931 DEBUG(10,("validate_cred: %s ok\n", keystr));
2935 static int validate_ul(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
2936 struct tdb_validation_status *state)
2938 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
2939 int32 num_entries, i;
2945 num_entries = (int32)centry_uint32(centry);
2947 for (i=0; i< num_entries; i++) {
2949 (void)centry_string(centry, mem_ctx);
2950 (void)centry_string(centry, mem_ctx);
2951 (void)centry_string(centry, mem_ctx);
2952 (void)centry_string(centry, mem_ctx);
2953 (void)centry_sid(centry, mem_ctx, &sid);
2954 (void)centry_sid(centry, mem_ctx, &sid);
2957 centry_free(centry);
2959 if (!(state->success)) {
2962 DEBUG(10,("validate_ul: %s ok\n", keystr));
2966 static int validate_gl(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
2967 struct tdb_validation_status *state)
2969 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
2970 int32 num_entries, i;
2976 num_entries = centry_uint32(centry);
2978 for (i=0; i< num_entries; i++) {
2979 (void)centry_string(centry, mem_ctx);
2980 (void)centry_string(centry, mem_ctx);
2981 (void)centry_uint32(centry);
2984 centry_free(centry);
2986 if (!(state->success)) {
2989 DEBUG(10,("validate_gl: %s ok\n", keystr));
2993 static int validate_ug(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
2994 struct tdb_validation_status *state)
2996 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
2997 int32 num_groups, i;
3003 num_groups = centry_uint32(centry);
3005 for (i=0; i< num_groups; i++) {
3007 centry_sid(centry, mem_ctx, &sid);
3010 centry_free(centry);
3012 if (!(state->success)) {
3015 DEBUG(10,("validate_ug: %s ok\n", keystr));
3019 static int validate_ua(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3020 struct tdb_validation_status *state)
3022 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3023 int32 num_aliases, i;
3029 num_aliases = centry_uint32(centry);
3031 for (i=0; i < num_aliases; i++) {
3032 (void)centry_uint32(centry);
3035 centry_free(centry);
3037 if (!(state->success)) {
3040 DEBUG(10,("validate_ua: %s ok\n", keystr));
3044 static int validate_gm(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3045 struct tdb_validation_status *state)
3047 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3054 num_names = centry_uint32(centry);
3056 for (i=0; i< num_names; i++) {
3058 centry_sid(centry, mem_ctx, &sid);
3059 (void)centry_string(centry, mem_ctx);
3060 (void)centry_uint32(centry);
3063 centry_free(centry);
3065 if (!(state->success)) {
3068 DEBUG(10,("validate_gm: %s ok\n", keystr));
3072 static int validate_dr(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3073 struct tdb_validation_status *state)
3075 /* Can't say anything about this other than must be nonzero. */
3076 if (dbuf.dsize == 0) {
3077 DEBUG(0,("validate_dr: Corrupt cache for key %s (len == 0) ?\n",
3079 state->bad_entry = True;
3080 state->success = False;
3084 DEBUG(10,("validate_dr: %s ok\n", keystr));
3088 static int validate_de(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3089 struct tdb_validation_status *state)
3091 /* Can't say anything about this other than must be nonzero. */
3092 if (dbuf.dsize == 0) {
3093 DEBUG(0,("validate_de: Corrupt cache for key %s (len == 0) ?\n",
3095 state->bad_entry = True;
3096 state->success = False;
3100 DEBUG(10,("validate_de: %s ok\n", keystr));
3104 static int validate_trustdoms(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3105 struct tdb_validation_status *state)
3107 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3108 int32 num_domains, i;
3114 num_domains = centry_uint32(centry);
3116 for (i=0; i< num_domains; i++) {
3118 (void)centry_string(centry, mem_ctx);
3119 (void)centry_string(centry, mem_ctx);
3120 (void)centry_sid(centry, mem_ctx, &sid);
3123 centry_free(centry);
3125 if (!(state->success)) {
3128 DEBUG(10,("validate_trustdoms: %s ok\n", keystr));
3132 static int validate_trustdomcache(TALLOC_CTX *mem_ctx, const char *keystr,
3134 struct tdb_validation_status *state)
3136 if (dbuf.dsize == 0) {
3137 DEBUG(0, ("validate_trustdomcache: Corrupt cache for "
3138 "key %s (len ==0) ?\n", keystr));
3139 state->bad_entry = True;
3140 state->success = False;
3144 DEBUG(10, ("validate_trustdomcache: %s ok\n", keystr));
3145 DEBUGADD(10, (" Don't trust me, I am a DUMMY!\n"));
3149 static int validate_offline(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3150 struct tdb_validation_status *state)
3152 if (dbuf.dsize != 4) {
3153 DEBUG(0,("validate_offline: Corrupt cache for key %s (len %u != 4) ?\n",
3154 keystr, (unsigned int)dbuf.dsize ));
3155 state->bad_entry = True;
3156 state->success = False;
3159 DEBUG(10,("validate_offline: %s ok\n", keystr));
3163 static int validate_cache_version(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3164 struct tdb_validation_status *state)
3166 if (dbuf.dsize != 4) {
3167 DEBUG(0, ("validate_cache_version: Corrupt cache for "
3168 "key %s (len %u != 4) ?\n",
3169 keystr, (unsigned int)dbuf.dsize));
3170 state->bad_entry = True;
3171 state->success = False;
3175 DEBUG(10, ("validate_cache_version: %s ok\n", keystr));
3179 /***********************************************************************
3180 A list of all possible cache tdb keys with associated validation
3182 ***********************************************************************/
3184 struct key_val_struct {
3185 const char *keyname;
3186 int (*validate_data_fn)(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, struct tdb_validation_status* state);
3188 {"SEQNUM/", validate_seqnum},
3189 {"NS/", validate_ns},
3190 {"SN/", validate_sn},
3192 {"LOC_POL/", validate_loc_pol},
3193 {"PWD_POL/", validate_pwd_pol},
3194 {"CRED/", validate_cred},
3195 {"UL/", validate_ul},
3196 {"GL/", validate_gl},
3197 {"UG/", validate_ug},
3198 {"UA", validate_ua},
3199 {"GM/", validate_gm},
3200 {"DR/", validate_dr},
3201 {"DE/", validate_de},
3202 {"TRUSTDOMS/", validate_trustdoms},
3203 {"TRUSTDOMCACHE/", validate_trustdomcache},
3204 {"WINBINDD_OFFLINE", validate_offline},
3205 {WINBINDD_CACHE_VERSION_KEYSTR, validate_cache_version},
3209 /***********************************************************************
3210 Function to look at every entry in the tdb and validate it as far as
3212 ***********************************************************************/
3214 static int cache_traverse_validate_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, void *state)
3217 struct tdb_validation_status *v_state = (struct tdb_validation_status *)state;
3219 /* Paranoia check. */
3220 if (kbuf.dsize > 1024) {
3221 DEBUG(0,("cache_traverse_validate_fn: key length too large (%u) > 1024\n\n",
3222 (unsigned int)kbuf.dsize ));
3226 for (i = 0; key_val[i].keyname; i++) {
3227 size_t namelen = strlen(key_val[i].keyname);
3228 if (kbuf.dsize >= namelen && (
3229 strncmp(key_val[i].keyname, (const char *)kbuf.dptr, namelen)) == 0) {
3230 TALLOC_CTX *mem_ctx;
3234 keystr = SMB_MALLOC_ARRAY(char, kbuf.dsize+1);
3238 memcpy(keystr, kbuf.dptr, kbuf.dsize);
3239 keystr[kbuf.dsize] = '\0';
3241 mem_ctx = talloc_init("validate_ctx");
3247 ret = key_val[i].validate_data_fn(mem_ctx, keystr, dbuf,
3251 talloc_destroy(mem_ctx);
3256 DEBUG(0,("cache_traverse_validate_fn: unknown cache entry\nkey :\n"));
3257 dump_data(0, (uint8 *)kbuf.dptr, kbuf.dsize);
3258 DEBUG(0,("data :\n"));
3259 dump_data(0, (uint8 *)dbuf.dptr, dbuf.dsize);
3260 v_state->unknown_key = True;
3261 v_state->success = False;
3262 return 1; /* terminate. */
3265 static void validate_panic(const char *const why)
3267 DEBUG(0,("validating cache: would panic %s\n", why ));
3268 DEBUGADD(0, ("exiting instead (cache validation mode)\n"));
3272 /***********************************************************************
3273 Try and validate every entry in the winbindd cache. If we fail here,
3274 delete the cache tdb and return non-zero - the caller (main winbindd
3275 function) will restart us as we don't know if we crashed or not.
3276 ***********************************************************************/
3278 int winbindd_validate_cache(void)
3281 const char *tdb_path = lock_path("winbindd_cache.tdb");
3282 TDB_CONTEXT *tdb = NULL;
3284 DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
3285 smb_panic_fn = validate_panic;
3288 tdb = tdb_open_log(tdb_path,
3289 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
3290 ( lp_winbind_offline_logon()
3292 : TDB_DEFAULT | TDB_CLEAR_IF_FIRST ),
3296 DEBUG(0, ("winbindd_validate_cache: "
3297 "error opening/initializing tdb\n"));
3302 ret = tdb_validate_and_backup(lock_path("winbindd_cache.tdb"),
3303 cache_traverse_validate_fn);
3306 DEBUG(10, ("winbindd_validate_cache: validation not successful.\n"));
3307 DEBUGADD(10, ("removing tdb %s.\n", tdb_path));
3312 DEBUG(10, ("winbindd_validate_cache: restoring panic function\n"));
3313 smb_panic_fn = smb_panic;
3317 /*********************************************************************
3318 ********************************************************************/
3320 static BOOL add_wbdomain_to_tdc_array( struct winbindd_domain *new_dom,
3321 struct winbindd_tdc_domain **domains,
3322 size_t *num_domains )
3324 struct winbindd_tdc_domain *list = NULL;
3327 BOOL set_only = False;
3329 /* don't allow duplicates */
3334 for ( i=0; i< (*num_domains); i++ ) {
3335 if ( strequal( new_dom->name, list[i].domain_name ) ) {
3336 DEBUG(10,("add_wbdomain_to_tdc_array: Found existing record for %s\n",
3347 list = TALLOC_ARRAY( NULL, struct winbindd_tdc_domain, 1 );
3350 list = TALLOC_REALLOC_ARRAY( *domains, *domains,
3351 struct winbindd_tdc_domain,
3356 ZERO_STRUCT( list[idx] );
3362 list[idx].domain_name = talloc_strdup( list, new_dom->name );
3363 list[idx].dns_name = talloc_strdup( list, new_dom->alt_name );
3365 if ( !is_null_sid( &new_dom->sid ) )
3366 sid_copy( &list[idx].sid, &new_dom->sid );
3368 if ( new_dom->domain_flags != 0x0 )
3369 list[idx].trust_flags = new_dom->domain_flags;
3371 if ( new_dom->domain_type != 0x0 )
3372 list[idx].trust_type = new_dom->domain_type;
3374 if ( new_dom->domain_trust_attribs != 0x0 )
3375 list[idx].trust_attribs = new_dom->domain_trust_attribs;
3379 *num_domains = idx + 1;
3385 /*********************************************************************
3386 ********************************************************************/
3388 static TDB_DATA make_tdc_key( const char *domain_name )
3390 char *keystr = NULL;
3391 TDB_DATA key = { NULL, 0 };
3393 if ( !domain_name ) {
3394 DEBUG(5,("make_tdc_key: Keyname workgroup is NULL!\n"));
3399 asprintf( &keystr, "TRUSTDOMCACHE/%s", domain_name );
3400 key = string_term_tdb_data(keystr);
3405 /*********************************************************************
3406 ********************************************************************/
3408 static int pack_tdc_domains( struct winbindd_tdc_domain *domains,
3410 unsigned char **buf )
3412 unsigned char *buffer = NULL;
3417 DEBUG(10,("pack_tdc_domains: Packing %d trusted domains\n",
3425 /* Store the number of array items first */
3426 len += tdb_pack( buffer+len, buflen-len, "d",
3429 /* now pack each domain trust record */
3430 for ( i=0; i<num_domains; i++ ) {
3433 DEBUG(10,("pack_tdc_domains: Packing domain %s (%s)\n",
3434 domains[i].domain_name,
3435 domains[i].dns_name ? domains[i].dns_name : "UNKNOWN" ));
3438 len += tdb_pack( buffer+len, buflen-len, "fffddd",
3439 domains[i].domain_name,
3440 domains[i].dns_name,
3441 sid_string_static(&domains[i].sid),
3442 domains[i].trust_flags,
3443 domains[i].trust_attribs,
3444 domains[i].trust_type );
3447 if ( buflen < len ) {
3449 if ( (buffer = SMB_MALLOC_ARRAY(unsigned char, len)) == NULL ) {
3450 DEBUG(0,("pack_tdc_domains: failed to alloc buffer!\n"));
3464 /*********************************************************************
3465 ********************************************************************/
3467 static size_t unpack_tdc_domains( unsigned char *buf, int buflen,
3468 struct winbindd_tdc_domain **domains )
3470 fstring domain_name, dns_name, sid_string;
3471 uint32 type, attribs, flags;
3475 struct winbindd_tdc_domain *list = NULL;
3477 /* get the number of domains */
3478 len += tdb_unpack( buf+len, buflen-len, "d", &num_domains);
3480 DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
3484 list = TALLOC_ARRAY( NULL, struct winbindd_tdc_domain, num_domains );
3486 DEBUG(0,("unpack_tdc_domains: Failed to talloc() domain list!\n"));
3490 for ( i=0; i<num_domains; i++ ) {
3491 len += tdb_unpack( buf+len, buflen-len, "fffddd",
3500 DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
3501 TALLOC_FREE( list );
3505 DEBUG(11,("unpack_tdc_domains: Unpacking domain %s (%s) "
3506 "SID %s, flags = 0x%x, attribs = 0x%x, type = 0x%x\n",
3507 domain_name, dns_name, sid_string,
3508 flags, attribs, type));
3510 list[i].domain_name = talloc_strdup( list, domain_name );
3511 list[i].dns_name = talloc_strdup( list, dns_name );
3512 if ( !string_to_sid( &(list[i].sid), sid_string ) ) {
3513 DEBUG(10,("unpack_tdc_domains: no SID for domain %s\n",
3516 list[i].trust_flags = flags;
3517 list[i].trust_attribs = attribs;
3518 list[i].trust_type = type;
3526 /*********************************************************************
3527 ********************************************************************/
3529 static BOOL wcache_tdc_store_list( struct winbindd_tdc_domain *domains, size_t num_domains )
3531 TDB_DATA key = make_tdc_key( lp_workgroup() );
3532 TDB_DATA data = { NULL, 0 };
3538 /* See if we were asked to delete the cache entry */
3541 ret = tdb_delete( wcache->tdb, key );
3545 data.dsize = pack_tdc_domains( domains, num_domains, &data.dptr );
3552 ret = tdb_store( wcache->tdb, key, data, 0 );
3555 SAFE_FREE( data.dptr );
3556 SAFE_FREE( key.dptr );
3558 return ( ret != -1 );
3561 /*********************************************************************
3562 ********************************************************************/
3564 BOOL wcache_tdc_fetch_list( struct winbindd_tdc_domain **domains, size_t *num_domains )
3566 TDB_DATA key = make_tdc_key( lp_workgroup() );
3567 TDB_DATA data = { NULL, 0 };
3575 data = tdb_fetch( wcache->tdb, key );
3577 SAFE_FREE( key.dptr );
3582 *num_domains = unpack_tdc_domains( data.dptr, data.dsize, domains );
3584 SAFE_FREE( data.dptr );
3592 /*********************************************************************
3593 ********************************************************************/
3595 BOOL wcache_tdc_add_domain( struct winbindd_domain *domain )
3597 struct winbindd_tdc_domain *dom_list = NULL;
3598 size_t num_domains = 0;
3601 DEBUG(10,("wcache_tdc_add_domain: Adding domain %s (%s), SID %s, "
3602 "flags = 0x%x, attributes = 0x%x, type = 0x%x\n",
3603 domain->name, domain->alt_name,
3604 sid_string_static(&domain->sid),
3605 domain->domain_flags,
3606 domain->domain_trust_attribs,
3607 domain->domain_type));
3609 if ( !init_wcache() ) {
3613 /* fetch the list */
3615 wcache_tdc_fetch_list( &dom_list, &num_domains );
3617 /* add the new domain */
3619 if ( !add_wbdomain_to_tdc_array( domain, &dom_list, &num_domains ) ) {
3623 /* pack the domain */
3625 if ( !wcache_tdc_store_list( dom_list, num_domains ) ) {
3633 TALLOC_FREE( dom_list );
3638 /*********************************************************************
3639 ********************************************************************/
3641 struct winbindd_tdc_domain * wcache_tdc_fetch_domain( TALLOC_CTX *ctx, const char *name )
3643 struct winbindd_tdc_domain *dom_list = NULL;
3644 size_t num_domains = 0;
3646 struct winbindd_tdc_domain *d = NULL;
3648 DEBUG(10,("wcache_tdc_fetch_domain: Searching for domain %s\n", name));
3650 if ( !init_wcache() ) {
3654 /* fetch the list */
3656 wcache_tdc_fetch_list( &dom_list, &num_domains );
3658 for ( i=0; i<num_domains; i++ ) {
3659 if ( strequal(name, dom_list[i].domain_name) ||
3660 strequal(name, dom_list[i].dns_name) )
3662 DEBUG(10,("wcache_tdc_fetch_domain: Found domain %s\n",
3665 d = TALLOC_P( ctx, struct winbindd_tdc_domain );
3669 d->domain_name = talloc_strdup( d, dom_list[i].domain_name );
3670 d->dns_name = talloc_strdup( d, dom_list[i].dns_name );
3671 sid_copy( &d->sid, &dom_list[i].sid );
3672 d->trust_flags = dom_list[i].trust_flags;
3673 d->trust_type = dom_list[i].trust_type;
3674 d->trust_attribs = dom_list[i].trust_attribs;
3680 TALLOC_FREE( dom_list );
3686 /*********************************************************************
3687 ********************************************************************/
3689 void wcache_tdc_clear( void )
3691 if ( !init_wcache() )
3694 wcache_tdc_store_list( NULL, 0 );
3700 /*********************************************************************
3701 ********************************************************************/
3703 static void wcache_save_user_pwinfo(struct winbindd_domain *domain,
3705 const DOM_SID *user_sid,
3706 const char *homedir,
3711 struct cache_entry *centry;
3713 if ( (centry = centry_start(domain, status)) == NULL )
3716 centry_put_string( centry, homedir );
3717 centry_put_string( centry, shell );
3718 centry_put_string( centry, gecos );
3719 centry_put_uint32( centry, gid );
3721 centry_end(centry, "NSS/PWINFO/%s", sid_string_static(user_sid) );
3723 DEBUG(10,("wcache_save_user_pwinfo: %s\n", sid_string_static(user_sid) ));
3725 centry_free(centry);
3728 NTSTATUS nss_get_info_cached( struct winbindd_domain *domain,
3729 const DOM_SID *user_sid,
3731 ADS_STRUCT *ads, LDAPMessage *msg,
3732 char **homedir, char **shell, char **gecos,
3735 struct winbind_cache *cache = get_cache(domain);
3736 struct cache_entry *centry = NULL;
3742 centry = wcache_fetch(cache, domain, "NSS/PWINFO/%s", sid_string_static(user_sid));
3747 *homedir = centry_string( centry, ctx );
3748 *shell = centry_string( centry, ctx );
3749 *gecos = centry_string( centry, ctx );
3750 *p_gid = centry_uint32( centry );
3752 centry_free(centry);
3754 DEBUG(10,("nss_get_info_cached: [Cached] - user_sid %s\n",
3755 sid_string_static(user_sid)));
3757 return NT_STATUS_OK;
3761 nt_status = nss_get_info( domain->name, user_sid, ctx, ads, msg,
3762 homedir, shell, gecos, p_gid );
3764 if ( NT_STATUS_IS_OK(nt_status) ) {
3765 wcache_save_user_pwinfo( domain, nt_status, user_sid,
3766 *homedir, *shell, *gecos, *p_gid );
3769 if ( NT_STATUS_EQUAL( nt_status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND ) ) {
3770 DEBUG(5,("nss_get_info_cached: Setting domain %s offline\n",
3772 set_domain_offline( domain );
3779 /* the cache backend methods are exposed via this structure */
3780 struct winbindd_methods cache_methods = {