r22847: The new validate_panic function calls exit (instead of setting
[Samba/bb.git] / source3 / nsswitch / winbindd_cache.c
blobe887363fcfc615af1e9469718372e277ca3a3522
1 /*
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 2 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, write to the Free Software
24 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
27 #include "includes.h"
28 #include "winbindd.h"
30 #undef DBGC_CLASS
31 #define DBGC_CLASS DBGC_WINBIND
33 #define WINBINDD_CACHE_VERSION 1
34 #define WINBINDD_CACHE_VERSION_KEYSTR "WINBINDD_CACHE_VERSION"
36 extern struct winbindd_methods reconnect_methods;
37 extern BOOL opt_nocache;
38 #ifdef HAVE_ADS
39 extern struct winbindd_methods ads_methods;
40 #endif
43 * JRA. KEEP THIS LIST UP TO DATE IF YOU ADD CACHE ENTRIES.
44 * Here are the list of entry types that are *not* stored
45 * as form struct cache_entry in the cache.
48 static const char *non_centry_keys[] = {
49 "SEQNUM/",
50 "DR/",
51 "DE/",
52 "WINBINDD_OFFLINE",
53 WINBINDD_CACHE_VERSION_KEYSTR,
54 NULL
57 /************************************************************************
58 Is this key a non-centry type ?
59 ************************************************************************/
61 static BOOL is_non_centry_key(TDB_DATA kbuf)
63 int i;
65 if (kbuf.dptr == NULL || kbuf.dsize == 0) {
66 return False;
68 for (i = 0; non_centry_keys[i] != NULL; i++) {
69 size_t namelen = strlen(non_centry_keys[i]);
70 if (kbuf.dsize < namelen) {
71 continue;
73 if (strncmp(non_centry_keys[i], (const char *)kbuf.dptr, namelen) == 0) {
74 return True;
77 return False;
80 /* Global online/offline state - False when online. winbindd starts up online
81 and sets this to true if the first query fails and there's an entry in
82 the cache tdb telling us to stay offline. */
84 static BOOL global_winbindd_offline_state;
86 struct winbind_cache {
87 TDB_CONTEXT *tdb;
90 struct cache_entry {
91 NTSTATUS status;
92 uint32 sequence_number;
93 uint8 *data;
94 uint32 len, ofs;
97 void (*smb_panic_fn)(const char *const why) = smb_panic;
99 #define WINBINDD_MAX_CACHE_SIZE (50*1024*1024)
101 static struct winbind_cache *wcache;
103 void winbindd_check_cache_size(time_t t)
105 static time_t last_check_time;
106 struct stat st;
108 if (last_check_time == (time_t)0)
109 last_check_time = t;
111 if (t - last_check_time < 60 && t - last_check_time > 0)
112 return;
114 if (wcache == NULL || wcache->tdb == NULL) {
115 DEBUG(0, ("Unable to check size of tdb cache - cache not open !\n"));
116 return;
119 if (fstat(tdb_fd(wcache->tdb), &st) == -1) {
120 DEBUG(0, ("Unable to check size of tdb cache %s!\n", strerror(errno) ));
121 return;
124 if (st.st_size > WINBINDD_MAX_CACHE_SIZE) {
125 DEBUG(10,("flushing cache due to size (%lu) > (%lu)\n",
126 (unsigned long)st.st_size,
127 (unsigned long)WINBINDD_MAX_CACHE_SIZE));
128 wcache_flush_cache();
132 /* get the winbind_cache structure */
133 static struct winbind_cache *get_cache(struct winbindd_domain *domain)
135 struct winbind_cache *ret = wcache;
136 #ifdef HAVE_ADS
137 struct winbindd_domain *our_domain = domain;
138 #endif
140 /* We have to know what type of domain we are dealing with first. */
142 if ( !domain->initialized ) {
143 init_dc_connection( domain );
147 OK. listen up becasue I'm only going to say this once.
148 We have the following scenarios to consider
149 (a) trusted AD domains on a Samba DC,
150 (b) trusted AD domains and we are joined to a non-kerberos domain
151 (c) trusted AD domains and we are joined to a kerberos (AD) domain
153 For (a) we can always contact the trusted domain using krb5
154 since we have the domain trust account password
156 For (b) we can only use RPC since we have no way of
157 getting a krb5 ticket in our own domain
159 For (c) we can always use krb5 since we have a kerberos trust
161 --jerry
164 if (!domain->backend) {
165 #ifdef HAVE_ADS
166 /* find our domain first so we can figure out if we
167 are joined to a kerberized domain */
169 if ( !domain->primary )
170 our_domain = find_our_domain();
172 if ( (our_domain->active_directory || IS_DC) && domain->active_directory ) {
173 DEBUG(5,("get_cache: Setting ADS methods for domain %s\n", domain->name));
174 domain->backend = &ads_methods;
175 } else {
176 #endif /* HAVE_ADS */
177 DEBUG(5,("get_cache: Setting MS-RPC methods for domain %s\n", domain->name));
178 domain->backend = &reconnect_methods;
179 #ifdef HAVE_ADS
181 #endif /* HAVE_ADS */
184 if (ret)
185 return ret;
187 ret = SMB_XMALLOC_P(struct winbind_cache);
188 ZERO_STRUCTP(ret);
190 wcache = ret;
191 wcache_flush_cache();
193 return ret;
197 free a centry structure
199 static void centry_free(struct cache_entry *centry)
201 if (!centry)
202 return;
203 SAFE_FREE(centry->data);
204 free(centry);
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));
213 return False;
215 return True;
219 pull a uint32 from a cache entry
221 static uint32 centry_uint32(struct cache_entry *centry)
223 uint32 ret;
225 if (!centry_check_bytes(centry, 4)) {
226 smb_panic_fn("centry_uint32");
228 ret = IVAL(centry->data, centry->ofs);
229 centry->ofs += 4;
230 return ret;
234 pull a uint16 from a cache entry
236 static uint16 centry_uint16(struct cache_entry *centry)
238 uint16 ret;
239 if (!centry_check_bytes(centry, 2)) {
240 smb_panic_fn("centry_uint16");
242 ret = CVAL(centry->data, centry->ofs);
243 centry->ofs += 2;
244 return ret;
248 pull a uint8 from a cache entry
250 static uint8 centry_uint8(struct cache_entry *centry)
252 uint8 ret;
253 if (!centry_check_bytes(centry, 1)) {
254 smb_panic_fn("centry_uint8");
256 ret = CVAL(centry->data, centry->ofs);
257 centry->ofs += 1;
258 return ret;
262 pull a NTTIME from a cache entry
264 static NTTIME centry_nttime(struct cache_entry *centry)
266 NTTIME ret;
267 if (!centry_check_bytes(centry, 8)) {
268 smb_panic_fn("centry_nttime");
270 ret = IVAL(centry->data, centry->ofs);
271 centry->ofs += 4;
272 ret += (uint64_t)IVAL(centry->data, centry->ofs) << 32;
273 centry->ofs += 4;
274 return ret;
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
286 talloc context
288 static char *centry_string(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
290 uint32 len;
291 char *ret;
293 len = centry_uint8(centry);
295 if (len == 0xFF) {
296 /* a deliberate NULL string */
297 return NULL;
300 if (!centry_check_bytes(centry, (size_t)len)) {
301 smb_panic_fn("centry_string");
304 ret = TALLOC_ARRAY(mem_ctx, char, len+1);
305 if (!ret) {
306 smb_panic_fn("centry_string out of memory\n");
308 memcpy(ret,centry->data + centry->ofs, len);
309 ret[len] = 0;
310 centry->ofs += len;
311 return ret;
314 /* pull a hash16 from a cache entry, using the supplied
315 talloc context
317 static char *centry_hash16(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
319 uint32 len;
320 char *ret;
322 len = centry_uint8(centry);
324 if (len != 16) {
325 DEBUG(0,("centry corruption? hash len (%u) != 16\n",
326 len ));
327 return NULL;
330 if (!centry_check_bytes(centry, 16)) {
331 return NULL;
334 ret = TALLOC_ARRAY(mem_ctx, char, 16);
335 if (!ret) {
336 smb_panic_fn("centry_hash out of memory\n");
338 memcpy(ret,centry->data + centry->ofs, 16);
339 centry->ofs += 16;
340 return ret;
343 /* pull a sid from a cache entry, using the supplied
344 talloc context
346 static BOOL centry_sid(struct cache_entry *centry, TALLOC_CTX *mem_ctx, DOM_SID *sid)
348 char *sid_string;
349 sid_string = centry_string(centry, mem_ctx);
350 if ((sid_string == NULL) || (!string_to_sid(sid, sid_string))) {
351 return False;
353 return True;
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)
359 BOOL ret;
361 if (!wcache->tdb)
362 return False;
364 ret = (domain->sequence_number == DOM_SEQUENCE_NONE);
366 if (ret)
367 DEBUG(10,("wcache_server_down: server for Domain %s down\n",
368 domain->name ));
369 return ret;
372 static NTSTATUS fetch_cache_seqnum( struct winbindd_domain *domain, time_t now )
374 TDB_DATA data;
375 fstring key;
376 uint32 time_diff;
378 if (!wcache->tdb) {
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));
410 return NT_STATUS_OK;
413 static NTSTATUS store_cache_seqnum( struct winbindd_domain *domain )
415 TDB_DATA data;
416 fstring key_str;
417 uint8 buf[8];
419 if (!wcache->tdb) {
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);
428 data.dptr = buf;
429 data.dsize = 8;
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));
440 return NT_STATUS_OK;
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)
450 NTSTATUS status;
451 unsigned time_diff;
452 time_t t = time(NULL);
453 unsigned cache_time = lp_winbind_cache_time();
455 if ( IS_DOMAIN_OFFLINE(domain) ) {
456 return;
459 get_cache( 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) {
464 cache_time *= 8;
466 #endif
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));
473 goto done;
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) )
481 goto done;
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);
489 } else {
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 */
499 get_cache( domain );
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 );
512 done:
513 DEBUG(10, ("refresh_sequence_number: %s seq number is now %d\n",
514 domain->name, domain->sequence_number));
516 return;
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 ));
528 return False;
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 ));
537 return False;
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 ));
546 return True;
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 ));
555 return False;
558 DEBUG(10,("centry_expired: Key %s for domain %s expired\n",
559 keystr, domain->name ));
561 /* it's expired */
562 return True;
565 static struct cache_entry *wcache_fetch_raw(char *kstr)
567 TDB_DATA data;
568 struct cache_entry *centry;
569 TDB_DATA key;
571 key = string_tdb_data(kstr);
572 data = tdb_fetch(wcache->tdb, key);
573 if (!data.dptr) {
574 /* a cache miss */
575 return NULL;
578 centry = SMB_XMALLOC_P(struct cache_entry);
579 centry->data = (unsigned char *)data.dptr;
580 centry->len = data.dsize;
581 centry->ofs = 0;
583 if (centry->len < 8) {
584 /* huh? corrupt cache? */
585 DEBUG(10,("wcache_fetch_raw: Corrupt cache for key %s (len < 8) ?\n", kstr));
586 centry_free(centry);
587 return NULL;
590 centry->status = NT_STATUS(centry_uint32(centry));
591 centry->sequence_number = centry_uint32(centry);
593 return 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, ...)
607 va_list ap;
608 char *kstr;
609 struct cache_entry *centry;
611 if (opt_nocache) {
612 return NULL;
615 refresh_sequence_number(domain, False);
617 va_start(ap, format);
618 smb_xvasprintf(&kstr, format, ap);
619 va_end(ap);
621 centry = wcache_fetch_raw(kstr);
622 if (centry == NULL) {
623 free(kstr);
624 return NULL;
627 if (centry_expired(domain, kstr, centry)) {
629 DEBUG(10,("wcache_fetch: entry %s expired for domain %s\n",
630 kstr, domain->name ));
632 centry_free(centry);
633 free(kstr);
634 return NULL;
637 DEBUG(10,("wcache_fetch: returning entry %s for domain %s\n",
638 kstr, domain->name ));
640 free(kstr);
641 return centry;
644 static void wcache_delete(const char *format, ...) PRINTF_ATTRIBUTE(1,2);
645 static void wcache_delete(const char *format, ...)
647 va_list ap;
648 char *kstr;
649 TDB_DATA key;
651 va_start(ap, format);
652 smb_xvasprintf(&kstr, format, ap);
653 va_end(ap);
655 key = string_tdb_data(kstr);
657 tdb_delete(wcache->tdb, key);
658 free(kstr);
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)
667 return;
668 centry->len *= 2;
669 centry->data = SMB_REALLOC_ARRAY(centry->data, unsigned char,
670 centry->len);
671 if (!centry->data) {
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);
684 centry->ofs += 4;
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);
694 centry->ofs += 2;
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);
704 centry->ofs += 1;
708 push a string into a centry
710 static void centry_put_string(struct cache_entry *centry, const char *s)
712 int len;
714 if (!s) {
715 /* null strings are marked as len 0xFFFF */
716 centry_put_uint8(centry, 0xFF);
717 return;
720 len = strlen(s);
721 /* can't handle more than 254 char strings. Truncating is probably best */
722 if (len > 254) {
723 DEBUG(10,("centry_put_string: truncating len (%d) to: 254\n", len));
724 len = 254;
726 centry_put_uint8(centry, len);
727 centry_expand(centry, len);
728 memcpy(centry->data + centry->ofs, s, len);
729 centry->ofs += 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);
740 centry->ofs += 16;
743 static void centry_put_sid(struct cache_entry *centry, const DOM_SID *sid)
745 fstring sid_string;
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);
756 centry->ofs += 4;
757 SIVAL(centry->data, centry->ofs, nt >> 32);
758 centry->ofs += 4;
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;
778 if (!wcache->tdb)
779 return NULL;
781 centry = SMB_XMALLOC_P(struct cache_entry);
783 centry->len = 8192; /* reasonable default */
784 centry->data = SMB_XMALLOC_ARRAY(uint8, centry->len);
785 centry->ofs = 0;
786 centry->sequence_number = domain->sequence_number;
787 centry_put_uint32(centry, NT_STATUS_V(status));
788 centry_put_uint32(centry, centry->sequence_number);
789 return centry;
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, ...)
798 va_list ap;
799 char *kstr;
800 TDB_DATA key, data;
802 va_start(ap, format);
803 smb_xvasprintf(&kstr, format, ap);
804 va_end(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);
811 free(kstr);
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;
820 fstring uname;
822 centry = centry_start(domain, status);
823 if (!centry)
824 return;
825 centry_put_uint32(centry, type);
826 centry_put_sid(centry, sid);
827 fstrcpy(uname, name);
828 strupper_m(uname);
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)));
832 centry_free(centry);
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;
839 fstring sid_string;
841 if (is_null_sid(sid)) {
842 return;
845 centry = centry_start(domain, status);
846 if (!centry)
847 return;
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)));
856 centry_free(centry);
860 static void wcache_save_user(struct winbindd_domain *domain, NTSTATUS status, WINBIND_USERINFO *info)
862 struct cache_entry *centry;
863 fstring sid_string;
865 if (is_null_sid(&info->user_sid)) {
866 return;
869 centry = centry_start(domain, status);
870 if (!centry)
871 return;
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));
881 centry_free(centry);
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);
889 if (!centry)
890 return;
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));
900 centry_free(centry);
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);
908 if (!centry)
909 return;
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));
921 centry_free(centry);
924 NTSTATUS wcache_cached_creds_exist(struct winbindd_domain *domain, const DOM_SID *sid)
926 struct winbind_cache *cache = get_cache(domain);
927 TDB_DATA data;
928 fstring key_str;
929 uint32 rid;
931 if (!cache->tdb) {
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));
946 if (!data.dptr) {
947 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
950 SAFE_FREE(data.dptr);
951 return NT_STATUS_OK;
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,
958 TALLOC_CTX *mem_ctx,
959 const DOM_SID *sid,
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;
965 NTSTATUS status;
966 time_t t;
967 uint32 rid;
969 if (!cache->tdb) {
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));
985 if (!centry) {
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
1003 don't have it. */
1004 DEBUG(0,("wcache_get_creds: bad entry for [CRED/%s] - deleting\n",
1005 sidstr));
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);
1014 } else {
1015 *cached_salt = NULL;
1018 #if DEBUG_PASSWORD
1019 dump_data(100, *cached_nt_pass, NT_HASH_LEN);
1020 if (*cached_salt) {
1021 dump_data(100, *cached_salt, NT_HASH_LEN);
1023 #endif
1024 status = centry->status;
1026 DEBUG(10,("wcache_get_creds: [Cached] - cached creds for user %s status: %s\n",
1027 sid_string_static(sid), nt_errstr(status) ));
1029 centry_free(centry);
1030 return status;
1033 /* Store creds for a SID - only writes out new salted ones. */
1035 NTSTATUS wcache_save_creds(struct winbindd_domain *domain,
1036 TALLOC_CTX *mem_ctx,
1037 const DOM_SID *sid,
1038 const uint8 nt_pass[NT_HASH_LEN])
1040 struct cache_entry *centry;
1041 fstring sid_string;
1042 uint32 rid;
1043 uint8 cred_salt[NT_HASH_LEN];
1044 uint8 salted_hash[NT_HASH_LEN];
1046 if (is_null_sid(sid)) {
1047 return NT_STATUS_INVALID_SID;
1050 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1051 return NT_STATUS_INVALID_SID;
1054 centry = centry_start(domain, NT_STATUS_OK);
1055 if (!centry) {
1056 return NT_STATUS_INTERNAL_DB_ERROR;
1059 #if DEBUG_PASSWORD
1060 dump_data(100, nt_pass, NT_HASH_LEN);
1061 #endif
1063 centry_put_time(centry, time(NULL));
1065 /* Create a salt and then salt the hash. */
1066 generate_random_buffer(cred_salt, NT_HASH_LEN);
1067 E_md5hash(cred_salt, nt_pass, salted_hash);
1069 centry_put_hash16(centry, salted_hash);
1070 centry_put_hash16(centry, cred_salt);
1071 centry_end(centry, "CRED/%s", sid_to_string(sid_string, sid));
1073 DEBUG(10,("wcache_save_creds: %s\n", sid_string));
1075 centry_free(centry);
1077 return NT_STATUS_OK;
1081 /* Query display info. This is the basic user list fn */
1082 static NTSTATUS query_user_list(struct winbindd_domain *domain,
1083 TALLOC_CTX *mem_ctx,
1084 uint32 *num_entries,
1085 WINBIND_USERINFO **info)
1087 struct winbind_cache *cache = get_cache(domain);
1088 struct cache_entry *centry = NULL;
1089 NTSTATUS status;
1090 unsigned int i, retry;
1092 if (!cache->tdb)
1093 goto do_query;
1095 centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
1096 if (!centry)
1097 goto do_query;
1099 *num_entries = centry_uint32(centry);
1101 if (*num_entries == 0)
1102 goto do_cached;
1104 (*info) = TALLOC_ARRAY(mem_ctx, WINBIND_USERINFO, *num_entries);
1105 if (! (*info)) {
1106 smb_panic_fn("query_user_list out of memory");
1108 for (i=0; i<(*num_entries); i++) {
1109 (*info)[i].acct_name = centry_string(centry, mem_ctx);
1110 (*info)[i].full_name = centry_string(centry, mem_ctx);
1111 (*info)[i].homedir = centry_string(centry, mem_ctx);
1112 (*info)[i].shell = centry_string(centry, mem_ctx);
1113 centry_sid(centry, mem_ctx, &(*info)[i].user_sid);
1114 centry_sid(centry, mem_ctx, &(*info)[i].group_sid);
1117 do_cached:
1118 status = centry->status;
1120 DEBUG(10,("query_user_list: [Cached] - cached list for domain %s status: %s\n",
1121 domain->name, nt_errstr(status) ));
1123 centry_free(centry);
1124 return status;
1126 do_query:
1127 *num_entries = 0;
1128 *info = NULL;
1130 /* Return status value returned by seq number check */
1132 if (!NT_STATUS_IS_OK(domain->last_status))
1133 return domain->last_status;
1135 /* Put the query_user_list() in a retry loop. There appears to be
1136 * some bug either with Windows 2000 or Samba's handling of large
1137 * rpc replies. This manifests itself as sudden disconnection
1138 * at a random point in the enumeration of a large (60k) user list.
1139 * The retry loop simply tries the operation again. )-: It's not
1140 * pretty but an acceptable workaround until we work out what the
1141 * real problem is. */
1143 retry = 0;
1144 do {
1146 DEBUG(10,("query_user_list: [Cached] - doing backend query for list for domain %s\n",
1147 domain->name ));
1149 status = domain->backend->query_user_list(domain, mem_ctx, num_entries, info);
1150 if (!NT_STATUS_IS_OK(status))
1151 DEBUG(3, ("query_user_list: returned 0x%08x, "
1152 "retrying\n", NT_STATUS_V(status)));
1153 if (NT_STATUS_EQUAL(status, NT_STATUS_UNSUCCESSFUL)) {
1154 DEBUG(3, ("query_user_list: flushing "
1155 "connection cache\n"));
1156 invalidate_cm_connection(&domain->conn);
1159 } while (NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_UNSUCCESSFUL) &&
1160 (retry++ < 5));
1162 /* and save it */
1163 refresh_sequence_number(domain, False);
1164 centry = centry_start(domain, status);
1165 if (!centry)
1166 goto skip_save;
1167 centry_put_uint32(centry, *num_entries);
1168 for (i=0; i<(*num_entries); i++) {
1169 centry_put_string(centry, (*info)[i].acct_name);
1170 centry_put_string(centry, (*info)[i].full_name);
1171 centry_put_string(centry, (*info)[i].homedir);
1172 centry_put_string(centry, (*info)[i].shell);
1173 centry_put_sid(centry, &(*info)[i].user_sid);
1174 centry_put_sid(centry, &(*info)[i].group_sid);
1175 if (domain->backend && domain->backend->consistent) {
1176 /* when the backend is consistent we can pre-prime some mappings */
1177 wcache_save_name_to_sid(domain, NT_STATUS_OK,
1178 domain->name,
1179 (*info)[i].acct_name,
1180 &(*info)[i].user_sid,
1181 SID_NAME_USER);
1182 wcache_save_sid_to_name(domain, NT_STATUS_OK,
1183 &(*info)[i].user_sid,
1184 domain->name,
1185 (*info)[i].acct_name,
1186 SID_NAME_USER);
1187 wcache_save_user(domain, NT_STATUS_OK, &(*info)[i]);
1190 centry_end(centry, "UL/%s", domain->name);
1191 centry_free(centry);
1193 skip_save:
1194 return status;
1197 /* list all domain groups */
1198 static NTSTATUS enum_dom_groups(struct winbindd_domain *domain,
1199 TALLOC_CTX *mem_ctx,
1200 uint32 *num_entries,
1201 struct acct_info **info)
1203 struct winbind_cache *cache = get_cache(domain);
1204 struct cache_entry *centry = NULL;
1205 NTSTATUS status;
1206 unsigned int i;
1208 if (!cache->tdb)
1209 goto do_query;
1211 centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
1212 if (!centry)
1213 goto do_query;
1215 *num_entries = centry_uint32(centry);
1217 if (*num_entries == 0)
1218 goto do_cached;
1220 (*info) = TALLOC_ARRAY(mem_ctx, struct acct_info, *num_entries);
1221 if (! (*info)) {
1222 smb_panic_fn("enum_dom_groups out of memory");
1224 for (i=0; i<(*num_entries); i++) {
1225 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1226 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1227 (*info)[i].rid = centry_uint32(centry);
1230 do_cached:
1231 status = centry->status;
1233 DEBUG(10,("enum_dom_groups: [Cached] - cached list for domain %s status: %s\n",
1234 domain->name, nt_errstr(status) ));
1236 centry_free(centry);
1237 return status;
1239 do_query:
1240 *num_entries = 0;
1241 *info = NULL;
1243 /* Return status value returned by seq number check */
1245 if (!NT_STATUS_IS_OK(domain->last_status))
1246 return domain->last_status;
1248 DEBUG(10,("enum_dom_groups: [Cached] - doing backend query for list for domain %s\n",
1249 domain->name ));
1251 status = domain->backend->enum_dom_groups(domain, mem_ctx, num_entries, info);
1253 /* and save it */
1254 refresh_sequence_number(domain, False);
1255 centry = centry_start(domain, status);
1256 if (!centry)
1257 goto skip_save;
1258 centry_put_uint32(centry, *num_entries);
1259 for (i=0; i<(*num_entries); i++) {
1260 centry_put_string(centry, (*info)[i].acct_name);
1261 centry_put_string(centry, (*info)[i].acct_desc);
1262 centry_put_uint32(centry, (*info)[i].rid);
1264 centry_end(centry, "GL/%s/domain", domain->name);
1265 centry_free(centry);
1267 skip_save:
1268 return status;
1271 /* list all domain groups */
1272 static NTSTATUS enum_local_groups(struct winbindd_domain *domain,
1273 TALLOC_CTX *mem_ctx,
1274 uint32 *num_entries,
1275 struct acct_info **info)
1277 struct winbind_cache *cache = get_cache(domain);
1278 struct cache_entry *centry = NULL;
1279 NTSTATUS status;
1280 unsigned int i;
1282 if (!cache->tdb)
1283 goto do_query;
1285 centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
1286 if (!centry)
1287 goto do_query;
1289 *num_entries = centry_uint32(centry);
1291 if (*num_entries == 0)
1292 goto do_cached;
1294 (*info) = TALLOC_ARRAY(mem_ctx, struct acct_info, *num_entries);
1295 if (! (*info)) {
1296 smb_panic_fn("enum_dom_groups out of memory");
1298 for (i=0; i<(*num_entries); i++) {
1299 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1300 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1301 (*info)[i].rid = centry_uint32(centry);
1304 do_cached:
1306 /* If we are returning cached data and the domain controller
1307 is down then we don't know whether the data is up to date
1308 or not. Return NT_STATUS_MORE_PROCESSING_REQUIRED to
1309 indicate this. */
1311 if (wcache_server_down(domain)) {
1312 DEBUG(10, ("enum_local_groups: returning cached user list and server was down\n"));
1313 status = NT_STATUS_MORE_PROCESSING_REQUIRED;
1314 } else
1315 status = centry->status;
1317 DEBUG(10,("enum_local_groups: [Cached] - cached list for domain %s status: %s\n",
1318 domain->name, nt_errstr(status) ));
1320 centry_free(centry);
1321 return status;
1323 do_query:
1324 *num_entries = 0;
1325 *info = NULL;
1327 /* Return status value returned by seq number check */
1329 if (!NT_STATUS_IS_OK(domain->last_status))
1330 return domain->last_status;
1332 DEBUG(10,("enum_local_groups: [Cached] - doing backend query for list for domain %s\n",
1333 domain->name ));
1335 status = domain->backend->enum_local_groups(domain, mem_ctx, num_entries, info);
1337 /* and save it */
1338 refresh_sequence_number(domain, False);
1339 centry = centry_start(domain, status);
1340 if (!centry)
1341 goto skip_save;
1342 centry_put_uint32(centry, *num_entries);
1343 for (i=0; i<(*num_entries); i++) {
1344 centry_put_string(centry, (*info)[i].acct_name);
1345 centry_put_string(centry, (*info)[i].acct_desc);
1346 centry_put_uint32(centry, (*info)[i].rid);
1348 centry_end(centry, "GL/%s/local", domain->name);
1349 centry_free(centry);
1351 skip_save:
1352 return status;
1355 /* convert a single name to a sid in a domain */
1356 static NTSTATUS name_to_sid(struct winbindd_domain *domain,
1357 TALLOC_CTX *mem_ctx,
1358 const char *domain_name,
1359 const char *name,
1360 DOM_SID *sid,
1361 enum lsa_SidType *type)
1363 struct winbind_cache *cache = get_cache(domain);
1364 struct cache_entry *centry = NULL;
1365 NTSTATUS status;
1366 fstring uname;
1368 if (!cache->tdb)
1369 goto do_query;
1371 fstrcpy(uname, name);
1372 strupper_m(uname);
1373 centry = wcache_fetch(cache, domain, "NS/%s/%s", domain_name, uname);
1374 if (!centry)
1375 goto do_query;
1376 *type = (enum lsa_SidType)centry_uint32(centry);
1377 status = centry->status;
1378 if (NT_STATUS_IS_OK(status)) {
1379 centry_sid(centry, mem_ctx, sid);
1382 DEBUG(10,("name_to_sid: [Cached] - cached name for domain %s status: %s\n",
1383 domain->name, nt_errstr(status) ));
1385 centry_free(centry);
1386 return status;
1388 do_query:
1389 ZERO_STRUCTP(sid);
1391 /* If the seq number check indicated that there is a problem
1392 * with this DC, then return that status... except for
1393 * access_denied. This is special because the dc may be in
1394 * "restrict anonymous = 1" mode, in which case it will deny
1395 * most unauthenticated operations, but *will* allow the LSA
1396 * name-to-sid that we try as a fallback. */
1398 if (!(NT_STATUS_IS_OK(domain->last_status)
1399 || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1400 return domain->last_status;
1402 DEBUG(10,("name_to_sid: [Cached] - doing backend query for name for domain %s\n",
1403 domain->name ));
1405 status = domain->backend->name_to_sid(domain, mem_ctx, domain_name, name, sid, type);
1407 /* and save it */
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 if (NT_STATUS_IS_OK(status)) {
1415 strupper_m(CONST_DISCARD(char *,domain_name));
1416 strlower_m(CONST_DISCARD(char *,name));
1417 wcache_save_sid_to_name(domain, status, sid, domain_name, name, *type);
1420 return status;
1423 /* convert a sid to a user or group name. The sid is guaranteed to be in the domain
1424 given */
1425 static NTSTATUS sid_to_name(struct winbindd_domain *domain,
1426 TALLOC_CTX *mem_ctx,
1427 const DOM_SID *sid,
1428 char **domain_name,
1429 char **name,
1430 enum lsa_SidType *type)
1432 struct winbind_cache *cache = get_cache(domain);
1433 struct cache_entry *centry = NULL;
1434 NTSTATUS status;
1435 fstring sid_string;
1437 if (!cache->tdb)
1438 goto do_query;
1440 centry = wcache_fetch(cache, domain, "SN/%s", sid_to_string(sid_string, sid));
1441 if (!centry)
1442 goto do_query;
1443 if (NT_STATUS_IS_OK(centry->status)) {
1444 *type = (enum lsa_SidType)centry_uint32(centry);
1445 *domain_name = centry_string(centry, mem_ctx);
1446 *name = centry_string(centry, mem_ctx);
1448 status = centry->status;
1450 DEBUG(10,("sid_to_name: [Cached] - cached name for domain %s status: %s\n",
1451 domain->name, nt_errstr(status) ));
1453 centry_free(centry);
1454 return status;
1456 do_query:
1457 *name = NULL;
1458 *domain_name = NULL;
1460 /* If the seq number check indicated that there is a problem
1461 * with this DC, then return that status... except for
1462 * access_denied. This is special because the dc may be in
1463 * "restrict anonymous = 1" mode, in which case it will deny
1464 * most unauthenticated operations, but *will* allow the LSA
1465 * sid-to-name that we try as a fallback. */
1467 if (!(NT_STATUS_IS_OK(domain->last_status)
1468 || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1469 return domain->last_status;
1471 DEBUG(10,("sid_to_name: [Cached] - doing backend query for name for domain %s\n",
1472 domain->name ));
1474 status = domain->backend->sid_to_name(domain, mem_ctx, sid, domain_name, name, type);
1476 /* and save it */
1477 refresh_sequence_number(domain, False);
1478 wcache_save_sid_to_name(domain, status, sid, *domain_name, *name, *type);
1480 /* We can't save the name to sid mapping here, as with sid history a
1481 * later name2sid would give the wrong sid. */
1483 return status;
1486 static NTSTATUS rids_to_names(struct winbindd_domain *domain,
1487 TALLOC_CTX *mem_ctx,
1488 const DOM_SID *domain_sid,
1489 uint32 *rids,
1490 size_t num_rids,
1491 char **domain_name,
1492 char ***names,
1493 enum lsa_SidType **types)
1495 struct winbind_cache *cache = get_cache(domain);
1496 size_t i;
1497 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
1498 BOOL have_mapped;
1499 BOOL have_unmapped;
1501 *domain_name = NULL;
1502 *names = NULL;
1503 *types = NULL;
1505 if (!cache->tdb) {
1506 goto do_query;
1509 if (num_rids == 0) {
1510 return NT_STATUS_OK;
1513 *names = TALLOC_ARRAY(mem_ctx, char *, num_rids);
1514 *types = TALLOC_ARRAY(mem_ctx, enum lsa_SidType, num_rids);
1516 if ((*names == NULL) || (*types == NULL)) {
1517 result = NT_STATUS_NO_MEMORY;
1518 goto error;
1521 have_mapped = have_unmapped = False;
1523 for (i=0; i<num_rids; i++) {
1524 DOM_SID sid;
1525 struct cache_entry *centry;
1527 if (!sid_compose(&sid, domain_sid, rids[i])) {
1528 result = NT_STATUS_INTERNAL_ERROR;
1529 goto error;
1532 centry = wcache_fetch(cache, domain, "SN/%s",
1533 sid_string_static(&sid));
1534 if (!centry) {
1535 goto do_query;
1538 (*types)[i] = SID_NAME_UNKNOWN;
1539 (*names)[i] = talloc_strdup(*names, "");
1541 if (NT_STATUS_IS_OK(centry->status)) {
1542 char *dom;
1543 have_mapped = True;
1544 (*types)[i] = (enum lsa_SidType)centry_uint32(centry);
1545 dom = centry_string(centry, mem_ctx);
1546 if (*domain_name == NULL) {
1547 *domain_name = dom;
1548 } else {
1549 talloc_free(dom);
1551 (*names)[i] = centry_string(centry, *names);
1552 } else {
1553 have_unmapped = True;
1556 centry_free(centry);
1559 if (!have_mapped) {
1560 return NT_STATUS_NONE_MAPPED;
1562 if (!have_unmapped) {
1563 return NT_STATUS_OK;
1565 return STATUS_SOME_UNMAPPED;
1567 do_query:
1569 TALLOC_FREE(*names);
1570 TALLOC_FREE(*types);
1572 result = domain->backend->rids_to_names(domain, mem_ctx, domain_sid,
1573 rids, num_rids, domain_name,
1574 names, types);
1576 if (!NT_STATUS_IS_OK(result) &&
1577 !NT_STATUS_EQUAL(result, STATUS_SOME_UNMAPPED)) {
1578 return result;
1581 refresh_sequence_number(domain, False);
1583 for (i=0; i<num_rids; i++) {
1584 DOM_SID sid;
1585 NTSTATUS status;
1587 if (!sid_compose(&sid, domain_sid, rids[i])) {
1588 result = NT_STATUS_INTERNAL_ERROR;
1589 goto error;
1592 status = (*types)[i] == SID_NAME_UNKNOWN ?
1593 NT_STATUS_NONE_MAPPED : NT_STATUS_OK;
1595 wcache_save_sid_to_name(domain, status, &sid, *domain_name,
1596 (*names)[i], (*types)[i]);
1599 return result;
1601 error:
1603 TALLOC_FREE(*names);
1604 TALLOC_FREE(*types);
1605 return result;
1608 /* Lookup user information from a rid */
1609 static NTSTATUS query_user(struct winbindd_domain *domain,
1610 TALLOC_CTX *mem_ctx,
1611 const DOM_SID *user_sid,
1612 WINBIND_USERINFO *info)
1614 struct winbind_cache *cache = get_cache(domain);
1615 struct cache_entry *centry = NULL;
1616 NTSTATUS status;
1618 if (!cache->tdb)
1619 goto do_query;
1621 centry = wcache_fetch(cache, domain, "U/%s", sid_string_static(user_sid));
1623 /* If we have an access denied cache entry and a cached info3 in the
1624 samlogon cache then do a query. This will force the rpc back end
1625 to return the info3 data. */
1627 if (NT_STATUS_V(domain->last_status) == NT_STATUS_V(NT_STATUS_ACCESS_DENIED) &&
1628 netsamlogon_cache_have(user_sid)) {
1629 DEBUG(10, ("query_user: cached access denied and have cached info3\n"));
1630 domain->last_status = NT_STATUS_OK;
1631 centry_free(centry);
1632 goto do_query;
1635 if (!centry)
1636 goto do_query;
1638 info->acct_name = centry_string(centry, mem_ctx);
1639 info->full_name = centry_string(centry, mem_ctx);
1640 info->homedir = centry_string(centry, mem_ctx);
1641 info->shell = centry_string(centry, mem_ctx);
1642 info->primary_gid = centry_uint32(centry);
1643 centry_sid(centry, mem_ctx, &info->user_sid);
1644 centry_sid(centry, mem_ctx, &info->group_sid);
1645 status = centry->status;
1647 DEBUG(10,("query_user: [Cached] - cached info for domain %s status: %s\n",
1648 domain->name, nt_errstr(status) ));
1650 centry_free(centry);
1651 return status;
1653 do_query:
1654 ZERO_STRUCTP(info);
1656 /* Return status value returned by seq number check */
1658 if (!NT_STATUS_IS_OK(domain->last_status))
1659 return domain->last_status;
1661 DEBUG(10,("query_user: [Cached] - doing backend query for info for domain %s\n",
1662 domain->name ));
1664 status = domain->backend->query_user(domain, mem_ctx, user_sid, info);
1666 /* and save it */
1667 refresh_sequence_number(domain, False);
1668 wcache_save_user(domain, status, info);
1670 return status;
1674 /* Lookup groups a user is a member of. */
1675 static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
1676 TALLOC_CTX *mem_ctx,
1677 const DOM_SID *user_sid,
1678 uint32 *num_groups, DOM_SID **user_gids)
1680 struct winbind_cache *cache = get_cache(domain);
1681 struct cache_entry *centry = NULL;
1682 NTSTATUS status;
1683 unsigned int i;
1684 fstring sid_string;
1686 if (!cache->tdb)
1687 goto do_query;
1689 centry = wcache_fetch(cache, domain, "UG/%s", sid_to_string(sid_string, user_sid));
1691 /* If we have an access denied cache entry and a cached info3 in the
1692 samlogon cache then do a query. This will force the rpc back end
1693 to return the info3 data. */
1695 if (NT_STATUS_V(domain->last_status) == NT_STATUS_V(NT_STATUS_ACCESS_DENIED) &&
1696 netsamlogon_cache_have(user_sid)) {
1697 DEBUG(10, ("lookup_usergroups: cached access denied and have cached info3\n"));
1698 domain->last_status = NT_STATUS_OK;
1699 centry_free(centry);
1700 goto do_query;
1703 if (!centry)
1704 goto do_query;
1706 *num_groups = centry_uint32(centry);
1708 if (*num_groups == 0)
1709 goto do_cached;
1711 (*user_gids) = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_groups);
1712 if (! (*user_gids)) {
1713 smb_panic_fn("lookup_usergroups out of memory");
1715 for (i=0; i<(*num_groups); i++) {
1716 centry_sid(centry, mem_ctx, &(*user_gids)[i]);
1719 do_cached:
1720 status = centry->status;
1722 DEBUG(10,("lookup_usergroups: [Cached] - cached info for domain %s status: %s\n",
1723 domain->name, nt_errstr(status) ));
1725 centry_free(centry);
1726 return status;
1728 do_query:
1729 (*num_groups) = 0;
1730 (*user_gids) = NULL;
1732 /* Return status value returned by seq number check */
1734 if (!NT_STATUS_IS_OK(domain->last_status))
1735 return domain->last_status;
1737 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info for domain %s\n",
1738 domain->name ));
1740 status = domain->backend->lookup_usergroups(domain, mem_ctx, user_sid, num_groups, user_gids);
1742 if ( NT_STATUS_EQUAL(status, NT_STATUS_SYNCHRONIZATION_REQUIRED) )
1743 goto skip_save;
1745 /* and save it */
1746 refresh_sequence_number(domain, False);
1747 centry = centry_start(domain, status);
1748 if (!centry)
1749 goto skip_save;
1750 centry_put_uint32(centry, *num_groups);
1751 for (i=0; i<(*num_groups); i++) {
1752 centry_put_sid(centry, &(*user_gids)[i]);
1754 centry_end(centry, "UG/%s", sid_to_string(sid_string, user_sid));
1755 centry_free(centry);
1757 skip_save:
1758 return status;
1761 static NTSTATUS lookup_useraliases(struct winbindd_domain *domain,
1762 TALLOC_CTX *mem_ctx,
1763 uint32 num_sids, const DOM_SID *sids,
1764 uint32 *num_aliases, uint32 **alias_rids)
1766 struct winbind_cache *cache = get_cache(domain);
1767 struct cache_entry *centry = NULL;
1768 NTSTATUS status;
1769 char *sidlist = talloc_strdup(mem_ctx, "");
1770 int i;
1772 if (!cache->tdb)
1773 goto do_query;
1775 if (num_sids == 0) {
1776 *num_aliases = 0;
1777 *alias_rids = NULL;
1778 return NT_STATUS_OK;
1781 /* We need to cache indexed by the whole list of SIDs, the aliases
1782 * resulting might come from any of the SIDs. */
1784 for (i=0; i<num_sids; i++) {
1785 sidlist = talloc_asprintf(mem_ctx, "%s/%s", sidlist,
1786 sid_string_static(&sids[i]));
1787 if (sidlist == NULL)
1788 return NT_STATUS_NO_MEMORY;
1791 centry = wcache_fetch(cache, domain, "UA%s", sidlist);
1793 if (!centry)
1794 goto do_query;
1796 *num_aliases = centry_uint32(centry);
1797 *alias_rids = NULL;
1799 if (*num_aliases) {
1800 (*alias_rids) = TALLOC_ARRAY(mem_ctx, uint32, *num_aliases);
1802 if ((*alias_rids) == NULL) {
1803 centry_free(centry);
1804 return NT_STATUS_NO_MEMORY;
1806 } else {
1807 (*alias_rids) = NULL;
1810 for (i=0; i<(*num_aliases); i++)
1811 (*alias_rids)[i] = centry_uint32(centry);
1813 status = centry->status;
1815 DEBUG(10,("lookup_useraliases: [Cached] - cached info for domain: %s "
1816 "status %s\n", domain->name, nt_errstr(status)));
1818 centry_free(centry);
1819 return status;
1821 do_query:
1822 (*num_aliases) = 0;
1823 (*alias_rids) = NULL;
1825 if (!NT_STATUS_IS_OK(domain->last_status))
1826 return domain->last_status;
1828 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info "
1829 "for domain %s\n", domain->name ));
1831 status = domain->backend->lookup_useraliases(domain, mem_ctx,
1832 num_sids, sids,
1833 num_aliases, alias_rids);
1835 /* and save it */
1836 refresh_sequence_number(domain, False);
1837 centry = centry_start(domain, status);
1838 if (!centry)
1839 goto skip_save;
1840 centry_put_uint32(centry, *num_aliases);
1841 for (i=0; i<(*num_aliases); i++)
1842 centry_put_uint32(centry, (*alias_rids)[i]);
1843 centry_end(centry, "UA%s", sidlist);
1844 centry_free(centry);
1846 skip_save:
1847 return status;
1851 static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
1852 TALLOC_CTX *mem_ctx,
1853 const DOM_SID *group_sid, uint32 *num_names,
1854 DOM_SID **sid_mem, char ***names,
1855 uint32 **name_types)
1857 struct winbind_cache *cache = get_cache(domain);
1858 struct cache_entry *centry = NULL;
1859 NTSTATUS status;
1860 unsigned int i;
1861 fstring sid_string;
1863 if (!cache->tdb)
1864 goto do_query;
1866 centry = wcache_fetch(cache, domain, "GM/%s", sid_to_string(sid_string, group_sid));
1867 if (!centry)
1868 goto do_query;
1870 *num_names = centry_uint32(centry);
1872 if (*num_names == 0)
1873 goto do_cached;
1875 (*sid_mem) = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_names);
1876 (*names) = TALLOC_ARRAY(mem_ctx, char *, *num_names);
1877 (*name_types) = TALLOC_ARRAY(mem_ctx, uint32, *num_names);
1879 if (! (*sid_mem) || ! (*names) || ! (*name_types)) {
1880 smb_panic_fn("lookup_groupmem out of memory");
1883 for (i=0; i<(*num_names); i++) {
1884 centry_sid(centry, mem_ctx, &(*sid_mem)[i]);
1885 (*names)[i] = centry_string(centry, mem_ctx);
1886 (*name_types)[i] = centry_uint32(centry);
1889 do_cached:
1890 status = centry->status;
1892 DEBUG(10,("lookup_groupmem: [Cached] - cached info for domain %s status: %s\n",
1893 domain->name, nt_errstr(status)));
1895 centry_free(centry);
1896 return status;
1898 do_query:
1899 (*num_names) = 0;
1900 (*sid_mem) = NULL;
1901 (*names) = NULL;
1902 (*name_types) = NULL;
1904 /* Return status value returned by seq number check */
1906 if (!NT_STATUS_IS_OK(domain->last_status))
1907 return domain->last_status;
1909 DEBUG(10,("lookup_groupmem: [Cached] - doing backend query for info for domain %s\n",
1910 domain->name ));
1912 status = domain->backend->lookup_groupmem(domain, mem_ctx, group_sid, num_names,
1913 sid_mem, names, name_types);
1915 /* and save it */
1916 refresh_sequence_number(domain, False);
1917 centry = centry_start(domain, status);
1918 if (!centry)
1919 goto skip_save;
1920 centry_put_uint32(centry, *num_names);
1921 for (i=0; i<(*num_names); i++) {
1922 centry_put_sid(centry, &(*sid_mem)[i]);
1923 centry_put_string(centry, (*names)[i]);
1924 centry_put_uint32(centry, (*name_types)[i]);
1926 centry_end(centry, "GM/%s", sid_to_string(sid_string, group_sid));
1927 centry_free(centry);
1929 skip_save:
1930 return status;
1933 /* find the sequence number for a domain */
1934 static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
1936 refresh_sequence_number(domain, False);
1938 *seq = domain->sequence_number;
1940 return NT_STATUS_OK;
1943 /* enumerate trusted domains
1944 * (we need to have the list of trustdoms in the cache when we go offline) -
1945 * Guenther */
1946 static NTSTATUS trusted_domains(struct winbindd_domain *domain,
1947 TALLOC_CTX *mem_ctx,
1948 uint32 *num_domains,
1949 char ***names,
1950 char ***alt_names,
1951 DOM_SID **dom_sids)
1953 struct winbind_cache *cache = get_cache(domain);
1954 struct cache_entry *centry = NULL;
1955 NTSTATUS status;
1956 int i;
1958 if (!cache->tdb)
1959 goto do_query;
1961 centry = wcache_fetch(cache, domain, "TRUSTDOMS/%s", domain->name);
1963 if (!centry) {
1964 goto do_query;
1967 *num_domains = centry_uint32(centry);
1969 if (*num_domains) {
1970 (*names) = TALLOC_ARRAY(mem_ctx, char *, *num_domains);
1971 (*alt_names) = TALLOC_ARRAY(mem_ctx, char *, *num_domains);
1972 (*dom_sids) = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_domains);
1974 if (! (*dom_sids) || ! (*names) || ! (*alt_names)) {
1975 smb_panic_fn("trusted_domains out of memory");
1977 } else {
1978 (*names) = NULL;
1979 (*alt_names) = NULL;
1980 (*dom_sids) = NULL;
1983 for (i=0; i<(*num_domains); i++) {
1984 (*names)[i] = centry_string(centry, mem_ctx);
1985 (*alt_names)[i] = centry_string(centry, mem_ctx);
1986 centry_sid(centry, mem_ctx, &(*dom_sids)[i]);
1989 status = centry->status;
1991 DEBUG(10,("trusted_domains: [Cached] - cached info for domain %s (%d trusts) status: %s\n",
1992 domain->name, *num_domains, nt_errstr(status) ));
1994 centry_free(centry);
1995 return status;
1997 do_query:
1998 (*num_domains) = 0;
1999 (*dom_sids) = NULL;
2000 (*names) = NULL;
2001 (*alt_names) = NULL;
2003 /* Return status value returned by seq number check */
2005 if (!NT_STATUS_IS_OK(domain->last_status))
2006 return domain->last_status;
2008 DEBUG(10,("trusted_domains: [Cached] - doing backend query for info for domain %s\n",
2009 domain->name ));
2011 status = domain->backend->trusted_domains(domain, mem_ctx, num_domains,
2012 names, alt_names, dom_sids);
2014 /* no trusts gives NT_STATUS_NO_MORE_ENTRIES resetting to NT_STATUS_OK
2015 * so that the generic centry handling still applies correctly -
2016 * Guenther*/
2018 if (!NT_STATUS_IS_ERR(status)) {
2019 status = NT_STATUS_OK;
2023 #if 0 /* Disabled as we want the trust dom list to be managed by
2024 the main parent and always to make the query. --jerry */
2026 /* and save it */
2027 refresh_sequence_number(domain, False);
2029 centry = centry_start(domain, status);
2030 if (!centry)
2031 goto skip_save;
2033 centry_put_uint32(centry, *num_domains);
2035 for (i=0; i<(*num_domains); i++) {
2036 centry_put_string(centry, (*names)[i]);
2037 centry_put_string(centry, (*alt_names)[i]);
2038 centry_put_sid(centry, &(*dom_sids)[i]);
2041 centry_end(centry, "TRUSTDOMS/%s", domain->name);
2043 centry_free(centry);
2045 skip_save:
2046 #endif
2048 return status;
2051 /* get lockout policy */
2052 static NTSTATUS lockout_policy(struct winbindd_domain *domain,
2053 TALLOC_CTX *mem_ctx,
2054 SAM_UNK_INFO_12 *policy){
2055 struct winbind_cache *cache = get_cache(domain);
2056 struct cache_entry *centry = NULL;
2057 NTSTATUS status;
2059 if (!cache->tdb)
2060 goto do_query;
2062 centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
2064 if (!centry)
2065 goto do_query;
2067 policy->duration = centry_nttime(centry);
2068 policy->reset_count = centry_nttime(centry);
2069 policy->bad_attempt_lockout = centry_uint16(centry);
2071 status = centry->status;
2073 DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2074 domain->name, nt_errstr(status) ));
2076 centry_free(centry);
2077 return status;
2079 do_query:
2080 ZERO_STRUCTP(policy);
2082 /* Return status value returned by seq number check */
2084 if (!NT_STATUS_IS_OK(domain->last_status))
2085 return domain->last_status;
2087 DEBUG(10,("lockout_policy: [Cached] - doing backend query for info for domain %s\n",
2088 domain->name ));
2090 status = domain->backend->lockout_policy(domain, mem_ctx, policy);
2092 /* and save it */
2093 refresh_sequence_number(domain, False);
2094 wcache_save_lockout_policy(domain, status, policy);
2096 return status;
2099 /* get password policy */
2100 static NTSTATUS password_policy(struct winbindd_domain *domain,
2101 TALLOC_CTX *mem_ctx,
2102 SAM_UNK_INFO_1 *policy)
2104 struct winbind_cache *cache = get_cache(domain);
2105 struct cache_entry *centry = NULL;
2106 NTSTATUS status;
2108 if (!cache->tdb)
2109 goto do_query;
2111 centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
2113 if (!centry)
2114 goto do_query;
2116 policy->min_length_password = centry_uint16(centry);
2117 policy->password_history = centry_uint16(centry);
2118 policy->password_properties = centry_uint32(centry);
2119 policy->expire = centry_nttime(centry);
2120 policy->min_passwordage = centry_nttime(centry);
2122 status = centry->status;
2124 DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2125 domain->name, nt_errstr(status) ));
2127 centry_free(centry);
2128 return status;
2130 do_query:
2131 ZERO_STRUCTP(policy);
2133 /* Return status value returned by seq number check */
2135 if (!NT_STATUS_IS_OK(domain->last_status))
2136 return domain->last_status;
2138 DEBUG(10,("password_policy: [Cached] - doing backend query for info for domain %s\n",
2139 domain->name ));
2141 status = domain->backend->password_policy(domain, mem_ctx, policy);
2143 /* and save it */
2144 refresh_sequence_number(domain, False);
2145 wcache_save_password_policy(domain, status, policy);
2147 return status;
2151 /* Invalidate cached user and group lists coherently */
2153 static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
2154 void *state)
2156 if (strncmp((const char *)kbuf.dptr, "UL/", 3) == 0 ||
2157 strncmp((const char *)kbuf.dptr, "GL/", 3) == 0)
2158 tdb_delete(the_tdb, kbuf);
2160 return 0;
2163 /* Invalidate the getpwnam and getgroups entries for a winbindd domain */
2165 void wcache_invalidate_samlogon(struct winbindd_domain *domain,
2166 NET_USER_INFO_3 *info3)
2168 struct winbind_cache *cache;
2170 /* dont clear cached U/SID and UG/SID entries when we want to logon
2171 * offline - gd */
2173 if (lp_winbind_offline_logon()) {
2174 return;
2177 if (!domain)
2178 return;
2180 cache = get_cache(domain);
2181 netsamlogon_clear_cached_user(cache->tdb, info3);
2184 void wcache_invalidate_cache(void)
2186 struct winbindd_domain *domain;
2188 for (domain = domain_list(); domain; domain = domain->next) {
2189 struct winbind_cache *cache = get_cache(domain);
2191 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
2192 "entries for %s\n", domain->name));
2193 if (cache)
2194 tdb_traverse(cache->tdb, traverse_fn, NULL);
2198 BOOL init_wcache(void)
2200 if (wcache == NULL) {
2201 wcache = SMB_XMALLOC_P(struct winbind_cache);
2202 ZERO_STRUCTP(wcache);
2205 if (wcache->tdb != NULL)
2206 return True;
2208 /* when working offline we must not clear the cache on restart */
2209 wcache->tdb = tdb_open_log(lock_path("winbindd_cache.tdb"),
2210 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
2211 lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST),
2212 O_RDWR|O_CREAT, 0600);
2214 if (wcache->tdb == NULL) {
2215 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
2216 return False;
2219 return True;
2222 /************************************************************************
2223 This is called by the parent to initialize the cache file.
2224 We don't need sophisticated locking here as we know we're the
2225 only opener.
2226 ************************************************************************/
2228 BOOL initialize_winbindd_cache(void)
2230 BOOL cache_bad = True;
2231 uint32 vers;
2233 if (!init_wcache()) {
2234 DEBUG(0,("initialize_winbindd_cache: init_wcache failed.\n"));
2235 return False;
2238 /* Check version number. */
2239 if (tdb_fetch_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, &vers) &&
2240 vers == WINBINDD_CACHE_VERSION) {
2241 cache_bad = False;
2244 if (cache_bad) {
2245 DEBUG(0,("initialize_winbindd_cache: clearing cache "
2246 "and re-creating with version number %d\n",
2247 WINBINDD_CACHE_VERSION ));
2249 tdb_close(wcache->tdb);
2250 wcache->tdb = NULL;
2252 if (unlink(lock_path("winbindd_cache.tdb")) == -1) {
2253 DEBUG(0,("initialize_winbindd_cache: unlink %s failed %s ",
2254 lock_path("winbindd_cache.tdb"),
2255 strerror(errno) ));
2256 return False;
2258 if (!init_wcache()) {
2259 DEBUG(0,("initialize_winbindd_cache: re-initialization "
2260 "init_wcache failed.\n"));
2261 return False;
2264 /* Write the version. */
2265 if (!tdb_store_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, WINBINDD_CACHE_VERSION)) {
2266 DEBUG(0,("initialize_winbindd_cache: version number store failed %s\n",
2267 tdb_errorstr(wcache->tdb) ));
2268 return False;
2272 tdb_close(wcache->tdb);
2273 wcache->tdb = NULL;
2274 return True;
2277 void cache_store_response(pid_t pid, struct winbindd_response *response)
2279 fstring key_str;
2281 if (!init_wcache())
2282 return;
2284 DEBUG(10, ("Storing response for pid %d, len %d\n",
2285 pid, response->length));
2287 fstr_sprintf(key_str, "DR/%d", pid);
2288 if (tdb_store(wcache->tdb, string_tdb_data(key_str),
2289 make_tdb_data((uint8 *)response, sizeof(*response)),
2290 TDB_REPLACE) == -1)
2291 return;
2293 if (response->length == sizeof(*response))
2294 return;
2296 /* There's extra data */
2298 DEBUG(10, ("Storing extra data: len=%d\n",
2299 (int)(response->length - sizeof(*response))));
2301 fstr_sprintf(key_str, "DE/%d", pid);
2302 if (tdb_store(wcache->tdb, string_tdb_data(key_str),
2303 make_tdb_data((uint8 *)response->extra_data.data,
2304 response->length - sizeof(*response)),
2305 TDB_REPLACE) == 0)
2306 return;
2308 /* We could not store the extra data, make sure the tdb does not
2309 * contain a main record with wrong dangling extra data */
2311 fstr_sprintf(key_str, "DR/%d", pid);
2312 tdb_delete(wcache->tdb, string_tdb_data(key_str));
2314 return;
2317 BOOL cache_retrieve_response(pid_t pid, struct winbindd_response * response)
2319 TDB_DATA data;
2320 fstring key_str;
2322 if (!init_wcache())
2323 return False;
2325 DEBUG(10, ("Retrieving response for pid %d\n", pid));
2327 fstr_sprintf(key_str, "DR/%d", pid);
2328 data = tdb_fetch(wcache->tdb, string_tdb_data(key_str));
2330 if (data.dptr == NULL)
2331 return False;
2333 if (data.dsize != sizeof(*response))
2334 return False;
2336 memcpy(response, data.dptr, data.dsize);
2337 SAFE_FREE(data.dptr);
2339 if (response->length == sizeof(*response)) {
2340 response->extra_data.data = NULL;
2341 return True;
2344 /* There's extra data */
2346 DEBUG(10, ("Retrieving extra data length=%d\n",
2347 (int)(response->length - sizeof(*response))));
2349 fstr_sprintf(key_str, "DE/%d", pid);
2350 data = tdb_fetch(wcache->tdb, string_tdb_data(key_str));
2352 if (data.dptr == NULL) {
2353 DEBUG(0, ("Did not find extra data\n"));
2354 return False;
2357 if (data.dsize != (response->length - sizeof(*response))) {
2358 DEBUG(0, ("Invalid extra data length: %d\n", (int)data.dsize));
2359 SAFE_FREE(data.dptr);
2360 return False;
2363 dump_data(11, (uint8 *)data.dptr, data.dsize);
2365 response->extra_data.data = data.dptr;
2366 return True;
2369 void cache_cleanup_response(pid_t pid)
2371 fstring key_str;
2373 if (!init_wcache())
2374 return;
2376 fstr_sprintf(key_str, "DR/%d", pid);
2377 tdb_delete(wcache->tdb, string_tdb_data(key_str));
2379 fstr_sprintf(key_str, "DE/%d", pid);
2380 tdb_delete(wcache->tdb, string_tdb_data(key_str));
2382 return;
2386 BOOL lookup_cached_sid(TALLOC_CTX *mem_ctx, const DOM_SID *sid,
2387 char **domain_name, char **name,
2388 enum lsa_SidType *type)
2390 struct winbindd_domain *domain;
2391 struct winbind_cache *cache;
2392 struct cache_entry *centry = NULL;
2393 NTSTATUS status;
2395 domain = find_lookup_domain_from_sid(sid);
2396 if (domain == NULL) {
2397 return False;
2400 cache = get_cache(domain);
2402 if (cache->tdb == NULL) {
2403 return False;
2406 centry = wcache_fetch(cache, domain, "SN/%s", sid_string_static(sid));
2407 if (centry == NULL) {
2408 return False;
2411 if (NT_STATUS_IS_OK(centry->status)) {
2412 *type = (enum lsa_SidType)centry_uint32(centry);
2413 *domain_name = centry_string(centry, mem_ctx);
2414 *name = centry_string(centry, mem_ctx);
2417 status = centry->status;
2418 centry_free(centry);
2419 return NT_STATUS_IS_OK(status);
2422 BOOL lookup_cached_name(TALLOC_CTX *mem_ctx,
2423 const char *domain_name,
2424 const char *name,
2425 DOM_SID *sid,
2426 enum lsa_SidType *type)
2428 struct winbindd_domain *domain;
2429 struct winbind_cache *cache;
2430 struct cache_entry *centry = NULL;
2431 NTSTATUS status;
2432 fstring uname;
2433 BOOL original_online_state;
2435 domain = find_lookup_domain_from_name(domain_name);
2436 if (domain == NULL) {
2437 return False;
2440 cache = get_cache(domain);
2442 if (cache->tdb == NULL) {
2443 return False;
2446 fstrcpy(uname, name);
2447 strupper_m(uname);
2449 /* If we are doing a cached logon, temporarily set the domain
2450 offline so the cache won't expire the entry */
2452 original_online_state = domain->online;
2453 domain->online = False;
2454 centry = wcache_fetch(cache, domain, "NS/%s/%s", domain_name, uname);
2455 domain->online = original_online_state;
2457 if (centry == NULL) {
2458 return False;
2461 if (NT_STATUS_IS_OK(centry->status)) {
2462 *type = (enum lsa_SidType)centry_uint32(centry);
2463 centry_sid(centry, mem_ctx, sid);
2466 status = centry->status;
2467 centry_free(centry);
2469 return NT_STATUS_IS_OK(status);
2472 void cache_name2sid(struct winbindd_domain *domain,
2473 const char *domain_name, const char *name,
2474 enum lsa_SidType type, const DOM_SID *sid)
2476 refresh_sequence_number(domain, False);
2477 wcache_save_name_to_sid(domain, NT_STATUS_OK, domain_name, name,
2478 sid, type);
2482 * The original idea that this cache only contains centries has
2483 * been blurred - now other stuff gets put in here. Ensure we
2484 * ignore these things on cleanup.
2487 static int traverse_fn_cleanup(TDB_CONTEXT *the_tdb, TDB_DATA kbuf,
2488 TDB_DATA dbuf, void *state)
2490 struct cache_entry *centry;
2492 if (is_non_centry_key(kbuf)) {
2493 return 0;
2496 centry = wcache_fetch_raw((char *)kbuf.dptr);
2497 if (!centry) {
2498 return 0;
2501 if (!NT_STATUS_IS_OK(centry->status)) {
2502 DEBUG(10,("deleting centry %s\n", (const char *)kbuf.dptr));
2503 tdb_delete(the_tdb, kbuf);
2506 centry_free(centry);
2507 return 0;
2510 /* flush the cache */
2511 void wcache_flush_cache(void)
2513 if (!wcache)
2514 return;
2515 if (wcache->tdb) {
2516 tdb_close(wcache->tdb);
2517 wcache->tdb = NULL;
2519 if (opt_nocache)
2520 return;
2522 /* when working offline we must not clear the cache on restart */
2523 wcache->tdb = tdb_open_log(lock_path("winbindd_cache.tdb"),
2524 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
2525 lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST),
2526 O_RDWR|O_CREAT, 0600);
2528 if (!wcache->tdb) {
2529 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
2530 return;
2533 tdb_traverse(wcache->tdb, traverse_fn_cleanup, NULL);
2535 DEBUG(10,("wcache_flush_cache success\n"));
2538 /* Count cached creds */
2540 static int traverse_fn_cached_creds(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
2541 void *state)
2543 int *cred_count = (int*)state;
2545 if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
2546 (*cred_count)++;
2548 return 0;
2551 NTSTATUS wcache_count_cached_creds(struct winbindd_domain *domain, int *count)
2553 struct winbind_cache *cache = get_cache(domain);
2555 *count = 0;
2557 if (!cache->tdb) {
2558 return NT_STATUS_INTERNAL_DB_ERROR;
2561 tdb_traverse(cache->tdb, traverse_fn_cached_creds, (void *)count);
2563 return NT_STATUS_OK;
2566 struct cred_list {
2567 struct cred_list *prev, *next;
2568 TDB_DATA key;
2569 fstring name;
2570 time_t created;
2572 static struct cred_list *wcache_cred_list;
2574 static int traverse_fn_get_credlist(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
2575 void *state)
2577 struct cred_list *cred;
2579 if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
2581 cred = SMB_MALLOC_P(struct cred_list);
2582 if (cred == NULL) {
2583 DEBUG(0,("traverse_fn_remove_first_creds: failed to malloc new entry for list\n"));
2584 return -1;
2587 ZERO_STRUCTP(cred);
2589 /* save a copy of the key */
2591 fstrcpy(cred->name, (const char *)kbuf.dptr);
2592 DLIST_ADD(wcache_cred_list, cred);
2595 return 0;
2598 NTSTATUS wcache_remove_oldest_cached_creds(struct winbindd_domain *domain, const DOM_SID *sid)
2600 struct winbind_cache *cache = get_cache(domain);
2601 NTSTATUS status;
2602 int ret;
2603 struct cred_list *cred, *oldest = NULL;
2605 if (!cache->tdb) {
2606 return NT_STATUS_INTERNAL_DB_ERROR;
2609 /* we possibly already have an entry */
2610 if (sid && NT_STATUS_IS_OK(wcache_cached_creds_exist(domain, sid))) {
2612 fstring key_str;
2614 DEBUG(11,("we already have an entry, deleting that\n"));
2616 fstr_sprintf(key_str, "CRED/%s", sid_string_static(sid));
2618 tdb_delete(cache->tdb, string_tdb_data(key_str));
2620 return NT_STATUS_OK;
2623 ret = tdb_traverse(cache->tdb, traverse_fn_get_credlist, NULL);
2624 if (ret == 0) {
2625 return NT_STATUS_OK;
2626 } else if ((ret == -1) || (wcache_cred_list == NULL)) {
2627 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
2630 ZERO_STRUCTP(oldest);
2632 for (cred = wcache_cred_list; cred; cred = cred->next) {
2634 TDB_DATA data;
2635 time_t t;
2637 data = tdb_fetch(cache->tdb, string_tdb_data(cred->name));
2638 if (!data.dptr) {
2639 DEBUG(10,("wcache_remove_oldest_cached_creds: entry for [%s] not found\n",
2640 cred->name));
2641 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
2642 goto done;
2645 t = IVAL(data.dptr, 0);
2646 SAFE_FREE(data.dptr);
2648 if (!oldest) {
2649 oldest = SMB_MALLOC_P(struct cred_list);
2650 if (oldest == NULL) {
2651 status = NT_STATUS_NO_MEMORY;
2652 goto done;
2655 fstrcpy(oldest->name, cred->name);
2656 oldest->created = t;
2657 continue;
2660 if (t < oldest->created) {
2661 fstrcpy(oldest->name, cred->name);
2662 oldest->created = t;
2666 if (tdb_delete(cache->tdb, string_tdb_data(oldest->name)) == 0) {
2667 status = NT_STATUS_OK;
2668 } else {
2669 status = NT_STATUS_UNSUCCESSFUL;
2671 done:
2672 SAFE_FREE(wcache_cred_list);
2673 SAFE_FREE(oldest);
2675 return status;
2678 /* Change the global online/offline state. */
2679 BOOL set_global_winbindd_state_offline(void)
2681 TDB_DATA data;
2683 DEBUG(10,("set_global_winbindd_state_offline: offline requested.\n"));
2685 /* Only go offline if someone has created
2686 the key "WINBINDD_OFFLINE" in the cache tdb. */
2688 if (wcache == NULL || wcache->tdb == NULL) {
2689 DEBUG(10,("set_global_winbindd_state_offline: wcache not open yet.\n"));
2690 return False;
2693 if (!lp_winbind_offline_logon()) {
2694 DEBUG(10,("set_global_winbindd_state_offline: rejecting.\n"));
2695 return False;
2698 if (global_winbindd_offline_state) {
2699 /* Already offline. */
2700 return True;
2703 data = tdb_fetch_bystring( wcache->tdb, "WINBINDD_OFFLINE" );
2705 if (!data.dptr || data.dsize != 4) {
2706 DEBUG(10,("set_global_winbindd_state_offline: offline state not set.\n"));
2707 SAFE_FREE(data.dptr);
2708 return False;
2709 } else {
2710 DEBUG(10,("set_global_winbindd_state_offline: offline state set.\n"));
2711 global_winbindd_offline_state = True;
2712 SAFE_FREE(data.dptr);
2713 return True;
2717 void set_global_winbindd_state_online(void)
2719 DEBUG(10,("set_global_winbindd_state_online: online requested.\n"));
2721 if (!lp_winbind_offline_logon()) {
2722 DEBUG(10,("set_global_winbindd_state_online: rejecting.\n"));
2723 return;
2726 if (!global_winbindd_offline_state) {
2727 /* Already online. */
2728 return;
2730 global_winbindd_offline_state = False;
2732 if (!wcache->tdb) {
2733 return;
2736 /* Ensure there is no key "WINBINDD_OFFLINE" in the cache tdb. */
2737 tdb_delete_bystring(wcache->tdb, "WINBINDD_OFFLINE");
2740 BOOL get_global_winbindd_state_offline(void)
2742 return global_winbindd_offline_state;
2745 /***********************************************************************
2746 Validate functions for all possible cache tdb keys.
2747 ***********************************************************************/
2749 struct validation_status {
2750 BOOL tdb_error;
2751 BOOL bad_freelist;
2752 BOOL bad_entry;
2753 BOOL unknown_key;
2754 BOOL success;
2757 static struct cache_entry *create_centry_validate(const char *kstr, TDB_DATA data,
2758 struct validation_status *state)
2760 struct cache_entry *centry;
2762 centry = SMB_XMALLOC_P(struct cache_entry);
2763 centry->data = (unsigned char *)memdup(data.dptr, data.dsize);
2764 if (!centry->data) {
2765 SAFE_FREE(centry);
2766 return NULL;
2768 centry->len = data.dsize;
2769 centry->ofs = 0;
2771 if (centry->len < 8) {
2772 /* huh? corrupt cache? */
2773 DEBUG(0,("create_centry_validate: Corrupt cache for key %s (len < 8) ?\n", kstr));
2774 centry_free(centry);
2775 state->bad_entry = True;
2776 state->success = False;
2777 return NULL;
2780 centry->status = NT_STATUS(centry_uint32(centry));
2781 centry->sequence_number = centry_uint32(centry);
2782 return centry;
2785 static int validate_seqnum(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
2786 struct validation_status *state)
2788 if (dbuf.dsize != 8) {
2789 DEBUG(0,("validate_seqnum: Corrupt cache for key %s (len %u != 8) ?\n",
2790 keystr, (unsigned int)dbuf.dsize ));
2791 state->bad_entry = True;
2792 return 1;
2794 return 0;
2797 static int validate_ns(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
2798 struct validation_status *state)
2800 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
2801 if (!centry) {
2802 return 1;
2805 (void)centry_uint32(centry);
2806 if (NT_STATUS_IS_OK(centry->status)) {
2807 DOM_SID sid;
2808 (void)centry_sid(centry, mem_ctx, &sid);
2811 centry_free(centry);
2813 if (!(state->success)) {
2814 return 1;
2816 DEBUG(10,("validate_ns: %s ok\n", keystr));
2817 return 0;
2820 static int validate_sn(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
2821 struct validation_status *state)
2823 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
2824 if (!centry) {
2825 return 1;
2828 if (NT_STATUS_IS_OK(centry->status)) {
2829 (void)centry_uint32(centry);
2830 (void)centry_string(centry, mem_ctx);
2831 (void)centry_string(centry, mem_ctx);
2834 centry_free(centry);
2836 if (!(state->success)) {
2837 return 1;
2839 DEBUG(10,("validate_sn: %s ok\n", keystr));
2840 return 0;
2843 static int validate_u(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
2844 struct validation_status *state)
2846 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
2847 DOM_SID sid;
2849 if (!centry) {
2850 return 1;
2853 (void)centry_string(centry, mem_ctx);
2854 (void)centry_string(centry, mem_ctx);
2855 (void)centry_string(centry, mem_ctx);
2856 (void)centry_string(centry, mem_ctx);
2857 (void)centry_uint32(centry);
2858 (void)centry_sid(centry, mem_ctx, &sid);
2859 (void)centry_sid(centry, mem_ctx, &sid);
2861 centry_free(centry);
2863 if (!(state->success)) {
2864 return 1;
2866 DEBUG(10,("validate_u: %s ok\n", keystr));
2867 return 0;
2870 static int validate_loc_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
2871 struct validation_status *state)
2873 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
2875 if (!centry) {
2876 return 1;
2879 (void)centry_nttime(centry);
2880 (void)centry_nttime(centry);
2881 (void)centry_uint16(centry);
2883 centry_free(centry);
2885 if (!(state->success)) {
2886 return 1;
2888 DEBUG(10,("validate_loc_pol: %s ok\n", keystr));
2889 return 0;
2892 static int validate_pwd_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
2893 struct validation_status *state)
2895 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
2897 if (!centry) {
2898 return 1;
2901 (void)centry_uint16(centry);
2902 (void)centry_uint16(centry);
2903 (void)centry_uint32(centry);
2904 (void)centry_nttime(centry);
2905 (void)centry_nttime(centry);
2907 centry_free(centry);
2909 if (!(state->success)) {
2910 return 1;
2912 DEBUG(10,("validate_pwd_pol: %s ok\n", keystr));
2913 return 0;
2916 static int validate_cred(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
2917 struct validation_status *state)
2919 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
2921 if (!centry) {
2922 return 1;
2925 (void)centry_time(centry);
2926 (void)centry_hash16(centry, mem_ctx);
2928 /* We only have 17 bytes more data in the salted cred case. */
2929 if (centry->len - centry->ofs == 17) {
2930 (void)centry_hash16(centry, mem_ctx);
2933 centry_free(centry);
2935 if (!(state->success)) {
2936 return 1;
2938 DEBUG(10,("validate_cred: %s ok\n", keystr));
2939 return 0;
2942 static int validate_ul(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
2943 struct validation_status *state)
2945 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
2946 int32 num_entries, i;
2948 if (!centry) {
2949 return 1;
2952 num_entries = (int32)centry_uint32(centry);
2954 for (i=0; i< num_entries; i++) {
2955 DOM_SID sid;
2956 (void)centry_string(centry, mem_ctx);
2957 (void)centry_string(centry, mem_ctx);
2958 (void)centry_string(centry, mem_ctx);
2959 (void)centry_string(centry, mem_ctx);
2960 (void)centry_sid(centry, mem_ctx, &sid);
2961 (void)centry_sid(centry, mem_ctx, &sid);
2964 centry_free(centry);
2966 if (!(state->success)) {
2967 return 1;
2969 DEBUG(10,("validate_ul: %s ok\n", keystr));
2970 return 0;
2973 static int validate_gl(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
2974 struct validation_status *state)
2976 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
2977 int32 num_entries, i;
2979 if (!centry) {
2980 return 1;
2983 num_entries = centry_uint32(centry);
2985 for (i=0; i< num_entries; i++) {
2986 (void)centry_string(centry, mem_ctx);
2987 (void)centry_string(centry, mem_ctx);
2988 (void)centry_uint32(centry);
2991 centry_free(centry);
2993 if (!(state->success)) {
2994 return 1;
2996 DEBUG(10,("validate_gl: %s ok\n", keystr));
2997 return 0;
3000 static int validate_ug(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3001 struct validation_status *state)
3003 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3004 int32 num_groups, i;
3006 if (!centry) {
3007 return 1;
3010 num_groups = centry_uint32(centry);
3012 for (i=0; i< num_groups; i++) {
3013 DOM_SID sid;
3014 centry_sid(centry, mem_ctx, &sid);
3017 centry_free(centry);
3019 if (!(state->success)) {
3020 return 1;
3022 DEBUG(10,("validate_ug: %s ok\n", keystr));
3023 return 0;
3026 static int validate_ua(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3027 struct validation_status *state)
3029 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3030 int32 num_aliases, i;
3032 if (!centry) {
3033 return 1;
3036 num_aliases = centry_uint32(centry);
3038 for (i=0; i < num_aliases; i++) {
3039 (void)centry_uint32(centry);
3042 centry_free(centry);
3044 if (!(state->success)) {
3045 return 1;
3047 DEBUG(10,("validate_ua: %s ok\n", keystr));
3048 return 0;
3051 static int validate_gm(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3052 struct validation_status *state)
3054 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3055 int32 num_names, i;
3057 if (!centry) {
3058 return 1;
3061 num_names = centry_uint32(centry);
3063 for (i=0; i< num_names; i++) {
3064 DOM_SID sid;
3065 centry_sid(centry, mem_ctx, &sid);
3066 (void)centry_string(centry, mem_ctx);
3067 (void)centry_uint32(centry);
3070 centry_free(centry);
3072 if (!(state->success)) {
3073 return 1;
3075 DEBUG(10,("validate_gm: %s ok\n", keystr));
3076 return 0;
3079 static int validate_dr(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3080 struct validation_status *state)
3082 /* Can't say anything about this other than must be nonzero. */
3083 if (dbuf.dsize == 0) {
3084 DEBUG(0,("validate_dr: Corrupt cache for key %s (len == 0) ?\n",
3085 keystr));
3086 state->bad_entry = True;
3087 state->success = False;
3088 return 1;
3091 DEBUG(10,("validate_dr: %s ok\n", keystr));
3092 return 0;
3095 static int validate_de(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3096 struct validation_status *state)
3098 /* Can't say anything about this other than must be nonzero. */
3099 if (dbuf.dsize == 0) {
3100 DEBUG(0,("validate_de: Corrupt cache for key %s (len == 0) ?\n",
3101 keystr));
3102 state->bad_entry = True;
3103 state->success = False;
3104 return 1;
3107 DEBUG(10,("validate_de: %s ok\n", keystr));
3108 return 0;
3111 static int validate_trustdoms(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3112 struct validation_status *state)
3114 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3115 int32 num_domains, i;
3117 if (!centry) {
3118 return 1;
3121 num_domains = centry_uint32(centry);
3123 for (i=0; i< num_domains; i++) {
3124 DOM_SID sid;
3125 (void)centry_string(centry, mem_ctx);
3126 (void)centry_string(centry, mem_ctx);
3127 (void)centry_sid(centry, mem_ctx, &sid);
3130 centry_free(centry);
3132 if (!(state->success)) {
3133 return 1;
3135 DEBUG(10,("validate_trustdoms: %s ok\n", keystr));
3136 return 0;
3139 static int validate_trustdomcache(TALLOC_CTX *mem_ctx, const char *keystr,
3140 TDB_DATA dbuf,
3141 struct validation_status *state)
3143 if (dbuf.dsize == 0) {
3144 DEBUG(0, ("validate_trustdomcache: Corrupt cache for "
3145 "key %s (len ==0) ?\n", keystr));
3146 state->bad_entry = True;
3147 state->success = False;
3148 return 1;
3151 DEBUG(10, ("validate_trustdomcache: %s ok\n", keystr));
3152 DEBUGADD(10, (" Don't trust me, I am a DUMMY!\n"));
3153 return 0;
3156 static int validate_offline(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3157 struct validation_status *state)
3159 if (dbuf.dsize != 4) {
3160 DEBUG(0,("validate_offline: Corrupt cache for key %s (len %u != 4) ?\n",
3161 keystr, (unsigned int)dbuf.dsize ));
3162 state->bad_entry = True;
3163 state->success = False;
3164 return 1;
3166 DEBUG(10,("validate_offline: %s ok\n", keystr));
3167 return 0;
3170 static int validate_cache_version(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3171 struct validation_status *state)
3173 if (dbuf.dsize != 4) {
3174 DEBUG(0, ("validate_cache_version: Corrupt cache for "
3175 "key %s (len %u != 4) ?\n",
3176 keystr, (unsigned int)dbuf.dsize));
3177 state->bad_entry = True;
3178 state->success = False;
3179 return 1;
3182 DEBUG(10, ("validate_cache_version: %s ok\n", keystr));
3183 return 0;
3186 /***********************************************************************
3187 A list of all possible cache tdb keys with associated validation
3188 functions.
3189 ***********************************************************************/
3191 struct key_val_struct {
3192 const char *keyname;
3193 int (*validate_data_fn)(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, struct validation_status* state);
3194 } key_val[] = {
3195 {"SEQNUM/", validate_seqnum},
3196 {"NS/", validate_ns},
3197 {"SN/", validate_sn},
3198 {"U/", validate_u},
3199 {"LOC_POL/", validate_loc_pol},
3200 {"PWD_POL/", validate_pwd_pol},
3201 {"CRED/", validate_cred},
3202 {"UL/", validate_ul},
3203 {"GL/", validate_gl},
3204 {"UG/", validate_ug},
3205 {"UA", validate_ua},
3206 {"GM/", validate_gm},
3207 {"DR/", validate_dr},
3208 {"DE/", validate_de},
3209 {"TRUSTDOMS/", validate_trustdoms},
3210 {"TRUSTDOMCACHE/", validate_trustdomcache},
3211 {"WINBINDD_OFFLINE", validate_offline},
3212 {WINBINDD_CACHE_VERSION_KEYSTR, validate_cache_version},
3213 {NULL, NULL}
3216 /***********************************************************************
3217 Function to look at every entry in the tdb and validate it as far as
3218 possible.
3219 ***********************************************************************/
3221 static int cache_traverse_validate_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, void *state)
3223 int i;
3224 struct validation_status *v_state = (struct validation_status *)state;
3226 /* Paranoia check. */
3227 if (kbuf.dsize > 1024) {
3228 DEBUG(0,("cache_traverse_validate_fn: key length too large (%u) > 1024\n\n",
3229 (unsigned int)kbuf.dsize ));
3230 return 1;
3233 for (i = 0; key_val[i].keyname; i++) {
3234 size_t namelen = strlen(key_val[i].keyname);
3235 if (kbuf.dsize >= namelen && (
3236 strncmp(key_val[i].keyname, (const char *)kbuf.dptr, namelen)) == 0) {
3237 TALLOC_CTX *mem_ctx;
3238 char *keystr;
3239 int ret;
3241 keystr = SMB_MALLOC_ARRAY(char, kbuf.dsize+1);
3242 if (!keystr) {
3243 return 1;
3245 memcpy(keystr, kbuf.dptr, kbuf.dsize);
3246 keystr[kbuf.dsize] = '\0';
3248 mem_ctx = talloc_init("validate_ctx");
3249 if (!mem_ctx) {
3250 SAFE_FREE(keystr);
3251 return 1;
3254 ret = key_val[i].validate_data_fn(mem_ctx, keystr, dbuf,
3255 v_state);
3257 SAFE_FREE(keystr);
3258 talloc_destroy(mem_ctx);
3259 return ret;
3263 DEBUG(0,("cache_traverse_validate_fn: unknown cache entry\nkey :\n"));
3264 dump_data(0, (uint8 *)kbuf.dptr, kbuf.dsize);
3265 DEBUG(0,("data :\n"));
3266 dump_data(0, (uint8 *)dbuf.dptr, dbuf.dsize);
3267 v_state->unknown_key = True;
3268 v_state->success = False;
3269 return 1; /* terminate. */
3272 static void validate_panic(const char *const why)
3274 DEBUG(0,("validating cache: would panic %s\n", why ));
3275 DEBUGADD(0, ("exiting instead (cache validation mode)\n"));
3276 exit(47);
3279 /***********************************************************************
3280 Try and validate every entry in the winbindd cache. If we fail here,
3281 delete the cache tdb and return non-zero - the caller (main winbindd
3282 function) will restart us as we don't know if we crashed or not.
3283 ***********************************************************************/
3286 * internal validation function, executed by the child.
3288 static int winbindd_validate_cache_child(const char *cache_path, int pfd)
3290 int ret = -1;
3291 int tfd = -1;
3292 int num_entries = 0;
3293 TDB_CONTEXT *tdb = NULL;
3294 struct validation_status v_status;
3296 v_status.tdb_error = False;
3297 v_status.bad_freelist = False;
3298 v_status.bad_entry = False;
3299 v_status.unknown_key = False;
3300 v_status.success = True;
3302 tdb = tdb_open_log(cache_path,
3303 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
3304 lp_winbind_offline_logon()
3305 ? TDB_DEFAULT
3306 : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST),
3307 O_RDWR|O_CREAT, 0600);
3308 if (!tdb) {
3309 v_status.tdb_error = True;
3310 v_status.success = False;
3311 goto out;
3314 tfd = tdb_fd(tdb);
3316 /* Check the cache freelist is good. */
3317 if (tdb_validate_freelist(tdb, &num_entries) == -1) {
3318 DEBUG(0,("winbindd_validate_cache_child: bad freelist in cache %s\n",
3319 cache_path));
3320 v_status.bad_freelist = True;
3321 v_status.success = False;
3322 goto out;
3325 DEBUG(10,("winbindd_validate_cache_child: cache %s freelist has %d entries\n",
3326 cache_path, num_entries));
3328 /* Now traverse the cache to validate it. */
3329 num_entries = tdb_traverse(tdb, cache_traverse_validate_fn, (void *)&v_status);
3330 if (num_entries == -1 || !(v_status.success)) {
3331 DEBUG(0,("winbindd_validate_cache_child: cache %s traverse failed\n",
3332 cache_path));
3333 if (!(v_status.success)) {
3334 if (v_status.bad_entry) {
3335 DEBUGADD(0, (" -> bad entry found\n"));
3337 if (v_status.unknown_key) {
3338 DEBUGADD(0, (" -> unknown key encountered\n"));
3341 goto out;
3344 DEBUG(10,("winbindd_validate_cache_child: cache %s is good "
3345 "with %d entries\n", cache_path, num_entries));
3346 ret = 0; /* Cache is good. */
3348 out:
3349 if (tdb) {
3350 if (ret == 0) {
3351 tdb_close(tdb);
3353 else if (tfd != -1) {
3354 close(tfd);
3358 DEBUG(10, ("winbindd_validate_cache_child: writing status to pipe\n"));
3359 write (pfd, (const char *)&v_status, sizeof(v_status));
3360 close(pfd);
3362 return ret;
3365 int winbindd_validate_cache(void)
3367 pid_t child_pid = -1;
3368 int child_status = 0;
3369 int wait_pid = 0;
3370 int ret = -1;
3371 int pipe_fds[2];
3372 struct validation_status v_status;
3373 int bytes_read = 0;
3374 const char *cache_path = lock_path("winbindd_cache.tdb");
3376 DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
3377 smb_panic_fn = validate_panic;
3379 /* fork and let the child do the validation.
3380 * benefit: no need to twist signal handlers and panic functions.
3381 * just let the child panic. we catch the signal.
3382 * communicate the extended status struct over a pipe. */
3384 if (pipe(pipe_fds) != 0) {
3385 DEBUG(0, ("winbindd_validate_cache: unable to create pipe, "
3386 "error %s", strerror(errno)));
3387 smb_panic("winbind_validate_cache: unable to create pipe.");
3390 DEBUG(10, ("winbindd_validate_cache: forking to let child do validation.\n"));
3391 child_pid = sys_fork();
3392 if (child_pid == 0) {
3393 DEBUG(10, ("winbindd_validate_cache (validation child): created\n"));
3394 close(pipe_fds[0]); /* close reading fd */
3395 DEBUG(10, ("winbindd_validate_cache (validation child): "
3396 "calling winbindd_validate_cache_child\n"));
3397 exit(winbindd_validate_cache_child(cache_path, pipe_fds[1]));
3399 else if (child_pid < 0) {
3400 smb_panic("winbindd_validate_cache: fork for validation failed.");
3403 /* parent */
3405 DEBUG(10, ("winbindd_validate_cache: fork succeeded, child PID = %d\n",
3406 child_pid));
3407 close(pipe_fds[1]); /* close writing fd */
3409 v_status.success = True;
3410 v_status.bad_entry = False;
3411 v_status.unknown_key = False;
3413 DEBUG(10, ("winbindd_validate_cache: reading from pipe.\n"));
3414 bytes_read = read(pipe_fds[0], (void *)&v_status, sizeof(v_status));
3415 close(pipe_fds[0]);
3417 if (bytes_read != sizeof(v_status)) {
3418 DEBUG(10, ("winbindd_validate_cache: read %d bytes from pipe "
3419 "but expected %d", bytes_read, sizeof(v_status)));
3420 DEBUGADD(10, (" -> assuming child crashed\n"));
3421 v_status.success = False;
3423 else {
3424 DEBUG(10, ("winbindd_validate_cache: read status from child\n"));
3425 DEBUGADD(10, (" * tdb error: %s\n", v_status.tdb_error ? "yes" : "no"));
3426 DEBUGADD(10, (" * bad freelist: %s\n", v_status.bad_freelist ? "yes" : "no"));
3427 DEBUGADD(10, (" * bad entry: %s\n", v_status.bad_entry ? "yes" : "no"));
3428 DEBUGADD(10, (" * unknown key: %s\n", v_status.unknown_key ? "yes" : "no"));
3429 DEBUGADD(10, (" => overall success: %s\n", v_status.success ? "yes" : "no"));
3432 if (!v_status.success) {
3433 DEBUG(10, ("winbindd_validate_cache: validation not successful.\n"));
3434 DEBUGADD(10, ("removing tdb %s.\n", cache_path));
3435 unlink(cache_path);
3438 DEBUG(10, ("winbindd_validate_cache: waiting for child to finish...\n"));
3439 while ((wait_pid = sys_waitpid(child_pid, &child_status, 0)) < 0) {
3440 if (errno == EINTR) {
3441 DEBUG(10, ("winbindd_validate_cache: got signal during "
3442 "waitpid, retrying\n"));
3443 errno = 0;
3444 continue;
3446 DEBUG(0, ("winbindd_validate_cache: waitpid failed with "
3447 "errno %s\n", strerror(errno)));
3448 smb_panic("winbindd_validate_cache: waitpid failed.");
3450 if (wait_pid != child_pid) {
3451 DEBUG(0, ("winbindd_validate_cache: waitpid returned pid %d, "
3452 "but %d was expexted\n", wait_pid, child_pid));
3453 smb_panic("winbindd_validate_cache: waitpid returned "
3454 "unexpected PID.");
3458 DEBUG(10, ("winbindd_validate_cache: validating child returned.\n"));
3459 if (WIFEXITED(child_status)) {
3460 DEBUG(10, ("winbindd_validate_cache: child exited, code %d.\n",
3461 WEXITSTATUS(child_status)));
3462 ret = WEXITSTATUS(child_status);
3464 if (WIFSIGNALED(child_status)) {
3465 DEBUG(10, ("winbindd_validate_cache: child terminated "
3466 "by signal %d%s\n",
3467 WTERMSIG(child_status),
3468 #if defined(WCOREDUMP)
3469 WCOREDUMP(child_status)?" (core dumped)":""
3470 #else
3472 #endif
3474 ret = WTERMSIG(child_status);
3476 if (WIFSTOPPED(child_status)) {
3477 DEBUG(10, ("winbindd_validate_cache: child was stopped "
3478 "by signal %d\n",
3479 WSTOPSIG(child_status)));
3480 ret = WSTOPSIG(child_status);
3483 DEBUG(10, ("winbindd_validate_cache: restoring panic function\n"));
3484 smb_panic_fn = smb_panic;
3485 return ret;
3488 /*********************************************************************
3489 ********************************************************************/
3491 static BOOL add_wbdomain_to_tdc_array( struct winbindd_domain *new_dom,
3492 struct winbindd_tdc_domain **domains,
3493 size_t *num_domains )
3495 struct winbindd_tdc_domain *list = NULL;
3496 size_t idx;
3497 int i;
3498 BOOL set_only = False;
3500 /* don't allow duplicates */
3502 idx = *num_domains;
3503 list = *domains;
3505 for ( i=0; i< (*num_domains); i++ ) {
3506 if ( strequal( new_dom->name, list[i].domain_name ) ) {
3507 DEBUG(10,("add_wbdomain_to_tdc_array: Found existing record for %s\n",
3508 new_dom->name));
3509 idx = i;
3510 set_only = True;
3512 break;
3516 if ( !set_only ) {
3517 if ( !*domains ) {
3518 list = TALLOC_ARRAY( NULL, struct winbindd_tdc_domain, 1 );
3519 idx = 0;
3520 } else {
3521 list = TALLOC_REALLOC_ARRAY( *domains, *domains,
3522 struct winbindd_tdc_domain,
3523 (*num_domains)+1);
3524 idx = *num_domains;
3527 ZERO_STRUCT( list[idx] );
3530 if ( !list )
3531 return False;
3533 list[idx].domain_name = talloc_strdup( list, new_dom->name );
3534 list[idx].dns_name = talloc_strdup( list, new_dom->alt_name );
3536 if ( !is_null_sid( &new_dom->sid ) )
3537 sid_copy( &list[idx].sid, &new_dom->sid );
3539 if ( new_dom->domain_flags != 0x0 )
3540 list[idx].trust_flags = new_dom->domain_flags;
3542 if ( new_dom->domain_type != 0x0 )
3543 list[idx].trust_type = new_dom->domain_type;
3545 if ( new_dom->domain_trust_attribs != 0x0 )
3546 list[idx].trust_attribs = new_dom->domain_trust_attribs;
3548 if ( !set_only ) {
3549 *domains = list;
3550 *num_domains = idx + 1;
3553 return True;
3556 /*********************************************************************
3557 ********************************************************************/
3559 static TDB_DATA make_tdc_key( const char *domain_name )
3561 char *keystr = NULL;
3562 TDB_DATA key = { NULL, 0 };
3564 if ( !domain_name ) {
3565 DEBUG(5,("make_tdc_key: Keyname workgroup is NULL!\n"));
3566 return key;
3570 asprintf( &keystr, "TRUSTDOMCACHE/%s", domain_name );
3571 key.dptr = (unsigned char*)keystr;
3572 key.dsize = strlen_m(keystr) + 1;
3574 return key;
3577 /*********************************************************************
3578 ********************************************************************/
3580 static int pack_tdc_domains( struct winbindd_tdc_domain *domains,
3581 size_t num_domains,
3582 unsigned char **buf )
3584 unsigned char *buffer = NULL;
3585 int len = 0;
3586 int buflen = 0;
3587 int i = 0;
3589 DEBUG(10,("pack_tdc_domains: Packing %d trusted domains\n",
3590 (int)num_domains));
3592 buflen = 0;
3594 again:
3595 len = 0;
3597 /* Store the number of array items first */
3598 len += tdb_pack( buffer+len, buflen-len, "d",
3599 num_domains );
3601 /* now pack each domain trust record */
3602 for ( i=0; i<num_domains; i++ ) {
3604 if ( buflen > 0 ) {
3605 DEBUG(10,("pack_tdc_domains: Packing domain %s (%s)\n",
3606 domains[i].domain_name,
3607 domains[i].dns_name ? domains[i].dns_name : "UNKNOWN" ));
3610 len += tdb_pack( buffer+len, buflen-len, "fffddd",
3611 domains[i].domain_name,
3612 domains[i].dns_name,
3613 sid_string_static(&domains[i].sid),
3614 domains[i].trust_flags,
3615 domains[i].trust_attribs,
3616 domains[i].trust_type );
3619 if ( buflen < len ) {
3620 if ( (buffer = SMB_MALLOC_ARRAY(unsigned char, len)) == NULL ) {
3621 DEBUG(0,("pack_tdc_domains: failed to alloc buffer!\n"));
3622 buflen = -1;
3623 goto done;
3625 buflen = len;
3626 goto again;
3629 *buf = buffer;
3631 done:
3632 return buflen;
3635 /*********************************************************************
3636 ********************************************************************/
3638 static size_t unpack_tdc_domains( unsigned char *buf, int buflen,
3639 struct winbindd_tdc_domain **domains )
3641 fstring domain_name, dns_name, sid_string;
3642 uint32 type, attribs, flags;
3643 int num_domains;
3644 int len = 0;
3645 int i;
3646 struct winbindd_tdc_domain *list = NULL;
3648 /* get the number of domains */
3649 len += tdb_unpack( buf+len, buflen-len, "d", &num_domains);
3650 if ( len == -1 ) {
3651 DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
3652 return 0;
3655 list = TALLOC_ARRAY( NULL, struct winbindd_tdc_domain, num_domains );
3656 if ( !list ) {
3657 DEBUG(0,("unpack_tdc_domains: Failed to talloc() domain list!\n"));
3658 return 0;
3661 for ( i=0; i<num_domains; i++ ) {
3662 len += tdb_unpack( buf+len, buflen-len, "fffddd",
3663 domain_name,
3664 dns_name,
3665 sid_string,
3666 &flags,
3667 &attribs,
3668 &type );
3670 if ( len == -1 ) {
3671 DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
3672 TALLOC_FREE( list );
3673 return 0;
3676 DEBUG(11,("unpack_tdc_domains: Unpacking domain %s (%s) "
3677 "SID %s, flags = 0x%x, attribs = 0x%x, type = 0x%x\n",
3678 domain_name, dns_name, sid_string,
3679 flags, attribs, type));
3681 list[i].domain_name = talloc_strdup( list, domain_name );
3682 list[i].dns_name = talloc_strdup( list, dns_name );
3683 if ( !string_to_sid( &(list[i].sid), sid_string ) ) {
3684 DEBUG(10,("unpack_tdc_domains: no SID for domain %s\n",
3685 domain_name));
3687 list[i].trust_flags = flags;
3688 list[i].trust_attribs = attribs;
3689 list[i].trust_type = type;
3692 *domains = list;
3694 return num_domains;
3697 /*********************************************************************
3698 ********************************************************************/
3700 static BOOL wcache_tdc_store_list( struct winbindd_tdc_domain *domains, size_t num_domains )
3702 TDB_DATA key = make_tdc_key( lp_workgroup() );
3703 TDB_DATA data = { NULL, 0 };
3704 int ret;
3706 if ( !key.dptr )
3707 return False;
3709 /* See if we were asked to delete the cache entry */
3711 if ( !domains ) {
3712 ret = tdb_delete( wcache->tdb, key );
3713 goto done;
3716 data.dsize = pack_tdc_domains( domains, num_domains, &data.dptr );
3718 if ( !data.dptr ) {
3719 ret = -1;
3720 goto done;
3723 ret = tdb_store( wcache->tdb, key, data, 0 );
3725 done:
3726 SAFE_FREE( data.dptr );
3727 SAFE_FREE( key.dptr );
3729 return ( ret != -1 );
3732 /*********************************************************************
3733 ********************************************************************/
3735 BOOL wcache_tdc_fetch_list( struct winbindd_tdc_domain **domains, size_t *num_domains )
3737 TDB_DATA key = make_tdc_key( lp_workgroup() );
3738 TDB_DATA data = { NULL, 0 };
3740 *domains = NULL;
3741 *num_domains = 0;
3743 if ( !key.dptr )
3744 return False;
3746 data = tdb_fetch( wcache->tdb, key );
3748 SAFE_FREE( key.dptr );
3750 if ( !data.dptr )
3751 return False;
3753 *num_domains = unpack_tdc_domains( data.dptr, data.dsize, domains );
3755 SAFE_FREE( data.dptr );
3757 if ( !*domains )
3758 return False;
3760 return True;
3763 /*********************************************************************
3764 ********************************************************************/
3766 BOOL wcache_tdc_add_domain( struct winbindd_domain *domain )
3768 struct winbindd_tdc_domain *dom_list = NULL;
3769 size_t num_domains = 0;
3770 BOOL ret = False;
3772 DEBUG(10,("wcache_tdc_add_domain: Adding domain %s (%s), SID %s, "
3773 "flags = 0x%x, attributes = 0x%x, type = 0x%x\n",
3774 domain->name, domain->alt_name,
3775 sid_string_static(&domain->sid),
3776 domain->domain_flags,
3777 domain->domain_trust_attribs,
3778 domain->domain_type));
3780 if ( !init_wcache() ) {
3781 return False;
3784 /* fetch the list */
3786 wcache_tdc_fetch_list( &dom_list, &num_domains );
3788 /* add the new domain */
3790 if ( !add_wbdomain_to_tdc_array( domain, &dom_list, &num_domains ) ) {
3791 goto done;
3794 /* pack the domain */
3796 if ( !wcache_tdc_store_list( dom_list, num_domains ) ) {
3797 goto done;
3800 /* Success */
3802 ret = True;
3803 done:
3804 TALLOC_FREE( dom_list );
3806 return ret;
3809 /*********************************************************************
3810 ********************************************************************/
3812 struct winbindd_tdc_domain * wcache_tdc_fetch_domain( TALLOC_CTX *ctx, const char *name )
3814 struct winbindd_tdc_domain *dom_list = NULL;
3815 size_t num_domains = 0;
3816 int i;
3817 struct winbindd_tdc_domain *d = NULL;
3819 DEBUG(10,("wcache_tdc_fetch_domain: Searching for domain %s\n", name));
3821 if ( !init_wcache() ) {
3822 return False;
3825 /* fetch the list */
3827 wcache_tdc_fetch_list( &dom_list, &num_domains );
3829 for ( i=0; i<num_domains; i++ ) {
3830 if ( strequal(name, dom_list[i].domain_name) ||
3831 strequal(name, dom_list[i].dns_name) )
3833 DEBUG(10,("wcache_tdc_fetch_domain: Found domain %s\n",
3834 name));
3836 d = TALLOC_P( ctx, struct winbindd_tdc_domain );
3837 if ( !d )
3838 break;
3840 d->domain_name = talloc_strdup( d, dom_list[i].domain_name );
3841 d->dns_name = talloc_strdup( d, dom_list[i].dns_name );
3842 sid_copy( &d->sid, &dom_list[i].sid );
3843 d->trust_flags = dom_list[i].trust_flags;
3844 d->trust_type = dom_list[i].trust_type;
3845 d->trust_attribs = dom_list[i].trust_attribs;
3847 break;
3851 TALLOC_FREE( dom_list );
3853 return d;
3857 /*********************************************************************
3858 ********************************************************************/
3860 void wcache_tdc_clear( void )
3862 if ( !init_wcache() )
3863 return;
3865 wcache_tdc_store_list( NULL, 0 );
3867 return;
3871 /*********************************************************************
3872 ********************************************************************/
3874 static void wcache_save_user_pwinfo(struct winbindd_domain *domain,
3875 NTSTATUS status,
3876 const DOM_SID *user_sid,
3877 const char *homedir,
3878 const char *shell,
3879 const char *gecos,
3880 uint32 gid)
3882 struct cache_entry *centry;
3884 if ( (centry = centry_start(domain, status)) == NULL )
3885 return;
3887 centry_put_string( centry, homedir );
3888 centry_put_string( centry, shell );
3889 centry_put_string( centry, gecos );
3890 centry_put_uint32( centry, gid );
3892 centry_end(centry, "NSS/PWINFO/%s", sid_string_static(user_sid) );
3894 DEBUG(10,("wcache_save_user_pwinfo: %s\n", sid_string_static(user_sid) ));
3896 centry_free(centry);
3899 NTSTATUS nss_get_info_cached( struct winbindd_domain *domain,
3900 const DOM_SID *user_sid,
3901 TALLOC_CTX *ctx,
3902 ADS_STRUCT *ads, LDAPMessage *msg,
3903 char **homedir, char **shell, char **gecos,
3904 gid_t *p_gid)
3906 struct winbind_cache *cache = get_cache(domain);
3907 struct cache_entry *centry = NULL;
3908 NTSTATUS nt_status;
3910 if (!cache->tdb)
3911 goto do_query;
3913 centry = wcache_fetch(cache, domain, "NSS/PWINFO/%s", sid_string_static(user_sid));
3915 if (!centry)
3916 goto do_query;
3918 *homedir = centry_string( centry, ctx );
3919 *shell = centry_string( centry, ctx );
3920 *gecos = centry_string( centry, ctx );
3921 *p_gid = centry_uint32( centry );
3923 centry_free(centry);
3925 DEBUG(10,("nss_get_info_cached: [Cached] - user_sid %s\n",
3926 sid_string_static(user_sid)));
3928 return NT_STATUS_OK;
3930 do_query:
3932 nt_status = nss_get_info( domain->name, user_sid, ctx, ads, msg,
3933 homedir, shell, gecos, p_gid );
3935 if ( NT_STATUS_IS_OK(nt_status) ) {
3936 wcache_save_user_pwinfo( domain, nt_status, user_sid,
3937 *homedir, *shell, *gecos, *p_gid );
3940 if ( NT_STATUS_EQUAL( nt_status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND ) ) {
3941 DEBUG(5,("nss_get_info_cached: Setting domain %s offline\n",
3942 domain->name ));
3943 set_domain_offline( domain );
3946 return nt_status;
3950 /* the cache backend methods are exposed via this structure */
3951 struct winbindd_methods cache_methods = {
3952 True,
3953 query_user_list,
3954 enum_dom_groups,
3955 enum_local_groups,
3956 name_to_sid,
3957 sid_to_name,
3958 rids_to_names,
3959 query_user,
3960 lookup_usergroups,
3961 lookup_useraliases,
3962 lookup_groupmem,
3963 sequence_number,
3964 lockout_policy,
3965 password_policy,
3966 trusted_domains