s3: winbind: Ensure we store name2sid with the correct cache sequence number.
[Samba.git] / source3 / winbindd / winbindd_cache.c
blob4a9782abc42278528bfa304d1aeb6c33a2f7b008
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 reconnect_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_t sequence_number;
103 uint64_t timeout;
104 uint8_t *data;
105 uint32_t 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 static char *wcache_path(void)
117 * Data needs to be kept persistent in state directory for
118 * running with "winbindd offline logon".
120 return state_path("winbindd_cache.tdb");
123 /* get the winbind_cache structure */
124 static struct winbind_cache *get_cache(struct winbindd_domain *domain)
126 struct winbind_cache *ret = wcache;
128 /* We have to know what type of domain we are dealing with first. */
130 if (domain->internal) {
131 domain->backend = &builtin_passdb_methods;
134 if (dom_sid_equal(&domain->sid, &global_sid_Builtin)) {
135 domain->initialized = true;
138 if (strequal(domain->name, get_global_sam_name()) &&
139 sid_check_is_our_sam(&domain->sid)) {
140 domain->backend = &sam_passdb_methods;
143 if ( !domain->initialized ) {
144 /* We do not need a connection to an RW DC for cache operation */
145 init_dc_connection(domain, false);
149 OK. Listen up because I'm only going to say this once.
150 We have the following scenarios to consider
151 (a) trusted AD domains on a Samba DC,
152 (b) trusted AD domains and we are joined to a non-kerberos domain
153 (c) trusted AD domains and we are joined to a kerberos (AD) domain
155 For (a) we can always contact the trusted domain using krb5
156 since we have the domain trust account password
158 For (b) we can only use RPC since we have no way of
159 getting a krb5 ticket in our own domain
161 For (c) we can always use krb5 since we have a kerberos trust
163 --jerry
166 if (!domain->backend) {
167 #ifdef HAVE_ADS
168 struct winbindd_domain *our_domain = domain;
170 /* find our domain first so we can figure out if we
171 are joined to a kerberized domain */
173 if ( !domain->primary )
174 our_domain = find_our_domain();
176 if ((our_domain->active_directory || IS_DC)
177 && domain->active_directory
178 && !lp_winbind_rpc_only()) {
179 DEBUG(5,("get_cache: Setting ADS methods for domain %s\n", domain->name));
180 domain->backend = &reconnect_ads_methods;
181 } else {
182 #endif /* HAVE_ADS */
183 DEBUG(5,("get_cache: Setting MS-RPC methods for domain %s\n", domain->name));
184 domain->backend = &reconnect_methods;
185 #ifdef HAVE_ADS
187 #endif /* HAVE_ADS */
190 if (ret)
191 return ret;
193 ret = SMB_XMALLOC_P(struct winbind_cache);
194 ZERO_STRUCTP(ret);
196 wcache = ret;
197 wcache_flush_cache();
199 return ret;
203 free a centry structure
205 static void centry_free(struct cache_entry *centry)
207 if (!centry)
208 return;
209 SAFE_FREE(centry->data);
210 free(centry);
213 static bool centry_check_bytes(struct cache_entry *centry, size_t nbytes)
215 if (centry->len - centry->ofs < nbytes) {
216 DEBUG(0,("centry corruption? needed %u bytes, have %d\n",
217 (unsigned int)nbytes,
218 centry->len - centry->ofs));
219 return false;
221 return true;
225 pull a uint64_t from a cache entry
227 static uint64_t centry_uint64_t(struct cache_entry *centry)
229 uint64_t ret;
231 if (!centry_check_bytes(centry, 8)) {
232 smb_panic_fn("centry_uint64_t");
234 ret = BVAL(centry->data, centry->ofs);
235 centry->ofs += 8;
236 return ret;
240 pull a uint32_t from a cache entry
242 static uint32_t centry_uint32(struct cache_entry *centry)
244 uint32_t ret;
246 if (!centry_check_bytes(centry, 4)) {
247 smb_panic_fn("centry_uint32");
249 ret = IVAL(centry->data, centry->ofs);
250 centry->ofs += 4;
251 return ret;
255 pull a uint16_t from a cache entry
257 static uint16_t centry_uint16(struct cache_entry *centry)
259 uint16_t ret;
260 if (!centry_check_bytes(centry, 2)) {
261 smb_panic_fn("centry_uint16");
263 ret = SVAL(centry->data, centry->ofs);
264 centry->ofs += 2;
265 return ret;
269 pull a uint8_t from a cache entry
271 static uint8_t centry_uint8(struct cache_entry *centry)
273 uint8_t ret;
274 if (!centry_check_bytes(centry, 1)) {
275 smb_panic_fn("centry_uint8");
277 ret = CVAL(centry->data, centry->ofs);
278 centry->ofs += 1;
279 return ret;
283 pull a NTTIME from a cache entry
285 static NTTIME centry_nttime(struct cache_entry *centry)
287 NTTIME ret;
288 if (!centry_check_bytes(centry, 8)) {
289 smb_panic_fn("centry_nttime");
291 ret = IVAL(centry->data, centry->ofs);
292 centry->ofs += 4;
293 ret += (uint64_t)IVAL(centry->data, centry->ofs) << 32;
294 centry->ofs += 4;
295 return ret;
299 pull a time_t from a cache entry. time_t stored portably as a 64-bit time.
301 static time_t centry_time(struct cache_entry *centry)
303 return (time_t)centry_nttime(centry);
306 /* pull a string from a cache entry, using the supplied
307 talloc context
309 static char *centry_string(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
311 uint32_t len;
312 char *ret;
314 len = centry_uint8(centry);
316 if (len == 0xFF) {
317 /* a deliberate NULL string */
318 return NULL;
321 if (!centry_check_bytes(centry, (size_t)len)) {
322 smb_panic_fn("centry_string");
325 ret = talloc_array(mem_ctx, char, len+1);
326 if (!ret) {
327 smb_panic_fn("centry_string out of memory\n");
329 memcpy(ret,centry->data + centry->ofs, len);
330 ret[len] = 0;
331 centry->ofs += len;
332 return ret;
335 /* pull a hash16 from a cache entry, using the supplied
336 talloc context
338 static char *centry_hash16(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
340 uint32_t len;
341 char *ret;
343 len = centry_uint8(centry);
345 if (len != 16) {
346 DEBUG(0,("centry corruption? hash len (%u) != 16\n",
347 len ));
348 return NULL;
351 if (!centry_check_bytes(centry, 16)) {
352 return NULL;
355 ret = talloc_array(mem_ctx, char, 16);
356 if (!ret) {
357 smb_panic_fn("centry_hash out of memory\n");
359 memcpy(ret,centry->data + centry->ofs, 16);
360 centry->ofs += 16;
361 return ret;
364 /* pull a sid from a cache entry, using the supplied
365 talloc context
367 static bool centry_sid(struct cache_entry *centry, struct dom_sid *sid)
369 char *sid_string;
370 bool ret;
372 sid_string = centry_string(centry, talloc_tos());
373 if (sid_string == NULL) {
374 return false;
376 ret = string_to_sid(sid, sid_string);
377 TALLOC_FREE(sid_string);
378 return ret;
383 pull a NTSTATUS from a cache entry
385 static NTSTATUS centry_ntstatus(struct cache_entry *centry)
387 NTSTATUS status;
389 status = NT_STATUS(centry_uint32(centry));
390 return status;
394 /* the server is considered down if it can't give us a sequence number */
395 static bool wcache_server_down(struct winbindd_domain *domain)
397 bool ret;
399 if (!wcache->tdb)
400 return false;
402 ret = (domain->sequence_number == DOM_SEQUENCE_NONE);
404 if (ret)
405 DEBUG(10,("wcache_server_down: server for Domain %s down\n",
406 domain->name ));
407 return ret;
410 struct wcache_seqnum_state {
411 uint32_t *seqnum;
412 uint32_t *last_seq_check;
415 static int wcache_seqnum_parser(TDB_DATA key, TDB_DATA data,
416 void *private_data)
418 struct wcache_seqnum_state *state = private_data;
420 if (data.dsize != 8) {
421 DEBUG(10, ("wcache_fetch_seqnum: invalid data size %d\n",
422 (int)data.dsize));
423 return -1;
426 *state->seqnum = IVAL(data.dptr, 0);
427 *state->last_seq_check = IVAL(data.dptr, 4);
428 return 0;
431 static bool wcache_fetch_seqnum(const char *domain_name, uint32_t *seqnum,
432 uint32_t *last_seq_check)
434 struct wcache_seqnum_state state = {
435 .seqnum = seqnum, .last_seq_check = last_seq_check
437 size_t len = strlen(domain_name);
438 char keystr[len+8];
439 TDB_DATA key = { .dptr = (uint8_t *)keystr, .dsize = sizeof(keystr) };
440 int ret;
442 if (wcache->tdb == NULL) {
443 DEBUG(10,("wcache_fetch_seqnum: tdb == NULL\n"));
444 return false;
447 snprintf(keystr, sizeof(keystr), "SEQNUM/%s", domain_name);
449 ret = tdb_parse_record(wcache->tdb, key, wcache_seqnum_parser,
450 &state);
451 return (ret == 0);
454 static NTSTATUS fetch_cache_seqnum( struct winbindd_domain *domain, time_t now )
456 uint32_t last_check, time_diff;
458 if (!wcache_fetch_seqnum(domain->name, &domain->sequence_number,
459 &last_check)) {
460 return NT_STATUS_UNSUCCESSFUL;
462 domain->last_seq_check = last_check;
464 /* have we expired? */
466 time_diff = now - domain->last_seq_check;
467 if ( time_diff > lp_winbind_cache_time() ) {
468 DEBUG(10,("fetch_cache_seqnum: timeout [%s][%u @ %u]\n",
469 domain->name, domain->sequence_number,
470 (uint32_t)domain->last_seq_check));
471 return NT_STATUS_UNSUCCESSFUL;
474 DEBUG(10,("fetch_cache_seqnum: success [%s][%u @ %u]\n",
475 domain->name, domain->sequence_number,
476 (uint32_t)domain->last_seq_check));
478 return NT_STATUS_OK;
481 bool wcache_store_seqnum(const char *domain_name, uint32_t seqnum,
482 time_t last_seq_check)
484 size_t len = strlen(domain_name);
485 char keystr[len+8];
486 TDB_DATA key = { .dptr = (uint8_t *)keystr, .dsize = sizeof(keystr) };
487 uint8_t buf[8];
488 int ret;
490 if (wcache->tdb == NULL) {
491 DEBUG(10, ("wcache_store_seqnum: wcache->tdb == NULL\n"));
492 return false;
495 snprintf(keystr, sizeof(keystr), "SEQNUM/%s", domain_name);
497 SIVAL(buf, 0, seqnum);
498 SIVAL(buf, 4, last_seq_check);
500 ret = tdb_store(wcache->tdb, key, make_tdb_data(buf, sizeof(buf)),
501 TDB_REPLACE);
502 if (ret != 0) {
503 DEBUG(10, ("tdb_store_bystring failed: %s\n",
504 tdb_errorstr(wcache->tdb)));
505 return false;
508 DEBUG(10, ("wcache_store_seqnum: success [%s][%u @ %u]\n",
509 domain_name, seqnum, (unsigned)last_seq_check));
511 return true;
514 static bool store_cache_seqnum( struct winbindd_domain *domain )
516 return wcache_store_seqnum(domain->name, domain->sequence_number,
517 domain->last_seq_check);
521 refresh the domain sequence number on timeout.
524 static void refresh_sequence_number(struct winbindd_domain *domain)
526 NTSTATUS status;
527 unsigned time_diff;
528 time_t t = time(NULL);
529 unsigned cache_time = lp_winbind_cache_time();
531 if (is_domain_offline(domain)) {
532 return;
535 get_cache( domain );
537 #if 0 /* JERRY -- disable as the default cache time is now 5 minutes */
538 /* trying to reconnect is expensive, don't do it too often */
539 if (domain->sequence_number == DOM_SEQUENCE_NONE) {
540 cache_time *= 8;
542 #endif
544 time_diff = t - domain->last_seq_check;
546 /* see if we have to refetch the domain sequence number */
547 if ((time_diff < cache_time) &&
548 (domain->sequence_number != DOM_SEQUENCE_NONE) &&
549 NT_STATUS_IS_OK(domain->last_status)) {
550 DEBUG(10, ("refresh_sequence_number: %s time ok\n", domain->name));
551 goto done;
554 /* try to get the sequence number from the tdb cache first */
555 /* this will update the timestamp as well */
557 status = fetch_cache_seqnum( domain, t );
558 if (NT_STATUS_IS_OK(status) &&
559 (domain->sequence_number != DOM_SEQUENCE_NONE) &&
560 NT_STATUS_IS_OK(domain->last_status)) {
561 goto done;
564 /* important! make sure that we know if this is a native
565 mode domain or not. And that we can contact it. */
567 if ( winbindd_can_contact_domain( domain ) ) {
568 status = domain->backend->sequence_number(domain,
569 &domain->sequence_number);
570 } else {
571 /* just use the current time */
572 status = NT_STATUS_OK;
573 domain->sequence_number = time(NULL);
577 /* the above call could have set our domain->backend to NULL when
578 * coming from offline to online mode, make sure to reinitialize the
579 * backend - Guenther */
580 get_cache( domain );
582 if (!NT_STATUS_IS_OK(status)) {
583 DEBUG(10,("refresh_sequence_number: failed with %s\n", nt_errstr(status)));
584 domain->sequence_number = DOM_SEQUENCE_NONE;
587 domain->last_status = status;
588 domain->last_seq_check = time(NULL);
590 /* save the new sequence number in the cache */
591 store_cache_seqnum( domain );
593 done:
594 DEBUG(10, ("refresh_sequence_number: %s seq number is now %d\n",
595 domain->name, domain->sequence_number));
597 return;
601 decide if a cache entry has expired
603 static bool centry_expired(struct winbindd_domain *domain, const char *keystr, struct cache_entry *centry)
605 /* If we've been told to be offline - stay in that state... */
606 if (lp_winbind_offline_logon() && global_winbindd_offline_state) {
607 DEBUG(10,("centry_expired: Key %s for domain %s valid as winbindd is globally offline.\n",
608 keystr, domain->name ));
609 return false;
612 /* when the domain is offline return the cached entry.
613 * This deals with transient offline states... */
615 if (!domain->online) {
616 DEBUG(10,("centry_expired: Key %s for domain %s valid as domain is offline.\n",
617 keystr, domain->name ));
618 return false;
621 /* if the server is OK and our cache entry came from when it was down then
622 the entry is invalid */
623 if ((domain->sequence_number != DOM_SEQUENCE_NONE) &&
624 (centry->sequence_number == DOM_SEQUENCE_NONE)) {
625 DEBUG(10,("centry_expired: Key %s for domain %s invalid sequence.\n",
626 keystr, domain->name ));
627 return true;
630 /* if the server is down or the cache entry is not older than the
631 current sequence number or it did not timeout then it is OK */
632 if (wcache_server_down(domain)
633 || ((centry->sequence_number == domain->sequence_number)
634 && (centry->timeout > time(NULL)))) {
635 DEBUG(10,("centry_expired: Key %s for domain %s is good.\n",
636 keystr, domain->name ));
637 return false;
640 DEBUG(10,("centry_expired: Key %s for domain %s expired\n",
641 keystr, domain->name ));
643 /* it's expired */
644 return true;
647 static struct cache_entry *wcache_fetch_raw(char *kstr)
649 TDB_DATA data;
650 struct cache_entry *centry;
651 TDB_DATA key;
653 key = string_tdb_data(kstr);
654 data = tdb_fetch(wcache->tdb, key);
655 if (!data.dptr) {
656 /* a cache miss */
657 return NULL;
660 centry = SMB_XMALLOC_P(struct cache_entry);
661 centry->data = (unsigned char *)data.dptr;
662 centry->len = data.dsize;
663 centry->ofs = 0;
665 if (centry->len < 16) {
666 /* huh? corrupt cache? */
667 DEBUG(10,("wcache_fetch_raw: Corrupt cache for key %s "
668 "(len < 16)?\n", kstr));
669 centry_free(centry);
670 return NULL;
673 centry->status = centry_ntstatus(centry);
674 centry->sequence_number = centry_uint32(centry);
675 centry->timeout = centry_uint64_t(centry);
677 return centry;
680 static bool is_my_own_sam_domain(struct winbindd_domain *domain)
682 if (strequal(domain->name, get_global_sam_name()) &&
683 sid_check_is_our_sam(&domain->sid)) {
684 return true;
687 return false;
690 static bool is_builtin_domain(struct winbindd_domain *domain)
692 if (strequal(domain->name, "BUILTIN") &&
693 sid_check_is_builtin(&domain->sid)) {
694 return true;
697 return false;
701 fetch an entry from the cache, with a varargs key. auto-fetch the sequence
702 number and return status
704 static struct cache_entry *wcache_fetch(struct winbind_cache *cache,
705 struct winbindd_domain *domain,
706 const char *format, ...) PRINTF_ATTRIBUTE(3,4);
707 static struct cache_entry *wcache_fetch(struct winbind_cache *cache,
708 struct winbindd_domain *domain,
709 const char *format, ...)
711 va_list ap;
712 char *kstr;
713 struct cache_entry *centry;
715 if (!winbindd_use_cache() ||
716 is_my_own_sam_domain(domain) ||
717 is_builtin_domain(domain)) {
718 return NULL;
721 refresh_sequence_number(domain);
723 va_start(ap, format);
724 smb_xvasprintf(&kstr, format, ap);
725 va_end(ap);
727 centry = wcache_fetch_raw(kstr);
728 if (centry == NULL) {
729 free(kstr);
730 return NULL;
733 if (centry_expired(domain, kstr, centry)) {
735 DEBUG(10,("wcache_fetch: entry %s expired for domain %s\n",
736 kstr, domain->name ));
738 centry_free(centry);
739 free(kstr);
740 return NULL;
743 DEBUG(10,("wcache_fetch: returning entry %s for domain %s\n",
744 kstr, domain->name ));
746 free(kstr);
747 return centry;
750 static void wcache_delete(const char *format, ...) PRINTF_ATTRIBUTE(1,2);
751 static void wcache_delete(const char *format, ...)
753 va_list ap;
754 char *kstr;
755 TDB_DATA key;
757 va_start(ap, format);
758 smb_xvasprintf(&kstr, format, ap);
759 va_end(ap);
761 key = string_tdb_data(kstr);
763 tdb_delete(wcache->tdb, key);
764 free(kstr);
768 make sure we have at least len bytes available in a centry
770 static void centry_expand(struct cache_entry *centry, uint32_t len)
772 if (centry->len - centry->ofs >= len)
773 return;
774 centry->len *= 2;
775 centry->data = SMB_REALLOC_ARRAY(centry->data, unsigned char,
776 centry->len);
777 if (!centry->data) {
778 DEBUG(0,("out of memory: needed %d bytes in centry_expand\n", centry->len));
779 smb_panic_fn("out of memory in centry_expand");
784 push a uint64_t into a centry
786 static void centry_put_uint64_t(struct cache_entry *centry, uint64_t v)
788 centry_expand(centry, 8);
789 SBVAL(centry->data, centry->ofs, v);
790 centry->ofs += 8;
794 push a uint32_t into a centry
796 static void centry_put_uint32(struct cache_entry *centry, uint32_t v)
798 centry_expand(centry, 4);
799 SIVAL(centry->data, centry->ofs, v);
800 centry->ofs += 4;
804 push a uint16_t into a centry
806 static void centry_put_uint16(struct cache_entry *centry, uint16_t v)
808 centry_expand(centry, 2);
809 SSVAL(centry->data, centry->ofs, v);
810 centry->ofs += 2;
814 push a uint8_t into a centry
816 static void centry_put_uint8(struct cache_entry *centry, uint8_t v)
818 centry_expand(centry, 1);
819 SCVAL(centry->data, centry->ofs, v);
820 centry->ofs += 1;
824 push a string into a centry
826 static void centry_put_string(struct cache_entry *centry, const char *s)
828 int len;
830 if (!s) {
831 /* null strings are marked as len 0xFFFF */
832 centry_put_uint8(centry, 0xFF);
833 return;
836 len = strlen(s);
837 /* can't handle more than 254 char strings. Truncating is probably best */
838 if (len > 254) {
839 DEBUG(10,("centry_put_string: truncating len (%d) to: 254\n", len));
840 len = 254;
842 centry_put_uint8(centry, len);
843 centry_expand(centry, len);
844 memcpy(centry->data + centry->ofs, s, len);
845 centry->ofs += len;
849 push a 16 byte hash into a centry - treat as 16 byte string.
851 static void centry_put_hash16(struct cache_entry *centry, const uint8_t val[16])
853 centry_put_uint8(centry, 16);
854 centry_expand(centry, 16);
855 memcpy(centry->data + centry->ofs, val, 16);
856 centry->ofs += 16;
859 static void centry_put_sid(struct cache_entry *centry, const struct dom_sid *sid)
861 fstring sid_string;
862 centry_put_string(centry, sid_to_fstring(sid_string, sid));
867 put NTSTATUS into a centry
869 static void centry_put_ntstatus(struct cache_entry *centry, NTSTATUS status)
871 uint32_t status_value = NT_STATUS_V(status);
872 centry_put_uint32(centry, status_value);
877 push a NTTIME into a centry
879 static void centry_put_nttime(struct cache_entry *centry, NTTIME nt)
881 centry_expand(centry, 8);
882 SIVAL(centry->data, centry->ofs, nt & 0xFFFFFFFF);
883 centry->ofs += 4;
884 SIVAL(centry->data, centry->ofs, nt >> 32);
885 centry->ofs += 4;
889 push a time_t into a centry - use a 64 bit size.
890 NTTIME here is being used as a convenient 64-bit size.
892 static void centry_put_time(struct cache_entry *centry, time_t t)
894 NTTIME nt = (NTTIME)t;
895 centry_put_nttime(centry, nt);
899 start a centry for output. When finished, call centry_end()
901 static struct cache_entry *centry_start(struct winbindd_domain *domain,
902 NTSTATUS status)
904 struct cache_entry *centry;
906 if (!wcache->tdb)
907 return NULL;
909 centry = SMB_XMALLOC_P(struct cache_entry);
911 centry->len = 8192; /* reasonable default */
912 centry->data = SMB_XMALLOC_ARRAY(uint8_t, centry->len);
913 centry->ofs = 0;
914 centry->sequence_number = domain->sequence_number;
915 centry->timeout = lp_winbind_cache_time() + time(NULL);
916 centry_put_ntstatus(centry, status);
917 centry_put_uint32(centry, centry->sequence_number);
918 centry_put_uint64_t(centry, centry->timeout);
919 return centry;
923 finish a centry and write it to the tdb
925 static void centry_end(struct cache_entry *centry, const char *format, ...) PRINTF_ATTRIBUTE(2,3);
926 static void centry_end(struct cache_entry *centry, const char *format, ...)
928 va_list ap;
929 char *kstr;
930 TDB_DATA key, data;
932 if (!winbindd_use_cache()) {
933 return;
936 va_start(ap, format);
937 smb_xvasprintf(&kstr, format, ap);
938 va_end(ap);
940 key = string_tdb_data(kstr);
941 data.dptr = centry->data;
942 data.dsize = centry->ofs;
944 tdb_store(wcache->tdb, key, data, TDB_REPLACE);
945 free(kstr);
948 static void wcache_save_name_to_sid(struct winbindd_domain *domain,
949 NTSTATUS status, const char *domain_name,
950 const char *name, const struct dom_sid *sid,
951 enum lsa_SidType type)
953 struct cache_entry *centry;
954 fstring uname;
956 centry = centry_start(domain, status);
957 if (!centry)
958 return;
960 if ((domain_name == NULL) || (domain_name[0] == '\0')) {
961 struct winbindd_domain *mydomain =
962 find_domain_from_sid_noinit(sid);
963 if (mydomain != NULL) {
964 domain_name = mydomain->name;
968 centry_put_uint32(centry, type);
969 centry_put_sid(centry, sid);
970 fstrcpy(uname, name);
971 (void)strupper_m(uname);
972 centry_end(centry, "NS/%s/%s", domain_name, uname);
973 DEBUG(10,("wcache_save_name_to_sid: %s\\%s -> %s (%s)\n", domain_name,
974 uname, sid_string_dbg(sid), nt_errstr(status)));
975 centry_free(centry);
978 static void wcache_save_sid_to_name(struct winbindd_domain *domain, NTSTATUS status,
979 const struct dom_sid *sid, const char *domain_name, const char *name, enum lsa_SidType type)
981 struct cache_entry *centry;
982 fstring sid_string;
984 centry = centry_start(domain, status);
985 if (!centry)
986 return;
988 if ((domain_name == NULL) || (domain_name[0] == '\0')) {
989 struct winbindd_domain *mydomain =
990 find_domain_from_sid_noinit(sid);
991 if (mydomain != NULL) {
992 domain_name = mydomain->name;
996 if (NT_STATUS_IS_OK(status)) {
997 centry_put_uint32(centry, type);
998 centry_put_string(centry, domain_name);
999 centry_put_string(centry, name);
1002 centry_end(centry, "SN/%s", sid_to_fstring(sid_string, sid));
1003 DEBUG(10,("wcache_save_sid_to_name: %s -> %s\\%s (%s)\n", sid_string,
1004 domain_name, name, nt_errstr(status)));
1005 centry_free(centry);
1009 static void wcache_save_user(struct winbindd_domain *domain, NTSTATUS status,
1010 struct wbint_userinfo *info)
1012 struct cache_entry *centry;
1013 fstring sid_string;
1015 if (is_null_sid(&info->user_sid)) {
1016 return;
1019 centry = centry_start(domain, status);
1020 if (!centry)
1021 return;
1022 centry_put_string(centry, info->acct_name);
1023 centry_put_string(centry, info->full_name);
1024 centry_put_string(centry, info->homedir);
1025 centry_put_string(centry, info->shell);
1026 centry_put_uint32(centry, info->primary_gid);
1027 centry_put_sid(centry, &info->user_sid);
1028 centry_put_sid(centry, &info->group_sid);
1029 centry_end(centry, "U/%s", sid_to_fstring(sid_string,
1030 &info->user_sid));
1031 DEBUG(10,("wcache_save_user: %s (acct_name %s)\n", sid_string, info->acct_name));
1032 centry_free(centry);
1035 static void wcache_save_lockout_policy(struct winbindd_domain *domain,
1036 NTSTATUS status,
1037 struct samr_DomInfo12 *lockout_policy)
1039 struct cache_entry *centry;
1041 centry = centry_start(domain, status);
1042 if (!centry)
1043 return;
1045 centry_put_nttime(centry, lockout_policy->lockout_duration);
1046 centry_put_nttime(centry, lockout_policy->lockout_window);
1047 centry_put_uint16(centry, lockout_policy->lockout_threshold);
1049 centry_end(centry, "LOC_POL/%s", domain->name);
1051 DEBUG(10,("wcache_save_lockout_policy: %s\n", domain->name));
1053 centry_free(centry);
1058 static void wcache_save_password_policy(struct winbindd_domain *domain,
1059 NTSTATUS status,
1060 struct samr_DomInfo1 *policy)
1062 struct cache_entry *centry;
1064 centry = centry_start(domain, status);
1065 if (!centry)
1066 return;
1068 centry_put_uint16(centry, policy->min_password_length);
1069 centry_put_uint16(centry, policy->password_history_length);
1070 centry_put_uint32(centry, policy->password_properties);
1071 centry_put_nttime(centry, policy->max_password_age);
1072 centry_put_nttime(centry, policy->min_password_age);
1074 centry_end(centry, "PWD_POL/%s", domain->name);
1076 DEBUG(10,("wcache_save_password_policy: %s\n", domain->name));
1078 centry_free(centry);
1081 /***************************************************************************
1082 ***************************************************************************/
1084 static void wcache_save_username_alias(struct winbindd_domain *domain,
1085 NTSTATUS status,
1086 const char *name, const char *alias)
1088 struct cache_entry *centry;
1089 fstring uname;
1091 if ( (centry = centry_start(domain, status)) == NULL )
1092 return;
1094 centry_put_string( centry, alias );
1096 fstrcpy(uname, name);
1097 (void)strupper_m(uname);
1098 centry_end(centry, "NSS/NA/%s", uname);
1100 DEBUG(10,("wcache_save_username_alias: %s -> %s\n", name, alias ));
1102 centry_free(centry);
1105 static void wcache_save_alias_username(struct winbindd_domain *domain,
1106 NTSTATUS status,
1107 const char *alias, const char *name)
1109 struct cache_entry *centry;
1110 fstring uname;
1112 if ( (centry = centry_start(domain, status)) == NULL )
1113 return;
1115 centry_put_string( centry, name );
1117 fstrcpy(uname, alias);
1118 (void)strupper_m(uname);
1119 centry_end(centry, "NSS/AN/%s", uname);
1121 DEBUG(10,("wcache_save_alias_username: %s -> %s\n", alias, name ));
1123 centry_free(centry);
1126 /***************************************************************************
1127 ***************************************************************************/
1129 NTSTATUS resolve_username_to_alias( TALLOC_CTX *mem_ctx,
1130 struct winbindd_domain *domain,
1131 const char *name, char **alias )
1133 struct winbind_cache *cache = get_cache(domain);
1134 struct cache_entry *centry = NULL;
1135 NTSTATUS status;
1136 char *upper_name;
1138 if ( domain->internal )
1139 return NT_STATUS_NOT_SUPPORTED;
1141 if (!cache->tdb)
1142 goto do_query;
1144 upper_name = talloc_strdup(mem_ctx, name);
1145 if (upper_name == NULL) {
1146 return NT_STATUS_NO_MEMORY;
1148 if (!strupper_m(upper_name)) {
1149 talloc_free(upper_name);
1150 return NT_STATUS_INVALID_PARAMETER;
1153 centry = wcache_fetch(cache, domain, "NSS/NA/%s", upper_name);
1155 talloc_free(upper_name);
1157 if (!centry)
1158 goto do_query;
1160 status = centry->status;
1162 if (!NT_STATUS_IS_OK(status)) {
1163 centry_free(centry);
1164 return status;
1167 *alias = centry_string( centry, mem_ctx );
1169 centry_free(centry);
1171 DEBUG(10,("resolve_username_to_alias: [Cached] - mapped %s to %s\n",
1172 name, *alias ? *alias : "(none)"));
1174 return (*alias) ? NT_STATUS_OK : NT_STATUS_OBJECT_NAME_NOT_FOUND;
1176 do_query:
1178 /* If its not in cache and we are offline, then fail */
1180 if ( get_global_winbindd_state_offline() || !domain->online ) {
1181 DEBUG(8,("resolve_username_to_alias: rejecting query "
1182 "in offline mode\n"));
1183 return NT_STATUS_NOT_FOUND;
1186 status = nss_map_to_alias( mem_ctx, domain->name, name, alias );
1188 if ( NT_STATUS_IS_OK( status ) ) {
1189 wcache_save_username_alias(domain, status, name, *alias);
1192 if ( NT_STATUS_EQUAL( status, NT_STATUS_NONE_MAPPED ) ) {
1193 wcache_save_username_alias(domain, status, name, "(NULL)");
1196 DEBUG(5,("resolve_username_to_alias: backend query returned %s\n",
1197 nt_errstr(status)));
1199 if ( NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ) {
1200 set_domain_offline( domain );
1203 return status;
1206 /***************************************************************************
1207 ***************************************************************************/
1209 NTSTATUS resolve_alias_to_username( TALLOC_CTX *mem_ctx,
1210 struct winbindd_domain *domain,
1211 const char *alias, char **name )
1213 struct winbind_cache *cache = get_cache(domain);
1214 struct cache_entry *centry = NULL;
1215 NTSTATUS status;
1216 char *upper_name;
1218 if ( domain->internal )
1219 return NT_STATUS_NOT_SUPPORTED;
1221 if (!cache->tdb)
1222 goto do_query;
1224 upper_name = talloc_strdup(mem_ctx, alias);
1225 if (upper_name == NULL) {
1226 return NT_STATUS_NO_MEMORY;
1228 if (!strupper_m(upper_name)) {
1229 talloc_free(upper_name);
1230 return NT_STATUS_INVALID_PARAMETER;
1233 centry = wcache_fetch(cache, domain, "NSS/AN/%s", upper_name);
1235 talloc_free(upper_name);
1237 if (!centry)
1238 goto do_query;
1240 status = centry->status;
1242 if (!NT_STATUS_IS_OK(status)) {
1243 centry_free(centry);
1244 return status;
1247 *name = centry_string( centry, mem_ctx );
1249 centry_free(centry);
1251 DEBUG(10,("resolve_alias_to_username: [Cached] - mapped %s to %s\n",
1252 alias, *name ? *name : "(none)"));
1254 return (*name) ? NT_STATUS_OK : NT_STATUS_OBJECT_NAME_NOT_FOUND;
1256 do_query:
1258 /* If its not in cache and we are offline, then fail */
1260 if ( get_global_winbindd_state_offline() || !domain->online ) {
1261 DEBUG(8,("resolve_alias_to_username: rejecting query "
1262 "in offline mode\n"));
1263 return NT_STATUS_NOT_FOUND;
1266 /* an alias cannot contain a domain prefix or '@' */
1268 if (strchr(alias, '\\') || strchr(alias, '@')) {
1269 DEBUG(10,("resolve_alias_to_username: skipping fully "
1270 "qualified name %s\n", alias));
1271 return NT_STATUS_OBJECT_NAME_INVALID;
1274 status = nss_map_from_alias( mem_ctx, domain->name, alias, name );
1276 if ( NT_STATUS_IS_OK( status ) ) {
1277 wcache_save_alias_username( domain, status, alias, *name );
1280 if (NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) {
1281 wcache_save_alias_username(domain, status, alias, "(NULL)");
1284 DEBUG(5,("resolve_alias_to_username: backend query returned %s\n",
1285 nt_errstr(status)));
1287 if ( NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ) {
1288 set_domain_offline( domain );
1291 return status;
1294 NTSTATUS wcache_cached_creds_exist(struct winbindd_domain *domain, const struct dom_sid *sid)
1296 struct winbind_cache *cache = get_cache(domain);
1297 TDB_DATA data;
1298 fstring key_str, tmp;
1299 uint32_t rid;
1301 if (!cache->tdb) {
1302 return NT_STATUS_INTERNAL_DB_ERROR;
1305 if (is_null_sid(sid)) {
1306 return NT_STATUS_INVALID_SID;
1309 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1310 return NT_STATUS_INVALID_SID;
1313 fstr_sprintf(key_str, "CRED/%s", sid_to_fstring(tmp, sid));
1315 data = tdb_fetch(cache->tdb, string_tdb_data(key_str));
1316 if (!data.dptr) {
1317 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1320 SAFE_FREE(data.dptr);
1321 return NT_STATUS_OK;
1324 /* Lookup creds for a SID - copes with old (unsalted) creds as well
1325 as new salted ones. */
1327 NTSTATUS wcache_get_creds(struct winbindd_domain *domain,
1328 TALLOC_CTX *mem_ctx,
1329 const struct dom_sid *sid,
1330 const uint8_t **cached_nt_pass,
1331 const uint8_t **cached_salt)
1333 struct winbind_cache *cache = get_cache(domain);
1334 struct cache_entry *centry = NULL;
1335 NTSTATUS status;
1336 uint32_t rid;
1337 fstring tmp;
1339 if (!cache->tdb) {
1340 return NT_STATUS_INTERNAL_DB_ERROR;
1343 if (is_null_sid(sid)) {
1344 return NT_STATUS_INVALID_SID;
1347 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1348 return NT_STATUS_INVALID_SID;
1351 /* Try and get a salted cred first. If we can't
1352 fall back to an unsalted cred. */
1354 centry = wcache_fetch(cache, domain, "CRED/%s",
1355 sid_to_fstring(tmp, sid));
1356 if (!centry) {
1357 DEBUG(10,("wcache_get_creds: entry for [CRED/%s] not found\n",
1358 sid_string_dbg(sid)));
1359 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1363 * We don't use the time element at this moment,
1364 * but we have to consume it, so that we don't
1365 * neet to change the disk format of the cache.
1367 (void)centry_time(centry);
1369 /* In the salted case this isn't actually the nt_hash itself,
1370 but the MD5 of the salt + nt_hash. Let the caller
1371 sort this out. It can tell as we only return the cached_salt
1372 if we are returning a salted cred. */
1374 *cached_nt_pass = (const uint8_t *)centry_hash16(centry, mem_ctx);
1375 if (*cached_nt_pass == NULL) {
1376 fstring sidstr;
1378 sid_to_fstring(sidstr, sid);
1380 /* Bad (old) cred cache. Delete and pretend we
1381 don't have it. */
1382 DEBUG(0,("wcache_get_creds: bad entry for [CRED/%s] - deleting\n",
1383 sidstr));
1384 wcache_delete("CRED/%s", sidstr);
1385 centry_free(centry);
1386 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1389 /* We only have 17 bytes more data in the salted cred case. */
1390 if (centry->len - centry->ofs == 17) {
1391 *cached_salt = (const uint8_t *)centry_hash16(centry, mem_ctx);
1392 } else {
1393 *cached_salt = NULL;
1396 dump_data_pw("cached_nt_pass", *cached_nt_pass, NT_HASH_LEN);
1397 if (*cached_salt) {
1398 dump_data_pw("cached_salt", *cached_salt, NT_HASH_LEN);
1401 status = centry->status;
1403 DEBUG(10,("wcache_get_creds: [Cached] - cached creds for user %s status: %s\n",
1404 sid_string_dbg(sid), nt_errstr(status) ));
1406 centry_free(centry);
1407 return status;
1410 /* Store creds for a SID - only writes out new salted ones. */
1412 NTSTATUS wcache_save_creds(struct winbindd_domain *domain,
1413 const struct dom_sid *sid,
1414 const uint8_t nt_pass[NT_HASH_LEN])
1416 struct cache_entry *centry;
1417 fstring sid_string;
1418 uint32_t rid;
1419 uint8_t cred_salt[NT_HASH_LEN];
1420 uint8_t salted_hash[NT_HASH_LEN];
1422 if (is_null_sid(sid)) {
1423 return NT_STATUS_INVALID_SID;
1426 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1427 return NT_STATUS_INVALID_SID;
1430 centry = centry_start(domain, NT_STATUS_OK);
1431 if (!centry) {
1432 return NT_STATUS_INTERNAL_DB_ERROR;
1435 dump_data_pw("nt_pass", nt_pass, NT_HASH_LEN);
1437 centry_put_time(centry, time(NULL));
1439 /* Create a salt and then salt the hash. */
1440 generate_random_buffer(cred_salt, NT_HASH_LEN);
1441 E_md5hash(cred_salt, nt_pass, salted_hash);
1443 centry_put_hash16(centry, salted_hash);
1444 centry_put_hash16(centry, cred_salt);
1445 centry_end(centry, "CRED/%s", sid_to_fstring(sid_string, sid));
1447 DEBUG(10,("wcache_save_creds: %s\n", sid_string));
1449 centry_free(centry);
1451 return NT_STATUS_OK;
1455 /* Query display info. This is the basic user list fn */
1456 NTSTATUS wb_cache_query_user_list(struct winbindd_domain *domain,
1457 TALLOC_CTX *mem_ctx,
1458 uint32_t *num_entries,
1459 struct wbint_userinfo **info)
1461 struct winbind_cache *cache = get_cache(domain);
1462 struct cache_entry *centry = NULL;
1463 NTSTATUS status;
1464 unsigned int i, retry;
1465 bool old_status = domain->online;
1467 if (!cache->tdb)
1468 goto do_query;
1470 centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
1471 if (!centry)
1472 goto do_query;
1474 do_fetch_cache:
1475 *num_entries = centry_uint32(centry);
1477 if (*num_entries == 0)
1478 goto do_cached;
1480 (*info) = talloc_array(mem_ctx, struct wbint_userinfo, *num_entries);
1481 if (! (*info)) {
1482 smb_panic_fn("query_user_list out of memory");
1484 for (i=0; i<(*num_entries); i++) {
1485 (*info)[i].acct_name = centry_string(centry, mem_ctx);
1486 (*info)[i].full_name = centry_string(centry, mem_ctx);
1487 (*info)[i].homedir = centry_string(centry, mem_ctx);
1488 (*info)[i].shell = centry_string(centry, mem_ctx);
1489 centry_sid(centry, &(*info)[i].user_sid);
1490 centry_sid(centry, &(*info)[i].group_sid);
1493 do_cached:
1494 status = centry->status;
1496 DEBUG(10,("query_user_list: [Cached] - cached list for domain %s status: %s\n",
1497 domain->name, nt_errstr(status) ));
1499 centry_free(centry);
1500 return status;
1502 do_query:
1503 *num_entries = 0;
1504 *info = NULL;
1506 /* Return status value returned by seq number check */
1508 if (!NT_STATUS_IS_OK(domain->last_status))
1509 return domain->last_status;
1511 /* Put the query_user_list() in a retry loop. There appears to be
1512 * some bug either with Windows 2000 or Samba's handling of large
1513 * rpc replies. This manifests itself as sudden disconnection
1514 * at a random point in the enumeration of a large (60k) user list.
1515 * The retry loop simply tries the operation again. )-: It's not
1516 * pretty but an acceptable workaround until we work out what the
1517 * real problem is. */
1519 retry = 0;
1520 do {
1522 DEBUG(10,("query_user_list: [Cached] - doing backend query for list for domain %s\n",
1523 domain->name ));
1525 status = domain->backend->query_user_list(domain, mem_ctx, num_entries, info);
1526 if (!NT_STATUS_IS_OK(status)) {
1527 DEBUG(3, ("query_user_list: returned 0x%08x, "
1528 "retrying\n", NT_STATUS_V(status)));
1530 if (NT_STATUS_EQUAL(status, NT_STATUS_UNSUCCESSFUL)) {
1531 DEBUG(3, ("query_user_list: flushing "
1532 "connection cache\n"));
1533 invalidate_cm_connection(domain);
1535 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1536 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1537 if (!domain->internal && old_status) {
1538 set_domain_offline(domain);
1540 /* store partial response. */
1541 if (*num_entries > 0) {
1543 * humm, what about the status used for cache?
1544 * Should it be NT_STATUS_OK?
1546 break;
1549 * domain is offline now, and there is no user entries,
1550 * try to fetch from cache again.
1552 if (cache->tdb && !domain->online && !domain->internal && old_status) {
1553 centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
1554 /* partial response... */
1555 if (!centry) {
1556 goto skip_save;
1557 } else {
1558 goto do_fetch_cache;
1560 } else {
1561 goto skip_save;
1565 } while (NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_UNSUCCESSFUL) &&
1566 (retry++ < 5));
1568 /* and save it */
1569 refresh_sequence_number(domain);
1570 if (!NT_STATUS_IS_OK(status)) {
1571 return status;
1573 centry = centry_start(domain, status);
1574 if (!centry)
1575 goto skip_save;
1576 centry_put_uint32(centry, *num_entries);
1577 for (i=0; i<(*num_entries); i++) {
1578 centry_put_string(centry, (*info)[i].acct_name);
1579 centry_put_string(centry, (*info)[i].full_name);
1580 centry_put_string(centry, (*info)[i].homedir);
1581 centry_put_string(centry, (*info)[i].shell);
1582 centry_put_sid(centry, &(*info)[i].user_sid);
1583 centry_put_sid(centry, &(*info)[i].group_sid);
1584 if (domain->backend && domain->backend->consistent) {
1585 /* when the backend is consistent we can pre-prime some mappings */
1586 wcache_save_name_to_sid(domain, NT_STATUS_OK,
1587 domain->name,
1588 (*info)[i].acct_name,
1589 &(*info)[i].user_sid,
1590 SID_NAME_USER);
1591 wcache_save_sid_to_name(domain, NT_STATUS_OK,
1592 &(*info)[i].user_sid,
1593 domain->name,
1594 (*info)[i].acct_name,
1595 SID_NAME_USER);
1596 wcache_save_user(domain, NT_STATUS_OK, &(*info)[i]);
1599 centry_end(centry, "UL/%s", domain->name);
1600 centry_free(centry);
1602 skip_save:
1603 return status;
1606 /* list all domain groups */
1607 NTSTATUS wb_cache_enum_dom_groups(struct winbindd_domain *domain,
1608 TALLOC_CTX *mem_ctx,
1609 uint32_t *num_entries,
1610 struct wb_acct_info **info)
1612 struct winbind_cache *cache = get_cache(domain);
1613 struct cache_entry *centry = NULL;
1614 NTSTATUS status;
1615 unsigned int i;
1616 bool old_status;
1618 old_status = domain->online;
1619 if (!cache->tdb)
1620 goto do_query;
1622 centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
1623 if (!centry)
1624 goto do_query;
1626 do_fetch_cache:
1627 *num_entries = centry_uint32(centry);
1629 if (*num_entries == 0)
1630 goto do_cached;
1632 (*info) = talloc_array(mem_ctx, struct wb_acct_info, *num_entries);
1633 if (! (*info)) {
1634 smb_panic_fn("enum_dom_groups out of memory");
1636 for (i=0; i<(*num_entries); i++) {
1637 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1638 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1639 (*info)[i].rid = centry_uint32(centry);
1642 do_cached:
1643 status = centry->status;
1645 DEBUG(10,("enum_dom_groups: [Cached] - cached list for domain %s status: %s\n",
1646 domain->name, nt_errstr(status) ));
1648 centry_free(centry);
1649 return status;
1651 do_query:
1652 *num_entries = 0;
1653 *info = NULL;
1655 /* Return status value returned by seq number check */
1657 if (!NT_STATUS_IS_OK(domain->last_status))
1658 return domain->last_status;
1660 DEBUG(10,("enum_dom_groups: [Cached] - doing backend query for list for domain %s\n",
1661 domain->name ));
1663 status = domain->backend->enum_dom_groups(domain, mem_ctx, num_entries, info);
1665 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1666 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1667 if (!domain->internal && old_status) {
1668 set_domain_offline(domain);
1670 if (cache->tdb &&
1671 !domain->online &&
1672 !domain->internal &&
1673 old_status) {
1674 centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
1675 if (centry) {
1676 goto do_fetch_cache;
1680 /* and save it */
1681 refresh_sequence_number(domain);
1682 if (!NT_STATUS_IS_OK(status)) {
1683 return status;
1685 centry = centry_start(domain, status);
1686 if (!centry)
1687 goto skip_save;
1688 centry_put_uint32(centry, *num_entries);
1689 for (i=0; i<(*num_entries); i++) {
1690 centry_put_string(centry, (*info)[i].acct_name);
1691 centry_put_string(centry, (*info)[i].acct_desc);
1692 centry_put_uint32(centry, (*info)[i].rid);
1694 centry_end(centry, "GL/%s/domain", domain->name);
1695 centry_free(centry);
1697 skip_save:
1698 return status;
1701 /* list all domain groups */
1702 NTSTATUS wb_cache_enum_local_groups(struct winbindd_domain *domain,
1703 TALLOC_CTX *mem_ctx,
1704 uint32_t *num_entries,
1705 struct wb_acct_info **info)
1707 struct winbind_cache *cache = get_cache(domain);
1708 struct cache_entry *centry = NULL;
1709 NTSTATUS status;
1710 unsigned int i;
1711 bool old_status;
1713 old_status = domain->online;
1714 if (!cache->tdb)
1715 goto do_query;
1717 centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
1718 if (!centry)
1719 goto do_query;
1721 do_fetch_cache:
1722 *num_entries = centry_uint32(centry);
1724 if (*num_entries == 0)
1725 goto do_cached;
1727 (*info) = talloc_array(mem_ctx, struct wb_acct_info, *num_entries);
1728 if (! (*info)) {
1729 smb_panic_fn("enum_dom_groups out of memory");
1731 for (i=0; i<(*num_entries); i++) {
1732 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1733 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1734 (*info)[i].rid = centry_uint32(centry);
1737 do_cached:
1739 /* If we are returning cached data and the domain controller
1740 is down then we don't know whether the data is up to date
1741 or not. Return NT_STATUS_MORE_PROCESSING_REQUIRED to
1742 indicate this. */
1744 if (wcache_server_down(domain)) {
1745 DEBUG(10, ("enum_local_groups: returning cached user list and server was down\n"));
1746 status = NT_STATUS_MORE_PROCESSING_REQUIRED;
1747 } else
1748 status = centry->status;
1750 DEBUG(10,("enum_local_groups: [Cached] - cached list for domain %s status: %s\n",
1751 domain->name, nt_errstr(status) ));
1753 centry_free(centry);
1754 return status;
1756 do_query:
1757 *num_entries = 0;
1758 *info = NULL;
1760 /* Return status value returned by seq number check */
1762 if (!NT_STATUS_IS_OK(domain->last_status))
1763 return domain->last_status;
1765 DEBUG(10,("enum_local_groups: [Cached] - doing backend query for list for domain %s\n",
1766 domain->name ));
1768 status = domain->backend->enum_local_groups(domain, mem_ctx, num_entries, info);
1770 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1771 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1772 if (!domain->internal && old_status) {
1773 set_domain_offline(domain);
1775 if (cache->tdb &&
1776 !domain->internal &&
1777 !domain->online &&
1778 old_status) {
1779 centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
1780 if (centry) {
1781 goto do_fetch_cache;
1785 /* and save it */
1786 refresh_sequence_number(domain);
1787 if (!NT_STATUS_IS_OK(status)) {
1788 return status;
1790 centry = centry_start(domain, status);
1791 if (!centry)
1792 goto skip_save;
1793 centry_put_uint32(centry, *num_entries);
1794 for (i=0; i<(*num_entries); i++) {
1795 centry_put_string(centry, (*info)[i].acct_name);
1796 centry_put_string(centry, (*info)[i].acct_desc);
1797 centry_put_uint32(centry, (*info)[i].rid);
1799 centry_end(centry, "GL/%s/local", domain->name);
1800 centry_free(centry);
1802 skip_save:
1803 return status;
1806 NTSTATUS wcache_name_to_sid(struct winbindd_domain *domain,
1807 const char *domain_name,
1808 const char *name,
1809 struct dom_sid *sid,
1810 enum lsa_SidType *type)
1812 struct winbind_cache *cache = get_cache(domain);
1813 struct cache_entry *centry;
1814 NTSTATUS status;
1815 char *uname;
1817 if (cache->tdb == NULL) {
1818 return NT_STATUS_NOT_FOUND;
1821 uname = talloc_strdup_upper(talloc_tos(), name);
1822 if (uname == NULL) {
1823 return NT_STATUS_NO_MEMORY;
1826 if ((domain_name == NULL) || (domain_name[0] == '\0')) {
1827 domain_name = domain->name;
1830 centry = wcache_fetch(cache, domain, "NS/%s/%s", domain_name, uname);
1831 TALLOC_FREE(uname);
1832 if (centry == NULL) {
1833 return NT_STATUS_NOT_FOUND;
1836 status = centry->status;
1837 if (NT_STATUS_IS_OK(status)) {
1838 *type = (enum lsa_SidType)centry_uint32(centry);
1839 centry_sid(centry, sid);
1842 DEBUG(10,("name_to_sid: [Cached] - cached name for domain %s status: "
1843 "%s\n", domain->name, nt_errstr(status) ));
1845 centry_free(centry);
1846 return status;
1849 /* convert a single name to a sid in a domain */
1850 NTSTATUS wb_cache_name_to_sid(struct winbindd_domain *domain,
1851 TALLOC_CTX *mem_ctx,
1852 const char *domain_name,
1853 const char *name,
1854 uint32_t flags,
1855 struct dom_sid *sid,
1856 enum lsa_SidType *type)
1858 NTSTATUS status;
1859 bool old_status;
1861 old_status = domain->online;
1863 status = wcache_name_to_sid(domain, domain_name, name, sid, type);
1864 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
1865 return status;
1868 ZERO_STRUCTP(sid);
1870 /* If the seq number check indicated that there is a problem
1871 * with this DC, then return that status... except for
1872 * access_denied. This is special because the dc may be in
1873 * "restrict anonymous = 1" mode, in which case it will deny
1874 * most unauthenticated operations, but *will* allow the LSA
1875 * name-to-sid that we try as a fallback. */
1877 if (!(NT_STATUS_IS_OK(domain->last_status)
1878 || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1879 return domain->last_status;
1881 DEBUG(10,("name_to_sid: [Cached] - doing backend query for name for domain %s\n",
1882 domain->name ));
1884 status = domain->backend->name_to_sid(domain, mem_ctx, domain_name,
1885 name, flags, sid, type);
1887 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1888 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1889 if (!domain->internal && old_status) {
1890 set_domain_offline(domain);
1892 if (!domain->internal &&
1893 !domain->online &&
1894 old_status) {
1895 NTSTATUS cache_status;
1896 cache_status = wcache_name_to_sid(domain, domain_name, name, sid, type);
1897 return cache_status;
1900 /* and save it */
1901 refresh_sequence_number(domain);
1903 if (domain->online &&
1904 (NT_STATUS_IS_OK(status) || NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED))) {
1905 wcache_save_name_to_sid(domain, status, domain_name, name, sid, *type);
1907 /* Only save the reverse mapping if this was not a UPN */
1908 if (!strchr(name, '@')) {
1909 if (!strupper_m(discard_const_p(char, domain_name))) {
1910 return NT_STATUS_INVALID_PARAMETER;
1912 (void)strlower_m(discard_const_p(char, name));
1913 wcache_save_sid_to_name(domain, status, sid, domain_name, name, *type);
1917 return status;
1920 static NTSTATUS wcache_sid_to_name(struct winbindd_domain *domain,
1921 const struct dom_sid *sid,
1922 TALLOC_CTX *mem_ctx,
1923 char **domain_name,
1924 char **name,
1925 enum lsa_SidType *type)
1927 struct winbind_cache *cache = get_cache(domain);
1928 struct cache_entry *centry;
1929 char *sid_string;
1930 NTSTATUS status;
1932 if (cache->tdb == NULL) {
1933 return NT_STATUS_NOT_FOUND;
1936 sid_string = sid_string_tos(sid);
1937 if (sid_string == NULL) {
1938 return NT_STATUS_NO_MEMORY;
1941 centry = wcache_fetch(cache, domain, "SN/%s", sid_string);
1942 TALLOC_FREE(sid_string);
1943 if (centry == NULL) {
1944 return NT_STATUS_NOT_FOUND;
1947 if (NT_STATUS_IS_OK(centry->status)) {
1948 *type = (enum lsa_SidType)centry_uint32(centry);
1949 *domain_name = centry_string(centry, mem_ctx);
1950 *name = centry_string(centry, mem_ctx);
1953 status = centry->status;
1954 centry_free(centry);
1956 DEBUG(10,("sid_to_name: [Cached] - cached name for domain %s status: "
1957 "%s\n", domain->name, nt_errstr(status) ));
1959 return status;
1962 /* convert a sid to a user or group name. The sid is guaranteed to be in the domain
1963 given */
1964 NTSTATUS wb_cache_sid_to_name(struct winbindd_domain *domain,
1965 TALLOC_CTX *mem_ctx,
1966 const struct dom_sid *sid,
1967 char **domain_name,
1968 char **name,
1969 enum lsa_SidType *type)
1971 NTSTATUS status;
1972 bool old_status;
1974 old_status = domain->online;
1975 status = wcache_sid_to_name(domain, sid, mem_ctx, domain_name, name,
1976 type);
1977 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
1978 return status;
1981 *name = NULL;
1982 *domain_name = NULL;
1984 /* If the seq number check indicated that there is a problem
1985 * with this DC, then return that status... except for
1986 * access_denied. This is special because the dc may be in
1987 * "restrict anonymous = 1" mode, in which case it will deny
1988 * most unauthenticated operations, but *will* allow the LSA
1989 * sid-to-name that we try as a fallback. */
1991 if (!(NT_STATUS_IS_OK(domain->last_status)
1992 || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1993 return domain->last_status;
1995 DEBUG(10,("sid_to_name: [Cached] - doing backend query for name for domain %s\n",
1996 domain->name ));
1998 status = domain->backend->sid_to_name(domain, mem_ctx, sid, domain_name, name, type);
2000 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2001 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2002 if (!domain->internal && old_status) {
2003 set_domain_offline(domain);
2005 if (!domain->internal &&
2006 !domain->online &&
2007 old_status) {
2008 NTSTATUS cache_status;
2009 cache_status = wcache_sid_to_name(domain, sid, mem_ctx,
2010 domain_name, name, type);
2011 return cache_status;
2014 /* and save it */
2015 refresh_sequence_number(domain);
2016 if (!NT_STATUS_IS_OK(status)) {
2017 return status;
2019 wcache_save_sid_to_name(domain, status, sid, *domain_name, *name, *type);
2021 /* We can't save the name to sid mapping here, as with sid history a
2022 * later name2sid would give the wrong sid. */
2024 return status;
2027 NTSTATUS wb_cache_rids_to_names(struct winbindd_domain *domain,
2028 TALLOC_CTX *mem_ctx,
2029 const struct dom_sid *domain_sid,
2030 uint32_t *rids,
2031 size_t num_rids,
2032 char **domain_name,
2033 char ***names,
2034 enum lsa_SidType **types)
2036 struct winbind_cache *cache = get_cache(domain);
2037 size_t i;
2038 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
2039 bool have_mapped;
2040 bool have_unmapped;
2041 bool old_status;
2043 old_status = domain->online;
2044 *domain_name = NULL;
2045 *names = NULL;
2046 *types = NULL;
2048 if (!cache->tdb) {
2049 goto do_query;
2052 if (num_rids == 0) {
2053 return NT_STATUS_OK;
2056 *names = talloc_array(mem_ctx, char *, num_rids);
2057 *types = talloc_array(mem_ctx, enum lsa_SidType, num_rids);
2059 if ((*names == NULL) || (*types == NULL)) {
2060 result = NT_STATUS_NO_MEMORY;
2061 goto error;
2064 have_mapped = have_unmapped = false;
2066 for (i=0; i<num_rids; i++) {
2067 struct dom_sid sid;
2068 struct cache_entry *centry;
2069 fstring tmp;
2071 if (!sid_compose(&sid, domain_sid, rids[i])) {
2072 result = NT_STATUS_INTERNAL_ERROR;
2073 goto error;
2076 centry = wcache_fetch(cache, domain, "SN/%s",
2077 sid_to_fstring(tmp, &sid));
2078 if (!centry) {
2079 goto do_query;
2082 (*types)[i] = SID_NAME_UNKNOWN;
2083 (*names)[i] = talloc_strdup(*names, "");
2085 if (NT_STATUS_IS_OK(centry->status)) {
2086 char *dom;
2087 have_mapped = true;
2088 (*types)[i] = (enum lsa_SidType)centry_uint32(centry);
2090 dom = centry_string(centry, mem_ctx);
2091 if (*domain_name == NULL) {
2092 *domain_name = dom;
2093 } else {
2094 talloc_free(dom);
2097 (*names)[i] = centry_string(centry, *names);
2099 } else if (NT_STATUS_EQUAL(centry->status, NT_STATUS_NONE_MAPPED)
2100 || NT_STATUS_EQUAL(centry->status, STATUS_SOME_UNMAPPED)) {
2101 have_unmapped = true;
2103 } else {
2104 /* something's definitely wrong */
2105 result = centry->status;
2106 centry_free(centry);
2107 goto error;
2110 centry_free(centry);
2113 if (!have_mapped) {
2114 return NT_STATUS_NONE_MAPPED;
2116 if (!have_unmapped) {
2117 return NT_STATUS_OK;
2119 return STATUS_SOME_UNMAPPED;
2121 do_query:
2123 TALLOC_FREE(*names);
2124 TALLOC_FREE(*types);
2126 result = domain->backend->rids_to_names(domain, mem_ctx, domain_sid,
2127 rids, num_rids, domain_name,
2128 names, types);
2130 if (NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
2131 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2132 if (!domain->internal && old_status) {
2133 set_domain_offline(domain);
2135 if (cache->tdb &&
2136 !domain->internal &&
2137 !domain->online &&
2138 old_status) {
2139 have_mapped = have_unmapped = false;
2141 *names = talloc_array(mem_ctx, char *, num_rids);
2142 if (*names == NULL) {
2143 result = NT_STATUS_NO_MEMORY;
2144 goto error;
2147 *types = talloc_array(mem_ctx, enum lsa_SidType,
2148 num_rids);
2149 if (*types == NULL) {
2150 result = NT_STATUS_NO_MEMORY;
2151 goto error;
2154 for (i=0; i<num_rids; i++) {
2155 struct dom_sid sid;
2156 struct cache_entry *centry;
2157 fstring tmp;
2159 if (!sid_compose(&sid, domain_sid, rids[i])) {
2160 result = NT_STATUS_INTERNAL_ERROR;
2161 goto error;
2164 centry = wcache_fetch(cache, domain, "SN/%s",
2165 sid_to_fstring(tmp, &sid));
2166 if (!centry) {
2167 (*types)[i] = SID_NAME_UNKNOWN;
2168 (*names)[i] = talloc_strdup(*names, "");
2169 continue;
2172 (*types)[i] = SID_NAME_UNKNOWN;
2173 (*names)[i] = talloc_strdup(*names, "");
2175 if (NT_STATUS_IS_OK(centry->status)) {
2176 char *dom;
2177 have_mapped = true;
2178 (*types)[i] = (enum lsa_SidType)centry_uint32(centry);
2180 dom = centry_string(centry, mem_ctx);
2181 if (*domain_name == NULL) {
2182 *domain_name = dom;
2183 } else {
2184 talloc_free(dom);
2187 (*names)[i] = centry_string(centry, *names);
2189 } else if (NT_STATUS_EQUAL(centry->status, NT_STATUS_NONE_MAPPED)) {
2190 have_unmapped = true;
2192 } else {
2193 /* something's definitely wrong */
2194 result = centry->status;
2195 centry_free(centry);
2196 goto error;
2199 centry_free(centry);
2202 if (!have_mapped) {
2203 return NT_STATUS_NONE_MAPPED;
2205 if (!have_unmapped) {
2206 return NT_STATUS_OK;
2208 return STATUS_SOME_UNMAPPED;
2212 None of the queried rids has been found so save all negative entries
2214 if (NT_STATUS_EQUAL(result, NT_STATUS_NONE_MAPPED)) {
2215 for (i = 0; i < num_rids; i++) {
2216 struct dom_sid sid;
2217 const char *name = "";
2218 const enum lsa_SidType type = SID_NAME_UNKNOWN;
2219 NTSTATUS status = NT_STATUS_NONE_MAPPED;
2221 if (!sid_compose(&sid, domain_sid, rids[i])) {
2222 return NT_STATUS_INTERNAL_ERROR;
2225 wcache_save_sid_to_name(domain, status, &sid, *domain_name,
2226 name, type);
2229 return result;
2233 Some or all of the queried rids have been found.
2235 if (!NT_STATUS_IS_OK(result) &&
2236 !NT_STATUS_EQUAL(result, STATUS_SOME_UNMAPPED)) {
2237 return result;
2240 refresh_sequence_number(domain);
2242 for (i=0; i<num_rids; i++) {
2243 struct dom_sid sid;
2244 NTSTATUS status;
2246 if (!sid_compose(&sid, domain_sid, rids[i])) {
2247 result = NT_STATUS_INTERNAL_ERROR;
2248 goto error;
2251 status = (*types)[i] == SID_NAME_UNKNOWN ?
2252 NT_STATUS_NONE_MAPPED : NT_STATUS_OK;
2254 wcache_save_sid_to_name(domain, status, &sid, *domain_name,
2255 (*names)[i], (*types)[i]);
2258 return result;
2260 error:
2261 TALLOC_FREE(*names);
2262 TALLOC_FREE(*types);
2263 return result;
2266 NTSTATUS wcache_query_user(struct winbindd_domain *domain,
2267 TALLOC_CTX *mem_ctx,
2268 const struct dom_sid *user_sid,
2269 struct wbint_userinfo *info)
2271 struct winbind_cache *cache = get_cache(domain);
2272 struct cache_entry *centry = NULL;
2273 NTSTATUS status;
2274 char *sid_string;
2276 if (cache->tdb == NULL) {
2277 return NT_STATUS_NOT_FOUND;
2280 sid_string = sid_string_tos(user_sid);
2281 if (sid_string == NULL) {
2282 return NT_STATUS_NO_MEMORY;
2285 centry = wcache_fetch(cache, domain, "U/%s", sid_string);
2286 TALLOC_FREE(sid_string);
2287 if (centry == NULL) {
2288 return NT_STATUS_NOT_FOUND;
2292 * If we have an access denied cache entry and a cached info3
2293 * in the samlogon cache then do a query. This will force the
2294 * rpc back end to return the info3 data.
2297 if (NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED) &&
2298 netsamlogon_cache_have(user_sid)) {
2299 DEBUG(10, ("query_user: cached access denied and have cached "
2300 "info3\n"));
2301 domain->last_status = NT_STATUS_OK;
2302 centry_free(centry);
2303 return NT_STATUS_NOT_FOUND;
2306 /* if status is not ok then this is a negative hit
2307 and the rest of the data doesn't matter */
2308 status = centry->status;
2309 if (NT_STATUS_IS_OK(status)) {
2310 info->acct_name = centry_string(centry, mem_ctx);
2311 info->full_name = centry_string(centry, mem_ctx);
2312 info->homedir = centry_string(centry, mem_ctx);
2313 info->shell = centry_string(centry, mem_ctx);
2314 info->primary_gid = centry_uint32(centry);
2315 centry_sid(centry, &info->user_sid);
2316 centry_sid(centry, &info->group_sid);
2319 DEBUG(10,("query_user: [Cached] - cached info for domain %s status: "
2320 "%s\n", domain->name, nt_errstr(status) ));
2322 centry_free(centry);
2323 return status;
2328 * @brief Query a fullname from the username cache (for further gecos processing)
2330 * @param domain A pointer to the winbindd_domain struct.
2331 * @param mem_ctx The talloc context.
2332 * @param user_sid The user sid.
2333 * @param full_name A pointer to the full_name string.
2335 * @return NTSTATUS code
2337 NTSTATUS wcache_query_user_fullname(struct winbindd_domain *domain,
2338 TALLOC_CTX *mem_ctx,
2339 const struct dom_sid *user_sid,
2340 const char **full_name)
2342 NTSTATUS status;
2343 struct wbint_userinfo info;
2345 status = wcache_query_user(domain, mem_ctx, user_sid, &info);
2346 if (!NT_STATUS_IS_OK(status)) {
2347 return status;
2350 if (info.full_name != NULL) {
2351 *full_name = talloc_strdup(mem_ctx, info.full_name);
2352 if (*full_name == NULL) {
2353 return NT_STATUS_NO_MEMORY;
2357 return NT_STATUS_OK;
2360 /* Lookup user information from a rid */
2361 NTSTATUS wb_cache_query_user(struct winbindd_domain *domain,
2362 TALLOC_CTX *mem_ctx,
2363 const struct dom_sid *user_sid,
2364 struct wbint_userinfo *info)
2366 NTSTATUS status;
2367 bool old_status;
2369 old_status = domain->online;
2370 status = wcache_query_user(domain, mem_ctx, user_sid, info);
2371 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2372 return status;
2375 ZERO_STRUCTP(info);
2377 /* Return status value returned by seq number check */
2379 if (!NT_STATUS_IS_OK(domain->last_status))
2380 return domain->last_status;
2382 DEBUG(10,("query_user: [Cached] - doing backend query for info for domain %s\n",
2383 domain->name ));
2385 status = domain->backend->query_user(domain, mem_ctx, user_sid, info);
2387 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2388 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2389 if (!domain->internal && old_status) {
2390 set_domain_offline(domain);
2392 if (!domain->internal &&
2393 !domain->online &&
2394 old_status) {
2395 NTSTATUS cache_status;
2396 cache_status = wcache_query_user(domain, mem_ctx, user_sid, info);
2397 return cache_status;
2400 /* and save it */
2401 refresh_sequence_number(domain);
2402 if (!NT_STATUS_IS_OK(status)) {
2403 return status;
2405 wcache_save_user(domain, status, info);
2407 return status;
2410 NTSTATUS wcache_lookup_usergroups(struct winbindd_domain *domain,
2411 TALLOC_CTX *mem_ctx,
2412 const struct dom_sid *user_sid,
2413 uint32_t *pnum_sids,
2414 struct dom_sid **psids)
2416 struct winbind_cache *cache = get_cache(domain);
2417 struct cache_entry *centry = NULL;
2418 NTSTATUS status;
2419 uint32_t i, num_sids;
2420 struct dom_sid *sids;
2421 fstring sid_string;
2423 if (cache->tdb == NULL) {
2424 return NT_STATUS_NOT_FOUND;
2427 centry = wcache_fetch(cache, domain, "UG/%s",
2428 sid_to_fstring(sid_string, user_sid));
2429 if (centry == NULL) {
2430 return NT_STATUS_NOT_FOUND;
2433 /* If we have an access denied cache entry and a cached info3 in the
2434 samlogon cache then do a query. This will force the rpc back end
2435 to return the info3 data. */
2437 if (NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)
2438 && netsamlogon_cache_have(user_sid)) {
2439 DEBUG(10, ("lookup_usergroups: cached access denied and have "
2440 "cached info3\n"));
2441 domain->last_status = NT_STATUS_OK;
2442 centry_free(centry);
2443 return NT_STATUS_NOT_FOUND;
2446 num_sids = centry_uint32(centry);
2447 sids = talloc_array(mem_ctx, struct dom_sid, num_sids);
2448 if (sids == NULL) {
2449 centry_free(centry);
2450 return NT_STATUS_NO_MEMORY;
2453 for (i=0; i<num_sids; i++) {
2454 centry_sid(centry, &sids[i]);
2457 status = centry->status;
2459 DEBUG(10,("lookup_usergroups: [Cached] - cached info for domain %s "
2460 "status: %s\n", domain->name, nt_errstr(status)));
2462 centry_free(centry);
2464 *pnum_sids = num_sids;
2465 *psids = sids;
2466 return status;
2469 /* Lookup groups a user is a member of. */
2470 NTSTATUS wb_cache_lookup_usergroups(struct winbindd_domain *domain,
2471 TALLOC_CTX *mem_ctx,
2472 const struct dom_sid *user_sid,
2473 uint32_t *num_groups,
2474 struct dom_sid **user_gids)
2476 struct cache_entry *centry = NULL;
2477 NTSTATUS status;
2478 unsigned int i;
2479 fstring sid_string;
2480 bool old_status;
2482 old_status = domain->online;
2483 status = wcache_lookup_usergroups(domain, mem_ctx, user_sid,
2484 num_groups, user_gids);
2485 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2486 return status;
2489 (*num_groups) = 0;
2490 (*user_gids) = NULL;
2492 /* Return status value returned by seq number check */
2494 if (!NT_STATUS_IS_OK(domain->last_status))
2495 return domain->last_status;
2497 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info for domain %s\n",
2498 domain->name ));
2500 status = domain->backend->lookup_usergroups(domain, mem_ctx, user_sid, num_groups, user_gids);
2502 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2503 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2504 if (!domain->internal && old_status) {
2505 set_domain_offline(domain);
2507 if (!domain->internal &&
2508 !domain->online &&
2509 old_status) {
2510 NTSTATUS cache_status;
2511 cache_status = wcache_lookup_usergroups(domain, mem_ctx, user_sid,
2512 num_groups, user_gids);
2513 return cache_status;
2516 if ( NT_STATUS_EQUAL(status, NT_STATUS_SYNCHRONIZATION_REQUIRED) )
2517 goto skip_save;
2519 /* and save it */
2520 refresh_sequence_number(domain);
2521 if (!NT_STATUS_IS_OK(status)) {
2522 return status;
2524 centry = centry_start(domain, status);
2525 if (!centry)
2526 goto skip_save;
2528 centry_put_uint32(centry, *num_groups);
2529 for (i=0; i<(*num_groups); i++) {
2530 centry_put_sid(centry, &(*user_gids)[i]);
2533 centry_end(centry, "UG/%s", sid_to_fstring(sid_string, user_sid));
2534 centry_free(centry);
2536 skip_save:
2537 return status;
2540 static char *wcache_make_sidlist(TALLOC_CTX *mem_ctx, uint32_t num_sids,
2541 const struct dom_sid *sids)
2543 uint32_t i;
2544 char *sidlist;
2546 sidlist = talloc_strdup(mem_ctx, "");
2547 if (sidlist == NULL) {
2548 return NULL;
2550 for (i=0; i<num_sids; i++) {
2551 fstring tmp;
2552 sidlist = talloc_asprintf_append_buffer(
2553 sidlist, "/%s", sid_to_fstring(tmp, &sids[i]));
2554 if (sidlist == NULL) {
2555 return NULL;
2558 return sidlist;
2561 NTSTATUS wcache_lookup_useraliases(struct winbindd_domain *domain,
2562 TALLOC_CTX *mem_ctx, uint32_t num_sids,
2563 const struct dom_sid *sids,
2564 uint32_t *pnum_aliases, uint32_t **paliases)
2566 struct winbind_cache *cache = get_cache(domain);
2567 struct cache_entry *centry = NULL;
2568 uint32_t num_aliases;
2569 uint32_t *aliases;
2570 NTSTATUS status;
2571 char *sidlist;
2572 int i;
2574 if (cache->tdb == NULL) {
2575 return NT_STATUS_NOT_FOUND;
2578 if (num_sids == 0) {
2579 *pnum_aliases = 0;
2580 *paliases = NULL;
2581 return NT_STATUS_OK;
2584 /* We need to cache indexed by the whole list of SIDs, the aliases
2585 * resulting might come from any of the SIDs. */
2587 sidlist = wcache_make_sidlist(talloc_tos(), num_sids, sids);
2588 if (sidlist == NULL) {
2589 return NT_STATUS_NO_MEMORY;
2592 centry = wcache_fetch(cache, domain, "UA%s", sidlist);
2593 TALLOC_FREE(sidlist);
2594 if (centry == NULL) {
2595 return NT_STATUS_NOT_FOUND;
2598 num_aliases = centry_uint32(centry);
2599 aliases = talloc_array(mem_ctx, uint32_t, num_aliases);
2600 if (aliases == NULL) {
2601 centry_free(centry);
2602 return NT_STATUS_NO_MEMORY;
2605 for (i=0; i<num_aliases; i++) {
2606 aliases[i] = centry_uint32(centry);
2609 status = centry->status;
2611 DEBUG(10,("lookup_useraliases: [Cached] - cached info for domain: %s "
2612 "status %s\n", domain->name, nt_errstr(status)));
2614 centry_free(centry);
2616 *pnum_aliases = num_aliases;
2617 *paliases = aliases;
2619 return status;
2622 NTSTATUS wb_cache_lookup_useraliases(struct winbindd_domain *domain,
2623 TALLOC_CTX *mem_ctx,
2624 uint32_t num_sids,
2625 const struct dom_sid *sids,
2626 uint32_t *num_aliases,
2627 uint32_t **alias_rids)
2629 struct cache_entry *centry = NULL;
2630 NTSTATUS status;
2631 char *sidlist;
2632 int i;
2633 bool old_status;
2635 old_status = domain->online;
2636 status = wcache_lookup_useraliases(domain, mem_ctx, num_sids, sids,
2637 num_aliases, alias_rids);
2638 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2639 return status;
2642 (*num_aliases) = 0;
2643 (*alias_rids) = NULL;
2645 if (!NT_STATUS_IS_OK(domain->last_status))
2646 return domain->last_status;
2648 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info "
2649 "for domain %s\n", domain->name ));
2651 sidlist = wcache_make_sidlist(talloc_tos(), num_sids, sids);
2652 if (sidlist == NULL) {
2653 return NT_STATUS_NO_MEMORY;
2656 status = domain->backend->lookup_useraliases(domain, mem_ctx,
2657 num_sids, sids,
2658 num_aliases, alias_rids);
2660 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2661 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2662 if (!domain->internal && old_status) {
2663 set_domain_offline(domain);
2665 if (!domain->internal &&
2666 !domain->online &&
2667 old_status) {
2668 NTSTATUS cache_status;
2669 cache_status = wcache_lookup_useraliases(domain, mem_ctx, num_sids,
2670 sids, num_aliases, alias_rids);
2671 return cache_status;
2674 /* and save it */
2675 refresh_sequence_number(domain);
2676 if (!NT_STATUS_IS_OK(status)) {
2677 return status;
2679 centry = centry_start(domain, status);
2680 if (!centry)
2681 goto skip_save;
2682 centry_put_uint32(centry, *num_aliases);
2683 for (i=0; i<(*num_aliases); i++)
2684 centry_put_uint32(centry, (*alias_rids)[i]);
2685 centry_end(centry, "UA%s", sidlist);
2686 centry_free(centry);
2688 skip_save:
2689 return status;
2692 NTSTATUS wcache_lookup_groupmem(struct winbindd_domain *domain,
2693 TALLOC_CTX *mem_ctx,
2694 const struct dom_sid *group_sid,
2695 uint32_t *num_names,
2696 struct dom_sid **sid_mem, char ***names,
2697 uint32_t **name_types)
2699 struct winbind_cache *cache = get_cache(domain);
2700 struct cache_entry *centry = NULL;
2701 NTSTATUS status;
2702 unsigned int i;
2703 char *sid_string;
2705 if (cache->tdb == NULL) {
2706 return NT_STATUS_NOT_FOUND;
2709 sid_string = sid_string_tos(group_sid);
2710 if (sid_string == NULL) {
2711 return NT_STATUS_NO_MEMORY;
2714 centry = wcache_fetch(cache, domain, "GM/%s", sid_string);
2715 TALLOC_FREE(sid_string);
2716 if (centry == NULL) {
2717 return NT_STATUS_NOT_FOUND;
2720 *sid_mem = NULL;
2721 *names = NULL;
2722 *name_types = NULL;
2724 *num_names = centry_uint32(centry);
2725 if (*num_names == 0) {
2726 centry_free(centry);
2727 return NT_STATUS_OK;
2730 *sid_mem = talloc_array(mem_ctx, struct dom_sid, *num_names);
2731 *names = talloc_array(mem_ctx, char *, *num_names);
2732 *name_types = talloc_array(mem_ctx, uint32_t, *num_names);
2734 if ((*sid_mem == NULL) || (*names == NULL) || (*name_types == NULL)) {
2735 TALLOC_FREE(*sid_mem);
2736 TALLOC_FREE(*names);
2737 TALLOC_FREE(*name_types);
2738 centry_free(centry);
2739 return NT_STATUS_NO_MEMORY;
2742 for (i=0; i<(*num_names); i++) {
2743 centry_sid(centry, &(*sid_mem)[i]);
2744 (*names)[i] = centry_string(centry, mem_ctx);
2745 (*name_types)[i] = centry_uint32(centry);
2748 status = centry->status;
2750 DEBUG(10,("lookup_groupmem: [Cached] - cached info for domain %s "
2751 "status: %s\n", domain->name, nt_errstr(status)));
2753 centry_free(centry);
2754 return status;
2757 NTSTATUS wb_cache_lookup_groupmem(struct winbindd_domain *domain,
2758 TALLOC_CTX *mem_ctx,
2759 const struct dom_sid *group_sid,
2760 enum lsa_SidType type,
2761 uint32_t *num_names,
2762 struct dom_sid **sid_mem,
2763 char ***names,
2764 uint32_t **name_types)
2766 struct cache_entry *centry = NULL;
2767 NTSTATUS status;
2768 unsigned int i;
2769 fstring sid_string;
2770 bool old_status;
2772 old_status = domain->online;
2773 status = wcache_lookup_groupmem(domain, mem_ctx, group_sid, num_names,
2774 sid_mem, names, name_types);
2775 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2776 return status;
2779 (*num_names) = 0;
2780 (*sid_mem) = NULL;
2781 (*names) = NULL;
2782 (*name_types) = NULL;
2784 /* Return status value returned by seq number check */
2786 if (!NT_STATUS_IS_OK(domain->last_status))
2787 return domain->last_status;
2789 DEBUG(10,("lookup_groupmem: [Cached] - doing backend query for info for domain %s\n",
2790 domain->name ));
2792 status = domain->backend->lookup_groupmem(domain, mem_ctx, group_sid,
2793 type, num_names,
2794 sid_mem, names, name_types);
2796 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2797 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2798 if (!domain->internal && old_status) {
2799 set_domain_offline(domain);
2801 if (!domain->internal &&
2802 !domain->online &&
2803 old_status) {
2804 NTSTATUS cache_status;
2805 cache_status = wcache_lookup_groupmem(domain, mem_ctx, group_sid,
2806 num_names, sid_mem, names,
2807 name_types);
2808 return cache_status;
2811 /* and save it */
2812 refresh_sequence_number(domain);
2813 if (!NT_STATUS_IS_OK(status)) {
2814 return status;
2816 centry = centry_start(domain, status);
2817 if (!centry)
2818 goto skip_save;
2819 centry_put_uint32(centry, *num_names);
2820 for (i=0; i<(*num_names); i++) {
2821 centry_put_sid(centry, &(*sid_mem)[i]);
2822 centry_put_string(centry, (*names)[i]);
2823 centry_put_uint32(centry, (*name_types)[i]);
2825 centry_end(centry, "GM/%s", sid_to_fstring(sid_string, group_sid));
2826 centry_free(centry);
2828 skip_save:
2829 return status;
2832 /* find the sequence number for a domain */
2833 NTSTATUS wb_cache_sequence_number(struct winbindd_domain *domain,
2834 uint32_t *seq)
2836 refresh_sequence_number(domain);
2838 *seq = domain->sequence_number;
2840 return NT_STATUS_OK;
2843 /* enumerate trusted domains
2844 * (we need to have the list of trustdoms in the cache when we go offline) -
2845 * Guenther */
2846 NTSTATUS wb_cache_trusted_domains(struct winbindd_domain *domain,
2847 TALLOC_CTX *mem_ctx,
2848 struct netr_DomainTrustList *trusts)
2850 NTSTATUS status;
2851 struct winbind_cache *cache;
2852 struct winbindd_tdc_domain *dom_list = NULL;
2853 size_t num_domains = 0;
2854 bool retval = false;
2855 int i;
2856 bool old_status;
2858 old_status = domain->online;
2859 trusts->count = 0;
2860 trusts->array = NULL;
2862 cache = get_cache(domain);
2863 if (!cache || !cache->tdb) {
2864 goto do_query;
2867 if (domain->online) {
2868 goto do_query;
2871 retval = wcache_tdc_fetch_list(&dom_list, &num_domains);
2872 if (!retval || !num_domains || !dom_list) {
2873 TALLOC_FREE(dom_list);
2874 goto do_query;
2877 do_fetch_cache:
2878 trusts->array = talloc_zero_array(mem_ctx, struct netr_DomainTrust, num_domains);
2879 if (!trusts->array) {
2880 TALLOC_FREE(dom_list);
2881 return NT_STATUS_NO_MEMORY;
2884 for (i = 0; i < num_domains; i++) {
2885 struct netr_DomainTrust *trust;
2886 struct dom_sid *sid;
2887 struct winbindd_domain *dom;
2889 dom = find_domain_from_name_noinit(dom_list[i].domain_name);
2890 if (dom && dom->internal) {
2891 continue;
2894 trust = &trusts->array[trusts->count];
2895 trust->netbios_name = talloc_strdup(trusts->array, dom_list[i].domain_name);
2896 trust->dns_name = talloc_strdup(trusts->array, dom_list[i].dns_name);
2897 sid = talloc(trusts->array, struct dom_sid);
2898 if (!trust->netbios_name || !trust->dns_name ||
2899 !sid) {
2900 TALLOC_FREE(dom_list);
2901 TALLOC_FREE(trusts->array);
2902 return NT_STATUS_NO_MEMORY;
2905 trust->trust_flags = dom_list[i].trust_flags;
2906 trust->trust_attributes = dom_list[i].trust_attribs;
2907 trust->trust_type = dom_list[i].trust_type;
2908 sid_copy(sid, &dom_list[i].sid);
2909 trust->sid = sid;
2910 trusts->count++;
2913 TALLOC_FREE(dom_list);
2914 return NT_STATUS_OK;
2916 do_query:
2917 /* Return status value returned by seq number check */
2919 if (!NT_STATUS_IS_OK(domain->last_status))
2920 return domain->last_status;
2922 DEBUG(10,("trusted_domains: [Cached] - doing backend query for info for domain %s\n",
2923 domain->name ));
2925 status = domain->backend->trusted_domains(domain, mem_ctx, trusts);
2927 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2928 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2929 if (!domain->internal && old_status) {
2930 set_domain_offline(domain);
2932 if (!domain->internal &&
2933 !domain->online &&
2934 old_status) {
2935 retval = wcache_tdc_fetch_list(&dom_list, &num_domains);
2936 if (retval && num_domains && dom_list) {
2937 TALLOC_FREE(trusts->array);
2938 trusts->count = 0;
2939 goto do_fetch_cache;
2943 /* no trusts gives NT_STATUS_NO_MORE_ENTRIES resetting to NT_STATUS_OK
2944 * so that the generic centry handling still applies correctly -
2945 * Guenther*/
2947 if (!NT_STATUS_IS_ERR(status)) {
2948 status = NT_STATUS_OK;
2950 return status;
2953 /* get lockout policy */
2954 NTSTATUS wb_cache_lockout_policy(struct winbindd_domain *domain,
2955 TALLOC_CTX *mem_ctx,
2956 struct samr_DomInfo12 *policy)
2958 struct winbind_cache *cache = get_cache(domain);
2959 struct cache_entry *centry = NULL;
2960 NTSTATUS status;
2961 bool old_status;
2963 old_status = domain->online;
2964 if (!cache->tdb)
2965 goto do_query;
2967 centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
2969 if (!centry)
2970 goto do_query;
2972 do_fetch_cache:
2973 policy->lockout_duration = centry_nttime(centry);
2974 policy->lockout_window = centry_nttime(centry);
2975 policy->lockout_threshold = centry_uint16(centry);
2977 status = centry->status;
2979 DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2980 domain->name, nt_errstr(status) ));
2982 centry_free(centry);
2983 return status;
2985 do_query:
2986 ZERO_STRUCTP(policy);
2988 /* Return status value returned by seq number check */
2990 if (!NT_STATUS_IS_OK(domain->last_status))
2991 return domain->last_status;
2993 DEBUG(10,("lockout_policy: [Cached] - doing backend query for info for domain %s\n",
2994 domain->name ));
2996 status = domain->backend->lockout_policy(domain, mem_ctx, policy);
2998 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2999 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
3000 if (!domain->internal && old_status) {
3001 set_domain_offline(domain);
3003 if (cache->tdb &&
3004 !domain->internal &&
3005 !domain->online &&
3006 old_status) {
3007 centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
3008 if (centry) {
3009 goto do_fetch_cache;
3013 /* and save it */
3014 refresh_sequence_number(domain);
3015 if (!NT_STATUS_IS_OK(status)) {
3016 return status;
3018 wcache_save_lockout_policy(domain, status, policy);
3020 return status;
3023 /* get password policy */
3024 NTSTATUS wb_cache_password_policy(struct winbindd_domain *domain,
3025 TALLOC_CTX *mem_ctx,
3026 struct samr_DomInfo1 *policy)
3028 struct winbind_cache *cache = get_cache(domain);
3029 struct cache_entry *centry = NULL;
3030 NTSTATUS status;
3031 bool old_status;
3033 old_status = domain->online;
3034 if (!cache->tdb)
3035 goto do_query;
3037 centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
3039 if (!centry)
3040 goto do_query;
3042 do_fetch_cache:
3043 policy->min_password_length = centry_uint16(centry);
3044 policy->password_history_length = centry_uint16(centry);
3045 policy->password_properties = centry_uint32(centry);
3046 policy->max_password_age = centry_nttime(centry);
3047 policy->min_password_age = centry_nttime(centry);
3049 status = centry->status;
3051 DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
3052 domain->name, nt_errstr(status) ));
3054 centry_free(centry);
3055 return status;
3057 do_query:
3058 ZERO_STRUCTP(policy);
3060 /* Return status value returned by seq number check */
3062 if (!NT_STATUS_IS_OK(domain->last_status))
3063 return domain->last_status;
3065 DEBUG(10,("password_policy: [Cached] - doing backend query for info for domain %s\n",
3066 domain->name ));
3068 status = domain->backend->password_policy(domain, mem_ctx, policy);
3070 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
3071 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
3072 if (!domain->internal && old_status) {
3073 set_domain_offline(domain);
3075 if (cache->tdb &&
3076 !domain->internal &&
3077 !domain->online &&
3078 old_status) {
3079 centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
3080 if (centry) {
3081 goto do_fetch_cache;
3085 /* and save it */
3086 refresh_sequence_number(domain);
3087 if (!NT_STATUS_IS_OK(status)) {
3088 return status;
3090 wcache_save_password_policy(domain, status, policy);
3092 return status;
3096 /* Invalidate cached user and group lists coherently */
3098 static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
3099 void *state)
3101 if (strncmp((const char *)kbuf.dptr, "UL/", 3) == 0 ||
3102 strncmp((const char *)kbuf.dptr, "GL/", 3) == 0)
3103 tdb_delete(the_tdb, kbuf);
3105 return 0;
3108 /* Invalidate the getpwnam and getgroups entries for a winbindd domain */
3110 void wcache_invalidate_samlogon(struct winbindd_domain *domain,
3111 const struct dom_sid *sid)
3113 fstring key_str, sid_string;
3114 struct winbind_cache *cache;
3116 /* don't clear cached U/SID and UG/SID entries when we want to logon
3117 * offline - gd */
3119 if (lp_winbind_offline_logon()) {
3120 return;
3123 if (!domain)
3124 return;
3126 cache = get_cache(domain);
3128 if (!cache->tdb) {
3129 return;
3132 /* Clear U/SID cache entry */
3133 fstr_sprintf(key_str, "U/%s", sid_to_fstring(sid_string, sid));
3134 DEBUG(10, ("wcache_invalidate_samlogon: clearing %s\n", key_str));
3135 tdb_delete(cache->tdb, string_tdb_data(key_str));
3137 /* Clear UG/SID cache entry */
3138 fstr_sprintf(key_str, "UG/%s", sid_to_fstring(sid_string, sid));
3139 DEBUG(10, ("wcache_invalidate_samlogon: clearing %s\n", key_str));
3140 tdb_delete(cache->tdb, string_tdb_data(key_str));
3142 /* Samba/winbindd never needs this. */
3143 netsamlogon_clear_cached_user(sid);
3146 bool wcache_invalidate_cache(void)
3148 struct winbindd_domain *domain;
3150 for (domain = domain_list(); domain; domain = domain->next) {
3151 struct winbind_cache *cache = get_cache(domain);
3153 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
3154 "entries for %s\n", domain->name));
3155 if (cache) {
3156 if (cache->tdb) {
3157 tdb_traverse(cache->tdb, traverse_fn, NULL);
3158 } else {
3159 return false;
3163 return true;
3166 bool wcache_invalidate_cache_noinit(void)
3168 struct winbindd_domain *domain;
3170 for (domain = domain_list(); domain; domain = domain->next) {
3171 struct winbind_cache *cache;
3173 /* Skip uninitialized domains. */
3174 if (!domain->initialized && !domain->internal) {
3175 continue;
3178 cache = get_cache(domain);
3180 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
3181 "entries for %s\n", domain->name));
3182 if (cache) {
3183 if (cache->tdb) {
3184 tdb_traverse(cache->tdb, traverse_fn, NULL);
3186 * Flushing cache has nothing to with domains.
3187 * return here if we successfully flushed once.
3188 * To avoid unnecessary traversing the cache.
3190 return true;
3191 } else {
3192 return false;
3196 return true;
3199 bool init_wcache(void)
3201 char *db_path;
3203 if (wcache == NULL) {
3204 wcache = SMB_XMALLOC_P(struct winbind_cache);
3205 ZERO_STRUCTP(wcache);
3208 if (wcache->tdb != NULL)
3209 return true;
3211 db_path = wcache_path();
3212 if (db_path == NULL) {
3213 return false;
3216 /* when working offline we must not clear the cache on restart */
3217 wcache->tdb = tdb_open_log(db_path,
3218 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
3219 TDB_INCOMPATIBLE_HASH |
3220 (lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST)),
3221 O_RDWR|O_CREAT, 0600);
3222 TALLOC_FREE(db_path);
3223 if (wcache->tdb == NULL) {
3224 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
3225 return false;
3228 return true;
3231 /************************************************************************
3232 This is called by the parent to initialize the cache file.
3233 We don't need sophisticated locking here as we know we're the
3234 only opener.
3235 ************************************************************************/
3237 bool initialize_winbindd_cache(void)
3239 bool cache_bad = true;
3240 uint32_t vers;
3242 if (!init_wcache()) {
3243 DEBUG(0,("initialize_winbindd_cache: init_wcache failed.\n"));
3244 return false;
3247 /* Check version number. */
3248 if (tdb_fetch_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, &vers) &&
3249 vers == WINBINDD_CACHE_VERSION) {
3250 cache_bad = false;
3253 if (cache_bad) {
3254 char *db_path;
3256 DEBUG(0,("initialize_winbindd_cache: clearing cache "
3257 "and re-creating with version number %d\n",
3258 WINBINDD_CACHE_VERSION ));
3260 tdb_close(wcache->tdb);
3261 wcache->tdb = NULL;
3263 db_path = wcache_path();
3264 if (db_path == NULL) {
3265 return false;
3268 if (unlink(db_path) == -1) {
3269 DEBUG(0,("initialize_winbindd_cache: unlink %s failed %s ",
3270 db_path,
3271 strerror(errno) ));
3272 TALLOC_FREE(db_path);
3273 return false;
3275 TALLOC_FREE(db_path);
3276 if (!init_wcache()) {
3277 DEBUG(0,("initialize_winbindd_cache: re-initialization "
3278 "init_wcache failed.\n"));
3279 return false;
3282 /* Write the version. */
3283 if (!tdb_store_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, WINBINDD_CACHE_VERSION)) {
3284 DEBUG(0,("initialize_winbindd_cache: version number store failed %s\n",
3285 tdb_errorstr(wcache->tdb) ));
3286 return false;
3290 tdb_close(wcache->tdb);
3291 wcache->tdb = NULL;
3292 return true;
3295 void close_winbindd_cache(void)
3297 if (!wcache) {
3298 return;
3300 if (wcache->tdb) {
3301 tdb_close(wcache->tdb);
3302 wcache->tdb = NULL;
3306 bool lookup_cached_sid(TALLOC_CTX *mem_ctx, const struct dom_sid *sid,
3307 char **domain_name, char **name,
3308 enum lsa_SidType *type)
3310 struct winbindd_domain *domain;
3311 NTSTATUS status;
3313 domain = find_lookup_domain_from_sid(sid);
3314 if (domain == NULL) {
3315 return false;
3317 status = wcache_sid_to_name(domain, sid, mem_ctx, domain_name, name,
3318 type);
3319 return NT_STATUS_IS_OK(status);
3322 bool lookup_cached_name(const char *domain_name,
3323 const char *name,
3324 struct dom_sid *sid,
3325 enum lsa_SidType *type)
3327 struct winbindd_domain *domain;
3328 NTSTATUS status;
3329 bool original_online_state;
3331 domain = find_lookup_domain_from_name(domain_name);
3332 if (domain == NULL) {
3333 return false;
3336 /* If we are doing a cached logon, temporarily set the domain
3337 offline so the cache won't expire the entry */
3339 original_online_state = domain->online;
3340 domain->online = false;
3341 status = wcache_name_to_sid(domain, domain_name, name, sid, type);
3342 domain->online = original_online_state;
3344 return NT_STATUS_IS_OK(status);
3348 * Cache a name to sid without checking the sequence number.
3349 * Used when caching from a trusted PAC.
3352 void cache_name2sid_trusted(struct winbindd_domain *domain,
3353 const char *domain_name,
3354 const char *name,
3355 enum lsa_SidType type,
3356 const struct dom_sid *sid)
3359 * Ensure we store the mapping with the
3360 * existing sequence number from the cache.
3362 get_cache(domain);
3363 (void)fetch_cache_seqnum(domain, time(NULL));
3364 wcache_save_name_to_sid(domain,
3365 NT_STATUS_OK,
3366 domain_name,
3367 name,
3368 sid,
3369 type);
3372 void cache_name2sid(struct winbindd_domain *domain,
3373 const char *domain_name, const char *name,
3374 enum lsa_SidType type, const struct dom_sid *sid)
3376 refresh_sequence_number(domain);
3377 wcache_save_name_to_sid(domain, NT_STATUS_OK, domain_name, name,
3378 sid, type);
3382 * The original idea that this cache only contains centries has
3383 * been blurred - now other stuff gets put in here. Ensure we
3384 * ignore these things on cleanup.
3387 static int traverse_fn_cleanup(TDB_CONTEXT *the_tdb, TDB_DATA kbuf,
3388 TDB_DATA dbuf, void *state)
3390 struct cache_entry *centry;
3392 if (is_non_centry_key(kbuf)) {
3393 return 0;
3396 centry = wcache_fetch_raw((char *)kbuf.dptr);
3397 if (!centry) {
3398 return 0;
3401 if (!NT_STATUS_IS_OK(centry->status)) {
3402 DEBUG(10,("deleting centry %s\n", (const char *)kbuf.dptr));
3403 tdb_delete(the_tdb, kbuf);
3406 centry_free(centry);
3407 return 0;
3410 /* flush the cache */
3411 void wcache_flush_cache(void)
3413 char *db_path;
3415 if (!wcache)
3416 return;
3417 if (wcache->tdb) {
3418 tdb_close(wcache->tdb);
3419 wcache->tdb = NULL;
3421 if (!winbindd_use_cache()) {
3422 return;
3425 db_path = wcache_path();
3426 if (db_path == NULL) {
3427 return;
3430 /* when working offline we must not clear the cache on restart */
3431 wcache->tdb = tdb_open_log(db_path,
3432 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
3433 TDB_INCOMPATIBLE_HASH |
3434 (lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST)),
3435 O_RDWR|O_CREAT, 0600);
3436 TALLOC_FREE(db_path);
3437 if (!wcache->tdb) {
3438 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
3439 return;
3442 tdb_traverse(wcache->tdb, traverse_fn_cleanup, NULL);
3444 DEBUG(10,("wcache_flush_cache success\n"));
3447 /* Count cached creds */
3449 static int traverse_fn_cached_creds(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
3450 void *state)
3452 int *cred_count = (int*)state;
3454 if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
3455 (*cred_count)++;
3457 return 0;
3460 NTSTATUS wcache_count_cached_creds(struct winbindd_domain *domain, int *count)
3462 struct winbind_cache *cache = get_cache(domain);
3464 *count = 0;
3466 if (!cache->tdb) {
3467 return NT_STATUS_INTERNAL_DB_ERROR;
3470 tdb_traverse(cache->tdb, traverse_fn_cached_creds, (void *)count);
3472 return NT_STATUS_OK;
3475 struct cred_list {
3476 struct cred_list *prev, *next;
3477 TDB_DATA key;
3478 fstring name;
3479 time_t created;
3481 static struct cred_list *wcache_cred_list;
3483 static int traverse_fn_get_credlist(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
3484 void *state)
3486 struct cred_list *cred;
3488 if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
3490 cred = SMB_MALLOC_P(struct cred_list);
3491 if (cred == NULL) {
3492 DEBUG(0,("traverse_fn_remove_first_creds: failed to malloc new entry for list\n"));
3493 return -1;
3496 ZERO_STRUCTP(cred);
3498 /* save a copy of the key */
3500 fstrcpy(cred->name, (const char *)kbuf.dptr);
3501 DLIST_ADD(wcache_cred_list, cred);
3504 return 0;
3507 NTSTATUS wcache_remove_oldest_cached_creds(struct winbindd_domain *domain, const struct dom_sid *sid)
3509 struct winbind_cache *cache = get_cache(domain);
3510 NTSTATUS status;
3511 int ret;
3512 struct cred_list *cred, *next, *oldest = NULL;
3514 if (!cache->tdb) {
3515 return NT_STATUS_INTERNAL_DB_ERROR;
3518 /* we possibly already have an entry */
3519 if (sid && NT_STATUS_IS_OK(wcache_cached_creds_exist(domain, sid))) {
3521 fstring key_str, tmp;
3523 DEBUG(11,("we already have an entry, deleting that\n"));
3525 fstr_sprintf(key_str, "CRED/%s", sid_to_fstring(tmp, sid));
3527 tdb_delete(cache->tdb, string_tdb_data(key_str));
3529 return NT_STATUS_OK;
3532 ret = tdb_traverse(cache->tdb, traverse_fn_get_credlist, NULL);
3533 if (ret == 0) {
3534 return NT_STATUS_OK;
3535 } else if ((ret < 0) || (wcache_cred_list == NULL)) {
3536 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
3539 ZERO_STRUCTP(oldest);
3541 for (cred = wcache_cred_list; cred; cred = cred->next) {
3543 TDB_DATA data;
3544 time_t t;
3546 data = tdb_fetch(cache->tdb, string_tdb_data(cred->name));
3547 if (!data.dptr) {
3548 DEBUG(10,("wcache_remove_oldest_cached_creds: entry for [%s] not found\n",
3549 cred->name));
3550 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
3551 goto done;
3554 t = IVAL(data.dptr, 0);
3555 SAFE_FREE(data.dptr);
3557 if (!oldest) {
3558 oldest = SMB_MALLOC_P(struct cred_list);
3559 if (oldest == NULL) {
3560 status = NT_STATUS_NO_MEMORY;
3561 goto done;
3564 fstrcpy(oldest->name, cred->name);
3565 oldest->created = t;
3566 continue;
3569 if (t < oldest->created) {
3570 fstrcpy(oldest->name, cred->name);
3571 oldest->created = t;
3575 if (tdb_delete(cache->tdb, string_tdb_data(oldest->name)) == 0) {
3576 status = NT_STATUS_OK;
3577 } else {
3578 status = NT_STATUS_UNSUCCESSFUL;
3580 done:
3581 for (cred = wcache_cred_list; cred; cred = next) {
3582 next = cred->next;
3583 DLIST_REMOVE(wcache_cred_list, cred);
3584 SAFE_FREE(cred);
3586 SAFE_FREE(oldest);
3588 return status;
3591 /* Change the global online/offline state. */
3592 bool set_global_winbindd_state_offline(void)
3594 TDB_DATA data;
3596 DEBUG(10,("set_global_winbindd_state_offline: offline requested.\n"));
3598 /* Only go offline if someone has created
3599 the key "WINBINDD_OFFLINE" in the cache tdb. */
3601 if (wcache == NULL || wcache->tdb == NULL) {
3602 DEBUG(10,("set_global_winbindd_state_offline: wcache not open yet.\n"));
3603 return false;
3606 if (!lp_winbind_offline_logon()) {
3607 DEBUG(10,("set_global_winbindd_state_offline: rejecting.\n"));
3608 return false;
3611 if (global_winbindd_offline_state) {
3612 /* Already offline. */
3613 return true;
3616 data = tdb_fetch_bystring( wcache->tdb, "WINBINDD_OFFLINE" );
3618 if (!data.dptr || data.dsize != 4) {
3619 DEBUG(10,("set_global_winbindd_state_offline: offline state not set.\n"));
3620 SAFE_FREE(data.dptr);
3621 return false;
3622 } else {
3623 DEBUG(10,("set_global_winbindd_state_offline: offline state set.\n"));
3624 global_winbindd_offline_state = true;
3625 SAFE_FREE(data.dptr);
3626 return true;
3630 void set_global_winbindd_state_online(void)
3632 DEBUG(10,("set_global_winbindd_state_online: online requested.\n"));
3634 if (!lp_winbind_offline_logon()) {
3635 DEBUG(10,("set_global_winbindd_state_online: rejecting.\n"));
3636 return;
3639 if (!global_winbindd_offline_state) {
3640 /* Already online. */
3641 return;
3643 global_winbindd_offline_state = false;
3645 if (!wcache->tdb) {
3646 return;
3649 /* Ensure there is no key "WINBINDD_OFFLINE" in the cache tdb. */
3650 tdb_delete_bystring(wcache->tdb, "WINBINDD_OFFLINE");
3653 bool get_global_winbindd_state_offline(void)
3655 return global_winbindd_offline_state;
3658 /***********************************************************************
3659 Validate functions for all possible cache tdb keys.
3660 ***********************************************************************/
3662 static struct cache_entry *create_centry_validate(const char *kstr, TDB_DATA data,
3663 struct tdb_validation_status *state)
3665 struct cache_entry *centry;
3667 centry = SMB_XMALLOC_P(struct cache_entry);
3668 centry->data = (unsigned char *)smb_memdup(data.dptr, data.dsize);
3669 if (!centry->data) {
3670 SAFE_FREE(centry);
3671 return NULL;
3673 centry->len = data.dsize;
3674 centry->ofs = 0;
3676 if (centry->len < 16) {
3677 /* huh? corrupt cache? */
3678 DEBUG(0,("create_centry_validate: Corrupt cache for key %s "
3679 "(len < 16) ?\n", kstr));
3680 centry_free(centry);
3681 state->bad_entry = true;
3682 state->success = false;
3683 return NULL;
3686 centry->status = NT_STATUS(centry_uint32(centry));
3687 centry->sequence_number = centry_uint32(centry);
3688 centry->timeout = centry_uint64_t(centry);
3689 return centry;
3692 static int validate_seqnum(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3693 struct tdb_validation_status *state)
3695 if (dbuf.dsize != 8) {
3696 DEBUG(0,("validate_seqnum: Corrupt cache for key %s (len %u != 8) ?\n",
3697 keystr, (unsigned int)dbuf.dsize ));
3698 state->bad_entry = true;
3699 return 1;
3701 return 0;
3704 static int validate_ns(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3705 struct tdb_validation_status *state)
3707 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3708 if (!centry) {
3709 return 1;
3712 (void)centry_uint32(centry);
3713 if (NT_STATUS_IS_OK(centry->status)) {
3714 struct dom_sid sid;
3715 (void)centry_sid(centry, &sid);
3718 centry_free(centry);
3720 if (!(state->success)) {
3721 return 1;
3723 DEBUG(10,("validate_ns: %s ok\n", keystr));
3724 return 0;
3727 static int validate_sn(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3728 struct tdb_validation_status *state)
3730 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3731 if (!centry) {
3732 return 1;
3735 if (NT_STATUS_IS_OK(centry->status)) {
3736 (void)centry_uint32(centry);
3737 (void)centry_string(centry, mem_ctx);
3738 (void)centry_string(centry, mem_ctx);
3741 centry_free(centry);
3743 if (!(state->success)) {
3744 return 1;
3746 DEBUG(10,("validate_sn: %s ok\n", keystr));
3747 return 0;
3750 static int validate_u(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3751 struct tdb_validation_status *state)
3753 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3754 struct dom_sid sid;
3756 if (!centry) {
3757 return 1;
3760 (void)centry_string(centry, mem_ctx);
3761 (void)centry_string(centry, mem_ctx);
3762 (void)centry_string(centry, mem_ctx);
3763 (void)centry_string(centry, mem_ctx);
3764 (void)centry_uint32(centry);
3765 (void)centry_sid(centry, &sid);
3766 (void)centry_sid(centry, &sid);
3768 centry_free(centry);
3770 if (!(state->success)) {
3771 return 1;
3773 DEBUG(10,("validate_u: %s ok\n", keystr));
3774 return 0;
3777 static int validate_loc_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3778 struct tdb_validation_status *state)
3780 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3782 if (!centry) {
3783 return 1;
3786 (void)centry_nttime(centry);
3787 (void)centry_nttime(centry);
3788 (void)centry_uint16(centry);
3790 centry_free(centry);
3792 if (!(state->success)) {
3793 return 1;
3795 DEBUG(10,("validate_loc_pol: %s ok\n", keystr));
3796 return 0;
3799 static int validate_pwd_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3800 struct tdb_validation_status *state)
3802 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3804 if (!centry) {
3805 return 1;
3808 (void)centry_uint16(centry);
3809 (void)centry_uint16(centry);
3810 (void)centry_uint32(centry);
3811 (void)centry_nttime(centry);
3812 (void)centry_nttime(centry);
3814 centry_free(centry);
3816 if (!(state->success)) {
3817 return 1;
3819 DEBUG(10,("validate_pwd_pol: %s ok\n", keystr));
3820 return 0;
3823 static int validate_cred(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3824 struct tdb_validation_status *state)
3826 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3828 if (!centry) {
3829 return 1;
3832 (void)centry_time(centry);
3833 (void)centry_hash16(centry, mem_ctx);
3835 /* We only have 17 bytes more data in the salted cred case. */
3836 if (centry->len - centry->ofs == 17) {
3837 (void)centry_hash16(centry, mem_ctx);
3840 centry_free(centry);
3842 if (!(state->success)) {
3843 return 1;
3845 DEBUG(10,("validate_cred: %s ok\n", keystr));
3846 return 0;
3849 static int validate_ul(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3850 struct tdb_validation_status *state)
3852 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3853 int32_t num_entries, i;
3855 if (!centry) {
3856 return 1;
3859 num_entries = (int32_t)centry_uint32(centry);
3861 for (i=0; i< num_entries; i++) {
3862 struct dom_sid sid;
3863 (void)centry_string(centry, mem_ctx);
3864 (void)centry_string(centry, mem_ctx);
3865 (void)centry_string(centry, mem_ctx);
3866 (void)centry_string(centry, mem_ctx);
3867 (void)centry_sid(centry, &sid);
3868 (void)centry_sid(centry, &sid);
3871 centry_free(centry);
3873 if (!(state->success)) {
3874 return 1;
3876 DEBUG(10,("validate_ul: %s ok\n", keystr));
3877 return 0;
3880 static int validate_gl(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3881 struct tdb_validation_status *state)
3883 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3884 int32_t num_entries, i;
3886 if (!centry) {
3887 return 1;
3890 num_entries = centry_uint32(centry);
3892 for (i=0; i< num_entries; i++) {
3893 (void)centry_string(centry, mem_ctx);
3894 (void)centry_string(centry, mem_ctx);
3895 (void)centry_uint32(centry);
3898 centry_free(centry);
3900 if (!(state->success)) {
3901 return 1;
3903 DEBUG(10,("validate_gl: %s ok\n", keystr));
3904 return 0;
3907 static int validate_ug(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3908 struct tdb_validation_status *state)
3910 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3911 int32_t num_groups, i;
3913 if (!centry) {
3914 return 1;
3917 num_groups = centry_uint32(centry);
3919 for (i=0; i< num_groups; i++) {
3920 struct dom_sid sid;
3921 centry_sid(centry, &sid);
3924 centry_free(centry);
3926 if (!(state->success)) {
3927 return 1;
3929 DEBUG(10,("validate_ug: %s ok\n", keystr));
3930 return 0;
3933 static int validate_ua(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3934 struct tdb_validation_status *state)
3936 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3937 int32_t num_aliases, i;
3939 if (!centry) {
3940 return 1;
3943 num_aliases = centry_uint32(centry);
3945 for (i=0; i < num_aliases; i++) {
3946 (void)centry_uint32(centry);
3949 centry_free(centry);
3951 if (!(state->success)) {
3952 return 1;
3954 DEBUG(10,("validate_ua: %s ok\n", keystr));
3955 return 0;
3958 static int validate_gm(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3959 struct tdb_validation_status *state)
3961 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3962 int32_t num_names, i;
3964 if (!centry) {
3965 return 1;
3968 num_names = centry_uint32(centry);
3970 for (i=0; i< num_names; i++) {
3971 struct dom_sid sid;
3972 centry_sid(centry, &sid);
3973 (void)centry_string(centry, mem_ctx);
3974 (void)centry_uint32(centry);
3977 centry_free(centry);
3979 if (!(state->success)) {
3980 return 1;
3982 DEBUG(10,("validate_gm: %s ok\n", keystr));
3983 return 0;
3986 static int validate_dr(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3987 struct tdb_validation_status *state)
3989 /* Can't say anything about this other than must be nonzero. */
3990 if (dbuf.dsize == 0) {
3991 DEBUG(0,("validate_dr: Corrupt cache for key %s (len == 0) ?\n",
3992 keystr));
3993 state->bad_entry = true;
3994 state->success = false;
3995 return 1;
3998 DEBUG(10,("validate_dr: %s ok\n", keystr));
3999 return 0;
4002 static int validate_de(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
4003 struct tdb_validation_status *state)
4005 /* Can't say anything about this other than must be nonzero. */
4006 if (dbuf.dsize == 0) {
4007 DEBUG(0,("validate_de: Corrupt cache for key %s (len == 0) ?\n",
4008 keystr));
4009 state->bad_entry = true;
4010 state->success = false;
4011 return 1;
4014 DEBUG(10,("validate_de: %s ok\n", keystr));
4015 return 0;
4018 static int validate_pwinfo(TALLOC_CTX *mem_ctx, const char *keystr,
4019 TDB_DATA dbuf, struct tdb_validation_status *state)
4021 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
4023 if (!centry) {
4024 return 1;
4027 (void)centry_string(centry, mem_ctx);
4028 (void)centry_string(centry, mem_ctx);
4029 (void)centry_string(centry, mem_ctx);
4030 (void)centry_uint32(centry);
4032 centry_free(centry);
4034 if (!(state->success)) {
4035 return 1;
4037 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
4038 return 0;
4041 static int validate_nss_an(TALLOC_CTX *mem_ctx, const char *keystr,
4042 TDB_DATA dbuf,
4043 struct tdb_validation_status *state)
4045 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
4047 if (!centry) {
4048 return 1;
4051 (void)centry_string( centry, mem_ctx );
4053 centry_free(centry);
4055 if (!(state->success)) {
4056 return 1;
4058 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
4059 return 0;
4062 static int validate_nss_na(TALLOC_CTX *mem_ctx, const char *keystr,
4063 TDB_DATA dbuf,
4064 struct tdb_validation_status *state)
4066 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
4068 if (!centry) {
4069 return 1;
4072 (void)centry_string( centry, mem_ctx );
4074 centry_free(centry);
4076 if (!(state->success)) {
4077 return 1;
4079 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
4080 return 0;
4083 static int validate_trustdomcache(TALLOC_CTX *mem_ctx, const char *keystr,
4084 TDB_DATA dbuf,
4085 struct tdb_validation_status *state)
4087 if (dbuf.dsize == 0) {
4088 DEBUG(0, ("validate_trustdomcache: Corrupt cache for "
4089 "key %s (len ==0) ?\n", keystr));
4090 state->bad_entry = true;
4091 state->success = false;
4092 return 1;
4095 DEBUG(10, ("validate_trustdomcache: %s ok\n", keystr));
4096 DEBUGADD(10, (" Don't trust me, I am a DUMMY!\n"));
4097 return 0;
4100 static int validate_offline(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
4101 struct tdb_validation_status *state)
4103 if (dbuf.dsize != 4) {
4104 DEBUG(0,("validate_offline: Corrupt cache for key %s (len %u != 4) ?\n",
4105 keystr, (unsigned int)dbuf.dsize ));
4106 state->bad_entry = true;
4107 state->success = false;
4108 return 1;
4110 DEBUG(10,("validate_offline: %s ok\n", keystr));
4111 return 0;
4114 static int validate_ndr(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
4115 struct tdb_validation_status *state)
4118 * Ignore validation for now. The proper way to do this is with a
4119 * checksum. Just pure parsing does not really catch much.
4121 return 0;
4124 static int validate_cache_version(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
4125 struct tdb_validation_status *state)
4127 if (dbuf.dsize != 4) {
4128 DEBUG(0, ("validate_cache_version: Corrupt cache for "
4129 "key %s (len %u != 4) ?\n",
4130 keystr, (unsigned int)dbuf.dsize));
4131 state->bad_entry = true;
4132 state->success = false;
4133 return 1;
4136 DEBUG(10, ("validate_cache_version: %s ok\n", keystr));
4137 return 0;
4140 /***********************************************************************
4141 A list of all possible cache tdb keys with associated validation
4142 functions.
4143 ***********************************************************************/
4145 struct key_val_struct {
4146 const char *keyname;
4147 int (*validate_data_fn)(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, struct tdb_validation_status* state);
4148 } key_val[] = {
4149 {"SEQNUM/", validate_seqnum},
4150 {"NS/", validate_ns},
4151 {"SN/", validate_sn},
4152 {"U/", validate_u},
4153 {"LOC_POL/", validate_loc_pol},
4154 {"PWD_POL/", validate_pwd_pol},
4155 {"CRED/", validate_cred},
4156 {"UL/", validate_ul},
4157 {"GL/", validate_gl},
4158 {"UG/", validate_ug},
4159 {"UA", validate_ua},
4160 {"GM/", validate_gm},
4161 {"DR/", validate_dr},
4162 {"DE/", validate_de},
4163 {"NSS/PWINFO/", validate_pwinfo},
4164 {"TRUSTDOMCACHE/", validate_trustdomcache},
4165 {"NSS/NA/", validate_nss_na},
4166 {"NSS/AN/", validate_nss_an},
4167 {"WINBINDD_OFFLINE", validate_offline},
4168 {"NDR/", validate_ndr},
4169 {WINBINDD_CACHE_VERSION_KEYSTR, validate_cache_version},
4170 {NULL, NULL}
4173 /***********************************************************************
4174 Function to look at every entry in the tdb and validate it as far as
4175 possible.
4176 ***********************************************************************/
4178 static int cache_traverse_validate_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, void *state)
4180 int i;
4181 unsigned int max_key_len = 1024;
4182 struct tdb_validation_status *v_state = (struct tdb_validation_status *)state;
4184 /* Paranoia check. */
4185 if (strncmp("UA/", (const char *)kbuf.dptr, 3) == 0 ||
4186 strncmp("NDR/", (const char *)kbuf.dptr, 4) == 0) {
4187 max_key_len = 1024 * 1024;
4189 if (kbuf.dsize > max_key_len) {
4190 DEBUG(0, ("cache_traverse_validate_fn: key length too large: "
4191 "(%u) > (%u)\n\n",
4192 (unsigned int)kbuf.dsize, (unsigned int)max_key_len));
4193 return 1;
4196 for (i = 0; key_val[i].keyname; i++) {
4197 size_t namelen = strlen(key_val[i].keyname);
4198 if (kbuf.dsize >= namelen && (
4199 strncmp(key_val[i].keyname, (const char *)kbuf.dptr, namelen)) == 0) {
4200 TALLOC_CTX *mem_ctx;
4201 char *keystr;
4202 int ret;
4204 keystr = SMB_MALLOC_ARRAY(char, kbuf.dsize+1);
4205 if (!keystr) {
4206 return 1;
4208 memcpy(keystr, kbuf.dptr, kbuf.dsize);
4209 keystr[kbuf.dsize] = '\0';
4211 mem_ctx = talloc_init("validate_ctx");
4212 if (!mem_ctx) {
4213 SAFE_FREE(keystr);
4214 return 1;
4217 ret = key_val[i].validate_data_fn(mem_ctx, keystr, dbuf,
4218 v_state);
4220 SAFE_FREE(keystr);
4221 talloc_destroy(mem_ctx);
4222 return ret;
4226 DEBUG(0,("cache_traverse_validate_fn: unknown cache entry\nkey :\n"));
4227 dump_data(0, (uint8_t *)kbuf.dptr, kbuf.dsize);
4228 DEBUG(0,("data :\n"));
4229 dump_data(0, (uint8_t *)dbuf.dptr, dbuf.dsize);
4230 v_state->unknown_key = true;
4231 v_state->success = false;
4232 return 1; /* terminate. */
4235 static void validate_panic(const char *const why)
4237 DEBUG(0,("validating cache: would panic %s\n", why ));
4238 DEBUGADD(0, ("exiting instead (cache validation mode)\n"));
4239 exit(47);
4242 static int wbcache_update_centry_fn(TDB_CONTEXT *tdb,
4243 TDB_DATA key,
4244 TDB_DATA data,
4245 void *state)
4247 uint64_t ctimeout;
4248 TDB_DATA blob;
4250 if (is_non_centry_key(key)) {
4251 return 0;
4254 if (data.dptr == NULL || data.dsize == 0) {
4255 if (tdb_delete(tdb, key) < 0) {
4256 DEBUG(0, ("tdb_delete for [%s] failed!\n",
4257 key.dptr));
4258 return 1;
4262 /* add timeout to blob (uint64_t) */
4263 blob.dsize = data.dsize + 8;
4265 blob.dptr = SMB_XMALLOC_ARRAY(uint8_t, blob.dsize);
4266 if (blob.dptr == NULL) {
4267 return 1;
4269 memset(blob.dptr, 0, blob.dsize);
4271 /* copy status and seqnum */
4272 memcpy(blob.dptr, data.dptr, 8);
4274 /* add timeout */
4275 ctimeout = lp_winbind_cache_time() + time(NULL);
4276 SBVAL(blob.dptr, 8, ctimeout);
4278 /* copy the rest */
4279 memcpy(blob.dptr + 16, data.dptr + 8, data.dsize - 8);
4281 if (tdb_store(tdb, key, blob, TDB_REPLACE) < 0) {
4282 DEBUG(0, ("tdb_store to update [%s] failed!\n",
4283 key.dptr));
4284 SAFE_FREE(blob.dptr);
4285 return 1;
4288 SAFE_FREE(blob.dptr);
4289 return 0;
4292 static bool wbcache_upgrade_v1_to_v2(TDB_CONTEXT *tdb)
4294 int rc;
4296 DEBUG(1, ("Upgrade to version 2 of the winbindd_cache.tdb\n"));
4298 rc = tdb_traverse(tdb, wbcache_update_centry_fn, NULL);
4299 if (rc < 0) {
4300 return false;
4303 return true;
4306 /***********************************************************************
4307 Try and validate every entry in the winbindd cache. If we fail here,
4308 delete the cache tdb and return non-zero.
4309 ***********************************************************************/
4311 int winbindd_validate_cache(void)
4313 int ret = -1;
4314 char *tdb_path = NULL;
4315 TDB_CONTEXT *tdb = NULL;
4316 uint32_t vers_id;
4317 bool ok;
4319 DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
4320 smb_panic_fn = validate_panic;
4322 tdb_path = wcache_path();
4323 if (tdb_path == NULL) {
4324 goto done;
4327 tdb = tdb_open_log(tdb_path,
4328 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
4329 TDB_INCOMPATIBLE_HASH |
4330 ( lp_winbind_offline_logon()
4331 ? TDB_DEFAULT
4332 : TDB_DEFAULT | TDB_CLEAR_IF_FIRST ),
4333 O_RDWR|O_CREAT,
4334 0600);
4335 if (!tdb) {
4336 DEBUG(0, ("winbindd_validate_cache: "
4337 "error opening/initializing tdb\n"));
4338 goto done;
4341 /* Version check and upgrade code. */
4342 if (!tdb_fetch_uint32(tdb, WINBINDD_CACHE_VERSION_KEYSTR, &vers_id)) {
4343 DEBUG(10, ("Fresh database\n"));
4344 tdb_store_uint32(tdb, WINBINDD_CACHE_VERSION_KEYSTR, WINBINDD_CACHE_VERSION);
4345 vers_id = WINBINDD_CACHE_VERSION;
4348 if (vers_id != WINBINDD_CACHE_VERSION) {
4349 if (vers_id == WINBINDD_CACHE_VER1) {
4350 ok = wbcache_upgrade_v1_to_v2(tdb);
4351 if (!ok) {
4352 DEBUG(10, ("winbindd_validate_cache: upgrade to version 2 failed.\n"));
4353 unlink(tdb_path);
4354 goto done;
4357 tdb_store_uint32(tdb,
4358 WINBINDD_CACHE_VERSION_KEYSTR,
4359 WINBINDD_CACHE_VERSION);
4360 vers_id = WINBINDD_CACHE_VER2;
4364 tdb_close(tdb);
4366 ret = tdb_validate_and_backup(tdb_path, cache_traverse_validate_fn);
4368 if (ret != 0) {
4369 DEBUG(10, ("winbindd_validate_cache: validation not successful.\n"));
4370 DEBUGADD(10, ("removing tdb %s.\n", tdb_path));
4371 unlink(tdb_path);
4374 done:
4375 TALLOC_FREE(tdb_path);
4376 DEBUG(10, ("winbindd_validate_cache: restoring panic function\n"));
4377 smb_panic_fn = smb_panic;
4378 return ret;
4381 /***********************************************************************
4382 Try and validate every entry in the winbindd cache.
4383 ***********************************************************************/
4385 int winbindd_validate_cache_nobackup(void)
4387 int ret = -1;
4388 char *tdb_path;
4390 DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
4391 smb_panic_fn = validate_panic;
4393 tdb_path = wcache_path();
4394 if (tdb_path == NULL) {
4395 goto err_panic_restore;
4398 if (wcache == NULL || wcache->tdb == NULL) {
4399 ret = tdb_validate_open(tdb_path, cache_traverse_validate_fn);
4400 } else {
4401 ret = tdb_validate(wcache->tdb, cache_traverse_validate_fn);
4404 if (ret != 0) {
4405 DEBUG(10, ("winbindd_validate_cache_nobackup: validation not "
4406 "successful.\n"));
4409 TALLOC_FREE(tdb_path);
4410 err_panic_restore:
4411 DEBUG(10, ("winbindd_validate_cache_nobackup: restoring panic "
4412 "function\n"));
4413 smb_panic_fn = smb_panic;
4414 return ret;
4417 bool winbindd_cache_validate_and_initialize(void)
4419 close_winbindd_cache();
4421 if (lp_winbind_offline_logon()) {
4422 if (winbindd_validate_cache() < 0) {
4423 DEBUG(0, ("winbindd cache tdb corrupt and no backup "
4424 "could be restored.\n"));
4428 return initialize_winbindd_cache();
4431 /*********************************************************************
4432 ********************************************************************/
4434 static bool add_wbdomain_to_tdc_array( struct winbindd_domain *new_dom,
4435 struct winbindd_tdc_domain **domains,
4436 size_t *num_domains )
4438 struct winbindd_tdc_domain *list = NULL;
4439 size_t idx;
4440 int i;
4441 bool set_only = false;
4443 /* don't allow duplicates */
4445 idx = *num_domains;
4446 list = *domains;
4448 for ( i=0; i< (*num_domains); i++ ) {
4449 if ( strequal( new_dom->name, list[i].domain_name ) ) {
4450 DEBUG(10,("add_wbdomain_to_tdc_array: Found existing record for %s\n",
4451 new_dom->name));
4452 idx = i;
4453 set_only = true;
4455 break;
4459 if ( !set_only ) {
4460 if ( !*domains ) {
4461 list = talloc_array( NULL, struct winbindd_tdc_domain, 1 );
4462 idx = 0;
4463 } else {
4464 list = talloc_realloc( *domains, *domains,
4465 struct winbindd_tdc_domain,
4466 (*num_domains)+1);
4467 idx = *num_domains;
4470 ZERO_STRUCT( list[idx] );
4473 if ( !list )
4474 return false;
4476 list[idx].domain_name = talloc_strdup(list, new_dom->name);
4477 if (list[idx].domain_name == NULL) {
4478 return false;
4480 if (new_dom->alt_name != NULL) {
4481 list[idx].dns_name = talloc_strdup(list, new_dom->alt_name);
4482 if (list[idx].dns_name == NULL) {
4483 return false;
4487 if ( !is_null_sid( &new_dom->sid ) ) {
4488 sid_copy( &list[idx].sid, &new_dom->sid );
4489 } else {
4490 sid_copy(&list[idx].sid, &global_sid_NULL);
4493 if ( new_dom->domain_flags != 0x0 )
4494 list[idx].trust_flags = new_dom->domain_flags;
4496 if ( new_dom->domain_type != 0x0 )
4497 list[idx].trust_type = new_dom->domain_type;
4499 if ( new_dom->domain_trust_attribs != 0x0 )
4500 list[idx].trust_attribs = new_dom->domain_trust_attribs;
4502 if ( !set_only ) {
4503 *domains = list;
4504 *num_domains = idx + 1;
4507 return true;
4510 /*********************************************************************
4511 ********************************************************************/
4513 static TDB_DATA make_tdc_key( const char *domain_name )
4515 char *keystr = NULL;
4516 TDB_DATA key = { NULL, 0 };
4518 if ( !domain_name ) {
4519 DEBUG(5,("make_tdc_key: Keyname workgroup is NULL!\n"));
4520 return key;
4523 if (asprintf( &keystr, "TRUSTDOMCACHE/%s", domain_name ) == -1) {
4524 return key;
4526 key = string_term_tdb_data(keystr);
4528 return key;
4531 /*********************************************************************
4532 ********************************************************************/
4534 static int pack_tdc_domains( struct winbindd_tdc_domain *domains,
4535 size_t num_domains,
4536 unsigned char **buf )
4538 unsigned char *buffer = NULL;
4539 int len = 0;
4540 int buflen = 0;
4541 int i = 0;
4543 DEBUG(10,("pack_tdc_domains: Packing %d trusted domains\n",
4544 (int)num_domains));
4546 buflen = 0;
4548 again:
4549 len = 0;
4551 /* Store the number of array items first */
4552 len += tdb_pack( buffer+len, buflen-len, "d",
4553 num_domains );
4555 /* now pack each domain trust record */
4556 for ( i=0; i<num_domains; i++ ) {
4558 fstring tmp;
4560 if ( buflen > 0 ) {
4561 DEBUG(10,("pack_tdc_domains: Packing domain %s (%s)\n",
4562 domains[i].domain_name,
4563 domains[i].dns_name ? domains[i].dns_name : "UNKNOWN" ));
4566 len += tdb_pack( buffer+len, buflen-len, "fffddd",
4567 domains[i].domain_name,
4568 domains[i].dns_name ? domains[i].dns_name : "",
4569 sid_to_fstring(tmp, &domains[i].sid),
4570 domains[i].trust_flags,
4571 domains[i].trust_attribs,
4572 domains[i].trust_type );
4575 if ( buflen < len ) {
4576 SAFE_FREE(buffer);
4577 if ( (buffer = SMB_MALLOC_ARRAY(unsigned char, len)) == NULL ) {
4578 DEBUG(0,("pack_tdc_domains: failed to alloc buffer!\n"));
4579 buflen = -1;
4580 goto done;
4582 buflen = len;
4583 goto again;
4586 *buf = buffer;
4588 done:
4589 return buflen;
4592 /*********************************************************************
4593 ********************************************************************/
4595 static size_t unpack_tdc_domains( unsigned char *buf, int buflen,
4596 struct winbindd_tdc_domain **domains )
4598 fstring domain_name, dns_name, sid_string;
4599 uint32_t type, attribs, flags;
4600 int num_domains;
4601 int len = 0;
4602 int i;
4603 struct winbindd_tdc_domain *list = NULL;
4605 /* get the number of domains */
4606 len += tdb_unpack( buf+len, buflen-len, "d", &num_domains);
4607 if ( len == -1 ) {
4608 DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
4609 return 0;
4612 list = talloc_array( NULL, struct winbindd_tdc_domain, num_domains );
4613 if ( !list ) {
4614 DEBUG(0,("unpack_tdc_domains: Failed to talloc() domain list!\n"));
4615 return 0;
4618 for ( i=0; i<num_domains; i++ ) {
4619 int this_len;
4621 this_len = tdb_unpack( buf+len, buflen-len, "fffddd",
4622 domain_name,
4623 dns_name,
4624 sid_string,
4625 &flags,
4626 &attribs,
4627 &type );
4629 if ( this_len == -1 ) {
4630 DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
4631 TALLOC_FREE( list );
4632 return 0;
4634 len += this_len;
4636 DEBUG(11,("unpack_tdc_domains: Unpacking domain %s (%s) "
4637 "SID %s, flags = 0x%x, attribs = 0x%x, type = 0x%x\n",
4638 domain_name, dns_name, sid_string,
4639 flags, attribs, type));
4641 list[i].domain_name = talloc_strdup( list, domain_name );
4642 list[i].dns_name = NULL;
4643 if (dns_name[0] != '\0') {
4644 list[i].dns_name = talloc_strdup(list, dns_name);
4646 if ( !string_to_sid( &(list[i].sid), sid_string ) ) {
4647 DEBUG(10,("unpack_tdc_domains: no SID for domain %s\n",
4648 domain_name));
4650 list[i].trust_flags = flags;
4651 list[i].trust_attribs = attribs;
4652 list[i].trust_type = type;
4655 *domains = list;
4657 return num_domains;
4660 /*********************************************************************
4661 ********************************************************************/
4663 static bool wcache_tdc_store_list( struct winbindd_tdc_domain *domains, size_t num_domains )
4665 TDB_DATA key = make_tdc_key( lp_workgroup() );
4666 TDB_DATA data = { NULL, 0 };
4667 int ret;
4669 if ( !key.dptr )
4670 return false;
4672 /* See if we were asked to delete the cache entry */
4674 if ( !domains ) {
4675 ret = tdb_delete( wcache->tdb, key );
4676 goto done;
4679 data.dsize = pack_tdc_domains( domains, num_domains, &data.dptr );
4681 if ( !data.dptr ) {
4682 ret = -1;
4683 goto done;
4686 ret = tdb_store( wcache->tdb, key, data, 0 );
4688 done:
4689 SAFE_FREE( data.dptr );
4690 SAFE_FREE( key.dptr );
4692 return ( ret == 0 );
4695 /*********************************************************************
4696 ********************************************************************/
4698 bool wcache_tdc_fetch_list( struct winbindd_tdc_domain **domains, size_t *num_domains )
4700 TDB_DATA key = make_tdc_key( lp_workgroup() );
4701 TDB_DATA data = { NULL, 0 };
4703 *domains = NULL;
4704 *num_domains = 0;
4706 if ( !key.dptr )
4707 return false;
4709 data = tdb_fetch( wcache->tdb, key );
4711 SAFE_FREE( key.dptr );
4713 if ( !data.dptr )
4714 return false;
4716 *num_domains = unpack_tdc_domains( data.dptr, data.dsize, domains );
4718 SAFE_FREE( data.dptr );
4720 if ( !*domains )
4721 return false;
4723 return true;
4726 /*********************************************************************
4727 ********************************************************************/
4729 bool wcache_tdc_add_domain( struct winbindd_domain *domain )
4731 struct winbindd_tdc_domain *dom_list = NULL;
4732 size_t num_domains = 0;
4733 bool ret = false;
4735 DEBUG(10,("wcache_tdc_add_domain: Adding domain %s (%s), SID %s, "
4736 "flags = 0x%x, attributes = 0x%x, type = 0x%x\n",
4737 domain->name, domain->alt_name,
4738 sid_string_dbg(&domain->sid),
4739 domain->domain_flags,
4740 domain->domain_trust_attribs,
4741 domain->domain_type));
4743 if ( !init_wcache() ) {
4744 return false;
4747 /* fetch the list */
4749 wcache_tdc_fetch_list( &dom_list, &num_domains );
4751 /* add the new domain */
4753 if ( !add_wbdomain_to_tdc_array( domain, &dom_list, &num_domains ) ) {
4754 goto done;
4757 /* pack the domain */
4759 if ( !wcache_tdc_store_list( dom_list, num_domains ) ) {
4760 goto done;
4763 /* Success */
4765 ret = true;
4766 done:
4767 TALLOC_FREE( dom_list );
4769 return ret;
4772 static struct winbindd_tdc_domain *wcache_tdc_dup_domain(
4773 TALLOC_CTX *mem_ctx, const struct winbindd_tdc_domain *src)
4775 struct winbindd_tdc_domain *dst;
4777 dst = talloc(mem_ctx, struct winbindd_tdc_domain);
4778 if (dst == NULL) {
4779 goto fail;
4781 dst->domain_name = talloc_strdup(dst, src->domain_name);
4782 if (dst->domain_name == NULL) {
4783 goto fail;
4786 dst->dns_name = NULL;
4787 if (src->dns_name != NULL) {
4788 dst->dns_name = talloc_strdup(dst, src->dns_name);
4789 if (dst->dns_name == NULL) {
4790 goto fail;
4794 sid_copy(&dst->sid, &src->sid);
4795 dst->trust_flags = src->trust_flags;
4796 dst->trust_type = src->trust_type;
4797 dst->trust_attribs = src->trust_attribs;
4798 return dst;
4799 fail:
4800 TALLOC_FREE(dst);
4801 return NULL;
4804 /*********************************************************************
4805 ********************************************************************/
4807 struct winbindd_tdc_domain * wcache_tdc_fetch_domain( TALLOC_CTX *ctx, const char *name )
4809 struct winbindd_tdc_domain *dom_list = NULL;
4810 size_t num_domains = 0;
4811 int i;
4812 struct winbindd_tdc_domain *d = NULL;
4814 DEBUG(10,("wcache_tdc_fetch_domain: Searching for domain %s\n", name));
4816 if ( !init_wcache() ) {
4817 return NULL;
4820 /* fetch the list */
4822 wcache_tdc_fetch_list( &dom_list, &num_domains );
4824 for ( i=0; i<num_domains; i++ ) {
4825 if ( strequal(name, dom_list[i].domain_name) ||
4826 strequal(name, dom_list[i].dns_name) )
4828 DEBUG(10,("wcache_tdc_fetch_domain: Found domain %s\n",
4829 name));
4831 d = wcache_tdc_dup_domain(ctx, &dom_list[i]);
4832 break;
4836 TALLOC_FREE( dom_list );
4838 return d;
4841 /*********************************************************************
4842 ********************************************************************/
4844 struct winbindd_tdc_domain*
4845 wcache_tdc_fetch_domainbysid(TALLOC_CTX *ctx,
4846 const struct dom_sid *sid)
4848 struct winbindd_tdc_domain *dom_list = NULL;
4849 size_t num_domains = 0;
4850 int i;
4851 struct winbindd_tdc_domain *d = NULL;
4853 DEBUG(10,("wcache_tdc_fetch_domainbysid: Searching for domain %s\n",
4854 sid_string_dbg(sid)));
4856 if (!init_wcache()) {
4857 return NULL;
4860 /* fetch the list */
4862 wcache_tdc_fetch_list(&dom_list, &num_domains);
4864 for (i = 0; i<num_domains; i++) {
4865 if (dom_sid_equal(sid, &(dom_list[i].sid))) {
4866 DEBUG(10, ("wcache_tdc_fetch_domainbysid: "
4867 "Found domain %s for SID %s\n",
4868 dom_list[i].domain_name,
4869 sid_string_dbg(sid)));
4871 d = wcache_tdc_dup_domain(ctx, &dom_list[i]);
4872 break;
4876 TALLOC_FREE(dom_list);
4878 return d;
4882 /*********************************************************************
4883 ********************************************************************/
4885 void wcache_tdc_clear( void )
4887 if ( !init_wcache() )
4888 return;
4890 wcache_tdc_store_list( NULL, 0 );
4892 return;
4896 /*********************************************************************
4897 ********************************************************************/
4899 static void wcache_save_user_pwinfo(struct winbindd_domain *domain,
4900 NTSTATUS status,
4901 const struct dom_sid *user_sid,
4902 const char *homedir,
4903 const char *shell,
4904 const char *gecos,
4905 uint32_t gid)
4907 struct cache_entry *centry;
4908 fstring tmp;
4910 if ( (centry = centry_start(domain, status)) == NULL )
4911 return;
4913 centry_put_string( centry, homedir );
4914 centry_put_string( centry, shell );
4915 centry_put_string( centry, gecos );
4916 centry_put_uint32( centry, gid );
4918 centry_end(centry, "NSS/PWINFO/%s", sid_to_fstring(tmp, user_sid) );
4920 DEBUG(10,("wcache_save_user_pwinfo: %s\n", sid_string_dbg(user_sid) ));
4922 centry_free(centry);
4925 #ifdef HAVE_ADS
4927 NTSTATUS nss_get_info_cached( struct winbindd_domain *domain,
4928 const struct dom_sid *user_sid,
4929 TALLOC_CTX *ctx,
4930 const char **homedir, const char **shell,
4931 const char **gecos, gid_t *p_gid)
4933 struct winbind_cache *cache = get_cache(domain);
4934 struct cache_entry *centry = NULL;
4935 NTSTATUS nt_status;
4936 fstring tmp;
4938 if (!cache->tdb)
4939 goto do_query;
4941 centry = wcache_fetch(cache, domain, "NSS/PWINFO/%s",
4942 sid_to_fstring(tmp, user_sid));
4944 if (!centry)
4945 goto do_query;
4947 *homedir = centry_string( centry, ctx );
4948 *shell = centry_string( centry, ctx );
4949 *gecos = centry_string( centry, ctx );
4950 *p_gid = centry_uint32( centry );
4952 centry_free(centry);
4954 DEBUG(10,("nss_get_info_cached: [Cached] - user_sid %s\n",
4955 sid_string_dbg(user_sid)));
4957 return NT_STATUS_OK;
4959 do_query:
4961 nt_status = nss_get_info( domain->name, user_sid, ctx,
4962 homedir, shell, gecos, p_gid );
4964 DEBUG(10, ("nss_get_info returned %s\n", nt_errstr(nt_status)));
4966 if ( NT_STATUS_IS_OK(nt_status) ) {
4967 DEBUG(10, ("result:\n\thomedir = '%s'\n", *homedir));
4968 DEBUGADD(10, ("\tshell = '%s'\n", *shell));
4969 DEBUGADD(10, ("\tgecos = '%s'\n", *gecos));
4970 DEBUGADD(10, ("\tgid = '%u'\n", (unsigned int)*p_gid));
4972 wcache_save_user_pwinfo( domain, nt_status, user_sid,
4973 *homedir, *shell, *gecos, *p_gid );
4976 if ( NT_STATUS_EQUAL( nt_status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND ) ) {
4977 DEBUG(5,("nss_get_info_cached: Setting domain %s offline\n",
4978 domain->name ));
4979 set_domain_offline( domain );
4982 return nt_status;
4985 #endif
4987 static bool wcache_ndr_key(TALLOC_CTX *mem_ctx, const char *domain_name,
4988 uint32_t opnum, const DATA_BLOB *req,
4989 TDB_DATA *pkey)
4991 char *key;
4992 size_t keylen;
4994 key = talloc_asprintf(mem_ctx, "NDR/%s/%d/", domain_name, (int)opnum);
4995 if (key == NULL) {
4996 return false;
4998 keylen = talloc_get_size(key) - 1;
5000 key = talloc_realloc(mem_ctx, key, char, keylen + req->length);
5001 if (key == NULL) {
5002 return false;
5004 memcpy(key + keylen, req->data, req->length);
5006 pkey->dptr = (uint8_t *)key;
5007 pkey->dsize = talloc_get_size(key);
5008 return true;
5011 static bool wcache_opnum_cacheable(uint32_t opnum)
5013 switch (opnum) {
5014 case NDR_WBINT_PING:
5015 case NDR_WBINT_QUERYSEQUENCENUMBER:
5016 case NDR_WBINT_ALLOCATEUID:
5017 case NDR_WBINT_ALLOCATEGID:
5018 case NDR_WBINT_CHECKMACHINEACCOUNT:
5019 case NDR_WBINT_CHANGEMACHINEACCOUNT:
5020 case NDR_WBINT_PINGDC:
5021 return false;
5023 return true;
5026 bool wcache_fetch_ndr(TALLOC_CTX *mem_ctx, struct winbindd_domain *domain,
5027 uint32_t opnum, const DATA_BLOB *req, DATA_BLOB *resp)
5029 TDB_DATA key, data;
5030 bool ret = false;
5032 if (!wcache_opnum_cacheable(opnum) ||
5033 is_my_own_sam_domain(domain) ||
5034 is_builtin_domain(domain)) {
5035 return false;
5038 if (wcache->tdb == NULL) {
5039 return false;
5042 if (!wcache_ndr_key(talloc_tos(), domain->name, opnum, req, &key)) {
5043 return false;
5045 data = tdb_fetch(wcache->tdb, key);
5046 TALLOC_FREE(key.dptr);
5048 if (data.dptr == NULL) {
5049 return false;
5051 if (data.dsize < 12) {
5052 goto fail;
5055 if (!is_domain_offline(domain)) {
5056 uint32_t entry_seqnum, dom_seqnum, last_check;
5057 uint64_t entry_timeout;
5059 if (!wcache_fetch_seqnum(domain->name, &dom_seqnum,
5060 &last_check)) {
5061 goto fail;
5063 entry_seqnum = IVAL(data.dptr, 0);
5064 if (entry_seqnum != dom_seqnum) {
5065 DEBUG(10, ("Entry has wrong sequence number: %d\n",
5066 (int)entry_seqnum));
5067 goto fail;
5069 entry_timeout = BVAL(data.dptr, 4);
5070 if (time(NULL) > entry_timeout) {
5071 DEBUG(10, ("Entry has timed out\n"));
5072 goto fail;
5076 resp->data = (uint8_t *)talloc_memdup(mem_ctx, data.dptr + 12,
5077 data.dsize - 12);
5078 if (resp->data == NULL) {
5079 DEBUG(10, ("talloc failed\n"));
5080 goto fail;
5082 resp->length = data.dsize - 12;
5084 ret = true;
5085 fail:
5086 SAFE_FREE(data.dptr);
5087 return ret;
5090 void wcache_store_ndr(struct winbindd_domain *domain, uint32_t opnum,
5091 const DATA_BLOB *req, const DATA_BLOB *resp)
5093 TDB_DATA key, data;
5094 uint32_t dom_seqnum, last_check;
5095 uint64_t timeout;
5097 if (!wcache_opnum_cacheable(opnum) ||
5098 is_my_own_sam_domain(domain) ||
5099 is_builtin_domain(domain)) {
5100 return;
5103 if (wcache->tdb == NULL) {
5104 return;
5107 if (!wcache_fetch_seqnum(domain->name, &dom_seqnum, &last_check)) {
5108 DEBUG(10, ("could not fetch seqnum for domain %s\n",
5109 domain->name));
5110 return;
5113 if (!wcache_ndr_key(talloc_tos(), domain->name, opnum, req, &key)) {
5114 return;
5117 timeout = time(NULL) + lp_winbind_cache_time();
5119 data.dsize = resp->length + 12;
5120 data.dptr = talloc_array(key.dptr, uint8_t, data.dsize);
5121 if (data.dptr == NULL) {
5122 goto done;
5125 SIVAL(data.dptr, 0, dom_seqnum);
5126 SBVAL(data.dptr, 4, timeout);
5127 memcpy(data.dptr + 12, resp->data, resp->length);
5129 tdb_store(wcache->tdb, key, data, 0);
5131 done:
5132 TALLOC_FREE(key.dptr);
5133 return;