s4-winbind: Use winbindd in the AD DC for fl2003dc and plugin_s4_dc
[Samba/wip.git] / source3 / winbindd / winbindd_cache.c
blob51f3e04eae74d782bbf5fc50312078a6a3ae114d
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 "system/filesys.h"
28 #include "winbindd.h"
29 #include "tdb_validate.h"
30 #include "../libcli/auth/libcli_auth.h"
31 #include "../librpc/gen_ndr/ndr_winbind.h"
32 #include "ads.h"
33 #include "nss_info.h"
34 #include "../libcli/security/security.h"
35 #include "passdb/machine_sid.h"
36 #include "util_tdb.h"
38 #undef DBGC_CLASS
39 #define DBGC_CLASS DBGC_WINBIND
41 #define WINBINDD_CACHE_VER1 1 /* initial db version */
42 #define WINBINDD_CACHE_VER2 2 /* second version with timeouts for NDR entries */
44 #define WINBINDD_CACHE_VERSION WINBINDD_CACHE_VER2
45 #define WINBINDD_CACHE_VERSION_KEYSTR "WINBINDD_CACHE_VERSION"
47 extern struct winbindd_methods reconnect_methods;
48 #ifdef HAVE_ADS
49 extern struct winbindd_methods ads_methods;
50 #endif
51 extern struct winbindd_methods builtin_passdb_methods;
52 extern struct winbindd_methods sam_passdb_methods;
55 * JRA. KEEP THIS LIST UP TO DATE IF YOU ADD CACHE ENTRIES.
56 * Here are the list of entry types that are *not* stored
57 * as form struct cache_entry in the cache.
60 static const char *non_centry_keys[] = {
61 "SEQNUM/",
62 "WINBINDD_OFFLINE",
63 WINBINDD_CACHE_VERSION_KEYSTR,
64 NULL
67 /************************************************************************
68 Is this key a non-centry type ?
69 ************************************************************************/
71 static bool is_non_centry_key(TDB_DATA kbuf)
73 int i;
75 if (kbuf.dptr == NULL || kbuf.dsize == 0) {
76 return false;
78 for (i = 0; non_centry_keys[i] != NULL; i++) {
79 size_t namelen = strlen(non_centry_keys[i]);
80 if (kbuf.dsize < namelen) {
81 continue;
83 if (strncmp(non_centry_keys[i], (const char *)kbuf.dptr, namelen) == 0) {
84 return true;
87 return false;
90 /* Global online/offline state - False when online. winbindd starts up online
91 and sets this to true if the first query fails and there's an entry in
92 the cache tdb telling us to stay offline. */
94 static bool global_winbindd_offline_state;
96 struct winbind_cache {
97 TDB_CONTEXT *tdb;
100 struct cache_entry {
101 NTSTATUS status;
102 uint32 sequence_number;
103 uint64_t timeout;
104 uint8 *data;
105 uint32 len, ofs;
108 void (*smb_panic_fn)(const char *const why) = smb_panic;
110 #define WINBINDD_MAX_CACHE_SIZE (50*1024*1024)
112 static struct winbind_cache *wcache;
114 /* get the winbind_cache structure */
115 static struct winbind_cache *get_cache(struct winbindd_domain *domain)
117 struct winbind_cache *ret = wcache;
119 /* We have to know what type of domain we are dealing with first. */
121 if (domain->internal) {
122 domain->backend = &builtin_passdb_methods;
125 if (dom_sid_equal(&domain->sid, &global_sid_Builtin)) {
126 domain->initialized = true;
129 if (strequal(domain->name, get_global_sam_name()) &&
130 sid_check_is_our_sam(&domain->sid)) {
131 domain->backend = &sam_passdb_methods;
134 if ( !domain->initialized ) {
135 init_dc_connection( domain );
139 OK. listen up becasue I'm only going to say this once.
140 We have the following scenarios to consider
141 (a) trusted AD domains on a Samba DC,
142 (b) trusted AD domains and we are joined to a non-kerberos domain
143 (c) trusted AD domains and we are joined to a kerberos (AD) domain
145 For (a) we can always contact the trusted domain using krb5
146 since we have the domain trust account password
148 For (b) we can only use RPC since we have no way of
149 getting a krb5 ticket in our own domain
151 For (c) we can always use krb5 since we have a kerberos trust
153 --jerry
156 if (!domain->backend) {
157 #ifdef HAVE_ADS
158 struct winbindd_domain *our_domain = domain;
160 /* find our domain first so we can figure out if we
161 are joined to a kerberized domain */
163 if ( !domain->primary )
164 our_domain = find_our_domain();
166 if ((our_domain->active_directory || IS_DC)
167 && domain->active_directory
168 && !lp_winbind_rpc_only()) {
169 DEBUG(5,("get_cache: Setting ADS methods for domain %s\n", domain->name));
170 domain->backend = &ads_methods;
171 } else {
172 #endif /* HAVE_ADS */
173 DEBUG(5,("get_cache: Setting MS-RPC methods for domain %s\n", domain->name));
174 domain->backend = &reconnect_methods;
175 #ifdef HAVE_ADS
177 #endif /* HAVE_ADS */
180 if (ret)
181 return ret;
183 ret = SMB_XMALLOC_P(struct winbind_cache);
184 ZERO_STRUCTP(ret);
186 wcache = ret;
187 wcache_flush_cache();
189 return ret;
193 free a centry structure
195 static void centry_free(struct cache_entry *centry)
197 if (!centry)
198 return;
199 SAFE_FREE(centry->data);
200 free(centry);
203 static bool centry_check_bytes(struct cache_entry *centry, size_t nbytes)
205 if (centry->len - centry->ofs < nbytes) {
206 DEBUG(0,("centry corruption? needed %u bytes, have %d\n",
207 (unsigned int)nbytes,
208 centry->len - centry->ofs));
209 return false;
211 return true;
215 pull a uint64_t from a cache entry
217 static uint64_t centry_uint64_t(struct cache_entry *centry)
219 uint64_t ret;
221 if (!centry_check_bytes(centry, 8)) {
222 smb_panic_fn("centry_uint64_t");
224 ret = BVAL(centry->data, centry->ofs);
225 centry->ofs += 8;
226 return ret;
230 pull a uint32 from a cache entry
232 static uint32 centry_uint32(struct cache_entry *centry)
234 uint32 ret;
236 if (!centry_check_bytes(centry, 4)) {
237 smb_panic_fn("centry_uint32");
239 ret = IVAL(centry->data, centry->ofs);
240 centry->ofs += 4;
241 return ret;
245 pull a uint16 from a cache entry
247 static uint16 centry_uint16(struct cache_entry *centry)
249 uint16 ret;
250 if (!centry_check_bytes(centry, 2)) {
251 smb_panic_fn("centry_uint16");
253 ret = SVAL(centry->data, centry->ofs);
254 centry->ofs += 2;
255 return ret;
259 pull a uint8 from a cache entry
261 static uint8 centry_uint8(struct cache_entry *centry)
263 uint8 ret;
264 if (!centry_check_bytes(centry, 1)) {
265 smb_panic_fn("centry_uint8");
267 ret = CVAL(centry->data, centry->ofs);
268 centry->ofs += 1;
269 return ret;
273 pull a NTTIME from a cache entry
275 static NTTIME centry_nttime(struct cache_entry *centry)
277 NTTIME ret;
278 if (!centry_check_bytes(centry, 8)) {
279 smb_panic_fn("centry_nttime");
281 ret = IVAL(centry->data, centry->ofs);
282 centry->ofs += 4;
283 ret += (uint64)IVAL(centry->data, centry->ofs) << 32;
284 centry->ofs += 4;
285 return ret;
289 pull a time_t from a cache entry. time_t stored portably as a 64-bit time.
291 static time_t centry_time(struct cache_entry *centry)
293 return (time_t)centry_nttime(centry);
296 /* pull a string from a cache entry, using the supplied
297 talloc context
299 static char *centry_string(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
301 uint32 len;
302 char *ret;
304 len = centry_uint8(centry);
306 if (len == 0xFF) {
307 /* a deliberate NULL string */
308 return NULL;
311 if (!centry_check_bytes(centry, (size_t)len)) {
312 smb_panic_fn("centry_string");
315 ret = talloc_array(mem_ctx, char, len+1);
316 if (!ret) {
317 smb_panic_fn("centry_string out of memory\n");
319 memcpy(ret,centry->data + centry->ofs, len);
320 ret[len] = 0;
321 centry->ofs += len;
322 return ret;
325 /* pull a hash16 from a cache entry, using the supplied
326 talloc context
328 static char *centry_hash16(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
330 uint32 len;
331 char *ret;
333 len = centry_uint8(centry);
335 if (len != 16) {
336 DEBUG(0,("centry corruption? hash len (%u) != 16\n",
337 len ));
338 return NULL;
341 if (!centry_check_bytes(centry, 16)) {
342 return NULL;
345 ret = talloc_array(mem_ctx, char, 16);
346 if (!ret) {
347 smb_panic_fn("centry_hash out of memory\n");
349 memcpy(ret,centry->data + centry->ofs, 16);
350 centry->ofs += 16;
351 return ret;
354 /* pull a sid from a cache entry, using the supplied
355 talloc context
357 static bool centry_sid(struct cache_entry *centry, struct dom_sid *sid)
359 char *sid_string;
360 bool ret;
362 sid_string = centry_string(centry, talloc_tos());
363 if (sid_string == NULL) {
364 return false;
366 ret = string_to_sid(sid, sid_string);
367 TALLOC_FREE(sid_string);
368 return ret;
373 pull a NTSTATUS from a cache entry
375 static NTSTATUS centry_ntstatus(struct cache_entry *centry)
377 NTSTATUS status;
379 status = NT_STATUS(centry_uint32(centry));
380 return status;
384 /* the server is considered down if it can't give us a sequence number */
385 static bool wcache_server_down(struct winbindd_domain *domain)
387 bool ret;
389 if (!wcache->tdb)
390 return false;
392 ret = (domain->sequence_number == DOM_SEQUENCE_NONE);
394 if (ret)
395 DEBUG(10,("wcache_server_down: server for Domain %s down\n",
396 domain->name ));
397 return ret;
400 static bool wcache_fetch_seqnum(const char *domain_name, uint32_t *seqnum,
401 uint32_t *last_seq_check)
403 char *key;
404 TDB_DATA data;
406 if (wcache->tdb == NULL) {
407 DEBUG(10,("wcache_fetch_seqnum: tdb == NULL\n"));
408 return false;
411 key = talloc_asprintf(talloc_tos(), "SEQNUM/%s", domain_name);
412 if (key == NULL) {
413 DEBUG(10, ("talloc failed\n"));
414 return false;
417 data = tdb_fetch_bystring(wcache->tdb, key);
418 TALLOC_FREE(key);
420 if (data.dptr == NULL) {
421 DEBUG(10, ("wcache_fetch_seqnum: %s not found\n",
422 domain_name));
423 return false;
425 if (data.dsize != 8) {
426 DEBUG(10, ("wcache_fetch_seqnum: invalid data size %d\n",
427 (int)data.dsize));
428 SAFE_FREE(data.dptr);
429 return false;
432 *seqnum = IVAL(data.dptr, 0);
433 *last_seq_check = IVAL(data.dptr, 4);
434 SAFE_FREE(data.dptr);
436 return true;
439 static NTSTATUS fetch_cache_seqnum( struct winbindd_domain *domain, time_t now )
441 uint32 last_check, time_diff;
443 if (!wcache_fetch_seqnum(domain->name, &domain->sequence_number,
444 &last_check)) {
445 return NT_STATUS_UNSUCCESSFUL;
447 domain->last_seq_check = last_check;
449 /* have we expired? */
451 time_diff = now - domain->last_seq_check;
452 if ( time_diff > lp_winbind_cache_time() ) {
453 DEBUG(10,("fetch_cache_seqnum: timeout [%s][%u @ %u]\n",
454 domain->name, domain->sequence_number,
455 (uint32)domain->last_seq_check));
456 return NT_STATUS_UNSUCCESSFUL;
459 DEBUG(10,("fetch_cache_seqnum: success [%s][%u @ %u]\n",
460 domain->name, domain->sequence_number,
461 (uint32)domain->last_seq_check));
463 return NT_STATUS_OK;
466 bool wcache_store_seqnum(const char *domain_name, uint32_t seqnum,
467 time_t last_seq_check)
469 char *key_str;
470 uint8_t buf[8];
471 int ret;
473 if (wcache->tdb == NULL) {
474 DEBUG(10, ("wcache_store_seqnum: wcache->tdb == NULL\n"));
475 return false;
478 key_str = talloc_asprintf(talloc_tos(), "SEQNUM/%s", domain_name);
479 if (key_str == NULL) {
480 DEBUG(10, ("talloc_asprintf failed\n"));
481 return false;
484 SIVAL(buf, 0, seqnum);
485 SIVAL(buf, 4, last_seq_check);
487 ret = tdb_store_bystring(wcache->tdb, key_str,
488 make_tdb_data(buf, sizeof(buf)), TDB_REPLACE);
489 TALLOC_FREE(key_str);
490 if (ret != 0) {
491 DEBUG(10, ("tdb_store_bystring failed: %s\n",
492 tdb_errorstr_compat(wcache->tdb)));
493 TALLOC_FREE(key_str);
494 return false;
497 DEBUG(10, ("wcache_store_seqnum: success [%s][%u @ %u]\n",
498 domain_name, seqnum, (unsigned)last_seq_check));
500 return true;
503 static bool store_cache_seqnum( struct winbindd_domain *domain )
505 return wcache_store_seqnum(domain->name, domain->sequence_number,
506 domain->last_seq_check);
510 refresh the domain sequence number. If force is true
511 then always refresh it, no matter how recently we fetched it
514 static void refresh_sequence_number(struct winbindd_domain *domain, bool force)
516 NTSTATUS status;
517 unsigned time_diff;
518 time_t t = time(NULL);
519 unsigned cache_time = lp_winbind_cache_time();
521 if (is_domain_offline(domain)) {
522 return;
525 get_cache( domain );
527 #if 0 /* JERRY -- disable as the default cache time is now 5 minutes */
528 /* trying to reconnect is expensive, don't do it too often */
529 if (domain->sequence_number == DOM_SEQUENCE_NONE) {
530 cache_time *= 8;
532 #endif
534 time_diff = t - domain->last_seq_check;
536 /* see if we have to refetch the domain sequence number */
537 if (!force && (time_diff < cache_time) &&
538 (domain->sequence_number != DOM_SEQUENCE_NONE) &&
539 NT_STATUS_IS_OK(domain->last_status)) {
540 DEBUG(10, ("refresh_sequence_number: %s time ok\n", domain->name));
541 goto done;
544 /* try to get the sequence number from the tdb cache first */
545 /* this will update the timestamp as well */
547 status = fetch_cache_seqnum( domain, t );
548 if (NT_STATUS_IS_OK(status) &&
549 (domain->sequence_number != DOM_SEQUENCE_NONE) &&
550 NT_STATUS_IS_OK(domain->last_status)) {
551 goto done;
554 /* important! make sure that we know if this is a native
555 mode domain or not. And that we can contact it. */
557 if ( winbindd_can_contact_domain( domain ) ) {
558 status = domain->backend->sequence_number(domain,
559 &domain->sequence_number);
560 } else {
561 /* just use the current time */
562 status = NT_STATUS_OK;
563 domain->sequence_number = time(NULL);
567 /* the above call could have set our domain->backend to NULL when
568 * coming from offline to online mode, make sure to reinitialize the
569 * backend - Guenther */
570 get_cache( domain );
572 if (!NT_STATUS_IS_OK(status)) {
573 DEBUG(10,("refresh_sequence_number: failed with %s\n", nt_errstr(status)));
574 domain->sequence_number = DOM_SEQUENCE_NONE;
577 domain->last_status = status;
578 domain->last_seq_check = time(NULL);
580 /* save the new sequence number in the cache */
581 store_cache_seqnum( domain );
583 done:
584 DEBUG(10, ("refresh_sequence_number: %s seq number is now %d\n",
585 domain->name, domain->sequence_number));
587 return;
591 decide if a cache entry has expired
593 static bool centry_expired(struct winbindd_domain *domain, const char *keystr, struct cache_entry *centry)
595 /* If we've been told to be offline - stay in that state... */
596 if (lp_winbind_offline_logon() && global_winbindd_offline_state) {
597 DEBUG(10,("centry_expired: Key %s for domain %s valid as winbindd is globally offline.\n",
598 keystr, domain->name ));
599 return false;
602 /* when the domain is offline return the cached entry.
603 * This deals with transient offline states... */
605 if (!domain->online) {
606 DEBUG(10,("centry_expired: Key %s for domain %s valid as domain is offline.\n",
607 keystr, domain->name ));
608 return false;
611 /* if the server is OK and our cache entry came from when it was down then
612 the entry is invalid */
613 if ((domain->sequence_number != DOM_SEQUENCE_NONE) &&
614 (centry->sequence_number == DOM_SEQUENCE_NONE)) {
615 DEBUG(10,("centry_expired: Key %s for domain %s invalid sequence.\n",
616 keystr, domain->name ));
617 return true;
620 /* if the server is down or the cache entry is not older than the
621 current sequence number or it did not timeout then it is OK */
622 if (wcache_server_down(domain)
623 || ((centry->sequence_number == domain->sequence_number)
624 && (centry->timeout > time(NULL)))) {
625 DEBUG(10,("centry_expired: Key %s for domain %s is good.\n",
626 keystr, domain->name ));
627 return false;
630 DEBUG(10,("centry_expired: Key %s for domain %s expired\n",
631 keystr, domain->name ));
633 /* it's expired */
634 return true;
637 static struct cache_entry *wcache_fetch_raw(char *kstr)
639 TDB_DATA data;
640 struct cache_entry *centry;
641 TDB_DATA key;
643 key = string_tdb_data(kstr);
644 data = tdb_fetch_compat(wcache->tdb, key);
645 if (!data.dptr) {
646 /* a cache miss */
647 return NULL;
650 centry = SMB_XMALLOC_P(struct cache_entry);
651 centry->data = (unsigned char *)data.dptr;
652 centry->len = data.dsize;
653 centry->ofs = 0;
655 if (centry->len < 16) {
656 /* huh? corrupt cache? */
657 DEBUG(10,("wcache_fetch_raw: Corrupt cache for key %s "
658 "(len < 16)?\n", kstr));
659 centry_free(centry);
660 return NULL;
663 centry->status = centry_ntstatus(centry);
664 centry->sequence_number = centry_uint32(centry);
665 centry->timeout = centry_uint64_t(centry);
667 return centry;
670 static bool is_my_own_sam_domain(struct winbindd_domain *domain)
672 if (strequal(domain->name, get_global_sam_name()) &&
673 sid_check_is_our_sam(&domain->sid)) {
674 return true;
677 return false;
680 static bool is_builtin_domain(struct winbindd_domain *domain)
682 if (strequal(domain->name, "BUILTIN") &&
683 sid_check_is_builtin(&domain->sid)) {
684 return true;
687 return false;
691 fetch an entry from the cache, with a varargs key. auto-fetch the sequence
692 number and return status
694 static struct cache_entry *wcache_fetch(struct winbind_cache *cache,
695 struct winbindd_domain *domain,
696 const char *format, ...) PRINTF_ATTRIBUTE(3,4);
697 static struct cache_entry *wcache_fetch(struct winbind_cache *cache,
698 struct winbindd_domain *domain,
699 const char *format, ...)
701 va_list ap;
702 char *kstr;
703 struct cache_entry *centry;
705 if (!winbindd_use_cache() ||
706 is_my_own_sam_domain(domain) ||
707 is_builtin_domain(domain)) {
708 return NULL;
711 refresh_sequence_number(domain, false);
713 va_start(ap, format);
714 smb_xvasprintf(&kstr, format, ap);
715 va_end(ap);
717 centry = wcache_fetch_raw(kstr);
718 if (centry == NULL) {
719 free(kstr);
720 return NULL;
723 if (centry_expired(domain, kstr, centry)) {
725 DEBUG(10,("wcache_fetch: entry %s expired for domain %s\n",
726 kstr, domain->name ));
728 centry_free(centry);
729 free(kstr);
730 return NULL;
733 DEBUG(10,("wcache_fetch: returning entry %s for domain %s\n",
734 kstr, domain->name ));
736 free(kstr);
737 return centry;
740 static void wcache_delete(const char *format, ...) PRINTF_ATTRIBUTE(1,2);
741 static void wcache_delete(const char *format, ...)
743 va_list ap;
744 char *kstr;
745 TDB_DATA key;
747 va_start(ap, format);
748 smb_xvasprintf(&kstr, format, ap);
749 va_end(ap);
751 key = string_tdb_data(kstr);
753 tdb_delete(wcache->tdb, key);
754 free(kstr);
758 make sure we have at least len bytes available in a centry
760 static void centry_expand(struct cache_entry *centry, uint32 len)
762 if (centry->len - centry->ofs >= len)
763 return;
764 centry->len *= 2;
765 centry->data = SMB_REALLOC_ARRAY(centry->data, unsigned char,
766 centry->len);
767 if (!centry->data) {
768 DEBUG(0,("out of memory: needed %d bytes in centry_expand\n", centry->len));
769 smb_panic_fn("out of memory in centry_expand");
774 push a uint64_t into a centry
776 static void centry_put_uint64_t(struct cache_entry *centry, uint64_t v)
778 centry_expand(centry, 8);
779 SBVAL(centry->data, centry->ofs, v);
780 centry->ofs += 8;
784 push a uint32 into a centry
786 static void centry_put_uint32(struct cache_entry *centry, uint32 v)
788 centry_expand(centry, 4);
789 SIVAL(centry->data, centry->ofs, v);
790 centry->ofs += 4;
794 push a uint16 into a centry
796 static void centry_put_uint16(struct cache_entry *centry, uint16 v)
798 centry_expand(centry, 2);
799 SSVAL(centry->data, centry->ofs, v);
800 centry->ofs += 2;
804 push a uint8 into a centry
806 static void centry_put_uint8(struct cache_entry *centry, uint8 v)
808 centry_expand(centry, 1);
809 SCVAL(centry->data, centry->ofs, v);
810 centry->ofs += 1;
814 push a string into a centry
816 static void centry_put_string(struct cache_entry *centry, const char *s)
818 int len;
820 if (!s) {
821 /* null strings are marked as len 0xFFFF */
822 centry_put_uint8(centry, 0xFF);
823 return;
826 len = strlen(s);
827 /* can't handle more than 254 char strings. Truncating is probably best */
828 if (len > 254) {
829 DEBUG(10,("centry_put_string: truncating len (%d) to: 254\n", len));
830 len = 254;
832 centry_put_uint8(centry, len);
833 centry_expand(centry, len);
834 memcpy(centry->data + centry->ofs, s, len);
835 centry->ofs += len;
839 push a 16 byte hash into a centry - treat as 16 byte string.
841 static void centry_put_hash16(struct cache_entry *centry, const uint8 val[16])
843 centry_put_uint8(centry, 16);
844 centry_expand(centry, 16);
845 memcpy(centry->data + centry->ofs, val, 16);
846 centry->ofs += 16;
849 static void centry_put_sid(struct cache_entry *centry, const struct dom_sid *sid)
851 fstring sid_string;
852 centry_put_string(centry, sid_to_fstring(sid_string, sid));
857 put NTSTATUS into a centry
859 static void centry_put_ntstatus(struct cache_entry *centry, NTSTATUS status)
861 uint32 status_value = NT_STATUS_V(status);
862 centry_put_uint32(centry, status_value);
867 push a NTTIME into a centry
869 static void centry_put_nttime(struct cache_entry *centry, NTTIME nt)
871 centry_expand(centry, 8);
872 SIVAL(centry->data, centry->ofs, nt & 0xFFFFFFFF);
873 centry->ofs += 4;
874 SIVAL(centry->data, centry->ofs, nt >> 32);
875 centry->ofs += 4;
879 push a time_t into a centry - use a 64 bit size.
880 NTTIME here is being used as a convenient 64-bit size.
882 static void centry_put_time(struct cache_entry *centry, time_t t)
884 NTTIME nt = (NTTIME)t;
885 centry_put_nttime(centry, nt);
889 start a centry for output. When finished, call centry_end()
891 static struct cache_entry *centry_start(struct winbindd_domain *domain,
892 NTSTATUS status)
894 struct cache_entry *centry;
896 if (!wcache->tdb)
897 return NULL;
899 centry = SMB_XMALLOC_P(struct cache_entry);
901 centry->len = 8192; /* reasonable default */
902 centry->data = SMB_XMALLOC_ARRAY(uint8, centry->len);
903 centry->ofs = 0;
904 centry->sequence_number = domain->sequence_number;
905 centry->timeout = lp_winbind_cache_time() + time(NULL);
906 centry_put_ntstatus(centry, status);
907 centry_put_uint32(centry, centry->sequence_number);
908 centry_put_uint64_t(centry, centry->timeout);
909 return centry;
913 finish a centry and write it to the tdb
915 static void centry_end(struct cache_entry *centry, const char *format, ...) PRINTF_ATTRIBUTE(2,3);
916 static void centry_end(struct cache_entry *centry, const char *format, ...)
918 va_list ap;
919 char *kstr;
920 TDB_DATA key, data;
922 if (!winbindd_use_cache()) {
923 return;
926 va_start(ap, format);
927 smb_xvasprintf(&kstr, format, ap);
928 va_end(ap);
930 key = string_tdb_data(kstr);
931 data.dptr = centry->data;
932 data.dsize = centry->ofs;
934 tdb_store(wcache->tdb, key, data, TDB_REPLACE);
935 free(kstr);
938 static void wcache_save_name_to_sid(struct winbindd_domain *domain,
939 NTSTATUS status, const char *domain_name,
940 const char *name, const struct dom_sid *sid,
941 enum lsa_SidType type)
943 struct cache_entry *centry;
944 fstring uname;
946 centry = centry_start(domain, status);
947 if (!centry)
948 return;
950 if ((domain_name == NULL) || (domain_name[0] == '\0')) {
951 struct winbindd_domain *mydomain =
952 find_domain_from_sid_noinit(sid);
953 if (mydomain != NULL) {
954 domain_name = mydomain->name;
958 centry_put_uint32(centry, type);
959 centry_put_sid(centry, sid);
960 fstrcpy(uname, name);
961 (void)strupper_m(uname);
962 centry_end(centry, "NS/%s/%s", domain_name, uname);
963 DEBUG(10,("wcache_save_name_to_sid: %s\\%s -> %s (%s)\n", domain_name,
964 uname, sid_string_dbg(sid), nt_errstr(status)));
965 centry_free(centry);
968 static void wcache_save_sid_to_name(struct winbindd_domain *domain, NTSTATUS status,
969 const struct dom_sid *sid, const char *domain_name, const char *name, enum lsa_SidType type)
971 struct cache_entry *centry;
972 fstring sid_string;
974 centry = centry_start(domain, status);
975 if (!centry)
976 return;
978 if ((domain_name == NULL) || (domain_name[0] == '\0')) {
979 struct winbindd_domain *mydomain =
980 find_domain_from_sid_noinit(sid);
981 if (mydomain != NULL) {
982 domain_name = mydomain->name;
986 if (NT_STATUS_IS_OK(status)) {
987 centry_put_uint32(centry, type);
988 centry_put_string(centry, domain_name);
989 centry_put_string(centry, name);
992 centry_end(centry, "SN/%s", sid_to_fstring(sid_string, sid));
993 DEBUG(10,("wcache_save_sid_to_name: %s -> %s\\%s (%s)\n", sid_string,
994 domain_name, name, nt_errstr(status)));
995 centry_free(centry);
999 static void wcache_save_user(struct winbindd_domain *domain, NTSTATUS status,
1000 struct wbint_userinfo *info)
1002 struct cache_entry *centry;
1003 fstring sid_string;
1005 if (is_null_sid(&info->user_sid)) {
1006 return;
1009 centry = centry_start(domain, status);
1010 if (!centry)
1011 return;
1012 centry_put_string(centry, info->acct_name);
1013 centry_put_string(centry, info->full_name);
1014 centry_put_string(centry, info->homedir);
1015 centry_put_string(centry, info->shell);
1016 centry_put_uint32(centry, info->primary_gid);
1017 centry_put_sid(centry, &info->user_sid);
1018 centry_put_sid(centry, &info->group_sid);
1019 centry_end(centry, "U/%s", sid_to_fstring(sid_string,
1020 &info->user_sid));
1021 DEBUG(10,("wcache_save_user: %s (acct_name %s)\n", sid_string, info->acct_name));
1022 centry_free(centry);
1025 static void wcache_save_lockout_policy(struct winbindd_domain *domain,
1026 NTSTATUS status,
1027 struct samr_DomInfo12 *lockout_policy)
1029 struct cache_entry *centry;
1031 centry = centry_start(domain, status);
1032 if (!centry)
1033 return;
1035 centry_put_nttime(centry, lockout_policy->lockout_duration);
1036 centry_put_nttime(centry, lockout_policy->lockout_window);
1037 centry_put_uint16(centry, lockout_policy->lockout_threshold);
1039 centry_end(centry, "LOC_POL/%s", domain->name);
1041 DEBUG(10,("wcache_save_lockout_policy: %s\n", domain->name));
1043 centry_free(centry);
1048 static void wcache_save_password_policy(struct winbindd_domain *domain,
1049 NTSTATUS status,
1050 struct samr_DomInfo1 *policy)
1052 struct cache_entry *centry;
1054 centry = centry_start(domain, status);
1055 if (!centry)
1056 return;
1058 centry_put_uint16(centry, policy->min_password_length);
1059 centry_put_uint16(centry, policy->password_history_length);
1060 centry_put_uint32(centry, policy->password_properties);
1061 centry_put_nttime(centry, policy->max_password_age);
1062 centry_put_nttime(centry, policy->min_password_age);
1064 centry_end(centry, "PWD_POL/%s", domain->name);
1066 DEBUG(10,("wcache_save_password_policy: %s\n", domain->name));
1068 centry_free(centry);
1071 /***************************************************************************
1072 ***************************************************************************/
1074 static void wcache_save_username_alias(struct winbindd_domain *domain,
1075 NTSTATUS status,
1076 const char *name, const char *alias)
1078 struct cache_entry *centry;
1079 fstring uname;
1081 if ( (centry = centry_start(domain, status)) == NULL )
1082 return;
1084 centry_put_string( centry, alias );
1086 fstrcpy(uname, name);
1087 (void)strupper_m(uname);
1088 centry_end(centry, "NSS/NA/%s", uname);
1090 DEBUG(10,("wcache_save_username_alias: %s -> %s\n", name, alias ));
1092 centry_free(centry);
1095 static void wcache_save_alias_username(struct winbindd_domain *domain,
1096 NTSTATUS status,
1097 const char *alias, const char *name)
1099 struct cache_entry *centry;
1100 fstring uname;
1102 if ( (centry = centry_start(domain, status)) == NULL )
1103 return;
1105 centry_put_string( centry, name );
1107 fstrcpy(uname, alias);
1108 (void)strupper_m(uname);
1109 centry_end(centry, "NSS/AN/%s", uname);
1111 DEBUG(10,("wcache_save_alias_username: %s -> %s\n", alias, name ));
1113 centry_free(centry);
1116 /***************************************************************************
1117 ***************************************************************************/
1119 NTSTATUS resolve_username_to_alias( TALLOC_CTX *mem_ctx,
1120 struct winbindd_domain *domain,
1121 const char *name, char **alias )
1123 struct winbind_cache *cache = get_cache(domain);
1124 struct cache_entry *centry = NULL;
1125 NTSTATUS status;
1126 char *upper_name;
1128 if ( domain->internal )
1129 return NT_STATUS_NOT_SUPPORTED;
1131 if (!cache->tdb)
1132 goto do_query;
1134 upper_name = talloc_strdup(mem_ctx, name);
1135 if (upper_name == NULL) {
1136 return NT_STATUS_NO_MEMORY;
1138 if (!strupper_m(upper_name)) {
1139 talloc_free(upper_name);
1140 return NT_STATUS_INVALID_PARAMETER;
1143 centry = wcache_fetch(cache, domain, "NSS/NA/%s", upper_name);
1145 talloc_free(upper_name);
1147 if (!centry)
1148 goto do_query;
1150 status = centry->status;
1152 if (!NT_STATUS_IS_OK(status)) {
1153 centry_free(centry);
1154 return status;
1157 *alias = centry_string( centry, mem_ctx );
1159 centry_free(centry);
1161 DEBUG(10,("resolve_username_to_alias: [Cached] - mapped %s to %s\n",
1162 name, *alias ? *alias : "(none)"));
1164 return (*alias) ? NT_STATUS_OK : NT_STATUS_OBJECT_NAME_NOT_FOUND;
1166 do_query:
1168 /* If its not in cache and we are offline, then fail */
1170 if ( get_global_winbindd_state_offline() || !domain->online ) {
1171 DEBUG(8,("resolve_username_to_alias: rejecting query "
1172 "in offline mode\n"));
1173 return NT_STATUS_NOT_FOUND;
1176 status = nss_map_to_alias( mem_ctx, domain->name, name, alias );
1178 if ( NT_STATUS_IS_OK( status ) ) {
1179 wcache_save_username_alias(domain, status, name, *alias);
1182 if ( NT_STATUS_EQUAL( status, NT_STATUS_NONE_MAPPED ) ) {
1183 wcache_save_username_alias(domain, status, name, "(NULL)");
1186 DEBUG(5,("resolve_username_to_alias: backend query returned %s\n",
1187 nt_errstr(status)));
1189 if ( NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ) {
1190 set_domain_offline( domain );
1193 return status;
1196 /***************************************************************************
1197 ***************************************************************************/
1199 NTSTATUS resolve_alias_to_username( TALLOC_CTX *mem_ctx,
1200 struct winbindd_domain *domain,
1201 const char *alias, char **name )
1203 struct winbind_cache *cache = get_cache(domain);
1204 struct cache_entry *centry = NULL;
1205 NTSTATUS status;
1206 char *upper_name;
1208 if ( domain->internal )
1209 return NT_STATUS_NOT_SUPPORTED;
1211 if (!cache->tdb)
1212 goto do_query;
1214 upper_name = talloc_strdup(mem_ctx, alias);
1215 if (upper_name == NULL) {
1216 return NT_STATUS_NO_MEMORY;
1218 if (!strupper_m(upper_name)) {
1219 talloc_free(upper_name);
1220 return NT_STATUS_INVALID_PARAMETER;
1223 centry = wcache_fetch(cache, domain, "NSS/AN/%s", upper_name);
1225 talloc_free(upper_name);
1227 if (!centry)
1228 goto do_query;
1230 status = centry->status;
1232 if (!NT_STATUS_IS_OK(status)) {
1233 centry_free(centry);
1234 return status;
1237 *name = centry_string( centry, mem_ctx );
1239 centry_free(centry);
1241 DEBUG(10,("resolve_alias_to_username: [Cached] - mapped %s to %s\n",
1242 alias, *name ? *name : "(none)"));
1244 return (*name) ? NT_STATUS_OK : NT_STATUS_OBJECT_NAME_NOT_FOUND;
1246 do_query:
1248 /* If its not in cache and we are offline, then fail */
1250 if ( get_global_winbindd_state_offline() || !domain->online ) {
1251 DEBUG(8,("resolve_alias_to_username: rejecting query "
1252 "in offline mode\n"));
1253 return NT_STATUS_NOT_FOUND;
1256 /* an alias cannot contain a domain prefix or '@' */
1258 if (strchr(alias, '\\') || strchr(alias, '@')) {
1259 DEBUG(10,("resolve_alias_to_username: skipping fully "
1260 "qualified name %s\n", alias));
1261 return NT_STATUS_OBJECT_NAME_INVALID;
1264 status = nss_map_from_alias( mem_ctx, domain->name, alias, name );
1266 if ( NT_STATUS_IS_OK( status ) ) {
1267 wcache_save_alias_username( domain, status, alias, *name );
1270 if (NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) {
1271 wcache_save_alias_username(domain, status, alias, "(NULL)");
1274 DEBUG(5,("resolve_alias_to_username: backend query returned %s\n",
1275 nt_errstr(status)));
1277 if ( NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ) {
1278 set_domain_offline( domain );
1281 return status;
1284 NTSTATUS wcache_cached_creds_exist(struct winbindd_domain *domain, const struct dom_sid *sid)
1286 struct winbind_cache *cache = get_cache(domain);
1287 TDB_DATA data;
1288 fstring key_str, tmp;
1289 uint32 rid;
1291 if (!cache->tdb) {
1292 return NT_STATUS_INTERNAL_DB_ERROR;
1295 if (is_null_sid(sid)) {
1296 return NT_STATUS_INVALID_SID;
1299 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1300 return NT_STATUS_INVALID_SID;
1303 fstr_sprintf(key_str, "CRED/%s", sid_to_fstring(tmp, sid));
1305 data = tdb_fetch_compat(cache->tdb, string_tdb_data(key_str));
1306 if (!data.dptr) {
1307 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1310 SAFE_FREE(data.dptr);
1311 return NT_STATUS_OK;
1314 /* Lookup creds for a SID - copes with old (unsalted) creds as well
1315 as new salted ones. */
1317 NTSTATUS wcache_get_creds(struct winbindd_domain *domain,
1318 TALLOC_CTX *mem_ctx,
1319 const struct dom_sid *sid,
1320 const uint8 **cached_nt_pass,
1321 const uint8 **cached_salt)
1323 struct winbind_cache *cache = get_cache(domain);
1324 struct cache_entry *centry = NULL;
1325 NTSTATUS status;
1326 uint32 rid;
1327 fstring tmp;
1329 if (!cache->tdb) {
1330 return NT_STATUS_INTERNAL_DB_ERROR;
1333 if (is_null_sid(sid)) {
1334 return NT_STATUS_INVALID_SID;
1337 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1338 return NT_STATUS_INVALID_SID;
1341 /* Try and get a salted cred first. If we can't
1342 fall back to an unsalted cred. */
1344 centry = wcache_fetch(cache, domain, "CRED/%s",
1345 sid_to_fstring(tmp, sid));
1346 if (!centry) {
1347 DEBUG(10,("wcache_get_creds: entry for [CRED/%s] not found\n",
1348 sid_string_dbg(sid)));
1349 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1353 * We don't use the time element at this moment,
1354 * but we have to consume it, so that we don't
1355 * neet to change the disk format of the cache.
1357 (void)centry_time(centry);
1359 /* In the salted case this isn't actually the nt_hash itself,
1360 but the MD5 of the salt + nt_hash. Let the caller
1361 sort this out. It can tell as we only return the cached_salt
1362 if we are returning a salted cred. */
1364 *cached_nt_pass = (const uint8 *)centry_hash16(centry, mem_ctx);
1365 if (*cached_nt_pass == NULL) {
1366 fstring sidstr;
1368 sid_to_fstring(sidstr, sid);
1370 /* Bad (old) cred cache. Delete and pretend we
1371 don't have it. */
1372 DEBUG(0,("wcache_get_creds: bad entry for [CRED/%s] - deleting\n",
1373 sidstr));
1374 wcache_delete("CRED/%s", sidstr);
1375 centry_free(centry);
1376 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1379 /* We only have 17 bytes more data in the salted cred case. */
1380 if (centry->len - centry->ofs == 17) {
1381 *cached_salt = (const uint8 *)centry_hash16(centry, mem_ctx);
1382 } else {
1383 *cached_salt = NULL;
1386 dump_data_pw("cached_nt_pass", *cached_nt_pass, NT_HASH_LEN);
1387 if (*cached_salt) {
1388 dump_data_pw("cached_salt", *cached_salt, NT_HASH_LEN);
1391 status = centry->status;
1393 DEBUG(10,("wcache_get_creds: [Cached] - cached creds for user %s status: %s\n",
1394 sid_string_dbg(sid), nt_errstr(status) ));
1396 centry_free(centry);
1397 return status;
1400 /* Store creds for a SID - only writes out new salted ones. */
1402 NTSTATUS wcache_save_creds(struct winbindd_domain *domain,
1403 const struct dom_sid *sid,
1404 const uint8 nt_pass[NT_HASH_LEN])
1406 struct cache_entry *centry;
1407 fstring sid_string;
1408 uint32 rid;
1409 uint8 cred_salt[NT_HASH_LEN];
1410 uint8 salted_hash[NT_HASH_LEN];
1412 if (is_null_sid(sid)) {
1413 return NT_STATUS_INVALID_SID;
1416 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1417 return NT_STATUS_INVALID_SID;
1420 centry = centry_start(domain, NT_STATUS_OK);
1421 if (!centry) {
1422 return NT_STATUS_INTERNAL_DB_ERROR;
1425 dump_data_pw("nt_pass", nt_pass, NT_HASH_LEN);
1427 centry_put_time(centry, time(NULL));
1429 /* Create a salt and then salt the hash. */
1430 generate_random_buffer(cred_salt, NT_HASH_LEN);
1431 E_md5hash(cred_salt, nt_pass, salted_hash);
1433 centry_put_hash16(centry, salted_hash);
1434 centry_put_hash16(centry, cred_salt);
1435 centry_end(centry, "CRED/%s", sid_to_fstring(sid_string, sid));
1437 DEBUG(10,("wcache_save_creds: %s\n", sid_string));
1439 centry_free(centry);
1441 return NT_STATUS_OK;
1445 /* Query display info. This is the basic user list fn */
1446 static NTSTATUS query_user_list(struct winbindd_domain *domain,
1447 TALLOC_CTX *mem_ctx,
1448 uint32 *num_entries,
1449 struct wbint_userinfo **info)
1451 struct winbind_cache *cache = get_cache(domain);
1452 struct cache_entry *centry = NULL;
1453 NTSTATUS status;
1454 unsigned int i, retry;
1455 bool old_status = domain->online;
1457 if (!cache->tdb)
1458 goto do_query;
1460 centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
1461 if (!centry)
1462 goto do_query;
1464 do_fetch_cache:
1465 *num_entries = centry_uint32(centry);
1467 if (*num_entries == 0)
1468 goto do_cached;
1470 (*info) = talloc_array(mem_ctx, struct wbint_userinfo, *num_entries);
1471 if (! (*info)) {
1472 smb_panic_fn("query_user_list out of memory");
1474 for (i=0; i<(*num_entries); i++) {
1475 (*info)[i].acct_name = centry_string(centry, mem_ctx);
1476 (*info)[i].full_name = centry_string(centry, mem_ctx);
1477 (*info)[i].homedir = centry_string(centry, mem_ctx);
1478 (*info)[i].shell = centry_string(centry, mem_ctx);
1479 centry_sid(centry, &(*info)[i].user_sid);
1480 centry_sid(centry, &(*info)[i].group_sid);
1483 do_cached:
1484 status = centry->status;
1486 DEBUG(10,("query_user_list: [Cached] - cached list for domain %s status: %s\n",
1487 domain->name, nt_errstr(status) ));
1489 centry_free(centry);
1490 return status;
1492 do_query:
1493 *num_entries = 0;
1494 *info = NULL;
1496 /* Return status value returned by seq number check */
1498 if (!NT_STATUS_IS_OK(domain->last_status))
1499 return domain->last_status;
1501 /* Put the query_user_list() in a retry loop. There appears to be
1502 * some bug either with Windows 2000 or Samba's handling of large
1503 * rpc replies. This manifests itself as sudden disconnection
1504 * at a random point in the enumeration of a large (60k) user list.
1505 * The retry loop simply tries the operation again. )-: It's not
1506 * pretty but an acceptable workaround until we work out what the
1507 * real problem is. */
1509 retry = 0;
1510 do {
1512 DEBUG(10,("query_user_list: [Cached] - doing backend query for list for domain %s\n",
1513 domain->name ));
1515 status = domain->backend->query_user_list(domain, mem_ctx, num_entries, info);
1516 if (!NT_STATUS_IS_OK(status)) {
1517 DEBUG(3, ("query_user_list: returned 0x%08x, "
1518 "retrying\n", NT_STATUS_V(status)));
1520 if (NT_STATUS_EQUAL(status, NT_STATUS_UNSUCCESSFUL)) {
1521 DEBUG(3, ("query_user_list: flushing "
1522 "connection cache\n"));
1523 invalidate_cm_connection(&domain->conn);
1525 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1526 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1527 if (!domain->internal && old_status) {
1528 set_domain_offline(domain);
1530 /* store partial response. */
1531 if (*num_entries > 0) {
1533 * humm, what about the status used for cache?
1534 * Should it be NT_STATUS_OK?
1536 break;
1539 * domain is offline now, and there is no user entries,
1540 * try to fetch from cache again.
1542 if (cache->tdb && !domain->online && !domain->internal && old_status) {
1543 centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
1544 /* partial response... */
1545 if (!centry) {
1546 goto skip_save;
1547 } else {
1548 goto do_fetch_cache;
1550 } else {
1551 goto skip_save;
1555 } while (NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_UNSUCCESSFUL) &&
1556 (retry++ < 5));
1558 /* and save it */
1559 refresh_sequence_number(domain, false);
1560 if (!NT_STATUS_IS_OK(status)) {
1561 return status;
1563 centry = centry_start(domain, status);
1564 if (!centry)
1565 goto skip_save;
1566 centry_put_uint32(centry, *num_entries);
1567 for (i=0; i<(*num_entries); i++) {
1568 centry_put_string(centry, (*info)[i].acct_name);
1569 centry_put_string(centry, (*info)[i].full_name);
1570 centry_put_string(centry, (*info)[i].homedir);
1571 centry_put_string(centry, (*info)[i].shell);
1572 centry_put_sid(centry, &(*info)[i].user_sid);
1573 centry_put_sid(centry, &(*info)[i].group_sid);
1574 if (domain->backend && domain->backend->consistent) {
1575 /* when the backend is consistent we can pre-prime some mappings */
1576 wcache_save_name_to_sid(domain, NT_STATUS_OK,
1577 domain->name,
1578 (*info)[i].acct_name,
1579 &(*info)[i].user_sid,
1580 SID_NAME_USER);
1581 wcache_save_sid_to_name(domain, NT_STATUS_OK,
1582 &(*info)[i].user_sid,
1583 domain->name,
1584 (*info)[i].acct_name,
1585 SID_NAME_USER);
1586 wcache_save_user(domain, NT_STATUS_OK, &(*info)[i]);
1589 centry_end(centry, "UL/%s", domain->name);
1590 centry_free(centry);
1592 skip_save:
1593 return status;
1596 /* list all domain groups */
1597 static NTSTATUS enum_dom_groups(struct winbindd_domain *domain,
1598 TALLOC_CTX *mem_ctx,
1599 uint32 *num_entries,
1600 struct wb_acct_info **info)
1602 struct winbind_cache *cache = get_cache(domain);
1603 struct cache_entry *centry = NULL;
1604 NTSTATUS status;
1605 unsigned int i;
1606 bool old_status;
1608 old_status = domain->online;
1609 if (!cache->tdb)
1610 goto do_query;
1612 centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
1613 if (!centry)
1614 goto do_query;
1616 do_fetch_cache:
1617 *num_entries = centry_uint32(centry);
1619 if (*num_entries == 0)
1620 goto do_cached;
1622 (*info) = talloc_array(mem_ctx, struct wb_acct_info, *num_entries);
1623 if (! (*info)) {
1624 smb_panic_fn("enum_dom_groups out of memory");
1626 for (i=0; i<(*num_entries); i++) {
1627 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1628 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1629 (*info)[i].rid = centry_uint32(centry);
1632 do_cached:
1633 status = centry->status;
1635 DEBUG(10,("enum_dom_groups: [Cached] - cached list for domain %s status: %s\n",
1636 domain->name, nt_errstr(status) ));
1638 centry_free(centry);
1639 return status;
1641 do_query:
1642 *num_entries = 0;
1643 *info = NULL;
1645 /* Return status value returned by seq number check */
1647 if (!NT_STATUS_IS_OK(domain->last_status))
1648 return domain->last_status;
1650 DEBUG(10,("enum_dom_groups: [Cached] - doing backend query for list for domain %s\n",
1651 domain->name ));
1653 status = domain->backend->enum_dom_groups(domain, mem_ctx, num_entries, info);
1655 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1656 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1657 if (!domain->internal && old_status) {
1658 set_domain_offline(domain);
1660 if (cache->tdb &&
1661 !domain->online &&
1662 !domain->internal &&
1663 old_status) {
1664 centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
1665 if (centry) {
1666 goto do_fetch_cache;
1670 /* and save it */
1671 refresh_sequence_number(domain, false);
1672 if (!NT_STATUS_IS_OK(status)) {
1673 return status;
1675 centry = centry_start(domain, status);
1676 if (!centry)
1677 goto skip_save;
1678 centry_put_uint32(centry, *num_entries);
1679 for (i=0; i<(*num_entries); i++) {
1680 centry_put_string(centry, (*info)[i].acct_name);
1681 centry_put_string(centry, (*info)[i].acct_desc);
1682 centry_put_uint32(centry, (*info)[i].rid);
1684 centry_end(centry, "GL/%s/domain", domain->name);
1685 centry_free(centry);
1687 skip_save:
1688 return status;
1691 /* list all domain groups */
1692 static NTSTATUS enum_local_groups(struct winbindd_domain *domain,
1693 TALLOC_CTX *mem_ctx,
1694 uint32 *num_entries,
1695 struct wb_acct_info **info)
1697 struct winbind_cache *cache = get_cache(domain);
1698 struct cache_entry *centry = NULL;
1699 NTSTATUS status;
1700 unsigned int i;
1701 bool old_status;
1703 old_status = domain->online;
1704 if (!cache->tdb)
1705 goto do_query;
1707 centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
1708 if (!centry)
1709 goto do_query;
1711 do_fetch_cache:
1712 *num_entries = centry_uint32(centry);
1714 if (*num_entries == 0)
1715 goto do_cached;
1717 (*info) = talloc_array(mem_ctx, struct wb_acct_info, *num_entries);
1718 if (! (*info)) {
1719 smb_panic_fn("enum_dom_groups out of memory");
1721 for (i=0; i<(*num_entries); i++) {
1722 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1723 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1724 (*info)[i].rid = centry_uint32(centry);
1727 do_cached:
1729 /* If we are returning cached data and the domain controller
1730 is down then we don't know whether the data is up to date
1731 or not. Return NT_STATUS_MORE_PROCESSING_REQUIRED to
1732 indicate this. */
1734 if (wcache_server_down(domain)) {
1735 DEBUG(10, ("enum_local_groups: returning cached user list and server was down\n"));
1736 status = NT_STATUS_MORE_PROCESSING_REQUIRED;
1737 } else
1738 status = centry->status;
1740 DEBUG(10,("enum_local_groups: [Cached] - cached list for domain %s status: %s\n",
1741 domain->name, nt_errstr(status) ));
1743 centry_free(centry);
1744 return status;
1746 do_query:
1747 *num_entries = 0;
1748 *info = NULL;
1750 /* Return status value returned by seq number check */
1752 if (!NT_STATUS_IS_OK(domain->last_status))
1753 return domain->last_status;
1755 DEBUG(10,("enum_local_groups: [Cached] - doing backend query for list for domain %s\n",
1756 domain->name ));
1758 status = domain->backend->enum_local_groups(domain, mem_ctx, num_entries, info);
1760 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1761 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1762 if (!domain->internal && old_status) {
1763 set_domain_offline(domain);
1765 if (cache->tdb &&
1766 !domain->internal &&
1767 !domain->online &&
1768 old_status) {
1769 centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
1770 if (centry) {
1771 goto do_fetch_cache;
1775 /* and save it */
1776 refresh_sequence_number(domain, false);
1777 if (!NT_STATUS_IS_OK(status)) {
1778 return status;
1780 centry = centry_start(domain, status);
1781 if (!centry)
1782 goto skip_save;
1783 centry_put_uint32(centry, *num_entries);
1784 for (i=0; i<(*num_entries); i++) {
1785 centry_put_string(centry, (*info)[i].acct_name);
1786 centry_put_string(centry, (*info)[i].acct_desc);
1787 centry_put_uint32(centry, (*info)[i].rid);
1789 centry_end(centry, "GL/%s/local", domain->name);
1790 centry_free(centry);
1792 skip_save:
1793 return status;
1796 NTSTATUS wcache_name_to_sid(struct winbindd_domain *domain,
1797 const char *domain_name,
1798 const char *name,
1799 struct dom_sid *sid,
1800 enum lsa_SidType *type)
1802 struct winbind_cache *cache = get_cache(domain);
1803 struct cache_entry *centry;
1804 NTSTATUS status;
1805 char *uname;
1807 if (cache->tdb == NULL) {
1808 return NT_STATUS_NOT_FOUND;
1811 uname = talloc_strdup_upper(talloc_tos(), name);
1812 if (uname == NULL) {
1813 return NT_STATUS_NO_MEMORY;
1816 if ((domain_name == NULL) || (domain_name[0] == '\0')) {
1817 domain_name = domain->name;
1820 centry = wcache_fetch(cache, domain, "NS/%s/%s", domain_name, uname);
1821 TALLOC_FREE(uname);
1822 if (centry == NULL) {
1823 return NT_STATUS_NOT_FOUND;
1826 status = centry->status;
1827 if (NT_STATUS_IS_OK(status)) {
1828 *type = (enum lsa_SidType)centry_uint32(centry);
1829 centry_sid(centry, sid);
1832 DEBUG(10,("name_to_sid: [Cached] - cached name for domain %s status: "
1833 "%s\n", domain->name, nt_errstr(status) ));
1835 centry_free(centry);
1836 return status;
1839 /* convert a single name to a sid in a domain */
1840 static NTSTATUS name_to_sid(struct winbindd_domain *domain,
1841 TALLOC_CTX *mem_ctx,
1842 const char *domain_name,
1843 const char *name,
1844 uint32_t flags,
1845 struct dom_sid *sid,
1846 enum lsa_SidType *type)
1848 NTSTATUS status;
1849 bool old_status;
1851 old_status = domain->online;
1853 status = wcache_name_to_sid(domain, domain_name, name, sid, type);
1854 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
1855 return status;
1858 ZERO_STRUCTP(sid);
1860 /* If the seq number check indicated that there is a problem
1861 * with this DC, then return that status... except for
1862 * access_denied. This is special because the dc may be in
1863 * "restrict anonymous = 1" mode, in which case it will deny
1864 * most unauthenticated operations, but *will* allow the LSA
1865 * name-to-sid that we try as a fallback. */
1867 if (!(NT_STATUS_IS_OK(domain->last_status)
1868 || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1869 return domain->last_status;
1871 DEBUG(10,("name_to_sid: [Cached] - doing backend query for name for domain %s\n",
1872 domain->name ));
1874 status = domain->backend->name_to_sid(domain, mem_ctx, domain_name,
1875 name, flags, sid, type);
1877 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1878 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1879 if (!domain->internal && old_status) {
1880 set_domain_offline(domain);
1882 if (!domain->internal &&
1883 !domain->online &&
1884 old_status) {
1885 NTSTATUS cache_status;
1886 cache_status = wcache_name_to_sid(domain, domain_name, name, sid, type);
1887 return cache_status;
1890 /* and save it */
1891 refresh_sequence_number(domain, false);
1893 if (domain->online &&
1894 (NT_STATUS_IS_OK(status) || NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED))) {
1895 wcache_save_name_to_sid(domain, status, domain_name, name, sid, *type);
1897 /* Only save the reverse mapping if this was not a UPN */
1898 if (!strchr(name, '@')) {
1899 if (!strupper_m(discard_const_p(char, domain_name))) {
1900 return NT_STATUS_INVALID_PARAMETER;
1902 (void)strlower_m(discard_const_p(char, name));
1903 wcache_save_sid_to_name(domain, status, sid, domain_name, name, *type);
1907 return status;
1910 NTSTATUS wcache_sid_to_name(struct winbindd_domain *domain,
1911 const struct dom_sid *sid,
1912 TALLOC_CTX *mem_ctx,
1913 char **domain_name,
1914 char **name,
1915 enum lsa_SidType *type)
1917 struct winbind_cache *cache = get_cache(domain);
1918 struct cache_entry *centry;
1919 char *sid_string;
1920 NTSTATUS status;
1922 if (cache->tdb == NULL) {
1923 return NT_STATUS_NOT_FOUND;
1926 sid_string = sid_string_tos(sid);
1927 if (sid_string == NULL) {
1928 return NT_STATUS_NO_MEMORY;
1931 centry = wcache_fetch(cache, domain, "SN/%s", sid_string);
1932 TALLOC_FREE(sid_string);
1933 if (centry == NULL) {
1934 return NT_STATUS_NOT_FOUND;
1937 if (NT_STATUS_IS_OK(centry->status)) {
1938 *type = (enum lsa_SidType)centry_uint32(centry);
1939 *domain_name = centry_string(centry, mem_ctx);
1940 *name = centry_string(centry, mem_ctx);
1943 status = centry->status;
1944 centry_free(centry);
1946 DEBUG(10,("sid_to_name: [Cached] - cached name for domain %s status: "
1947 "%s\n", domain->name, nt_errstr(status) ));
1949 return status;
1952 /* convert a sid to a user or group name. The sid is guaranteed to be in the domain
1953 given */
1954 static NTSTATUS sid_to_name(struct winbindd_domain *domain,
1955 TALLOC_CTX *mem_ctx,
1956 const struct dom_sid *sid,
1957 char **domain_name,
1958 char **name,
1959 enum lsa_SidType *type)
1961 NTSTATUS status;
1962 bool old_status;
1964 old_status = domain->online;
1965 status = wcache_sid_to_name(domain, sid, mem_ctx, domain_name, name,
1966 type);
1967 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
1968 return status;
1971 *name = NULL;
1972 *domain_name = NULL;
1974 /* If the seq number check indicated that there is a problem
1975 * with this DC, then return that status... except for
1976 * access_denied. This is special because the dc may be in
1977 * "restrict anonymous = 1" mode, in which case it will deny
1978 * most unauthenticated operations, but *will* allow the LSA
1979 * sid-to-name that we try as a fallback. */
1981 if (!(NT_STATUS_IS_OK(domain->last_status)
1982 || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1983 return domain->last_status;
1985 DEBUG(10,("sid_to_name: [Cached] - doing backend query for name for domain %s\n",
1986 domain->name ));
1988 status = domain->backend->sid_to_name(domain, mem_ctx, sid, domain_name, name, type);
1990 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1991 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1992 if (!domain->internal && old_status) {
1993 set_domain_offline(domain);
1995 if (!domain->internal &&
1996 !domain->online &&
1997 old_status) {
1998 NTSTATUS cache_status;
1999 cache_status = wcache_sid_to_name(domain, sid, mem_ctx,
2000 domain_name, name, type);
2001 return cache_status;
2004 /* and save it */
2005 refresh_sequence_number(domain, false);
2006 if (!NT_STATUS_IS_OK(status)) {
2007 return status;
2009 wcache_save_sid_to_name(domain, status, sid, *domain_name, *name, *type);
2011 /* We can't save the name to sid mapping here, as with sid history a
2012 * later name2sid would give the wrong sid. */
2014 return status;
2017 static NTSTATUS rids_to_names(struct winbindd_domain *domain,
2018 TALLOC_CTX *mem_ctx,
2019 const struct dom_sid *domain_sid,
2020 uint32 *rids,
2021 size_t num_rids,
2022 char **domain_name,
2023 char ***names,
2024 enum lsa_SidType **types)
2026 struct winbind_cache *cache = get_cache(domain);
2027 size_t i;
2028 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
2029 bool have_mapped;
2030 bool have_unmapped;
2031 bool old_status;
2033 old_status = domain->online;
2034 *domain_name = NULL;
2035 *names = NULL;
2036 *types = NULL;
2038 if (!cache->tdb) {
2039 goto do_query;
2042 if (num_rids == 0) {
2043 return NT_STATUS_OK;
2046 *names = talloc_array(mem_ctx, char *, num_rids);
2047 *types = talloc_array(mem_ctx, enum lsa_SidType, num_rids);
2049 if ((*names == NULL) || (*types == NULL)) {
2050 result = NT_STATUS_NO_MEMORY;
2051 goto error;
2054 have_mapped = have_unmapped = false;
2056 for (i=0; i<num_rids; i++) {
2057 struct dom_sid sid;
2058 struct cache_entry *centry;
2059 fstring tmp;
2061 if (!sid_compose(&sid, domain_sid, rids[i])) {
2062 result = NT_STATUS_INTERNAL_ERROR;
2063 goto error;
2066 centry = wcache_fetch(cache, domain, "SN/%s",
2067 sid_to_fstring(tmp, &sid));
2068 if (!centry) {
2069 goto do_query;
2072 (*types)[i] = SID_NAME_UNKNOWN;
2073 (*names)[i] = talloc_strdup(*names, "");
2075 if (NT_STATUS_IS_OK(centry->status)) {
2076 char *dom;
2077 have_mapped = true;
2078 (*types)[i] = (enum lsa_SidType)centry_uint32(centry);
2080 dom = centry_string(centry, mem_ctx);
2081 if (*domain_name == NULL) {
2082 *domain_name = dom;
2083 } else {
2084 talloc_free(dom);
2087 (*names)[i] = centry_string(centry, *names);
2089 } else if (NT_STATUS_EQUAL(centry->status, NT_STATUS_NONE_MAPPED)
2090 || NT_STATUS_EQUAL(centry->status, STATUS_SOME_UNMAPPED)) {
2091 have_unmapped = true;
2093 } else {
2094 /* something's definitely wrong */
2095 result = centry->status;
2096 centry_free(centry);
2097 goto error;
2100 centry_free(centry);
2103 if (!have_mapped) {
2104 return NT_STATUS_NONE_MAPPED;
2106 if (!have_unmapped) {
2107 return NT_STATUS_OK;
2109 return STATUS_SOME_UNMAPPED;
2111 do_query:
2113 TALLOC_FREE(*names);
2114 TALLOC_FREE(*types);
2116 result = domain->backend->rids_to_names(domain, mem_ctx, domain_sid,
2117 rids, num_rids, domain_name,
2118 names, types);
2120 if (NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
2121 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2122 if (!domain->internal && old_status) {
2123 set_domain_offline(domain);
2125 if (cache->tdb &&
2126 !domain->internal &&
2127 !domain->online &&
2128 old_status) {
2129 have_mapped = have_unmapped = false;
2131 *names = talloc_array(mem_ctx, char *, num_rids);
2132 if (*names != NULL) {
2133 result = NT_STATUS_NO_MEMORY;
2134 goto error;
2137 *types = talloc_array(mem_ctx, enum lsa_SidType,
2138 num_rids);
2139 if (*types != NULL) {
2140 result = NT_STATUS_NO_MEMORY;
2141 goto error;
2144 for (i=0; i<num_rids; i++) {
2145 struct dom_sid sid;
2146 struct cache_entry *centry;
2147 fstring tmp;
2149 if (!sid_compose(&sid, domain_sid, rids[i])) {
2150 result = NT_STATUS_INTERNAL_ERROR;
2151 goto error;
2154 centry = wcache_fetch(cache, domain, "SN/%s",
2155 sid_to_fstring(tmp, &sid));
2156 if (!centry) {
2157 (*types)[i] = SID_NAME_UNKNOWN;
2158 (*names)[i] = talloc_strdup(*names, "");
2159 continue;
2162 (*types)[i] = SID_NAME_UNKNOWN;
2163 (*names)[i] = talloc_strdup(*names, "");
2165 if (NT_STATUS_IS_OK(centry->status)) {
2166 char *dom;
2167 have_mapped = true;
2168 (*types)[i] = (enum lsa_SidType)centry_uint32(centry);
2170 dom = centry_string(centry, mem_ctx);
2171 if (*domain_name == NULL) {
2172 *domain_name = dom;
2173 } else {
2174 talloc_free(dom);
2177 (*names)[i] = centry_string(centry, *names);
2179 } else if (NT_STATUS_EQUAL(centry->status, NT_STATUS_NONE_MAPPED)) {
2180 have_unmapped = true;
2182 } else {
2183 /* something's definitely wrong */
2184 result = centry->status;
2185 centry_free(centry);
2186 goto error;
2189 centry_free(centry);
2192 if (!have_mapped) {
2193 return NT_STATUS_NONE_MAPPED;
2195 if (!have_unmapped) {
2196 return NT_STATUS_OK;
2198 return STATUS_SOME_UNMAPPED;
2202 None of the queried rids has been found so save all negative entries
2204 if (NT_STATUS_EQUAL(result, NT_STATUS_NONE_MAPPED)) {
2205 for (i = 0; i < num_rids; i++) {
2206 struct dom_sid sid;
2207 const char *name = "";
2208 const enum lsa_SidType type = SID_NAME_UNKNOWN;
2209 NTSTATUS status = NT_STATUS_NONE_MAPPED;
2211 if (!sid_compose(&sid, domain_sid, rids[i])) {
2212 return NT_STATUS_INTERNAL_ERROR;
2215 wcache_save_sid_to_name(domain, status, &sid, *domain_name,
2216 name, type);
2219 return result;
2223 Some or all of the queried rids have been found.
2225 if (!NT_STATUS_IS_OK(result) &&
2226 !NT_STATUS_EQUAL(result, STATUS_SOME_UNMAPPED)) {
2227 return result;
2230 refresh_sequence_number(domain, false);
2232 for (i=0; i<num_rids; i++) {
2233 struct dom_sid sid;
2234 NTSTATUS status;
2236 if (!sid_compose(&sid, domain_sid, rids[i])) {
2237 result = NT_STATUS_INTERNAL_ERROR;
2238 goto error;
2241 status = (*types)[i] == SID_NAME_UNKNOWN ?
2242 NT_STATUS_NONE_MAPPED : NT_STATUS_OK;
2244 wcache_save_sid_to_name(domain, status, &sid, *domain_name,
2245 (*names)[i], (*types)[i]);
2248 return result;
2250 error:
2251 TALLOC_FREE(*names);
2252 TALLOC_FREE(*types);
2253 return result;
2256 NTSTATUS wcache_query_user(struct winbindd_domain *domain,
2257 TALLOC_CTX *mem_ctx,
2258 const struct dom_sid *user_sid,
2259 struct wbint_userinfo *info)
2261 struct winbind_cache *cache = get_cache(domain);
2262 struct cache_entry *centry = NULL;
2263 NTSTATUS status;
2264 char *sid_string;
2266 if (cache->tdb == NULL) {
2267 return NT_STATUS_NOT_FOUND;
2270 sid_string = sid_string_tos(user_sid);
2271 if (sid_string == NULL) {
2272 return NT_STATUS_NO_MEMORY;
2275 centry = wcache_fetch(cache, domain, "U/%s", sid_string);
2276 TALLOC_FREE(sid_string);
2277 if (centry == NULL) {
2278 return NT_STATUS_NOT_FOUND;
2282 * If we have an access denied cache entry and a cached info3
2283 * in the samlogon cache then do a query. This will force the
2284 * rpc back end to return the info3 data.
2287 if (NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED) &&
2288 netsamlogon_cache_have(user_sid)) {
2289 DEBUG(10, ("query_user: cached access denied and have cached "
2290 "info3\n"));
2291 domain->last_status = NT_STATUS_OK;
2292 centry_free(centry);
2293 return NT_STATUS_NOT_FOUND;
2296 /* if status is not ok then this is a negative hit
2297 and the rest of the data doesn't matter */
2298 status = centry->status;
2299 if (NT_STATUS_IS_OK(status)) {
2300 info->acct_name = centry_string(centry, mem_ctx);
2301 info->full_name = centry_string(centry, mem_ctx);
2302 info->homedir = centry_string(centry, mem_ctx);
2303 info->shell = centry_string(centry, mem_ctx);
2304 info->primary_gid = centry_uint32(centry);
2305 centry_sid(centry, &info->user_sid);
2306 centry_sid(centry, &info->group_sid);
2309 DEBUG(10,("query_user: [Cached] - cached info for domain %s status: "
2310 "%s\n", domain->name, nt_errstr(status) ));
2312 centry_free(centry);
2313 return status;
2316 /* Lookup user information from a rid */
2317 static NTSTATUS query_user(struct winbindd_domain *domain,
2318 TALLOC_CTX *mem_ctx,
2319 const struct dom_sid *user_sid,
2320 struct wbint_userinfo *info)
2322 NTSTATUS status;
2323 bool old_status;
2325 old_status = domain->online;
2326 status = wcache_query_user(domain, mem_ctx, user_sid, info);
2327 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2328 return status;
2331 ZERO_STRUCTP(info);
2333 /* Return status value returned by seq number check */
2335 if (!NT_STATUS_IS_OK(domain->last_status))
2336 return domain->last_status;
2338 DEBUG(10,("query_user: [Cached] - doing backend query for info for domain %s\n",
2339 domain->name ));
2341 status = domain->backend->query_user(domain, mem_ctx, user_sid, info);
2343 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2344 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2345 if (!domain->internal && old_status) {
2346 set_domain_offline(domain);
2348 if (!domain->internal &&
2349 !domain->online &&
2350 old_status) {
2351 NTSTATUS cache_status;
2352 cache_status = wcache_query_user(domain, mem_ctx, user_sid, info);
2353 return cache_status;
2356 /* and save it */
2357 refresh_sequence_number(domain, false);
2358 if (!NT_STATUS_IS_OK(status)) {
2359 return status;
2361 wcache_save_user(domain, status, info);
2363 return status;
2366 NTSTATUS wcache_lookup_usergroups(struct winbindd_domain *domain,
2367 TALLOC_CTX *mem_ctx,
2368 const struct dom_sid *user_sid,
2369 uint32_t *pnum_sids,
2370 struct dom_sid **psids)
2372 struct winbind_cache *cache = get_cache(domain);
2373 struct cache_entry *centry = NULL;
2374 NTSTATUS status;
2375 uint32_t i, num_sids;
2376 struct dom_sid *sids;
2377 fstring sid_string;
2379 if (cache->tdb == NULL) {
2380 return NT_STATUS_NOT_FOUND;
2383 centry = wcache_fetch(cache, domain, "UG/%s",
2384 sid_to_fstring(sid_string, user_sid));
2385 if (centry == NULL) {
2386 return NT_STATUS_NOT_FOUND;
2389 /* If we have an access denied cache entry and a cached info3 in the
2390 samlogon cache then do a query. This will force the rpc back end
2391 to return the info3 data. */
2393 if (NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)
2394 && netsamlogon_cache_have(user_sid)) {
2395 DEBUG(10, ("lookup_usergroups: cached access denied and have "
2396 "cached info3\n"));
2397 domain->last_status = NT_STATUS_OK;
2398 centry_free(centry);
2399 return NT_STATUS_NOT_FOUND;
2402 num_sids = centry_uint32(centry);
2403 sids = talloc_array(mem_ctx, struct dom_sid, num_sids);
2404 if (sids == NULL) {
2405 centry_free(centry);
2406 return NT_STATUS_NO_MEMORY;
2409 for (i=0; i<num_sids; i++) {
2410 centry_sid(centry, &sids[i]);
2413 status = centry->status;
2415 DEBUG(10,("lookup_usergroups: [Cached] - cached info for domain %s "
2416 "status: %s\n", domain->name, nt_errstr(status)));
2418 centry_free(centry);
2420 *pnum_sids = num_sids;
2421 *psids = sids;
2422 return status;
2425 /* Lookup groups a user is a member of. */
2426 static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
2427 TALLOC_CTX *mem_ctx,
2428 const struct dom_sid *user_sid,
2429 uint32 *num_groups, struct dom_sid **user_gids)
2431 struct cache_entry *centry = NULL;
2432 NTSTATUS status;
2433 unsigned int i;
2434 fstring sid_string;
2435 bool old_status;
2437 old_status = domain->online;
2438 status = wcache_lookup_usergroups(domain, mem_ctx, user_sid,
2439 num_groups, user_gids);
2440 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2441 return status;
2444 (*num_groups) = 0;
2445 (*user_gids) = NULL;
2447 /* Return status value returned by seq number check */
2449 if (!NT_STATUS_IS_OK(domain->last_status))
2450 return domain->last_status;
2452 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info for domain %s\n",
2453 domain->name ));
2455 status = domain->backend->lookup_usergroups(domain, mem_ctx, user_sid, num_groups, user_gids);
2457 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2458 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2459 if (!domain->internal && old_status) {
2460 set_domain_offline(domain);
2462 if (!domain->internal &&
2463 !domain->online &&
2464 old_status) {
2465 NTSTATUS cache_status;
2466 cache_status = wcache_lookup_usergroups(domain, mem_ctx, user_sid,
2467 num_groups, user_gids);
2468 return cache_status;
2471 if ( NT_STATUS_EQUAL(status, NT_STATUS_SYNCHRONIZATION_REQUIRED) )
2472 goto skip_save;
2474 /* and save it */
2475 refresh_sequence_number(domain, false);
2476 if (!NT_STATUS_IS_OK(status)) {
2477 return status;
2479 centry = centry_start(domain, status);
2480 if (!centry)
2481 goto skip_save;
2483 centry_put_uint32(centry, *num_groups);
2484 for (i=0; i<(*num_groups); i++) {
2485 centry_put_sid(centry, &(*user_gids)[i]);
2488 centry_end(centry, "UG/%s", sid_to_fstring(sid_string, user_sid));
2489 centry_free(centry);
2491 skip_save:
2492 return status;
2495 static char *wcache_make_sidlist(TALLOC_CTX *mem_ctx, uint32_t num_sids,
2496 const struct dom_sid *sids)
2498 uint32_t i;
2499 char *sidlist;
2501 sidlist = talloc_strdup(mem_ctx, "");
2502 if (sidlist == NULL) {
2503 return NULL;
2505 for (i=0; i<num_sids; i++) {
2506 fstring tmp;
2507 sidlist = talloc_asprintf_append_buffer(
2508 sidlist, "/%s", sid_to_fstring(tmp, &sids[i]));
2509 if (sidlist == NULL) {
2510 return NULL;
2513 return sidlist;
2516 NTSTATUS wcache_lookup_useraliases(struct winbindd_domain *domain,
2517 TALLOC_CTX *mem_ctx, uint32_t num_sids,
2518 const struct dom_sid *sids,
2519 uint32_t *pnum_aliases, uint32_t **paliases)
2521 struct winbind_cache *cache = get_cache(domain);
2522 struct cache_entry *centry = NULL;
2523 uint32_t num_aliases;
2524 uint32_t *aliases;
2525 NTSTATUS status;
2526 char *sidlist;
2527 int i;
2529 if (cache->tdb == NULL) {
2530 return NT_STATUS_NOT_FOUND;
2533 if (num_sids == 0) {
2534 *pnum_aliases = 0;
2535 *paliases = NULL;
2536 return NT_STATUS_OK;
2539 /* We need to cache indexed by the whole list of SIDs, the aliases
2540 * resulting might come from any of the SIDs. */
2542 sidlist = wcache_make_sidlist(talloc_tos(), num_sids, sids);
2543 if (sidlist == NULL) {
2544 return NT_STATUS_NO_MEMORY;
2547 centry = wcache_fetch(cache, domain, "UA%s", sidlist);
2548 TALLOC_FREE(sidlist);
2549 if (centry == NULL) {
2550 return NT_STATUS_NOT_FOUND;
2553 num_aliases = centry_uint32(centry);
2554 aliases = talloc_array(mem_ctx, uint32_t, num_aliases);
2555 if (aliases == NULL) {
2556 centry_free(centry);
2557 return NT_STATUS_NO_MEMORY;
2560 for (i=0; i<num_aliases; i++) {
2561 aliases[i] = centry_uint32(centry);
2564 status = centry->status;
2566 DEBUG(10,("lookup_useraliases: [Cached] - cached info for domain: %s "
2567 "status %s\n", domain->name, nt_errstr(status)));
2569 centry_free(centry);
2571 *pnum_aliases = num_aliases;
2572 *paliases = aliases;
2574 return status;
2577 static NTSTATUS lookup_useraliases(struct winbindd_domain *domain,
2578 TALLOC_CTX *mem_ctx,
2579 uint32 num_sids, const struct dom_sid *sids,
2580 uint32 *num_aliases, uint32 **alias_rids)
2582 struct cache_entry *centry = NULL;
2583 NTSTATUS status;
2584 char *sidlist;
2585 int i;
2586 bool old_status;
2588 old_status = domain->online;
2589 status = wcache_lookup_useraliases(domain, mem_ctx, num_sids, sids,
2590 num_aliases, alias_rids);
2591 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2592 return status;
2595 (*num_aliases) = 0;
2596 (*alias_rids) = NULL;
2598 if (!NT_STATUS_IS_OK(domain->last_status))
2599 return domain->last_status;
2601 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info "
2602 "for domain %s\n", domain->name ));
2604 sidlist = wcache_make_sidlist(talloc_tos(), num_sids, sids);
2605 if (sidlist == NULL) {
2606 return NT_STATUS_NO_MEMORY;
2609 status = domain->backend->lookup_useraliases(domain, mem_ctx,
2610 num_sids, sids,
2611 num_aliases, alias_rids);
2613 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2614 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2615 if (!domain->internal && old_status) {
2616 set_domain_offline(domain);
2618 if (!domain->internal &&
2619 !domain->online &&
2620 old_status) {
2621 NTSTATUS cache_status;
2622 cache_status = wcache_lookup_useraliases(domain, mem_ctx, num_sids,
2623 sids, num_aliases, alias_rids);
2624 return cache_status;
2627 /* and save it */
2628 refresh_sequence_number(domain, false);
2629 if (!NT_STATUS_IS_OK(status)) {
2630 return status;
2632 centry = centry_start(domain, status);
2633 if (!centry)
2634 goto skip_save;
2635 centry_put_uint32(centry, *num_aliases);
2636 for (i=0; i<(*num_aliases); i++)
2637 centry_put_uint32(centry, (*alias_rids)[i]);
2638 centry_end(centry, "UA%s", sidlist);
2639 centry_free(centry);
2641 skip_save:
2642 return status;
2645 NTSTATUS wcache_lookup_groupmem(struct winbindd_domain *domain,
2646 TALLOC_CTX *mem_ctx,
2647 const struct dom_sid *group_sid,
2648 uint32_t *num_names,
2649 struct dom_sid **sid_mem, char ***names,
2650 uint32_t **name_types)
2652 struct winbind_cache *cache = get_cache(domain);
2653 struct cache_entry *centry = NULL;
2654 NTSTATUS status;
2655 unsigned int i;
2656 char *sid_string;
2658 if (cache->tdb == NULL) {
2659 return NT_STATUS_NOT_FOUND;
2662 sid_string = sid_string_tos(group_sid);
2663 if (sid_string == NULL) {
2664 return NT_STATUS_NO_MEMORY;
2667 centry = wcache_fetch(cache, domain, "GM/%s", sid_string);
2668 TALLOC_FREE(sid_string);
2669 if (centry == NULL) {
2670 return NT_STATUS_NOT_FOUND;
2673 *sid_mem = NULL;
2674 *names = NULL;
2675 *name_types = NULL;
2677 *num_names = centry_uint32(centry);
2678 if (*num_names == 0) {
2679 centry_free(centry);
2680 return NT_STATUS_OK;
2683 *sid_mem = talloc_array(mem_ctx, struct dom_sid, *num_names);
2684 *names = talloc_array(mem_ctx, char *, *num_names);
2685 *name_types = talloc_array(mem_ctx, uint32, *num_names);
2687 if ((*sid_mem == NULL) || (*names == NULL) || (*name_types == NULL)) {
2688 TALLOC_FREE(*sid_mem);
2689 TALLOC_FREE(*names);
2690 TALLOC_FREE(*name_types);
2691 centry_free(centry);
2692 return NT_STATUS_NO_MEMORY;
2695 for (i=0; i<(*num_names); i++) {
2696 centry_sid(centry, &(*sid_mem)[i]);
2697 (*names)[i] = centry_string(centry, mem_ctx);
2698 (*name_types)[i] = centry_uint32(centry);
2701 status = centry->status;
2703 DEBUG(10,("lookup_groupmem: [Cached] - cached info for domain %s "
2704 "status: %s\n", domain->name, nt_errstr(status)));
2706 centry_free(centry);
2707 return status;
2710 static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
2711 TALLOC_CTX *mem_ctx,
2712 const struct dom_sid *group_sid,
2713 enum lsa_SidType type,
2714 uint32 *num_names,
2715 struct dom_sid **sid_mem, char ***names,
2716 uint32 **name_types)
2718 struct cache_entry *centry = NULL;
2719 NTSTATUS status;
2720 unsigned int i;
2721 fstring sid_string;
2722 bool old_status;
2724 old_status = domain->online;
2725 status = wcache_lookup_groupmem(domain, mem_ctx, group_sid, num_names,
2726 sid_mem, names, name_types);
2727 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2728 return status;
2731 (*num_names) = 0;
2732 (*sid_mem) = NULL;
2733 (*names) = NULL;
2734 (*name_types) = NULL;
2736 /* Return status value returned by seq number check */
2738 if (!NT_STATUS_IS_OK(domain->last_status))
2739 return domain->last_status;
2741 DEBUG(10,("lookup_groupmem: [Cached] - doing backend query for info for domain %s\n",
2742 domain->name ));
2744 status = domain->backend->lookup_groupmem(domain, mem_ctx, group_sid,
2745 type, num_names,
2746 sid_mem, names, name_types);
2748 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2749 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2750 if (!domain->internal && old_status) {
2751 set_domain_offline(domain);
2753 if (!domain->internal &&
2754 !domain->online &&
2755 old_status) {
2756 NTSTATUS cache_status;
2757 cache_status = wcache_lookup_groupmem(domain, mem_ctx, group_sid,
2758 num_names, sid_mem, names,
2759 name_types);
2760 return cache_status;
2763 /* and save it */
2764 refresh_sequence_number(domain, false);
2765 if (!NT_STATUS_IS_OK(status)) {
2766 return status;
2768 centry = centry_start(domain, status);
2769 if (!centry)
2770 goto skip_save;
2771 centry_put_uint32(centry, *num_names);
2772 for (i=0; i<(*num_names); i++) {
2773 centry_put_sid(centry, &(*sid_mem)[i]);
2774 centry_put_string(centry, (*names)[i]);
2775 centry_put_uint32(centry, (*name_types)[i]);
2777 centry_end(centry, "GM/%s", sid_to_fstring(sid_string, group_sid));
2778 centry_free(centry);
2780 skip_save:
2781 return status;
2784 /* find the sequence number for a domain */
2785 static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
2787 refresh_sequence_number(domain, false);
2789 *seq = domain->sequence_number;
2791 return NT_STATUS_OK;
2794 /* enumerate trusted domains
2795 * (we need to have the list of trustdoms in the cache when we go offline) -
2796 * Guenther */
2797 static NTSTATUS trusted_domains(struct winbindd_domain *domain,
2798 TALLOC_CTX *mem_ctx,
2799 struct netr_DomainTrustList *trusts)
2801 NTSTATUS status;
2802 struct winbind_cache *cache;
2803 struct winbindd_tdc_domain *dom_list = NULL;
2804 size_t num_domains = 0;
2805 bool retval = false;
2806 int i;
2807 bool old_status;
2809 old_status = domain->online;
2810 trusts->count = 0;
2811 trusts->array = NULL;
2813 cache = get_cache(domain);
2814 if (!cache || !cache->tdb) {
2815 goto do_query;
2818 if (domain->online) {
2819 goto do_query;
2822 retval = wcache_tdc_fetch_list(&dom_list, &num_domains);
2823 if (!retval || !num_domains || !dom_list) {
2824 TALLOC_FREE(dom_list);
2825 goto do_query;
2828 do_fetch_cache:
2829 trusts->array = talloc_zero_array(mem_ctx, struct netr_DomainTrust, num_domains);
2830 if (!trusts->array) {
2831 TALLOC_FREE(dom_list);
2832 return NT_STATUS_NO_MEMORY;
2835 for (i = 0; i < num_domains; i++) {
2836 struct netr_DomainTrust *trust;
2837 struct dom_sid *sid;
2838 struct winbindd_domain *dom;
2840 dom = find_domain_from_name_noinit(dom_list[i].domain_name);
2841 if (dom && dom->internal) {
2842 continue;
2845 trust = &trusts->array[trusts->count];
2846 trust->netbios_name = talloc_strdup(trusts->array, dom_list[i].domain_name);
2847 trust->dns_name = talloc_strdup(trusts->array, dom_list[i].dns_name);
2848 sid = talloc(trusts->array, struct dom_sid);
2849 if (!trust->netbios_name || !trust->dns_name ||
2850 !sid) {
2851 TALLOC_FREE(dom_list);
2852 TALLOC_FREE(trusts->array);
2853 return NT_STATUS_NO_MEMORY;
2856 trust->trust_flags = dom_list[i].trust_flags;
2857 trust->trust_attributes = dom_list[i].trust_attribs;
2858 trust->trust_type = dom_list[i].trust_type;
2859 sid_copy(sid, &dom_list[i].sid);
2860 trust->sid = sid;
2861 trusts->count++;
2864 TALLOC_FREE(dom_list);
2865 return NT_STATUS_OK;
2867 do_query:
2868 /* Return status value returned by seq number check */
2870 if (!NT_STATUS_IS_OK(domain->last_status))
2871 return domain->last_status;
2873 DEBUG(10,("trusted_domains: [Cached] - doing backend query for info for domain %s\n",
2874 domain->name ));
2876 status = domain->backend->trusted_domains(domain, mem_ctx, trusts);
2878 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2879 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2880 if (!domain->internal && old_status) {
2881 set_domain_offline(domain);
2883 if (!domain->internal &&
2884 !domain->online &&
2885 old_status) {
2886 retval = wcache_tdc_fetch_list(&dom_list, &num_domains);
2887 if (retval && num_domains && dom_list) {
2888 TALLOC_FREE(trusts->array);
2889 trusts->count = 0;
2890 goto do_fetch_cache;
2894 /* no trusts gives NT_STATUS_NO_MORE_ENTRIES resetting to NT_STATUS_OK
2895 * so that the generic centry handling still applies correctly -
2896 * Guenther*/
2898 if (!NT_STATUS_IS_ERR(status)) {
2899 status = NT_STATUS_OK;
2901 return status;
2904 /* get lockout policy */
2905 static NTSTATUS lockout_policy(struct winbindd_domain *domain,
2906 TALLOC_CTX *mem_ctx,
2907 struct samr_DomInfo12 *policy)
2909 struct winbind_cache *cache = get_cache(domain);
2910 struct cache_entry *centry = NULL;
2911 NTSTATUS status;
2912 bool old_status;
2914 old_status = domain->online;
2915 if (!cache->tdb)
2916 goto do_query;
2918 centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
2920 if (!centry)
2921 goto do_query;
2923 do_fetch_cache:
2924 policy->lockout_duration = centry_nttime(centry);
2925 policy->lockout_window = centry_nttime(centry);
2926 policy->lockout_threshold = centry_uint16(centry);
2928 status = centry->status;
2930 DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2931 domain->name, nt_errstr(status) ));
2933 centry_free(centry);
2934 return status;
2936 do_query:
2937 ZERO_STRUCTP(policy);
2939 /* Return status value returned by seq number check */
2941 if (!NT_STATUS_IS_OK(domain->last_status))
2942 return domain->last_status;
2944 DEBUG(10,("lockout_policy: [Cached] - doing backend query for info for domain %s\n",
2945 domain->name ));
2947 status = domain->backend->lockout_policy(domain, mem_ctx, policy);
2949 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2950 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2951 if (!domain->internal && old_status) {
2952 set_domain_offline(domain);
2954 if (cache->tdb &&
2955 !domain->internal &&
2956 !domain->online &&
2957 old_status) {
2958 centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
2959 if (centry) {
2960 goto do_fetch_cache;
2964 /* and save it */
2965 refresh_sequence_number(domain, false);
2966 if (!NT_STATUS_IS_OK(status)) {
2967 return status;
2969 wcache_save_lockout_policy(domain, status, policy);
2971 return status;
2974 /* get password policy */
2975 static NTSTATUS password_policy(struct winbindd_domain *domain,
2976 TALLOC_CTX *mem_ctx,
2977 struct samr_DomInfo1 *policy)
2979 struct winbind_cache *cache = get_cache(domain);
2980 struct cache_entry *centry = NULL;
2981 NTSTATUS status;
2982 bool old_status;
2984 old_status = domain->online;
2985 if (!cache->tdb)
2986 goto do_query;
2988 centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
2990 if (!centry)
2991 goto do_query;
2993 do_fetch_cache:
2994 policy->min_password_length = centry_uint16(centry);
2995 policy->password_history_length = centry_uint16(centry);
2996 policy->password_properties = centry_uint32(centry);
2997 policy->max_password_age = centry_nttime(centry);
2998 policy->min_password_age = centry_nttime(centry);
3000 status = centry->status;
3002 DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
3003 domain->name, nt_errstr(status) ));
3005 centry_free(centry);
3006 return status;
3008 do_query:
3009 ZERO_STRUCTP(policy);
3011 /* Return status value returned by seq number check */
3013 if (!NT_STATUS_IS_OK(domain->last_status))
3014 return domain->last_status;
3016 DEBUG(10,("password_policy: [Cached] - doing backend query for info for domain %s\n",
3017 domain->name ));
3019 status = domain->backend->password_policy(domain, mem_ctx, policy);
3021 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
3022 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
3023 if (!domain->internal && old_status) {
3024 set_domain_offline(domain);
3026 if (cache->tdb &&
3027 !domain->internal &&
3028 !domain->online &&
3029 old_status) {
3030 centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
3031 if (centry) {
3032 goto do_fetch_cache;
3036 /* and save it */
3037 refresh_sequence_number(domain, false);
3038 if (!NT_STATUS_IS_OK(status)) {
3039 return status;
3041 wcache_save_password_policy(domain, status, policy);
3043 return status;
3047 /* Invalidate cached user and group lists coherently */
3049 static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
3050 void *state)
3052 if (strncmp((const char *)kbuf.dptr, "UL/", 3) == 0 ||
3053 strncmp((const char *)kbuf.dptr, "GL/", 3) == 0)
3054 tdb_delete(the_tdb, kbuf);
3056 return 0;
3059 /* Invalidate the getpwnam and getgroups entries for a winbindd domain */
3061 void wcache_invalidate_samlogon(struct winbindd_domain *domain,
3062 const struct dom_sid *sid)
3064 fstring key_str, sid_string;
3065 struct winbind_cache *cache;
3067 /* dont clear cached U/SID and UG/SID entries when we want to logon
3068 * offline - gd */
3070 if (lp_winbind_offline_logon()) {
3071 return;
3074 if (!domain)
3075 return;
3077 cache = get_cache(domain);
3079 if (!cache->tdb) {
3080 return;
3083 /* Clear U/SID cache entry */
3084 fstr_sprintf(key_str, "U/%s", sid_to_fstring(sid_string, sid));
3085 DEBUG(10, ("wcache_invalidate_samlogon: clearing %s\n", key_str));
3086 tdb_delete(cache->tdb, string_tdb_data(key_str));
3088 /* Clear UG/SID cache entry */
3089 fstr_sprintf(key_str, "UG/%s", sid_to_fstring(sid_string, sid));
3090 DEBUG(10, ("wcache_invalidate_samlogon: clearing %s\n", key_str));
3091 tdb_delete(cache->tdb, string_tdb_data(key_str));
3093 /* Samba/winbindd never needs this. */
3094 netsamlogon_clear_cached_user(sid);
3097 bool wcache_invalidate_cache(void)
3099 struct winbindd_domain *domain;
3101 for (domain = domain_list(); domain; domain = domain->next) {
3102 struct winbind_cache *cache = get_cache(domain);
3104 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
3105 "entries for %s\n", domain->name));
3106 if (cache) {
3107 if (cache->tdb) {
3108 tdb_traverse(cache->tdb, traverse_fn, NULL);
3109 } else {
3110 return false;
3114 return true;
3117 bool wcache_invalidate_cache_noinit(void)
3119 struct winbindd_domain *domain;
3121 for (domain = domain_list(); domain; domain = domain->next) {
3122 struct winbind_cache *cache;
3124 /* Skip uninitialized domains. */
3125 if (!domain->initialized && !domain->internal) {
3126 continue;
3129 cache = get_cache(domain);
3131 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
3132 "entries for %s\n", domain->name));
3133 if (cache) {
3134 if (cache->tdb) {
3135 tdb_traverse(cache->tdb, traverse_fn, NULL);
3137 * Flushing cache has nothing to with domains.
3138 * return here if we successfully flushed once.
3139 * To avoid unnecessary traversing the cache.
3141 return true;
3142 } else {
3143 return false;
3147 return true;
3150 bool init_wcache(void)
3152 if (wcache == NULL) {
3153 wcache = SMB_XMALLOC_P(struct winbind_cache);
3154 ZERO_STRUCTP(wcache);
3157 if (wcache->tdb != NULL)
3158 return true;
3160 /* when working offline we must not clear the cache on restart */
3161 wcache->tdb = tdb_open_log(state_path("winbindd_cache.tdb"),
3162 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
3163 TDB_INCOMPATIBLE_HASH |
3164 (lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST)),
3165 O_RDWR|O_CREAT, 0600);
3167 if (wcache->tdb == NULL) {
3168 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
3169 return false;
3172 return true;
3175 /************************************************************************
3176 This is called by the parent to initialize the cache file.
3177 We don't need sophisticated locking here as we know we're the
3178 only opener.
3179 ************************************************************************/
3181 bool initialize_winbindd_cache(void)
3183 bool cache_bad = true;
3184 uint32 vers;
3186 if (!init_wcache()) {
3187 DEBUG(0,("initialize_winbindd_cache: init_wcache failed.\n"));
3188 return false;
3191 /* Check version number. */
3192 if (tdb_fetch_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, &vers) &&
3193 vers == WINBINDD_CACHE_VERSION) {
3194 cache_bad = false;
3197 if (cache_bad) {
3198 DEBUG(0,("initialize_winbindd_cache: clearing cache "
3199 "and re-creating with version number %d\n",
3200 WINBINDD_CACHE_VERSION ));
3202 tdb_close(wcache->tdb);
3203 wcache->tdb = NULL;
3205 if (unlink(state_path("winbindd_cache.tdb")) == -1) {
3206 DEBUG(0,("initialize_winbindd_cache: unlink %s failed %s ",
3207 state_path("winbindd_cache.tdb"),
3208 strerror(errno) ));
3209 return false;
3211 if (!init_wcache()) {
3212 DEBUG(0,("initialize_winbindd_cache: re-initialization "
3213 "init_wcache failed.\n"));
3214 return false;
3217 /* Write the version. */
3218 if (!tdb_store_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, WINBINDD_CACHE_VERSION)) {
3219 DEBUG(0,("initialize_winbindd_cache: version number store failed %s\n",
3220 tdb_errorstr_compat(wcache->tdb) ));
3221 return false;
3225 tdb_close(wcache->tdb);
3226 wcache->tdb = NULL;
3227 return true;
3230 void close_winbindd_cache(void)
3232 if (!wcache) {
3233 return;
3235 if (wcache->tdb) {
3236 tdb_close(wcache->tdb);
3237 wcache->tdb = NULL;
3241 bool lookup_cached_sid(TALLOC_CTX *mem_ctx, const struct dom_sid *sid,
3242 char **domain_name, char **name,
3243 enum lsa_SidType *type)
3245 struct winbindd_domain *domain;
3246 NTSTATUS status;
3248 domain = find_lookup_domain_from_sid(sid);
3249 if (domain == NULL) {
3250 return false;
3252 status = wcache_sid_to_name(domain, sid, mem_ctx, domain_name, name,
3253 type);
3254 return NT_STATUS_IS_OK(status);
3257 bool lookup_cached_name(const char *domain_name,
3258 const char *name,
3259 struct dom_sid *sid,
3260 enum lsa_SidType *type)
3262 struct winbindd_domain *domain;
3263 NTSTATUS status;
3264 bool original_online_state;
3266 domain = find_lookup_domain_from_name(domain_name);
3267 if (domain == NULL) {
3268 return false;
3271 /* If we are doing a cached logon, temporarily set the domain
3272 offline so the cache won't expire the entry */
3274 original_online_state = domain->online;
3275 domain->online = false;
3276 status = wcache_name_to_sid(domain, domain_name, name, sid, type);
3277 domain->online = original_online_state;
3279 return NT_STATUS_IS_OK(status);
3282 void cache_name2sid(struct winbindd_domain *domain,
3283 const char *domain_name, const char *name,
3284 enum lsa_SidType type, const struct dom_sid *sid)
3286 refresh_sequence_number(domain, false);
3287 wcache_save_name_to_sid(domain, NT_STATUS_OK, domain_name, name,
3288 sid, type);
3292 * The original idea that this cache only contains centries has
3293 * been blurred - now other stuff gets put in here. Ensure we
3294 * ignore these things on cleanup.
3297 static int traverse_fn_cleanup(TDB_CONTEXT *the_tdb, TDB_DATA kbuf,
3298 TDB_DATA dbuf, void *state)
3300 struct cache_entry *centry;
3302 if (is_non_centry_key(kbuf)) {
3303 return 0;
3306 centry = wcache_fetch_raw((char *)kbuf.dptr);
3307 if (!centry) {
3308 return 0;
3311 if (!NT_STATUS_IS_OK(centry->status)) {
3312 DEBUG(10,("deleting centry %s\n", (const char *)kbuf.dptr));
3313 tdb_delete(the_tdb, kbuf);
3316 centry_free(centry);
3317 return 0;
3320 /* flush the cache */
3321 void wcache_flush_cache(void)
3323 if (!wcache)
3324 return;
3325 if (wcache->tdb) {
3326 tdb_close(wcache->tdb);
3327 wcache->tdb = NULL;
3329 if (!winbindd_use_cache()) {
3330 return;
3333 /* when working offline we must not clear the cache on restart */
3334 wcache->tdb = tdb_open_log(state_path("winbindd_cache.tdb"),
3335 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
3336 TDB_INCOMPATIBLE_HASH |
3337 (lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST)),
3338 O_RDWR|O_CREAT, 0600);
3340 if (!wcache->tdb) {
3341 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
3342 return;
3345 tdb_traverse(wcache->tdb, traverse_fn_cleanup, NULL);
3347 DEBUG(10,("wcache_flush_cache success\n"));
3350 /* Count cached creds */
3352 static int traverse_fn_cached_creds(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
3353 void *state)
3355 int *cred_count = (int*)state;
3357 if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
3358 (*cred_count)++;
3360 return 0;
3363 NTSTATUS wcache_count_cached_creds(struct winbindd_domain *domain, int *count)
3365 struct winbind_cache *cache = get_cache(domain);
3367 *count = 0;
3369 if (!cache->tdb) {
3370 return NT_STATUS_INTERNAL_DB_ERROR;
3373 tdb_traverse(cache->tdb, traverse_fn_cached_creds, (void *)count);
3375 return NT_STATUS_OK;
3378 struct cred_list {
3379 struct cred_list *prev, *next;
3380 TDB_DATA key;
3381 fstring name;
3382 time_t created;
3384 static struct cred_list *wcache_cred_list;
3386 static int traverse_fn_get_credlist(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
3387 void *state)
3389 struct cred_list *cred;
3391 if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
3393 cred = SMB_MALLOC_P(struct cred_list);
3394 if (cred == NULL) {
3395 DEBUG(0,("traverse_fn_remove_first_creds: failed to malloc new entry for list\n"));
3396 return -1;
3399 ZERO_STRUCTP(cred);
3401 /* save a copy of the key */
3403 fstrcpy(cred->name, (const char *)kbuf.dptr);
3404 DLIST_ADD(wcache_cred_list, cred);
3407 return 0;
3410 NTSTATUS wcache_remove_oldest_cached_creds(struct winbindd_domain *domain, const struct dom_sid *sid)
3412 struct winbind_cache *cache = get_cache(domain);
3413 NTSTATUS status;
3414 int ret;
3415 struct cred_list *cred, *oldest = NULL;
3417 if (!cache->tdb) {
3418 return NT_STATUS_INTERNAL_DB_ERROR;
3421 /* we possibly already have an entry */
3422 if (sid && NT_STATUS_IS_OK(wcache_cached_creds_exist(domain, sid))) {
3424 fstring key_str, tmp;
3426 DEBUG(11,("we already have an entry, deleting that\n"));
3428 fstr_sprintf(key_str, "CRED/%s", sid_to_fstring(tmp, sid));
3430 tdb_delete(cache->tdb, string_tdb_data(key_str));
3432 return NT_STATUS_OK;
3435 ret = tdb_traverse(cache->tdb, traverse_fn_get_credlist, NULL);
3436 if (ret == 0) {
3437 return NT_STATUS_OK;
3438 } else if ((ret < 0) || (wcache_cred_list == NULL)) {
3439 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
3442 ZERO_STRUCTP(oldest);
3444 for (cred = wcache_cred_list; cred; cred = cred->next) {
3446 TDB_DATA data;
3447 time_t t;
3449 data = tdb_fetch_compat(cache->tdb, string_tdb_data(cred->name));
3450 if (!data.dptr) {
3451 DEBUG(10,("wcache_remove_oldest_cached_creds: entry for [%s] not found\n",
3452 cred->name));
3453 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
3454 goto done;
3457 t = IVAL(data.dptr, 0);
3458 SAFE_FREE(data.dptr);
3460 if (!oldest) {
3461 oldest = SMB_MALLOC_P(struct cred_list);
3462 if (oldest == NULL) {
3463 status = NT_STATUS_NO_MEMORY;
3464 goto done;
3467 fstrcpy(oldest->name, cred->name);
3468 oldest->created = t;
3469 continue;
3472 if (t < oldest->created) {
3473 fstrcpy(oldest->name, cred->name);
3474 oldest->created = t;
3478 if (tdb_delete(cache->tdb, string_tdb_data(oldest->name)) == 0) {
3479 status = NT_STATUS_OK;
3480 } else {
3481 status = NT_STATUS_UNSUCCESSFUL;
3483 done:
3484 SAFE_FREE(wcache_cred_list);
3485 SAFE_FREE(oldest);
3487 return status;
3490 /* Change the global online/offline state. */
3491 bool set_global_winbindd_state_offline(void)
3493 TDB_DATA data;
3495 DEBUG(10,("set_global_winbindd_state_offline: offline requested.\n"));
3497 /* Only go offline if someone has created
3498 the key "WINBINDD_OFFLINE" in the cache tdb. */
3500 if (wcache == NULL || wcache->tdb == NULL) {
3501 DEBUG(10,("set_global_winbindd_state_offline: wcache not open yet.\n"));
3502 return false;
3505 if (!lp_winbind_offline_logon()) {
3506 DEBUG(10,("set_global_winbindd_state_offline: rejecting.\n"));
3507 return false;
3510 if (global_winbindd_offline_state) {
3511 /* Already offline. */
3512 return true;
3515 data = tdb_fetch_bystring( wcache->tdb, "WINBINDD_OFFLINE" );
3517 if (!data.dptr || data.dsize != 4) {
3518 DEBUG(10,("set_global_winbindd_state_offline: offline state not set.\n"));
3519 SAFE_FREE(data.dptr);
3520 return false;
3521 } else {
3522 DEBUG(10,("set_global_winbindd_state_offline: offline state set.\n"));
3523 global_winbindd_offline_state = true;
3524 SAFE_FREE(data.dptr);
3525 return true;
3529 void set_global_winbindd_state_online(void)
3531 DEBUG(10,("set_global_winbindd_state_online: online requested.\n"));
3533 if (!lp_winbind_offline_logon()) {
3534 DEBUG(10,("set_global_winbindd_state_online: rejecting.\n"));
3535 return;
3538 if (!global_winbindd_offline_state) {
3539 /* Already online. */
3540 return;
3542 global_winbindd_offline_state = false;
3544 if (!wcache->tdb) {
3545 return;
3548 /* Ensure there is no key "WINBINDD_OFFLINE" in the cache tdb. */
3549 tdb_delete_bystring(wcache->tdb, "WINBINDD_OFFLINE");
3552 bool get_global_winbindd_state_offline(void)
3554 return global_winbindd_offline_state;
3557 /***********************************************************************
3558 Validate functions for all possible cache tdb keys.
3559 ***********************************************************************/
3561 static struct cache_entry *create_centry_validate(const char *kstr, TDB_DATA data,
3562 struct tdb_validation_status *state)
3564 struct cache_entry *centry;
3566 centry = SMB_XMALLOC_P(struct cache_entry);
3567 centry->data = (unsigned char *)smb_memdup(data.dptr, data.dsize);
3568 if (!centry->data) {
3569 SAFE_FREE(centry);
3570 return NULL;
3572 centry->len = data.dsize;
3573 centry->ofs = 0;
3575 if (centry->len < 16) {
3576 /* huh? corrupt cache? */
3577 DEBUG(0,("create_centry_validate: Corrupt cache for key %s "
3578 "(len < 16) ?\n", kstr));
3579 centry_free(centry);
3580 state->bad_entry = true;
3581 state->success = false;
3582 return NULL;
3585 centry->status = NT_STATUS(centry_uint32(centry));
3586 centry->sequence_number = centry_uint32(centry);
3587 centry->timeout = centry_uint64_t(centry);
3588 return centry;
3591 static int validate_seqnum(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3592 struct tdb_validation_status *state)
3594 if (dbuf.dsize != 8) {
3595 DEBUG(0,("validate_seqnum: Corrupt cache for key %s (len %u != 8) ?\n",
3596 keystr, (unsigned int)dbuf.dsize ));
3597 state->bad_entry = true;
3598 return 1;
3600 return 0;
3603 static int validate_ns(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3604 struct tdb_validation_status *state)
3606 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3607 if (!centry) {
3608 return 1;
3611 (void)centry_uint32(centry);
3612 if (NT_STATUS_IS_OK(centry->status)) {
3613 struct dom_sid sid;
3614 (void)centry_sid(centry, &sid);
3617 centry_free(centry);
3619 if (!(state->success)) {
3620 return 1;
3622 DEBUG(10,("validate_ns: %s ok\n", keystr));
3623 return 0;
3626 static int validate_sn(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3627 struct tdb_validation_status *state)
3629 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3630 if (!centry) {
3631 return 1;
3634 if (NT_STATUS_IS_OK(centry->status)) {
3635 (void)centry_uint32(centry);
3636 (void)centry_string(centry, mem_ctx);
3637 (void)centry_string(centry, mem_ctx);
3640 centry_free(centry);
3642 if (!(state->success)) {
3643 return 1;
3645 DEBUG(10,("validate_sn: %s ok\n", keystr));
3646 return 0;
3649 static int validate_u(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3650 struct tdb_validation_status *state)
3652 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3653 struct dom_sid sid;
3655 if (!centry) {
3656 return 1;
3659 (void)centry_string(centry, mem_ctx);
3660 (void)centry_string(centry, mem_ctx);
3661 (void)centry_string(centry, mem_ctx);
3662 (void)centry_string(centry, mem_ctx);
3663 (void)centry_uint32(centry);
3664 (void)centry_sid(centry, &sid);
3665 (void)centry_sid(centry, &sid);
3667 centry_free(centry);
3669 if (!(state->success)) {
3670 return 1;
3672 DEBUG(10,("validate_u: %s ok\n", keystr));
3673 return 0;
3676 static int validate_loc_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3677 struct tdb_validation_status *state)
3679 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3681 if (!centry) {
3682 return 1;
3685 (void)centry_nttime(centry);
3686 (void)centry_nttime(centry);
3687 (void)centry_uint16(centry);
3689 centry_free(centry);
3691 if (!(state->success)) {
3692 return 1;
3694 DEBUG(10,("validate_loc_pol: %s ok\n", keystr));
3695 return 0;
3698 static int validate_pwd_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3699 struct tdb_validation_status *state)
3701 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3703 if (!centry) {
3704 return 1;
3707 (void)centry_uint16(centry);
3708 (void)centry_uint16(centry);
3709 (void)centry_uint32(centry);
3710 (void)centry_nttime(centry);
3711 (void)centry_nttime(centry);
3713 centry_free(centry);
3715 if (!(state->success)) {
3716 return 1;
3718 DEBUG(10,("validate_pwd_pol: %s ok\n", keystr));
3719 return 0;
3722 static int validate_cred(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3723 struct tdb_validation_status *state)
3725 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3727 if (!centry) {
3728 return 1;
3731 (void)centry_time(centry);
3732 (void)centry_hash16(centry, mem_ctx);
3734 /* We only have 17 bytes more data in the salted cred case. */
3735 if (centry->len - centry->ofs == 17) {
3736 (void)centry_hash16(centry, mem_ctx);
3739 centry_free(centry);
3741 if (!(state->success)) {
3742 return 1;
3744 DEBUG(10,("validate_cred: %s ok\n", keystr));
3745 return 0;
3748 static int validate_ul(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3749 struct tdb_validation_status *state)
3751 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3752 int32 num_entries, i;
3754 if (!centry) {
3755 return 1;
3758 num_entries = (int32)centry_uint32(centry);
3760 for (i=0; i< num_entries; i++) {
3761 struct dom_sid sid;
3762 (void)centry_string(centry, mem_ctx);
3763 (void)centry_string(centry, mem_ctx);
3764 (void)centry_string(centry, mem_ctx);
3765 (void)centry_string(centry, mem_ctx);
3766 (void)centry_sid(centry, &sid);
3767 (void)centry_sid(centry, &sid);
3770 centry_free(centry);
3772 if (!(state->success)) {
3773 return 1;
3775 DEBUG(10,("validate_ul: %s ok\n", keystr));
3776 return 0;
3779 static int validate_gl(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3780 struct tdb_validation_status *state)
3782 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3783 int32 num_entries, i;
3785 if (!centry) {
3786 return 1;
3789 num_entries = centry_uint32(centry);
3791 for (i=0; i< num_entries; i++) {
3792 (void)centry_string(centry, mem_ctx);
3793 (void)centry_string(centry, mem_ctx);
3794 (void)centry_uint32(centry);
3797 centry_free(centry);
3799 if (!(state->success)) {
3800 return 1;
3802 DEBUG(10,("validate_gl: %s ok\n", keystr));
3803 return 0;
3806 static int validate_ug(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3807 struct tdb_validation_status *state)
3809 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3810 int32 num_groups, i;
3812 if (!centry) {
3813 return 1;
3816 num_groups = centry_uint32(centry);
3818 for (i=0; i< num_groups; i++) {
3819 struct dom_sid sid;
3820 centry_sid(centry, &sid);
3823 centry_free(centry);
3825 if (!(state->success)) {
3826 return 1;
3828 DEBUG(10,("validate_ug: %s ok\n", keystr));
3829 return 0;
3832 static int validate_ua(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3833 struct tdb_validation_status *state)
3835 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3836 int32 num_aliases, i;
3838 if (!centry) {
3839 return 1;
3842 num_aliases = centry_uint32(centry);
3844 for (i=0; i < num_aliases; i++) {
3845 (void)centry_uint32(centry);
3848 centry_free(centry);
3850 if (!(state->success)) {
3851 return 1;
3853 DEBUG(10,("validate_ua: %s ok\n", keystr));
3854 return 0;
3857 static int validate_gm(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3858 struct tdb_validation_status *state)
3860 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3861 int32 num_names, i;
3863 if (!centry) {
3864 return 1;
3867 num_names = centry_uint32(centry);
3869 for (i=0; i< num_names; i++) {
3870 struct dom_sid sid;
3871 centry_sid(centry, &sid);
3872 (void)centry_string(centry, mem_ctx);
3873 (void)centry_uint32(centry);
3876 centry_free(centry);
3878 if (!(state->success)) {
3879 return 1;
3881 DEBUG(10,("validate_gm: %s ok\n", keystr));
3882 return 0;
3885 static int validate_dr(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3886 struct tdb_validation_status *state)
3888 /* Can't say anything about this other than must be nonzero. */
3889 if (dbuf.dsize == 0) {
3890 DEBUG(0,("validate_dr: Corrupt cache for key %s (len == 0) ?\n",
3891 keystr));
3892 state->bad_entry = true;
3893 state->success = false;
3894 return 1;
3897 DEBUG(10,("validate_dr: %s ok\n", keystr));
3898 return 0;
3901 static int validate_de(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3902 struct tdb_validation_status *state)
3904 /* Can't say anything about this other than must be nonzero. */
3905 if (dbuf.dsize == 0) {
3906 DEBUG(0,("validate_de: Corrupt cache for key %s (len == 0) ?\n",
3907 keystr));
3908 state->bad_entry = true;
3909 state->success = false;
3910 return 1;
3913 DEBUG(10,("validate_de: %s ok\n", keystr));
3914 return 0;
3917 static int validate_pwinfo(TALLOC_CTX *mem_ctx, const char *keystr,
3918 TDB_DATA dbuf, struct tdb_validation_status *state)
3920 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3922 if (!centry) {
3923 return 1;
3926 (void)centry_string(centry, mem_ctx);
3927 (void)centry_string(centry, mem_ctx);
3928 (void)centry_string(centry, mem_ctx);
3929 (void)centry_uint32(centry);
3931 centry_free(centry);
3933 if (!(state->success)) {
3934 return 1;
3936 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3937 return 0;
3940 static int validate_nss_an(TALLOC_CTX *mem_ctx, const char *keystr,
3941 TDB_DATA dbuf,
3942 struct tdb_validation_status *state)
3944 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3946 if (!centry) {
3947 return 1;
3950 (void)centry_string( centry, mem_ctx );
3952 centry_free(centry);
3954 if (!(state->success)) {
3955 return 1;
3957 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3958 return 0;
3961 static int validate_nss_na(TALLOC_CTX *mem_ctx, const char *keystr,
3962 TDB_DATA dbuf,
3963 struct tdb_validation_status *state)
3965 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3967 if (!centry) {
3968 return 1;
3971 (void)centry_string( centry, mem_ctx );
3973 centry_free(centry);
3975 if (!(state->success)) {
3976 return 1;
3978 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3979 return 0;
3982 static int validate_trustdomcache(TALLOC_CTX *mem_ctx, const char *keystr,
3983 TDB_DATA dbuf,
3984 struct tdb_validation_status *state)
3986 if (dbuf.dsize == 0) {
3987 DEBUG(0, ("validate_trustdomcache: Corrupt cache for "
3988 "key %s (len ==0) ?\n", keystr));
3989 state->bad_entry = true;
3990 state->success = false;
3991 return 1;
3994 DEBUG(10, ("validate_trustdomcache: %s ok\n", keystr));
3995 DEBUGADD(10, (" Don't trust me, I am a DUMMY!\n"));
3996 return 0;
3999 static int validate_offline(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
4000 struct tdb_validation_status *state)
4002 if (dbuf.dsize != 4) {
4003 DEBUG(0,("validate_offline: Corrupt cache for key %s (len %u != 4) ?\n",
4004 keystr, (unsigned int)dbuf.dsize ));
4005 state->bad_entry = true;
4006 state->success = false;
4007 return 1;
4009 DEBUG(10,("validate_offline: %s ok\n", keystr));
4010 return 0;
4013 static int validate_ndr(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
4014 struct tdb_validation_status *state)
4017 * Ignore validation for now. The proper way to do this is with a
4018 * checksum. Just pure parsing does not really catch much.
4020 return 0;
4023 static int validate_cache_version(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
4024 struct tdb_validation_status *state)
4026 if (dbuf.dsize != 4) {
4027 DEBUG(0, ("validate_cache_version: Corrupt cache for "
4028 "key %s (len %u != 4) ?\n",
4029 keystr, (unsigned int)dbuf.dsize));
4030 state->bad_entry = true;
4031 state->success = false;
4032 return 1;
4035 DEBUG(10, ("validate_cache_version: %s ok\n", keystr));
4036 return 0;
4039 /***********************************************************************
4040 A list of all possible cache tdb keys with associated validation
4041 functions.
4042 ***********************************************************************/
4044 struct key_val_struct {
4045 const char *keyname;
4046 int (*validate_data_fn)(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, struct tdb_validation_status* state);
4047 } key_val[] = {
4048 {"SEQNUM/", validate_seqnum},
4049 {"NS/", validate_ns},
4050 {"SN/", validate_sn},
4051 {"U/", validate_u},
4052 {"LOC_POL/", validate_loc_pol},
4053 {"PWD_POL/", validate_pwd_pol},
4054 {"CRED/", validate_cred},
4055 {"UL/", validate_ul},
4056 {"GL/", validate_gl},
4057 {"UG/", validate_ug},
4058 {"UA", validate_ua},
4059 {"GM/", validate_gm},
4060 {"DR/", validate_dr},
4061 {"DE/", validate_de},
4062 {"NSS/PWINFO/", validate_pwinfo},
4063 {"TRUSTDOMCACHE/", validate_trustdomcache},
4064 {"NSS/NA/", validate_nss_na},
4065 {"NSS/AN/", validate_nss_an},
4066 {"WINBINDD_OFFLINE", validate_offline},
4067 {"NDR/", validate_ndr},
4068 {WINBINDD_CACHE_VERSION_KEYSTR, validate_cache_version},
4069 {NULL, NULL}
4072 /***********************************************************************
4073 Function to look at every entry in the tdb and validate it as far as
4074 possible.
4075 ***********************************************************************/
4077 static int cache_traverse_validate_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, void *state)
4079 int i;
4080 unsigned int max_key_len = 1024;
4081 struct tdb_validation_status *v_state = (struct tdb_validation_status *)state;
4083 /* Paranoia check. */
4084 if (strncmp("UA/", (const char *)kbuf.dptr, 3) == 0 ||
4085 strncmp("NDR/", (const char *)kbuf.dptr, 4) == 0) {
4086 max_key_len = 1024 * 1024;
4088 if (kbuf.dsize > max_key_len) {
4089 DEBUG(0, ("cache_traverse_validate_fn: key length too large: "
4090 "(%u) > (%u)\n\n",
4091 (unsigned int)kbuf.dsize, (unsigned int)max_key_len));
4092 return 1;
4095 for (i = 0; key_val[i].keyname; i++) {
4096 size_t namelen = strlen(key_val[i].keyname);
4097 if (kbuf.dsize >= namelen && (
4098 strncmp(key_val[i].keyname, (const char *)kbuf.dptr, namelen)) == 0) {
4099 TALLOC_CTX *mem_ctx;
4100 char *keystr;
4101 int ret;
4103 keystr = SMB_MALLOC_ARRAY(char, kbuf.dsize+1);
4104 if (!keystr) {
4105 return 1;
4107 memcpy(keystr, kbuf.dptr, kbuf.dsize);
4108 keystr[kbuf.dsize] = '\0';
4110 mem_ctx = talloc_init("validate_ctx");
4111 if (!mem_ctx) {
4112 SAFE_FREE(keystr);
4113 return 1;
4116 ret = key_val[i].validate_data_fn(mem_ctx, keystr, dbuf,
4117 v_state);
4119 SAFE_FREE(keystr);
4120 talloc_destroy(mem_ctx);
4121 return ret;
4125 DEBUG(0,("cache_traverse_validate_fn: unknown cache entry\nkey :\n"));
4126 dump_data(0, (uint8 *)kbuf.dptr, kbuf.dsize);
4127 DEBUG(0,("data :\n"));
4128 dump_data(0, (uint8 *)dbuf.dptr, dbuf.dsize);
4129 v_state->unknown_key = true;
4130 v_state->success = false;
4131 return 1; /* terminate. */
4134 static void validate_panic(const char *const why)
4136 DEBUG(0,("validating cache: would panic %s\n", why ));
4137 DEBUGADD(0, ("exiting instead (cache validation mode)\n"));
4138 exit(47);
4141 static int wbcache_update_centry_fn(TDB_CONTEXT *tdb,
4142 TDB_DATA key,
4143 TDB_DATA data,
4144 void *state)
4146 uint64_t ctimeout;
4147 TDB_DATA blob;
4149 if (is_non_centry_key(key)) {
4150 return 0;
4153 if (data.dptr == NULL || data.dsize == 0) {
4154 if (tdb_delete(tdb, key) < 0) {
4155 DEBUG(0, ("tdb_delete for [%s] failed!\n",
4156 key.dptr));
4157 return 1;
4161 /* add timeout to blob (uint64_t) */
4162 blob.dsize = data.dsize + 8;
4164 blob.dptr = SMB_XMALLOC_ARRAY(uint8_t, blob.dsize);
4165 if (blob.dptr == NULL) {
4166 return 1;
4168 memset(blob.dptr, 0, blob.dsize);
4170 /* copy status and seqnum */
4171 memcpy(blob.dptr, data.dptr, 8);
4173 /* add timeout */
4174 ctimeout = lp_winbind_cache_time() + time(NULL);
4175 SBVAL(blob.dptr, 8, ctimeout);
4177 /* copy the rest */
4178 memcpy(blob.dptr + 16, data.dptr + 8, data.dsize - 8);
4180 if (tdb_store(tdb, key, blob, TDB_REPLACE) < 0) {
4181 DEBUG(0, ("tdb_store to update [%s] failed!\n",
4182 key.dptr));
4183 SAFE_FREE(blob.dptr);
4184 return 1;
4187 SAFE_FREE(blob.dptr);
4188 return 0;
4191 static bool wbcache_upgrade_v1_to_v2(TDB_CONTEXT *tdb)
4193 int rc;
4195 DEBUG(1, ("Upgrade to version 2 of the winbindd_cache.tdb\n"));
4197 rc = tdb_traverse(tdb, wbcache_update_centry_fn, NULL);
4198 if (rc < 0) {
4199 return false;
4202 return true;
4205 /***********************************************************************
4206 Try and validate every entry in the winbindd cache. If we fail here,
4207 delete the cache tdb and return non-zero.
4208 ***********************************************************************/
4210 int winbindd_validate_cache(void)
4212 int ret = -1;
4213 const char *tdb_path = state_path("winbindd_cache.tdb");
4214 TDB_CONTEXT *tdb = NULL;
4215 uint32_t vers_id;
4216 bool ok;
4218 DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
4219 smb_panic_fn = validate_panic;
4221 tdb = tdb_open_log(tdb_path,
4222 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
4223 TDB_INCOMPATIBLE_HASH |
4224 ( lp_winbind_offline_logon()
4225 ? TDB_DEFAULT
4226 : TDB_DEFAULT | TDB_CLEAR_IF_FIRST ),
4227 O_RDWR|O_CREAT,
4228 0600);
4229 if (!tdb) {
4230 DEBUG(0, ("winbindd_validate_cache: "
4231 "error opening/initializing tdb\n"));
4232 goto done;
4235 /* Version check and upgrade code. */
4236 if (!tdb_fetch_uint32(tdb, WINBINDD_CACHE_VERSION_KEYSTR, &vers_id)) {
4237 DEBUG(10, ("Fresh database\n"));
4238 tdb_store_uint32(tdb, WINBINDD_CACHE_VERSION_KEYSTR, WINBINDD_CACHE_VERSION);
4239 vers_id = WINBINDD_CACHE_VERSION;
4242 if (vers_id != WINBINDD_CACHE_VERSION) {
4243 if (vers_id == WINBINDD_CACHE_VER1) {
4244 ok = wbcache_upgrade_v1_to_v2(tdb);
4245 if (!ok) {
4246 DEBUG(10, ("winbindd_validate_cache: upgrade to version 2 failed.\n"));
4247 unlink(tdb_path);
4248 goto done;
4251 tdb_store_uint32(tdb,
4252 WINBINDD_CACHE_VERSION_KEYSTR,
4253 WINBINDD_CACHE_VERSION);
4254 vers_id = WINBINDD_CACHE_VER2;
4258 tdb_close(tdb);
4260 ret = tdb_validate_and_backup(tdb_path, cache_traverse_validate_fn);
4262 if (ret != 0) {
4263 DEBUG(10, ("winbindd_validate_cache: validation not successful.\n"));
4264 DEBUGADD(10, ("removing tdb %s.\n", tdb_path));
4265 unlink(tdb_path);
4268 done:
4269 DEBUG(10, ("winbindd_validate_cache: restoring panic function\n"));
4270 smb_panic_fn = smb_panic;
4271 return ret;
4274 /***********************************************************************
4275 Try and validate every entry in the winbindd cache.
4276 ***********************************************************************/
4278 int winbindd_validate_cache_nobackup(void)
4280 int ret = -1;
4281 const char *tdb_path = state_path("winbindd_cache.tdb");
4283 DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
4284 smb_panic_fn = validate_panic;
4287 if (wcache == NULL || wcache->tdb == NULL) {
4288 ret = tdb_validate_open(tdb_path, cache_traverse_validate_fn);
4289 } else {
4290 ret = tdb_validate(wcache->tdb, cache_traverse_validate_fn);
4293 if (ret != 0) {
4294 DEBUG(10, ("winbindd_validate_cache_nobackup: validation not "
4295 "successful.\n"));
4298 DEBUG(10, ("winbindd_validate_cache_nobackup: restoring panic "
4299 "function\n"));
4300 smb_panic_fn = smb_panic;
4301 return ret;
4304 bool winbindd_cache_validate_and_initialize(void)
4306 close_winbindd_cache();
4308 if (lp_winbind_offline_logon()) {
4309 if (winbindd_validate_cache() < 0) {
4310 DEBUG(0, ("winbindd cache tdb corrupt and no backup "
4311 "could be restored.\n"));
4315 return initialize_winbindd_cache();
4318 /*********************************************************************
4319 ********************************************************************/
4321 static bool add_wbdomain_to_tdc_array( struct winbindd_domain *new_dom,
4322 struct winbindd_tdc_domain **domains,
4323 size_t *num_domains )
4325 struct winbindd_tdc_domain *list = NULL;
4326 size_t idx;
4327 int i;
4328 bool set_only = false;
4330 /* don't allow duplicates */
4332 idx = *num_domains;
4333 list = *domains;
4335 for ( i=0; i< (*num_domains); i++ ) {
4336 if ( strequal( new_dom->name, list[i].domain_name ) ) {
4337 DEBUG(10,("add_wbdomain_to_tdc_array: Found existing record for %s\n",
4338 new_dom->name));
4339 idx = i;
4340 set_only = true;
4342 break;
4346 if ( !set_only ) {
4347 if ( !*domains ) {
4348 list = talloc_array( NULL, struct winbindd_tdc_domain, 1 );
4349 idx = 0;
4350 } else {
4351 list = talloc_realloc( *domains, *domains,
4352 struct winbindd_tdc_domain,
4353 (*num_domains)+1);
4354 idx = *num_domains;
4357 ZERO_STRUCT( list[idx] );
4360 if ( !list )
4361 return false;
4363 list[idx].domain_name = talloc_strdup(list, new_dom->name);
4364 if (list[idx].domain_name == NULL) {
4365 return false;
4367 if (new_dom->alt_name != NULL) {
4368 list[idx].dns_name = talloc_strdup(list, new_dom->alt_name);
4369 if (list[idx].dns_name == NULL) {
4370 return false;
4374 if ( !is_null_sid( &new_dom->sid ) ) {
4375 sid_copy( &list[idx].sid, &new_dom->sid );
4376 } else {
4377 sid_copy(&list[idx].sid, &global_sid_NULL);
4380 if ( new_dom->domain_flags != 0x0 )
4381 list[idx].trust_flags = new_dom->domain_flags;
4383 if ( new_dom->domain_type != 0x0 )
4384 list[idx].trust_type = new_dom->domain_type;
4386 if ( new_dom->domain_trust_attribs != 0x0 )
4387 list[idx].trust_attribs = new_dom->domain_trust_attribs;
4389 if ( !set_only ) {
4390 *domains = list;
4391 *num_domains = idx + 1;
4394 return true;
4397 /*********************************************************************
4398 ********************************************************************/
4400 static TDB_DATA make_tdc_key( const char *domain_name )
4402 char *keystr = NULL;
4403 TDB_DATA key = { NULL, 0 };
4405 if ( !domain_name ) {
4406 DEBUG(5,("make_tdc_key: Keyname workgroup is NULL!\n"));
4407 return key;
4410 if (asprintf( &keystr, "TRUSTDOMCACHE/%s", domain_name ) == -1) {
4411 return key;
4413 key = string_term_tdb_data(keystr);
4415 return key;
4418 /*********************************************************************
4419 ********************************************************************/
4421 static int pack_tdc_domains( struct winbindd_tdc_domain *domains,
4422 size_t num_domains,
4423 unsigned char **buf )
4425 unsigned char *buffer = NULL;
4426 int len = 0;
4427 int buflen = 0;
4428 int i = 0;
4430 DEBUG(10,("pack_tdc_domains: Packing %d trusted domains\n",
4431 (int)num_domains));
4433 buflen = 0;
4435 again:
4436 len = 0;
4438 /* Store the number of array items first */
4439 len += tdb_pack( buffer+len, buflen-len, "d",
4440 num_domains );
4442 /* now pack each domain trust record */
4443 for ( i=0; i<num_domains; i++ ) {
4445 fstring tmp;
4447 if ( buflen > 0 ) {
4448 DEBUG(10,("pack_tdc_domains: Packing domain %s (%s)\n",
4449 domains[i].domain_name,
4450 domains[i].dns_name ? domains[i].dns_name : "UNKNOWN" ));
4453 len += tdb_pack( buffer+len, buflen-len, "fffddd",
4454 domains[i].domain_name,
4455 domains[i].dns_name ? domains[i].dns_name : "",
4456 sid_to_fstring(tmp, &domains[i].sid),
4457 domains[i].trust_flags,
4458 domains[i].trust_attribs,
4459 domains[i].trust_type );
4462 if ( buflen < len ) {
4463 SAFE_FREE(buffer);
4464 if ( (buffer = SMB_MALLOC_ARRAY(unsigned char, len)) == NULL ) {
4465 DEBUG(0,("pack_tdc_domains: failed to alloc buffer!\n"));
4466 buflen = -1;
4467 goto done;
4469 buflen = len;
4470 goto again;
4473 *buf = buffer;
4475 done:
4476 return buflen;
4479 /*********************************************************************
4480 ********************************************************************/
4482 static size_t unpack_tdc_domains( unsigned char *buf, int buflen,
4483 struct winbindd_tdc_domain **domains )
4485 fstring domain_name, dns_name, sid_string;
4486 uint32 type, attribs, flags;
4487 int num_domains;
4488 int len = 0;
4489 int i;
4490 struct winbindd_tdc_domain *list = NULL;
4492 /* get the number of domains */
4493 len += tdb_unpack( buf+len, buflen-len, "d", &num_domains);
4494 if ( len == -1 ) {
4495 DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
4496 return 0;
4499 list = talloc_array( NULL, struct winbindd_tdc_domain, num_domains );
4500 if ( !list ) {
4501 DEBUG(0,("unpack_tdc_domains: Failed to talloc() domain list!\n"));
4502 return 0;
4505 for ( i=0; i<num_domains; i++ ) {
4506 int this_len;
4508 this_len = tdb_unpack( buf+len, buflen-len, "fffddd",
4509 domain_name,
4510 dns_name,
4511 sid_string,
4512 &flags,
4513 &attribs,
4514 &type );
4516 if ( this_len == -1 ) {
4517 DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
4518 TALLOC_FREE( list );
4519 return 0;
4521 len += this_len;
4523 DEBUG(11,("unpack_tdc_domains: Unpacking domain %s (%s) "
4524 "SID %s, flags = 0x%x, attribs = 0x%x, type = 0x%x\n",
4525 domain_name, dns_name, sid_string,
4526 flags, attribs, type));
4528 list[i].domain_name = talloc_strdup( list, domain_name );
4529 list[i].dns_name = NULL;
4530 if (dns_name[0] != '\0') {
4531 list[i].dns_name = talloc_strdup(list, dns_name);
4533 if ( !string_to_sid( &(list[i].sid), sid_string ) ) {
4534 DEBUG(10,("unpack_tdc_domains: no SID for domain %s\n",
4535 domain_name));
4537 list[i].trust_flags = flags;
4538 list[i].trust_attribs = attribs;
4539 list[i].trust_type = type;
4542 *domains = list;
4544 return num_domains;
4547 /*********************************************************************
4548 ********************************************************************/
4550 static bool wcache_tdc_store_list( struct winbindd_tdc_domain *domains, size_t num_domains )
4552 TDB_DATA key = make_tdc_key( lp_workgroup() );
4553 TDB_DATA data = { NULL, 0 };
4554 int ret;
4556 if ( !key.dptr )
4557 return false;
4559 /* See if we were asked to delete the cache entry */
4561 if ( !domains ) {
4562 ret = tdb_delete( wcache->tdb, key );
4563 goto done;
4566 data.dsize = pack_tdc_domains( domains, num_domains, &data.dptr );
4568 if ( !data.dptr ) {
4569 ret = -1;
4570 goto done;
4573 ret = tdb_store( wcache->tdb, key, data, 0 );
4575 done:
4576 SAFE_FREE( data.dptr );
4577 SAFE_FREE( key.dptr );
4579 return ( ret == 0 );
4582 /*********************************************************************
4583 ********************************************************************/
4585 bool wcache_tdc_fetch_list( struct winbindd_tdc_domain **domains, size_t *num_domains )
4587 TDB_DATA key = make_tdc_key( lp_workgroup() );
4588 TDB_DATA data = { NULL, 0 };
4590 *domains = NULL;
4591 *num_domains = 0;
4593 if ( !key.dptr )
4594 return false;
4596 data = tdb_fetch_compat( wcache->tdb, key );
4598 SAFE_FREE( key.dptr );
4600 if ( !data.dptr )
4601 return false;
4603 *num_domains = unpack_tdc_domains( data.dptr, data.dsize, domains );
4605 SAFE_FREE( data.dptr );
4607 if ( !*domains )
4608 return false;
4610 return true;
4613 /*********************************************************************
4614 ********************************************************************/
4616 bool wcache_tdc_add_domain( struct winbindd_domain *domain )
4618 struct winbindd_tdc_domain *dom_list = NULL;
4619 size_t num_domains = 0;
4620 bool ret = false;
4622 DEBUG(10,("wcache_tdc_add_domain: Adding domain %s (%s), SID %s, "
4623 "flags = 0x%x, attributes = 0x%x, type = 0x%x\n",
4624 domain->name, domain->alt_name,
4625 sid_string_dbg(&domain->sid),
4626 domain->domain_flags,
4627 domain->domain_trust_attribs,
4628 domain->domain_type));
4630 if ( !init_wcache() ) {
4631 return false;
4634 /* fetch the list */
4636 wcache_tdc_fetch_list( &dom_list, &num_domains );
4638 /* add the new domain */
4640 if ( !add_wbdomain_to_tdc_array( domain, &dom_list, &num_domains ) ) {
4641 goto done;
4644 /* pack the domain */
4646 if ( !wcache_tdc_store_list( dom_list, num_domains ) ) {
4647 goto done;
4650 /* Success */
4652 ret = true;
4653 done:
4654 TALLOC_FREE( dom_list );
4656 return ret;
4659 static struct winbindd_tdc_domain *wcache_tdc_dup_domain(
4660 TALLOC_CTX *mem_ctx, const struct winbindd_tdc_domain *src)
4662 struct winbindd_tdc_domain *dst;
4664 dst = talloc(mem_ctx, struct winbindd_tdc_domain);
4665 if (dst == NULL) {
4666 goto fail;
4668 dst->domain_name = talloc_strdup(dst, src->domain_name);
4669 if (dst->domain_name == NULL) {
4670 goto fail;
4673 dst->dns_name = NULL;
4674 if (src->dns_name != NULL) {
4675 dst->dns_name = talloc_strdup(dst, src->dns_name);
4676 if (dst->dns_name == NULL) {
4677 goto fail;
4681 sid_copy(&dst->sid, &src->sid);
4682 dst->trust_flags = src->trust_flags;
4683 dst->trust_type = src->trust_type;
4684 dst->trust_attribs = src->trust_attribs;
4685 return dst;
4686 fail:
4687 TALLOC_FREE(dst);
4688 return NULL;
4691 /*********************************************************************
4692 ********************************************************************/
4694 struct winbindd_tdc_domain * wcache_tdc_fetch_domain( TALLOC_CTX *ctx, const char *name )
4696 struct winbindd_tdc_domain *dom_list = NULL;
4697 size_t num_domains = 0;
4698 int i;
4699 struct winbindd_tdc_domain *d = NULL;
4701 DEBUG(10,("wcache_tdc_fetch_domain: Searching for domain %s\n", name));
4703 if ( !init_wcache() ) {
4704 return NULL;
4707 /* fetch the list */
4709 wcache_tdc_fetch_list( &dom_list, &num_domains );
4711 for ( i=0; i<num_domains; i++ ) {
4712 if ( strequal(name, dom_list[i].domain_name) ||
4713 strequal(name, dom_list[i].dns_name) )
4715 DEBUG(10,("wcache_tdc_fetch_domain: Found domain %s\n",
4716 name));
4718 d = wcache_tdc_dup_domain(ctx, &dom_list[i]);
4719 break;
4723 TALLOC_FREE( dom_list );
4725 return d;
4728 /*********************************************************************
4729 ********************************************************************/
4731 struct winbindd_tdc_domain*
4732 wcache_tdc_fetch_domainbysid(TALLOC_CTX *ctx,
4733 const struct dom_sid *sid)
4735 struct winbindd_tdc_domain *dom_list = NULL;
4736 size_t num_domains = 0;
4737 int i;
4738 struct winbindd_tdc_domain *d = NULL;
4740 DEBUG(10,("wcache_tdc_fetch_domainbysid: Searching for domain %s\n",
4741 sid_string_dbg(sid)));
4743 if (!init_wcache()) {
4744 return NULL;
4747 /* fetch the list */
4749 wcache_tdc_fetch_list(&dom_list, &num_domains);
4751 for (i = 0; i<num_domains; i++) {
4752 if (dom_sid_equal(sid, &(dom_list[i].sid))) {
4753 DEBUG(10, ("wcache_tdc_fetch_domainbysid: "
4754 "Found domain %s for SID %s\n",
4755 dom_list[i].domain_name,
4756 sid_string_dbg(sid)));
4758 d = wcache_tdc_dup_domain(ctx, &dom_list[i]);
4759 break;
4763 TALLOC_FREE(dom_list);
4765 return d;
4769 /*********************************************************************
4770 ********************************************************************/
4772 void wcache_tdc_clear( void )
4774 if ( !init_wcache() )
4775 return;
4777 wcache_tdc_store_list( NULL, 0 );
4779 return;
4783 /*********************************************************************
4784 ********************************************************************/
4786 static void wcache_save_user_pwinfo(struct winbindd_domain *domain,
4787 NTSTATUS status,
4788 const struct dom_sid *user_sid,
4789 const char *homedir,
4790 const char *shell,
4791 const char *gecos,
4792 uint32 gid)
4794 struct cache_entry *centry;
4795 fstring tmp;
4797 if ( (centry = centry_start(domain, status)) == NULL )
4798 return;
4800 centry_put_string( centry, homedir );
4801 centry_put_string( centry, shell );
4802 centry_put_string( centry, gecos );
4803 centry_put_uint32( centry, gid );
4805 centry_end(centry, "NSS/PWINFO/%s", sid_to_fstring(tmp, user_sid) );
4807 DEBUG(10,("wcache_save_user_pwinfo: %s\n", sid_string_dbg(user_sid) ));
4809 centry_free(centry);
4812 #ifdef HAVE_ADS
4814 NTSTATUS nss_get_info_cached( struct winbindd_domain *domain,
4815 const struct dom_sid *user_sid,
4816 TALLOC_CTX *ctx,
4817 const char **homedir, const char **shell,
4818 const char **gecos, gid_t *p_gid)
4820 struct winbind_cache *cache = get_cache(domain);
4821 struct cache_entry *centry = NULL;
4822 NTSTATUS nt_status;
4823 fstring tmp;
4825 if (!cache->tdb)
4826 goto do_query;
4828 centry = wcache_fetch(cache, domain, "NSS/PWINFO/%s",
4829 sid_to_fstring(tmp, user_sid));
4831 if (!centry)
4832 goto do_query;
4834 *homedir = centry_string( centry, ctx );
4835 *shell = centry_string( centry, ctx );
4836 *gecos = centry_string( centry, ctx );
4837 *p_gid = centry_uint32( centry );
4839 centry_free(centry);
4841 DEBUG(10,("nss_get_info_cached: [Cached] - user_sid %s\n",
4842 sid_string_dbg(user_sid)));
4844 return NT_STATUS_OK;
4846 do_query:
4848 nt_status = nss_get_info( domain->name, user_sid, ctx,
4849 homedir, shell, gecos, p_gid );
4851 DEBUG(10, ("nss_get_info returned %s\n", nt_errstr(nt_status)));
4853 if ( NT_STATUS_IS_OK(nt_status) ) {
4854 DEBUG(10, ("result:\n\thomedir = '%s'\n", *homedir));
4855 DEBUGADD(10, ("\tshell = '%s'\n", *shell));
4856 DEBUGADD(10, ("\tgecos = '%s'\n", *gecos));
4857 DEBUGADD(10, ("\tgid = '%u'\n", (unsigned int)*p_gid));
4859 wcache_save_user_pwinfo( domain, nt_status, user_sid,
4860 *homedir, *shell, *gecos, *p_gid );
4863 if ( NT_STATUS_EQUAL( nt_status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND ) ) {
4864 DEBUG(5,("nss_get_info_cached: Setting domain %s offline\n",
4865 domain->name ));
4866 set_domain_offline( domain );
4869 return nt_status;
4872 #endif
4874 /* the cache backend methods are exposed via this structure */
4875 struct winbindd_methods cache_methods = {
4876 true,
4877 query_user_list,
4878 enum_dom_groups,
4879 enum_local_groups,
4880 name_to_sid,
4881 sid_to_name,
4882 rids_to_names,
4883 query_user,
4884 lookup_usergroups,
4885 lookup_useraliases,
4886 lookup_groupmem,
4887 sequence_number,
4888 lockout_policy,
4889 password_policy,
4890 trusted_domains
4893 static bool wcache_ndr_key(TALLOC_CTX *mem_ctx, const char *domain_name,
4894 uint32_t opnum, const DATA_BLOB *req,
4895 TDB_DATA *pkey)
4897 char *key;
4898 size_t keylen;
4900 key = talloc_asprintf(mem_ctx, "NDR/%s/%d/", domain_name, (int)opnum);
4901 if (key == NULL) {
4902 return false;
4904 keylen = talloc_get_size(key) - 1;
4906 key = talloc_realloc(mem_ctx, key, char, keylen + req->length);
4907 if (key == NULL) {
4908 return false;
4910 memcpy(key + keylen, req->data, req->length);
4912 pkey->dptr = (uint8_t *)key;
4913 pkey->dsize = talloc_get_size(key);
4914 return true;
4917 static bool wcache_opnum_cacheable(uint32_t opnum)
4919 switch (opnum) {
4920 case NDR_WBINT_PING:
4921 case NDR_WBINT_QUERYSEQUENCENUMBER:
4922 case NDR_WBINT_ALLOCATEUID:
4923 case NDR_WBINT_ALLOCATEGID:
4924 case NDR_WBINT_CHECKMACHINEACCOUNT:
4925 case NDR_WBINT_CHANGEMACHINEACCOUNT:
4926 case NDR_WBINT_PINGDC:
4927 return false;
4929 return true;
4932 bool wcache_fetch_ndr(TALLOC_CTX *mem_ctx, struct winbindd_domain *domain,
4933 uint32_t opnum, const DATA_BLOB *req, DATA_BLOB *resp)
4935 TDB_DATA key, data;
4936 bool ret = false;
4938 if (!wcache_opnum_cacheable(opnum) ||
4939 is_my_own_sam_domain(domain) ||
4940 is_builtin_domain(domain)) {
4941 return false;
4944 if (wcache->tdb == NULL) {
4945 return false;
4948 if (!wcache_ndr_key(talloc_tos(), domain->name, opnum, req, &key)) {
4949 return false;
4951 data = tdb_fetch_compat(wcache->tdb, key);
4952 TALLOC_FREE(key.dptr);
4954 if (data.dptr == NULL) {
4955 return false;
4957 if (data.dsize < 12) {
4958 goto fail;
4961 if (!is_domain_offline(domain)) {
4962 uint32_t entry_seqnum, dom_seqnum, last_check;
4963 uint64_t entry_timeout;
4965 if (!wcache_fetch_seqnum(domain->name, &dom_seqnum,
4966 &last_check)) {
4967 goto fail;
4969 entry_seqnum = IVAL(data.dptr, 0);
4970 if (entry_seqnum != dom_seqnum) {
4971 DEBUG(10, ("Entry has wrong sequence number: %d\n",
4972 (int)entry_seqnum));
4973 goto fail;
4975 entry_timeout = BVAL(data.dptr, 4);
4976 if (time(NULL) > entry_timeout) {
4977 DEBUG(10, ("Entry has timed out\n"));
4978 goto fail;
4982 resp->data = (uint8_t *)talloc_memdup(mem_ctx, data.dptr + 12,
4983 data.dsize - 12);
4984 if (resp->data == NULL) {
4985 DEBUG(10, ("talloc failed\n"));
4986 goto fail;
4988 resp->length = data.dsize - 12;
4990 ret = true;
4991 fail:
4992 SAFE_FREE(data.dptr);
4993 return ret;
4996 void wcache_store_ndr(struct winbindd_domain *domain, uint32_t opnum,
4997 const DATA_BLOB *req, const DATA_BLOB *resp)
4999 TDB_DATA key, data;
5000 uint32_t dom_seqnum, last_check;
5001 uint64_t timeout;
5003 if (!wcache_opnum_cacheable(opnum) ||
5004 is_my_own_sam_domain(domain) ||
5005 is_builtin_domain(domain)) {
5006 return;
5009 if (wcache->tdb == NULL) {
5010 return;
5013 if (!wcache_fetch_seqnum(domain->name, &dom_seqnum, &last_check)) {
5014 DEBUG(10, ("could not fetch seqnum for domain %s\n",
5015 domain->name));
5016 return;
5019 if (!wcache_ndr_key(talloc_tos(), domain->name, opnum, req, &key)) {
5020 return;
5023 timeout = time(NULL) + lp_winbind_cache_time();
5025 data.dsize = resp->length + 12;
5026 data.dptr = talloc_array(key.dptr, uint8_t, data.dsize);
5027 if (data.dptr == NULL) {
5028 goto done;
5031 SIVAL(data.dptr, 0, dom_seqnum);
5032 SBVAL(data.dptr, 4, timeout);
5033 memcpy(data.dptr + 12, resp->data, resp->length);
5035 tdb_store(wcache->tdb, key, data, 0);
5037 done:
5038 TALLOC_FREE(key.dptr);
5039 return;