r23784: use the GPLv3 boilerplate as recommended by the FSF and the license text
[Samba/bb.git] / source / nsswitch / winbindd_cache.c
blob8c4ce9c42eb0007f8907f9b2fdb45f15ebf7ea76
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 3 of the License, or
15 (at your option) any later version.
17 This program is distributed in the hope that it will be useful,
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 GNU General Public License for more details.
22 You should have received a copy of the GNU General Public License
23 along with this program. If not, see <http://www.gnu.org/licenses/>.
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;
136 /* We have to know what type of domain we are dealing with first. */
138 if ( !domain->initialized ) {
139 init_dc_connection( domain );
143 OK. listen up becasue I'm only going to say this once.
144 We have the following scenarios to consider
145 (a) trusted AD domains on a Samba DC,
146 (b) trusted AD domains and we are joined to a non-kerberos domain
147 (c) trusted AD domains and we are joined to a kerberos (AD) domain
149 For (a) we can always contact the trusted domain using krb5
150 since we have the domain trust account password
152 For (b) we can only use RPC since we have no way of
153 getting a krb5 ticket in our own domain
155 For (c) we can always use krb5 since we have a kerberos trust
157 --jerry
160 if (!domain->backend) {
161 #ifdef HAVE_ADS
162 struct winbindd_domain *our_domain = domain;
164 /* find our domain first so we can figure out if we
165 are joined to a kerberized domain */
167 if ( !domain->primary )
168 our_domain = find_our_domain();
170 if ((our_domain->active_directory || IS_DC)
171 && domain->active_directory
172 && !lp_winbind_rpc_only()) {
173 DEBUG(5,("get_cache: Setting ADS methods for domain %s\n", domain->name));
174 domain->backend = &ads_methods;
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 enum winbindd_cmd orig_cmd,
1359 const char *domain_name,
1360 const char *name,
1361 DOM_SID *sid,
1362 enum lsa_SidType *type)
1364 struct winbind_cache *cache = get_cache(domain);
1365 struct cache_entry *centry = NULL;
1366 NTSTATUS status;
1367 fstring uname;
1369 if (!cache->tdb)
1370 goto do_query;
1372 fstrcpy(uname, name);
1373 strupper_m(uname);
1374 centry = wcache_fetch(cache, domain, "NS/%s/%s", domain_name, uname);
1375 if (!centry)
1376 goto do_query;
1377 *type = (enum lsa_SidType)centry_uint32(centry);
1378 status = centry->status;
1379 if (NT_STATUS_IS_OK(status)) {
1380 centry_sid(centry, mem_ctx, sid);
1383 DEBUG(10,("name_to_sid: [Cached] - cached name for domain %s status: %s\n",
1384 domain->name, nt_errstr(status) ));
1386 centry_free(centry);
1387 return status;
1389 do_query:
1390 ZERO_STRUCTP(sid);
1392 /* If the seq number check indicated that there is a problem
1393 * with this DC, then return that status... except for
1394 * access_denied. This is special because the dc may be in
1395 * "restrict anonymous = 1" mode, in which case it will deny
1396 * most unauthenticated operations, but *will* allow the LSA
1397 * name-to-sid that we try as a fallback. */
1399 if (!(NT_STATUS_IS_OK(domain->last_status)
1400 || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1401 return domain->last_status;
1403 DEBUG(10,("name_to_sid: [Cached] - doing backend query for name for domain %s\n",
1404 domain->name ));
1406 status = domain->backend->name_to_sid(domain, mem_ctx, orig_cmd,
1407 domain_name, name, sid, type);
1409 /* and save it */
1410 refresh_sequence_number(domain, False);
1412 if (domain->online && !is_null_sid(sid)) {
1413 wcache_save_name_to_sid(domain, status, domain_name, name, sid, *type);
1416 if (NT_STATUS_IS_OK(status)) {
1417 strupper_m(CONST_DISCARD(char *,domain_name));
1418 strlower_m(CONST_DISCARD(char *,name));
1419 wcache_save_sid_to_name(domain, status, sid, domain_name, name, *type);
1422 return status;
1425 /* convert a sid to a user or group name. The sid is guaranteed to be in the domain
1426 given */
1427 static NTSTATUS sid_to_name(struct winbindd_domain *domain,
1428 TALLOC_CTX *mem_ctx,
1429 const DOM_SID *sid,
1430 char **domain_name,
1431 char **name,
1432 enum lsa_SidType *type)
1434 struct winbind_cache *cache = get_cache(domain);
1435 struct cache_entry *centry = NULL;
1436 NTSTATUS status;
1437 fstring sid_string;
1439 if (!cache->tdb)
1440 goto do_query;
1442 centry = wcache_fetch(cache, domain, "SN/%s", sid_to_string(sid_string, sid));
1443 if (!centry)
1444 goto do_query;
1445 if (NT_STATUS_IS_OK(centry->status)) {
1446 *type = (enum lsa_SidType)centry_uint32(centry);
1447 *domain_name = centry_string(centry, mem_ctx);
1448 *name = centry_string(centry, mem_ctx);
1450 status = centry->status;
1452 DEBUG(10,("sid_to_name: [Cached] - cached name for domain %s status: %s\n",
1453 domain->name, nt_errstr(status) ));
1455 centry_free(centry);
1456 return status;
1458 do_query:
1459 *name = NULL;
1460 *domain_name = NULL;
1462 /* If the seq number check indicated that there is a problem
1463 * with this DC, then return that status... except for
1464 * access_denied. This is special because the dc may be in
1465 * "restrict anonymous = 1" mode, in which case it will deny
1466 * most unauthenticated operations, but *will* allow the LSA
1467 * sid-to-name that we try as a fallback. */
1469 if (!(NT_STATUS_IS_OK(domain->last_status)
1470 || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1471 return domain->last_status;
1473 DEBUG(10,("sid_to_name: [Cached] - doing backend query for name for domain %s\n",
1474 domain->name ));
1476 status = domain->backend->sid_to_name(domain, mem_ctx, sid, domain_name, name, type);
1478 /* and save it */
1479 refresh_sequence_number(domain, False);
1480 wcache_save_sid_to_name(domain, status, sid, *domain_name, *name, *type);
1482 /* We can't save the name to sid mapping here, as with sid history a
1483 * later name2sid would give the wrong sid. */
1485 return status;
1488 static NTSTATUS rids_to_names(struct winbindd_domain *domain,
1489 TALLOC_CTX *mem_ctx,
1490 const DOM_SID *domain_sid,
1491 uint32 *rids,
1492 size_t num_rids,
1493 char **domain_name,
1494 char ***names,
1495 enum lsa_SidType **types)
1497 struct winbind_cache *cache = get_cache(domain);
1498 size_t i;
1499 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
1500 BOOL have_mapped;
1501 BOOL have_unmapped;
1503 *domain_name = NULL;
1504 *names = NULL;
1505 *types = NULL;
1507 if (!cache->tdb) {
1508 goto do_query;
1511 if (num_rids == 0) {
1512 return NT_STATUS_OK;
1515 *names = TALLOC_ARRAY(mem_ctx, char *, num_rids);
1516 *types = TALLOC_ARRAY(mem_ctx, enum lsa_SidType, num_rids);
1518 if ((*names == NULL) || (*types == NULL)) {
1519 result = NT_STATUS_NO_MEMORY;
1520 goto error;
1523 have_mapped = have_unmapped = False;
1525 for (i=0; i<num_rids; i++) {
1526 DOM_SID sid;
1527 struct cache_entry *centry;
1529 if (!sid_compose(&sid, domain_sid, rids[i])) {
1530 result = NT_STATUS_INTERNAL_ERROR;
1531 goto error;
1534 centry = wcache_fetch(cache, domain, "SN/%s",
1535 sid_string_static(&sid));
1536 if (!centry) {
1537 goto do_query;
1540 (*types)[i] = SID_NAME_UNKNOWN;
1541 (*names)[i] = talloc_strdup(*names, "");
1543 if (NT_STATUS_IS_OK(centry->status)) {
1544 char *dom;
1545 have_mapped = True;
1546 (*types)[i] = (enum lsa_SidType)centry_uint32(centry);
1547 dom = centry_string(centry, mem_ctx);
1548 if (*domain_name == NULL) {
1549 *domain_name = dom;
1550 } else {
1551 talloc_free(dom);
1553 (*names)[i] = centry_string(centry, *names);
1554 } else {
1555 have_unmapped = True;
1558 centry_free(centry);
1561 if (!have_mapped) {
1562 return NT_STATUS_NONE_MAPPED;
1564 if (!have_unmapped) {
1565 return NT_STATUS_OK;
1567 return STATUS_SOME_UNMAPPED;
1569 do_query:
1571 TALLOC_FREE(*names);
1572 TALLOC_FREE(*types);
1574 result = domain->backend->rids_to_names(domain, mem_ctx, domain_sid,
1575 rids, num_rids, domain_name,
1576 names, types);
1578 if (!NT_STATUS_IS_OK(result) &&
1579 !NT_STATUS_EQUAL(result, STATUS_SOME_UNMAPPED)) {
1580 return result;
1583 refresh_sequence_number(domain, False);
1585 for (i=0; i<num_rids; i++) {
1586 DOM_SID sid;
1587 NTSTATUS status;
1589 if (!sid_compose(&sid, domain_sid, rids[i])) {
1590 result = NT_STATUS_INTERNAL_ERROR;
1591 goto error;
1594 status = (*types)[i] == SID_NAME_UNKNOWN ?
1595 NT_STATUS_NONE_MAPPED : NT_STATUS_OK;
1597 wcache_save_sid_to_name(domain, status, &sid, *domain_name,
1598 (*names)[i], (*types)[i]);
1601 return result;
1603 error:
1605 TALLOC_FREE(*names);
1606 TALLOC_FREE(*types);
1607 return result;
1610 /* Lookup user information from a rid */
1611 static NTSTATUS query_user(struct winbindd_domain *domain,
1612 TALLOC_CTX *mem_ctx,
1613 const DOM_SID *user_sid,
1614 WINBIND_USERINFO *info)
1616 struct winbind_cache *cache = get_cache(domain);
1617 struct cache_entry *centry = NULL;
1618 NTSTATUS status;
1620 if (!cache->tdb)
1621 goto do_query;
1623 centry = wcache_fetch(cache, domain, "U/%s", sid_string_static(user_sid));
1625 /* If we have an access denied cache entry and a cached info3 in the
1626 samlogon cache then do a query. This will force the rpc back end
1627 to return the info3 data. */
1629 if (NT_STATUS_V(domain->last_status) == NT_STATUS_V(NT_STATUS_ACCESS_DENIED) &&
1630 netsamlogon_cache_have(user_sid)) {
1631 DEBUG(10, ("query_user: cached access denied and have cached info3\n"));
1632 domain->last_status = NT_STATUS_OK;
1633 centry_free(centry);
1634 goto do_query;
1637 if (!centry)
1638 goto do_query;
1640 info->acct_name = centry_string(centry, mem_ctx);
1641 info->full_name = centry_string(centry, mem_ctx);
1642 info->homedir = centry_string(centry, mem_ctx);
1643 info->shell = centry_string(centry, mem_ctx);
1644 info->primary_gid = centry_uint32(centry);
1645 centry_sid(centry, mem_ctx, &info->user_sid);
1646 centry_sid(centry, mem_ctx, &info->group_sid);
1647 status = centry->status;
1649 DEBUG(10,("query_user: [Cached] - cached info for domain %s status: %s\n",
1650 domain->name, nt_errstr(status) ));
1652 centry_free(centry);
1653 return status;
1655 do_query:
1656 ZERO_STRUCTP(info);
1658 /* Return status value returned by seq number check */
1660 if (!NT_STATUS_IS_OK(domain->last_status))
1661 return domain->last_status;
1663 DEBUG(10,("query_user: [Cached] - doing backend query for info for domain %s\n",
1664 domain->name ));
1666 status = domain->backend->query_user(domain, mem_ctx, user_sid, info);
1668 /* and save it */
1669 refresh_sequence_number(domain, False);
1670 wcache_save_user(domain, status, info);
1672 return status;
1676 /* Lookup groups a user is a member of. */
1677 static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
1678 TALLOC_CTX *mem_ctx,
1679 const DOM_SID *user_sid,
1680 uint32 *num_groups, DOM_SID **user_gids)
1682 struct winbind_cache *cache = get_cache(domain);
1683 struct cache_entry *centry = NULL;
1684 NTSTATUS status;
1685 unsigned int i;
1686 fstring sid_string;
1688 if (!cache->tdb)
1689 goto do_query;
1691 centry = wcache_fetch(cache, domain, "UG/%s", sid_to_string(sid_string, user_sid));
1693 /* If we have an access denied cache entry and a cached info3 in the
1694 samlogon cache then do a query. This will force the rpc back end
1695 to return the info3 data. */
1697 if (NT_STATUS_V(domain->last_status) == NT_STATUS_V(NT_STATUS_ACCESS_DENIED) &&
1698 netsamlogon_cache_have(user_sid)) {
1699 DEBUG(10, ("lookup_usergroups: cached access denied and have cached info3\n"));
1700 domain->last_status = NT_STATUS_OK;
1701 centry_free(centry);
1702 goto do_query;
1705 if (!centry)
1706 goto do_query;
1708 *num_groups = centry_uint32(centry);
1710 if (*num_groups == 0)
1711 goto do_cached;
1713 (*user_gids) = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_groups);
1714 if (! (*user_gids)) {
1715 smb_panic_fn("lookup_usergroups out of memory");
1717 for (i=0; i<(*num_groups); i++) {
1718 centry_sid(centry, mem_ctx, &(*user_gids)[i]);
1721 do_cached:
1722 status = centry->status;
1724 DEBUG(10,("lookup_usergroups: [Cached] - cached info for domain %s status: %s\n",
1725 domain->name, nt_errstr(status) ));
1727 centry_free(centry);
1728 return status;
1730 do_query:
1731 (*num_groups) = 0;
1732 (*user_gids) = NULL;
1734 /* Return status value returned by seq number check */
1736 if (!NT_STATUS_IS_OK(domain->last_status))
1737 return domain->last_status;
1739 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info for domain %s\n",
1740 domain->name ));
1742 status = domain->backend->lookup_usergroups(domain, mem_ctx, user_sid, num_groups, user_gids);
1744 if ( NT_STATUS_EQUAL(status, NT_STATUS_SYNCHRONIZATION_REQUIRED) )
1745 goto skip_save;
1747 /* and save it */
1748 refresh_sequence_number(domain, False);
1749 centry = centry_start(domain, status);
1750 if (!centry)
1751 goto skip_save;
1752 centry_put_uint32(centry, *num_groups);
1753 for (i=0; i<(*num_groups); i++) {
1754 centry_put_sid(centry, &(*user_gids)[i]);
1756 centry_end(centry, "UG/%s", sid_to_string(sid_string, user_sid));
1757 centry_free(centry);
1759 skip_save:
1760 return status;
1763 static NTSTATUS lookup_useraliases(struct winbindd_domain *domain,
1764 TALLOC_CTX *mem_ctx,
1765 uint32 num_sids, const DOM_SID *sids,
1766 uint32 *num_aliases, uint32 **alias_rids)
1768 struct winbind_cache *cache = get_cache(domain);
1769 struct cache_entry *centry = NULL;
1770 NTSTATUS status;
1771 char *sidlist = talloc_strdup(mem_ctx, "");
1772 int i;
1774 if (!cache->tdb)
1775 goto do_query;
1777 if (num_sids == 0) {
1778 *num_aliases = 0;
1779 *alias_rids = NULL;
1780 return NT_STATUS_OK;
1783 /* We need to cache indexed by the whole list of SIDs, the aliases
1784 * resulting might come from any of the SIDs. */
1786 for (i=0; i<num_sids; i++) {
1787 sidlist = talloc_asprintf(mem_ctx, "%s/%s", sidlist,
1788 sid_string_static(&sids[i]));
1789 if (sidlist == NULL)
1790 return NT_STATUS_NO_MEMORY;
1793 centry = wcache_fetch(cache, domain, "UA%s", sidlist);
1795 if (!centry)
1796 goto do_query;
1798 *num_aliases = centry_uint32(centry);
1799 *alias_rids = NULL;
1801 if (*num_aliases) {
1802 (*alias_rids) = TALLOC_ARRAY(mem_ctx, uint32, *num_aliases);
1804 if ((*alias_rids) == NULL) {
1805 centry_free(centry);
1806 return NT_STATUS_NO_MEMORY;
1808 } else {
1809 (*alias_rids) = NULL;
1812 for (i=0; i<(*num_aliases); i++)
1813 (*alias_rids)[i] = centry_uint32(centry);
1815 status = centry->status;
1817 DEBUG(10,("lookup_useraliases: [Cached] - cached info for domain: %s "
1818 "status %s\n", domain->name, nt_errstr(status)));
1820 centry_free(centry);
1821 return status;
1823 do_query:
1824 (*num_aliases) = 0;
1825 (*alias_rids) = NULL;
1827 if (!NT_STATUS_IS_OK(domain->last_status))
1828 return domain->last_status;
1830 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info "
1831 "for domain %s\n", domain->name ));
1833 status = domain->backend->lookup_useraliases(domain, mem_ctx,
1834 num_sids, sids,
1835 num_aliases, alias_rids);
1837 /* and save it */
1838 refresh_sequence_number(domain, False);
1839 centry = centry_start(domain, status);
1840 if (!centry)
1841 goto skip_save;
1842 centry_put_uint32(centry, *num_aliases);
1843 for (i=0; i<(*num_aliases); i++)
1844 centry_put_uint32(centry, (*alias_rids)[i]);
1845 centry_end(centry, "UA%s", sidlist);
1846 centry_free(centry);
1848 skip_save:
1849 return status;
1853 static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
1854 TALLOC_CTX *mem_ctx,
1855 const DOM_SID *group_sid, uint32 *num_names,
1856 DOM_SID **sid_mem, char ***names,
1857 uint32 **name_types)
1859 struct winbind_cache *cache = get_cache(domain);
1860 struct cache_entry *centry = NULL;
1861 NTSTATUS status;
1862 unsigned int i;
1863 fstring sid_string;
1865 if (!cache->tdb)
1866 goto do_query;
1868 centry = wcache_fetch(cache, domain, "GM/%s", sid_to_string(sid_string, group_sid));
1869 if (!centry)
1870 goto do_query;
1872 *num_names = centry_uint32(centry);
1874 if (*num_names == 0)
1875 goto do_cached;
1877 (*sid_mem) = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_names);
1878 (*names) = TALLOC_ARRAY(mem_ctx, char *, *num_names);
1879 (*name_types) = TALLOC_ARRAY(mem_ctx, uint32, *num_names);
1881 if (! (*sid_mem) || ! (*names) || ! (*name_types)) {
1882 smb_panic_fn("lookup_groupmem out of memory");
1885 for (i=0; i<(*num_names); i++) {
1886 centry_sid(centry, mem_ctx, &(*sid_mem)[i]);
1887 (*names)[i] = centry_string(centry, mem_ctx);
1888 (*name_types)[i] = centry_uint32(centry);
1891 do_cached:
1892 status = centry->status;
1894 DEBUG(10,("lookup_groupmem: [Cached] - cached info for domain %s status: %s\n",
1895 domain->name, nt_errstr(status)));
1897 centry_free(centry);
1898 return status;
1900 do_query:
1901 (*num_names) = 0;
1902 (*sid_mem) = NULL;
1903 (*names) = NULL;
1904 (*name_types) = NULL;
1906 /* Return status value returned by seq number check */
1908 if (!NT_STATUS_IS_OK(domain->last_status))
1909 return domain->last_status;
1911 DEBUG(10,("lookup_groupmem: [Cached] - doing backend query for info for domain %s\n",
1912 domain->name ));
1914 status = domain->backend->lookup_groupmem(domain, mem_ctx, group_sid, num_names,
1915 sid_mem, names, name_types);
1917 /* and save it */
1918 refresh_sequence_number(domain, False);
1919 centry = centry_start(domain, status);
1920 if (!centry)
1921 goto skip_save;
1922 centry_put_uint32(centry, *num_names);
1923 for (i=0; i<(*num_names); i++) {
1924 centry_put_sid(centry, &(*sid_mem)[i]);
1925 centry_put_string(centry, (*names)[i]);
1926 centry_put_uint32(centry, (*name_types)[i]);
1928 centry_end(centry, "GM/%s", sid_to_string(sid_string, group_sid));
1929 centry_free(centry);
1931 skip_save:
1932 return status;
1935 /* find the sequence number for a domain */
1936 static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
1938 refresh_sequence_number(domain, False);
1940 *seq = domain->sequence_number;
1942 return NT_STATUS_OK;
1945 /* enumerate trusted domains
1946 * (we need to have the list of trustdoms in the cache when we go offline) -
1947 * Guenther */
1948 static NTSTATUS trusted_domains(struct winbindd_domain *domain,
1949 TALLOC_CTX *mem_ctx,
1950 uint32 *num_domains,
1951 char ***names,
1952 char ***alt_names,
1953 DOM_SID **dom_sids)
1955 struct winbind_cache *cache = get_cache(domain);
1956 struct cache_entry *centry = NULL;
1957 NTSTATUS status;
1958 int i;
1960 if (!cache->tdb)
1961 goto do_query;
1963 centry = wcache_fetch(cache, domain, "TRUSTDOMS/%s", domain->name);
1965 if (!centry) {
1966 goto do_query;
1969 *num_domains = centry_uint32(centry);
1971 if (*num_domains) {
1972 (*names) = TALLOC_ARRAY(mem_ctx, char *, *num_domains);
1973 (*alt_names) = TALLOC_ARRAY(mem_ctx, char *, *num_domains);
1974 (*dom_sids) = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_domains);
1976 if (! (*dom_sids) || ! (*names) || ! (*alt_names)) {
1977 smb_panic_fn("trusted_domains out of memory");
1979 } else {
1980 (*names) = NULL;
1981 (*alt_names) = NULL;
1982 (*dom_sids) = NULL;
1985 for (i=0; i<(*num_domains); i++) {
1986 (*names)[i] = centry_string(centry, mem_ctx);
1987 (*alt_names)[i] = centry_string(centry, mem_ctx);
1988 centry_sid(centry, mem_ctx, &(*dom_sids)[i]);
1991 status = centry->status;
1993 DEBUG(10,("trusted_domains: [Cached] - cached info for domain %s (%d trusts) status: %s\n",
1994 domain->name, *num_domains, nt_errstr(status) ));
1996 centry_free(centry);
1997 return status;
1999 do_query:
2000 (*num_domains) = 0;
2001 (*dom_sids) = NULL;
2002 (*names) = NULL;
2003 (*alt_names) = NULL;
2005 /* Return status value returned by seq number check */
2007 if (!NT_STATUS_IS_OK(domain->last_status))
2008 return domain->last_status;
2010 DEBUG(10,("trusted_domains: [Cached] - doing backend query for info for domain %s\n",
2011 domain->name ));
2013 status = domain->backend->trusted_domains(domain, mem_ctx, num_domains,
2014 names, alt_names, dom_sids);
2016 /* no trusts gives NT_STATUS_NO_MORE_ENTRIES resetting to NT_STATUS_OK
2017 * so that the generic centry handling still applies correctly -
2018 * Guenther*/
2020 if (!NT_STATUS_IS_ERR(status)) {
2021 status = NT_STATUS_OK;
2025 #if 0 /* Disabled as we want the trust dom list to be managed by
2026 the main parent and always to make the query. --jerry */
2028 /* and save it */
2029 refresh_sequence_number(domain, False);
2031 centry = centry_start(domain, status);
2032 if (!centry)
2033 goto skip_save;
2035 centry_put_uint32(centry, *num_domains);
2037 for (i=0; i<(*num_domains); i++) {
2038 centry_put_string(centry, (*names)[i]);
2039 centry_put_string(centry, (*alt_names)[i]);
2040 centry_put_sid(centry, &(*dom_sids)[i]);
2043 centry_end(centry, "TRUSTDOMS/%s", domain->name);
2045 centry_free(centry);
2047 skip_save:
2048 #endif
2050 return status;
2053 /* get lockout policy */
2054 static NTSTATUS lockout_policy(struct winbindd_domain *domain,
2055 TALLOC_CTX *mem_ctx,
2056 SAM_UNK_INFO_12 *policy){
2057 struct winbind_cache *cache = get_cache(domain);
2058 struct cache_entry *centry = NULL;
2059 NTSTATUS status;
2061 if (!cache->tdb)
2062 goto do_query;
2064 centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
2066 if (!centry)
2067 goto do_query;
2069 policy->duration = centry_nttime(centry);
2070 policy->reset_count = centry_nttime(centry);
2071 policy->bad_attempt_lockout = centry_uint16(centry);
2073 status = centry->status;
2075 DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2076 domain->name, nt_errstr(status) ));
2078 centry_free(centry);
2079 return status;
2081 do_query:
2082 ZERO_STRUCTP(policy);
2084 /* Return status value returned by seq number check */
2086 if (!NT_STATUS_IS_OK(domain->last_status))
2087 return domain->last_status;
2089 DEBUG(10,("lockout_policy: [Cached] - doing backend query for info for domain %s\n",
2090 domain->name ));
2092 status = domain->backend->lockout_policy(domain, mem_ctx, policy);
2094 /* and save it */
2095 refresh_sequence_number(domain, False);
2096 wcache_save_lockout_policy(domain, status, policy);
2098 return status;
2101 /* get password policy */
2102 static NTSTATUS password_policy(struct winbindd_domain *domain,
2103 TALLOC_CTX *mem_ctx,
2104 SAM_UNK_INFO_1 *policy)
2106 struct winbind_cache *cache = get_cache(domain);
2107 struct cache_entry *centry = NULL;
2108 NTSTATUS status;
2110 if (!cache->tdb)
2111 goto do_query;
2113 centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
2115 if (!centry)
2116 goto do_query;
2118 policy->min_length_password = centry_uint16(centry);
2119 policy->password_history = centry_uint16(centry);
2120 policy->password_properties = centry_uint32(centry);
2121 policy->expire = centry_nttime(centry);
2122 policy->min_passwordage = centry_nttime(centry);
2124 status = centry->status;
2126 DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2127 domain->name, nt_errstr(status) ));
2129 centry_free(centry);
2130 return status;
2132 do_query:
2133 ZERO_STRUCTP(policy);
2135 /* Return status value returned by seq number check */
2137 if (!NT_STATUS_IS_OK(domain->last_status))
2138 return domain->last_status;
2140 DEBUG(10,("password_policy: [Cached] - doing backend query for info for domain %s\n",
2141 domain->name ));
2143 status = domain->backend->password_policy(domain, mem_ctx, policy);
2145 /* and save it */
2146 refresh_sequence_number(domain, False);
2147 wcache_save_password_policy(domain, status, policy);
2149 return status;
2153 /* Invalidate cached user and group lists coherently */
2155 static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
2156 void *state)
2158 if (strncmp((const char *)kbuf.dptr, "UL/", 3) == 0 ||
2159 strncmp((const char *)kbuf.dptr, "GL/", 3) == 0)
2160 tdb_delete(the_tdb, kbuf);
2162 return 0;
2165 /* Invalidate the getpwnam and getgroups entries for a winbindd domain */
2167 void wcache_invalidate_samlogon(struct winbindd_domain *domain,
2168 NET_USER_INFO_3 *info3)
2170 struct winbind_cache *cache;
2172 /* dont clear cached U/SID and UG/SID entries when we want to logon
2173 * offline - gd */
2175 if (lp_winbind_offline_logon()) {
2176 return;
2179 if (!domain)
2180 return;
2182 cache = get_cache(domain);
2183 netsamlogon_clear_cached_user(cache->tdb, info3);
2186 void wcache_invalidate_cache(void)
2188 struct winbindd_domain *domain;
2190 for (domain = domain_list(); domain; domain = domain->next) {
2191 struct winbind_cache *cache = get_cache(domain);
2193 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
2194 "entries for %s\n", domain->name));
2195 if (cache)
2196 tdb_traverse(cache->tdb, traverse_fn, NULL);
2200 BOOL init_wcache(void)
2202 if (wcache == NULL) {
2203 wcache = SMB_XMALLOC_P(struct winbind_cache);
2204 ZERO_STRUCTP(wcache);
2207 if (wcache->tdb != NULL)
2208 return True;
2210 /* when working offline we must not clear the cache on restart */
2211 wcache->tdb = tdb_open_log(lock_path("winbindd_cache.tdb"),
2212 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
2213 lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST),
2214 O_RDWR|O_CREAT, 0600);
2216 if (wcache->tdb == NULL) {
2217 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
2218 return False;
2221 return True;
2224 /************************************************************************
2225 This is called by the parent to initialize the cache file.
2226 We don't need sophisticated locking here as we know we're the
2227 only opener.
2228 ************************************************************************/
2230 BOOL initialize_winbindd_cache(void)
2232 BOOL cache_bad = True;
2233 uint32 vers;
2235 if (!init_wcache()) {
2236 DEBUG(0,("initialize_winbindd_cache: init_wcache failed.\n"));
2237 return False;
2240 /* Check version number. */
2241 if (tdb_fetch_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, &vers) &&
2242 vers == WINBINDD_CACHE_VERSION) {
2243 cache_bad = False;
2246 if (cache_bad) {
2247 DEBUG(0,("initialize_winbindd_cache: clearing cache "
2248 "and re-creating with version number %d\n",
2249 WINBINDD_CACHE_VERSION ));
2251 tdb_close(wcache->tdb);
2252 wcache->tdb = NULL;
2254 if (unlink(lock_path("winbindd_cache.tdb")) == -1) {
2255 DEBUG(0,("initialize_winbindd_cache: unlink %s failed %s ",
2256 lock_path("winbindd_cache.tdb"),
2257 strerror(errno) ));
2258 return False;
2260 if (!init_wcache()) {
2261 DEBUG(0,("initialize_winbindd_cache: re-initialization "
2262 "init_wcache failed.\n"));
2263 return False;
2266 /* Write the version. */
2267 if (!tdb_store_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, WINBINDD_CACHE_VERSION)) {
2268 DEBUG(0,("initialize_winbindd_cache: version number store failed %s\n",
2269 tdb_errorstr(wcache->tdb) ));
2270 return False;
2274 tdb_close(wcache->tdb);
2275 wcache->tdb = NULL;
2276 return True;
2279 void cache_store_response(pid_t pid, struct winbindd_response *response)
2281 fstring key_str;
2283 if (!init_wcache())
2284 return;
2286 DEBUG(10, ("Storing response for pid %d, len %d\n",
2287 pid, response->length));
2289 fstr_sprintf(key_str, "DR/%d", pid);
2290 if (tdb_store(wcache->tdb, string_tdb_data(key_str),
2291 make_tdb_data((uint8 *)response, sizeof(*response)),
2292 TDB_REPLACE) == -1)
2293 return;
2295 if (response->length == sizeof(*response))
2296 return;
2298 /* There's extra data */
2300 DEBUG(10, ("Storing extra data: len=%d\n",
2301 (int)(response->length - sizeof(*response))));
2303 fstr_sprintf(key_str, "DE/%d", pid);
2304 if (tdb_store(wcache->tdb, string_tdb_data(key_str),
2305 make_tdb_data((uint8 *)response->extra_data.data,
2306 response->length - sizeof(*response)),
2307 TDB_REPLACE) == 0)
2308 return;
2310 /* We could not store the extra data, make sure the tdb does not
2311 * contain a main record with wrong dangling extra data */
2313 fstr_sprintf(key_str, "DR/%d", pid);
2314 tdb_delete(wcache->tdb, string_tdb_data(key_str));
2316 return;
2319 BOOL cache_retrieve_response(pid_t pid, struct winbindd_response * response)
2321 TDB_DATA data;
2322 fstring key_str;
2324 if (!init_wcache())
2325 return False;
2327 DEBUG(10, ("Retrieving response for pid %d\n", pid));
2329 fstr_sprintf(key_str, "DR/%d", pid);
2330 data = tdb_fetch(wcache->tdb, string_tdb_data(key_str));
2332 if (data.dptr == NULL)
2333 return False;
2335 if (data.dsize != sizeof(*response))
2336 return False;
2338 memcpy(response, data.dptr, data.dsize);
2339 SAFE_FREE(data.dptr);
2341 if (response->length == sizeof(*response)) {
2342 response->extra_data.data = NULL;
2343 return True;
2346 /* There's extra data */
2348 DEBUG(10, ("Retrieving extra data length=%d\n",
2349 (int)(response->length - sizeof(*response))));
2351 fstr_sprintf(key_str, "DE/%d", pid);
2352 data = tdb_fetch(wcache->tdb, string_tdb_data(key_str));
2354 if (data.dptr == NULL) {
2355 DEBUG(0, ("Did not find extra data\n"));
2356 return False;
2359 if (data.dsize != (response->length - sizeof(*response))) {
2360 DEBUG(0, ("Invalid extra data length: %d\n", (int)data.dsize));
2361 SAFE_FREE(data.dptr);
2362 return False;
2365 dump_data(11, (uint8 *)data.dptr, data.dsize);
2367 response->extra_data.data = data.dptr;
2368 return True;
2371 void cache_cleanup_response(pid_t pid)
2373 fstring key_str;
2375 if (!init_wcache())
2376 return;
2378 fstr_sprintf(key_str, "DR/%d", pid);
2379 tdb_delete(wcache->tdb, string_tdb_data(key_str));
2381 fstr_sprintf(key_str, "DE/%d", pid);
2382 tdb_delete(wcache->tdb, string_tdb_data(key_str));
2384 return;
2388 BOOL lookup_cached_sid(TALLOC_CTX *mem_ctx, const DOM_SID *sid,
2389 char **domain_name, char **name,
2390 enum lsa_SidType *type)
2392 struct winbindd_domain *domain;
2393 struct winbind_cache *cache;
2394 struct cache_entry *centry = NULL;
2395 NTSTATUS status;
2397 domain = find_lookup_domain_from_sid(sid);
2398 if (domain == NULL) {
2399 return False;
2402 cache = get_cache(domain);
2404 if (cache->tdb == NULL) {
2405 return False;
2408 centry = wcache_fetch(cache, domain, "SN/%s", sid_string_static(sid));
2409 if (centry == NULL) {
2410 return False;
2413 if (NT_STATUS_IS_OK(centry->status)) {
2414 *type = (enum lsa_SidType)centry_uint32(centry);
2415 *domain_name = centry_string(centry, mem_ctx);
2416 *name = centry_string(centry, mem_ctx);
2419 status = centry->status;
2420 centry_free(centry);
2421 return NT_STATUS_IS_OK(status);
2424 BOOL lookup_cached_name(TALLOC_CTX *mem_ctx,
2425 const char *domain_name,
2426 const char *name,
2427 DOM_SID *sid,
2428 enum lsa_SidType *type)
2430 struct winbindd_domain *domain;
2431 struct winbind_cache *cache;
2432 struct cache_entry *centry = NULL;
2433 NTSTATUS status;
2434 fstring uname;
2435 BOOL original_online_state;
2437 domain = find_lookup_domain_from_name(domain_name);
2438 if (domain == NULL) {
2439 return False;
2442 cache = get_cache(domain);
2444 if (cache->tdb == NULL) {
2445 return False;
2448 fstrcpy(uname, name);
2449 strupper_m(uname);
2451 /* If we are doing a cached logon, temporarily set the domain
2452 offline so the cache won't expire the entry */
2454 original_online_state = domain->online;
2455 domain->online = False;
2456 centry = wcache_fetch(cache, domain, "NS/%s/%s", domain_name, uname);
2457 domain->online = original_online_state;
2459 if (centry == NULL) {
2460 return False;
2463 if (NT_STATUS_IS_OK(centry->status)) {
2464 *type = (enum lsa_SidType)centry_uint32(centry);
2465 centry_sid(centry, mem_ctx, sid);
2468 status = centry->status;
2469 centry_free(centry);
2471 return NT_STATUS_IS_OK(status);
2474 void cache_name2sid(struct winbindd_domain *domain,
2475 const char *domain_name, const char *name,
2476 enum lsa_SidType type, const DOM_SID *sid)
2478 refresh_sequence_number(domain, False);
2479 wcache_save_name_to_sid(domain, NT_STATUS_OK, domain_name, name,
2480 sid, type);
2484 * The original idea that this cache only contains centries has
2485 * been blurred - now other stuff gets put in here. Ensure we
2486 * ignore these things on cleanup.
2489 static int traverse_fn_cleanup(TDB_CONTEXT *the_tdb, TDB_DATA kbuf,
2490 TDB_DATA dbuf, void *state)
2492 struct cache_entry *centry;
2494 if (is_non_centry_key(kbuf)) {
2495 return 0;
2498 centry = wcache_fetch_raw((char *)kbuf.dptr);
2499 if (!centry) {
2500 return 0;
2503 if (!NT_STATUS_IS_OK(centry->status)) {
2504 DEBUG(10,("deleting centry %s\n", (const char *)kbuf.dptr));
2505 tdb_delete(the_tdb, kbuf);
2508 centry_free(centry);
2509 return 0;
2512 /* flush the cache */
2513 void wcache_flush_cache(void)
2515 if (!wcache)
2516 return;
2517 if (wcache->tdb) {
2518 tdb_close(wcache->tdb);
2519 wcache->tdb = NULL;
2521 if (opt_nocache)
2522 return;
2524 /* when working offline we must not clear the cache on restart */
2525 wcache->tdb = tdb_open_log(lock_path("winbindd_cache.tdb"),
2526 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
2527 lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST),
2528 O_RDWR|O_CREAT, 0600);
2530 if (!wcache->tdb) {
2531 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
2532 return;
2535 tdb_traverse(wcache->tdb, traverse_fn_cleanup, NULL);
2537 DEBUG(10,("wcache_flush_cache success\n"));
2540 /* Count cached creds */
2542 static int traverse_fn_cached_creds(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
2543 void *state)
2545 int *cred_count = (int*)state;
2547 if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
2548 (*cred_count)++;
2550 return 0;
2553 NTSTATUS wcache_count_cached_creds(struct winbindd_domain *domain, int *count)
2555 struct winbind_cache *cache = get_cache(domain);
2557 *count = 0;
2559 if (!cache->tdb) {
2560 return NT_STATUS_INTERNAL_DB_ERROR;
2563 tdb_traverse(cache->tdb, traverse_fn_cached_creds, (void *)count);
2565 return NT_STATUS_OK;
2568 struct cred_list {
2569 struct cred_list *prev, *next;
2570 TDB_DATA key;
2571 fstring name;
2572 time_t created;
2574 static struct cred_list *wcache_cred_list;
2576 static int traverse_fn_get_credlist(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
2577 void *state)
2579 struct cred_list *cred;
2581 if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
2583 cred = SMB_MALLOC_P(struct cred_list);
2584 if (cred == NULL) {
2585 DEBUG(0,("traverse_fn_remove_first_creds: failed to malloc new entry for list\n"));
2586 return -1;
2589 ZERO_STRUCTP(cred);
2591 /* save a copy of the key */
2593 fstrcpy(cred->name, (const char *)kbuf.dptr);
2594 DLIST_ADD(wcache_cred_list, cred);
2597 return 0;
2600 NTSTATUS wcache_remove_oldest_cached_creds(struct winbindd_domain *domain, const DOM_SID *sid)
2602 struct winbind_cache *cache = get_cache(domain);
2603 NTSTATUS status;
2604 int ret;
2605 struct cred_list *cred, *oldest = NULL;
2607 if (!cache->tdb) {
2608 return NT_STATUS_INTERNAL_DB_ERROR;
2611 /* we possibly already have an entry */
2612 if (sid && NT_STATUS_IS_OK(wcache_cached_creds_exist(domain, sid))) {
2614 fstring key_str;
2616 DEBUG(11,("we already have an entry, deleting that\n"));
2618 fstr_sprintf(key_str, "CRED/%s", sid_string_static(sid));
2620 tdb_delete(cache->tdb, string_tdb_data(key_str));
2622 return NT_STATUS_OK;
2625 ret = tdb_traverse(cache->tdb, traverse_fn_get_credlist, NULL);
2626 if (ret == 0) {
2627 return NT_STATUS_OK;
2628 } else if ((ret == -1) || (wcache_cred_list == NULL)) {
2629 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
2632 ZERO_STRUCTP(oldest);
2634 for (cred = wcache_cred_list; cred; cred = cred->next) {
2636 TDB_DATA data;
2637 time_t t;
2639 data = tdb_fetch(cache->tdb, string_tdb_data(cred->name));
2640 if (!data.dptr) {
2641 DEBUG(10,("wcache_remove_oldest_cached_creds: entry for [%s] not found\n",
2642 cred->name));
2643 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
2644 goto done;
2647 t = IVAL(data.dptr, 0);
2648 SAFE_FREE(data.dptr);
2650 if (!oldest) {
2651 oldest = SMB_MALLOC_P(struct cred_list);
2652 if (oldest == NULL) {
2653 status = NT_STATUS_NO_MEMORY;
2654 goto done;
2657 fstrcpy(oldest->name, cred->name);
2658 oldest->created = t;
2659 continue;
2662 if (t < oldest->created) {
2663 fstrcpy(oldest->name, cred->name);
2664 oldest->created = t;
2668 if (tdb_delete(cache->tdb, string_tdb_data(oldest->name)) == 0) {
2669 status = NT_STATUS_OK;
2670 } else {
2671 status = NT_STATUS_UNSUCCESSFUL;
2673 done:
2674 SAFE_FREE(wcache_cred_list);
2675 SAFE_FREE(oldest);
2677 return status;
2680 /* Change the global online/offline state. */
2681 BOOL set_global_winbindd_state_offline(void)
2683 TDB_DATA data;
2685 DEBUG(10,("set_global_winbindd_state_offline: offline requested.\n"));
2687 /* Only go offline if someone has created
2688 the key "WINBINDD_OFFLINE" in the cache tdb. */
2690 if (wcache == NULL || wcache->tdb == NULL) {
2691 DEBUG(10,("set_global_winbindd_state_offline: wcache not open yet.\n"));
2692 return False;
2695 if (!lp_winbind_offline_logon()) {
2696 DEBUG(10,("set_global_winbindd_state_offline: rejecting.\n"));
2697 return False;
2700 if (global_winbindd_offline_state) {
2701 /* Already offline. */
2702 return True;
2705 data = tdb_fetch_bystring( wcache->tdb, "WINBINDD_OFFLINE" );
2707 if (!data.dptr || data.dsize != 4) {
2708 DEBUG(10,("set_global_winbindd_state_offline: offline state not set.\n"));
2709 SAFE_FREE(data.dptr);
2710 return False;
2711 } else {
2712 DEBUG(10,("set_global_winbindd_state_offline: offline state set.\n"));
2713 global_winbindd_offline_state = True;
2714 SAFE_FREE(data.dptr);
2715 return True;
2719 void set_global_winbindd_state_online(void)
2721 DEBUG(10,("set_global_winbindd_state_online: online requested.\n"));
2723 if (!lp_winbind_offline_logon()) {
2724 DEBUG(10,("set_global_winbindd_state_online: rejecting.\n"));
2725 return;
2728 if (!global_winbindd_offline_state) {
2729 /* Already online. */
2730 return;
2732 global_winbindd_offline_state = False;
2734 if (!wcache->tdb) {
2735 return;
2738 /* Ensure there is no key "WINBINDD_OFFLINE" in the cache tdb. */
2739 tdb_delete_bystring(wcache->tdb, "WINBINDD_OFFLINE");
2742 BOOL get_global_winbindd_state_offline(void)
2744 return global_winbindd_offline_state;
2747 /***********************************************************************
2748 Validate functions for all possible cache tdb keys.
2749 ***********************************************************************/
2751 static struct cache_entry *create_centry_validate(const char *kstr, TDB_DATA data,
2752 struct tdb_validation_status *state)
2754 struct cache_entry *centry;
2756 centry = SMB_XMALLOC_P(struct cache_entry);
2757 centry->data = (unsigned char *)memdup(data.dptr, data.dsize);
2758 if (!centry->data) {
2759 SAFE_FREE(centry);
2760 return NULL;
2762 centry->len = data.dsize;
2763 centry->ofs = 0;
2765 if (centry->len < 8) {
2766 /* huh? corrupt cache? */
2767 DEBUG(0,("create_centry_validate: Corrupt cache for key %s (len < 8) ?\n", kstr));
2768 centry_free(centry);
2769 state->bad_entry = True;
2770 state->success = False;
2771 return NULL;
2774 centry->status = NT_STATUS(centry_uint32(centry));
2775 centry->sequence_number = centry_uint32(centry);
2776 return centry;
2779 static int validate_seqnum(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
2780 struct tdb_validation_status *state)
2782 if (dbuf.dsize != 8) {
2783 DEBUG(0,("validate_seqnum: Corrupt cache for key %s (len %u != 8) ?\n",
2784 keystr, (unsigned int)dbuf.dsize ));
2785 state->bad_entry = True;
2786 return 1;
2788 return 0;
2791 static int validate_ns(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
2792 struct tdb_validation_status *state)
2794 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
2795 if (!centry) {
2796 return 1;
2799 (void)centry_uint32(centry);
2800 if (NT_STATUS_IS_OK(centry->status)) {
2801 DOM_SID sid;
2802 (void)centry_sid(centry, mem_ctx, &sid);
2805 centry_free(centry);
2807 if (!(state->success)) {
2808 return 1;
2810 DEBUG(10,("validate_ns: %s ok\n", keystr));
2811 return 0;
2814 static int validate_sn(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
2815 struct tdb_validation_status *state)
2817 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
2818 if (!centry) {
2819 return 1;
2822 if (NT_STATUS_IS_OK(centry->status)) {
2823 (void)centry_uint32(centry);
2824 (void)centry_string(centry, mem_ctx);
2825 (void)centry_string(centry, mem_ctx);
2828 centry_free(centry);
2830 if (!(state->success)) {
2831 return 1;
2833 DEBUG(10,("validate_sn: %s ok\n", keystr));
2834 return 0;
2837 static int validate_u(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
2838 struct tdb_validation_status *state)
2840 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
2841 DOM_SID sid;
2843 if (!centry) {
2844 return 1;
2847 (void)centry_string(centry, mem_ctx);
2848 (void)centry_string(centry, mem_ctx);
2849 (void)centry_string(centry, mem_ctx);
2850 (void)centry_string(centry, mem_ctx);
2851 (void)centry_uint32(centry);
2852 (void)centry_sid(centry, mem_ctx, &sid);
2853 (void)centry_sid(centry, mem_ctx, &sid);
2855 centry_free(centry);
2857 if (!(state->success)) {
2858 return 1;
2860 DEBUG(10,("validate_u: %s ok\n", keystr));
2861 return 0;
2864 static int validate_loc_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
2865 struct tdb_validation_status *state)
2867 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
2869 if (!centry) {
2870 return 1;
2873 (void)centry_nttime(centry);
2874 (void)centry_nttime(centry);
2875 (void)centry_uint16(centry);
2877 centry_free(centry);
2879 if (!(state->success)) {
2880 return 1;
2882 DEBUG(10,("validate_loc_pol: %s ok\n", keystr));
2883 return 0;
2886 static int validate_pwd_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
2887 struct tdb_validation_status *state)
2889 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
2891 if (!centry) {
2892 return 1;
2895 (void)centry_uint16(centry);
2896 (void)centry_uint16(centry);
2897 (void)centry_uint32(centry);
2898 (void)centry_nttime(centry);
2899 (void)centry_nttime(centry);
2901 centry_free(centry);
2903 if (!(state->success)) {
2904 return 1;
2906 DEBUG(10,("validate_pwd_pol: %s ok\n", keystr));
2907 return 0;
2910 static int validate_cred(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
2911 struct tdb_validation_status *state)
2913 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
2915 if (!centry) {
2916 return 1;
2919 (void)centry_time(centry);
2920 (void)centry_hash16(centry, mem_ctx);
2922 /* We only have 17 bytes more data in the salted cred case. */
2923 if (centry->len - centry->ofs == 17) {
2924 (void)centry_hash16(centry, mem_ctx);
2927 centry_free(centry);
2929 if (!(state->success)) {
2930 return 1;
2932 DEBUG(10,("validate_cred: %s ok\n", keystr));
2933 return 0;
2936 static int validate_ul(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
2937 struct tdb_validation_status *state)
2939 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
2940 int32 num_entries, i;
2942 if (!centry) {
2943 return 1;
2946 num_entries = (int32)centry_uint32(centry);
2948 for (i=0; i< num_entries; i++) {
2949 DOM_SID sid;
2950 (void)centry_string(centry, mem_ctx);
2951 (void)centry_string(centry, mem_ctx);
2952 (void)centry_string(centry, mem_ctx);
2953 (void)centry_string(centry, mem_ctx);
2954 (void)centry_sid(centry, mem_ctx, &sid);
2955 (void)centry_sid(centry, mem_ctx, &sid);
2958 centry_free(centry);
2960 if (!(state->success)) {
2961 return 1;
2963 DEBUG(10,("validate_ul: %s ok\n", keystr));
2964 return 0;
2967 static int validate_gl(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
2968 struct tdb_validation_status *state)
2970 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
2971 int32 num_entries, i;
2973 if (!centry) {
2974 return 1;
2977 num_entries = centry_uint32(centry);
2979 for (i=0; i< num_entries; i++) {
2980 (void)centry_string(centry, mem_ctx);
2981 (void)centry_string(centry, mem_ctx);
2982 (void)centry_uint32(centry);
2985 centry_free(centry);
2987 if (!(state->success)) {
2988 return 1;
2990 DEBUG(10,("validate_gl: %s ok\n", keystr));
2991 return 0;
2994 static int validate_ug(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
2995 struct tdb_validation_status *state)
2997 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
2998 int32 num_groups, i;
3000 if (!centry) {
3001 return 1;
3004 num_groups = centry_uint32(centry);
3006 for (i=0; i< num_groups; i++) {
3007 DOM_SID sid;
3008 centry_sid(centry, mem_ctx, &sid);
3011 centry_free(centry);
3013 if (!(state->success)) {
3014 return 1;
3016 DEBUG(10,("validate_ug: %s ok\n", keystr));
3017 return 0;
3020 static int validate_ua(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3021 struct tdb_validation_status *state)
3023 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3024 int32 num_aliases, i;
3026 if (!centry) {
3027 return 1;
3030 num_aliases = centry_uint32(centry);
3032 for (i=0; i < num_aliases; i++) {
3033 (void)centry_uint32(centry);
3036 centry_free(centry);
3038 if (!(state->success)) {
3039 return 1;
3041 DEBUG(10,("validate_ua: %s ok\n", keystr));
3042 return 0;
3045 static int validate_gm(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3046 struct tdb_validation_status *state)
3048 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3049 int32 num_names, i;
3051 if (!centry) {
3052 return 1;
3055 num_names = centry_uint32(centry);
3057 for (i=0; i< num_names; i++) {
3058 DOM_SID sid;
3059 centry_sid(centry, mem_ctx, &sid);
3060 (void)centry_string(centry, mem_ctx);
3061 (void)centry_uint32(centry);
3064 centry_free(centry);
3066 if (!(state->success)) {
3067 return 1;
3069 DEBUG(10,("validate_gm: %s ok\n", keystr));
3070 return 0;
3073 static int validate_dr(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3074 struct tdb_validation_status *state)
3076 /* Can't say anything about this other than must be nonzero. */
3077 if (dbuf.dsize == 0) {
3078 DEBUG(0,("validate_dr: Corrupt cache for key %s (len == 0) ?\n",
3079 keystr));
3080 state->bad_entry = True;
3081 state->success = False;
3082 return 1;
3085 DEBUG(10,("validate_dr: %s ok\n", keystr));
3086 return 0;
3089 static int validate_de(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3090 struct tdb_validation_status *state)
3092 /* Can't say anything about this other than must be nonzero. */
3093 if (dbuf.dsize == 0) {
3094 DEBUG(0,("validate_de: Corrupt cache for key %s (len == 0) ?\n",
3095 keystr));
3096 state->bad_entry = True;
3097 state->success = False;
3098 return 1;
3101 DEBUG(10,("validate_de: %s ok\n", keystr));
3102 return 0;
3105 static int validate_trustdoms(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3106 struct tdb_validation_status *state)
3108 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3109 int32 num_domains, i;
3111 if (!centry) {
3112 return 1;
3115 num_domains = centry_uint32(centry);
3117 for (i=0; i< num_domains; i++) {
3118 DOM_SID sid;
3119 (void)centry_string(centry, mem_ctx);
3120 (void)centry_string(centry, mem_ctx);
3121 (void)centry_sid(centry, mem_ctx, &sid);
3124 centry_free(centry);
3126 if (!(state->success)) {
3127 return 1;
3129 DEBUG(10,("validate_trustdoms: %s ok\n", keystr));
3130 return 0;
3133 static int validate_trustdomcache(TALLOC_CTX *mem_ctx, const char *keystr,
3134 TDB_DATA dbuf,
3135 struct tdb_validation_status *state)
3137 if (dbuf.dsize == 0) {
3138 DEBUG(0, ("validate_trustdomcache: Corrupt cache for "
3139 "key %s (len ==0) ?\n", keystr));
3140 state->bad_entry = True;
3141 state->success = False;
3142 return 1;
3145 DEBUG(10, ("validate_trustdomcache: %s ok\n", keystr));
3146 DEBUGADD(10, (" Don't trust me, I am a DUMMY!\n"));
3147 return 0;
3150 static int validate_offline(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3151 struct tdb_validation_status *state)
3153 if (dbuf.dsize != 4) {
3154 DEBUG(0,("validate_offline: Corrupt cache for key %s (len %u != 4) ?\n",
3155 keystr, (unsigned int)dbuf.dsize ));
3156 state->bad_entry = True;
3157 state->success = False;
3158 return 1;
3160 DEBUG(10,("validate_offline: %s ok\n", keystr));
3161 return 0;
3164 static int validate_cache_version(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3165 struct tdb_validation_status *state)
3167 if (dbuf.dsize != 4) {
3168 DEBUG(0, ("validate_cache_version: Corrupt cache for "
3169 "key %s (len %u != 4) ?\n",
3170 keystr, (unsigned int)dbuf.dsize));
3171 state->bad_entry = True;
3172 state->success = False;
3173 return 1;
3176 DEBUG(10, ("validate_cache_version: %s ok\n", keystr));
3177 return 0;
3180 /***********************************************************************
3181 A list of all possible cache tdb keys with associated validation
3182 functions.
3183 ***********************************************************************/
3185 struct key_val_struct {
3186 const char *keyname;
3187 int (*validate_data_fn)(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, struct tdb_validation_status* state);
3188 } key_val[] = {
3189 {"SEQNUM/", validate_seqnum},
3190 {"NS/", validate_ns},
3191 {"SN/", validate_sn},
3192 {"U/", validate_u},
3193 {"LOC_POL/", validate_loc_pol},
3194 {"PWD_POL/", validate_pwd_pol},
3195 {"CRED/", validate_cred},
3196 {"UL/", validate_ul},
3197 {"GL/", validate_gl},
3198 {"UG/", validate_ug},
3199 {"UA", validate_ua},
3200 {"GM/", validate_gm},
3201 {"DR/", validate_dr},
3202 {"DE/", validate_de},
3203 {"TRUSTDOMS/", validate_trustdoms},
3204 {"TRUSTDOMCACHE/", validate_trustdomcache},
3205 {"WINBINDD_OFFLINE", validate_offline},
3206 {WINBINDD_CACHE_VERSION_KEYSTR, validate_cache_version},
3207 {NULL, NULL}
3210 /***********************************************************************
3211 Function to look at every entry in the tdb and validate it as far as
3212 possible.
3213 ***********************************************************************/
3215 static int cache_traverse_validate_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, void *state)
3217 int i;
3218 struct tdb_validation_status *v_state = (struct tdb_validation_status *)state;
3220 /* Paranoia check. */
3221 if (kbuf.dsize > 1024) {
3222 DEBUG(0,("cache_traverse_validate_fn: key length too large (%u) > 1024\n\n",
3223 (unsigned int)kbuf.dsize ));
3224 return 1;
3227 for (i = 0; key_val[i].keyname; i++) {
3228 size_t namelen = strlen(key_val[i].keyname);
3229 if (kbuf.dsize >= namelen && (
3230 strncmp(key_val[i].keyname, (const char *)kbuf.dptr, namelen)) == 0) {
3231 TALLOC_CTX *mem_ctx;
3232 char *keystr;
3233 int ret;
3235 keystr = SMB_MALLOC_ARRAY(char, kbuf.dsize+1);
3236 if (!keystr) {
3237 return 1;
3239 memcpy(keystr, kbuf.dptr, kbuf.dsize);
3240 keystr[kbuf.dsize] = '\0';
3242 mem_ctx = talloc_init("validate_ctx");
3243 if (!mem_ctx) {
3244 SAFE_FREE(keystr);
3245 return 1;
3248 ret = key_val[i].validate_data_fn(mem_ctx, keystr, dbuf,
3249 v_state);
3251 SAFE_FREE(keystr);
3252 talloc_destroy(mem_ctx);
3253 return ret;
3257 DEBUG(0,("cache_traverse_validate_fn: unknown cache entry\nkey :\n"));
3258 dump_data(0, (uint8 *)kbuf.dptr, kbuf.dsize);
3259 DEBUG(0,("data :\n"));
3260 dump_data(0, (uint8 *)dbuf.dptr, dbuf.dsize);
3261 v_state->unknown_key = True;
3262 v_state->success = False;
3263 return 1; /* terminate. */
3266 static void validate_panic(const char *const why)
3268 DEBUG(0,("validating cache: would panic %s\n", why ));
3269 DEBUGADD(0, ("exiting instead (cache validation mode)\n"));
3270 exit(47);
3273 /***********************************************************************
3274 Try and validate every entry in the winbindd cache. If we fail here,
3275 delete the cache tdb and return non-zero - the caller (main winbindd
3276 function) will restart us as we don't know if we crashed or not.
3277 ***********************************************************************/
3279 int winbindd_validate_cache(void)
3281 int ret = -1;
3282 const char *tdb_path = lock_path("winbindd_cache.tdb");
3283 TDB_CONTEXT *tdb = NULL;
3285 DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
3286 smb_panic_fn = validate_panic;
3289 tdb = tdb_open_log(tdb_path,
3290 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
3291 ( lp_winbind_offline_logon()
3292 ? TDB_DEFAULT
3293 : TDB_DEFAULT | TDB_CLEAR_IF_FIRST ),
3294 O_RDWR|O_CREAT,
3295 0600);
3296 if (!tdb) {
3297 DEBUG(0, ("winbindd_validate_cache: "
3298 "error opening/initializing tdb\n"));
3299 goto done;
3301 tdb_close(tdb);
3303 ret = tdb_validate(lock_path("winbindd_cache.tdb"),
3304 cache_traverse_validate_fn);
3306 if (ret != 0) {
3307 DEBUG(10, ("winbindd_validate_cache: validation not successful.\n"));
3308 DEBUGADD(10, ("removing tdb %s.\n", tdb_path));
3309 unlink(tdb_path);
3312 done:
3313 DEBUG(10, ("winbindd_validate_cache: restoring panic function\n"));
3314 smb_panic_fn = smb_panic;
3315 return ret;
3318 /*********************************************************************
3319 ********************************************************************/
3321 static BOOL add_wbdomain_to_tdc_array( struct winbindd_domain *new_dom,
3322 struct winbindd_tdc_domain **domains,
3323 size_t *num_domains )
3325 struct winbindd_tdc_domain *list = NULL;
3326 size_t idx;
3327 int i;
3328 BOOL set_only = False;
3330 /* don't allow duplicates */
3332 idx = *num_domains;
3333 list = *domains;
3335 for ( i=0; i< (*num_domains); i++ ) {
3336 if ( strequal( new_dom->name, list[i].domain_name ) ) {
3337 DEBUG(10,("add_wbdomain_to_tdc_array: Found existing record for %s\n",
3338 new_dom->name));
3339 idx = i;
3340 set_only = True;
3342 break;
3346 if ( !set_only ) {
3347 if ( !*domains ) {
3348 list = TALLOC_ARRAY( NULL, struct winbindd_tdc_domain, 1 );
3349 idx = 0;
3350 } else {
3351 list = TALLOC_REALLOC_ARRAY( *domains, *domains,
3352 struct winbindd_tdc_domain,
3353 (*num_domains)+1);
3354 idx = *num_domains;
3357 ZERO_STRUCT( list[idx] );
3360 if ( !list )
3361 return False;
3363 list[idx].domain_name = talloc_strdup( list, new_dom->name );
3364 list[idx].dns_name = talloc_strdup( list, new_dom->alt_name );
3366 if ( !is_null_sid( &new_dom->sid ) )
3367 sid_copy( &list[idx].sid, &new_dom->sid );
3369 if ( new_dom->domain_flags != 0x0 )
3370 list[idx].trust_flags = new_dom->domain_flags;
3372 if ( new_dom->domain_type != 0x0 )
3373 list[idx].trust_type = new_dom->domain_type;
3375 if ( new_dom->domain_trust_attribs != 0x0 )
3376 list[idx].trust_attribs = new_dom->domain_trust_attribs;
3378 if ( !set_only ) {
3379 *domains = list;
3380 *num_domains = idx + 1;
3383 return True;
3386 /*********************************************************************
3387 ********************************************************************/
3389 static TDB_DATA make_tdc_key( const char *domain_name )
3391 char *keystr = NULL;
3392 TDB_DATA key = { NULL, 0 };
3394 if ( !domain_name ) {
3395 DEBUG(5,("make_tdc_key: Keyname workgroup is NULL!\n"));
3396 return key;
3400 asprintf( &keystr, "TRUSTDOMCACHE/%s", domain_name );
3401 key = string_term_tdb_data(keystr);
3403 return key;
3406 /*********************************************************************
3407 ********************************************************************/
3409 static int pack_tdc_domains( struct winbindd_tdc_domain *domains,
3410 size_t num_domains,
3411 unsigned char **buf )
3413 unsigned char *buffer = NULL;
3414 int len = 0;
3415 int buflen = 0;
3416 int i = 0;
3418 DEBUG(10,("pack_tdc_domains: Packing %d trusted domains\n",
3419 (int)num_domains));
3421 buflen = 0;
3423 again:
3424 len = 0;
3426 /* Store the number of array items first */
3427 len += tdb_pack( buffer+len, buflen-len, "d",
3428 num_domains );
3430 /* now pack each domain trust record */
3431 for ( i=0; i<num_domains; i++ ) {
3433 if ( buflen > 0 ) {
3434 DEBUG(10,("pack_tdc_domains: Packing domain %s (%s)\n",
3435 domains[i].domain_name,
3436 domains[i].dns_name ? domains[i].dns_name : "UNKNOWN" ));
3439 len += tdb_pack( buffer+len, buflen-len, "fffddd",
3440 domains[i].domain_name,
3441 domains[i].dns_name,
3442 sid_string_static(&domains[i].sid),
3443 domains[i].trust_flags,
3444 domains[i].trust_attribs,
3445 domains[i].trust_type );
3448 if ( buflen < len ) {
3449 SAFE_FREE(buffer);
3450 if ( (buffer = SMB_MALLOC_ARRAY(unsigned char, len)) == NULL ) {
3451 DEBUG(0,("pack_tdc_domains: failed to alloc buffer!\n"));
3452 buflen = -1;
3453 goto done;
3455 buflen = len;
3456 goto again;
3459 *buf = buffer;
3461 done:
3462 return buflen;
3465 /*********************************************************************
3466 ********************************************************************/
3468 static size_t unpack_tdc_domains( unsigned char *buf, int buflen,
3469 struct winbindd_tdc_domain **domains )
3471 fstring domain_name, dns_name, sid_string;
3472 uint32 type, attribs, flags;
3473 int num_domains;
3474 int len = 0;
3475 int i;
3476 struct winbindd_tdc_domain *list = NULL;
3478 /* get the number of domains */
3479 len += tdb_unpack( buf+len, buflen-len, "d", &num_domains);
3480 if ( len == -1 ) {
3481 DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
3482 return 0;
3485 list = TALLOC_ARRAY( NULL, struct winbindd_tdc_domain, num_domains );
3486 if ( !list ) {
3487 DEBUG(0,("unpack_tdc_domains: Failed to talloc() domain list!\n"));
3488 return 0;
3491 for ( i=0; i<num_domains; i++ ) {
3492 len += tdb_unpack( buf+len, buflen-len, "fffddd",
3493 domain_name,
3494 dns_name,
3495 sid_string,
3496 &flags,
3497 &attribs,
3498 &type );
3500 if ( len == -1 ) {
3501 DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
3502 TALLOC_FREE( list );
3503 return 0;
3506 DEBUG(11,("unpack_tdc_domains: Unpacking domain %s (%s) "
3507 "SID %s, flags = 0x%x, attribs = 0x%x, type = 0x%x\n",
3508 domain_name, dns_name, sid_string,
3509 flags, attribs, type));
3511 list[i].domain_name = talloc_strdup( list, domain_name );
3512 list[i].dns_name = talloc_strdup( list, dns_name );
3513 if ( !string_to_sid( &(list[i].sid), sid_string ) ) {
3514 DEBUG(10,("unpack_tdc_domains: no SID for domain %s\n",
3515 domain_name));
3517 list[i].trust_flags = flags;
3518 list[i].trust_attribs = attribs;
3519 list[i].trust_type = type;
3522 *domains = list;
3524 return num_domains;
3527 /*********************************************************************
3528 ********************************************************************/
3530 static BOOL wcache_tdc_store_list( struct winbindd_tdc_domain *domains, size_t num_domains )
3532 TDB_DATA key = make_tdc_key( lp_workgroup() );
3533 TDB_DATA data = { NULL, 0 };
3534 int ret;
3536 if ( !key.dptr )
3537 return False;
3539 /* See if we were asked to delete the cache entry */
3541 if ( !domains ) {
3542 ret = tdb_delete( wcache->tdb, key );
3543 goto done;
3546 data.dsize = pack_tdc_domains( domains, num_domains, &data.dptr );
3548 if ( !data.dptr ) {
3549 ret = -1;
3550 goto done;
3553 ret = tdb_store( wcache->tdb, key, data, 0 );
3555 done:
3556 SAFE_FREE( data.dptr );
3557 SAFE_FREE( key.dptr );
3559 return ( ret != -1 );
3562 /*********************************************************************
3563 ********************************************************************/
3565 BOOL wcache_tdc_fetch_list( struct winbindd_tdc_domain **domains, size_t *num_domains )
3567 TDB_DATA key = make_tdc_key( lp_workgroup() );
3568 TDB_DATA data = { NULL, 0 };
3570 *domains = NULL;
3571 *num_domains = 0;
3573 if ( !key.dptr )
3574 return False;
3576 data = tdb_fetch( wcache->tdb, key );
3578 SAFE_FREE( key.dptr );
3580 if ( !data.dptr )
3581 return False;
3583 *num_domains = unpack_tdc_domains( data.dptr, data.dsize, domains );
3585 SAFE_FREE( data.dptr );
3587 if ( !*domains )
3588 return False;
3590 return True;
3593 /*********************************************************************
3594 ********************************************************************/
3596 BOOL wcache_tdc_add_domain( struct winbindd_domain *domain )
3598 struct winbindd_tdc_domain *dom_list = NULL;
3599 size_t num_domains = 0;
3600 BOOL ret = False;
3602 DEBUG(10,("wcache_tdc_add_domain: Adding domain %s (%s), SID %s, "
3603 "flags = 0x%x, attributes = 0x%x, type = 0x%x\n",
3604 domain->name, domain->alt_name,
3605 sid_string_static(&domain->sid),
3606 domain->domain_flags,
3607 domain->domain_trust_attribs,
3608 domain->domain_type));
3610 if ( !init_wcache() ) {
3611 return False;
3614 /* fetch the list */
3616 wcache_tdc_fetch_list( &dom_list, &num_domains );
3618 /* add the new domain */
3620 if ( !add_wbdomain_to_tdc_array( domain, &dom_list, &num_domains ) ) {
3621 goto done;
3624 /* pack the domain */
3626 if ( !wcache_tdc_store_list( dom_list, num_domains ) ) {
3627 goto done;
3630 /* Success */
3632 ret = True;
3633 done:
3634 TALLOC_FREE( dom_list );
3636 return ret;
3639 /*********************************************************************
3640 ********************************************************************/
3642 struct winbindd_tdc_domain * wcache_tdc_fetch_domain( TALLOC_CTX *ctx, const char *name )
3644 struct winbindd_tdc_domain *dom_list = NULL;
3645 size_t num_domains = 0;
3646 int i;
3647 struct winbindd_tdc_domain *d = NULL;
3649 DEBUG(10,("wcache_tdc_fetch_domain: Searching for domain %s\n", name));
3651 if ( !init_wcache() ) {
3652 return False;
3655 /* fetch the list */
3657 wcache_tdc_fetch_list( &dom_list, &num_domains );
3659 for ( i=0; i<num_domains; i++ ) {
3660 if ( strequal(name, dom_list[i].domain_name) ||
3661 strequal(name, dom_list[i].dns_name) )
3663 DEBUG(10,("wcache_tdc_fetch_domain: Found domain %s\n",
3664 name));
3666 d = TALLOC_P( ctx, struct winbindd_tdc_domain );
3667 if ( !d )
3668 break;
3670 d->domain_name = talloc_strdup( d, dom_list[i].domain_name );
3671 d->dns_name = talloc_strdup( d, dom_list[i].dns_name );
3672 sid_copy( &d->sid, &dom_list[i].sid );
3673 d->trust_flags = dom_list[i].trust_flags;
3674 d->trust_type = dom_list[i].trust_type;
3675 d->trust_attribs = dom_list[i].trust_attribs;
3677 break;
3681 TALLOC_FREE( dom_list );
3683 return d;
3687 /*********************************************************************
3688 ********************************************************************/
3690 void wcache_tdc_clear( void )
3692 if ( !init_wcache() )
3693 return;
3695 wcache_tdc_store_list( NULL, 0 );
3697 return;
3701 /*********************************************************************
3702 ********************************************************************/
3704 static void wcache_save_user_pwinfo(struct winbindd_domain *domain,
3705 NTSTATUS status,
3706 const DOM_SID *user_sid,
3707 const char *homedir,
3708 const char *shell,
3709 const char *gecos,
3710 uint32 gid)
3712 struct cache_entry *centry;
3714 if ( (centry = centry_start(domain, status)) == NULL )
3715 return;
3717 centry_put_string( centry, homedir );
3718 centry_put_string( centry, shell );
3719 centry_put_string( centry, gecos );
3720 centry_put_uint32( centry, gid );
3722 centry_end(centry, "NSS/PWINFO/%s", sid_string_static(user_sid) );
3724 DEBUG(10,("wcache_save_user_pwinfo: %s\n", sid_string_static(user_sid) ));
3726 centry_free(centry);
3729 NTSTATUS nss_get_info_cached( struct winbindd_domain *domain,
3730 const DOM_SID *user_sid,
3731 TALLOC_CTX *ctx,
3732 ADS_STRUCT *ads, LDAPMessage *msg,
3733 char **homedir, char **shell, char **gecos,
3734 gid_t *p_gid)
3736 struct winbind_cache *cache = get_cache(domain);
3737 struct cache_entry *centry = NULL;
3738 NTSTATUS nt_status;
3740 if (!cache->tdb)
3741 goto do_query;
3743 centry = wcache_fetch(cache, domain, "NSS/PWINFO/%s", sid_string_static(user_sid));
3745 if (!centry)
3746 goto do_query;
3748 *homedir = centry_string( centry, ctx );
3749 *shell = centry_string( centry, ctx );
3750 *gecos = centry_string( centry, ctx );
3751 *p_gid = centry_uint32( centry );
3753 centry_free(centry);
3755 DEBUG(10,("nss_get_info_cached: [Cached] - user_sid %s\n",
3756 sid_string_static(user_sid)));
3758 return NT_STATUS_OK;
3760 do_query:
3762 nt_status = nss_get_info( domain->name, user_sid, ctx, ads, msg,
3763 homedir, shell, gecos, p_gid );
3765 if ( NT_STATUS_IS_OK(nt_status) ) {
3766 wcache_save_user_pwinfo( domain, nt_status, user_sid,
3767 *homedir, *shell, *gecos, *p_gid );
3770 if ( NT_STATUS_EQUAL( nt_status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND ) ) {
3771 DEBUG(5,("nss_get_info_cached: Setting domain %s offline\n",
3772 domain->name ));
3773 set_domain_offline( domain );
3776 return nt_status;
3780 /* the cache backend methods are exposed via this structure */
3781 struct winbindd_methods cache_methods = {
3782 True,
3783 query_user_list,
3784 enum_dom_groups,
3785 enum_local_groups,
3786 name_to_sid,
3787 sid_to_name,
3788 rids_to_names,
3789 query_user,
3790 lookup_usergroups,
3791 lookup_useraliases,
3792 lookup_groupmem,
3793 sequence_number,
3794 lockout_policy,
3795 password_policy,
3796 trusted_domains