WHATSNEW: Add release notes for Samba 4.0.24.
[Samba.git] / source3 / winbindd / winbindd_cache.c
blob050ec6c239405972eda4fa38e29e1fc45e1034af
1 /*
2 Unix SMB/CIFS implementation.
4 Winbind cache backend functions
6 Copyright (C) Andrew Tridgell 2001
7 Copyright (C) Gerald Carter 2003-2007
8 Copyright (C) Volker Lendecke 2005
9 Copyright (C) Guenther Deschner 2005
10 Copyright (C) Michael Adam 2007
12 This program is free software; you can redistribute it and/or modify
13 it under the terms of the GNU General Public License as published by
14 the Free Software Foundation; either version 3 of the License, or
15 (at your option) any later version.
17 This program is distributed in the hope that it will be useful,
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 GNU General Public License for more details.
22 You should have received a copy of the GNU General Public License
23 along with this program. If not, see <http://www.gnu.org/licenses/>.
26 #include "includes.h"
27 #include "system/filesys.h"
28 #include "winbindd.h"
29 #include "tdb_validate.h"
30 #include "../libcli/auth/libcli_auth.h"
31 #include "../librpc/gen_ndr/ndr_wbint.h"
32 #include "ads.h"
33 #include "nss_info.h"
34 #include "../libcli/security/security.h"
35 #include "passdb/machine_sid.h"
36 #include "util_tdb.h"
38 #undef DBGC_CLASS
39 #define DBGC_CLASS DBGC_WINBIND
41 #define WINBINDD_CACHE_VER1 1 /* initial db version */
42 #define WINBINDD_CACHE_VER2 2 /* second version with timeouts for NDR entries */
44 #define WINBINDD_CACHE_VERSION WINBINDD_CACHE_VER2
45 #define WINBINDD_CACHE_VERSION_KEYSTR "WINBINDD_CACHE_VERSION"
47 extern struct winbindd_methods reconnect_methods;
48 #ifdef HAVE_ADS
49 extern struct winbindd_methods ads_methods;
50 #endif
51 extern struct winbindd_methods builtin_passdb_methods;
52 extern struct winbindd_methods sam_passdb_methods;
55 * JRA. KEEP THIS LIST UP TO DATE IF YOU ADD CACHE ENTRIES.
56 * Here are the list of entry types that are *not* stored
57 * as form struct cache_entry in the cache.
60 static const char *non_centry_keys[] = {
61 "SEQNUM/",
62 "WINBINDD_OFFLINE",
63 WINBINDD_CACHE_VERSION_KEYSTR,
64 NULL
67 /************************************************************************
68 Is this key a non-centry type ?
69 ************************************************************************/
71 static bool is_non_centry_key(TDB_DATA kbuf)
73 int i;
75 if (kbuf.dptr == NULL || kbuf.dsize == 0) {
76 return false;
78 for (i = 0; non_centry_keys[i] != NULL; i++) {
79 size_t namelen = strlen(non_centry_keys[i]);
80 if (kbuf.dsize < namelen) {
81 continue;
83 if (strncmp(non_centry_keys[i], (const char *)kbuf.dptr, namelen) == 0) {
84 return true;
87 return false;
90 /* Global online/offline state - False when online. winbindd starts up online
91 and sets this to true if the first query fails and there's an entry in
92 the cache tdb telling us to stay offline. */
94 static bool global_winbindd_offline_state;
96 struct winbind_cache {
97 TDB_CONTEXT *tdb;
100 struct cache_entry {
101 NTSTATUS status;
102 uint32 sequence_number;
103 uint64_t timeout;
104 uint8 *data;
105 uint32 len, ofs;
108 void (*smb_panic_fn)(const char *const why) = smb_panic;
110 #define WINBINDD_MAX_CACHE_SIZE (50*1024*1024)
112 static struct winbind_cache *wcache;
114 /* get the winbind_cache structure */
115 static struct winbind_cache *get_cache(struct winbindd_domain *domain)
117 struct winbind_cache *ret = wcache;
119 /* We have to know what type of domain we are dealing with first. */
121 if (domain->internal) {
122 domain->backend = &builtin_passdb_methods;
123 domain->initialized = True;
126 if (strequal(domain->name, get_global_sam_name()) &&
127 sid_check_is_our_sam(&domain->sid)) {
128 domain->backend = &sam_passdb_methods;
129 domain->initialized = True;
132 if ( !domain->initialized ) {
133 init_dc_connection( domain );
137 OK. listen up becasue I'm only going to say this once.
138 We have the following scenarios to consider
139 (a) trusted AD domains on a Samba DC,
140 (b) trusted AD domains and we are joined to a non-kerberos domain
141 (c) trusted AD domains and we are joined to a kerberos (AD) domain
143 For (a) we can always contact the trusted domain using krb5
144 since we have the domain trust account password
146 For (b) we can only use RPC since we have no way of
147 getting a krb5 ticket in our own domain
149 For (c) we can always use krb5 since we have a kerberos trust
151 --jerry
154 if (!domain->backend) {
155 #ifdef HAVE_ADS
156 struct winbindd_domain *our_domain = domain;
158 /* find our domain first so we can figure out if we
159 are joined to a kerberized domain */
161 if ( !domain->primary )
162 our_domain = find_our_domain();
164 if ((our_domain->active_directory || IS_DC)
165 && domain->active_directory
166 && !lp_winbind_rpc_only()) {
167 DEBUG(5,("get_cache: Setting ADS methods for domain %s\n", domain->name));
168 domain->backend = &ads_methods;
169 } else {
170 #endif /* HAVE_ADS */
171 DEBUG(5,("get_cache: Setting MS-RPC methods for domain %s\n", domain->name));
172 domain->backend = &reconnect_methods;
173 #ifdef HAVE_ADS
175 #endif /* HAVE_ADS */
178 if (ret)
179 return ret;
181 ret = SMB_XMALLOC_P(struct winbind_cache);
182 ZERO_STRUCTP(ret);
184 wcache = ret;
185 wcache_flush_cache();
187 return ret;
191 free a centry structure
193 static void centry_free(struct cache_entry *centry)
195 if (!centry)
196 return;
197 SAFE_FREE(centry->data);
198 free(centry);
201 static bool centry_check_bytes(struct cache_entry *centry, size_t nbytes)
203 if (centry->len - centry->ofs < nbytes) {
204 DEBUG(0,("centry corruption? needed %u bytes, have %d\n",
205 (unsigned int)nbytes,
206 centry->len - centry->ofs));
207 return false;
209 return true;
213 pull a uint64_t from a cache entry
215 static uint64_t centry_uint64_t(struct cache_entry *centry)
217 uint64_t ret;
219 if (!centry_check_bytes(centry, 8)) {
220 smb_panic_fn("centry_uint64_t");
222 ret = BVAL(centry->data, centry->ofs);
223 centry->ofs += 8;
224 return ret;
228 pull a uint32 from a cache entry
230 static uint32 centry_uint32(struct cache_entry *centry)
232 uint32 ret;
234 if (!centry_check_bytes(centry, 4)) {
235 smb_panic_fn("centry_uint32");
237 ret = IVAL(centry->data, centry->ofs);
238 centry->ofs += 4;
239 return ret;
243 pull a uint16 from a cache entry
245 static uint16 centry_uint16(struct cache_entry *centry)
247 uint16 ret;
248 if (!centry_check_bytes(centry, 2)) {
249 smb_panic_fn("centry_uint16");
251 ret = SVAL(centry->data, centry->ofs);
252 centry->ofs += 2;
253 return ret;
257 pull a uint8 from a cache entry
259 static uint8 centry_uint8(struct cache_entry *centry)
261 uint8 ret;
262 if (!centry_check_bytes(centry, 1)) {
263 smb_panic_fn("centry_uint8");
265 ret = CVAL(centry->data, centry->ofs);
266 centry->ofs += 1;
267 return ret;
271 pull a NTTIME from a cache entry
273 static NTTIME centry_nttime(struct cache_entry *centry)
275 NTTIME ret;
276 if (!centry_check_bytes(centry, 8)) {
277 smb_panic_fn("centry_nttime");
279 ret = IVAL(centry->data, centry->ofs);
280 centry->ofs += 4;
281 ret += (uint64)IVAL(centry->data, centry->ofs) << 32;
282 centry->ofs += 4;
283 return ret;
287 pull a time_t from a cache entry. time_t stored portably as a 64-bit time.
289 static time_t centry_time(struct cache_entry *centry)
291 return (time_t)centry_nttime(centry);
294 /* pull a string from a cache entry, using the supplied
295 talloc context
297 static char *centry_string(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
299 uint32 len;
300 char *ret;
302 len = centry_uint8(centry);
304 if (len == 0xFF) {
305 /* a deliberate NULL string */
306 return NULL;
309 if (!centry_check_bytes(centry, (size_t)len)) {
310 smb_panic_fn("centry_string");
313 ret = talloc_array(mem_ctx, char, len+1);
314 if (!ret) {
315 smb_panic_fn("centry_string out of memory\n");
317 memcpy(ret,centry->data + centry->ofs, len);
318 ret[len] = 0;
319 centry->ofs += len;
320 return ret;
323 /* pull a hash16 from a cache entry, using the supplied
324 talloc context
326 static char *centry_hash16(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
328 uint32 len;
329 char *ret;
331 len = centry_uint8(centry);
333 if (len != 16) {
334 DEBUG(0,("centry corruption? hash len (%u) != 16\n",
335 len ));
336 return NULL;
339 if (!centry_check_bytes(centry, 16)) {
340 return NULL;
343 ret = talloc_array(mem_ctx, char, 16);
344 if (!ret) {
345 smb_panic_fn("centry_hash out of memory\n");
347 memcpy(ret,centry->data + centry->ofs, 16);
348 centry->ofs += 16;
349 return ret;
352 /* pull a sid from a cache entry, using the supplied
353 talloc context
355 static bool centry_sid(struct cache_entry *centry, struct dom_sid *sid)
357 char *sid_string;
358 bool ret;
360 sid_string = centry_string(centry, talloc_tos());
361 if (sid_string == NULL) {
362 return false;
364 ret = string_to_sid(sid, sid_string);
365 TALLOC_FREE(sid_string);
366 return ret;
371 pull a NTSTATUS from a cache entry
373 static NTSTATUS centry_ntstatus(struct cache_entry *centry)
375 NTSTATUS status;
377 status = NT_STATUS(centry_uint32(centry));
378 return status;
382 /* the server is considered down if it can't give us a sequence number */
383 static bool wcache_server_down(struct winbindd_domain *domain)
385 bool ret;
387 if (!wcache->tdb)
388 return false;
390 ret = (domain->sequence_number == DOM_SEQUENCE_NONE);
392 if (ret)
393 DEBUG(10,("wcache_server_down: server for Domain %s down\n",
394 domain->name ));
395 return ret;
398 static bool wcache_fetch_seqnum(const char *domain_name, uint32_t *seqnum,
399 uint32_t *last_seq_check)
401 char *key;
402 TDB_DATA data;
404 if (wcache->tdb == NULL) {
405 DEBUG(10,("wcache_fetch_seqnum: tdb == NULL\n"));
406 return false;
409 key = talloc_asprintf(talloc_tos(), "SEQNUM/%s", domain_name);
410 if (key == NULL) {
411 DEBUG(10, ("talloc failed\n"));
412 return false;
415 data = tdb_fetch_bystring(wcache->tdb, key);
416 TALLOC_FREE(key);
418 if (data.dptr == NULL) {
419 DEBUG(10, ("wcache_fetch_seqnum: %s not found\n",
420 domain_name));
421 return false;
423 if (data.dsize != 8) {
424 DEBUG(10, ("wcache_fetch_seqnum: invalid data size %d\n",
425 (int)data.dsize));
426 SAFE_FREE(data.dptr);
427 return false;
430 *seqnum = IVAL(data.dptr, 0);
431 *last_seq_check = IVAL(data.dptr, 4);
432 SAFE_FREE(data.dptr);
434 return true;
437 static NTSTATUS fetch_cache_seqnum( struct winbindd_domain *domain, time_t now )
439 uint32 last_check, time_diff;
441 if (!wcache_fetch_seqnum(domain->name, &domain->sequence_number,
442 &last_check)) {
443 return NT_STATUS_UNSUCCESSFUL;
445 domain->last_seq_check = last_check;
447 /* have we expired? */
449 time_diff = now - domain->last_seq_check;
450 if ( time_diff > lp_winbind_cache_time() ) {
451 DEBUG(10,("fetch_cache_seqnum: timeout [%s][%u @ %u]\n",
452 domain->name, domain->sequence_number,
453 (uint32)domain->last_seq_check));
454 return NT_STATUS_UNSUCCESSFUL;
457 DEBUG(10,("fetch_cache_seqnum: success [%s][%u @ %u]\n",
458 domain->name, domain->sequence_number,
459 (uint32)domain->last_seq_check));
461 return NT_STATUS_OK;
464 bool wcache_store_seqnum(const char *domain_name, uint32_t seqnum,
465 time_t last_seq_check)
467 char *key_str;
468 uint8_t buf[8];
469 int ret;
471 if (wcache->tdb == NULL) {
472 DEBUG(10, ("wcache_store_seqnum: wcache->tdb == NULL\n"));
473 return false;
476 key_str = talloc_asprintf(talloc_tos(), "SEQNUM/%s", domain_name);
477 if (key_str == NULL) {
478 DEBUG(10, ("talloc_asprintf failed\n"));
479 return false;
482 SIVAL(buf, 0, seqnum);
483 SIVAL(buf, 4, last_seq_check);
485 ret = tdb_store_bystring(wcache->tdb, key_str,
486 make_tdb_data(buf, sizeof(buf)), TDB_REPLACE);
487 TALLOC_FREE(key_str);
488 if (ret != 0) {
489 DEBUG(10, ("tdb_store_bystring failed: %s\n",
490 tdb_errorstr_compat(wcache->tdb)));
491 TALLOC_FREE(key_str);
492 return false;
495 DEBUG(10, ("wcache_store_seqnum: success [%s][%u @ %u]\n",
496 domain_name, seqnum, (unsigned)last_seq_check));
498 return true;
501 static bool store_cache_seqnum( struct winbindd_domain *domain )
503 return wcache_store_seqnum(domain->name, domain->sequence_number,
504 domain->last_seq_check);
508 refresh the domain sequence number. If force is true
509 then always refresh it, no matter how recently we fetched it
512 static void refresh_sequence_number(struct winbindd_domain *domain, bool force)
514 NTSTATUS status;
515 unsigned time_diff;
516 time_t t = time(NULL);
517 unsigned cache_time = lp_winbind_cache_time();
519 if (is_domain_offline(domain)) {
520 return;
523 get_cache( domain );
525 #if 0 /* JERRY -- disable as the default cache time is now 5 minutes */
526 /* trying to reconnect is expensive, don't do it too often */
527 if (domain->sequence_number == DOM_SEQUENCE_NONE) {
528 cache_time *= 8;
530 #endif
532 time_diff = t - domain->last_seq_check;
534 /* see if we have to refetch the domain sequence number */
535 if (!force && (time_diff < cache_time) &&
536 (domain->sequence_number != DOM_SEQUENCE_NONE) &&
537 NT_STATUS_IS_OK(domain->last_status)) {
538 DEBUG(10, ("refresh_sequence_number: %s time ok\n", domain->name));
539 goto done;
542 /* try to get the sequence number from the tdb cache first */
543 /* this will update the timestamp as well */
545 status = fetch_cache_seqnum( domain, t );
546 if (NT_STATUS_IS_OK(status) &&
547 (domain->sequence_number != DOM_SEQUENCE_NONE) &&
548 NT_STATUS_IS_OK(domain->last_status)) {
549 goto done;
552 /* important! make sure that we know if this is a native
553 mode domain or not. And that we can contact it. */
555 if ( winbindd_can_contact_domain( domain ) ) {
556 status = domain->backend->sequence_number(domain,
557 &domain->sequence_number);
558 } else {
559 /* just use the current time */
560 status = NT_STATUS_OK;
561 domain->sequence_number = time(NULL);
565 /* the above call could have set our domain->backend to NULL when
566 * coming from offline to online mode, make sure to reinitialize the
567 * backend - Guenther */
568 get_cache( domain );
570 if (!NT_STATUS_IS_OK(status)) {
571 DEBUG(10,("refresh_sequence_number: failed with %s\n", nt_errstr(status)));
572 domain->sequence_number = DOM_SEQUENCE_NONE;
575 domain->last_status = status;
576 domain->last_seq_check = time(NULL);
578 /* save the new sequence number in the cache */
579 store_cache_seqnum( domain );
581 done:
582 DEBUG(10, ("refresh_sequence_number: %s seq number is now %d\n",
583 domain->name, domain->sequence_number));
585 return;
589 decide if a cache entry has expired
591 static bool centry_expired(struct winbindd_domain *domain, const char *keystr, struct cache_entry *centry)
593 /* If we've been told to be offline - stay in that state... */
594 if (lp_winbind_offline_logon() && global_winbindd_offline_state) {
595 DEBUG(10,("centry_expired: Key %s for domain %s valid as winbindd is globally offline.\n",
596 keystr, domain->name ));
597 return false;
600 /* when the domain is offline return the cached entry.
601 * This deals with transient offline states... */
603 if (!domain->online) {
604 DEBUG(10,("centry_expired: Key %s for domain %s valid as domain is offline.\n",
605 keystr, domain->name ));
606 return false;
609 /* if the server is OK and our cache entry came from when it was down then
610 the entry is invalid */
611 if ((domain->sequence_number != DOM_SEQUENCE_NONE) &&
612 (centry->sequence_number == DOM_SEQUENCE_NONE)) {
613 DEBUG(10,("centry_expired: Key %s for domain %s invalid sequence.\n",
614 keystr, domain->name ));
615 return true;
618 /* if the server is down or the cache entry is not older than the
619 current sequence number or it did not timeout then it is OK */
620 if (wcache_server_down(domain)
621 || ((centry->sequence_number == domain->sequence_number)
622 && (centry->timeout > time(NULL)))) {
623 DEBUG(10,("centry_expired: Key %s for domain %s is good.\n",
624 keystr, domain->name ));
625 return false;
628 DEBUG(10,("centry_expired: Key %s for domain %s expired\n",
629 keystr, domain->name ));
631 /* it's expired */
632 return true;
635 static struct cache_entry *wcache_fetch_raw(char *kstr)
637 TDB_DATA data;
638 struct cache_entry *centry;
639 TDB_DATA key;
641 key = string_tdb_data(kstr);
642 data = tdb_fetch_compat(wcache->tdb, key);
643 if (!data.dptr) {
644 /* a cache miss */
645 return NULL;
648 centry = SMB_XMALLOC_P(struct cache_entry);
649 centry->data = (unsigned char *)data.dptr;
650 centry->len = data.dsize;
651 centry->ofs = 0;
653 if (centry->len < 16) {
654 /* huh? corrupt cache? */
655 DEBUG(10,("wcache_fetch_raw: Corrupt cache for key %s "
656 "(len < 16)?\n", kstr));
657 centry_free(centry);
658 return NULL;
661 centry->status = centry_ntstatus(centry);
662 centry->sequence_number = centry_uint32(centry);
663 centry->timeout = centry_uint64_t(centry);
665 return centry;
668 static bool is_my_own_sam_domain(struct winbindd_domain *domain)
670 if (strequal(domain->name, get_global_sam_name()) &&
671 sid_check_is_our_sam(&domain->sid)) {
672 return true;
675 return false;
678 static bool is_builtin_domain(struct winbindd_domain *domain)
680 if (strequal(domain->name, "BUILTIN") &&
681 sid_check_is_builtin(&domain->sid)) {
682 return true;
685 return false;
689 fetch an entry from the cache, with a varargs key. auto-fetch the sequence
690 number and return status
692 static struct cache_entry *wcache_fetch(struct winbind_cache *cache,
693 struct winbindd_domain *domain,
694 const char *format, ...) PRINTF_ATTRIBUTE(3,4);
695 static struct cache_entry *wcache_fetch(struct winbind_cache *cache,
696 struct winbindd_domain *domain,
697 const char *format, ...)
699 va_list ap;
700 char *kstr;
701 struct cache_entry *centry;
703 if (!winbindd_use_cache() ||
704 is_my_own_sam_domain(domain) ||
705 is_builtin_domain(domain)) {
706 return NULL;
709 refresh_sequence_number(domain, false);
711 va_start(ap, format);
712 smb_xvasprintf(&kstr, format, ap);
713 va_end(ap);
715 centry = wcache_fetch_raw(kstr);
716 if (centry == NULL) {
717 free(kstr);
718 return NULL;
721 if (centry_expired(domain, kstr, centry)) {
723 DEBUG(10,("wcache_fetch: entry %s expired for domain %s\n",
724 kstr, domain->name ));
726 centry_free(centry);
727 free(kstr);
728 return NULL;
731 DEBUG(10,("wcache_fetch: returning entry %s for domain %s\n",
732 kstr, domain->name ));
734 free(kstr);
735 return centry;
738 static void wcache_delete(const char *format, ...) PRINTF_ATTRIBUTE(1,2);
739 static void wcache_delete(const char *format, ...)
741 va_list ap;
742 char *kstr;
743 TDB_DATA key;
745 va_start(ap, format);
746 smb_xvasprintf(&kstr, format, ap);
747 va_end(ap);
749 key = string_tdb_data(kstr);
751 tdb_delete(wcache->tdb, key);
752 free(kstr);
756 make sure we have at least len bytes available in a centry
758 static void centry_expand(struct cache_entry *centry, uint32 len)
760 if (centry->len - centry->ofs >= len)
761 return;
762 centry->len *= 2;
763 centry->data = SMB_REALLOC_ARRAY(centry->data, unsigned char,
764 centry->len);
765 if (!centry->data) {
766 DEBUG(0,("out of memory: needed %d bytes in centry_expand\n", centry->len));
767 smb_panic_fn("out of memory in centry_expand");
772 push a uint64_t into a centry
774 static void centry_put_uint64_t(struct cache_entry *centry, uint64_t v)
776 centry_expand(centry, 8);
777 SBVAL(centry->data, centry->ofs, v);
778 centry->ofs += 8;
782 push a uint32 into a centry
784 static void centry_put_uint32(struct cache_entry *centry, uint32 v)
786 centry_expand(centry, 4);
787 SIVAL(centry->data, centry->ofs, v);
788 centry->ofs += 4;
792 push a uint16 into a centry
794 static void centry_put_uint16(struct cache_entry *centry, uint16 v)
796 centry_expand(centry, 2);
797 SSVAL(centry->data, centry->ofs, v);
798 centry->ofs += 2;
802 push a uint8 into a centry
804 static void centry_put_uint8(struct cache_entry *centry, uint8 v)
806 centry_expand(centry, 1);
807 SCVAL(centry->data, centry->ofs, v);
808 centry->ofs += 1;
812 push a string into a centry
814 static void centry_put_string(struct cache_entry *centry, const char *s)
816 int len;
818 if (!s) {
819 /* null strings are marked as len 0xFFFF */
820 centry_put_uint8(centry, 0xFF);
821 return;
824 len = strlen(s);
825 /* can't handle more than 254 char strings. Truncating is probably best */
826 if (len > 254) {
827 DEBUG(10,("centry_put_string: truncating len (%d) to: 254\n", len));
828 len = 254;
830 centry_put_uint8(centry, len);
831 centry_expand(centry, len);
832 memcpy(centry->data + centry->ofs, s, len);
833 centry->ofs += len;
837 push a 16 byte hash into a centry - treat as 16 byte string.
839 static void centry_put_hash16(struct cache_entry *centry, const uint8 val[16])
841 centry_put_uint8(centry, 16);
842 centry_expand(centry, 16);
843 memcpy(centry->data + centry->ofs, val, 16);
844 centry->ofs += 16;
847 static void centry_put_sid(struct cache_entry *centry, const struct dom_sid *sid)
849 fstring sid_string;
850 centry_put_string(centry, sid_to_fstring(sid_string, sid));
855 put NTSTATUS into a centry
857 static void centry_put_ntstatus(struct cache_entry *centry, NTSTATUS status)
859 uint32 status_value = NT_STATUS_V(status);
860 centry_put_uint32(centry, status_value);
865 push a NTTIME into a centry
867 static void centry_put_nttime(struct cache_entry *centry, NTTIME nt)
869 centry_expand(centry, 8);
870 SIVAL(centry->data, centry->ofs, nt & 0xFFFFFFFF);
871 centry->ofs += 4;
872 SIVAL(centry->data, centry->ofs, nt >> 32);
873 centry->ofs += 4;
877 push a time_t into a centry - use a 64 bit size.
878 NTTIME here is being used as a convenient 64-bit size.
880 static void centry_put_time(struct cache_entry *centry, time_t t)
882 NTTIME nt = (NTTIME)t;
883 centry_put_nttime(centry, nt);
887 start a centry for output. When finished, call centry_end()
889 struct cache_entry *centry_start(struct winbindd_domain *domain, NTSTATUS status)
891 struct cache_entry *centry;
893 if (!wcache->tdb)
894 return NULL;
896 centry = SMB_XMALLOC_P(struct cache_entry);
898 centry->len = 8192; /* reasonable default */
899 centry->data = SMB_XMALLOC_ARRAY(uint8, centry->len);
900 centry->ofs = 0;
901 centry->sequence_number = domain->sequence_number;
902 centry->timeout = lp_winbind_cache_time() + time(NULL);
903 centry_put_ntstatus(centry, status);
904 centry_put_uint32(centry, centry->sequence_number);
905 centry_put_uint64_t(centry, centry->timeout);
906 return centry;
910 finish a centry and write it to the tdb
912 static void centry_end(struct cache_entry *centry, const char *format, ...) PRINTF_ATTRIBUTE(2,3);
913 static void centry_end(struct cache_entry *centry, const char *format, ...)
915 va_list ap;
916 char *kstr;
917 TDB_DATA key, data;
919 if (!winbindd_use_cache()) {
920 return;
923 va_start(ap, format);
924 smb_xvasprintf(&kstr, format, ap);
925 va_end(ap);
927 key = string_tdb_data(kstr);
928 data.dptr = centry->data;
929 data.dsize = centry->ofs;
931 tdb_store(wcache->tdb, key, data, TDB_REPLACE);
932 free(kstr);
935 static void wcache_save_name_to_sid(struct winbindd_domain *domain,
936 NTSTATUS status, const char *domain_name,
937 const char *name, const struct dom_sid *sid,
938 enum lsa_SidType type)
940 struct cache_entry *centry;
941 fstring uname;
943 centry = centry_start(domain, status);
944 if (!centry)
945 return;
947 if ((domain_name == NULL) || (domain_name[0] == '\0')) {
948 struct winbindd_domain *mydomain =
949 find_domain_from_sid_noinit(sid);
950 if (mydomain != NULL) {
951 domain_name = mydomain->name;
955 centry_put_uint32(centry, type);
956 centry_put_sid(centry, sid);
957 fstrcpy(uname, name);
958 (void)strupper_m(uname);
959 centry_end(centry, "NS/%s/%s", domain_name, uname);
960 DEBUG(10,("wcache_save_name_to_sid: %s\\%s -> %s (%s)\n", domain_name,
961 uname, sid_string_dbg(sid), nt_errstr(status)));
962 centry_free(centry);
965 static void wcache_save_sid_to_name(struct winbindd_domain *domain, NTSTATUS status,
966 const struct dom_sid *sid, const char *domain_name, const char *name, enum lsa_SidType type)
968 struct cache_entry *centry;
969 fstring sid_string;
971 centry = centry_start(domain, status);
972 if (!centry)
973 return;
975 if ((domain_name == NULL) || (domain_name[0] == '\0')) {
976 struct winbindd_domain *mydomain =
977 find_domain_from_sid_noinit(sid);
978 if (mydomain != NULL) {
979 domain_name = mydomain->name;
983 if (NT_STATUS_IS_OK(status)) {
984 centry_put_uint32(centry, type);
985 centry_put_string(centry, domain_name);
986 centry_put_string(centry, name);
989 centry_end(centry, "SN/%s", sid_to_fstring(sid_string, sid));
990 DEBUG(10,("wcache_save_sid_to_name: %s -> %s\\%s (%s)\n", sid_string,
991 domain_name, name, nt_errstr(status)));
992 centry_free(centry);
996 static void wcache_save_user(struct winbindd_domain *domain, NTSTATUS status,
997 struct wbint_userinfo *info)
999 struct cache_entry *centry;
1000 fstring sid_string;
1002 if (is_null_sid(&info->user_sid)) {
1003 return;
1006 centry = centry_start(domain, status);
1007 if (!centry)
1008 return;
1009 centry_put_string(centry, info->acct_name);
1010 centry_put_string(centry, info->full_name);
1011 centry_put_string(centry, info->homedir);
1012 centry_put_string(centry, info->shell);
1013 centry_put_uint32(centry, info->primary_gid);
1014 centry_put_sid(centry, &info->user_sid);
1015 centry_put_sid(centry, &info->group_sid);
1016 centry_end(centry, "U/%s", sid_to_fstring(sid_string,
1017 &info->user_sid));
1018 DEBUG(10,("wcache_save_user: %s (acct_name %s)\n", sid_string, info->acct_name));
1019 centry_free(centry);
1022 static void wcache_save_lockout_policy(struct winbindd_domain *domain,
1023 NTSTATUS status,
1024 struct samr_DomInfo12 *lockout_policy)
1026 struct cache_entry *centry;
1028 centry = centry_start(domain, status);
1029 if (!centry)
1030 return;
1032 centry_put_nttime(centry, lockout_policy->lockout_duration);
1033 centry_put_nttime(centry, lockout_policy->lockout_window);
1034 centry_put_uint16(centry, lockout_policy->lockout_threshold);
1036 centry_end(centry, "LOC_POL/%s", domain->name);
1038 DEBUG(10,("wcache_save_lockout_policy: %s\n", domain->name));
1040 centry_free(centry);
1045 static void wcache_save_password_policy(struct winbindd_domain *domain,
1046 NTSTATUS status,
1047 struct samr_DomInfo1 *policy)
1049 struct cache_entry *centry;
1051 centry = centry_start(domain, status);
1052 if (!centry)
1053 return;
1055 centry_put_uint16(centry, policy->min_password_length);
1056 centry_put_uint16(centry, policy->password_history_length);
1057 centry_put_uint32(centry, policy->password_properties);
1058 centry_put_nttime(centry, policy->max_password_age);
1059 centry_put_nttime(centry, policy->min_password_age);
1061 centry_end(centry, "PWD_POL/%s", domain->name);
1063 DEBUG(10,("wcache_save_password_policy: %s\n", domain->name));
1065 centry_free(centry);
1068 /***************************************************************************
1069 ***************************************************************************/
1071 static void wcache_save_username_alias(struct winbindd_domain *domain,
1072 NTSTATUS status,
1073 const char *name, const char *alias)
1075 struct cache_entry *centry;
1076 fstring uname;
1078 if ( (centry = centry_start(domain, status)) == NULL )
1079 return;
1081 centry_put_string( centry, alias );
1083 fstrcpy(uname, name);
1084 (void)strupper_m(uname);
1085 centry_end(centry, "NSS/NA/%s", uname);
1087 DEBUG(10,("wcache_save_username_alias: %s -> %s\n", name, alias ));
1089 centry_free(centry);
1092 static void wcache_save_alias_username(struct winbindd_domain *domain,
1093 NTSTATUS status,
1094 const char *alias, const char *name)
1096 struct cache_entry *centry;
1097 fstring uname;
1099 if ( (centry = centry_start(domain, status)) == NULL )
1100 return;
1102 centry_put_string( centry, name );
1104 fstrcpy(uname, alias);
1105 (void)strupper_m(uname);
1106 centry_end(centry, "NSS/AN/%s", uname);
1108 DEBUG(10,("wcache_save_alias_username: %s -> %s\n", alias, name ));
1110 centry_free(centry);
1113 /***************************************************************************
1114 ***************************************************************************/
1116 NTSTATUS resolve_username_to_alias( TALLOC_CTX *mem_ctx,
1117 struct winbindd_domain *domain,
1118 const char *name, char **alias )
1120 struct winbind_cache *cache = get_cache(domain);
1121 struct cache_entry *centry = NULL;
1122 NTSTATUS status;
1123 char *upper_name;
1125 if ( domain->internal )
1126 return NT_STATUS_NOT_SUPPORTED;
1128 if (!cache->tdb)
1129 goto do_query;
1131 if ( (upper_name = SMB_STRDUP(name)) == NULL )
1132 return NT_STATUS_NO_MEMORY;
1133 if (!strupper_m(upper_name)) {
1134 SAFE_FREE(name);
1135 return NT_STATUS_INVALID_PARAMETER;
1138 centry = wcache_fetch(cache, domain, "NSS/NA/%s", upper_name);
1140 SAFE_FREE( upper_name );
1142 if (!centry)
1143 goto do_query;
1145 status = centry->status;
1147 if (!NT_STATUS_IS_OK(status)) {
1148 centry_free(centry);
1149 return status;
1152 *alias = centry_string( centry, mem_ctx );
1154 centry_free(centry);
1156 DEBUG(10,("resolve_username_to_alias: [Cached] - mapped %s to %s\n",
1157 name, *alias ? *alias : "(none)"));
1159 return (*alias) ? NT_STATUS_OK : NT_STATUS_OBJECT_NAME_NOT_FOUND;
1161 do_query:
1163 /* If its not in cache and we are offline, then fail */
1165 if ( get_global_winbindd_state_offline() || !domain->online ) {
1166 DEBUG(8,("resolve_username_to_alias: rejecting query "
1167 "in offline mode\n"));
1168 return NT_STATUS_NOT_FOUND;
1171 status = nss_map_to_alias( mem_ctx, domain->name, name, alias );
1173 if ( NT_STATUS_IS_OK( status ) ) {
1174 wcache_save_username_alias(domain, status, name, *alias);
1177 if ( NT_STATUS_EQUAL( status, NT_STATUS_NONE_MAPPED ) ) {
1178 wcache_save_username_alias(domain, status, name, "(NULL)");
1181 DEBUG(5,("resolve_username_to_alias: backend query returned %s\n",
1182 nt_errstr(status)));
1184 if ( NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ) {
1185 set_domain_offline( domain );
1188 return status;
1191 /***************************************************************************
1192 ***************************************************************************/
1194 NTSTATUS resolve_alias_to_username( TALLOC_CTX *mem_ctx,
1195 struct winbindd_domain *domain,
1196 const char *alias, char **name )
1198 struct winbind_cache *cache = get_cache(domain);
1199 struct cache_entry *centry = NULL;
1200 NTSTATUS status;
1201 char *upper_name;
1203 if ( domain->internal )
1204 return NT_STATUS_NOT_SUPPORTED;
1206 if (!cache->tdb)
1207 goto do_query;
1209 if ( (upper_name = SMB_STRDUP(alias)) == NULL )
1210 return NT_STATUS_NO_MEMORY;
1211 if (!strupper_m(upper_name)) {
1212 SAFE_FREE(alias);
1213 return NT_STATUS_INVALID_PARAMETER;
1216 centry = wcache_fetch(cache, domain, "NSS/AN/%s", upper_name);
1218 SAFE_FREE( upper_name );
1220 if (!centry)
1221 goto do_query;
1223 status = centry->status;
1225 if (!NT_STATUS_IS_OK(status)) {
1226 centry_free(centry);
1227 return status;
1230 *name = centry_string( centry, mem_ctx );
1232 centry_free(centry);
1234 DEBUG(10,("resolve_alias_to_username: [Cached] - mapped %s to %s\n",
1235 alias, *name ? *name : "(none)"));
1237 return (*name) ? NT_STATUS_OK : NT_STATUS_OBJECT_NAME_NOT_FOUND;
1239 do_query:
1241 /* If its not in cache and we are offline, then fail */
1243 if ( get_global_winbindd_state_offline() || !domain->online ) {
1244 DEBUG(8,("resolve_alias_to_username: rejecting query "
1245 "in offline mode\n"));
1246 return NT_STATUS_NOT_FOUND;
1249 /* an alias cannot contain a domain prefix or '@' */
1251 if (strchr(alias, '\\') || strchr(alias, '@')) {
1252 DEBUG(10,("resolve_alias_to_username: skipping fully "
1253 "qualified name %s\n", alias));
1254 return NT_STATUS_OBJECT_NAME_INVALID;
1257 status = nss_map_from_alias( mem_ctx, domain->name, alias, name );
1259 if ( NT_STATUS_IS_OK( status ) ) {
1260 wcache_save_alias_username( domain, status, alias, *name );
1263 if (NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) {
1264 wcache_save_alias_username(domain, status, alias, "(NULL)");
1267 DEBUG(5,("resolve_alias_to_username: backend query returned %s\n",
1268 nt_errstr(status)));
1270 if ( NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ) {
1271 set_domain_offline( domain );
1274 return status;
1277 NTSTATUS wcache_cached_creds_exist(struct winbindd_domain *domain, const struct dom_sid *sid)
1279 struct winbind_cache *cache = get_cache(domain);
1280 TDB_DATA data;
1281 fstring key_str, tmp;
1282 uint32 rid;
1284 if (!cache->tdb) {
1285 return NT_STATUS_INTERNAL_DB_ERROR;
1288 if (is_null_sid(sid)) {
1289 return NT_STATUS_INVALID_SID;
1292 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1293 return NT_STATUS_INVALID_SID;
1296 fstr_sprintf(key_str, "CRED/%s", sid_to_fstring(tmp, sid));
1298 data = tdb_fetch_compat(cache->tdb, string_tdb_data(key_str));
1299 if (!data.dptr) {
1300 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1303 SAFE_FREE(data.dptr);
1304 return NT_STATUS_OK;
1307 /* Lookup creds for a SID - copes with old (unsalted) creds as well
1308 as new salted ones. */
1310 NTSTATUS wcache_get_creds(struct winbindd_domain *domain,
1311 TALLOC_CTX *mem_ctx,
1312 const struct dom_sid *sid,
1313 const uint8 **cached_nt_pass,
1314 const uint8 **cached_salt)
1316 struct winbind_cache *cache = get_cache(domain);
1317 struct cache_entry *centry = NULL;
1318 NTSTATUS status;
1319 uint32 rid;
1320 fstring tmp;
1322 if (!cache->tdb) {
1323 return NT_STATUS_INTERNAL_DB_ERROR;
1326 if (is_null_sid(sid)) {
1327 return NT_STATUS_INVALID_SID;
1330 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1331 return NT_STATUS_INVALID_SID;
1334 /* Try and get a salted cred first. If we can't
1335 fall back to an unsalted cred. */
1337 centry = wcache_fetch(cache, domain, "CRED/%s",
1338 sid_to_fstring(tmp, sid));
1339 if (!centry) {
1340 DEBUG(10,("wcache_get_creds: entry for [CRED/%s] not found\n",
1341 sid_string_dbg(sid)));
1342 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1346 * We don't use the time element at this moment,
1347 * but we have to consume it, so that we don't
1348 * neet to change the disk format of the cache.
1350 (void)centry_time(centry);
1352 /* In the salted case this isn't actually the nt_hash itself,
1353 but the MD5 of the salt + nt_hash. Let the caller
1354 sort this out. It can tell as we only return the cached_salt
1355 if we are returning a salted cred. */
1357 *cached_nt_pass = (const uint8 *)centry_hash16(centry, mem_ctx);
1358 if (*cached_nt_pass == NULL) {
1359 fstring sidstr;
1361 sid_to_fstring(sidstr, sid);
1363 /* Bad (old) cred cache. Delete and pretend we
1364 don't have it. */
1365 DEBUG(0,("wcache_get_creds: bad entry for [CRED/%s] - deleting\n",
1366 sidstr));
1367 wcache_delete("CRED/%s", sidstr);
1368 centry_free(centry);
1369 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1372 /* We only have 17 bytes more data in the salted cred case. */
1373 if (centry->len - centry->ofs == 17) {
1374 *cached_salt = (const uint8 *)centry_hash16(centry, mem_ctx);
1375 } else {
1376 *cached_salt = NULL;
1379 dump_data_pw("cached_nt_pass", *cached_nt_pass, NT_HASH_LEN);
1380 if (*cached_salt) {
1381 dump_data_pw("cached_salt", *cached_salt, NT_HASH_LEN);
1384 status = centry->status;
1386 DEBUG(10,("wcache_get_creds: [Cached] - cached creds for user %s status: %s\n",
1387 sid_string_dbg(sid), nt_errstr(status) ));
1389 centry_free(centry);
1390 return status;
1393 /* Store creds for a SID - only writes out new salted ones. */
1395 NTSTATUS wcache_save_creds(struct winbindd_domain *domain,
1396 const struct dom_sid *sid,
1397 const uint8 nt_pass[NT_HASH_LEN])
1399 struct cache_entry *centry;
1400 fstring sid_string;
1401 uint32 rid;
1402 uint8 cred_salt[NT_HASH_LEN];
1403 uint8 salted_hash[NT_HASH_LEN];
1405 if (is_null_sid(sid)) {
1406 return NT_STATUS_INVALID_SID;
1409 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1410 return NT_STATUS_INVALID_SID;
1413 centry = centry_start(domain, NT_STATUS_OK);
1414 if (!centry) {
1415 return NT_STATUS_INTERNAL_DB_ERROR;
1418 dump_data_pw("nt_pass", nt_pass, NT_HASH_LEN);
1420 centry_put_time(centry, time(NULL));
1422 /* Create a salt and then salt the hash. */
1423 generate_random_buffer(cred_salt, NT_HASH_LEN);
1424 E_md5hash(cred_salt, nt_pass, salted_hash);
1426 centry_put_hash16(centry, salted_hash);
1427 centry_put_hash16(centry, cred_salt);
1428 centry_end(centry, "CRED/%s", sid_to_fstring(sid_string, sid));
1430 DEBUG(10,("wcache_save_creds: %s\n", sid_string));
1432 centry_free(centry);
1434 return NT_STATUS_OK;
1438 /* Query display info. This is the basic user list fn */
1439 static NTSTATUS query_user_list(struct winbindd_domain *domain,
1440 TALLOC_CTX *mem_ctx,
1441 uint32 *num_entries,
1442 struct wbint_userinfo **info)
1444 struct winbind_cache *cache = get_cache(domain);
1445 struct cache_entry *centry = NULL;
1446 NTSTATUS status;
1447 unsigned int i, retry;
1448 bool old_status = domain->online;
1450 if (!cache->tdb)
1451 goto do_query;
1453 centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
1454 if (!centry)
1455 goto do_query;
1457 do_fetch_cache:
1458 *num_entries = centry_uint32(centry);
1460 if (*num_entries == 0)
1461 goto do_cached;
1463 (*info) = talloc_array(mem_ctx, struct wbint_userinfo, *num_entries);
1464 if (! (*info)) {
1465 smb_panic_fn("query_user_list out of memory");
1467 for (i=0; i<(*num_entries); i++) {
1468 (*info)[i].acct_name = centry_string(centry, mem_ctx);
1469 (*info)[i].full_name = centry_string(centry, mem_ctx);
1470 (*info)[i].homedir = centry_string(centry, mem_ctx);
1471 (*info)[i].shell = centry_string(centry, mem_ctx);
1472 centry_sid(centry, &(*info)[i].user_sid);
1473 centry_sid(centry, &(*info)[i].group_sid);
1476 do_cached:
1477 status = centry->status;
1479 DEBUG(10,("query_user_list: [Cached] - cached list for domain %s status: %s\n",
1480 domain->name, nt_errstr(status) ));
1482 centry_free(centry);
1483 return status;
1485 do_query:
1486 *num_entries = 0;
1487 *info = NULL;
1489 /* Return status value returned by seq number check */
1491 if (!NT_STATUS_IS_OK(domain->last_status))
1492 return domain->last_status;
1494 /* Put the query_user_list() in a retry loop. There appears to be
1495 * some bug either with Windows 2000 or Samba's handling of large
1496 * rpc replies. This manifests itself as sudden disconnection
1497 * at a random point in the enumeration of a large (60k) user list.
1498 * The retry loop simply tries the operation again. )-: It's not
1499 * pretty but an acceptable workaround until we work out what the
1500 * real problem is. */
1502 retry = 0;
1503 do {
1505 DEBUG(10,("query_user_list: [Cached] - doing backend query for list for domain %s\n",
1506 domain->name ));
1508 status = domain->backend->query_user_list(domain, mem_ctx, num_entries, info);
1509 if (!NT_STATUS_IS_OK(status)) {
1510 DEBUG(3, ("query_user_list: returned 0x%08x, "
1511 "retrying\n", NT_STATUS_V(status)));
1513 if (NT_STATUS_EQUAL(status, NT_STATUS_UNSUCCESSFUL)) {
1514 DEBUG(3, ("query_user_list: flushing "
1515 "connection cache\n"));
1516 invalidate_cm_connection(&domain->conn);
1518 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1519 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1520 if (!domain->internal && old_status) {
1521 set_domain_offline(domain);
1523 /* store partial response. */
1524 if (*num_entries > 0) {
1526 * humm, what about the status used for cache?
1527 * Should it be NT_STATUS_OK?
1529 break;
1532 * domain is offline now, and there is no user entries,
1533 * try to fetch from cache again.
1535 if (cache->tdb && !domain->online && !domain->internal && old_status) {
1536 centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
1537 /* partial response... */
1538 if (!centry) {
1539 goto skip_save;
1540 } else {
1541 goto do_fetch_cache;
1543 } else {
1544 goto skip_save;
1548 } while (NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_UNSUCCESSFUL) &&
1549 (retry++ < 5));
1551 /* and save it */
1552 refresh_sequence_number(domain, false);
1553 if (!NT_STATUS_IS_OK(status)) {
1554 return status;
1556 centry = centry_start(domain, status);
1557 if (!centry)
1558 goto skip_save;
1559 centry_put_uint32(centry, *num_entries);
1560 for (i=0; i<(*num_entries); i++) {
1561 centry_put_string(centry, (*info)[i].acct_name);
1562 centry_put_string(centry, (*info)[i].full_name);
1563 centry_put_string(centry, (*info)[i].homedir);
1564 centry_put_string(centry, (*info)[i].shell);
1565 centry_put_sid(centry, &(*info)[i].user_sid);
1566 centry_put_sid(centry, &(*info)[i].group_sid);
1567 if (domain->backend && domain->backend->consistent) {
1568 /* when the backend is consistent we can pre-prime some mappings */
1569 wcache_save_name_to_sid(domain, NT_STATUS_OK,
1570 domain->name,
1571 (*info)[i].acct_name,
1572 &(*info)[i].user_sid,
1573 SID_NAME_USER);
1574 wcache_save_sid_to_name(domain, NT_STATUS_OK,
1575 &(*info)[i].user_sid,
1576 domain->name,
1577 (*info)[i].acct_name,
1578 SID_NAME_USER);
1579 wcache_save_user(domain, NT_STATUS_OK, &(*info)[i]);
1582 centry_end(centry, "UL/%s", domain->name);
1583 centry_free(centry);
1585 skip_save:
1586 return status;
1589 /* list all domain groups */
1590 static NTSTATUS enum_dom_groups(struct winbindd_domain *domain,
1591 TALLOC_CTX *mem_ctx,
1592 uint32 *num_entries,
1593 struct wb_acct_info **info)
1595 struct winbind_cache *cache = get_cache(domain);
1596 struct cache_entry *centry = NULL;
1597 NTSTATUS status;
1598 unsigned int i;
1599 bool old_status;
1601 old_status = domain->online;
1602 if (!cache->tdb)
1603 goto do_query;
1605 centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
1606 if (!centry)
1607 goto do_query;
1609 do_fetch_cache:
1610 *num_entries = centry_uint32(centry);
1612 if (*num_entries == 0)
1613 goto do_cached;
1615 (*info) = talloc_array(mem_ctx, struct wb_acct_info, *num_entries);
1616 if (! (*info)) {
1617 smb_panic_fn("enum_dom_groups out of memory");
1619 for (i=0; i<(*num_entries); i++) {
1620 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1621 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1622 (*info)[i].rid = centry_uint32(centry);
1625 do_cached:
1626 status = centry->status;
1628 DEBUG(10,("enum_dom_groups: [Cached] - cached list for domain %s status: %s\n",
1629 domain->name, nt_errstr(status) ));
1631 centry_free(centry);
1632 return status;
1634 do_query:
1635 *num_entries = 0;
1636 *info = NULL;
1638 /* Return status value returned by seq number check */
1640 if (!NT_STATUS_IS_OK(domain->last_status))
1641 return domain->last_status;
1643 DEBUG(10,("enum_dom_groups: [Cached] - doing backend query for list for domain %s\n",
1644 domain->name ));
1646 status = domain->backend->enum_dom_groups(domain, mem_ctx, num_entries, info);
1648 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1649 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1650 if (!domain->internal && old_status) {
1651 set_domain_offline(domain);
1653 if (cache->tdb &&
1654 !domain->online &&
1655 !domain->internal &&
1656 old_status) {
1657 centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
1658 if (centry) {
1659 goto do_fetch_cache;
1663 /* and save it */
1664 refresh_sequence_number(domain, false);
1665 if (!NT_STATUS_IS_OK(status)) {
1666 return status;
1668 centry = centry_start(domain, status);
1669 if (!centry)
1670 goto skip_save;
1671 centry_put_uint32(centry, *num_entries);
1672 for (i=0; i<(*num_entries); i++) {
1673 centry_put_string(centry, (*info)[i].acct_name);
1674 centry_put_string(centry, (*info)[i].acct_desc);
1675 centry_put_uint32(centry, (*info)[i].rid);
1677 centry_end(centry, "GL/%s/domain", domain->name);
1678 centry_free(centry);
1680 skip_save:
1681 return status;
1684 /* list all domain groups */
1685 static NTSTATUS enum_local_groups(struct winbindd_domain *domain,
1686 TALLOC_CTX *mem_ctx,
1687 uint32 *num_entries,
1688 struct wb_acct_info **info)
1690 struct winbind_cache *cache = get_cache(domain);
1691 struct cache_entry *centry = NULL;
1692 NTSTATUS status;
1693 unsigned int i;
1694 bool old_status;
1696 old_status = domain->online;
1697 if (!cache->tdb)
1698 goto do_query;
1700 centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
1701 if (!centry)
1702 goto do_query;
1704 do_fetch_cache:
1705 *num_entries = centry_uint32(centry);
1707 if (*num_entries == 0)
1708 goto do_cached;
1710 (*info) = talloc_array(mem_ctx, struct wb_acct_info, *num_entries);
1711 if (! (*info)) {
1712 smb_panic_fn("enum_dom_groups out of memory");
1714 for (i=0; i<(*num_entries); i++) {
1715 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1716 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1717 (*info)[i].rid = centry_uint32(centry);
1720 do_cached:
1722 /* If we are returning cached data and the domain controller
1723 is down then we don't know whether the data is up to date
1724 or not. Return NT_STATUS_MORE_PROCESSING_REQUIRED to
1725 indicate this. */
1727 if (wcache_server_down(domain)) {
1728 DEBUG(10, ("enum_local_groups: returning cached user list and server was down\n"));
1729 status = NT_STATUS_MORE_PROCESSING_REQUIRED;
1730 } else
1731 status = centry->status;
1733 DEBUG(10,("enum_local_groups: [Cached] - cached list for domain %s status: %s\n",
1734 domain->name, nt_errstr(status) ));
1736 centry_free(centry);
1737 return status;
1739 do_query:
1740 *num_entries = 0;
1741 *info = NULL;
1743 /* Return status value returned by seq number check */
1745 if (!NT_STATUS_IS_OK(domain->last_status))
1746 return domain->last_status;
1748 DEBUG(10,("enum_local_groups: [Cached] - doing backend query for list for domain %s\n",
1749 domain->name ));
1751 status = domain->backend->enum_local_groups(domain, mem_ctx, num_entries, info);
1753 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1754 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1755 if (!domain->internal && old_status) {
1756 set_domain_offline(domain);
1758 if (cache->tdb &&
1759 !domain->internal &&
1760 !domain->online &&
1761 old_status) {
1762 centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
1763 if (centry) {
1764 goto do_fetch_cache;
1768 /* and save it */
1769 refresh_sequence_number(domain, false);
1770 if (!NT_STATUS_IS_OK(status)) {
1771 return status;
1773 centry = centry_start(domain, status);
1774 if (!centry)
1775 goto skip_save;
1776 centry_put_uint32(centry, *num_entries);
1777 for (i=0; i<(*num_entries); i++) {
1778 centry_put_string(centry, (*info)[i].acct_name);
1779 centry_put_string(centry, (*info)[i].acct_desc);
1780 centry_put_uint32(centry, (*info)[i].rid);
1782 centry_end(centry, "GL/%s/local", domain->name);
1783 centry_free(centry);
1785 skip_save:
1786 return status;
1789 NTSTATUS wcache_name_to_sid(struct winbindd_domain *domain,
1790 const char *domain_name,
1791 const char *name,
1792 struct dom_sid *sid,
1793 enum lsa_SidType *type)
1795 struct winbind_cache *cache = get_cache(domain);
1796 struct cache_entry *centry;
1797 NTSTATUS status;
1798 char *uname;
1800 if (cache->tdb == NULL) {
1801 return NT_STATUS_NOT_FOUND;
1804 uname = talloc_strdup_upper(talloc_tos(), name);
1805 if (uname == NULL) {
1806 return NT_STATUS_NO_MEMORY;
1809 if ((domain_name == NULL) || (domain_name[0] == '\0')) {
1810 domain_name = domain->name;
1813 centry = wcache_fetch(cache, domain, "NS/%s/%s", domain_name, uname);
1814 TALLOC_FREE(uname);
1815 if (centry == NULL) {
1816 return NT_STATUS_NOT_FOUND;
1819 status = centry->status;
1820 if (NT_STATUS_IS_OK(status)) {
1821 *type = (enum lsa_SidType)centry_uint32(centry);
1822 centry_sid(centry, sid);
1825 DEBUG(10,("name_to_sid: [Cached] - cached name for domain %s status: "
1826 "%s\n", domain->name, nt_errstr(status) ));
1828 centry_free(centry);
1829 return status;
1832 /* convert a single name to a sid in a domain */
1833 static NTSTATUS name_to_sid(struct winbindd_domain *domain,
1834 TALLOC_CTX *mem_ctx,
1835 const char *domain_name,
1836 const char *name,
1837 uint32_t flags,
1838 struct dom_sid *sid,
1839 enum lsa_SidType *type)
1841 NTSTATUS status;
1842 bool old_status;
1844 old_status = domain->online;
1846 status = wcache_name_to_sid(domain, domain_name, name, sid, type);
1847 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
1848 return status;
1851 ZERO_STRUCTP(sid);
1853 /* If the seq number check indicated that there is a problem
1854 * with this DC, then return that status... except for
1855 * access_denied. This is special because the dc may be in
1856 * "restrict anonymous = 1" mode, in which case it will deny
1857 * most unauthenticated operations, but *will* allow the LSA
1858 * name-to-sid that we try as a fallback. */
1860 if (!(NT_STATUS_IS_OK(domain->last_status)
1861 || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1862 return domain->last_status;
1864 DEBUG(10,("name_to_sid: [Cached] - doing backend query for name for domain %s\n",
1865 domain->name ));
1867 status = domain->backend->name_to_sid(domain, mem_ctx, domain_name,
1868 name, flags, sid, type);
1870 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1871 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1872 if (!domain->internal && old_status) {
1873 set_domain_offline(domain);
1875 if (!domain->internal &&
1876 !domain->online &&
1877 old_status) {
1878 NTSTATUS cache_status;
1879 cache_status = wcache_name_to_sid(domain, domain_name, name, sid, type);
1880 return cache_status;
1883 /* and save it */
1884 refresh_sequence_number(domain, false);
1886 if (domain->online &&
1887 (NT_STATUS_IS_OK(status) || NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED))) {
1888 wcache_save_name_to_sid(domain, status, domain_name, name, sid, *type);
1890 /* Only save the reverse mapping if this was not a UPN */
1891 if (!strchr(name, '@')) {
1892 if (!strupper_m(discard_const_p(char, domain_name))) {
1893 return NT_STATUS_INVALID_PARAMETER;
1895 (void)strlower_m(discard_const_p(char, name));
1896 wcache_save_sid_to_name(domain, status, sid, domain_name, name, *type);
1900 return status;
1903 NTSTATUS wcache_sid_to_name(struct winbindd_domain *domain,
1904 const struct dom_sid *sid,
1905 TALLOC_CTX *mem_ctx,
1906 char **domain_name,
1907 char **name,
1908 enum lsa_SidType *type)
1910 struct winbind_cache *cache = get_cache(domain);
1911 struct cache_entry *centry;
1912 char *sid_string;
1913 NTSTATUS status;
1915 if (cache->tdb == NULL) {
1916 return NT_STATUS_NOT_FOUND;
1919 sid_string = sid_string_tos(sid);
1920 if (sid_string == NULL) {
1921 return NT_STATUS_NO_MEMORY;
1924 centry = wcache_fetch(cache, domain, "SN/%s", sid_string);
1925 TALLOC_FREE(sid_string);
1926 if (centry == NULL) {
1927 return NT_STATUS_NOT_FOUND;
1930 if (NT_STATUS_IS_OK(centry->status)) {
1931 *type = (enum lsa_SidType)centry_uint32(centry);
1932 *domain_name = centry_string(centry, mem_ctx);
1933 *name = centry_string(centry, mem_ctx);
1936 status = centry->status;
1937 centry_free(centry);
1939 DEBUG(10,("sid_to_name: [Cached] - cached name for domain %s status: "
1940 "%s\n", domain->name, nt_errstr(status) ));
1942 return status;
1945 /* convert a sid to a user or group name. The sid is guaranteed to be in the domain
1946 given */
1947 static NTSTATUS sid_to_name(struct winbindd_domain *domain,
1948 TALLOC_CTX *mem_ctx,
1949 const struct dom_sid *sid,
1950 char **domain_name,
1951 char **name,
1952 enum lsa_SidType *type)
1954 NTSTATUS status;
1955 bool old_status;
1957 old_status = domain->online;
1958 status = wcache_sid_to_name(domain, sid, mem_ctx, domain_name, name,
1959 type);
1960 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
1961 return status;
1964 *name = NULL;
1965 *domain_name = NULL;
1967 /* If the seq number check indicated that there is a problem
1968 * with this DC, then return that status... except for
1969 * access_denied. This is special because the dc may be in
1970 * "restrict anonymous = 1" mode, in which case it will deny
1971 * most unauthenticated operations, but *will* allow the LSA
1972 * sid-to-name that we try as a fallback. */
1974 if (!(NT_STATUS_IS_OK(domain->last_status)
1975 || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1976 return domain->last_status;
1978 DEBUG(10,("sid_to_name: [Cached] - doing backend query for name for domain %s\n",
1979 domain->name ));
1981 status = domain->backend->sid_to_name(domain, mem_ctx, sid, domain_name, name, type);
1983 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1984 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1985 if (!domain->internal && old_status) {
1986 set_domain_offline(domain);
1988 if (!domain->internal &&
1989 !domain->online &&
1990 old_status) {
1991 NTSTATUS cache_status;
1992 cache_status = wcache_sid_to_name(domain, sid, mem_ctx,
1993 domain_name, name, type);
1994 return cache_status;
1997 /* and save it */
1998 refresh_sequence_number(domain, false);
1999 if (!NT_STATUS_IS_OK(status)) {
2000 return status;
2002 wcache_save_sid_to_name(domain, status, sid, *domain_name, *name, *type);
2004 /* We can't save the name to sid mapping here, as with sid history a
2005 * later name2sid would give the wrong sid. */
2007 return status;
2010 static NTSTATUS rids_to_names(struct winbindd_domain *domain,
2011 TALLOC_CTX *mem_ctx,
2012 const struct dom_sid *domain_sid,
2013 uint32 *rids,
2014 size_t num_rids,
2015 char **domain_name,
2016 char ***names,
2017 enum lsa_SidType **types)
2019 struct winbind_cache *cache = get_cache(domain);
2020 size_t i;
2021 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
2022 bool have_mapped;
2023 bool have_unmapped;
2024 bool old_status;
2026 old_status = domain->online;
2027 *domain_name = NULL;
2028 *names = NULL;
2029 *types = NULL;
2031 if (!cache->tdb) {
2032 goto do_query;
2035 if (num_rids == 0) {
2036 return NT_STATUS_OK;
2039 *names = talloc_array(mem_ctx, char *, num_rids);
2040 *types = talloc_array(mem_ctx, enum lsa_SidType, num_rids);
2042 if ((*names == NULL) || (*types == NULL)) {
2043 result = NT_STATUS_NO_MEMORY;
2044 goto error;
2047 have_mapped = have_unmapped = false;
2049 for (i=0; i<num_rids; i++) {
2050 struct dom_sid sid;
2051 struct cache_entry *centry;
2052 fstring tmp;
2054 if (!sid_compose(&sid, domain_sid, rids[i])) {
2055 result = NT_STATUS_INTERNAL_ERROR;
2056 goto error;
2059 centry = wcache_fetch(cache, domain, "SN/%s",
2060 sid_to_fstring(tmp, &sid));
2061 if (!centry) {
2062 goto do_query;
2065 (*types)[i] = SID_NAME_UNKNOWN;
2066 (*names)[i] = talloc_strdup(*names, "");
2068 if (NT_STATUS_IS_OK(centry->status)) {
2069 char *dom;
2070 have_mapped = true;
2071 (*types)[i] = (enum lsa_SidType)centry_uint32(centry);
2073 dom = centry_string(centry, mem_ctx);
2074 if (*domain_name == NULL) {
2075 *domain_name = dom;
2076 } else {
2077 talloc_free(dom);
2080 (*names)[i] = centry_string(centry, *names);
2082 } else if (NT_STATUS_EQUAL(centry->status, NT_STATUS_NONE_MAPPED)
2083 || NT_STATUS_EQUAL(centry->status, STATUS_SOME_UNMAPPED)) {
2084 have_unmapped = true;
2086 } else {
2087 /* something's definitely wrong */
2088 result = centry->status;
2089 goto error;
2092 centry_free(centry);
2095 if (!have_mapped) {
2096 return NT_STATUS_NONE_MAPPED;
2098 if (!have_unmapped) {
2099 return NT_STATUS_OK;
2101 return STATUS_SOME_UNMAPPED;
2103 do_query:
2105 TALLOC_FREE(*names);
2106 TALLOC_FREE(*types);
2108 result = domain->backend->rids_to_names(domain, mem_ctx, domain_sid,
2109 rids, num_rids, domain_name,
2110 names, types);
2112 if (NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
2113 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2114 if (!domain->internal && old_status) {
2115 set_domain_offline(domain);
2117 if (cache->tdb &&
2118 !domain->internal &&
2119 !domain->online &&
2120 old_status) {
2121 have_mapped = have_unmapped = false;
2123 *names = talloc_array(mem_ctx, char *, num_rids);
2124 if (*names == NULL) {
2125 result = NT_STATUS_NO_MEMORY;
2126 goto error;
2129 *types = talloc_array(mem_ctx, enum lsa_SidType,
2130 num_rids);
2131 if (*types == NULL) {
2132 result = NT_STATUS_NO_MEMORY;
2133 goto error;
2136 for (i=0; i<num_rids; i++) {
2137 struct dom_sid sid;
2138 struct cache_entry *centry;
2139 fstring tmp;
2141 if (!sid_compose(&sid, domain_sid, rids[i])) {
2142 result = NT_STATUS_INTERNAL_ERROR;
2143 goto error;
2146 centry = wcache_fetch(cache, domain, "SN/%s",
2147 sid_to_fstring(tmp, &sid));
2148 if (!centry) {
2149 (*types)[i] = SID_NAME_UNKNOWN;
2150 (*names)[i] = talloc_strdup(*names, "");
2151 continue;
2154 (*types)[i] = SID_NAME_UNKNOWN;
2155 (*names)[i] = talloc_strdup(*names, "");
2157 if (NT_STATUS_IS_OK(centry->status)) {
2158 char *dom;
2159 have_mapped = true;
2160 (*types)[i] = (enum lsa_SidType)centry_uint32(centry);
2162 dom = centry_string(centry, mem_ctx);
2163 if (*domain_name == NULL) {
2164 *domain_name = dom;
2165 } else {
2166 talloc_free(dom);
2169 (*names)[i] = centry_string(centry, *names);
2171 } else if (NT_STATUS_EQUAL(centry->status, NT_STATUS_NONE_MAPPED)) {
2172 have_unmapped = true;
2174 } else {
2175 /* something's definitely wrong */
2176 result = centry->status;
2177 centry_free(centry);
2178 goto error;
2181 centry_free(centry);
2184 if (!have_mapped) {
2185 return NT_STATUS_NONE_MAPPED;
2187 if (!have_unmapped) {
2188 return NT_STATUS_OK;
2190 return STATUS_SOME_UNMAPPED;
2194 None of the queried rids has been found so save all negative entries
2196 if (NT_STATUS_EQUAL(result, NT_STATUS_NONE_MAPPED)) {
2197 for (i = 0; i < num_rids; i++) {
2198 struct dom_sid sid;
2199 const char *name = "";
2200 const enum lsa_SidType type = SID_NAME_UNKNOWN;
2201 NTSTATUS status = NT_STATUS_NONE_MAPPED;
2203 if (!sid_compose(&sid, domain_sid, rids[i])) {
2204 return NT_STATUS_INTERNAL_ERROR;
2207 wcache_save_sid_to_name(domain, status, &sid, *domain_name,
2208 name, type);
2211 return result;
2215 Some or all of the queried rids have been found.
2217 if (!NT_STATUS_IS_OK(result) &&
2218 !NT_STATUS_EQUAL(result, STATUS_SOME_UNMAPPED)) {
2219 return result;
2222 refresh_sequence_number(domain, false);
2224 for (i=0; i<num_rids; i++) {
2225 struct dom_sid sid;
2226 NTSTATUS status;
2228 if (!sid_compose(&sid, domain_sid, rids[i])) {
2229 result = NT_STATUS_INTERNAL_ERROR;
2230 goto error;
2233 status = (*types)[i] == SID_NAME_UNKNOWN ?
2234 NT_STATUS_NONE_MAPPED : NT_STATUS_OK;
2236 wcache_save_sid_to_name(domain, status, &sid, *domain_name,
2237 (*names)[i], (*types)[i]);
2240 return result;
2242 error:
2243 TALLOC_FREE(*names);
2244 TALLOC_FREE(*types);
2245 return result;
2248 NTSTATUS wcache_query_user(struct winbindd_domain *domain,
2249 TALLOC_CTX *mem_ctx,
2250 const struct dom_sid *user_sid,
2251 struct wbint_userinfo *info)
2253 struct winbind_cache *cache = get_cache(domain);
2254 struct cache_entry *centry = NULL;
2255 NTSTATUS status;
2256 char *sid_string;
2258 if (cache->tdb == NULL) {
2259 return NT_STATUS_NOT_FOUND;
2262 sid_string = sid_string_tos(user_sid);
2263 if (sid_string == NULL) {
2264 return NT_STATUS_NO_MEMORY;
2267 centry = wcache_fetch(cache, domain, "U/%s", sid_string);
2268 TALLOC_FREE(sid_string);
2269 if (centry == NULL) {
2270 return NT_STATUS_NOT_FOUND;
2274 * If we have an access denied cache entry and a cached info3
2275 * in the samlogon cache then do a query. This will force the
2276 * rpc back end to return the info3 data.
2279 if (NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED) &&
2280 netsamlogon_cache_have(user_sid)) {
2281 DEBUG(10, ("query_user: cached access denied and have cached "
2282 "info3\n"));
2283 domain->last_status = NT_STATUS_OK;
2284 centry_free(centry);
2285 return NT_STATUS_NOT_FOUND;
2288 /* if status is not ok then this is a negative hit
2289 and the rest of the data doesn't matter */
2290 status = centry->status;
2291 if (NT_STATUS_IS_OK(status)) {
2292 info->acct_name = centry_string(centry, mem_ctx);
2293 info->full_name = centry_string(centry, mem_ctx);
2294 info->homedir = centry_string(centry, mem_ctx);
2295 info->shell = centry_string(centry, mem_ctx);
2296 info->primary_gid = centry_uint32(centry);
2297 centry_sid(centry, &info->user_sid);
2298 centry_sid(centry, &info->group_sid);
2301 DEBUG(10,("query_user: [Cached] - cached info for domain %s status: "
2302 "%s\n", domain->name, nt_errstr(status) ));
2304 centry_free(centry);
2305 return status;
2308 /* Lookup user information from a rid */
2309 static NTSTATUS query_user(struct winbindd_domain *domain,
2310 TALLOC_CTX *mem_ctx,
2311 const struct dom_sid *user_sid,
2312 struct wbint_userinfo *info)
2314 NTSTATUS status;
2315 bool old_status;
2317 old_status = domain->online;
2318 status = wcache_query_user(domain, mem_ctx, user_sid, info);
2319 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2320 return status;
2323 ZERO_STRUCTP(info);
2325 /* Return status value returned by seq number check */
2327 if (!NT_STATUS_IS_OK(domain->last_status))
2328 return domain->last_status;
2330 DEBUG(10,("query_user: [Cached] - doing backend query for info for domain %s\n",
2331 domain->name ));
2333 status = domain->backend->query_user(domain, mem_ctx, user_sid, info);
2335 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2336 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2337 if (!domain->internal && old_status) {
2338 set_domain_offline(domain);
2340 if (!domain->internal &&
2341 !domain->online &&
2342 old_status) {
2343 NTSTATUS cache_status;
2344 cache_status = wcache_query_user(domain, mem_ctx, user_sid, info);
2345 return cache_status;
2348 /* and save it */
2349 refresh_sequence_number(domain, false);
2350 if (!NT_STATUS_IS_OK(status)) {
2351 return status;
2353 wcache_save_user(domain, status, info);
2355 return status;
2358 NTSTATUS wcache_lookup_usergroups(struct winbindd_domain *domain,
2359 TALLOC_CTX *mem_ctx,
2360 const struct dom_sid *user_sid,
2361 uint32_t *pnum_sids,
2362 struct dom_sid **psids)
2364 struct winbind_cache *cache = get_cache(domain);
2365 struct cache_entry *centry = NULL;
2366 NTSTATUS status;
2367 uint32_t i, num_sids;
2368 struct dom_sid *sids;
2369 fstring sid_string;
2371 if (cache->tdb == NULL) {
2372 return NT_STATUS_NOT_FOUND;
2375 centry = wcache_fetch(cache, domain, "UG/%s",
2376 sid_to_fstring(sid_string, user_sid));
2377 if (centry == NULL) {
2378 return NT_STATUS_NOT_FOUND;
2381 /* If we have an access denied cache entry and a cached info3 in the
2382 samlogon cache then do a query. This will force the rpc back end
2383 to return the info3 data. */
2385 if (NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)
2386 && netsamlogon_cache_have(user_sid)) {
2387 DEBUG(10, ("lookup_usergroups: cached access denied and have "
2388 "cached info3\n"));
2389 domain->last_status = NT_STATUS_OK;
2390 centry_free(centry);
2391 return NT_STATUS_NOT_FOUND;
2394 num_sids = centry_uint32(centry);
2395 sids = talloc_array(mem_ctx, struct dom_sid, num_sids);
2396 if (sids == NULL) {
2397 centry_free(centry);
2398 return NT_STATUS_NO_MEMORY;
2401 for (i=0; i<num_sids; i++) {
2402 centry_sid(centry, &sids[i]);
2405 status = centry->status;
2407 DEBUG(10,("lookup_usergroups: [Cached] - cached info for domain %s "
2408 "status: %s\n", domain->name, nt_errstr(status)));
2410 centry_free(centry);
2412 *pnum_sids = num_sids;
2413 *psids = sids;
2414 return status;
2417 /* Lookup groups a user is a member of. */
2418 static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
2419 TALLOC_CTX *mem_ctx,
2420 const struct dom_sid *user_sid,
2421 uint32 *num_groups, struct dom_sid **user_gids)
2423 struct cache_entry *centry = NULL;
2424 NTSTATUS status;
2425 unsigned int i;
2426 fstring sid_string;
2427 bool old_status;
2429 old_status = domain->online;
2430 status = wcache_lookup_usergroups(domain, mem_ctx, user_sid,
2431 num_groups, user_gids);
2432 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2433 return status;
2436 (*num_groups) = 0;
2437 (*user_gids) = NULL;
2439 /* Return status value returned by seq number check */
2441 if (!NT_STATUS_IS_OK(domain->last_status))
2442 return domain->last_status;
2444 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info for domain %s\n",
2445 domain->name ));
2447 status = domain->backend->lookup_usergroups(domain, mem_ctx, user_sid, num_groups, user_gids);
2449 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2450 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2451 if (!domain->internal && old_status) {
2452 set_domain_offline(domain);
2454 if (!domain->internal &&
2455 !domain->online &&
2456 old_status) {
2457 NTSTATUS cache_status;
2458 cache_status = wcache_lookup_usergroups(domain, mem_ctx, user_sid,
2459 num_groups, user_gids);
2460 return cache_status;
2463 if ( NT_STATUS_EQUAL(status, NT_STATUS_SYNCHRONIZATION_REQUIRED) )
2464 goto skip_save;
2466 /* and save it */
2467 refresh_sequence_number(domain, false);
2468 if (!NT_STATUS_IS_OK(status)) {
2469 return status;
2471 centry = centry_start(domain, status);
2472 if (!centry)
2473 goto skip_save;
2475 centry_put_uint32(centry, *num_groups);
2476 for (i=0; i<(*num_groups); i++) {
2477 centry_put_sid(centry, &(*user_gids)[i]);
2480 centry_end(centry, "UG/%s", sid_to_fstring(sid_string, user_sid));
2481 centry_free(centry);
2483 skip_save:
2484 return status;
2487 static char *wcache_make_sidlist(TALLOC_CTX *mem_ctx, uint32_t num_sids,
2488 const struct dom_sid *sids)
2490 uint32_t i;
2491 char *sidlist;
2493 sidlist = talloc_strdup(mem_ctx, "");
2494 if (sidlist == NULL) {
2495 return NULL;
2497 for (i=0; i<num_sids; i++) {
2498 fstring tmp;
2499 sidlist = talloc_asprintf_append_buffer(
2500 sidlist, "/%s", sid_to_fstring(tmp, &sids[i]));
2501 if (sidlist == NULL) {
2502 return NULL;
2505 return sidlist;
2508 NTSTATUS wcache_lookup_useraliases(struct winbindd_domain *domain,
2509 TALLOC_CTX *mem_ctx, uint32_t num_sids,
2510 const struct dom_sid *sids,
2511 uint32_t *pnum_aliases, uint32_t **paliases)
2513 struct winbind_cache *cache = get_cache(domain);
2514 struct cache_entry *centry = NULL;
2515 uint32_t num_aliases;
2516 uint32_t *aliases;
2517 NTSTATUS status;
2518 char *sidlist;
2519 int i;
2521 if (cache->tdb == NULL) {
2522 return NT_STATUS_NOT_FOUND;
2525 if (num_sids == 0) {
2526 *pnum_aliases = 0;
2527 *paliases = NULL;
2528 return NT_STATUS_OK;
2531 /* We need to cache indexed by the whole list of SIDs, the aliases
2532 * resulting might come from any of the SIDs. */
2534 sidlist = wcache_make_sidlist(talloc_tos(), num_sids, sids);
2535 if (sidlist == NULL) {
2536 return NT_STATUS_NO_MEMORY;
2539 centry = wcache_fetch(cache, domain, "UA%s", sidlist);
2540 TALLOC_FREE(sidlist);
2541 if (centry == NULL) {
2542 return NT_STATUS_NOT_FOUND;
2545 num_aliases = centry_uint32(centry);
2546 aliases = talloc_array(mem_ctx, uint32_t, num_aliases);
2547 if (aliases == NULL) {
2548 centry_free(centry);
2549 return NT_STATUS_NO_MEMORY;
2552 for (i=0; i<num_aliases; i++) {
2553 aliases[i] = centry_uint32(centry);
2556 status = centry->status;
2558 DEBUG(10,("lookup_useraliases: [Cached] - cached info for domain: %s "
2559 "status %s\n", domain->name, nt_errstr(status)));
2561 centry_free(centry);
2563 *pnum_aliases = num_aliases;
2564 *paliases = aliases;
2566 return status;
2569 static NTSTATUS lookup_useraliases(struct winbindd_domain *domain,
2570 TALLOC_CTX *mem_ctx,
2571 uint32 num_sids, const struct dom_sid *sids,
2572 uint32 *num_aliases, uint32 **alias_rids)
2574 struct cache_entry *centry = NULL;
2575 NTSTATUS status;
2576 char *sidlist;
2577 int i;
2578 bool old_status;
2580 old_status = domain->online;
2581 status = wcache_lookup_useraliases(domain, mem_ctx, num_sids, sids,
2582 num_aliases, alias_rids);
2583 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2584 return status;
2587 (*num_aliases) = 0;
2588 (*alias_rids) = NULL;
2590 if (!NT_STATUS_IS_OK(domain->last_status))
2591 return domain->last_status;
2593 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info "
2594 "for domain %s\n", domain->name ));
2596 sidlist = wcache_make_sidlist(talloc_tos(), num_sids, sids);
2597 if (sidlist == NULL) {
2598 return NT_STATUS_NO_MEMORY;
2601 status = domain->backend->lookup_useraliases(domain, mem_ctx,
2602 num_sids, sids,
2603 num_aliases, alias_rids);
2605 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2606 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2607 if (!domain->internal && old_status) {
2608 set_domain_offline(domain);
2610 if (!domain->internal &&
2611 !domain->online &&
2612 old_status) {
2613 NTSTATUS cache_status;
2614 cache_status = wcache_lookup_useraliases(domain, mem_ctx, num_sids,
2615 sids, num_aliases, alias_rids);
2616 return cache_status;
2619 /* and save it */
2620 refresh_sequence_number(domain, false);
2621 if (!NT_STATUS_IS_OK(status)) {
2622 return status;
2624 centry = centry_start(domain, status);
2625 if (!centry)
2626 goto skip_save;
2627 centry_put_uint32(centry, *num_aliases);
2628 for (i=0; i<(*num_aliases); i++)
2629 centry_put_uint32(centry, (*alias_rids)[i]);
2630 centry_end(centry, "UA%s", sidlist);
2631 centry_free(centry);
2633 skip_save:
2634 return status;
2637 NTSTATUS wcache_lookup_groupmem(struct winbindd_domain *domain,
2638 TALLOC_CTX *mem_ctx,
2639 const struct dom_sid *group_sid,
2640 uint32_t *num_names,
2641 struct dom_sid **sid_mem, char ***names,
2642 uint32_t **name_types)
2644 struct winbind_cache *cache = get_cache(domain);
2645 struct cache_entry *centry = NULL;
2646 NTSTATUS status;
2647 unsigned int i;
2648 char *sid_string;
2650 if (cache->tdb == NULL) {
2651 return NT_STATUS_NOT_FOUND;
2654 sid_string = sid_string_tos(group_sid);
2655 if (sid_string == NULL) {
2656 return NT_STATUS_NO_MEMORY;
2659 centry = wcache_fetch(cache, domain, "GM/%s", sid_string);
2660 TALLOC_FREE(sid_string);
2661 if (centry == NULL) {
2662 return NT_STATUS_NOT_FOUND;
2665 *sid_mem = NULL;
2666 *names = NULL;
2667 *name_types = NULL;
2669 *num_names = centry_uint32(centry);
2670 if (*num_names == 0) {
2671 centry_free(centry);
2672 return NT_STATUS_OK;
2675 *sid_mem = talloc_array(mem_ctx, struct dom_sid, *num_names);
2676 *names = talloc_array(mem_ctx, char *, *num_names);
2677 *name_types = talloc_array(mem_ctx, uint32, *num_names);
2679 if ((*sid_mem == NULL) || (*names == NULL) || (*name_types == NULL)) {
2680 TALLOC_FREE(*sid_mem);
2681 TALLOC_FREE(*names);
2682 TALLOC_FREE(*name_types);
2683 centry_free(centry);
2684 return NT_STATUS_NO_MEMORY;
2687 for (i=0; i<(*num_names); i++) {
2688 centry_sid(centry, &(*sid_mem)[i]);
2689 (*names)[i] = centry_string(centry, mem_ctx);
2690 (*name_types)[i] = centry_uint32(centry);
2693 status = centry->status;
2695 DEBUG(10,("lookup_groupmem: [Cached] - cached info for domain %s "
2696 "status: %s\n", domain->name, nt_errstr(status)));
2698 centry_free(centry);
2699 return status;
2702 static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
2703 TALLOC_CTX *mem_ctx,
2704 const struct dom_sid *group_sid,
2705 enum lsa_SidType type,
2706 uint32 *num_names,
2707 struct dom_sid **sid_mem, char ***names,
2708 uint32 **name_types)
2710 struct cache_entry *centry = NULL;
2711 NTSTATUS status;
2712 unsigned int i;
2713 fstring sid_string;
2714 bool old_status;
2716 old_status = domain->online;
2717 status = wcache_lookup_groupmem(domain, mem_ctx, group_sid, num_names,
2718 sid_mem, names, name_types);
2719 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2720 return status;
2723 (*num_names) = 0;
2724 (*sid_mem) = NULL;
2725 (*names) = NULL;
2726 (*name_types) = NULL;
2728 /* Return status value returned by seq number check */
2730 if (!NT_STATUS_IS_OK(domain->last_status))
2731 return domain->last_status;
2733 DEBUG(10,("lookup_groupmem: [Cached] - doing backend query for info for domain %s\n",
2734 domain->name ));
2736 status = domain->backend->lookup_groupmem(domain, mem_ctx, group_sid,
2737 type, num_names,
2738 sid_mem, names, name_types);
2740 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2741 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2742 if (!domain->internal && old_status) {
2743 set_domain_offline(domain);
2745 if (!domain->internal &&
2746 !domain->online &&
2747 old_status) {
2748 NTSTATUS cache_status;
2749 cache_status = wcache_lookup_groupmem(domain, mem_ctx, group_sid,
2750 num_names, sid_mem, names,
2751 name_types);
2752 return cache_status;
2755 /* and save it */
2756 refresh_sequence_number(domain, false);
2757 if (!NT_STATUS_IS_OK(status)) {
2758 return status;
2760 centry = centry_start(domain, status);
2761 if (!centry)
2762 goto skip_save;
2763 centry_put_uint32(centry, *num_names);
2764 for (i=0; i<(*num_names); i++) {
2765 centry_put_sid(centry, &(*sid_mem)[i]);
2766 centry_put_string(centry, (*names)[i]);
2767 centry_put_uint32(centry, (*name_types)[i]);
2769 centry_end(centry, "GM/%s", sid_to_fstring(sid_string, group_sid));
2770 centry_free(centry);
2772 skip_save:
2773 return status;
2776 /* find the sequence number for a domain */
2777 static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
2779 refresh_sequence_number(domain, false);
2781 *seq = domain->sequence_number;
2783 return NT_STATUS_OK;
2786 /* enumerate trusted domains
2787 * (we need to have the list of trustdoms in the cache when we go offline) -
2788 * Guenther */
2789 static NTSTATUS trusted_domains(struct winbindd_domain *domain,
2790 TALLOC_CTX *mem_ctx,
2791 struct netr_DomainTrustList *trusts)
2793 NTSTATUS status;
2794 struct winbind_cache *cache;
2795 struct winbindd_tdc_domain *dom_list = NULL;
2796 size_t num_domains = 0;
2797 bool retval = false;
2798 int i;
2799 bool old_status;
2801 old_status = domain->online;
2802 trusts->count = 0;
2803 trusts->array = NULL;
2805 cache = get_cache(domain);
2806 if (!cache || !cache->tdb) {
2807 goto do_query;
2810 if (domain->online) {
2811 goto do_query;
2814 retval = wcache_tdc_fetch_list(&dom_list, &num_domains);
2815 if (!retval || !num_domains || !dom_list) {
2816 TALLOC_FREE(dom_list);
2817 goto do_query;
2820 do_fetch_cache:
2821 trusts->array = talloc_zero_array(mem_ctx, struct netr_DomainTrust, num_domains);
2822 if (!trusts->array) {
2823 TALLOC_FREE(dom_list);
2824 return NT_STATUS_NO_MEMORY;
2827 for (i = 0; i < num_domains; i++) {
2828 struct netr_DomainTrust *trust;
2829 struct dom_sid *sid;
2830 struct winbindd_domain *dom;
2832 dom = find_domain_from_name_noinit(dom_list[i].domain_name);
2833 if (dom && dom->internal) {
2834 continue;
2837 trust = &trusts->array[trusts->count];
2838 trust->netbios_name = talloc_strdup(trusts->array, dom_list[i].domain_name);
2839 trust->dns_name = talloc_strdup(trusts->array, dom_list[i].dns_name);
2840 sid = talloc(trusts->array, struct dom_sid);
2841 if (!trust->netbios_name || !trust->dns_name ||
2842 !sid) {
2843 TALLOC_FREE(dom_list);
2844 TALLOC_FREE(trusts->array);
2845 return NT_STATUS_NO_MEMORY;
2848 trust->trust_flags = dom_list[i].trust_flags;
2849 trust->trust_attributes = dom_list[i].trust_attribs;
2850 trust->trust_type = dom_list[i].trust_type;
2851 sid_copy(sid, &dom_list[i].sid);
2852 trust->sid = sid;
2853 trusts->count++;
2856 TALLOC_FREE(dom_list);
2857 return NT_STATUS_OK;
2859 do_query:
2860 /* Return status value returned by seq number check */
2862 if (!NT_STATUS_IS_OK(domain->last_status))
2863 return domain->last_status;
2865 DEBUG(10,("trusted_domains: [Cached] - doing backend query for info for domain %s\n",
2866 domain->name ));
2868 status = domain->backend->trusted_domains(domain, mem_ctx, trusts);
2870 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2871 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2872 if (!domain->internal && old_status) {
2873 set_domain_offline(domain);
2875 if (!domain->internal &&
2876 !domain->online &&
2877 old_status) {
2878 retval = wcache_tdc_fetch_list(&dom_list, &num_domains);
2879 if (retval && num_domains && dom_list) {
2880 TALLOC_FREE(trusts->array);
2881 trusts->count = 0;
2882 goto do_fetch_cache;
2886 /* no trusts gives NT_STATUS_NO_MORE_ENTRIES resetting to NT_STATUS_OK
2887 * so that the generic centry handling still applies correctly -
2888 * Guenther*/
2890 if (!NT_STATUS_IS_ERR(status)) {
2891 status = NT_STATUS_OK;
2893 return status;
2896 /* get lockout policy */
2897 static NTSTATUS lockout_policy(struct winbindd_domain *domain,
2898 TALLOC_CTX *mem_ctx,
2899 struct samr_DomInfo12 *policy)
2901 struct winbind_cache *cache = get_cache(domain);
2902 struct cache_entry *centry = NULL;
2903 NTSTATUS status;
2904 bool old_status;
2906 old_status = domain->online;
2907 if (!cache->tdb)
2908 goto do_query;
2910 centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
2912 if (!centry)
2913 goto do_query;
2915 do_fetch_cache:
2916 policy->lockout_duration = centry_nttime(centry);
2917 policy->lockout_window = centry_nttime(centry);
2918 policy->lockout_threshold = centry_uint16(centry);
2920 status = centry->status;
2922 DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2923 domain->name, nt_errstr(status) ));
2925 centry_free(centry);
2926 return status;
2928 do_query:
2929 ZERO_STRUCTP(policy);
2931 /* Return status value returned by seq number check */
2933 if (!NT_STATUS_IS_OK(domain->last_status))
2934 return domain->last_status;
2936 DEBUG(10,("lockout_policy: [Cached] - doing backend query for info for domain %s\n",
2937 domain->name ));
2939 status = domain->backend->lockout_policy(domain, mem_ctx, policy);
2941 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2942 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2943 if (!domain->internal && old_status) {
2944 set_domain_offline(domain);
2946 if (cache->tdb &&
2947 !domain->internal &&
2948 !domain->online &&
2949 old_status) {
2950 centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
2951 if (centry) {
2952 goto do_fetch_cache;
2956 /* and save it */
2957 refresh_sequence_number(domain, false);
2958 if (!NT_STATUS_IS_OK(status)) {
2959 return status;
2961 wcache_save_lockout_policy(domain, status, policy);
2963 return status;
2966 /* get password policy */
2967 static NTSTATUS password_policy(struct winbindd_domain *domain,
2968 TALLOC_CTX *mem_ctx,
2969 struct samr_DomInfo1 *policy)
2971 struct winbind_cache *cache = get_cache(domain);
2972 struct cache_entry *centry = NULL;
2973 NTSTATUS status;
2974 bool old_status;
2976 old_status = domain->online;
2977 if (!cache->tdb)
2978 goto do_query;
2980 centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
2982 if (!centry)
2983 goto do_query;
2985 do_fetch_cache:
2986 policy->min_password_length = centry_uint16(centry);
2987 policy->password_history_length = centry_uint16(centry);
2988 policy->password_properties = centry_uint32(centry);
2989 policy->max_password_age = centry_nttime(centry);
2990 policy->min_password_age = centry_nttime(centry);
2992 status = centry->status;
2994 DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2995 domain->name, nt_errstr(status) ));
2997 centry_free(centry);
2998 return status;
3000 do_query:
3001 ZERO_STRUCTP(policy);
3003 /* Return status value returned by seq number check */
3005 if (!NT_STATUS_IS_OK(domain->last_status))
3006 return domain->last_status;
3008 DEBUG(10,("password_policy: [Cached] - doing backend query for info for domain %s\n",
3009 domain->name ));
3011 status = domain->backend->password_policy(domain, mem_ctx, policy);
3013 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
3014 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
3015 if (!domain->internal && old_status) {
3016 set_domain_offline(domain);
3018 if (cache->tdb &&
3019 !domain->internal &&
3020 !domain->online &&
3021 old_status) {
3022 centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
3023 if (centry) {
3024 goto do_fetch_cache;
3028 /* and save it */
3029 refresh_sequence_number(domain, false);
3030 if (!NT_STATUS_IS_OK(status)) {
3031 return status;
3033 wcache_save_password_policy(domain, status, policy);
3035 return status;
3039 /* Invalidate cached user and group lists coherently */
3041 static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
3042 void *state)
3044 if (strncmp((const char *)kbuf.dptr, "UL/", 3) == 0 ||
3045 strncmp((const char *)kbuf.dptr, "GL/", 3) == 0)
3046 tdb_delete(the_tdb, kbuf);
3048 return 0;
3051 /* Invalidate the getpwnam and getgroups entries for a winbindd domain */
3053 void wcache_invalidate_samlogon(struct winbindd_domain *domain,
3054 const struct dom_sid *sid)
3056 fstring key_str, sid_string;
3057 struct winbind_cache *cache;
3059 /* dont clear cached U/SID and UG/SID entries when we want to logon
3060 * offline - gd */
3062 if (lp_winbind_offline_logon()) {
3063 return;
3066 if (!domain)
3067 return;
3069 cache = get_cache(domain);
3071 if (!cache->tdb) {
3072 return;
3075 /* Clear U/SID cache entry */
3076 fstr_sprintf(key_str, "U/%s", sid_to_fstring(sid_string, sid));
3077 DEBUG(10, ("wcache_invalidate_samlogon: clearing %s\n", key_str));
3078 tdb_delete(cache->tdb, string_tdb_data(key_str));
3080 /* Clear UG/SID cache entry */
3081 fstr_sprintf(key_str, "UG/%s", sid_to_fstring(sid_string, sid));
3082 DEBUG(10, ("wcache_invalidate_samlogon: clearing %s\n", key_str));
3083 tdb_delete(cache->tdb, string_tdb_data(key_str));
3085 /* Samba/winbindd never needs this. */
3086 netsamlogon_clear_cached_user(sid);
3089 bool wcache_invalidate_cache(void)
3091 struct winbindd_domain *domain;
3093 for (domain = domain_list(); domain; domain = domain->next) {
3094 struct winbind_cache *cache = get_cache(domain);
3096 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
3097 "entries for %s\n", domain->name));
3098 if (cache) {
3099 if (cache->tdb) {
3100 tdb_traverse(cache->tdb, traverse_fn, NULL);
3101 } else {
3102 return false;
3106 return true;
3109 bool wcache_invalidate_cache_noinit(void)
3111 struct winbindd_domain *domain;
3113 for (domain = domain_list(); domain; domain = domain->next) {
3114 struct winbind_cache *cache;
3116 /* Skip uninitialized domains. */
3117 if (!domain->initialized && !domain->internal) {
3118 continue;
3121 cache = get_cache(domain);
3123 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
3124 "entries for %s\n", domain->name));
3125 if (cache) {
3126 if (cache->tdb) {
3127 tdb_traverse(cache->tdb, traverse_fn, NULL);
3129 * Flushing cache has nothing to with domains.
3130 * return here if we successfully flushed once.
3131 * To avoid unnecessary traversing the cache.
3133 return true;
3134 } else {
3135 return false;
3139 return true;
3142 bool init_wcache(void)
3144 if (wcache == NULL) {
3145 wcache = SMB_XMALLOC_P(struct winbind_cache);
3146 ZERO_STRUCTP(wcache);
3149 if (wcache->tdb != NULL)
3150 return true;
3152 /* when working offline we must not clear the cache on restart */
3153 wcache->tdb = tdb_open_log(state_path("winbindd_cache.tdb"),
3154 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
3155 TDB_INCOMPATIBLE_HASH |
3156 (lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST)),
3157 O_RDWR|O_CREAT, 0600);
3159 if (wcache->tdb == NULL) {
3160 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
3161 return false;
3164 return true;
3167 /************************************************************************
3168 This is called by the parent to initialize the cache file.
3169 We don't need sophisticated locking here as we know we're the
3170 only opener.
3171 ************************************************************************/
3173 bool initialize_winbindd_cache(void)
3175 bool cache_bad = true;
3176 uint32 vers;
3178 if (!init_wcache()) {
3179 DEBUG(0,("initialize_winbindd_cache: init_wcache failed.\n"));
3180 return false;
3183 /* Check version number. */
3184 if (tdb_fetch_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, &vers) &&
3185 vers == WINBINDD_CACHE_VERSION) {
3186 cache_bad = false;
3189 if (cache_bad) {
3190 DEBUG(0,("initialize_winbindd_cache: clearing cache "
3191 "and re-creating with version number %d\n",
3192 WINBINDD_CACHE_VERSION ));
3194 tdb_close(wcache->tdb);
3195 wcache->tdb = NULL;
3197 if (unlink(state_path("winbindd_cache.tdb")) == -1) {
3198 DEBUG(0,("initialize_winbindd_cache: unlink %s failed %s ",
3199 state_path("winbindd_cache.tdb"),
3200 strerror(errno) ));
3201 return false;
3203 if (!init_wcache()) {
3204 DEBUG(0,("initialize_winbindd_cache: re-initialization "
3205 "init_wcache failed.\n"));
3206 return false;
3209 /* Write the version. */
3210 if (!tdb_store_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, WINBINDD_CACHE_VERSION)) {
3211 DEBUG(0,("initialize_winbindd_cache: version number store failed %s\n",
3212 tdb_errorstr_compat(wcache->tdb) ));
3213 return false;
3217 tdb_close(wcache->tdb);
3218 wcache->tdb = NULL;
3219 return true;
3222 void close_winbindd_cache(void)
3224 if (!wcache) {
3225 return;
3227 if (wcache->tdb) {
3228 tdb_close(wcache->tdb);
3229 wcache->tdb = NULL;
3233 bool lookup_cached_sid(TALLOC_CTX *mem_ctx, const struct dom_sid *sid,
3234 char **domain_name, char **name,
3235 enum lsa_SidType *type)
3237 struct winbindd_domain *domain;
3238 NTSTATUS status;
3240 domain = find_lookup_domain_from_sid(sid);
3241 if (domain == NULL) {
3242 return false;
3244 status = wcache_sid_to_name(domain, sid, mem_ctx, domain_name, name,
3245 type);
3246 return NT_STATUS_IS_OK(status);
3249 bool lookup_cached_name(const char *domain_name,
3250 const char *name,
3251 struct dom_sid *sid,
3252 enum lsa_SidType *type)
3254 struct winbindd_domain *domain;
3255 NTSTATUS status;
3256 bool original_online_state;
3258 domain = find_lookup_domain_from_name(domain_name);
3259 if (domain == NULL) {
3260 return false;
3263 /* If we are doing a cached logon, temporarily set the domain
3264 offline so the cache won't expire the entry */
3266 original_online_state = domain->online;
3267 domain->online = false;
3268 status = wcache_name_to_sid(domain, domain_name, name, sid, type);
3269 domain->online = original_online_state;
3271 return NT_STATUS_IS_OK(status);
3274 void cache_name2sid(struct winbindd_domain *domain,
3275 const char *domain_name, const char *name,
3276 enum lsa_SidType type, const struct dom_sid *sid)
3278 refresh_sequence_number(domain, false);
3279 wcache_save_name_to_sid(domain, NT_STATUS_OK, domain_name, name,
3280 sid, type);
3284 * The original idea that this cache only contains centries has
3285 * been blurred - now other stuff gets put in here. Ensure we
3286 * ignore these things on cleanup.
3289 static int traverse_fn_cleanup(TDB_CONTEXT *the_tdb, TDB_DATA kbuf,
3290 TDB_DATA dbuf, void *state)
3292 struct cache_entry *centry;
3294 if (is_non_centry_key(kbuf)) {
3295 return 0;
3298 centry = wcache_fetch_raw((char *)kbuf.dptr);
3299 if (!centry) {
3300 return 0;
3303 if (!NT_STATUS_IS_OK(centry->status)) {
3304 DEBUG(10,("deleting centry %s\n", (const char *)kbuf.dptr));
3305 tdb_delete(the_tdb, kbuf);
3308 centry_free(centry);
3309 return 0;
3312 /* flush the cache */
3313 void wcache_flush_cache(void)
3315 if (!wcache)
3316 return;
3317 if (wcache->tdb) {
3318 tdb_close(wcache->tdb);
3319 wcache->tdb = NULL;
3321 if (!winbindd_use_cache()) {
3322 return;
3325 /* when working offline we must not clear the cache on restart */
3326 wcache->tdb = tdb_open_log(state_path("winbindd_cache.tdb"),
3327 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
3328 TDB_INCOMPATIBLE_HASH |
3329 (lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST)),
3330 O_RDWR|O_CREAT, 0600);
3332 if (!wcache->tdb) {
3333 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
3334 return;
3337 tdb_traverse(wcache->tdb, traverse_fn_cleanup, NULL);
3339 DEBUG(10,("wcache_flush_cache success\n"));
3342 /* Count cached creds */
3344 static int traverse_fn_cached_creds(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
3345 void *state)
3347 int *cred_count = (int*)state;
3349 if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
3350 (*cred_count)++;
3352 return 0;
3355 NTSTATUS wcache_count_cached_creds(struct winbindd_domain *domain, int *count)
3357 struct winbind_cache *cache = get_cache(domain);
3359 *count = 0;
3361 if (!cache->tdb) {
3362 return NT_STATUS_INTERNAL_DB_ERROR;
3365 tdb_traverse(cache->tdb, traverse_fn_cached_creds, (void *)count);
3367 return NT_STATUS_OK;
3370 struct cred_list {
3371 struct cred_list *prev, *next;
3372 TDB_DATA key;
3373 fstring name;
3374 time_t created;
3376 static struct cred_list *wcache_cred_list;
3378 static int traverse_fn_get_credlist(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
3379 void *state)
3381 struct cred_list *cred;
3383 if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
3385 cred = SMB_MALLOC_P(struct cred_list);
3386 if (cred == NULL) {
3387 DEBUG(0,("traverse_fn_remove_first_creds: failed to malloc new entry for list\n"));
3388 return -1;
3391 ZERO_STRUCTP(cred);
3393 /* save a copy of the key */
3395 fstrcpy(cred->name, (const char *)kbuf.dptr);
3396 DLIST_ADD(wcache_cred_list, cred);
3399 return 0;
3402 NTSTATUS wcache_remove_oldest_cached_creds(struct winbindd_domain *domain, const struct dom_sid *sid)
3404 struct winbind_cache *cache = get_cache(domain);
3405 NTSTATUS status;
3406 int ret;
3407 struct cred_list *cred, *oldest = NULL;
3409 if (!cache->tdb) {
3410 return NT_STATUS_INTERNAL_DB_ERROR;
3413 /* we possibly already have an entry */
3414 if (sid && NT_STATUS_IS_OK(wcache_cached_creds_exist(domain, sid))) {
3416 fstring key_str, tmp;
3418 DEBUG(11,("we already have an entry, deleting that\n"));
3420 fstr_sprintf(key_str, "CRED/%s", sid_to_fstring(tmp, sid));
3422 tdb_delete(cache->tdb, string_tdb_data(key_str));
3424 return NT_STATUS_OK;
3427 ret = tdb_traverse(cache->tdb, traverse_fn_get_credlist, NULL);
3428 if (ret == 0) {
3429 return NT_STATUS_OK;
3430 } else if ((ret < 0) || (wcache_cred_list == NULL)) {
3431 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
3434 ZERO_STRUCTP(oldest);
3436 for (cred = wcache_cred_list; cred; cred = cred->next) {
3438 TDB_DATA data;
3439 time_t t;
3441 data = tdb_fetch_compat(cache->tdb, string_tdb_data(cred->name));
3442 if (!data.dptr) {
3443 DEBUG(10,("wcache_remove_oldest_cached_creds: entry for [%s] not found\n",
3444 cred->name));
3445 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
3446 goto done;
3449 t = IVAL(data.dptr, 0);
3450 SAFE_FREE(data.dptr);
3452 if (!oldest) {
3453 oldest = SMB_MALLOC_P(struct cred_list);
3454 if (oldest == NULL) {
3455 status = NT_STATUS_NO_MEMORY;
3456 goto done;
3459 fstrcpy(oldest->name, cred->name);
3460 oldest->created = t;
3461 continue;
3464 if (t < oldest->created) {
3465 fstrcpy(oldest->name, cred->name);
3466 oldest->created = t;
3470 if (tdb_delete(cache->tdb, string_tdb_data(oldest->name)) == 0) {
3471 status = NT_STATUS_OK;
3472 } else {
3473 status = NT_STATUS_UNSUCCESSFUL;
3475 done:
3476 SAFE_FREE(wcache_cred_list);
3477 SAFE_FREE(oldest);
3479 return status;
3482 /* Change the global online/offline state. */
3483 bool set_global_winbindd_state_offline(void)
3485 TDB_DATA data;
3487 DEBUG(10,("set_global_winbindd_state_offline: offline requested.\n"));
3489 /* Only go offline if someone has created
3490 the key "WINBINDD_OFFLINE" in the cache tdb. */
3492 if (wcache == NULL || wcache->tdb == NULL) {
3493 DEBUG(10,("set_global_winbindd_state_offline: wcache not open yet.\n"));
3494 return false;
3497 if (!lp_winbind_offline_logon()) {
3498 DEBUG(10,("set_global_winbindd_state_offline: rejecting.\n"));
3499 return false;
3502 if (global_winbindd_offline_state) {
3503 /* Already offline. */
3504 return true;
3507 data = tdb_fetch_bystring( wcache->tdb, "WINBINDD_OFFLINE" );
3509 if (!data.dptr || data.dsize != 4) {
3510 DEBUG(10,("set_global_winbindd_state_offline: offline state not set.\n"));
3511 SAFE_FREE(data.dptr);
3512 return false;
3513 } else {
3514 DEBUG(10,("set_global_winbindd_state_offline: offline state set.\n"));
3515 global_winbindd_offline_state = true;
3516 SAFE_FREE(data.dptr);
3517 return true;
3521 void set_global_winbindd_state_online(void)
3523 DEBUG(10,("set_global_winbindd_state_online: online requested.\n"));
3525 if (!lp_winbind_offline_logon()) {
3526 DEBUG(10,("set_global_winbindd_state_online: rejecting.\n"));
3527 return;
3530 if (!global_winbindd_offline_state) {
3531 /* Already online. */
3532 return;
3534 global_winbindd_offline_state = false;
3536 if (!wcache->tdb) {
3537 return;
3540 /* Ensure there is no key "WINBINDD_OFFLINE" in the cache tdb. */
3541 tdb_delete_bystring(wcache->tdb, "WINBINDD_OFFLINE");
3544 bool get_global_winbindd_state_offline(void)
3546 return global_winbindd_offline_state;
3549 /***********************************************************************
3550 Validate functions for all possible cache tdb keys.
3551 ***********************************************************************/
3553 static struct cache_entry *create_centry_validate(const char *kstr, TDB_DATA data,
3554 struct tdb_validation_status *state)
3556 struct cache_entry *centry;
3558 centry = SMB_XMALLOC_P(struct cache_entry);
3559 centry->data = (unsigned char *)smb_memdup(data.dptr, data.dsize);
3560 if (!centry->data) {
3561 SAFE_FREE(centry);
3562 return NULL;
3564 centry->len = data.dsize;
3565 centry->ofs = 0;
3567 if (centry->len < 16) {
3568 /* huh? corrupt cache? */
3569 DEBUG(0,("create_centry_validate: Corrupt cache for key %s "
3570 "(len < 16) ?\n", kstr));
3571 centry_free(centry);
3572 state->bad_entry = true;
3573 state->success = false;
3574 return NULL;
3577 centry->status = NT_STATUS(centry_uint32(centry));
3578 centry->sequence_number = centry_uint32(centry);
3579 centry->timeout = centry_uint64_t(centry);
3580 return centry;
3583 static int validate_seqnum(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3584 struct tdb_validation_status *state)
3586 if (dbuf.dsize != 8) {
3587 DEBUG(0,("validate_seqnum: Corrupt cache for key %s (len %u != 8) ?\n",
3588 keystr, (unsigned int)dbuf.dsize ));
3589 state->bad_entry = true;
3590 return 1;
3592 return 0;
3595 static int validate_ns(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3596 struct tdb_validation_status *state)
3598 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3599 if (!centry) {
3600 return 1;
3603 (void)centry_uint32(centry);
3604 if (NT_STATUS_IS_OK(centry->status)) {
3605 struct dom_sid sid;
3606 (void)centry_sid(centry, &sid);
3609 centry_free(centry);
3611 if (!(state->success)) {
3612 return 1;
3614 DEBUG(10,("validate_ns: %s ok\n", keystr));
3615 return 0;
3618 static int validate_sn(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3619 struct tdb_validation_status *state)
3621 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3622 if (!centry) {
3623 return 1;
3626 if (NT_STATUS_IS_OK(centry->status)) {
3627 (void)centry_uint32(centry);
3628 (void)centry_string(centry, mem_ctx);
3629 (void)centry_string(centry, mem_ctx);
3632 centry_free(centry);
3634 if (!(state->success)) {
3635 return 1;
3637 DEBUG(10,("validate_sn: %s ok\n", keystr));
3638 return 0;
3641 static int validate_u(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3642 struct tdb_validation_status *state)
3644 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3645 struct dom_sid sid;
3647 if (!centry) {
3648 return 1;
3651 (void)centry_string(centry, mem_ctx);
3652 (void)centry_string(centry, mem_ctx);
3653 (void)centry_string(centry, mem_ctx);
3654 (void)centry_string(centry, mem_ctx);
3655 (void)centry_uint32(centry);
3656 (void)centry_sid(centry, &sid);
3657 (void)centry_sid(centry, &sid);
3659 centry_free(centry);
3661 if (!(state->success)) {
3662 return 1;
3664 DEBUG(10,("validate_u: %s ok\n", keystr));
3665 return 0;
3668 static int validate_loc_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3669 struct tdb_validation_status *state)
3671 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3673 if (!centry) {
3674 return 1;
3677 (void)centry_nttime(centry);
3678 (void)centry_nttime(centry);
3679 (void)centry_uint16(centry);
3681 centry_free(centry);
3683 if (!(state->success)) {
3684 return 1;
3686 DEBUG(10,("validate_loc_pol: %s ok\n", keystr));
3687 return 0;
3690 static int validate_pwd_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3691 struct tdb_validation_status *state)
3693 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3695 if (!centry) {
3696 return 1;
3699 (void)centry_uint16(centry);
3700 (void)centry_uint16(centry);
3701 (void)centry_uint32(centry);
3702 (void)centry_nttime(centry);
3703 (void)centry_nttime(centry);
3705 centry_free(centry);
3707 if (!(state->success)) {
3708 return 1;
3710 DEBUG(10,("validate_pwd_pol: %s ok\n", keystr));
3711 return 0;
3714 static int validate_cred(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3715 struct tdb_validation_status *state)
3717 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3719 if (!centry) {
3720 return 1;
3723 (void)centry_time(centry);
3724 (void)centry_hash16(centry, mem_ctx);
3726 /* We only have 17 bytes more data in the salted cred case. */
3727 if (centry->len - centry->ofs == 17) {
3728 (void)centry_hash16(centry, mem_ctx);
3731 centry_free(centry);
3733 if (!(state->success)) {
3734 return 1;
3736 DEBUG(10,("validate_cred: %s ok\n", keystr));
3737 return 0;
3740 static int validate_ul(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3741 struct tdb_validation_status *state)
3743 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3744 int32 num_entries, i;
3746 if (!centry) {
3747 return 1;
3750 num_entries = (int32)centry_uint32(centry);
3752 for (i=0; i< num_entries; i++) {
3753 struct dom_sid sid;
3754 (void)centry_string(centry, mem_ctx);
3755 (void)centry_string(centry, mem_ctx);
3756 (void)centry_string(centry, mem_ctx);
3757 (void)centry_string(centry, mem_ctx);
3758 (void)centry_sid(centry, &sid);
3759 (void)centry_sid(centry, &sid);
3762 centry_free(centry);
3764 if (!(state->success)) {
3765 return 1;
3767 DEBUG(10,("validate_ul: %s ok\n", keystr));
3768 return 0;
3771 static int validate_gl(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3772 struct tdb_validation_status *state)
3774 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3775 int32 num_entries, i;
3777 if (!centry) {
3778 return 1;
3781 num_entries = centry_uint32(centry);
3783 for (i=0; i< num_entries; i++) {
3784 (void)centry_string(centry, mem_ctx);
3785 (void)centry_string(centry, mem_ctx);
3786 (void)centry_uint32(centry);
3789 centry_free(centry);
3791 if (!(state->success)) {
3792 return 1;
3794 DEBUG(10,("validate_gl: %s ok\n", keystr));
3795 return 0;
3798 static int validate_ug(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3799 struct tdb_validation_status *state)
3801 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3802 int32 num_groups, i;
3804 if (!centry) {
3805 return 1;
3808 num_groups = centry_uint32(centry);
3810 for (i=0; i< num_groups; i++) {
3811 struct dom_sid sid;
3812 centry_sid(centry, &sid);
3815 centry_free(centry);
3817 if (!(state->success)) {
3818 return 1;
3820 DEBUG(10,("validate_ug: %s ok\n", keystr));
3821 return 0;
3824 static int validate_ua(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3825 struct tdb_validation_status *state)
3827 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3828 int32 num_aliases, i;
3830 if (!centry) {
3831 return 1;
3834 num_aliases = centry_uint32(centry);
3836 for (i=0; i < num_aliases; i++) {
3837 (void)centry_uint32(centry);
3840 centry_free(centry);
3842 if (!(state->success)) {
3843 return 1;
3845 DEBUG(10,("validate_ua: %s ok\n", keystr));
3846 return 0;
3849 static int validate_gm(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 num_names, i;
3855 if (!centry) {
3856 return 1;
3859 num_names = centry_uint32(centry);
3861 for (i=0; i< num_names; i++) {
3862 struct dom_sid sid;
3863 centry_sid(centry, &sid);
3864 (void)centry_string(centry, mem_ctx);
3865 (void)centry_uint32(centry);
3868 centry_free(centry);
3870 if (!(state->success)) {
3871 return 1;
3873 DEBUG(10,("validate_gm: %s ok\n", keystr));
3874 return 0;
3877 static int validate_dr(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3878 struct tdb_validation_status *state)
3880 /* Can't say anything about this other than must be nonzero. */
3881 if (dbuf.dsize == 0) {
3882 DEBUG(0,("validate_dr: Corrupt cache for key %s (len == 0) ?\n",
3883 keystr));
3884 state->bad_entry = true;
3885 state->success = false;
3886 return 1;
3889 DEBUG(10,("validate_dr: %s ok\n", keystr));
3890 return 0;
3893 static int validate_de(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3894 struct tdb_validation_status *state)
3896 /* Can't say anything about this other than must be nonzero. */
3897 if (dbuf.dsize == 0) {
3898 DEBUG(0,("validate_de: Corrupt cache for key %s (len == 0) ?\n",
3899 keystr));
3900 state->bad_entry = true;
3901 state->success = false;
3902 return 1;
3905 DEBUG(10,("validate_de: %s ok\n", keystr));
3906 return 0;
3909 static int validate_pwinfo(TALLOC_CTX *mem_ctx, const char *keystr,
3910 TDB_DATA dbuf, struct tdb_validation_status *state)
3912 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3914 if (!centry) {
3915 return 1;
3918 (void)centry_string(centry, mem_ctx);
3919 (void)centry_string(centry, mem_ctx);
3920 (void)centry_string(centry, mem_ctx);
3921 (void)centry_uint32(centry);
3923 centry_free(centry);
3925 if (!(state->success)) {
3926 return 1;
3928 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3929 return 0;
3932 static int validate_nss_an(TALLOC_CTX *mem_ctx, const char *keystr,
3933 TDB_DATA dbuf,
3934 struct tdb_validation_status *state)
3936 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3938 if (!centry) {
3939 return 1;
3942 (void)centry_string( centry, mem_ctx );
3944 centry_free(centry);
3946 if (!(state->success)) {
3947 return 1;
3949 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3950 return 0;
3953 static int validate_nss_na(TALLOC_CTX *mem_ctx, const char *keystr,
3954 TDB_DATA dbuf,
3955 struct tdb_validation_status *state)
3957 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3959 if (!centry) {
3960 return 1;
3963 (void)centry_string( centry, mem_ctx );
3965 centry_free(centry);
3967 if (!(state->success)) {
3968 return 1;
3970 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3971 return 0;
3974 static int validate_trustdomcache(TALLOC_CTX *mem_ctx, const char *keystr,
3975 TDB_DATA dbuf,
3976 struct tdb_validation_status *state)
3978 if (dbuf.dsize == 0) {
3979 DEBUG(0, ("validate_trustdomcache: Corrupt cache for "
3980 "key %s (len ==0) ?\n", keystr));
3981 state->bad_entry = true;
3982 state->success = false;
3983 return 1;
3986 DEBUG(10, ("validate_trustdomcache: %s ok\n", keystr));
3987 DEBUGADD(10, (" Don't trust me, I am a DUMMY!\n"));
3988 return 0;
3991 static int validate_offline(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3992 struct tdb_validation_status *state)
3994 if (dbuf.dsize != 4) {
3995 DEBUG(0,("validate_offline: Corrupt cache for key %s (len %u != 4) ?\n",
3996 keystr, (unsigned int)dbuf.dsize ));
3997 state->bad_entry = true;
3998 state->success = false;
3999 return 1;
4001 DEBUG(10,("validate_offline: %s ok\n", keystr));
4002 return 0;
4005 static int validate_ndr(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
4006 struct tdb_validation_status *state)
4009 * Ignore validation for now. The proper way to do this is with a
4010 * checksum. Just pure parsing does not really catch much.
4012 return 0;
4015 static int validate_cache_version(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
4016 struct tdb_validation_status *state)
4018 if (dbuf.dsize != 4) {
4019 DEBUG(0, ("validate_cache_version: Corrupt cache for "
4020 "key %s (len %u != 4) ?\n",
4021 keystr, (unsigned int)dbuf.dsize));
4022 state->bad_entry = true;
4023 state->success = false;
4024 return 1;
4027 DEBUG(10, ("validate_cache_version: %s ok\n", keystr));
4028 return 0;
4031 /***********************************************************************
4032 A list of all possible cache tdb keys with associated validation
4033 functions.
4034 ***********************************************************************/
4036 struct key_val_struct {
4037 const char *keyname;
4038 int (*validate_data_fn)(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, struct tdb_validation_status* state);
4039 } key_val[] = {
4040 {"SEQNUM/", validate_seqnum},
4041 {"NS/", validate_ns},
4042 {"SN/", validate_sn},
4043 {"U/", validate_u},
4044 {"LOC_POL/", validate_loc_pol},
4045 {"PWD_POL/", validate_pwd_pol},
4046 {"CRED/", validate_cred},
4047 {"UL/", validate_ul},
4048 {"GL/", validate_gl},
4049 {"UG/", validate_ug},
4050 {"UA", validate_ua},
4051 {"GM/", validate_gm},
4052 {"DR/", validate_dr},
4053 {"DE/", validate_de},
4054 {"NSS/PWINFO/", validate_pwinfo},
4055 {"TRUSTDOMCACHE/", validate_trustdomcache},
4056 {"NSS/NA/", validate_nss_na},
4057 {"NSS/AN/", validate_nss_an},
4058 {"WINBINDD_OFFLINE", validate_offline},
4059 {"NDR/", validate_ndr},
4060 {WINBINDD_CACHE_VERSION_KEYSTR, validate_cache_version},
4061 {NULL, NULL}
4064 /***********************************************************************
4065 Function to look at every entry in the tdb and validate it as far as
4066 possible.
4067 ***********************************************************************/
4069 static int cache_traverse_validate_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, void *state)
4071 int i;
4072 unsigned int max_key_len = 1024;
4073 struct tdb_validation_status *v_state = (struct tdb_validation_status *)state;
4075 /* Paranoia check. */
4076 if (strncmp("UA/", (const char *)kbuf.dptr, 3) == 0 ||
4077 strncmp("NDR/", (const char *)kbuf.dptr, 4) == 0) {
4078 max_key_len = 1024 * 1024;
4080 if (kbuf.dsize > max_key_len) {
4081 DEBUG(0, ("cache_traverse_validate_fn: key length too large: "
4082 "(%u) > (%u)\n\n",
4083 (unsigned int)kbuf.dsize, (unsigned int)max_key_len));
4084 return 1;
4087 for (i = 0; key_val[i].keyname; i++) {
4088 size_t namelen = strlen(key_val[i].keyname);
4089 if (kbuf.dsize >= namelen && (
4090 strncmp(key_val[i].keyname, (const char *)kbuf.dptr, namelen)) == 0) {
4091 TALLOC_CTX *mem_ctx;
4092 char *keystr;
4093 int ret;
4095 keystr = SMB_MALLOC_ARRAY(char, kbuf.dsize+1);
4096 if (!keystr) {
4097 return 1;
4099 memcpy(keystr, kbuf.dptr, kbuf.dsize);
4100 keystr[kbuf.dsize] = '\0';
4102 mem_ctx = talloc_init("validate_ctx");
4103 if (!mem_ctx) {
4104 SAFE_FREE(keystr);
4105 return 1;
4108 ret = key_val[i].validate_data_fn(mem_ctx, keystr, dbuf,
4109 v_state);
4111 SAFE_FREE(keystr);
4112 talloc_destroy(mem_ctx);
4113 return ret;
4117 DEBUG(0,("cache_traverse_validate_fn: unknown cache entry\nkey :\n"));
4118 dump_data(0, (uint8 *)kbuf.dptr, kbuf.dsize);
4119 DEBUG(0,("data :\n"));
4120 dump_data(0, (uint8 *)dbuf.dptr, dbuf.dsize);
4121 v_state->unknown_key = true;
4122 v_state->success = false;
4123 return 1; /* terminate. */
4126 static void validate_panic(const char *const why)
4128 DEBUG(0,("validating cache: would panic %s\n", why ));
4129 DEBUGADD(0, ("exiting instead (cache validation mode)\n"));
4130 exit(47);
4133 static int wbcache_update_centry_fn(TDB_CONTEXT *tdb,
4134 TDB_DATA key,
4135 TDB_DATA data,
4136 void *state)
4138 uint64_t ctimeout;
4139 TDB_DATA blob;
4141 if (is_non_centry_key(key)) {
4142 return 0;
4145 if (data.dptr == NULL || data.dsize == 0) {
4146 if (tdb_delete(tdb, key) < 0) {
4147 DEBUG(0, ("tdb_delete for [%s] failed!\n",
4148 key.dptr));
4149 return 1;
4153 /* add timeout to blob (uint64_t) */
4154 blob.dsize = data.dsize + 8;
4156 blob.dptr = SMB_XMALLOC_ARRAY(uint8_t, blob.dsize);
4157 if (blob.dptr == NULL) {
4158 return 1;
4160 memset(blob.dptr, 0, blob.dsize);
4162 /* copy status and seqnum */
4163 memcpy(blob.dptr, data.dptr, 8);
4165 /* add timeout */
4166 ctimeout = lp_winbind_cache_time() + time(NULL);
4167 SBVAL(blob.dptr, 8, ctimeout);
4169 /* copy the rest */
4170 memcpy(blob.dptr + 16, data.dptr + 8, data.dsize - 8);
4172 if (tdb_store(tdb, key, blob, TDB_REPLACE) < 0) {
4173 DEBUG(0, ("tdb_store to update [%s] failed!\n",
4174 key.dptr));
4175 SAFE_FREE(blob.dptr);
4176 return 1;
4179 SAFE_FREE(blob.dptr);
4180 return 0;
4183 static bool wbcache_upgrade_v1_to_v2(TDB_CONTEXT *tdb)
4185 int rc;
4187 DEBUG(1, ("Upgrade to version 2 of the winbindd_cache.tdb\n"));
4189 rc = tdb_traverse(tdb, wbcache_update_centry_fn, NULL);
4190 if (rc < 0) {
4191 return false;
4194 return true;
4197 /***********************************************************************
4198 Try and validate every entry in the winbindd cache. If we fail here,
4199 delete the cache tdb and return non-zero.
4200 ***********************************************************************/
4202 int winbindd_validate_cache(void)
4204 int ret = -1;
4205 const char *tdb_path = state_path("winbindd_cache.tdb");
4206 TDB_CONTEXT *tdb = NULL;
4207 uint32_t vers_id;
4208 bool ok;
4210 DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
4211 smb_panic_fn = validate_panic;
4213 tdb = tdb_open_log(tdb_path,
4214 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
4215 TDB_INCOMPATIBLE_HASH |
4216 ( lp_winbind_offline_logon()
4217 ? TDB_DEFAULT
4218 : TDB_DEFAULT | TDB_CLEAR_IF_FIRST ),
4219 O_RDWR|O_CREAT,
4220 0600);
4221 if (!tdb) {
4222 DEBUG(0, ("winbindd_validate_cache: "
4223 "error opening/initializing tdb\n"));
4224 goto done;
4227 /* Version check and upgrade code. */
4228 if (!tdb_fetch_uint32(tdb, WINBINDD_CACHE_VERSION_KEYSTR, &vers_id)) {
4229 DEBUG(10, ("Fresh database\n"));
4230 tdb_store_uint32(tdb, WINBINDD_CACHE_VERSION_KEYSTR, WINBINDD_CACHE_VERSION);
4231 vers_id = WINBINDD_CACHE_VERSION;
4234 if (vers_id != WINBINDD_CACHE_VERSION) {
4235 if (vers_id == WINBINDD_CACHE_VER1) {
4236 ok = wbcache_upgrade_v1_to_v2(tdb);
4237 if (!ok) {
4238 DEBUG(10, ("winbindd_validate_cache: upgrade to version 2 failed.\n"));
4239 unlink(tdb_path);
4240 goto done;
4243 tdb_store_uint32(tdb,
4244 WINBINDD_CACHE_VERSION_KEYSTR,
4245 WINBINDD_CACHE_VERSION);
4246 vers_id = WINBINDD_CACHE_VER2;
4250 tdb_close(tdb);
4252 ret = tdb_validate_and_backup(tdb_path, cache_traverse_validate_fn);
4254 if (ret != 0) {
4255 DEBUG(10, ("winbindd_validate_cache: validation not successful.\n"));
4256 DEBUGADD(10, ("removing tdb %s.\n", tdb_path));
4257 unlink(tdb_path);
4260 done:
4261 DEBUG(10, ("winbindd_validate_cache: restoring panic function\n"));
4262 smb_panic_fn = smb_panic;
4263 return ret;
4266 /***********************************************************************
4267 Try and validate every entry in the winbindd cache.
4268 ***********************************************************************/
4270 int winbindd_validate_cache_nobackup(void)
4272 int ret = -1;
4273 const char *tdb_path = state_path("winbindd_cache.tdb");
4275 DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
4276 smb_panic_fn = validate_panic;
4279 if (wcache == NULL || wcache->tdb == NULL) {
4280 ret = tdb_validate_open(tdb_path, cache_traverse_validate_fn);
4281 } else {
4282 ret = tdb_validate(wcache->tdb, cache_traverse_validate_fn);
4285 if (ret != 0) {
4286 DEBUG(10, ("winbindd_validate_cache_nobackup: validation not "
4287 "successful.\n"));
4290 DEBUG(10, ("winbindd_validate_cache_nobackup: restoring panic "
4291 "function\n"));
4292 smb_panic_fn = smb_panic;
4293 return ret;
4296 bool winbindd_cache_validate_and_initialize(void)
4298 close_winbindd_cache();
4300 if (lp_winbind_offline_logon()) {
4301 if (winbindd_validate_cache() < 0) {
4302 DEBUG(0, ("winbindd cache tdb corrupt and no backup "
4303 "could be restored.\n"));
4307 return initialize_winbindd_cache();
4310 /*********************************************************************
4311 ********************************************************************/
4313 static bool add_wbdomain_to_tdc_array( struct winbindd_domain *new_dom,
4314 struct winbindd_tdc_domain **domains,
4315 size_t *num_domains )
4317 struct winbindd_tdc_domain *list = NULL;
4318 size_t idx;
4319 int i;
4320 bool set_only = false;
4322 /* don't allow duplicates */
4324 idx = *num_domains;
4325 list = *domains;
4327 for ( i=0; i< (*num_domains); i++ ) {
4328 if ( strequal( new_dom->name, list[i].domain_name ) ) {
4329 DEBUG(10,("add_wbdomain_to_tdc_array: Found existing record for %s\n",
4330 new_dom->name));
4331 idx = i;
4332 set_only = true;
4334 break;
4338 if ( !set_only ) {
4339 if ( !*domains ) {
4340 list = talloc_array( NULL, struct winbindd_tdc_domain, 1 );
4341 idx = 0;
4342 } else {
4343 list = talloc_realloc( *domains, *domains,
4344 struct winbindd_tdc_domain,
4345 (*num_domains)+1);
4346 idx = *num_domains;
4349 ZERO_STRUCT( list[idx] );
4352 if ( !list )
4353 return false;
4355 list[idx].domain_name = talloc_strdup( list, new_dom->name );
4356 list[idx].dns_name = talloc_strdup( list, new_dom->alt_name );
4358 if ( !is_null_sid( &new_dom->sid ) ) {
4359 sid_copy( &list[idx].sid, &new_dom->sid );
4360 } else {
4361 sid_copy(&list[idx].sid, &global_sid_NULL);
4364 if ( new_dom->domain_flags != 0x0 )
4365 list[idx].trust_flags = new_dom->domain_flags;
4367 if ( new_dom->domain_type != 0x0 )
4368 list[idx].trust_type = new_dom->domain_type;
4370 if ( new_dom->domain_trust_attribs != 0x0 )
4371 list[idx].trust_attribs = new_dom->domain_trust_attribs;
4373 if ( !set_only ) {
4374 *domains = list;
4375 *num_domains = idx + 1;
4378 return true;
4381 /*********************************************************************
4382 ********************************************************************/
4384 static TDB_DATA make_tdc_key( const char *domain_name )
4386 char *keystr = NULL;
4387 TDB_DATA key = { NULL, 0 };
4389 if ( !domain_name ) {
4390 DEBUG(5,("make_tdc_key: Keyname workgroup is NULL!\n"));
4391 return key;
4394 if (asprintf( &keystr, "TRUSTDOMCACHE/%s", domain_name ) == -1) {
4395 return key;
4397 key = string_term_tdb_data(keystr);
4399 return key;
4402 /*********************************************************************
4403 ********************************************************************/
4405 static int pack_tdc_domains( struct winbindd_tdc_domain *domains,
4406 size_t num_domains,
4407 unsigned char **buf )
4409 unsigned char *buffer = NULL;
4410 int len = 0;
4411 int buflen = 0;
4412 int i = 0;
4414 DEBUG(10,("pack_tdc_domains: Packing %d trusted domains\n",
4415 (int)num_domains));
4417 buflen = 0;
4419 again:
4420 len = 0;
4422 /* Store the number of array items first */
4423 len += tdb_pack( buffer+len, buflen-len, "d",
4424 num_domains );
4426 /* now pack each domain trust record */
4427 for ( i=0; i<num_domains; i++ ) {
4429 fstring tmp;
4431 if ( buflen > 0 ) {
4432 DEBUG(10,("pack_tdc_domains: Packing domain %s (%s)\n",
4433 domains[i].domain_name,
4434 domains[i].dns_name ? domains[i].dns_name : "UNKNOWN" ));
4437 len += tdb_pack( buffer+len, buflen-len, "fffddd",
4438 domains[i].domain_name,
4439 domains[i].dns_name,
4440 sid_to_fstring(tmp, &domains[i].sid),
4441 domains[i].trust_flags,
4442 domains[i].trust_attribs,
4443 domains[i].trust_type );
4446 if ( buflen < len ) {
4447 SAFE_FREE(buffer);
4448 if ( (buffer = SMB_MALLOC_ARRAY(unsigned char, len)) == NULL ) {
4449 DEBUG(0,("pack_tdc_domains: failed to alloc buffer!\n"));
4450 buflen = -1;
4451 goto done;
4453 buflen = len;
4454 goto again;
4457 *buf = buffer;
4459 done:
4460 return buflen;
4463 /*********************************************************************
4464 ********************************************************************/
4466 static size_t unpack_tdc_domains( unsigned char *buf, int buflen,
4467 struct winbindd_tdc_domain **domains )
4469 fstring domain_name, dns_name, sid_string;
4470 uint32 type, attribs, flags;
4471 int num_domains;
4472 int len = 0;
4473 int i;
4474 struct winbindd_tdc_domain *list = NULL;
4476 /* get the number of domains */
4477 len += tdb_unpack( buf+len, buflen-len, "d", &num_domains);
4478 if ( len == -1 ) {
4479 DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
4480 return 0;
4483 list = talloc_array( NULL, struct winbindd_tdc_domain, num_domains );
4484 if ( !list ) {
4485 DEBUG(0,("unpack_tdc_domains: Failed to talloc() domain list!\n"));
4486 return 0;
4489 for ( i=0; i<num_domains; i++ ) {
4490 len += tdb_unpack( buf+len, buflen-len, "fffddd",
4491 domain_name,
4492 dns_name,
4493 sid_string,
4494 &flags,
4495 &attribs,
4496 &type );
4498 if ( len == -1 ) {
4499 DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
4500 TALLOC_FREE( list );
4501 return 0;
4504 DEBUG(11,("unpack_tdc_domains: Unpacking domain %s (%s) "
4505 "SID %s, flags = 0x%x, attribs = 0x%x, type = 0x%x\n",
4506 domain_name, dns_name, sid_string,
4507 flags, attribs, type));
4509 list[i].domain_name = talloc_strdup( list, domain_name );
4510 list[i].dns_name = talloc_strdup( list, dns_name );
4511 if ( !string_to_sid( &(list[i].sid), sid_string ) ) {
4512 DEBUG(10,("unpack_tdc_domains: no SID for domain %s\n",
4513 domain_name));
4515 list[i].trust_flags = flags;
4516 list[i].trust_attribs = attribs;
4517 list[i].trust_type = type;
4520 *domains = list;
4522 return num_domains;
4525 /*********************************************************************
4526 ********************************************************************/
4528 static bool wcache_tdc_store_list( struct winbindd_tdc_domain *domains, size_t num_domains )
4530 TDB_DATA key = make_tdc_key( lp_workgroup() );
4531 TDB_DATA data = { NULL, 0 };
4532 int ret;
4534 if ( !key.dptr )
4535 return false;
4537 /* See if we were asked to delete the cache entry */
4539 if ( !domains ) {
4540 ret = tdb_delete( wcache->tdb, key );
4541 goto done;
4544 data.dsize = pack_tdc_domains( domains, num_domains, &data.dptr );
4546 if ( !data.dptr ) {
4547 ret = -1;
4548 goto done;
4551 ret = tdb_store( wcache->tdb, key, data, 0 );
4553 done:
4554 SAFE_FREE( data.dptr );
4555 SAFE_FREE( key.dptr );
4557 return ( ret == 0 );
4560 /*********************************************************************
4561 ********************************************************************/
4563 bool wcache_tdc_fetch_list( struct winbindd_tdc_domain **domains, size_t *num_domains )
4565 TDB_DATA key = make_tdc_key( lp_workgroup() );
4566 TDB_DATA data = { NULL, 0 };
4568 *domains = NULL;
4569 *num_domains = 0;
4571 if ( !key.dptr )
4572 return false;
4574 data = tdb_fetch_compat( wcache->tdb, key );
4576 SAFE_FREE( key.dptr );
4578 if ( !data.dptr )
4579 return false;
4581 *num_domains = unpack_tdc_domains( data.dptr, data.dsize, domains );
4583 SAFE_FREE( data.dptr );
4585 if ( !*domains )
4586 return false;
4588 return true;
4591 /*********************************************************************
4592 ********************************************************************/
4594 bool wcache_tdc_add_domain( struct winbindd_domain *domain )
4596 struct winbindd_tdc_domain *dom_list = NULL;
4597 size_t num_domains = 0;
4598 bool ret = false;
4600 DEBUG(10,("wcache_tdc_add_domain: Adding domain %s (%s), SID %s, "
4601 "flags = 0x%x, attributes = 0x%x, type = 0x%x\n",
4602 domain->name, domain->alt_name,
4603 sid_string_dbg(&domain->sid),
4604 domain->domain_flags,
4605 domain->domain_trust_attribs,
4606 domain->domain_type));
4608 if ( !init_wcache() ) {
4609 return false;
4612 /* fetch the list */
4614 wcache_tdc_fetch_list( &dom_list, &num_domains );
4616 /* add the new domain */
4618 if ( !add_wbdomain_to_tdc_array( domain, &dom_list, &num_domains ) ) {
4619 goto done;
4622 /* pack the domain */
4624 if ( !wcache_tdc_store_list( dom_list, num_domains ) ) {
4625 goto done;
4628 /* Success */
4630 ret = true;
4631 done:
4632 TALLOC_FREE( dom_list );
4634 return ret;
4637 /*********************************************************************
4638 ********************************************************************/
4640 struct winbindd_tdc_domain * wcache_tdc_fetch_domain( TALLOC_CTX *ctx, const char *name )
4642 struct winbindd_tdc_domain *dom_list = NULL;
4643 size_t num_domains = 0;
4644 int i;
4645 struct winbindd_tdc_domain *d = NULL;
4647 DEBUG(10,("wcache_tdc_fetch_domain: Searching for domain %s\n", name));
4649 if ( !init_wcache() ) {
4650 return NULL;
4653 /* fetch the list */
4655 wcache_tdc_fetch_list( &dom_list, &num_domains );
4657 for ( i=0; i<num_domains; i++ ) {
4658 if ( strequal(name, dom_list[i].domain_name) ||
4659 strequal(name, dom_list[i].dns_name) )
4661 DEBUG(10,("wcache_tdc_fetch_domain: Found domain %s\n",
4662 name));
4664 d = talloc( ctx, struct winbindd_tdc_domain );
4665 if ( !d )
4666 break;
4668 d->domain_name = talloc_strdup( d, dom_list[i].domain_name );
4669 d->dns_name = talloc_strdup( d, dom_list[i].dns_name );
4670 sid_copy( &d->sid, &dom_list[i].sid );
4671 d->trust_flags = dom_list[i].trust_flags;
4672 d->trust_type = dom_list[i].trust_type;
4673 d->trust_attribs = dom_list[i].trust_attribs;
4675 break;
4679 TALLOC_FREE( dom_list );
4681 return d;
4684 /*********************************************************************
4685 ********************************************************************/
4687 struct winbindd_tdc_domain*
4688 wcache_tdc_fetch_domainbysid(TALLOC_CTX *ctx,
4689 const struct dom_sid *sid)
4691 struct winbindd_tdc_domain *dom_list = NULL;
4692 size_t num_domains = 0;
4693 int i;
4694 struct winbindd_tdc_domain *d = NULL;
4696 DEBUG(10,("wcache_tdc_fetch_domainbysid: Searching for domain %s\n",
4697 sid_string_dbg(sid)));
4699 if (!init_wcache()) {
4700 return NULL;
4703 /* fetch the list */
4705 wcache_tdc_fetch_list(&dom_list, &num_domains);
4707 for (i = 0; i<num_domains; i++) {
4708 if (dom_sid_equal(sid, &(dom_list[i].sid))) {
4709 DEBUG(10, ("wcache_tdc_fetch_domainbysid: "
4710 "Found domain %s for SID %s\n",
4711 dom_list[i].domain_name,
4712 sid_string_dbg(sid)));
4714 d = talloc(ctx, struct winbindd_tdc_domain);
4715 if (!d)
4716 break;
4718 d->domain_name = talloc_strdup(d,
4719 dom_list[i].domain_name);
4721 d->dns_name = talloc_strdup(d, dom_list[i].dns_name);
4722 sid_copy(&d->sid, &dom_list[i].sid);
4723 d->trust_flags = dom_list[i].trust_flags;
4724 d->trust_type = dom_list[i].trust_type;
4725 d->trust_attribs = dom_list[i].trust_attribs;
4727 break;
4731 TALLOC_FREE(dom_list);
4733 return d;
4737 /*********************************************************************
4738 ********************************************************************/
4740 void wcache_tdc_clear( void )
4742 if ( !init_wcache() )
4743 return;
4745 wcache_tdc_store_list( NULL, 0 );
4747 return;
4751 /*********************************************************************
4752 ********************************************************************/
4754 static void wcache_save_user_pwinfo(struct winbindd_domain *domain,
4755 NTSTATUS status,
4756 const struct dom_sid *user_sid,
4757 const char *homedir,
4758 const char *shell,
4759 const char *gecos,
4760 uint32 gid)
4762 struct cache_entry *centry;
4763 fstring tmp;
4765 if ( (centry = centry_start(domain, status)) == NULL )
4766 return;
4768 centry_put_string( centry, homedir );
4769 centry_put_string( centry, shell );
4770 centry_put_string( centry, gecos );
4771 centry_put_uint32( centry, gid );
4773 centry_end(centry, "NSS/PWINFO/%s", sid_to_fstring(tmp, user_sid) );
4775 DEBUG(10,("wcache_save_user_pwinfo: %s\n", sid_string_dbg(user_sid) ));
4777 centry_free(centry);
4780 #ifdef HAVE_ADS
4782 NTSTATUS nss_get_info_cached( struct winbindd_domain *domain,
4783 const struct dom_sid *user_sid,
4784 TALLOC_CTX *ctx,
4785 const char **homedir, const char **shell,
4786 const char **gecos, gid_t *p_gid)
4788 struct winbind_cache *cache = get_cache(domain);
4789 struct cache_entry *centry = NULL;
4790 NTSTATUS nt_status;
4791 fstring tmp;
4793 if (!cache->tdb)
4794 goto do_query;
4796 centry = wcache_fetch(cache, domain, "NSS/PWINFO/%s",
4797 sid_to_fstring(tmp, user_sid));
4799 if (!centry)
4800 goto do_query;
4802 *homedir = centry_string( centry, ctx );
4803 *shell = centry_string( centry, ctx );
4804 *gecos = centry_string( centry, ctx );
4805 *p_gid = centry_uint32( centry );
4807 centry_free(centry);
4809 DEBUG(10,("nss_get_info_cached: [Cached] - user_sid %s\n",
4810 sid_string_dbg(user_sid)));
4812 return NT_STATUS_OK;
4814 do_query:
4816 nt_status = nss_get_info( domain->name, user_sid, ctx,
4817 homedir, shell, gecos, p_gid );
4819 DEBUG(10, ("nss_get_info returned %s\n", nt_errstr(nt_status)));
4821 if ( NT_STATUS_IS_OK(nt_status) ) {
4822 DEBUG(10, ("result:\n\thomedir = '%s'\n", *homedir));
4823 DEBUGADD(10, ("\tshell = '%s'\n", *shell));
4824 DEBUGADD(10, ("\tgecos = '%s'\n", *gecos));
4825 DEBUGADD(10, ("\tgid = '%u'\n", (unsigned int)*p_gid));
4827 wcache_save_user_pwinfo( domain, nt_status, user_sid,
4828 *homedir, *shell, *gecos, *p_gid );
4831 if ( NT_STATUS_EQUAL( nt_status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND ) ) {
4832 DEBUG(5,("nss_get_info_cached: Setting domain %s offline\n",
4833 domain->name ));
4834 set_domain_offline( domain );
4837 return nt_status;
4840 #endif
4842 /* the cache backend methods are exposed via this structure */
4843 struct winbindd_methods cache_methods = {
4844 true,
4845 query_user_list,
4846 enum_dom_groups,
4847 enum_local_groups,
4848 name_to_sid,
4849 sid_to_name,
4850 rids_to_names,
4851 query_user,
4852 lookup_usergroups,
4853 lookup_useraliases,
4854 lookup_groupmem,
4855 sequence_number,
4856 lockout_policy,
4857 password_policy,
4858 trusted_domains
4861 static bool wcache_ndr_key(TALLOC_CTX *mem_ctx, char *domain_name,
4862 uint32_t opnum, const DATA_BLOB *req,
4863 TDB_DATA *pkey)
4865 char *key;
4866 size_t keylen;
4868 key = talloc_asprintf(mem_ctx, "NDR/%s/%d/", domain_name, (int)opnum);
4869 if (key == NULL) {
4870 return false;
4872 keylen = talloc_get_size(key) - 1;
4874 key = talloc_realloc(mem_ctx, key, char, keylen + req->length);
4875 if (key == NULL) {
4876 return false;
4878 memcpy(key + keylen, req->data, req->length);
4880 pkey->dptr = (uint8_t *)key;
4881 pkey->dsize = talloc_get_size(key);
4882 return true;
4885 static bool wcache_opnum_cacheable(uint32_t opnum)
4887 switch (opnum) {
4888 case NDR_WBINT_PING:
4889 case NDR_WBINT_QUERYSEQUENCENUMBER:
4890 case NDR_WBINT_ALLOCATEUID:
4891 case NDR_WBINT_ALLOCATEGID:
4892 case NDR_WBINT_CHECKMACHINEACCOUNT:
4893 case NDR_WBINT_CHANGEMACHINEACCOUNT:
4894 case NDR_WBINT_PINGDC:
4895 return false;
4897 return true;
4900 bool wcache_fetch_ndr(TALLOC_CTX *mem_ctx, struct winbindd_domain *domain,
4901 uint32_t opnum, const DATA_BLOB *req, DATA_BLOB *resp)
4903 TDB_DATA key, data;
4904 bool ret = false;
4906 if (!wcache_opnum_cacheable(opnum) ||
4907 is_my_own_sam_domain(domain) ||
4908 is_builtin_domain(domain)) {
4909 return false;
4912 if (wcache->tdb == NULL) {
4913 return false;
4916 if (!wcache_ndr_key(talloc_tos(), domain->name, opnum, req, &key)) {
4917 return false;
4919 data = tdb_fetch_compat(wcache->tdb, key);
4920 TALLOC_FREE(key.dptr);
4922 if (data.dptr == NULL) {
4923 return false;
4925 if (data.dsize < 12) {
4926 goto fail;
4929 if (!is_domain_offline(domain)) {
4930 uint32_t entry_seqnum, dom_seqnum, last_check;
4931 uint64_t entry_timeout;
4933 if (!wcache_fetch_seqnum(domain->name, &dom_seqnum,
4934 &last_check)) {
4935 goto fail;
4937 entry_seqnum = IVAL(data.dptr, 0);
4938 if (entry_seqnum != dom_seqnum) {
4939 DEBUG(10, ("Entry has wrong sequence number: %d\n",
4940 (int)entry_seqnum));
4941 goto fail;
4943 entry_timeout = BVAL(data.dptr, 4);
4944 if (time(NULL) > entry_timeout) {
4945 DEBUG(10, ("Entry has timed out\n"));
4946 goto fail;
4950 resp->data = (uint8_t *)talloc_memdup(mem_ctx, data.dptr + 12,
4951 data.dsize - 12);
4952 if (resp->data == NULL) {
4953 DEBUG(10, ("talloc failed\n"));
4954 goto fail;
4956 resp->length = data.dsize - 12;
4958 ret = true;
4959 fail:
4960 SAFE_FREE(data.dptr);
4961 return ret;
4964 void wcache_store_ndr(struct winbindd_domain *domain, uint32_t opnum,
4965 const DATA_BLOB *req, const DATA_BLOB *resp)
4967 TDB_DATA key, data;
4968 uint32_t dom_seqnum, last_check;
4969 uint64_t timeout;
4971 if (!wcache_opnum_cacheable(opnum) ||
4972 is_my_own_sam_domain(domain) ||
4973 is_builtin_domain(domain)) {
4974 return;
4977 if (wcache->tdb == NULL) {
4978 return;
4981 if (!wcache_fetch_seqnum(domain->name, &dom_seqnum, &last_check)) {
4982 DEBUG(10, ("could not fetch seqnum for domain %s\n",
4983 domain->name));
4984 return;
4987 if (!wcache_ndr_key(talloc_tos(), domain->name, opnum, req, &key)) {
4988 return;
4991 timeout = time(NULL) + lp_winbind_cache_time();
4993 data.dsize = resp->length + 12;
4994 data.dptr = talloc_array(key.dptr, uint8_t, data.dsize);
4995 if (data.dptr == NULL) {
4996 goto done;
4999 SIVAL(data.dptr, 0, dom_seqnum);
5000 SBVAL(data.dptr, 4, timeout);
5001 memcpy(data.dptr + 12, resp->data, resp->length);
5003 tdb_store(wcache->tdb, key, data, 0);
5005 done:
5006 TALLOC_FREE(key.dptr);
5007 return;