param: Use IDL-based constants for NBT and NBT dgram ports
[Samba.git] / source3 / winbindd / winbindd_cache.c
blob443fd81ece80e6f04716adb0500f51afa0226208
1 /*
2 Unix SMB/CIFS implementation.
4 Winbind cache backend functions
6 Copyright (C) Andrew Tridgell 2001
7 Copyright (C) Gerald Carter 2003-2007
8 Copyright (C) Volker Lendecke 2005
9 Copyright (C) Guenther Deschner 2005
10 Copyright (C) Michael Adam 2007
12 This program is free software; you can redistribute it and/or modify
13 it under the terms of the GNU General Public License as published by
14 the Free Software Foundation; either version 3 of the License, or
15 (at your option) any later version.
17 This program is distributed in the hope that it will be useful,
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 GNU General Public License for more details.
22 You should have received a copy of the GNU General Public License
23 along with this program. If not, see <http://www.gnu.org/licenses/>.
26 #include "includes.h"
27 #include "system/filesys.h"
28 #include "winbindd.h"
29 #include "tdb_validate.h"
30 #include "../libcli/auth/libcli_auth.h"
31 #include "../librpc/gen_ndr/ndr_winbind.h"
32 #include "ads.h"
33 #include "nss_info.h"
34 #include "../libcli/security/security.h"
35 #include "passdb/machine_sid.h"
36 #include "util_tdb.h"
38 #undef DBGC_CLASS
39 #define DBGC_CLASS DBGC_WINBIND
41 #define WINBINDD_CACHE_VER1 1 /* initial db version */
42 #define WINBINDD_CACHE_VER2 2 /* second version with timeouts for NDR entries */
44 #define WINBINDD_CACHE_VERSION WINBINDD_CACHE_VER2
45 #define WINBINDD_CACHE_VERSION_KEYSTR "WINBINDD_CACHE_VERSION"
47 extern struct winbindd_methods reconnect_methods;
48 #ifdef HAVE_ADS
49 extern struct winbindd_methods ads_methods;
50 #endif
51 extern struct winbindd_methods builtin_passdb_methods;
52 extern struct winbindd_methods sam_passdb_methods;
55 * JRA. KEEP THIS LIST UP TO DATE IF YOU ADD CACHE ENTRIES.
56 * Here are the list of entry types that are *not* stored
57 * as form struct cache_entry in the cache.
60 static const char *non_centry_keys[] = {
61 "SEQNUM/",
62 "WINBINDD_OFFLINE",
63 WINBINDD_CACHE_VERSION_KEYSTR,
64 NULL
67 /************************************************************************
68 Is this key a non-centry type ?
69 ************************************************************************/
71 static bool is_non_centry_key(TDB_DATA kbuf)
73 int i;
75 if (kbuf.dptr == NULL || kbuf.dsize == 0) {
76 return false;
78 for (i = 0; non_centry_keys[i] != NULL; i++) {
79 size_t namelen = strlen(non_centry_keys[i]);
80 if (kbuf.dsize < namelen) {
81 continue;
83 if (strncmp(non_centry_keys[i], (const char *)kbuf.dptr, namelen) == 0) {
84 return true;
87 return false;
90 /* Global online/offline state - False when online. winbindd starts up online
91 and sets this to true if the first query fails and there's an entry in
92 the cache tdb telling us to stay offline. */
94 static bool global_winbindd_offline_state;
96 struct winbind_cache {
97 TDB_CONTEXT *tdb;
100 struct cache_entry {
101 NTSTATUS status;
102 uint32 sequence_number;
103 uint64_t timeout;
104 uint8 *data;
105 uint32 len, ofs;
108 void (*smb_panic_fn)(const char *const why) = smb_panic;
110 #define WINBINDD_MAX_CACHE_SIZE (50*1024*1024)
112 static struct winbind_cache *wcache;
114 /* get the winbind_cache structure */
115 static struct winbind_cache *get_cache(struct winbindd_domain *domain)
117 struct winbind_cache *ret = wcache;
119 /* We have to know what type of domain we are dealing with first. */
121 if (domain->internal) {
122 domain->backend = &builtin_passdb_methods;
125 if (dom_sid_equal(&domain->sid, &global_sid_Builtin)) {
126 domain->initialized = true;
129 if (strequal(domain->name, get_global_sam_name()) &&
130 sid_check_is_our_sam(&domain->sid)) {
131 domain->backend = &sam_passdb_methods;
134 if ( !domain->initialized ) {
135 /* We do not need a connection to an RW DC for cache operation */
136 init_dc_connection(domain, false);
140 OK. listen up becasue I'm only going to say this once.
141 We have the following scenarios to consider
142 (a) trusted AD domains on a Samba DC,
143 (b) trusted AD domains and we are joined to a non-kerberos domain
144 (c) trusted AD domains and we are joined to a kerberos (AD) domain
146 For (a) we can always contact the trusted domain using krb5
147 since we have the domain trust account password
149 For (b) we can only use RPC since we have no way of
150 getting a krb5 ticket in our own domain
152 For (c) we can always use krb5 since we have a kerberos trust
154 --jerry
157 if (!domain->backend) {
158 #ifdef HAVE_ADS
159 struct winbindd_domain *our_domain = domain;
161 /* find our domain first so we can figure out if we
162 are joined to a kerberized domain */
164 if ( !domain->primary )
165 our_domain = find_our_domain();
167 if ((our_domain->active_directory || IS_DC)
168 && domain->active_directory
169 && !lp_winbind_rpc_only()) {
170 DEBUG(5,("get_cache: Setting ADS methods for domain %s\n", domain->name));
171 domain->backend = &ads_methods;
172 } else {
173 #endif /* HAVE_ADS */
174 DEBUG(5,("get_cache: Setting MS-RPC methods for domain %s\n", domain->name));
175 domain->backend = &reconnect_methods;
176 #ifdef HAVE_ADS
178 #endif /* HAVE_ADS */
181 if (ret)
182 return ret;
184 ret = SMB_XMALLOC_P(struct winbind_cache);
185 ZERO_STRUCTP(ret);
187 wcache = ret;
188 wcache_flush_cache();
190 return ret;
194 free a centry structure
196 static void centry_free(struct cache_entry *centry)
198 if (!centry)
199 return;
200 SAFE_FREE(centry->data);
201 free(centry);
204 static bool centry_check_bytes(struct cache_entry *centry, size_t nbytes)
206 if (centry->len - centry->ofs < nbytes) {
207 DEBUG(0,("centry corruption? needed %u bytes, have %d\n",
208 (unsigned int)nbytes,
209 centry->len - centry->ofs));
210 return false;
212 return true;
216 pull a uint64_t from a cache entry
218 static uint64_t centry_uint64_t(struct cache_entry *centry)
220 uint64_t ret;
222 if (!centry_check_bytes(centry, 8)) {
223 smb_panic_fn("centry_uint64_t");
225 ret = BVAL(centry->data, centry->ofs);
226 centry->ofs += 8;
227 return ret;
231 pull a uint32 from a cache entry
233 static uint32 centry_uint32(struct cache_entry *centry)
235 uint32 ret;
237 if (!centry_check_bytes(centry, 4)) {
238 smb_panic_fn("centry_uint32");
240 ret = IVAL(centry->data, centry->ofs);
241 centry->ofs += 4;
242 return ret;
246 pull a uint16 from a cache entry
248 static uint16 centry_uint16(struct cache_entry *centry)
250 uint16 ret;
251 if (!centry_check_bytes(centry, 2)) {
252 smb_panic_fn("centry_uint16");
254 ret = SVAL(centry->data, centry->ofs);
255 centry->ofs += 2;
256 return ret;
260 pull a uint8 from a cache entry
262 static uint8 centry_uint8(struct cache_entry *centry)
264 uint8 ret;
265 if (!centry_check_bytes(centry, 1)) {
266 smb_panic_fn("centry_uint8");
268 ret = CVAL(centry->data, centry->ofs);
269 centry->ofs += 1;
270 return ret;
274 pull a NTTIME from a cache entry
276 static NTTIME centry_nttime(struct cache_entry *centry)
278 NTTIME ret;
279 if (!centry_check_bytes(centry, 8)) {
280 smb_panic_fn("centry_nttime");
282 ret = IVAL(centry->data, centry->ofs);
283 centry->ofs += 4;
284 ret += (uint64)IVAL(centry->data, centry->ofs) << 32;
285 centry->ofs += 4;
286 return ret;
290 pull a time_t from a cache entry. time_t stored portably as a 64-bit time.
292 static time_t centry_time(struct cache_entry *centry)
294 return (time_t)centry_nttime(centry);
297 /* pull a string from a cache entry, using the supplied
298 talloc context
300 static char *centry_string(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
302 uint32 len;
303 char *ret;
305 len = centry_uint8(centry);
307 if (len == 0xFF) {
308 /* a deliberate NULL string */
309 return NULL;
312 if (!centry_check_bytes(centry, (size_t)len)) {
313 smb_panic_fn("centry_string");
316 ret = talloc_array(mem_ctx, char, len+1);
317 if (!ret) {
318 smb_panic_fn("centry_string out of memory\n");
320 memcpy(ret,centry->data + centry->ofs, len);
321 ret[len] = 0;
322 centry->ofs += len;
323 return ret;
326 /* pull a hash16 from a cache entry, using the supplied
327 talloc context
329 static char *centry_hash16(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
331 uint32 len;
332 char *ret;
334 len = centry_uint8(centry);
336 if (len != 16) {
337 DEBUG(0,("centry corruption? hash len (%u) != 16\n",
338 len ));
339 return NULL;
342 if (!centry_check_bytes(centry, 16)) {
343 return NULL;
346 ret = talloc_array(mem_ctx, char, 16);
347 if (!ret) {
348 smb_panic_fn("centry_hash out of memory\n");
350 memcpy(ret,centry->data + centry->ofs, 16);
351 centry->ofs += 16;
352 return ret;
355 /* pull a sid from a cache entry, using the supplied
356 talloc context
358 static bool centry_sid(struct cache_entry *centry, struct dom_sid *sid)
360 char *sid_string;
361 bool ret;
363 sid_string = centry_string(centry, talloc_tos());
364 if (sid_string == NULL) {
365 return false;
367 ret = string_to_sid(sid, sid_string);
368 TALLOC_FREE(sid_string);
369 return ret;
374 pull a NTSTATUS from a cache entry
376 static NTSTATUS centry_ntstatus(struct cache_entry *centry)
378 NTSTATUS status;
380 status = NT_STATUS(centry_uint32(centry));
381 return status;
385 /* the server is considered down if it can't give us a sequence number */
386 static bool wcache_server_down(struct winbindd_domain *domain)
388 bool ret;
390 if (!wcache->tdb)
391 return false;
393 ret = (domain->sequence_number == DOM_SEQUENCE_NONE);
395 if (ret)
396 DEBUG(10,("wcache_server_down: server for Domain %s down\n",
397 domain->name ));
398 return ret;
401 static bool wcache_fetch_seqnum(const char *domain_name, uint32_t *seqnum,
402 uint32_t *last_seq_check)
404 char *key;
405 TDB_DATA data;
407 if (wcache->tdb == NULL) {
408 DEBUG(10,("wcache_fetch_seqnum: tdb == NULL\n"));
409 return false;
412 key = talloc_asprintf(talloc_tos(), "SEQNUM/%s", domain_name);
413 if (key == NULL) {
414 DEBUG(10, ("talloc failed\n"));
415 return false;
418 data = tdb_fetch_bystring(wcache->tdb, key);
419 TALLOC_FREE(key);
421 if (data.dptr == NULL) {
422 DEBUG(10, ("wcache_fetch_seqnum: %s not found\n",
423 domain_name));
424 return false;
426 if (data.dsize != 8) {
427 DEBUG(10, ("wcache_fetch_seqnum: invalid data size %d\n",
428 (int)data.dsize));
429 SAFE_FREE(data.dptr);
430 return false;
433 *seqnum = IVAL(data.dptr, 0);
434 *last_seq_check = IVAL(data.dptr, 4);
435 SAFE_FREE(data.dptr);
437 return true;
440 static NTSTATUS fetch_cache_seqnum( struct winbindd_domain *domain, time_t now )
442 uint32 last_check, time_diff;
444 if (!wcache_fetch_seqnum(domain->name, &domain->sequence_number,
445 &last_check)) {
446 return NT_STATUS_UNSUCCESSFUL;
448 domain->last_seq_check = last_check;
450 /* have we expired? */
452 time_diff = now - domain->last_seq_check;
453 if ( time_diff > lp_winbind_cache_time() ) {
454 DEBUG(10,("fetch_cache_seqnum: timeout [%s][%u @ %u]\n",
455 domain->name, domain->sequence_number,
456 (uint32)domain->last_seq_check));
457 return NT_STATUS_UNSUCCESSFUL;
460 DEBUG(10,("fetch_cache_seqnum: success [%s][%u @ %u]\n",
461 domain->name, domain->sequence_number,
462 (uint32)domain->last_seq_check));
464 return NT_STATUS_OK;
467 bool wcache_store_seqnum(const char *domain_name, uint32_t seqnum,
468 time_t last_seq_check)
470 char *key_str;
471 uint8_t buf[8];
472 int ret;
474 if (wcache->tdb == NULL) {
475 DEBUG(10, ("wcache_store_seqnum: wcache->tdb == NULL\n"));
476 return false;
479 key_str = talloc_asprintf(talloc_tos(), "SEQNUM/%s", domain_name);
480 if (key_str == NULL) {
481 DEBUG(10, ("talloc_asprintf failed\n"));
482 return false;
485 SIVAL(buf, 0, seqnum);
486 SIVAL(buf, 4, last_seq_check);
488 ret = tdb_store_bystring(wcache->tdb, key_str,
489 make_tdb_data(buf, sizeof(buf)), TDB_REPLACE);
490 TALLOC_FREE(key_str);
491 if (ret != 0) {
492 DEBUG(10, ("tdb_store_bystring failed: %s\n",
493 tdb_errorstr_compat(wcache->tdb)));
494 TALLOC_FREE(key_str);
495 return false;
498 DEBUG(10, ("wcache_store_seqnum: success [%s][%u @ %u]\n",
499 domain_name, seqnum, (unsigned)last_seq_check));
501 return true;
504 static bool store_cache_seqnum( struct winbindd_domain *domain )
506 return wcache_store_seqnum(domain->name, domain->sequence_number,
507 domain->last_seq_check);
511 refresh the domain sequence number. If force is true
512 then always refresh it, no matter how recently we fetched it
515 static void refresh_sequence_number(struct winbindd_domain *domain, bool force)
517 NTSTATUS status;
518 unsigned time_diff;
519 time_t t = time(NULL);
520 unsigned cache_time = lp_winbind_cache_time();
522 if (is_domain_offline(domain)) {
523 return;
526 get_cache( domain );
528 #if 0 /* JERRY -- disable as the default cache time is now 5 minutes */
529 /* trying to reconnect is expensive, don't do it too often */
530 if (domain->sequence_number == DOM_SEQUENCE_NONE) {
531 cache_time *= 8;
533 #endif
535 time_diff = t - domain->last_seq_check;
537 /* see if we have to refetch the domain sequence number */
538 if (!force && (time_diff < cache_time) &&
539 (domain->sequence_number != DOM_SEQUENCE_NONE) &&
540 NT_STATUS_IS_OK(domain->last_status)) {
541 DEBUG(10, ("refresh_sequence_number: %s time ok\n", domain->name));
542 goto done;
545 /* try to get the sequence number from the tdb cache first */
546 /* this will update the timestamp as well */
548 status = fetch_cache_seqnum( domain, t );
549 if (NT_STATUS_IS_OK(status) &&
550 (domain->sequence_number != DOM_SEQUENCE_NONE) &&
551 NT_STATUS_IS_OK(domain->last_status)) {
552 goto done;
555 /* important! make sure that we know if this is a native
556 mode domain or not. And that we can contact it. */
558 if ( winbindd_can_contact_domain( domain ) ) {
559 status = domain->backend->sequence_number(domain,
560 &domain->sequence_number);
561 } else {
562 /* just use the current time */
563 status = NT_STATUS_OK;
564 domain->sequence_number = time(NULL);
568 /* the above call could have set our domain->backend to NULL when
569 * coming from offline to online mode, make sure to reinitialize the
570 * backend - Guenther */
571 get_cache( domain );
573 if (!NT_STATUS_IS_OK(status)) {
574 DEBUG(10,("refresh_sequence_number: failed with %s\n", nt_errstr(status)));
575 domain->sequence_number = DOM_SEQUENCE_NONE;
578 domain->last_status = status;
579 domain->last_seq_check = time(NULL);
581 /* save the new sequence number in the cache */
582 store_cache_seqnum( domain );
584 done:
585 DEBUG(10, ("refresh_sequence_number: %s seq number is now %d\n",
586 domain->name, domain->sequence_number));
588 return;
592 decide if a cache entry has expired
594 static bool centry_expired(struct winbindd_domain *domain, const char *keystr, struct cache_entry *centry)
596 /* If we've been told to be offline - stay in that state... */
597 if (lp_winbind_offline_logon() && global_winbindd_offline_state) {
598 DEBUG(10,("centry_expired: Key %s for domain %s valid as winbindd is globally offline.\n",
599 keystr, domain->name ));
600 return false;
603 /* when the domain is offline return the cached entry.
604 * This deals with transient offline states... */
606 if (!domain->online) {
607 DEBUG(10,("centry_expired: Key %s for domain %s valid as domain is offline.\n",
608 keystr, domain->name ));
609 return false;
612 /* if the server is OK and our cache entry came from when it was down then
613 the entry is invalid */
614 if ((domain->sequence_number != DOM_SEQUENCE_NONE) &&
615 (centry->sequence_number == DOM_SEQUENCE_NONE)) {
616 DEBUG(10,("centry_expired: Key %s for domain %s invalid sequence.\n",
617 keystr, domain->name ));
618 return true;
621 /* if the server is down or the cache entry is not older than the
622 current sequence number or it did not timeout then it is OK */
623 if (wcache_server_down(domain)
624 || ((centry->sequence_number == domain->sequence_number)
625 && (centry->timeout > time(NULL)))) {
626 DEBUG(10,("centry_expired: Key %s for domain %s is good.\n",
627 keystr, domain->name ));
628 return false;
631 DEBUG(10,("centry_expired: Key %s for domain %s expired\n",
632 keystr, domain->name ));
634 /* it's expired */
635 return true;
638 static struct cache_entry *wcache_fetch_raw(char *kstr)
640 TDB_DATA data;
641 struct cache_entry *centry;
642 TDB_DATA key;
644 key = string_tdb_data(kstr);
645 data = tdb_fetch_compat(wcache->tdb, key);
646 if (!data.dptr) {
647 /* a cache miss */
648 return NULL;
651 centry = SMB_XMALLOC_P(struct cache_entry);
652 centry->data = (unsigned char *)data.dptr;
653 centry->len = data.dsize;
654 centry->ofs = 0;
656 if (centry->len < 16) {
657 /* huh? corrupt cache? */
658 DEBUG(10,("wcache_fetch_raw: Corrupt cache for key %s "
659 "(len < 16)?\n", kstr));
660 centry_free(centry);
661 return NULL;
664 centry->status = centry_ntstatus(centry);
665 centry->sequence_number = centry_uint32(centry);
666 centry->timeout = centry_uint64_t(centry);
668 return centry;
671 static bool is_my_own_sam_domain(struct winbindd_domain *domain)
673 if (strequal(domain->name, get_global_sam_name()) &&
674 sid_check_is_our_sam(&domain->sid)) {
675 return true;
678 return false;
681 static bool is_builtin_domain(struct winbindd_domain *domain)
683 if (strequal(domain->name, "BUILTIN") &&
684 sid_check_is_builtin(&domain->sid)) {
685 return true;
688 return false;
692 fetch an entry from the cache, with a varargs key. auto-fetch the sequence
693 number and return status
695 static struct cache_entry *wcache_fetch(struct winbind_cache *cache,
696 struct winbindd_domain *domain,
697 const char *format, ...) PRINTF_ATTRIBUTE(3,4);
698 static struct cache_entry *wcache_fetch(struct winbind_cache *cache,
699 struct winbindd_domain *domain,
700 const char *format, ...)
702 va_list ap;
703 char *kstr;
704 struct cache_entry *centry;
706 if (!winbindd_use_cache() ||
707 is_my_own_sam_domain(domain) ||
708 is_builtin_domain(domain)) {
709 return NULL;
712 refresh_sequence_number(domain, false);
714 va_start(ap, format);
715 smb_xvasprintf(&kstr, format, ap);
716 va_end(ap);
718 centry = wcache_fetch_raw(kstr);
719 if (centry == NULL) {
720 free(kstr);
721 return NULL;
724 if (centry_expired(domain, kstr, centry)) {
726 DEBUG(10,("wcache_fetch: entry %s expired for domain %s\n",
727 kstr, domain->name ));
729 centry_free(centry);
730 free(kstr);
731 return NULL;
734 DEBUG(10,("wcache_fetch: returning entry %s for domain %s\n",
735 kstr, domain->name ));
737 free(kstr);
738 return centry;
741 static void wcache_delete(const char *format, ...) PRINTF_ATTRIBUTE(1,2);
742 static void wcache_delete(const char *format, ...)
744 va_list ap;
745 char *kstr;
746 TDB_DATA key;
748 va_start(ap, format);
749 smb_xvasprintf(&kstr, format, ap);
750 va_end(ap);
752 key = string_tdb_data(kstr);
754 tdb_delete(wcache->tdb, key);
755 free(kstr);
759 make sure we have at least len bytes available in a centry
761 static void centry_expand(struct cache_entry *centry, uint32 len)
763 if (centry->len - centry->ofs >= len)
764 return;
765 centry->len *= 2;
766 centry->data = SMB_REALLOC_ARRAY(centry->data, unsigned char,
767 centry->len);
768 if (!centry->data) {
769 DEBUG(0,("out of memory: needed %d bytes in centry_expand\n", centry->len));
770 smb_panic_fn("out of memory in centry_expand");
775 push a uint64_t into a centry
777 static void centry_put_uint64_t(struct cache_entry *centry, uint64_t v)
779 centry_expand(centry, 8);
780 SBVAL(centry->data, centry->ofs, v);
781 centry->ofs += 8;
785 push a uint32 into a centry
787 static void centry_put_uint32(struct cache_entry *centry, uint32 v)
789 centry_expand(centry, 4);
790 SIVAL(centry->data, centry->ofs, v);
791 centry->ofs += 4;
795 push a uint16 into a centry
797 static void centry_put_uint16(struct cache_entry *centry, uint16 v)
799 centry_expand(centry, 2);
800 SSVAL(centry->data, centry->ofs, v);
801 centry->ofs += 2;
805 push a uint8 into a centry
807 static void centry_put_uint8(struct cache_entry *centry, uint8 v)
809 centry_expand(centry, 1);
810 SCVAL(centry->data, centry->ofs, v);
811 centry->ofs += 1;
815 push a string into a centry
817 static void centry_put_string(struct cache_entry *centry, const char *s)
819 int len;
821 if (!s) {
822 /* null strings are marked as len 0xFFFF */
823 centry_put_uint8(centry, 0xFF);
824 return;
827 len = strlen(s);
828 /* can't handle more than 254 char strings. Truncating is probably best */
829 if (len > 254) {
830 DEBUG(10,("centry_put_string: truncating len (%d) to: 254\n", len));
831 len = 254;
833 centry_put_uint8(centry, len);
834 centry_expand(centry, len);
835 memcpy(centry->data + centry->ofs, s, len);
836 centry->ofs += len;
840 push a 16 byte hash into a centry - treat as 16 byte string.
842 static void centry_put_hash16(struct cache_entry *centry, const uint8 val[16])
844 centry_put_uint8(centry, 16);
845 centry_expand(centry, 16);
846 memcpy(centry->data + centry->ofs, val, 16);
847 centry->ofs += 16;
850 static void centry_put_sid(struct cache_entry *centry, const struct dom_sid *sid)
852 fstring sid_string;
853 centry_put_string(centry, sid_to_fstring(sid_string, sid));
858 put NTSTATUS into a centry
860 static void centry_put_ntstatus(struct cache_entry *centry, NTSTATUS status)
862 uint32 status_value = NT_STATUS_V(status);
863 centry_put_uint32(centry, status_value);
868 push a NTTIME into a centry
870 static void centry_put_nttime(struct cache_entry *centry, NTTIME nt)
872 centry_expand(centry, 8);
873 SIVAL(centry->data, centry->ofs, nt & 0xFFFFFFFF);
874 centry->ofs += 4;
875 SIVAL(centry->data, centry->ofs, nt >> 32);
876 centry->ofs += 4;
880 push a time_t into a centry - use a 64 bit size.
881 NTTIME here is being used as a convenient 64-bit size.
883 static void centry_put_time(struct cache_entry *centry, time_t t)
885 NTTIME nt = (NTTIME)t;
886 centry_put_nttime(centry, nt);
890 start a centry for output. When finished, call centry_end()
892 static struct cache_entry *centry_start(struct winbindd_domain *domain,
893 NTSTATUS status)
895 struct cache_entry *centry;
897 if (!wcache->tdb)
898 return NULL;
900 centry = SMB_XMALLOC_P(struct cache_entry);
902 centry->len = 8192; /* reasonable default */
903 centry->data = SMB_XMALLOC_ARRAY(uint8, centry->len);
904 centry->ofs = 0;
905 centry->sequence_number = domain->sequence_number;
906 centry->timeout = lp_winbind_cache_time() + time(NULL);
907 centry_put_ntstatus(centry, status);
908 centry_put_uint32(centry, centry->sequence_number);
909 centry_put_uint64_t(centry, centry->timeout);
910 return centry;
914 finish a centry and write it to the tdb
916 static void centry_end(struct cache_entry *centry, const char *format, ...) PRINTF_ATTRIBUTE(2,3);
917 static void centry_end(struct cache_entry *centry, const char *format, ...)
919 va_list ap;
920 char *kstr;
921 TDB_DATA key, data;
923 if (!winbindd_use_cache()) {
924 return;
927 va_start(ap, format);
928 smb_xvasprintf(&kstr, format, ap);
929 va_end(ap);
931 key = string_tdb_data(kstr);
932 data.dptr = centry->data;
933 data.dsize = centry->ofs;
935 tdb_store(wcache->tdb, key, data, TDB_REPLACE);
936 free(kstr);
939 static void wcache_save_name_to_sid(struct winbindd_domain *domain,
940 NTSTATUS status, const char *domain_name,
941 const char *name, const struct dom_sid *sid,
942 enum lsa_SidType type)
944 struct cache_entry *centry;
945 fstring uname;
947 centry = centry_start(domain, status);
948 if (!centry)
949 return;
951 if ((domain_name == NULL) || (domain_name[0] == '\0')) {
952 struct winbindd_domain *mydomain =
953 find_domain_from_sid_noinit(sid);
954 if (mydomain != NULL) {
955 domain_name = mydomain->name;
959 centry_put_uint32(centry, type);
960 centry_put_sid(centry, sid);
961 fstrcpy(uname, name);
962 (void)strupper_m(uname);
963 centry_end(centry, "NS/%s/%s", domain_name, uname);
964 DEBUG(10,("wcache_save_name_to_sid: %s\\%s -> %s (%s)\n", domain_name,
965 uname, sid_string_dbg(sid), nt_errstr(status)));
966 centry_free(centry);
969 static void wcache_save_sid_to_name(struct winbindd_domain *domain, NTSTATUS status,
970 const struct dom_sid *sid, const char *domain_name, const char *name, enum lsa_SidType type)
972 struct cache_entry *centry;
973 fstring sid_string;
975 centry = centry_start(domain, status);
976 if (!centry)
977 return;
979 if ((domain_name == NULL) || (domain_name[0] == '\0')) {
980 struct winbindd_domain *mydomain =
981 find_domain_from_sid_noinit(sid);
982 if (mydomain != NULL) {
983 domain_name = mydomain->name;
987 if (NT_STATUS_IS_OK(status)) {
988 centry_put_uint32(centry, type);
989 centry_put_string(centry, domain_name);
990 centry_put_string(centry, name);
993 centry_end(centry, "SN/%s", sid_to_fstring(sid_string, sid));
994 DEBUG(10,("wcache_save_sid_to_name: %s -> %s\\%s (%s)\n", sid_string,
995 domain_name, name, nt_errstr(status)));
996 centry_free(centry);
1000 static void wcache_save_user(struct winbindd_domain *domain, NTSTATUS status,
1001 struct wbint_userinfo *info)
1003 struct cache_entry *centry;
1004 fstring sid_string;
1006 if (is_null_sid(&info->user_sid)) {
1007 return;
1010 centry = centry_start(domain, status);
1011 if (!centry)
1012 return;
1013 centry_put_string(centry, info->acct_name);
1014 centry_put_string(centry, info->full_name);
1015 centry_put_string(centry, info->homedir);
1016 centry_put_string(centry, info->shell);
1017 centry_put_uint32(centry, info->primary_gid);
1018 centry_put_sid(centry, &info->user_sid);
1019 centry_put_sid(centry, &info->group_sid);
1020 centry_end(centry, "U/%s", sid_to_fstring(sid_string,
1021 &info->user_sid));
1022 DEBUG(10,("wcache_save_user: %s (acct_name %s)\n", sid_string, info->acct_name));
1023 centry_free(centry);
1026 static void wcache_save_lockout_policy(struct winbindd_domain *domain,
1027 NTSTATUS status,
1028 struct samr_DomInfo12 *lockout_policy)
1030 struct cache_entry *centry;
1032 centry = centry_start(domain, status);
1033 if (!centry)
1034 return;
1036 centry_put_nttime(centry, lockout_policy->lockout_duration);
1037 centry_put_nttime(centry, lockout_policy->lockout_window);
1038 centry_put_uint16(centry, lockout_policy->lockout_threshold);
1040 centry_end(centry, "LOC_POL/%s", domain->name);
1042 DEBUG(10,("wcache_save_lockout_policy: %s\n", domain->name));
1044 centry_free(centry);
1049 static void wcache_save_password_policy(struct winbindd_domain *domain,
1050 NTSTATUS status,
1051 struct samr_DomInfo1 *policy)
1053 struct cache_entry *centry;
1055 centry = centry_start(domain, status);
1056 if (!centry)
1057 return;
1059 centry_put_uint16(centry, policy->min_password_length);
1060 centry_put_uint16(centry, policy->password_history_length);
1061 centry_put_uint32(centry, policy->password_properties);
1062 centry_put_nttime(centry, policy->max_password_age);
1063 centry_put_nttime(centry, policy->min_password_age);
1065 centry_end(centry, "PWD_POL/%s", domain->name);
1067 DEBUG(10,("wcache_save_password_policy: %s\n", domain->name));
1069 centry_free(centry);
1072 /***************************************************************************
1073 ***************************************************************************/
1075 static void wcache_save_username_alias(struct winbindd_domain *domain,
1076 NTSTATUS status,
1077 const char *name, const char *alias)
1079 struct cache_entry *centry;
1080 fstring uname;
1082 if ( (centry = centry_start(domain, status)) == NULL )
1083 return;
1085 centry_put_string( centry, alias );
1087 fstrcpy(uname, name);
1088 (void)strupper_m(uname);
1089 centry_end(centry, "NSS/NA/%s", uname);
1091 DEBUG(10,("wcache_save_username_alias: %s -> %s\n", name, alias ));
1093 centry_free(centry);
1096 static void wcache_save_alias_username(struct winbindd_domain *domain,
1097 NTSTATUS status,
1098 const char *alias, const char *name)
1100 struct cache_entry *centry;
1101 fstring uname;
1103 if ( (centry = centry_start(domain, status)) == NULL )
1104 return;
1106 centry_put_string( centry, name );
1108 fstrcpy(uname, alias);
1109 (void)strupper_m(uname);
1110 centry_end(centry, "NSS/AN/%s", uname);
1112 DEBUG(10,("wcache_save_alias_username: %s -> %s\n", alias, name ));
1114 centry_free(centry);
1117 /***************************************************************************
1118 ***************************************************************************/
1120 NTSTATUS resolve_username_to_alias( TALLOC_CTX *mem_ctx,
1121 struct winbindd_domain *domain,
1122 const char *name, char **alias )
1124 struct winbind_cache *cache = get_cache(domain);
1125 struct cache_entry *centry = NULL;
1126 NTSTATUS status;
1127 char *upper_name;
1129 if ( domain->internal )
1130 return NT_STATUS_NOT_SUPPORTED;
1132 if (!cache->tdb)
1133 goto do_query;
1135 upper_name = talloc_strdup(mem_ctx, name);
1136 if (upper_name == NULL) {
1137 return NT_STATUS_NO_MEMORY;
1139 if (!strupper_m(upper_name)) {
1140 talloc_free(upper_name);
1141 return NT_STATUS_INVALID_PARAMETER;
1144 centry = wcache_fetch(cache, domain, "NSS/NA/%s", upper_name);
1146 talloc_free(upper_name);
1148 if (!centry)
1149 goto do_query;
1151 status = centry->status;
1153 if (!NT_STATUS_IS_OK(status)) {
1154 centry_free(centry);
1155 return status;
1158 *alias = centry_string( centry, mem_ctx );
1160 centry_free(centry);
1162 DEBUG(10,("resolve_username_to_alias: [Cached] - mapped %s to %s\n",
1163 name, *alias ? *alias : "(none)"));
1165 return (*alias) ? NT_STATUS_OK : NT_STATUS_OBJECT_NAME_NOT_FOUND;
1167 do_query:
1169 /* If its not in cache and we are offline, then fail */
1171 if ( get_global_winbindd_state_offline() || !domain->online ) {
1172 DEBUG(8,("resolve_username_to_alias: rejecting query "
1173 "in offline mode\n"));
1174 return NT_STATUS_NOT_FOUND;
1177 status = nss_map_to_alias( mem_ctx, domain->name, name, alias );
1179 if ( NT_STATUS_IS_OK( status ) ) {
1180 wcache_save_username_alias(domain, status, name, *alias);
1183 if ( NT_STATUS_EQUAL( status, NT_STATUS_NONE_MAPPED ) ) {
1184 wcache_save_username_alias(domain, status, name, "(NULL)");
1187 DEBUG(5,("resolve_username_to_alias: backend query returned %s\n",
1188 nt_errstr(status)));
1190 if ( NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ) {
1191 set_domain_offline( domain );
1194 return status;
1197 /***************************************************************************
1198 ***************************************************************************/
1200 NTSTATUS resolve_alias_to_username( TALLOC_CTX *mem_ctx,
1201 struct winbindd_domain *domain,
1202 const char *alias, char **name )
1204 struct winbind_cache *cache = get_cache(domain);
1205 struct cache_entry *centry = NULL;
1206 NTSTATUS status;
1207 char *upper_name;
1209 if ( domain->internal )
1210 return NT_STATUS_NOT_SUPPORTED;
1212 if (!cache->tdb)
1213 goto do_query;
1215 upper_name = talloc_strdup(mem_ctx, alias);
1216 if (upper_name == NULL) {
1217 return NT_STATUS_NO_MEMORY;
1219 if (!strupper_m(upper_name)) {
1220 talloc_free(upper_name);
1221 return NT_STATUS_INVALID_PARAMETER;
1224 centry = wcache_fetch(cache, domain, "NSS/AN/%s", upper_name);
1226 talloc_free(upper_name);
1228 if (!centry)
1229 goto do_query;
1231 status = centry->status;
1233 if (!NT_STATUS_IS_OK(status)) {
1234 centry_free(centry);
1235 return status;
1238 *name = centry_string( centry, mem_ctx );
1240 centry_free(centry);
1242 DEBUG(10,("resolve_alias_to_username: [Cached] - mapped %s to %s\n",
1243 alias, *name ? *name : "(none)"));
1245 return (*name) ? NT_STATUS_OK : NT_STATUS_OBJECT_NAME_NOT_FOUND;
1247 do_query:
1249 /* If its not in cache and we are offline, then fail */
1251 if ( get_global_winbindd_state_offline() || !domain->online ) {
1252 DEBUG(8,("resolve_alias_to_username: rejecting query "
1253 "in offline mode\n"));
1254 return NT_STATUS_NOT_FOUND;
1257 /* an alias cannot contain a domain prefix or '@' */
1259 if (strchr(alias, '\\') || strchr(alias, '@')) {
1260 DEBUG(10,("resolve_alias_to_username: skipping fully "
1261 "qualified name %s\n", alias));
1262 return NT_STATUS_OBJECT_NAME_INVALID;
1265 status = nss_map_from_alias( mem_ctx, domain->name, alias, name );
1267 if ( NT_STATUS_IS_OK( status ) ) {
1268 wcache_save_alias_username( domain, status, alias, *name );
1271 if (NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) {
1272 wcache_save_alias_username(domain, status, alias, "(NULL)");
1275 DEBUG(5,("resolve_alias_to_username: backend query returned %s\n",
1276 nt_errstr(status)));
1278 if ( NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ) {
1279 set_domain_offline( domain );
1282 return status;
1285 NTSTATUS wcache_cached_creds_exist(struct winbindd_domain *domain, const struct dom_sid *sid)
1287 struct winbind_cache *cache = get_cache(domain);
1288 TDB_DATA data;
1289 fstring key_str, tmp;
1290 uint32 rid;
1292 if (!cache->tdb) {
1293 return NT_STATUS_INTERNAL_DB_ERROR;
1296 if (is_null_sid(sid)) {
1297 return NT_STATUS_INVALID_SID;
1300 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1301 return NT_STATUS_INVALID_SID;
1304 fstr_sprintf(key_str, "CRED/%s", sid_to_fstring(tmp, sid));
1306 data = tdb_fetch_compat(cache->tdb, string_tdb_data(key_str));
1307 if (!data.dptr) {
1308 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1311 SAFE_FREE(data.dptr);
1312 return NT_STATUS_OK;
1315 /* Lookup creds for a SID - copes with old (unsalted) creds as well
1316 as new salted ones. */
1318 NTSTATUS wcache_get_creds(struct winbindd_domain *domain,
1319 TALLOC_CTX *mem_ctx,
1320 const struct dom_sid *sid,
1321 const uint8 **cached_nt_pass,
1322 const uint8 **cached_salt)
1324 struct winbind_cache *cache = get_cache(domain);
1325 struct cache_entry *centry = NULL;
1326 NTSTATUS status;
1327 uint32 rid;
1328 fstring tmp;
1330 if (!cache->tdb) {
1331 return NT_STATUS_INTERNAL_DB_ERROR;
1334 if (is_null_sid(sid)) {
1335 return NT_STATUS_INVALID_SID;
1338 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1339 return NT_STATUS_INVALID_SID;
1342 /* Try and get a salted cred first. If we can't
1343 fall back to an unsalted cred. */
1345 centry = wcache_fetch(cache, domain, "CRED/%s",
1346 sid_to_fstring(tmp, sid));
1347 if (!centry) {
1348 DEBUG(10,("wcache_get_creds: entry for [CRED/%s] not found\n",
1349 sid_string_dbg(sid)));
1350 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1354 * We don't use the time element at this moment,
1355 * but we have to consume it, so that we don't
1356 * neet to change the disk format of the cache.
1358 (void)centry_time(centry);
1360 /* In the salted case this isn't actually the nt_hash itself,
1361 but the MD5 of the salt + nt_hash. Let the caller
1362 sort this out. It can tell as we only return the cached_salt
1363 if we are returning a salted cred. */
1365 *cached_nt_pass = (const uint8 *)centry_hash16(centry, mem_ctx);
1366 if (*cached_nt_pass == NULL) {
1367 fstring sidstr;
1369 sid_to_fstring(sidstr, sid);
1371 /* Bad (old) cred cache. Delete and pretend we
1372 don't have it. */
1373 DEBUG(0,("wcache_get_creds: bad entry for [CRED/%s] - deleting\n",
1374 sidstr));
1375 wcache_delete("CRED/%s", sidstr);
1376 centry_free(centry);
1377 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1380 /* We only have 17 bytes more data in the salted cred case. */
1381 if (centry->len - centry->ofs == 17) {
1382 *cached_salt = (const uint8 *)centry_hash16(centry, mem_ctx);
1383 } else {
1384 *cached_salt = NULL;
1387 dump_data_pw("cached_nt_pass", *cached_nt_pass, NT_HASH_LEN);
1388 if (*cached_salt) {
1389 dump_data_pw("cached_salt", *cached_salt, NT_HASH_LEN);
1392 status = centry->status;
1394 DEBUG(10,("wcache_get_creds: [Cached] - cached creds for user %s status: %s\n",
1395 sid_string_dbg(sid), nt_errstr(status) ));
1397 centry_free(centry);
1398 return status;
1401 /* Store creds for a SID - only writes out new salted ones. */
1403 NTSTATUS wcache_save_creds(struct winbindd_domain *domain,
1404 const struct dom_sid *sid,
1405 const uint8 nt_pass[NT_HASH_LEN])
1407 struct cache_entry *centry;
1408 fstring sid_string;
1409 uint32 rid;
1410 uint8 cred_salt[NT_HASH_LEN];
1411 uint8 salted_hash[NT_HASH_LEN];
1413 if (is_null_sid(sid)) {
1414 return NT_STATUS_INVALID_SID;
1417 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1418 return NT_STATUS_INVALID_SID;
1421 centry = centry_start(domain, NT_STATUS_OK);
1422 if (!centry) {
1423 return NT_STATUS_INTERNAL_DB_ERROR;
1426 dump_data_pw("nt_pass", nt_pass, NT_HASH_LEN);
1428 centry_put_time(centry, time(NULL));
1430 /* Create a salt and then salt the hash. */
1431 generate_random_buffer(cred_salt, NT_HASH_LEN);
1432 E_md5hash(cred_salt, nt_pass, salted_hash);
1434 centry_put_hash16(centry, salted_hash);
1435 centry_put_hash16(centry, cred_salt);
1436 centry_end(centry, "CRED/%s", sid_to_fstring(sid_string, sid));
1438 DEBUG(10,("wcache_save_creds: %s\n", sid_string));
1440 centry_free(centry);
1442 return NT_STATUS_OK;
1446 /* Query display info. This is the basic user list fn */
1447 static NTSTATUS query_user_list(struct winbindd_domain *domain,
1448 TALLOC_CTX *mem_ctx,
1449 uint32 *num_entries,
1450 struct wbint_userinfo **info)
1452 struct winbind_cache *cache = get_cache(domain);
1453 struct cache_entry *centry = NULL;
1454 NTSTATUS status;
1455 unsigned int i, retry;
1456 bool old_status = domain->online;
1458 if (!cache->tdb)
1459 goto do_query;
1461 centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
1462 if (!centry)
1463 goto do_query;
1465 do_fetch_cache:
1466 *num_entries = centry_uint32(centry);
1468 if (*num_entries == 0)
1469 goto do_cached;
1471 (*info) = talloc_array(mem_ctx, struct wbint_userinfo, *num_entries);
1472 if (! (*info)) {
1473 smb_panic_fn("query_user_list out of memory");
1475 for (i=0; i<(*num_entries); i++) {
1476 (*info)[i].acct_name = centry_string(centry, mem_ctx);
1477 (*info)[i].full_name = centry_string(centry, mem_ctx);
1478 (*info)[i].homedir = centry_string(centry, mem_ctx);
1479 (*info)[i].shell = centry_string(centry, mem_ctx);
1480 centry_sid(centry, &(*info)[i].user_sid);
1481 centry_sid(centry, &(*info)[i].group_sid);
1484 do_cached:
1485 status = centry->status;
1487 DEBUG(10,("query_user_list: [Cached] - cached list for domain %s status: %s\n",
1488 domain->name, nt_errstr(status) ));
1490 centry_free(centry);
1491 return status;
1493 do_query:
1494 *num_entries = 0;
1495 *info = NULL;
1497 /* Return status value returned by seq number check */
1499 if (!NT_STATUS_IS_OK(domain->last_status))
1500 return domain->last_status;
1502 /* Put the query_user_list() in a retry loop. There appears to be
1503 * some bug either with Windows 2000 or Samba's handling of large
1504 * rpc replies. This manifests itself as sudden disconnection
1505 * at a random point in the enumeration of a large (60k) user list.
1506 * The retry loop simply tries the operation again. )-: It's not
1507 * pretty but an acceptable workaround until we work out what the
1508 * real problem is. */
1510 retry = 0;
1511 do {
1513 DEBUG(10,("query_user_list: [Cached] - doing backend query for list for domain %s\n",
1514 domain->name ));
1516 status = domain->backend->query_user_list(domain, mem_ctx, num_entries, info);
1517 if (!NT_STATUS_IS_OK(status)) {
1518 DEBUG(3, ("query_user_list: returned 0x%08x, "
1519 "retrying\n", NT_STATUS_V(status)));
1521 if (NT_STATUS_EQUAL(status, NT_STATUS_UNSUCCESSFUL)) {
1522 DEBUG(3, ("query_user_list: flushing "
1523 "connection cache\n"));
1524 invalidate_cm_connection(domain);
1526 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1527 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1528 if (!domain->internal && old_status) {
1529 set_domain_offline(domain);
1531 /* store partial response. */
1532 if (*num_entries > 0) {
1534 * humm, what about the status used for cache?
1535 * Should it be NT_STATUS_OK?
1537 break;
1540 * domain is offline now, and there is no user entries,
1541 * try to fetch from cache again.
1543 if (cache->tdb && !domain->online && !domain->internal && old_status) {
1544 centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
1545 /* partial response... */
1546 if (!centry) {
1547 goto skip_save;
1548 } else {
1549 goto do_fetch_cache;
1551 } else {
1552 goto skip_save;
1556 } while (NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_UNSUCCESSFUL) &&
1557 (retry++ < 5));
1559 /* and save it */
1560 refresh_sequence_number(domain, false);
1561 if (!NT_STATUS_IS_OK(status)) {
1562 return status;
1564 centry = centry_start(domain, status);
1565 if (!centry)
1566 goto skip_save;
1567 centry_put_uint32(centry, *num_entries);
1568 for (i=0; i<(*num_entries); i++) {
1569 centry_put_string(centry, (*info)[i].acct_name);
1570 centry_put_string(centry, (*info)[i].full_name);
1571 centry_put_string(centry, (*info)[i].homedir);
1572 centry_put_string(centry, (*info)[i].shell);
1573 centry_put_sid(centry, &(*info)[i].user_sid);
1574 centry_put_sid(centry, &(*info)[i].group_sid);
1575 if (domain->backend && domain->backend->consistent) {
1576 /* when the backend is consistent we can pre-prime some mappings */
1577 wcache_save_name_to_sid(domain, NT_STATUS_OK,
1578 domain->name,
1579 (*info)[i].acct_name,
1580 &(*info)[i].user_sid,
1581 SID_NAME_USER);
1582 wcache_save_sid_to_name(domain, NT_STATUS_OK,
1583 &(*info)[i].user_sid,
1584 domain->name,
1585 (*info)[i].acct_name,
1586 SID_NAME_USER);
1587 wcache_save_user(domain, NT_STATUS_OK, &(*info)[i]);
1590 centry_end(centry, "UL/%s", domain->name);
1591 centry_free(centry);
1593 skip_save:
1594 return status;
1597 /* list all domain groups */
1598 static NTSTATUS enum_dom_groups(struct winbindd_domain *domain,
1599 TALLOC_CTX *mem_ctx,
1600 uint32 *num_entries,
1601 struct wb_acct_info **info)
1603 struct winbind_cache *cache = get_cache(domain);
1604 struct cache_entry *centry = NULL;
1605 NTSTATUS status;
1606 unsigned int i;
1607 bool old_status;
1609 old_status = domain->online;
1610 if (!cache->tdb)
1611 goto do_query;
1613 centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
1614 if (!centry)
1615 goto do_query;
1617 do_fetch_cache:
1618 *num_entries = centry_uint32(centry);
1620 if (*num_entries == 0)
1621 goto do_cached;
1623 (*info) = talloc_array(mem_ctx, struct wb_acct_info, *num_entries);
1624 if (! (*info)) {
1625 smb_panic_fn("enum_dom_groups out of memory");
1627 for (i=0; i<(*num_entries); i++) {
1628 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1629 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1630 (*info)[i].rid = centry_uint32(centry);
1633 do_cached:
1634 status = centry->status;
1636 DEBUG(10,("enum_dom_groups: [Cached] - cached list for domain %s status: %s\n",
1637 domain->name, nt_errstr(status) ));
1639 centry_free(centry);
1640 return status;
1642 do_query:
1643 *num_entries = 0;
1644 *info = NULL;
1646 /* Return status value returned by seq number check */
1648 if (!NT_STATUS_IS_OK(domain->last_status))
1649 return domain->last_status;
1651 DEBUG(10,("enum_dom_groups: [Cached] - doing backend query for list for domain %s\n",
1652 domain->name ));
1654 status = domain->backend->enum_dom_groups(domain, mem_ctx, num_entries, info);
1656 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1657 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1658 if (!domain->internal && old_status) {
1659 set_domain_offline(domain);
1661 if (cache->tdb &&
1662 !domain->online &&
1663 !domain->internal &&
1664 old_status) {
1665 centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
1666 if (centry) {
1667 goto do_fetch_cache;
1671 /* and save it */
1672 refresh_sequence_number(domain, false);
1673 if (!NT_STATUS_IS_OK(status)) {
1674 return status;
1676 centry = centry_start(domain, status);
1677 if (!centry)
1678 goto skip_save;
1679 centry_put_uint32(centry, *num_entries);
1680 for (i=0; i<(*num_entries); i++) {
1681 centry_put_string(centry, (*info)[i].acct_name);
1682 centry_put_string(centry, (*info)[i].acct_desc);
1683 centry_put_uint32(centry, (*info)[i].rid);
1685 centry_end(centry, "GL/%s/domain", domain->name);
1686 centry_free(centry);
1688 skip_save:
1689 return status;
1692 /* list all domain groups */
1693 static NTSTATUS enum_local_groups(struct winbindd_domain *domain,
1694 TALLOC_CTX *mem_ctx,
1695 uint32 *num_entries,
1696 struct wb_acct_info **info)
1698 struct winbind_cache *cache = get_cache(domain);
1699 struct cache_entry *centry = NULL;
1700 NTSTATUS status;
1701 unsigned int i;
1702 bool old_status;
1704 old_status = domain->online;
1705 if (!cache->tdb)
1706 goto do_query;
1708 centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
1709 if (!centry)
1710 goto do_query;
1712 do_fetch_cache:
1713 *num_entries = centry_uint32(centry);
1715 if (*num_entries == 0)
1716 goto do_cached;
1718 (*info) = talloc_array(mem_ctx, struct wb_acct_info, *num_entries);
1719 if (! (*info)) {
1720 smb_panic_fn("enum_dom_groups out of memory");
1722 for (i=0; i<(*num_entries); i++) {
1723 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1724 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1725 (*info)[i].rid = centry_uint32(centry);
1728 do_cached:
1730 /* If we are returning cached data and the domain controller
1731 is down then we don't know whether the data is up to date
1732 or not. Return NT_STATUS_MORE_PROCESSING_REQUIRED to
1733 indicate this. */
1735 if (wcache_server_down(domain)) {
1736 DEBUG(10, ("enum_local_groups: returning cached user list and server was down\n"));
1737 status = NT_STATUS_MORE_PROCESSING_REQUIRED;
1738 } else
1739 status = centry->status;
1741 DEBUG(10,("enum_local_groups: [Cached] - cached list for domain %s status: %s\n",
1742 domain->name, nt_errstr(status) ));
1744 centry_free(centry);
1745 return status;
1747 do_query:
1748 *num_entries = 0;
1749 *info = NULL;
1751 /* Return status value returned by seq number check */
1753 if (!NT_STATUS_IS_OK(domain->last_status))
1754 return domain->last_status;
1756 DEBUG(10,("enum_local_groups: [Cached] - doing backend query for list for domain %s\n",
1757 domain->name ));
1759 status = domain->backend->enum_local_groups(domain, mem_ctx, num_entries, info);
1761 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1762 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1763 if (!domain->internal && old_status) {
1764 set_domain_offline(domain);
1766 if (cache->tdb &&
1767 !domain->internal &&
1768 !domain->online &&
1769 old_status) {
1770 centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
1771 if (centry) {
1772 goto do_fetch_cache;
1776 /* and save it */
1777 refresh_sequence_number(domain, false);
1778 if (!NT_STATUS_IS_OK(status)) {
1779 return status;
1781 centry = centry_start(domain, status);
1782 if (!centry)
1783 goto skip_save;
1784 centry_put_uint32(centry, *num_entries);
1785 for (i=0; i<(*num_entries); i++) {
1786 centry_put_string(centry, (*info)[i].acct_name);
1787 centry_put_string(centry, (*info)[i].acct_desc);
1788 centry_put_uint32(centry, (*info)[i].rid);
1790 centry_end(centry, "GL/%s/local", domain->name);
1791 centry_free(centry);
1793 skip_save:
1794 return status;
1797 NTSTATUS wcache_name_to_sid(struct winbindd_domain *domain,
1798 const char *domain_name,
1799 const char *name,
1800 struct dom_sid *sid,
1801 enum lsa_SidType *type)
1803 struct winbind_cache *cache = get_cache(domain);
1804 struct cache_entry *centry;
1805 NTSTATUS status;
1806 char *uname;
1808 if (cache->tdb == NULL) {
1809 return NT_STATUS_NOT_FOUND;
1812 uname = talloc_strdup_upper(talloc_tos(), name);
1813 if (uname == NULL) {
1814 return NT_STATUS_NO_MEMORY;
1817 if ((domain_name == NULL) || (domain_name[0] == '\0')) {
1818 domain_name = domain->name;
1821 centry = wcache_fetch(cache, domain, "NS/%s/%s", domain_name, uname);
1822 TALLOC_FREE(uname);
1823 if (centry == NULL) {
1824 return NT_STATUS_NOT_FOUND;
1827 status = centry->status;
1828 if (NT_STATUS_IS_OK(status)) {
1829 *type = (enum lsa_SidType)centry_uint32(centry);
1830 centry_sid(centry, sid);
1833 DEBUG(10,("name_to_sid: [Cached] - cached name for domain %s status: "
1834 "%s\n", domain->name, nt_errstr(status) ));
1836 centry_free(centry);
1837 return status;
1840 /* convert a single name to a sid in a domain */
1841 static NTSTATUS name_to_sid(struct winbindd_domain *domain,
1842 TALLOC_CTX *mem_ctx,
1843 const char *domain_name,
1844 const char *name,
1845 uint32_t flags,
1846 struct dom_sid *sid,
1847 enum lsa_SidType *type)
1849 NTSTATUS status;
1850 bool old_status;
1852 old_status = domain->online;
1854 status = wcache_name_to_sid(domain, domain_name, name, sid, type);
1855 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
1856 return status;
1859 ZERO_STRUCTP(sid);
1861 /* If the seq number check indicated that there is a problem
1862 * with this DC, then return that status... except for
1863 * access_denied. This is special because the dc may be in
1864 * "restrict anonymous = 1" mode, in which case it will deny
1865 * most unauthenticated operations, but *will* allow the LSA
1866 * name-to-sid that we try as a fallback. */
1868 if (!(NT_STATUS_IS_OK(domain->last_status)
1869 || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1870 return domain->last_status;
1872 DEBUG(10,("name_to_sid: [Cached] - doing backend query for name for domain %s\n",
1873 domain->name ));
1875 status = domain->backend->name_to_sid(domain, mem_ctx, domain_name,
1876 name, flags, sid, type);
1878 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1879 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1880 if (!domain->internal && old_status) {
1881 set_domain_offline(domain);
1883 if (!domain->internal &&
1884 !domain->online &&
1885 old_status) {
1886 NTSTATUS cache_status;
1887 cache_status = wcache_name_to_sid(domain, domain_name, name, sid, type);
1888 return cache_status;
1891 /* and save it */
1892 refresh_sequence_number(domain, false);
1894 if (domain->online &&
1895 (NT_STATUS_IS_OK(status) || NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED))) {
1896 wcache_save_name_to_sid(domain, status, domain_name, name, sid, *type);
1898 /* Only save the reverse mapping if this was not a UPN */
1899 if (!strchr(name, '@')) {
1900 if (!strupper_m(discard_const_p(char, domain_name))) {
1901 return NT_STATUS_INVALID_PARAMETER;
1903 (void)strlower_m(discard_const_p(char, name));
1904 wcache_save_sid_to_name(domain, status, sid, domain_name, name, *type);
1908 return status;
1911 static NTSTATUS wcache_sid_to_name(struct winbindd_domain *domain,
1912 const struct dom_sid *sid,
1913 TALLOC_CTX *mem_ctx,
1914 char **domain_name,
1915 char **name,
1916 enum lsa_SidType *type)
1918 struct winbind_cache *cache = get_cache(domain);
1919 struct cache_entry *centry;
1920 char *sid_string;
1921 NTSTATUS status;
1923 if (cache->tdb == NULL) {
1924 return NT_STATUS_NOT_FOUND;
1927 sid_string = sid_string_tos(sid);
1928 if (sid_string == NULL) {
1929 return NT_STATUS_NO_MEMORY;
1932 centry = wcache_fetch(cache, domain, "SN/%s", sid_string);
1933 TALLOC_FREE(sid_string);
1934 if (centry == NULL) {
1935 return NT_STATUS_NOT_FOUND;
1938 if (NT_STATUS_IS_OK(centry->status)) {
1939 *type = (enum lsa_SidType)centry_uint32(centry);
1940 *domain_name = centry_string(centry, mem_ctx);
1941 *name = centry_string(centry, mem_ctx);
1944 status = centry->status;
1945 centry_free(centry);
1947 DEBUG(10,("sid_to_name: [Cached] - cached name for domain %s status: "
1948 "%s\n", domain->name, nt_errstr(status) ));
1950 return status;
1953 /* convert a sid to a user or group name. The sid is guaranteed to be in the domain
1954 given */
1955 static NTSTATUS sid_to_name(struct winbindd_domain *domain,
1956 TALLOC_CTX *mem_ctx,
1957 const struct dom_sid *sid,
1958 char **domain_name,
1959 char **name,
1960 enum lsa_SidType *type)
1962 NTSTATUS status;
1963 bool old_status;
1965 old_status = domain->online;
1966 status = wcache_sid_to_name(domain, sid, mem_ctx, domain_name, name,
1967 type);
1968 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
1969 return status;
1972 *name = NULL;
1973 *domain_name = NULL;
1975 /* If the seq number check indicated that there is a problem
1976 * with this DC, then return that status... except for
1977 * access_denied. This is special because the dc may be in
1978 * "restrict anonymous = 1" mode, in which case it will deny
1979 * most unauthenticated operations, but *will* allow the LSA
1980 * sid-to-name that we try as a fallback. */
1982 if (!(NT_STATUS_IS_OK(domain->last_status)
1983 || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1984 return domain->last_status;
1986 DEBUG(10,("sid_to_name: [Cached] - doing backend query for name for domain %s\n",
1987 domain->name ));
1989 status = domain->backend->sid_to_name(domain, mem_ctx, sid, domain_name, name, type);
1991 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1992 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1993 if (!domain->internal && old_status) {
1994 set_domain_offline(domain);
1996 if (!domain->internal &&
1997 !domain->online &&
1998 old_status) {
1999 NTSTATUS cache_status;
2000 cache_status = wcache_sid_to_name(domain, sid, mem_ctx,
2001 domain_name, name, type);
2002 return cache_status;
2005 /* and save it */
2006 refresh_sequence_number(domain, false);
2007 if (!NT_STATUS_IS_OK(status)) {
2008 return status;
2010 wcache_save_sid_to_name(domain, status, sid, *domain_name, *name, *type);
2012 /* We can't save the name to sid mapping here, as with sid history a
2013 * later name2sid would give the wrong sid. */
2015 return status;
2018 static NTSTATUS rids_to_names(struct winbindd_domain *domain,
2019 TALLOC_CTX *mem_ctx,
2020 const struct dom_sid *domain_sid,
2021 uint32 *rids,
2022 size_t num_rids,
2023 char **domain_name,
2024 char ***names,
2025 enum lsa_SidType **types)
2027 struct winbind_cache *cache = get_cache(domain);
2028 size_t i;
2029 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
2030 bool have_mapped;
2031 bool have_unmapped;
2032 bool old_status;
2034 old_status = domain->online;
2035 *domain_name = NULL;
2036 *names = NULL;
2037 *types = NULL;
2039 if (!cache->tdb) {
2040 goto do_query;
2043 if (num_rids == 0) {
2044 return NT_STATUS_OK;
2047 *names = talloc_array(mem_ctx, char *, num_rids);
2048 *types = talloc_array(mem_ctx, enum lsa_SidType, num_rids);
2050 if ((*names == NULL) || (*types == NULL)) {
2051 result = NT_STATUS_NO_MEMORY;
2052 goto error;
2055 have_mapped = have_unmapped = false;
2057 for (i=0; i<num_rids; i++) {
2058 struct dom_sid sid;
2059 struct cache_entry *centry;
2060 fstring tmp;
2062 if (!sid_compose(&sid, domain_sid, rids[i])) {
2063 result = NT_STATUS_INTERNAL_ERROR;
2064 goto error;
2067 centry = wcache_fetch(cache, domain, "SN/%s",
2068 sid_to_fstring(tmp, &sid));
2069 if (!centry) {
2070 goto do_query;
2073 (*types)[i] = SID_NAME_UNKNOWN;
2074 (*names)[i] = talloc_strdup(*names, "");
2076 if (NT_STATUS_IS_OK(centry->status)) {
2077 char *dom;
2078 have_mapped = true;
2079 (*types)[i] = (enum lsa_SidType)centry_uint32(centry);
2081 dom = centry_string(centry, mem_ctx);
2082 if (*domain_name == NULL) {
2083 *domain_name = dom;
2084 } else {
2085 talloc_free(dom);
2088 (*names)[i] = centry_string(centry, *names);
2090 } else if (NT_STATUS_EQUAL(centry->status, NT_STATUS_NONE_MAPPED)
2091 || NT_STATUS_EQUAL(centry->status, STATUS_SOME_UNMAPPED)) {
2092 have_unmapped = true;
2094 } else {
2095 /* something's definitely wrong */
2096 result = centry->status;
2097 centry_free(centry);
2098 goto error;
2101 centry_free(centry);
2104 if (!have_mapped) {
2105 return NT_STATUS_NONE_MAPPED;
2107 if (!have_unmapped) {
2108 return NT_STATUS_OK;
2110 return STATUS_SOME_UNMAPPED;
2112 do_query:
2114 TALLOC_FREE(*names);
2115 TALLOC_FREE(*types);
2117 result = domain->backend->rids_to_names(domain, mem_ctx, domain_sid,
2118 rids, num_rids, domain_name,
2119 names, types);
2121 if (NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
2122 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2123 if (!domain->internal && old_status) {
2124 set_domain_offline(domain);
2126 if (cache->tdb &&
2127 !domain->internal &&
2128 !domain->online &&
2129 old_status) {
2130 have_mapped = have_unmapped = false;
2132 *names = talloc_array(mem_ctx, char *, num_rids);
2133 if (*names == NULL) {
2134 result = NT_STATUS_NO_MEMORY;
2135 goto error;
2138 *types = talloc_array(mem_ctx, enum lsa_SidType,
2139 num_rids);
2140 if (*types == NULL) {
2141 result = NT_STATUS_NO_MEMORY;
2142 goto error;
2145 for (i=0; i<num_rids; i++) {
2146 struct dom_sid sid;
2147 struct cache_entry *centry;
2148 fstring tmp;
2150 if (!sid_compose(&sid, domain_sid, rids[i])) {
2151 result = NT_STATUS_INTERNAL_ERROR;
2152 goto error;
2155 centry = wcache_fetch(cache, domain, "SN/%s",
2156 sid_to_fstring(tmp, &sid));
2157 if (!centry) {
2158 (*types)[i] = SID_NAME_UNKNOWN;
2159 (*names)[i] = talloc_strdup(*names, "");
2160 continue;
2163 (*types)[i] = SID_NAME_UNKNOWN;
2164 (*names)[i] = talloc_strdup(*names, "");
2166 if (NT_STATUS_IS_OK(centry->status)) {
2167 char *dom;
2168 have_mapped = true;
2169 (*types)[i] = (enum lsa_SidType)centry_uint32(centry);
2171 dom = centry_string(centry, mem_ctx);
2172 if (*domain_name == NULL) {
2173 *domain_name = dom;
2174 } else {
2175 talloc_free(dom);
2178 (*names)[i] = centry_string(centry, *names);
2180 } else if (NT_STATUS_EQUAL(centry->status, NT_STATUS_NONE_MAPPED)) {
2181 have_unmapped = true;
2183 } else {
2184 /* something's definitely wrong */
2185 result = centry->status;
2186 centry_free(centry);
2187 goto error;
2190 centry_free(centry);
2193 if (!have_mapped) {
2194 return NT_STATUS_NONE_MAPPED;
2196 if (!have_unmapped) {
2197 return NT_STATUS_OK;
2199 return STATUS_SOME_UNMAPPED;
2203 None of the queried rids has been found so save all negative entries
2205 if (NT_STATUS_EQUAL(result, NT_STATUS_NONE_MAPPED)) {
2206 for (i = 0; i < num_rids; i++) {
2207 struct dom_sid sid;
2208 const char *name = "";
2209 const enum lsa_SidType type = SID_NAME_UNKNOWN;
2210 NTSTATUS status = NT_STATUS_NONE_MAPPED;
2212 if (!sid_compose(&sid, domain_sid, rids[i])) {
2213 return NT_STATUS_INTERNAL_ERROR;
2216 wcache_save_sid_to_name(domain, status, &sid, *domain_name,
2217 name, type);
2220 return result;
2224 Some or all of the queried rids have been found.
2226 if (!NT_STATUS_IS_OK(result) &&
2227 !NT_STATUS_EQUAL(result, STATUS_SOME_UNMAPPED)) {
2228 return result;
2231 refresh_sequence_number(domain, false);
2233 for (i=0; i<num_rids; i++) {
2234 struct dom_sid sid;
2235 NTSTATUS status;
2237 if (!sid_compose(&sid, domain_sid, rids[i])) {
2238 result = NT_STATUS_INTERNAL_ERROR;
2239 goto error;
2242 status = (*types)[i] == SID_NAME_UNKNOWN ?
2243 NT_STATUS_NONE_MAPPED : NT_STATUS_OK;
2245 wcache_save_sid_to_name(domain, status, &sid, *domain_name,
2246 (*names)[i], (*types)[i]);
2249 return result;
2251 error:
2252 TALLOC_FREE(*names);
2253 TALLOC_FREE(*types);
2254 return result;
2257 NTSTATUS wcache_query_user(struct winbindd_domain *domain,
2258 TALLOC_CTX *mem_ctx,
2259 const struct dom_sid *user_sid,
2260 struct wbint_userinfo *info)
2262 struct winbind_cache *cache = get_cache(domain);
2263 struct cache_entry *centry = NULL;
2264 NTSTATUS status;
2265 char *sid_string;
2267 if (cache->tdb == NULL) {
2268 return NT_STATUS_NOT_FOUND;
2271 sid_string = sid_string_tos(user_sid);
2272 if (sid_string == NULL) {
2273 return NT_STATUS_NO_MEMORY;
2276 centry = wcache_fetch(cache, domain, "U/%s", sid_string);
2277 TALLOC_FREE(sid_string);
2278 if (centry == NULL) {
2279 return NT_STATUS_NOT_FOUND;
2283 * If we have an access denied cache entry and a cached info3
2284 * in the samlogon cache then do a query. This will force the
2285 * rpc back end to return the info3 data.
2288 if (NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED) &&
2289 netsamlogon_cache_have(user_sid)) {
2290 DEBUG(10, ("query_user: cached access denied and have cached "
2291 "info3\n"));
2292 domain->last_status = NT_STATUS_OK;
2293 centry_free(centry);
2294 return NT_STATUS_NOT_FOUND;
2297 /* if status is not ok then this is a negative hit
2298 and the rest of the data doesn't matter */
2299 status = centry->status;
2300 if (NT_STATUS_IS_OK(status)) {
2301 info->acct_name = centry_string(centry, mem_ctx);
2302 info->full_name = centry_string(centry, mem_ctx);
2303 info->homedir = centry_string(centry, mem_ctx);
2304 info->shell = centry_string(centry, mem_ctx);
2305 info->primary_gid = centry_uint32(centry);
2306 centry_sid(centry, &info->user_sid);
2307 centry_sid(centry, &info->group_sid);
2310 DEBUG(10,("query_user: [Cached] - cached info for domain %s status: "
2311 "%s\n", domain->name, nt_errstr(status) ));
2313 centry_free(centry);
2314 return status;
2319 * @brief Query a fullname from the username cache (for further gecos processing)
2321 * @param domain A pointer to the winbindd_domain struct.
2322 * @param mem_ctx The talloc context.
2323 * @param user_sid The user sid.
2324 * @param full_name A pointer to the full_name string.
2326 * @return NTSTATUS code
2328 NTSTATUS wcache_query_user_fullname(struct winbindd_domain *domain,
2329 TALLOC_CTX *mem_ctx,
2330 const struct dom_sid *user_sid,
2331 const char **full_name)
2333 NTSTATUS status;
2334 struct wbint_userinfo info;
2336 status = wcache_query_user(domain, mem_ctx, user_sid, &info);
2337 if (!NT_STATUS_IS_OK(status)) {
2338 return status;
2341 if (info.full_name != NULL) {
2342 *full_name = talloc_strdup(mem_ctx, info.full_name);
2343 if (*full_name == NULL) {
2344 return NT_STATUS_NO_MEMORY;
2348 return NT_STATUS_OK;
2351 /* Lookup user information from a rid */
2352 static NTSTATUS query_user(struct winbindd_domain *domain,
2353 TALLOC_CTX *mem_ctx,
2354 const struct dom_sid *user_sid,
2355 struct wbint_userinfo *info)
2357 NTSTATUS status;
2358 bool old_status;
2360 old_status = domain->online;
2361 status = wcache_query_user(domain, mem_ctx, user_sid, info);
2362 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2363 return status;
2366 ZERO_STRUCTP(info);
2368 /* Return status value returned by seq number check */
2370 if (!NT_STATUS_IS_OK(domain->last_status))
2371 return domain->last_status;
2373 DEBUG(10,("query_user: [Cached] - doing backend query for info for domain %s\n",
2374 domain->name ));
2376 status = domain->backend->query_user(domain, mem_ctx, user_sid, info);
2378 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2379 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2380 if (!domain->internal && old_status) {
2381 set_domain_offline(domain);
2383 if (!domain->internal &&
2384 !domain->online &&
2385 old_status) {
2386 NTSTATUS cache_status;
2387 cache_status = wcache_query_user(domain, mem_ctx, user_sid, info);
2388 return cache_status;
2391 /* and save it */
2392 refresh_sequence_number(domain, false);
2393 if (!NT_STATUS_IS_OK(status)) {
2394 return status;
2396 wcache_save_user(domain, status, info);
2398 return status;
2401 NTSTATUS wcache_lookup_usergroups(struct winbindd_domain *domain,
2402 TALLOC_CTX *mem_ctx,
2403 const struct dom_sid *user_sid,
2404 uint32_t *pnum_sids,
2405 struct dom_sid **psids)
2407 struct winbind_cache *cache = get_cache(domain);
2408 struct cache_entry *centry = NULL;
2409 NTSTATUS status;
2410 uint32_t i, num_sids;
2411 struct dom_sid *sids;
2412 fstring sid_string;
2414 if (cache->tdb == NULL) {
2415 return NT_STATUS_NOT_FOUND;
2418 centry = wcache_fetch(cache, domain, "UG/%s",
2419 sid_to_fstring(sid_string, user_sid));
2420 if (centry == NULL) {
2421 return NT_STATUS_NOT_FOUND;
2424 /* If we have an access denied cache entry and a cached info3 in the
2425 samlogon cache then do a query. This will force the rpc back end
2426 to return the info3 data. */
2428 if (NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)
2429 && netsamlogon_cache_have(user_sid)) {
2430 DEBUG(10, ("lookup_usergroups: cached access denied and have "
2431 "cached info3\n"));
2432 domain->last_status = NT_STATUS_OK;
2433 centry_free(centry);
2434 return NT_STATUS_NOT_FOUND;
2437 num_sids = centry_uint32(centry);
2438 sids = talloc_array(mem_ctx, struct dom_sid, num_sids);
2439 if (sids == NULL) {
2440 centry_free(centry);
2441 return NT_STATUS_NO_MEMORY;
2444 for (i=0; i<num_sids; i++) {
2445 centry_sid(centry, &sids[i]);
2448 status = centry->status;
2450 DEBUG(10,("lookup_usergroups: [Cached] - cached info for domain %s "
2451 "status: %s\n", domain->name, nt_errstr(status)));
2453 centry_free(centry);
2455 *pnum_sids = num_sids;
2456 *psids = sids;
2457 return status;
2460 /* Lookup groups a user is a member of. */
2461 static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
2462 TALLOC_CTX *mem_ctx,
2463 const struct dom_sid *user_sid,
2464 uint32 *num_groups, struct dom_sid **user_gids)
2466 struct cache_entry *centry = NULL;
2467 NTSTATUS status;
2468 unsigned int i;
2469 fstring sid_string;
2470 bool old_status;
2472 old_status = domain->online;
2473 status = wcache_lookup_usergroups(domain, mem_ctx, user_sid,
2474 num_groups, user_gids);
2475 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2476 return status;
2479 (*num_groups) = 0;
2480 (*user_gids) = NULL;
2482 /* Return status value returned by seq number check */
2484 if (!NT_STATUS_IS_OK(domain->last_status))
2485 return domain->last_status;
2487 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info for domain %s\n",
2488 domain->name ));
2490 status = domain->backend->lookup_usergroups(domain, mem_ctx, user_sid, num_groups, user_gids);
2492 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2493 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2494 if (!domain->internal && old_status) {
2495 set_domain_offline(domain);
2497 if (!domain->internal &&
2498 !domain->online &&
2499 old_status) {
2500 NTSTATUS cache_status;
2501 cache_status = wcache_lookup_usergroups(domain, mem_ctx, user_sid,
2502 num_groups, user_gids);
2503 return cache_status;
2506 if ( NT_STATUS_EQUAL(status, NT_STATUS_SYNCHRONIZATION_REQUIRED) )
2507 goto skip_save;
2509 /* and save it */
2510 refresh_sequence_number(domain, false);
2511 if (!NT_STATUS_IS_OK(status)) {
2512 return status;
2514 centry = centry_start(domain, status);
2515 if (!centry)
2516 goto skip_save;
2518 centry_put_uint32(centry, *num_groups);
2519 for (i=0; i<(*num_groups); i++) {
2520 centry_put_sid(centry, &(*user_gids)[i]);
2523 centry_end(centry, "UG/%s", sid_to_fstring(sid_string, user_sid));
2524 centry_free(centry);
2526 skip_save:
2527 return status;
2530 static char *wcache_make_sidlist(TALLOC_CTX *mem_ctx, uint32_t num_sids,
2531 const struct dom_sid *sids)
2533 uint32_t i;
2534 char *sidlist;
2536 sidlist = talloc_strdup(mem_ctx, "");
2537 if (sidlist == NULL) {
2538 return NULL;
2540 for (i=0; i<num_sids; i++) {
2541 fstring tmp;
2542 sidlist = talloc_asprintf_append_buffer(
2543 sidlist, "/%s", sid_to_fstring(tmp, &sids[i]));
2544 if (sidlist == NULL) {
2545 return NULL;
2548 return sidlist;
2551 NTSTATUS wcache_lookup_useraliases(struct winbindd_domain *domain,
2552 TALLOC_CTX *mem_ctx, uint32_t num_sids,
2553 const struct dom_sid *sids,
2554 uint32_t *pnum_aliases, uint32_t **paliases)
2556 struct winbind_cache *cache = get_cache(domain);
2557 struct cache_entry *centry = NULL;
2558 uint32_t num_aliases;
2559 uint32_t *aliases;
2560 NTSTATUS status;
2561 char *sidlist;
2562 int i;
2564 if (cache->tdb == NULL) {
2565 return NT_STATUS_NOT_FOUND;
2568 if (num_sids == 0) {
2569 *pnum_aliases = 0;
2570 *paliases = NULL;
2571 return NT_STATUS_OK;
2574 /* We need to cache indexed by the whole list of SIDs, the aliases
2575 * resulting might come from any of the SIDs. */
2577 sidlist = wcache_make_sidlist(talloc_tos(), num_sids, sids);
2578 if (sidlist == NULL) {
2579 return NT_STATUS_NO_MEMORY;
2582 centry = wcache_fetch(cache, domain, "UA%s", sidlist);
2583 TALLOC_FREE(sidlist);
2584 if (centry == NULL) {
2585 return NT_STATUS_NOT_FOUND;
2588 num_aliases = centry_uint32(centry);
2589 aliases = talloc_array(mem_ctx, uint32_t, num_aliases);
2590 if (aliases == NULL) {
2591 centry_free(centry);
2592 return NT_STATUS_NO_MEMORY;
2595 for (i=0; i<num_aliases; i++) {
2596 aliases[i] = centry_uint32(centry);
2599 status = centry->status;
2601 DEBUG(10,("lookup_useraliases: [Cached] - cached info for domain: %s "
2602 "status %s\n", domain->name, nt_errstr(status)));
2604 centry_free(centry);
2606 *pnum_aliases = num_aliases;
2607 *paliases = aliases;
2609 return status;
2612 static NTSTATUS lookup_useraliases(struct winbindd_domain *domain,
2613 TALLOC_CTX *mem_ctx,
2614 uint32 num_sids, const struct dom_sid *sids,
2615 uint32 *num_aliases, uint32 **alias_rids)
2617 struct cache_entry *centry = NULL;
2618 NTSTATUS status;
2619 char *sidlist;
2620 int i;
2621 bool old_status;
2623 old_status = domain->online;
2624 status = wcache_lookup_useraliases(domain, mem_ctx, num_sids, sids,
2625 num_aliases, alias_rids);
2626 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2627 return status;
2630 (*num_aliases) = 0;
2631 (*alias_rids) = NULL;
2633 if (!NT_STATUS_IS_OK(domain->last_status))
2634 return domain->last_status;
2636 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info "
2637 "for domain %s\n", domain->name ));
2639 sidlist = wcache_make_sidlist(talloc_tos(), num_sids, sids);
2640 if (sidlist == NULL) {
2641 return NT_STATUS_NO_MEMORY;
2644 status = domain->backend->lookup_useraliases(domain, mem_ctx,
2645 num_sids, sids,
2646 num_aliases, alias_rids);
2648 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2649 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2650 if (!domain->internal && old_status) {
2651 set_domain_offline(domain);
2653 if (!domain->internal &&
2654 !domain->online &&
2655 old_status) {
2656 NTSTATUS cache_status;
2657 cache_status = wcache_lookup_useraliases(domain, mem_ctx, num_sids,
2658 sids, num_aliases, alias_rids);
2659 return cache_status;
2662 /* and save it */
2663 refresh_sequence_number(domain, false);
2664 if (!NT_STATUS_IS_OK(status)) {
2665 return status;
2667 centry = centry_start(domain, status);
2668 if (!centry)
2669 goto skip_save;
2670 centry_put_uint32(centry, *num_aliases);
2671 for (i=0; i<(*num_aliases); i++)
2672 centry_put_uint32(centry, (*alias_rids)[i]);
2673 centry_end(centry, "UA%s", sidlist);
2674 centry_free(centry);
2676 skip_save:
2677 return status;
2680 NTSTATUS wcache_lookup_groupmem(struct winbindd_domain *domain,
2681 TALLOC_CTX *mem_ctx,
2682 const struct dom_sid *group_sid,
2683 uint32_t *num_names,
2684 struct dom_sid **sid_mem, char ***names,
2685 uint32_t **name_types)
2687 struct winbind_cache *cache = get_cache(domain);
2688 struct cache_entry *centry = NULL;
2689 NTSTATUS status;
2690 unsigned int i;
2691 char *sid_string;
2693 if (cache->tdb == NULL) {
2694 return NT_STATUS_NOT_FOUND;
2697 sid_string = sid_string_tos(group_sid);
2698 if (sid_string == NULL) {
2699 return NT_STATUS_NO_MEMORY;
2702 centry = wcache_fetch(cache, domain, "GM/%s", sid_string);
2703 TALLOC_FREE(sid_string);
2704 if (centry == NULL) {
2705 return NT_STATUS_NOT_FOUND;
2708 *sid_mem = NULL;
2709 *names = NULL;
2710 *name_types = NULL;
2712 *num_names = centry_uint32(centry);
2713 if (*num_names == 0) {
2714 centry_free(centry);
2715 return NT_STATUS_OK;
2718 *sid_mem = talloc_array(mem_ctx, struct dom_sid, *num_names);
2719 *names = talloc_array(mem_ctx, char *, *num_names);
2720 *name_types = talloc_array(mem_ctx, uint32, *num_names);
2722 if ((*sid_mem == NULL) || (*names == NULL) || (*name_types == NULL)) {
2723 TALLOC_FREE(*sid_mem);
2724 TALLOC_FREE(*names);
2725 TALLOC_FREE(*name_types);
2726 centry_free(centry);
2727 return NT_STATUS_NO_MEMORY;
2730 for (i=0; i<(*num_names); i++) {
2731 centry_sid(centry, &(*sid_mem)[i]);
2732 (*names)[i] = centry_string(centry, mem_ctx);
2733 (*name_types)[i] = centry_uint32(centry);
2736 status = centry->status;
2738 DEBUG(10,("lookup_groupmem: [Cached] - cached info for domain %s "
2739 "status: %s\n", domain->name, nt_errstr(status)));
2741 centry_free(centry);
2742 return status;
2745 static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
2746 TALLOC_CTX *mem_ctx,
2747 const struct dom_sid *group_sid,
2748 enum lsa_SidType type,
2749 uint32 *num_names,
2750 struct dom_sid **sid_mem, char ***names,
2751 uint32 **name_types)
2753 struct cache_entry *centry = NULL;
2754 NTSTATUS status;
2755 unsigned int i;
2756 fstring sid_string;
2757 bool old_status;
2759 old_status = domain->online;
2760 status = wcache_lookup_groupmem(domain, mem_ctx, group_sid, num_names,
2761 sid_mem, names, name_types);
2762 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2763 return status;
2766 (*num_names) = 0;
2767 (*sid_mem) = NULL;
2768 (*names) = NULL;
2769 (*name_types) = NULL;
2771 /* Return status value returned by seq number check */
2773 if (!NT_STATUS_IS_OK(domain->last_status))
2774 return domain->last_status;
2776 DEBUG(10,("lookup_groupmem: [Cached] - doing backend query for info for domain %s\n",
2777 domain->name ));
2779 status = domain->backend->lookup_groupmem(domain, mem_ctx, group_sid,
2780 type, num_names,
2781 sid_mem, names, name_types);
2783 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2784 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2785 if (!domain->internal && old_status) {
2786 set_domain_offline(domain);
2788 if (!domain->internal &&
2789 !domain->online &&
2790 old_status) {
2791 NTSTATUS cache_status;
2792 cache_status = wcache_lookup_groupmem(domain, mem_ctx, group_sid,
2793 num_names, sid_mem, names,
2794 name_types);
2795 return cache_status;
2798 /* and save it */
2799 refresh_sequence_number(domain, false);
2800 if (!NT_STATUS_IS_OK(status)) {
2801 return status;
2803 centry = centry_start(domain, status);
2804 if (!centry)
2805 goto skip_save;
2806 centry_put_uint32(centry, *num_names);
2807 for (i=0; i<(*num_names); i++) {
2808 centry_put_sid(centry, &(*sid_mem)[i]);
2809 centry_put_string(centry, (*names)[i]);
2810 centry_put_uint32(centry, (*name_types)[i]);
2812 centry_end(centry, "GM/%s", sid_to_fstring(sid_string, group_sid));
2813 centry_free(centry);
2815 skip_save:
2816 return status;
2819 /* find the sequence number for a domain */
2820 static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
2822 refresh_sequence_number(domain, false);
2824 *seq = domain->sequence_number;
2826 return NT_STATUS_OK;
2829 /* enumerate trusted domains
2830 * (we need to have the list of trustdoms in the cache when we go offline) -
2831 * Guenther */
2832 static NTSTATUS trusted_domains(struct winbindd_domain *domain,
2833 TALLOC_CTX *mem_ctx,
2834 struct netr_DomainTrustList *trusts)
2836 NTSTATUS status;
2837 struct winbind_cache *cache;
2838 struct winbindd_tdc_domain *dom_list = NULL;
2839 size_t num_domains = 0;
2840 bool retval = false;
2841 int i;
2842 bool old_status;
2844 old_status = domain->online;
2845 trusts->count = 0;
2846 trusts->array = NULL;
2848 cache = get_cache(domain);
2849 if (!cache || !cache->tdb) {
2850 goto do_query;
2853 if (domain->online) {
2854 goto do_query;
2857 retval = wcache_tdc_fetch_list(&dom_list, &num_domains);
2858 if (!retval || !num_domains || !dom_list) {
2859 TALLOC_FREE(dom_list);
2860 goto do_query;
2863 do_fetch_cache:
2864 trusts->array = talloc_zero_array(mem_ctx, struct netr_DomainTrust, num_domains);
2865 if (!trusts->array) {
2866 TALLOC_FREE(dom_list);
2867 return NT_STATUS_NO_MEMORY;
2870 for (i = 0; i < num_domains; i++) {
2871 struct netr_DomainTrust *trust;
2872 struct dom_sid *sid;
2873 struct winbindd_domain *dom;
2875 dom = find_domain_from_name_noinit(dom_list[i].domain_name);
2876 if (dom && dom->internal) {
2877 continue;
2880 trust = &trusts->array[trusts->count];
2881 trust->netbios_name = talloc_strdup(trusts->array, dom_list[i].domain_name);
2882 trust->dns_name = talloc_strdup(trusts->array, dom_list[i].dns_name);
2883 sid = talloc(trusts->array, struct dom_sid);
2884 if (!trust->netbios_name || !trust->dns_name ||
2885 !sid) {
2886 TALLOC_FREE(dom_list);
2887 TALLOC_FREE(trusts->array);
2888 return NT_STATUS_NO_MEMORY;
2891 trust->trust_flags = dom_list[i].trust_flags;
2892 trust->trust_attributes = dom_list[i].trust_attribs;
2893 trust->trust_type = dom_list[i].trust_type;
2894 sid_copy(sid, &dom_list[i].sid);
2895 trust->sid = sid;
2896 trusts->count++;
2899 TALLOC_FREE(dom_list);
2900 return NT_STATUS_OK;
2902 do_query:
2903 /* Return status value returned by seq number check */
2905 if (!NT_STATUS_IS_OK(domain->last_status))
2906 return domain->last_status;
2908 DEBUG(10,("trusted_domains: [Cached] - doing backend query for info for domain %s\n",
2909 domain->name ));
2911 status = domain->backend->trusted_domains(domain, mem_ctx, trusts);
2913 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2914 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2915 if (!domain->internal && old_status) {
2916 set_domain_offline(domain);
2918 if (!domain->internal &&
2919 !domain->online &&
2920 old_status) {
2921 retval = wcache_tdc_fetch_list(&dom_list, &num_domains);
2922 if (retval && num_domains && dom_list) {
2923 TALLOC_FREE(trusts->array);
2924 trusts->count = 0;
2925 goto do_fetch_cache;
2929 /* no trusts gives NT_STATUS_NO_MORE_ENTRIES resetting to NT_STATUS_OK
2930 * so that the generic centry handling still applies correctly -
2931 * Guenther*/
2933 if (!NT_STATUS_IS_ERR(status)) {
2934 status = NT_STATUS_OK;
2936 return status;
2939 /* get lockout policy */
2940 static NTSTATUS lockout_policy(struct winbindd_domain *domain,
2941 TALLOC_CTX *mem_ctx,
2942 struct samr_DomInfo12 *policy)
2944 struct winbind_cache *cache = get_cache(domain);
2945 struct cache_entry *centry = NULL;
2946 NTSTATUS status;
2947 bool old_status;
2949 old_status = domain->online;
2950 if (!cache->tdb)
2951 goto do_query;
2953 centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
2955 if (!centry)
2956 goto do_query;
2958 do_fetch_cache:
2959 policy->lockout_duration = centry_nttime(centry);
2960 policy->lockout_window = centry_nttime(centry);
2961 policy->lockout_threshold = centry_uint16(centry);
2963 status = centry->status;
2965 DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2966 domain->name, nt_errstr(status) ));
2968 centry_free(centry);
2969 return status;
2971 do_query:
2972 ZERO_STRUCTP(policy);
2974 /* Return status value returned by seq number check */
2976 if (!NT_STATUS_IS_OK(domain->last_status))
2977 return domain->last_status;
2979 DEBUG(10,("lockout_policy: [Cached] - doing backend query for info for domain %s\n",
2980 domain->name ));
2982 status = domain->backend->lockout_policy(domain, mem_ctx, policy);
2984 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2985 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2986 if (!domain->internal && old_status) {
2987 set_domain_offline(domain);
2989 if (cache->tdb &&
2990 !domain->internal &&
2991 !domain->online &&
2992 old_status) {
2993 centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
2994 if (centry) {
2995 goto do_fetch_cache;
2999 /* and save it */
3000 refresh_sequence_number(domain, false);
3001 if (!NT_STATUS_IS_OK(status)) {
3002 return status;
3004 wcache_save_lockout_policy(domain, status, policy);
3006 return status;
3009 /* get password policy */
3010 static NTSTATUS password_policy(struct winbindd_domain *domain,
3011 TALLOC_CTX *mem_ctx,
3012 struct samr_DomInfo1 *policy)
3014 struct winbind_cache *cache = get_cache(domain);
3015 struct cache_entry *centry = NULL;
3016 NTSTATUS status;
3017 bool old_status;
3019 old_status = domain->online;
3020 if (!cache->tdb)
3021 goto do_query;
3023 centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
3025 if (!centry)
3026 goto do_query;
3028 do_fetch_cache:
3029 policy->min_password_length = centry_uint16(centry);
3030 policy->password_history_length = centry_uint16(centry);
3031 policy->password_properties = centry_uint32(centry);
3032 policy->max_password_age = centry_nttime(centry);
3033 policy->min_password_age = centry_nttime(centry);
3035 status = centry->status;
3037 DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
3038 domain->name, nt_errstr(status) ));
3040 centry_free(centry);
3041 return status;
3043 do_query:
3044 ZERO_STRUCTP(policy);
3046 /* Return status value returned by seq number check */
3048 if (!NT_STATUS_IS_OK(domain->last_status))
3049 return domain->last_status;
3051 DEBUG(10,("password_policy: [Cached] - doing backend query for info for domain %s\n",
3052 domain->name ));
3054 status = domain->backend->password_policy(domain, mem_ctx, policy);
3056 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
3057 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
3058 if (!domain->internal && old_status) {
3059 set_domain_offline(domain);
3061 if (cache->tdb &&
3062 !domain->internal &&
3063 !domain->online &&
3064 old_status) {
3065 centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
3066 if (centry) {
3067 goto do_fetch_cache;
3071 /* and save it */
3072 refresh_sequence_number(domain, false);
3073 if (!NT_STATUS_IS_OK(status)) {
3074 return status;
3076 wcache_save_password_policy(domain, status, policy);
3078 return status;
3082 /* Invalidate cached user and group lists coherently */
3084 static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
3085 void *state)
3087 if (strncmp((const char *)kbuf.dptr, "UL/", 3) == 0 ||
3088 strncmp((const char *)kbuf.dptr, "GL/", 3) == 0)
3089 tdb_delete(the_tdb, kbuf);
3091 return 0;
3094 /* Invalidate the getpwnam and getgroups entries for a winbindd domain */
3096 void wcache_invalidate_samlogon(struct winbindd_domain *domain,
3097 const struct dom_sid *sid)
3099 fstring key_str, sid_string;
3100 struct winbind_cache *cache;
3102 /* dont clear cached U/SID and UG/SID entries when we want to logon
3103 * offline - gd */
3105 if (lp_winbind_offline_logon()) {
3106 return;
3109 if (!domain)
3110 return;
3112 cache = get_cache(domain);
3114 if (!cache->tdb) {
3115 return;
3118 /* Clear U/SID cache entry */
3119 fstr_sprintf(key_str, "U/%s", sid_to_fstring(sid_string, sid));
3120 DEBUG(10, ("wcache_invalidate_samlogon: clearing %s\n", key_str));
3121 tdb_delete(cache->tdb, string_tdb_data(key_str));
3123 /* Clear UG/SID cache entry */
3124 fstr_sprintf(key_str, "UG/%s", sid_to_fstring(sid_string, sid));
3125 DEBUG(10, ("wcache_invalidate_samlogon: clearing %s\n", key_str));
3126 tdb_delete(cache->tdb, string_tdb_data(key_str));
3128 /* Samba/winbindd never needs this. */
3129 netsamlogon_clear_cached_user(sid);
3132 bool wcache_invalidate_cache(void)
3134 struct winbindd_domain *domain;
3136 for (domain = domain_list(); domain; domain = domain->next) {
3137 struct winbind_cache *cache = get_cache(domain);
3139 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
3140 "entries for %s\n", domain->name));
3141 if (cache) {
3142 if (cache->tdb) {
3143 tdb_traverse(cache->tdb, traverse_fn, NULL);
3144 } else {
3145 return false;
3149 return true;
3152 bool wcache_invalidate_cache_noinit(void)
3154 struct winbindd_domain *domain;
3156 for (domain = domain_list(); domain; domain = domain->next) {
3157 struct winbind_cache *cache;
3159 /* Skip uninitialized domains. */
3160 if (!domain->initialized && !domain->internal) {
3161 continue;
3164 cache = get_cache(domain);
3166 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
3167 "entries for %s\n", domain->name));
3168 if (cache) {
3169 if (cache->tdb) {
3170 tdb_traverse(cache->tdb, traverse_fn, NULL);
3172 * Flushing cache has nothing to with domains.
3173 * return here if we successfully flushed once.
3174 * To avoid unnecessary traversing the cache.
3176 return true;
3177 } else {
3178 return false;
3182 return true;
3185 bool init_wcache(void)
3187 char *db_path;
3189 if (wcache == NULL) {
3190 wcache = SMB_XMALLOC_P(struct winbind_cache);
3191 ZERO_STRUCTP(wcache);
3194 if (wcache->tdb != NULL)
3195 return true;
3197 db_path = state_path("winbindd_cache.tdb");
3198 if (db_path == NULL) {
3199 return false;
3202 /* when working offline we must not clear the cache on restart */
3203 wcache->tdb = tdb_open_log(db_path,
3204 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
3205 TDB_INCOMPATIBLE_HASH |
3206 (lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST)),
3207 O_RDWR|O_CREAT, 0600);
3208 TALLOC_FREE(db_path);
3209 if (wcache->tdb == NULL) {
3210 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
3211 return false;
3214 return true;
3217 /************************************************************************
3218 This is called by the parent to initialize the cache file.
3219 We don't need sophisticated locking here as we know we're the
3220 only opener.
3221 ************************************************************************/
3223 bool initialize_winbindd_cache(void)
3225 bool cache_bad = true;
3226 uint32 vers;
3228 if (!init_wcache()) {
3229 DEBUG(0,("initialize_winbindd_cache: init_wcache failed.\n"));
3230 return false;
3233 /* Check version number. */
3234 if (tdb_fetch_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, &vers) &&
3235 vers == WINBINDD_CACHE_VERSION) {
3236 cache_bad = false;
3239 if (cache_bad) {
3240 char *db_path;
3242 DEBUG(0,("initialize_winbindd_cache: clearing cache "
3243 "and re-creating with version number %d\n",
3244 WINBINDD_CACHE_VERSION ));
3246 tdb_close(wcache->tdb);
3247 wcache->tdb = NULL;
3249 db_path = state_path("winbindd_cache.tdb");
3250 if (db_path == NULL) {
3251 return false;
3254 if (unlink(db_path) == -1) {
3255 DEBUG(0,("initialize_winbindd_cache: unlink %s failed %s ",
3256 db_path,
3257 strerror(errno) ));
3258 TALLOC_FREE(db_path);
3259 return false;
3261 TALLOC_FREE(db_path);
3262 if (!init_wcache()) {
3263 DEBUG(0,("initialize_winbindd_cache: re-initialization "
3264 "init_wcache failed.\n"));
3265 return false;
3268 /* Write the version. */
3269 if (!tdb_store_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, WINBINDD_CACHE_VERSION)) {
3270 DEBUG(0,("initialize_winbindd_cache: version number store failed %s\n",
3271 tdb_errorstr_compat(wcache->tdb) ));
3272 return false;
3276 tdb_close(wcache->tdb);
3277 wcache->tdb = NULL;
3278 return true;
3281 void close_winbindd_cache(void)
3283 if (!wcache) {
3284 return;
3286 if (wcache->tdb) {
3287 tdb_close(wcache->tdb);
3288 wcache->tdb = NULL;
3292 bool lookup_cached_sid(TALLOC_CTX *mem_ctx, const struct dom_sid *sid,
3293 char **domain_name, char **name,
3294 enum lsa_SidType *type)
3296 struct winbindd_domain *domain;
3297 NTSTATUS status;
3299 domain = find_lookup_domain_from_sid(sid);
3300 if (domain == NULL) {
3301 return false;
3303 status = wcache_sid_to_name(domain, sid, mem_ctx, domain_name, name,
3304 type);
3305 return NT_STATUS_IS_OK(status);
3308 bool lookup_cached_name(const char *domain_name,
3309 const char *name,
3310 struct dom_sid *sid,
3311 enum lsa_SidType *type)
3313 struct winbindd_domain *domain;
3314 NTSTATUS status;
3315 bool original_online_state;
3317 domain = find_lookup_domain_from_name(domain_name);
3318 if (domain == NULL) {
3319 return false;
3322 /* If we are doing a cached logon, temporarily set the domain
3323 offline so the cache won't expire the entry */
3325 original_online_state = domain->online;
3326 domain->online = false;
3327 status = wcache_name_to_sid(domain, domain_name, name, sid, type);
3328 domain->online = original_online_state;
3330 return NT_STATUS_IS_OK(status);
3333 void cache_name2sid(struct winbindd_domain *domain,
3334 const char *domain_name, const char *name,
3335 enum lsa_SidType type, const struct dom_sid *sid)
3337 refresh_sequence_number(domain, false);
3338 wcache_save_name_to_sid(domain, NT_STATUS_OK, domain_name, name,
3339 sid, type);
3343 * The original idea that this cache only contains centries has
3344 * been blurred - now other stuff gets put in here. Ensure we
3345 * ignore these things on cleanup.
3348 static int traverse_fn_cleanup(TDB_CONTEXT *the_tdb, TDB_DATA kbuf,
3349 TDB_DATA dbuf, void *state)
3351 struct cache_entry *centry;
3353 if (is_non_centry_key(kbuf)) {
3354 return 0;
3357 centry = wcache_fetch_raw((char *)kbuf.dptr);
3358 if (!centry) {
3359 return 0;
3362 if (!NT_STATUS_IS_OK(centry->status)) {
3363 DEBUG(10,("deleting centry %s\n", (const char *)kbuf.dptr));
3364 tdb_delete(the_tdb, kbuf);
3367 centry_free(centry);
3368 return 0;
3371 /* flush the cache */
3372 void wcache_flush_cache(void)
3374 char *db_path;
3376 if (!wcache)
3377 return;
3378 if (wcache->tdb) {
3379 tdb_close(wcache->tdb);
3380 wcache->tdb = NULL;
3382 if (!winbindd_use_cache()) {
3383 return;
3386 db_path = state_path("winbindd_cache.tdb");
3387 if (db_path == NULL) {
3388 return;
3391 /* when working offline we must not clear the cache on restart */
3392 wcache->tdb = tdb_open_log(db_path,
3393 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
3394 TDB_INCOMPATIBLE_HASH |
3395 (lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST)),
3396 O_RDWR|O_CREAT, 0600);
3397 TALLOC_FREE(db_path);
3398 if (!wcache->tdb) {
3399 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
3400 return;
3403 tdb_traverse(wcache->tdb, traverse_fn_cleanup, NULL);
3405 DEBUG(10,("wcache_flush_cache success\n"));
3408 /* Count cached creds */
3410 static int traverse_fn_cached_creds(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
3411 void *state)
3413 int *cred_count = (int*)state;
3415 if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
3416 (*cred_count)++;
3418 return 0;
3421 NTSTATUS wcache_count_cached_creds(struct winbindd_domain *domain, int *count)
3423 struct winbind_cache *cache = get_cache(domain);
3425 *count = 0;
3427 if (!cache->tdb) {
3428 return NT_STATUS_INTERNAL_DB_ERROR;
3431 tdb_traverse(cache->tdb, traverse_fn_cached_creds, (void *)count);
3433 return NT_STATUS_OK;
3436 struct cred_list {
3437 struct cred_list *prev, *next;
3438 TDB_DATA key;
3439 fstring name;
3440 time_t created;
3442 static struct cred_list *wcache_cred_list;
3444 static int traverse_fn_get_credlist(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
3445 void *state)
3447 struct cred_list *cred;
3449 if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
3451 cred = SMB_MALLOC_P(struct cred_list);
3452 if (cred == NULL) {
3453 DEBUG(0,("traverse_fn_remove_first_creds: failed to malloc new entry for list\n"));
3454 return -1;
3457 ZERO_STRUCTP(cred);
3459 /* save a copy of the key */
3461 fstrcpy(cred->name, (const char *)kbuf.dptr);
3462 DLIST_ADD(wcache_cred_list, cred);
3465 return 0;
3468 NTSTATUS wcache_remove_oldest_cached_creds(struct winbindd_domain *domain, const struct dom_sid *sid)
3470 struct winbind_cache *cache = get_cache(domain);
3471 NTSTATUS status;
3472 int ret;
3473 struct cred_list *cred, *oldest = NULL;
3475 if (!cache->tdb) {
3476 return NT_STATUS_INTERNAL_DB_ERROR;
3479 /* we possibly already have an entry */
3480 if (sid && NT_STATUS_IS_OK(wcache_cached_creds_exist(domain, sid))) {
3482 fstring key_str, tmp;
3484 DEBUG(11,("we already have an entry, deleting that\n"));
3486 fstr_sprintf(key_str, "CRED/%s", sid_to_fstring(tmp, sid));
3488 tdb_delete(cache->tdb, string_tdb_data(key_str));
3490 return NT_STATUS_OK;
3493 ret = tdb_traverse(cache->tdb, traverse_fn_get_credlist, NULL);
3494 if (ret == 0) {
3495 return NT_STATUS_OK;
3496 } else if ((ret < 0) || (wcache_cred_list == NULL)) {
3497 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
3500 ZERO_STRUCTP(oldest);
3502 for (cred = wcache_cred_list; cred; cred = cred->next) {
3504 TDB_DATA data;
3505 time_t t;
3507 data = tdb_fetch_compat(cache->tdb, string_tdb_data(cred->name));
3508 if (!data.dptr) {
3509 DEBUG(10,("wcache_remove_oldest_cached_creds: entry for [%s] not found\n",
3510 cred->name));
3511 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
3512 goto done;
3515 t = IVAL(data.dptr, 0);
3516 SAFE_FREE(data.dptr);
3518 if (!oldest) {
3519 oldest = SMB_MALLOC_P(struct cred_list);
3520 if (oldest == NULL) {
3521 status = NT_STATUS_NO_MEMORY;
3522 goto done;
3525 fstrcpy(oldest->name, cred->name);
3526 oldest->created = t;
3527 continue;
3530 if (t < oldest->created) {
3531 fstrcpy(oldest->name, cred->name);
3532 oldest->created = t;
3536 if (tdb_delete(cache->tdb, string_tdb_data(oldest->name)) == 0) {
3537 status = NT_STATUS_OK;
3538 } else {
3539 status = NT_STATUS_UNSUCCESSFUL;
3541 done:
3542 SAFE_FREE(wcache_cred_list);
3543 SAFE_FREE(oldest);
3545 return status;
3548 /* Change the global online/offline state. */
3549 bool set_global_winbindd_state_offline(void)
3551 TDB_DATA data;
3553 DEBUG(10,("set_global_winbindd_state_offline: offline requested.\n"));
3555 /* Only go offline if someone has created
3556 the key "WINBINDD_OFFLINE" in the cache tdb. */
3558 if (wcache == NULL || wcache->tdb == NULL) {
3559 DEBUG(10,("set_global_winbindd_state_offline: wcache not open yet.\n"));
3560 return false;
3563 if (!lp_winbind_offline_logon()) {
3564 DEBUG(10,("set_global_winbindd_state_offline: rejecting.\n"));
3565 return false;
3568 if (global_winbindd_offline_state) {
3569 /* Already offline. */
3570 return true;
3573 data = tdb_fetch_bystring( wcache->tdb, "WINBINDD_OFFLINE" );
3575 if (!data.dptr || data.dsize != 4) {
3576 DEBUG(10,("set_global_winbindd_state_offline: offline state not set.\n"));
3577 SAFE_FREE(data.dptr);
3578 return false;
3579 } else {
3580 DEBUG(10,("set_global_winbindd_state_offline: offline state set.\n"));
3581 global_winbindd_offline_state = true;
3582 SAFE_FREE(data.dptr);
3583 return true;
3587 void set_global_winbindd_state_online(void)
3589 DEBUG(10,("set_global_winbindd_state_online: online requested.\n"));
3591 if (!lp_winbind_offline_logon()) {
3592 DEBUG(10,("set_global_winbindd_state_online: rejecting.\n"));
3593 return;
3596 if (!global_winbindd_offline_state) {
3597 /* Already online. */
3598 return;
3600 global_winbindd_offline_state = false;
3602 if (!wcache->tdb) {
3603 return;
3606 /* Ensure there is no key "WINBINDD_OFFLINE" in the cache tdb. */
3607 tdb_delete_bystring(wcache->tdb, "WINBINDD_OFFLINE");
3610 bool get_global_winbindd_state_offline(void)
3612 return global_winbindd_offline_state;
3615 /***********************************************************************
3616 Validate functions for all possible cache tdb keys.
3617 ***********************************************************************/
3619 static struct cache_entry *create_centry_validate(const char *kstr, TDB_DATA data,
3620 struct tdb_validation_status *state)
3622 struct cache_entry *centry;
3624 centry = SMB_XMALLOC_P(struct cache_entry);
3625 centry->data = (unsigned char *)smb_memdup(data.dptr, data.dsize);
3626 if (!centry->data) {
3627 SAFE_FREE(centry);
3628 return NULL;
3630 centry->len = data.dsize;
3631 centry->ofs = 0;
3633 if (centry->len < 16) {
3634 /* huh? corrupt cache? */
3635 DEBUG(0,("create_centry_validate: Corrupt cache for key %s "
3636 "(len < 16) ?\n", kstr));
3637 centry_free(centry);
3638 state->bad_entry = true;
3639 state->success = false;
3640 return NULL;
3643 centry->status = NT_STATUS(centry_uint32(centry));
3644 centry->sequence_number = centry_uint32(centry);
3645 centry->timeout = centry_uint64_t(centry);
3646 return centry;
3649 static int validate_seqnum(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3650 struct tdb_validation_status *state)
3652 if (dbuf.dsize != 8) {
3653 DEBUG(0,("validate_seqnum: Corrupt cache for key %s (len %u != 8) ?\n",
3654 keystr, (unsigned int)dbuf.dsize ));
3655 state->bad_entry = true;
3656 return 1;
3658 return 0;
3661 static int validate_ns(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3662 struct tdb_validation_status *state)
3664 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3665 if (!centry) {
3666 return 1;
3669 (void)centry_uint32(centry);
3670 if (NT_STATUS_IS_OK(centry->status)) {
3671 struct dom_sid sid;
3672 (void)centry_sid(centry, &sid);
3675 centry_free(centry);
3677 if (!(state->success)) {
3678 return 1;
3680 DEBUG(10,("validate_ns: %s ok\n", keystr));
3681 return 0;
3684 static int validate_sn(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3685 struct tdb_validation_status *state)
3687 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3688 if (!centry) {
3689 return 1;
3692 if (NT_STATUS_IS_OK(centry->status)) {
3693 (void)centry_uint32(centry);
3694 (void)centry_string(centry, mem_ctx);
3695 (void)centry_string(centry, mem_ctx);
3698 centry_free(centry);
3700 if (!(state->success)) {
3701 return 1;
3703 DEBUG(10,("validate_sn: %s ok\n", keystr));
3704 return 0;
3707 static int validate_u(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3708 struct tdb_validation_status *state)
3710 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3711 struct dom_sid sid;
3713 if (!centry) {
3714 return 1;
3717 (void)centry_string(centry, mem_ctx);
3718 (void)centry_string(centry, mem_ctx);
3719 (void)centry_string(centry, mem_ctx);
3720 (void)centry_string(centry, mem_ctx);
3721 (void)centry_uint32(centry);
3722 (void)centry_sid(centry, &sid);
3723 (void)centry_sid(centry, &sid);
3725 centry_free(centry);
3727 if (!(state->success)) {
3728 return 1;
3730 DEBUG(10,("validate_u: %s ok\n", keystr));
3731 return 0;
3734 static int validate_loc_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3735 struct tdb_validation_status *state)
3737 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3739 if (!centry) {
3740 return 1;
3743 (void)centry_nttime(centry);
3744 (void)centry_nttime(centry);
3745 (void)centry_uint16(centry);
3747 centry_free(centry);
3749 if (!(state->success)) {
3750 return 1;
3752 DEBUG(10,("validate_loc_pol: %s ok\n", keystr));
3753 return 0;
3756 static int validate_pwd_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3757 struct tdb_validation_status *state)
3759 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3761 if (!centry) {
3762 return 1;
3765 (void)centry_uint16(centry);
3766 (void)centry_uint16(centry);
3767 (void)centry_uint32(centry);
3768 (void)centry_nttime(centry);
3769 (void)centry_nttime(centry);
3771 centry_free(centry);
3773 if (!(state->success)) {
3774 return 1;
3776 DEBUG(10,("validate_pwd_pol: %s ok\n", keystr));
3777 return 0;
3780 static int validate_cred(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);
3785 if (!centry) {
3786 return 1;
3789 (void)centry_time(centry);
3790 (void)centry_hash16(centry, mem_ctx);
3792 /* We only have 17 bytes more data in the salted cred case. */
3793 if (centry->len - centry->ofs == 17) {
3794 (void)centry_hash16(centry, mem_ctx);
3797 centry_free(centry);
3799 if (!(state->success)) {
3800 return 1;
3802 DEBUG(10,("validate_cred: %s ok\n", keystr));
3803 return 0;
3806 static int validate_ul(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3807 struct tdb_validation_status *state)
3809 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3810 int32 num_entries, i;
3812 if (!centry) {
3813 return 1;
3816 num_entries = (int32)centry_uint32(centry);
3818 for (i=0; i< num_entries; i++) {
3819 struct dom_sid sid;
3820 (void)centry_string(centry, mem_ctx);
3821 (void)centry_string(centry, mem_ctx);
3822 (void)centry_string(centry, mem_ctx);
3823 (void)centry_string(centry, mem_ctx);
3824 (void)centry_sid(centry, &sid);
3825 (void)centry_sid(centry, &sid);
3828 centry_free(centry);
3830 if (!(state->success)) {
3831 return 1;
3833 DEBUG(10,("validate_ul: %s ok\n", keystr));
3834 return 0;
3837 static int validate_gl(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3838 struct tdb_validation_status *state)
3840 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3841 int32 num_entries, i;
3843 if (!centry) {
3844 return 1;
3847 num_entries = centry_uint32(centry);
3849 for (i=0; i< num_entries; i++) {
3850 (void)centry_string(centry, mem_ctx);
3851 (void)centry_string(centry, mem_ctx);
3852 (void)centry_uint32(centry);
3855 centry_free(centry);
3857 if (!(state->success)) {
3858 return 1;
3860 DEBUG(10,("validate_gl: %s ok\n", keystr));
3861 return 0;
3864 static int validate_ug(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3865 struct tdb_validation_status *state)
3867 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3868 int32 num_groups, i;
3870 if (!centry) {
3871 return 1;
3874 num_groups = centry_uint32(centry);
3876 for (i=0; i< num_groups; i++) {
3877 struct dom_sid sid;
3878 centry_sid(centry, &sid);
3881 centry_free(centry);
3883 if (!(state->success)) {
3884 return 1;
3886 DEBUG(10,("validate_ug: %s ok\n", keystr));
3887 return 0;
3890 static int validate_ua(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3891 struct tdb_validation_status *state)
3893 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3894 int32 num_aliases, i;
3896 if (!centry) {
3897 return 1;
3900 num_aliases = centry_uint32(centry);
3902 for (i=0; i < num_aliases; i++) {
3903 (void)centry_uint32(centry);
3906 centry_free(centry);
3908 if (!(state->success)) {
3909 return 1;
3911 DEBUG(10,("validate_ua: %s ok\n", keystr));
3912 return 0;
3915 static int validate_gm(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3916 struct tdb_validation_status *state)
3918 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3919 int32 num_names, i;
3921 if (!centry) {
3922 return 1;
3925 num_names = centry_uint32(centry);
3927 for (i=0; i< num_names; i++) {
3928 struct dom_sid sid;
3929 centry_sid(centry, &sid);
3930 (void)centry_string(centry, mem_ctx);
3931 (void)centry_uint32(centry);
3934 centry_free(centry);
3936 if (!(state->success)) {
3937 return 1;
3939 DEBUG(10,("validate_gm: %s ok\n", keystr));
3940 return 0;
3943 static int validate_dr(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3944 struct tdb_validation_status *state)
3946 /* Can't say anything about this other than must be nonzero. */
3947 if (dbuf.dsize == 0) {
3948 DEBUG(0,("validate_dr: Corrupt cache for key %s (len == 0) ?\n",
3949 keystr));
3950 state->bad_entry = true;
3951 state->success = false;
3952 return 1;
3955 DEBUG(10,("validate_dr: %s ok\n", keystr));
3956 return 0;
3959 static int validate_de(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3960 struct tdb_validation_status *state)
3962 /* Can't say anything about this other than must be nonzero. */
3963 if (dbuf.dsize == 0) {
3964 DEBUG(0,("validate_de: Corrupt cache for key %s (len == 0) ?\n",
3965 keystr));
3966 state->bad_entry = true;
3967 state->success = false;
3968 return 1;
3971 DEBUG(10,("validate_de: %s ok\n", keystr));
3972 return 0;
3975 static int validate_pwinfo(TALLOC_CTX *mem_ctx, const char *keystr,
3976 TDB_DATA dbuf, struct tdb_validation_status *state)
3978 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3980 if (!centry) {
3981 return 1;
3984 (void)centry_string(centry, mem_ctx);
3985 (void)centry_string(centry, mem_ctx);
3986 (void)centry_string(centry, mem_ctx);
3987 (void)centry_uint32(centry);
3989 centry_free(centry);
3991 if (!(state->success)) {
3992 return 1;
3994 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3995 return 0;
3998 static int validate_nss_an(TALLOC_CTX *mem_ctx, const char *keystr,
3999 TDB_DATA dbuf,
4000 struct tdb_validation_status *state)
4002 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
4004 if (!centry) {
4005 return 1;
4008 (void)centry_string( centry, mem_ctx );
4010 centry_free(centry);
4012 if (!(state->success)) {
4013 return 1;
4015 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
4016 return 0;
4019 static int validate_nss_na(TALLOC_CTX *mem_ctx, const char *keystr,
4020 TDB_DATA dbuf,
4021 struct tdb_validation_status *state)
4023 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
4025 if (!centry) {
4026 return 1;
4029 (void)centry_string( centry, mem_ctx );
4031 centry_free(centry);
4033 if (!(state->success)) {
4034 return 1;
4036 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
4037 return 0;
4040 static int validate_trustdomcache(TALLOC_CTX *mem_ctx, const char *keystr,
4041 TDB_DATA dbuf,
4042 struct tdb_validation_status *state)
4044 if (dbuf.dsize == 0) {
4045 DEBUG(0, ("validate_trustdomcache: Corrupt cache for "
4046 "key %s (len ==0) ?\n", keystr));
4047 state->bad_entry = true;
4048 state->success = false;
4049 return 1;
4052 DEBUG(10, ("validate_trustdomcache: %s ok\n", keystr));
4053 DEBUGADD(10, (" Don't trust me, I am a DUMMY!\n"));
4054 return 0;
4057 static int validate_offline(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
4058 struct tdb_validation_status *state)
4060 if (dbuf.dsize != 4) {
4061 DEBUG(0,("validate_offline: Corrupt cache for key %s (len %u != 4) ?\n",
4062 keystr, (unsigned int)dbuf.dsize ));
4063 state->bad_entry = true;
4064 state->success = false;
4065 return 1;
4067 DEBUG(10,("validate_offline: %s ok\n", keystr));
4068 return 0;
4071 static int validate_ndr(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
4072 struct tdb_validation_status *state)
4075 * Ignore validation for now. The proper way to do this is with a
4076 * checksum. Just pure parsing does not really catch much.
4078 return 0;
4081 static int validate_cache_version(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
4082 struct tdb_validation_status *state)
4084 if (dbuf.dsize != 4) {
4085 DEBUG(0, ("validate_cache_version: Corrupt cache for "
4086 "key %s (len %u != 4) ?\n",
4087 keystr, (unsigned int)dbuf.dsize));
4088 state->bad_entry = true;
4089 state->success = false;
4090 return 1;
4093 DEBUG(10, ("validate_cache_version: %s ok\n", keystr));
4094 return 0;
4097 /***********************************************************************
4098 A list of all possible cache tdb keys with associated validation
4099 functions.
4100 ***********************************************************************/
4102 struct key_val_struct {
4103 const char *keyname;
4104 int (*validate_data_fn)(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, struct tdb_validation_status* state);
4105 } key_val[] = {
4106 {"SEQNUM/", validate_seqnum},
4107 {"NS/", validate_ns},
4108 {"SN/", validate_sn},
4109 {"U/", validate_u},
4110 {"LOC_POL/", validate_loc_pol},
4111 {"PWD_POL/", validate_pwd_pol},
4112 {"CRED/", validate_cred},
4113 {"UL/", validate_ul},
4114 {"GL/", validate_gl},
4115 {"UG/", validate_ug},
4116 {"UA", validate_ua},
4117 {"GM/", validate_gm},
4118 {"DR/", validate_dr},
4119 {"DE/", validate_de},
4120 {"NSS/PWINFO/", validate_pwinfo},
4121 {"TRUSTDOMCACHE/", validate_trustdomcache},
4122 {"NSS/NA/", validate_nss_na},
4123 {"NSS/AN/", validate_nss_an},
4124 {"WINBINDD_OFFLINE", validate_offline},
4125 {"NDR/", validate_ndr},
4126 {WINBINDD_CACHE_VERSION_KEYSTR, validate_cache_version},
4127 {NULL, NULL}
4130 /***********************************************************************
4131 Function to look at every entry in the tdb and validate it as far as
4132 possible.
4133 ***********************************************************************/
4135 static int cache_traverse_validate_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, void *state)
4137 int i;
4138 unsigned int max_key_len = 1024;
4139 struct tdb_validation_status *v_state = (struct tdb_validation_status *)state;
4141 /* Paranoia check. */
4142 if (strncmp("UA/", (const char *)kbuf.dptr, 3) == 0 ||
4143 strncmp("NDR/", (const char *)kbuf.dptr, 4) == 0) {
4144 max_key_len = 1024 * 1024;
4146 if (kbuf.dsize > max_key_len) {
4147 DEBUG(0, ("cache_traverse_validate_fn: key length too large: "
4148 "(%u) > (%u)\n\n",
4149 (unsigned int)kbuf.dsize, (unsigned int)max_key_len));
4150 return 1;
4153 for (i = 0; key_val[i].keyname; i++) {
4154 size_t namelen = strlen(key_val[i].keyname);
4155 if (kbuf.dsize >= namelen && (
4156 strncmp(key_val[i].keyname, (const char *)kbuf.dptr, namelen)) == 0) {
4157 TALLOC_CTX *mem_ctx;
4158 char *keystr;
4159 int ret;
4161 keystr = SMB_MALLOC_ARRAY(char, kbuf.dsize+1);
4162 if (!keystr) {
4163 return 1;
4165 memcpy(keystr, kbuf.dptr, kbuf.dsize);
4166 keystr[kbuf.dsize] = '\0';
4168 mem_ctx = talloc_init("validate_ctx");
4169 if (!mem_ctx) {
4170 SAFE_FREE(keystr);
4171 return 1;
4174 ret = key_val[i].validate_data_fn(mem_ctx, keystr, dbuf,
4175 v_state);
4177 SAFE_FREE(keystr);
4178 talloc_destroy(mem_ctx);
4179 return ret;
4183 DEBUG(0,("cache_traverse_validate_fn: unknown cache entry\nkey :\n"));
4184 dump_data(0, (uint8 *)kbuf.dptr, kbuf.dsize);
4185 DEBUG(0,("data :\n"));
4186 dump_data(0, (uint8 *)dbuf.dptr, dbuf.dsize);
4187 v_state->unknown_key = true;
4188 v_state->success = false;
4189 return 1; /* terminate. */
4192 static void validate_panic(const char *const why)
4194 DEBUG(0,("validating cache: would panic %s\n", why ));
4195 DEBUGADD(0, ("exiting instead (cache validation mode)\n"));
4196 exit(47);
4199 static int wbcache_update_centry_fn(TDB_CONTEXT *tdb,
4200 TDB_DATA key,
4201 TDB_DATA data,
4202 void *state)
4204 uint64_t ctimeout;
4205 TDB_DATA blob;
4207 if (is_non_centry_key(key)) {
4208 return 0;
4211 if (data.dptr == NULL || data.dsize == 0) {
4212 if (tdb_delete(tdb, key) < 0) {
4213 DEBUG(0, ("tdb_delete for [%s] failed!\n",
4214 key.dptr));
4215 return 1;
4219 /* add timeout to blob (uint64_t) */
4220 blob.dsize = data.dsize + 8;
4222 blob.dptr = SMB_XMALLOC_ARRAY(uint8_t, blob.dsize);
4223 if (blob.dptr == NULL) {
4224 return 1;
4226 memset(blob.dptr, 0, blob.dsize);
4228 /* copy status and seqnum */
4229 memcpy(blob.dptr, data.dptr, 8);
4231 /* add timeout */
4232 ctimeout = lp_winbind_cache_time() + time(NULL);
4233 SBVAL(blob.dptr, 8, ctimeout);
4235 /* copy the rest */
4236 memcpy(blob.dptr + 16, data.dptr + 8, data.dsize - 8);
4238 if (tdb_store(tdb, key, blob, TDB_REPLACE) < 0) {
4239 DEBUG(0, ("tdb_store to update [%s] failed!\n",
4240 key.dptr));
4241 SAFE_FREE(blob.dptr);
4242 return 1;
4245 SAFE_FREE(blob.dptr);
4246 return 0;
4249 static bool wbcache_upgrade_v1_to_v2(TDB_CONTEXT *tdb)
4251 int rc;
4253 DEBUG(1, ("Upgrade to version 2 of the winbindd_cache.tdb\n"));
4255 rc = tdb_traverse(tdb, wbcache_update_centry_fn, NULL);
4256 if (rc < 0) {
4257 return false;
4260 return true;
4263 /***********************************************************************
4264 Try and validate every entry in the winbindd cache. If we fail here,
4265 delete the cache tdb and return non-zero.
4266 ***********************************************************************/
4268 int winbindd_validate_cache(void)
4270 int ret = -1;
4271 char *tdb_path = NULL;
4272 TDB_CONTEXT *tdb = NULL;
4273 uint32_t vers_id;
4274 bool ok;
4276 DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
4277 smb_panic_fn = validate_panic;
4279 tdb_path = state_path("winbindd_cache.tdb");
4280 if (tdb_path == NULL) {
4281 goto done;
4284 tdb = tdb_open_log(tdb_path,
4285 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
4286 TDB_INCOMPATIBLE_HASH |
4287 ( lp_winbind_offline_logon()
4288 ? TDB_DEFAULT
4289 : TDB_DEFAULT | TDB_CLEAR_IF_FIRST ),
4290 O_RDWR|O_CREAT,
4291 0600);
4292 if (!tdb) {
4293 DEBUG(0, ("winbindd_validate_cache: "
4294 "error opening/initializing tdb\n"));
4295 goto done;
4298 /* Version check and upgrade code. */
4299 if (!tdb_fetch_uint32(tdb, WINBINDD_CACHE_VERSION_KEYSTR, &vers_id)) {
4300 DEBUG(10, ("Fresh database\n"));
4301 tdb_store_uint32(tdb, WINBINDD_CACHE_VERSION_KEYSTR, WINBINDD_CACHE_VERSION);
4302 vers_id = WINBINDD_CACHE_VERSION;
4305 if (vers_id != WINBINDD_CACHE_VERSION) {
4306 if (vers_id == WINBINDD_CACHE_VER1) {
4307 ok = wbcache_upgrade_v1_to_v2(tdb);
4308 if (!ok) {
4309 DEBUG(10, ("winbindd_validate_cache: upgrade to version 2 failed.\n"));
4310 unlink(tdb_path);
4311 goto done;
4314 tdb_store_uint32(tdb,
4315 WINBINDD_CACHE_VERSION_KEYSTR,
4316 WINBINDD_CACHE_VERSION);
4317 vers_id = WINBINDD_CACHE_VER2;
4321 tdb_close(tdb);
4323 ret = tdb_validate_and_backup(tdb_path, cache_traverse_validate_fn);
4325 if (ret != 0) {
4326 DEBUG(10, ("winbindd_validate_cache: validation not successful.\n"));
4327 DEBUGADD(10, ("removing tdb %s.\n", tdb_path));
4328 unlink(tdb_path);
4331 done:
4332 TALLOC_FREE(tdb_path);
4333 DEBUG(10, ("winbindd_validate_cache: restoring panic function\n"));
4334 smb_panic_fn = smb_panic;
4335 return ret;
4338 /***********************************************************************
4339 Try and validate every entry in the winbindd cache.
4340 ***********************************************************************/
4342 int winbindd_validate_cache_nobackup(void)
4344 int ret = -1;
4345 char *tdb_path;
4347 DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
4348 smb_panic_fn = validate_panic;
4350 tdb_path = state_path("winbindd_cache.tdb");
4351 if (tdb_path == NULL) {
4352 goto err_panic_restore;
4355 if (wcache == NULL || wcache->tdb == NULL) {
4356 ret = tdb_validate_open(tdb_path, cache_traverse_validate_fn);
4357 } else {
4358 ret = tdb_validate(wcache->tdb, cache_traverse_validate_fn);
4361 if (ret != 0) {
4362 DEBUG(10, ("winbindd_validate_cache_nobackup: validation not "
4363 "successful.\n"));
4366 TALLOC_FREE(tdb_path);
4367 err_panic_restore:
4368 DEBUG(10, ("winbindd_validate_cache_nobackup: restoring panic "
4369 "function\n"));
4370 smb_panic_fn = smb_panic;
4371 return ret;
4374 bool winbindd_cache_validate_and_initialize(void)
4376 close_winbindd_cache();
4378 if (lp_winbind_offline_logon()) {
4379 if (winbindd_validate_cache() < 0) {
4380 DEBUG(0, ("winbindd cache tdb corrupt and no backup "
4381 "could be restored.\n"));
4385 return initialize_winbindd_cache();
4388 /*********************************************************************
4389 ********************************************************************/
4391 static bool add_wbdomain_to_tdc_array( struct winbindd_domain *new_dom,
4392 struct winbindd_tdc_domain **domains,
4393 size_t *num_domains )
4395 struct winbindd_tdc_domain *list = NULL;
4396 size_t idx;
4397 int i;
4398 bool set_only = false;
4400 /* don't allow duplicates */
4402 idx = *num_domains;
4403 list = *domains;
4405 for ( i=0; i< (*num_domains); i++ ) {
4406 if ( strequal( new_dom->name, list[i].domain_name ) ) {
4407 DEBUG(10,("add_wbdomain_to_tdc_array: Found existing record for %s\n",
4408 new_dom->name));
4409 idx = i;
4410 set_only = true;
4412 break;
4416 if ( !set_only ) {
4417 if ( !*domains ) {
4418 list = talloc_array( NULL, struct winbindd_tdc_domain, 1 );
4419 idx = 0;
4420 } else {
4421 list = talloc_realloc( *domains, *domains,
4422 struct winbindd_tdc_domain,
4423 (*num_domains)+1);
4424 idx = *num_domains;
4427 ZERO_STRUCT( list[idx] );
4430 if ( !list )
4431 return false;
4433 list[idx].domain_name = talloc_strdup(list, new_dom->name);
4434 if (list[idx].domain_name == NULL) {
4435 return false;
4437 if (new_dom->alt_name != NULL) {
4438 list[idx].dns_name = talloc_strdup(list, new_dom->alt_name);
4439 if (list[idx].dns_name == NULL) {
4440 return false;
4444 if ( !is_null_sid( &new_dom->sid ) ) {
4445 sid_copy( &list[idx].sid, &new_dom->sid );
4446 } else {
4447 sid_copy(&list[idx].sid, &global_sid_NULL);
4450 if ( new_dom->domain_flags != 0x0 )
4451 list[idx].trust_flags = new_dom->domain_flags;
4453 if ( new_dom->domain_type != 0x0 )
4454 list[idx].trust_type = new_dom->domain_type;
4456 if ( new_dom->domain_trust_attribs != 0x0 )
4457 list[idx].trust_attribs = new_dom->domain_trust_attribs;
4459 if ( !set_only ) {
4460 *domains = list;
4461 *num_domains = idx + 1;
4464 return true;
4467 /*********************************************************************
4468 ********************************************************************/
4470 static TDB_DATA make_tdc_key( const char *domain_name )
4472 char *keystr = NULL;
4473 TDB_DATA key = { NULL, 0 };
4475 if ( !domain_name ) {
4476 DEBUG(5,("make_tdc_key: Keyname workgroup is NULL!\n"));
4477 return key;
4480 if (asprintf( &keystr, "TRUSTDOMCACHE/%s", domain_name ) == -1) {
4481 return key;
4483 key = string_term_tdb_data(keystr);
4485 return key;
4488 /*********************************************************************
4489 ********************************************************************/
4491 static int pack_tdc_domains( struct winbindd_tdc_domain *domains,
4492 size_t num_domains,
4493 unsigned char **buf )
4495 unsigned char *buffer = NULL;
4496 int len = 0;
4497 int buflen = 0;
4498 int i = 0;
4500 DEBUG(10,("pack_tdc_domains: Packing %d trusted domains\n",
4501 (int)num_domains));
4503 buflen = 0;
4505 again:
4506 len = 0;
4508 /* Store the number of array items first */
4509 len += tdb_pack( buffer+len, buflen-len, "d",
4510 num_domains );
4512 /* now pack each domain trust record */
4513 for ( i=0; i<num_domains; i++ ) {
4515 fstring tmp;
4517 if ( buflen > 0 ) {
4518 DEBUG(10,("pack_tdc_domains: Packing domain %s (%s)\n",
4519 domains[i].domain_name,
4520 domains[i].dns_name ? domains[i].dns_name : "UNKNOWN" ));
4523 len += tdb_pack( buffer+len, buflen-len, "fffddd",
4524 domains[i].domain_name,
4525 domains[i].dns_name ? domains[i].dns_name : "",
4526 sid_to_fstring(tmp, &domains[i].sid),
4527 domains[i].trust_flags,
4528 domains[i].trust_attribs,
4529 domains[i].trust_type );
4532 if ( buflen < len ) {
4533 SAFE_FREE(buffer);
4534 if ( (buffer = SMB_MALLOC_ARRAY(unsigned char, len)) == NULL ) {
4535 DEBUG(0,("pack_tdc_domains: failed to alloc buffer!\n"));
4536 buflen = -1;
4537 goto done;
4539 buflen = len;
4540 goto again;
4543 *buf = buffer;
4545 done:
4546 return buflen;
4549 /*********************************************************************
4550 ********************************************************************/
4552 static size_t unpack_tdc_domains( unsigned char *buf, int buflen,
4553 struct winbindd_tdc_domain **domains )
4555 fstring domain_name, dns_name, sid_string;
4556 uint32 type, attribs, flags;
4557 int num_domains;
4558 int len = 0;
4559 int i;
4560 struct winbindd_tdc_domain *list = NULL;
4562 /* get the number of domains */
4563 len += tdb_unpack( buf+len, buflen-len, "d", &num_domains);
4564 if ( len == -1 ) {
4565 DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
4566 return 0;
4569 list = talloc_array( NULL, struct winbindd_tdc_domain, num_domains );
4570 if ( !list ) {
4571 DEBUG(0,("unpack_tdc_domains: Failed to talloc() domain list!\n"));
4572 return 0;
4575 for ( i=0; i<num_domains; i++ ) {
4576 int this_len;
4578 this_len = tdb_unpack( buf+len, buflen-len, "fffddd",
4579 domain_name,
4580 dns_name,
4581 sid_string,
4582 &flags,
4583 &attribs,
4584 &type );
4586 if ( this_len == -1 ) {
4587 DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
4588 TALLOC_FREE( list );
4589 return 0;
4591 len += this_len;
4593 DEBUG(11,("unpack_tdc_domains: Unpacking domain %s (%s) "
4594 "SID %s, flags = 0x%x, attribs = 0x%x, type = 0x%x\n",
4595 domain_name, dns_name, sid_string,
4596 flags, attribs, type));
4598 list[i].domain_name = talloc_strdup( list, domain_name );
4599 list[i].dns_name = NULL;
4600 if (dns_name[0] != '\0') {
4601 list[i].dns_name = talloc_strdup(list, dns_name);
4603 if ( !string_to_sid( &(list[i].sid), sid_string ) ) {
4604 DEBUG(10,("unpack_tdc_domains: no SID for domain %s\n",
4605 domain_name));
4607 list[i].trust_flags = flags;
4608 list[i].trust_attribs = attribs;
4609 list[i].trust_type = type;
4612 *domains = list;
4614 return num_domains;
4617 /*********************************************************************
4618 ********************************************************************/
4620 static bool wcache_tdc_store_list( struct winbindd_tdc_domain *domains, size_t num_domains )
4622 TDB_DATA key = make_tdc_key( lp_workgroup() );
4623 TDB_DATA data = { NULL, 0 };
4624 int ret;
4626 if ( !key.dptr )
4627 return false;
4629 /* See if we were asked to delete the cache entry */
4631 if ( !domains ) {
4632 ret = tdb_delete( wcache->tdb, key );
4633 goto done;
4636 data.dsize = pack_tdc_domains( domains, num_domains, &data.dptr );
4638 if ( !data.dptr ) {
4639 ret = -1;
4640 goto done;
4643 ret = tdb_store( wcache->tdb, key, data, 0 );
4645 done:
4646 SAFE_FREE( data.dptr );
4647 SAFE_FREE( key.dptr );
4649 return ( ret == 0 );
4652 /*********************************************************************
4653 ********************************************************************/
4655 bool wcache_tdc_fetch_list( struct winbindd_tdc_domain **domains, size_t *num_domains )
4657 TDB_DATA key = make_tdc_key( lp_workgroup() );
4658 TDB_DATA data = { NULL, 0 };
4660 *domains = NULL;
4661 *num_domains = 0;
4663 if ( !key.dptr )
4664 return false;
4666 data = tdb_fetch_compat( wcache->tdb, key );
4668 SAFE_FREE( key.dptr );
4670 if ( !data.dptr )
4671 return false;
4673 *num_domains = unpack_tdc_domains( data.dptr, data.dsize, domains );
4675 SAFE_FREE( data.dptr );
4677 if ( !*domains )
4678 return false;
4680 return true;
4683 /*********************************************************************
4684 ********************************************************************/
4686 bool wcache_tdc_add_domain( struct winbindd_domain *domain )
4688 struct winbindd_tdc_domain *dom_list = NULL;
4689 size_t num_domains = 0;
4690 bool ret = false;
4692 DEBUG(10,("wcache_tdc_add_domain: Adding domain %s (%s), SID %s, "
4693 "flags = 0x%x, attributes = 0x%x, type = 0x%x\n",
4694 domain->name, domain->alt_name,
4695 sid_string_dbg(&domain->sid),
4696 domain->domain_flags,
4697 domain->domain_trust_attribs,
4698 domain->domain_type));
4700 if ( !init_wcache() ) {
4701 return false;
4704 /* fetch the list */
4706 wcache_tdc_fetch_list( &dom_list, &num_domains );
4708 /* add the new domain */
4710 if ( !add_wbdomain_to_tdc_array( domain, &dom_list, &num_domains ) ) {
4711 goto done;
4714 /* pack the domain */
4716 if ( !wcache_tdc_store_list( dom_list, num_domains ) ) {
4717 goto done;
4720 /* Success */
4722 ret = true;
4723 done:
4724 TALLOC_FREE( dom_list );
4726 return ret;
4729 static struct winbindd_tdc_domain *wcache_tdc_dup_domain(
4730 TALLOC_CTX *mem_ctx, const struct winbindd_tdc_domain *src)
4732 struct winbindd_tdc_domain *dst;
4734 dst = talloc(mem_ctx, struct winbindd_tdc_domain);
4735 if (dst == NULL) {
4736 goto fail;
4738 dst->domain_name = talloc_strdup(dst, src->domain_name);
4739 if (dst->domain_name == NULL) {
4740 goto fail;
4743 dst->dns_name = NULL;
4744 if (src->dns_name != NULL) {
4745 dst->dns_name = talloc_strdup(dst, src->dns_name);
4746 if (dst->dns_name == NULL) {
4747 goto fail;
4751 sid_copy(&dst->sid, &src->sid);
4752 dst->trust_flags = src->trust_flags;
4753 dst->trust_type = src->trust_type;
4754 dst->trust_attribs = src->trust_attribs;
4755 return dst;
4756 fail:
4757 TALLOC_FREE(dst);
4758 return NULL;
4761 /*********************************************************************
4762 ********************************************************************/
4764 struct winbindd_tdc_domain * wcache_tdc_fetch_domain( TALLOC_CTX *ctx, const char *name )
4766 struct winbindd_tdc_domain *dom_list = NULL;
4767 size_t num_domains = 0;
4768 int i;
4769 struct winbindd_tdc_domain *d = NULL;
4771 DEBUG(10,("wcache_tdc_fetch_domain: Searching for domain %s\n", name));
4773 if ( !init_wcache() ) {
4774 return NULL;
4777 /* fetch the list */
4779 wcache_tdc_fetch_list( &dom_list, &num_domains );
4781 for ( i=0; i<num_domains; i++ ) {
4782 if ( strequal(name, dom_list[i].domain_name) ||
4783 strequal(name, dom_list[i].dns_name) )
4785 DEBUG(10,("wcache_tdc_fetch_domain: Found domain %s\n",
4786 name));
4788 d = wcache_tdc_dup_domain(ctx, &dom_list[i]);
4789 break;
4793 TALLOC_FREE( dom_list );
4795 return d;
4798 /*********************************************************************
4799 ********************************************************************/
4801 struct winbindd_tdc_domain*
4802 wcache_tdc_fetch_domainbysid(TALLOC_CTX *ctx,
4803 const struct dom_sid *sid)
4805 struct winbindd_tdc_domain *dom_list = NULL;
4806 size_t num_domains = 0;
4807 int i;
4808 struct winbindd_tdc_domain *d = NULL;
4810 DEBUG(10,("wcache_tdc_fetch_domainbysid: Searching for domain %s\n",
4811 sid_string_dbg(sid)));
4813 if (!init_wcache()) {
4814 return NULL;
4817 /* fetch the list */
4819 wcache_tdc_fetch_list(&dom_list, &num_domains);
4821 for (i = 0; i<num_domains; i++) {
4822 if (dom_sid_equal(sid, &(dom_list[i].sid))) {
4823 DEBUG(10, ("wcache_tdc_fetch_domainbysid: "
4824 "Found domain %s for SID %s\n",
4825 dom_list[i].domain_name,
4826 sid_string_dbg(sid)));
4828 d = wcache_tdc_dup_domain(ctx, &dom_list[i]);
4829 break;
4833 TALLOC_FREE(dom_list);
4835 return d;
4839 /*********************************************************************
4840 ********************************************************************/
4842 void wcache_tdc_clear( void )
4844 if ( !init_wcache() )
4845 return;
4847 wcache_tdc_store_list( NULL, 0 );
4849 return;
4853 /*********************************************************************
4854 ********************************************************************/
4856 static void wcache_save_user_pwinfo(struct winbindd_domain *domain,
4857 NTSTATUS status,
4858 const struct dom_sid *user_sid,
4859 const char *homedir,
4860 const char *shell,
4861 const char *gecos,
4862 uint32 gid)
4864 struct cache_entry *centry;
4865 fstring tmp;
4867 if ( (centry = centry_start(domain, status)) == NULL )
4868 return;
4870 centry_put_string( centry, homedir );
4871 centry_put_string( centry, shell );
4872 centry_put_string( centry, gecos );
4873 centry_put_uint32( centry, gid );
4875 centry_end(centry, "NSS/PWINFO/%s", sid_to_fstring(tmp, user_sid) );
4877 DEBUG(10,("wcache_save_user_pwinfo: %s\n", sid_string_dbg(user_sid) ));
4879 centry_free(centry);
4882 #ifdef HAVE_ADS
4884 NTSTATUS nss_get_info_cached( struct winbindd_domain *domain,
4885 const struct dom_sid *user_sid,
4886 TALLOC_CTX *ctx,
4887 const char **homedir, const char **shell,
4888 const char **gecos, gid_t *p_gid)
4890 struct winbind_cache *cache = get_cache(domain);
4891 struct cache_entry *centry = NULL;
4892 NTSTATUS nt_status;
4893 fstring tmp;
4895 if (!cache->tdb)
4896 goto do_query;
4898 centry = wcache_fetch(cache, domain, "NSS/PWINFO/%s",
4899 sid_to_fstring(tmp, user_sid));
4901 if (!centry)
4902 goto do_query;
4904 *homedir = centry_string( centry, ctx );
4905 *shell = centry_string( centry, ctx );
4906 *gecos = centry_string( centry, ctx );
4907 *p_gid = centry_uint32( centry );
4909 centry_free(centry);
4911 DEBUG(10,("nss_get_info_cached: [Cached] - user_sid %s\n",
4912 sid_string_dbg(user_sid)));
4914 return NT_STATUS_OK;
4916 do_query:
4918 nt_status = nss_get_info( domain->name, user_sid, ctx,
4919 homedir, shell, gecos, p_gid );
4921 DEBUG(10, ("nss_get_info returned %s\n", nt_errstr(nt_status)));
4923 if ( NT_STATUS_IS_OK(nt_status) ) {
4924 DEBUG(10, ("result:\n\thomedir = '%s'\n", *homedir));
4925 DEBUGADD(10, ("\tshell = '%s'\n", *shell));
4926 DEBUGADD(10, ("\tgecos = '%s'\n", *gecos));
4927 DEBUGADD(10, ("\tgid = '%u'\n", (unsigned int)*p_gid));
4929 wcache_save_user_pwinfo( domain, nt_status, user_sid,
4930 *homedir, *shell, *gecos, *p_gid );
4933 if ( NT_STATUS_EQUAL( nt_status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND ) ) {
4934 DEBUG(5,("nss_get_info_cached: Setting domain %s offline\n",
4935 domain->name ));
4936 set_domain_offline( domain );
4939 return nt_status;
4942 #endif
4944 /* the cache backend methods are exposed via this structure */
4945 struct winbindd_methods cache_methods = {
4946 true,
4947 query_user_list,
4948 enum_dom_groups,
4949 enum_local_groups,
4950 name_to_sid,
4951 sid_to_name,
4952 rids_to_names,
4953 query_user,
4954 lookup_usergroups,
4955 lookup_useraliases,
4956 lookup_groupmem,
4957 sequence_number,
4958 lockout_policy,
4959 password_policy,
4960 trusted_domains
4963 static bool wcache_ndr_key(TALLOC_CTX *mem_ctx, const char *domain_name,
4964 uint32_t opnum, const DATA_BLOB *req,
4965 TDB_DATA *pkey)
4967 char *key;
4968 size_t keylen;
4970 key = talloc_asprintf(mem_ctx, "NDR/%s/%d/", domain_name, (int)opnum);
4971 if (key == NULL) {
4972 return false;
4974 keylen = talloc_get_size(key) - 1;
4976 key = talloc_realloc(mem_ctx, key, char, keylen + req->length);
4977 if (key == NULL) {
4978 return false;
4980 memcpy(key + keylen, req->data, req->length);
4982 pkey->dptr = (uint8_t *)key;
4983 pkey->dsize = talloc_get_size(key);
4984 return true;
4987 static bool wcache_opnum_cacheable(uint32_t opnum)
4989 switch (opnum) {
4990 case NDR_WBINT_PING:
4991 case NDR_WBINT_QUERYSEQUENCENUMBER:
4992 case NDR_WBINT_ALLOCATEUID:
4993 case NDR_WBINT_ALLOCATEGID:
4994 case NDR_WBINT_CHECKMACHINEACCOUNT:
4995 case NDR_WBINT_CHANGEMACHINEACCOUNT:
4996 case NDR_WBINT_PINGDC:
4997 return false;
4999 return true;
5002 bool wcache_fetch_ndr(TALLOC_CTX *mem_ctx, struct winbindd_domain *domain,
5003 uint32_t opnum, const DATA_BLOB *req, DATA_BLOB *resp)
5005 TDB_DATA key, data;
5006 bool ret = false;
5008 if (!wcache_opnum_cacheable(opnum) ||
5009 is_my_own_sam_domain(domain) ||
5010 is_builtin_domain(domain)) {
5011 return false;
5014 if (wcache->tdb == NULL) {
5015 return false;
5018 if (!wcache_ndr_key(talloc_tos(), domain->name, opnum, req, &key)) {
5019 return false;
5021 data = tdb_fetch_compat(wcache->tdb, key);
5022 TALLOC_FREE(key.dptr);
5024 if (data.dptr == NULL) {
5025 return false;
5027 if (data.dsize < 12) {
5028 goto fail;
5031 if (!is_domain_offline(domain)) {
5032 uint32_t entry_seqnum, dom_seqnum, last_check;
5033 uint64_t entry_timeout;
5035 if (!wcache_fetch_seqnum(domain->name, &dom_seqnum,
5036 &last_check)) {
5037 goto fail;
5039 entry_seqnum = IVAL(data.dptr, 0);
5040 if (entry_seqnum != dom_seqnum) {
5041 DEBUG(10, ("Entry has wrong sequence number: %d\n",
5042 (int)entry_seqnum));
5043 goto fail;
5045 entry_timeout = BVAL(data.dptr, 4);
5046 if (time(NULL) > entry_timeout) {
5047 DEBUG(10, ("Entry has timed out\n"));
5048 goto fail;
5052 resp->data = (uint8_t *)talloc_memdup(mem_ctx, data.dptr + 12,
5053 data.dsize - 12);
5054 if (resp->data == NULL) {
5055 DEBUG(10, ("talloc failed\n"));
5056 goto fail;
5058 resp->length = data.dsize - 12;
5060 ret = true;
5061 fail:
5062 SAFE_FREE(data.dptr);
5063 return ret;
5066 void wcache_store_ndr(struct winbindd_domain *domain, uint32_t opnum,
5067 const DATA_BLOB *req, const DATA_BLOB *resp)
5069 TDB_DATA key, data;
5070 uint32_t dom_seqnum, last_check;
5071 uint64_t timeout;
5073 if (!wcache_opnum_cacheable(opnum) ||
5074 is_my_own_sam_domain(domain) ||
5075 is_builtin_domain(domain)) {
5076 return;
5079 if (wcache->tdb == NULL) {
5080 return;
5083 if (!wcache_fetch_seqnum(domain->name, &dom_seqnum, &last_check)) {
5084 DEBUG(10, ("could not fetch seqnum for domain %s\n",
5085 domain->name));
5086 return;
5089 if (!wcache_ndr_key(talloc_tos(), domain->name, opnum, req, &key)) {
5090 return;
5093 timeout = time(NULL) + lp_winbind_cache_time();
5095 data.dsize = resp->length + 12;
5096 data.dptr = talloc_array(key.dptr, uint8_t, data.dsize);
5097 if (data.dptr == NULL) {
5098 goto done;
5101 SIVAL(data.dptr, 0, dom_seqnum);
5102 SBVAL(data.dptr, 4, timeout);
5103 memcpy(data.dptr + 12, resp->data, resp->length);
5105 tdb_store(wcache->tdb, key, data, 0);
5107 done:
5108 TALLOC_FREE(key.dptr);
5109 return;