winbind: Avoid a few talloc_tos() in winbindd_cache.c
[Samba.git] / source3 / winbindd / winbindd_cache.c
blobdef5fa0da56266b8f1bbbfb6c40807179c6a0e4d
1 /*
2 Unix SMB/CIFS implementation.
4 Winbind cache backend functions
6 Copyright (C) Andrew Tridgell 2001
7 Copyright (C) Gerald Carter 2003-2007
8 Copyright (C) Volker Lendecke 2005
9 Copyright (C) Guenther Deschner 2005
10 Copyright (C) Michael Adam 2007
12 This program is free software; you can redistribute it and/or modify
13 it under the terms of the GNU General Public License as published by
14 the Free Software Foundation; either version 3 of the License, or
15 (at your option) any later version.
17 This program is distributed in the hope that it will be useful,
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 GNU General Public License for more details.
22 You should have received a copy of the GNU General Public License
23 along with this program. If not, see <http://www.gnu.org/licenses/>.
26 #include "includes.h"
27 #include "system/filesys.h"
28 #include "winbindd.h"
29 #include "tdb_validate.h"
30 #include "../libcli/auth/libcli_auth.h"
31 #include "../librpc/gen_ndr/ndr_winbind.h"
32 #include "ads.h"
33 #include "nss_info.h"
34 #include "../libcli/security/security.h"
35 #include "passdb/machine_sid.h"
36 #include "util_tdb.h"
38 #undef DBGC_CLASS
39 #define DBGC_CLASS DBGC_WINBIND
41 #define WINBINDD_CACHE_VER1 1 /* initial db version */
42 #define WINBINDD_CACHE_VER2 2 /* second version with timeouts for NDR entries */
44 #define WINBINDD_CACHE_VERSION WINBINDD_CACHE_VER2
45 #define WINBINDD_CACHE_VERSION_KEYSTR "WINBINDD_CACHE_VERSION"
47 extern struct winbindd_methods reconnect_methods;
48 #ifdef HAVE_ADS
49 extern struct winbindd_methods ads_methods;
50 #endif
51 extern struct winbindd_methods builtin_passdb_methods;
52 extern struct winbindd_methods sam_passdb_methods;
55 * JRA. KEEP THIS LIST UP TO DATE IF YOU ADD CACHE ENTRIES.
56 * Here are the list of entry types that are *not* stored
57 * as form struct cache_entry in the cache.
60 static const char *non_centry_keys[] = {
61 "SEQNUM/",
62 "WINBINDD_OFFLINE",
63 WINBINDD_CACHE_VERSION_KEYSTR,
64 NULL
67 /************************************************************************
68 Is this key a non-centry type ?
69 ************************************************************************/
71 static bool is_non_centry_key(TDB_DATA kbuf)
73 int i;
75 if (kbuf.dptr == NULL || kbuf.dsize == 0) {
76 return false;
78 for (i = 0; non_centry_keys[i] != NULL; i++) {
79 size_t namelen = strlen(non_centry_keys[i]);
80 if (kbuf.dsize < namelen) {
81 continue;
83 if (strncmp(non_centry_keys[i], (const char *)kbuf.dptr, namelen) == 0) {
84 return true;
87 return false;
90 /* Global online/offline state - False when online. winbindd starts up online
91 and sets this to true if the first query fails and there's an entry in
92 the cache tdb telling us to stay offline. */
94 static bool global_winbindd_offline_state;
96 struct winbind_cache {
97 TDB_CONTEXT *tdb;
100 struct cache_entry {
101 NTSTATUS status;
102 uint32 sequence_number;
103 uint64_t timeout;
104 uint8 *data;
105 uint32 len, ofs;
108 void (*smb_panic_fn)(const char *const why) = smb_panic;
110 #define WINBINDD_MAX_CACHE_SIZE (50*1024*1024)
112 static struct winbind_cache *wcache;
114 /* get the winbind_cache structure */
115 static struct winbind_cache *get_cache(struct winbindd_domain *domain)
117 struct winbind_cache *ret = wcache;
119 /* We have to know what type of domain we are dealing with first. */
121 if (domain->internal) {
122 domain->backend = &builtin_passdb_methods;
125 if (dom_sid_equal(&domain->sid, &global_sid_Builtin)) {
126 domain->initialized = true;
129 if (strequal(domain->name, get_global_sam_name()) &&
130 sid_check_is_our_sam(&domain->sid)) {
131 domain->backend = &sam_passdb_methods;
134 if ( !domain->initialized ) {
135 /* We do not need a connection to an RW DC for cache operation */
136 init_dc_connection(domain, false);
140 OK. listen up becasue I'm only going to say this once.
141 We have the following scenarios to consider
142 (a) trusted AD domains on a Samba DC,
143 (b) trusted AD domains and we are joined to a non-kerberos domain
144 (c) trusted AD domains and we are joined to a kerberos (AD) domain
146 For (a) we can always contact the trusted domain using krb5
147 since we have the domain trust account password
149 For (b) we can only use RPC since we have no way of
150 getting a krb5 ticket in our own domain
152 For (c) we can always use krb5 since we have a kerberos trust
154 --jerry
157 if (!domain->backend) {
158 #ifdef HAVE_ADS
159 struct winbindd_domain *our_domain = domain;
161 /* find our domain first so we can figure out if we
162 are joined to a kerberized domain */
164 if ( !domain->primary )
165 our_domain = find_our_domain();
167 if ((our_domain->active_directory || IS_DC)
168 && domain->active_directory
169 && !lp_winbind_rpc_only()) {
170 DEBUG(5,("get_cache: Setting ADS methods for domain %s\n", domain->name));
171 domain->backend = &ads_methods;
172 } else {
173 #endif /* HAVE_ADS */
174 DEBUG(5,("get_cache: Setting MS-RPC methods for domain %s\n", domain->name));
175 domain->backend = &reconnect_methods;
176 #ifdef HAVE_ADS
178 #endif /* HAVE_ADS */
181 if (ret)
182 return ret;
184 ret = SMB_XMALLOC_P(struct winbind_cache);
185 ZERO_STRUCTP(ret);
187 wcache = ret;
188 wcache_flush_cache();
190 return ret;
194 free a centry structure
196 static void centry_free(struct cache_entry *centry)
198 if (!centry)
199 return;
200 SAFE_FREE(centry->data);
201 free(centry);
204 static bool centry_check_bytes(struct cache_entry *centry, size_t nbytes)
206 if (centry->len - centry->ofs < nbytes) {
207 DEBUG(0,("centry corruption? needed %u bytes, have %d\n",
208 (unsigned int)nbytes,
209 centry->len - centry->ofs));
210 return false;
212 return true;
216 pull a uint64_t from a cache entry
218 static uint64_t centry_uint64_t(struct cache_entry *centry)
220 uint64_t ret;
222 if (!centry_check_bytes(centry, 8)) {
223 smb_panic_fn("centry_uint64_t");
225 ret = BVAL(centry->data, centry->ofs);
226 centry->ofs += 8;
227 return ret;
231 pull a uint32 from a cache entry
233 static uint32 centry_uint32(struct cache_entry *centry)
235 uint32 ret;
237 if (!centry_check_bytes(centry, 4)) {
238 smb_panic_fn("centry_uint32");
240 ret = IVAL(centry->data, centry->ofs);
241 centry->ofs += 4;
242 return ret;
246 pull a uint16 from a cache entry
248 static uint16 centry_uint16(struct cache_entry *centry)
250 uint16 ret;
251 if (!centry_check_bytes(centry, 2)) {
252 smb_panic_fn("centry_uint16");
254 ret = SVAL(centry->data, centry->ofs);
255 centry->ofs += 2;
256 return ret;
260 pull a uint8 from a cache entry
262 static uint8 centry_uint8(struct cache_entry *centry)
264 uint8 ret;
265 if (!centry_check_bytes(centry, 1)) {
266 smb_panic_fn("centry_uint8");
268 ret = CVAL(centry->data, centry->ofs);
269 centry->ofs += 1;
270 return ret;
274 pull a NTTIME from a cache entry
276 static NTTIME centry_nttime(struct cache_entry *centry)
278 NTTIME ret;
279 if (!centry_check_bytes(centry, 8)) {
280 smb_panic_fn("centry_nttime");
282 ret = IVAL(centry->data, centry->ofs);
283 centry->ofs += 4;
284 ret += (uint64)IVAL(centry->data, centry->ofs) << 32;
285 centry->ofs += 4;
286 return ret;
290 pull a time_t from a cache entry. time_t stored portably as a 64-bit time.
292 static time_t centry_time(struct cache_entry *centry)
294 return (time_t)centry_nttime(centry);
297 /* pull a string from a cache entry, using the supplied
298 talloc context
300 static char *centry_string(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
302 uint32 len;
303 char *ret;
305 len = centry_uint8(centry);
307 if (len == 0xFF) {
308 /* a deliberate NULL string */
309 return NULL;
312 if (!centry_check_bytes(centry, (size_t)len)) {
313 smb_panic_fn("centry_string");
316 ret = talloc_array(mem_ctx, char, len+1);
317 if (!ret) {
318 smb_panic_fn("centry_string out of memory\n");
320 memcpy(ret,centry->data + centry->ofs, len);
321 ret[len] = 0;
322 centry->ofs += len;
323 return ret;
326 /* pull a hash16 from a cache entry, using the supplied
327 talloc context
329 static char *centry_hash16(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
331 uint32 len;
332 char *ret;
334 len = centry_uint8(centry);
336 if (len != 16) {
337 DEBUG(0,("centry corruption? hash len (%u) != 16\n",
338 len ));
339 return NULL;
342 if (!centry_check_bytes(centry, 16)) {
343 return NULL;
346 ret = talloc_array(mem_ctx, char, 16);
347 if (!ret) {
348 smb_panic_fn("centry_hash out of memory\n");
350 memcpy(ret,centry->data + centry->ofs, 16);
351 centry->ofs += 16;
352 return ret;
355 /* pull a sid from a cache entry, using the supplied
356 talloc context
358 static bool centry_sid(struct cache_entry *centry, struct dom_sid *sid)
360 char *sid_string;
361 bool ret;
363 sid_string = centry_string(centry, talloc_tos());
364 if (sid_string == NULL) {
365 return false;
367 ret = string_to_sid(sid, sid_string);
368 TALLOC_FREE(sid_string);
369 return ret;
374 pull a NTSTATUS from a cache entry
376 static NTSTATUS centry_ntstatus(struct cache_entry *centry)
378 NTSTATUS status;
380 status = NT_STATUS(centry_uint32(centry));
381 return status;
385 /* the server is considered down if it can't give us a sequence number */
386 static bool wcache_server_down(struct winbindd_domain *domain)
388 bool ret;
390 if (!wcache->tdb)
391 return false;
393 ret = (domain->sequence_number == DOM_SEQUENCE_NONE);
395 if (ret)
396 DEBUG(10,("wcache_server_down: server for Domain %s down\n",
397 domain->name ));
398 return ret;
401 struct wcache_seqnum_state {
402 uint32_t *seqnum;
403 uint32_t *last_seq_check;
406 static int wcache_seqnum_parser(TDB_DATA key, TDB_DATA data,
407 void *private_data)
409 struct wcache_seqnum_state *state = private_data;
411 if (data.dsize != 8) {
412 DEBUG(10, ("wcache_fetch_seqnum: invalid data size %d\n",
413 (int)data.dsize));
414 return -1;
417 *state->seqnum = IVAL(data.dptr, 0);
418 *state->last_seq_check = IVAL(data.dptr, 4);
419 return 0;
422 static bool wcache_fetch_seqnum(const char *domain_name, uint32_t *seqnum,
423 uint32_t *last_seq_check)
425 struct wcache_seqnum_state state = {
426 .seqnum = seqnum, .last_seq_check = last_seq_check
428 size_t len = strlen(domain_name);
429 char keystr[len+8];
430 TDB_DATA key = { .dptr = (uint8_t *)keystr, .dsize = sizeof(keystr) };
431 int ret;
433 if (wcache->tdb == NULL) {
434 DEBUG(10,("wcache_fetch_seqnum: tdb == NULL\n"));
435 return false;
438 snprintf(keystr, sizeof(keystr), "SEQNUM/%s", domain_name);
440 ret = tdb_parse_record(wcache->tdb, key, wcache_seqnum_parser,
441 &state);
442 return (ret == 0);
445 static NTSTATUS fetch_cache_seqnum( struct winbindd_domain *domain, time_t now )
447 uint32 last_check, time_diff;
449 if (!wcache_fetch_seqnum(domain->name, &domain->sequence_number,
450 &last_check)) {
451 return NT_STATUS_UNSUCCESSFUL;
453 domain->last_seq_check = last_check;
455 /* have we expired? */
457 time_diff = now - domain->last_seq_check;
458 if ( time_diff > lp_winbind_cache_time() ) {
459 DEBUG(10,("fetch_cache_seqnum: timeout [%s][%u @ %u]\n",
460 domain->name, domain->sequence_number,
461 (uint32)domain->last_seq_check));
462 return NT_STATUS_UNSUCCESSFUL;
465 DEBUG(10,("fetch_cache_seqnum: success [%s][%u @ %u]\n",
466 domain->name, domain->sequence_number,
467 (uint32)domain->last_seq_check));
469 return NT_STATUS_OK;
472 bool wcache_store_seqnum(const char *domain_name, uint32_t seqnum,
473 time_t last_seq_check)
475 size_t len = strlen(domain_name);
476 char keystr[len+8];
477 TDB_DATA key = { .dptr = (uint8_t *)keystr, .dsize = sizeof(keystr) };
478 uint8_t buf[8];
479 int ret;
481 if (wcache->tdb == NULL) {
482 DEBUG(10, ("wcache_store_seqnum: wcache->tdb == NULL\n"));
483 return false;
486 snprintf(keystr, sizeof(keystr), "SEQNUM/%s", domain_name);
488 SIVAL(buf, 0, seqnum);
489 SIVAL(buf, 4, last_seq_check);
491 ret = tdb_store(wcache->tdb, key, make_tdb_data(buf, sizeof(buf)),
492 TDB_REPLACE);
493 if (ret != 0) {
494 DEBUG(10, ("tdb_store_bystring failed: %s\n",
495 tdb_errorstr(wcache->tdb)));
496 return false;
499 DEBUG(10, ("wcache_store_seqnum: success [%s][%u @ %u]\n",
500 domain_name, seqnum, (unsigned)last_seq_check));
502 return true;
505 static bool store_cache_seqnum( struct winbindd_domain *domain )
507 return wcache_store_seqnum(domain->name, domain->sequence_number,
508 domain->last_seq_check);
512 refresh the domain sequence number. If force is true
513 then always refresh it, no matter how recently we fetched it
516 static void refresh_sequence_number(struct winbindd_domain *domain, bool force)
518 NTSTATUS status;
519 unsigned time_diff;
520 time_t t = time(NULL);
521 unsigned cache_time = lp_winbind_cache_time();
523 if (is_domain_offline(domain)) {
524 return;
527 get_cache( domain );
529 #if 0 /* JERRY -- disable as the default cache time is now 5 minutes */
530 /* trying to reconnect is expensive, don't do it too often */
531 if (domain->sequence_number == DOM_SEQUENCE_NONE) {
532 cache_time *= 8;
534 #endif
536 time_diff = t - domain->last_seq_check;
538 /* see if we have to refetch the domain sequence number */
539 if (!force && (time_diff < cache_time) &&
540 (domain->sequence_number != DOM_SEQUENCE_NONE) &&
541 NT_STATUS_IS_OK(domain->last_status)) {
542 DEBUG(10, ("refresh_sequence_number: %s time ok\n", domain->name));
543 goto done;
546 /* try to get the sequence number from the tdb cache first */
547 /* this will update the timestamp as well */
549 status = fetch_cache_seqnum( domain, t );
550 if (NT_STATUS_IS_OK(status) &&
551 (domain->sequence_number != DOM_SEQUENCE_NONE) &&
552 NT_STATUS_IS_OK(domain->last_status)) {
553 goto done;
556 /* important! make sure that we know if this is a native
557 mode domain or not. And that we can contact it. */
559 if ( winbindd_can_contact_domain( domain ) ) {
560 status = domain->backend->sequence_number(domain,
561 &domain->sequence_number);
562 } else {
563 /* just use the current time */
564 status = NT_STATUS_OK;
565 domain->sequence_number = time(NULL);
569 /* the above call could have set our domain->backend to NULL when
570 * coming from offline to online mode, make sure to reinitialize the
571 * backend - Guenther */
572 get_cache( domain );
574 if (!NT_STATUS_IS_OK(status)) {
575 DEBUG(10,("refresh_sequence_number: failed with %s\n", nt_errstr(status)));
576 domain->sequence_number = DOM_SEQUENCE_NONE;
579 domain->last_status = status;
580 domain->last_seq_check = time(NULL);
582 /* save the new sequence number in the cache */
583 store_cache_seqnum( domain );
585 done:
586 DEBUG(10, ("refresh_sequence_number: %s seq number is now %d\n",
587 domain->name, domain->sequence_number));
589 return;
593 decide if a cache entry has expired
595 static bool centry_expired(struct winbindd_domain *domain, const char *keystr, struct cache_entry *centry)
597 /* If we've been told to be offline - stay in that state... */
598 if (lp_winbind_offline_logon() && global_winbindd_offline_state) {
599 DEBUG(10,("centry_expired: Key %s for domain %s valid as winbindd is globally offline.\n",
600 keystr, domain->name ));
601 return false;
604 /* when the domain is offline return the cached entry.
605 * This deals with transient offline states... */
607 if (!domain->online) {
608 DEBUG(10,("centry_expired: Key %s for domain %s valid as domain is offline.\n",
609 keystr, domain->name ));
610 return false;
613 /* if the server is OK and our cache entry came from when it was down then
614 the entry is invalid */
615 if ((domain->sequence_number != DOM_SEQUENCE_NONE) &&
616 (centry->sequence_number == DOM_SEQUENCE_NONE)) {
617 DEBUG(10,("centry_expired: Key %s for domain %s invalid sequence.\n",
618 keystr, domain->name ));
619 return true;
622 /* if the server is down or the cache entry is not older than the
623 current sequence number or it did not timeout then it is OK */
624 if (wcache_server_down(domain)
625 || ((centry->sequence_number == domain->sequence_number)
626 && (centry->timeout > time(NULL)))) {
627 DEBUG(10,("centry_expired: Key %s for domain %s is good.\n",
628 keystr, domain->name ));
629 return false;
632 DEBUG(10,("centry_expired: Key %s for domain %s expired\n",
633 keystr, domain->name ));
635 /* it's expired */
636 return true;
639 static struct cache_entry *wcache_fetch_raw(char *kstr)
641 TDB_DATA data;
642 struct cache_entry *centry;
643 TDB_DATA key;
645 key = string_tdb_data(kstr);
646 data = tdb_fetch(wcache->tdb, key);
647 if (!data.dptr) {
648 /* a cache miss */
649 return NULL;
652 centry = SMB_XMALLOC_P(struct cache_entry);
653 centry->data = (unsigned char *)data.dptr;
654 centry->len = data.dsize;
655 centry->ofs = 0;
657 if (centry->len < 16) {
658 /* huh? corrupt cache? */
659 DEBUG(10,("wcache_fetch_raw: Corrupt cache for key %s "
660 "(len < 16)?\n", kstr));
661 centry_free(centry);
662 return NULL;
665 centry->status = centry_ntstatus(centry);
666 centry->sequence_number = centry_uint32(centry);
667 centry->timeout = centry_uint64_t(centry);
669 return centry;
672 static bool is_my_own_sam_domain(struct winbindd_domain *domain)
674 if (strequal(domain->name, get_global_sam_name()) &&
675 sid_check_is_our_sam(&domain->sid)) {
676 return true;
679 return false;
682 static bool is_builtin_domain(struct winbindd_domain *domain)
684 if (strequal(domain->name, "BUILTIN") &&
685 sid_check_is_builtin(&domain->sid)) {
686 return true;
689 return false;
693 fetch an entry from the cache, with a varargs key. auto-fetch the sequence
694 number and return status
696 static struct cache_entry *wcache_fetch(struct winbind_cache *cache,
697 struct winbindd_domain *domain,
698 const char *format, ...) PRINTF_ATTRIBUTE(3,4);
699 static struct cache_entry *wcache_fetch(struct winbind_cache *cache,
700 struct winbindd_domain *domain,
701 const char *format, ...)
703 va_list ap;
704 char *kstr;
705 struct cache_entry *centry;
707 if (!winbindd_use_cache() ||
708 is_my_own_sam_domain(domain) ||
709 is_builtin_domain(domain)) {
710 return NULL;
713 refresh_sequence_number(domain, false);
715 va_start(ap, format);
716 smb_xvasprintf(&kstr, format, ap);
717 va_end(ap);
719 centry = wcache_fetch_raw(kstr);
720 if (centry == NULL) {
721 free(kstr);
722 return NULL;
725 if (centry_expired(domain, kstr, centry)) {
727 DEBUG(10,("wcache_fetch: entry %s expired for domain %s\n",
728 kstr, domain->name ));
730 centry_free(centry);
731 free(kstr);
732 return NULL;
735 DEBUG(10,("wcache_fetch: returning entry %s for domain %s\n",
736 kstr, domain->name ));
738 free(kstr);
739 return centry;
742 static void wcache_delete(const char *format, ...) PRINTF_ATTRIBUTE(1,2);
743 static void wcache_delete(const char *format, ...)
745 va_list ap;
746 char *kstr;
747 TDB_DATA key;
749 va_start(ap, format);
750 smb_xvasprintf(&kstr, format, ap);
751 va_end(ap);
753 key = string_tdb_data(kstr);
755 tdb_delete(wcache->tdb, key);
756 free(kstr);
760 make sure we have at least len bytes available in a centry
762 static void centry_expand(struct cache_entry *centry, uint32 len)
764 if (centry->len - centry->ofs >= len)
765 return;
766 centry->len *= 2;
767 centry->data = SMB_REALLOC_ARRAY(centry->data, unsigned char,
768 centry->len);
769 if (!centry->data) {
770 DEBUG(0,("out of memory: needed %d bytes in centry_expand\n", centry->len));
771 smb_panic_fn("out of memory in centry_expand");
776 push a uint64_t into a centry
778 static void centry_put_uint64_t(struct cache_entry *centry, uint64_t v)
780 centry_expand(centry, 8);
781 SBVAL(centry->data, centry->ofs, v);
782 centry->ofs += 8;
786 push a uint32 into a centry
788 static void centry_put_uint32(struct cache_entry *centry, uint32 v)
790 centry_expand(centry, 4);
791 SIVAL(centry->data, centry->ofs, v);
792 centry->ofs += 4;
796 push a uint16 into a centry
798 static void centry_put_uint16(struct cache_entry *centry, uint16 v)
800 centry_expand(centry, 2);
801 SSVAL(centry->data, centry->ofs, v);
802 centry->ofs += 2;
806 push a uint8 into a centry
808 static void centry_put_uint8(struct cache_entry *centry, uint8 v)
810 centry_expand(centry, 1);
811 SCVAL(centry->data, centry->ofs, v);
812 centry->ofs += 1;
816 push a string into a centry
818 static void centry_put_string(struct cache_entry *centry, const char *s)
820 int len;
822 if (!s) {
823 /* null strings are marked as len 0xFFFF */
824 centry_put_uint8(centry, 0xFF);
825 return;
828 len = strlen(s);
829 /* can't handle more than 254 char strings. Truncating is probably best */
830 if (len > 254) {
831 DEBUG(10,("centry_put_string: truncating len (%d) to: 254\n", len));
832 len = 254;
834 centry_put_uint8(centry, len);
835 centry_expand(centry, len);
836 memcpy(centry->data + centry->ofs, s, len);
837 centry->ofs += len;
841 push a 16 byte hash into a centry - treat as 16 byte string.
843 static void centry_put_hash16(struct cache_entry *centry, const uint8 val[16])
845 centry_put_uint8(centry, 16);
846 centry_expand(centry, 16);
847 memcpy(centry->data + centry->ofs, val, 16);
848 centry->ofs += 16;
851 static void centry_put_sid(struct cache_entry *centry, const struct dom_sid *sid)
853 fstring sid_string;
854 centry_put_string(centry, sid_to_fstring(sid_string, sid));
859 put NTSTATUS into a centry
861 static void centry_put_ntstatus(struct cache_entry *centry, NTSTATUS status)
863 uint32 status_value = NT_STATUS_V(status);
864 centry_put_uint32(centry, status_value);
869 push a NTTIME into a centry
871 static void centry_put_nttime(struct cache_entry *centry, NTTIME nt)
873 centry_expand(centry, 8);
874 SIVAL(centry->data, centry->ofs, nt & 0xFFFFFFFF);
875 centry->ofs += 4;
876 SIVAL(centry->data, centry->ofs, nt >> 32);
877 centry->ofs += 4;
881 push a time_t into a centry - use a 64 bit size.
882 NTTIME here is being used as a convenient 64-bit size.
884 static void centry_put_time(struct cache_entry *centry, time_t t)
886 NTTIME nt = (NTTIME)t;
887 centry_put_nttime(centry, nt);
891 start a centry for output. When finished, call centry_end()
893 static struct cache_entry *centry_start(struct winbindd_domain *domain,
894 NTSTATUS status)
896 struct cache_entry *centry;
898 if (!wcache->tdb)
899 return NULL;
901 centry = SMB_XMALLOC_P(struct cache_entry);
903 centry->len = 8192; /* reasonable default */
904 centry->data = SMB_XMALLOC_ARRAY(uint8, centry->len);
905 centry->ofs = 0;
906 centry->sequence_number = domain->sequence_number;
907 centry->timeout = lp_winbind_cache_time() + time(NULL);
908 centry_put_ntstatus(centry, status);
909 centry_put_uint32(centry, centry->sequence_number);
910 centry_put_uint64_t(centry, centry->timeout);
911 return centry;
915 finish a centry and write it to the tdb
917 static void centry_end(struct cache_entry *centry, const char *format, ...) PRINTF_ATTRIBUTE(2,3);
918 static void centry_end(struct cache_entry *centry, const char *format, ...)
920 va_list ap;
921 char *kstr;
922 TDB_DATA key, data;
924 if (!winbindd_use_cache()) {
925 return;
928 va_start(ap, format);
929 smb_xvasprintf(&kstr, format, ap);
930 va_end(ap);
932 key = string_tdb_data(kstr);
933 data.dptr = centry->data;
934 data.dsize = centry->ofs;
936 tdb_store(wcache->tdb, key, data, TDB_REPLACE);
937 free(kstr);
940 static void wcache_save_name_to_sid(struct winbindd_domain *domain,
941 NTSTATUS status, const char *domain_name,
942 const char *name, const struct dom_sid *sid,
943 enum lsa_SidType type)
945 struct cache_entry *centry;
946 fstring uname;
948 centry = centry_start(domain, status);
949 if (!centry)
950 return;
952 if ((domain_name == NULL) || (domain_name[0] == '\0')) {
953 struct winbindd_domain *mydomain =
954 find_domain_from_sid_noinit(sid);
955 if (mydomain != NULL) {
956 domain_name = mydomain->name;
960 centry_put_uint32(centry, type);
961 centry_put_sid(centry, sid);
962 fstrcpy(uname, name);
963 (void)strupper_m(uname);
964 centry_end(centry, "NS/%s/%s", domain_name, uname);
965 DEBUG(10,("wcache_save_name_to_sid: %s\\%s -> %s (%s)\n", domain_name,
966 uname, sid_string_dbg(sid), nt_errstr(status)));
967 centry_free(centry);
970 static void wcache_save_sid_to_name(struct winbindd_domain *domain, NTSTATUS status,
971 const struct dom_sid *sid, const char *domain_name, const char *name, enum lsa_SidType type)
973 struct cache_entry *centry;
974 fstring sid_string;
976 centry = centry_start(domain, status);
977 if (!centry)
978 return;
980 if ((domain_name == NULL) || (domain_name[0] == '\0')) {
981 struct winbindd_domain *mydomain =
982 find_domain_from_sid_noinit(sid);
983 if (mydomain != NULL) {
984 domain_name = mydomain->name;
988 if (NT_STATUS_IS_OK(status)) {
989 centry_put_uint32(centry, type);
990 centry_put_string(centry, domain_name);
991 centry_put_string(centry, name);
994 centry_end(centry, "SN/%s", sid_to_fstring(sid_string, sid));
995 DEBUG(10,("wcache_save_sid_to_name: %s -> %s\\%s (%s)\n", sid_string,
996 domain_name, name, nt_errstr(status)));
997 centry_free(centry);
1001 static void wcache_save_user(struct winbindd_domain *domain, NTSTATUS status,
1002 struct wbint_userinfo *info)
1004 struct cache_entry *centry;
1005 fstring sid_string;
1007 if (is_null_sid(&info->user_sid)) {
1008 return;
1011 centry = centry_start(domain, status);
1012 if (!centry)
1013 return;
1014 centry_put_string(centry, info->acct_name);
1015 centry_put_string(centry, info->full_name);
1016 centry_put_string(centry, info->homedir);
1017 centry_put_string(centry, info->shell);
1018 centry_put_uint32(centry, info->primary_gid);
1019 centry_put_sid(centry, &info->user_sid);
1020 centry_put_sid(centry, &info->group_sid);
1021 centry_end(centry, "U/%s", sid_to_fstring(sid_string,
1022 &info->user_sid));
1023 DEBUG(10,("wcache_save_user: %s (acct_name %s)\n", sid_string, info->acct_name));
1024 centry_free(centry);
1027 static void wcache_save_lockout_policy(struct winbindd_domain *domain,
1028 NTSTATUS status,
1029 struct samr_DomInfo12 *lockout_policy)
1031 struct cache_entry *centry;
1033 centry = centry_start(domain, status);
1034 if (!centry)
1035 return;
1037 centry_put_nttime(centry, lockout_policy->lockout_duration);
1038 centry_put_nttime(centry, lockout_policy->lockout_window);
1039 centry_put_uint16(centry, lockout_policy->lockout_threshold);
1041 centry_end(centry, "LOC_POL/%s", domain->name);
1043 DEBUG(10,("wcache_save_lockout_policy: %s\n", domain->name));
1045 centry_free(centry);
1050 static void wcache_save_password_policy(struct winbindd_domain *domain,
1051 NTSTATUS status,
1052 struct samr_DomInfo1 *policy)
1054 struct cache_entry *centry;
1056 centry = centry_start(domain, status);
1057 if (!centry)
1058 return;
1060 centry_put_uint16(centry, policy->min_password_length);
1061 centry_put_uint16(centry, policy->password_history_length);
1062 centry_put_uint32(centry, policy->password_properties);
1063 centry_put_nttime(centry, policy->max_password_age);
1064 centry_put_nttime(centry, policy->min_password_age);
1066 centry_end(centry, "PWD_POL/%s", domain->name);
1068 DEBUG(10,("wcache_save_password_policy: %s\n", domain->name));
1070 centry_free(centry);
1073 /***************************************************************************
1074 ***************************************************************************/
1076 static void wcache_save_username_alias(struct winbindd_domain *domain,
1077 NTSTATUS status,
1078 const char *name, const char *alias)
1080 struct cache_entry *centry;
1081 fstring uname;
1083 if ( (centry = centry_start(domain, status)) == NULL )
1084 return;
1086 centry_put_string( centry, alias );
1088 fstrcpy(uname, name);
1089 (void)strupper_m(uname);
1090 centry_end(centry, "NSS/NA/%s", uname);
1092 DEBUG(10,("wcache_save_username_alias: %s -> %s\n", name, alias ));
1094 centry_free(centry);
1097 static void wcache_save_alias_username(struct winbindd_domain *domain,
1098 NTSTATUS status,
1099 const char *alias, const char *name)
1101 struct cache_entry *centry;
1102 fstring uname;
1104 if ( (centry = centry_start(domain, status)) == NULL )
1105 return;
1107 centry_put_string( centry, name );
1109 fstrcpy(uname, alias);
1110 (void)strupper_m(uname);
1111 centry_end(centry, "NSS/AN/%s", uname);
1113 DEBUG(10,("wcache_save_alias_username: %s -> %s\n", alias, name ));
1115 centry_free(centry);
1118 /***************************************************************************
1119 ***************************************************************************/
1121 NTSTATUS resolve_username_to_alias( TALLOC_CTX *mem_ctx,
1122 struct winbindd_domain *domain,
1123 const char *name, char **alias )
1125 struct winbind_cache *cache = get_cache(domain);
1126 struct cache_entry *centry = NULL;
1127 NTSTATUS status;
1128 char *upper_name;
1130 if ( domain->internal )
1131 return NT_STATUS_NOT_SUPPORTED;
1133 if (!cache->tdb)
1134 goto do_query;
1136 upper_name = talloc_strdup(mem_ctx, name);
1137 if (upper_name == NULL) {
1138 return NT_STATUS_NO_MEMORY;
1140 if (!strupper_m(upper_name)) {
1141 talloc_free(upper_name);
1142 return NT_STATUS_INVALID_PARAMETER;
1145 centry = wcache_fetch(cache, domain, "NSS/NA/%s", upper_name);
1147 talloc_free(upper_name);
1149 if (!centry)
1150 goto do_query;
1152 status = centry->status;
1154 if (!NT_STATUS_IS_OK(status)) {
1155 centry_free(centry);
1156 return status;
1159 *alias = centry_string( centry, mem_ctx );
1161 centry_free(centry);
1163 DEBUG(10,("resolve_username_to_alias: [Cached] - mapped %s to %s\n",
1164 name, *alias ? *alias : "(none)"));
1166 return (*alias) ? NT_STATUS_OK : NT_STATUS_OBJECT_NAME_NOT_FOUND;
1168 do_query:
1170 /* If its not in cache and we are offline, then fail */
1172 if ( get_global_winbindd_state_offline() || !domain->online ) {
1173 DEBUG(8,("resolve_username_to_alias: rejecting query "
1174 "in offline mode\n"));
1175 return NT_STATUS_NOT_FOUND;
1178 status = nss_map_to_alias( mem_ctx, domain->name, name, alias );
1180 if ( NT_STATUS_IS_OK( status ) ) {
1181 wcache_save_username_alias(domain, status, name, *alias);
1184 if ( NT_STATUS_EQUAL( status, NT_STATUS_NONE_MAPPED ) ) {
1185 wcache_save_username_alias(domain, status, name, "(NULL)");
1188 DEBUG(5,("resolve_username_to_alias: backend query returned %s\n",
1189 nt_errstr(status)));
1191 if ( NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ) {
1192 set_domain_offline( domain );
1195 return status;
1198 /***************************************************************************
1199 ***************************************************************************/
1201 NTSTATUS resolve_alias_to_username( TALLOC_CTX *mem_ctx,
1202 struct winbindd_domain *domain,
1203 const char *alias, char **name )
1205 struct winbind_cache *cache = get_cache(domain);
1206 struct cache_entry *centry = NULL;
1207 NTSTATUS status;
1208 char *upper_name;
1210 if ( domain->internal )
1211 return NT_STATUS_NOT_SUPPORTED;
1213 if (!cache->tdb)
1214 goto do_query;
1216 upper_name = talloc_strdup(mem_ctx, alias);
1217 if (upper_name == NULL) {
1218 return NT_STATUS_NO_MEMORY;
1220 if (!strupper_m(upper_name)) {
1221 talloc_free(upper_name);
1222 return NT_STATUS_INVALID_PARAMETER;
1225 centry = wcache_fetch(cache, domain, "NSS/AN/%s", upper_name);
1227 talloc_free(upper_name);
1229 if (!centry)
1230 goto do_query;
1232 status = centry->status;
1234 if (!NT_STATUS_IS_OK(status)) {
1235 centry_free(centry);
1236 return status;
1239 *name = centry_string( centry, mem_ctx );
1241 centry_free(centry);
1243 DEBUG(10,("resolve_alias_to_username: [Cached] - mapped %s to %s\n",
1244 alias, *name ? *name : "(none)"));
1246 return (*name) ? NT_STATUS_OK : NT_STATUS_OBJECT_NAME_NOT_FOUND;
1248 do_query:
1250 /* If its not in cache and we are offline, then fail */
1252 if ( get_global_winbindd_state_offline() || !domain->online ) {
1253 DEBUG(8,("resolve_alias_to_username: rejecting query "
1254 "in offline mode\n"));
1255 return NT_STATUS_NOT_FOUND;
1258 /* an alias cannot contain a domain prefix or '@' */
1260 if (strchr(alias, '\\') || strchr(alias, '@')) {
1261 DEBUG(10,("resolve_alias_to_username: skipping fully "
1262 "qualified name %s\n", alias));
1263 return NT_STATUS_OBJECT_NAME_INVALID;
1266 status = nss_map_from_alias( mem_ctx, domain->name, alias, name );
1268 if ( NT_STATUS_IS_OK( status ) ) {
1269 wcache_save_alias_username( domain, status, alias, *name );
1272 if (NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) {
1273 wcache_save_alias_username(domain, status, alias, "(NULL)");
1276 DEBUG(5,("resolve_alias_to_username: backend query returned %s\n",
1277 nt_errstr(status)));
1279 if ( NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ) {
1280 set_domain_offline( domain );
1283 return status;
1286 NTSTATUS wcache_cached_creds_exist(struct winbindd_domain *domain, const struct dom_sid *sid)
1288 struct winbind_cache *cache = get_cache(domain);
1289 TDB_DATA data;
1290 fstring key_str, tmp;
1291 uint32 rid;
1293 if (!cache->tdb) {
1294 return NT_STATUS_INTERNAL_DB_ERROR;
1297 if (is_null_sid(sid)) {
1298 return NT_STATUS_INVALID_SID;
1301 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1302 return NT_STATUS_INVALID_SID;
1305 fstr_sprintf(key_str, "CRED/%s", sid_to_fstring(tmp, sid));
1307 data = tdb_fetch(cache->tdb, string_tdb_data(key_str));
1308 if (!data.dptr) {
1309 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1312 SAFE_FREE(data.dptr);
1313 return NT_STATUS_OK;
1316 /* Lookup creds for a SID - copes with old (unsalted) creds as well
1317 as new salted ones. */
1319 NTSTATUS wcache_get_creds(struct winbindd_domain *domain,
1320 TALLOC_CTX *mem_ctx,
1321 const struct dom_sid *sid,
1322 const uint8 **cached_nt_pass,
1323 const uint8 **cached_salt)
1325 struct winbind_cache *cache = get_cache(domain);
1326 struct cache_entry *centry = NULL;
1327 NTSTATUS status;
1328 uint32 rid;
1329 fstring tmp;
1331 if (!cache->tdb) {
1332 return NT_STATUS_INTERNAL_DB_ERROR;
1335 if (is_null_sid(sid)) {
1336 return NT_STATUS_INVALID_SID;
1339 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1340 return NT_STATUS_INVALID_SID;
1343 /* Try and get a salted cred first. If we can't
1344 fall back to an unsalted cred. */
1346 centry = wcache_fetch(cache, domain, "CRED/%s",
1347 sid_to_fstring(tmp, sid));
1348 if (!centry) {
1349 DEBUG(10,("wcache_get_creds: entry for [CRED/%s] not found\n",
1350 sid_string_dbg(sid)));
1351 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1355 * We don't use the time element at this moment,
1356 * but we have to consume it, so that we don't
1357 * neet to change the disk format of the cache.
1359 (void)centry_time(centry);
1361 /* In the salted case this isn't actually the nt_hash itself,
1362 but the MD5 of the salt + nt_hash. Let the caller
1363 sort this out. It can tell as we only return the cached_salt
1364 if we are returning a salted cred. */
1366 *cached_nt_pass = (const uint8 *)centry_hash16(centry, mem_ctx);
1367 if (*cached_nt_pass == NULL) {
1368 fstring sidstr;
1370 sid_to_fstring(sidstr, sid);
1372 /* Bad (old) cred cache. Delete and pretend we
1373 don't have it. */
1374 DEBUG(0,("wcache_get_creds: bad entry for [CRED/%s] - deleting\n",
1375 sidstr));
1376 wcache_delete("CRED/%s", sidstr);
1377 centry_free(centry);
1378 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1381 /* We only have 17 bytes more data in the salted cred case. */
1382 if (centry->len - centry->ofs == 17) {
1383 *cached_salt = (const uint8 *)centry_hash16(centry, mem_ctx);
1384 } else {
1385 *cached_salt = NULL;
1388 dump_data_pw("cached_nt_pass", *cached_nt_pass, NT_HASH_LEN);
1389 if (*cached_salt) {
1390 dump_data_pw("cached_salt", *cached_salt, NT_HASH_LEN);
1393 status = centry->status;
1395 DEBUG(10,("wcache_get_creds: [Cached] - cached creds for user %s status: %s\n",
1396 sid_string_dbg(sid), nt_errstr(status) ));
1398 centry_free(centry);
1399 return status;
1402 /* Store creds for a SID - only writes out new salted ones. */
1404 NTSTATUS wcache_save_creds(struct winbindd_domain *domain,
1405 const struct dom_sid *sid,
1406 const uint8 nt_pass[NT_HASH_LEN])
1408 struct cache_entry *centry;
1409 fstring sid_string;
1410 uint32 rid;
1411 uint8 cred_salt[NT_HASH_LEN];
1412 uint8 salted_hash[NT_HASH_LEN];
1414 if (is_null_sid(sid)) {
1415 return NT_STATUS_INVALID_SID;
1418 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1419 return NT_STATUS_INVALID_SID;
1422 centry = centry_start(domain, NT_STATUS_OK);
1423 if (!centry) {
1424 return NT_STATUS_INTERNAL_DB_ERROR;
1427 dump_data_pw("nt_pass", nt_pass, NT_HASH_LEN);
1429 centry_put_time(centry, time(NULL));
1431 /* Create a salt and then salt the hash. */
1432 generate_random_buffer(cred_salt, NT_HASH_LEN);
1433 E_md5hash(cred_salt, nt_pass, salted_hash);
1435 centry_put_hash16(centry, salted_hash);
1436 centry_put_hash16(centry, cred_salt);
1437 centry_end(centry, "CRED/%s", sid_to_fstring(sid_string, sid));
1439 DEBUG(10,("wcache_save_creds: %s\n", sid_string));
1441 centry_free(centry);
1443 return NT_STATUS_OK;
1447 /* Query display info. This is the basic user list fn */
1448 static NTSTATUS query_user_list(struct winbindd_domain *domain,
1449 TALLOC_CTX *mem_ctx,
1450 uint32 *num_entries,
1451 struct wbint_userinfo **info)
1453 struct winbind_cache *cache = get_cache(domain);
1454 struct cache_entry *centry = NULL;
1455 NTSTATUS status;
1456 unsigned int i, retry;
1457 bool old_status = domain->online;
1459 if (!cache->tdb)
1460 goto do_query;
1462 centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
1463 if (!centry)
1464 goto do_query;
1466 do_fetch_cache:
1467 *num_entries = centry_uint32(centry);
1469 if (*num_entries == 0)
1470 goto do_cached;
1472 (*info) = talloc_array(mem_ctx, struct wbint_userinfo, *num_entries);
1473 if (! (*info)) {
1474 smb_panic_fn("query_user_list out of memory");
1476 for (i=0; i<(*num_entries); i++) {
1477 (*info)[i].acct_name = centry_string(centry, mem_ctx);
1478 (*info)[i].full_name = centry_string(centry, mem_ctx);
1479 (*info)[i].homedir = centry_string(centry, mem_ctx);
1480 (*info)[i].shell = centry_string(centry, mem_ctx);
1481 centry_sid(centry, &(*info)[i].user_sid);
1482 centry_sid(centry, &(*info)[i].group_sid);
1485 do_cached:
1486 status = centry->status;
1488 DEBUG(10,("query_user_list: [Cached] - cached list for domain %s status: %s\n",
1489 domain->name, nt_errstr(status) ));
1491 centry_free(centry);
1492 return status;
1494 do_query:
1495 *num_entries = 0;
1496 *info = NULL;
1498 /* Return status value returned by seq number check */
1500 if (!NT_STATUS_IS_OK(domain->last_status))
1501 return domain->last_status;
1503 /* Put the query_user_list() in a retry loop. There appears to be
1504 * some bug either with Windows 2000 or Samba's handling of large
1505 * rpc replies. This manifests itself as sudden disconnection
1506 * at a random point in the enumeration of a large (60k) user list.
1507 * The retry loop simply tries the operation again. )-: It's not
1508 * pretty but an acceptable workaround until we work out what the
1509 * real problem is. */
1511 retry = 0;
1512 do {
1514 DEBUG(10,("query_user_list: [Cached] - doing backend query for list for domain %s\n",
1515 domain->name ));
1517 status = domain->backend->query_user_list(domain, mem_ctx, num_entries, info);
1518 if (!NT_STATUS_IS_OK(status)) {
1519 DEBUG(3, ("query_user_list: returned 0x%08x, "
1520 "retrying\n", NT_STATUS_V(status)));
1522 if (NT_STATUS_EQUAL(status, NT_STATUS_UNSUCCESSFUL)) {
1523 DEBUG(3, ("query_user_list: flushing "
1524 "connection cache\n"));
1525 invalidate_cm_connection(domain);
1527 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1528 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1529 if (!domain->internal && old_status) {
1530 set_domain_offline(domain);
1532 /* store partial response. */
1533 if (*num_entries > 0) {
1535 * humm, what about the status used for cache?
1536 * Should it be NT_STATUS_OK?
1538 break;
1541 * domain is offline now, and there is no user entries,
1542 * try to fetch from cache again.
1544 if (cache->tdb && !domain->online && !domain->internal && old_status) {
1545 centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
1546 /* partial response... */
1547 if (!centry) {
1548 goto skip_save;
1549 } else {
1550 goto do_fetch_cache;
1552 } else {
1553 goto skip_save;
1557 } while (NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_UNSUCCESSFUL) &&
1558 (retry++ < 5));
1560 /* and save it */
1561 refresh_sequence_number(domain, false);
1562 if (!NT_STATUS_IS_OK(status)) {
1563 return status;
1565 centry = centry_start(domain, status);
1566 if (!centry)
1567 goto skip_save;
1568 centry_put_uint32(centry, *num_entries);
1569 for (i=0; i<(*num_entries); i++) {
1570 centry_put_string(centry, (*info)[i].acct_name);
1571 centry_put_string(centry, (*info)[i].full_name);
1572 centry_put_string(centry, (*info)[i].homedir);
1573 centry_put_string(centry, (*info)[i].shell);
1574 centry_put_sid(centry, &(*info)[i].user_sid);
1575 centry_put_sid(centry, &(*info)[i].group_sid);
1576 if (domain->backend && domain->backend->consistent) {
1577 /* when the backend is consistent we can pre-prime some mappings */
1578 wcache_save_name_to_sid(domain, NT_STATUS_OK,
1579 domain->name,
1580 (*info)[i].acct_name,
1581 &(*info)[i].user_sid,
1582 SID_NAME_USER);
1583 wcache_save_sid_to_name(domain, NT_STATUS_OK,
1584 &(*info)[i].user_sid,
1585 domain->name,
1586 (*info)[i].acct_name,
1587 SID_NAME_USER);
1588 wcache_save_user(domain, NT_STATUS_OK, &(*info)[i]);
1591 centry_end(centry, "UL/%s", domain->name);
1592 centry_free(centry);
1594 skip_save:
1595 return status;
1598 /* list all domain groups */
1599 static NTSTATUS enum_dom_groups(struct winbindd_domain *domain,
1600 TALLOC_CTX *mem_ctx,
1601 uint32 *num_entries,
1602 struct wb_acct_info **info)
1604 struct winbind_cache *cache = get_cache(domain);
1605 struct cache_entry *centry = NULL;
1606 NTSTATUS status;
1607 unsigned int i;
1608 bool old_status;
1610 old_status = domain->online;
1611 if (!cache->tdb)
1612 goto do_query;
1614 centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
1615 if (!centry)
1616 goto do_query;
1618 do_fetch_cache:
1619 *num_entries = centry_uint32(centry);
1621 if (*num_entries == 0)
1622 goto do_cached;
1624 (*info) = talloc_array(mem_ctx, struct wb_acct_info, *num_entries);
1625 if (! (*info)) {
1626 smb_panic_fn("enum_dom_groups out of memory");
1628 for (i=0; i<(*num_entries); i++) {
1629 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1630 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1631 (*info)[i].rid = centry_uint32(centry);
1634 do_cached:
1635 status = centry->status;
1637 DEBUG(10,("enum_dom_groups: [Cached] - cached list for domain %s status: %s\n",
1638 domain->name, nt_errstr(status) ));
1640 centry_free(centry);
1641 return status;
1643 do_query:
1644 *num_entries = 0;
1645 *info = NULL;
1647 /* Return status value returned by seq number check */
1649 if (!NT_STATUS_IS_OK(domain->last_status))
1650 return domain->last_status;
1652 DEBUG(10,("enum_dom_groups: [Cached] - doing backend query for list for domain %s\n",
1653 domain->name ));
1655 status = domain->backend->enum_dom_groups(domain, mem_ctx, num_entries, info);
1657 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1658 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1659 if (!domain->internal && old_status) {
1660 set_domain_offline(domain);
1662 if (cache->tdb &&
1663 !domain->online &&
1664 !domain->internal &&
1665 old_status) {
1666 centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
1667 if (centry) {
1668 goto do_fetch_cache;
1672 /* and save it */
1673 refresh_sequence_number(domain, false);
1674 if (!NT_STATUS_IS_OK(status)) {
1675 return status;
1677 centry = centry_start(domain, status);
1678 if (!centry)
1679 goto skip_save;
1680 centry_put_uint32(centry, *num_entries);
1681 for (i=0; i<(*num_entries); i++) {
1682 centry_put_string(centry, (*info)[i].acct_name);
1683 centry_put_string(centry, (*info)[i].acct_desc);
1684 centry_put_uint32(centry, (*info)[i].rid);
1686 centry_end(centry, "GL/%s/domain", domain->name);
1687 centry_free(centry);
1689 skip_save:
1690 return status;
1693 /* list all domain groups */
1694 static NTSTATUS enum_local_groups(struct winbindd_domain *domain,
1695 TALLOC_CTX *mem_ctx,
1696 uint32 *num_entries,
1697 struct wb_acct_info **info)
1699 struct winbind_cache *cache = get_cache(domain);
1700 struct cache_entry *centry = NULL;
1701 NTSTATUS status;
1702 unsigned int i;
1703 bool old_status;
1705 old_status = domain->online;
1706 if (!cache->tdb)
1707 goto do_query;
1709 centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
1710 if (!centry)
1711 goto do_query;
1713 do_fetch_cache:
1714 *num_entries = centry_uint32(centry);
1716 if (*num_entries == 0)
1717 goto do_cached;
1719 (*info) = talloc_array(mem_ctx, struct wb_acct_info, *num_entries);
1720 if (! (*info)) {
1721 smb_panic_fn("enum_dom_groups out of memory");
1723 for (i=0; i<(*num_entries); i++) {
1724 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1725 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1726 (*info)[i].rid = centry_uint32(centry);
1729 do_cached:
1731 /* If we are returning cached data and the domain controller
1732 is down then we don't know whether the data is up to date
1733 or not. Return NT_STATUS_MORE_PROCESSING_REQUIRED to
1734 indicate this. */
1736 if (wcache_server_down(domain)) {
1737 DEBUG(10, ("enum_local_groups: returning cached user list and server was down\n"));
1738 status = NT_STATUS_MORE_PROCESSING_REQUIRED;
1739 } else
1740 status = centry->status;
1742 DEBUG(10,("enum_local_groups: [Cached] - cached list for domain %s status: %s\n",
1743 domain->name, nt_errstr(status) ));
1745 centry_free(centry);
1746 return status;
1748 do_query:
1749 *num_entries = 0;
1750 *info = NULL;
1752 /* Return status value returned by seq number check */
1754 if (!NT_STATUS_IS_OK(domain->last_status))
1755 return domain->last_status;
1757 DEBUG(10,("enum_local_groups: [Cached] - doing backend query for list for domain %s\n",
1758 domain->name ));
1760 status = domain->backend->enum_local_groups(domain, mem_ctx, num_entries, info);
1762 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1763 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1764 if (!domain->internal && old_status) {
1765 set_domain_offline(domain);
1767 if (cache->tdb &&
1768 !domain->internal &&
1769 !domain->online &&
1770 old_status) {
1771 centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
1772 if (centry) {
1773 goto do_fetch_cache;
1777 /* and save it */
1778 refresh_sequence_number(domain, false);
1779 if (!NT_STATUS_IS_OK(status)) {
1780 return status;
1782 centry = centry_start(domain, status);
1783 if (!centry)
1784 goto skip_save;
1785 centry_put_uint32(centry, *num_entries);
1786 for (i=0; i<(*num_entries); i++) {
1787 centry_put_string(centry, (*info)[i].acct_name);
1788 centry_put_string(centry, (*info)[i].acct_desc);
1789 centry_put_uint32(centry, (*info)[i].rid);
1791 centry_end(centry, "GL/%s/local", domain->name);
1792 centry_free(centry);
1794 skip_save:
1795 return status;
1798 NTSTATUS wcache_name_to_sid(struct winbindd_domain *domain,
1799 const char *domain_name,
1800 const char *name,
1801 struct dom_sid *sid,
1802 enum lsa_SidType *type)
1804 struct winbind_cache *cache = get_cache(domain);
1805 struct cache_entry *centry;
1806 NTSTATUS status;
1807 char *uname;
1809 if (cache->tdb == NULL) {
1810 return NT_STATUS_NOT_FOUND;
1813 uname = talloc_strdup_upper(talloc_tos(), name);
1814 if (uname == NULL) {
1815 return NT_STATUS_NO_MEMORY;
1818 if ((domain_name == NULL) || (domain_name[0] == '\0')) {
1819 domain_name = domain->name;
1822 centry = wcache_fetch(cache, domain, "NS/%s/%s", domain_name, uname);
1823 TALLOC_FREE(uname);
1824 if (centry == NULL) {
1825 return NT_STATUS_NOT_FOUND;
1828 status = centry->status;
1829 if (NT_STATUS_IS_OK(status)) {
1830 *type = (enum lsa_SidType)centry_uint32(centry);
1831 centry_sid(centry, sid);
1834 DEBUG(10,("name_to_sid: [Cached] - cached name for domain %s status: "
1835 "%s\n", domain->name, nt_errstr(status) ));
1837 centry_free(centry);
1838 return status;
1841 /* convert a single name to a sid in a domain */
1842 static NTSTATUS name_to_sid(struct winbindd_domain *domain,
1843 TALLOC_CTX *mem_ctx,
1844 const char *domain_name,
1845 const char *name,
1846 uint32_t flags,
1847 struct dom_sid *sid,
1848 enum lsa_SidType *type)
1850 NTSTATUS status;
1851 bool old_status;
1853 old_status = domain->online;
1855 status = wcache_name_to_sid(domain, domain_name, name, sid, type);
1856 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
1857 return status;
1860 ZERO_STRUCTP(sid);
1862 /* If the seq number check indicated that there is a problem
1863 * with this DC, then return that status... except for
1864 * access_denied. This is special because the dc may be in
1865 * "restrict anonymous = 1" mode, in which case it will deny
1866 * most unauthenticated operations, but *will* allow the LSA
1867 * name-to-sid that we try as a fallback. */
1869 if (!(NT_STATUS_IS_OK(domain->last_status)
1870 || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1871 return domain->last_status;
1873 DEBUG(10,("name_to_sid: [Cached] - doing backend query for name for domain %s\n",
1874 domain->name ));
1876 status = domain->backend->name_to_sid(domain, mem_ctx, domain_name,
1877 name, flags, sid, type);
1879 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1880 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1881 if (!domain->internal && old_status) {
1882 set_domain_offline(domain);
1884 if (!domain->internal &&
1885 !domain->online &&
1886 old_status) {
1887 NTSTATUS cache_status;
1888 cache_status = wcache_name_to_sid(domain, domain_name, name, sid, type);
1889 return cache_status;
1892 /* and save it */
1893 refresh_sequence_number(domain, false);
1895 if (domain->online &&
1896 (NT_STATUS_IS_OK(status) || NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED))) {
1897 wcache_save_name_to_sid(domain, status, domain_name, name, sid, *type);
1899 /* Only save the reverse mapping if this was not a UPN */
1900 if (!strchr(name, '@')) {
1901 if (!strupper_m(discard_const_p(char, domain_name))) {
1902 return NT_STATUS_INVALID_PARAMETER;
1904 (void)strlower_m(discard_const_p(char, name));
1905 wcache_save_sid_to_name(domain, status, sid, domain_name, name, *type);
1909 return status;
1912 static NTSTATUS wcache_sid_to_name(struct winbindd_domain *domain,
1913 const struct dom_sid *sid,
1914 TALLOC_CTX *mem_ctx,
1915 char **domain_name,
1916 char **name,
1917 enum lsa_SidType *type)
1919 struct winbind_cache *cache = get_cache(domain);
1920 struct cache_entry *centry;
1921 char *sid_string;
1922 NTSTATUS status;
1924 if (cache->tdb == NULL) {
1925 return NT_STATUS_NOT_FOUND;
1928 sid_string = sid_string_tos(sid);
1929 if (sid_string == NULL) {
1930 return NT_STATUS_NO_MEMORY;
1933 centry = wcache_fetch(cache, domain, "SN/%s", sid_string);
1934 TALLOC_FREE(sid_string);
1935 if (centry == NULL) {
1936 return NT_STATUS_NOT_FOUND;
1939 if (NT_STATUS_IS_OK(centry->status)) {
1940 *type = (enum lsa_SidType)centry_uint32(centry);
1941 *domain_name = centry_string(centry, mem_ctx);
1942 *name = centry_string(centry, mem_ctx);
1945 status = centry->status;
1946 centry_free(centry);
1948 DEBUG(10,("sid_to_name: [Cached] - cached name for domain %s status: "
1949 "%s\n", domain->name, nt_errstr(status) ));
1951 return status;
1954 /* convert a sid to a user or group name. The sid is guaranteed to be in the domain
1955 given */
1956 static NTSTATUS sid_to_name(struct winbindd_domain *domain,
1957 TALLOC_CTX *mem_ctx,
1958 const struct dom_sid *sid,
1959 char **domain_name,
1960 char **name,
1961 enum lsa_SidType *type)
1963 NTSTATUS status;
1964 bool old_status;
1966 old_status = domain->online;
1967 status = wcache_sid_to_name(domain, sid, mem_ctx, domain_name, name,
1968 type);
1969 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
1970 return status;
1973 *name = NULL;
1974 *domain_name = NULL;
1976 /* If the seq number check indicated that there is a problem
1977 * with this DC, then return that status... except for
1978 * access_denied. This is special because the dc may be in
1979 * "restrict anonymous = 1" mode, in which case it will deny
1980 * most unauthenticated operations, but *will* allow the LSA
1981 * sid-to-name that we try as a fallback. */
1983 if (!(NT_STATUS_IS_OK(domain->last_status)
1984 || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1985 return domain->last_status;
1987 DEBUG(10,("sid_to_name: [Cached] - doing backend query for name for domain %s\n",
1988 domain->name ));
1990 status = domain->backend->sid_to_name(domain, mem_ctx, sid, domain_name, name, type);
1992 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1993 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1994 if (!domain->internal && old_status) {
1995 set_domain_offline(domain);
1997 if (!domain->internal &&
1998 !domain->online &&
1999 old_status) {
2000 NTSTATUS cache_status;
2001 cache_status = wcache_sid_to_name(domain, sid, mem_ctx,
2002 domain_name, name, type);
2003 return cache_status;
2006 /* and save it */
2007 refresh_sequence_number(domain, false);
2008 if (!NT_STATUS_IS_OK(status)) {
2009 return status;
2011 wcache_save_sid_to_name(domain, status, sid, *domain_name, *name, *type);
2013 /* We can't save the name to sid mapping here, as with sid history a
2014 * later name2sid would give the wrong sid. */
2016 return status;
2019 static NTSTATUS rids_to_names(struct winbindd_domain *domain,
2020 TALLOC_CTX *mem_ctx,
2021 const struct dom_sid *domain_sid,
2022 uint32 *rids,
2023 size_t num_rids,
2024 char **domain_name,
2025 char ***names,
2026 enum lsa_SidType **types)
2028 struct winbind_cache *cache = get_cache(domain);
2029 size_t i;
2030 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
2031 bool have_mapped;
2032 bool have_unmapped;
2033 bool old_status;
2035 old_status = domain->online;
2036 *domain_name = NULL;
2037 *names = NULL;
2038 *types = NULL;
2040 if (!cache->tdb) {
2041 goto do_query;
2044 if (num_rids == 0) {
2045 return NT_STATUS_OK;
2048 *names = talloc_array(mem_ctx, char *, num_rids);
2049 *types = talloc_array(mem_ctx, enum lsa_SidType, num_rids);
2051 if ((*names == NULL) || (*types == NULL)) {
2052 result = NT_STATUS_NO_MEMORY;
2053 goto error;
2056 have_mapped = have_unmapped = false;
2058 for (i=0; i<num_rids; i++) {
2059 struct dom_sid sid;
2060 struct cache_entry *centry;
2061 fstring tmp;
2063 if (!sid_compose(&sid, domain_sid, rids[i])) {
2064 result = NT_STATUS_INTERNAL_ERROR;
2065 goto error;
2068 centry = wcache_fetch(cache, domain, "SN/%s",
2069 sid_to_fstring(tmp, &sid));
2070 if (!centry) {
2071 goto do_query;
2074 (*types)[i] = SID_NAME_UNKNOWN;
2075 (*names)[i] = talloc_strdup(*names, "");
2077 if (NT_STATUS_IS_OK(centry->status)) {
2078 char *dom;
2079 have_mapped = true;
2080 (*types)[i] = (enum lsa_SidType)centry_uint32(centry);
2082 dom = centry_string(centry, mem_ctx);
2083 if (*domain_name == NULL) {
2084 *domain_name = dom;
2085 } else {
2086 talloc_free(dom);
2089 (*names)[i] = centry_string(centry, *names);
2091 } else if (NT_STATUS_EQUAL(centry->status, NT_STATUS_NONE_MAPPED)
2092 || NT_STATUS_EQUAL(centry->status, STATUS_SOME_UNMAPPED)) {
2093 have_unmapped = true;
2095 } else {
2096 /* something's definitely wrong */
2097 result = centry->status;
2098 centry_free(centry);
2099 goto error;
2102 centry_free(centry);
2105 if (!have_mapped) {
2106 return NT_STATUS_NONE_MAPPED;
2108 if (!have_unmapped) {
2109 return NT_STATUS_OK;
2111 return STATUS_SOME_UNMAPPED;
2113 do_query:
2115 TALLOC_FREE(*names);
2116 TALLOC_FREE(*types);
2118 result = domain->backend->rids_to_names(domain, mem_ctx, domain_sid,
2119 rids, num_rids, domain_name,
2120 names, types);
2122 if (NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
2123 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2124 if (!domain->internal && old_status) {
2125 set_domain_offline(domain);
2127 if (cache->tdb &&
2128 !domain->internal &&
2129 !domain->online &&
2130 old_status) {
2131 have_mapped = have_unmapped = false;
2133 *names = talloc_array(mem_ctx, char *, num_rids);
2134 if (*names == NULL) {
2135 result = NT_STATUS_NO_MEMORY;
2136 goto error;
2139 *types = talloc_array(mem_ctx, enum lsa_SidType,
2140 num_rids);
2141 if (*types == NULL) {
2142 result = NT_STATUS_NO_MEMORY;
2143 goto error;
2146 for (i=0; i<num_rids; i++) {
2147 struct dom_sid sid;
2148 struct cache_entry *centry;
2149 fstring tmp;
2151 if (!sid_compose(&sid, domain_sid, rids[i])) {
2152 result = NT_STATUS_INTERNAL_ERROR;
2153 goto error;
2156 centry = wcache_fetch(cache, domain, "SN/%s",
2157 sid_to_fstring(tmp, &sid));
2158 if (!centry) {
2159 (*types)[i] = SID_NAME_UNKNOWN;
2160 (*names)[i] = talloc_strdup(*names, "");
2161 continue;
2164 (*types)[i] = SID_NAME_UNKNOWN;
2165 (*names)[i] = talloc_strdup(*names, "");
2167 if (NT_STATUS_IS_OK(centry->status)) {
2168 char *dom;
2169 have_mapped = true;
2170 (*types)[i] = (enum lsa_SidType)centry_uint32(centry);
2172 dom = centry_string(centry, mem_ctx);
2173 if (*domain_name == NULL) {
2174 *domain_name = dom;
2175 } else {
2176 talloc_free(dom);
2179 (*names)[i] = centry_string(centry, *names);
2181 } else if (NT_STATUS_EQUAL(centry->status, NT_STATUS_NONE_MAPPED)) {
2182 have_unmapped = true;
2184 } else {
2185 /* something's definitely wrong */
2186 result = centry->status;
2187 centry_free(centry);
2188 goto error;
2191 centry_free(centry);
2194 if (!have_mapped) {
2195 return NT_STATUS_NONE_MAPPED;
2197 if (!have_unmapped) {
2198 return NT_STATUS_OK;
2200 return STATUS_SOME_UNMAPPED;
2204 None of the queried rids has been found so save all negative entries
2206 if (NT_STATUS_EQUAL(result, NT_STATUS_NONE_MAPPED)) {
2207 for (i = 0; i < num_rids; i++) {
2208 struct dom_sid sid;
2209 const char *name = "";
2210 const enum lsa_SidType type = SID_NAME_UNKNOWN;
2211 NTSTATUS status = NT_STATUS_NONE_MAPPED;
2213 if (!sid_compose(&sid, domain_sid, rids[i])) {
2214 return NT_STATUS_INTERNAL_ERROR;
2217 wcache_save_sid_to_name(domain, status, &sid, *domain_name,
2218 name, type);
2221 return result;
2225 Some or all of the queried rids have been found.
2227 if (!NT_STATUS_IS_OK(result) &&
2228 !NT_STATUS_EQUAL(result, STATUS_SOME_UNMAPPED)) {
2229 return result;
2232 refresh_sequence_number(domain, false);
2234 for (i=0; i<num_rids; i++) {
2235 struct dom_sid sid;
2236 NTSTATUS status;
2238 if (!sid_compose(&sid, domain_sid, rids[i])) {
2239 result = NT_STATUS_INTERNAL_ERROR;
2240 goto error;
2243 status = (*types)[i] == SID_NAME_UNKNOWN ?
2244 NT_STATUS_NONE_MAPPED : NT_STATUS_OK;
2246 wcache_save_sid_to_name(domain, status, &sid, *domain_name,
2247 (*names)[i], (*types)[i]);
2250 return result;
2252 error:
2253 TALLOC_FREE(*names);
2254 TALLOC_FREE(*types);
2255 return result;
2258 NTSTATUS wcache_query_user(struct winbindd_domain *domain,
2259 TALLOC_CTX *mem_ctx,
2260 const struct dom_sid *user_sid,
2261 struct wbint_userinfo *info)
2263 struct winbind_cache *cache = get_cache(domain);
2264 struct cache_entry *centry = NULL;
2265 NTSTATUS status;
2266 char *sid_string;
2268 if (cache->tdb == NULL) {
2269 return NT_STATUS_NOT_FOUND;
2272 sid_string = sid_string_tos(user_sid);
2273 if (sid_string == NULL) {
2274 return NT_STATUS_NO_MEMORY;
2277 centry = wcache_fetch(cache, domain, "U/%s", sid_string);
2278 TALLOC_FREE(sid_string);
2279 if (centry == NULL) {
2280 return NT_STATUS_NOT_FOUND;
2284 * If we have an access denied cache entry and a cached info3
2285 * in the samlogon cache then do a query. This will force the
2286 * rpc back end to return the info3 data.
2289 if (NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED) &&
2290 netsamlogon_cache_have(user_sid)) {
2291 DEBUG(10, ("query_user: cached access denied and have cached "
2292 "info3\n"));
2293 domain->last_status = NT_STATUS_OK;
2294 centry_free(centry);
2295 return NT_STATUS_NOT_FOUND;
2298 /* if status is not ok then this is a negative hit
2299 and the rest of the data doesn't matter */
2300 status = centry->status;
2301 if (NT_STATUS_IS_OK(status)) {
2302 info->acct_name = centry_string(centry, mem_ctx);
2303 info->full_name = centry_string(centry, mem_ctx);
2304 info->homedir = centry_string(centry, mem_ctx);
2305 info->shell = centry_string(centry, mem_ctx);
2306 info->primary_gid = centry_uint32(centry);
2307 centry_sid(centry, &info->user_sid);
2308 centry_sid(centry, &info->group_sid);
2311 DEBUG(10,("query_user: [Cached] - cached info for domain %s status: "
2312 "%s\n", domain->name, nt_errstr(status) ));
2314 centry_free(centry);
2315 return status;
2320 * @brief Query a fullname from the username cache (for further gecos processing)
2322 * @param domain A pointer to the winbindd_domain struct.
2323 * @param mem_ctx The talloc context.
2324 * @param user_sid The user sid.
2325 * @param full_name A pointer to the full_name string.
2327 * @return NTSTATUS code
2329 NTSTATUS wcache_query_user_fullname(struct winbindd_domain *domain,
2330 TALLOC_CTX *mem_ctx,
2331 const struct dom_sid *user_sid,
2332 const char **full_name)
2334 NTSTATUS status;
2335 struct wbint_userinfo info;
2337 status = wcache_query_user(domain, mem_ctx, user_sid, &info);
2338 if (!NT_STATUS_IS_OK(status)) {
2339 return status;
2342 if (info.full_name != NULL) {
2343 *full_name = talloc_strdup(mem_ctx, info.full_name);
2344 if (*full_name == NULL) {
2345 return NT_STATUS_NO_MEMORY;
2349 return NT_STATUS_OK;
2352 /* Lookup user information from a rid */
2353 static NTSTATUS query_user(struct winbindd_domain *domain,
2354 TALLOC_CTX *mem_ctx,
2355 const struct dom_sid *user_sid,
2356 struct wbint_userinfo *info)
2358 NTSTATUS status;
2359 bool old_status;
2361 old_status = domain->online;
2362 status = wcache_query_user(domain, mem_ctx, user_sid, info);
2363 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2364 return status;
2367 ZERO_STRUCTP(info);
2369 /* Return status value returned by seq number check */
2371 if (!NT_STATUS_IS_OK(domain->last_status))
2372 return domain->last_status;
2374 DEBUG(10,("query_user: [Cached] - doing backend query for info for domain %s\n",
2375 domain->name ));
2377 status = domain->backend->query_user(domain, mem_ctx, user_sid, info);
2379 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2380 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2381 if (!domain->internal && old_status) {
2382 set_domain_offline(domain);
2384 if (!domain->internal &&
2385 !domain->online &&
2386 old_status) {
2387 NTSTATUS cache_status;
2388 cache_status = wcache_query_user(domain, mem_ctx, user_sid, info);
2389 return cache_status;
2392 /* and save it */
2393 refresh_sequence_number(domain, false);
2394 if (!NT_STATUS_IS_OK(status)) {
2395 return status;
2397 wcache_save_user(domain, status, info);
2399 return status;
2402 NTSTATUS wcache_lookup_usergroups(struct winbindd_domain *domain,
2403 TALLOC_CTX *mem_ctx,
2404 const struct dom_sid *user_sid,
2405 uint32_t *pnum_sids,
2406 struct dom_sid **psids)
2408 struct winbind_cache *cache = get_cache(domain);
2409 struct cache_entry *centry = NULL;
2410 NTSTATUS status;
2411 uint32_t i, num_sids;
2412 struct dom_sid *sids;
2413 fstring sid_string;
2415 if (cache->tdb == NULL) {
2416 return NT_STATUS_NOT_FOUND;
2419 centry = wcache_fetch(cache, domain, "UG/%s",
2420 sid_to_fstring(sid_string, user_sid));
2421 if (centry == NULL) {
2422 return NT_STATUS_NOT_FOUND;
2425 /* If we have an access denied cache entry and a cached info3 in the
2426 samlogon cache then do a query. This will force the rpc back end
2427 to return the info3 data. */
2429 if (NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)
2430 && netsamlogon_cache_have(user_sid)) {
2431 DEBUG(10, ("lookup_usergroups: cached access denied and have "
2432 "cached info3\n"));
2433 domain->last_status = NT_STATUS_OK;
2434 centry_free(centry);
2435 return NT_STATUS_NOT_FOUND;
2438 num_sids = centry_uint32(centry);
2439 sids = talloc_array(mem_ctx, struct dom_sid, num_sids);
2440 if (sids == NULL) {
2441 centry_free(centry);
2442 return NT_STATUS_NO_MEMORY;
2445 for (i=0; i<num_sids; i++) {
2446 centry_sid(centry, &sids[i]);
2449 status = centry->status;
2451 DEBUG(10,("lookup_usergroups: [Cached] - cached info for domain %s "
2452 "status: %s\n", domain->name, nt_errstr(status)));
2454 centry_free(centry);
2456 *pnum_sids = num_sids;
2457 *psids = sids;
2458 return status;
2461 /* Lookup groups a user is a member of. */
2462 static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
2463 TALLOC_CTX *mem_ctx,
2464 const struct dom_sid *user_sid,
2465 uint32 *num_groups, struct dom_sid **user_gids)
2467 struct cache_entry *centry = NULL;
2468 NTSTATUS status;
2469 unsigned int i;
2470 fstring sid_string;
2471 bool old_status;
2473 old_status = domain->online;
2474 status = wcache_lookup_usergroups(domain, mem_ctx, user_sid,
2475 num_groups, user_gids);
2476 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2477 return status;
2480 (*num_groups) = 0;
2481 (*user_gids) = NULL;
2483 /* Return status value returned by seq number check */
2485 if (!NT_STATUS_IS_OK(domain->last_status))
2486 return domain->last_status;
2488 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info for domain %s\n",
2489 domain->name ));
2491 status = domain->backend->lookup_usergroups(domain, mem_ctx, user_sid, num_groups, user_gids);
2493 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2494 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2495 if (!domain->internal && old_status) {
2496 set_domain_offline(domain);
2498 if (!domain->internal &&
2499 !domain->online &&
2500 old_status) {
2501 NTSTATUS cache_status;
2502 cache_status = wcache_lookup_usergroups(domain, mem_ctx, user_sid,
2503 num_groups, user_gids);
2504 return cache_status;
2507 if ( NT_STATUS_EQUAL(status, NT_STATUS_SYNCHRONIZATION_REQUIRED) )
2508 goto skip_save;
2510 /* and save it */
2511 refresh_sequence_number(domain, false);
2512 if (!NT_STATUS_IS_OK(status)) {
2513 return status;
2515 centry = centry_start(domain, status);
2516 if (!centry)
2517 goto skip_save;
2519 centry_put_uint32(centry, *num_groups);
2520 for (i=0; i<(*num_groups); i++) {
2521 centry_put_sid(centry, &(*user_gids)[i]);
2524 centry_end(centry, "UG/%s", sid_to_fstring(sid_string, user_sid));
2525 centry_free(centry);
2527 skip_save:
2528 return status;
2531 static char *wcache_make_sidlist(TALLOC_CTX *mem_ctx, uint32_t num_sids,
2532 const struct dom_sid *sids)
2534 uint32_t i;
2535 char *sidlist;
2537 sidlist = talloc_strdup(mem_ctx, "");
2538 if (sidlist == NULL) {
2539 return NULL;
2541 for (i=0; i<num_sids; i++) {
2542 fstring tmp;
2543 sidlist = talloc_asprintf_append_buffer(
2544 sidlist, "/%s", sid_to_fstring(tmp, &sids[i]));
2545 if (sidlist == NULL) {
2546 return NULL;
2549 return sidlist;
2552 NTSTATUS wcache_lookup_useraliases(struct winbindd_domain *domain,
2553 TALLOC_CTX *mem_ctx, uint32_t num_sids,
2554 const struct dom_sid *sids,
2555 uint32_t *pnum_aliases, uint32_t **paliases)
2557 struct winbind_cache *cache = get_cache(domain);
2558 struct cache_entry *centry = NULL;
2559 uint32_t num_aliases;
2560 uint32_t *aliases;
2561 NTSTATUS status;
2562 char *sidlist;
2563 int i;
2565 if (cache->tdb == NULL) {
2566 return NT_STATUS_NOT_FOUND;
2569 if (num_sids == 0) {
2570 *pnum_aliases = 0;
2571 *paliases = NULL;
2572 return NT_STATUS_OK;
2575 /* We need to cache indexed by the whole list of SIDs, the aliases
2576 * resulting might come from any of the SIDs. */
2578 sidlist = wcache_make_sidlist(talloc_tos(), num_sids, sids);
2579 if (sidlist == NULL) {
2580 return NT_STATUS_NO_MEMORY;
2583 centry = wcache_fetch(cache, domain, "UA%s", sidlist);
2584 TALLOC_FREE(sidlist);
2585 if (centry == NULL) {
2586 return NT_STATUS_NOT_FOUND;
2589 num_aliases = centry_uint32(centry);
2590 aliases = talloc_array(mem_ctx, uint32_t, num_aliases);
2591 if (aliases == NULL) {
2592 centry_free(centry);
2593 return NT_STATUS_NO_MEMORY;
2596 for (i=0; i<num_aliases; i++) {
2597 aliases[i] = centry_uint32(centry);
2600 status = centry->status;
2602 DEBUG(10,("lookup_useraliases: [Cached] - cached info for domain: %s "
2603 "status %s\n", domain->name, nt_errstr(status)));
2605 centry_free(centry);
2607 *pnum_aliases = num_aliases;
2608 *paliases = aliases;
2610 return status;
2613 static NTSTATUS lookup_useraliases(struct winbindd_domain *domain,
2614 TALLOC_CTX *mem_ctx,
2615 uint32 num_sids, const struct dom_sid *sids,
2616 uint32 *num_aliases, uint32 **alias_rids)
2618 struct cache_entry *centry = NULL;
2619 NTSTATUS status;
2620 char *sidlist;
2621 int i;
2622 bool old_status;
2624 old_status = domain->online;
2625 status = wcache_lookup_useraliases(domain, mem_ctx, num_sids, sids,
2626 num_aliases, alias_rids);
2627 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2628 return status;
2631 (*num_aliases) = 0;
2632 (*alias_rids) = NULL;
2634 if (!NT_STATUS_IS_OK(domain->last_status))
2635 return domain->last_status;
2637 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info "
2638 "for domain %s\n", domain->name ));
2640 sidlist = wcache_make_sidlist(talloc_tos(), num_sids, sids);
2641 if (sidlist == NULL) {
2642 return NT_STATUS_NO_MEMORY;
2645 status = domain->backend->lookup_useraliases(domain, mem_ctx,
2646 num_sids, sids,
2647 num_aliases, alias_rids);
2649 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2650 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2651 if (!domain->internal && old_status) {
2652 set_domain_offline(domain);
2654 if (!domain->internal &&
2655 !domain->online &&
2656 old_status) {
2657 NTSTATUS cache_status;
2658 cache_status = wcache_lookup_useraliases(domain, mem_ctx, num_sids,
2659 sids, num_aliases, alias_rids);
2660 return cache_status;
2663 /* and save it */
2664 refresh_sequence_number(domain, false);
2665 if (!NT_STATUS_IS_OK(status)) {
2666 return status;
2668 centry = centry_start(domain, status);
2669 if (!centry)
2670 goto skip_save;
2671 centry_put_uint32(centry, *num_aliases);
2672 for (i=0; i<(*num_aliases); i++)
2673 centry_put_uint32(centry, (*alias_rids)[i]);
2674 centry_end(centry, "UA%s", sidlist);
2675 centry_free(centry);
2677 skip_save:
2678 return status;
2681 NTSTATUS wcache_lookup_groupmem(struct winbindd_domain *domain,
2682 TALLOC_CTX *mem_ctx,
2683 const struct dom_sid *group_sid,
2684 uint32_t *num_names,
2685 struct dom_sid **sid_mem, char ***names,
2686 uint32_t **name_types)
2688 struct winbind_cache *cache = get_cache(domain);
2689 struct cache_entry *centry = NULL;
2690 NTSTATUS status;
2691 unsigned int i;
2692 char *sid_string;
2694 if (cache->tdb == NULL) {
2695 return NT_STATUS_NOT_FOUND;
2698 sid_string = sid_string_tos(group_sid);
2699 if (sid_string == NULL) {
2700 return NT_STATUS_NO_MEMORY;
2703 centry = wcache_fetch(cache, domain, "GM/%s", sid_string);
2704 TALLOC_FREE(sid_string);
2705 if (centry == NULL) {
2706 return NT_STATUS_NOT_FOUND;
2709 *sid_mem = NULL;
2710 *names = NULL;
2711 *name_types = NULL;
2713 *num_names = centry_uint32(centry);
2714 if (*num_names == 0) {
2715 centry_free(centry);
2716 return NT_STATUS_OK;
2719 *sid_mem = talloc_array(mem_ctx, struct dom_sid, *num_names);
2720 *names = talloc_array(mem_ctx, char *, *num_names);
2721 *name_types = talloc_array(mem_ctx, uint32, *num_names);
2723 if ((*sid_mem == NULL) || (*names == NULL) || (*name_types == NULL)) {
2724 TALLOC_FREE(*sid_mem);
2725 TALLOC_FREE(*names);
2726 TALLOC_FREE(*name_types);
2727 centry_free(centry);
2728 return NT_STATUS_NO_MEMORY;
2731 for (i=0; i<(*num_names); i++) {
2732 centry_sid(centry, &(*sid_mem)[i]);
2733 (*names)[i] = centry_string(centry, mem_ctx);
2734 (*name_types)[i] = centry_uint32(centry);
2737 status = centry->status;
2739 DEBUG(10,("lookup_groupmem: [Cached] - cached info for domain %s "
2740 "status: %s\n", domain->name, nt_errstr(status)));
2742 centry_free(centry);
2743 return status;
2746 static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
2747 TALLOC_CTX *mem_ctx,
2748 const struct dom_sid *group_sid,
2749 enum lsa_SidType type,
2750 uint32 *num_names,
2751 struct dom_sid **sid_mem, char ***names,
2752 uint32 **name_types)
2754 struct cache_entry *centry = NULL;
2755 NTSTATUS status;
2756 unsigned int i;
2757 fstring sid_string;
2758 bool old_status;
2760 old_status = domain->online;
2761 status = wcache_lookup_groupmem(domain, mem_ctx, group_sid, num_names,
2762 sid_mem, names, name_types);
2763 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2764 return status;
2767 (*num_names) = 0;
2768 (*sid_mem) = NULL;
2769 (*names) = NULL;
2770 (*name_types) = NULL;
2772 /* Return status value returned by seq number check */
2774 if (!NT_STATUS_IS_OK(domain->last_status))
2775 return domain->last_status;
2777 DEBUG(10,("lookup_groupmem: [Cached] - doing backend query for info for domain %s\n",
2778 domain->name ));
2780 status = domain->backend->lookup_groupmem(domain, mem_ctx, group_sid,
2781 type, num_names,
2782 sid_mem, names, name_types);
2784 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2785 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2786 if (!domain->internal && old_status) {
2787 set_domain_offline(domain);
2789 if (!domain->internal &&
2790 !domain->online &&
2791 old_status) {
2792 NTSTATUS cache_status;
2793 cache_status = wcache_lookup_groupmem(domain, mem_ctx, group_sid,
2794 num_names, sid_mem, names,
2795 name_types);
2796 return cache_status;
2799 /* and save it */
2800 refresh_sequence_number(domain, false);
2801 if (!NT_STATUS_IS_OK(status)) {
2802 return status;
2804 centry = centry_start(domain, status);
2805 if (!centry)
2806 goto skip_save;
2807 centry_put_uint32(centry, *num_names);
2808 for (i=0; i<(*num_names); i++) {
2809 centry_put_sid(centry, &(*sid_mem)[i]);
2810 centry_put_string(centry, (*names)[i]);
2811 centry_put_uint32(centry, (*name_types)[i]);
2813 centry_end(centry, "GM/%s", sid_to_fstring(sid_string, group_sid));
2814 centry_free(centry);
2816 skip_save:
2817 return status;
2820 /* find the sequence number for a domain */
2821 static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
2823 refresh_sequence_number(domain, false);
2825 *seq = domain->sequence_number;
2827 return NT_STATUS_OK;
2830 /* enumerate trusted domains
2831 * (we need to have the list of trustdoms in the cache when we go offline) -
2832 * Guenther */
2833 static NTSTATUS trusted_domains(struct winbindd_domain *domain,
2834 TALLOC_CTX *mem_ctx,
2835 struct netr_DomainTrustList *trusts)
2837 NTSTATUS status;
2838 struct winbind_cache *cache;
2839 struct winbindd_tdc_domain *dom_list = NULL;
2840 size_t num_domains = 0;
2841 bool retval = false;
2842 int i;
2843 bool old_status;
2845 old_status = domain->online;
2846 trusts->count = 0;
2847 trusts->array = NULL;
2849 cache = get_cache(domain);
2850 if (!cache || !cache->tdb) {
2851 goto do_query;
2854 if (domain->online) {
2855 goto do_query;
2858 retval = wcache_tdc_fetch_list(&dom_list, &num_domains);
2859 if (!retval || !num_domains || !dom_list) {
2860 TALLOC_FREE(dom_list);
2861 goto do_query;
2864 do_fetch_cache:
2865 trusts->array = talloc_zero_array(mem_ctx, struct netr_DomainTrust, num_domains);
2866 if (!trusts->array) {
2867 TALLOC_FREE(dom_list);
2868 return NT_STATUS_NO_MEMORY;
2871 for (i = 0; i < num_domains; i++) {
2872 struct netr_DomainTrust *trust;
2873 struct dom_sid *sid;
2874 struct winbindd_domain *dom;
2876 dom = find_domain_from_name_noinit(dom_list[i].domain_name);
2877 if (dom && dom->internal) {
2878 continue;
2881 trust = &trusts->array[trusts->count];
2882 trust->netbios_name = talloc_strdup(trusts->array, dom_list[i].domain_name);
2883 trust->dns_name = talloc_strdup(trusts->array, dom_list[i].dns_name);
2884 sid = talloc(trusts->array, struct dom_sid);
2885 if (!trust->netbios_name || !trust->dns_name ||
2886 !sid) {
2887 TALLOC_FREE(dom_list);
2888 TALLOC_FREE(trusts->array);
2889 return NT_STATUS_NO_MEMORY;
2892 trust->trust_flags = dom_list[i].trust_flags;
2893 trust->trust_attributes = dom_list[i].trust_attribs;
2894 trust->trust_type = dom_list[i].trust_type;
2895 sid_copy(sid, &dom_list[i].sid);
2896 trust->sid = sid;
2897 trusts->count++;
2900 TALLOC_FREE(dom_list);
2901 return NT_STATUS_OK;
2903 do_query:
2904 /* Return status value returned by seq number check */
2906 if (!NT_STATUS_IS_OK(domain->last_status))
2907 return domain->last_status;
2909 DEBUG(10,("trusted_domains: [Cached] - doing backend query for info for domain %s\n",
2910 domain->name ));
2912 status = domain->backend->trusted_domains(domain, mem_ctx, trusts);
2914 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2915 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2916 if (!domain->internal && old_status) {
2917 set_domain_offline(domain);
2919 if (!domain->internal &&
2920 !domain->online &&
2921 old_status) {
2922 retval = wcache_tdc_fetch_list(&dom_list, &num_domains);
2923 if (retval && num_domains && dom_list) {
2924 TALLOC_FREE(trusts->array);
2925 trusts->count = 0;
2926 goto do_fetch_cache;
2930 /* no trusts gives NT_STATUS_NO_MORE_ENTRIES resetting to NT_STATUS_OK
2931 * so that the generic centry handling still applies correctly -
2932 * Guenther*/
2934 if (!NT_STATUS_IS_ERR(status)) {
2935 status = NT_STATUS_OK;
2937 return status;
2940 /* get lockout policy */
2941 static NTSTATUS lockout_policy(struct winbindd_domain *domain,
2942 TALLOC_CTX *mem_ctx,
2943 struct samr_DomInfo12 *policy)
2945 struct winbind_cache *cache = get_cache(domain);
2946 struct cache_entry *centry = NULL;
2947 NTSTATUS status;
2948 bool old_status;
2950 old_status = domain->online;
2951 if (!cache->tdb)
2952 goto do_query;
2954 centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
2956 if (!centry)
2957 goto do_query;
2959 do_fetch_cache:
2960 policy->lockout_duration = centry_nttime(centry);
2961 policy->lockout_window = centry_nttime(centry);
2962 policy->lockout_threshold = centry_uint16(centry);
2964 status = centry->status;
2966 DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2967 domain->name, nt_errstr(status) ));
2969 centry_free(centry);
2970 return status;
2972 do_query:
2973 ZERO_STRUCTP(policy);
2975 /* Return status value returned by seq number check */
2977 if (!NT_STATUS_IS_OK(domain->last_status))
2978 return domain->last_status;
2980 DEBUG(10,("lockout_policy: [Cached] - doing backend query for info for domain %s\n",
2981 domain->name ));
2983 status = domain->backend->lockout_policy(domain, mem_ctx, policy);
2985 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2986 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2987 if (!domain->internal && old_status) {
2988 set_domain_offline(domain);
2990 if (cache->tdb &&
2991 !domain->internal &&
2992 !domain->online &&
2993 old_status) {
2994 centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
2995 if (centry) {
2996 goto do_fetch_cache;
3000 /* and save it */
3001 refresh_sequence_number(domain, false);
3002 if (!NT_STATUS_IS_OK(status)) {
3003 return status;
3005 wcache_save_lockout_policy(domain, status, policy);
3007 return status;
3010 /* get password policy */
3011 static NTSTATUS password_policy(struct winbindd_domain *domain,
3012 TALLOC_CTX *mem_ctx,
3013 struct samr_DomInfo1 *policy)
3015 struct winbind_cache *cache = get_cache(domain);
3016 struct cache_entry *centry = NULL;
3017 NTSTATUS status;
3018 bool old_status;
3020 old_status = domain->online;
3021 if (!cache->tdb)
3022 goto do_query;
3024 centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
3026 if (!centry)
3027 goto do_query;
3029 do_fetch_cache:
3030 policy->min_password_length = centry_uint16(centry);
3031 policy->password_history_length = centry_uint16(centry);
3032 policy->password_properties = centry_uint32(centry);
3033 policy->max_password_age = centry_nttime(centry);
3034 policy->min_password_age = centry_nttime(centry);
3036 status = centry->status;
3038 DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
3039 domain->name, nt_errstr(status) ));
3041 centry_free(centry);
3042 return status;
3044 do_query:
3045 ZERO_STRUCTP(policy);
3047 /* Return status value returned by seq number check */
3049 if (!NT_STATUS_IS_OK(domain->last_status))
3050 return domain->last_status;
3052 DEBUG(10,("password_policy: [Cached] - doing backend query for info for domain %s\n",
3053 domain->name ));
3055 status = domain->backend->password_policy(domain, mem_ctx, policy);
3057 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
3058 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
3059 if (!domain->internal && old_status) {
3060 set_domain_offline(domain);
3062 if (cache->tdb &&
3063 !domain->internal &&
3064 !domain->online &&
3065 old_status) {
3066 centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
3067 if (centry) {
3068 goto do_fetch_cache;
3072 /* and save it */
3073 refresh_sequence_number(domain, false);
3074 if (!NT_STATUS_IS_OK(status)) {
3075 return status;
3077 wcache_save_password_policy(domain, status, policy);
3079 return status;
3083 /* Invalidate cached user and group lists coherently */
3085 static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
3086 void *state)
3088 if (strncmp((const char *)kbuf.dptr, "UL/", 3) == 0 ||
3089 strncmp((const char *)kbuf.dptr, "GL/", 3) == 0)
3090 tdb_delete(the_tdb, kbuf);
3092 return 0;
3095 /* Invalidate the getpwnam and getgroups entries for a winbindd domain */
3097 void wcache_invalidate_samlogon(struct winbindd_domain *domain,
3098 const struct dom_sid *sid)
3100 fstring key_str, sid_string;
3101 struct winbind_cache *cache;
3103 /* dont clear cached U/SID and UG/SID entries when we want to logon
3104 * offline - gd */
3106 if (lp_winbind_offline_logon()) {
3107 return;
3110 if (!domain)
3111 return;
3113 cache = get_cache(domain);
3115 if (!cache->tdb) {
3116 return;
3119 /* Clear U/SID cache entry */
3120 fstr_sprintf(key_str, "U/%s", sid_to_fstring(sid_string, sid));
3121 DEBUG(10, ("wcache_invalidate_samlogon: clearing %s\n", key_str));
3122 tdb_delete(cache->tdb, string_tdb_data(key_str));
3124 /* Clear UG/SID cache entry */
3125 fstr_sprintf(key_str, "UG/%s", sid_to_fstring(sid_string, sid));
3126 DEBUG(10, ("wcache_invalidate_samlogon: clearing %s\n", key_str));
3127 tdb_delete(cache->tdb, string_tdb_data(key_str));
3129 /* Samba/winbindd never needs this. */
3130 netsamlogon_clear_cached_user(sid);
3133 bool wcache_invalidate_cache(void)
3135 struct winbindd_domain *domain;
3137 for (domain = domain_list(); domain; domain = domain->next) {
3138 struct winbind_cache *cache = get_cache(domain);
3140 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
3141 "entries for %s\n", domain->name));
3142 if (cache) {
3143 if (cache->tdb) {
3144 tdb_traverse(cache->tdb, traverse_fn, NULL);
3145 } else {
3146 return false;
3150 return true;
3153 bool wcache_invalidate_cache_noinit(void)
3155 struct winbindd_domain *domain;
3157 for (domain = domain_list(); domain; domain = domain->next) {
3158 struct winbind_cache *cache;
3160 /* Skip uninitialized domains. */
3161 if (!domain->initialized && !domain->internal) {
3162 continue;
3165 cache = get_cache(domain);
3167 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
3168 "entries for %s\n", domain->name));
3169 if (cache) {
3170 if (cache->tdb) {
3171 tdb_traverse(cache->tdb, traverse_fn, NULL);
3173 * Flushing cache has nothing to with domains.
3174 * return here if we successfully flushed once.
3175 * To avoid unnecessary traversing the cache.
3177 return true;
3178 } else {
3179 return false;
3183 return true;
3186 bool init_wcache(void)
3188 char *db_path;
3190 if (wcache == NULL) {
3191 wcache = SMB_XMALLOC_P(struct winbind_cache);
3192 ZERO_STRUCTP(wcache);
3195 if (wcache->tdb != NULL)
3196 return true;
3198 db_path = state_path("winbindd_cache.tdb");
3199 if (db_path == NULL) {
3200 return false;
3203 /* when working offline we must not clear the cache on restart */
3204 wcache->tdb = tdb_open_log(db_path,
3205 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
3206 TDB_INCOMPATIBLE_HASH |
3207 (lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST)),
3208 O_RDWR|O_CREAT, 0600);
3209 TALLOC_FREE(db_path);
3210 if (wcache->tdb == NULL) {
3211 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
3212 return false;
3215 return true;
3218 /************************************************************************
3219 This is called by the parent to initialize the cache file.
3220 We don't need sophisticated locking here as we know we're the
3221 only opener.
3222 ************************************************************************/
3224 bool initialize_winbindd_cache(void)
3226 bool cache_bad = true;
3227 uint32 vers;
3229 if (!init_wcache()) {
3230 DEBUG(0,("initialize_winbindd_cache: init_wcache failed.\n"));
3231 return false;
3234 /* Check version number. */
3235 if (tdb_fetch_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, &vers) &&
3236 vers == WINBINDD_CACHE_VERSION) {
3237 cache_bad = false;
3240 if (cache_bad) {
3241 char *db_path;
3243 DEBUG(0,("initialize_winbindd_cache: clearing cache "
3244 "and re-creating with version number %d\n",
3245 WINBINDD_CACHE_VERSION ));
3247 tdb_close(wcache->tdb);
3248 wcache->tdb = NULL;
3250 db_path = state_path("winbindd_cache.tdb");
3251 if (db_path == NULL) {
3252 return false;
3255 if (unlink(db_path) == -1) {
3256 DEBUG(0,("initialize_winbindd_cache: unlink %s failed %s ",
3257 db_path,
3258 strerror(errno) ));
3259 TALLOC_FREE(db_path);
3260 return false;
3262 TALLOC_FREE(db_path);
3263 if (!init_wcache()) {
3264 DEBUG(0,("initialize_winbindd_cache: re-initialization "
3265 "init_wcache failed.\n"));
3266 return false;
3269 /* Write the version. */
3270 if (!tdb_store_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, WINBINDD_CACHE_VERSION)) {
3271 DEBUG(0,("initialize_winbindd_cache: version number store failed %s\n",
3272 tdb_errorstr(wcache->tdb) ));
3273 return false;
3277 tdb_close(wcache->tdb);
3278 wcache->tdb = NULL;
3279 return true;
3282 void close_winbindd_cache(void)
3284 if (!wcache) {
3285 return;
3287 if (wcache->tdb) {
3288 tdb_close(wcache->tdb);
3289 wcache->tdb = NULL;
3293 bool lookup_cached_sid(TALLOC_CTX *mem_ctx, const struct dom_sid *sid,
3294 char **domain_name, char **name,
3295 enum lsa_SidType *type)
3297 struct winbindd_domain *domain;
3298 NTSTATUS status;
3300 domain = find_lookup_domain_from_sid(sid);
3301 if (domain == NULL) {
3302 return false;
3304 status = wcache_sid_to_name(domain, sid, mem_ctx, domain_name, name,
3305 type);
3306 return NT_STATUS_IS_OK(status);
3309 bool lookup_cached_name(const char *domain_name,
3310 const char *name,
3311 struct dom_sid *sid,
3312 enum lsa_SidType *type)
3314 struct winbindd_domain *domain;
3315 NTSTATUS status;
3316 bool original_online_state;
3318 domain = find_lookup_domain_from_name(domain_name);
3319 if (domain == NULL) {
3320 return false;
3323 /* If we are doing a cached logon, temporarily set the domain
3324 offline so the cache won't expire the entry */
3326 original_online_state = domain->online;
3327 domain->online = false;
3328 status = wcache_name_to_sid(domain, domain_name, name, sid, type);
3329 domain->online = original_online_state;
3331 return NT_STATUS_IS_OK(status);
3334 void cache_name2sid(struct winbindd_domain *domain,
3335 const char *domain_name, const char *name,
3336 enum lsa_SidType type, const struct dom_sid *sid)
3338 refresh_sequence_number(domain, false);
3339 wcache_save_name_to_sid(domain, NT_STATUS_OK, domain_name, name,
3340 sid, type);
3344 * The original idea that this cache only contains centries has
3345 * been blurred - now other stuff gets put in here. Ensure we
3346 * ignore these things on cleanup.
3349 static int traverse_fn_cleanup(TDB_CONTEXT *the_tdb, TDB_DATA kbuf,
3350 TDB_DATA dbuf, void *state)
3352 struct cache_entry *centry;
3354 if (is_non_centry_key(kbuf)) {
3355 return 0;
3358 centry = wcache_fetch_raw((char *)kbuf.dptr);
3359 if (!centry) {
3360 return 0;
3363 if (!NT_STATUS_IS_OK(centry->status)) {
3364 DEBUG(10,("deleting centry %s\n", (const char *)kbuf.dptr));
3365 tdb_delete(the_tdb, kbuf);
3368 centry_free(centry);
3369 return 0;
3372 /* flush the cache */
3373 void wcache_flush_cache(void)
3375 char *db_path;
3377 if (!wcache)
3378 return;
3379 if (wcache->tdb) {
3380 tdb_close(wcache->tdb);
3381 wcache->tdb = NULL;
3383 if (!winbindd_use_cache()) {
3384 return;
3387 db_path = state_path("winbindd_cache.tdb");
3388 if (db_path == NULL) {
3389 return;
3392 /* when working offline we must not clear the cache on restart */
3393 wcache->tdb = tdb_open_log(db_path,
3394 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
3395 TDB_INCOMPATIBLE_HASH |
3396 (lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST)),
3397 O_RDWR|O_CREAT, 0600);
3398 TALLOC_FREE(db_path);
3399 if (!wcache->tdb) {
3400 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
3401 return;
3404 tdb_traverse(wcache->tdb, traverse_fn_cleanup, NULL);
3406 DEBUG(10,("wcache_flush_cache success\n"));
3409 /* Count cached creds */
3411 static int traverse_fn_cached_creds(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
3412 void *state)
3414 int *cred_count = (int*)state;
3416 if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
3417 (*cred_count)++;
3419 return 0;
3422 NTSTATUS wcache_count_cached_creds(struct winbindd_domain *domain, int *count)
3424 struct winbind_cache *cache = get_cache(domain);
3426 *count = 0;
3428 if (!cache->tdb) {
3429 return NT_STATUS_INTERNAL_DB_ERROR;
3432 tdb_traverse(cache->tdb, traverse_fn_cached_creds, (void *)count);
3434 return NT_STATUS_OK;
3437 struct cred_list {
3438 struct cred_list *prev, *next;
3439 TDB_DATA key;
3440 fstring name;
3441 time_t created;
3443 static struct cred_list *wcache_cred_list;
3445 static int traverse_fn_get_credlist(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
3446 void *state)
3448 struct cred_list *cred;
3450 if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
3452 cred = SMB_MALLOC_P(struct cred_list);
3453 if (cred == NULL) {
3454 DEBUG(0,("traverse_fn_remove_first_creds: failed to malloc new entry for list\n"));
3455 return -1;
3458 ZERO_STRUCTP(cred);
3460 /* save a copy of the key */
3462 fstrcpy(cred->name, (const char *)kbuf.dptr);
3463 DLIST_ADD(wcache_cred_list, cred);
3466 return 0;
3469 NTSTATUS wcache_remove_oldest_cached_creds(struct winbindd_domain *domain, const struct dom_sid *sid)
3471 struct winbind_cache *cache = get_cache(domain);
3472 NTSTATUS status;
3473 int ret;
3474 struct cred_list *cred, *oldest = NULL;
3476 if (!cache->tdb) {
3477 return NT_STATUS_INTERNAL_DB_ERROR;
3480 /* we possibly already have an entry */
3481 if (sid && NT_STATUS_IS_OK(wcache_cached_creds_exist(domain, sid))) {
3483 fstring key_str, tmp;
3485 DEBUG(11,("we already have an entry, deleting that\n"));
3487 fstr_sprintf(key_str, "CRED/%s", sid_to_fstring(tmp, sid));
3489 tdb_delete(cache->tdb, string_tdb_data(key_str));
3491 return NT_STATUS_OK;
3494 ret = tdb_traverse(cache->tdb, traverse_fn_get_credlist, NULL);
3495 if (ret == 0) {
3496 return NT_STATUS_OK;
3497 } else if ((ret < 0) || (wcache_cred_list == NULL)) {
3498 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
3501 ZERO_STRUCTP(oldest);
3503 for (cred = wcache_cred_list; cred; cred = cred->next) {
3505 TDB_DATA data;
3506 time_t t;
3508 data = tdb_fetch(cache->tdb, string_tdb_data(cred->name));
3509 if (!data.dptr) {
3510 DEBUG(10,("wcache_remove_oldest_cached_creds: entry for [%s] not found\n",
3511 cred->name));
3512 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
3513 goto done;
3516 t = IVAL(data.dptr, 0);
3517 SAFE_FREE(data.dptr);
3519 if (!oldest) {
3520 oldest = SMB_MALLOC_P(struct cred_list);
3521 if (oldest == NULL) {
3522 status = NT_STATUS_NO_MEMORY;
3523 goto done;
3526 fstrcpy(oldest->name, cred->name);
3527 oldest->created = t;
3528 continue;
3531 if (t < oldest->created) {
3532 fstrcpy(oldest->name, cred->name);
3533 oldest->created = t;
3537 if (tdb_delete(cache->tdb, string_tdb_data(oldest->name)) == 0) {
3538 status = NT_STATUS_OK;
3539 } else {
3540 status = NT_STATUS_UNSUCCESSFUL;
3542 done:
3543 SAFE_FREE(wcache_cred_list);
3544 SAFE_FREE(oldest);
3546 return status;
3549 /* Change the global online/offline state. */
3550 bool set_global_winbindd_state_offline(void)
3552 TDB_DATA data;
3554 DEBUG(10,("set_global_winbindd_state_offline: offline requested.\n"));
3556 /* Only go offline if someone has created
3557 the key "WINBINDD_OFFLINE" in the cache tdb. */
3559 if (wcache == NULL || wcache->tdb == NULL) {
3560 DEBUG(10,("set_global_winbindd_state_offline: wcache not open yet.\n"));
3561 return false;
3564 if (!lp_winbind_offline_logon()) {
3565 DEBUG(10,("set_global_winbindd_state_offline: rejecting.\n"));
3566 return false;
3569 if (global_winbindd_offline_state) {
3570 /* Already offline. */
3571 return true;
3574 data = tdb_fetch_bystring( wcache->tdb, "WINBINDD_OFFLINE" );
3576 if (!data.dptr || data.dsize != 4) {
3577 DEBUG(10,("set_global_winbindd_state_offline: offline state not set.\n"));
3578 SAFE_FREE(data.dptr);
3579 return false;
3580 } else {
3581 DEBUG(10,("set_global_winbindd_state_offline: offline state set.\n"));
3582 global_winbindd_offline_state = true;
3583 SAFE_FREE(data.dptr);
3584 return true;
3588 void set_global_winbindd_state_online(void)
3590 DEBUG(10,("set_global_winbindd_state_online: online requested.\n"));
3592 if (!lp_winbind_offline_logon()) {
3593 DEBUG(10,("set_global_winbindd_state_online: rejecting.\n"));
3594 return;
3597 if (!global_winbindd_offline_state) {
3598 /* Already online. */
3599 return;
3601 global_winbindd_offline_state = false;
3603 if (!wcache->tdb) {
3604 return;
3607 /* Ensure there is no key "WINBINDD_OFFLINE" in the cache tdb. */
3608 tdb_delete_bystring(wcache->tdb, "WINBINDD_OFFLINE");
3611 bool get_global_winbindd_state_offline(void)
3613 return global_winbindd_offline_state;
3616 /***********************************************************************
3617 Validate functions for all possible cache tdb keys.
3618 ***********************************************************************/
3620 static struct cache_entry *create_centry_validate(const char *kstr, TDB_DATA data,
3621 struct tdb_validation_status *state)
3623 struct cache_entry *centry;
3625 centry = SMB_XMALLOC_P(struct cache_entry);
3626 centry->data = (unsigned char *)smb_memdup(data.dptr, data.dsize);
3627 if (!centry->data) {
3628 SAFE_FREE(centry);
3629 return NULL;
3631 centry->len = data.dsize;
3632 centry->ofs = 0;
3634 if (centry->len < 16) {
3635 /* huh? corrupt cache? */
3636 DEBUG(0,("create_centry_validate: Corrupt cache for key %s "
3637 "(len < 16) ?\n", kstr));
3638 centry_free(centry);
3639 state->bad_entry = true;
3640 state->success = false;
3641 return NULL;
3644 centry->status = NT_STATUS(centry_uint32(centry));
3645 centry->sequence_number = centry_uint32(centry);
3646 centry->timeout = centry_uint64_t(centry);
3647 return centry;
3650 static int validate_seqnum(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3651 struct tdb_validation_status *state)
3653 if (dbuf.dsize != 8) {
3654 DEBUG(0,("validate_seqnum: Corrupt cache for key %s (len %u != 8) ?\n",
3655 keystr, (unsigned int)dbuf.dsize ));
3656 state->bad_entry = true;
3657 return 1;
3659 return 0;
3662 static int validate_ns(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3663 struct tdb_validation_status *state)
3665 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3666 if (!centry) {
3667 return 1;
3670 (void)centry_uint32(centry);
3671 if (NT_STATUS_IS_OK(centry->status)) {
3672 struct dom_sid sid;
3673 (void)centry_sid(centry, &sid);
3676 centry_free(centry);
3678 if (!(state->success)) {
3679 return 1;
3681 DEBUG(10,("validate_ns: %s ok\n", keystr));
3682 return 0;
3685 static int validate_sn(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3686 struct tdb_validation_status *state)
3688 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3689 if (!centry) {
3690 return 1;
3693 if (NT_STATUS_IS_OK(centry->status)) {
3694 (void)centry_uint32(centry);
3695 (void)centry_string(centry, mem_ctx);
3696 (void)centry_string(centry, mem_ctx);
3699 centry_free(centry);
3701 if (!(state->success)) {
3702 return 1;
3704 DEBUG(10,("validate_sn: %s ok\n", keystr));
3705 return 0;
3708 static int validate_u(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3709 struct tdb_validation_status *state)
3711 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3712 struct dom_sid sid;
3714 if (!centry) {
3715 return 1;
3718 (void)centry_string(centry, mem_ctx);
3719 (void)centry_string(centry, mem_ctx);
3720 (void)centry_string(centry, mem_ctx);
3721 (void)centry_string(centry, mem_ctx);
3722 (void)centry_uint32(centry);
3723 (void)centry_sid(centry, &sid);
3724 (void)centry_sid(centry, &sid);
3726 centry_free(centry);
3728 if (!(state->success)) {
3729 return 1;
3731 DEBUG(10,("validate_u: %s ok\n", keystr));
3732 return 0;
3735 static int validate_loc_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3736 struct tdb_validation_status *state)
3738 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3740 if (!centry) {
3741 return 1;
3744 (void)centry_nttime(centry);
3745 (void)centry_nttime(centry);
3746 (void)centry_uint16(centry);
3748 centry_free(centry);
3750 if (!(state->success)) {
3751 return 1;
3753 DEBUG(10,("validate_loc_pol: %s ok\n", keystr));
3754 return 0;
3757 static int validate_pwd_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3758 struct tdb_validation_status *state)
3760 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3762 if (!centry) {
3763 return 1;
3766 (void)centry_uint16(centry);
3767 (void)centry_uint16(centry);
3768 (void)centry_uint32(centry);
3769 (void)centry_nttime(centry);
3770 (void)centry_nttime(centry);
3772 centry_free(centry);
3774 if (!(state->success)) {
3775 return 1;
3777 DEBUG(10,("validate_pwd_pol: %s ok\n", keystr));
3778 return 0;
3781 static int validate_cred(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3782 struct tdb_validation_status *state)
3784 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3786 if (!centry) {
3787 return 1;
3790 (void)centry_time(centry);
3791 (void)centry_hash16(centry, mem_ctx);
3793 /* We only have 17 bytes more data in the salted cred case. */
3794 if (centry->len - centry->ofs == 17) {
3795 (void)centry_hash16(centry, mem_ctx);
3798 centry_free(centry);
3800 if (!(state->success)) {
3801 return 1;
3803 DEBUG(10,("validate_cred: %s ok\n", keystr));
3804 return 0;
3807 static int validate_ul(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3808 struct tdb_validation_status *state)
3810 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3811 int32 num_entries, i;
3813 if (!centry) {
3814 return 1;
3817 num_entries = (int32)centry_uint32(centry);
3819 for (i=0; i< num_entries; i++) {
3820 struct dom_sid sid;
3821 (void)centry_string(centry, mem_ctx);
3822 (void)centry_string(centry, mem_ctx);
3823 (void)centry_string(centry, mem_ctx);
3824 (void)centry_string(centry, mem_ctx);
3825 (void)centry_sid(centry, &sid);
3826 (void)centry_sid(centry, &sid);
3829 centry_free(centry);
3831 if (!(state->success)) {
3832 return 1;
3834 DEBUG(10,("validate_ul: %s ok\n", keystr));
3835 return 0;
3838 static int validate_gl(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3839 struct tdb_validation_status *state)
3841 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3842 int32 num_entries, i;
3844 if (!centry) {
3845 return 1;
3848 num_entries = centry_uint32(centry);
3850 for (i=0; i< num_entries; i++) {
3851 (void)centry_string(centry, mem_ctx);
3852 (void)centry_string(centry, mem_ctx);
3853 (void)centry_uint32(centry);
3856 centry_free(centry);
3858 if (!(state->success)) {
3859 return 1;
3861 DEBUG(10,("validate_gl: %s ok\n", keystr));
3862 return 0;
3865 static int validate_ug(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3866 struct tdb_validation_status *state)
3868 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3869 int32 num_groups, i;
3871 if (!centry) {
3872 return 1;
3875 num_groups = centry_uint32(centry);
3877 for (i=0; i< num_groups; i++) {
3878 struct dom_sid sid;
3879 centry_sid(centry, &sid);
3882 centry_free(centry);
3884 if (!(state->success)) {
3885 return 1;
3887 DEBUG(10,("validate_ug: %s ok\n", keystr));
3888 return 0;
3891 static int validate_ua(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3892 struct tdb_validation_status *state)
3894 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3895 int32 num_aliases, i;
3897 if (!centry) {
3898 return 1;
3901 num_aliases = centry_uint32(centry);
3903 for (i=0; i < num_aliases; i++) {
3904 (void)centry_uint32(centry);
3907 centry_free(centry);
3909 if (!(state->success)) {
3910 return 1;
3912 DEBUG(10,("validate_ua: %s ok\n", keystr));
3913 return 0;
3916 static int validate_gm(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3917 struct tdb_validation_status *state)
3919 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3920 int32 num_names, i;
3922 if (!centry) {
3923 return 1;
3926 num_names = centry_uint32(centry);
3928 for (i=0; i< num_names; i++) {
3929 struct dom_sid sid;
3930 centry_sid(centry, &sid);
3931 (void)centry_string(centry, mem_ctx);
3932 (void)centry_uint32(centry);
3935 centry_free(centry);
3937 if (!(state->success)) {
3938 return 1;
3940 DEBUG(10,("validate_gm: %s ok\n", keystr));
3941 return 0;
3944 static int validate_dr(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3945 struct tdb_validation_status *state)
3947 /* Can't say anything about this other than must be nonzero. */
3948 if (dbuf.dsize == 0) {
3949 DEBUG(0,("validate_dr: Corrupt cache for key %s (len == 0) ?\n",
3950 keystr));
3951 state->bad_entry = true;
3952 state->success = false;
3953 return 1;
3956 DEBUG(10,("validate_dr: %s ok\n", keystr));
3957 return 0;
3960 static int validate_de(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3961 struct tdb_validation_status *state)
3963 /* Can't say anything about this other than must be nonzero. */
3964 if (dbuf.dsize == 0) {
3965 DEBUG(0,("validate_de: Corrupt cache for key %s (len == 0) ?\n",
3966 keystr));
3967 state->bad_entry = true;
3968 state->success = false;
3969 return 1;
3972 DEBUG(10,("validate_de: %s ok\n", keystr));
3973 return 0;
3976 static int validate_pwinfo(TALLOC_CTX *mem_ctx, const char *keystr,
3977 TDB_DATA dbuf, struct tdb_validation_status *state)
3979 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3981 if (!centry) {
3982 return 1;
3985 (void)centry_string(centry, mem_ctx);
3986 (void)centry_string(centry, mem_ctx);
3987 (void)centry_string(centry, mem_ctx);
3988 (void)centry_uint32(centry);
3990 centry_free(centry);
3992 if (!(state->success)) {
3993 return 1;
3995 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3996 return 0;
3999 static int validate_nss_an(TALLOC_CTX *mem_ctx, const char *keystr,
4000 TDB_DATA dbuf,
4001 struct tdb_validation_status *state)
4003 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
4005 if (!centry) {
4006 return 1;
4009 (void)centry_string( centry, mem_ctx );
4011 centry_free(centry);
4013 if (!(state->success)) {
4014 return 1;
4016 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
4017 return 0;
4020 static int validate_nss_na(TALLOC_CTX *mem_ctx, const char *keystr,
4021 TDB_DATA dbuf,
4022 struct tdb_validation_status *state)
4024 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
4026 if (!centry) {
4027 return 1;
4030 (void)centry_string( centry, mem_ctx );
4032 centry_free(centry);
4034 if (!(state->success)) {
4035 return 1;
4037 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
4038 return 0;
4041 static int validate_trustdomcache(TALLOC_CTX *mem_ctx, const char *keystr,
4042 TDB_DATA dbuf,
4043 struct tdb_validation_status *state)
4045 if (dbuf.dsize == 0) {
4046 DEBUG(0, ("validate_trustdomcache: Corrupt cache for "
4047 "key %s (len ==0) ?\n", keystr));
4048 state->bad_entry = true;
4049 state->success = false;
4050 return 1;
4053 DEBUG(10, ("validate_trustdomcache: %s ok\n", keystr));
4054 DEBUGADD(10, (" Don't trust me, I am a DUMMY!\n"));
4055 return 0;
4058 static int validate_offline(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
4059 struct tdb_validation_status *state)
4061 if (dbuf.dsize != 4) {
4062 DEBUG(0,("validate_offline: Corrupt cache for key %s (len %u != 4) ?\n",
4063 keystr, (unsigned int)dbuf.dsize ));
4064 state->bad_entry = true;
4065 state->success = false;
4066 return 1;
4068 DEBUG(10,("validate_offline: %s ok\n", keystr));
4069 return 0;
4072 static int validate_ndr(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
4073 struct tdb_validation_status *state)
4076 * Ignore validation for now. The proper way to do this is with a
4077 * checksum. Just pure parsing does not really catch much.
4079 return 0;
4082 static int validate_cache_version(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
4083 struct tdb_validation_status *state)
4085 if (dbuf.dsize != 4) {
4086 DEBUG(0, ("validate_cache_version: Corrupt cache for "
4087 "key %s (len %u != 4) ?\n",
4088 keystr, (unsigned int)dbuf.dsize));
4089 state->bad_entry = true;
4090 state->success = false;
4091 return 1;
4094 DEBUG(10, ("validate_cache_version: %s ok\n", keystr));
4095 return 0;
4098 /***********************************************************************
4099 A list of all possible cache tdb keys with associated validation
4100 functions.
4101 ***********************************************************************/
4103 struct key_val_struct {
4104 const char *keyname;
4105 int (*validate_data_fn)(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, struct tdb_validation_status* state);
4106 } key_val[] = {
4107 {"SEQNUM/", validate_seqnum},
4108 {"NS/", validate_ns},
4109 {"SN/", validate_sn},
4110 {"U/", validate_u},
4111 {"LOC_POL/", validate_loc_pol},
4112 {"PWD_POL/", validate_pwd_pol},
4113 {"CRED/", validate_cred},
4114 {"UL/", validate_ul},
4115 {"GL/", validate_gl},
4116 {"UG/", validate_ug},
4117 {"UA", validate_ua},
4118 {"GM/", validate_gm},
4119 {"DR/", validate_dr},
4120 {"DE/", validate_de},
4121 {"NSS/PWINFO/", validate_pwinfo},
4122 {"TRUSTDOMCACHE/", validate_trustdomcache},
4123 {"NSS/NA/", validate_nss_na},
4124 {"NSS/AN/", validate_nss_an},
4125 {"WINBINDD_OFFLINE", validate_offline},
4126 {"NDR/", validate_ndr},
4127 {WINBINDD_CACHE_VERSION_KEYSTR, validate_cache_version},
4128 {NULL, NULL}
4131 /***********************************************************************
4132 Function to look at every entry in the tdb and validate it as far as
4133 possible.
4134 ***********************************************************************/
4136 static int cache_traverse_validate_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, void *state)
4138 int i;
4139 unsigned int max_key_len = 1024;
4140 struct tdb_validation_status *v_state = (struct tdb_validation_status *)state;
4142 /* Paranoia check. */
4143 if (strncmp("UA/", (const char *)kbuf.dptr, 3) == 0 ||
4144 strncmp("NDR/", (const char *)kbuf.dptr, 4) == 0) {
4145 max_key_len = 1024 * 1024;
4147 if (kbuf.dsize > max_key_len) {
4148 DEBUG(0, ("cache_traverse_validate_fn: key length too large: "
4149 "(%u) > (%u)\n\n",
4150 (unsigned int)kbuf.dsize, (unsigned int)max_key_len));
4151 return 1;
4154 for (i = 0; key_val[i].keyname; i++) {
4155 size_t namelen = strlen(key_val[i].keyname);
4156 if (kbuf.dsize >= namelen && (
4157 strncmp(key_val[i].keyname, (const char *)kbuf.dptr, namelen)) == 0) {
4158 TALLOC_CTX *mem_ctx;
4159 char *keystr;
4160 int ret;
4162 keystr = SMB_MALLOC_ARRAY(char, kbuf.dsize+1);
4163 if (!keystr) {
4164 return 1;
4166 memcpy(keystr, kbuf.dptr, kbuf.dsize);
4167 keystr[kbuf.dsize] = '\0';
4169 mem_ctx = talloc_init("validate_ctx");
4170 if (!mem_ctx) {
4171 SAFE_FREE(keystr);
4172 return 1;
4175 ret = key_val[i].validate_data_fn(mem_ctx, keystr, dbuf,
4176 v_state);
4178 SAFE_FREE(keystr);
4179 talloc_destroy(mem_ctx);
4180 return ret;
4184 DEBUG(0,("cache_traverse_validate_fn: unknown cache entry\nkey :\n"));
4185 dump_data(0, (uint8 *)kbuf.dptr, kbuf.dsize);
4186 DEBUG(0,("data :\n"));
4187 dump_data(0, (uint8 *)dbuf.dptr, dbuf.dsize);
4188 v_state->unknown_key = true;
4189 v_state->success = false;
4190 return 1; /* terminate. */
4193 static void validate_panic(const char *const why)
4195 DEBUG(0,("validating cache: would panic %s\n", why ));
4196 DEBUGADD(0, ("exiting instead (cache validation mode)\n"));
4197 exit(47);
4200 static int wbcache_update_centry_fn(TDB_CONTEXT *tdb,
4201 TDB_DATA key,
4202 TDB_DATA data,
4203 void *state)
4205 uint64_t ctimeout;
4206 TDB_DATA blob;
4208 if (is_non_centry_key(key)) {
4209 return 0;
4212 if (data.dptr == NULL || data.dsize == 0) {
4213 if (tdb_delete(tdb, key) < 0) {
4214 DEBUG(0, ("tdb_delete for [%s] failed!\n",
4215 key.dptr));
4216 return 1;
4220 /* add timeout to blob (uint64_t) */
4221 blob.dsize = data.dsize + 8;
4223 blob.dptr = SMB_XMALLOC_ARRAY(uint8_t, blob.dsize);
4224 if (blob.dptr == NULL) {
4225 return 1;
4227 memset(blob.dptr, 0, blob.dsize);
4229 /* copy status and seqnum */
4230 memcpy(blob.dptr, data.dptr, 8);
4232 /* add timeout */
4233 ctimeout = lp_winbind_cache_time() + time(NULL);
4234 SBVAL(blob.dptr, 8, ctimeout);
4236 /* copy the rest */
4237 memcpy(blob.dptr + 16, data.dptr + 8, data.dsize - 8);
4239 if (tdb_store(tdb, key, blob, TDB_REPLACE) < 0) {
4240 DEBUG(0, ("tdb_store to update [%s] failed!\n",
4241 key.dptr));
4242 SAFE_FREE(blob.dptr);
4243 return 1;
4246 SAFE_FREE(blob.dptr);
4247 return 0;
4250 static bool wbcache_upgrade_v1_to_v2(TDB_CONTEXT *tdb)
4252 int rc;
4254 DEBUG(1, ("Upgrade to version 2 of the winbindd_cache.tdb\n"));
4256 rc = tdb_traverse(tdb, wbcache_update_centry_fn, NULL);
4257 if (rc < 0) {
4258 return false;
4261 return true;
4264 /***********************************************************************
4265 Try and validate every entry in the winbindd cache. If we fail here,
4266 delete the cache tdb and return non-zero.
4267 ***********************************************************************/
4269 int winbindd_validate_cache(void)
4271 int ret = -1;
4272 char *tdb_path = NULL;
4273 TDB_CONTEXT *tdb = NULL;
4274 uint32_t vers_id;
4275 bool ok;
4277 DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
4278 smb_panic_fn = validate_panic;
4280 tdb_path = state_path("winbindd_cache.tdb");
4281 if (tdb_path == NULL) {
4282 goto done;
4285 tdb = tdb_open_log(tdb_path,
4286 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
4287 TDB_INCOMPATIBLE_HASH |
4288 ( lp_winbind_offline_logon()
4289 ? TDB_DEFAULT
4290 : TDB_DEFAULT | TDB_CLEAR_IF_FIRST ),
4291 O_RDWR|O_CREAT,
4292 0600);
4293 if (!tdb) {
4294 DEBUG(0, ("winbindd_validate_cache: "
4295 "error opening/initializing tdb\n"));
4296 goto done;
4299 /* Version check and upgrade code. */
4300 if (!tdb_fetch_uint32(tdb, WINBINDD_CACHE_VERSION_KEYSTR, &vers_id)) {
4301 DEBUG(10, ("Fresh database\n"));
4302 tdb_store_uint32(tdb, WINBINDD_CACHE_VERSION_KEYSTR, WINBINDD_CACHE_VERSION);
4303 vers_id = WINBINDD_CACHE_VERSION;
4306 if (vers_id != WINBINDD_CACHE_VERSION) {
4307 if (vers_id == WINBINDD_CACHE_VER1) {
4308 ok = wbcache_upgrade_v1_to_v2(tdb);
4309 if (!ok) {
4310 DEBUG(10, ("winbindd_validate_cache: upgrade to version 2 failed.\n"));
4311 unlink(tdb_path);
4312 goto done;
4315 tdb_store_uint32(tdb,
4316 WINBINDD_CACHE_VERSION_KEYSTR,
4317 WINBINDD_CACHE_VERSION);
4318 vers_id = WINBINDD_CACHE_VER2;
4322 tdb_close(tdb);
4324 ret = tdb_validate_and_backup(tdb_path, cache_traverse_validate_fn);
4326 if (ret != 0) {
4327 DEBUG(10, ("winbindd_validate_cache: validation not successful.\n"));
4328 DEBUGADD(10, ("removing tdb %s.\n", tdb_path));
4329 unlink(tdb_path);
4332 done:
4333 TALLOC_FREE(tdb_path);
4334 DEBUG(10, ("winbindd_validate_cache: restoring panic function\n"));
4335 smb_panic_fn = smb_panic;
4336 return ret;
4339 /***********************************************************************
4340 Try and validate every entry in the winbindd cache.
4341 ***********************************************************************/
4343 int winbindd_validate_cache_nobackup(void)
4345 int ret = -1;
4346 char *tdb_path;
4348 DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
4349 smb_panic_fn = validate_panic;
4351 tdb_path = state_path("winbindd_cache.tdb");
4352 if (tdb_path == NULL) {
4353 goto err_panic_restore;
4356 if (wcache == NULL || wcache->tdb == NULL) {
4357 ret = tdb_validate_open(tdb_path, cache_traverse_validate_fn);
4358 } else {
4359 ret = tdb_validate(wcache->tdb, cache_traverse_validate_fn);
4362 if (ret != 0) {
4363 DEBUG(10, ("winbindd_validate_cache_nobackup: validation not "
4364 "successful.\n"));
4367 TALLOC_FREE(tdb_path);
4368 err_panic_restore:
4369 DEBUG(10, ("winbindd_validate_cache_nobackup: restoring panic "
4370 "function\n"));
4371 smb_panic_fn = smb_panic;
4372 return ret;
4375 bool winbindd_cache_validate_and_initialize(void)
4377 close_winbindd_cache();
4379 if (lp_winbind_offline_logon()) {
4380 if (winbindd_validate_cache() < 0) {
4381 DEBUG(0, ("winbindd cache tdb corrupt and no backup "
4382 "could be restored.\n"));
4386 return initialize_winbindd_cache();
4389 /*********************************************************************
4390 ********************************************************************/
4392 static bool add_wbdomain_to_tdc_array( struct winbindd_domain *new_dom,
4393 struct winbindd_tdc_domain **domains,
4394 size_t *num_domains )
4396 struct winbindd_tdc_domain *list = NULL;
4397 size_t idx;
4398 int i;
4399 bool set_only = false;
4401 /* don't allow duplicates */
4403 idx = *num_domains;
4404 list = *domains;
4406 for ( i=0; i< (*num_domains); i++ ) {
4407 if ( strequal( new_dom->name, list[i].domain_name ) ) {
4408 DEBUG(10,("add_wbdomain_to_tdc_array: Found existing record for %s\n",
4409 new_dom->name));
4410 idx = i;
4411 set_only = true;
4413 break;
4417 if ( !set_only ) {
4418 if ( !*domains ) {
4419 list = talloc_array( NULL, struct winbindd_tdc_domain, 1 );
4420 idx = 0;
4421 } else {
4422 list = talloc_realloc( *domains, *domains,
4423 struct winbindd_tdc_domain,
4424 (*num_domains)+1);
4425 idx = *num_domains;
4428 ZERO_STRUCT( list[idx] );
4431 if ( !list )
4432 return false;
4434 list[idx].domain_name = talloc_strdup(list, new_dom->name);
4435 if (list[idx].domain_name == NULL) {
4436 return false;
4438 if (new_dom->alt_name != NULL) {
4439 list[idx].dns_name = talloc_strdup(list, new_dom->alt_name);
4440 if (list[idx].dns_name == NULL) {
4441 return false;
4445 if ( !is_null_sid( &new_dom->sid ) ) {
4446 sid_copy( &list[idx].sid, &new_dom->sid );
4447 } else {
4448 sid_copy(&list[idx].sid, &global_sid_NULL);
4451 if ( new_dom->domain_flags != 0x0 )
4452 list[idx].trust_flags = new_dom->domain_flags;
4454 if ( new_dom->domain_type != 0x0 )
4455 list[idx].trust_type = new_dom->domain_type;
4457 if ( new_dom->domain_trust_attribs != 0x0 )
4458 list[idx].trust_attribs = new_dom->domain_trust_attribs;
4460 if ( !set_only ) {
4461 *domains = list;
4462 *num_domains = idx + 1;
4465 return true;
4468 /*********************************************************************
4469 ********************************************************************/
4471 static TDB_DATA make_tdc_key( const char *domain_name )
4473 char *keystr = NULL;
4474 TDB_DATA key = { NULL, 0 };
4476 if ( !domain_name ) {
4477 DEBUG(5,("make_tdc_key: Keyname workgroup is NULL!\n"));
4478 return key;
4481 if (asprintf( &keystr, "TRUSTDOMCACHE/%s", domain_name ) == -1) {
4482 return key;
4484 key = string_term_tdb_data(keystr);
4486 return key;
4489 /*********************************************************************
4490 ********************************************************************/
4492 static int pack_tdc_domains( struct winbindd_tdc_domain *domains,
4493 size_t num_domains,
4494 unsigned char **buf )
4496 unsigned char *buffer = NULL;
4497 int len = 0;
4498 int buflen = 0;
4499 int i = 0;
4501 DEBUG(10,("pack_tdc_domains: Packing %d trusted domains\n",
4502 (int)num_domains));
4504 buflen = 0;
4506 again:
4507 len = 0;
4509 /* Store the number of array items first */
4510 len += tdb_pack( buffer+len, buflen-len, "d",
4511 num_domains );
4513 /* now pack each domain trust record */
4514 for ( i=0; i<num_domains; i++ ) {
4516 fstring tmp;
4518 if ( buflen > 0 ) {
4519 DEBUG(10,("pack_tdc_domains: Packing domain %s (%s)\n",
4520 domains[i].domain_name,
4521 domains[i].dns_name ? domains[i].dns_name : "UNKNOWN" ));
4524 len += tdb_pack( buffer+len, buflen-len, "fffddd",
4525 domains[i].domain_name,
4526 domains[i].dns_name ? domains[i].dns_name : "",
4527 sid_to_fstring(tmp, &domains[i].sid),
4528 domains[i].trust_flags,
4529 domains[i].trust_attribs,
4530 domains[i].trust_type );
4533 if ( buflen < len ) {
4534 SAFE_FREE(buffer);
4535 if ( (buffer = SMB_MALLOC_ARRAY(unsigned char, len)) == NULL ) {
4536 DEBUG(0,("pack_tdc_domains: failed to alloc buffer!\n"));
4537 buflen = -1;
4538 goto done;
4540 buflen = len;
4541 goto again;
4544 *buf = buffer;
4546 done:
4547 return buflen;
4550 /*********************************************************************
4551 ********************************************************************/
4553 static size_t unpack_tdc_domains( unsigned char *buf, int buflen,
4554 struct winbindd_tdc_domain **domains )
4556 fstring domain_name, dns_name, sid_string;
4557 uint32 type, attribs, flags;
4558 int num_domains;
4559 int len = 0;
4560 int i;
4561 struct winbindd_tdc_domain *list = NULL;
4563 /* get the number of domains */
4564 len += tdb_unpack( buf+len, buflen-len, "d", &num_domains);
4565 if ( len == -1 ) {
4566 DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
4567 return 0;
4570 list = talloc_array( NULL, struct winbindd_tdc_domain, num_domains );
4571 if ( !list ) {
4572 DEBUG(0,("unpack_tdc_domains: Failed to talloc() domain list!\n"));
4573 return 0;
4576 for ( i=0; i<num_domains; i++ ) {
4577 int this_len;
4579 this_len = tdb_unpack( buf+len, buflen-len, "fffddd",
4580 domain_name,
4581 dns_name,
4582 sid_string,
4583 &flags,
4584 &attribs,
4585 &type );
4587 if ( this_len == -1 ) {
4588 DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
4589 TALLOC_FREE( list );
4590 return 0;
4592 len += this_len;
4594 DEBUG(11,("unpack_tdc_domains: Unpacking domain %s (%s) "
4595 "SID %s, flags = 0x%x, attribs = 0x%x, type = 0x%x\n",
4596 domain_name, dns_name, sid_string,
4597 flags, attribs, type));
4599 list[i].domain_name = talloc_strdup( list, domain_name );
4600 list[i].dns_name = NULL;
4601 if (dns_name[0] != '\0') {
4602 list[i].dns_name = talloc_strdup(list, dns_name);
4604 if ( !string_to_sid( &(list[i].sid), sid_string ) ) {
4605 DEBUG(10,("unpack_tdc_domains: no SID for domain %s\n",
4606 domain_name));
4608 list[i].trust_flags = flags;
4609 list[i].trust_attribs = attribs;
4610 list[i].trust_type = type;
4613 *domains = list;
4615 return num_domains;
4618 /*********************************************************************
4619 ********************************************************************/
4621 static bool wcache_tdc_store_list( struct winbindd_tdc_domain *domains, size_t num_domains )
4623 TDB_DATA key = make_tdc_key( lp_workgroup() );
4624 TDB_DATA data = { NULL, 0 };
4625 int ret;
4627 if ( !key.dptr )
4628 return false;
4630 /* See if we were asked to delete the cache entry */
4632 if ( !domains ) {
4633 ret = tdb_delete( wcache->tdb, key );
4634 goto done;
4637 data.dsize = pack_tdc_domains( domains, num_domains, &data.dptr );
4639 if ( !data.dptr ) {
4640 ret = -1;
4641 goto done;
4644 ret = tdb_store( wcache->tdb, key, data, 0 );
4646 done:
4647 SAFE_FREE( data.dptr );
4648 SAFE_FREE( key.dptr );
4650 return ( ret == 0 );
4653 /*********************************************************************
4654 ********************************************************************/
4656 bool wcache_tdc_fetch_list( struct winbindd_tdc_domain **domains, size_t *num_domains )
4658 TDB_DATA key = make_tdc_key( lp_workgroup() );
4659 TDB_DATA data = { NULL, 0 };
4661 *domains = NULL;
4662 *num_domains = 0;
4664 if ( !key.dptr )
4665 return false;
4667 data = tdb_fetch( wcache->tdb, key );
4669 SAFE_FREE( key.dptr );
4671 if ( !data.dptr )
4672 return false;
4674 *num_domains = unpack_tdc_domains( data.dptr, data.dsize, domains );
4676 SAFE_FREE( data.dptr );
4678 if ( !*domains )
4679 return false;
4681 return true;
4684 /*********************************************************************
4685 ********************************************************************/
4687 bool wcache_tdc_add_domain( struct winbindd_domain *domain )
4689 struct winbindd_tdc_domain *dom_list = NULL;
4690 size_t num_domains = 0;
4691 bool ret = false;
4693 DEBUG(10,("wcache_tdc_add_domain: Adding domain %s (%s), SID %s, "
4694 "flags = 0x%x, attributes = 0x%x, type = 0x%x\n",
4695 domain->name, domain->alt_name,
4696 sid_string_dbg(&domain->sid),
4697 domain->domain_flags,
4698 domain->domain_trust_attribs,
4699 domain->domain_type));
4701 if ( !init_wcache() ) {
4702 return false;
4705 /* fetch the list */
4707 wcache_tdc_fetch_list( &dom_list, &num_domains );
4709 /* add the new domain */
4711 if ( !add_wbdomain_to_tdc_array( domain, &dom_list, &num_domains ) ) {
4712 goto done;
4715 /* pack the domain */
4717 if ( !wcache_tdc_store_list( dom_list, num_domains ) ) {
4718 goto done;
4721 /* Success */
4723 ret = true;
4724 done:
4725 TALLOC_FREE( dom_list );
4727 return ret;
4730 static struct winbindd_tdc_domain *wcache_tdc_dup_domain(
4731 TALLOC_CTX *mem_ctx, const struct winbindd_tdc_domain *src)
4733 struct winbindd_tdc_domain *dst;
4735 dst = talloc(mem_ctx, struct winbindd_tdc_domain);
4736 if (dst == NULL) {
4737 goto fail;
4739 dst->domain_name = talloc_strdup(dst, src->domain_name);
4740 if (dst->domain_name == NULL) {
4741 goto fail;
4744 dst->dns_name = NULL;
4745 if (src->dns_name != NULL) {
4746 dst->dns_name = talloc_strdup(dst, src->dns_name);
4747 if (dst->dns_name == NULL) {
4748 goto fail;
4752 sid_copy(&dst->sid, &src->sid);
4753 dst->trust_flags = src->trust_flags;
4754 dst->trust_type = src->trust_type;
4755 dst->trust_attribs = src->trust_attribs;
4756 return dst;
4757 fail:
4758 TALLOC_FREE(dst);
4759 return NULL;
4762 /*********************************************************************
4763 ********************************************************************/
4765 struct winbindd_tdc_domain * wcache_tdc_fetch_domain( TALLOC_CTX *ctx, const char *name )
4767 struct winbindd_tdc_domain *dom_list = NULL;
4768 size_t num_domains = 0;
4769 int i;
4770 struct winbindd_tdc_domain *d = NULL;
4772 DEBUG(10,("wcache_tdc_fetch_domain: Searching for domain %s\n", name));
4774 if ( !init_wcache() ) {
4775 return NULL;
4778 /* fetch the list */
4780 wcache_tdc_fetch_list( &dom_list, &num_domains );
4782 for ( i=0; i<num_domains; i++ ) {
4783 if ( strequal(name, dom_list[i].domain_name) ||
4784 strequal(name, dom_list[i].dns_name) )
4786 DEBUG(10,("wcache_tdc_fetch_domain: Found domain %s\n",
4787 name));
4789 d = wcache_tdc_dup_domain(ctx, &dom_list[i]);
4790 break;
4794 TALLOC_FREE( dom_list );
4796 return d;
4799 /*********************************************************************
4800 ********************************************************************/
4802 struct winbindd_tdc_domain*
4803 wcache_tdc_fetch_domainbysid(TALLOC_CTX *ctx,
4804 const struct dom_sid *sid)
4806 struct winbindd_tdc_domain *dom_list = NULL;
4807 size_t num_domains = 0;
4808 int i;
4809 struct winbindd_tdc_domain *d = NULL;
4811 DEBUG(10,("wcache_tdc_fetch_domainbysid: Searching for domain %s\n",
4812 sid_string_dbg(sid)));
4814 if (!init_wcache()) {
4815 return NULL;
4818 /* fetch the list */
4820 wcache_tdc_fetch_list(&dom_list, &num_domains);
4822 for (i = 0; i<num_domains; i++) {
4823 if (dom_sid_equal(sid, &(dom_list[i].sid))) {
4824 DEBUG(10, ("wcache_tdc_fetch_domainbysid: "
4825 "Found domain %s for SID %s\n",
4826 dom_list[i].domain_name,
4827 sid_string_dbg(sid)));
4829 d = wcache_tdc_dup_domain(ctx, &dom_list[i]);
4830 break;
4834 TALLOC_FREE(dom_list);
4836 return d;
4840 /*********************************************************************
4841 ********************************************************************/
4843 void wcache_tdc_clear( void )
4845 if ( !init_wcache() )
4846 return;
4848 wcache_tdc_store_list( NULL, 0 );
4850 return;
4854 /*********************************************************************
4855 ********************************************************************/
4857 static void wcache_save_user_pwinfo(struct winbindd_domain *domain,
4858 NTSTATUS status,
4859 const struct dom_sid *user_sid,
4860 const char *homedir,
4861 const char *shell,
4862 const char *gecos,
4863 uint32 gid)
4865 struct cache_entry *centry;
4866 fstring tmp;
4868 if ( (centry = centry_start(domain, status)) == NULL )
4869 return;
4871 centry_put_string( centry, homedir );
4872 centry_put_string( centry, shell );
4873 centry_put_string( centry, gecos );
4874 centry_put_uint32( centry, gid );
4876 centry_end(centry, "NSS/PWINFO/%s", sid_to_fstring(tmp, user_sid) );
4878 DEBUG(10,("wcache_save_user_pwinfo: %s\n", sid_string_dbg(user_sid) ));
4880 centry_free(centry);
4883 #ifdef HAVE_ADS
4885 NTSTATUS nss_get_info_cached( struct winbindd_domain *domain,
4886 const struct dom_sid *user_sid,
4887 TALLOC_CTX *ctx,
4888 const char **homedir, const char **shell,
4889 const char **gecos, gid_t *p_gid)
4891 struct winbind_cache *cache = get_cache(domain);
4892 struct cache_entry *centry = NULL;
4893 NTSTATUS nt_status;
4894 fstring tmp;
4896 if (!cache->tdb)
4897 goto do_query;
4899 centry = wcache_fetch(cache, domain, "NSS/PWINFO/%s",
4900 sid_to_fstring(tmp, user_sid));
4902 if (!centry)
4903 goto do_query;
4905 *homedir = centry_string( centry, ctx );
4906 *shell = centry_string( centry, ctx );
4907 *gecos = centry_string( centry, ctx );
4908 *p_gid = centry_uint32( centry );
4910 centry_free(centry);
4912 DEBUG(10,("nss_get_info_cached: [Cached] - user_sid %s\n",
4913 sid_string_dbg(user_sid)));
4915 return NT_STATUS_OK;
4917 do_query:
4919 nt_status = nss_get_info( domain->name, user_sid, ctx,
4920 homedir, shell, gecos, p_gid );
4922 DEBUG(10, ("nss_get_info returned %s\n", nt_errstr(nt_status)));
4924 if ( NT_STATUS_IS_OK(nt_status) ) {
4925 DEBUG(10, ("result:\n\thomedir = '%s'\n", *homedir));
4926 DEBUGADD(10, ("\tshell = '%s'\n", *shell));
4927 DEBUGADD(10, ("\tgecos = '%s'\n", *gecos));
4928 DEBUGADD(10, ("\tgid = '%u'\n", (unsigned int)*p_gid));
4930 wcache_save_user_pwinfo( domain, nt_status, user_sid,
4931 *homedir, *shell, *gecos, *p_gid );
4934 if ( NT_STATUS_EQUAL( nt_status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND ) ) {
4935 DEBUG(5,("nss_get_info_cached: Setting domain %s offline\n",
4936 domain->name ));
4937 set_domain_offline( domain );
4940 return nt_status;
4943 #endif
4945 /* the cache backend methods are exposed via this structure */
4946 struct winbindd_methods cache_methods = {
4947 true,
4948 query_user_list,
4949 enum_dom_groups,
4950 enum_local_groups,
4951 name_to_sid,
4952 sid_to_name,
4953 rids_to_names,
4954 query_user,
4955 lookup_usergroups,
4956 lookup_useraliases,
4957 lookup_groupmem,
4958 sequence_number,
4959 lockout_policy,
4960 password_policy,
4961 trusted_domains
4964 static bool wcache_ndr_key(TALLOC_CTX *mem_ctx, const char *domain_name,
4965 uint32_t opnum, const DATA_BLOB *req,
4966 TDB_DATA *pkey)
4968 char *key;
4969 size_t keylen;
4971 key = talloc_asprintf(mem_ctx, "NDR/%s/%d/", domain_name, (int)opnum);
4972 if (key == NULL) {
4973 return false;
4975 keylen = talloc_get_size(key) - 1;
4977 key = talloc_realloc(mem_ctx, key, char, keylen + req->length);
4978 if (key == NULL) {
4979 return false;
4981 memcpy(key + keylen, req->data, req->length);
4983 pkey->dptr = (uint8_t *)key;
4984 pkey->dsize = talloc_get_size(key);
4985 return true;
4988 static bool wcache_opnum_cacheable(uint32_t opnum)
4990 switch (opnum) {
4991 case NDR_WBINT_PING:
4992 case NDR_WBINT_QUERYSEQUENCENUMBER:
4993 case NDR_WBINT_ALLOCATEUID:
4994 case NDR_WBINT_ALLOCATEGID:
4995 case NDR_WBINT_CHECKMACHINEACCOUNT:
4996 case NDR_WBINT_CHANGEMACHINEACCOUNT:
4997 case NDR_WBINT_PINGDC:
4998 return false;
5000 return true;
5003 bool wcache_fetch_ndr(TALLOC_CTX *mem_ctx, struct winbindd_domain *domain,
5004 uint32_t opnum, const DATA_BLOB *req, DATA_BLOB *resp)
5006 TDB_DATA key, data;
5007 bool ret = false;
5009 if (!wcache_opnum_cacheable(opnum) ||
5010 is_my_own_sam_domain(domain) ||
5011 is_builtin_domain(domain)) {
5012 return false;
5015 if (wcache->tdb == NULL) {
5016 return false;
5019 if (!wcache_ndr_key(talloc_tos(), domain->name, opnum, req, &key)) {
5020 return false;
5022 data = tdb_fetch(wcache->tdb, key);
5023 TALLOC_FREE(key.dptr);
5025 if (data.dptr == NULL) {
5026 return false;
5028 if (data.dsize < 12) {
5029 goto fail;
5032 if (!is_domain_offline(domain)) {
5033 uint32_t entry_seqnum, dom_seqnum, last_check;
5034 uint64_t entry_timeout;
5036 if (!wcache_fetch_seqnum(domain->name, &dom_seqnum,
5037 &last_check)) {
5038 goto fail;
5040 entry_seqnum = IVAL(data.dptr, 0);
5041 if (entry_seqnum != dom_seqnum) {
5042 DEBUG(10, ("Entry has wrong sequence number: %d\n",
5043 (int)entry_seqnum));
5044 goto fail;
5046 entry_timeout = BVAL(data.dptr, 4);
5047 if (time(NULL) > entry_timeout) {
5048 DEBUG(10, ("Entry has timed out\n"));
5049 goto fail;
5053 resp->data = (uint8_t *)talloc_memdup(mem_ctx, data.dptr + 12,
5054 data.dsize - 12);
5055 if (resp->data == NULL) {
5056 DEBUG(10, ("talloc failed\n"));
5057 goto fail;
5059 resp->length = data.dsize - 12;
5061 ret = true;
5062 fail:
5063 SAFE_FREE(data.dptr);
5064 return ret;
5067 void wcache_store_ndr(struct winbindd_domain *domain, uint32_t opnum,
5068 const DATA_BLOB *req, const DATA_BLOB *resp)
5070 TDB_DATA key, data;
5071 uint32_t dom_seqnum, last_check;
5072 uint64_t timeout;
5074 if (!wcache_opnum_cacheable(opnum) ||
5075 is_my_own_sam_domain(domain) ||
5076 is_builtin_domain(domain)) {
5077 return;
5080 if (wcache->tdb == NULL) {
5081 return;
5084 if (!wcache_fetch_seqnum(domain->name, &dom_seqnum, &last_check)) {
5085 DEBUG(10, ("could not fetch seqnum for domain %s\n",
5086 domain->name));
5087 return;
5090 if (!wcache_ndr_key(talloc_tos(), domain->name, opnum, req, &key)) {
5091 return;
5094 timeout = time(NULL) + lp_winbind_cache_time();
5096 data.dsize = resp->length + 12;
5097 data.dptr = talloc_array(key.dptr, uint8_t, data.dsize);
5098 if (data.dptr == NULL) {
5099 goto done;
5102 SIVAL(data.dptr, 0, dom_seqnum);
5103 SBVAL(data.dptr, 4, timeout);
5104 memcpy(data.dptr + 12, resp->data, resp->length);
5106 tdb_store(wcache->tdb, key, data, 0);
5108 done:
5109 TALLOC_FREE(key.dptr);
5110 return;