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, ...)
806 va_start(ap, format);
807 smb_xvasprintf(&kstr, format, ap);
810 key = string_tdb_data(kstr);
811 data.dptr = centry->data;
812 data.dsize = centry->ofs;
814 tdb_store(wcache->tdb, key, data, TDB_REPLACE);
818 static void wcache_save_name_to_sid(struct winbindd_domain *domain,
819 NTSTATUS status, const char *domain_name,
820 const char *name, const DOM_SID *sid,
821 enum lsa_SidType type)
823 struct cache_entry *centry;
826 centry = centry_start(domain, status);
829 centry_put_uint32(centry, type);
830 centry_put_sid(centry, sid);
831 fstrcpy(uname, name);
833 centry_end(centry, "NS/%s/%s", domain_name, uname);
834 DEBUG(10,("wcache_save_name_to_sid: %s\\%s -> %s (%s)\n", domain_name, uname,
835 sid_string_static(sid), nt_errstr(status)));
839 static void wcache_save_sid_to_name(struct winbindd_domain *domain, NTSTATUS status,
840 const DOM_SID *sid, const char *domain_name, const char *name, enum lsa_SidType type)
842 struct cache_entry *centry;
845 if (is_null_sid(sid)) {
849 centry = centry_start(domain, status);
852 if (NT_STATUS_IS_OK(status)) {
853 centry_put_uint32(centry, type);
854 centry_put_string(centry, domain_name);
855 centry_put_string(centry, name);
857 centry_end(centry, "SN/%s", sid_to_string(sid_string, sid));
858 DEBUG(10,("wcache_save_sid_to_name: %s -> %s (%s)\n", sid_string,
859 name, nt_errstr(status)));
864 static void wcache_save_user(struct winbindd_domain *domain, NTSTATUS status, WINBIND_USERINFO *info)
866 struct cache_entry *centry;
869 if (is_null_sid(&info->user_sid)) {
873 centry = centry_start(domain, status);
876 centry_put_string(centry, info->acct_name);
877 centry_put_string(centry, info->full_name);
878 centry_put_string(centry, info->homedir);
879 centry_put_string(centry, info->shell);
880 centry_put_uint32(centry, info->primary_gid);
881 centry_put_sid(centry, &info->user_sid);
882 centry_put_sid(centry, &info->group_sid);
883 centry_end(centry, "U/%s", sid_to_string(sid_string, &info->user_sid));
884 DEBUG(10,("wcache_save_user: %s (acct_name %s)\n", sid_string, info->acct_name));
888 static void wcache_save_lockout_policy(struct winbindd_domain *domain, NTSTATUS status, SAM_UNK_INFO_12 *lockout_policy)
890 struct cache_entry *centry;
892 centry = centry_start(domain, status);
896 centry_put_nttime(centry, lockout_policy->duration);
897 centry_put_nttime(centry, lockout_policy->reset_count);
898 centry_put_uint16(centry, lockout_policy->bad_attempt_lockout);
900 centry_end(centry, "LOC_POL/%s", domain->name);
902 DEBUG(10,("wcache_save_lockout_policy: %s\n", domain->name));
907 static void wcache_save_password_policy(struct winbindd_domain *domain, NTSTATUS status, SAM_UNK_INFO_1 *policy)
909 struct cache_entry *centry;
911 centry = centry_start(domain, status);
915 centry_put_uint16(centry, policy->min_length_password);
916 centry_put_uint16(centry, policy->password_history);
917 centry_put_uint32(centry, policy->password_properties);
918 centry_put_nttime(centry, policy->expire);
919 centry_put_nttime(centry, policy->min_passwordage);
921 centry_end(centry, "PWD_POL/%s", domain->name);
923 DEBUG(10,("wcache_save_password_policy: %s\n", domain->name));
928 NTSTATUS wcache_cached_creds_exist(struct winbindd_domain *domain, const DOM_SID *sid)
930 struct winbind_cache *cache = get_cache(domain);
936 return NT_STATUS_INTERNAL_DB_ERROR;
939 if (is_null_sid(sid)) {
940 return NT_STATUS_INVALID_SID;
943 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
944 return NT_STATUS_INVALID_SID;
947 fstr_sprintf(key_str, "CRED/%s", sid_string_static(sid));
949 data = tdb_fetch(cache->tdb, string_tdb_data(key_str));
951 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
954 SAFE_FREE(data.dptr);
958 /* Lookup creds for a SID - copes with old (unsalted) creds as well
959 as new salted ones. */
961 NTSTATUS wcache_get_creds(struct winbindd_domain *domain,
964 const uint8 **cached_nt_pass,
965 const uint8 **cached_salt)
967 struct winbind_cache *cache = get_cache(domain);
968 struct cache_entry *centry = NULL;
974 return NT_STATUS_INTERNAL_DB_ERROR;
977 if (is_null_sid(sid)) {
978 return NT_STATUS_INVALID_SID;
981 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
982 return NT_STATUS_INVALID_SID;
985 /* Try and get a salted cred first. If we can't
986 fall back to an unsalted cred. */
988 centry = wcache_fetch(cache, domain, "CRED/%s", sid_string_static(sid));
990 DEBUG(10,("wcache_get_creds: entry for [CRED/%s] not found\n",
991 sid_string_static(sid)));
992 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
995 t = centry_time(centry);
997 /* In the salted case this isn't actually the nt_hash itself,
998 but the MD5 of the salt + nt_hash. Let the caller
999 sort this out. It can tell as we only return the cached_salt
1000 if we are returning a salted cred. */
1002 *cached_nt_pass = (const uint8 *)centry_hash16(centry, mem_ctx);
1003 if (*cached_nt_pass == NULL) {
1004 const char *sidstr = sid_string_static(sid);
1006 /* Bad (old) cred cache. Delete and pretend we
1008 DEBUG(0,("wcache_get_creds: bad entry for [CRED/%s] - deleting\n",
1010 wcache_delete("CRED/%s", sidstr);
1011 centry_free(centry);
1012 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1015 /* We only have 17 bytes more data in the salted cred case. */
1016 if (centry->len - centry->ofs == 17) {
1017 *cached_salt = (const uint8 *)centry_hash16(centry, mem_ctx);
1019 *cached_salt = NULL;
1022 dump_data_pw("cached_nt_pass", *cached_nt_pass, NT_HASH_LEN);
1024 dump_data_pw("cached_salt", *cached_salt, NT_HASH_LEN);
1027 status = centry->status;
1029 DEBUG(10,("wcache_get_creds: [Cached] - cached creds for user %s status: %s\n",
1030 sid_string_static(sid), nt_errstr(status) ));
1032 centry_free(centry);
1036 /* Store creds for a SID - only writes out new salted ones. */
1038 NTSTATUS wcache_save_creds(struct winbindd_domain *domain,
1039 TALLOC_CTX *mem_ctx,
1041 const uint8 nt_pass[NT_HASH_LEN])
1043 struct cache_entry *centry;
1046 uint8 cred_salt[NT_HASH_LEN];
1047 uint8 salted_hash[NT_HASH_LEN];
1049 if (is_null_sid(sid)) {
1050 return NT_STATUS_INVALID_SID;
1053 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1054 return NT_STATUS_INVALID_SID;
1057 centry = centry_start(domain, NT_STATUS_OK);
1059 return NT_STATUS_INTERNAL_DB_ERROR;
1062 dump_data_pw("nt_pass", nt_pass, NT_HASH_LEN);
1064 centry_put_time(centry, time(NULL));
1066 /* Create a salt and then salt the hash. */
1067 generate_random_buffer(cred_salt, NT_HASH_LEN);
1068 E_md5hash(cred_salt, nt_pass, salted_hash);
1070 centry_put_hash16(centry, salted_hash);
1071 centry_put_hash16(centry, cred_salt);
1072 centry_end(centry, "CRED/%s", sid_to_string(sid_string, sid));
1074 DEBUG(10,("wcache_save_creds: %s\n", sid_string));
1076 centry_free(centry);
1078 return NT_STATUS_OK;
1082 /* Query display info. This is the basic user list fn */
1083 static NTSTATUS query_user_list(struct winbindd_domain *domain,
1084 TALLOC_CTX *mem_ctx,
1085 uint32 *num_entries,
1086 WINBIND_USERINFO **info)
1088 struct winbind_cache *cache = get_cache(domain);
1089 struct cache_entry *centry = NULL;
1091 unsigned int i, retry;
1096 centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
1100 *num_entries = centry_uint32(centry);
1102 if (*num_entries == 0)
1105 (*info) = TALLOC_ARRAY(mem_ctx, WINBIND_USERINFO, *num_entries);
1107 smb_panic_fn("query_user_list out of memory");
1109 for (i=0; i<(*num_entries); i++) {
1110 (*info)[i].acct_name = centry_string(centry, mem_ctx);
1111 (*info)[i].full_name = centry_string(centry, mem_ctx);
1112 (*info)[i].homedir = centry_string(centry, mem_ctx);
1113 (*info)[i].shell = centry_string(centry, mem_ctx);
1114 centry_sid(centry, mem_ctx, &(*info)[i].user_sid);
1115 centry_sid(centry, mem_ctx, &(*info)[i].group_sid);
1119 status = centry->status;
1121 DEBUG(10,("query_user_list: [Cached] - cached list for domain %s status: %s\n",
1122 domain->name, nt_errstr(status) ));
1124 centry_free(centry);
1131 /* Return status value returned by seq number check */
1133 if (!NT_STATUS_IS_OK(domain->last_status))
1134 return domain->last_status;
1136 /* Put the query_user_list() in a retry loop. There appears to be
1137 * some bug either with Windows 2000 or Samba's handling of large
1138 * rpc replies. This manifests itself as sudden disconnection
1139 * at a random point in the enumeration of a large (60k) user list.
1140 * The retry loop simply tries the operation again. )-: It's not
1141 * pretty but an acceptable workaround until we work out what the
1142 * real problem is. */
1147 DEBUG(10,("query_user_list: [Cached] - doing backend query for list for domain %s\n",
1150 status = domain->backend->query_user_list(domain, mem_ctx, num_entries, info);
1151 if (!NT_STATUS_IS_OK(status)) {
1152 DEBUG(3, ("query_user_list: returned 0x%08x, "
1153 "retrying\n", NT_STATUS_V(status)));
1155 if (NT_STATUS_EQUAL(status, NT_STATUS_UNSUCCESSFUL)) {
1156 DEBUG(3, ("query_user_list: flushing "
1157 "connection cache\n"));
1158 invalidate_cm_connection(&domain->conn);
1161 } while (NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_UNSUCCESSFUL) &&
1165 refresh_sequence_number(domain, False);
1166 centry = centry_start(domain, status);
1169 centry_put_uint32(centry, *num_entries);
1170 for (i=0; i<(*num_entries); i++) {
1171 centry_put_string(centry, (*info)[i].acct_name);
1172 centry_put_string(centry, (*info)[i].full_name);
1173 centry_put_string(centry, (*info)[i].homedir);
1174 centry_put_string(centry, (*info)[i].shell);
1175 centry_put_sid(centry, &(*info)[i].user_sid);
1176 centry_put_sid(centry, &(*info)[i].group_sid);
1177 if (domain->backend && domain->backend->consistent) {
1178 /* when the backend is consistent we can pre-prime some mappings */
1179 wcache_save_name_to_sid(domain, NT_STATUS_OK,
1181 (*info)[i].acct_name,
1182 &(*info)[i].user_sid,
1184 wcache_save_sid_to_name(domain, NT_STATUS_OK,
1185 &(*info)[i].user_sid,
1187 (*info)[i].acct_name,
1189 wcache_save_user(domain, NT_STATUS_OK, &(*info)[i]);
1192 centry_end(centry, "UL/%s", domain->name);
1193 centry_free(centry);
1199 /* list all domain groups */
1200 static NTSTATUS enum_dom_groups(struct winbindd_domain *domain,
1201 TALLOC_CTX *mem_ctx,
1202 uint32 *num_entries,
1203 struct acct_info **info)
1205 struct winbind_cache *cache = get_cache(domain);
1206 struct cache_entry *centry = NULL;
1213 centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
1217 *num_entries = centry_uint32(centry);
1219 if (*num_entries == 0)
1222 (*info) = TALLOC_ARRAY(mem_ctx, struct acct_info, *num_entries);
1224 smb_panic_fn("enum_dom_groups out of memory");
1226 for (i=0; i<(*num_entries); i++) {
1227 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1228 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1229 (*info)[i].rid = centry_uint32(centry);
1233 status = centry->status;
1235 DEBUG(10,("enum_dom_groups: [Cached] - cached list for domain %s status: %s\n",
1236 domain->name, nt_errstr(status) ));
1238 centry_free(centry);
1245 /* Return status value returned by seq number check */
1247 if (!NT_STATUS_IS_OK(domain->last_status))
1248 return domain->last_status;
1250 DEBUG(10,("enum_dom_groups: [Cached] - doing backend query for list for domain %s\n",
1253 status = domain->backend->enum_dom_groups(domain, mem_ctx, num_entries, info);
1256 refresh_sequence_number(domain, False);
1257 centry = centry_start(domain, status);
1260 centry_put_uint32(centry, *num_entries);
1261 for (i=0; i<(*num_entries); i++) {
1262 centry_put_string(centry, (*info)[i].acct_name);
1263 centry_put_string(centry, (*info)[i].acct_desc);
1264 centry_put_uint32(centry, (*info)[i].rid);
1266 centry_end(centry, "GL/%s/domain", domain->name);
1267 centry_free(centry);
1273 /* list all domain groups */
1274 static NTSTATUS enum_local_groups(struct winbindd_domain *domain,
1275 TALLOC_CTX *mem_ctx,
1276 uint32 *num_entries,
1277 struct acct_info **info)
1279 struct winbind_cache *cache = get_cache(domain);
1280 struct cache_entry *centry = NULL;
1287 centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
1291 *num_entries = centry_uint32(centry);
1293 if (*num_entries == 0)
1296 (*info) = TALLOC_ARRAY(mem_ctx, struct acct_info, *num_entries);
1298 smb_panic_fn("enum_dom_groups out of memory");
1300 for (i=0; i<(*num_entries); i++) {
1301 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1302 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1303 (*info)[i].rid = centry_uint32(centry);
1308 /* If we are returning cached data and the domain controller
1309 is down then we don't know whether the data is up to date
1310 or not. Return NT_STATUS_MORE_PROCESSING_REQUIRED to
1313 if (wcache_server_down(domain)) {
1314 DEBUG(10, ("enum_local_groups: returning cached user list and server was down\n"));
1315 status = NT_STATUS_MORE_PROCESSING_REQUIRED;
1317 status = centry->status;
1319 DEBUG(10,("enum_local_groups: [Cached] - cached list for domain %s status: %s\n",
1320 domain->name, nt_errstr(status) ));
1322 centry_free(centry);
1329 /* Return status value returned by seq number check */
1331 if (!NT_STATUS_IS_OK(domain->last_status))
1332 return domain->last_status;
1334 DEBUG(10,("enum_local_groups: [Cached] - doing backend query for list for domain %s\n",
1337 status = domain->backend->enum_local_groups(domain, mem_ctx, num_entries, info);
1340 refresh_sequence_number(domain, False);
1341 centry = centry_start(domain, status);
1344 centry_put_uint32(centry, *num_entries);
1345 for (i=0; i<(*num_entries); i++) {
1346 centry_put_string(centry, (*info)[i].acct_name);
1347 centry_put_string(centry, (*info)[i].acct_desc);
1348 centry_put_uint32(centry, (*info)[i].rid);
1350 centry_end(centry, "GL/%s/local", domain->name);
1351 centry_free(centry);
1357 /* convert a single name to a sid in a domain */
1358 static NTSTATUS name_to_sid(struct winbindd_domain *domain,
1359 TALLOC_CTX *mem_ctx,
1360 enum winbindd_cmd orig_cmd,
1361 const char *domain_name,
1364 enum lsa_SidType *type)
1366 struct winbind_cache *cache = get_cache(domain);
1367 struct cache_entry *centry = NULL;
1374 fstrcpy(uname, name);
1376 centry = wcache_fetch(cache, domain, "NS/%s/%s", domain_name, uname);
1379 *type = (enum lsa_SidType)centry_uint32(centry);
1380 status = centry->status;
1381 if (NT_STATUS_IS_OK(status)) {
1382 centry_sid(centry, mem_ctx, sid);
1385 DEBUG(10,("name_to_sid: [Cached] - cached name for domain %s status: %s\n",
1386 domain->name, nt_errstr(status) ));
1388 centry_free(centry);
1394 /* If the seq number check indicated that there is a problem
1395 * with this DC, then return that status... except for
1396 * access_denied. This is special because the dc may be in
1397 * "restrict anonymous = 1" mode, in which case it will deny
1398 * most unauthenticated operations, but *will* allow the LSA
1399 * name-to-sid that we try as a fallback. */
1401 if (!(NT_STATUS_IS_OK(domain->last_status)
1402 || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1403 return domain->last_status;
1405 DEBUG(10,("name_to_sid: [Cached] - doing backend query for name for domain %s\n",
1408 status = domain->backend->name_to_sid(domain, mem_ctx, orig_cmd,
1409 domain_name, name, sid, type);
1412 refresh_sequence_number(domain, False);
1414 if (domain->online && !is_null_sid(sid)) {
1415 wcache_save_name_to_sid(domain, status, domain_name, name, sid, *type);
1418 /* Only save the reverse mapping if this was not a UPN */
1419 if (NT_STATUS_IS_OK(status) && !strchr(name, '@')) {
1420 strupper_m(CONST_DISCARD(char *,domain_name));
1421 strlower_m(CONST_DISCARD(char *,name));
1422 wcache_save_sid_to_name(domain, status, sid, domain_name, name, *type);
1428 /* convert a sid to a user or group name. The sid is guaranteed to be in the domain
1430 static NTSTATUS sid_to_name(struct winbindd_domain *domain,
1431 TALLOC_CTX *mem_ctx,
1435 enum lsa_SidType *type)
1437 struct winbind_cache *cache = get_cache(domain);
1438 struct cache_entry *centry = NULL;
1445 centry = wcache_fetch(cache, domain, "SN/%s", sid_to_string(sid_string, sid));
1448 if (NT_STATUS_IS_OK(centry->status)) {
1449 *type = (enum lsa_SidType)centry_uint32(centry);
1450 *domain_name = centry_string(centry, mem_ctx);
1451 *name = centry_string(centry, mem_ctx);
1453 status = centry->status;
1455 DEBUG(10,("sid_to_name: [Cached] - cached name for domain %s status: %s\n",
1456 domain->name, nt_errstr(status) ));
1458 centry_free(centry);
1463 *domain_name = NULL;
1465 /* If the seq number check indicated that there is a problem
1466 * with this DC, then return that status... except for
1467 * access_denied. This is special because the dc may be in
1468 * "restrict anonymous = 1" mode, in which case it will deny
1469 * most unauthenticated operations, but *will* allow the LSA
1470 * sid-to-name that we try as a fallback. */
1472 if (!(NT_STATUS_IS_OK(domain->last_status)
1473 || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1474 return domain->last_status;
1476 DEBUG(10,("sid_to_name: [Cached] - doing backend query for name for domain %s\n",
1479 status = domain->backend->sid_to_name(domain, mem_ctx, sid, domain_name, name, type);
1482 refresh_sequence_number(domain, False);
1483 wcache_save_sid_to_name(domain, status, sid, *domain_name, *name, *type);
1485 /* We can't save the name to sid mapping here, as with sid history a
1486 * later name2sid would give the wrong sid. */
1491 static NTSTATUS rids_to_names(struct winbindd_domain *domain,
1492 TALLOC_CTX *mem_ctx,
1493 const DOM_SID *domain_sid,
1498 enum lsa_SidType **types)
1500 struct winbind_cache *cache = get_cache(domain);
1502 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
1506 *domain_name = NULL;
1514 if (num_rids == 0) {
1515 return NT_STATUS_OK;
1518 *names = TALLOC_ARRAY(mem_ctx, char *, num_rids);
1519 *types = TALLOC_ARRAY(mem_ctx, enum lsa_SidType, num_rids);
1521 if ((*names == NULL) || (*types == NULL)) {
1522 result = NT_STATUS_NO_MEMORY;
1526 have_mapped = have_unmapped = False;
1528 for (i=0; i<num_rids; i++) {
1530 struct cache_entry *centry;
1532 if (!sid_compose(&sid, domain_sid, rids[i])) {
1533 result = NT_STATUS_INTERNAL_ERROR;
1537 centry = wcache_fetch(cache, domain, "SN/%s",
1538 sid_string_static(&sid));
1543 (*types)[i] = SID_NAME_UNKNOWN;
1544 (*names)[i] = talloc_strdup(*names, "");
1546 if (NT_STATUS_IS_OK(centry->status)) {
1549 (*types)[i] = (enum lsa_SidType)centry_uint32(centry);
1550 dom = centry_string(centry, mem_ctx);
1551 if (*domain_name == NULL) {
1556 (*names)[i] = centry_string(centry, *names);
1558 have_unmapped = True;
1561 centry_free(centry);
1565 return NT_STATUS_NONE_MAPPED;
1567 if (!have_unmapped) {
1568 return NT_STATUS_OK;
1570 return STATUS_SOME_UNMAPPED;
1574 TALLOC_FREE(*names);
1575 TALLOC_FREE(*types);
1577 result = domain->backend->rids_to_names(domain, mem_ctx, domain_sid,
1578 rids, num_rids, domain_name,
1581 if (!NT_STATUS_IS_OK(result) &&
1582 !NT_STATUS_EQUAL(result, STATUS_SOME_UNMAPPED)) {
1586 refresh_sequence_number(domain, False);
1588 for (i=0; i<num_rids; i++) {
1592 if (!sid_compose(&sid, domain_sid, rids[i])) {
1593 result = NT_STATUS_INTERNAL_ERROR;
1597 status = (*types)[i] == SID_NAME_UNKNOWN ?
1598 NT_STATUS_NONE_MAPPED : NT_STATUS_OK;
1600 wcache_save_sid_to_name(domain, status, &sid, *domain_name,
1601 (*names)[i], (*types)[i]);
1608 TALLOC_FREE(*names);
1609 TALLOC_FREE(*types);
1613 /* Lookup user information from a rid */
1614 static NTSTATUS query_user(struct winbindd_domain *domain,
1615 TALLOC_CTX *mem_ctx,
1616 const DOM_SID *user_sid,
1617 WINBIND_USERINFO *info)
1619 struct winbind_cache *cache = get_cache(domain);
1620 struct cache_entry *centry = NULL;
1626 centry = wcache_fetch(cache, domain, "U/%s", sid_string_static(user_sid));
1628 /* If we have an access denied cache entry and a cached info3 in the
1629 samlogon cache then do a query. This will force the rpc back end
1630 to return the info3 data. */
1632 if (NT_STATUS_V(domain->last_status) == NT_STATUS_V(NT_STATUS_ACCESS_DENIED) &&
1633 netsamlogon_cache_have(user_sid)) {
1634 DEBUG(10, ("query_user: cached access denied and have cached info3\n"));
1635 domain->last_status = NT_STATUS_OK;
1636 centry_free(centry);
1643 info->acct_name = centry_string(centry, mem_ctx);
1644 info->full_name = centry_string(centry, mem_ctx);
1645 info->homedir = centry_string(centry, mem_ctx);
1646 info->shell = centry_string(centry, mem_ctx);
1647 info->primary_gid = centry_uint32(centry);
1648 centry_sid(centry, mem_ctx, &info->user_sid);
1649 centry_sid(centry, mem_ctx, &info->group_sid);
1650 status = centry->status;
1652 DEBUG(10,("query_user: [Cached] - cached info for domain %s status: %s\n",
1653 domain->name, nt_errstr(status) ));
1655 centry_free(centry);
1661 /* Return status value returned by seq number check */
1663 if (!NT_STATUS_IS_OK(domain->last_status))
1664 return domain->last_status;
1666 DEBUG(10,("query_user: [Cached] - doing backend query for info for domain %s\n",
1669 status = domain->backend->query_user(domain, mem_ctx, user_sid, info);
1672 refresh_sequence_number(domain, False);
1673 wcache_save_user(domain, status, info);
1679 /* Lookup groups a user is a member of. */
1680 static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
1681 TALLOC_CTX *mem_ctx,
1682 const DOM_SID *user_sid,
1683 uint32 *num_groups, DOM_SID **user_gids)
1685 struct winbind_cache *cache = get_cache(domain);
1686 struct cache_entry *centry = NULL;
1694 centry = wcache_fetch(cache, domain, "UG/%s", sid_to_string(sid_string, user_sid));
1696 /* If we have an access denied cache entry and a cached info3 in the
1697 samlogon cache then do a query. This will force the rpc back end
1698 to return the info3 data. */
1700 if (NT_STATUS_V(domain->last_status) == NT_STATUS_V(NT_STATUS_ACCESS_DENIED) &&
1701 netsamlogon_cache_have(user_sid)) {
1702 DEBUG(10, ("lookup_usergroups: cached access denied and have cached info3\n"));
1703 domain->last_status = NT_STATUS_OK;
1704 centry_free(centry);
1711 *num_groups = centry_uint32(centry);
1713 if (*num_groups == 0)
1716 (*user_gids) = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_groups);
1717 if (! (*user_gids)) {
1718 smb_panic_fn("lookup_usergroups out of memory");
1720 for (i=0; i<(*num_groups); i++) {
1721 centry_sid(centry, mem_ctx, &(*user_gids)[i]);
1725 status = centry->status;
1727 DEBUG(10,("lookup_usergroups: [Cached] - cached info for domain %s status: %s\n",
1728 domain->name, nt_errstr(status) ));
1730 centry_free(centry);
1735 (*user_gids) = NULL;
1737 /* Return status value returned by seq number check */
1739 if (!NT_STATUS_IS_OK(domain->last_status))
1740 return domain->last_status;
1742 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info for domain %s\n",
1745 status = domain->backend->lookup_usergroups(domain, mem_ctx, user_sid, num_groups, user_gids);
1747 if ( NT_STATUS_EQUAL(status, NT_STATUS_SYNCHRONIZATION_REQUIRED) )
1751 refresh_sequence_number(domain, False);
1752 centry = centry_start(domain, status);
1755 centry_put_uint32(centry, *num_groups);
1756 for (i=0; i<(*num_groups); i++) {
1757 centry_put_sid(centry, &(*user_gids)[i]);
1759 centry_end(centry, "UG/%s", sid_to_string(sid_string, user_sid));
1760 centry_free(centry);
1766 static NTSTATUS lookup_useraliases(struct winbindd_domain *domain,
1767 TALLOC_CTX *mem_ctx,
1768 uint32 num_sids, const DOM_SID *sids,
1769 uint32 *num_aliases, uint32 **alias_rids)
1771 struct winbind_cache *cache = get_cache(domain);
1772 struct cache_entry *centry = NULL;
1774 char *sidlist = talloc_strdup(mem_ctx, "");
1780 if (num_sids == 0) {
1783 return NT_STATUS_OK;
1786 /* We need to cache indexed by the whole list of SIDs, the aliases
1787 * resulting might come from any of the SIDs. */
1789 for (i=0; i<num_sids; i++) {
1790 sidlist = talloc_asprintf(mem_ctx, "%s/%s", sidlist,
1791 sid_string_static(&sids[i]));
1792 if (sidlist == NULL)
1793 return NT_STATUS_NO_MEMORY;
1796 centry = wcache_fetch(cache, domain, "UA%s", sidlist);
1801 *num_aliases = centry_uint32(centry);
1805 (*alias_rids) = TALLOC_ARRAY(mem_ctx, uint32, *num_aliases);
1807 if ((*alias_rids) == NULL) {
1808 centry_free(centry);
1809 return NT_STATUS_NO_MEMORY;
1812 (*alias_rids) = NULL;
1815 for (i=0; i<(*num_aliases); i++)
1816 (*alias_rids)[i] = centry_uint32(centry);
1818 status = centry->status;
1820 DEBUG(10,("lookup_useraliases: [Cached] - cached info for domain: %s "
1821 "status %s\n", domain->name, nt_errstr(status)));
1823 centry_free(centry);
1828 (*alias_rids) = NULL;
1830 if (!NT_STATUS_IS_OK(domain->last_status))
1831 return domain->last_status;
1833 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info "
1834 "for domain %s\n", domain->name ));
1836 status = domain->backend->lookup_useraliases(domain, mem_ctx,
1838 num_aliases, alias_rids);
1841 refresh_sequence_number(domain, False);
1842 centry = centry_start(domain, status);
1845 centry_put_uint32(centry, *num_aliases);
1846 for (i=0; i<(*num_aliases); i++)
1847 centry_put_uint32(centry, (*alias_rids)[i]);
1848 centry_end(centry, "UA%s", sidlist);
1849 centry_free(centry);
1856 static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
1857 TALLOC_CTX *mem_ctx,
1858 const DOM_SID *group_sid, uint32 *num_names,
1859 DOM_SID **sid_mem, char ***names,
1860 uint32 **name_types)
1862 struct winbind_cache *cache = get_cache(domain);
1863 struct cache_entry *centry = NULL;
1871 centry = wcache_fetch(cache, domain, "GM/%s", sid_to_string(sid_string, group_sid));
1875 *num_names = centry_uint32(centry);
1877 if (*num_names == 0)
1880 (*sid_mem) = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_names);
1881 (*names) = TALLOC_ARRAY(mem_ctx, char *, *num_names);
1882 (*name_types) = TALLOC_ARRAY(mem_ctx, uint32, *num_names);
1884 if (! (*sid_mem) || ! (*names) || ! (*name_types)) {
1885 smb_panic_fn("lookup_groupmem out of memory");
1888 for (i=0; i<(*num_names); i++) {
1889 centry_sid(centry, mem_ctx, &(*sid_mem)[i]);
1890 (*names)[i] = centry_string(centry, mem_ctx);
1891 (*name_types)[i] = centry_uint32(centry);
1895 status = centry->status;
1897 DEBUG(10,("lookup_groupmem: [Cached] - cached info for domain %s status: %s\n",
1898 domain->name, nt_errstr(status)));
1900 centry_free(centry);
1907 (*name_types) = NULL;
1909 /* Return status value returned by seq number check */
1911 if (!NT_STATUS_IS_OK(domain->last_status))
1912 return domain->last_status;
1914 DEBUG(10,("lookup_groupmem: [Cached] - doing backend query for info for domain %s\n",
1917 status = domain->backend->lookup_groupmem(domain, mem_ctx, group_sid, num_names,
1918 sid_mem, names, name_types);
1921 refresh_sequence_number(domain, False);
1922 centry = centry_start(domain, status);
1925 centry_put_uint32(centry, *num_names);
1926 for (i=0; i<(*num_names); i++) {
1927 centry_put_sid(centry, &(*sid_mem)[i]);
1928 centry_put_string(centry, (*names)[i]);
1929 centry_put_uint32(centry, (*name_types)[i]);
1931 centry_end(centry, "GM/%s", sid_to_string(sid_string, group_sid));
1932 centry_free(centry);
1938 /* find the sequence number for a domain */
1939 static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
1941 refresh_sequence_number(domain, False);
1943 *seq = domain->sequence_number;
1945 return NT_STATUS_OK;
1948 /* enumerate trusted domains
1949 * (we need to have the list of trustdoms in the cache when we go offline) -
1951 static NTSTATUS trusted_domains(struct winbindd_domain *domain,
1952 TALLOC_CTX *mem_ctx,
1953 uint32 *num_domains,
1958 struct winbind_cache *cache = get_cache(domain);
1959 struct cache_entry *centry = NULL;
1966 centry = wcache_fetch(cache, domain, "TRUSTDOMS/%s", domain->name);
1972 *num_domains = centry_uint32(centry);
1975 (*names) = TALLOC_ARRAY(mem_ctx, char *, *num_domains);
1976 (*alt_names) = TALLOC_ARRAY(mem_ctx, char *, *num_domains);
1977 (*dom_sids) = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_domains);
1979 if (! (*dom_sids) || ! (*names) || ! (*alt_names)) {
1980 smb_panic_fn("trusted_domains out of memory");
1984 (*alt_names) = NULL;
1988 for (i=0; i<(*num_domains); i++) {
1989 (*names)[i] = centry_string(centry, mem_ctx);
1990 (*alt_names)[i] = centry_string(centry, mem_ctx);
1991 centry_sid(centry, mem_ctx, &(*dom_sids)[i]);
1994 status = centry->status;
1996 DEBUG(10,("trusted_domains: [Cached] - cached info for domain %s (%d trusts) status: %s\n",
1997 domain->name, *num_domains, nt_errstr(status) ));
1999 centry_free(centry);
2006 (*alt_names) = NULL;
2008 /* Return status value returned by seq number check */
2010 if (!NT_STATUS_IS_OK(domain->last_status))
2011 return domain->last_status;
2013 DEBUG(10,("trusted_domains: [Cached] - doing backend query for info for domain %s\n",
2016 status = domain->backend->trusted_domains(domain, mem_ctx, num_domains,
2017 names, alt_names, dom_sids);
2019 /* no trusts gives NT_STATUS_NO_MORE_ENTRIES resetting to NT_STATUS_OK
2020 * so that the generic centry handling still applies correctly -
2023 if (!NT_STATUS_IS_ERR(status)) {
2024 status = NT_STATUS_OK;
2028 #if 0 /* Disabled as we want the trust dom list to be managed by
2029 the main parent and always to make the query. --jerry */
2032 refresh_sequence_number(domain, False);
2034 centry = centry_start(domain, status);
2038 centry_put_uint32(centry, *num_domains);
2040 for (i=0; i<(*num_domains); i++) {
2041 centry_put_string(centry, (*names)[i]);
2042 centry_put_string(centry, (*alt_names)[i]);
2043 centry_put_sid(centry, &(*dom_sids)[i]);
2046 centry_end(centry, "TRUSTDOMS/%s", domain->name);
2048 centry_free(centry);
2056 /* get lockout policy */
2057 static NTSTATUS lockout_policy(struct winbindd_domain *domain,
2058 TALLOC_CTX *mem_ctx,
2059 SAM_UNK_INFO_12 *policy){
2060 struct winbind_cache *cache = get_cache(domain);
2061 struct cache_entry *centry = NULL;
2067 centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
2072 policy->duration = centry_nttime(centry);
2073 policy->reset_count = centry_nttime(centry);
2074 policy->bad_attempt_lockout = centry_uint16(centry);
2076 status = centry->status;
2078 DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2079 domain->name, nt_errstr(status) ));
2081 centry_free(centry);
2085 ZERO_STRUCTP(policy);
2087 /* Return status value returned by seq number check */
2089 if (!NT_STATUS_IS_OK(domain->last_status))
2090 return domain->last_status;
2092 DEBUG(10,("lockout_policy: [Cached] - doing backend query for info for domain %s\n",
2095 status = domain->backend->lockout_policy(domain, mem_ctx, policy);
2098 refresh_sequence_number(domain, False);
2099 wcache_save_lockout_policy(domain, status, policy);
2104 /* get password policy */
2105 static NTSTATUS password_policy(struct winbindd_domain *domain,
2106 TALLOC_CTX *mem_ctx,
2107 SAM_UNK_INFO_1 *policy)
2109 struct winbind_cache *cache = get_cache(domain);
2110 struct cache_entry *centry = NULL;
2116 centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
2121 policy->min_length_password = centry_uint16(centry);
2122 policy->password_history = centry_uint16(centry);
2123 policy->password_properties = centry_uint32(centry);
2124 policy->expire = centry_nttime(centry);
2125 policy->min_passwordage = centry_nttime(centry);
2127 status = centry->status;
2129 DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2130 domain->name, nt_errstr(status) ));
2132 centry_free(centry);
2136 ZERO_STRUCTP(policy);
2138 /* Return status value returned by seq number check */
2140 if (!NT_STATUS_IS_OK(domain->last_status))
2141 return domain->last_status;
2143 DEBUG(10,("password_policy: [Cached] - doing backend query for info for domain %s\n",
2146 status = domain->backend->password_policy(domain, mem_ctx, policy);
2149 refresh_sequence_number(domain, False);
2150 wcache_save_password_policy(domain, status, policy);
2156 /* Invalidate cached user and group lists coherently */
2158 static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
2161 if (strncmp((const char *)kbuf.dptr, "UL/", 3) == 0 ||
2162 strncmp((const char *)kbuf.dptr, "GL/", 3) == 0)
2163 tdb_delete(the_tdb, kbuf);
2168 /* Invalidate the getpwnam and getgroups entries for a winbindd domain */
2170 void wcache_invalidate_samlogon(struct winbindd_domain *domain,
2171 NET_USER_INFO_3 *info3)
2173 struct winbind_cache *cache;
2175 /* dont clear cached U/SID and UG/SID entries when we want to logon
2178 if (lp_winbind_offline_logon()) {
2185 cache = get_cache(domain);
2186 netsamlogon_clear_cached_user(cache->tdb, info3);
2189 void wcache_invalidate_cache(void)
2191 struct winbindd_domain *domain;
2193 for (domain = domain_list(); domain; domain = domain->next) {
2194 struct winbind_cache *cache = get_cache(domain);
2196 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
2197 "entries for %s\n", domain->name));
2199 tdb_traverse(cache->tdb, traverse_fn, NULL);
2203 BOOL init_wcache(void)
2205 if (wcache == NULL) {
2206 wcache = SMB_XMALLOC_P(struct winbind_cache);
2207 ZERO_STRUCTP(wcache);
2210 if (wcache->tdb != NULL)
2213 /* when working offline we must not clear the cache on restart */
2214 wcache->tdb = tdb_open_log(lock_path("winbindd_cache.tdb"),
2215 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
2216 lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST),
2217 O_RDWR|O_CREAT, 0600);
2219 if (wcache->tdb == NULL) {
2220 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
2227 /************************************************************************
2228 This is called by the parent to initialize the cache file.
2229 We don't need sophisticated locking here as we know we're the
2231 ************************************************************************/
2233 BOOL initialize_winbindd_cache(void)
2235 BOOL cache_bad = True;
2238 if (!init_wcache()) {
2239 DEBUG(0,("initialize_winbindd_cache: init_wcache failed.\n"));
2243 /* Check version number. */
2244 if (tdb_fetch_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, &vers) &&
2245 vers == WINBINDD_CACHE_VERSION) {
2250 DEBUG(0,("initialize_winbindd_cache: clearing cache "
2251 "and re-creating with version number %d\n",
2252 WINBINDD_CACHE_VERSION ));
2254 tdb_close(wcache->tdb);
2257 if (unlink(lock_path("winbindd_cache.tdb")) == -1) {
2258 DEBUG(0,("initialize_winbindd_cache: unlink %s failed %s ",
2259 lock_path("winbindd_cache.tdb"),
2263 if (!init_wcache()) {
2264 DEBUG(0,("initialize_winbindd_cache: re-initialization "
2265 "init_wcache failed.\n"));
2269 /* Write the version. */
2270 if (!tdb_store_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, WINBINDD_CACHE_VERSION)) {
2271 DEBUG(0,("initialize_winbindd_cache: version number store failed %s\n",
2272 tdb_errorstr(wcache->tdb) ));
2277 tdb_close(wcache->tdb);
2282 void cache_store_response(pid_t pid, struct winbindd_response *response)
2289 DEBUG(10, ("Storing response for pid %d, len %d\n",
2290 pid, response->length));
2292 fstr_sprintf(key_str, "DR/%d", pid);
2293 if (tdb_store(wcache->tdb, string_tdb_data(key_str),
2294 make_tdb_data((uint8 *)response, sizeof(*response)),
2298 if (response->length == sizeof(*response))
2301 /* There's extra data */
2303 DEBUG(10, ("Storing extra data: len=%d\n",
2304 (int)(response->length - sizeof(*response))));
2306 fstr_sprintf(key_str, "DE/%d", pid);
2307 if (tdb_store(wcache->tdb, string_tdb_data(key_str),
2308 make_tdb_data((uint8 *)response->extra_data.data,
2309 response->length - sizeof(*response)),
2313 /* We could not store the extra data, make sure the tdb does not
2314 * contain a main record with wrong dangling extra data */
2316 fstr_sprintf(key_str, "DR/%d", pid);
2317 tdb_delete(wcache->tdb, string_tdb_data(key_str));
2322 BOOL cache_retrieve_response(pid_t pid, struct winbindd_response * response)
2330 DEBUG(10, ("Retrieving response for pid %d\n", pid));
2332 fstr_sprintf(key_str, "DR/%d", pid);
2333 data = tdb_fetch(wcache->tdb, string_tdb_data(key_str));
2335 if (data.dptr == NULL)
2338 if (data.dsize != sizeof(*response))
2341 memcpy(response, data.dptr, data.dsize);
2342 SAFE_FREE(data.dptr);
2344 if (response->length == sizeof(*response)) {
2345 response->extra_data.data = NULL;
2349 /* There's extra data */
2351 DEBUG(10, ("Retrieving extra data length=%d\n",
2352 (int)(response->length - sizeof(*response))));
2354 fstr_sprintf(key_str, "DE/%d", pid);
2355 data = tdb_fetch(wcache->tdb, string_tdb_data(key_str));
2357 if (data.dptr == NULL) {
2358 DEBUG(0, ("Did not find extra data\n"));
2362 if (data.dsize != (response->length - sizeof(*response))) {
2363 DEBUG(0, ("Invalid extra data length: %d\n", (int)data.dsize));
2364 SAFE_FREE(data.dptr);
2368 dump_data(11, (uint8 *)data.dptr, data.dsize);
2370 response->extra_data.data = data.dptr;
2374 void cache_cleanup_response(pid_t pid)
2381 fstr_sprintf(key_str, "DR/%d", pid);
2382 tdb_delete(wcache->tdb, string_tdb_data(key_str));
2384 fstr_sprintf(key_str, "DE/%d", pid);
2385 tdb_delete(wcache->tdb, string_tdb_data(key_str));
2391 BOOL lookup_cached_sid(TALLOC_CTX *mem_ctx, const DOM_SID *sid,
2392 char **domain_name, char **name,
2393 enum lsa_SidType *type)
2395 struct winbindd_domain *domain;
2396 struct winbind_cache *cache;
2397 struct cache_entry *centry = NULL;
2400 domain = find_lookup_domain_from_sid(sid);
2401 if (domain == NULL) {
2405 cache = get_cache(domain);
2407 if (cache->tdb == NULL) {
2411 centry = wcache_fetch(cache, domain, "SN/%s", sid_string_static(sid));
2412 if (centry == NULL) {
2416 if (NT_STATUS_IS_OK(centry->status)) {
2417 *type = (enum lsa_SidType)centry_uint32(centry);
2418 *domain_name = centry_string(centry, mem_ctx);
2419 *name = centry_string(centry, mem_ctx);
2422 status = centry->status;
2423 centry_free(centry);
2424 return NT_STATUS_IS_OK(status);
2427 BOOL lookup_cached_name(TALLOC_CTX *mem_ctx,
2428 const char *domain_name,
2431 enum lsa_SidType *type)
2433 struct winbindd_domain *domain;
2434 struct winbind_cache *cache;
2435 struct cache_entry *centry = NULL;
2438 BOOL original_online_state;
2440 domain = find_lookup_domain_from_name(domain_name);
2441 if (domain == NULL) {
2445 cache = get_cache(domain);
2447 if (cache->tdb == NULL) {
2451 fstrcpy(uname, name);
2454 /* If we are doing a cached logon, temporarily set the domain
2455 offline so the cache won't expire the entry */
2457 original_online_state = domain->online;
2458 domain->online = False;
2459 centry = wcache_fetch(cache, domain, "NS/%s/%s", domain_name, uname);
2460 domain->online = original_online_state;
2462 if (centry == NULL) {
2466 if (NT_STATUS_IS_OK(centry->status)) {
2467 *type = (enum lsa_SidType)centry_uint32(centry);
2468 centry_sid(centry, mem_ctx, sid);
2471 status = centry->status;
2472 centry_free(centry);
2474 return NT_STATUS_IS_OK(status);
2477 void cache_name2sid(struct winbindd_domain *domain,
2478 const char *domain_name, const char *name,
2479 enum lsa_SidType type, const DOM_SID *sid)
2481 refresh_sequence_number(domain, False);
2482 wcache_save_name_to_sid(domain, NT_STATUS_OK, domain_name, name,
2487 * The original idea that this cache only contains centries has
2488 * been blurred - now other stuff gets put in here. Ensure we
2489 * ignore these things on cleanup.
2492 static int traverse_fn_cleanup(TDB_CONTEXT *the_tdb, TDB_DATA kbuf,
2493 TDB_DATA dbuf, void *state)
2495 struct cache_entry *centry;
2497 if (is_non_centry_key(kbuf)) {
2501 centry = wcache_fetch_raw((char *)kbuf.dptr);
2506 if (!NT_STATUS_IS_OK(centry->status)) {
2507 DEBUG(10,("deleting centry %s\n", (const char *)kbuf.dptr));
2508 tdb_delete(the_tdb, kbuf);
2511 centry_free(centry);
2515 /* flush the cache */
2516 void wcache_flush_cache(void)
2521 tdb_close(wcache->tdb);
2527 /* when working offline we must not clear the cache on restart */
2528 wcache->tdb = tdb_open_log(lock_path("winbindd_cache.tdb"),
2529 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
2530 lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST),
2531 O_RDWR|O_CREAT, 0600);
2534 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
2538 tdb_traverse(wcache->tdb, traverse_fn_cleanup, NULL);
2540 DEBUG(10,("wcache_flush_cache success\n"));
2543 /* Count cached creds */
2545 static int traverse_fn_cached_creds(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
2548 int *cred_count = (int*)state;
2550 if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
2556 NTSTATUS wcache_count_cached_creds(struct winbindd_domain *domain, int *count)
2558 struct winbind_cache *cache = get_cache(domain);
2563 return NT_STATUS_INTERNAL_DB_ERROR;
2566 tdb_traverse(cache->tdb, traverse_fn_cached_creds, (void *)count);
2568 return NT_STATUS_OK;
2572 struct cred_list *prev, *next;
2577 static struct cred_list *wcache_cred_list;
2579 static int traverse_fn_get_credlist(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
2582 struct cred_list *cred;
2584 if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
2586 cred = SMB_MALLOC_P(struct cred_list);
2588 DEBUG(0,("traverse_fn_remove_first_creds: failed to malloc new entry for list\n"));
2594 /* save a copy of the key */
2596 fstrcpy(cred->name, (const char *)kbuf.dptr);
2597 DLIST_ADD(wcache_cred_list, cred);
2603 NTSTATUS wcache_remove_oldest_cached_creds(struct winbindd_domain *domain, const DOM_SID *sid)
2605 struct winbind_cache *cache = get_cache(domain);
2608 struct cred_list *cred, *oldest = NULL;
2611 return NT_STATUS_INTERNAL_DB_ERROR;
2614 /* we possibly already have an entry */
2615 if (sid && NT_STATUS_IS_OK(wcache_cached_creds_exist(domain, sid))) {
2619 DEBUG(11,("we already have an entry, deleting that\n"));
2621 fstr_sprintf(key_str, "CRED/%s", sid_string_static(sid));
2623 tdb_delete(cache->tdb, string_tdb_data(key_str));
2625 return NT_STATUS_OK;
2628 ret = tdb_traverse(cache->tdb, traverse_fn_get_credlist, NULL);
2630 return NT_STATUS_OK;
2631 } else if ((ret == -1) || (wcache_cred_list == NULL)) {
2632 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
2635 ZERO_STRUCTP(oldest);
2637 for (cred = wcache_cred_list; cred; cred = cred->next) {
2642 data = tdb_fetch(cache->tdb, string_tdb_data(cred->name));
2644 DEBUG(10,("wcache_remove_oldest_cached_creds: entry for [%s] not found\n",
2646 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
2650 t = IVAL(data.dptr, 0);
2651 SAFE_FREE(data.dptr);
2654 oldest = SMB_MALLOC_P(struct cred_list);
2655 if (oldest == NULL) {
2656 status = NT_STATUS_NO_MEMORY;
2660 fstrcpy(oldest->name, cred->name);
2661 oldest->created = t;
2665 if (t < oldest->created) {
2666 fstrcpy(oldest->name, cred->name);
2667 oldest->created = t;
2671 if (tdb_delete(cache->tdb, string_tdb_data(oldest->name)) == 0) {
2672 status = NT_STATUS_OK;
2674 status = NT_STATUS_UNSUCCESSFUL;
2677 SAFE_FREE(wcache_cred_list);
2683 /* Change the global online/offline state. */
2684 BOOL set_global_winbindd_state_offline(void)
2688 DEBUG(10,("set_global_winbindd_state_offline: offline requested.\n"));
2690 /* Only go offline if someone has created
2691 the key "WINBINDD_OFFLINE" in the cache tdb. */
2693 if (wcache == NULL || wcache->tdb == NULL) {
2694 DEBUG(10,("set_global_winbindd_state_offline: wcache not open yet.\n"));
2698 if (!lp_winbind_offline_logon()) {
2699 DEBUG(10,("set_global_winbindd_state_offline: rejecting.\n"));
2703 if (global_winbindd_offline_state) {
2704 /* Already offline. */
2708 data = tdb_fetch_bystring( wcache->tdb, "WINBINDD_OFFLINE" );
2710 if (!data.dptr || data.dsize != 4) {
2711 DEBUG(10,("set_global_winbindd_state_offline: offline state not set.\n"));
2712 SAFE_FREE(data.dptr);
2715 DEBUG(10,("set_global_winbindd_state_offline: offline state set.\n"));
2716 global_winbindd_offline_state = True;
2717 SAFE_FREE(data.dptr);
2722 void set_global_winbindd_state_online(void)
2724 DEBUG(10,("set_global_winbindd_state_online: online requested.\n"));
2726 if (!lp_winbind_offline_logon()) {
2727 DEBUG(10,("set_global_winbindd_state_online: rejecting.\n"));
2731 if (!global_winbindd_offline_state) {
2732 /* Already online. */
2735 global_winbindd_offline_state = False;
2741 /* Ensure there is no key "WINBINDD_OFFLINE" in the cache tdb. */
2742 tdb_delete_bystring(wcache->tdb, "WINBINDD_OFFLINE");
2745 BOOL get_global_winbindd_state_offline(void)
2747 return global_winbindd_offline_state;
2750 /***********************************************************************
2751 Validate functions for all possible cache tdb keys.
2752 ***********************************************************************/
2754 static struct cache_entry *create_centry_validate(const char *kstr, TDB_DATA data,
2755 struct tdb_validation_status *state)
2757 struct cache_entry *centry;
2759 centry = SMB_XMALLOC_P(struct cache_entry);
2760 centry->data = (unsigned char *)memdup(data.dptr, data.dsize);
2761 if (!centry->data) {
2765 centry->len = data.dsize;
2768 if (centry->len < 8) {
2769 /* huh? corrupt cache? */
2770 DEBUG(0,("create_centry_validate: Corrupt cache for key %s (len < 8) ?\n", kstr));
2771 centry_free(centry);
2772 state->bad_entry = True;
2773 state->success = False;
2777 centry->status = NT_STATUS(centry_uint32(centry));
2778 centry->sequence_number = centry_uint32(centry);
2782 static int validate_seqnum(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
2783 struct tdb_validation_status *state)
2785 if (dbuf.dsize != 8) {
2786 DEBUG(0,("validate_seqnum: Corrupt cache for key %s (len %u != 8) ?\n",
2787 keystr, (unsigned int)dbuf.dsize ));
2788 state->bad_entry = True;
2794 static int validate_ns(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
2795 struct tdb_validation_status *state)
2797 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
2802 (void)centry_uint32(centry);
2803 if (NT_STATUS_IS_OK(centry->status)) {
2805 (void)centry_sid(centry, mem_ctx, &sid);
2808 centry_free(centry);
2810 if (!(state->success)) {
2813 DEBUG(10,("validate_ns: %s ok\n", keystr));
2817 static int validate_sn(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
2818 struct tdb_validation_status *state)
2820 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
2825 if (NT_STATUS_IS_OK(centry->status)) {
2826 (void)centry_uint32(centry);
2827 (void)centry_string(centry, mem_ctx);
2828 (void)centry_string(centry, mem_ctx);
2831 centry_free(centry);
2833 if (!(state->success)) {
2836 DEBUG(10,("validate_sn: %s ok\n", keystr));
2840 static int validate_u(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
2841 struct tdb_validation_status *state)
2843 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
2850 (void)centry_string(centry, mem_ctx);
2851 (void)centry_string(centry, mem_ctx);
2852 (void)centry_string(centry, mem_ctx);
2853 (void)centry_string(centry, mem_ctx);
2854 (void)centry_uint32(centry);
2855 (void)centry_sid(centry, mem_ctx, &sid);
2856 (void)centry_sid(centry, mem_ctx, &sid);
2858 centry_free(centry);
2860 if (!(state->success)) {
2863 DEBUG(10,("validate_u: %s ok\n", keystr));
2867 static int validate_loc_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
2868 struct tdb_validation_status *state)
2870 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
2876 (void)centry_nttime(centry);
2877 (void)centry_nttime(centry);
2878 (void)centry_uint16(centry);
2880 centry_free(centry);
2882 if (!(state->success)) {
2885 DEBUG(10,("validate_loc_pol: %s ok\n", keystr));
2889 static int validate_pwd_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
2890 struct tdb_validation_status *state)
2892 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
2898 (void)centry_uint16(centry);
2899 (void)centry_uint16(centry);
2900 (void)centry_uint32(centry);
2901 (void)centry_nttime(centry);
2902 (void)centry_nttime(centry);
2904 centry_free(centry);
2906 if (!(state->success)) {
2909 DEBUG(10,("validate_pwd_pol: %s ok\n", keystr));
2913 static int validate_cred(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
2914 struct tdb_validation_status *state)
2916 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
2922 (void)centry_time(centry);
2923 (void)centry_hash16(centry, mem_ctx);
2925 /* We only have 17 bytes more data in the salted cred case. */
2926 if (centry->len - centry->ofs == 17) {
2927 (void)centry_hash16(centry, mem_ctx);
2930 centry_free(centry);
2932 if (!(state->success)) {
2935 DEBUG(10,("validate_cred: %s ok\n", keystr));
2939 static int validate_ul(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
2940 struct tdb_validation_status *state)
2942 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
2943 int32 num_entries, i;
2949 num_entries = (int32)centry_uint32(centry);
2951 for (i=0; i< num_entries; i++) {
2953 (void)centry_string(centry, mem_ctx);
2954 (void)centry_string(centry, mem_ctx);
2955 (void)centry_string(centry, mem_ctx);
2956 (void)centry_string(centry, mem_ctx);
2957 (void)centry_sid(centry, mem_ctx, &sid);
2958 (void)centry_sid(centry, mem_ctx, &sid);
2961 centry_free(centry);
2963 if (!(state->success)) {
2966 DEBUG(10,("validate_ul: %s ok\n", keystr));
2970 static int validate_gl(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
2971 struct tdb_validation_status *state)
2973 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
2974 int32 num_entries, i;
2980 num_entries = centry_uint32(centry);
2982 for (i=0; i< num_entries; i++) {
2983 (void)centry_string(centry, mem_ctx);
2984 (void)centry_string(centry, mem_ctx);
2985 (void)centry_uint32(centry);
2988 centry_free(centry);
2990 if (!(state->success)) {
2993 DEBUG(10,("validate_gl: %s ok\n", keystr));
2997 static int validate_ug(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
2998 struct tdb_validation_status *state)
3000 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3001 int32 num_groups, i;
3007 num_groups = centry_uint32(centry);
3009 for (i=0; i< num_groups; i++) {
3011 centry_sid(centry, mem_ctx, &sid);
3014 centry_free(centry);
3016 if (!(state->success)) {
3019 DEBUG(10,("validate_ug: %s ok\n", keystr));
3023 static int validate_ua(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3024 struct tdb_validation_status *state)
3026 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3027 int32 num_aliases, i;
3033 num_aliases = centry_uint32(centry);
3035 for (i=0; i < num_aliases; i++) {
3036 (void)centry_uint32(centry);
3039 centry_free(centry);
3041 if (!(state->success)) {
3044 DEBUG(10,("validate_ua: %s ok\n", keystr));
3048 static int validate_gm(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3049 struct tdb_validation_status *state)
3051 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3058 num_names = centry_uint32(centry);
3060 for (i=0; i< num_names; i++) {
3062 centry_sid(centry, mem_ctx, &sid);
3063 (void)centry_string(centry, mem_ctx);
3064 (void)centry_uint32(centry);
3067 centry_free(centry);
3069 if (!(state->success)) {
3072 DEBUG(10,("validate_gm: %s ok\n", keystr));
3076 static int validate_dr(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3077 struct tdb_validation_status *state)
3079 /* Can't say anything about this other than must be nonzero. */
3080 if (dbuf.dsize == 0) {
3081 DEBUG(0,("validate_dr: Corrupt cache for key %s (len == 0) ?\n",
3083 state->bad_entry = True;
3084 state->success = False;
3088 DEBUG(10,("validate_dr: %s ok\n", keystr));
3092 static int validate_de(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3093 struct tdb_validation_status *state)
3095 /* Can't say anything about this other than must be nonzero. */
3096 if (dbuf.dsize == 0) {
3097 DEBUG(0,("validate_de: Corrupt cache for key %s (len == 0) ?\n",
3099 state->bad_entry = True;
3100 state->success = False;
3104 DEBUG(10,("validate_de: %s ok\n", keystr));
3108 static int validate_trustdoms(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3109 struct tdb_validation_status *state)
3111 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3112 int32 num_domains, i;
3118 num_domains = centry_uint32(centry);
3120 for (i=0; i< num_domains; i++) {
3122 (void)centry_string(centry, mem_ctx);
3123 (void)centry_string(centry, mem_ctx);
3124 (void)centry_sid(centry, mem_ctx, &sid);
3127 centry_free(centry);
3129 if (!(state->success)) {
3132 DEBUG(10,("validate_trustdoms: %s ok\n", keystr));
3136 static int validate_trustdomcache(TALLOC_CTX *mem_ctx, const char *keystr,
3138 struct tdb_validation_status *state)
3140 if (dbuf.dsize == 0) {
3141 DEBUG(0, ("validate_trustdomcache: Corrupt cache for "
3142 "key %s (len ==0) ?\n", keystr));
3143 state->bad_entry = True;
3144 state->success = False;
3148 DEBUG(10, ("validate_trustdomcache: %s ok\n", keystr));
3149 DEBUGADD(10, (" Don't trust me, I am a DUMMY!\n"));
3153 static int validate_offline(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3154 struct tdb_validation_status *state)
3156 if (dbuf.dsize != 4) {
3157 DEBUG(0,("validate_offline: Corrupt cache for key %s (len %u != 4) ?\n",
3158 keystr, (unsigned int)dbuf.dsize ));
3159 state->bad_entry = True;
3160 state->success = False;
3163 DEBUG(10,("validate_offline: %s ok\n", keystr));
3167 static int validate_cache_version(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3168 struct tdb_validation_status *state)
3170 if (dbuf.dsize != 4) {
3171 DEBUG(0, ("validate_cache_version: Corrupt cache for "
3172 "key %s (len %u != 4) ?\n",
3173 keystr, (unsigned int)dbuf.dsize));
3174 state->bad_entry = True;
3175 state->success = False;
3179 DEBUG(10, ("validate_cache_version: %s ok\n", keystr));
3183 /***********************************************************************
3184 A list of all possible cache tdb keys with associated validation
3186 ***********************************************************************/
3188 struct key_val_struct {
3189 const char *keyname;
3190 int (*validate_data_fn)(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, struct tdb_validation_status* state);
3192 {"SEQNUM/", validate_seqnum},
3193 {"NS/", validate_ns},
3194 {"SN/", validate_sn},
3196 {"LOC_POL/", validate_loc_pol},
3197 {"PWD_POL/", validate_pwd_pol},
3198 {"CRED/", validate_cred},
3199 {"UL/", validate_ul},
3200 {"GL/", validate_gl},
3201 {"UG/", validate_ug},
3202 {"UA", validate_ua},
3203 {"GM/", validate_gm},
3204 {"DR/", validate_dr},
3205 {"DE/", validate_de},
3206 {"TRUSTDOMS/", validate_trustdoms},
3207 {"TRUSTDOMCACHE/", validate_trustdomcache},
3208 {"WINBINDD_OFFLINE", validate_offline},
3209 {WINBINDD_CACHE_VERSION_KEYSTR, validate_cache_version},
3213 /***********************************************************************
3214 Function to look at every entry in the tdb and validate it as far as
3216 ***********************************************************************/
3218 static int cache_traverse_validate_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, void *state)
3221 struct tdb_validation_status *v_state = (struct tdb_validation_status *)state;
3223 /* Paranoia check. */
3224 if (kbuf.dsize > 1024) {
3225 DEBUG(0,("cache_traverse_validate_fn: key length too large (%u) > 1024\n\n",
3226 (unsigned int)kbuf.dsize ));
3230 for (i = 0; key_val[i].keyname; i++) {
3231 size_t namelen = strlen(key_val[i].keyname);
3232 if (kbuf.dsize >= namelen && (
3233 strncmp(key_val[i].keyname, (const char *)kbuf.dptr, namelen)) == 0) {
3234 TALLOC_CTX *mem_ctx;
3238 keystr = SMB_MALLOC_ARRAY(char, kbuf.dsize+1);
3242 memcpy(keystr, kbuf.dptr, kbuf.dsize);
3243 keystr[kbuf.dsize] = '\0';
3245 mem_ctx = talloc_init("validate_ctx");
3251 ret = key_val[i].validate_data_fn(mem_ctx, keystr, dbuf,
3255 talloc_destroy(mem_ctx);
3260 DEBUG(0,("cache_traverse_validate_fn: unknown cache entry\nkey :\n"));
3261 dump_data(0, (uint8 *)kbuf.dptr, kbuf.dsize);
3262 DEBUG(0,("data :\n"));
3263 dump_data(0, (uint8 *)dbuf.dptr, dbuf.dsize);
3264 v_state->unknown_key = True;
3265 v_state->success = False;
3266 return 1; /* terminate. */
3269 static void validate_panic(const char *const why)
3271 DEBUG(0,("validating cache: would panic %s\n", why ));
3272 DEBUGADD(0, ("exiting instead (cache validation mode)\n"));
3276 /***********************************************************************
3277 Try and validate every entry in the winbindd cache. If we fail here,
3278 delete the cache tdb and return non-zero - the caller (main winbindd
3279 function) will restart us as we don't know if we crashed or not.
3280 ***********************************************************************/
3282 int winbindd_validate_cache(void)
3285 const char *tdb_path = lock_path("winbindd_cache.tdb");
3286 TDB_CONTEXT *tdb = NULL;
3288 DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
3289 smb_panic_fn = validate_panic;
3292 tdb = tdb_open_log(tdb_path,
3293 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
3294 ( lp_winbind_offline_logon()
3296 : TDB_DEFAULT | TDB_CLEAR_IF_FIRST ),
3300 DEBUG(0, ("winbindd_validate_cache: "
3301 "error opening/initializing tdb\n"));
3306 ret = tdb_validate_and_backup(tdb_path, cache_traverse_validate_fn);
3309 DEBUG(10, ("winbindd_validate_cache: validation not successful.\n"));
3310 DEBUGADD(10, ("removing tdb %s.\n", tdb_path));
3315 DEBUG(10, ("winbindd_validate_cache: restoring panic function\n"));
3316 smb_panic_fn = smb_panic;
3320 /***********************************************************************
3321 Try and validate every entry in the winbindd cache.
3322 ***********************************************************************/
3324 int winbindd_validate_cache_nobackup(void)
3327 const char *tdb_path = lock_path("winbindd_cache.tdb");
3329 DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
3330 smb_panic_fn = validate_panic;
3332 ret = tdb_validate(tdb_path, cache_traverse_validate_fn);
3335 DEBUG(10, ("winbindd_validate_cache_nobackup: validation not "
3339 DEBUG(10, ("winbindd_validate_cache_nobackup: restoring panic "
3341 smb_panic_fn = smb_panic;
3345 /*********************************************************************
3346 ********************************************************************/
3348 static BOOL add_wbdomain_to_tdc_array( struct winbindd_domain *new_dom,
3349 struct winbindd_tdc_domain **domains,
3350 size_t *num_domains )
3352 struct winbindd_tdc_domain *list = NULL;
3355 BOOL set_only = False;
3357 /* don't allow duplicates */
3362 for ( i=0; i< (*num_domains); i++ ) {
3363 if ( strequal( new_dom->name, list[i].domain_name ) ) {
3364 DEBUG(10,("add_wbdomain_to_tdc_array: Found existing record for %s\n",
3375 list = TALLOC_ARRAY( NULL, struct winbindd_tdc_domain, 1 );
3378 list = TALLOC_REALLOC_ARRAY( *domains, *domains,
3379 struct winbindd_tdc_domain,
3384 ZERO_STRUCT( list[idx] );
3390 list[idx].domain_name = talloc_strdup( list, new_dom->name );
3391 list[idx].dns_name = talloc_strdup( list, new_dom->alt_name );
3393 if ( !is_null_sid( &new_dom->sid ) )
3394 sid_copy( &list[idx].sid, &new_dom->sid );
3396 if ( new_dom->domain_flags != 0x0 )
3397 list[idx].trust_flags = new_dom->domain_flags;
3399 if ( new_dom->domain_type != 0x0 )
3400 list[idx].trust_type = new_dom->domain_type;
3402 if ( new_dom->domain_trust_attribs != 0x0 )
3403 list[idx].trust_attribs = new_dom->domain_trust_attribs;
3407 *num_domains = idx + 1;
3413 /*********************************************************************
3414 ********************************************************************/
3416 static TDB_DATA make_tdc_key( const char *domain_name )
3418 char *keystr = NULL;
3419 TDB_DATA key = { NULL, 0 };
3421 if ( !domain_name ) {
3422 DEBUG(5,("make_tdc_key: Keyname workgroup is NULL!\n"));
3427 asprintf( &keystr, "TRUSTDOMCACHE/%s", domain_name );
3428 key = string_term_tdb_data(keystr);
3433 /*********************************************************************
3434 ********************************************************************/
3436 static int pack_tdc_domains( struct winbindd_tdc_domain *domains,
3438 unsigned char **buf )
3440 unsigned char *buffer = NULL;
3445 DEBUG(10,("pack_tdc_domains: Packing %d trusted domains\n",
3453 /* Store the number of array items first */
3454 len += tdb_pack( buffer+len, buflen-len, "d",
3457 /* now pack each domain trust record */
3458 for ( i=0; i<num_domains; i++ ) {
3461 DEBUG(10,("pack_tdc_domains: Packing domain %s (%s)\n",
3462 domains[i].domain_name,
3463 domains[i].dns_name ? domains[i].dns_name : "UNKNOWN" ));
3466 len += tdb_pack( buffer+len, buflen-len, "fffddd",
3467 domains[i].domain_name,
3468 domains[i].dns_name,
3469 sid_string_static(&domains[i].sid),
3470 domains[i].trust_flags,
3471 domains[i].trust_attribs,
3472 domains[i].trust_type );
3475 if ( buflen < len ) {
3477 if ( (buffer = SMB_MALLOC_ARRAY(unsigned char, len)) == NULL ) {
3478 DEBUG(0,("pack_tdc_domains: failed to alloc buffer!\n"));
3492 /*********************************************************************
3493 ********************************************************************/
3495 static size_t unpack_tdc_domains( unsigned char *buf, int buflen,
3496 struct winbindd_tdc_domain **domains )
3498 fstring domain_name, dns_name, sid_string;
3499 uint32 type, attribs, flags;
3503 struct winbindd_tdc_domain *list = NULL;
3505 /* get the number of domains */
3506 len += tdb_unpack( buf+len, buflen-len, "d", &num_domains);
3508 DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
3512 list = TALLOC_ARRAY( NULL, struct winbindd_tdc_domain, num_domains );
3514 DEBUG(0,("unpack_tdc_domains: Failed to talloc() domain list!\n"));
3518 for ( i=0; i<num_domains; i++ ) {
3519 len += tdb_unpack( buf+len, buflen-len, "fffddd",
3528 DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
3529 TALLOC_FREE( list );
3533 DEBUG(11,("unpack_tdc_domains: Unpacking domain %s (%s) "
3534 "SID %s, flags = 0x%x, attribs = 0x%x, type = 0x%x\n",
3535 domain_name, dns_name, sid_string,
3536 flags, attribs, type));
3538 list[i].domain_name = talloc_strdup( list, domain_name );
3539 list[i].dns_name = talloc_strdup( list, dns_name );
3540 if ( !string_to_sid( &(list[i].sid), sid_string ) ) {
3541 DEBUG(10,("unpack_tdc_domains: no SID for domain %s\n",
3544 list[i].trust_flags = flags;
3545 list[i].trust_attribs = attribs;
3546 list[i].trust_type = type;
3554 /*********************************************************************
3555 ********************************************************************/
3557 static BOOL wcache_tdc_store_list( struct winbindd_tdc_domain *domains, size_t num_domains )
3559 TDB_DATA key = make_tdc_key( lp_workgroup() );
3560 TDB_DATA data = { NULL, 0 };
3566 /* See if we were asked to delete the cache entry */
3569 ret = tdb_delete( wcache->tdb, key );
3573 data.dsize = pack_tdc_domains( domains, num_domains, &data.dptr );
3580 ret = tdb_store( wcache->tdb, key, data, 0 );
3583 SAFE_FREE( data.dptr );
3584 SAFE_FREE( key.dptr );
3586 return ( ret != -1 );
3589 /*********************************************************************
3590 ********************************************************************/
3592 BOOL wcache_tdc_fetch_list( struct winbindd_tdc_domain **domains, size_t *num_domains )
3594 TDB_DATA key = make_tdc_key( lp_workgroup() );
3595 TDB_DATA data = { NULL, 0 };
3603 data = tdb_fetch( wcache->tdb, key );
3605 SAFE_FREE( key.dptr );
3610 *num_domains = unpack_tdc_domains( data.dptr, data.dsize, domains );
3612 SAFE_FREE( data.dptr );
3620 /*********************************************************************
3621 ********************************************************************/
3623 BOOL wcache_tdc_add_domain( struct winbindd_domain *domain )
3625 struct winbindd_tdc_domain *dom_list = NULL;
3626 size_t num_domains = 0;
3629 DEBUG(10,("wcache_tdc_add_domain: Adding domain %s (%s), SID %s, "
3630 "flags = 0x%x, attributes = 0x%x, type = 0x%x\n",
3631 domain->name, domain->alt_name,
3632 sid_string_static(&domain->sid),
3633 domain->domain_flags,
3634 domain->domain_trust_attribs,
3635 domain->domain_type));
3637 if ( !init_wcache() ) {
3641 /* fetch the list */
3643 wcache_tdc_fetch_list( &dom_list, &num_domains );
3645 /* add the new domain */
3647 if ( !add_wbdomain_to_tdc_array( domain, &dom_list, &num_domains ) ) {
3651 /* pack the domain */
3653 if ( !wcache_tdc_store_list( dom_list, num_domains ) ) {
3661 TALLOC_FREE( dom_list );
3666 /*********************************************************************
3667 ********************************************************************/
3669 struct winbindd_tdc_domain * wcache_tdc_fetch_domain( TALLOC_CTX *ctx, const char *name )
3671 struct winbindd_tdc_domain *dom_list = NULL;
3672 size_t num_domains = 0;
3674 struct winbindd_tdc_domain *d = NULL;
3676 DEBUG(10,("wcache_tdc_fetch_domain: Searching for domain %s\n", name));
3678 if ( !init_wcache() ) {
3682 /* fetch the list */
3684 wcache_tdc_fetch_list( &dom_list, &num_domains );
3686 for ( i=0; i<num_domains; i++ ) {
3687 if ( strequal(name, dom_list[i].domain_name) ||
3688 strequal(name, dom_list[i].dns_name) )
3690 DEBUG(10,("wcache_tdc_fetch_domain: Found domain %s\n",
3693 d = TALLOC_P( ctx, struct winbindd_tdc_domain );
3697 d->domain_name = talloc_strdup( d, dom_list[i].domain_name );
3698 d->dns_name = talloc_strdup( d, dom_list[i].dns_name );
3699 sid_copy( &d->sid, &dom_list[i].sid );
3700 d->trust_flags = dom_list[i].trust_flags;
3701 d->trust_type = dom_list[i].trust_type;
3702 d->trust_attribs = dom_list[i].trust_attribs;
3708 TALLOC_FREE( dom_list );
3714 /*********************************************************************
3715 ********************************************************************/
3717 void wcache_tdc_clear( void )
3719 if ( !init_wcache() )
3722 wcache_tdc_store_list( NULL, 0 );
3728 /*********************************************************************
3729 ********************************************************************/
3731 static void wcache_save_user_pwinfo(struct winbindd_domain *domain,
3733 const DOM_SID *user_sid,
3734 const char *homedir,
3739 struct cache_entry *centry;
3741 if ( (centry = centry_start(domain, status)) == NULL )
3744 centry_put_string( centry, homedir );
3745 centry_put_string( centry, shell );
3746 centry_put_string( centry, gecos );
3747 centry_put_uint32( centry, gid );
3749 centry_end(centry, "NSS/PWINFO/%s", sid_string_static(user_sid) );
3751 DEBUG(10,("wcache_save_user_pwinfo: %s\n", sid_string_static(user_sid) ));
3753 centry_free(centry);
3756 NTSTATUS nss_get_info_cached( struct winbindd_domain *domain,
3757 const DOM_SID *user_sid,
3759 ADS_STRUCT *ads, LDAPMessage *msg,
3760 char **homedir, char **shell, char **gecos,
3763 struct winbind_cache *cache = get_cache(domain);
3764 struct cache_entry *centry = NULL;
3770 centry = wcache_fetch(cache, domain, "NSS/PWINFO/%s", sid_string_static(user_sid));
3775 *homedir = centry_string( centry, ctx );
3776 *shell = centry_string( centry, ctx );
3777 *gecos = centry_string( centry, ctx );
3778 *p_gid = centry_uint32( centry );
3780 centry_free(centry);
3782 DEBUG(10,("nss_get_info_cached: [Cached] - user_sid %s\n",
3783 sid_string_static(user_sid)));
3785 return NT_STATUS_OK;
3789 nt_status = nss_get_info( domain->name, user_sid, ctx, ads, msg,
3790 homedir, shell, gecos, p_gid );
3792 if ( NT_STATUS_IS_OK(nt_status) ) {
3793 wcache_save_user_pwinfo( domain, nt_status, user_sid,
3794 *homedir, *shell, *gecos, *p_gid );
3797 if ( NT_STATUS_EQUAL( nt_status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND ) ) {
3798 DEBUG(5,("nss_get_info_cached: Setting domain %s offline\n",
3800 set_domain_offline( domain );
3807 /* the cache backend methods are exposed via this structure */
3808 struct winbindd_methods cache_methods = {