winbind: Don't leak centry memory. Reviewed-by: Alexander Bokovoy <ab@samba.org>
[Samba.git] / source3 / winbindd / winbindd_cache.c
blob81b9b1799b8f8107df6882985c9bba79c4958b43
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_wbint.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 "DR/",
63 "DE/",
64 "WINBINDD_OFFLINE",
65 WINBINDD_CACHE_VERSION_KEYSTR,
66 NULL
69 /************************************************************************
70 Is this key a non-centry type ?
71 ************************************************************************/
73 static bool is_non_centry_key(TDB_DATA kbuf)
75 int i;
77 if (kbuf.dptr == NULL || kbuf.dsize == 0) {
78 return false;
80 for (i = 0; non_centry_keys[i] != NULL; i++) {
81 size_t namelen = strlen(non_centry_keys[i]);
82 if (kbuf.dsize < namelen) {
83 continue;
85 if (strncmp(non_centry_keys[i], (const char *)kbuf.dptr, namelen) == 0) {
86 return true;
89 return false;
92 /* Global online/offline state - False when online. winbindd starts up online
93 and sets this to true if the first query fails and there's an entry in
94 the cache tdb telling us to stay offline. */
96 static bool global_winbindd_offline_state;
98 struct winbind_cache {
99 TDB_CONTEXT *tdb;
102 struct cache_entry {
103 NTSTATUS status;
104 uint32 sequence_number;
105 uint64_t timeout;
106 uint8 *data;
107 uint32 len, ofs;
110 void (*smb_panic_fn)(const char *const why) = smb_panic;
112 #define WINBINDD_MAX_CACHE_SIZE (50*1024*1024)
114 static struct winbind_cache *wcache;
116 /* get the winbind_cache structure */
117 static struct winbind_cache *get_cache(struct winbindd_domain *domain)
119 struct winbind_cache *ret = wcache;
121 /* We have to know what type of domain we are dealing with first. */
123 if (domain->internal) {
124 domain->backend = &builtin_passdb_methods;
125 domain->initialized = True;
128 if (strequal(domain->name, get_global_sam_name()) &&
129 sid_check_is_domain(&domain->sid)) {
130 domain->backend = &sam_passdb_methods;
131 domain->initialized = True;
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 == -1) {
491 DEBUG(10, ("tdb_store_bystring failed: %s\n",
492 tdb_errorstr(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(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_domain(&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 struct cache_entry *centry_start(struct winbindd_domain *domain, NTSTATUS status)
893 struct cache_entry *centry;
895 if (!wcache->tdb)
896 return NULL;
898 centry = SMB_XMALLOC_P(struct cache_entry);
900 centry->len = 8192; /* reasonable default */
901 centry->data = SMB_XMALLOC_ARRAY(uint8, centry->len);
902 centry->ofs = 0;
903 centry->sequence_number = domain->sequence_number;
904 centry->timeout = lp_winbind_cache_time() + time(NULL);
905 centry_put_ntstatus(centry, status);
906 centry_put_uint32(centry, centry->sequence_number);
907 centry_put_uint64_t(centry, centry->timeout);
908 return centry;
912 finish a centry and write it to the tdb
914 static void centry_end(struct cache_entry *centry, const char *format, ...) PRINTF_ATTRIBUTE(2,3);
915 static void centry_end(struct cache_entry *centry, const char *format, ...)
917 va_list ap;
918 char *kstr;
919 TDB_DATA key, data;
921 if (!winbindd_use_cache()) {
922 return;
925 va_start(ap, format);
926 smb_xvasprintf(&kstr, format, ap);
927 va_end(ap);
929 key = string_tdb_data(kstr);
930 data.dptr = centry->data;
931 data.dsize = centry->ofs;
933 tdb_store(wcache->tdb, key, data, TDB_REPLACE);
934 free(kstr);
937 static void wcache_save_name_to_sid(struct winbindd_domain *domain,
938 NTSTATUS status, const char *domain_name,
939 const char *name, const struct dom_sid *sid,
940 enum lsa_SidType type)
942 struct cache_entry *centry;
943 fstring uname;
945 centry = centry_start(domain, status);
946 if (!centry)
947 return;
948 centry_put_uint32(centry, type);
949 centry_put_sid(centry, sid);
950 fstrcpy(uname, name);
951 strupper_m(uname);
952 centry_end(centry, "NS/%s/%s", domain_name, uname);
953 DEBUG(10,("wcache_save_name_to_sid: %s\\%s -> %s (%s)\n", domain_name,
954 uname, sid_string_dbg(sid), nt_errstr(status)));
955 centry_free(centry);
958 static void wcache_save_sid_to_name(struct winbindd_domain *domain, NTSTATUS status,
959 const struct dom_sid *sid, const char *domain_name, const char *name, enum lsa_SidType type)
961 struct cache_entry *centry;
962 fstring sid_string;
964 centry = centry_start(domain, status);
965 if (!centry)
966 return;
968 if (NT_STATUS_IS_OK(status)) {
969 centry_put_uint32(centry, type);
970 centry_put_string(centry, domain_name);
971 centry_put_string(centry, name);
974 centry_end(centry, "SN/%s", sid_to_fstring(sid_string, sid));
975 DEBUG(10,("wcache_save_sid_to_name: %s -> %s (%s)\n", sid_string,
976 name, nt_errstr(status)));
977 centry_free(centry);
981 static void wcache_save_user(struct winbindd_domain *domain, NTSTATUS status,
982 struct wbint_userinfo *info)
984 struct cache_entry *centry;
985 fstring sid_string;
987 if (is_null_sid(&info->user_sid)) {
988 return;
991 centry = centry_start(domain, status);
992 if (!centry)
993 return;
994 centry_put_string(centry, info->acct_name);
995 centry_put_string(centry, info->full_name);
996 centry_put_string(centry, info->homedir);
997 centry_put_string(centry, info->shell);
998 centry_put_uint32(centry, info->primary_gid);
999 centry_put_sid(centry, &info->user_sid);
1000 centry_put_sid(centry, &info->group_sid);
1001 centry_end(centry, "U/%s", sid_to_fstring(sid_string,
1002 &info->user_sid));
1003 DEBUG(10,("wcache_save_user: %s (acct_name %s)\n", sid_string, info->acct_name));
1004 centry_free(centry);
1007 static void wcache_save_lockout_policy(struct winbindd_domain *domain,
1008 NTSTATUS status,
1009 struct samr_DomInfo12 *lockout_policy)
1011 struct cache_entry *centry;
1013 centry = centry_start(domain, status);
1014 if (!centry)
1015 return;
1017 centry_put_nttime(centry, lockout_policy->lockout_duration);
1018 centry_put_nttime(centry, lockout_policy->lockout_window);
1019 centry_put_uint16(centry, lockout_policy->lockout_threshold);
1021 centry_end(centry, "LOC_POL/%s", domain->name);
1023 DEBUG(10,("wcache_save_lockout_policy: %s\n", domain->name));
1025 centry_free(centry);
1030 static void wcache_save_password_policy(struct winbindd_domain *domain,
1031 NTSTATUS status,
1032 struct samr_DomInfo1 *policy)
1034 struct cache_entry *centry;
1036 centry = centry_start(domain, status);
1037 if (!centry)
1038 return;
1040 centry_put_uint16(centry, policy->min_password_length);
1041 centry_put_uint16(centry, policy->password_history_length);
1042 centry_put_uint32(centry, policy->password_properties);
1043 centry_put_nttime(centry, policy->max_password_age);
1044 centry_put_nttime(centry, policy->min_password_age);
1046 centry_end(centry, "PWD_POL/%s", domain->name);
1048 DEBUG(10,("wcache_save_password_policy: %s\n", domain->name));
1050 centry_free(centry);
1053 /***************************************************************************
1054 ***************************************************************************/
1056 static void wcache_save_username_alias(struct winbindd_domain *domain,
1057 NTSTATUS status,
1058 const char *name, const char *alias)
1060 struct cache_entry *centry;
1061 fstring uname;
1063 if ( (centry = centry_start(domain, status)) == NULL )
1064 return;
1066 centry_put_string( centry, alias );
1068 fstrcpy(uname, name);
1069 strupper_m(uname);
1070 centry_end(centry, "NSS/NA/%s", uname);
1072 DEBUG(10,("wcache_save_username_alias: %s -> %s\n", name, alias ));
1074 centry_free(centry);
1077 static void wcache_save_alias_username(struct winbindd_domain *domain,
1078 NTSTATUS status,
1079 const char *alias, const char *name)
1081 struct cache_entry *centry;
1082 fstring uname;
1084 if ( (centry = centry_start(domain, status)) == NULL )
1085 return;
1087 centry_put_string( centry, name );
1089 fstrcpy(uname, alias);
1090 strupper_m(uname);
1091 centry_end(centry, "NSS/AN/%s", uname);
1093 DEBUG(10,("wcache_save_alias_username: %s -> %s\n", alias, name ));
1095 centry_free(centry);
1098 /***************************************************************************
1099 ***************************************************************************/
1101 NTSTATUS resolve_username_to_alias( TALLOC_CTX *mem_ctx,
1102 struct winbindd_domain *domain,
1103 const char *name, char **alias )
1105 struct winbind_cache *cache = get_cache(domain);
1106 struct cache_entry *centry = NULL;
1107 NTSTATUS status;
1108 char *upper_name;
1110 if ( domain->internal )
1111 return NT_STATUS_NOT_SUPPORTED;
1113 if (!cache->tdb)
1114 goto do_query;
1116 if ( (upper_name = SMB_STRDUP(name)) == NULL )
1117 return NT_STATUS_NO_MEMORY;
1118 strupper_m(upper_name);
1120 centry = wcache_fetch(cache, domain, "NSS/NA/%s", upper_name);
1122 SAFE_FREE( upper_name );
1124 if (!centry)
1125 goto do_query;
1127 status = centry->status;
1129 if (!NT_STATUS_IS_OK(status)) {
1130 centry_free(centry);
1131 return status;
1134 *alias = centry_string( centry, mem_ctx );
1136 centry_free(centry);
1138 DEBUG(10,("resolve_username_to_alias: [Cached] - mapped %s to %s\n",
1139 name, *alias ? *alias : "(none)"));
1141 return (*alias) ? NT_STATUS_OK : NT_STATUS_OBJECT_NAME_NOT_FOUND;
1143 do_query:
1145 /* If its not in cache and we are offline, then fail */
1147 if ( get_global_winbindd_state_offline() || !domain->online ) {
1148 DEBUG(8,("resolve_username_to_alias: rejecting query "
1149 "in offline mode\n"));
1150 return NT_STATUS_NOT_FOUND;
1153 status = nss_map_to_alias( mem_ctx, domain->name, name, alias );
1155 if ( NT_STATUS_IS_OK( status ) ) {
1156 wcache_save_username_alias(domain, status, name, *alias);
1159 if ( NT_STATUS_EQUAL( status, NT_STATUS_NONE_MAPPED ) ) {
1160 wcache_save_username_alias(domain, status, name, "(NULL)");
1163 DEBUG(5,("resolve_username_to_alias: backend query returned %s\n",
1164 nt_errstr(status)));
1166 if ( NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ) {
1167 set_domain_offline( domain );
1170 return status;
1173 /***************************************************************************
1174 ***************************************************************************/
1176 NTSTATUS resolve_alias_to_username( TALLOC_CTX *mem_ctx,
1177 struct winbindd_domain *domain,
1178 const char *alias, char **name )
1180 struct winbind_cache *cache = get_cache(domain);
1181 struct cache_entry *centry = NULL;
1182 NTSTATUS status;
1183 char *upper_name;
1185 if ( domain->internal )
1186 return NT_STATUS_NOT_SUPPORTED;
1188 if (!cache->tdb)
1189 goto do_query;
1191 if ( (upper_name = SMB_STRDUP(alias)) == NULL )
1192 return NT_STATUS_NO_MEMORY;
1193 strupper_m(upper_name);
1195 centry = wcache_fetch(cache, domain, "NSS/AN/%s", upper_name);
1197 SAFE_FREE( upper_name );
1199 if (!centry)
1200 goto do_query;
1202 status = centry->status;
1204 if (!NT_STATUS_IS_OK(status)) {
1205 centry_free(centry);
1206 return status;
1209 *name = centry_string( centry, mem_ctx );
1211 centry_free(centry);
1213 DEBUG(10,("resolve_alias_to_username: [Cached] - mapped %s to %s\n",
1214 alias, *name ? *name : "(none)"));
1216 return (*name) ? NT_STATUS_OK : NT_STATUS_OBJECT_NAME_NOT_FOUND;
1218 do_query:
1220 /* If its not in cache and we are offline, then fail */
1222 if ( get_global_winbindd_state_offline() || !domain->online ) {
1223 DEBUG(8,("resolve_alias_to_username: rejecting query "
1224 "in offline mode\n"));
1225 return NT_STATUS_NOT_FOUND;
1228 /* an alias cannot contain a domain prefix or '@' */
1230 if (strchr(alias, '\\') || strchr(alias, '@')) {
1231 DEBUG(10,("resolve_alias_to_username: skipping fully "
1232 "qualified name %s\n", alias));
1233 return NT_STATUS_OBJECT_NAME_INVALID;
1236 status = nss_map_from_alias( mem_ctx, domain->name, alias, name );
1238 if ( NT_STATUS_IS_OK( status ) ) {
1239 wcache_save_alias_username( domain, status, alias, *name );
1242 if (NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) {
1243 wcache_save_alias_username(domain, status, alias, "(NULL)");
1246 DEBUG(5,("resolve_alias_to_username: backend query returned %s\n",
1247 nt_errstr(status)));
1249 if ( NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ) {
1250 set_domain_offline( domain );
1253 return status;
1256 NTSTATUS wcache_cached_creds_exist(struct winbindd_domain *domain, const struct dom_sid *sid)
1258 struct winbind_cache *cache = get_cache(domain);
1259 TDB_DATA data;
1260 fstring key_str, tmp;
1261 uint32 rid;
1263 if (!cache->tdb) {
1264 return NT_STATUS_INTERNAL_DB_ERROR;
1267 if (is_null_sid(sid)) {
1268 return NT_STATUS_INVALID_SID;
1271 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1272 return NT_STATUS_INVALID_SID;
1275 fstr_sprintf(key_str, "CRED/%s", sid_to_fstring(tmp, sid));
1277 data = tdb_fetch(cache->tdb, string_tdb_data(key_str));
1278 if (!data.dptr) {
1279 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1282 SAFE_FREE(data.dptr);
1283 return NT_STATUS_OK;
1286 /* Lookup creds for a SID - copes with old (unsalted) creds as well
1287 as new salted ones. */
1289 NTSTATUS wcache_get_creds(struct winbindd_domain *domain,
1290 TALLOC_CTX *mem_ctx,
1291 const struct dom_sid *sid,
1292 const uint8 **cached_nt_pass,
1293 const uint8 **cached_salt)
1295 struct winbind_cache *cache = get_cache(domain);
1296 struct cache_entry *centry = NULL;
1297 NTSTATUS status;
1298 time_t t;
1299 uint32 rid;
1300 fstring tmp;
1302 if (!cache->tdb) {
1303 return NT_STATUS_INTERNAL_DB_ERROR;
1306 if (is_null_sid(sid)) {
1307 return NT_STATUS_INVALID_SID;
1310 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1311 return NT_STATUS_INVALID_SID;
1314 /* Try and get a salted cred first. If we can't
1315 fall back to an unsalted cred. */
1317 centry = wcache_fetch(cache, domain, "CRED/%s",
1318 sid_to_fstring(tmp, sid));
1319 if (!centry) {
1320 DEBUG(10,("wcache_get_creds: entry for [CRED/%s] not found\n",
1321 sid_string_dbg(sid)));
1322 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1325 t = centry_time(centry);
1327 /* In the salted case this isn't actually the nt_hash itself,
1328 but the MD5 of the salt + nt_hash. Let the caller
1329 sort this out. It can tell as we only return the cached_salt
1330 if we are returning a salted cred. */
1332 *cached_nt_pass = (const uint8 *)centry_hash16(centry, mem_ctx);
1333 if (*cached_nt_pass == NULL) {
1334 fstring sidstr;
1336 sid_to_fstring(sidstr, sid);
1338 /* Bad (old) cred cache. Delete and pretend we
1339 don't have it. */
1340 DEBUG(0,("wcache_get_creds: bad entry for [CRED/%s] - deleting\n",
1341 sidstr));
1342 wcache_delete("CRED/%s", sidstr);
1343 centry_free(centry);
1344 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1347 /* We only have 17 bytes more data in the salted cred case. */
1348 if (centry->len - centry->ofs == 17) {
1349 *cached_salt = (const uint8 *)centry_hash16(centry, mem_ctx);
1350 } else {
1351 *cached_salt = NULL;
1354 dump_data_pw("cached_nt_pass", *cached_nt_pass, NT_HASH_LEN);
1355 if (*cached_salt) {
1356 dump_data_pw("cached_salt", *cached_salt, NT_HASH_LEN);
1359 status = centry->status;
1361 DEBUG(10,("wcache_get_creds: [Cached] - cached creds for user %s status: %s\n",
1362 sid_string_dbg(sid), nt_errstr(status) ));
1364 centry_free(centry);
1365 return status;
1368 /* Store creds for a SID - only writes out new salted ones. */
1370 NTSTATUS wcache_save_creds(struct winbindd_domain *domain,
1371 const struct dom_sid *sid,
1372 const uint8 nt_pass[NT_HASH_LEN])
1374 struct cache_entry *centry;
1375 fstring sid_string;
1376 uint32 rid;
1377 uint8 cred_salt[NT_HASH_LEN];
1378 uint8 salted_hash[NT_HASH_LEN];
1380 if (is_null_sid(sid)) {
1381 return NT_STATUS_INVALID_SID;
1384 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1385 return NT_STATUS_INVALID_SID;
1388 centry = centry_start(domain, NT_STATUS_OK);
1389 if (!centry) {
1390 return NT_STATUS_INTERNAL_DB_ERROR;
1393 dump_data_pw("nt_pass", nt_pass, NT_HASH_LEN);
1395 centry_put_time(centry, time(NULL));
1397 /* Create a salt and then salt the hash. */
1398 generate_random_buffer(cred_salt, NT_HASH_LEN);
1399 E_md5hash(cred_salt, nt_pass, salted_hash);
1401 centry_put_hash16(centry, salted_hash);
1402 centry_put_hash16(centry, cred_salt);
1403 centry_end(centry, "CRED/%s", sid_to_fstring(sid_string, sid));
1405 DEBUG(10,("wcache_save_creds: %s\n", sid_string));
1407 centry_free(centry);
1409 return NT_STATUS_OK;
1413 /* Query display info. This is the basic user list fn */
1414 static NTSTATUS query_user_list(struct winbindd_domain *domain,
1415 TALLOC_CTX *mem_ctx,
1416 uint32 *num_entries,
1417 struct wbint_userinfo **info)
1419 struct winbind_cache *cache = get_cache(domain);
1420 struct cache_entry *centry = NULL;
1421 NTSTATUS status;
1422 unsigned int i, retry;
1423 bool old_status = domain->online;
1425 if (!cache->tdb)
1426 goto do_query;
1428 centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
1429 if (!centry)
1430 goto do_query;
1432 do_fetch_cache:
1433 *num_entries = centry_uint32(centry);
1435 if (*num_entries == 0)
1436 goto do_cached;
1438 (*info) = TALLOC_ARRAY(mem_ctx, struct wbint_userinfo, *num_entries);
1439 if (! (*info)) {
1440 smb_panic_fn("query_user_list out of memory");
1442 for (i=0; i<(*num_entries); i++) {
1443 (*info)[i].acct_name = centry_string(centry, mem_ctx);
1444 (*info)[i].full_name = centry_string(centry, mem_ctx);
1445 (*info)[i].homedir = centry_string(centry, mem_ctx);
1446 (*info)[i].shell = centry_string(centry, mem_ctx);
1447 centry_sid(centry, &(*info)[i].user_sid);
1448 centry_sid(centry, &(*info)[i].group_sid);
1451 do_cached:
1452 status = centry->status;
1454 DEBUG(10,("query_user_list: [Cached] - cached list for domain %s status: %s\n",
1455 domain->name, nt_errstr(status) ));
1457 centry_free(centry);
1458 return status;
1460 do_query:
1461 *num_entries = 0;
1462 *info = NULL;
1464 /* Return status value returned by seq number check */
1466 if (!NT_STATUS_IS_OK(domain->last_status))
1467 return domain->last_status;
1469 /* Put the query_user_list() in a retry loop. There appears to be
1470 * some bug either with Windows 2000 or Samba's handling of large
1471 * rpc replies. This manifests itself as sudden disconnection
1472 * at a random point in the enumeration of a large (60k) user list.
1473 * The retry loop simply tries the operation again. )-: It's not
1474 * pretty but an acceptable workaround until we work out what the
1475 * real problem is. */
1477 retry = 0;
1478 do {
1480 DEBUG(10,("query_user_list: [Cached] - doing backend query for list for domain %s\n",
1481 domain->name ));
1483 status = domain->backend->query_user_list(domain, mem_ctx, num_entries, info);
1484 if (!NT_STATUS_IS_OK(status)) {
1485 DEBUG(3, ("query_user_list: returned 0x%08x, "
1486 "retrying\n", NT_STATUS_V(status)));
1488 if (NT_STATUS_EQUAL(status, NT_STATUS_UNSUCCESSFUL)) {
1489 DEBUG(3, ("query_user_list: flushing "
1490 "connection cache\n"));
1491 invalidate_cm_connection(&domain->conn);
1493 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1494 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1495 if (!domain->internal && old_status) {
1496 set_domain_offline(domain);
1498 /* store partial response. */
1499 if (*num_entries > 0) {
1501 * humm, what about the status used for cache?
1502 * Should it be NT_STATUS_OK?
1504 break;
1507 * domain is offline now, and there is no user entries,
1508 * try to fetch from cache again.
1510 if (cache->tdb && !domain->online && !domain->internal && old_status) {
1511 centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
1512 /* partial response... */
1513 if (!centry) {
1514 goto skip_save;
1515 } else {
1516 goto do_fetch_cache;
1518 } else {
1519 goto skip_save;
1523 } while (NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_UNSUCCESSFUL) &&
1524 (retry++ < 5));
1526 /* and save it */
1527 refresh_sequence_number(domain, false);
1528 if (!NT_STATUS_IS_OK(status)) {
1529 return status;
1531 centry = centry_start(domain, status);
1532 if (!centry)
1533 goto skip_save;
1534 centry_put_uint32(centry, *num_entries);
1535 for (i=0; i<(*num_entries); i++) {
1536 centry_put_string(centry, (*info)[i].acct_name);
1537 centry_put_string(centry, (*info)[i].full_name);
1538 centry_put_string(centry, (*info)[i].homedir);
1539 centry_put_string(centry, (*info)[i].shell);
1540 centry_put_sid(centry, &(*info)[i].user_sid);
1541 centry_put_sid(centry, &(*info)[i].group_sid);
1542 if (domain->backend && domain->backend->consistent) {
1543 /* when the backend is consistent we can pre-prime some mappings */
1544 wcache_save_name_to_sid(domain, NT_STATUS_OK,
1545 domain->name,
1546 (*info)[i].acct_name,
1547 &(*info)[i].user_sid,
1548 SID_NAME_USER);
1549 wcache_save_sid_to_name(domain, NT_STATUS_OK,
1550 &(*info)[i].user_sid,
1551 domain->name,
1552 (*info)[i].acct_name,
1553 SID_NAME_USER);
1554 wcache_save_user(domain, NT_STATUS_OK, &(*info)[i]);
1557 centry_end(centry, "UL/%s", domain->name);
1558 centry_free(centry);
1560 skip_save:
1561 return status;
1564 /* list all domain groups */
1565 static NTSTATUS enum_dom_groups(struct winbindd_domain *domain,
1566 TALLOC_CTX *mem_ctx,
1567 uint32 *num_entries,
1568 struct wb_acct_info **info)
1570 struct winbind_cache *cache = get_cache(domain);
1571 struct cache_entry *centry = NULL;
1572 NTSTATUS status;
1573 unsigned int i;
1574 bool old_status;
1576 old_status = domain->online;
1577 if (!cache->tdb)
1578 goto do_query;
1580 centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
1581 if (!centry)
1582 goto do_query;
1584 do_fetch_cache:
1585 *num_entries = centry_uint32(centry);
1587 if (*num_entries == 0)
1588 goto do_cached;
1590 (*info) = TALLOC_ARRAY(mem_ctx, struct wb_acct_info, *num_entries);
1591 if (! (*info)) {
1592 smb_panic_fn("enum_dom_groups out of memory");
1594 for (i=0; i<(*num_entries); i++) {
1595 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1596 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1597 (*info)[i].rid = centry_uint32(centry);
1600 do_cached:
1601 status = centry->status;
1603 DEBUG(10,("enum_dom_groups: [Cached] - cached list for domain %s status: %s\n",
1604 domain->name, nt_errstr(status) ));
1606 centry_free(centry);
1607 return status;
1609 do_query:
1610 *num_entries = 0;
1611 *info = NULL;
1613 /* Return status value returned by seq number check */
1615 if (!NT_STATUS_IS_OK(domain->last_status))
1616 return domain->last_status;
1618 DEBUG(10,("enum_dom_groups: [Cached] - doing backend query for list for domain %s\n",
1619 domain->name ));
1621 status = domain->backend->enum_dom_groups(domain, mem_ctx, num_entries, info);
1623 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1624 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1625 if (!domain->internal && old_status) {
1626 set_domain_offline(domain);
1628 if (cache->tdb &&
1629 !domain->online &&
1630 !domain->internal &&
1631 old_status) {
1632 centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
1633 if (centry) {
1634 goto do_fetch_cache;
1638 /* and save it */
1639 refresh_sequence_number(domain, false);
1640 if (!NT_STATUS_IS_OK(status)) {
1641 return status;
1643 centry = centry_start(domain, status);
1644 if (!centry)
1645 goto skip_save;
1646 centry_put_uint32(centry, *num_entries);
1647 for (i=0; i<(*num_entries); i++) {
1648 centry_put_string(centry, (*info)[i].acct_name);
1649 centry_put_string(centry, (*info)[i].acct_desc);
1650 centry_put_uint32(centry, (*info)[i].rid);
1652 centry_end(centry, "GL/%s/domain", domain->name);
1653 centry_free(centry);
1655 skip_save:
1656 return status;
1659 /* list all domain groups */
1660 static NTSTATUS enum_local_groups(struct winbindd_domain *domain,
1661 TALLOC_CTX *mem_ctx,
1662 uint32 *num_entries,
1663 struct wb_acct_info **info)
1665 struct winbind_cache *cache = get_cache(domain);
1666 struct cache_entry *centry = NULL;
1667 NTSTATUS status;
1668 unsigned int i;
1669 bool old_status;
1671 old_status = domain->online;
1672 if (!cache->tdb)
1673 goto do_query;
1675 centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
1676 if (!centry)
1677 goto do_query;
1679 do_fetch_cache:
1680 *num_entries = centry_uint32(centry);
1682 if (*num_entries == 0)
1683 goto do_cached;
1685 (*info) = TALLOC_ARRAY(mem_ctx, struct wb_acct_info, *num_entries);
1686 if (! (*info)) {
1687 smb_panic_fn("enum_dom_groups out of memory");
1689 for (i=0; i<(*num_entries); i++) {
1690 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1691 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1692 (*info)[i].rid = centry_uint32(centry);
1695 do_cached:
1697 /* If we are returning cached data and the domain controller
1698 is down then we don't know whether the data is up to date
1699 or not. Return NT_STATUS_MORE_PROCESSING_REQUIRED to
1700 indicate this. */
1702 if (wcache_server_down(domain)) {
1703 DEBUG(10, ("enum_local_groups: returning cached user list and server was down\n"));
1704 status = NT_STATUS_MORE_PROCESSING_REQUIRED;
1705 } else
1706 status = centry->status;
1708 DEBUG(10,("enum_local_groups: [Cached] - cached list for domain %s status: %s\n",
1709 domain->name, nt_errstr(status) ));
1711 centry_free(centry);
1712 return status;
1714 do_query:
1715 *num_entries = 0;
1716 *info = NULL;
1718 /* Return status value returned by seq number check */
1720 if (!NT_STATUS_IS_OK(domain->last_status))
1721 return domain->last_status;
1723 DEBUG(10,("enum_local_groups: [Cached] - doing backend query for list for domain %s\n",
1724 domain->name ));
1726 status = domain->backend->enum_local_groups(domain, mem_ctx, num_entries, info);
1728 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1729 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1730 if (!domain->internal && old_status) {
1731 set_domain_offline(domain);
1733 if (cache->tdb &&
1734 !domain->internal &&
1735 !domain->online &&
1736 old_status) {
1737 centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
1738 if (centry) {
1739 goto do_fetch_cache;
1743 /* and save it */
1744 refresh_sequence_number(domain, false);
1745 if (!NT_STATUS_IS_OK(status)) {
1746 return status;
1748 centry = centry_start(domain, status);
1749 if (!centry)
1750 goto skip_save;
1751 centry_put_uint32(centry, *num_entries);
1752 for (i=0; i<(*num_entries); i++) {
1753 centry_put_string(centry, (*info)[i].acct_name);
1754 centry_put_string(centry, (*info)[i].acct_desc);
1755 centry_put_uint32(centry, (*info)[i].rid);
1757 centry_end(centry, "GL/%s/local", domain->name);
1758 centry_free(centry);
1760 skip_save:
1761 return status;
1764 NTSTATUS wcache_name_to_sid(struct winbindd_domain *domain,
1765 const char *domain_name,
1766 const char *name,
1767 struct dom_sid *sid,
1768 enum lsa_SidType *type)
1770 struct winbind_cache *cache = get_cache(domain);
1771 struct cache_entry *centry;
1772 NTSTATUS status;
1773 char *uname;
1775 if (cache->tdb == NULL) {
1776 return NT_STATUS_NOT_FOUND;
1779 uname = talloc_strdup_upper(talloc_tos(), name);
1780 if (uname == NULL) {
1781 return NT_STATUS_NO_MEMORY;
1784 centry = wcache_fetch(cache, domain, "NS/%s/%s", domain_name, uname);
1785 TALLOC_FREE(uname);
1786 if (centry == NULL) {
1787 return NT_STATUS_NOT_FOUND;
1790 status = centry->status;
1791 if (NT_STATUS_IS_OK(status)) {
1792 *type = (enum lsa_SidType)centry_uint32(centry);
1793 centry_sid(centry, sid);
1796 DEBUG(10,("name_to_sid: [Cached] - cached name for domain %s status: "
1797 "%s\n", domain->name, nt_errstr(status) ));
1799 centry_free(centry);
1800 return status;
1803 /* convert a single name to a sid in a domain */
1804 static NTSTATUS name_to_sid(struct winbindd_domain *domain,
1805 TALLOC_CTX *mem_ctx,
1806 const char *domain_name,
1807 const char *name,
1808 uint32_t flags,
1809 struct dom_sid *sid,
1810 enum lsa_SidType *type)
1812 NTSTATUS status;
1813 bool old_status;
1815 old_status = domain->online;
1817 status = wcache_name_to_sid(domain, domain_name, name, sid, type);
1818 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
1819 return status;
1822 ZERO_STRUCTP(sid);
1824 /* If the seq number check indicated that there is a problem
1825 * with this DC, then return that status... except for
1826 * access_denied. This is special because the dc may be in
1827 * "restrict anonymous = 1" mode, in which case it will deny
1828 * most unauthenticated operations, but *will* allow the LSA
1829 * name-to-sid that we try as a fallback. */
1831 if (!(NT_STATUS_IS_OK(domain->last_status)
1832 || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1833 return domain->last_status;
1835 DEBUG(10,("name_to_sid: [Cached] - doing backend query for name for domain %s\n",
1836 domain->name ));
1838 status = domain->backend->name_to_sid(domain, mem_ctx, domain_name,
1839 name, flags, sid, type);
1841 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1842 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1843 if (!domain->internal && old_status) {
1844 set_domain_offline(domain);
1846 if (!domain->internal &&
1847 !domain->online &&
1848 old_status) {
1849 NTSTATUS cache_status;
1850 cache_status = wcache_name_to_sid(domain, domain_name, name, sid, type);
1851 return cache_status;
1854 /* and save it */
1855 refresh_sequence_number(domain, false);
1857 if (domain->online &&
1858 (NT_STATUS_IS_OK(status) || NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED))) {
1859 wcache_save_name_to_sid(domain, status, domain_name, name, sid, *type);
1861 /* Only save the reverse mapping if this was not a UPN */
1862 if (!strchr(name, '@')) {
1863 strupper_m(CONST_DISCARD(char *,domain_name));
1864 strlower_m(CONST_DISCARD(char *,name));
1865 wcache_save_sid_to_name(domain, status, sid, domain_name, name, *type);
1869 return status;
1872 NTSTATUS wcache_sid_to_name(struct winbindd_domain *domain,
1873 const struct dom_sid *sid,
1874 TALLOC_CTX *mem_ctx,
1875 char **domain_name,
1876 char **name,
1877 enum lsa_SidType *type)
1879 struct winbind_cache *cache = get_cache(domain);
1880 struct cache_entry *centry;
1881 char *sid_string;
1882 NTSTATUS status;
1884 if (cache->tdb == NULL) {
1885 return NT_STATUS_NOT_FOUND;
1888 sid_string = sid_string_tos(sid);
1889 if (sid_string == NULL) {
1890 return NT_STATUS_NO_MEMORY;
1893 centry = wcache_fetch(cache, domain, "SN/%s", sid_string);
1894 TALLOC_FREE(sid_string);
1895 if (centry == NULL) {
1896 return NT_STATUS_NOT_FOUND;
1899 if (NT_STATUS_IS_OK(centry->status)) {
1900 *type = (enum lsa_SidType)centry_uint32(centry);
1901 *domain_name = centry_string(centry, mem_ctx);
1902 *name = centry_string(centry, mem_ctx);
1905 status = centry->status;
1906 centry_free(centry);
1908 DEBUG(10,("sid_to_name: [Cached] - cached name for domain %s status: "
1909 "%s\n", domain->name, nt_errstr(status) ));
1911 return status;
1914 /* convert a sid to a user or group name. The sid is guaranteed to be in the domain
1915 given */
1916 static NTSTATUS sid_to_name(struct winbindd_domain *domain,
1917 TALLOC_CTX *mem_ctx,
1918 const struct dom_sid *sid,
1919 char **domain_name,
1920 char **name,
1921 enum lsa_SidType *type)
1923 NTSTATUS status;
1924 bool old_status;
1926 old_status = domain->online;
1927 status = wcache_sid_to_name(domain, sid, mem_ctx, domain_name, name,
1928 type);
1929 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
1930 return status;
1933 *name = NULL;
1934 *domain_name = NULL;
1936 /* If the seq number check indicated that there is a problem
1937 * with this DC, then return that status... except for
1938 * access_denied. This is special because the dc may be in
1939 * "restrict anonymous = 1" mode, in which case it will deny
1940 * most unauthenticated operations, but *will* allow the LSA
1941 * sid-to-name that we try as a fallback. */
1943 if (!(NT_STATUS_IS_OK(domain->last_status)
1944 || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1945 return domain->last_status;
1947 DEBUG(10,("sid_to_name: [Cached] - doing backend query for name for domain %s\n",
1948 domain->name ));
1950 status = domain->backend->sid_to_name(domain, mem_ctx, sid, domain_name, name, type);
1952 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1953 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1954 if (!domain->internal && old_status) {
1955 set_domain_offline(domain);
1957 if (!domain->internal &&
1958 !domain->online &&
1959 old_status) {
1960 NTSTATUS cache_status;
1961 cache_status = wcache_sid_to_name(domain, sid, mem_ctx,
1962 domain_name, name, type);
1963 return cache_status;
1966 /* and save it */
1967 refresh_sequence_number(domain, false);
1968 if (!NT_STATUS_IS_OK(status)) {
1969 return status;
1971 wcache_save_sid_to_name(domain, status, sid, *domain_name, *name, *type);
1973 /* We can't save the name to sid mapping here, as with sid history a
1974 * later name2sid would give the wrong sid. */
1976 return status;
1979 static NTSTATUS rids_to_names(struct winbindd_domain *domain,
1980 TALLOC_CTX *mem_ctx,
1981 const struct dom_sid *domain_sid,
1982 uint32 *rids,
1983 size_t num_rids,
1984 char **domain_name,
1985 char ***names,
1986 enum lsa_SidType **types)
1988 struct winbind_cache *cache = get_cache(domain);
1989 size_t i;
1990 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
1991 bool have_mapped;
1992 bool have_unmapped;
1993 bool old_status;
1995 old_status = domain->online;
1996 *domain_name = NULL;
1997 *names = NULL;
1998 *types = NULL;
2000 if (!cache->tdb) {
2001 goto do_query;
2004 if (num_rids == 0) {
2005 return NT_STATUS_OK;
2008 *names = TALLOC_ARRAY(mem_ctx, char *, num_rids);
2009 *types = TALLOC_ARRAY(mem_ctx, enum lsa_SidType, num_rids);
2011 if ((*names == NULL) || (*types == NULL)) {
2012 result = NT_STATUS_NO_MEMORY;
2013 goto error;
2016 have_mapped = have_unmapped = false;
2018 for (i=0; i<num_rids; i++) {
2019 struct dom_sid sid;
2020 struct cache_entry *centry;
2021 fstring tmp;
2023 if (!sid_compose(&sid, domain_sid, rids[i])) {
2024 result = NT_STATUS_INTERNAL_ERROR;
2025 goto error;
2028 centry = wcache_fetch(cache, domain, "SN/%s",
2029 sid_to_fstring(tmp, &sid));
2030 if (!centry) {
2031 goto do_query;
2034 (*types)[i] = SID_NAME_UNKNOWN;
2035 (*names)[i] = talloc_strdup(*names, "");
2037 if (NT_STATUS_IS_OK(centry->status)) {
2038 char *dom;
2039 have_mapped = true;
2040 (*types)[i] = (enum lsa_SidType)centry_uint32(centry);
2042 dom = centry_string(centry, mem_ctx);
2043 if (*domain_name == NULL) {
2044 *domain_name = dom;
2045 } else {
2046 talloc_free(dom);
2049 (*names)[i] = centry_string(centry, *names);
2051 } else if (NT_STATUS_EQUAL(centry->status, NT_STATUS_NONE_MAPPED)
2052 || NT_STATUS_EQUAL(centry->status, STATUS_SOME_UNMAPPED)) {
2053 have_unmapped = true;
2055 } else {
2056 /* something's definitely wrong */
2057 result = centry->status;
2058 goto error;
2061 centry_free(centry);
2064 if (!have_mapped) {
2065 return NT_STATUS_NONE_MAPPED;
2067 if (!have_unmapped) {
2068 return NT_STATUS_OK;
2070 return STATUS_SOME_UNMAPPED;
2072 do_query:
2074 TALLOC_FREE(*names);
2075 TALLOC_FREE(*types);
2077 result = domain->backend->rids_to_names(domain, mem_ctx, domain_sid,
2078 rids, num_rids, domain_name,
2079 names, types);
2081 if (NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
2082 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2083 if (!domain->internal && old_status) {
2084 set_domain_offline(domain);
2086 if (cache->tdb &&
2087 !domain->internal &&
2088 !domain->online &&
2089 old_status) {
2090 have_mapped = have_unmapped = false;
2092 for (i=0; i<num_rids; i++) {
2093 struct dom_sid sid;
2094 struct cache_entry *centry;
2095 fstring tmp;
2097 if (!sid_compose(&sid, domain_sid, rids[i])) {
2098 result = NT_STATUS_INTERNAL_ERROR;
2099 goto error;
2102 centry = wcache_fetch(cache, domain, "SN/%s",
2103 sid_to_fstring(tmp, &sid));
2104 if (!centry) {
2105 (*types)[i] = SID_NAME_UNKNOWN;
2106 (*names)[i] = talloc_strdup(*names, "");
2107 continue;
2110 (*types)[i] = SID_NAME_UNKNOWN;
2111 (*names)[i] = talloc_strdup(*names, "");
2113 if (NT_STATUS_IS_OK(centry->status)) {
2114 char *dom;
2115 have_mapped = true;
2116 (*types)[i] = (enum lsa_SidType)centry_uint32(centry);
2118 dom = centry_string(centry, mem_ctx);
2119 if (*domain_name == NULL) {
2120 *domain_name = dom;
2121 } else {
2122 talloc_free(dom);
2125 (*names)[i] = centry_string(centry, *names);
2127 } else if (NT_STATUS_EQUAL(centry->status, NT_STATUS_NONE_MAPPED)) {
2128 have_unmapped = true;
2130 } else {
2131 /* something's definitely wrong */
2132 result = centry->status;
2133 centry_free(centry);
2134 goto error;
2137 centry_free(centry);
2140 if (!have_mapped) {
2141 return NT_STATUS_NONE_MAPPED;
2143 if (!have_unmapped) {
2144 return NT_STATUS_OK;
2146 return STATUS_SOME_UNMAPPED;
2150 None of the queried rids has been found so save all negative entries
2152 if (NT_STATUS_EQUAL(result, NT_STATUS_NONE_MAPPED)) {
2153 for (i = 0; i < num_rids; i++) {
2154 struct dom_sid sid;
2155 const char *name = "";
2156 const enum lsa_SidType type = SID_NAME_UNKNOWN;
2157 NTSTATUS status = NT_STATUS_NONE_MAPPED;
2159 if (!sid_compose(&sid, domain_sid, rids[i])) {
2160 return NT_STATUS_INTERNAL_ERROR;
2163 wcache_save_sid_to_name(domain, status, &sid, *domain_name,
2164 name, type);
2167 return result;
2171 Some or all of the queried rids have been found.
2173 if (!NT_STATUS_IS_OK(result) &&
2174 !NT_STATUS_EQUAL(result, STATUS_SOME_UNMAPPED)) {
2175 return result;
2178 refresh_sequence_number(domain, false);
2180 for (i=0; i<num_rids; i++) {
2181 struct dom_sid sid;
2182 NTSTATUS status;
2184 if (!sid_compose(&sid, domain_sid, rids[i])) {
2185 result = NT_STATUS_INTERNAL_ERROR;
2186 goto error;
2189 status = (*types)[i] == SID_NAME_UNKNOWN ?
2190 NT_STATUS_NONE_MAPPED : NT_STATUS_OK;
2192 wcache_save_sid_to_name(domain, status, &sid, *domain_name,
2193 (*names)[i], (*types)[i]);
2196 return result;
2198 error:
2199 TALLOC_FREE(*names);
2200 TALLOC_FREE(*types);
2201 return result;
2204 NTSTATUS wcache_query_user(struct winbindd_domain *domain,
2205 TALLOC_CTX *mem_ctx,
2206 const struct dom_sid *user_sid,
2207 struct wbint_userinfo *info)
2209 struct winbind_cache *cache = get_cache(domain);
2210 struct cache_entry *centry = NULL;
2211 NTSTATUS status;
2212 char *sid_string;
2214 if (cache->tdb == NULL) {
2215 return NT_STATUS_NOT_FOUND;
2218 sid_string = sid_string_tos(user_sid);
2219 if (sid_string == NULL) {
2220 return NT_STATUS_NO_MEMORY;
2223 centry = wcache_fetch(cache, domain, "U/%s", sid_string);
2224 TALLOC_FREE(sid_string);
2225 if (centry == NULL) {
2226 return NT_STATUS_NOT_FOUND;
2230 * If we have an access denied cache entry and a cached info3
2231 * in the samlogon cache then do a query. This will force the
2232 * rpc back end to return the info3 data.
2235 if (NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED) &&
2236 netsamlogon_cache_have(user_sid)) {
2237 DEBUG(10, ("query_user: cached access denied and have cached "
2238 "info3\n"));
2239 domain->last_status = NT_STATUS_OK;
2240 centry_free(centry);
2241 return NT_STATUS_NOT_FOUND;
2244 /* if status is not ok then this is a negative hit
2245 and the rest of the data doesn't matter */
2246 status = centry->status;
2247 if (NT_STATUS_IS_OK(status)) {
2248 info->acct_name = centry_string(centry, mem_ctx);
2249 info->full_name = centry_string(centry, mem_ctx);
2250 info->homedir = centry_string(centry, mem_ctx);
2251 info->shell = centry_string(centry, mem_ctx);
2252 info->primary_gid = centry_uint32(centry);
2253 centry_sid(centry, &info->user_sid);
2254 centry_sid(centry, &info->group_sid);
2257 DEBUG(10,("query_user: [Cached] - cached info for domain %s status: "
2258 "%s\n", domain->name, nt_errstr(status) ));
2260 centry_free(centry);
2261 return status;
2264 /* Lookup user information from a rid */
2265 static NTSTATUS query_user(struct winbindd_domain *domain,
2266 TALLOC_CTX *mem_ctx,
2267 const struct dom_sid *user_sid,
2268 struct wbint_userinfo *info)
2270 NTSTATUS status;
2271 bool old_status;
2273 old_status = domain->online;
2274 status = wcache_query_user(domain, mem_ctx, user_sid, info);
2275 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2276 return status;
2279 ZERO_STRUCTP(info);
2281 /* Return status value returned by seq number check */
2283 if (!NT_STATUS_IS_OK(domain->last_status))
2284 return domain->last_status;
2286 DEBUG(10,("query_user: [Cached] - doing backend query for info for domain %s\n",
2287 domain->name ));
2289 status = domain->backend->query_user(domain, mem_ctx, user_sid, info);
2291 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2292 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2293 if (!domain->internal && old_status) {
2294 set_domain_offline(domain);
2296 if (!domain->internal &&
2297 !domain->online &&
2298 old_status) {
2299 NTSTATUS cache_status;
2300 cache_status = wcache_query_user(domain, mem_ctx, user_sid, info);
2301 return cache_status;
2304 /* and save it */
2305 refresh_sequence_number(domain, false);
2306 if (!NT_STATUS_IS_OK(status)) {
2307 return status;
2309 wcache_save_user(domain, status, info);
2311 return status;
2314 NTSTATUS wcache_lookup_usergroups(struct winbindd_domain *domain,
2315 TALLOC_CTX *mem_ctx,
2316 const struct dom_sid *user_sid,
2317 uint32_t *pnum_sids,
2318 struct dom_sid **psids)
2320 struct winbind_cache *cache = get_cache(domain);
2321 struct cache_entry *centry = NULL;
2322 NTSTATUS status;
2323 uint32_t i, num_sids;
2324 struct dom_sid *sids;
2325 fstring sid_string;
2327 if (cache->tdb == NULL) {
2328 return NT_STATUS_NOT_FOUND;
2331 centry = wcache_fetch(cache, domain, "UG/%s",
2332 sid_to_fstring(sid_string, user_sid));
2333 if (centry == NULL) {
2334 return NT_STATUS_NOT_FOUND;
2337 /* If we have an access denied cache entry and a cached info3 in the
2338 samlogon cache then do a query. This will force the rpc back end
2339 to return the info3 data. */
2341 if (NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)
2342 && netsamlogon_cache_have(user_sid)) {
2343 DEBUG(10, ("lookup_usergroups: cached access denied and have "
2344 "cached info3\n"));
2345 domain->last_status = NT_STATUS_OK;
2346 centry_free(centry);
2347 return NT_STATUS_NOT_FOUND;
2350 num_sids = centry_uint32(centry);
2351 sids = talloc_array(mem_ctx, struct dom_sid, num_sids);
2352 if (sids == NULL) {
2353 centry_free(centry);
2354 return NT_STATUS_NO_MEMORY;
2357 for (i=0; i<num_sids; i++) {
2358 centry_sid(centry, &sids[i]);
2361 status = centry->status;
2363 DEBUG(10,("lookup_usergroups: [Cached] - cached info for domain %s "
2364 "status: %s\n", domain->name, nt_errstr(status)));
2366 centry_free(centry);
2368 *pnum_sids = num_sids;
2369 *psids = sids;
2370 return status;
2373 /* Lookup groups a user is a member of. */
2374 static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
2375 TALLOC_CTX *mem_ctx,
2376 const struct dom_sid *user_sid,
2377 uint32 *num_groups, struct dom_sid **user_gids)
2379 struct cache_entry *centry = NULL;
2380 NTSTATUS status;
2381 unsigned int i;
2382 fstring sid_string;
2383 bool old_status;
2385 old_status = domain->online;
2386 status = wcache_lookup_usergroups(domain, mem_ctx, user_sid,
2387 num_groups, user_gids);
2388 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2389 return status;
2392 (*num_groups) = 0;
2393 (*user_gids) = NULL;
2395 /* Return status value returned by seq number check */
2397 if (!NT_STATUS_IS_OK(domain->last_status))
2398 return domain->last_status;
2400 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info for domain %s\n",
2401 domain->name ));
2403 status = domain->backend->lookup_usergroups(domain, mem_ctx, user_sid, num_groups, user_gids);
2405 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2406 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2407 if (!domain->internal && old_status) {
2408 set_domain_offline(domain);
2410 if (!domain->internal &&
2411 !domain->online &&
2412 old_status) {
2413 NTSTATUS cache_status;
2414 cache_status = wcache_lookup_usergroups(domain, mem_ctx, user_sid,
2415 num_groups, user_gids);
2416 return cache_status;
2419 if ( NT_STATUS_EQUAL(status, NT_STATUS_SYNCHRONIZATION_REQUIRED) )
2420 goto skip_save;
2422 /* and save it */
2423 refresh_sequence_number(domain, false);
2424 if (!NT_STATUS_IS_OK(status)) {
2425 return status;
2427 centry = centry_start(domain, status);
2428 if (!centry)
2429 goto skip_save;
2431 centry_put_uint32(centry, *num_groups);
2432 for (i=0; i<(*num_groups); i++) {
2433 centry_put_sid(centry, &(*user_gids)[i]);
2436 centry_end(centry, "UG/%s", sid_to_fstring(sid_string, user_sid));
2437 centry_free(centry);
2439 skip_save:
2440 return status;
2443 static char *wcache_make_sidlist(TALLOC_CTX *mem_ctx, uint32_t num_sids,
2444 const struct dom_sid *sids)
2446 uint32_t i;
2447 char *sidlist;
2449 sidlist = talloc_strdup(mem_ctx, "");
2450 if (sidlist == NULL) {
2451 return NULL;
2453 for (i=0; i<num_sids; i++) {
2454 fstring tmp;
2455 sidlist = talloc_asprintf_append_buffer(
2456 sidlist, "/%s", sid_to_fstring(tmp, &sids[i]));
2457 if (sidlist == NULL) {
2458 return NULL;
2461 return sidlist;
2464 NTSTATUS wcache_lookup_useraliases(struct winbindd_domain *domain,
2465 TALLOC_CTX *mem_ctx, uint32_t num_sids,
2466 const struct dom_sid *sids,
2467 uint32_t *pnum_aliases, uint32_t **paliases)
2469 struct winbind_cache *cache = get_cache(domain);
2470 struct cache_entry *centry = NULL;
2471 uint32_t num_aliases;
2472 uint32_t *aliases;
2473 NTSTATUS status;
2474 char *sidlist;
2475 int i;
2477 if (cache->tdb == NULL) {
2478 return NT_STATUS_NOT_FOUND;
2481 if (num_sids == 0) {
2482 *pnum_aliases = 0;
2483 *paliases = NULL;
2484 return NT_STATUS_OK;
2487 /* We need to cache indexed by the whole list of SIDs, the aliases
2488 * resulting might come from any of the SIDs. */
2490 sidlist = wcache_make_sidlist(talloc_tos(), num_sids, sids);
2491 if (sidlist == NULL) {
2492 return NT_STATUS_NO_MEMORY;
2495 centry = wcache_fetch(cache, domain, "UA%s", sidlist);
2496 TALLOC_FREE(sidlist);
2497 if (centry == NULL) {
2498 return NT_STATUS_NOT_FOUND;
2501 num_aliases = centry_uint32(centry);
2502 aliases = talloc_array(mem_ctx, uint32_t, num_aliases);
2503 if (aliases == NULL) {
2504 centry_free(centry);
2505 return NT_STATUS_NO_MEMORY;
2508 for (i=0; i<num_aliases; i++) {
2509 aliases[i] = centry_uint32(centry);
2512 status = centry->status;
2514 DEBUG(10,("lookup_useraliases: [Cached] - cached info for domain: %s "
2515 "status %s\n", domain->name, nt_errstr(status)));
2517 centry_free(centry);
2519 *pnum_aliases = num_aliases;
2520 *paliases = aliases;
2522 return status;
2525 static NTSTATUS lookup_useraliases(struct winbindd_domain *domain,
2526 TALLOC_CTX *mem_ctx,
2527 uint32 num_sids, const struct dom_sid *sids,
2528 uint32 *num_aliases, uint32 **alias_rids)
2530 struct cache_entry *centry = NULL;
2531 NTSTATUS status;
2532 char *sidlist;
2533 int i;
2534 bool old_status;
2536 old_status = domain->online;
2537 status = wcache_lookup_useraliases(domain, mem_ctx, num_sids, sids,
2538 num_aliases, alias_rids);
2539 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2540 return status;
2543 (*num_aliases) = 0;
2544 (*alias_rids) = NULL;
2546 if (!NT_STATUS_IS_OK(domain->last_status))
2547 return domain->last_status;
2549 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info "
2550 "for domain %s\n", domain->name ));
2552 sidlist = wcache_make_sidlist(talloc_tos(), num_sids, sids);
2553 if (sidlist == NULL) {
2554 return NT_STATUS_NO_MEMORY;
2557 status = domain->backend->lookup_useraliases(domain, mem_ctx,
2558 num_sids, sids,
2559 num_aliases, alias_rids);
2561 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2562 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2563 if (!domain->internal && old_status) {
2564 set_domain_offline(domain);
2566 if (!domain->internal &&
2567 !domain->online &&
2568 old_status) {
2569 NTSTATUS cache_status;
2570 cache_status = wcache_lookup_useraliases(domain, mem_ctx, num_sids,
2571 sids, num_aliases, alias_rids);
2572 return cache_status;
2575 /* and save it */
2576 refresh_sequence_number(domain, false);
2577 if (!NT_STATUS_IS_OK(status)) {
2578 return status;
2580 centry = centry_start(domain, status);
2581 if (!centry)
2582 goto skip_save;
2583 centry_put_uint32(centry, *num_aliases);
2584 for (i=0; i<(*num_aliases); i++)
2585 centry_put_uint32(centry, (*alias_rids)[i]);
2586 centry_end(centry, "UA%s", sidlist);
2587 centry_free(centry);
2589 skip_save:
2590 return status;
2593 NTSTATUS wcache_lookup_groupmem(struct winbindd_domain *domain,
2594 TALLOC_CTX *mem_ctx,
2595 const struct dom_sid *group_sid,
2596 uint32_t *num_names,
2597 struct dom_sid **sid_mem, char ***names,
2598 uint32_t **name_types)
2600 struct winbind_cache *cache = get_cache(domain);
2601 struct cache_entry *centry = NULL;
2602 NTSTATUS status;
2603 unsigned int i;
2604 char *sid_string;
2606 if (cache->tdb == NULL) {
2607 return NT_STATUS_NOT_FOUND;
2610 sid_string = sid_string_tos(group_sid);
2611 if (sid_string == NULL) {
2612 return NT_STATUS_NO_MEMORY;
2615 centry = wcache_fetch(cache, domain, "GM/%s", sid_string);
2616 TALLOC_FREE(sid_string);
2617 if (centry == NULL) {
2618 return NT_STATUS_NOT_FOUND;
2621 *sid_mem = NULL;
2622 *names = NULL;
2623 *name_types = NULL;
2625 *num_names = centry_uint32(centry);
2626 if (*num_names == 0) {
2627 centry_free(centry);
2628 return NT_STATUS_OK;
2631 *sid_mem = talloc_array(mem_ctx, struct dom_sid, *num_names);
2632 *names = talloc_array(mem_ctx, char *, *num_names);
2633 *name_types = talloc_array(mem_ctx, uint32, *num_names);
2635 if ((*sid_mem == NULL) || (*names == NULL) || (*name_types == NULL)) {
2636 TALLOC_FREE(*sid_mem);
2637 TALLOC_FREE(*names);
2638 TALLOC_FREE(*name_types);
2639 centry_free(centry);
2640 return NT_STATUS_NO_MEMORY;
2643 for (i=0; i<(*num_names); i++) {
2644 centry_sid(centry, &(*sid_mem)[i]);
2645 (*names)[i] = centry_string(centry, mem_ctx);
2646 (*name_types)[i] = centry_uint32(centry);
2649 status = centry->status;
2651 DEBUG(10,("lookup_groupmem: [Cached] - cached info for domain %s "
2652 "status: %s\n", domain->name, nt_errstr(status)));
2654 centry_free(centry);
2655 return status;
2658 static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
2659 TALLOC_CTX *mem_ctx,
2660 const struct dom_sid *group_sid,
2661 enum lsa_SidType type,
2662 uint32 *num_names,
2663 struct dom_sid **sid_mem, char ***names,
2664 uint32 **name_types)
2666 struct cache_entry *centry = NULL;
2667 NTSTATUS status;
2668 unsigned int i;
2669 fstring sid_string;
2670 bool old_status;
2672 old_status = domain->online;
2673 status = wcache_lookup_groupmem(domain, mem_ctx, group_sid, num_names,
2674 sid_mem, names, name_types);
2675 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2676 return status;
2679 (*num_names) = 0;
2680 (*sid_mem) = NULL;
2681 (*names) = NULL;
2682 (*name_types) = NULL;
2684 /* Return status value returned by seq number check */
2686 if (!NT_STATUS_IS_OK(domain->last_status))
2687 return domain->last_status;
2689 DEBUG(10,("lookup_groupmem: [Cached] - doing backend query for info for domain %s\n",
2690 domain->name ));
2692 status = domain->backend->lookup_groupmem(domain, mem_ctx, group_sid,
2693 type, num_names,
2694 sid_mem, names, name_types);
2696 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2697 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2698 if (!domain->internal && old_status) {
2699 set_domain_offline(domain);
2701 if (!domain->internal &&
2702 !domain->online &&
2703 old_status) {
2704 NTSTATUS cache_status;
2705 cache_status = wcache_lookup_groupmem(domain, mem_ctx, group_sid,
2706 num_names, sid_mem, names,
2707 name_types);
2708 return cache_status;
2711 /* and save it */
2712 refresh_sequence_number(domain, false);
2713 if (!NT_STATUS_IS_OK(status)) {
2714 return status;
2716 centry = centry_start(domain, status);
2717 if (!centry)
2718 goto skip_save;
2719 centry_put_uint32(centry, *num_names);
2720 for (i=0; i<(*num_names); i++) {
2721 centry_put_sid(centry, &(*sid_mem)[i]);
2722 centry_put_string(centry, (*names)[i]);
2723 centry_put_uint32(centry, (*name_types)[i]);
2725 centry_end(centry, "GM/%s", sid_to_fstring(sid_string, group_sid));
2726 centry_free(centry);
2728 skip_save:
2729 return status;
2732 /* find the sequence number for a domain */
2733 static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
2735 refresh_sequence_number(domain, false);
2737 *seq = domain->sequence_number;
2739 return NT_STATUS_OK;
2742 /* enumerate trusted domains
2743 * (we need to have the list of trustdoms in the cache when we go offline) -
2744 * Guenther */
2745 static NTSTATUS trusted_domains(struct winbindd_domain *domain,
2746 TALLOC_CTX *mem_ctx,
2747 struct netr_DomainTrustList *trusts)
2749 NTSTATUS status;
2750 struct winbind_cache *cache;
2751 struct winbindd_tdc_domain *dom_list = NULL;
2752 size_t num_domains = 0;
2753 bool retval = false;
2754 int i;
2755 bool old_status;
2757 old_status = domain->online;
2758 trusts->count = 0;
2759 trusts->array = NULL;
2761 cache = get_cache(domain);
2762 if (!cache || !cache->tdb) {
2763 goto do_query;
2766 if (domain->online) {
2767 goto do_query;
2770 retval = wcache_tdc_fetch_list(&dom_list, &num_domains);
2771 if (!retval || !num_domains || !dom_list) {
2772 TALLOC_FREE(dom_list);
2773 goto do_query;
2776 do_fetch_cache:
2777 trusts->array = TALLOC_ZERO_ARRAY(mem_ctx, struct netr_DomainTrust, num_domains);
2778 if (!trusts->array) {
2779 TALLOC_FREE(dom_list);
2780 return NT_STATUS_NO_MEMORY;
2783 for (i = 0; i < num_domains; i++) {
2784 struct netr_DomainTrust *trust;
2785 struct dom_sid *sid;
2786 struct winbindd_domain *dom;
2788 dom = find_domain_from_name_noinit(dom_list[i].domain_name);
2789 if (dom && dom->internal) {
2790 continue;
2793 trust = &trusts->array[trusts->count];
2794 trust->netbios_name = talloc_strdup(trusts->array, dom_list[i].domain_name);
2795 trust->dns_name = talloc_strdup(trusts->array, dom_list[i].dns_name);
2796 sid = talloc(trusts->array, struct dom_sid);
2797 if (!trust->netbios_name || !trust->dns_name ||
2798 !sid) {
2799 TALLOC_FREE(dom_list);
2800 TALLOC_FREE(trusts->array);
2801 return NT_STATUS_NO_MEMORY;
2804 trust->trust_flags = dom_list[i].trust_flags;
2805 trust->trust_attributes = dom_list[i].trust_attribs;
2806 trust->trust_type = dom_list[i].trust_type;
2807 sid_copy(sid, &dom_list[i].sid);
2808 trust->sid = sid;
2809 trusts->count++;
2812 TALLOC_FREE(dom_list);
2813 return NT_STATUS_OK;
2815 do_query:
2816 /* Return status value returned by seq number check */
2818 if (!NT_STATUS_IS_OK(domain->last_status))
2819 return domain->last_status;
2821 DEBUG(10,("trusted_domains: [Cached] - doing backend query for info for domain %s\n",
2822 domain->name ));
2824 status = domain->backend->trusted_domains(domain, mem_ctx, trusts);
2826 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2827 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2828 if (!domain->internal && old_status) {
2829 set_domain_offline(domain);
2831 if (!domain->internal &&
2832 !domain->online &&
2833 old_status) {
2834 retval = wcache_tdc_fetch_list(&dom_list, &num_domains);
2835 if (retval && num_domains && dom_list) {
2836 TALLOC_FREE(trusts->array);
2837 trusts->count = 0;
2838 goto do_fetch_cache;
2842 /* no trusts gives NT_STATUS_NO_MORE_ENTRIES resetting to NT_STATUS_OK
2843 * so that the generic centry handling still applies correctly -
2844 * Guenther*/
2846 if (!NT_STATUS_IS_ERR(status)) {
2847 status = NT_STATUS_OK;
2849 return status;
2852 /* get lockout policy */
2853 static NTSTATUS lockout_policy(struct winbindd_domain *domain,
2854 TALLOC_CTX *mem_ctx,
2855 struct samr_DomInfo12 *policy)
2857 struct winbind_cache *cache = get_cache(domain);
2858 struct cache_entry *centry = NULL;
2859 NTSTATUS status;
2860 bool old_status;
2862 old_status = domain->online;
2863 if (!cache->tdb)
2864 goto do_query;
2866 centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
2868 if (!centry)
2869 goto do_query;
2871 do_fetch_cache:
2872 policy->lockout_duration = centry_nttime(centry);
2873 policy->lockout_window = centry_nttime(centry);
2874 policy->lockout_threshold = centry_uint16(centry);
2876 status = centry->status;
2878 DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2879 domain->name, nt_errstr(status) ));
2881 centry_free(centry);
2882 return status;
2884 do_query:
2885 ZERO_STRUCTP(policy);
2887 /* Return status value returned by seq number check */
2889 if (!NT_STATUS_IS_OK(domain->last_status))
2890 return domain->last_status;
2892 DEBUG(10,("lockout_policy: [Cached] - doing backend query for info for domain %s\n",
2893 domain->name ));
2895 status = domain->backend->lockout_policy(domain, mem_ctx, policy);
2897 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2898 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2899 if (!domain->internal && old_status) {
2900 set_domain_offline(domain);
2902 if (cache->tdb &&
2903 !domain->internal &&
2904 !domain->online &&
2905 old_status) {
2906 centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
2907 if (centry) {
2908 goto do_fetch_cache;
2912 /* and save it */
2913 refresh_sequence_number(domain, false);
2914 if (!NT_STATUS_IS_OK(status)) {
2915 return status;
2917 wcache_save_lockout_policy(domain, status, policy);
2919 return status;
2922 /* get password policy */
2923 static NTSTATUS password_policy(struct winbindd_domain *domain,
2924 TALLOC_CTX *mem_ctx,
2925 struct samr_DomInfo1 *policy)
2927 struct winbind_cache *cache = get_cache(domain);
2928 struct cache_entry *centry = NULL;
2929 NTSTATUS status;
2930 bool old_status;
2932 old_status = domain->online;
2933 if (!cache->tdb)
2934 goto do_query;
2936 centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
2938 if (!centry)
2939 goto do_query;
2941 do_fetch_cache:
2942 policy->min_password_length = centry_uint16(centry);
2943 policy->password_history_length = centry_uint16(centry);
2944 policy->password_properties = centry_uint32(centry);
2945 policy->max_password_age = centry_nttime(centry);
2946 policy->min_password_age = centry_nttime(centry);
2948 status = centry->status;
2950 DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2951 domain->name, nt_errstr(status) ));
2953 centry_free(centry);
2954 return status;
2956 do_query:
2957 ZERO_STRUCTP(policy);
2959 /* Return status value returned by seq number check */
2961 if (!NT_STATUS_IS_OK(domain->last_status))
2962 return domain->last_status;
2964 DEBUG(10,("password_policy: [Cached] - doing backend query for info for domain %s\n",
2965 domain->name ));
2967 status = domain->backend->password_policy(domain, mem_ctx, policy);
2969 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2970 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2971 if (!domain->internal && old_status) {
2972 set_domain_offline(domain);
2974 if (cache->tdb &&
2975 !domain->internal &&
2976 !domain->online &&
2977 old_status) {
2978 centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
2979 if (centry) {
2980 goto do_fetch_cache;
2984 /* and save it */
2985 refresh_sequence_number(domain, false);
2986 if (!NT_STATUS_IS_OK(status)) {
2987 return status;
2989 wcache_save_password_policy(domain, status, policy);
2991 return status;
2995 /* Invalidate cached user and group lists coherently */
2997 static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
2998 void *state)
3000 if (strncmp((const char *)kbuf.dptr, "UL/", 3) == 0 ||
3001 strncmp((const char *)kbuf.dptr, "GL/", 3) == 0)
3002 tdb_delete(the_tdb, kbuf);
3004 return 0;
3007 /* Invalidate the getpwnam and getgroups entries for a winbindd domain */
3009 void wcache_invalidate_samlogon(struct winbindd_domain *domain,
3010 const struct dom_sid *sid)
3012 fstring key_str, sid_string;
3013 struct winbind_cache *cache;
3015 /* dont clear cached U/SID and UG/SID entries when we want to logon
3016 * offline - gd */
3018 if (lp_winbind_offline_logon()) {
3019 return;
3022 if (!domain)
3023 return;
3025 cache = get_cache(domain);
3027 if (!cache->tdb) {
3028 return;
3031 /* Clear U/SID cache entry */
3032 fstr_sprintf(key_str, "U/%s", sid_to_fstring(sid_string, sid));
3033 DEBUG(10, ("wcache_invalidate_samlogon: clearing %s\n", key_str));
3034 tdb_delete(cache->tdb, string_tdb_data(key_str));
3036 /* Clear UG/SID cache entry */
3037 fstr_sprintf(key_str, "UG/%s", sid_to_fstring(sid_string, sid));
3038 DEBUG(10, ("wcache_invalidate_samlogon: clearing %s\n", key_str));
3039 tdb_delete(cache->tdb, string_tdb_data(key_str));
3041 /* Samba/winbindd never needs this. */
3042 netsamlogon_clear_cached_user(sid);
3045 bool wcache_invalidate_cache(void)
3047 struct winbindd_domain *domain;
3049 for (domain = domain_list(); domain; domain = domain->next) {
3050 struct winbind_cache *cache = get_cache(domain);
3052 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
3053 "entries for %s\n", domain->name));
3054 if (cache) {
3055 if (cache->tdb) {
3056 tdb_traverse(cache->tdb, traverse_fn, NULL);
3057 } else {
3058 return false;
3062 return true;
3065 bool wcache_invalidate_cache_noinit(void)
3067 struct winbindd_domain *domain;
3069 for (domain = domain_list(); domain; domain = domain->next) {
3070 struct winbind_cache *cache;
3072 /* Skip uninitialized domains. */
3073 if (!domain->initialized && !domain->internal) {
3074 continue;
3077 cache = get_cache(domain);
3079 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
3080 "entries for %s\n", domain->name));
3081 if (cache) {
3082 if (cache->tdb) {
3083 tdb_traverse(cache->tdb, traverse_fn, NULL);
3085 * Flushing cache has nothing to with domains.
3086 * return here if we successfully flushed once.
3087 * To avoid unnecessary traversing the cache.
3089 return true;
3090 } else {
3091 return false;
3095 return true;
3098 bool init_wcache(void)
3100 if (wcache == NULL) {
3101 wcache = SMB_XMALLOC_P(struct winbind_cache);
3102 ZERO_STRUCTP(wcache);
3105 if (wcache->tdb != NULL)
3106 return true;
3108 /* when working offline we must not clear the cache on restart */
3109 wcache->tdb = tdb_open_log(cache_path("winbindd_cache.tdb"),
3110 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
3111 TDB_INCOMPATIBLE_HASH |
3112 (lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST)),
3113 O_RDWR|O_CREAT, 0600);
3115 if (wcache->tdb == NULL) {
3116 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
3117 return false;
3120 return true;
3123 /************************************************************************
3124 This is called by the parent to initialize the cache file.
3125 We don't need sophisticated locking here as we know we're the
3126 only opener.
3127 ************************************************************************/
3129 bool initialize_winbindd_cache(void)
3131 bool cache_bad = true;
3132 uint32 vers;
3134 if (!init_wcache()) {
3135 DEBUG(0,("initialize_winbindd_cache: init_wcache failed.\n"));
3136 return false;
3139 /* Check version number. */
3140 if (tdb_fetch_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, &vers) &&
3141 vers == WINBINDD_CACHE_VERSION) {
3142 cache_bad = false;
3145 if (cache_bad) {
3146 DEBUG(0,("initialize_winbindd_cache: clearing cache "
3147 "and re-creating with version number %d\n",
3148 WINBINDD_CACHE_VERSION ));
3150 tdb_close(wcache->tdb);
3151 wcache->tdb = NULL;
3153 if (unlink(cache_path("winbindd_cache.tdb")) == -1) {
3154 DEBUG(0,("initialize_winbindd_cache: unlink %s failed %s ",
3155 cache_path("winbindd_cache.tdb"),
3156 strerror(errno) ));
3157 return false;
3159 if (!init_wcache()) {
3160 DEBUG(0,("initialize_winbindd_cache: re-initialization "
3161 "init_wcache failed.\n"));
3162 return false;
3165 /* Write the version. */
3166 if (!tdb_store_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, WINBINDD_CACHE_VERSION)) {
3167 DEBUG(0,("initialize_winbindd_cache: version number store failed %s\n",
3168 tdb_errorstr(wcache->tdb) ));
3169 return false;
3173 tdb_close(wcache->tdb);
3174 wcache->tdb = NULL;
3175 return true;
3178 void close_winbindd_cache(void)
3180 if (!wcache) {
3181 return;
3183 if (wcache->tdb) {
3184 tdb_close(wcache->tdb);
3185 wcache->tdb = NULL;
3189 bool lookup_cached_sid(TALLOC_CTX *mem_ctx, const struct dom_sid *sid,
3190 char **domain_name, char **name,
3191 enum lsa_SidType *type)
3193 struct winbindd_domain *domain;
3194 NTSTATUS status;
3196 domain = find_lookup_domain_from_sid(sid);
3197 if (domain == NULL) {
3198 return false;
3200 status = wcache_sid_to_name(domain, sid, mem_ctx, domain_name, name,
3201 type);
3202 return NT_STATUS_IS_OK(status);
3205 bool lookup_cached_name(const char *domain_name,
3206 const char *name,
3207 struct dom_sid *sid,
3208 enum lsa_SidType *type)
3210 struct winbindd_domain *domain;
3211 NTSTATUS status;
3212 bool original_online_state;
3214 domain = find_lookup_domain_from_name(domain_name);
3215 if (domain == NULL) {
3216 return false;
3219 /* If we are doing a cached logon, temporarily set the domain
3220 offline so the cache won't expire the entry */
3222 original_online_state = domain->online;
3223 domain->online = false;
3224 status = wcache_name_to_sid(domain, domain_name, name, sid, type);
3225 domain->online = original_online_state;
3227 return NT_STATUS_IS_OK(status);
3230 void cache_name2sid(struct winbindd_domain *domain,
3231 const char *domain_name, const char *name,
3232 enum lsa_SidType type, const struct dom_sid *sid)
3234 refresh_sequence_number(domain, false);
3235 wcache_save_name_to_sid(domain, NT_STATUS_OK, domain_name, name,
3236 sid, type);
3240 * The original idea that this cache only contains centries has
3241 * been blurred - now other stuff gets put in here. Ensure we
3242 * ignore these things on cleanup.
3245 static int traverse_fn_cleanup(TDB_CONTEXT *the_tdb, TDB_DATA kbuf,
3246 TDB_DATA dbuf, void *state)
3248 struct cache_entry *centry;
3250 if (is_non_centry_key(kbuf)) {
3251 return 0;
3254 centry = wcache_fetch_raw((char *)kbuf.dptr);
3255 if (!centry) {
3256 return 0;
3259 if (!NT_STATUS_IS_OK(centry->status)) {
3260 DEBUG(10,("deleting centry %s\n", (const char *)kbuf.dptr));
3261 tdb_delete(the_tdb, kbuf);
3264 centry_free(centry);
3265 return 0;
3268 /* flush the cache */
3269 void wcache_flush_cache(void)
3271 if (!wcache)
3272 return;
3273 if (wcache->tdb) {
3274 tdb_close(wcache->tdb);
3275 wcache->tdb = NULL;
3277 if (!winbindd_use_cache()) {
3278 return;
3281 /* when working offline we must not clear the cache on restart */
3282 wcache->tdb = tdb_open_log(cache_path("winbindd_cache.tdb"),
3283 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
3284 TDB_INCOMPATIBLE_HASH |
3285 (lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST)),
3286 O_RDWR|O_CREAT, 0600);
3288 if (!wcache->tdb) {
3289 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
3290 return;
3293 tdb_traverse(wcache->tdb, traverse_fn_cleanup, NULL);
3295 DEBUG(10,("wcache_flush_cache success\n"));
3298 /* Count cached creds */
3300 static int traverse_fn_cached_creds(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
3301 void *state)
3303 int *cred_count = (int*)state;
3305 if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
3306 (*cred_count)++;
3308 return 0;
3311 NTSTATUS wcache_count_cached_creds(struct winbindd_domain *domain, int *count)
3313 struct winbind_cache *cache = get_cache(domain);
3315 *count = 0;
3317 if (!cache->tdb) {
3318 return NT_STATUS_INTERNAL_DB_ERROR;
3321 tdb_traverse(cache->tdb, traverse_fn_cached_creds, (void *)count);
3323 return NT_STATUS_OK;
3326 struct cred_list {
3327 struct cred_list *prev, *next;
3328 TDB_DATA key;
3329 fstring name;
3330 time_t created;
3332 static struct cred_list *wcache_cred_list;
3334 static int traverse_fn_get_credlist(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
3335 void *state)
3337 struct cred_list *cred;
3339 if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
3341 cred = SMB_MALLOC_P(struct cred_list);
3342 if (cred == NULL) {
3343 DEBUG(0,("traverse_fn_remove_first_creds: failed to malloc new entry for list\n"));
3344 return -1;
3347 ZERO_STRUCTP(cred);
3349 /* save a copy of the key */
3351 fstrcpy(cred->name, (const char *)kbuf.dptr);
3352 DLIST_ADD(wcache_cred_list, cred);
3355 return 0;
3358 NTSTATUS wcache_remove_oldest_cached_creds(struct winbindd_domain *domain, const struct dom_sid *sid)
3360 struct winbind_cache *cache = get_cache(domain);
3361 NTSTATUS status;
3362 int ret;
3363 struct cred_list *cred, *oldest = NULL;
3365 if (!cache->tdb) {
3366 return NT_STATUS_INTERNAL_DB_ERROR;
3369 /* we possibly already have an entry */
3370 if (sid && NT_STATUS_IS_OK(wcache_cached_creds_exist(domain, sid))) {
3372 fstring key_str, tmp;
3374 DEBUG(11,("we already have an entry, deleting that\n"));
3376 fstr_sprintf(key_str, "CRED/%s", sid_to_fstring(tmp, sid));
3378 tdb_delete(cache->tdb, string_tdb_data(key_str));
3380 return NT_STATUS_OK;
3383 ret = tdb_traverse(cache->tdb, traverse_fn_get_credlist, NULL);
3384 if (ret == 0) {
3385 return NT_STATUS_OK;
3386 } else if ((ret == -1) || (wcache_cred_list == NULL)) {
3387 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
3390 ZERO_STRUCTP(oldest);
3392 for (cred = wcache_cred_list; cred; cred = cred->next) {
3394 TDB_DATA data;
3395 time_t t;
3397 data = tdb_fetch(cache->tdb, string_tdb_data(cred->name));
3398 if (!data.dptr) {
3399 DEBUG(10,("wcache_remove_oldest_cached_creds: entry for [%s] not found\n",
3400 cred->name));
3401 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
3402 goto done;
3405 t = IVAL(data.dptr, 0);
3406 SAFE_FREE(data.dptr);
3408 if (!oldest) {
3409 oldest = SMB_MALLOC_P(struct cred_list);
3410 if (oldest == NULL) {
3411 status = NT_STATUS_NO_MEMORY;
3412 goto done;
3415 fstrcpy(oldest->name, cred->name);
3416 oldest->created = t;
3417 continue;
3420 if (t < oldest->created) {
3421 fstrcpy(oldest->name, cred->name);
3422 oldest->created = t;
3426 if (tdb_delete(cache->tdb, string_tdb_data(oldest->name)) == 0) {
3427 status = NT_STATUS_OK;
3428 } else {
3429 status = NT_STATUS_UNSUCCESSFUL;
3431 done:
3432 SAFE_FREE(wcache_cred_list);
3433 SAFE_FREE(oldest);
3435 return status;
3438 /* Change the global online/offline state. */
3439 bool set_global_winbindd_state_offline(void)
3441 TDB_DATA data;
3443 DEBUG(10,("set_global_winbindd_state_offline: offline requested.\n"));
3445 /* Only go offline if someone has created
3446 the key "WINBINDD_OFFLINE" in the cache tdb. */
3448 if (wcache == NULL || wcache->tdb == NULL) {
3449 DEBUG(10,("set_global_winbindd_state_offline: wcache not open yet.\n"));
3450 return false;
3453 if (!lp_winbind_offline_logon()) {
3454 DEBUG(10,("set_global_winbindd_state_offline: rejecting.\n"));
3455 return false;
3458 if (global_winbindd_offline_state) {
3459 /* Already offline. */
3460 return true;
3463 data = tdb_fetch_bystring( wcache->tdb, "WINBINDD_OFFLINE" );
3465 if (!data.dptr || data.dsize != 4) {
3466 DEBUG(10,("set_global_winbindd_state_offline: offline state not set.\n"));
3467 SAFE_FREE(data.dptr);
3468 return false;
3469 } else {
3470 DEBUG(10,("set_global_winbindd_state_offline: offline state set.\n"));
3471 global_winbindd_offline_state = true;
3472 SAFE_FREE(data.dptr);
3473 return true;
3477 void set_global_winbindd_state_online(void)
3479 DEBUG(10,("set_global_winbindd_state_online: online requested.\n"));
3481 if (!lp_winbind_offline_logon()) {
3482 DEBUG(10,("set_global_winbindd_state_online: rejecting.\n"));
3483 return;
3486 if (!global_winbindd_offline_state) {
3487 /* Already online. */
3488 return;
3490 global_winbindd_offline_state = false;
3492 if (!wcache->tdb) {
3493 return;
3496 /* Ensure there is no key "WINBINDD_OFFLINE" in the cache tdb. */
3497 tdb_delete_bystring(wcache->tdb, "WINBINDD_OFFLINE");
3500 bool get_global_winbindd_state_offline(void)
3502 return global_winbindd_offline_state;
3505 /***********************************************************************
3506 Validate functions for all possible cache tdb keys.
3507 ***********************************************************************/
3509 static struct cache_entry *create_centry_validate(const char *kstr, TDB_DATA data,
3510 struct tdb_validation_status *state)
3512 struct cache_entry *centry;
3514 centry = SMB_XMALLOC_P(struct cache_entry);
3515 centry->data = (unsigned char *)memdup(data.dptr, data.dsize);
3516 if (!centry->data) {
3517 SAFE_FREE(centry);
3518 return NULL;
3520 centry->len = data.dsize;
3521 centry->ofs = 0;
3523 if (centry->len < 16) {
3524 /* huh? corrupt cache? */
3525 DEBUG(0,("create_centry_validate: Corrupt cache for key %s "
3526 "(len < 16) ?\n", kstr));
3527 centry_free(centry);
3528 state->bad_entry = true;
3529 state->success = false;
3530 return NULL;
3533 centry->status = NT_STATUS(centry_uint32(centry));
3534 centry->sequence_number = centry_uint32(centry);
3535 centry->timeout = centry_uint64_t(centry);
3536 return centry;
3539 static int validate_seqnum(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3540 struct tdb_validation_status *state)
3542 if (dbuf.dsize != 8) {
3543 DEBUG(0,("validate_seqnum: Corrupt cache for key %s (len %u != 8) ?\n",
3544 keystr, (unsigned int)dbuf.dsize ));
3545 state->bad_entry = true;
3546 return 1;
3548 return 0;
3551 static int validate_ns(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3552 struct tdb_validation_status *state)
3554 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3555 if (!centry) {
3556 return 1;
3559 (void)centry_uint32(centry);
3560 if (NT_STATUS_IS_OK(centry->status)) {
3561 struct dom_sid sid;
3562 (void)centry_sid(centry, &sid);
3565 centry_free(centry);
3567 if (!(state->success)) {
3568 return 1;
3570 DEBUG(10,("validate_ns: %s ok\n", keystr));
3571 return 0;
3574 static int validate_sn(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3575 struct tdb_validation_status *state)
3577 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3578 if (!centry) {
3579 return 1;
3582 if (NT_STATUS_IS_OK(centry->status)) {
3583 (void)centry_uint32(centry);
3584 (void)centry_string(centry, mem_ctx);
3585 (void)centry_string(centry, mem_ctx);
3588 centry_free(centry);
3590 if (!(state->success)) {
3591 return 1;
3593 DEBUG(10,("validate_sn: %s ok\n", keystr));
3594 return 0;
3597 static int validate_u(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3598 struct tdb_validation_status *state)
3600 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3601 struct dom_sid sid;
3603 if (!centry) {
3604 return 1;
3607 (void)centry_string(centry, mem_ctx);
3608 (void)centry_string(centry, mem_ctx);
3609 (void)centry_string(centry, mem_ctx);
3610 (void)centry_string(centry, mem_ctx);
3611 (void)centry_uint32(centry);
3612 (void)centry_sid(centry, &sid);
3613 (void)centry_sid(centry, &sid);
3615 centry_free(centry);
3617 if (!(state->success)) {
3618 return 1;
3620 DEBUG(10,("validate_u: %s ok\n", keystr));
3621 return 0;
3624 static int validate_loc_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3625 struct tdb_validation_status *state)
3627 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3629 if (!centry) {
3630 return 1;
3633 (void)centry_nttime(centry);
3634 (void)centry_nttime(centry);
3635 (void)centry_uint16(centry);
3637 centry_free(centry);
3639 if (!(state->success)) {
3640 return 1;
3642 DEBUG(10,("validate_loc_pol: %s ok\n", keystr));
3643 return 0;
3646 static int validate_pwd_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3647 struct tdb_validation_status *state)
3649 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3651 if (!centry) {
3652 return 1;
3655 (void)centry_uint16(centry);
3656 (void)centry_uint16(centry);
3657 (void)centry_uint32(centry);
3658 (void)centry_nttime(centry);
3659 (void)centry_nttime(centry);
3661 centry_free(centry);
3663 if (!(state->success)) {
3664 return 1;
3666 DEBUG(10,("validate_pwd_pol: %s ok\n", keystr));
3667 return 0;
3670 static int validate_cred(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3671 struct tdb_validation_status *state)
3673 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3675 if (!centry) {
3676 return 1;
3679 (void)centry_time(centry);
3680 (void)centry_hash16(centry, mem_ctx);
3682 /* We only have 17 bytes more data in the salted cred case. */
3683 if (centry->len - centry->ofs == 17) {
3684 (void)centry_hash16(centry, mem_ctx);
3687 centry_free(centry);
3689 if (!(state->success)) {
3690 return 1;
3692 DEBUG(10,("validate_cred: %s ok\n", keystr));
3693 return 0;
3696 static int validate_ul(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3697 struct tdb_validation_status *state)
3699 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3700 int32 num_entries, i;
3702 if (!centry) {
3703 return 1;
3706 num_entries = (int32)centry_uint32(centry);
3708 for (i=0; i< num_entries; i++) {
3709 struct dom_sid sid;
3710 (void)centry_string(centry, mem_ctx);
3711 (void)centry_string(centry, mem_ctx);
3712 (void)centry_string(centry, mem_ctx);
3713 (void)centry_string(centry, mem_ctx);
3714 (void)centry_sid(centry, &sid);
3715 (void)centry_sid(centry, &sid);
3718 centry_free(centry);
3720 if (!(state->success)) {
3721 return 1;
3723 DEBUG(10,("validate_ul: %s ok\n", keystr));
3724 return 0;
3727 static int validate_gl(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3728 struct tdb_validation_status *state)
3730 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3731 int32 num_entries, i;
3733 if (!centry) {
3734 return 1;
3737 num_entries = centry_uint32(centry);
3739 for (i=0; i< num_entries; i++) {
3740 (void)centry_string(centry, mem_ctx);
3741 (void)centry_string(centry, mem_ctx);
3742 (void)centry_uint32(centry);
3745 centry_free(centry);
3747 if (!(state->success)) {
3748 return 1;
3750 DEBUG(10,("validate_gl: %s ok\n", keystr));
3751 return 0;
3754 static int validate_ug(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3755 struct tdb_validation_status *state)
3757 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3758 int32 num_groups, i;
3760 if (!centry) {
3761 return 1;
3764 num_groups = centry_uint32(centry);
3766 for (i=0; i< num_groups; i++) {
3767 struct dom_sid sid;
3768 centry_sid(centry, &sid);
3771 centry_free(centry);
3773 if (!(state->success)) {
3774 return 1;
3776 DEBUG(10,("validate_ug: %s ok\n", keystr));
3777 return 0;
3780 static int validate_ua(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3781 struct tdb_validation_status *state)
3783 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3784 int32 num_aliases, i;
3786 if (!centry) {
3787 return 1;
3790 num_aliases = centry_uint32(centry);
3792 for (i=0; i < num_aliases; i++) {
3793 (void)centry_uint32(centry);
3796 centry_free(centry);
3798 if (!(state->success)) {
3799 return 1;
3801 DEBUG(10,("validate_ua: %s ok\n", keystr));
3802 return 0;
3805 static int validate_gm(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3806 struct tdb_validation_status *state)
3808 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3809 int32 num_names, i;
3811 if (!centry) {
3812 return 1;
3815 num_names = centry_uint32(centry);
3817 for (i=0; i< num_names; i++) {
3818 struct dom_sid sid;
3819 centry_sid(centry, &sid);
3820 (void)centry_string(centry, mem_ctx);
3821 (void)centry_uint32(centry);
3824 centry_free(centry);
3826 if (!(state->success)) {
3827 return 1;
3829 DEBUG(10,("validate_gm: %s ok\n", keystr));
3830 return 0;
3833 static int validate_dr(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3834 struct tdb_validation_status *state)
3836 /* Can't say anything about this other than must be nonzero. */
3837 if (dbuf.dsize == 0) {
3838 DEBUG(0,("validate_dr: Corrupt cache for key %s (len == 0) ?\n",
3839 keystr));
3840 state->bad_entry = true;
3841 state->success = false;
3842 return 1;
3845 DEBUG(10,("validate_dr: %s ok\n", keystr));
3846 return 0;
3849 static int validate_de(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3850 struct tdb_validation_status *state)
3852 /* Can't say anything about this other than must be nonzero. */
3853 if (dbuf.dsize == 0) {
3854 DEBUG(0,("validate_de: Corrupt cache for key %s (len == 0) ?\n",
3855 keystr));
3856 state->bad_entry = true;
3857 state->success = false;
3858 return 1;
3861 DEBUG(10,("validate_de: %s ok\n", keystr));
3862 return 0;
3865 static int validate_pwinfo(TALLOC_CTX *mem_ctx, const char *keystr,
3866 TDB_DATA dbuf, struct tdb_validation_status *state)
3868 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3870 if (!centry) {
3871 return 1;
3874 (void)centry_string(centry, mem_ctx);
3875 (void)centry_string(centry, mem_ctx);
3876 (void)centry_string(centry, mem_ctx);
3877 (void)centry_uint32(centry);
3879 centry_free(centry);
3881 if (!(state->success)) {
3882 return 1;
3884 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3885 return 0;
3888 static int validate_nss_an(TALLOC_CTX *mem_ctx, const char *keystr,
3889 TDB_DATA dbuf,
3890 struct tdb_validation_status *state)
3892 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3894 if (!centry) {
3895 return 1;
3898 (void)centry_string( centry, mem_ctx );
3900 centry_free(centry);
3902 if (!(state->success)) {
3903 return 1;
3905 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3906 return 0;
3909 static int validate_nss_na(TALLOC_CTX *mem_ctx, const char *keystr,
3910 TDB_DATA dbuf,
3911 struct tdb_validation_status *state)
3913 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3915 if (!centry) {
3916 return 1;
3919 (void)centry_string( centry, mem_ctx );
3921 centry_free(centry);
3923 if (!(state->success)) {
3924 return 1;
3926 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3927 return 0;
3930 static int validate_trustdomcache(TALLOC_CTX *mem_ctx, const char *keystr,
3931 TDB_DATA dbuf,
3932 struct tdb_validation_status *state)
3934 if (dbuf.dsize == 0) {
3935 DEBUG(0, ("validate_trustdomcache: Corrupt cache for "
3936 "key %s (len ==0) ?\n", keystr));
3937 state->bad_entry = true;
3938 state->success = false;
3939 return 1;
3942 DEBUG(10, ("validate_trustdomcache: %s ok\n", keystr));
3943 DEBUGADD(10, (" Don't trust me, I am a DUMMY!\n"));
3944 return 0;
3947 static int validate_offline(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3948 struct tdb_validation_status *state)
3950 if (dbuf.dsize != 4) {
3951 DEBUG(0,("validate_offline: Corrupt cache for key %s (len %u != 4) ?\n",
3952 keystr, (unsigned int)dbuf.dsize ));
3953 state->bad_entry = true;
3954 state->success = false;
3955 return 1;
3957 DEBUG(10,("validate_offline: %s ok\n", keystr));
3958 return 0;
3961 static int validate_ndr(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3962 struct tdb_validation_status *state)
3965 * Ignore validation for now. The proper way to do this is with a
3966 * checksum. Just pure parsing does not really catch much.
3968 return 0;
3971 static int validate_cache_version(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3972 struct tdb_validation_status *state)
3974 if (dbuf.dsize != 4) {
3975 DEBUG(0, ("validate_cache_version: Corrupt cache for "
3976 "key %s (len %u != 4) ?\n",
3977 keystr, (unsigned int)dbuf.dsize));
3978 state->bad_entry = true;
3979 state->success = false;
3980 return 1;
3983 DEBUG(10, ("validate_cache_version: %s ok\n", keystr));
3984 return 0;
3987 /***********************************************************************
3988 A list of all possible cache tdb keys with associated validation
3989 functions.
3990 ***********************************************************************/
3992 struct key_val_struct {
3993 const char *keyname;
3994 int (*validate_data_fn)(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, struct tdb_validation_status* state);
3995 } key_val[] = {
3996 {"SEQNUM/", validate_seqnum},
3997 {"NS/", validate_ns},
3998 {"SN/", validate_sn},
3999 {"U/", validate_u},
4000 {"LOC_POL/", validate_loc_pol},
4001 {"PWD_POL/", validate_pwd_pol},
4002 {"CRED/", validate_cred},
4003 {"UL/", validate_ul},
4004 {"GL/", validate_gl},
4005 {"UG/", validate_ug},
4006 {"UA", validate_ua},
4007 {"GM/", validate_gm},
4008 {"DR/", validate_dr},
4009 {"DE/", validate_de},
4010 {"NSS/PWINFO/", validate_pwinfo},
4011 {"TRUSTDOMCACHE/", validate_trustdomcache},
4012 {"NSS/NA/", validate_nss_na},
4013 {"NSS/AN/", validate_nss_an},
4014 {"WINBINDD_OFFLINE", validate_offline},
4015 {"NDR/", validate_ndr},
4016 {WINBINDD_CACHE_VERSION_KEYSTR, validate_cache_version},
4017 {NULL, NULL}
4020 /***********************************************************************
4021 Function to look at every entry in the tdb and validate it as far as
4022 possible.
4023 ***********************************************************************/
4025 static int cache_traverse_validate_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, void *state)
4027 int i;
4028 unsigned int max_key_len = 1024;
4029 struct tdb_validation_status *v_state = (struct tdb_validation_status *)state;
4031 /* Paranoia check. */
4032 if (strncmp("UA/", (const char *)kbuf.dptr, 3) == 0) {
4033 max_key_len = 1024 * 1024;
4035 if (kbuf.dsize > max_key_len) {
4036 DEBUG(0, ("cache_traverse_validate_fn: key length too large: "
4037 "(%u) > (%u)\n\n",
4038 (unsigned int)kbuf.dsize, (unsigned int)max_key_len));
4039 return 1;
4042 for (i = 0; key_val[i].keyname; i++) {
4043 size_t namelen = strlen(key_val[i].keyname);
4044 if (kbuf.dsize >= namelen && (
4045 strncmp(key_val[i].keyname, (const char *)kbuf.dptr, namelen)) == 0) {
4046 TALLOC_CTX *mem_ctx;
4047 char *keystr;
4048 int ret;
4050 keystr = SMB_MALLOC_ARRAY(char, kbuf.dsize+1);
4051 if (!keystr) {
4052 return 1;
4054 memcpy(keystr, kbuf.dptr, kbuf.dsize);
4055 keystr[kbuf.dsize] = '\0';
4057 mem_ctx = talloc_init("validate_ctx");
4058 if (!mem_ctx) {
4059 SAFE_FREE(keystr);
4060 return 1;
4063 ret = key_val[i].validate_data_fn(mem_ctx, keystr, dbuf,
4064 v_state);
4066 SAFE_FREE(keystr);
4067 talloc_destroy(mem_ctx);
4068 return ret;
4072 DEBUG(0,("cache_traverse_validate_fn: unknown cache entry\nkey :\n"));
4073 dump_data(0, (uint8 *)kbuf.dptr, kbuf.dsize);
4074 DEBUG(0,("data :\n"));
4075 dump_data(0, (uint8 *)dbuf.dptr, dbuf.dsize);
4076 v_state->unknown_key = true;
4077 v_state->success = false;
4078 return 1; /* terminate. */
4081 static void validate_panic(const char *const why)
4083 DEBUG(0,("validating cache: would panic %s\n", why ));
4084 DEBUGADD(0, ("exiting instead (cache validation mode)\n"));
4085 exit(47);
4088 static int wbcache_update_centry_fn(TDB_CONTEXT *tdb,
4089 TDB_DATA key,
4090 TDB_DATA data,
4091 void *state)
4093 uint64_t ctimeout;
4094 TDB_DATA blob;
4096 if (is_non_centry_key(key)) {
4097 return 0;
4100 if (data.dptr == NULL || data.dsize == 0) {
4101 if (tdb_delete(tdb, key) < 0) {
4102 DEBUG(0, ("tdb_delete for [%s] failed!\n",
4103 key.dptr));
4104 return 1;
4108 /* add timeout to blob (uint64_t) */
4109 blob.dsize = data.dsize + 8;
4111 blob.dptr = SMB_XMALLOC_ARRAY(uint8_t, blob.dsize);
4112 if (blob.dptr == NULL) {
4113 return 1;
4115 memset(blob.dptr, 0, blob.dsize);
4117 /* copy status and seqnum */
4118 memcpy(blob.dptr, data.dptr, 8);
4120 /* add timeout */
4121 ctimeout = lp_winbind_cache_time() + time(NULL);
4122 SBVAL(blob.dptr, 8, ctimeout);
4124 /* copy the rest */
4125 memcpy(blob.dptr + 16, data.dptr + 8, data.dsize - 8);
4127 if (tdb_store(tdb, key, blob, TDB_REPLACE) < 0) {
4128 DEBUG(0, ("tdb_store to update [%s] failed!\n",
4129 key.dptr));
4130 SAFE_FREE(blob.dptr);
4131 return 1;
4134 SAFE_FREE(blob.dptr);
4135 return 0;
4138 static bool wbcache_upgrade_v1_to_v2(TDB_CONTEXT *tdb)
4140 int rc;
4142 DEBUG(1, ("Upgrade to version 2 of the winbindd_cache.tdb\n"));
4144 rc = tdb_traverse(tdb, wbcache_update_centry_fn, NULL);
4145 if (rc < 0) {
4146 return false;
4149 return true;
4152 /***********************************************************************
4153 Try and validate every entry in the winbindd cache. If we fail here,
4154 delete the cache tdb and return non-zero.
4155 ***********************************************************************/
4157 int winbindd_validate_cache(void)
4159 int ret = -1;
4160 const char *tdb_path = cache_path("winbindd_cache.tdb");
4161 TDB_CONTEXT *tdb = NULL;
4162 uint32_t vers_id;
4163 bool ok;
4165 DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
4166 smb_panic_fn = validate_panic;
4168 tdb = tdb_open_log(tdb_path,
4169 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
4170 TDB_INCOMPATIBLE_HASH |
4171 ( lp_winbind_offline_logon()
4172 ? TDB_DEFAULT
4173 : TDB_DEFAULT | TDB_CLEAR_IF_FIRST ),
4174 O_RDWR|O_CREAT,
4175 0600);
4176 if (!tdb) {
4177 DEBUG(0, ("winbindd_validate_cache: "
4178 "error opening/initializing tdb\n"));
4179 goto done;
4182 /* Version check and upgrade code. */
4183 if (!tdb_fetch_uint32(tdb, WINBINDD_CACHE_VERSION_KEYSTR, &vers_id)) {
4184 DEBUG(10, ("Fresh database\n"));
4185 tdb_store_uint32(tdb, WINBINDD_CACHE_VERSION_KEYSTR, WINBINDD_CACHE_VERSION);
4186 vers_id = WINBINDD_CACHE_VERSION;
4189 if (vers_id != WINBINDD_CACHE_VERSION) {
4190 if (vers_id == WINBINDD_CACHE_VER1) {
4191 ok = wbcache_upgrade_v1_to_v2(tdb);
4192 if (!ok) {
4193 DEBUG(10, ("winbindd_validate_cache: upgrade to version 2 failed.\n"));
4194 unlink(tdb_path);
4195 goto done;
4198 tdb_store_uint32(tdb,
4199 WINBINDD_CACHE_VERSION_KEYSTR,
4200 WINBINDD_CACHE_VERSION);
4201 vers_id = WINBINDD_CACHE_VER2;
4205 tdb_close(tdb);
4207 ret = tdb_validate_and_backup(tdb_path, cache_traverse_validate_fn);
4209 if (ret != 0) {
4210 DEBUG(10, ("winbindd_validate_cache: validation not successful.\n"));
4211 DEBUGADD(10, ("removing tdb %s.\n", tdb_path));
4212 unlink(tdb_path);
4215 done:
4216 DEBUG(10, ("winbindd_validate_cache: restoring panic function\n"));
4217 smb_panic_fn = smb_panic;
4218 return ret;
4221 /***********************************************************************
4222 Try and validate every entry in the winbindd cache.
4223 ***********************************************************************/
4225 int winbindd_validate_cache_nobackup(void)
4227 int ret = -1;
4228 const char *tdb_path = cache_path("winbindd_cache.tdb");
4230 DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
4231 smb_panic_fn = validate_panic;
4234 if (wcache == NULL || wcache->tdb == NULL) {
4235 ret = tdb_validate_open(tdb_path, cache_traverse_validate_fn);
4236 } else {
4237 ret = tdb_validate(wcache->tdb, cache_traverse_validate_fn);
4240 if (ret != 0) {
4241 DEBUG(10, ("winbindd_validate_cache_nobackup: validation not "
4242 "successful.\n"));
4245 DEBUG(10, ("winbindd_validate_cache_nobackup: restoring panic "
4246 "function\n"));
4247 smb_panic_fn = smb_panic;
4248 return ret;
4251 bool winbindd_cache_validate_and_initialize(void)
4253 close_winbindd_cache();
4255 if (lp_winbind_offline_logon()) {
4256 if (winbindd_validate_cache() < 0) {
4257 DEBUG(0, ("winbindd cache tdb corrupt and no backup "
4258 "could be restored.\n"));
4262 return initialize_winbindd_cache();
4265 /*********************************************************************
4266 ********************************************************************/
4268 static bool add_wbdomain_to_tdc_array( struct winbindd_domain *new_dom,
4269 struct winbindd_tdc_domain **domains,
4270 size_t *num_domains )
4272 struct winbindd_tdc_domain *list = NULL;
4273 size_t idx;
4274 int i;
4275 bool set_only = false;
4277 /* don't allow duplicates */
4279 idx = *num_domains;
4280 list = *domains;
4282 for ( i=0; i< (*num_domains); i++ ) {
4283 if ( strequal( new_dom->name, list[i].domain_name ) ) {
4284 DEBUG(10,("add_wbdomain_to_tdc_array: Found existing record for %s\n",
4285 new_dom->name));
4286 idx = i;
4287 set_only = true;
4289 break;
4293 if ( !set_only ) {
4294 if ( !*domains ) {
4295 list = TALLOC_ARRAY( NULL, struct winbindd_tdc_domain, 1 );
4296 idx = 0;
4297 } else {
4298 list = TALLOC_REALLOC_ARRAY( *domains, *domains,
4299 struct winbindd_tdc_domain,
4300 (*num_domains)+1);
4301 idx = *num_domains;
4304 ZERO_STRUCT( list[idx] );
4307 if ( !list )
4308 return false;
4310 list[idx].domain_name = talloc_strdup( list, new_dom->name );
4311 list[idx].dns_name = talloc_strdup( list, new_dom->alt_name );
4313 if ( !is_null_sid( &new_dom->sid ) ) {
4314 sid_copy( &list[idx].sid, &new_dom->sid );
4315 } else {
4316 sid_copy(&list[idx].sid, &global_sid_NULL);
4319 if ( new_dom->domain_flags != 0x0 )
4320 list[idx].trust_flags = new_dom->domain_flags;
4322 if ( new_dom->domain_type != 0x0 )
4323 list[idx].trust_type = new_dom->domain_type;
4325 if ( new_dom->domain_trust_attribs != 0x0 )
4326 list[idx].trust_attribs = new_dom->domain_trust_attribs;
4328 if ( !set_only ) {
4329 *domains = list;
4330 *num_domains = idx + 1;
4333 return true;
4336 /*********************************************************************
4337 ********************************************************************/
4339 static TDB_DATA make_tdc_key( const char *domain_name )
4341 char *keystr = NULL;
4342 TDB_DATA key = { NULL, 0 };
4344 if ( !domain_name ) {
4345 DEBUG(5,("make_tdc_key: Keyname workgroup is NULL!\n"));
4346 return key;
4349 if (asprintf( &keystr, "TRUSTDOMCACHE/%s", domain_name ) == -1) {
4350 return key;
4352 key = string_term_tdb_data(keystr);
4354 return key;
4357 /*********************************************************************
4358 ********************************************************************/
4360 static int pack_tdc_domains( struct winbindd_tdc_domain *domains,
4361 size_t num_domains,
4362 unsigned char **buf )
4364 unsigned char *buffer = NULL;
4365 int len = 0;
4366 int buflen = 0;
4367 int i = 0;
4369 DEBUG(10,("pack_tdc_domains: Packing %d trusted domains\n",
4370 (int)num_domains));
4372 buflen = 0;
4374 again:
4375 len = 0;
4377 /* Store the number of array items first */
4378 len += tdb_pack( buffer+len, buflen-len, "d",
4379 num_domains );
4381 /* now pack each domain trust record */
4382 for ( i=0; i<num_domains; i++ ) {
4384 fstring tmp;
4386 if ( buflen > 0 ) {
4387 DEBUG(10,("pack_tdc_domains: Packing domain %s (%s)\n",
4388 domains[i].domain_name,
4389 domains[i].dns_name ? domains[i].dns_name : "UNKNOWN" ));
4392 len += tdb_pack( buffer+len, buflen-len, "fffddd",
4393 domains[i].domain_name,
4394 domains[i].dns_name,
4395 sid_to_fstring(tmp, &domains[i].sid),
4396 domains[i].trust_flags,
4397 domains[i].trust_attribs,
4398 domains[i].trust_type );
4401 if ( buflen < len ) {
4402 SAFE_FREE(buffer);
4403 if ( (buffer = SMB_MALLOC_ARRAY(unsigned char, len)) == NULL ) {
4404 DEBUG(0,("pack_tdc_domains: failed to alloc buffer!\n"));
4405 buflen = -1;
4406 goto done;
4408 buflen = len;
4409 goto again;
4412 *buf = buffer;
4414 done:
4415 return buflen;
4418 /*********************************************************************
4419 ********************************************************************/
4421 static size_t unpack_tdc_domains( unsigned char *buf, int buflen,
4422 struct winbindd_tdc_domain **domains )
4424 fstring domain_name, dns_name, sid_string;
4425 uint32 type, attribs, flags;
4426 int num_domains;
4427 int len = 0;
4428 int i;
4429 struct winbindd_tdc_domain *list = NULL;
4431 /* get the number of domains */
4432 len += tdb_unpack( buf+len, buflen-len, "d", &num_domains);
4433 if ( len == -1 ) {
4434 DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
4435 return 0;
4438 list = TALLOC_ARRAY( NULL, struct winbindd_tdc_domain, num_domains );
4439 if ( !list ) {
4440 DEBUG(0,("unpack_tdc_domains: Failed to talloc() domain list!\n"));
4441 return 0;
4444 for ( i=0; i<num_domains; i++ ) {
4445 len += tdb_unpack( buf+len, buflen-len, "fffddd",
4446 domain_name,
4447 dns_name,
4448 sid_string,
4449 &flags,
4450 &attribs,
4451 &type );
4453 if ( len == -1 ) {
4454 DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
4455 TALLOC_FREE( list );
4456 return 0;
4459 DEBUG(11,("unpack_tdc_domains: Unpacking domain %s (%s) "
4460 "SID %s, flags = 0x%x, attribs = 0x%x, type = 0x%x\n",
4461 domain_name, dns_name, sid_string,
4462 flags, attribs, type));
4464 list[i].domain_name = talloc_strdup( list, domain_name );
4465 list[i].dns_name = talloc_strdup( list, dns_name );
4466 if ( !string_to_sid( &(list[i].sid), sid_string ) ) {
4467 DEBUG(10,("unpack_tdc_domains: no SID for domain %s\n",
4468 domain_name));
4470 list[i].trust_flags = flags;
4471 list[i].trust_attribs = attribs;
4472 list[i].trust_type = type;
4475 *domains = list;
4477 return num_domains;
4480 /*********************************************************************
4481 ********************************************************************/
4483 static bool wcache_tdc_store_list( struct winbindd_tdc_domain *domains, size_t num_domains )
4485 TDB_DATA key = make_tdc_key( lp_workgroup() );
4486 TDB_DATA data = { NULL, 0 };
4487 int ret;
4489 if ( !key.dptr )
4490 return false;
4492 /* See if we were asked to delete the cache entry */
4494 if ( !domains ) {
4495 ret = tdb_delete( wcache->tdb, key );
4496 goto done;
4499 data.dsize = pack_tdc_domains( domains, num_domains, &data.dptr );
4501 if ( !data.dptr ) {
4502 ret = -1;
4503 goto done;
4506 ret = tdb_store( wcache->tdb, key, data, 0 );
4508 done:
4509 SAFE_FREE( data.dptr );
4510 SAFE_FREE( key.dptr );
4512 return ( ret != -1 );
4515 /*********************************************************************
4516 ********************************************************************/
4518 bool wcache_tdc_fetch_list( struct winbindd_tdc_domain **domains, size_t *num_domains )
4520 TDB_DATA key = make_tdc_key( lp_workgroup() );
4521 TDB_DATA data = { NULL, 0 };
4523 *domains = NULL;
4524 *num_domains = 0;
4526 if ( !key.dptr )
4527 return false;
4529 data = tdb_fetch( wcache->tdb, key );
4531 SAFE_FREE( key.dptr );
4533 if ( !data.dptr )
4534 return false;
4536 *num_domains = unpack_tdc_domains( data.dptr, data.dsize, domains );
4538 SAFE_FREE( data.dptr );
4540 if ( !*domains )
4541 return false;
4543 return true;
4546 /*********************************************************************
4547 ********************************************************************/
4549 bool wcache_tdc_add_domain( struct winbindd_domain *domain )
4551 struct winbindd_tdc_domain *dom_list = NULL;
4552 size_t num_domains = 0;
4553 bool ret = false;
4555 DEBUG(10,("wcache_tdc_add_domain: Adding domain %s (%s), SID %s, "
4556 "flags = 0x%x, attributes = 0x%x, type = 0x%x\n",
4557 domain->name, domain->alt_name,
4558 sid_string_dbg(&domain->sid),
4559 domain->domain_flags,
4560 domain->domain_trust_attribs,
4561 domain->domain_type));
4563 if ( !init_wcache() ) {
4564 return false;
4567 /* fetch the list */
4569 wcache_tdc_fetch_list( &dom_list, &num_domains );
4571 /* add the new domain */
4573 if ( !add_wbdomain_to_tdc_array( domain, &dom_list, &num_domains ) ) {
4574 goto done;
4577 /* pack the domain */
4579 if ( !wcache_tdc_store_list( dom_list, num_domains ) ) {
4580 goto done;
4583 /* Success */
4585 ret = true;
4586 done:
4587 TALLOC_FREE( dom_list );
4589 return ret;
4592 /*********************************************************************
4593 ********************************************************************/
4595 struct winbindd_tdc_domain * wcache_tdc_fetch_domain( TALLOC_CTX *ctx, const char *name )
4597 struct winbindd_tdc_domain *dom_list = NULL;
4598 size_t num_domains = 0;
4599 int i;
4600 struct winbindd_tdc_domain *d = NULL;
4602 DEBUG(10,("wcache_tdc_fetch_domain: Searching for domain %s\n", name));
4604 if ( !init_wcache() ) {
4605 return false;
4608 /* fetch the list */
4610 wcache_tdc_fetch_list( &dom_list, &num_domains );
4612 for ( i=0; i<num_domains; i++ ) {
4613 if ( strequal(name, dom_list[i].domain_name) ||
4614 strequal(name, dom_list[i].dns_name) )
4616 DEBUG(10,("wcache_tdc_fetch_domain: Found domain %s\n",
4617 name));
4619 d = TALLOC_P( ctx, struct winbindd_tdc_domain );
4620 if ( !d )
4621 break;
4623 d->domain_name = talloc_strdup( d, dom_list[i].domain_name );
4624 d->dns_name = talloc_strdup( d, dom_list[i].dns_name );
4625 sid_copy( &d->sid, &dom_list[i].sid );
4626 d->trust_flags = dom_list[i].trust_flags;
4627 d->trust_type = dom_list[i].trust_type;
4628 d->trust_attribs = dom_list[i].trust_attribs;
4630 break;
4634 TALLOC_FREE( dom_list );
4636 return d;
4639 /*********************************************************************
4640 ********************************************************************/
4642 struct winbindd_tdc_domain*
4643 wcache_tdc_fetch_domainbysid(TALLOC_CTX *ctx,
4644 const struct dom_sid *sid)
4646 struct winbindd_tdc_domain *dom_list = NULL;
4647 size_t num_domains = 0;
4648 int i;
4649 struct winbindd_tdc_domain *d = NULL;
4651 DEBUG(10,("wcache_tdc_fetch_domainbysid: Searching for domain %s\n",
4652 sid_string_dbg(sid)));
4654 if (!init_wcache()) {
4655 return false;
4658 /* fetch the list */
4660 wcache_tdc_fetch_list(&dom_list, &num_domains);
4662 for (i = 0; i<num_domains; i++) {
4663 if (sid_equal(sid, &(dom_list[i].sid))) {
4664 DEBUG(10, ("wcache_tdc_fetch_domainbysid: "
4665 "Found domain %s for SID %s\n",
4666 dom_list[i].domain_name,
4667 sid_string_dbg(sid)));
4669 d = TALLOC_P(ctx, struct winbindd_tdc_domain);
4670 if (!d)
4671 break;
4673 d->domain_name = talloc_strdup(d,
4674 dom_list[i].domain_name);
4676 d->dns_name = talloc_strdup(d, dom_list[i].dns_name);
4677 sid_copy(&d->sid, &dom_list[i].sid);
4678 d->trust_flags = dom_list[i].trust_flags;
4679 d->trust_type = dom_list[i].trust_type;
4680 d->trust_attribs = dom_list[i].trust_attribs;
4682 break;
4686 TALLOC_FREE(dom_list);
4688 return d;
4692 /*********************************************************************
4693 ********************************************************************/
4695 void wcache_tdc_clear( void )
4697 if ( !init_wcache() )
4698 return;
4700 wcache_tdc_store_list( NULL, 0 );
4702 return;
4706 /*********************************************************************
4707 ********************************************************************/
4709 static void wcache_save_user_pwinfo(struct winbindd_domain *domain,
4710 NTSTATUS status,
4711 const struct dom_sid *user_sid,
4712 const char *homedir,
4713 const char *shell,
4714 const char *gecos,
4715 uint32 gid)
4717 struct cache_entry *centry;
4718 fstring tmp;
4720 if ( (centry = centry_start(domain, status)) == NULL )
4721 return;
4723 centry_put_string( centry, homedir );
4724 centry_put_string( centry, shell );
4725 centry_put_string( centry, gecos );
4726 centry_put_uint32( centry, gid );
4728 centry_end(centry, "NSS/PWINFO/%s", sid_to_fstring(tmp, user_sid) );
4730 DEBUG(10,("wcache_save_user_pwinfo: %s\n", sid_string_dbg(user_sid) ));
4732 centry_free(centry);
4735 #ifdef HAVE_ADS
4737 NTSTATUS nss_get_info_cached( struct winbindd_domain *domain,
4738 const struct dom_sid *user_sid,
4739 TALLOC_CTX *ctx,
4740 const char **homedir, const char **shell,
4741 const char **gecos, gid_t *p_gid)
4743 struct winbind_cache *cache = get_cache(domain);
4744 struct cache_entry *centry = NULL;
4745 NTSTATUS nt_status;
4746 fstring tmp;
4748 if (!cache->tdb)
4749 goto do_query;
4751 centry = wcache_fetch(cache, domain, "NSS/PWINFO/%s",
4752 sid_to_fstring(tmp, user_sid));
4754 if (!centry)
4755 goto do_query;
4757 *homedir = centry_string( centry, ctx );
4758 *shell = centry_string( centry, ctx );
4759 *gecos = centry_string( centry, ctx );
4760 *p_gid = centry_uint32( centry );
4762 centry_free(centry);
4764 DEBUG(10,("nss_get_info_cached: [Cached] - user_sid %s\n",
4765 sid_string_dbg(user_sid)));
4767 return NT_STATUS_OK;
4769 do_query:
4771 nt_status = nss_get_info( domain->name, user_sid, ctx,
4772 homedir, shell, gecos, p_gid );
4774 DEBUG(10, ("nss_get_info returned %s\n", nt_errstr(nt_status)));
4776 if ( NT_STATUS_IS_OK(nt_status) ) {
4777 DEBUG(10, ("result:\n\thomedir = '%s'\n", *homedir));
4778 DEBUGADD(10, ("\tshell = '%s'\n", *shell));
4779 DEBUGADD(10, ("\tgecos = '%s'\n", *gecos));
4780 DEBUGADD(10, ("\tgid = '%u'\n", (unsigned int)*p_gid));
4782 wcache_save_user_pwinfo( domain, nt_status, user_sid,
4783 *homedir, *shell, *gecos, *p_gid );
4786 if ( NT_STATUS_EQUAL( nt_status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND ) ) {
4787 DEBUG(5,("nss_get_info_cached: Setting domain %s offline\n",
4788 domain->name ));
4789 set_domain_offline( domain );
4792 return nt_status;
4795 #endif
4797 /* the cache backend methods are exposed via this structure */
4798 struct winbindd_methods cache_methods = {
4799 true,
4800 query_user_list,
4801 enum_dom_groups,
4802 enum_local_groups,
4803 name_to_sid,
4804 sid_to_name,
4805 rids_to_names,
4806 query_user,
4807 lookup_usergroups,
4808 lookup_useraliases,
4809 lookup_groupmem,
4810 sequence_number,
4811 lockout_policy,
4812 password_policy,
4813 trusted_domains
4816 static bool wcache_ndr_key(TALLOC_CTX *mem_ctx, char *domain_name,
4817 uint32_t opnum, const DATA_BLOB *req,
4818 TDB_DATA *pkey)
4820 char *key;
4821 size_t keylen;
4823 key = talloc_asprintf(mem_ctx, "NDR/%s/%d/", domain_name, (int)opnum);
4824 if (key == NULL) {
4825 return false;
4827 keylen = talloc_get_size(key) - 1;
4829 key = talloc_realloc(mem_ctx, key, char, keylen + req->length);
4830 if (key == NULL) {
4831 return false;
4833 memcpy(key + keylen, req->data, req->length);
4835 pkey->dptr = (uint8_t *)key;
4836 pkey->dsize = talloc_get_size(key);
4837 return true;
4840 static bool wcache_opnum_cacheable(uint32_t opnum)
4842 switch (opnum) {
4843 case NDR_WBINT_PING:
4844 case NDR_WBINT_QUERYSEQUENCENUMBER:
4845 case NDR_WBINT_ALLOCATEUID:
4846 case NDR_WBINT_ALLOCATEGID:
4847 case NDR_WBINT_CHECKMACHINEACCOUNT:
4848 case NDR_WBINT_CHANGEMACHINEACCOUNT:
4849 case NDR_WBINT_PINGDC:
4850 return false;
4852 return true;
4855 bool wcache_fetch_ndr(TALLOC_CTX *mem_ctx, struct winbindd_domain *domain,
4856 uint32_t opnum, const DATA_BLOB *req, DATA_BLOB *resp)
4858 TDB_DATA key, data;
4859 bool ret = false;
4861 if (!wcache_opnum_cacheable(opnum) ||
4862 is_my_own_sam_domain(domain) ||
4863 is_builtin_domain(domain)) {
4864 return false;
4867 if (wcache->tdb == NULL) {
4868 return false;
4871 if (!wcache_ndr_key(talloc_tos(), domain->name, opnum, req, &key)) {
4872 return false;
4874 data = tdb_fetch(wcache->tdb, key);
4875 TALLOC_FREE(key.dptr);
4877 if (data.dptr == NULL) {
4878 return false;
4880 if (data.dsize < 12) {
4881 goto fail;
4884 if (!is_domain_offline(domain)) {
4885 uint32_t entry_seqnum, dom_seqnum, last_check;
4886 uint64_t entry_timeout;
4888 if (!wcache_fetch_seqnum(domain->name, &dom_seqnum,
4889 &last_check)) {
4890 goto fail;
4892 entry_seqnum = IVAL(data.dptr, 0);
4893 if (entry_seqnum != dom_seqnum) {
4894 DEBUG(10, ("Entry has wrong sequence number: %d\n",
4895 (int)entry_seqnum));
4896 goto fail;
4898 entry_timeout = BVAL(data.dptr, 4);
4899 if (time(NULL) > entry_timeout) {
4900 DEBUG(10, ("Entry has timed out\n"));
4901 goto fail;
4905 resp->data = (uint8_t *)talloc_memdup(mem_ctx, data.dptr + 12,
4906 data.dsize - 12);
4907 if (resp->data == NULL) {
4908 DEBUG(10, ("talloc failed\n"));
4909 goto fail;
4911 resp->length = data.dsize - 12;
4913 ret = true;
4914 fail:
4915 SAFE_FREE(data.dptr);
4916 return ret;
4919 void wcache_store_ndr(struct winbindd_domain *domain, uint32_t opnum,
4920 const DATA_BLOB *req, const DATA_BLOB *resp)
4922 TDB_DATA key, data;
4923 uint32_t dom_seqnum, last_check;
4924 uint64_t timeout;
4926 if (!wcache_opnum_cacheable(opnum) ||
4927 is_my_own_sam_domain(domain) ||
4928 is_builtin_domain(domain)) {
4929 return;
4932 if (wcache->tdb == NULL) {
4933 return;
4936 if (!wcache_fetch_seqnum(domain->name, &dom_seqnum, &last_check)) {
4937 DEBUG(10, ("could not fetch seqnum for domain %s\n",
4938 domain->name));
4939 return;
4942 if (!wcache_ndr_key(talloc_tos(), domain->name, opnum, req, &key)) {
4943 return;
4946 timeout = time(NULL) + lp_winbind_cache_time();
4948 data.dsize = resp->length + 12;
4949 data.dptr = talloc_array(key.dptr, uint8_t, data.dsize);
4950 if (data.dptr == NULL) {
4951 goto done;
4954 SIVAL(data.dptr, 0, dom_seqnum);
4955 SBVAL(data.dptr, 4, timeout);
4956 memcpy(data.dptr + 12, resp->data, resp->length);
4958 tdb_store(wcache->tdb, key, data, 0);
4960 done:
4961 TALLOC_FREE(key.dptr);
4962 return;