winbind: Use tdb_parse_record in wcache_fetch_seqnum
[Samba.git] / source3 / winbindd / winbindd_cache.c
blob1986aad0afd4195c5e7c53f6ee66a747c0615d0e
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 char *keystr;
429 TDB_DATA key;
430 int ret;
432 if (wcache->tdb == NULL) {
433 DEBUG(10,("wcache_fetch_seqnum: tdb == NULL\n"));
434 return false;
437 keystr = talloc_asprintf(talloc_tos(), "SEQNUM/%s", domain_name);
438 if (keystr == NULL) {
439 DEBUG(10, ("talloc failed\n"));
440 return false;
442 key = string_term_tdb_data(keystr);
444 ret = tdb_parse_record(wcache->tdb, key, wcache_seqnum_parser,
445 &state);
446 TALLOC_FREE(keystr);
448 return (ret == 0);
451 static NTSTATUS fetch_cache_seqnum( struct winbindd_domain *domain, time_t now )
453 uint32 last_check, time_diff;
455 if (!wcache_fetch_seqnum(domain->name, &domain->sequence_number,
456 &last_check)) {
457 return NT_STATUS_UNSUCCESSFUL;
459 domain->last_seq_check = last_check;
461 /* have we expired? */
463 time_diff = now - domain->last_seq_check;
464 if ( time_diff > lp_winbind_cache_time() ) {
465 DEBUG(10,("fetch_cache_seqnum: timeout [%s][%u @ %u]\n",
466 domain->name, domain->sequence_number,
467 (uint32)domain->last_seq_check));
468 return NT_STATUS_UNSUCCESSFUL;
471 DEBUG(10,("fetch_cache_seqnum: success [%s][%u @ %u]\n",
472 domain->name, domain->sequence_number,
473 (uint32)domain->last_seq_check));
475 return NT_STATUS_OK;
478 bool wcache_store_seqnum(const char *domain_name, uint32_t seqnum,
479 time_t last_seq_check)
481 char *key_str;
482 uint8_t buf[8];
483 int ret;
485 if (wcache->tdb == NULL) {
486 DEBUG(10, ("wcache_store_seqnum: wcache->tdb == NULL\n"));
487 return false;
490 key_str = talloc_asprintf(talloc_tos(), "SEQNUM/%s", domain_name);
491 if (key_str == NULL) {
492 DEBUG(10, ("talloc_asprintf failed\n"));
493 return false;
496 SIVAL(buf, 0, seqnum);
497 SIVAL(buf, 4, last_seq_check);
499 ret = tdb_store_bystring(wcache->tdb, key_str,
500 make_tdb_data(buf, sizeof(buf)), TDB_REPLACE);
501 TALLOC_FREE(key_str);
502 if (ret != 0) {
503 DEBUG(10, ("tdb_store_bystring failed: %s\n",
504 tdb_errorstr(wcache->tdb)));
505 TALLOC_FREE(key_str);
506 return false;
509 DEBUG(10, ("wcache_store_seqnum: success [%s][%u @ %u]\n",
510 domain_name, seqnum, (unsigned)last_seq_check));
512 return true;
515 static bool store_cache_seqnum( struct winbindd_domain *domain )
517 return wcache_store_seqnum(domain->name, domain->sequence_number,
518 domain->last_seq_check);
522 refresh the domain sequence number. If force is true
523 then always refresh it, no matter how recently we fetched it
526 static void refresh_sequence_number(struct winbindd_domain *domain, bool force)
528 NTSTATUS status;
529 unsigned time_diff;
530 time_t t = time(NULL);
531 unsigned cache_time = lp_winbind_cache_time();
533 if (is_domain_offline(domain)) {
534 return;
537 get_cache( domain );
539 #if 0 /* JERRY -- disable as the default cache time is now 5 minutes */
540 /* trying to reconnect is expensive, don't do it too often */
541 if (domain->sequence_number == DOM_SEQUENCE_NONE) {
542 cache_time *= 8;
544 #endif
546 time_diff = t - domain->last_seq_check;
548 /* see if we have to refetch the domain sequence number */
549 if (!force && (time_diff < cache_time) &&
550 (domain->sequence_number != DOM_SEQUENCE_NONE) &&
551 NT_STATUS_IS_OK(domain->last_status)) {
552 DEBUG(10, ("refresh_sequence_number: %s time ok\n", domain->name));
553 goto done;
556 /* try to get the sequence number from the tdb cache first */
557 /* this will update the timestamp as well */
559 status = fetch_cache_seqnum( domain, t );
560 if (NT_STATUS_IS_OK(status) &&
561 (domain->sequence_number != DOM_SEQUENCE_NONE) &&
562 NT_STATUS_IS_OK(domain->last_status)) {
563 goto done;
566 /* important! make sure that we know if this is a native
567 mode domain or not. And that we can contact it. */
569 if ( winbindd_can_contact_domain( domain ) ) {
570 status = domain->backend->sequence_number(domain,
571 &domain->sequence_number);
572 } else {
573 /* just use the current time */
574 status = NT_STATUS_OK;
575 domain->sequence_number = time(NULL);
579 /* the above call could have set our domain->backend to NULL when
580 * coming from offline to online mode, make sure to reinitialize the
581 * backend - Guenther */
582 get_cache( domain );
584 if (!NT_STATUS_IS_OK(status)) {
585 DEBUG(10,("refresh_sequence_number: failed with %s\n", nt_errstr(status)));
586 domain->sequence_number = DOM_SEQUENCE_NONE;
589 domain->last_status = status;
590 domain->last_seq_check = time(NULL);
592 /* save the new sequence number in the cache */
593 store_cache_seqnum( domain );
595 done:
596 DEBUG(10, ("refresh_sequence_number: %s seq number is now %d\n",
597 domain->name, domain->sequence_number));
599 return;
603 decide if a cache entry has expired
605 static bool centry_expired(struct winbindd_domain *domain, const char *keystr, struct cache_entry *centry)
607 /* If we've been told to be offline - stay in that state... */
608 if (lp_winbind_offline_logon() && global_winbindd_offline_state) {
609 DEBUG(10,("centry_expired: Key %s for domain %s valid as winbindd is globally offline.\n",
610 keystr, domain->name ));
611 return false;
614 /* when the domain is offline return the cached entry.
615 * This deals with transient offline states... */
617 if (!domain->online) {
618 DEBUG(10,("centry_expired: Key %s for domain %s valid as domain is offline.\n",
619 keystr, domain->name ));
620 return false;
623 /* if the server is OK and our cache entry came from when it was down then
624 the entry is invalid */
625 if ((domain->sequence_number != DOM_SEQUENCE_NONE) &&
626 (centry->sequence_number == DOM_SEQUENCE_NONE)) {
627 DEBUG(10,("centry_expired: Key %s for domain %s invalid sequence.\n",
628 keystr, domain->name ));
629 return true;
632 /* if the server is down or the cache entry is not older than the
633 current sequence number or it did not timeout then it is OK */
634 if (wcache_server_down(domain)
635 || ((centry->sequence_number == domain->sequence_number)
636 && (centry->timeout > time(NULL)))) {
637 DEBUG(10,("centry_expired: Key %s for domain %s is good.\n",
638 keystr, domain->name ));
639 return false;
642 DEBUG(10,("centry_expired: Key %s for domain %s expired\n",
643 keystr, domain->name ));
645 /* it's expired */
646 return true;
649 static struct cache_entry *wcache_fetch_raw(char *kstr)
651 TDB_DATA data;
652 struct cache_entry *centry;
653 TDB_DATA key;
655 key = string_tdb_data(kstr);
656 data = tdb_fetch(wcache->tdb, key);
657 if (!data.dptr) {
658 /* a cache miss */
659 return NULL;
662 centry = SMB_XMALLOC_P(struct cache_entry);
663 centry->data = (unsigned char *)data.dptr;
664 centry->len = data.dsize;
665 centry->ofs = 0;
667 if (centry->len < 16) {
668 /* huh? corrupt cache? */
669 DEBUG(10,("wcache_fetch_raw: Corrupt cache for key %s "
670 "(len < 16)?\n", kstr));
671 centry_free(centry);
672 return NULL;
675 centry->status = centry_ntstatus(centry);
676 centry->sequence_number = centry_uint32(centry);
677 centry->timeout = centry_uint64_t(centry);
679 return centry;
682 static bool is_my_own_sam_domain(struct winbindd_domain *domain)
684 if (strequal(domain->name, get_global_sam_name()) &&
685 sid_check_is_our_sam(&domain->sid)) {
686 return true;
689 return false;
692 static bool is_builtin_domain(struct winbindd_domain *domain)
694 if (strequal(domain->name, "BUILTIN") &&
695 sid_check_is_builtin(&domain->sid)) {
696 return true;
699 return false;
703 fetch an entry from the cache, with a varargs key. auto-fetch the sequence
704 number and return status
706 static struct cache_entry *wcache_fetch(struct winbind_cache *cache,
707 struct winbindd_domain *domain,
708 const char *format, ...) PRINTF_ATTRIBUTE(3,4);
709 static struct cache_entry *wcache_fetch(struct winbind_cache *cache,
710 struct winbindd_domain *domain,
711 const char *format, ...)
713 va_list ap;
714 char *kstr;
715 struct cache_entry *centry;
717 if (!winbindd_use_cache() ||
718 is_my_own_sam_domain(domain) ||
719 is_builtin_domain(domain)) {
720 return NULL;
723 refresh_sequence_number(domain, false);
725 va_start(ap, format);
726 smb_xvasprintf(&kstr, format, ap);
727 va_end(ap);
729 centry = wcache_fetch_raw(kstr);
730 if (centry == NULL) {
731 free(kstr);
732 return NULL;
735 if (centry_expired(domain, kstr, centry)) {
737 DEBUG(10,("wcache_fetch: entry %s expired for domain %s\n",
738 kstr, domain->name ));
740 centry_free(centry);
741 free(kstr);
742 return NULL;
745 DEBUG(10,("wcache_fetch: returning entry %s for domain %s\n",
746 kstr, domain->name ));
748 free(kstr);
749 return centry;
752 static void wcache_delete(const char *format, ...) PRINTF_ATTRIBUTE(1,2);
753 static void wcache_delete(const char *format, ...)
755 va_list ap;
756 char *kstr;
757 TDB_DATA key;
759 va_start(ap, format);
760 smb_xvasprintf(&kstr, format, ap);
761 va_end(ap);
763 key = string_tdb_data(kstr);
765 tdb_delete(wcache->tdb, key);
766 free(kstr);
770 make sure we have at least len bytes available in a centry
772 static void centry_expand(struct cache_entry *centry, uint32 len)
774 if (centry->len - centry->ofs >= len)
775 return;
776 centry->len *= 2;
777 centry->data = SMB_REALLOC_ARRAY(centry->data, unsigned char,
778 centry->len);
779 if (!centry->data) {
780 DEBUG(0,("out of memory: needed %d bytes in centry_expand\n", centry->len));
781 smb_panic_fn("out of memory in centry_expand");
786 push a uint64_t into a centry
788 static void centry_put_uint64_t(struct cache_entry *centry, uint64_t v)
790 centry_expand(centry, 8);
791 SBVAL(centry->data, centry->ofs, v);
792 centry->ofs += 8;
796 push a uint32 into a centry
798 static void centry_put_uint32(struct cache_entry *centry, uint32 v)
800 centry_expand(centry, 4);
801 SIVAL(centry->data, centry->ofs, v);
802 centry->ofs += 4;
806 push a uint16 into a centry
808 static void centry_put_uint16(struct cache_entry *centry, uint16 v)
810 centry_expand(centry, 2);
811 SSVAL(centry->data, centry->ofs, v);
812 centry->ofs += 2;
816 push a uint8 into a centry
818 static void centry_put_uint8(struct cache_entry *centry, uint8 v)
820 centry_expand(centry, 1);
821 SCVAL(centry->data, centry->ofs, v);
822 centry->ofs += 1;
826 push a string into a centry
828 static void centry_put_string(struct cache_entry *centry, const char *s)
830 int len;
832 if (!s) {
833 /* null strings are marked as len 0xFFFF */
834 centry_put_uint8(centry, 0xFF);
835 return;
838 len = strlen(s);
839 /* can't handle more than 254 char strings. Truncating is probably best */
840 if (len > 254) {
841 DEBUG(10,("centry_put_string: truncating len (%d) to: 254\n", len));
842 len = 254;
844 centry_put_uint8(centry, len);
845 centry_expand(centry, len);
846 memcpy(centry->data + centry->ofs, s, len);
847 centry->ofs += len;
851 push a 16 byte hash into a centry - treat as 16 byte string.
853 static void centry_put_hash16(struct cache_entry *centry, const uint8 val[16])
855 centry_put_uint8(centry, 16);
856 centry_expand(centry, 16);
857 memcpy(centry->data + centry->ofs, val, 16);
858 centry->ofs += 16;
861 static void centry_put_sid(struct cache_entry *centry, const struct dom_sid *sid)
863 fstring sid_string;
864 centry_put_string(centry, sid_to_fstring(sid_string, sid));
869 put NTSTATUS into a centry
871 static void centry_put_ntstatus(struct cache_entry *centry, NTSTATUS status)
873 uint32 status_value = NT_STATUS_V(status);
874 centry_put_uint32(centry, status_value);
879 push a NTTIME into a centry
881 static void centry_put_nttime(struct cache_entry *centry, NTTIME nt)
883 centry_expand(centry, 8);
884 SIVAL(centry->data, centry->ofs, nt & 0xFFFFFFFF);
885 centry->ofs += 4;
886 SIVAL(centry->data, centry->ofs, nt >> 32);
887 centry->ofs += 4;
891 push a time_t into a centry - use a 64 bit size.
892 NTTIME here is being used as a convenient 64-bit size.
894 static void centry_put_time(struct cache_entry *centry, time_t t)
896 NTTIME nt = (NTTIME)t;
897 centry_put_nttime(centry, nt);
901 start a centry for output. When finished, call centry_end()
903 static struct cache_entry *centry_start(struct winbindd_domain *domain,
904 NTSTATUS status)
906 struct cache_entry *centry;
908 if (!wcache->tdb)
909 return NULL;
911 centry = SMB_XMALLOC_P(struct cache_entry);
913 centry->len = 8192; /* reasonable default */
914 centry->data = SMB_XMALLOC_ARRAY(uint8, centry->len);
915 centry->ofs = 0;
916 centry->sequence_number = domain->sequence_number;
917 centry->timeout = lp_winbind_cache_time() + time(NULL);
918 centry_put_ntstatus(centry, status);
919 centry_put_uint32(centry, centry->sequence_number);
920 centry_put_uint64_t(centry, centry->timeout);
921 return centry;
925 finish a centry and write it to the tdb
927 static void centry_end(struct cache_entry *centry, const char *format, ...) PRINTF_ATTRIBUTE(2,3);
928 static void centry_end(struct cache_entry *centry, const char *format, ...)
930 va_list ap;
931 char *kstr;
932 TDB_DATA key, data;
934 if (!winbindd_use_cache()) {
935 return;
938 va_start(ap, format);
939 smb_xvasprintf(&kstr, format, ap);
940 va_end(ap);
942 key = string_tdb_data(kstr);
943 data.dptr = centry->data;
944 data.dsize = centry->ofs;
946 tdb_store(wcache->tdb, key, data, TDB_REPLACE);
947 free(kstr);
950 static void wcache_save_name_to_sid(struct winbindd_domain *domain,
951 NTSTATUS status, const char *domain_name,
952 const char *name, const struct dom_sid *sid,
953 enum lsa_SidType type)
955 struct cache_entry *centry;
956 fstring uname;
958 centry = centry_start(domain, status);
959 if (!centry)
960 return;
962 if ((domain_name == NULL) || (domain_name[0] == '\0')) {
963 struct winbindd_domain *mydomain =
964 find_domain_from_sid_noinit(sid);
965 if (mydomain != NULL) {
966 domain_name = mydomain->name;
970 centry_put_uint32(centry, type);
971 centry_put_sid(centry, sid);
972 fstrcpy(uname, name);
973 (void)strupper_m(uname);
974 centry_end(centry, "NS/%s/%s", domain_name, uname);
975 DEBUG(10,("wcache_save_name_to_sid: %s\\%s -> %s (%s)\n", domain_name,
976 uname, sid_string_dbg(sid), nt_errstr(status)));
977 centry_free(centry);
980 static void wcache_save_sid_to_name(struct winbindd_domain *domain, NTSTATUS status,
981 const struct dom_sid *sid, const char *domain_name, const char *name, enum lsa_SidType type)
983 struct cache_entry *centry;
984 fstring sid_string;
986 centry = centry_start(domain, status);
987 if (!centry)
988 return;
990 if ((domain_name == NULL) || (domain_name[0] == '\0')) {
991 struct winbindd_domain *mydomain =
992 find_domain_from_sid_noinit(sid);
993 if (mydomain != NULL) {
994 domain_name = mydomain->name;
998 if (NT_STATUS_IS_OK(status)) {
999 centry_put_uint32(centry, type);
1000 centry_put_string(centry, domain_name);
1001 centry_put_string(centry, name);
1004 centry_end(centry, "SN/%s", sid_to_fstring(sid_string, sid));
1005 DEBUG(10,("wcache_save_sid_to_name: %s -> %s\\%s (%s)\n", sid_string,
1006 domain_name, name, nt_errstr(status)));
1007 centry_free(centry);
1011 static void wcache_save_user(struct winbindd_domain *domain, NTSTATUS status,
1012 struct wbint_userinfo *info)
1014 struct cache_entry *centry;
1015 fstring sid_string;
1017 if (is_null_sid(&info->user_sid)) {
1018 return;
1021 centry = centry_start(domain, status);
1022 if (!centry)
1023 return;
1024 centry_put_string(centry, info->acct_name);
1025 centry_put_string(centry, info->full_name);
1026 centry_put_string(centry, info->homedir);
1027 centry_put_string(centry, info->shell);
1028 centry_put_uint32(centry, info->primary_gid);
1029 centry_put_sid(centry, &info->user_sid);
1030 centry_put_sid(centry, &info->group_sid);
1031 centry_end(centry, "U/%s", sid_to_fstring(sid_string,
1032 &info->user_sid));
1033 DEBUG(10,("wcache_save_user: %s (acct_name %s)\n", sid_string, info->acct_name));
1034 centry_free(centry);
1037 static void wcache_save_lockout_policy(struct winbindd_domain *domain,
1038 NTSTATUS status,
1039 struct samr_DomInfo12 *lockout_policy)
1041 struct cache_entry *centry;
1043 centry = centry_start(domain, status);
1044 if (!centry)
1045 return;
1047 centry_put_nttime(centry, lockout_policy->lockout_duration);
1048 centry_put_nttime(centry, lockout_policy->lockout_window);
1049 centry_put_uint16(centry, lockout_policy->lockout_threshold);
1051 centry_end(centry, "LOC_POL/%s", domain->name);
1053 DEBUG(10,("wcache_save_lockout_policy: %s\n", domain->name));
1055 centry_free(centry);
1060 static void wcache_save_password_policy(struct winbindd_domain *domain,
1061 NTSTATUS status,
1062 struct samr_DomInfo1 *policy)
1064 struct cache_entry *centry;
1066 centry = centry_start(domain, status);
1067 if (!centry)
1068 return;
1070 centry_put_uint16(centry, policy->min_password_length);
1071 centry_put_uint16(centry, policy->password_history_length);
1072 centry_put_uint32(centry, policy->password_properties);
1073 centry_put_nttime(centry, policy->max_password_age);
1074 centry_put_nttime(centry, policy->min_password_age);
1076 centry_end(centry, "PWD_POL/%s", domain->name);
1078 DEBUG(10,("wcache_save_password_policy: %s\n", domain->name));
1080 centry_free(centry);
1083 /***************************************************************************
1084 ***************************************************************************/
1086 static void wcache_save_username_alias(struct winbindd_domain *domain,
1087 NTSTATUS status,
1088 const char *name, const char *alias)
1090 struct cache_entry *centry;
1091 fstring uname;
1093 if ( (centry = centry_start(domain, status)) == NULL )
1094 return;
1096 centry_put_string( centry, alias );
1098 fstrcpy(uname, name);
1099 (void)strupper_m(uname);
1100 centry_end(centry, "NSS/NA/%s", uname);
1102 DEBUG(10,("wcache_save_username_alias: %s -> %s\n", name, alias ));
1104 centry_free(centry);
1107 static void wcache_save_alias_username(struct winbindd_domain *domain,
1108 NTSTATUS status,
1109 const char *alias, const char *name)
1111 struct cache_entry *centry;
1112 fstring uname;
1114 if ( (centry = centry_start(domain, status)) == NULL )
1115 return;
1117 centry_put_string( centry, name );
1119 fstrcpy(uname, alias);
1120 (void)strupper_m(uname);
1121 centry_end(centry, "NSS/AN/%s", uname);
1123 DEBUG(10,("wcache_save_alias_username: %s -> %s\n", alias, name ));
1125 centry_free(centry);
1128 /***************************************************************************
1129 ***************************************************************************/
1131 NTSTATUS resolve_username_to_alias( TALLOC_CTX *mem_ctx,
1132 struct winbindd_domain *domain,
1133 const char *name, char **alias )
1135 struct winbind_cache *cache = get_cache(domain);
1136 struct cache_entry *centry = NULL;
1137 NTSTATUS status;
1138 char *upper_name;
1140 if ( domain->internal )
1141 return NT_STATUS_NOT_SUPPORTED;
1143 if (!cache->tdb)
1144 goto do_query;
1146 upper_name = talloc_strdup(mem_ctx, name);
1147 if (upper_name == NULL) {
1148 return NT_STATUS_NO_MEMORY;
1150 if (!strupper_m(upper_name)) {
1151 talloc_free(upper_name);
1152 return NT_STATUS_INVALID_PARAMETER;
1155 centry = wcache_fetch(cache, domain, "NSS/NA/%s", upper_name);
1157 talloc_free(upper_name);
1159 if (!centry)
1160 goto do_query;
1162 status = centry->status;
1164 if (!NT_STATUS_IS_OK(status)) {
1165 centry_free(centry);
1166 return status;
1169 *alias = centry_string( centry, mem_ctx );
1171 centry_free(centry);
1173 DEBUG(10,("resolve_username_to_alias: [Cached] - mapped %s to %s\n",
1174 name, *alias ? *alias : "(none)"));
1176 return (*alias) ? NT_STATUS_OK : NT_STATUS_OBJECT_NAME_NOT_FOUND;
1178 do_query:
1180 /* If its not in cache and we are offline, then fail */
1182 if ( get_global_winbindd_state_offline() || !domain->online ) {
1183 DEBUG(8,("resolve_username_to_alias: rejecting query "
1184 "in offline mode\n"));
1185 return NT_STATUS_NOT_FOUND;
1188 status = nss_map_to_alias( mem_ctx, domain->name, name, alias );
1190 if ( NT_STATUS_IS_OK( status ) ) {
1191 wcache_save_username_alias(domain, status, name, *alias);
1194 if ( NT_STATUS_EQUAL( status, NT_STATUS_NONE_MAPPED ) ) {
1195 wcache_save_username_alias(domain, status, name, "(NULL)");
1198 DEBUG(5,("resolve_username_to_alias: backend query returned %s\n",
1199 nt_errstr(status)));
1201 if ( NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ) {
1202 set_domain_offline( domain );
1205 return status;
1208 /***************************************************************************
1209 ***************************************************************************/
1211 NTSTATUS resolve_alias_to_username( TALLOC_CTX *mem_ctx,
1212 struct winbindd_domain *domain,
1213 const char *alias, char **name )
1215 struct winbind_cache *cache = get_cache(domain);
1216 struct cache_entry *centry = NULL;
1217 NTSTATUS status;
1218 char *upper_name;
1220 if ( domain->internal )
1221 return NT_STATUS_NOT_SUPPORTED;
1223 if (!cache->tdb)
1224 goto do_query;
1226 upper_name = talloc_strdup(mem_ctx, alias);
1227 if (upper_name == NULL) {
1228 return NT_STATUS_NO_MEMORY;
1230 if (!strupper_m(upper_name)) {
1231 talloc_free(upper_name);
1232 return NT_STATUS_INVALID_PARAMETER;
1235 centry = wcache_fetch(cache, domain, "NSS/AN/%s", upper_name);
1237 talloc_free(upper_name);
1239 if (!centry)
1240 goto do_query;
1242 status = centry->status;
1244 if (!NT_STATUS_IS_OK(status)) {
1245 centry_free(centry);
1246 return status;
1249 *name = centry_string( centry, mem_ctx );
1251 centry_free(centry);
1253 DEBUG(10,("resolve_alias_to_username: [Cached] - mapped %s to %s\n",
1254 alias, *name ? *name : "(none)"));
1256 return (*name) ? NT_STATUS_OK : NT_STATUS_OBJECT_NAME_NOT_FOUND;
1258 do_query:
1260 /* If its not in cache and we are offline, then fail */
1262 if ( get_global_winbindd_state_offline() || !domain->online ) {
1263 DEBUG(8,("resolve_alias_to_username: rejecting query "
1264 "in offline mode\n"));
1265 return NT_STATUS_NOT_FOUND;
1268 /* an alias cannot contain a domain prefix or '@' */
1270 if (strchr(alias, '\\') || strchr(alias, '@')) {
1271 DEBUG(10,("resolve_alias_to_username: skipping fully "
1272 "qualified name %s\n", alias));
1273 return NT_STATUS_OBJECT_NAME_INVALID;
1276 status = nss_map_from_alias( mem_ctx, domain->name, alias, name );
1278 if ( NT_STATUS_IS_OK( status ) ) {
1279 wcache_save_alias_username( domain, status, alias, *name );
1282 if (NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) {
1283 wcache_save_alias_username(domain, status, alias, "(NULL)");
1286 DEBUG(5,("resolve_alias_to_username: backend query returned %s\n",
1287 nt_errstr(status)));
1289 if ( NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ) {
1290 set_domain_offline( domain );
1293 return status;
1296 NTSTATUS wcache_cached_creds_exist(struct winbindd_domain *domain, const struct dom_sid *sid)
1298 struct winbind_cache *cache = get_cache(domain);
1299 TDB_DATA data;
1300 fstring key_str, tmp;
1301 uint32 rid;
1303 if (!cache->tdb) {
1304 return NT_STATUS_INTERNAL_DB_ERROR;
1307 if (is_null_sid(sid)) {
1308 return NT_STATUS_INVALID_SID;
1311 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1312 return NT_STATUS_INVALID_SID;
1315 fstr_sprintf(key_str, "CRED/%s", sid_to_fstring(tmp, sid));
1317 data = tdb_fetch(cache->tdb, string_tdb_data(key_str));
1318 if (!data.dptr) {
1319 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1322 SAFE_FREE(data.dptr);
1323 return NT_STATUS_OK;
1326 /* Lookup creds for a SID - copes with old (unsalted) creds as well
1327 as new salted ones. */
1329 NTSTATUS wcache_get_creds(struct winbindd_domain *domain,
1330 TALLOC_CTX *mem_ctx,
1331 const struct dom_sid *sid,
1332 const uint8 **cached_nt_pass,
1333 const uint8 **cached_salt)
1335 struct winbind_cache *cache = get_cache(domain);
1336 struct cache_entry *centry = NULL;
1337 NTSTATUS status;
1338 uint32 rid;
1339 fstring tmp;
1341 if (!cache->tdb) {
1342 return NT_STATUS_INTERNAL_DB_ERROR;
1345 if (is_null_sid(sid)) {
1346 return NT_STATUS_INVALID_SID;
1349 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1350 return NT_STATUS_INVALID_SID;
1353 /* Try and get a salted cred first. If we can't
1354 fall back to an unsalted cred. */
1356 centry = wcache_fetch(cache, domain, "CRED/%s",
1357 sid_to_fstring(tmp, sid));
1358 if (!centry) {
1359 DEBUG(10,("wcache_get_creds: entry for [CRED/%s] not found\n",
1360 sid_string_dbg(sid)));
1361 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1365 * We don't use the time element at this moment,
1366 * but we have to consume it, so that we don't
1367 * neet to change the disk format of the cache.
1369 (void)centry_time(centry);
1371 /* In the salted case this isn't actually the nt_hash itself,
1372 but the MD5 of the salt + nt_hash. Let the caller
1373 sort this out. It can tell as we only return the cached_salt
1374 if we are returning a salted cred. */
1376 *cached_nt_pass = (const uint8 *)centry_hash16(centry, mem_ctx);
1377 if (*cached_nt_pass == NULL) {
1378 fstring sidstr;
1380 sid_to_fstring(sidstr, sid);
1382 /* Bad (old) cred cache. Delete and pretend we
1383 don't have it. */
1384 DEBUG(0,("wcache_get_creds: bad entry for [CRED/%s] - deleting\n",
1385 sidstr));
1386 wcache_delete("CRED/%s", sidstr);
1387 centry_free(centry);
1388 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1391 /* We only have 17 bytes more data in the salted cred case. */
1392 if (centry->len - centry->ofs == 17) {
1393 *cached_salt = (const uint8 *)centry_hash16(centry, mem_ctx);
1394 } else {
1395 *cached_salt = NULL;
1398 dump_data_pw("cached_nt_pass", *cached_nt_pass, NT_HASH_LEN);
1399 if (*cached_salt) {
1400 dump_data_pw("cached_salt", *cached_salt, NT_HASH_LEN);
1403 status = centry->status;
1405 DEBUG(10,("wcache_get_creds: [Cached] - cached creds for user %s status: %s\n",
1406 sid_string_dbg(sid), nt_errstr(status) ));
1408 centry_free(centry);
1409 return status;
1412 /* Store creds for a SID - only writes out new salted ones. */
1414 NTSTATUS wcache_save_creds(struct winbindd_domain *domain,
1415 const struct dom_sid *sid,
1416 const uint8 nt_pass[NT_HASH_LEN])
1418 struct cache_entry *centry;
1419 fstring sid_string;
1420 uint32 rid;
1421 uint8 cred_salt[NT_HASH_LEN];
1422 uint8 salted_hash[NT_HASH_LEN];
1424 if (is_null_sid(sid)) {
1425 return NT_STATUS_INVALID_SID;
1428 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1429 return NT_STATUS_INVALID_SID;
1432 centry = centry_start(domain, NT_STATUS_OK);
1433 if (!centry) {
1434 return NT_STATUS_INTERNAL_DB_ERROR;
1437 dump_data_pw("nt_pass", nt_pass, NT_HASH_LEN);
1439 centry_put_time(centry, time(NULL));
1441 /* Create a salt and then salt the hash. */
1442 generate_random_buffer(cred_salt, NT_HASH_LEN);
1443 E_md5hash(cred_salt, nt_pass, salted_hash);
1445 centry_put_hash16(centry, salted_hash);
1446 centry_put_hash16(centry, cred_salt);
1447 centry_end(centry, "CRED/%s", sid_to_fstring(sid_string, sid));
1449 DEBUG(10,("wcache_save_creds: %s\n", sid_string));
1451 centry_free(centry);
1453 return NT_STATUS_OK;
1457 /* Query display info. This is the basic user list fn */
1458 static NTSTATUS query_user_list(struct winbindd_domain *domain,
1459 TALLOC_CTX *mem_ctx,
1460 uint32 *num_entries,
1461 struct wbint_userinfo **info)
1463 struct winbind_cache *cache = get_cache(domain);
1464 struct cache_entry *centry = NULL;
1465 NTSTATUS status;
1466 unsigned int i, retry;
1467 bool old_status = domain->online;
1469 if (!cache->tdb)
1470 goto do_query;
1472 centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
1473 if (!centry)
1474 goto do_query;
1476 do_fetch_cache:
1477 *num_entries = centry_uint32(centry);
1479 if (*num_entries == 0)
1480 goto do_cached;
1482 (*info) = talloc_array(mem_ctx, struct wbint_userinfo, *num_entries);
1483 if (! (*info)) {
1484 smb_panic_fn("query_user_list out of memory");
1486 for (i=0; i<(*num_entries); i++) {
1487 (*info)[i].acct_name = centry_string(centry, mem_ctx);
1488 (*info)[i].full_name = centry_string(centry, mem_ctx);
1489 (*info)[i].homedir = centry_string(centry, mem_ctx);
1490 (*info)[i].shell = centry_string(centry, mem_ctx);
1491 centry_sid(centry, &(*info)[i].user_sid);
1492 centry_sid(centry, &(*info)[i].group_sid);
1495 do_cached:
1496 status = centry->status;
1498 DEBUG(10,("query_user_list: [Cached] - cached list for domain %s status: %s\n",
1499 domain->name, nt_errstr(status) ));
1501 centry_free(centry);
1502 return status;
1504 do_query:
1505 *num_entries = 0;
1506 *info = NULL;
1508 /* Return status value returned by seq number check */
1510 if (!NT_STATUS_IS_OK(domain->last_status))
1511 return domain->last_status;
1513 /* Put the query_user_list() in a retry loop. There appears to be
1514 * some bug either with Windows 2000 or Samba's handling of large
1515 * rpc replies. This manifests itself as sudden disconnection
1516 * at a random point in the enumeration of a large (60k) user list.
1517 * The retry loop simply tries the operation again. )-: It's not
1518 * pretty but an acceptable workaround until we work out what the
1519 * real problem is. */
1521 retry = 0;
1522 do {
1524 DEBUG(10,("query_user_list: [Cached] - doing backend query for list for domain %s\n",
1525 domain->name ));
1527 status = domain->backend->query_user_list(domain, mem_ctx, num_entries, info);
1528 if (!NT_STATUS_IS_OK(status)) {
1529 DEBUG(3, ("query_user_list: returned 0x%08x, "
1530 "retrying\n", NT_STATUS_V(status)));
1532 if (NT_STATUS_EQUAL(status, NT_STATUS_UNSUCCESSFUL)) {
1533 DEBUG(3, ("query_user_list: flushing "
1534 "connection cache\n"));
1535 invalidate_cm_connection(domain);
1537 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1538 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1539 if (!domain->internal && old_status) {
1540 set_domain_offline(domain);
1542 /* store partial response. */
1543 if (*num_entries > 0) {
1545 * humm, what about the status used for cache?
1546 * Should it be NT_STATUS_OK?
1548 break;
1551 * domain is offline now, and there is no user entries,
1552 * try to fetch from cache again.
1554 if (cache->tdb && !domain->online && !domain->internal && old_status) {
1555 centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
1556 /* partial response... */
1557 if (!centry) {
1558 goto skip_save;
1559 } else {
1560 goto do_fetch_cache;
1562 } else {
1563 goto skip_save;
1567 } while (NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_UNSUCCESSFUL) &&
1568 (retry++ < 5));
1570 /* and save it */
1571 refresh_sequence_number(domain, false);
1572 if (!NT_STATUS_IS_OK(status)) {
1573 return status;
1575 centry = centry_start(domain, status);
1576 if (!centry)
1577 goto skip_save;
1578 centry_put_uint32(centry, *num_entries);
1579 for (i=0; i<(*num_entries); i++) {
1580 centry_put_string(centry, (*info)[i].acct_name);
1581 centry_put_string(centry, (*info)[i].full_name);
1582 centry_put_string(centry, (*info)[i].homedir);
1583 centry_put_string(centry, (*info)[i].shell);
1584 centry_put_sid(centry, &(*info)[i].user_sid);
1585 centry_put_sid(centry, &(*info)[i].group_sid);
1586 if (domain->backend && domain->backend->consistent) {
1587 /* when the backend is consistent we can pre-prime some mappings */
1588 wcache_save_name_to_sid(domain, NT_STATUS_OK,
1589 domain->name,
1590 (*info)[i].acct_name,
1591 &(*info)[i].user_sid,
1592 SID_NAME_USER);
1593 wcache_save_sid_to_name(domain, NT_STATUS_OK,
1594 &(*info)[i].user_sid,
1595 domain->name,
1596 (*info)[i].acct_name,
1597 SID_NAME_USER);
1598 wcache_save_user(domain, NT_STATUS_OK, &(*info)[i]);
1601 centry_end(centry, "UL/%s", domain->name);
1602 centry_free(centry);
1604 skip_save:
1605 return status;
1608 /* list all domain groups */
1609 static NTSTATUS enum_dom_groups(struct winbindd_domain *domain,
1610 TALLOC_CTX *mem_ctx,
1611 uint32 *num_entries,
1612 struct wb_acct_info **info)
1614 struct winbind_cache *cache = get_cache(domain);
1615 struct cache_entry *centry = NULL;
1616 NTSTATUS status;
1617 unsigned int i;
1618 bool old_status;
1620 old_status = domain->online;
1621 if (!cache->tdb)
1622 goto do_query;
1624 centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
1625 if (!centry)
1626 goto do_query;
1628 do_fetch_cache:
1629 *num_entries = centry_uint32(centry);
1631 if (*num_entries == 0)
1632 goto do_cached;
1634 (*info) = talloc_array(mem_ctx, struct wb_acct_info, *num_entries);
1635 if (! (*info)) {
1636 smb_panic_fn("enum_dom_groups out of memory");
1638 for (i=0; i<(*num_entries); i++) {
1639 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1640 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1641 (*info)[i].rid = centry_uint32(centry);
1644 do_cached:
1645 status = centry->status;
1647 DEBUG(10,("enum_dom_groups: [Cached] - cached list for domain %s status: %s\n",
1648 domain->name, nt_errstr(status) ));
1650 centry_free(centry);
1651 return status;
1653 do_query:
1654 *num_entries = 0;
1655 *info = NULL;
1657 /* Return status value returned by seq number check */
1659 if (!NT_STATUS_IS_OK(domain->last_status))
1660 return domain->last_status;
1662 DEBUG(10,("enum_dom_groups: [Cached] - doing backend query for list for domain %s\n",
1663 domain->name ));
1665 status = domain->backend->enum_dom_groups(domain, mem_ctx, num_entries, info);
1667 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1668 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1669 if (!domain->internal && old_status) {
1670 set_domain_offline(domain);
1672 if (cache->tdb &&
1673 !domain->online &&
1674 !domain->internal &&
1675 old_status) {
1676 centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
1677 if (centry) {
1678 goto do_fetch_cache;
1682 /* and save it */
1683 refresh_sequence_number(domain, false);
1684 if (!NT_STATUS_IS_OK(status)) {
1685 return status;
1687 centry = centry_start(domain, status);
1688 if (!centry)
1689 goto skip_save;
1690 centry_put_uint32(centry, *num_entries);
1691 for (i=0; i<(*num_entries); i++) {
1692 centry_put_string(centry, (*info)[i].acct_name);
1693 centry_put_string(centry, (*info)[i].acct_desc);
1694 centry_put_uint32(centry, (*info)[i].rid);
1696 centry_end(centry, "GL/%s/domain", domain->name);
1697 centry_free(centry);
1699 skip_save:
1700 return status;
1703 /* list all domain groups */
1704 static NTSTATUS enum_local_groups(struct winbindd_domain *domain,
1705 TALLOC_CTX *mem_ctx,
1706 uint32 *num_entries,
1707 struct wb_acct_info **info)
1709 struct winbind_cache *cache = get_cache(domain);
1710 struct cache_entry *centry = NULL;
1711 NTSTATUS status;
1712 unsigned int i;
1713 bool old_status;
1715 old_status = domain->online;
1716 if (!cache->tdb)
1717 goto do_query;
1719 centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
1720 if (!centry)
1721 goto do_query;
1723 do_fetch_cache:
1724 *num_entries = centry_uint32(centry);
1726 if (*num_entries == 0)
1727 goto do_cached;
1729 (*info) = talloc_array(mem_ctx, struct wb_acct_info, *num_entries);
1730 if (! (*info)) {
1731 smb_panic_fn("enum_dom_groups out of memory");
1733 for (i=0; i<(*num_entries); i++) {
1734 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1735 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1736 (*info)[i].rid = centry_uint32(centry);
1739 do_cached:
1741 /* If we are returning cached data and the domain controller
1742 is down then we don't know whether the data is up to date
1743 or not. Return NT_STATUS_MORE_PROCESSING_REQUIRED to
1744 indicate this. */
1746 if (wcache_server_down(domain)) {
1747 DEBUG(10, ("enum_local_groups: returning cached user list and server was down\n"));
1748 status = NT_STATUS_MORE_PROCESSING_REQUIRED;
1749 } else
1750 status = centry->status;
1752 DEBUG(10,("enum_local_groups: [Cached] - cached list for domain %s status: %s\n",
1753 domain->name, nt_errstr(status) ));
1755 centry_free(centry);
1756 return status;
1758 do_query:
1759 *num_entries = 0;
1760 *info = NULL;
1762 /* Return status value returned by seq number check */
1764 if (!NT_STATUS_IS_OK(domain->last_status))
1765 return domain->last_status;
1767 DEBUG(10,("enum_local_groups: [Cached] - doing backend query for list for domain %s\n",
1768 domain->name ));
1770 status = domain->backend->enum_local_groups(domain, mem_ctx, num_entries, info);
1772 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1773 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1774 if (!domain->internal && old_status) {
1775 set_domain_offline(domain);
1777 if (cache->tdb &&
1778 !domain->internal &&
1779 !domain->online &&
1780 old_status) {
1781 centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
1782 if (centry) {
1783 goto do_fetch_cache;
1787 /* and save it */
1788 refresh_sequence_number(domain, false);
1789 if (!NT_STATUS_IS_OK(status)) {
1790 return status;
1792 centry = centry_start(domain, status);
1793 if (!centry)
1794 goto skip_save;
1795 centry_put_uint32(centry, *num_entries);
1796 for (i=0; i<(*num_entries); i++) {
1797 centry_put_string(centry, (*info)[i].acct_name);
1798 centry_put_string(centry, (*info)[i].acct_desc);
1799 centry_put_uint32(centry, (*info)[i].rid);
1801 centry_end(centry, "GL/%s/local", domain->name);
1802 centry_free(centry);
1804 skip_save:
1805 return status;
1808 NTSTATUS wcache_name_to_sid(struct winbindd_domain *domain,
1809 const char *domain_name,
1810 const char *name,
1811 struct dom_sid *sid,
1812 enum lsa_SidType *type)
1814 struct winbind_cache *cache = get_cache(domain);
1815 struct cache_entry *centry;
1816 NTSTATUS status;
1817 char *uname;
1819 if (cache->tdb == NULL) {
1820 return NT_STATUS_NOT_FOUND;
1823 uname = talloc_strdup_upper(talloc_tos(), name);
1824 if (uname == NULL) {
1825 return NT_STATUS_NO_MEMORY;
1828 if ((domain_name == NULL) || (domain_name[0] == '\0')) {
1829 domain_name = domain->name;
1832 centry = wcache_fetch(cache, domain, "NS/%s/%s", domain_name, uname);
1833 TALLOC_FREE(uname);
1834 if (centry == NULL) {
1835 return NT_STATUS_NOT_FOUND;
1838 status = centry->status;
1839 if (NT_STATUS_IS_OK(status)) {
1840 *type = (enum lsa_SidType)centry_uint32(centry);
1841 centry_sid(centry, sid);
1844 DEBUG(10,("name_to_sid: [Cached] - cached name for domain %s status: "
1845 "%s\n", domain->name, nt_errstr(status) ));
1847 centry_free(centry);
1848 return status;
1851 /* convert a single name to a sid in a domain */
1852 static NTSTATUS name_to_sid(struct winbindd_domain *domain,
1853 TALLOC_CTX *mem_ctx,
1854 const char *domain_name,
1855 const char *name,
1856 uint32_t flags,
1857 struct dom_sid *sid,
1858 enum lsa_SidType *type)
1860 NTSTATUS status;
1861 bool old_status;
1863 old_status = domain->online;
1865 status = wcache_name_to_sid(domain, domain_name, name, sid, type);
1866 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
1867 return status;
1870 ZERO_STRUCTP(sid);
1872 /* If the seq number check indicated that there is a problem
1873 * with this DC, then return that status... except for
1874 * access_denied. This is special because the dc may be in
1875 * "restrict anonymous = 1" mode, in which case it will deny
1876 * most unauthenticated operations, but *will* allow the LSA
1877 * name-to-sid that we try as a fallback. */
1879 if (!(NT_STATUS_IS_OK(domain->last_status)
1880 || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1881 return domain->last_status;
1883 DEBUG(10,("name_to_sid: [Cached] - doing backend query for name for domain %s\n",
1884 domain->name ));
1886 status = domain->backend->name_to_sid(domain, mem_ctx, domain_name,
1887 name, flags, sid, type);
1889 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1890 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1891 if (!domain->internal && old_status) {
1892 set_domain_offline(domain);
1894 if (!domain->internal &&
1895 !domain->online &&
1896 old_status) {
1897 NTSTATUS cache_status;
1898 cache_status = wcache_name_to_sid(domain, domain_name, name, sid, type);
1899 return cache_status;
1902 /* and save it */
1903 refresh_sequence_number(domain, false);
1905 if (domain->online &&
1906 (NT_STATUS_IS_OK(status) || NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED))) {
1907 wcache_save_name_to_sid(domain, status, domain_name, name, sid, *type);
1909 /* Only save the reverse mapping if this was not a UPN */
1910 if (!strchr(name, '@')) {
1911 if (!strupper_m(discard_const_p(char, domain_name))) {
1912 return NT_STATUS_INVALID_PARAMETER;
1914 (void)strlower_m(discard_const_p(char, name));
1915 wcache_save_sid_to_name(domain, status, sid, domain_name, name, *type);
1919 return status;
1922 static NTSTATUS wcache_sid_to_name(struct winbindd_domain *domain,
1923 const struct dom_sid *sid,
1924 TALLOC_CTX *mem_ctx,
1925 char **domain_name,
1926 char **name,
1927 enum lsa_SidType *type)
1929 struct winbind_cache *cache = get_cache(domain);
1930 struct cache_entry *centry;
1931 char *sid_string;
1932 NTSTATUS status;
1934 if (cache->tdb == NULL) {
1935 return NT_STATUS_NOT_FOUND;
1938 sid_string = sid_string_tos(sid);
1939 if (sid_string == NULL) {
1940 return NT_STATUS_NO_MEMORY;
1943 centry = wcache_fetch(cache, domain, "SN/%s", sid_string);
1944 TALLOC_FREE(sid_string);
1945 if (centry == NULL) {
1946 return NT_STATUS_NOT_FOUND;
1949 if (NT_STATUS_IS_OK(centry->status)) {
1950 *type = (enum lsa_SidType)centry_uint32(centry);
1951 *domain_name = centry_string(centry, mem_ctx);
1952 *name = centry_string(centry, mem_ctx);
1955 status = centry->status;
1956 centry_free(centry);
1958 DEBUG(10,("sid_to_name: [Cached] - cached name for domain %s status: "
1959 "%s\n", domain->name, nt_errstr(status) ));
1961 return status;
1964 /* convert a sid to a user or group name. The sid is guaranteed to be in the domain
1965 given */
1966 static NTSTATUS sid_to_name(struct winbindd_domain *domain,
1967 TALLOC_CTX *mem_ctx,
1968 const struct dom_sid *sid,
1969 char **domain_name,
1970 char **name,
1971 enum lsa_SidType *type)
1973 NTSTATUS status;
1974 bool old_status;
1976 old_status = domain->online;
1977 status = wcache_sid_to_name(domain, sid, mem_ctx, domain_name, name,
1978 type);
1979 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
1980 return status;
1983 *name = NULL;
1984 *domain_name = NULL;
1986 /* If the seq number check indicated that there is a problem
1987 * with this DC, then return that status... except for
1988 * access_denied. This is special because the dc may be in
1989 * "restrict anonymous = 1" mode, in which case it will deny
1990 * most unauthenticated operations, but *will* allow the LSA
1991 * sid-to-name that we try as a fallback. */
1993 if (!(NT_STATUS_IS_OK(domain->last_status)
1994 || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1995 return domain->last_status;
1997 DEBUG(10,("sid_to_name: [Cached] - doing backend query for name for domain %s\n",
1998 domain->name ));
2000 status = domain->backend->sid_to_name(domain, mem_ctx, sid, domain_name, name, type);
2002 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2003 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2004 if (!domain->internal && old_status) {
2005 set_domain_offline(domain);
2007 if (!domain->internal &&
2008 !domain->online &&
2009 old_status) {
2010 NTSTATUS cache_status;
2011 cache_status = wcache_sid_to_name(domain, sid, mem_ctx,
2012 domain_name, name, type);
2013 return cache_status;
2016 /* and save it */
2017 refresh_sequence_number(domain, false);
2018 if (!NT_STATUS_IS_OK(status)) {
2019 return status;
2021 wcache_save_sid_to_name(domain, status, sid, *domain_name, *name, *type);
2023 /* We can't save the name to sid mapping here, as with sid history a
2024 * later name2sid would give the wrong sid. */
2026 return status;
2029 static NTSTATUS rids_to_names(struct winbindd_domain *domain,
2030 TALLOC_CTX *mem_ctx,
2031 const struct dom_sid *domain_sid,
2032 uint32 *rids,
2033 size_t num_rids,
2034 char **domain_name,
2035 char ***names,
2036 enum lsa_SidType **types)
2038 struct winbind_cache *cache = get_cache(domain);
2039 size_t i;
2040 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
2041 bool have_mapped;
2042 bool have_unmapped;
2043 bool old_status;
2045 old_status = domain->online;
2046 *domain_name = NULL;
2047 *names = NULL;
2048 *types = NULL;
2050 if (!cache->tdb) {
2051 goto do_query;
2054 if (num_rids == 0) {
2055 return NT_STATUS_OK;
2058 *names = talloc_array(mem_ctx, char *, num_rids);
2059 *types = talloc_array(mem_ctx, enum lsa_SidType, num_rids);
2061 if ((*names == NULL) || (*types == NULL)) {
2062 result = NT_STATUS_NO_MEMORY;
2063 goto error;
2066 have_mapped = have_unmapped = false;
2068 for (i=0; i<num_rids; i++) {
2069 struct dom_sid sid;
2070 struct cache_entry *centry;
2071 fstring tmp;
2073 if (!sid_compose(&sid, domain_sid, rids[i])) {
2074 result = NT_STATUS_INTERNAL_ERROR;
2075 goto error;
2078 centry = wcache_fetch(cache, domain, "SN/%s",
2079 sid_to_fstring(tmp, &sid));
2080 if (!centry) {
2081 goto do_query;
2084 (*types)[i] = SID_NAME_UNKNOWN;
2085 (*names)[i] = talloc_strdup(*names, "");
2087 if (NT_STATUS_IS_OK(centry->status)) {
2088 char *dom;
2089 have_mapped = true;
2090 (*types)[i] = (enum lsa_SidType)centry_uint32(centry);
2092 dom = centry_string(centry, mem_ctx);
2093 if (*domain_name == NULL) {
2094 *domain_name = dom;
2095 } else {
2096 talloc_free(dom);
2099 (*names)[i] = centry_string(centry, *names);
2101 } else if (NT_STATUS_EQUAL(centry->status, NT_STATUS_NONE_MAPPED)
2102 || NT_STATUS_EQUAL(centry->status, STATUS_SOME_UNMAPPED)) {
2103 have_unmapped = true;
2105 } else {
2106 /* something's definitely wrong */
2107 result = centry->status;
2108 centry_free(centry);
2109 goto error;
2112 centry_free(centry);
2115 if (!have_mapped) {
2116 return NT_STATUS_NONE_MAPPED;
2118 if (!have_unmapped) {
2119 return NT_STATUS_OK;
2121 return STATUS_SOME_UNMAPPED;
2123 do_query:
2125 TALLOC_FREE(*names);
2126 TALLOC_FREE(*types);
2128 result = domain->backend->rids_to_names(domain, mem_ctx, domain_sid,
2129 rids, num_rids, domain_name,
2130 names, types);
2132 if (NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
2133 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2134 if (!domain->internal && old_status) {
2135 set_domain_offline(domain);
2137 if (cache->tdb &&
2138 !domain->internal &&
2139 !domain->online &&
2140 old_status) {
2141 have_mapped = have_unmapped = false;
2143 *names = talloc_array(mem_ctx, char *, num_rids);
2144 if (*names == NULL) {
2145 result = NT_STATUS_NO_MEMORY;
2146 goto error;
2149 *types = talloc_array(mem_ctx, enum lsa_SidType,
2150 num_rids);
2151 if (*types == NULL) {
2152 result = NT_STATUS_NO_MEMORY;
2153 goto error;
2156 for (i=0; i<num_rids; i++) {
2157 struct dom_sid sid;
2158 struct cache_entry *centry;
2159 fstring tmp;
2161 if (!sid_compose(&sid, domain_sid, rids[i])) {
2162 result = NT_STATUS_INTERNAL_ERROR;
2163 goto error;
2166 centry = wcache_fetch(cache, domain, "SN/%s",
2167 sid_to_fstring(tmp, &sid));
2168 if (!centry) {
2169 (*types)[i] = SID_NAME_UNKNOWN;
2170 (*names)[i] = talloc_strdup(*names, "");
2171 continue;
2174 (*types)[i] = SID_NAME_UNKNOWN;
2175 (*names)[i] = talloc_strdup(*names, "");
2177 if (NT_STATUS_IS_OK(centry->status)) {
2178 char *dom;
2179 have_mapped = true;
2180 (*types)[i] = (enum lsa_SidType)centry_uint32(centry);
2182 dom = centry_string(centry, mem_ctx);
2183 if (*domain_name == NULL) {
2184 *domain_name = dom;
2185 } else {
2186 talloc_free(dom);
2189 (*names)[i] = centry_string(centry, *names);
2191 } else if (NT_STATUS_EQUAL(centry->status, NT_STATUS_NONE_MAPPED)) {
2192 have_unmapped = true;
2194 } else {
2195 /* something's definitely wrong */
2196 result = centry->status;
2197 centry_free(centry);
2198 goto error;
2201 centry_free(centry);
2204 if (!have_mapped) {
2205 return NT_STATUS_NONE_MAPPED;
2207 if (!have_unmapped) {
2208 return NT_STATUS_OK;
2210 return STATUS_SOME_UNMAPPED;
2214 None of the queried rids has been found so save all negative entries
2216 if (NT_STATUS_EQUAL(result, NT_STATUS_NONE_MAPPED)) {
2217 for (i = 0; i < num_rids; i++) {
2218 struct dom_sid sid;
2219 const char *name = "";
2220 const enum lsa_SidType type = SID_NAME_UNKNOWN;
2221 NTSTATUS status = NT_STATUS_NONE_MAPPED;
2223 if (!sid_compose(&sid, domain_sid, rids[i])) {
2224 return NT_STATUS_INTERNAL_ERROR;
2227 wcache_save_sid_to_name(domain, status, &sid, *domain_name,
2228 name, type);
2231 return result;
2235 Some or all of the queried rids have been found.
2237 if (!NT_STATUS_IS_OK(result) &&
2238 !NT_STATUS_EQUAL(result, STATUS_SOME_UNMAPPED)) {
2239 return result;
2242 refresh_sequence_number(domain, false);
2244 for (i=0; i<num_rids; i++) {
2245 struct dom_sid sid;
2246 NTSTATUS status;
2248 if (!sid_compose(&sid, domain_sid, rids[i])) {
2249 result = NT_STATUS_INTERNAL_ERROR;
2250 goto error;
2253 status = (*types)[i] == SID_NAME_UNKNOWN ?
2254 NT_STATUS_NONE_MAPPED : NT_STATUS_OK;
2256 wcache_save_sid_to_name(domain, status, &sid, *domain_name,
2257 (*names)[i], (*types)[i]);
2260 return result;
2262 error:
2263 TALLOC_FREE(*names);
2264 TALLOC_FREE(*types);
2265 return result;
2268 NTSTATUS wcache_query_user(struct winbindd_domain *domain,
2269 TALLOC_CTX *mem_ctx,
2270 const struct dom_sid *user_sid,
2271 struct wbint_userinfo *info)
2273 struct winbind_cache *cache = get_cache(domain);
2274 struct cache_entry *centry = NULL;
2275 NTSTATUS status;
2276 char *sid_string;
2278 if (cache->tdb == NULL) {
2279 return NT_STATUS_NOT_FOUND;
2282 sid_string = sid_string_tos(user_sid);
2283 if (sid_string == NULL) {
2284 return NT_STATUS_NO_MEMORY;
2287 centry = wcache_fetch(cache, domain, "U/%s", sid_string);
2288 TALLOC_FREE(sid_string);
2289 if (centry == NULL) {
2290 return NT_STATUS_NOT_FOUND;
2294 * If we have an access denied cache entry and a cached info3
2295 * in the samlogon cache then do a query. This will force the
2296 * rpc back end to return the info3 data.
2299 if (NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED) &&
2300 netsamlogon_cache_have(user_sid)) {
2301 DEBUG(10, ("query_user: cached access denied and have cached "
2302 "info3\n"));
2303 domain->last_status = NT_STATUS_OK;
2304 centry_free(centry);
2305 return NT_STATUS_NOT_FOUND;
2308 /* if status is not ok then this is a negative hit
2309 and the rest of the data doesn't matter */
2310 status = centry->status;
2311 if (NT_STATUS_IS_OK(status)) {
2312 info->acct_name = centry_string(centry, mem_ctx);
2313 info->full_name = centry_string(centry, mem_ctx);
2314 info->homedir = centry_string(centry, mem_ctx);
2315 info->shell = centry_string(centry, mem_ctx);
2316 info->primary_gid = centry_uint32(centry);
2317 centry_sid(centry, &info->user_sid);
2318 centry_sid(centry, &info->group_sid);
2321 DEBUG(10,("query_user: [Cached] - cached info for domain %s status: "
2322 "%s\n", domain->name, nt_errstr(status) ));
2324 centry_free(centry);
2325 return status;
2330 * @brief Query a fullname from the username cache (for further gecos processing)
2332 * @param domain A pointer to the winbindd_domain struct.
2333 * @param mem_ctx The talloc context.
2334 * @param user_sid The user sid.
2335 * @param full_name A pointer to the full_name string.
2337 * @return NTSTATUS code
2339 NTSTATUS wcache_query_user_fullname(struct winbindd_domain *domain,
2340 TALLOC_CTX *mem_ctx,
2341 const struct dom_sid *user_sid,
2342 const char **full_name)
2344 NTSTATUS status;
2345 struct wbint_userinfo info;
2347 status = wcache_query_user(domain, mem_ctx, user_sid, &info);
2348 if (!NT_STATUS_IS_OK(status)) {
2349 return status;
2352 if (info.full_name != NULL) {
2353 *full_name = talloc_strdup(mem_ctx, info.full_name);
2354 if (*full_name == NULL) {
2355 return NT_STATUS_NO_MEMORY;
2359 return NT_STATUS_OK;
2362 /* Lookup user information from a rid */
2363 static NTSTATUS query_user(struct winbindd_domain *domain,
2364 TALLOC_CTX *mem_ctx,
2365 const struct dom_sid *user_sid,
2366 struct wbint_userinfo *info)
2368 NTSTATUS status;
2369 bool old_status;
2371 old_status = domain->online;
2372 status = wcache_query_user(domain, mem_ctx, user_sid, info);
2373 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2374 return status;
2377 ZERO_STRUCTP(info);
2379 /* Return status value returned by seq number check */
2381 if (!NT_STATUS_IS_OK(domain->last_status))
2382 return domain->last_status;
2384 DEBUG(10,("query_user: [Cached] - doing backend query for info for domain %s\n",
2385 domain->name ));
2387 status = domain->backend->query_user(domain, mem_ctx, user_sid, info);
2389 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2390 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2391 if (!domain->internal && old_status) {
2392 set_domain_offline(domain);
2394 if (!domain->internal &&
2395 !domain->online &&
2396 old_status) {
2397 NTSTATUS cache_status;
2398 cache_status = wcache_query_user(domain, mem_ctx, user_sid, info);
2399 return cache_status;
2402 /* and save it */
2403 refresh_sequence_number(domain, false);
2404 if (!NT_STATUS_IS_OK(status)) {
2405 return status;
2407 wcache_save_user(domain, status, info);
2409 return status;
2412 NTSTATUS wcache_lookup_usergroups(struct winbindd_domain *domain,
2413 TALLOC_CTX *mem_ctx,
2414 const struct dom_sid *user_sid,
2415 uint32_t *pnum_sids,
2416 struct dom_sid **psids)
2418 struct winbind_cache *cache = get_cache(domain);
2419 struct cache_entry *centry = NULL;
2420 NTSTATUS status;
2421 uint32_t i, num_sids;
2422 struct dom_sid *sids;
2423 fstring sid_string;
2425 if (cache->tdb == NULL) {
2426 return NT_STATUS_NOT_FOUND;
2429 centry = wcache_fetch(cache, domain, "UG/%s",
2430 sid_to_fstring(sid_string, user_sid));
2431 if (centry == NULL) {
2432 return NT_STATUS_NOT_FOUND;
2435 /* If we have an access denied cache entry and a cached info3 in the
2436 samlogon cache then do a query. This will force the rpc back end
2437 to return the info3 data. */
2439 if (NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)
2440 && netsamlogon_cache_have(user_sid)) {
2441 DEBUG(10, ("lookup_usergroups: cached access denied and have "
2442 "cached info3\n"));
2443 domain->last_status = NT_STATUS_OK;
2444 centry_free(centry);
2445 return NT_STATUS_NOT_FOUND;
2448 num_sids = centry_uint32(centry);
2449 sids = talloc_array(mem_ctx, struct dom_sid, num_sids);
2450 if (sids == NULL) {
2451 centry_free(centry);
2452 return NT_STATUS_NO_MEMORY;
2455 for (i=0; i<num_sids; i++) {
2456 centry_sid(centry, &sids[i]);
2459 status = centry->status;
2461 DEBUG(10,("lookup_usergroups: [Cached] - cached info for domain %s "
2462 "status: %s\n", domain->name, nt_errstr(status)));
2464 centry_free(centry);
2466 *pnum_sids = num_sids;
2467 *psids = sids;
2468 return status;
2471 /* Lookup groups a user is a member of. */
2472 static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
2473 TALLOC_CTX *mem_ctx,
2474 const struct dom_sid *user_sid,
2475 uint32 *num_groups, struct dom_sid **user_gids)
2477 struct cache_entry *centry = NULL;
2478 NTSTATUS status;
2479 unsigned int i;
2480 fstring sid_string;
2481 bool old_status;
2483 old_status = domain->online;
2484 status = wcache_lookup_usergroups(domain, mem_ctx, user_sid,
2485 num_groups, user_gids);
2486 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2487 return status;
2490 (*num_groups) = 0;
2491 (*user_gids) = NULL;
2493 /* Return status value returned by seq number check */
2495 if (!NT_STATUS_IS_OK(domain->last_status))
2496 return domain->last_status;
2498 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info for domain %s\n",
2499 domain->name ));
2501 status = domain->backend->lookup_usergroups(domain, mem_ctx, user_sid, num_groups, user_gids);
2503 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2504 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2505 if (!domain->internal && old_status) {
2506 set_domain_offline(domain);
2508 if (!domain->internal &&
2509 !domain->online &&
2510 old_status) {
2511 NTSTATUS cache_status;
2512 cache_status = wcache_lookup_usergroups(domain, mem_ctx, user_sid,
2513 num_groups, user_gids);
2514 return cache_status;
2517 if ( NT_STATUS_EQUAL(status, NT_STATUS_SYNCHRONIZATION_REQUIRED) )
2518 goto skip_save;
2520 /* and save it */
2521 refresh_sequence_number(domain, false);
2522 if (!NT_STATUS_IS_OK(status)) {
2523 return status;
2525 centry = centry_start(domain, status);
2526 if (!centry)
2527 goto skip_save;
2529 centry_put_uint32(centry, *num_groups);
2530 for (i=0; i<(*num_groups); i++) {
2531 centry_put_sid(centry, &(*user_gids)[i]);
2534 centry_end(centry, "UG/%s", sid_to_fstring(sid_string, user_sid));
2535 centry_free(centry);
2537 skip_save:
2538 return status;
2541 static char *wcache_make_sidlist(TALLOC_CTX *mem_ctx, uint32_t num_sids,
2542 const struct dom_sid *sids)
2544 uint32_t i;
2545 char *sidlist;
2547 sidlist = talloc_strdup(mem_ctx, "");
2548 if (sidlist == NULL) {
2549 return NULL;
2551 for (i=0; i<num_sids; i++) {
2552 fstring tmp;
2553 sidlist = talloc_asprintf_append_buffer(
2554 sidlist, "/%s", sid_to_fstring(tmp, &sids[i]));
2555 if (sidlist == NULL) {
2556 return NULL;
2559 return sidlist;
2562 NTSTATUS wcache_lookup_useraliases(struct winbindd_domain *domain,
2563 TALLOC_CTX *mem_ctx, uint32_t num_sids,
2564 const struct dom_sid *sids,
2565 uint32_t *pnum_aliases, uint32_t **paliases)
2567 struct winbind_cache *cache = get_cache(domain);
2568 struct cache_entry *centry = NULL;
2569 uint32_t num_aliases;
2570 uint32_t *aliases;
2571 NTSTATUS status;
2572 char *sidlist;
2573 int i;
2575 if (cache->tdb == NULL) {
2576 return NT_STATUS_NOT_FOUND;
2579 if (num_sids == 0) {
2580 *pnum_aliases = 0;
2581 *paliases = NULL;
2582 return NT_STATUS_OK;
2585 /* We need to cache indexed by the whole list of SIDs, the aliases
2586 * resulting might come from any of the SIDs. */
2588 sidlist = wcache_make_sidlist(talloc_tos(), num_sids, sids);
2589 if (sidlist == NULL) {
2590 return NT_STATUS_NO_MEMORY;
2593 centry = wcache_fetch(cache, domain, "UA%s", sidlist);
2594 TALLOC_FREE(sidlist);
2595 if (centry == NULL) {
2596 return NT_STATUS_NOT_FOUND;
2599 num_aliases = centry_uint32(centry);
2600 aliases = talloc_array(mem_ctx, uint32_t, num_aliases);
2601 if (aliases == NULL) {
2602 centry_free(centry);
2603 return NT_STATUS_NO_MEMORY;
2606 for (i=0; i<num_aliases; i++) {
2607 aliases[i] = centry_uint32(centry);
2610 status = centry->status;
2612 DEBUG(10,("lookup_useraliases: [Cached] - cached info for domain: %s "
2613 "status %s\n", domain->name, nt_errstr(status)));
2615 centry_free(centry);
2617 *pnum_aliases = num_aliases;
2618 *paliases = aliases;
2620 return status;
2623 static NTSTATUS lookup_useraliases(struct winbindd_domain *domain,
2624 TALLOC_CTX *mem_ctx,
2625 uint32 num_sids, const struct dom_sid *sids,
2626 uint32 *num_aliases, uint32 **alias_rids)
2628 struct cache_entry *centry = NULL;
2629 NTSTATUS status;
2630 char *sidlist;
2631 int i;
2632 bool old_status;
2634 old_status = domain->online;
2635 status = wcache_lookup_useraliases(domain, mem_ctx, num_sids, sids,
2636 num_aliases, alias_rids);
2637 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2638 return status;
2641 (*num_aliases) = 0;
2642 (*alias_rids) = NULL;
2644 if (!NT_STATUS_IS_OK(domain->last_status))
2645 return domain->last_status;
2647 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info "
2648 "for domain %s\n", domain->name ));
2650 sidlist = wcache_make_sidlist(talloc_tos(), num_sids, sids);
2651 if (sidlist == NULL) {
2652 return NT_STATUS_NO_MEMORY;
2655 status = domain->backend->lookup_useraliases(domain, mem_ctx,
2656 num_sids, sids,
2657 num_aliases, alias_rids);
2659 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2660 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2661 if (!domain->internal && old_status) {
2662 set_domain_offline(domain);
2664 if (!domain->internal &&
2665 !domain->online &&
2666 old_status) {
2667 NTSTATUS cache_status;
2668 cache_status = wcache_lookup_useraliases(domain, mem_ctx, num_sids,
2669 sids, num_aliases, alias_rids);
2670 return cache_status;
2673 /* and save it */
2674 refresh_sequence_number(domain, false);
2675 if (!NT_STATUS_IS_OK(status)) {
2676 return status;
2678 centry = centry_start(domain, status);
2679 if (!centry)
2680 goto skip_save;
2681 centry_put_uint32(centry, *num_aliases);
2682 for (i=0; i<(*num_aliases); i++)
2683 centry_put_uint32(centry, (*alias_rids)[i]);
2684 centry_end(centry, "UA%s", sidlist);
2685 centry_free(centry);
2687 skip_save:
2688 return status;
2691 NTSTATUS wcache_lookup_groupmem(struct winbindd_domain *domain,
2692 TALLOC_CTX *mem_ctx,
2693 const struct dom_sid *group_sid,
2694 uint32_t *num_names,
2695 struct dom_sid **sid_mem, char ***names,
2696 uint32_t **name_types)
2698 struct winbind_cache *cache = get_cache(domain);
2699 struct cache_entry *centry = NULL;
2700 NTSTATUS status;
2701 unsigned int i;
2702 char *sid_string;
2704 if (cache->tdb == NULL) {
2705 return NT_STATUS_NOT_FOUND;
2708 sid_string = sid_string_tos(group_sid);
2709 if (sid_string == NULL) {
2710 return NT_STATUS_NO_MEMORY;
2713 centry = wcache_fetch(cache, domain, "GM/%s", sid_string);
2714 TALLOC_FREE(sid_string);
2715 if (centry == NULL) {
2716 return NT_STATUS_NOT_FOUND;
2719 *sid_mem = NULL;
2720 *names = NULL;
2721 *name_types = NULL;
2723 *num_names = centry_uint32(centry);
2724 if (*num_names == 0) {
2725 centry_free(centry);
2726 return NT_STATUS_OK;
2729 *sid_mem = talloc_array(mem_ctx, struct dom_sid, *num_names);
2730 *names = talloc_array(mem_ctx, char *, *num_names);
2731 *name_types = talloc_array(mem_ctx, uint32, *num_names);
2733 if ((*sid_mem == NULL) || (*names == NULL) || (*name_types == NULL)) {
2734 TALLOC_FREE(*sid_mem);
2735 TALLOC_FREE(*names);
2736 TALLOC_FREE(*name_types);
2737 centry_free(centry);
2738 return NT_STATUS_NO_MEMORY;
2741 for (i=0; i<(*num_names); i++) {
2742 centry_sid(centry, &(*sid_mem)[i]);
2743 (*names)[i] = centry_string(centry, mem_ctx);
2744 (*name_types)[i] = centry_uint32(centry);
2747 status = centry->status;
2749 DEBUG(10,("lookup_groupmem: [Cached] - cached info for domain %s "
2750 "status: %s\n", domain->name, nt_errstr(status)));
2752 centry_free(centry);
2753 return status;
2756 static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
2757 TALLOC_CTX *mem_ctx,
2758 const struct dom_sid *group_sid,
2759 enum lsa_SidType type,
2760 uint32 *num_names,
2761 struct dom_sid **sid_mem, char ***names,
2762 uint32 **name_types)
2764 struct cache_entry *centry = NULL;
2765 NTSTATUS status;
2766 unsigned int i;
2767 fstring sid_string;
2768 bool old_status;
2770 old_status = domain->online;
2771 status = wcache_lookup_groupmem(domain, mem_ctx, group_sid, num_names,
2772 sid_mem, names, name_types);
2773 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2774 return status;
2777 (*num_names) = 0;
2778 (*sid_mem) = NULL;
2779 (*names) = NULL;
2780 (*name_types) = NULL;
2782 /* Return status value returned by seq number check */
2784 if (!NT_STATUS_IS_OK(domain->last_status))
2785 return domain->last_status;
2787 DEBUG(10,("lookup_groupmem: [Cached] - doing backend query for info for domain %s\n",
2788 domain->name ));
2790 status = domain->backend->lookup_groupmem(domain, mem_ctx, group_sid,
2791 type, num_names,
2792 sid_mem, names, name_types);
2794 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2795 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2796 if (!domain->internal && old_status) {
2797 set_domain_offline(domain);
2799 if (!domain->internal &&
2800 !domain->online &&
2801 old_status) {
2802 NTSTATUS cache_status;
2803 cache_status = wcache_lookup_groupmem(domain, mem_ctx, group_sid,
2804 num_names, sid_mem, names,
2805 name_types);
2806 return cache_status;
2809 /* and save it */
2810 refresh_sequence_number(domain, false);
2811 if (!NT_STATUS_IS_OK(status)) {
2812 return status;
2814 centry = centry_start(domain, status);
2815 if (!centry)
2816 goto skip_save;
2817 centry_put_uint32(centry, *num_names);
2818 for (i=0; i<(*num_names); i++) {
2819 centry_put_sid(centry, &(*sid_mem)[i]);
2820 centry_put_string(centry, (*names)[i]);
2821 centry_put_uint32(centry, (*name_types)[i]);
2823 centry_end(centry, "GM/%s", sid_to_fstring(sid_string, group_sid));
2824 centry_free(centry);
2826 skip_save:
2827 return status;
2830 /* find the sequence number for a domain */
2831 static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
2833 refresh_sequence_number(domain, false);
2835 *seq = domain->sequence_number;
2837 return NT_STATUS_OK;
2840 /* enumerate trusted domains
2841 * (we need to have the list of trustdoms in the cache when we go offline) -
2842 * Guenther */
2843 static NTSTATUS trusted_domains(struct winbindd_domain *domain,
2844 TALLOC_CTX *mem_ctx,
2845 struct netr_DomainTrustList *trusts)
2847 NTSTATUS status;
2848 struct winbind_cache *cache;
2849 struct winbindd_tdc_domain *dom_list = NULL;
2850 size_t num_domains = 0;
2851 bool retval = false;
2852 int i;
2853 bool old_status;
2855 old_status = domain->online;
2856 trusts->count = 0;
2857 trusts->array = NULL;
2859 cache = get_cache(domain);
2860 if (!cache || !cache->tdb) {
2861 goto do_query;
2864 if (domain->online) {
2865 goto do_query;
2868 retval = wcache_tdc_fetch_list(&dom_list, &num_domains);
2869 if (!retval || !num_domains || !dom_list) {
2870 TALLOC_FREE(dom_list);
2871 goto do_query;
2874 do_fetch_cache:
2875 trusts->array = talloc_zero_array(mem_ctx, struct netr_DomainTrust, num_domains);
2876 if (!trusts->array) {
2877 TALLOC_FREE(dom_list);
2878 return NT_STATUS_NO_MEMORY;
2881 for (i = 0; i < num_domains; i++) {
2882 struct netr_DomainTrust *trust;
2883 struct dom_sid *sid;
2884 struct winbindd_domain *dom;
2886 dom = find_domain_from_name_noinit(dom_list[i].domain_name);
2887 if (dom && dom->internal) {
2888 continue;
2891 trust = &trusts->array[trusts->count];
2892 trust->netbios_name = talloc_strdup(trusts->array, dom_list[i].domain_name);
2893 trust->dns_name = talloc_strdup(trusts->array, dom_list[i].dns_name);
2894 sid = talloc(trusts->array, struct dom_sid);
2895 if (!trust->netbios_name || !trust->dns_name ||
2896 !sid) {
2897 TALLOC_FREE(dom_list);
2898 TALLOC_FREE(trusts->array);
2899 return NT_STATUS_NO_MEMORY;
2902 trust->trust_flags = dom_list[i].trust_flags;
2903 trust->trust_attributes = dom_list[i].trust_attribs;
2904 trust->trust_type = dom_list[i].trust_type;
2905 sid_copy(sid, &dom_list[i].sid);
2906 trust->sid = sid;
2907 trusts->count++;
2910 TALLOC_FREE(dom_list);
2911 return NT_STATUS_OK;
2913 do_query:
2914 /* Return status value returned by seq number check */
2916 if (!NT_STATUS_IS_OK(domain->last_status))
2917 return domain->last_status;
2919 DEBUG(10,("trusted_domains: [Cached] - doing backend query for info for domain %s\n",
2920 domain->name ));
2922 status = domain->backend->trusted_domains(domain, mem_ctx, trusts);
2924 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2925 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2926 if (!domain->internal && old_status) {
2927 set_domain_offline(domain);
2929 if (!domain->internal &&
2930 !domain->online &&
2931 old_status) {
2932 retval = wcache_tdc_fetch_list(&dom_list, &num_domains);
2933 if (retval && num_domains && dom_list) {
2934 TALLOC_FREE(trusts->array);
2935 trusts->count = 0;
2936 goto do_fetch_cache;
2940 /* no trusts gives NT_STATUS_NO_MORE_ENTRIES resetting to NT_STATUS_OK
2941 * so that the generic centry handling still applies correctly -
2942 * Guenther*/
2944 if (!NT_STATUS_IS_ERR(status)) {
2945 status = NT_STATUS_OK;
2947 return status;
2950 /* get lockout policy */
2951 static NTSTATUS lockout_policy(struct winbindd_domain *domain,
2952 TALLOC_CTX *mem_ctx,
2953 struct samr_DomInfo12 *policy)
2955 struct winbind_cache *cache = get_cache(domain);
2956 struct cache_entry *centry = NULL;
2957 NTSTATUS status;
2958 bool old_status;
2960 old_status = domain->online;
2961 if (!cache->tdb)
2962 goto do_query;
2964 centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
2966 if (!centry)
2967 goto do_query;
2969 do_fetch_cache:
2970 policy->lockout_duration = centry_nttime(centry);
2971 policy->lockout_window = centry_nttime(centry);
2972 policy->lockout_threshold = centry_uint16(centry);
2974 status = centry->status;
2976 DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2977 domain->name, nt_errstr(status) ));
2979 centry_free(centry);
2980 return status;
2982 do_query:
2983 ZERO_STRUCTP(policy);
2985 /* Return status value returned by seq number check */
2987 if (!NT_STATUS_IS_OK(domain->last_status))
2988 return domain->last_status;
2990 DEBUG(10,("lockout_policy: [Cached] - doing backend query for info for domain %s\n",
2991 domain->name ));
2993 status = domain->backend->lockout_policy(domain, mem_ctx, policy);
2995 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2996 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2997 if (!domain->internal && old_status) {
2998 set_domain_offline(domain);
3000 if (cache->tdb &&
3001 !domain->internal &&
3002 !domain->online &&
3003 old_status) {
3004 centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
3005 if (centry) {
3006 goto do_fetch_cache;
3010 /* and save it */
3011 refresh_sequence_number(domain, false);
3012 if (!NT_STATUS_IS_OK(status)) {
3013 return status;
3015 wcache_save_lockout_policy(domain, status, policy);
3017 return status;
3020 /* get password policy */
3021 static NTSTATUS password_policy(struct winbindd_domain *domain,
3022 TALLOC_CTX *mem_ctx,
3023 struct samr_DomInfo1 *policy)
3025 struct winbind_cache *cache = get_cache(domain);
3026 struct cache_entry *centry = NULL;
3027 NTSTATUS status;
3028 bool old_status;
3030 old_status = domain->online;
3031 if (!cache->tdb)
3032 goto do_query;
3034 centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
3036 if (!centry)
3037 goto do_query;
3039 do_fetch_cache:
3040 policy->min_password_length = centry_uint16(centry);
3041 policy->password_history_length = centry_uint16(centry);
3042 policy->password_properties = centry_uint32(centry);
3043 policy->max_password_age = centry_nttime(centry);
3044 policy->min_password_age = centry_nttime(centry);
3046 status = centry->status;
3048 DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
3049 domain->name, nt_errstr(status) ));
3051 centry_free(centry);
3052 return status;
3054 do_query:
3055 ZERO_STRUCTP(policy);
3057 /* Return status value returned by seq number check */
3059 if (!NT_STATUS_IS_OK(domain->last_status))
3060 return domain->last_status;
3062 DEBUG(10,("password_policy: [Cached] - doing backend query for info for domain %s\n",
3063 domain->name ));
3065 status = domain->backend->password_policy(domain, mem_ctx, policy);
3067 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
3068 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
3069 if (!domain->internal && old_status) {
3070 set_domain_offline(domain);
3072 if (cache->tdb &&
3073 !domain->internal &&
3074 !domain->online &&
3075 old_status) {
3076 centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
3077 if (centry) {
3078 goto do_fetch_cache;
3082 /* and save it */
3083 refresh_sequence_number(domain, false);
3084 if (!NT_STATUS_IS_OK(status)) {
3085 return status;
3087 wcache_save_password_policy(domain, status, policy);
3089 return status;
3093 /* Invalidate cached user and group lists coherently */
3095 static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
3096 void *state)
3098 if (strncmp((const char *)kbuf.dptr, "UL/", 3) == 0 ||
3099 strncmp((const char *)kbuf.dptr, "GL/", 3) == 0)
3100 tdb_delete(the_tdb, kbuf);
3102 return 0;
3105 /* Invalidate the getpwnam and getgroups entries for a winbindd domain */
3107 void wcache_invalidate_samlogon(struct winbindd_domain *domain,
3108 const struct dom_sid *sid)
3110 fstring key_str, sid_string;
3111 struct winbind_cache *cache;
3113 /* dont clear cached U/SID and UG/SID entries when we want to logon
3114 * offline - gd */
3116 if (lp_winbind_offline_logon()) {
3117 return;
3120 if (!domain)
3121 return;
3123 cache = get_cache(domain);
3125 if (!cache->tdb) {
3126 return;
3129 /* Clear U/SID cache entry */
3130 fstr_sprintf(key_str, "U/%s", sid_to_fstring(sid_string, sid));
3131 DEBUG(10, ("wcache_invalidate_samlogon: clearing %s\n", key_str));
3132 tdb_delete(cache->tdb, string_tdb_data(key_str));
3134 /* Clear UG/SID cache entry */
3135 fstr_sprintf(key_str, "UG/%s", sid_to_fstring(sid_string, sid));
3136 DEBUG(10, ("wcache_invalidate_samlogon: clearing %s\n", key_str));
3137 tdb_delete(cache->tdb, string_tdb_data(key_str));
3139 /* Samba/winbindd never needs this. */
3140 netsamlogon_clear_cached_user(sid);
3143 bool wcache_invalidate_cache(void)
3145 struct winbindd_domain *domain;
3147 for (domain = domain_list(); domain; domain = domain->next) {
3148 struct winbind_cache *cache = get_cache(domain);
3150 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
3151 "entries for %s\n", domain->name));
3152 if (cache) {
3153 if (cache->tdb) {
3154 tdb_traverse(cache->tdb, traverse_fn, NULL);
3155 } else {
3156 return false;
3160 return true;
3163 bool wcache_invalidate_cache_noinit(void)
3165 struct winbindd_domain *domain;
3167 for (domain = domain_list(); domain; domain = domain->next) {
3168 struct winbind_cache *cache;
3170 /* Skip uninitialized domains. */
3171 if (!domain->initialized && !domain->internal) {
3172 continue;
3175 cache = get_cache(domain);
3177 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
3178 "entries for %s\n", domain->name));
3179 if (cache) {
3180 if (cache->tdb) {
3181 tdb_traverse(cache->tdb, traverse_fn, NULL);
3183 * Flushing cache has nothing to with domains.
3184 * return here if we successfully flushed once.
3185 * To avoid unnecessary traversing the cache.
3187 return true;
3188 } else {
3189 return false;
3193 return true;
3196 bool init_wcache(void)
3198 char *db_path;
3200 if (wcache == NULL) {
3201 wcache = SMB_XMALLOC_P(struct winbind_cache);
3202 ZERO_STRUCTP(wcache);
3205 if (wcache->tdb != NULL)
3206 return true;
3208 db_path = state_path("winbindd_cache.tdb");
3209 if (db_path == NULL) {
3210 return false;
3213 /* when working offline we must not clear the cache on restart */
3214 wcache->tdb = tdb_open_log(db_path,
3215 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
3216 TDB_INCOMPATIBLE_HASH |
3217 (lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST)),
3218 O_RDWR|O_CREAT, 0600);
3219 TALLOC_FREE(db_path);
3220 if (wcache->tdb == NULL) {
3221 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
3222 return false;
3225 return true;
3228 /************************************************************************
3229 This is called by the parent to initialize the cache file.
3230 We don't need sophisticated locking here as we know we're the
3231 only opener.
3232 ************************************************************************/
3234 bool initialize_winbindd_cache(void)
3236 bool cache_bad = true;
3237 uint32 vers;
3239 if (!init_wcache()) {
3240 DEBUG(0,("initialize_winbindd_cache: init_wcache failed.\n"));
3241 return false;
3244 /* Check version number. */
3245 if (tdb_fetch_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, &vers) &&
3246 vers == WINBINDD_CACHE_VERSION) {
3247 cache_bad = false;
3250 if (cache_bad) {
3251 char *db_path;
3253 DEBUG(0,("initialize_winbindd_cache: clearing cache "
3254 "and re-creating with version number %d\n",
3255 WINBINDD_CACHE_VERSION ));
3257 tdb_close(wcache->tdb);
3258 wcache->tdb = NULL;
3260 db_path = state_path("winbindd_cache.tdb");
3261 if (db_path == NULL) {
3262 return false;
3265 if (unlink(db_path) == -1) {
3266 DEBUG(0,("initialize_winbindd_cache: unlink %s failed %s ",
3267 db_path,
3268 strerror(errno) ));
3269 TALLOC_FREE(db_path);
3270 return false;
3272 TALLOC_FREE(db_path);
3273 if (!init_wcache()) {
3274 DEBUG(0,("initialize_winbindd_cache: re-initialization "
3275 "init_wcache failed.\n"));
3276 return false;
3279 /* Write the version. */
3280 if (!tdb_store_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, WINBINDD_CACHE_VERSION)) {
3281 DEBUG(0,("initialize_winbindd_cache: version number store failed %s\n",
3282 tdb_errorstr(wcache->tdb) ));
3283 return false;
3287 tdb_close(wcache->tdb);
3288 wcache->tdb = NULL;
3289 return true;
3292 void close_winbindd_cache(void)
3294 if (!wcache) {
3295 return;
3297 if (wcache->tdb) {
3298 tdb_close(wcache->tdb);
3299 wcache->tdb = NULL;
3303 bool lookup_cached_sid(TALLOC_CTX *mem_ctx, const struct dom_sid *sid,
3304 char **domain_name, char **name,
3305 enum lsa_SidType *type)
3307 struct winbindd_domain *domain;
3308 NTSTATUS status;
3310 domain = find_lookup_domain_from_sid(sid);
3311 if (domain == NULL) {
3312 return false;
3314 status = wcache_sid_to_name(domain, sid, mem_ctx, domain_name, name,
3315 type);
3316 return NT_STATUS_IS_OK(status);
3319 bool lookup_cached_name(const char *domain_name,
3320 const char *name,
3321 struct dom_sid *sid,
3322 enum lsa_SidType *type)
3324 struct winbindd_domain *domain;
3325 NTSTATUS status;
3326 bool original_online_state;
3328 domain = find_lookup_domain_from_name(domain_name);
3329 if (domain == NULL) {
3330 return false;
3333 /* If we are doing a cached logon, temporarily set the domain
3334 offline so the cache won't expire the entry */
3336 original_online_state = domain->online;
3337 domain->online = false;
3338 status = wcache_name_to_sid(domain, domain_name, name, sid, type);
3339 domain->online = original_online_state;
3341 return NT_STATUS_IS_OK(status);
3344 void cache_name2sid(struct winbindd_domain *domain,
3345 const char *domain_name, const char *name,
3346 enum lsa_SidType type, const struct dom_sid *sid)
3348 refresh_sequence_number(domain, false);
3349 wcache_save_name_to_sid(domain, NT_STATUS_OK, domain_name, name,
3350 sid, type);
3354 * The original idea that this cache only contains centries has
3355 * been blurred - now other stuff gets put in here. Ensure we
3356 * ignore these things on cleanup.
3359 static int traverse_fn_cleanup(TDB_CONTEXT *the_tdb, TDB_DATA kbuf,
3360 TDB_DATA dbuf, void *state)
3362 struct cache_entry *centry;
3364 if (is_non_centry_key(kbuf)) {
3365 return 0;
3368 centry = wcache_fetch_raw((char *)kbuf.dptr);
3369 if (!centry) {
3370 return 0;
3373 if (!NT_STATUS_IS_OK(centry->status)) {
3374 DEBUG(10,("deleting centry %s\n", (const char *)kbuf.dptr));
3375 tdb_delete(the_tdb, kbuf);
3378 centry_free(centry);
3379 return 0;
3382 /* flush the cache */
3383 void wcache_flush_cache(void)
3385 char *db_path;
3387 if (!wcache)
3388 return;
3389 if (wcache->tdb) {
3390 tdb_close(wcache->tdb);
3391 wcache->tdb = NULL;
3393 if (!winbindd_use_cache()) {
3394 return;
3397 db_path = state_path("winbindd_cache.tdb");
3398 if (db_path == NULL) {
3399 return;
3402 /* when working offline we must not clear the cache on restart */
3403 wcache->tdb = tdb_open_log(db_path,
3404 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
3405 TDB_INCOMPATIBLE_HASH |
3406 (lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST)),
3407 O_RDWR|O_CREAT, 0600);
3408 TALLOC_FREE(db_path);
3409 if (!wcache->tdb) {
3410 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
3411 return;
3414 tdb_traverse(wcache->tdb, traverse_fn_cleanup, NULL);
3416 DEBUG(10,("wcache_flush_cache success\n"));
3419 /* Count cached creds */
3421 static int traverse_fn_cached_creds(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
3422 void *state)
3424 int *cred_count = (int*)state;
3426 if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
3427 (*cred_count)++;
3429 return 0;
3432 NTSTATUS wcache_count_cached_creds(struct winbindd_domain *domain, int *count)
3434 struct winbind_cache *cache = get_cache(domain);
3436 *count = 0;
3438 if (!cache->tdb) {
3439 return NT_STATUS_INTERNAL_DB_ERROR;
3442 tdb_traverse(cache->tdb, traverse_fn_cached_creds, (void *)count);
3444 return NT_STATUS_OK;
3447 struct cred_list {
3448 struct cred_list *prev, *next;
3449 TDB_DATA key;
3450 fstring name;
3451 time_t created;
3453 static struct cred_list *wcache_cred_list;
3455 static int traverse_fn_get_credlist(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
3456 void *state)
3458 struct cred_list *cred;
3460 if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
3462 cred = SMB_MALLOC_P(struct cred_list);
3463 if (cred == NULL) {
3464 DEBUG(0,("traverse_fn_remove_first_creds: failed to malloc new entry for list\n"));
3465 return -1;
3468 ZERO_STRUCTP(cred);
3470 /* save a copy of the key */
3472 fstrcpy(cred->name, (const char *)kbuf.dptr);
3473 DLIST_ADD(wcache_cred_list, cred);
3476 return 0;
3479 NTSTATUS wcache_remove_oldest_cached_creds(struct winbindd_domain *domain, const struct dom_sid *sid)
3481 struct winbind_cache *cache = get_cache(domain);
3482 NTSTATUS status;
3483 int ret;
3484 struct cred_list *cred, *oldest = NULL;
3486 if (!cache->tdb) {
3487 return NT_STATUS_INTERNAL_DB_ERROR;
3490 /* we possibly already have an entry */
3491 if (sid && NT_STATUS_IS_OK(wcache_cached_creds_exist(domain, sid))) {
3493 fstring key_str, tmp;
3495 DEBUG(11,("we already have an entry, deleting that\n"));
3497 fstr_sprintf(key_str, "CRED/%s", sid_to_fstring(tmp, sid));
3499 tdb_delete(cache->tdb, string_tdb_data(key_str));
3501 return NT_STATUS_OK;
3504 ret = tdb_traverse(cache->tdb, traverse_fn_get_credlist, NULL);
3505 if (ret == 0) {
3506 return NT_STATUS_OK;
3507 } else if ((ret < 0) || (wcache_cred_list == NULL)) {
3508 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
3511 ZERO_STRUCTP(oldest);
3513 for (cred = wcache_cred_list; cred; cred = cred->next) {
3515 TDB_DATA data;
3516 time_t t;
3518 data = tdb_fetch(cache->tdb, string_tdb_data(cred->name));
3519 if (!data.dptr) {
3520 DEBUG(10,("wcache_remove_oldest_cached_creds: entry for [%s] not found\n",
3521 cred->name));
3522 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
3523 goto done;
3526 t = IVAL(data.dptr, 0);
3527 SAFE_FREE(data.dptr);
3529 if (!oldest) {
3530 oldest = SMB_MALLOC_P(struct cred_list);
3531 if (oldest == NULL) {
3532 status = NT_STATUS_NO_MEMORY;
3533 goto done;
3536 fstrcpy(oldest->name, cred->name);
3537 oldest->created = t;
3538 continue;
3541 if (t < oldest->created) {
3542 fstrcpy(oldest->name, cred->name);
3543 oldest->created = t;
3547 if (tdb_delete(cache->tdb, string_tdb_data(oldest->name)) == 0) {
3548 status = NT_STATUS_OK;
3549 } else {
3550 status = NT_STATUS_UNSUCCESSFUL;
3552 done:
3553 SAFE_FREE(wcache_cred_list);
3554 SAFE_FREE(oldest);
3556 return status;
3559 /* Change the global online/offline state. */
3560 bool set_global_winbindd_state_offline(void)
3562 TDB_DATA data;
3564 DEBUG(10,("set_global_winbindd_state_offline: offline requested.\n"));
3566 /* Only go offline if someone has created
3567 the key "WINBINDD_OFFLINE" in the cache tdb. */
3569 if (wcache == NULL || wcache->tdb == NULL) {
3570 DEBUG(10,("set_global_winbindd_state_offline: wcache not open yet.\n"));
3571 return false;
3574 if (!lp_winbind_offline_logon()) {
3575 DEBUG(10,("set_global_winbindd_state_offline: rejecting.\n"));
3576 return false;
3579 if (global_winbindd_offline_state) {
3580 /* Already offline. */
3581 return true;
3584 data = tdb_fetch_bystring( wcache->tdb, "WINBINDD_OFFLINE" );
3586 if (!data.dptr || data.dsize != 4) {
3587 DEBUG(10,("set_global_winbindd_state_offline: offline state not set.\n"));
3588 SAFE_FREE(data.dptr);
3589 return false;
3590 } else {
3591 DEBUG(10,("set_global_winbindd_state_offline: offline state set.\n"));
3592 global_winbindd_offline_state = true;
3593 SAFE_FREE(data.dptr);
3594 return true;
3598 void set_global_winbindd_state_online(void)
3600 DEBUG(10,("set_global_winbindd_state_online: online requested.\n"));
3602 if (!lp_winbind_offline_logon()) {
3603 DEBUG(10,("set_global_winbindd_state_online: rejecting.\n"));
3604 return;
3607 if (!global_winbindd_offline_state) {
3608 /* Already online. */
3609 return;
3611 global_winbindd_offline_state = false;
3613 if (!wcache->tdb) {
3614 return;
3617 /* Ensure there is no key "WINBINDD_OFFLINE" in the cache tdb. */
3618 tdb_delete_bystring(wcache->tdb, "WINBINDD_OFFLINE");
3621 bool get_global_winbindd_state_offline(void)
3623 return global_winbindd_offline_state;
3626 /***********************************************************************
3627 Validate functions for all possible cache tdb keys.
3628 ***********************************************************************/
3630 static struct cache_entry *create_centry_validate(const char *kstr, TDB_DATA data,
3631 struct tdb_validation_status *state)
3633 struct cache_entry *centry;
3635 centry = SMB_XMALLOC_P(struct cache_entry);
3636 centry->data = (unsigned char *)smb_memdup(data.dptr, data.dsize);
3637 if (!centry->data) {
3638 SAFE_FREE(centry);
3639 return NULL;
3641 centry->len = data.dsize;
3642 centry->ofs = 0;
3644 if (centry->len < 16) {
3645 /* huh? corrupt cache? */
3646 DEBUG(0,("create_centry_validate: Corrupt cache for key %s "
3647 "(len < 16) ?\n", kstr));
3648 centry_free(centry);
3649 state->bad_entry = true;
3650 state->success = false;
3651 return NULL;
3654 centry->status = NT_STATUS(centry_uint32(centry));
3655 centry->sequence_number = centry_uint32(centry);
3656 centry->timeout = centry_uint64_t(centry);
3657 return centry;
3660 static int validate_seqnum(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3661 struct tdb_validation_status *state)
3663 if (dbuf.dsize != 8) {
3664 DEBUG(0,("validate_seqnum: Corrupt cache for key %s (len %u != 8) ?\n",
3665 keystr, (unsigned int)dbuf.dsize ));
3666 state->bad_entry = true;
3667 return 1;
3669 return 0;
3672 static int validate_ns(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3673 struct tdb_validation_status *state)
3675 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3676 if (!centry) {
3677 return 1;
3680 (void)centry_uint32(centry);
3681 if (NT_STATUS_IS_OK(centry->status)) {
3682 struct dom_sid sid;
3683 (void)centry_sid(centry, &sid);
3686 centry_free(centry);
3688 if (!(state->success)) {
3689 return 1;
3691 DEBUG(10,("validate_ns: %s ok\n", keystr));
3692 return 0;
3695 static int validate_sn(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3696 struct tdb_validation_status *state)
3698 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3699 if (!centry) {
3700 return 1;
3703 if (NT_STATUS_IS_OK(centry->status)) {
3704 (void)centry_uint32(centry);
3705 (void)centry_string(centry, mem_ctx);
3706 (void)centry_string(centry, mem_ctx);
3709 centry_free(centry);
3711 if (!(state->success)) {
3712 return 1;
3714 DEBUG(10,("validate_sn: %s ok\n", keystr));
3715 return 0;
3718 static int validate_u(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3719 struct tdb_validation_status *state)
3721 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3722 struct dom_sid sid;
3724 if (!centry) {
3725 return 1;
3728 (void)centry_string(centry, mem_ctx);
3729 (void)centry_string(centry, mem_ctx);
3730 (void)centry_string(centry, mem_ctx);
3731 (void)centry_string(centry, mem_ctx);
3732 (void)centry_uint32(centry);
3733 (void)centry_sid(centry, &sid);
3734 (void)centry_sid(centry, &sid);
3736 centry_free(centry);
3738 if (!(state->success)) {
3739 return 1;
3741 DEBUG(10,("validate_u: %s ok\n", keystr));
3742 return 0;
3745 static int validate_loc_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3746 struct tdb_validation_status *state)
3748 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3750 if (!centry) {
3751 return 1;
3754 (void)centry_nttime(centry);
3755 (void)centry_nttime(centry);
3756 (void)centry_uint16(centry);
3758 centry_free(centry);
3760 if (!(state->success)) {
3761 return 1;
3763 DEBUG(10,("validate_loc_pol: %s ok\n", keystr));
3764 return 0;
3767 static int validate_pwd_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3768 struct tdb_validation_status *state)
3770 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3772 if (!centry) {
3773 return 1;
3776 (void)centry_uint16(centry);
3777 (void)centry_uint16(centry);
3778 (void)centry_uint32(centry);
3779 (void)centry_nttime(centry);
3780 (void)centry_nttime(centry);
3782 centry_free(centry);
3784 if (!(state->success)) {
3785 return 1;
3787 DEBUG(10,("validate_pwd_pol: %s ok\n", keystr));
3788 return 0;
3791 static int validate_cred(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3792 struct tdb_validation_status *state)
3794 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3796 if (!centry) {
3797 return 1;
3800 (void)centry_time(centry);
3801 (void)centry_hash16(centry, mem_ctx);
3803 /* We only have 17 bytes more data in the salted cred case. */
3804 if (centry->len - centry->ofs == 17) {
3805 (void)centry_hash16(centry, mem_ctx);
3808 centry_free(centry);
3810 if (!(state->success)) {
3811 return 1;
3813 DEBUG(10,("validate_cred: %s ok\n", keystr));
3814 return 0;
3817 static int validate_ul(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3818 struct tdb_validation_status *state)
3820 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3821 int32 num_entries, i;
3823 if (!centry) {
3824 return 1;
3827 num_entries = (int32)centry_uint32(centry);
3829 for (i=0; i< num_entries; i++) {
3830 struct dom_sid sid;
3831 (void)centry_string(centry, mem_ctx);
3832 (void)centry_string(centry, mem_ctx);
3833 (void)centry_string(centry, mem_ctx);
3834 (void)centry_string(centry, mem_ctx);
3835 (void)centry_sid(centry, &sid);
3836 (void)centry_sid(centry, &sid);
3839 centry_free(centry);
3841 if (!(state->success)) {
3842 return 1;
3844 DEBUG(10,("validate_ul: %s ok\n", keystr));
3845 return 0;
3848 static int validate_gl(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3849 struct tdb_validation_status *state)
3851 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3852 int32 num_entries, i;
3854 if (!centry) {
3855 return 1;
3858 num_entries = centry_uint32(centry);
3860 for (i=0; i< num_entries; i++) {
3861 (void)centry_string(centry, mem_ctx);
3862 (void)centry_string(centry, mem_ctx);
3863 (void)centry_uint32(centry);
3866 centry_free(centry);
3868 if (!(state->success)) {
3869 return 1;
3871 DEBUG(10,("validate_gl: %s ok\n", keystr));
3872 return 0;
3875 static int validate_ug(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3876 struct tdb_validation_status *state)
3878 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3879 int32 num_groups, i;
3881 if (!centry) {
3882 return 1;
3885 num_groups = centry_uint32(centry);
3887 for (i=0; i< num_groups; i++) {
3888 struct dom_sid sid;
3889 centry_sid(centry, &sid);
3892 centry_free(centry);
3894 if (!(state->success)) {
3895 return 1;
3897 DEBUG(10,("validate_ug: %s ok\n", keystr));
3898 return 0;
3901 static int validate_ua(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3902 struct tdb_validation_status *state)
3904 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3905 int32 num_aliases, i;
3907 if (!centry) {
3908 return 1;
3911 num_aliases = centry_uint32(centry);
3913 for (i=0; i < num_aliases; i++) {
3914 (void)centry_uint32(centry);
3917 centry_free(centry);
3919 if (!(state->success)) {
3920 return 1;
3922 DEBUG(10,("validate_ua: %s ok\n", keystr));
3923 return 0;
3926 static int validate_gm(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3927 struct tdb_validation_status *state)
3929 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3930 int32 num_names, i;
3932 if (!centry) {
3933 return 1;
3936 num_names = centry_uint32(centry);
3938 for (i=0; i< num_names; i++) {
3939 struct dom_sid sid;
3940 centry_sid(centry, &sid);
3941 (void)centry_string(centry, mem_ctx);
3942 (void)centry_uint32(centry);
3945 centry_free(centry);
3947 if (!(state->success)) {
3948 return 1;
3950 DEBUG(10,("validate_gm: %s ok\n", keystr));
3951 return 0;
3954 static int validate_dr(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3955 struct tdb_validation_status *state)
3957 /* Can't say anything about this other than must be nonzero. */
3958 if (dbuf.dsize == 0) {
3959 DEBUG(0,("validate_dr: Corrupt cache for key %s (len == 0) ?\n",
3960 keystr));
3961 state->bad_entry = true;
3962 state->success = false;
3963 return 1;
3966 DEBUG(10,("validate_dr: %s ok\n", keystr));
3967 return 0;
3970 static int validate_de(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3971 struct tdb_validation_status *state)
3973 /* Can't say anything about this other than must be nonzero. */
3974 if (dbuf.dsize == 0) {
3975 DEBUG(0,("validate_de: Corrupt cache for key %s (len == 0) ?\n",
3976 keystr));
3977 state->bad_entry = true;
3978 state->success = false;
3979 return 1;
3982 DEBUG(10,("validate_de: %s ok\n", keystr));
3983 return 0;
3986 static int validate_pwinfo(TALLOC_CTX *mem_ctx, const char *keystr,
3987 TDB_DATA dbuf, struct tdb_validation_status *state)
3989 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3991 if (!centry) {
3992 return 1;
3995 (void)centry_string(centry, mem_ctx);
3996 (void)centry_string(centry, mem_ctx);
3997 (void)centry_string(centry, mem_ctx);
3998 (void)centry_uint32(centry);
4000 centry_free(centry);
4002 if (!(state->success)) {
4003 return 1;
4005 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
4006 return 0;
4009 static int validate_nss_an(TALLOC_CTX *mem_ctx, const char *keystr,
4010 TDB_DATA dbuf,
4011 struct tdb_validation_status *state)
4013 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
4015 if (!centry) {
4016 return 1;
4019 (void)centry_string( centry, mem_ctx );
4021 centry_free(centry);
4023 if (!(state->success)) {
4024 return 1;
4026 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
4027 return 0;
4030 static int validate_nss_na(TALLOC_CTX *mem_ctx, const char *keystr,
4031 TDB_DATA dbuf,
4032 struct tdb_validation_status *state)
4034 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
4036 if (!centry) {
4037 return 1;
4040 (void)centry_string( centry, mem_ctx );
4042 centry_free(centry);
4044 if (!(state->success)) {
4045 return 1;
4047 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
4048 return 0;
4051 static int validate_trustdomcache(TALLOC_CTX *mem_ctx, const char *keystr,
4052 TDB_DATA dbuf,
4053 struct tdb_validation_status *state)
4055 if (dbuf.dsize == 0) {
4056 DEBUG(0, ("validate_trustdomcache: Corrupt cache for "
4057 "key %s (len ==0) ?\n", keystr));
4058 state->bad_entry = true;
4059 state->success = false;
4060 return 1;
4063 DEBUG(10, ("validate_trustdomcache: %s ok\n", keystr));
4064 DEBUGADD(10, (" Don't trust me, I am a DUMMY!\n"));
4065 return 0;
4068 static int validate_offline(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
4069 struct tdb_validation_status *state)
4071 if (dbuf.dsize != 4) {
4072 DEBUG(0,("validate_offline: Corrupt cache for key %s (len %u != 4) ?\n",
4073 keystr, (unsigned int)dbuf.dsize ));
4074 state->bad_entry = true;
4075 state->success = false;
4076 return 1;
4078 DEBUG(10,("validate_offline: %s ok\n", keystr));
4079 return 0;
4082 static int validate_ndr(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
4083 struct tdb_validation_status *state)
4086 * Ignore validation for now. The proper way to do this is with a
4087 * checksum. Just pure parsing does not really catch much.
4089 return 0;
4092 static int validate_cache_version(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
4093 struct tdb_validation_status *state)
4095 if (dbuf.dsize != 4) {
4096 DEBUG(0, ("validate_cache_version: Corrupt cache for "
4097 "key %s (len %u != 4) ?\n",
4098 keystr, (unsigned int)dbuf.dsize));
4099 state->bad_entry = true;
4100 state->success = false;
4101 return 1;
4104 DEBUG(10, ("validate_cache_version: %s ok\n", keystr));
4105 return 0;
4108 /***********************************************************************
4109 A list of all possible cache tdb keys with associated validation
4110 functions.
4111 ***********************************************************************/
4113 struct key_val_struct {
4114 const char *keyname;
4115 int (*validate_data_fn)(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, struct tdb_validation_status* state);
4116 } key_val[] = {
4117 {"SEQNUM/", validate_seqnum},
4118 {"NS/", validate_ns},
4119 {"SN/", validate_sn},
4120 {"U/", validate_u},
4121 {"LOC_POL/", validate_loc_pol},
4122 {"PWD_POL/", validate_pwd_pol},
4123 {"CRED/", validate_cred},
4124 {"UL/", validate_ul},
4125 {"GL/", validate_gl},
4126 {"UG/", validate_ug},
4127 {"UA", validate_ua},
4128 {"GM/", validate_gm},
4129 {"DR/", validate_dr},
4130 {"DE/", validate_de},
4131 {"NSS/PWINFO/", validate_pwinfo},
4132 {"TRUSTDOMCACHE/", validate_trustdomcache},
4133 {"NSS/NA/", validate_nss_na},
4134 {"NSS/AN/", validate_nss_an},
4135 {"WINBINDD_OFFLINE", validate_offline},
4136 {"NDR/", validate_ndr},
4137 {WINBINDD_CACHE_VERSION_KEYSTR, validate_cache_version},
4138 {NULL, NULL}
4141 /***********************************************************************
4142 Function to look at every entry in the tdb and validate it as far as
4143 possible.
4144 ***********************************************************************/
4146 static int cache_traverse_validate_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, void *state)
4148 int i;
4149 unsigned int max_key_len = 1024;
4150 struct tdb_validation_status *v_state = (struct tdb_validation_status *)state;
4152 /* Paranoia check. */
4153 if (strncmp("UA/", (const char *)kbuf.dptr, 3) == 0 ||
4154 strncmp("NDR/", (const char *)kbuf.dptr, 4) == 0) {
4155 max_key_len = 1024 * 1024;
4157 if (kbuf.dsize > max_key_len) {
4158 DEBUG(0, ("cache_traverse_validate_fn: key length too large: "
4159 "(%u) > (%u)\n\n",
4160 (unsigned int)kbuf.dsize, (unsigned int)max_key_len));
4161 return 1;
4164 for (i = 0; key_val[i].keyname; i++) {
4165 size_t namelen = strlen(key_val[i].keyname);
4166 if (kbuf.dsize >= namelen && (
4167 strncmp(key_val[i].keyname, (const char *)kbuf.dptr, namelen)) == 0) {
4168 TALLOC_CTX *mem_ctx;
4169 char *keystr;
4170 int ret;
4172 keystr = SMB_MALLOC_ARRAY(char, kbuf.dsize+1);
4173 if (!keystr) {
4174 return 1;
4176 memcpy(keystr, kbuf.dptr, kbuf.dsize);
4177 keystr[kbuf.dsize] = '\0';
4179 mem_ctx = talloc_init("validate_ctx");
4180 if (!mem_ctx) {
4181 SAFE_FREE(keystr);
4182 return 1;
4185 ret = key_val[i].validate_data_fn(mem_ctx, keystr, dbuf,
4186 v_state);
4188 SAFE_FREE(keystr);
4189 talloc_destroy(mem_ctx);
4190 return ret;
4194 DEBUG(0,("cache_traverse_validate_fn: unknown cache entry\nkey :\n"));
4195 dump_data(0, (uint8 *)kbuf.dptr, kbuf.dsize);
4196 DEBUG(0,("data :\n"));
4197 dump_data(0, (uint8 *)dbuf.dptr, dbuf.dsize);
4198 v_state->unknown_key = true;
4199 v_state->success = false;
4200 return 1; /* terminate. */
4203 static void validate_panic(const char *const why)
4205 DEBUG(0,("validating cache: would panic %s\n", why ));
4206 DEBUGADD(0, ("exiting instead (cache validation mode)\n"));
4207 exit(47);
4210 static int wbcache_update_centry_fn(TDB_CONTEXT *tdb,
4211 TDB_DATA key,
4212 TDB_DATA data,
4213 void *state)
4215 uint64_t ctimeout;
4216 TDB_DATA blob;
4218 if (is_non_centry_key(key)) {
4219 return 0;
4222 if (data.dptr == NULL || data.dsize == 0) {
4223 if (tdb_delete(tdb, key) < 0) {
4224 DEBUG(0, ("tdb_delete for [%s] failed!\n",
4225 key.dptr));
4226 return 1;
4230 /* add timeout to blob (uint64_t) */
4231 blob.dsize = data.dsize + 8;
4233 blob.dptr = SMB_XMALLOC_ARRAY(uint8_t, blob.dsize);
4234 if (blob.dptr == NULL) {
4235 return 1;
4237 memset(blob.dptr, 0, blob.dsize);
4239 /* copy status and seqnum */
4240 memcpy(blob.dptr, data.dptr, 8);
4242 /* add timeout */
4243 ctimeout = lp_winbind_cache_time() + time(NULL);
4244 SBVAL(blob.dptr, 8, ctimeout);
4246 /* copy the rest */
4247 memcpy(blob.dptr + 16, data.dptr + 8, data.dsize - 8);
4249 if (tdb_store(tdb, key, blob, TDB_REPLACE) < 0) {
4250 DEBUG(0, ("tdb_store to update [%s] failed!\n",
4251 key.dptr));
4252 SAFE_FREE(blob.dptr);
4253 return 1;
4256 SAFE_FREE(blob.dptr);
4257 return 0;
4260 static bool wbcache_upgrade_v1_to_v2(TDB_CONTEXT *tdb)
4262 int rc;
4264 DEBUG(1, ("Upgrade to version 2 of the winbindd_cache.tdb\n"));
4266 rc = tdb_traverse(tdb, wbcache_update_centry_fn, NULL);
4267 if (rc < 0) {
4268 return false;
4271 return true;
4274 /***********************************************************************
4275 Try and validate every entry in the winbindd cache. If we fail here,
4276 delete the cache tdb and return non-zero.
4277 ***********************************************************************/
4279 int winbindd_validate_cache(void)
4281 int ret = -1;
4282 char *tdb_path = NULL;
4283 TDB_CONTEXT *tdb = NULL;
4284 uint32_t vers_id;
4285 bool ok;
4287 DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
4288 smb_panic_fn = validate_panic;
4290 tdb_path = state_path("winbindd_cache.tdb");
4291 if (tdb_path == NULL) {
4292 goto done;
4295 tdb = tdb_open_log(tdb_path,
4296 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
4297 TDB_INCOMPATIBLE_HASH |
4298 ( lp_winbind_offline_logon()
4299 ? TDB_DEFAULT
4300 : TDB_DEFAULT | TDB_CLEAR_IF_FIRST ),
4301 O_RDWR|O_CREAT,
4302 0600);
4303 if (!tdb) {
4304 DEBUG(0, ("winbindd_validate_cache: "
4305 "error opening/initializing tdb\n"));
4306 goto done;
4309 /* Version check and upgrade code. */
4310 if (!tdb_fetch_uint32(tdb, WINBINDD_CACHE_VERSION_KEYSTR, &vers_id)) {
4311 DEBUG(10, ("Fresh database\n"));
4312 tdb_store_uint32(tdb, WINBINDD_CACHE_VERSION_KEYSTR, WINBINDD_CACHE_VERSION);
4313 vers_id = WINBINDD_CACHE_VERSION;
4316 if (vers_id != WINBINDD_CACHE_VERSION) {
4317 if (vers_id == WINBINDD_CACHE_VER1) {
4318 ok = wbcache_upgrade_v1_to_v2(tdb);
4319 if (!ok) {
4320 DEBUG(10, ("winbindd_validate_cache: upgrade to version 2 failed.\n"));
4321 unlink(tdb_path);
4322 goto done;
4325 tdb_store_uint32(tdb,
4326 WINBINDD_CACHE_VERSION_KEYSTR,
4327 WINBINDD_CACHE_VERSION);
4328 vers_id = WINBINDD_CACHE_VER2;
4332 tdb_close(tdb);
4334 ret = tdb_validate_and_backup(tdb_path, cache_traverse_validate_fn);
4336 if (ret != 0) {
4337 DEBUG(10, ("winbindd_validate_cache: validation not successful.\n"));
4338 DEBUGADD(10, ("removing tdb %s.\n", tdb_path));
4339 unlink(tdb_path);
4342 done:
4343 TALLOC_FREE(tdb_path);
4344 DEBUG(10, ("winbindd_validate_cache: restoring panic function\n"));
4345 smb_panic_fn = smb_panic;
4346 return ret;
4349 /***********************************************************************
4350 Try and validate every entry in the winbindd cache.
4351 ***********************************************************************/
4353 int winbindd_validate_cache_nobackup(void)
4355 int ret = -1;
4356 char *tdb_path;
4358 DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
4359 smb_panic_fn = validate_panic;
4361 tdb_path = state_path("winbindd_cache.tdb");
4362 if (tdb_path == NULL) {
4363 goto err_panic_restore;
4366 if (wcache == NULL || wcache->tdb == NULL) {
4367 ret = tdb_validate_open(tdb_path, cache_traverse_validate_fn);
4368 } else {
4369 ret = tdb_validate(wcache->tdb, cache_traverse_validate_fn);
4372 if (ret != 0) {
4373 DEBUG(10, ("winbindd_validate_cache_nobackup: validation not "
4374 "successful.\n"));
4377 TALLOC_FREE(tdb_path);
4378 err_panic_restore:
4379 DEBUG(10, ("winbindd_validate_cache_nobackup: restoring panic "
4380 "function\n"));
4381 smb_panic_fn = smb_panic;
4382 return ret;
4385 bool winbindd_cache_validate_and_initialize(void)
4387 close_winbindd_cache();
4389 if (lp_winbind_offline_logon()) {
4390 if (winbindd_validate_cache() < 0) {
4391 DEBUG(0, ("winbindd cache tdb corrupt and no backup "
4392 "could be restored.\n"));
4396 return initialize_winbindd_cache();
4399 /*********************************************************************
4400 ********************************************************************/
4402 static bool add_wbdomain_to_tdc_array( struct winbindd_domain *new_dom,
4403 struct winbindd_tdc_domain **domains,
4404 size_t *num_domains )
4406 struct winbindd_tdc_domain *list = NULL;
4407 size_t idx;
4408 int i;
4409 bool set_only = false;
4411 /* don't allow duplicates */
4413 idx = *num_domains;
4414 list = *domains;
4416 for ( i=0; i< (*num_domains); i++ ) {
4417 if ( strequal( new_dom->name, list[i].domain_name ) ) {
4418 DEBUG(10,("add_wbdomain_to_tdc_array: Found existing record for %s\n",
4419 new_dom->name));
4420 idx = i;
4421 set_only = true;
4423 break;
4427 if ( !set_only ) {
4428 if ( !*domains ) {
4429 list = talloc_array( NULL, struct winbindd_tdc_domain, 1 );
4430 idx = 0;
4431 } else {
4432 list = talloc_realloc( *domains, *domains,
4433 struct winbindd_tdc_domain,
4434 (*num_domains)+1);
4435 idx = *num_domains;
4438 ZERO_STRUCT( list[idx] );
4441 if ( !list )
4442 return false;
4444 list[idx].domain_name = talloc_strdup(list, new_dom->name);
4445 if (list[idx].domain_name == NULL) {
4446 return false;
4448 if (new_dom->alt_name != NULL) {
4449 list[idx].dns_name = talloc_strdup(list, new_dom->alt_name);
4450 if (list[idx].dns_name == NULL) {
4451 return false;
4455 if ( !is_null_sid( &new_dom->sid ) ) {
4456 sid_copy( &list[idx].sid, &new_dom->sid );
4457 } else {
4458 sid_copy(&list[idx].sid, &global_sid_NULL);
4461 if ( new_dom->domain_flags != 0x0 )
4462 list[idx].trust_flags = new_dom->domain_flags;
4464 if ( new_dom->domain_type != 0x0 )
4465 list[idx].trust_type = new_dom->domain_type;
4467 if ( new_dom->domain_trust_attribs != 0x0 )
4468 list[idx].trust_attribs = new_dom->domain_trust_attribs;
4470 if ( !set_only ) {
4471 *domains = list;
4472 *num_domains = idx + 1;
4475 return true;
4478 /*********************************************************************
4479 ********************************************************************/
4481 static TDB_DATA make_tdc_key( const char *domain_name )
4483 char *keystr = NULL;
4484 TDB_DATA key = { NULL, 0 };
4486 if ( !domain_name ) {
4487 DEBUG(5,("make_tdc_key: Keyname workgroup is NULL!\n"));
4488 return key;
4491 if (asprintf( &keystr, "TRUSTDOMCACHE/%s", domain_name ) == -1) {
4492 return key;
4494 key = string_term_tdb_data(keystr);
4496 return key;
4499 /*********************************************************************
4500 ********************************************************************/
4502 static int pack_tdc_domains( struct winbindd_tdc_domain *domains,
4503 size_t num_domains,
4504 unsigned char **buf )
4506 unsigned char *buffer = NULL;
4507 int len = 0;
4508 int buflen = 0;
4509 int i = 0;
4511 DEBUG(10,("pack_tdc_domains: Packing %d trusted domains\n",
4512 (int)num_domains));
4514 buflen = 0;
4516 again:
4517 len = 0;
4519 /* Store the number of array items first */
4520 len += tdb_pack( buffer+len, buflen-len, "d",
4521 num_domains );
4523 /* now pack each domain trust record */
4524 for ( i=0; i<num_domains; i++ ) {
4526 fstring tmp;
4528 if ( buflen > 0 ) {
4529 DEBUG(10,("pack_tdc_domains: Packing domain %s (%s)\n",
4530 domains[i].domain_name,
4531 domains[i].dns_name ? domains[i].dns_name : "UNKNOWN" ));
4534 len += tdb_pack( buffer+len, buflen-len, "fffddd",
4535 domains[i].domain_name,
4536 domains[i].dns_name ? domains[i].dns_name : "",
4537 sid_to_fstring(tmp, &domains[i].sid),
4538 domains[i].trust_flags,
4539 domains[i].trust_attribs,
4540 domains[i].trust_type );
4543 if ( buflen < len ) {
4544 SAFE_FREE(buffer);
4545 if ( (buffer = SMB_MALLOC_ARRAY(unsigned char, len)) == NULL ) {
4546 DEBUG(0,("pack_tdc_domains: failed to alloc buffer!\n"));
4547 buflen = -1;
4548 goto done;
4550 buflen = len;
4551 goto again;
4554 *buf = buffer;
4556 done:
4557 return buflen;
4560 /*********************************************************************
4561 ********************************************************************/
4563 static size_t unpack_tdc_domains( unsigned char *buf, int buflen,
4564 struct winbindd_tdc_domain **domains )
4566 fstring domain_name, dns_name, sid_string;
4567 uint32 type, attribs, flags;
4568 int num_domains;
4569 int len = 0;
4570 int i;
4571 struct winbindd_tdc_domain *list = NULL;
4573 /* get the number of domains */
4574 len += tdb_unpack( buf+len, buflen-len, "d", &num_domains);
4575 if ( len == -1 ) {
4576 DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
4577 return 0;
4580 list = talloc_array( NULL, struct winbindd_tdc_domain, num_domains );
4581 if ( !list ) {
4582 DEBUG(0,("unpack_tdc_domains: Failed to talloc() domain list!\n"));
4583 return 0;
4586 for ( i=0; i<num_domains; i++ ) {
4587 int this_len;
4589 this_len = tdb_unpack( buf+len, buflen-len, "fffddd",
4590 domain_name,
4591 dns_name,
4592 sid_string,
4593 &flags,
4594 &attribs,
4595 &type );
4597 if ( this_len == -1 ) {
4598 DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
4599 TALLOC_FREE( list );
4600 return 0;
4602 len += this_len;
4604 DEBUG(11,("unpack_tdc_domains: Unpacking domain %s (%s) "
4605 "SID %s, flags = 0x%x, attribs = 0x%x, type = 0x%x\n",
4606 domain_name, dns_name, sid_string,
4607 flags, attribs, type));
4609 list[i].domain_name = talloc_strdup( list, domain_name );
4610 list[i].dns_name = NULL;
4611 if (dns_name[0] != '\0') {
4612 list[i].dns_name = talloc_strdup(list, dns_name);
4614 if ( !string_to_sid( &(list[i].sid), sid_string ) ) {
4615 DEBUG(10,("unpack_tdc_domains: no SID for domain %s\n",
4616 domain_name));
4618 list[i].trust_flags = flags;
4619 list[i].trust_attribs = attribs;
4620 list[i].trust_type = type;
4623 *domains = list;
4625 return num_domains;
4628 /*********************************************************************
4629 ********************************************************************/
4631 static bool wcache_tdc_store_list( struct winbindd_tdc_domain *domains, size_t num_domains )
4633 TDB_DATA key = make_tdc_key( lp_workgroup() );
4634 TDB_DATA data = { NULL, 0 };
4635 int ret;
4637 if ( !key.dptr )
4638 return false;
4640 /* See if we were asked to delete the cache entry */
4642 if ( !domains ) {
4643 ret = tdb_delete( wcache->tdb, key );
4644 goto done;
4647 data.dsize = pack_tdc_domains( domains, num_domains, &data.dptr );
4649 if ( !data.dptr ) {
4650 ret = -1;
4651 goto done;
4654 ret = tdb_store( wcache->tdb, key, data, 0 );
4656 done:
4657 SAFE_FREE( data.dptr );
4658 SAFE_FREE( key.dptr );
4660 return ( ret == 0 );
4663 /*********************************************************************
4664 ********************************************************************/
4666 bool wcache_tdc_fetch_list( struct winbindd_tdc_domain **domains, size_t *num_domains )
4668 TDB_DATA key = make_tdc_key( lp_workgroup() );
4669 TDB_DATA data = { NULL, 0 };
4671 *domains = NULL;
4672 *num_domains = 0;
4674 if ( !key.dptr )
4675 return false;
4677 data = tdb_fetch( wcache->tdb, key );
4679 SAFE_FREE( key.dptr );
4681 if ( !data.dptr )
4682 return false;
4684 *num_domains = unpack_tdc_domains( data.dptr, data.dsize, domains );
4686 SAFE_FREE( data.dptr );
4688 if ( !*domains )
4689 return false;
4691 return true;
4694 /*********************************************************************
4695 ********************************************************************/
4697 bool wcache_tdc_add_domain( struct winbindd_domain *domain )
4699 struct winbindd_tdc_domain *dom_list = NULL;
4700 size_t num_domains = 0;
4701 bool ret = false;
4703 DEBUG(10,("wcache_tdc_add_domain: Adding domain %s (%s), SID %s, "
4704 "flags = 0x%x, attributes = 0x%x, type = 0x%x\n",
4705 domain->name, domain->alt_name,
4706 sid_string_dbg(&domain->sid),
4707 domain->domain_flags,
4708 domain->domain_trust_attribs,
4709 domain->domain_type));
4711 if ( !init_wcache() ) {
4712 return false;
4715 /* fetch the list */
4717 wcache_tdc_fetch_list( &dom_list, &num_domains );
4719 /* add the new domain */
4721 if ( !add_wbdomain_to_tdc_array( domain, &dom_list, &num_domains ) ) {
4722 goto done;
4725 /* pack the domain */
4727 if ( !wcache_tdc_store_list( dom_list, num_domains ) ) {
4728 goto done;
4731 /* Success */
4733 ret = true;
4734 done:
4735 TALLOC_FREE( dom_list );
4737 return ret;
4740 static struct winbindd_tdc_domain *wcache_tdc_dup_domain(
4741 TALLOC_CTX *mem_ctx, const struct winbindd_tdc_domain *src)
4743 struct winbindd_tdc_domain *dst;
4745 dst = talloc(mem_ctx, struct winbindd_tdc_domain);
4746 if (dst == NULL) {
4747 goto fail;
4749 dst->domain_name = talloc_strdup(dst, src->domain_name);
4750 if (dst->domain_name == NULL) {
4751 goto fail;
4754 dst->dns_name = NULL;
4755 if (src->dns_name != NULL) {
4756 dst->dns_name = talloc_strdup(dst, src->dns_name);
4757 if (dst->dns_name == NULL) {
4758 goto fail;
4762 sid_copy(&dst->sid, &src->sid);
4763 dst->trust_flags = src->trust_flags;
4764 dst->trust_type = src->trust_type;
4765 dst->trust_attribs = src->trust_attribs;
4766 return dst;
4767 fail:
4768 TALLOC_FREE(dst);
4769 return NULL;
4772 /*********************************************************************
4773 ********************************************************************/
4775 struct winbindd_tdc_domain * wcache_tdc_fetch_domain( TALLOC_CTX *ctx, const char *name )
4777 struct winbindd_tdc_domain *dom_list = NULL;
4778 size_t num_domains = 0;
4779 int i;
4780 struct winbindd_tdc_domain *d = NULL;
4782 DEBUG(10,("wcache_tdc_fetch_domain: Searching for domain %s\n", name));
4784 if ( !init_wcache() ) {
4785 return NULL;
4788 /* fetch the list */
4790 wcache_tdc_fetch_list( &dom_list, &num_domains );
4792 for ( i=0; i<num_domains; i++ ) {
4793 if ( strequal(name, dom_list[i].domain_name) ||
4794 strequal(name, dom_list[i].dns_name) )
4796 DEBUG(10,("wcache_tdc_fetch_domain: Found domain %s\n",
4797 name));
4799 d = wcache_tdc_dup_domain(ctx, &dom_list[i]);
4800 break;
4804 TALLOC_FREE( dom_list );
4806 return d;
4809 /*********************************************************************
4810 ********************************************************************/
4812 struct winbindd_tdc_domain*
4813 wcache_tdc_fetch_domainbysid(TALLOC_CTX *ctx,
4814 const struct dom_sid *sid)
4816 struct winbindd_tdc_domain *dom_list = NULL;
4817 size_t num_domains = 0;
4818 int i;
4819 struct winbindd_tdc_domain *d = NULL;
4821 DEBUG(10,("wcache_tdc_fetch_domainbysid: Searching for domain %s\n",
4822 sid_string_dbg(sid)));
4824 if (!init_wcache()) {
4825 return NULL;
4828 /* fetch the list */
4830 wcache_tdc_fetch_list(&dom_list, &num_domains);
4832 for (i = 0; i<num_domains; i++) {
4833 if (dom_sid_equal(sid, &(dom_list[i].sid))) {
4834 DEBUG(10, ("wcache_tdc_fetch_domainbysid: "
4835 "Found domain %s for SID %s\n",
4836 dom_list[i].domain_name,
4837 sid_string_dbg(sid)));
4839 d = wcache_tdc_dup_domain(ctx, &dom_list[i]);
4840 break;
4844 TALLOC_FREE(dom_list);
4846 return d;
4850 /*********************************************************************
4851 ********************************************************************/
4853 void wcache_tdc_clear( void )
4855 if ( !init_wcache() )
4856 return;
4858 wcache_tdc_store_list( NULL, 0 );
4860 return;
4864 /*********************************************************************
4865 ********************************************************************/
4867 static void wcache_save_user_pwinfo(struct winbindd_domain *domain,
4868 NTSTATUS status,
4869 const struct dom_sid *user_sid,
4870 const char *homedir,
4871 const char *shell,
4872 const char *gecos,
4873 uint32 gid)
4875 struct cache_entry *centry;
4876 fstring tmp;
4878 if ( (centry = centry_start(domain, status)) == NULL )
4879 return;
4881 centry_put_string( centry, homedir );
4882 centry_put_string( centry, shell );
4883 centry_put_string( centry, gecos );
4884 centry_put_uint32( centry, gid );
4886 centry_end(centry, "NSS/PWINFO/%s", sid_to_fstring(tmp, user_sid) );
4888 DEBUG(10,("wcache_save_user_pwinfo: %s\n", sid_string_dbg(user_sid) ));
4890 centry_free(centry);
4893 #ifdef HAVE_ADS
4895 NTSTATUS nss_get_info_cached( struct winbindd_domain *domain,
4896 const struct dom_sid *user_sid,
4897 TALLOC_CTX *ctx,
4898 const char **homedir, const char **shell,
4899 const char **gecos, gid_t *p_gid)
4901 struct winbind_cache *cache = get_cache(domain);
4902 struct cache_entry *centry = NULL;
4903 NTSTATUS nt_status;
4904 fstring tmp;
4906 if (!cache->tdb)
4907 goto do_query;
4909 centry = wcache_fetch(cache, domain, "NSS/PWINFO/%s",
4910 sid_to_fstring(tmp, user_sid));
4912 if (!centry)
4913 goto do_query;
4915 *homedir = centry_string( centry, ctx );
4916 *shell = centry_string( centry, ctx );
4917 *gecos = centry_string( centry, ctx );
4918 *p_gid = centry_uint32( centry );
4920 centry_free(centry);
4922 DEBUG(10,("nss_get_info_cached: [Cached] - user_sid %s\n",
4923 sid_string_dbg(user_sid)));
4925 return NT_STATUS_OK;
4927 do_query:
4929 nt_status = nss_get_info( domain->name, user_sid, ctx,
4930 homedir, shell, gecos, p_gid );
4932 DEBUG(10, ("nss_get_info returned %s\n", nt_errstr(nt_status)));
4934 if ( NT_STATUS_IS_OK(nt_status) ) {
4935 DEBUG(10, ("result:\n\thomedir = '%s'\n", *homedir));
4936 DEBUGADD(10, ("\tshell = '%s'\n", *shell));
4937 DEBUGADD(10, ("\tgecos = '%s'\n", *gecos));
4938 DEBUGADD(10, ("\tgid = '%u'\n", (unsigned int)*p_gid));
4940 wcache_save_user_pwinfo( domain, nt_status, user_sid,
4941 *homedir, *shell, *gecos, *p_gid );
4944 if ( NT_STATUS_EQUAL( nt_status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND ) ) {
4945 DEBUG(5,("nss_get_info_cached: Setting domain %s offline\n",
4946 domain->name ));
4947 set_domain_offline( domain );
4950 return nt_status;
4953 #endif
4955 /* the cache backend methods are exposed via this structure */
4956 struct winbindd_methods cache_methods = {
4957 true,
4958 query_user_list,
4959 enum_dom_groups,
4960 enum_local_groups,
4961 name_to_sid,
4962 sid_to_name,
4963 rids_to_names,
4964 query_user,
4965 lookup_usergroups,
4966 lookup_useraliases,
4967 lookup_groupmem,
4968 sequence_number,
4969 lockout_policy,
4970 password_policy,
4971 trusted_domains
4974 static bool wcache_ndr_key(TALLOC_CTX *mem_ctx, const char *domain_name,
4975 uint32_t opnum, const DATA_BLOB *req,
4976 TDB_DATA *pkey)
4978 char *key;
4979 size_t keylen;
4981 key = talloc_asprintf(mem_ctx, "NDR/%s/%d/", domain_name, (int)opnum);
4982 if (key == NULL) {
4983 return false;
4985 keylen = talloc_get_size(key) - 1;
4987 key = talloc_realloc(mem_ctx, key, char, keylen + req->length);
4988 if (key == NULL) {
4989 return false;
4991 memcpy(key + keylen, req->data, req->length);
4993 pkey->dptr = (uint8_t *)key;
4994 pkey->dsize = talloc_get_size(key);
4995 return true;
4998 static bool wcache_opnum_cacheable(uint32_t opnum)
5000 switch (opnum) {
5001 case NDR_WBINT_PING:
5002 case NDR_WBINT_QUERYSEQUENCENUMBER:
5003 case NDR_WBINT_ALLOCATEUID:
5004 case NDR_WBINT_ALLOCATEGID:
5005 case NDR_WBINT_CHECKMACHINEACCOUNT:
5006 case NDR_WBINT_CHANGEMACHINEACCOUNT:
5007 case NDR_WBINT_PINGDC:
5008 return false;
5010 return true;
5013 bool wcache_fetch_ndr(TALLOC_CTX *mem_ctx, struct winbindd_domain *domain,
5014 uint32_t opnum, const DATA_BLOB *req, DATA_BLOB *resp)
5016 TDB_DATA key, data;
5017 bool ret = false;
5019 if (!wcache_opnum_cacheable(opnum) ||
5020 is_my_own_sam_domain(domain) ||
5021 is_builtin_domain(domain)) {
5022 return false;
5025 if (wcache->tdb == NULL) {
5026 return false;
5029 if (!wcache_ndr_key(talloc_tos(), domain->name, opnum, req, &key)) {
5030 return false;
5032 data = tdb_fetch(wcache->tdb, key);
5033 TALLOC_FREE(key.dptr);
5035 if (data.dptr == NULL) {
5036 return false;
5038 if (data.dsize < 12) {
5039 goto fail;
5042 if (!is_domain_offline(domain)) {
5043 uint32_t entry_seqnum, dom_seqnum, last_check;
5044 uint64_t entry_timeout;
5046 if (!wcache_fetch_seqnum(domain->name, &dom_seqnum,
5047 &last_check)) {
5048 goto fail;
5050 entry_seqnum = IVAL(data.dptr, 0);
5051 if (entry_seqnum != dom_seqnum) {
5052 DEBUG(10, ("Entry has wrong sequence number: %d\n",
5053 (int)entry_seqnum));
5054 goto fail;
5056 entry_timeout = BVAL(data.dptr, 4);
5057 if (time(NULL) > entry_timeout) {
5058 DEBUG(10, ("Entry has timed out\n"));
5059 goto fail;
5063 resp->data = (uint8_t *)talloc_memdup(mem_ctx, data.dptr + 12,
5064 data.dsize - 12);
5065 if (resp->data == NULL) {
5066 DEBUG(10, ("talloc failed\n"));
5067 goto fail;
5069 resp->length = data.dsize - 12;
5071 ret = true;
5072 fail:
5073 SAFE_FREE(data.dptr);
5074 return ret;
5077 void wcache_store_ndr(struct winbindd_domain *domain, uint32_t opnum,
5078 const DATA_BLOB *req, const DATA_BLOB *resp)
5080 TDB_DATA key, data;
5081 uint32_t dom_seqnum, last_check;
5082 uint64_t timeout;
5084 if (!wcache_opnum_cacheable(opnum) ||
5085 is_my_own_sam_domain(domain) ||
5086 is_builtin_domain(domain)) {
5087 return;
5090 if (wcache->tdb == NULL) {
5091 return;
5094 if (!wcache_fetch_seqnum(domain->name, &dom_seqnum, &last_check)) {
5095 DEBUG(10, ("could not fetch seqnum for domain %s\n",
5096 domain->name));
5097 return;
5100 if (!wcache_ndr_key(talloc_tos(), domain->name, opnum, req, &key)) {
5101 return;
5104 timeout = time(NULL) + lp_winbind_cache_time();
5106 data.dsize = resp->length + 12;
5107 data.dptr = talloc_array(key.dptr, uint8_t, data.dsize);
5108 if (data.dptr == NULL) {
5109 goto done;
5112 SIVAL(data.dptr, 0, dom_seqnum);
5113 SBVAL(data.dptr, 4, timeout);
5114 memcpy(data.dptr + 12, resp->data, resp->length);
5116 tdb_store(wcache->tdb, key, data, 0);
5118 done:
5119 TALLOC_FREE(key.dptr);
5120 return;