r22209: Fix the storage of time_t -> make it 64 bits (use the
[Samba/bb.git] / source3 / nsswitch / winbindd_cache.c
blob6b9721a83b930bf52ec2e9efd3b679b9ef325050
1 /*
2 Unix SMB/CIFS implementation.
4 Winbind cache backend functions
6 Copyright (C) Andrew Tridgell 2001
7 Copyright (C) Gerald Carter 2003
8 Copyright (C) Volker Lendecke 2005
9 Copyright (C) Guenther Deschner 2005
11 This program is free software; you can redistribute it and/or modify
12 it under the terms of the GNU General Public License as published by
13 the Free Software Foundation; either version 2 of the License, or
14 (at your option) any later version.
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 GNU General Public License for more details.
21 You should have received a copy of the GNU General Public License
22 along with this program; if not, write to the Free Software
23 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26 #include "includes.h"
27 #include "winbindd.h"
29 #undef DBGC_CLASS
30 #define DBGC_CLASS DBGC_WINBIND
32 #define WINBINDD_CACHE_VERSION 1
33 #define WINBINDD_CACHE_VERSION_KEYSTR "WINBINDD_CACHE_VERSION"
35 extern struct winbindd_methods reconnect_methods;
36 extern BOOL opt_nocache;
37 #ifdef HAVE_ADS
38 extern struct winbindd_methods ads_methods;
39 #endif
42 * JRA. KEEP THIS LIST UP TO DATE IF YOU ADD CACHE ENTRIES.
43 * Here are the list of entry types that are *not* stored
44 * as form struct cache_entry in the cache.
47 static const char *non_centry_keys[] = {
48 "SEQNUM/",
49 "DR/",
50 "DE/",
51 "WINBINDD_OFFLINE",
52 WINBINDD_CACHE_VERSION_KEYSTR,
53 NULL
56 /************************************************************************
57 Is this key a non-centry type ?
58 ************************************************************************/
60 static BOOL is_non_centry_key(TDB_DATA kbuf)
62 int i;
64 if (kbuf.dptr == NULL || kbuf.dsize == 0) {
65 return False;
67 for (i = 0; non_centry_keys[i] != NULL; i++) {
68 size_t namelen = strlen(non_centry_keys[i]);
69 if (kbuf.dsize <= namelen) {
70 continue;
72 if (strncmp(non_centry_keys[i], (const char *)kbuf.dptr, namelen) == 0) {
73 return True;
76 return False;
79 /* Global online/offline state - False when online. winbindd starts up online
80 and sets this to true if the first query fails and there's an entry in
81 the cache tdb telling us to stay offline. */
83 static BOOL global_winbindd_offline_state;
85 struct winbind_cache {
86 TDB_CONTEXT *tdb;
89 struct cache_entry {
90 NTSTATUS status;
91 uint32 sequence_number;
92 uint8 *data;
93 uint32 len, ofs;
96 void (*smb_panic_fn)(const char *const why) = smb_panic;
98 #define WINBINDD_MAX_CACHE_SIZE (50*1024*1024)
100 static struct winbind_cache *wcache;
102 void winbindd_check_cache_size(time_t t)
104 static time_t last_check_time;
105 struct stat st;
107 if (last_check_time == (time_t)0)
108 last_check_time = t;
110 if (t - last_check_time < 60 && t - last_check_time > 0)
111 return;
113 if (wcache == NULL || wcache->tdb == NULL) {
114 DEBUG(0, ("Unable to check size of tdb cache - cache not open !\n"));
115 return;
118 if (fstat(tdb_fd(wcache->tdb), &st) == -1) {
119 DEBUG(0, ("Unable to check size of tdb cache %s!\n", strerror(errno) ));
120 return;
123 if (st.st_size > WINBINDD_MAX_CACHE_SIZE) {
124 DEBUG(10,("flushing cache due to size (%lu) > (%lu)\n",
125 (unsigned long)st.st_size,
126 (unsigned long)WINBINDD_MAX_CACHE_SIZE));
127 wcache_flush_cache();
131 /* get the winbind_cache structure */
132 static struct winbind_cache *get_cache(struct winbindd_domain *domain)
134 struct winbind_cache *ret = wcache;
135 #ifdef HAVE_ADS
136 struct winbindd_domain *our_domain = domain;
137 #endif
139 /* We have to know what type of domain we are dealing with first. */
141 if ( !domain->initialized ) {
142 init_dc_connection( domain );
146 OK. listen up becasue I'm only going to say this once.
147 We have the following scenarios to consider
148 (a) trusted AD domains on a Samba DC,
149 (b) trusted AD domains and we are joined to a non-kerberos domain
150 (c) trusted AD domains and we are joined to a kerberos (AD) domain
152 For (a) we can always contact the trusted domain using krb5
153 since we have the domain trust account password
155 For (b) we can only use RPC since we have no way of
156 getting a krb5 ticket in our own domain
158 For (c) we can always use krb5 since we have a kerberos trust
160 --jerry
163 if (!domain->backend) {
164 #ifdef HAVE_ADS
165 /* find our domain first so we can figure out if we
166 are joined to a kerberized domain */
168 if ( !domain->primary )
169 our_domain = find_our_domain();
171 if ( (our_domain->active_directory || IS_DC) && domain->active_directory ) {
172 DEBUG(5,("get_cache: Setting ADS methods for domain %s\n", domain->name));
173 domain->backend = &ads_methods;
174 } else {
175 #endif /* HAVE_ADS */
176 DEBUG(5,("get_cache: Setting MS-RPC methods for domain %s\n", domain->name));
177 domain->backend = &reconnect_methods;
178 #ifdef HAVE_ADS
180 #endif /* HAVE_ADS */
183 if (ret)
184 return ret;
186 ret = SMB_XMALLOC_P(struct winbind_cache);
187 ZERO_STRUCTP(ret);
189 wcache = ret;
190 wcache_flush_cache();
192 return ret;
196 free a centry structure
198 static void centry_free(struct cache_entry *centry)
200 if (!centry)
201 return;
202 SAFE_FREE(centry->data);
203 free(centry);
206 static BOOL centry_check_bytes(struct cache_entry *centry, size_t nbytes)
208 if (centry->len - centry->ofs < nbytes) {
209 DEBUG(0,("centry corruption? needed %u bytes, have %d\n",
210 (unsigned int)nbytes,
211 centry->len - centry->ofs));
212 return False;
214 return True;
218 pull a uint32 from a cache entry
220 static uint32 centry_uint32(struct cache_entry *centry)
222 uint32 ret;
224 if (centry_check_bytes(centry, 4)) {
225 smb_panic_fn("centry_uint32");
226 return (uint32)-1;
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");
241 return (uint16)-1;
243 ret = CVAL(centry->data, centry->ofs);
244 centry->ofs += 2;
245 return ret;
249 pull a uint8 from a cache entry
251 static uint8 centry_uint8(struct cache_entry *centry)
253 uint8 ret;
254 if (centry_check_bytes(centry, 1)) {
255 smb_panic_fn("centry_uint8");
256 return (uint8)-1;
258 ret = CVAL(centry->data, centry->ofs);
259 centry->ofs += 1;
260 return ret;
264 pull a NTTIME from a cache entry
266 static NTTIME centry_nttime(struct cache_entry *centry)
268 NTTIME ret;
269 if (centry_check_bytes(centry, 8)) {
270 smb_panic_fn("centry_nttime");
271 return (NTTIME)-1;
273 ret = IVAL(centry->data, centry->ofs);
274 centry->ofs += 4;
275 ret += (uint64_t)IVAL(centry->data, centry->ofs) << 32;
276 centry->ofs += 4;
277 return ret;
281 pull a time_t from a cache entry. time_t stored portably as a 64-bit time.
283 static time_t centry_time(struct cache_entry *centry)
285 return (time_t)centry_nttime(centry);
288 /* pull a string from a cache entry, using the supplied
289 talloc context
291 static char *centry_string(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
293 uint32 len;
294 char *ret;
296 len = centry_uint8(centry);
298 if (len == 0xFF) {
299 /* a deliberate NULL string */
300 return NULL;
303 if (centry_check_bytes(centry, (size_t)len)) {
304 smb_panic_fn("centry_string");
305 return NULL;
308 ret = TALLOC_ARRAY(mem_ctx, char, len+1);
309 if (!ret) {
310 smb_panic_fn("centry_string out of memory\n");
311 return NULL;
313 memcpy(ret,centry->data + centry->ofs, len);
314 ret[len] = 0;
315 centry->ofs += len;
316 return ret;
319 /* pull a hash16 from a cache entry, using the supplied
320 talloc context
322 static char *centry_hash16(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
324 uint32 len;
325 char *ret;
327 len = centry_uint8(centry);
329 if (len != 16) {
330 DEBUG(0,("centry corruption? hash len (%u) != 16\n",
331 len ));
332 return NULL;
335 if (centry_check_bytes(centry, 16)) {
336 return NULL;
339 ret = TALLOC_ARRAY(mem_ctx, char, 16);
340 if (!ret) {
341 smb_panic_fn("centry_hash out of memory\n");
342 return NULL;
344 memcpy(ret,centry->data + centry->ofs, 16);
345 centry->ofs += 16;
346 return ret;
349 /* pull a sid from a cache entry, using the supplied
350 talloc context
352 static BOOL centry_sid(struct cache_entry *centry, TALLOC_CTX *mem_ctx, DOM_SID *sid)
354 char *sid_string;
355 sid_string = centry_string(centry, mem_ctx);
356 if ((sid_string == NULL) || (!string_to_sid(sid, sid_string))) {
357 return False;
359 return True;
362 /* the server is considered down if it can't give us a sequence number */
363 static BOOL wcache_server_down(struct winbindd_domain *domain)
365 BOOL ret;
367 if (!wcache->tdb)
368 return False;
370 ret = (domain->sequence_number == DOM_SEQUENCE_NONE);
372 if (ret)
373 DEBUG(10,("wcache_server_down: server for Domain %s down\n",
374 domain->name ));
375 return ret;
378 static NTSTATUS fetch_cache_seqnum( struct winbindd_domain *domain, time_t now )
380 TDB_DATA data;
381 fstring key;
382 uint32 time_diff;
384 if (!wcache->tdb) {
385 DEBUG(10,("fetch_cache_seqnum: tdb == NULL\n"));
386 return NT_STATUS_UNSUCCESSFUL;
389 fstr_sprintf( key, "SEQNUM/%s", domain->name );
391 data = tdb_fetch_bystring( wcache->tdb, key );
392 if ( !data.dptr || data.dsize!=8 ) {
393 DEBUG(10,("fetch_cache_seqnum: invalid data size key [%s]\n", key ));
394 return NT_STATUS_UNSUCCESSFUL;
397 domain->sequence_number = IVAL(data.dptr, 0);
398 domain->last_seq_check = IVAL(data.dptr, 4);
400 SAFE_FREE(data.dptr);
402 /* have we expired? */
404 time_diff = now - domain->last_seq_check;
405 if ( time_diff > lp_winbind_cache_time() ) {
406 DEBUG(10,("fetch_cache_seqnum: timeout [%s][%u @ %u]\n",
407 domain->name, domain->sequence_number,
408 (uint32)domain->last_seq_check));
409 return NT_STATUS_UNSUCCESSFUL;
412 DEBUG(10,("fetch_cache_seqnum: success [%s][%u @ %u]\n",
413 domain->name, domain->sequence_number,
414 (uint32)domain->last_seq_check));
416 return NT_STATUS_OK;
419 static NTSTATUS store_cache_seqnum( struct winbindd_domain *domain )
421 TDB_DATA data;
422 fstring key_str;
423 uint8 buf[8];
425 if (!wcache->tdb) {
426 DEBUG(10,("store_cache_seqnum: tdb == NULL\n"));
427 return NT_STATUS_UNSUCCESSFUL;
430 fstr_sprintf( key_str, "SEQNUM/%s", domain->name );
432 SIVAL(buf, 0, domain->sequence_number);
433 SIVAL(buf, 4, domain->last_seq_check);
434 data.dptr = buf;
435 data.dsize = 8;
437 if ( tdb_store_bystring( wcache->tdb, key_str, data, TDB_REPLACE) == -1 ) {
438 DEBUG(10,("store_cache_seqnum: tdb_store fail key [%s]\n", key_str ));
439 return NT_STATUS_UNSUCCESSFUL;
442 DEBUG(10,("store_cache_seqnum: success [%s][%u @ %u]\n",
443 domain->name, domain->sequence_number,
444 (uint32)domain->last_seq_check));
446 return NT_STATUS_OK;
450 refresh the domain sequence number. If force is True
451 then always refresh it, no matter how recently we fetched it
454 static void refresh_sequence_number(struct winbindd_domain *domain, BOOL force)
456 NTSTATUS status;
457 unsigned time_diff;
458 time_t t = time(NULL);
459 unsigned cache_time = lp_winbind_cache_time();
461 get_cache( domain );
463 #if 0 /* JERRY -- disable as the default cache time is now 5 minutes */
464 /* trying to reconnect is expensive, don't do it too often */
465 if (domain->sequence_number == DOM_SEQUENCE_NONE) {
466 cache_time *= 8;
468 #endif
470 time_diff = t - domain->last_seq_check;
472 /* see if we have to refetch the domain sequence number */
473 if (!force && (time_diff < cache_time)) {
474 DEBUG(10, ("refresh_sequence_number: %s time ok\n", domain->name));
475 goto done;
478 /* try to get the sequence number from the tdb cache first */
479 /* this will update the timestamp as well */
481 status = fetch_cache_seqnum( domain, t );
482 if ( NT_STATUS_IS_OK(status) )
483 goto done;
485 /* important! make sure that we know if this is a native
486 mode domain or not */
488 status = domain->backend->sequence_number(domain, &domain->sequence_number);
490 /* the above call could have set our domain->backend to NULL when
491 * coming from offline to online mode, make sure to reinitialize the
492 * backend - Guenther */
493 get_cache( domain );
495 if (!NT_STATUS_IS_OK(status)) {
496 DEBUG(10,("refresh_sequence_number: failed with %s\n", nt_errstr(status)));
497 domain->sequence_number = DOM_SEQUENCE_NONE;
500 domain->last_status = status;
501 domain->last_seq_check = time(NULL);
503 /* save the new sequence number ni the cache */
504 store_cache_seqnum( domain );
506 done:
507 DEBUG(10, ("refresh_sequence_number: %s seq number is now %d\n",
508 domain->name, domain->sequence_number));
510 return;
514 decide if a cache entry has expired
516 static BOOL centry_expired(struct winbindd_domain *domain, const char *keystr, struct cache_entry *centry)
518 /* If we've been told to be offline - stay in that state... */
519 if (lp_winbind_offline_logon() && global_winbindd_offline_state) {
520 DEBUG(10,("centry_expired: Key %s for domain %s valid as winbindd is globally offline.\n",
521 keystr, domain->name ));
522 return False;
525 /* when the domain is offline return the cached entry.
526 * This deals with transient offline states... */
528 if (!domain->online) {
529 DEBUG(10,("centry_expired: Key %s for domain %s valid as domain is offline.\n",
530 keystr, domain->name ));
531 return False;
534 /* if the server is OK and our cache entry came from when it was down then
535 the entry is invalid */
536 if ((domain->sequence_number != DOM_SEQUENCE_NONE) &&
537 (centry->sequence_number == DOM_SEQUENCE_NONE)) {
538 DEBUG(10,("centry_expired: Key %s for domain %s invalid sequence.\n",
539 keystr, domain->name ));
540 return True;
543 /* if the server is down or the cache entry is not older than the
544 current sequence number then it is OK */
545 if (wcache_server_down(domain) ||
546 centry->sequence_number == domain->sequence_number) {
547 DEBUG(10,("centry_expired: Key %s for domain %s is good.\n",
548 keystr, domain->name ));
549 return False;
552 DEBUG(10,("centry_expired: Key %s for domain %s expired\n",
553 keystr, domain->name ));
555 /* it's expired */
556 return True;
559 static struct cache_entry *wcache_fetch_raw(char *kstr)
561 TDB_DATA data;
562 struct cache_entry *centry;
563 TDB_DATA key;
565 key = string_tdb_data(kstr);
566 data = tdb_fetch(wcache->tdb, key);
567 if (!data.dptr) {
568 /* a cache miss */
569 return NULL;
572 centry = SMB_XMALLOC_P(struct cache_entry);
573 centry->data = (unsigned char *)data.dptr;
574 centry->len = data.dsize;
575 centry->ofs = 0;
577 if (centry->len < 8) {
578 /* huh? corrupt cache? */
579 DEBUG(10,("wcache_fetch_raw: Corrupt cache for key %s (len < 8) ?\n", kstr));
580 centry_free(centry);
581 return NULL;
584 centry->status = NT_STATUS(centry_uint32(centry));
585 centry->sequence_number = centry_uint32(centry);
587 return centry;
591 fetch an entry from the cache, with a varargs key. auto-fetch the sequence
592 number and return status
594 static struct cache_entry *wcache_fetch(struct winbind_cache *cache,
595 struct winbindd_domain *domain,
596 const char *format, ...) PRINTF_ATTRIBUTE(3,4);
597 static struct cache_entry *wcache_fetch(struct winbind_cache *cache,
598 struct winbindd_domain *domain,
599 const char *format, ...)
601 va_list ap;
602 char *kstr;
603 struct cache_entry *centry;
605 if (opt_nocache) {
606 return NULL;
609 refresh_sequence_number(domain, False);
611 va_start(ap, format);
612 smb_xvasprintf(&kstr, format, ap);
613 va_end(ap);
615 centry = wcache_fetch_raw(kstr);
616 if (centry == NULL) {
617 free(kstr);
618 return NULL;
621 if (centry_expired(domain, kstr, centry)) {
623 DEBUG(10,("wcache_fetch: entry %s expired for domain %s\n",
624 kstr, domain->name ));
626 centry_free(centry);
627 free(kstr);
628 return NULL;
631 DEBUG(10,("wcache_fetch: returning entry %s for domain %s\n",
632 kstr, domain->name ));
634 free(kstr);
635 return centry;
638 static void wcache_delete(const char *format, ...) PRINTF_ATTRIBUTE(1,2);
639 static void wcache_delete(const char *format, ...)
641 va_list ap;
642 char *kstr;
643 TDB_DATA key;
645 va_start(ap, format);
646 smb_xvasprintf(&kstr, format, ap);
647 va_end(ap);
649 key = string_tdb_data(kstr);
651 tdb_delete(wcache->tdb, key);
652 free(kstr);
656 make sure we have at least len bytes available in a centry
658 static void centry_expand(struct cache_entry *centry, uint32 len)
660 if (centry->len - centry->ofs >= len)
661 return;
662 centry->len *= 2;
663 centry->data = SMB_REALLOC_ARRAY(centry->data, unsigned char,
664 centry->len);
665 if (!centry->data) {
666 DEBUG(0,("out of memory: needed %d bytes in centry_expand\n", centry->len));
667 smb_panic_fn("out of memory in centry_expand");
672 push a uint32 into a centry
674 static void centry_put_uint32(struct cache_entry *centry, uint32 v)
676 centry_expand(centry, 4);
677 SIVAL(centry->data, centry->ofs, v);
678 centry->ofs += 4;
682 push a uint16 into a centry
684 static void centry_put_uint16(struct cache_entry *centry, uint16 v)
686 centry_expand(centry, 2);
687 SIVAL(centry->data, centry->ofs, v);
688 centry->ofs += 2;
692 push a uint8 into a centry
694 static void centry_put_uint8(struct cache_entry *centry, uint8 v)
696 centry_expand(centry, 1);
697 SCVAL(centry->data, centry->ofs, v);
698 centry->ofs += 1;
702 push a string into a centry
704 static void centry_put_string(struct cache_entry *centry, const char *s)
706 int len;
708 if (!s) {
709 /* null strings are marked as len 0xFFFF */
710 centry_put_uint8(centry, 0xFF);
711 return;
714 len = strlen(s);
715 /* can't handle more than 254 char strings. Truncating is probably best */
716 if (len > 254) {
717 DEBUG(10,("centry_put_string: truncating len (%d) to: 254\n", len));
718 len = 254;
720 centry_put_uint8(centry, len);
721 centry_expand(centry, len);
722 memcpy(centry->data + centry->ofs, s, len);
723 centry->ofs += len;
727 push a 16 byte hash into a centry - treat as 16 byte string.
729 static void centry_put_hash16(struct cache_entry *centry, const uint8 val[16])
731 centry_put_uint8(centry, 16);
732 centry_expand(centry, 16);
733 memcpy(centry->data + centry->ofs, val, 16);
734 centry->ofs += 16;
737 static void centry_put_sid(struct cache_entry *centry, const DOM_SID *sid)
739 fstring sid_string;
740 centry_put_string(centry, sid_to_string(sid_string, sid));
744 push a NTTIME into a centry
746 static void centry_put_nttime(struct cache_entry *centry, NTTIME nt)
748 centry_expand(centry, 8);
749 SIVAL(centry->data, centry->ofs, nt & 0xFFFFFFFF);
750 centry->ofs += 4;
751 SIVAL(centry->data, centry->ofs, nt >> 32);
752 centry->ofs += 4;
756 push a time_t into a centry - use a 64 bit size.
757 NTTIME here is being used as a convenient 64-bit size.
759 static void centry_put_time(struct cache_entry *centry, time_t t)
761 NTTIME nt = (NTTIME)t;
762 return centry_put_nttime(centry, nt);
766 start a centry for output. When finished, call centry_end()
768 struct cache_entry *centry_start(struct winbindd_domain *domain, NTSTATUS status)
770 struct cache_entry *centry;
772 if (!wcache->tdb)
773 return NULL;
775 centry = SMB_XMALLOC_P(struct cache_entry);
777 centry->len = 8192; /* reasonable default */
778 centry->data = SMB_XMALLOC_ARRAY(uint8, centry->len);
779 centry->ofs = 0;
780 centry->sequence_number = domain->sequence_number;
781 centry_put_uint32(centry, NT_STATUS_V(status));
782 centry_put_uint32(centry, centry->sequence_number);
783 return centry;
787 finish a centry and write it to the tdb
789 static void centry_end(struct cache_entry *centry, const char *format, ...) PRINTF_ATTRIBUTE(2,3);
790 static void centry_end(struct cache_entry *centry, const char *format, ...)
792 va_list ap;
793 char *kstr;
794 TDB_DATA key, data;
796 va_start(ap, format);
797 smb_xvasprintf(&kstr, format, ap);
798 va_end(ap);
800 key = string_tdb_data(kstr);
801 data.dptr = centry->data;
802 data.dsize = centry->ofs;
804 tdb_store(wcache->tdb, key, data, TDB_REPLACE);
805 free(kstr);
808 static void wcache_save_name_to_sid(struct winbindd_domain *domain,
809 NTSTATUS status, const char *domain_name,
810 const char *name, const DOM_SID *sid,
811 enum lsa_SidType type)
813 struct cache_entry *centry;
814 fstring uname;
816 centry = centry_start(domain, status);
817 if (!centry)
818 return;
819 centry_put_uint32(centry, type);
820 centry_put_sid(centry, sid);
821 fstrcpy(uname, name);
822 strupper_m(uname);
823 centry_end(centry, "NS/%s/%s", domain_name, uname);
824 DEBUG(10,("wcache_save_name_to_sid: %s\\%s -> %s\n", domain_name, uname,
825 sid_string_static(sid)));
826 centry_free(centry);
829 static void wcache_save_sid_to_name(struct winbindd_domain *domain, NTSTATUS status,
830 const DOM_SID *sid, const char *domain_name, const char *name, enum lsa_SidType type)
832 struct cache_entry *centry;
833 fstring sid_string;
835 if (is_null_sid(sid)) {
836 return;
839 centry = centry_start(domain, status);
840 if (!centry)
841 return;
842 if (NT_STATUS_IS_OK(status)) {
843 centry_put_uint32(centry, type);
844 centry_put_string(centry, domain_name);
845 centry_put_string(centry, name);
847 centry_end(centry, "SN/%s", sid_to_string(sid_string, sid));
848 DEBUG(10,("wcache_save_sid_to_name: %s -> %s\n", sid_string, name));
849 centry_free(centry);
853 static void wcache_save_user(struct winbindd_domain *domain, NTSTATUS status, WINBIND_USERINFO *info)
855 struct cache_entry *centry;
856 fstring sid_string;
858 if (is_null_sid(&info->user_sid)) {
859 return;
862 centry = centry_start(domain, status);
863 if (!centry)
864 return;
865 centry_put_string(centry, info->acct_name);
866 centry_put_string(centry, info->full_name);
867 centry_put_string(centry, info->homedir);
868 centry_put_string(centry, info->shell);
869 centry_put_uint32(centry, info->primary_gid);
870 centry_put_sid(centry, &info->user_sid);
871 centry_put_sid(centry, &info->group_sid);
872 centry_end(centry, "U/%s", sid_to_string(sid_string, &info->user_sid));
873 DEBUG(10,("wcache_save_user: %s (acct_name %s)\n", sid_string, info->acct_name));
874 centry_free(centry);
877 static void wcache_save_lockout_policy(struct winbindd_domain *domain, NTSTATUS status, SAM_UNK_INFO_12 *lockout_policy)
879 struct cache_entry *centry;
881 centry = centry_start(domain, status);
882 if (!centry)
883 return;
885 centry_put_nttime(centry, lockout_policy->duration);
886 centry_put_nttime(centry, lockout_policy->reset_count);
887 centry_put_uint16(centry, lockout_policy->bad_attempt_lockout);
889 centry_end(centry, "LOC_POL/%s", domain->name);
891 DEBUG(10,("wcache_save_lockout_policy: %s\n", domain->name));
893 centry_free(centry);
896 static void wcache_save_password_policy(struct winbindd_domain *domain, NTSTATUS status, SAM_UNK_INFO_1 *policy)
898 struct cache_entry *centry;
900 centry = centry_start(domain, status);
901 if (!centry)
902 return;
904 centry_put_uint16(centry, policy->min_length_password);
905 centry_put_uint16(centry, policy->password_history);
906 centry_put_uint32(centry, policy->password_properties);
907 centry_put_nttime(centry, policy->expire);
908 centry_put_nttime(centry, policy->min_passwordage);
910 centry_end(centry, "PWD_POL/%s", domain->name);
912 DEBUG(10,("wcache_save_password_policy: %s\n", domain->name));
914 centry_free(centry);
917 NTSTATUS wcache_cached_creds_exist(struct winbindd_domain *domain, const DOM_SID *sid)
919 struct winbind_cache *cache = get_cache(domain);
920 TDB_DATA data;
921 fstring key_str;
922 uint32 rid;
924 if (!cache->tdb) {
925 return NT_STATUS_INTERNAL_DB_ERROR;
928 if (is_null_sid(sid)) {
929 return NT_STATUS_INVALID_SID;
932 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
933 return NT_STATUS_INVALID_SID;
936 fstr_sprintf(key_str, "CRED/%s", sid_string_static(sid));
938 data = tdb_fetch(cache->tdb, string_tdb_data(key_str));
939 if (!data.dptr) {
940 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
943 SAFE_FREE(data.dptr);
944 return NT_STATUS_OK;
947 /* Lookup creds for a SID - copes with old (unsalted) creds as well
948 as new salted ones. */
950 NTSTATUS wcache_get_creds(struct winbindd_domain *domain,
951 TALLOC_CTX *mem_ctx,
952 const DOM_SID *sid,
953 const uint8 **cached_nt_pass,
954 const uint8 **cached_salt)
956 struct winbind_cache *cache = get_cache(domain);
957 struct cache_entry *centry = NULL;
958 NTSTATUS status;
959 time_t t;
960 uint32 rid;
962 if (!cache->tdb) {
963 return NT_STATUS_INTERNAL_DB_ERROR;
966 if (is_null_sid(sid)) {
967 return NT_STATUS_INVALID_SID;
970 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
971 return NT_STATUS_INVALID_SID;
974 /* Try and get a salted cred first. If we can't
975 fall back to an unsalted cred. */
977 centry = wcache_fetch(cache, domain, "CRED/%s", sid_string_static(sid));
978 if (!centry) {
979 DEBUG(10,("wcache_get_creds: entry for [CRED/%s] not found\n",
980 sid_string_static(sid)));
981 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
984 t = centry_time(centry);
986 /* In the salted case this isn't actually the nt_hash itself,
987 but the MD5 of the salt + nt_hash. Let the caller
988 sort this out. It can tell as we only return the cached_salt
989 if we are returning a salted cred. */
991 *cached_nt_pass = (const uint8 *)centry_hash16(centry, mem_ctx);
992 if (*cached_nt_pass == NULL) {
993 const char *sidstr = sid_string_static(sid);
995 /* Bad (old) cred cache. Delete and pretend we
996 don't have it. */
997 DEBUG(0,("wcache_get_creds: bad entry for [CRED/%s] - deleting\n",
998 sidstr));
999 wcache_delete("CRED/%s", sidstr);
1000 centry_free(centry);
1001 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1004 /* We only have 17 bytes more data in the salted cred case. */
1005 if (centry->len - centry->ofs == 17) {
1006 *cached_salt = (const uint8 *)centry_hash16(centry, mem_ctx);
1007 } else {
1008 *cached_salt = NULL;
1011 #if DEBUG_PASSWORD
1012 dump_data(100, *cached_nt_pass, NT_HASH_LEN);
1013 if (*cached_salt) {
1014 dump_data(100, *cached_salt, NT_HASH_LEN);
1016 #endif
1017 status = centry->status;
1019 DEBUG(10,("wcache_get_creds: [Cached] - cached creds for user %s status: %s\n",
1020 sid_string_static(sid), nt_errstr(status) ));
1022 centry_free(centry);
1023 return status;
1026 /* Store creds for a SID - only writes out new salted ones. */
1028 NTSTATUS wcache_save_creds(struct winbindd_domain *domain,
1029 TALLOC_CTX *mem_ctx,
1030 const DOM_SID *sid,
1031 const uint8 nt_pass[NT_HASH_LEN])
1033 struct cache_entry *centry;
1034 fstring sid_string;
1035 uint32 rid;
1036 uint8 cred_salt[NT_HASH_LEN];
1037 uint8 salted_hash[NT_HASH_LEN];
1039 if (is_null_sid(sid)) {
1040 return NT_STATUS_INVALID_SID;
1043 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1044 return NT_STATUS_INVALID_SID;
1047 centry = centry_start(domain, NT_STATUS_OK);
1048 if (!centry) {
1049 return NT_STATUS_INTERNAL_DB_ERROR;
1052 #if DEBUG_PASSWORD
1053 dump_data(100, nt_pass, NT_HASH_LEN);
1054 #endif
1056 centry_put_time(centry, time(NULL));
1058 /* Create a salt and then salt the hash. */
1059 generate_random_buffer(cred_salt, NT_HASH_LEN);
1060 E_md5hash(cred_salt, nt_pass, salted_hash);
1062 centry_put_hash16(centry, salted_hash);
1063 centry_put_hash16(centry, cred_salt);
1064 centry_end(centry, "CRED/%s", sid_to_string(sid_string, sid));
1066 DEBUG(10,("wcache_save_creds: %s\n", sid_string));
1068 centry_free(centry);
1070 return NT_STATUS_OK;
1074 /* Query display info. This is the basic user list fn */
1075 static NTSTATUS query_user_list(struct winbindd_domain *domain,
1076 TALLOC_CTX *mem_ctx,
1077 uint32 *num_entries,
1078 WINBIND_USERINFO **info)
1080 struct winbind_cache *cache = get_cache(domain);
1081 struct cache_entry *centry = NULL;
1082 NTSTATUS status;
1083 unsigned int i, retry;
1085 if (!cache->tdb)
1086 goto do_query;
1088 centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
1089 if (!centry)
1090 goto do_query;
1092 *num_entries = centry_uint32(centry);
1094 if (*num_entries == 0)
1095 goto do_cached;
1097 (*info) = TALLOC_ARRAY(mem_ctx, WINBIND_USERINFO, *num_entries);
1098 if (! (*info)) {
1099 smb_panic_fn("query_user_list out of memory");
1100 centry_free(centry);
1101 return NT_STATUS_NO_MEMORY;
1103 for (i=0; i<(*num_entries); i++) {
1104 (*info)[i].acct_name = centry_string(centry, mem_ctx);
1105 (*info)[i].full_name = centry_string(centry, mem_ctx);
1106 (*info)[i].homedir = centry_string(centry, mem_ctx);
1107 (*info)[i].shell = centry_string(centry, mem_ctx);
1108 centry_sid(centry, mem_ctx, &(*info)[i].user_sid);
1109 centry_sid(centry, mem_ctx, &(*info)[i].group_sid);
1112 do_cached:
1113 status = centry->status;
1115 DEBUG(10,("query_user_list: [Cached] - cached list for domain %s status: %s\n",
1116 domain->name, nt_errstr(status) ));
1118 centry_free(centry);
1119 return status;
1121 do_query:
1122 *num_entries = 0;
1123 *info = NULL;
1125 /* Return status value returned by seq number check */
1127 if (!NT_STATUS_IS_OK(domain->last_status))
1128 return domain->last_status;
1130 /* Put the query_user_list() in a retry loop. There appears to be
1131 * some bug either with Windows 2000 or Samba's handling of large
1132 * rpc replies. This manifests itself as sudden disconnection
1133 * at a random point in the enumeration of a large (60k) user list.
1134 * The retry loop simply tries the operation again. )-: It's not
1135 * pretty but an acceptable workaround until we work out what the
1136 * real problem is. */
1138 retry = 0;
1139 do {
1141 DEBUG(10,("query_user_list: [Cached] - doing backend query for list for domain %s\n",
1142 domain->name ));
1144 status = domain->backend->query_user_list(domain, mem_ctx, num_entries, info);
1145 if (!NT_STATUS_IS_OK(status))
1146 DEBUG(3, ("query_user_list: returned 0x%08x, "
1147 "retrying\n", NT_STATUS_V(status)));
1148 if (NT_STATUS_EQUAL(status, NT_STATUS_UNSUCCESSFUL)) {
1149 DEBUG(3, ("query_user_list: flushing "
1150 "connection cache\n"));
1151 invalidate_cm_connection(&domain->conn);
1154 } while (NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_UNSUCCESSFUL) &&
1155 (retry++ < 5));
1157 /* and save it */
1158 refresh_sequence_number(domain, False);
1159 centry = centry_start(domain, status);
1160 if (!centry)
1161 goto skip_save;
1162 centry_put_uint32(centry, *num_entries);
1163 for (i=0; i<(*num_entries); i++) {
1164 centry_put_string(centry, (*info)[i].acct_name);
1165 centry_put_string(centry, (*info)[i].full_name);
1166 centry_put_string(centry, (*info)[i].homedir);
1167 centry_put_string(centry, (*info)[i].shell);
1168 centry_put_sid(centry, &(*info)[i].user_sid);
1169 centry_put_sid(centry, &(*info)[i].group_sid);
1170 if (domain->backend && domain->backend->consistent) {
1171 /* when the backend is consistent we can pre-prime some mappings */
1172 wcache_save_name_to_sid(domain, NT_STATUS_OK,
1173 domain->name,
1174 (*info)[i].acct_name,
1175 &(*info)[i].user_sid,
1176 SID_NAME_USER);
1177 wcache_save_sid_to_name(domain, NT_STATUS_OK,
1178 &(*info)[i].user_sid,
1179 domain->name,
1180 (*info)[i].acct_name,
1181 SID_NAME_USER);
1182 wcache_save_user(domain, NT_STATUS_OK, &(*info)[i]);
1185 centry_end(centry, "UL/%s", domain->name);
1186 centry_free(centry);
1188 skip_save:
1189 return status;
1192 /* list all domain groups */
1193 static NTSTATUS enum_dom_groups(struct winbindd_domain *domain,
1194 TALLOC_CTX *mem_ctx,
1195 uint32 *num_entries,
1196 struct acct_info **info)
1198 struct winbind_cache *cache = get_cache(domain);
1199 struct cache_entry *centry = NULL;
1200 NTSTATUS status;
1201 unsigned int i;
1203 if (!cache->tdb)
1204 goto do_query;
1206 centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
1207 if (!centry)
1208 goto do_query;
1210 *num_entries = centry_uint32(centry);
1212 if (*num_entries == 0)
1213 goto do_cached;
1215 (*info) = TALLOC_ARRAY(mem_ctx, struct acct_info, *num_entries);
1216 if (! (*info)) {
1217 smb_panic_fn("enum_dom_groups out of memory");
1218 centry_free(centry);
1219 return NT_STATUS_NO_MEMORY;
1221 for (i=0; i<(*num_entries); i++) {
1222 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1223 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1224 (*info)[i].rid = centry_uint32(centry);
1227 do_cached:
1228 status = centry->status;
1230 DEBUG(10,("enum_dom_groups: [Cached] - cached list for domain %s status: %s\n",
1231 domain->name, nt_errstr(status) ));
1233 centry_free(centry);
1234 return status;
1236 do_query:
1237 *num_entries = 0;
1238 *info = NULL;
1240 /* Return status value returned by seq number check */
1242 if (!NT_STATUS_IS_OK(domain->last_status))
1243 return domain->last_status;
1245 DEBUG(10,("enum_dom_groups: [Cached] - doing backend query for list for domain %s\n",
1246 domain->name ));
1248 status = domain->backend->enum_dom_groups(domain, mem_ctx, num_entries, info);
1250 /* and save it */
1251 refresh_sequence_number(domain, False);
1252 centry = centry_start(domain, status);
1253 if (!centry)
1254 goto skip_save;
1255 centry_put_uint32(centry, *num_entries);
1256 for (i=0; i<(*num_entries); i++) {
1257 centry_put_string(centry, (*info)[i].acct_name);
1258 centry_put_string(centry, (*info)[i].acct_desc);
1259 centry_put_uint32(centry, (*info)[i].rid);
1261 centry_end(centry, "GL/%s/domain", domain->name);
1262 centry_free(centry);
1264 skip_save:
1265 return status;
1268 /* list all domain groups */
1269 static NTSTATUS enum_local_groups(struct winbindd_domain *domain,
1270 TALLOC_CTX *mem_ctx,
1271 uint32 *num_entries,
1272 struct acct_info **info)
1274 struct winbind_cache *cache = get_cache(domain);
1275 struct cache_entry *centry = NULL;
1276 NTSTATUS status;
1277 unsigned int i;
1279 if (!cache->tdb)
1280 goto do_query;
1282 centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
1283 if (!centry)
1284 goto do_query;
1286 *num_entries = centry_uint32(centry);
1288 if (*num_entries == 0)
1289 goto do_cached;
1291 (*info) = TALLOC_ARRAY(mem_ctx, struct acct_info, *num_entries);
1292 if (! (*info)) {
1293 smb_panic_fn("enum_dom_groups out of memory");
1294 centry_free(centry);
1295 return NT_STATUS_NO_MEMORY;
1297 for (i=0; i<(*num_entries); i++) {
1298 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1299 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1300 (*info)[i].rid = centry_uint32(centry);
1303 do_cached:
1305 /* If we are returning cached data and the domain controller
1306 is down then we don't know whether the data is up to date
1307 or not. Return NT_STATUS_MORE_PROCESSING_REQUIRED to
1308 indicate this. */
1310 if (wcache_server_down(domain)) {
1311 DEBUG(10, ("enum_local_groups: returning cached user list and server was down\n"));
1312 status = NT_STATUS_MORE_PROCESSING_REQUIRED;
1313 } else
1314 status = centry->status;
1316 DEBUG(10,("enum_local_groups: [Cached] - cached list for domain %s status: %s\n",
1317 domain->name, nt_errstr(status) ));
1319 centry_free(centry);
1320 return status;
1322 do_query:
1323 *num_entries = 0;
1324 *info = NULL;
1326 /* Return status value returned by seq number check */
1328 if (!NT_STATUS_IS_OK(domain->last_status))
1329 return domain->last_status;
1331 DEBUG(10,("enum_local_groups: [Cached] - doing backend query for list for domain %s\n",
1332 domain->name ));
1334 status = domain->backend->enum_local_groups(domain, mem_ctx, num_entries, info);
1336 /* and save it */
1337 refresh_sequence_number(domain, False);
1338 centry = centry_start(domain, status);
1339 if (!centry)
1340 goto skip_save;
1341 centry_put_uint32(centry, *num_entries);
1342 for (i=0; i<(*num_entries); i++) {
1343 centry_put_string(centry, (*info)[i].acct_name);
1344 centry_put_string(centry, (*info)[i].acct_desc);
1345 centry_put_uint32(centry, (*info)[i].rid);
1347 centry_end(centry, "GL/%s/local", domain->name);
1348 centry_free(centry);
1350 skip_save:
1351 return status;
1354 /* convert a single name to a sid in a domain */
1355 static NTSTATUS name_to_sid(struct winbindd_domain *domain,
1356 TALLOC_CTX *mem_ctx,
1357 const char *domain_name,
1358 const char *name,
1359 DOM_SID *sid,
1360 enum lsa_SidType *type)
1362 struct winbind_cache *cache = get_cache(domain);
1363 struct cache_entry *centry = NULL;
1364 NTSTATUS status;
1365 fstring uname;
1367 if (!cache->tdb)
1368 goto do_query;
1370 fstrcpy(uname, name);
1371 strupper_m(uname);
1372 centry = wcache_fetch(cache, domain, "NS/%s/%s", domain_name, uname);
1373 if (!centry)
1374 goto do_query;
1375 *type = (enum lsa_SidType)centry_uint32(centry);
1376 status = centry->status;
1377 if (NT_STATUS_IS_OK(status)) {
1378 centry_sid(centry, mem_ctx, sid);
1381 DEBUG(10,("name_to_sid: [Cached] - cached name for domain %s status: %s\n",
1382 domain->name, nt_errstr(status) ));
1384 centry_free(centry);
1385 return status;
1387 do_query:
1388 ZERO_STRUCTP(sid);
1390 /* If the seq number check indicated that there is a problem
1391 * with this DC, then return that status... except for
1392 * access_denied. This is special because the dc may be in
1393 * "restrict anonymous = 1" mode, in which case it will deny
1394 * most unauthenticated operations, but *will* allow the LSA
1395 * name-to-sid that we try as a fallback. */
1397 if (!(NT_STATUS_IS_OK(domain->last_status)
1398 || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1399 return domain->last_status;
1401 DEBUG(10,("name_to_sid: [Cached] - doing backend query for name for domain %s\n",
1402 domain->name ));
1404 status = domain->backend->name_to_sid(domain, mem_ctx, domain_name, name, sid, type);
1406 /* and save it */
1407 refresh_sequence_number(domain, False);
1409 if (domain->online && !is_null_sid(sid)) {
1410 wcache_save_name_to_sid(domain, status, domain_name, name, sid, *type);
1413 if (NT_STATUS_IS_OK(status)) {
1414 strupper_m(CONST_DISCARD(char *,domain_name));
1415 strlower_m(CONST_DISCARD(char *,name));
1416 wcache_save_sid_to_name(domain, status, sid, domain_name, name, *type);
1419 return status;
1422 /* convert a sid to a user or group name. The sid is guaranteed to be in the domain
1423 given */
1424 static NTSTATUS sid_to_name(struct winbindd_domain *domain,
1425 TALLOC_CTX *mem_ctx,
1426 const DOM_SID *sid,
1427 char **domain_name,
1428 char **name,
1429 enum lsa_SidType *type)
1431 struct winbind_cache *cache = get_cache(domain);
1432 struct cache_entry *centry = NULL;
1433 NTSTATUS status;
1434 fstring sid_string;
1436 if (!cache->tdb)
1437 goto do_query;
1439 centry = wcache_fetch(cache, domain, "SN/%s", sid_to_string(sid_string, sid));
1440 if (!centry)
1441 goto do_query;
1442 if (NT_STATUS_IS_OK(centry->status)) {
1443 *type = (enum lsa_SidType)centry_uint32(centry);
1444 *domain_name = centry_string(centry, mem_ctx);
1445 *name = centry_string(centry, mem_ctx);
1447 status = centry->status;
1449 DEBUG(10,("sid_to_name: [Cached] - cached name for domain %s status: %s\n",
1450 domain->name, nt_errstr(status) ));
1452 centry_free(centry);
1453 return status;
1455 do_query:
1456 *name = NULL;
1457 *domain_name = NULL;
1459 /* If the seq number check indicated that there is a problem
1460 * with this DC, then return that status... except for
1461 * access_denied. This is special because the dc may be in
1462 * "restrict anonymous = 1" mode, in which case it will deny
1463 * most unauthenticated operations, but *will* allow the LSA
1464 * sid-to-name that we try as a fallback. */
1466 if (!(NT_STATUS_IS_OK(domain->last_status)
1467 || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1468 return domain->last_status;
1470 DEBUG(10,("sid_to_name: [Cached] - doing backend query for name for domain %s\n",
1471 domain->name ));
1473 status = domain->backend->sid_to_name(domain, mem_ctx, sid, domain_name, name, type);
1475 /* and save it */
1476 refresh_sequence_number(domain, False);
1477 wcache_save_sid_to_name(domain, status, sid, *domain_name, *name, *type);
1479 /* We can't save the name to sid mapping here, as with sid history a
1480 * later name2sid would give the wrong sid. */
1482 return status;
1485 static NTSTATUS rids_to_names(struct winbindd_domain *domain,
1486 TALLOC_CTX *mem_ctx,
1487 const DOM_SID *domain_sid,
1488 uint32 *rids,
1489 size_t num_rids,
1490 char **domain_name,
1491 char ***names,
1492 enum lsa_SidType **types)
1494 struct winbind_cache *cache = get_cache(domain);
1495 size_t i;
1496 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
1497 BOOL have_mapped;
1498 BOOL have_unmapped;
1500 *domain_name = NULL;
1501 *names = NULL;
1502 *types = NULL;
1504 if (!cache->tdb) {
1505 goto do_query;
1508 if (num_rids == 0) {
1509 return NT_STATUS_OK;
1512 *names = TALLOC_ARRAY(mem_ctx, char *, num_rids);
1513 *types = TALLOC_ARRAY(mem_ctx, enum lsa_SidType, num_rids);
1515 if ((*names == NULL) || (*types == NULL)) {
1516 result = NT_STATUS_NO_MEMORY;
1517 goto error;
1520 have_mapped = have_unmapped = False;
1522 for (i=0; i<num_rids; i++) {
1523 DOM_SID sid;
1524 struct cache_entry *centry;
1526 if (!sid_compose(&sid, domain_sid, rids[i])) {
1527 result = NT_STATUS_INTERNAL_ERROR;
1528 goto error;
1531 centry = wcache_fetch(cache, domain, "SN/%s",
1532 sid_string_static(&sid));
1533 if (!centry) {
1534 goto do_query;
1537 (*types)[i] = SID_NAME_UNKNOWN;
1538 (*names)[i] = talloc_strdup(*names, "");
1540 if (NT_STATUS_IS_OK(centry->status)) {
1541 char *dom;
1542 have_mapped = True;
1543 (*types)[i] = (enum lsa_SidType)centry_uint32(centry);
1544 dom = centry_string(centry, mem_ctx);
1545 if (*domain_name == NULL) {
1546 *domain_name = dom;
1547 } else {
1548 talloc_free(dom);
1550 (*names)[i] = centry_string(centry, *names);
1551 } else {
1552 have_unmapped = True;
1555 centry_free(centry);
1558 if (!have_mapped) {
1559 return NT_STATUS_NONE_MAPPED;
1561 if (!have_unmapped) {
1562 return NT_STATUS_OK;
1564 return STATUS_SOME_UNMAPPED;
1566 do_query:
1568 TALLOC_FREE(*names);
1569 TALLOC_FREE(*types);
1571 result = domain->backend->rids_to_names(domain, mem_ctx, domain_sid,
1572 rids, num_rids, domain_name,
1573 names, types);
1575 if (!NT_STATUS_IS_OK(result) &&
1576 !NT_STATUS_EQUAL(result, STATUS_SOME_UNMAPPED)) {
1577 return result;
1580 refresh_sequence_number(domain, False);
1582 for (i=0; i<num_rids; i++) {
1583 DOM_SID sid;
1584 NTSTATUS status;
1586 if (!sid_compose(&sid, domain_sid, rids[i])) {
1587 result = NT_STATUS_INTERNAL_ERROR;
1588 goto error;
1591 status = (*types)[i] == SID_NAME_UNKNOWN ?
1592 NT_STATUS_NONE_MAPPED : NT_STATUS_OK;
1594 wcache_save_sid_to_name(domain, status, &sid, *domain_name,
1595 (*names)[i], (*types)[i]);
1598 return result;
1600 error:
1602 TALLOC_FREE(*names);
1603 TALLOC_FREE(*types);
1604 return result;
1607 /* Lookup user information from a rid */
1608 static NTSTATUS query_user(struct winbindd_domain *domain,
1609 TALLOC_CTX *mem_ctx,
1610 const DOM_SID *user_sid,
1611 WINBIND_USERINFO *info)
1613 struct winbind_cache *cache = get_cache(domain);
1614 struct cache_entry *centry = NULL;
1615 NTSTATUS status;
1617 if (!cache->tdb)
1618 goto do_query;
1620 centry = wcache_fetch(cache, domain, "U/%s", sid_string_static(user_sid));
1622 /* If we have an access denied cache entry and a cached info3 in the
1623 samlogon cache then do a query. This will force the rpc back end
1624 to return the info3 data. */
1626 if (NT_STATUS_V(domain->last_status) == NT_STATUS_V(NT_STATUS_ACCESS_DENIED) &&
1627 netsamlogon_cache_have(user_sid)) {
1628 DEBUG(10, ("query_user: cached access denied and have cached info3\n"));
1629 domain->last_status = NT_STATUS_OK;
1630 centry_free(centry);
1631 goto do_query;
1634 if (!centry)
1635 goto do_query;
1637 info->acct_name = centry_string(centry, mem_ctx);
1638 info->full_name = centry_string(centry, mem_ctx);
1639 info->homedir = centry_string(centry, mem_ctx);
1640 info->shell = centry_string(centry, mem_ctx);
1641 info->primary_gid = centry_uint32(centry);
1642 centry_sid(centry, mem_ctx, &info->user_sid);
1643 centry_sid(centry, mem_ctx, &info->group_sid);
1644 status = centry->status;
1646 DEBUG(10,("query_user: [Cached] - cached info for domain %s status: %s\n",
1647 domain->name, nt_errstr(status) ));
1649 centry_free(centry);
1650 return status;
1652 do_query:
1653 ZERO_STRUCTP(info);
1655 /* Return status value returned by seq number check */
1657 if (!NT_STATUS_IS_OK(domain->last_status))
1658 return domain->last_status;
1660 DEBUG(10,("query_user: [Cached] - doing backend query for info for domain %s\n",
1661 domain->name ));
1663 status = domain->backend->query_user(domain, mem_ctx, user_sid, info);
1665 /* and save it */
1666 refresh_sequence_number(domain, False);
1667 wcache_save_user(domain, status, info);
1669 return status;
1673 /* Lookup groups a user is a member of. */
1674 static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
1675 TALLOC_CTX *mem_ctx,
1676 const DOM_SID *user_sid,
1677 uint32 *num_groups, DOM_SID **user_gids)
1679 struct winbind_cache *cache = get_cache(domain);
1680 struct cache_entry *centry = NULL;
1681 NTSTATUS status;
1682 unsigned int i;
1683 fstring sid_string;
1685 if (!cache->tdb)
1686 goto do_query;
1688 centry = wcache_fetch(cache, domain, "UG/%s", sid_to_string(sid_string, user_sid));
1690 /* If we have an access denied cache entry and a cached info3 in the
1691 samlogon cache then do a query. This will force the rpc back end
1692 to return the info3 data. */
1694 if (NT_STATUS_V(domain->last_status) == NT_STATUS_V(NT_STATUS_ACCESS_DENIED) &&
1695 netsamlogon_cache_have(user_sid)) {
1696 DEBUG(10, ("lookup_usergroups: cached access denied and have cached info3\n"));
1697 domain->last_status = NT_STATUS_OK;
1698 centry_free(centry);
1699 goto do_query;
1702 if (!centry)
1703 goto do_query;
1705 *num_groups = centry_uint32(centry);
1707 if (*num_groups == 0)
1708 goto do_cached;
1710 (*user_gids) = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_groups);
1711 if (! (*user_gids)) {
1712 smb_panic_fn("lookup_usergroups out of memory");
1713 centry_free(centry);
1714 return NT_STATUS_NO_MEMORY;
1716 for (i=0; i<(*num_groups); i++) {
1717 centry_sid(centry, mem_ctx, &(*user_gids)[i]);
1720 do_cached:
1721 status = centry->status;
1723 DEBUG(10,("lookup_usergroups: [Cached] - cached info for domain %s status: %s\n",
1724 domain->name, nt_errstr(status) ));
1726 centry_free(centry);
1727 return status;
1729 do_query:
1730 (*num_groups) = 0;
1731 (*user_gids) = NULL;
1733 /* Return status value returned by seq number check */
1735 if (!NT_STATUS_IS_OK(domain->last_status))
1736 return domain->last_status;
1738 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info for domain %s\n",
1739 domain->name ));
1741 status = domain->backend->lookup_usergroups(domain, mem_ctx, user_sid, num_groups, user_gids);
1743 /* and save it */
1744 refresh_sequence_number(domain, False);
1745 centry = centry_start(domain, status);
1746 if (!centry)
1747 goto skip_save;
1748 centry_put_uint32(centry, *num_groups);
1749 for (i=0; i<(*num_groups); i++) {
1750 centry_put_sid(centry, &(*user_gids)[i]);
1752 centry_end(centry, "UG/%s", sid_to_string(sid_string, user_sid));
1753 centry_free(centry);
1755 skip_save:
1756 return status;
1759 static NTSTATUS lookup_useraliases(struct winbindd_domain *domain,
1760 TALLOC_CTX *mem_ctx,
1761 uint32 num_sids, const DOM_SID *sids,
1762 uint32 *num_aliases, uint32 **alias_rids)
1764 struct winbind_cache *cache = get_cache(domain);
1765 struct cache_entry *centry = NULL;
1766 NTSTATUS status;
1767 char *sidlist = talloc_strdup(mem_ctx, "");
1768 int i;
1770 if (!cache->tdb)
1771 goto do_query;
1773 if (num_sids == 0) {
1774 *num_aliases = 0;
1775 *alias_rids = NULL;
1776 return NT_STATUS_OK;
1779 /* We need to cache indexed by the whole list of SIDs, the aliases
1780 * resulting might come from any of the SIDs. */
1782 for (i=0; i<num_sids; i++) {
1783 sidlist = talloc_asprintf(mem_ctx, "%s/%s", sidlist,
1784 sid_string_static(&sids[i]));
1785 if (sidlist == NULL)
1786 return NT_STATUS_NO_MEMORY;
1789 centry = wcache_fetch(cache, domain, "UA%s", sidlist);
1791 if (!centry)
1792 goto do_query;
1794 *num_aliases = centry_uint32(centry);
1795 *alias_rids = NULL;
1797 (*alias_rids) = TALLOC_ARRAY(mem_ctx, uint32, *num_aliases);
1799 if ((*num_aliases != 0) && ((*alias_rids) == NULL)) {
1800 centry_free(centry);
1801 return NT_STATUS_NO_MEMORY;
1804 for (i=0; i<(*num_aliases); i++)
1805 (*alias_rids)[i] = centry_uint32(centry);
1807 status = centry->status;
1809 DEBUG(10,("lookup_useraliases: [Cached] - cached info for domain: %s "
1810 "status %s\n", domain->name, nt_errstr(status)));
1812 centry_free(centry);
1813 return status;
1815 do_query:
1816 (*num_aliases) = 0;
1817 (*alias_rids) = NULL;
1819 if (!NT_STATUS_IS_OK(domain->last_status))
1820 return domain->last_status;
1822 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info "
1823 "for domain %s\n", domain->name ));
1825 status = domain->backend->lookup_useraliases(domain, mem_ctx,
1826 num_sids, sids,
1827 num_aliases, alias_rids);
1829 /* and save it */
1830 refresh_sequence_number(domain, False);
1831 centry = centry_start(domain, status);
1832 if (!centry)
1833 goto skip_save;
1834 centry_put_uint32(centry, *num_aliases);
1835 for (i=0; i<(*num_aliases); i++)
1836 centry_put_uint32(centry, (*alias_rids)[i]);
1837 centry_end(centry, "UA%s", sidlist);
1838 centry_free(centry);
1840 skip_save:
1841 return status;
1845 static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
1846 TALLOC_CTX *mem_ctx,
1847 const DOM_SID *group_sid, uint32 *num_names,
1848 DOM_SID **sid_mem, char ***names,
1849 uint32 **name_types)
1851 struct winbind_cache *cache = get_cache(domain);
1852 struct cache_entry *centry = NULL;
1853 NTSTATUS status;
1854 unsigned int i;
1855 fstring sid_string;
1857 if (!cache->tdb)
1858 goto do_query;
1860 centry = wcache_fetch(cache, domain, "GM/%s", sid_to_string(sid_string, group_sid));
1861 if (!centry)
1862 goto do_query;
1864 *num_names = centry_uint32(centry);
1866 if (*num_names == 0)
1867 goto do_cached;
1869 (*sid_mem) = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_names);
1870 (*names) = TALLOC_ARRAY(mem_ctx, char *, *num_names);
1871 (*name_types) = TALLOC_ARRAY(mem_ctx, uint32, *num_names);
1873 if (! (*sid_mem) || ! (*names) || ! (*name_types)) {
1874 smb_panic_fn("lookup_groupmem out of memory");
1875 centry_free(centry);
1876 return NT_STATUS_NO_MEMORY;
1879 for (i=0; i<(*num_names); i++) {
1880 centry_sid(centry, mem_ctx, &(*sid_mem)[i]);
1881 (*names)[i] = centry_string(centry, mem_ctx);
1882 (*name_types)[i] = centry_uint32(centry);
1885 do_cached:
1886 status = centry->status;
1888 DEBUG(10,("lookup_groupmem: [Cached] - cached info for domain %s status: %s\n",
1889 domain->name, nt_errstr(status)));
1891 centry_free(centry);
1892 return status;
1894 do_query:
1895 (*num_names) = 0;
1896 (*sid_mem) = NULL;
1897 (*names) = NULL;
1898 (*name_types) = NULL;
1900 /* Return status value returned by seq number check */
1902 if (!NT_STATUS_IS_OK(domain->last_status))
1903 return domain->last_status;
1905 DEBUG(10,("lookup_groupmem: [Cached] - doing backend query for info for domain %s\n",
1906 domain->name ));
1908 status = domain->backend->lookup_groupmem(domain, mem_ctx, group_sid, num_names,
1909 sid_mem, names, name_types);
1911 /* and save it */
1912 refresh_sequence_number(domain, False);
1913 centry = centry_start(domain, status);
1914 if (!centry)
1915 goto skip_save;
1916 centry_put_uint32(centry, *num_names);
1917 for (i=0; i<(*num_names); i++) {
1918 centry_put_sid(centry, &(*sid_mem)[i]);
1919 centry_put_string(centry, (*names)[i]);
1920 centry_put_uint32(centry, (*name_types)[i]);
1922 centry_end(centry, "GM/%s", sid_to_string(sid_string, group_sid));
1923 centry_free(centry);
1925 skip_save:
1926 return status;
1929 /* find the sequence number for a domain */
1930 static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
1932 refresh_sequence_number(domain, False);
1934 *seq = domain->sequence_number;
1936 return NT_STATUS_OK;
1939 /* enumerate trusted domains
1940 * (we need to have the list of trustdoms in the cache when we go offline) -
1941 * Guenther */
1942 static NTSTATUS trusted_domains(struct winbindd_domain *domain,
1943 TALLOC_CTX *mem_ctx,
1944 uint32 *num_domains,
1945 char ***names,
1946 char ***alt_names,
1947 DOM_SID **dom_sids)
1949 struct winbind_cache *cache = get_cache(domain);
1950 struct cache_entry *centry = NULL;
1951 NTSTATUS status;
1952 int i;
1954 if (!cache->tdb)
1955 goto do_query;
1957 centry = wcache_fetch(cache, domain, "TRUSTDOMS/%s", domain->name);
1959 if (!centry) {
1960 goto do_query;
1963 *num_domains = centry_uint32(centry);
1965 (*names) = TALLOC_ARRAY(mem_ctx, char *, *num_domains);
1966 (*alt_names) = TALLOC_ARRAY(mem_ctx, char *, *num_domains);
1967 (*dom_sids) = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_domains);
1969 if (! (*dom_sids) || ! (*names) || ! (*alt_names)) {
1970 smb_panic_fn("trusted_domains out of memory");
1971 centry_free(centry);
1972 return NT_STATUS_NO_MEMORY;
1975 for (i=0; i<(*num_domains); i++) {
1976 (*names)[i] = centry_string(centry, mem_ctx);
1977 (*alt_names)[i] = centry_string(centry, mem_ctx);
1978 centry_sid(centry, mem_ctx, &(*dom_sids)[i]);
1981 status = centry->status;
1983 DEBUG(10,("trusted_domains: [Cached] - cached info for domain %s (%d trusts) status: %s\n",
1984 domain->name, *num_domains, nt_errstr(status) ));
1986 centry_free(centry);
1987 return status;
1989 do_query:
1990 (*num_domains) = 0;
1991 (*dom_sids) = NULL;
1992 (*names) = NULL;
1993 (*alt_names) = NULL;
1995 /* Return status value returned by seq number check */
1997 if (!NT_STATUS_IS_OK(domain->last_status))
1998 return domain->last_status;
2000 DEBUG(10,("trusted_domains: [Cached] - doing backend query for info for domain %s\n",
2001 domain->name ));
2003 status = domain->backend->trusted_domains(domain, mem_ctx, num_domains,
2004 names, alt_names, dom_sids);
2006 /* no trusts gives NT_STATUS_NO_MORE_ENTRIES resetting to NT_STATUS_OK
2007 * so that the generic centry handling still applies correctly -
2008 * Guenther*/
2010 if (!NT_STATUS_IS_ERR(status)) {
2011 status = NT_STATUS_OK;
2014 /* and save it */
2015 refresh_sequence_number(domain, False);
2017 centry = centry_start(domain, status);
2018 if (!centry)
2019 goto skip_save;
2021 centry_put_uint32(centry, *num_domains);
2023 for (i=0; i<(*num_domains); i++) {
2024 centry_put_string(centry, (*names)[i]);
2025 centry_put_string(centry, (*alt_names)[i]);
2026 centry_put_sid(centry, &(*dom_sids)[i]);
2029 centry_end(centry, "TRUSTDOMS/%s", domain->name);
2031 centry_free(centry);
2033 skip_save:
2034 return status;
2037 /* get lockout policy */
2038 static NTSTATUS lockout_policy(struct winbindd_domain *domain,
2039 TALLOC_CTX *mem_ctx,
2040 SAM_UNK_INFO_12 *policy){
2041 struct winbind_cache *cache = get_cache(domain);
2042 struct cache_entry *centry = NULL;
2043 NTSTATUS status;
2045 if (!cache->tdb)
2046 goto do_query;
2048 centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
2050 if (!centry)
2051 goto do_query;
2053 policy->duration = centry_nttime(centry);
2054 policy->reset_count = centry_nttime(centry);
2055 policy->bad_attempt_lockout = centry_uint16(centry);
2057 status = centry->status;
2059 DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2060 domain->name, nt_errstr(status) ));
2062 centry_free(centry);
2063 return status;
2065 do_query:
2066 ZERO_STRUCTP(policy);
2068 /* Return status value returned by seq number check */
2070 if (!NT_STATUS_IS_OK(domain->last_status))
2071 return domain->last_status;
2073 DEBUG(10,("lockout_policy: [Cached] - doing backend query for info for domain %s\n",
2074 domain->name ));
2076 status = domain->backend->lockout_policy(domain, mem_ctx, policy);
2078 /* and save it */
2079 refresh_sequence_number(domain, False);
2080 wcache_save_lockout_policy(domain, status, policy);
2082 return status;
2085 /* get password policy */
2086 static NTSTATUS password_policy(struct winbindd_domain *domain,
2087 TALLOC_CTX *mem_ctx,
2088 SAM_UNK_INFO_1 *policy)
2090 struct winbind_cache *cache = get_cache(domain);
2091 struct cache_entry *centry = NULL;
2092 NTSTATUS status;
2094 if (!cache->tdb)
2095 goto do_query;
2097 centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
2099 if (!centry)
2100 goto do_query;
2102 policy->min_length_password = centry_uint16(centry);
2103 policy->password_history = centry_uint16(centry);
2104 policy->password_properties = centry_uint32(centry);
2105 policy->expire = centry_nttime(centry);
2106 policy->min_passwordage = centry_nttime(centry);
2108 status = centry->status;
2110 DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2111 domain->name, nt_errstr(status) ));
2113 centry_free(centry);
2114 return status;
2116 do_query:
2117 ZERO_STRUCTP(policy);
2119 /* Return status value returned by seq number check */
2121 if (!NT_STATUS_IS_OK(domain->last_status))
2122 return domain->last_status;
2124 DEBUG(10,("password_policy: [Cached] - doing backend query for info for domain %s\n",
2125 domain->name ));
2127 status = domain->backend->password_policy(domain, mem_ctx, policy);
2129 /* and save it */
2130 refresh_sequence_number(domain, False);
2131 wcache_save_password_policy(domain, status, policy);
2133 return status;
2137 /* Invalidate cached user and group lists coherently */
2139 static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
2140 void *state)
2142 if (strncmp((const char *)kbuf.dptr, "UL/", 3) == 0 ||
2143 strncmp((const char *)kbuf.dptr, "GL/", 3) == 0)
2144 tdb_delete(the_tdb, kbuf);
2146 return 0;
2149 /* Invalidate the getpwnam and getgroups entries for a winbindd domain */
2151 void wcache_invalidate_samlogon(struct winbindd_domain *domain,
2152 NET_USER_INFO_3 *info3)
2154 struct winbind_cache *cache;
2156 if (!domain)
2157 return;
2159 cache = get_cache(domain);
2160 netsamlogon_clear_cached_user(cache->tdb, info3);
2163 void wcache_invalidate_cache(void)
2165 struct winbindd_domain *domain;
2167 for (domain = domain_list(); domain; domain = domain->next) {
2168 struct winbind_cache *cache = get_cache(domain);
2170 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
2171 "entries for %s\n", domain->name));
2172 if (cache)
2173 tdb_traverse(cache->tdb, traverse_fn, NULL);
2177 static BOOL init_wcache(void)
2179 if (wcache == NULL) {
2180 wcache = SMB_XMALLOC_P(struct winbind_cache);
2181 ZERO_STRUCTP(wcache);
2184 if (wcache->tdb != NULL)
2185 return True;
2187 /* when working offline we must not clear the cache on restart */
2188 wcache->tdb = tdb_open_log(lock_path("winbindd_cache.tdb"),
2189 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
2190 lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST),
2191 O_RDWR|O_CREAT, 0600);
2193 if (wcache->tdb == NULL) {
2194 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
2195 return False;
2198 return True;
2201 /************************************************************************
2202 This is called by the parent to initialize the cache file.
2203 We don't need sophisticated locking here as we know we're the
2204 only opener.
2205 ************************************************************************/
2207 BOOL initialize_winbindd_cache(void)
2209 BOOL cache_bad = True;
2210 uint32 vers;
2212 if (!init_wcache()) {
2213 DEBUG(0,("initialize_winbindd_cache: init_wcache failed.\n"));
2214 return False;
2217 /* Check version number. */
2218 if (tdb_fetch_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, &vers) &&
2219 vers == WINBINDD_CACHE_VERSION) {
2220 cache_bad = False;
2223 if (cache_bad) {
2224 DEBUG(0,("initialize_winbindd_cache: clearing cache "
2225 "and re-creating with version number %d\n",
2226 WINBINDD_CACHE_VERSION ));
2228 tdb_close(wcache->tdb);
2229 wcache->tdb = NULL;
2231 if (unlink(lock_path("winbindd_cache.tdb")) == -1) {
2232 DEBUG(0,("initialize_winbindd_cache: unlink %s failed %s ",
2233 lock_path("winbindd_cache.tdb"),
2234 strerror(errno) ));
2235 return False;
2237 if (!init_wcache()) {
2238 DEBUG(0,("initialize_winbindd_cache: re-initialization "
2239 "init_wcache failed.\n"));
2240 return False;
2243 /* Write the version. */
2244 if (!tdb_store_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, WINBINDD_CACHE_VERSION)) {
2245 DEBUG(0,("initialize_winbindd_cache: version number store failed %s\n",
2246 tdb_errorstr(wcache->tdb) ));
2247 return False;
2251 tdb_close(wcache->tdb);
2252 wcache->tdb = NULL;
2253 return True;
2256 void cache_store_response(pid_t pid, struct winbindd_response *response)
2258 fstring key_str;
2260 if (!init_wcache())
2261 return;
2263 DEBUG(10, ("Storing response for pid %d, len %d\n",
2264 pid, response->length));
2266 fstr_sprintf(key_str, "DR/%d", pid);
2267 if (tdb_store(wcache->tdb, string_tdb_data(key_str),
2268 make_tdb_data((uint8 *)response, sizeof(*response)),
2269 TDB_REPLACE) == -1)
2270 return;
2272 if (response->length == sizeof(*response))
2273 return;
2275 /* There's extra data */
2277 DEBUG(10, ("Storing extra data: len=%d\n",
2278 (int)(response->length - sizeof(*response))));
2280 fstr_sprintf(key_str, "DE/%d", pid);
2281 if (tdb_store(wcache->tdb, string_tdb_data(key_str),
2282 make_tdb_data(response->extra_data.data,
2283 response->length - sizeof(*response)),
2284 TDB_REPLACE) == 0)
2285 return;
2287 /* We could not store the extra data, make sure the tdb does not
2288 * contain a main record with wrong dangling extra data */
2290 fstr_sprintf(key_str, "DR/%d", pid);
2291 tdb_delete(wcache->tdb, string_tdb_data(key_str));
2293 return;
2296 BOOL cache_retrieve_response(pid_t pid, struct winbindd_response * response)
2298 TDB_DATA data;
2299 fstring key_str;
2301 if (!init_wcache())
2302 return False;
2304 DEBUG(10, ("Retrieving response for pid %d\n", pid));
2306 fstr_sprintf(key_str, "DR/%d", pid);
2307 data = tdb_fetch(wcache->tdb, string_tdb_data(key_str));
2309 if (data.dptr == NULL)
2310 return False;
2312 if (data.dsize != sizeof(*response))
2313 return False;
2315 memcpy(response, data.dptr, data.dsize);
2316 SAFE_FREE(data.dptr);
2318 if (response->length == sizeof(*response)) {
2319 response->extra_data.data = NULL;
2320 return True;
2323 /* There's extra data */
2325 DEBUG(10, ("Retrieving extra data length=%d\n",
2326 (int)(response->length - sizeof(*response))));
2328 fstr_sprintf(key_str, "DE/%d", pid);
2329 data = tdb_fetch(wcache->tdb, string_tdb_data(key_str));
2331 if (data.dptr == NULL) {
2332 DEBUG(0, ("Did not find extra data\n"));
2333 return False;
2336 if (data.dsize != (response->length - sizeof(*response))) {
2337 DEBUG(0, ("Invalid extra data length: %d\n", (int)data.dsize));
2338 SAFE_FREE(data.dptr);
2339 return False;
2342 dump_data(11, (uint8 *)data.dptr, data.dsize);
2344 response->extra_data.data = data.dptr;
2345 return True;
2348 void cache_cleanup_response(pid_t pid)
2350 fstring key_str;
2352 if (!init_wcache())
2353 return;
2355 fstr_sprintf(key_str, "DR/%d", pid);
2356 tdb_delete(wcache->tdb, string_tdb_data(key_str));
2358 fstr_sprintf(key_str, "DE/%d", pid);
2359 tdb_delete(wcache->tdb, string_tdb_data(key_str));
2361 return;
2365 BOOL lookup_cached_sid(TALLOC_CTX *mem_ctx, const DOM_SID *sid,
2366 const char **domain_name, const char **name,
2367 enum lsa_SidType *type)
2369 struct winbindd_domain *domain;
2370 struct winbind_cache *cache;
2371 struct cache_entry *centry = NULL;
2372 NTSTATUS status;
2374 domain = find_lookup_domain_from_sid(sid);
2375 if (domain == NULL) {
2376 return False;
2379 cache = get_cache(domain);
2381 if (cache->tdb == NULL) {
2382 return False;
2385 centry = wcache_fetch(cache, domain, "SN/%s", sid_string_static(sid));
2386 if (centry == NULL) {
2387 return False;
2390 if (NT_STATUS_IS_OK(centry->status)) {
2391 *type = (enum lsa_SidType)centry_uint32(centry);
2392 *domain_name = centry_string(centry, mem_ctx);
2393 *name = centry_string(centry, mem_ctx);
2396 status = centry->status;
2397 centry_free(centry);
2398 return NT_STATUS_IS_OK(status);
2401 BOOL lookup_cached_name(TALLOC_CTX *mem_ctx,
2402 const char *domain_name,
2403 const char *name,
2404 DOM_SID *sid,
2405 enum lsa_SidType *type)
2407 struct winbindd_domain *domain;
2408 struct winbind_cache *cache;
2409 struct cache_entry *centry = NULL;
2410 NTSTATUS status;
2411 fstring uname;
2413 domain = find_lookup_domain_from_name(domain_name);
2414 if (domain == NULL) {
2415 return False;
2418 cache = get_cache(domain);
2420 if (cache->tdb == NULL) {
2421 return False;
2424 fstrcpy(uname, name);
2425 strupper_m(uname);
2427 centry = wcache_fetch(cache, domain, "NS/%s/%s", domain_name, uname);
2428 if (centry == NULL) {
2429 return False;
2432 if (NT_STATUS_IS_OK(centry->status)) {
2433 *type = (enum lsa_SidType)centry_uint32(centry);
2434 centry_sid(centry, mem_ctx, sid);
2437 status = centry->status;
2438 centry_free(centry);
2440 return NT_STATUS_IS_OK(status);
2443 void cache_name2sid(struct winbindd_domain *domain,
2444 const char *domain_name, const char *name,
2445 enum lsa_SidType type, const DOM_SID *sid)
2447 refresh_sequence_number(domain, False);
2448 wcache_save_name_to_sid(domain, NT_STATUS_OK, domain_name, name,
2449 sid, type);
2453 * The original idea that this cache only contains centries has
2454 * been blurred - now other stuff gets put in here. Ensure we
2455 * ignore these things on cleanup.
2458 static int traverse_fn_cleanup(TDB_CONTEXT *the_tdb, TDB_DATA kbuf,
2459 TDB_DATA dbuf, void *state)
2461 struct cache_entry *centry;
2463 if (is_non_centry_key(kbuf)) {
2464 return 0;
2467 centry = wcache_fetch_raw((char *)kbuf.dptr);
2468 if (!centry) {
2469 return 0;
2472 if (!NT_STATUS_IS_OK(centry->status)) {
2473 DEBUG(10,("deleting centry %s\n", (const char *)kbuf.dptr));
2474 tdb_delete(the_tdb, kbuf);
2477 centry_free(centry);
2478 return 0;
2481 /* flush the cache */
2482 void wcache_flush_cache(void)
2484 if (!wcache)
2485 return;
2486 if (wcache->tdb) {
2487 tdb_close(wcache->tdb);
2488 wcache->tdb = NULL;
2490 if (opt_nocache)
2491 return;
2493 /* when working offline we must not clear the cache on restart */
2494 wcache->tdb = tdb_open_log(lock_path("winbindd_cache.tdb"),
2495 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
2496 lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST),
2497 O_RDWR|O_CREAT, 0600);
2499 if (!wcache->tdb) {
2500 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
2501 return;
2504 tdb_traverse(wcache->tdb, traverse_fn_cleanup, NULL);
2506 DEBUG(10,("wcache_flush_cache success\n"));
2509 /* Count cached creds */
2511 static int traverse_fn_cached_creds(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
2512 void *state)
2514 int *cred_count = (int*)state;
2516 if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
2517 (*cred_count)++;
2519 return 0;
2522 NTSTATUS wcache_count_cached_creds(struct winbindd_domain *domain, int *count)
2524 struct winbind_cache *cache = get_cache(domain);
2526 *count = 0;
2528 if (!cache->tdb) {
2529 return NT_STATUS_INTERNAL_DB_ERROR;
2532 tdb_traverse(cache->tdb, traverse_fn_cached_creds, (void *)count);
2534 return NT_STATUS_OK;
2537 struct cred_list {
2538 struct cred_list *prev, *next;
2539 TDB_DATA key;
2540 fstring name;
2541 time_t created;
2543 static struct cred_list *wcache_cred_list;
2545 static int traverse_fn_get_credlist(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
2546 void *state)
2548 struct cred_list *cred;
2550 if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
2552 cred = SMB_MALLOC_P(struct cred_list);
2553 if (cred == NULL) {
2554 DEBUG(0,("traverse_fn_remove_first_creds: failed to malloc new entry for list\n"));
2555 return -1;
2558 ZERO_STRUCTP(cred);
2560 /* save a copy of the key */
2562 fstrcpy(cred->name, (const char *)kbuf.dptr);
2563 DLIST_ADD(wcache_cred_list, cred);
2566 return 0;
2569 NTSTATUS wcache_remove_oldest_cached_creds(struct winbindd_domain *domain, const DOM_SID *sid)
2571 struct winbind_cache *cache = get_cache(domain);
2572 NTSTATUS status;
2573 int ret;
2574 struct cred_list *cred, *oldest = NULL;
2576 if (!cache->tdb) {
2577 return NT_STATUS_INTERNAL_DB_ERROR;
2580 /* we possibly already have an entry */
2581 if (sid && NT_STATUS_IS_OK(wcache_cached_creds_exist(domain, sid))) {
2583 fstring key_str;
2585 DEBUG(11,("we already have an entry, deleting that\n"));
2587 fstr_sprintf(key_str, "CRED/%s", sid_string_static(sid));
2589 tdb_delete(cache->tdb, string_tdb_data(key_str));
2591 return NT_STATUS_OK;
2594 ret = tdb_traverse(cache->tdb, traverse_fn_get_credlist, NULL);
2595 if (ret == 0) {
2596 return NT_STATUS_OK;
2597 } else if ((ret == -1) || (wcache_cred_list == NULL)) {
2598 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
2601 ZERO_STRUCTP(oldest);
2603 for (cred = wcache_cred_list; cred; cred = cred->next) {
2605 TDB_DATA data;
2606 time_t t;
2608 data = tdb_fetch(cache->tdb, string_tdb_data(cred->name));
2609 if (!data.dptr) {
2610 DEBUG(10,("wcache_remove_oldest_cached_creds: entry for [%s] not found\n",
2611 cred->name));
2612 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
2613 goto done;
2616 t = IVAL(data.dptr, 0);
2617 SAFE_FREE(data.dptr);
2619 if (!oldest) {
2620 oldest = SMB_MALLOC_P(struct cred_list);
2621 if (oldest == NULL) {
2622 status = NT_STATUS_NO_MEMORY;
2623 goto done;
2626 fstrcpy(oldest->name, cred->name);
2627 oldest->created = t;
2628 continue;
2631 if (t < oldest->created) {
2632 fstrcpy(oldest->name, cred->name);
2633 oldest->created = t;
2637 if (tdb_delete(cache->tdb, string_tdb_data(oldest->name)) == 0) {
2638 status = NT_STATUS_OK;
2639 } else {
2640 status = NT_STATUS_UNSUCCESSFUL;
2642 done:
2643 SAFE_FREE(wcache_cred_list);
2644 SAFE_FREE(oldest);
2646 return status;
2649 /* Change the global online/offline state. */
2650 BOOL set_global_winbindd_state_offline(void)
2652 TDB_DATA data;
2654 DEBUG(10,("set_global_winbindd_state_offline: offline requested.\n"));
2656 /* Only go offline if someone has created
2657 the key "WINBINDD_OFFLINE" in the cache tdb. */
2659 if (wcache == NULL || wcache->tdb == NULL) {
2660 DEBUG(10,("set_global_winbindd_state_offline: wcache not open yet.\n"));
2661 return False;
2664 if (!lp_winbind_offline_logon()) {
2665 DEBUG(10,("set_global_winbindd_state_offline: rejecting.\n"));
2666 return False;
2669 if (global_winbindd_offline_state) {
2670 /* Already offline. */
2671 return True;
2674 data = tdb_fetch_bystring( wcache->tdb, "WINBINDD_OFFLINE" );
2676 if (!data.dptr || data.dsize != 4) {
2677 DEBUG(10,("set_global_winbindd_state_offline: offline state not set.\n"));
2678 SAFE_FREE(data.dptr);
2679 return False;
2680 } else {
2681 DEBUG(10,("set_global_winbindd_state_offline: offline state set.\n"));
2682 global_winbindd_offline_state = True;
2683 SAFE_FREE(data.dptr);
2684 return True;
2688 void set_global_winbindd_state_online(void)
2690 DEBUG(10,("set_global_winbindd_state_online: online requested.\n"));
2692 if (!lp_winbind_offline_logon()) {
2693 DEBUG(10,("set_global_winbindd_state_online: rejecting.\n"));
2694 return;
2697 if (!global_winbindd_offline_state) {
2698 /* Already online. */
2699 return;
2701 global_winbindd_offline_state = False;
2703 if (!wcache->tdb) {
2704 return;
2707 /* Ensure there is no key "WINBINDD_OFFLINE" in the cache tdb. */
2708 tdb_delete_bystring(wcache->tdb, "WINBINDD_OFFLINE");
2711 BOOL get_global_winbindd_state_offline(void)
2713 return global_winbindd_offline_state;
2716 /***********************************************************************
2717 Validate functions for all possible cache tdb keys.
2718 ***********************************************************************/
2720 static BOOL bad_cache_entry;
2722 static struct cache_entry *create_centry_validate(const char *kstr, TDB_DATA data)
2724 struct cache_entry *centry;
2726 centry = SMB_XMALLOC_P(struct cache_entry);
2727 centry->data = (unsigned char *)memdup(data.dptr, data.dsize);
2728 if (!centry->data) {
2729 SAFE_FREE(centry);
2730 return NULL;
2732 centry->len = data.dsize;
2733 centry->ofs = 0;
2735 if (centry->len < 8) {
2736 /* huh? corrupt cache? */
2737 DEBUG(0,("validate_create_centry: Corrupt cache for key %s (len < 8) ?\n", kstr));
2738 centry_free(centry);
2739 bad_cache_entry = True;
2740 return NULL;
2743 centry->status = NT_STATUS(centry_uint32(centry));
2744 centry->sequence_number = centry_uint32(centry);
2745 return centry;
2748 static int validate_seqnum(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf)
2750 if (dbuf.dsize != 8) {
2751 DEBUG(0,("validate_seqnum: Corrupt cache for key %s (len %u != 8) ?\n",
2752 keystr, (unsigned int)dbuf.dsize ));
2753 bad_cache_entry = True;
2754 return 1;
2756 return 0;
2759 static int validate_ns(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf)
2761 struct cache_entry *centry = create_centry_validate(keystr, dbuf);
2762 if (!centry) {
2763 return 1;
2766 (void)centry_uint32(centry);
2767 if (NT_STATUS_IS_OK(centry->status)) {
2768 DOM_SID sid;
2769 (void)centry_sid(centry, mem_ctx, &sid);
2772 centry_free(centry);
2774 if (bad_cache_entry) {
2775 return 1;
2777 DEBUG(10,("validate_ns: %s ok\n", keystr));
2778 return 0;
2781 static int validate_sn(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf)
2783 struct cache_entry *centry = create_centry_validate(keystr, dbuf);
2784 if (!centry) {
2785 return 1;
2788 if (NT_STATUS_IS_OK(centry->status)) {
2789 (void)centry_uint32(centry);
2790 (void)centry_string(centry, mem_ctx);
2791 (void)centry_string(centry, mem_ctx);
2794 centry_free(centry);
2796 if (bad_cache_entry) {
2797 return 1;
2799 DEBUG(10,("validate_sn: %s ok\n", keystr));
2800 return 0;
2803 static int validate_u(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf)
2805 struct cache_entry *centry = create_centry_validate(keystr, dbuf);
2806 DOM_SID sid;
2808 if (!centry) {
2809 return 1;
2812 (void)centry_string(centry, mem_ctx);
2813 (void)centry_string(centry, mem_ctx);
2814 (void)centry_string(centry, mem_ctx);
2815 (void)centry_string(centry, mem_ctx);
2816 (void)centry_uint32(centry);
2817 (void)centry_sid(centry, mem_ctx, &sid);
2818 (void)centry_sid(centry, mem_ctx, &sid);
2820 centry_free(centry);
2822 if (bad_cache_entry) {
2823 return 1;
2825 DEBUG(10,("validate_u: %s ok\n", keystr));
2826 return 0;
2829 static int validate_loc_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf)
2831 struct cache_entry *centry = create_centry_validate(keystr, dbuf);
2833 if (!centry) {
2834 return 1;
2837 (void)centry_nttime(centry);
2838 (void)centry_nttime(centry);
2839 (void)centry_uint16(centry);
2841 centry_free(centry);
2843 if (bad_cache_entry) {
2844 return 1;
2846 DEBUG(10,("validate_loc_pol: %s ok\n", keystr));
2847 return 0;
2850 static int validate_pwd_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf)
2852 struct cache_entry *centry = create_centry_validate(keystr, dbuf);
2854 if (!centry) {
2855 return 1;
2858 (void)centry_uint16(centry);
2859 (void)centry_uint16(centry);
2860 (void)centry_uint32(centry);
2861 (void)centry_nttime(centry);
2862 (void)centry_nttime(centry);
2864 centry_free(centry);
2866 if (bad_cache_entry) {
2867 return 1;
2869 DEBUG(10,("validate_pwd_pol: %s ok\n", keystr));
2870 return 0;
2873 static int validate_cred(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf)
2875 struct cache_entry *centry = create_centry_validate(keystr, dbuf);
2877 if (!centry) {
2878 return 1;
2881 (void)centry_time(centry);
2882 (void)centry_hash16(centry, mem_ctx);
2884 /* We only have 17 bytes more data in the salted cred case. */
2885 if (centry->len - centry->ofs == 17) {
2886 (void)centry_hash16(centry, mem_ctx);
2889 centry_free(centry);
2891 if (bad_cache_entry) {
2892 return 1;
2894 DEBUG(10,("validate_cred: %s ok\n", keystr));
2895 return 0;
2898 static int validate_ul(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf)
2900 struct cache_entry *centry = create_centry_validate(keystr, dbuf);
2901 int32 num_entries, i;
2903 if (!centry) {
2904 return 1;
2907 num_entries = (int32)centry_uint32(centry);
2909 for (i=0; i< num_entries; i++) {
2910 DOM_SID sid;
2911 (void)centry_string(centry, mem_ctx);
2912 (void)centry_string(centry, mem_ctx);
2913 (void)centry_string(centry, mem_ctx);
2914 (void)centry_string(centry, mem_ctx);
2915 (void)centry_sid(centry, mem_ctx, &sid);
2916 (void)centry_sid(centry, mem_ctx, &sid);
2919 centry_free(centry);
2921 if (bad_cache_entry) {
2922 return 1;
2924 DEBUG(10,("validate_ul: %s ok\n", keystr));
2925 return 0;
2928 static int validate_gl(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf)
2930 struct cache_entry *centry = create_centry_validate(keystr, dbuf);
2931 int32 num_entries, i;
2933 if (!centry) {
2934 return 1;
2937 num_entries = centry_uint32(centry);
2939 for (i=0; i< num_entries; i++) {
2940 (void)centry_string(centry, mem_ctx);
2941 (void)centry_string(centry, mem_ctx);
2942 (void)centry_uint32(centry);
2945 centry_free(centry);
2947 if (bad_cache_entry) {
2948 return 1;
2950 DEBUG(10,("validate_gl: %s ok\n", keystr));
2951 return 0;
2954 static int validate_ug(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf)
2956 struct cache_entry *centry = create_centry_validate(keystr, dbuf);
2957 int32 num_groups, i;
2959 if (!centry) {
2960 return 1;
2963 num_groups = centry_uint32(centry);
2965 for (i=0; i< num_groups; i++) {
2966 DOM_SID sid;
2967 centry_sid(centry, mem_ctx, &sid);
2970 centry_free(centry);
2972 if (bad_cache_entry) {
2973 return 1;
2975 DEBUG(10,("validate_ug: %s ok\n", keystr));
2976 return 0;
2979 static int validate_ua(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf)
2981 struct cache_entry *centry = create_centry_validate(keystr, dbuf);
2982 int32 num_aliases, i;
2984 if (!centry) {
2985 return 1;
2988 num_aliases = centry_uint32(centry);
2990 for (i=0; i < num_aliases; i++) {
2991 (void)centry_uint32(centry);
2994 centry_free(centry);
2996 if (bad_cache_entry) {
2997 return 1;
2999 DEBUG(10,("validate_ua: %s ok\n", keystr));
3000 return 0;
3003 static int validate_gm(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf)
3005 struct cache_entry *centry = create_centry_validate(keystr, dbuf);
3006 int32 num_names, i;
3008 if (!centry) {
3009 return 1;
3012 num_names = centry_uint32(centry);
3014 for (i=0; i< num_names; i++) {
3015 DOM_SID sid;
3016 centry_sid(centry, mem_ctx, &sid);
3017 (void)centry_string(centry, mem_ctx);
3018 (void)centry_uint32(centry);
3021 centry_free(centry);
3023 if (bad_cache_entry) {
3024 return 1;
3026 DEBUG(10,("validate_gm: %s ok\n", keystr));
3027 return 0;
3030 static int validate_dr(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf)
3032 /* Can't say anything about this other than must be nonzero. */
3033 if (dbuf.dsize == 0) {
3034 DEBUG(0,("validate_dr: Corrupt cache for key %s (len == 0) ?\n",
3035 keystr));
3036 bad_cache_entry = True;
3037 return 1;
3040 DEBUG(10,("validate_dr: %s ok\n", keystr));
3041 return 0;
3044 static int validate_de(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf)
3046 /* Can't say anything about this other than must be nonzero. */
3047 if (dbuf.dsize == 0) {
3048 DEBUG(0,("validate_de: Corrupt cache for key %s (len == 0) ?\n",
3049 keystr));
3050 bad_cache_entry = True;
3051 return 1;
3054 DEBUG(10,("validate_de: %s ok\n", keystr));
3055 return 0;
3058 static int validate_trustdoms(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf)
3060 struct cache_entry *centry = create_centry_validate(keystr, dbuf);
3061 int32 num_domains, i;
3063 if (!centry) {
3064 return 1;
3067 num_domains = centry_uint32(centry);
3069 for (i=0; i< num_domains; i++) {
3070 DOM_SID sid;
3071 (void)centry_string(centry, mem_ctx);
3072 (void)centry_string(centry, mem_ctx);
3073 (void)centry_sid(centry, mem_ctx, &sid);
3076 centry_free(centry);
3078 if (bad_cache_entry) {
3079 return 1;
3081 DEBUG(10,("validate_trustdoms: %s ok\n", keystr));
3082 return 0;
3085 static int validate_offline(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf)
3087 if (dbuf.dsize != 4) {
3088 DEBUG(0,("validate_offline: Corrupt cache for key %s (len %u != 4) ?\n",
3089 keystr, (unsigned int)dbuf.dsize ));
3090 bad_cache_entry = True;
3091 return 1;
3093 DEBUG(10,("validate_offline: %s ok\n", keystr));
3094 return 0;
3097 /***********************************************************************
3098 A list of all possible cache tdb keys with associated validation
3099 functions.
3100 ***********************************************************************/
3102 struct key_val_struct {
3103 const char *keyname;
3104 int (*validate_data_fn)(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf);
3105 } key_val[] = {
3106 {"SEQNUM/", validate_seqnum},
3107 {"NS/", validate_ns},
3108 {"SN/", validate_sn},
3109 {"U/", validate_u},
3110 {"LOC_POL/", validate_loc_pol},
3111 {"PWD_POL/", validate_pwd_pol},
3112 {"CRED/", validate_cred},
3113 {"UL/", validate_ul},
3114 {"GL/", validate_gl},
3115 {"UG/", validate_ug},
3116 {"UA", validate_ua},
3117 {"GM/", validate_gm},
3118 {"DR/", validate_dr},
3119 {"DE/", validate_de},
3120 {"TRUSTDOMS/", validate_trustdoms},
3121 {"WINBINDD_OFFLINE", validate_offline},
3122 {NULL, NULL}
3125 /***********************************************************************
3126 Function to look at every entry in the tdb and validate it as far as
3127 possible.
3128 ***********************************************************************/
3130 static int cache_traverse_validate_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, void *state)
3132 int i;
3134 /* Paranoia check. */
3135 if (kbuf.dsize > 1024) {
3136 DEBUG(0,("cache_traverse_validate_fn: key length too large (%u) > 1024\n\n",
3137 (unsigned int)kbuf.dsize ));
3138 return 1;
3141 for (i = 0; key_val[i].keyname; i++) {
3142 size_t namelen = strlen(key_val[i].keyname);
3143 if (kbuf.dsize >= namelen && (
3144 strncmp(key_val[i].keyname, (const char *)kbuf.dptr, namelen)) == 0) {
3145 TALLOC_CTX *mem_ctx;
3146 char *keystr;
3147 int ret;
3149 keystr = SMB_MALLOC(kbuf.dsize+1);
3150 if (!keystr) {
3151 return 1;
3153 memcpy(keystr, kbuf.dptr, kbuf.dsize);
3154 keystr[kbuf.dsize] = '\0';
3156 mem_ctx = talloc_init("validate_ctx");
3157 if (!mem_ctx) {
3158 SAFE_FREE(keystr);
3159 return 1;
3162 ret = key_val[i].validate_data_fn(mem_ctx, keystr, dbuf);
3164 SAFE_FREE(keystr);
3165 talloc_destroy(mem_ctx);
3166 return ret;
3170 DEBUG(0,("cache_traverse_validate_fn: unknown cache entry\nkey :\n"));
3171 dump_data(0, (uint8 *)kbuf.dptr, kbuf.dsize);
3172 DEBUG(0,("data :\n"));
3173 dump_data(0, (uint8 *)dbuf.dptr, dbuf.dsize);
3174 return 1; /* terminate. */
3177 static void validate_panic(const char *const why)
3179 DEBUG(0,("validating cache: would panic %s\n", why ));
3180 bad_cache_entry = True;
3183 /* Handle any signals generated when validating a possibly
3184 bad cache tdb. */
3186 static jmp_buf jmpbuf;
3188 #ifdef SIGSEGV
3189 static void sig_segv(int sig)
3191 longjmp(jmpbuf, SIGSEGV);
3193 #endif
3195 #ifdef SIGBUS
3196 static void sig_bus(int sig)
3198 longjmp(jmpbuf, SIGBUS);
3200 #endif
3202 #ifdef SIGABRT
3203 static void sig_abrt(int sig)
3205 longjmp(jmpbuf, SIGABRT);
3207 #endif
3209 /***********************************************************************
3210 Try and validate every entry in the winbindd cache. If we fail here,
3211 delete the cache tdb and return non-zero - the caller (main winbindd
3212 function) will restart us as we don't know if we crashed or not.
3213 ***********************************************************************/
3215 int winbindd_validate_cache(void)
3217 BOOL ret = -1;
3218 int fd = -1;
3219 int num_entries = 0;
3220 TDB_CONTEXT *tdb = NULL;
3221 const char *cache_path = lock_path("winbindd_cache.tdb");
3223 #ifdef SIGSEGV
3224 void (*old_segv_handler)(int) = CatchSignal(SIGSEGV,SIGNAL_CAST sig_segv);
3225 #endif
3226 #ifdef SIGBUS
3227 void (*old_bus_handler)(int) = CatchSignal(SIGBUS,SIGNAL_CAST sig_bus);
3228 #endif
3229 #ifdef SIGABRT
3230 void (*old_abrt_handler)(int) = CatchSignal(SIGABRT,SIGNAL_CAST sig_abrt);
3231 #endif
3233 switch((ret = setjmp(jmpbuf))) {
3234 case 0:
3235 ret = -1;
3236 break;
3237 case SIGSEGV:
3238 case SIGBUS:
3239 case SIGABRT:
3240 default:
3241 goto out;
3244 /* Doh ! Volker is very smart :-). Use TDB_NOMMAP to prevent
3245 * any wild pointer references when reading a corrupt tdb file. */
3247 tdb = tdb_open_log(cache_path,
3248 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
3249 lp_winbind_offline_logon() ? TDB_NOMMAP : (TDB_NOMMAP | TDB_CLEAR_IF_FIRST),
3250 O_RDWR|O_CREAT, 0600);
3251 if (!tdb) {
3252 goto out;
3255 fd = tdb_fd(tdb);
3257 /* Check the cache freelist is good. */
3258 if (tdb_validate_freelist(tdb, &num_entries) == -1) {
3259 DEBUG(0,("winbindd_validate_cache: bad freelist in cache %s\n",
3260 cache_path));
3261 goto out;
3264 DEBUG(10,("winbindd_validate_cache: cache %s freelist has %d entries\n",
3265 cache_path, num_entries));
3267 smb_panic_fn = validate_panic;
3269 /* Now traverse the cache to validate it. */
3270 num_entries = tdb_traverse(tdb, cache_traverse_validate_fn, NULL);
3271 if (num_entries == -1 || bad_cache_entry) {
3272 DEBUG(0,("winbindd_validate_cache: cache %s traverse failed\n",
3273 cache_path));
3274 goto out;
3277 DEBUG(10,("winbindd_validate_cache: cache %s is good "
3278 "with %d entries\n", cache_path, num_entries));
3279 ret = 0; /* Cache is good. */
3281 out:
3283 bad_cache_entry = False;
3284 smb_panic_fn = smb_panic;
3286 /* Ensure if we segv on exit we use the original
3287 handlers to avoid a loop. */
3289 #ifdef SIGSEGV
3290 CatchSignal(SIGSEGV,SIGNAL_CAST old_segv_handler);
3291 #endif
3292 #ifdef SIGBUS
3293 CatchSignal(SIGBUS,SIGNAL_CAST old_bus_handler);
3294 #endif
3295 #ifdef SIGABRT
3296 CatchSignal(SIGABRT,SIGNAL_CAST old_abrt_handler);
3297 #endif
3299 if (tdb) {
3300 if (ret == 0) {
3301 tdb_close(tdb);
3302 } else if (fd != -1) {
3303 close(fd);
3307 if (ret) {
3308 unlink(cache_path);
3311 return ret;
3314 /* the cache backend methods are exposed via this structure */
3315 struct winbindd_methods cache_methods = {
3316 True,
3317 query_user_list,
3318 enum_dom_groups,
3319 enum_local_groups,
3320 name_to_sid,
3321 sid_to_name,
3322 rids_to_names,
3323 query_user,
3324 lookup_usergroups,
3325 lookup_useraliases,
3326 lookup_groupmem,
3327 sequence_number,
3328 lockout_policy,
3329 password_policy,
3330 trusted_domains