s3: Call sid_check_is_domain instead of dom_sid_equal
[Samba.git] / source3 / winbindd / winbindd_cache.c
blobc0f44dbb1f6b50a5d6ca794ad66e3d4aed4ca134
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 "winbindd.h"
28 #include "tdb_validate.h"
29 #include "../libcli/auth/libcli_auth.h"
30 #include "../librpc/gen_ndr/ndr_wbint.h"
31 #include "ads.h"
32 #include "nss_info.h"
33 #include "../libcli/security/security.h"
35 #undef DBGC_CLASS
36 #define DBGC_CLASS DBGC_WINBIND
38 #define WINBINDD_CACHE_VERSION 2
39 #define WINBINDD_CACHE_VERSION_KEYSTR "WINBINDD_CACHE_VERSION"
41 extern struct winbindd_methods reconnect_methods;
42 #ifdef HAVE_ADS
43 extern struct winbindd_methods ads_methods;
44 #endif
45 extern struct winbindd_methods builtin_passdb_methods;
46 extern struct winbindd_methods sam_passdb_methods;
49 * JRA. KEEP THIS LIST UP TO DATE IF YOU ADD CACHE ENTRIES.
50 * Here are the list of entry types that are *not* stored
51 * as form struct cache_entry in the cache.
54 static const char *non_centry_keys[] = {
55 "SEQNUM/",
56 "DR/",
57 "DE/",
58 "WINBINDD_OFFLINE",
59 WINBINDD_CACHE_VERSION_KEYSTR,
60 NULL
63 /************************************************************************
64 Is this key a non-centry type ?
65 ************************************************************************/
67 static bool is_non_centry_key(TDB_DATA kbuf)
69 int i;
71 if (kbuf.dptr == NULL || kbuf.dsize == 0) {
72 return false;
74 for (i = 0; non_centry_keys[i] != NULL; i++) {
75 size_t namelen = strlen(non_centry_keys[i]);
76 if (kbuf.dsize < namelen) {
77 continue;
79 if (strncmp(non_centry_keys[i], (const char *)kbuf.dptr, namelen) == 0) {
80 return true;
83 return false;
86 /* Global online/offline state - False when online. winbindd starts up online
87 and sets this to true if the first query fails and there's an entry in
88 the cache tdb telling us to stay offline. */
90 static bool global_winbindd_offline_state;
92 struct winbind_cache {
93 TDB_CONTEXT *tdb;
96 struct cache_entry {
97 NTSTATUS status;
98 uint32 sequence_number;
99 uint64_t timeout;
100 uint8 *data;
101 uint32 len, ofs;
104 void (*smb_panic_fn)(const char *const why) = smb_panic;
106 #define WINBINDD_MAX_CACHE_SIZE (50*1024*1024)
108 static struct winbind_cache *wcache;
110 /* get the winbind_cache structure */
111 static struct winbind_cache *get_cache(struct winbindd_domain *domain)
113 struct winbind_cache *ret = wcache;
115 /* We have to know what type of domain we are dealing with first. */
117 if (domain->internal) {
118 domain->backend = &builtin_passdb_methods;
119 domain->initialized = True;
122 if (strequal(domain->name, get_global_sam_name()) &&
123 sid_check_is_domain(&domain->sid)) {
124 domain->backend = &sam_passdb_methods;
125 domain->initialized = True;
128 if ( !domain->initialized ) {
129 init_dc_connection( domain );
133 OK. listen up becasue I'm only going to say this once.
134 We have the following scenarios to consider
135 (a) trusted AD domains on a Samba DC,
136 (b) trusted AD domains and we are joined to a non-kerberos domain
137 (c) trusted AD domains and we are joined to a kerberos (AD) domain
139 For (a) we can always contact the trusted domain using krb5
140 since we have the domain trust account password
142 For (b) we can only use RPC since we have no way of
143 getting a krb5 ticket in our own domain
145 For (c) we can always use krb5 since we have a kerberos trust
147 --jerry
150 if (!domain->backend) {
151 #ifdef HAVE_ADS
152 struct winbindd_domain *our_domain = domain;
154 /* find our domain first so we can figure out if we
155 are joined to a kerberized domain */
157 if ( !domain->primary )
158 our_domain = find_our_domain();
160 if ((our_domain->active_directory || IS_DC)
161 && domain->active_directory
162 && !lp_winbind_rpc_only()) {
163 DEBUG(5,("get_cache: Setting ADS methods for domain %s\n", domain->name));
164 domain->backend = &ads_methods;
165 } else {
166 #endif /* HAVE_ADS */
167 DEBUG(5,("get_cache: Setting MS-RPC methods for domain %s\n", domain->name));
168 domain->backend = &reconnect_methods;
169 #ifdef HAVE_ADS
171 #endif /* HAVE_ADS */
174 if (ret)
175 return ret;
177 ret = SMB_XMALLOC_P(struct winbind_cache);
178 ZERO_STRUCTP(ret);
180 wcache = ret;
181 wcache_flush_cache();
183 return ret;
187 free a centry structure
189 static void centry_free(struct cache_entry *centry)
191 if (!centry)
192 return;
193 SAFE_FREE(centry->data);
194 free(centry);
197 static bool centry_check_bytes(struct cache_entry *centry, size_t nbytes)
199 if (centry->len - centry->ofs < nbytes) {
200 DEBUG(0,("centry corruption? needed %u bytes, have %d\n",
201 (unsigned int)nbytes,
202 centry->len - centry->ofs));
203 return false;
205 return true;
209 pull a uint64_t from a cache entry
211 static uint64_t centry_uint64_t(struct cache_entry *centry)
213 uint64_t ret;
215 if (!centry_check_bytes(centry, 8)) {
216 smb_panic_fn("centry_uint64_t");
218 ret = BVAL(centry->data, centry->ofs);
219 centry->ofs += 8;
220 return ret;
224 pull a uint32 from a cache entry
226 static uint32 centry_uint32(struct cache_entry *centry)
228 uint32 ret;
230 if (!centry_check_bytes(centry, 4)) {
231 smb_panic_fn("centry_uint32");
233 ret = IVAL(centry->data, centry->ofs);
234 centry->ofs += 4;
235 return ret;
239 pull a uint16 from a cache entry
241 static uint16 centry_uint16(struct cache_entry *centry)
243 uint16 ret;
244 if (!centry_check_bytes(centry, 2)) {
245 smb_panic_fn("centry_uint16");
247 ret = CVAL(centry->data, centry->ofs);
248 centry->ofs += 2;
249 return ret;
253 pull a uint8 from a cache entry
255 static uint8 centry_uint8(struct cache_entry *centry)
257 uint8 ret;
258 if (!centry_check_bytes(centry, 1)) {
259 smb_panic_fn("centry_uint8");
261 ret = CVAL(centry->data, centry->ofs);
262 centry->ofs += 1;
263 return ret;
267 pull a NTTIME from a cache entry
269 static NTTIME centry_nttime(struct cache_entry *centry)
271 NTTIME ret;
272 if (!centry_check_bytes(centry, 8)) {
273 smb_panic_fn("centry_nttime");
275 ret = IVAL(centry->data, centry->ofs);
276 centry->ofs += 4;
277 ret += (uint64)IVAL(centry->data, centry->ofs) << 32;
278 centry->ofs += 4;
279 return ret;
283 pull a time_t from a cache entry. time_t stored portably as a 64-bit time.
285 static time_t centry_time(struct cache_entry *centry)
287 return (time_t)centry_nttime(centry);
290 /* pull a string from a cache entry, using the supplied
291 talloc context
293 static char *centry_string(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
295 uint32 len;
296 char *ret;
298 len = centry_uint8(centry);
300 if (len == 0xFF) {
301 /* a deliberate NULL string */
302 return NULL;
305 if (!centry_check_bytes(centry, (size_t)len)) {
306 smb_panic_fn("centry_string");
309 ret = TALLOC_ARRAY(mem_ctx, char, len+1);
310 if (!ret) {
311 smb_panic_fn("centry_string out of memory\n");
313 memcpy(ret,centry->data + centry->ofs, len);
314 ret[len] = 0;
315 centry->ofs += len;
316 return ret;
319 /* pull a hash16 from a cache entry, using the supplied
320 talloc context
322 static char *centry_hash16(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
324 uint32 len;
325 char *ret;
327 len = centry_uint8(centry);
329 if (len != 16) {
330 DEBUG(0,("centry corruption? hash len (%u) != 16\n",
331 len ));
332 return NULL;
335 if (!centry_check_bytes(centry, 16)) {
336 return NULL;
339 ret = TALLOC_ARRAY(mem_ctx, char, 16);
340 if (!ret) {
341 smb_panic_fn("centry_hash out of memory\n");
343 memcpy(ret,centry->data + centry->ofs, 16);
344 centry->ofs += 16;
345 return ret;
348 /* pull a sid from a cache entry, using the supplied
349 talloc context
351 static bool centry_sid(struct cache_entry *centry, struct dom_sid *sid)
353 char *sid_string;
354 bool ret;
356 sid_string = centry_string(centry, talloc_tos());
357 if (sid_string == NULL) {
358 return false;
360 ret = string_to_sid(sid, sid_string);
361 TALLOC_FREE(sid_string);
362 return ret;
367 pull a NTSTATUS from a cache entry
369 static NTSTATUS centry_ntstatus(struct cache_entry *centry)
371 NTSTATUS status;
373 status = NT_STATUS(centry_uint32(centry));
374 return status;
378 /* the server is considered down if it can't give us a sequence number */
379 static bool wcache_server_down(struct winbindd_domain *domain)
381 bool ret;
383 if (!wcache->tdb)
384 return false;
386 ret = (domain->sequence_number == DOM_SEQUENCE_NONE);
388 if (ret)
389 DEBUG(10,("wcache_server_down: server for Domain %s down\n",
390 domain->name ));
391 return ret;
394 static bool wcache_fetch_seqnum(const char *domain_name, uint32_t *seqnum,
395 uint32_t *last_seq_check)
397 char *key;
398 TDB_DATA data;
400 if (wcache->tdb == NULL) {
401 DEBUG(10,("wcache_fetch_seqnum: tdb == NULL\n"));
402 return false;
405 key = talloc_asprintf(talloc_tos(), "SEQNUM/%s", domain_name);
406 if (key == NULL) {
407 DEBUG(10, ("talloc failed\n"));
408 return false;
411 data = tdb_fetch_bystring(wcache->tdb, key);
412 TALLOC_FREE(key);
414 if (data.dptr == NULL) {
415 DEBUG(10, ("wcache_fetch_seqnum: %s not found\n",
416 domain_name));
417 return false;
419 if (data.dsize != 8) {
420 DEBUG(10, ("wcache_fetch_seqnum: invalid data size %d\n",
421 (int)data.dsize));
422 SAFE_FREE(data.dptr);
423 return false;
426 *seqnum = IVAL(data.dptr, 0);
427 *last_seq_check = IVAL(data.dptr, 4);
428 SAFE_FREE(data.dptr);
430 return true;
433 static NTSTATUS fetch_cache_seqnum( struct winbindd_domain *domain, time_t now )
435 uint32 last_check, time_diff;
437 if (!wcache_fetch_seqnum(domain->name, &domain->sequence_number,
438 &last_check)) {
439 return NT_STATUS_UNSUCCESSFUL;
441 domain->last_seq_check = last_check;
443 /* have we expired? */
445 time_diff = now - domain->last_seq_check;
446 if ( time_diff > lp_winbind_cache_time() ) {
447 DEBUG(10,("fetch_cache_seqnum: timeout [%s][%u @ %u]\n",
448 domain->name, domain->sequence_number,
449 (uint32)domain->last_seq_check));
450 return NT_STATUS_UNSUCCESSFUL;
453 DEBUG(10,("fetch_cache_seqnum: success [%s][%u @ %u]\n",
454 domain->name, domain->sequence_number,
455 (uint32)domain->last_seq_check));
457 return NT_STATUS_OK;
460 bool wcache_store_seqnum(const char *domain_name, uint32_t seqnum,
461 time_t last_seq_check)
463 char *key_str;
464 uint8_t buf[8];
465 int ret;
467 if (wcache->tdb == NULL) {
468 DEBUG(10, ("wcache_store_seqnum: wcache->tdb == NULL\n"));
469 return false;
472 key_str = talloc_asprintf(talloc_tos(), "SEQNUM/%s", domain_name);
473 if (key_str == NULL) {
474 DEBUG(10, ("talloc_asprintf failed\n"));
475 return false;
478 SIVAL(buf, 0, seqnum);
479 SIVAL(buf, 4, last_seq_check);
481 ret = tdb_store_bystring(wcache->tdb, key_str,
482 make_tdb_data(buf, sizeof(buf)), TDB_REPLACE);
483 TALLOC_FREE(key_str);
484 if (ret == -1) {
485 DEBUG(10, ("tdb_store_bystring failed: %s\n",
486 tdb_errorstr(wcache->tdb)));
487 TALLOC_FREE(key_str);
488 return false;
491 DEBUG(10, ("wcache_store_seqnum: success [%s][%u @ %u]\n",
492 domain_name, seqnum, (unsigned)last_seq_check));
494 return true;
497 static bool store_cache_seqnum( struct winbindd_domain *domain )
499 return wcache_store_seqnum(domain->name, domain->sequence_number,
500 domain->last_seq_check);
504 refresh the domain sequence number. If force is true
505 then always refresh it, no matter how recently we fetched it
508 static void refresh_sequence_number(struct winbindd_domain *domain, bool force)
510 NTSTATUS status;
511 unsigned time_diff;
512 time_t t = time(NULL);
513 unsigned cache_time = lp_winbind_cache_time();
515 if (is_domain_offline(domain)) {
516 return;
519 get_cache( domain );
521 #if 0 /* JERRY -- disable as the default cache time is now 5 minutes */
522 /* trying to reconnect is expensive, don't do it too often */
523 if (domain->sequence_number == DOM_SEQUENCE_NONE) {
524 cache_time *= 8;
526 #endif
528 time_diff = t - domain->last_seq_check;
530 /* see if we have to refetch the domain sequence number */
531 if (!force && (time_diff < cache_time) &&
532 (domain->sequence_number != DOM_SEQUENCE_NONE) &&
533 NT_STATUS_IS_OK(domain->last_status)) {
534 DEBUG(10, ("refresh_sequence_number: %s time ok\n", domain->name));
535 goto done;
538 /* try to get the sequence number from the tdb cache first */
539 /* this will update the timestamp as well */
541 status = fetch_cache_seqnum( domain, t );
542 if (NT_STATUS_IS_OK(status) &&
543 (domain->sequence_number != DOM_SEQUENCE_NONE) &&
544 NT_STATUS_IS_OK(domain->last_status)) {
545 goto done;
548 /* important! make sure that we know if this is a native
549 mode domain or not. And that we can contact it. */
551 if ( winbindd_can_contact_domain( domain ) ) {
552 status = domain->backend->sequence_number(domain,
553 &domain->sequence_number);
554 } else {
555 /* just use the current time */
556 status = NT_STATUS_OK;
557 domain->sequence_number = time(NULL);
561 /* the above call could have set our domain->backend to NULL when
562 * coming from offline to online mode, make sure to reinitialize the
563 * backend - Guenther */
564 get_cache( domain );
566 if (!NT_STATUS_IS_OK(status)) {
567 DEBUG(10,("refresh_sequence_number: failed with %s\n", nt_errstr(status)));
568 domain->sequence_number = DOM_SEQUENCE_NONE;
571 domain->last_status = status;
572 domain->last_seq_check = time(NULL);
574 /* save the new sequence number in the cache */
575 store_cache_seqnum( domain );
577 done:
578 DEBUG(10, ("refresh_sequence_number: %s seq number is now %d\n",
579 domain->name, domain->sequence_number));
581 return;
585 decide if a cache entry has expired
587 static bool centry_expired(struct winbindd_domain *domain, const char *keystr, struct cache_entry *centry)
589 /* If we've been told to be offline - stay in that state... */
590 if (lp_winbind_offline_logon() && global_winbindd_offline_state) {
591 DEBUG(10,("centry_expired: Key %s for domain %s valid as winbindd is globally offline.\n",
592 keystr, domain->name ));
593 return false;
596 /* when the domain is offline return the cached entry.
597 * This deals with transient offline states... */
599 if (!domain->online) {
600 DEBUG(10,("centry_expired: Key %s for domain %s valid as domain is offline.\n",
601 keystr, domain->name ));
602 return false;
605 /* if the server is OK and our cache entry came from when it was down then
606 the entry is invalid */
607 if ((domain->sequence_number != DOM_SEQUENCE_NONE) &&
608 (centry->sequence_number == DOM_SEQUENCE_NONE)) {
609 DEBUG(10,("centry_expired: Key %s for domain %s invalid sequence.\n",
610 keystr, domain->name ));
611 return true;
614 /* if the server is down or the cache entry is not older than the
615 current sequence number or it did not timeout then it is OK */
616 if (wcache_server_down(domain)
617 || ((centry->sequence_number == domain->sequence_number)
618 && (centry->timeout > time(NULL)))) {
619 DEBUG(10,("centry_expired: Key %s for domain %s is good.\n",
620 keystr, domain->name ));
621 return false;
624 DEBUG(10,("centry_expired: Key %s for domain %s expired\n",
625 keystr, domain->name ));
627 /* it's expired */
628 return true;
631 static struct cache_entry *wcache_fetch_raw(char *kstr)
633 TDB_DATA data;
634 struct cache_entry *centry;
635 TDB_DATA key;
637 key = string_tdb_data(kstr);
638 data = tdb_fetch(wcache->tdb, key);
639 if (!data.dptr) {
640 /* a cache miss */
641 return NULL;
644 centry = SMB_XMALLOC_P(struct cache_entry);
645 centry->data = (unsigned char *)data.dptr;
646 centry->len = data.dsize;
647 centry->ofs = 0;
649 if (centry->len < 16) {
650 /* huh? corrupt cache? */
651 DEBUG(10,("wcache_fetch_raw: Corrupt cache for key %s "
652 "(len < 16)?\n", kstr));
653 centry_free(centry);
654 return NULL;
657 centry->status = centry_ntstatus(centry);
658 centry->sequence_number = centry_uint32(centry);
659 centry->timeout = centry_uint64_t(centry);
661 return centry;
664 static bool is_my_own_sam_domain(struct winbindd_domain *domain)
666 if (strequal(domain->name, get_global_sam_name()) &&
667 dom_sid_equal(&domain->sid, get_global_sam_sid())) {
668 return true;
671 return false;
674 static bool is_builtin_domain(struct winbindd_domain *domain)
676 if (strequal(domain->name, "BUILTIN") &&
677 dom_sid_equal(&domain->sid, &global_sid_Builtin)) {
678 return true;
681 return false;
685 fetch an entry from the cache, with a varargs key. auto-fetch the sequence
686 number and return status
688 static struct cache_entry *wcache_fetch(struct winbind_cache *cache,
689 struct winbindd_domain *domain,
690 const char *format, ...) PRINTF_ATTRIBUTE(3,4);
691 static struct cache_entry *wcache_fetch(struct winbind_cache *cache,
692 struct winbindd_domain *domain,
693 const char *format, ...)
695 va_list ap;
696 char *kstr;
697 struct cache_entry *centry;
699 if (!winbindd_use_cache() ||
700 is_my_own_sam_domain(domain) ||
701 is_builtin_domain(domain)) {
702 return NULL;
705 refresh_sequence_number(domain, false);
707 va_start(ap, format);
708 smb_xvasprintf(&kstr, format, ap);
709 va_end(ap);
711 centry = wcache_fetch_raw(kstr);
712 if (centry == NULL) {
713 free(kstr);
714 return NULL;
717 if (centry_expired(domain, kstr, centry)) {
719 DEBUG(10,("wcache_fetch: entry %s expired for domain %s\n",
720 kstr, domain->name ));
722 centry_free(centry);
723 free(kstr);
724 return NULL;
727 DEBUG(10,("wcache_fetch: returning entry %s for domain %s\n",
728 kstr, domain->name ));
730 free(kstr);
731 return centry;
734 static void wcache_delete(const char *format, ...) PRINTF_ATTRIBUTE(1,2);
735 static void wcache_delete(const char *format, ...)
737 va_list ap;
738 char *kstr;
739 TDB_DATA key;
741 va_start(ap, format);
742 smb_xvasprintf(&kstr, format, ap);
743 va_end(ap);
745 key = string_tdb_data(kstr);
747 tdb_delete(wcache->tdb, key);
748 free(kstr);
752 make sure we have at least len bytes available in a centry
754 static void centry_expand(struct cache_entry *centry, uint32 len)
756 if (centry->len - centry->ofs >= len)
757 return;
758 centry->len *= 2;
759 centry->data = SMB_REALLOC_ARRAY(centry->data, unsigned char,
760 centry->len);
761 if (!centry->data) {
762 DEBUG(0,("out of memory: needed %d bytes in centry_expand\n", centry->len));
763 smb_panic_fn("out of memory in centry_expand");
768 push a uint64_t into a centry
770 static void centry_put_uint64_t(struct cache_entry *centry, uint64_t v)
772 centry_expand(centry, 8);
773 SBVAL(centry->data, centry->ofs, v);
774 centry->ofs += 8;
778 push a uint32 into a centry
780 static void centry_put_uint32(struct cache_entry *centry, uint32 v)
782 centry_expand(centry, 4);
783 SIVAL(centry->data, centry->ofs, v);
784 centry->ofs += 4;
788 push a uint16 into a centry
790 static void centry_put_uint16(struct cache_entry *centry, uint16 v)
792 centry_expand(centry, 2);
793 SIVAL(centry->data, centry->ofs, v);
794 centry->ofs += 2;
798 push a uint8 into a centry
800 static void centry_put_uint8(struct cache_entry *centry, uint8 v)
802 centry_expand(centry, 1);
803 SCVAL(centry->data, centry->ofs, v);
804 centry->ofs += 1;
808 push a string into a centry
810 static void centry_put_string(struct cache_entry *centry, const char *s)
812 int len;
814 if (!s) {
815 /* null strings are marked as len 0xFFFF */
816 centry_put_uint8(centry, 0xFF);
817 return;
820 len = strlen(s);
821 /* can't handle more than 254 char strings. Truncating is probably best */
822 if (len > 254) {
823 DEBUG(10,("centry_put_string: truncating len (%d) to: 254\n", len));
824 len = 254;
826 centry_put_uint8(centry, len);
827 centry_expand(centry, len);
828 memcpy(centry->data + centry->ofs, s, len);
829 centry->ofs += len;
833 push a 16 byte hash into a centry - treat as 16 byte string.
835 static void centry_put_hash16(struct cache_entry *centry, const uint8 val[16])
837 centry_put_uint8(centry, 16);
838 centry_expand(centry, 16);
839 memcpy(centry->data + centry->ofs, val, 16);
840 centry->ofs += 16;
843 static void centry_put_sid(struct cache_entry *centry, const struct dom_sid *sid)
845 fstring sid_string;
846 centry_put_string(centry, sid_to_fstring(sid_string, sid));
851 put NTSTATUS into a centry
853 static void centry_put_ntstatus(struct cache_entry *centry, NTSTATUS status)
855 uint32 status_value = NT_STATUS_V(status);
856 centry_put_uint32(centry, status_value);
861 push a NTTIME into a centry
863 static void centry_put_nttime(struct cache_entry *centry, NTTIME nt)
865 centry_expand(centry, 8);
866 SIVAL(centry->data, centry->ofs, nt & 0xFFFFFFFF);
867 centry->ofs += 4;
868 SIVAL(centry->data, centry->ofs, nt >> 32);
869 centry->ofs += 4;
873 push a time_t into a centry - use a 64 bit size.
874 NTTIME here is being used as a convenient 64-bit size.
876 static void centry_put_time(struct cache_entry *centry, time_t t)
878 NTTIME nt = (NTTIME)t;
879 centry_put_nttime(centry, nt);
883 start a centry for output. When finished, call centry_end()
885 struct cache_entry *centry_start(struct winbindd_domain *domain, NTSTATUS status)
887 struct cache_entry *centry;
889 if (!wcache->tdb)
890 return NULL;
892 centry = SMB_XMALLOC_P(struct cache_entry);
894 centry->len = 8192; /* reasonable default */
895 centry->data = SMB_XMALLOC_ARRAY(uint8, centry->len);
896 centry->ofs = 0;
897 centry->sequence_number = domain->sequence_number;
898 centry->timeout = lp_winbind_cache_time() + time(NULL);
899 centry_put_ntstatus(centry, status);
900 centry_put_uint32(centry, centry->sequence_number);
901 centry_put_uint64_t(centry, centry->timeout);
902 return centry;
906 finish a centry and write it to the tdb
908 static void centry_end(struct cache_entry *centry, const char *format, ...) PRINTF_ATTRIBUTE(2,3);
909 static void centry_end(struct cache_entry *centry, const char *format, ...)
911 va_list ap;
912 char *kstr;
913 TDB_DATA key, data;
915 if (!winbindd_use_cache()) {
916 return;
919 va_start(ap, format);
920 smb_xvasprintf(&kstr, format, ap);
921 va_end(ap);
923 key = string_tdb_data(kstr);
924 data.dptr = centry->data;
925 data.dsize = centry->ofs;
927 tdb_store(wcache->tdb, key, data, TDB_REPLACE);
928 free(kstr);
931 static void wcache_save_name_to_sid(struct winbindd_domain *domain,
932 NTSTATUS status, const char *domain_name,
933 const char *name, const struct dom_sid *sid,
934 enum lsa_SidType type)
936 struct cache_entry *centry;
937 fstring uname;
939 centry = centry_start(domain, status);
940 if (!centry)
941 return;
942 centry_put_uint32(centry, type);
943 centry_put_sid(centry, sid);
944 fstrcpy(uname, name);
945 strupper_m(uname);
946 centry_end(centry, "NS/%s/%s", domain_name, uname);
947 DEBUG(10,("wcache_save_name_to_sid: %s\\%s -> %s (%s)\n", domain_name,
948 uname, sid_string_dbg(sid), nt_errstr(status)));
949 centry_free(centry);
952 static void wcache_save_sid_to_name(struct winbindd_domain *domain, NTSTATUS status,
953 const struct dom_sid *sid, const char *domain_name, const char *name, enum lsa_SidType type)
955 struct cache_entry *centry;
956 fstring sid_string;
958 centry = centry_start(domain, status);
959 if (!centry)
960 return;
962 if (NT_STATUS_IS_OK(status)) {
963 centry_put_uint32(centry, type);
964 centry_put_string(centry, domain_name);
965 centry_put_string(centry, name);
968 centry_end(centry, "SN/%s", sid_to_fstring(sid_string, sid));
969 DEBUG(10,("wcache_save_sid_to_name: %s -> %s (%s)\n", sid_string,
970 name, nt_errstr(status)));
971 centry_free(centry);
975 static void wcache_save_user(struct winbindd_domain *domain, NTSTATUS status,
976 struct wbint_userinfo *info)
978 struct cache_entry *centry;
979 fstring sid_string;
981 if (is_null_sid(&info->user_sid)) {
982 return;
985 centry = centry_start(domain, status);
986 if (!centry)
987 return;
988 centry_put_string(centry, info->acct_name);
989 centry_put_string(centry, info->full_name);
990 centry_put_string(centry, info->homedir);
991 centry_put_string(centry, info->shell);
992 centry_put_uint32(centry, info->primary_gid);
993 centry_put_sid(centry, &info->user_sid);
994 centry_put_sid(centry, &info->group_sid);
995 centry_end(centry, "U/%s", sid_to_fstring(sid_string,
996 &info->user_sid));
997 DEBUG(10,("wcache_save_user: %s (acct_name %s)\n", sid_string, info->acct_name));
998 centry_free(centry);
1001 static void wcache_save_lockout_policy(struct winbindd_domain *domain,
1002 NTSTATUS status,
1003 struct samr_DomInfo12 *lockout_policy)
1005 struct cache_entry *centry;
1007 centry = centry_start(domain, status);
1008 if (!centry)
1009 return;
1011 centry_put_nttime(centry, lockout_policy->lockout_duration);
1012 centry_put_nttime(centry, lockout_policy->lockout_window);
1013 centry_put_uint16(centry, lockout_policy->lockout_threshold);
1015 centry_end(centry, "LOC_POL/%s", domain->name);
1017 DEBUG(10,("wcache_save_lockout_policy: %s\n", domain->name));
1019 centry_free(centry);
1024 static void wcache_save_password_policy(struct winbindd_domain *domain,
1025 NTSTATUS status,
1026 struct samr_DomInfo1 *policy)
1028 struct cache_entry *centry;
1030 centry = centry_start(domain, status);
1031 if (!centry)
1032 return;
1034 centry_put_uint16(centry, policy->min_password_length);
1035 centry_put_uint16(centry, policy->password_history_length);
1036 centry_put_uint32(centry, policy->password_properties);
1037 centry_put_nttime(centry, policy->max_password_age);
1038 centry_put_nttime(centry, policy->min_password_age);
1040 centry_end(centry, "PWD_POL/%s", domain->name);
1042 DEBUG(10,("wcache_save_password_policy: %s\n", domain->name));
1044 centry_free(centry);
1047 /***************************************************************************
1048 ***************************************************************************/
1050 static void wcache_save_username_alias(struct winbindd_domain *domain,
1051 NTSTATUS status,
1052 const char *name, const char *alias)
1054 struct cache_entry *centry;
1055 fstring uname;
1057 if ( (centry = centry_start(domain, status)) == NULL )
1058 return;
1060 centry_put_string( centry, alias );
1062 fstrcpy(uname, name);
1063 strupper_m(uname);
1064 centry_end(centry, "NSS/NA/%s", uname);
1066 DEBUG(10,("wcache_save_username_alias: %s -> %s\n", name, alias ));
1068 centry_free(centry);
1071 static void wcache_save_alias_username(struct winbindd_domain *domain,
1072 NTSTATUS status,
1073 const char *alias, const char *name)
1075 struct cache_entry *centry;
1076 fstring uname;
1078 if ( (centry = centry_start(domain, status)) == NULL )
1079 return;
1081 centry_put_string( centry, name );
1083 fstrcpy(uname, alias);
1084 strupper_m(uname);
1085 centry_end(centry, "NSS/AN/%s", uname);
1087 DEBUG(10,("wcache_save_alias_username: %s -> %s\n", alias, name ));
1089 centry_free(centry);
1092 /***************************************************************************
1093 ***************************************************************************/
1095 NTSTATUS resolve_username_to_alias( TALLOC_CTX *mem_ctx,
1096 struct winbindd_domain *domain,
1097 const char *name, char **alias )
1099 struct winbind_cache *cache = get_cache(domain);
1100 struct cache_entry *centry = NULL;
1101 NTSTATUS status;
1102 char *upper_name;
1104 if ( domain->internal )
1105 return NT_STATUS_NOT_SUPPORTED;
1107 if (!cache->tdb)
1108 goto do_query;
1110 if ( (upper_name = SMB_STRDUP(name)) == NULL )
1111 return NT_STATUS_NO_MEMORY;
1112 strupper_m(upper_name);
1114 centry = wcache_fetch(cache, domain, "NSS/NA/%s", upper_name);
1116 SAFE_FREE( upper_name );
1118 if (!centry)
1119 goto do_query;
1121 status = centry->status;
1123 if (!NT_STATUS_IS_OK(status)) {
1124 centry_free(centry);
1125 return status;
1128 *alias = centry_string( centry, mem_ctx );
1130 centry_free(centry);
1132 DEBUG(10,("resolve_username_to_alias: [Cached] - mapped %s to %s\n",
1133 name, *alias ? *alias : "(none)"));
1135 return (*alias) ? NT_STATUS_OK : NT_STATUS_OBJECT_NAME_NOT_FOUND;
1137 do_query:
1139 /* If its not in cache and we are offline, then fail */
1141 if ( get_global_winbindd_state_offline() || !domain->online ) {
1142 DEBUG(8,("resolve_username_to_alias: rejecting query "
1143 "in offline mode\n"));
1144 return NT_STATUS_NOT_FOUND;
1147 status = nss_map_to_alias( mem_ctx, domain->name, name, alias );
1149 if ( NT_STATUS_IS_OK( status ) ) {
1150 wcache_save_username_alias(domain, status, name, *alias);
1153 if ( NT_STATUS_EQUAL( status, NT_STATUS_NONE_MAPPED ) ) {
1154 wcache_save_username_alias(domain, status, name, "(NULL)");
1157 DEBUG(5,("resolve_username_to_alias: backend query returned %s\n",
1158 nt_errstr(status)));
1160 if ( NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ) {
1161 set_domain_offline( domain );
1164 return status;
1167 /***************************************************************************
1168 ***************************************************************************/
1170 NTSTATUS resolve_alias_to_username( TALLOC_CTX *mem_ctx,
1171 struct winbindd_domain *domain,
1172 const char *alias, char **name )
1174 struct winbind_cache *cache = get_cache(domain);
1175 struct cache_entry *centry = NULL;
1176 NTSTATUS status;
1177 char *upper_name;
1179 if ( domain->internal )
1180 return NT_STATUS_NOT_SUPPORTED;
1182 if (!cache->tdb)
1183 goto do_query;
1185 if ( (upper_name = SMB_STRDUP(alias)) == NULL )
1186 return NT_STATUS_NO_MEMORY;
1187 strupper_m(upper_name);
1189 centry = wcache_fetch(cache, domain, "NSS/AN/%s", upper_name);
1191 SAFE_FREE( upper_name );
1193 if (!centry)
1194 goto do_query;
1196 status = centry->status;
1198 if (!NT_STATUS_IS_OK(status)) {
1199 centry_free(centry);
1200 return status;
1203 *name = centry_string( centry, mem_ctx );
1205 centry_free(centry);
1207 DEBUG(10,("resolve_alias_to_username: [Cached] - mapped %s to %s\n",
1208 alias, *name ? *name : "(none)"));
1210 return (*name) ? NT_STATUS_OK : NT_STATUS_OBJECT_NAME_NOT_FOUND;
1212 do_query:
1214 /* If its not in cache and we are offline, then fail */
1216 if ( get_global_winbindd_state_offline() || !domain->online ) {
1217 DEBUG(8,("resolve_alias_to_username: rejecting query "
1218 "in offline mode\n"));
1219 return NT_STATUS_NOT_FOUND;
1222 /* an alias cannot contain a domain prefix or '@' */
1224 if (strchr(alias, '\\') || strchr(alias, '@')) {
1225 DEBUG(10,("resolve_alias_to_username: skipping fully "
1226 "qualified name %s\n", alias));
1227 return NT_STATUS_OBJECT_NAME_INVALID;
1230 status = nss_map_from_alias( mem_ctx, domain->name, alias, name );
1232 if ( NT_STATUS_IS_OK( status ) ) {
1233 wcache_save_alias_username( domain, status, alias, *name );
1236 if (NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) {
1237 wcache_save_alias_username(domain, status, alias, "(NULL)");
1240 DEBUG(5,("resolve_alias_to_username: backend query returned %s\n",
1241 nt_errstr(status)));
1243 if ( NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ) {
1244 set_domain_offline( domain );
1247 return status;
1250 NTSTATUS wcache_cached_creds_exist(struct winbindd_domain *domain, const struct dom_sid *sid)
1252 struct winbind_cache *cache = get_cache(domain);
1253 TDB_DATA data;
1254 fstring key_str, tmp;
1255 uint32 rid;
1257 if (!cache->tdb) {
1258 return NT_STATUS_INTERNAL_DB_ERROR;
1261 if (is_null_sid(sid)) {
1262 return NT_STATUS_INVALID_SID;
1265 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1266 return NT_STATUS_INVALID_SID;
1269 fstr_sprintf(key_str, "CRED/%s", sid_to_fstring(tmp, sid));
1271 data = tdb_fetch(cache->tdb, string_tdb_data(key_str));
1272 if (!data.dptr) {
1273 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1276 SAFE_FREE(data.dptr);
1277 return NT_STATUS_OK;
1280 /* Lookup creds for a SID - copes with old (unsalted) creds as well
1281 as new salted ones. */
1283 NTSTATUS wcache_get_creds(struct winbindd_domain *domain,
1284 TALLOC_CTX *mem_ctx,
1285 const struct dom_sid *sid,
1286 const uint8 **cached_nt_pass,
1287 const uint8 **cached_salt)
1289 struct winbind_cache *cache = get_cache(domain);
1290 struct cache_entry *centry = NULL;
1291 NTSTATUS status;
1292 time_t t;
1293 uint32 rid;
1294 fstring tmp;
1296 if (!cache->tdb) {
1297 return NT_STATUS_INTERNAL_DB_ERROR;
1300 if (is_null_sid(sid)) {
1301 return NT_STATUS_INVALID_SID;
1304 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1305 return NT_STATUS_INVALID_SID;
1308 /* Try and get a salted cred first. If we can't
1309 fall back to an unsalted cred. */
1311 centry = wcache_fetch(cache, domain, "CRED/%s",
1312 sid_to_fstring(tmp, sid));
1313 if (!centry) {
1314 DEBUG(10,("wcache_get_creds: entry for [CRED/%s] not found\n",
1315 sid_string_dbg(sid)));
1316 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1319 t = centry_time(centry);
1321 /* In the salted case this isn't actually the nt_hash itself,
1322 but the MD5 of the salt + nt_hash. Let the caller
1323 sort this out. It can tell as we only return the cached_salt
1324 if we are returning a salted cred. */
1326 *cached_nt_pass = (const uint8 *)centry_hash16(centry, mem_ctx);
1327 if (*cached_nt_pass == NULL) {
1328 fstring sidstr;
1330 sid_to_fstring(sidstr, sid);
1332 /* Bad (old) cred cache. Delete and pretend we
1333 don't have it. */
1334 DEBUG(0,("wcache_get_creds: bad entry for [CRED/%s] - deleting\n",
1335 sidstr));
1336 wcache_delete("CRED/%s", sidstr);
1337 centry_free(centry);
1338 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1341 /* We only have 17 bytes more data in the salted cred case. */
1342 if (centry->len - centry->ofs == 17) {
1343 *cached_salt = (const uint8 *)centry_hash16(centry, mem_ctx);
1344 } else {
1345 *cached_salt = NULL;
1348 dump_data_pw("cached_nt_pass", *cached_nt_pass, NT_HASH_LEN);
1349 if (*cached_salt) {
1350 dump_data_pw("cached_salt", *cached_salt, NT_HASH_LEN);
1353 status = centry->status;
1355 DEBUG(10,("wcache_get_creds: [Cached] - cached creds for user %s status: %s\n",
1356 sid_string_dbg(sid), nt_errstr(status) ));
1358 centry_free(centry);
1359 return status;
1362 /* Store creds for a SID - only writes out new salted ones. */
1364 NTSTATUS wcache_save_creds(struct winbindd_domain *domain,
1365 const struct dom_sid *sid,
1366 const uint8 nt_pass[NT_HASH_LEN])
1368 struct cache_entry *centry;
1369 fstring sid_string;
1370 uint32 rid;
1371 uint8 cred_salt[NT_HASH_LEN];
1372 uint8 salted_hash[NT_HASH_LEN];
1374 if (is_null_sid(sid)) {
1375 return NT_STATUS_INVALID_SID;
1378 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1379 return NT_STATUS_INVALID_SID;
1382 centry = centry_start(domain, NT_STATUS_OK);
1383 if (!centry) {
1384 return NT_STATUS_INTERNAL_DB_ERROR;
1387 dump_data_pw("nt_pass", nt_pass, NT_HASH_LEN);
1389 centry_put_time(centry, time(NULL));
1391 /* Create a salt and then salt the hash. */
1392 generate_random_buffer(cred_salt, NT_HASH_LEN);
1393 E_md5hash(cred_salt, nt_pass, salted_hash);
1395 centry_put_hash16(centry, salted_hash);
1396 centry_put_hash16(centry, cred_salt);
1397 centry_end(centry, "CRED/%s", sid_to_fstring(sid_string, sid));
1399 DEBUG(10,("wcache_save_creds: %s\n", sid_string));
1401 centry_free(centry);
1403 return NT_STATUS_OK;
1407 /* Query display info. This is the basic user list fn */
1408 static NTSTATUS query_user_list(struct winbindd_domain *domain,
1409 TALLOC_CTX *mem_ctx,
1410 uint32 *num_entries,
1411 struct wbint_userinfo **info)
1413 struct winbind_cache *cache = get_cache(domain);
1414 struct cache_entry *centry = NULL;
1415 NTSTATUS status;
1416 unsigned int i, retry;
1417 bool old_status = domain->online;
1419 if (!cache->tdb)
1420 goto do_query;
1422 centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
1423 if (!centry)
1424 goto do_query;
1426 do_fetch_cache:
1427 *num_entries = centry_uint32(centry);
1429 if (*num_entries == 0)
1430 goto do_cached;
1432 (*info) = TALLOC_ARRAY(mem_ctx, struct wbint_userinfo, *num_entries);
1433 if (! (*info)) {
1434 smb_panic_fn("query_user_list out of memory");
1436 for (i=0; i<(*num_entries); i++) {
1437 (*info)[i].acct_name = centry_string(centry, mem_ctx);
1438 (*info)[i].full_name = centry_string(centry, mem_ctx);
1439 (*info)[i].homedir = centry_string(centry, mem_ctx);
1440 (*info)[i].shell = centry_string(centry, mem_ctx);
1441 centry_sid(centry, &(*info)[i].user_sid);
1442 centry_sid(centry, &(*info)[i].group_sid);
1445 do_cached:
1446 status = centry->status;
1448 DEBUG(10,("query_user_list: [Cached] - cached list for domain %s status: %s\n",
1449 domain->name, nt_errstr(status) ));
1451 centry_free(centry);
1452 return status;
1454 do_query:
1455 *num_entries = 0;
1456 *info = NULL;
1458 /* Return status value returned by seq number check */
1460 if (!NT_STATUS_IS_OK(domain->last_status))
1461 return domain->last_status;
1463 /* Put the query_user_list() in a retry loop. There appears to be
1464 * some bug either with Windows 2000 or Samba's handling of large
1465 * rpc replies. This manifests itself as sudden disconnection
1466 * at a random point in the enumeration of a large (60k) user list.
1467 * The retry loop simply tries the operation again. )-: It's not
1468 * pretty but an acceptable workaround until we work out what the
1469 * real problem is. */
1471 retry = 0;
1472 do {
1474 DEBUG(10,("query_user_list: [Cached] - doing backend query for list for domain %s\n",
1475 domain->name ));
1477 status = domain->backend->query_user_list(domain, mem_ctx, num_entries, info);
1478 if (!NT_STATUS_IS_OK(status)) {
1479 DEBUG(3, ("query_user_list: returned 0x%08x, "
1480 "retrying\n", NT_STATUS_V(status)));
1482 if (NT_STATUS_EQUAL(status, NT_STATUS_UNSUCCESSFUL)) {
1483 DEBUG(3, ("query_user_list: flushing "
1484 "connection cache\n"));
1485 invalidate_cm_connection(&domain->conn);
1487 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1488 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1489 if (!domain->internal && old_status) {
1490 set_domain_offline(domain);
1492 /* store partial response. */
1493 if (*num_entries > 0) {
1495 * humm, what about the status used for cache?
1496 * Should it be NT_STATUS_OK?
1498 break;
1501 * domain is offline now, and there is no user entries,
1502 * try to fetch from cache again.
1504 if (cache->tdb && !domain->online && !domain->internal && old_status) {
1505 centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
1506 /* partial response... */
1507 if (!centry) {
1508 goto skip_save;
1509 } else {
1510 goto do_fetch_cache;
1512 } else {
1513 goto skip_save;
1517 } while (NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_UNSUCCESSFUL) &&
1518 (retry++ < 5));
1520 /* and save it */
1521 refresh_sequence_number(domain, false);
1522 if (!NT_STATUS_IS_OK(status)) {
1523 return status;
1525 centry = centry_start(domain, status);
1526 if (!centry)
1527 goto skip_save;
1528 centry_put_uint32(centry, *num_entries);
1529 for (i=0; i<(*num_entries); i++) {
1530 centry_put_string(centry, (*info)[i].acct_name);
1531 centry_put_string(centry, (*info)[i].full_name);
1532 centry_put_string(centry, (*info)[i].homedir);
1533 centry_put_string(centry, (*info)[i].shell);
1534 centry_put_sid(centry, &(*info)[i].user_sid);
1535 centry_put_sid(centry, &(*info)[i].group_sid);
1536 if (domain->backend && domain->backend->consistent) {
1537 /* when the backend is consistent we can pre-prime some mappings */
1538 wcache_save_name_to_sid(domain, NT_STATUS_OK,
1539 domain->name,
1540 (*info)[i].acct_name,
1541 &(*info)[i].user_sid,
1542 SID_NAME_USER);
1543 wcache_save_sid_to_name(domain, NT_STATUS_OK,
1544 &(*info)[i].user_sid,
1545 domain->name,
1546 (*info)[i].acct_name,
1547 SID_NAME_USER);
1548 wcache_save_user(domain, NT_STATUS_OK, &(*info)[i]);
1551 centry_end(centry, "UL/%s", domain->name);
1552 centry_free(centry);
1554 skip_save:
1555 return status;
1558 /* list all domain groups */
1559 static NTSTATUS enum_dom_groups(struct winbindd_domain *domain,
1560 TALLOC_CTX *mem_ctx,
1561 uint32 *num_entries,
1562 struct acct_info **info)
1564 struct winbind_cache *cache = get_cache(domain);
1565 struct cache_entry *centry = NULL;
1566 NTSTATUS status;
1567 unsigned int i;
1568 bool old_status;
1570 old_status = domain->online;
1571 if (!cache->tdb)
1572 goto do_query;
1574 centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
1575 if (!centry)
1576 goto do_query;
1578 do_fetch_cache:
1579 *num_entries = centry_uint32(centry);
1581 if (*num_entries == 0)
1582 goto do_cached;
1584 (*info) = TALLOC_ARRAY(mem_ctx, struct acct_info, *num_entries);
1585 if (! (*info)) {
1586 smb_panic_fn("enum_dom_groups out of memory");
1588 for (i=0; i<(*num_entries); i++) {
1589 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1590 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1591 (*info)[i].rid = centry_uint32(centry);
1594 do_cached:
1595 status = centry->status;
1597 DEBUG(10,("enum_dom_groups: [Cached] - cached list for domain %s status: %s\n",
1598 domain->name, nt_errstr(status) ));
1600 centry_free(centry);
1601 return status;
1603 do_query:
1604 *num_entries = 0;
1605 *info = NULL;
1607 /* Return status value returned by seq number check */
1609 if (!NT_STATUS_IS_OK(domain->last_status))
1610 return domain->last_status;
1612 DEBUG(10,("enum_dom_groups: [Cached] - doing backend query for list for domain %s\n",
1613 domain->name ));
1615 status = domain->backend->enum_dom_groups(domain, mem_ctx, num_entries, info);
1617 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1618 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1619 if (!domain->internal && old_status) {
1620 set_domain_offline(domain);
1622 if (cache->tdb &&
1623 !domain->online &&
1624 !domain->internal &&
1625 old_status) {
1626 centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
1627 if (centry) {
1628 goto do_fetch_cache;
1632 /* and save it */
1633 refresh_sequence_number(domain, false);
1634 if (!NT_STATUS_IS_OK(status)) {
1635 return status;
1637 centry = centry_start(domain, status);
1638 if (!centry)
1639 goto skip_save;
1640 centry_put_uint32(centry, *num_entries);
1641 for (i=0; i<(*num_entries); i++) {
1642 centry_put_string(centry, (*info)[i].acct_name);
1643 centry_put_string(centry, (*info)[i].acct_desc);
1644 centry_put_uint32(centry, (*info)[i].rid);
1646 centry_end(centry, "GL/%s/domain", domain->name);
1647 centry_free(centry);
1649 skip_save:
1650 return status;
1653 /* list all domain groups */
1654 static NTSTATUS enum_local_groups(struct winbindd_domain *domain,
1655 TALLOC_CTX *mem_ctx,
1656 uint32 *num_entries,
1657 struct acct_info **info)
1659 struct winbind_cache *cache = get_cache(domain);
1660 struct cache_entry *centry = NULL;
1661 NTSTATUS status;
1662 unsigned int i;
1663 bool old_status;
1665 old_status = domain->online;
1666 if (!cache->tdb)
1667 goto do_query;
1669 centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
1670 if (!centry)
1671 goto do_query;
1673 do_fetch_cache:
1674 *num_entries = centry_uint32(centry);
1676 if (*num_entries == 0)
1677 goto do_cached;
1679 (*info) = TALLOC_ARRAY(mem_ctx, struct acct_info, *num_entries);
1680 if (! (*info)) {
1681 smb_panic_fn("enum_dom_groups out of memory");
1683 for (i=0; i<(*num_entries); i++) {
1684 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1685 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1686 (*info)[i].rid = centry_uint32(centry);
1689 do_cached:
1691 /* If we are returning cached data and the domain controller
1692 is down then we don't know whether the data is up to date
1693 or not. Return NT_STATUS_MORE_PROCESSING_REQUIRED to
1694 indicate this. */
1696 if (wcache_server_down(domain)) {
1697 DEBUG(10, ("enum_local_groups: returning cached user list and server was down\n"));
1698 status = NT_STATUS_MORE_PROCESSING_REQUIRED;
1699 } else
1700 status = centry->status;
1702 DEBUG(10,("enum_local_groups: [Cached] - cached list for domain %s status: %s\n",
1703 domain->name, nt_errstr(status) ));
1705 centry_free(centry);
1706 return status;
1708 do_query:
1709 *num_entries = 0;
1710 *info = NULL;
1712 /* Return status value returned by seq number check */
1714 if (!NT_STATUS_IS_OK(domain->last_status))
1715 return domain->last_status;
1717 DEBUG(10,("enum_local_groups: [Cached] - doing backend query for list for domain %s\n",
1718 domain->name ));
1720 status = domain->backend->enum_local_groups(domain, mem_ctx, num_entries, info);
1722 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1723 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1724 if (!domain->internal && old_status) {
1725 set_domain_offline(domain);
1727 if (cache->tdb &&
1728 !domain->internal &&
1729 !domain->online &&
1730 old_status) {
1731 centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
1732 if (centry) {
1733 goto do_fetch_cache;
1737 /* and save it */
1738 refresh_sequence_number(domain, false);
1739 if (!NT_STATUS_IS_OK(status)) {
1740 return status;
1742 centry = centry_start(domain, status);
1743 if (!centry)
1744 goto skip_save;
1745 centry_put_uint32(centry, *num_entries);
1746 for (i=0; i<(*num_entries); i++) {
1747 centry_put_string(centry, (*info)[i].acct_name);
1748 centry_put_string(centry, (*info)[i].acct_desc);
1749 centry_put_uint32(centry, (*info)[i].rid);
1751 centry_end(centry, "GL/%s/local", domain->name);
1752 centry_free(centry);
1754 skip_save:
1755 return status;
1758 NTSTATUS wcache_name_to_sid(struct winbindd_domain *domain,
1759 const char *domain_name,
1760 const char *name,
1761 struct dom_sid *sid,
1762 enum lsa_SidType *type)
1764 struct winbind_cache *cache = get_cache(domain);
1765 struct cache_entry *centry;
1766 NTSTATUS status;
1767 char *uname;
1769 if (cache->tdb == NULL) {
1770 return NT_STATUS_NOT_FOUND;
1773 uname = talloc_strdup_upper(talloc_tos(), name);
1774 if (uname == NULL) {
1775 return NT_STATUS_NO_MEMORY;
1778 centry = wcache_fetch(cache, domain, "NS/%s/%s", domain_name, uname);
1779 TALLOC_FREE(uname);
1780 if (centry == NULL) {
1781 return NT_STATUS_NOT_FOUND;
1784 status = centry->status;
1785 if (NT_STATUS_IS_OK(status)) {
1786 *type = (enum lsa_SidType)centry_uint32(centry);
1787 centry_sid(centry, sid);
1790 DEBUG(10,("name_to_sid: [Cached] - cached name for domain %s status: "
1791 "%s\n", domain->name, nt_errstr(status) ));
1793 centry_free(centry);
1794 return status;
1797 /* convert a single name to a sid in a domain */
1798 static NTSTATUS name_to_sid(struct winbindd_domain *domain,
1799 TALLOC_CTX *mem_ctx,
1800 const char *domain_name,
1801 const char *name,
1802 uint32_t flags,
1803 struct dom_sid *sid,
1804 enum lsa_SidType *type)
1806 NTSTATUS status;
1807 bool old_status;
1809 old_status = domain->online;
1811 status = wcache_name_to_sid(domain, domain_name, name, sid, type);
1812 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
1813 return status;
1816 ZERO_STRUCTP(sid);
1818 /* If the seq number check indicated that there is a problem
1819 * with this DC, then return that status... except for
1820 * access_denied. This is special because the dc may be in
1821 * "restrict anonymous = 1" mode, in which case it will deny
1822 * most unauthenticated operations, but *will* allow the LSA
1823 * name-to-sid that we try as a fallback. */
1825 if (!(NT_STATUS_IS_OK(domain->last_status)
1826 || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1827 return domain->last_status;
1829 DEBUG(10,("name_to_sid: [Cached] - doing backend query for name for domain %s\n",
1830 domain->name ));
1832 status = domain->backend->name_to_sid(domain, mem_ctx, domain_name,
1833 name, flags, sid, type);
1835 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1836 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1837 if (!domain->internal && old_status) {
1838 set_domain_offline(domain);
1840 if (!domain->internal &&
1841 !domain->online &&
1842 old_status) {
1843 NTSTATUS cache_status;
1844 cache_status = wcache_name_to_sid(domain, domain_name, name, sid, type);
1845 return cache_status;
1848 /* and save it */
1849 refresh_sequence_number(domain, false);
1851 if (domain->online &&
1852 (NT_STATUS_IS_OK(status) || NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED))) {
1853 wcache_save_name_to_sid(domain, status, domain_name, name, sid, *type);
1855 /* Only save the reverse mapping if this was not a UPN */
1856 if (!strchr(name, '@')) {
1857 strupper_m(CONST_DISCARD(char *,domain_name));
1858 strlower_m(CONST_DISCARD(char *,name));
1859 wcache_save_sid_to_name(domain, status, sid, domain_name, name, *type);
1863 return status;
1866 NTSTATUS wcache_sid_to_name(struct winbindd_domain *domain,
1867 const struct dom_sid *sid,
1868 TALLOC_CTX *mem_ctx,
1869 char **domain_name,
1870 char **name,
1871 enum lsa_SidType *type)
1873 struct winbind_cache *cache = get_cache(domain);
1874 struct cache_entry *centry;
1875 char *sid_string;
1876 NTSTATUS status;
1878 if (cache->tdb == NULL) {
1879 return NT_STATUS_NOT_FOUND;
1882 sid_string = sid_string_tos(sid);
1883 if (sid_string == NULL) {
1884 return NT_STATUS_NO_MEMORY;
1887 centry = wcache_fetch(cache, domain, "SN/%s", sid_string);
1888 TALLOC_FREE(sid_string);
1889 if (centry == NULL) {
1890 return NT_STATUS_NOT_FOUND;
1893 if (NT_STATUS_IS_OK(centry->status)) {
1894 *type = (enum lsa_SidType)centry_uint32(centry);
1895 *domain_name = centry_string(centry, mem_ctx);
1896 *name = centry_string(centry, mem_ctx);
1899 status = centry->status;
1900 centry_free(centry);
1902 DEBUG(10,("sid_to_name: [Cached] - cached name for domain %s status: "
1903 "%s\n", domain->name, nt_errstr(status) ));
1905 return status;
1908 /* convert a sid to a user or group name. The sid is guaranteed to be in the domain
1909 given */
1910 static NTSTATUS sid_to_name(struct winbindd_domain *domain,
1911 TALLOC_CTX *mem_ctx,
1912 const struct dom_sid *sid,
1913 char **domain_name,
1914 char **name,
1915 enum lsa_SidType *type)
1917 NTSTATUS status;
1918 bool old_status;
1920 old_status = domain->online;
1921 status = wcache_sid_to_name(domain, sid, mem_ctx, domain_name, name,
1922 type);
1923 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
1924 return status;
1927 *name = NULL;
1928 *domain_name = NULL;
1930 /* If the seq number check indicated that there is a problem
1931 * with this DC, then return that status... except for
1932 * access_denied. This is special because the dc may be in
1933 * "restrict anonymous = 1" mode, in which case it will deny
1934 * most unauthenticated operations, but *will* allow the LSA
1935 * sid-to-name that we try as a fallback. */
1937 if (!(NT_STATUS_IS_OK(domain->last_status)
1938 || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1939 return domain->last_status;
1941 DEBUG(10,("sid_to_name: [Cached] - doing backend query for name for domain %s\n",
1942 domain->name ));
1944 status = domain->backend->sid_to_name(domain, mem_ctx, sid, domain_name, name, type);
1946 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1947 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1948 if (!domain->internal && old_status) {
1949 set_domain_offline(domain);
1951 if (!domain->internal &&
1952 !domain->online &&
1953 old_status) {
1954 NTSTATUS cache_status;
1955 cache_status = wcache_sid_to_name(domain, sid, mem_ctx,
1956 domain_name, name, type);
1957 return cache_status;
1960 /* and save it */
1961 refresh_sequence_number(domain, false);
1962 if (!NT_STATUS_IS_OK(status)) {
1963 return status;
1965 wcache_save_sid_to_name(domain, status, sid, *domain_name, *name, *type);
1967 /* We can't save the name to sid mapping here, as with sid history a
1968 * later name2sid would give the wrong sid. */
1970 return status;
1973 static NTSTATUS rids_to_names(struct winbindd_domain *domain,
1974 TALLOC_CTX *mem_ctx,
1975 const struct dom_sid *domain_sid,
1976 uint32 *rids,
1977 size_t num_rids,
1978 char **domain_name,
1979 char ***names,
1980 enum lsa_SidType **types)
1982 struct winbind_cache *cache = get_cache(domain);
1983 size_t i;
1984 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
1985 bool have_mapped;
1986 bool have_unmapped;
1987 bool old_status;
1989 old_status = domain->online;
1990 *domain_name = NULL;
1991 *names = NULL;
1992 *types = NULL;
1994 if (!cache->tdb) {
1995 goto do_query;
1998 if (num_rids == 0) {
1999 return NT_STATUS_OK;
2002 *names = TALLOC_ARRAY(mem_ctx, char *, num_rids);
2003 *types = TALLOC_ARRAY(mem_ctx, enum lsa_SidType, num_rids);
2005 if ((*names == NULL) || (*types == NULL)) {
2006 result = NT_STATUS_NO_MEMORY;
2007 goto error;
2010 have_mapped = have_unmapped = false;
2012 for (i=0; i<num_rids; i++) {
2013 struct dom_sid sid;
2014 struct cache_entry *centry;
2015 fstring tmp;
2017 if (!sid_compose(&sid, domain_sid, rids[i])) {
2018 result = NT_STATUS_INTERNAL_ERROR;
2019 goto error;
2022 centry = wcache_fetch(cache, domain, "SN/%s",
2023 sid_to_fstring(tmp, &sid));
2024 if (!centry) {
2025 goto do_query;
2028 (*types)[i] = SID_NAME_UNKNOWN;
2029 (*names)[i] = talloc_strdup(*names, "");
2031 if (NT_STATUS_IS_OK(centry->status)) {
2032 char *dom;
2033 have_mapped = true;
2034 (*types)[i] = (enum lsa_SidType)centry_uint32(centry);
2036 dom = centry_string(centry, mem_ctx);
2037 if (*domain_name == NULL) {
2038 *domain_name = dom;
2039 } else {
2040 talloc_free(dom);
2043 (*names)[i] = centry_string(centry, *names);
2045 } else if (NT_STATUS_EQUAL(centry->status, NT_STATUS_NONE_MAPPED)) {
2046 have_unmapped = true;
2048 } else {
2049 /* something's definitely wrong */
2050 result = centry->status;
2051 goto error;
2054 centry_free(centry);
2057 if (!have_mapped) {
2058 return NT_STATUS_NONE_MAPPED;
2060 if (!have_unmapped) {
2061 return NT_STATUS_OK;
2063 return STATUS_SOME_UNMAPPED;
2065 do_query:
2067 TALLOC_FREE(*names);
2068 TALLOC_FREE(*types);
2070 result = domain->backend->rids_to_names(domain, mem_ctx, domain_sid,
2071 rids, num_rids, domain_name,
2072 names, types);
2074 if (NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
2075 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2076 if (!domain->internal && old_status) {
2077 set_domain_offline(domain);
2079 if (cache->tdb &&
2080 !domain->internal &&
2081 !domain->online &&
2082 old_status) {
2083 have_mapped = have_unmapped = false;
2085 for (i=0; i<num_rids; i++) {
2086 struct dom_sid sid;
2087 struct cache_entry *centry;
2088 fstring tmp;
2090 if (!sid_compose(&sid, domain_sid, rids[i])) {
2091 result = NT_STATUS_INTERNAL_ERROR;
2092 goto error;
2095 centry = wcache_fetch(cache, domain, "SN/%s",
2096 sid_to_fstring(tmp, &sid));
2097 if (!centry) {
2098 (*types)[i] = SID_NAME_UNKNOWN;
2099 (*names)[i] = talloc_strdup(*names, "");
2100 continue;
2103 (*types)[i] = SID_NAME_UNKNOWN;
2104 (*names)[i] = talloc_strdup(*names, "");
2106 if (NT_STATUS_IS_OK(centry->status)) {
2107 char *dom;
2108 have_mapped = true;
2109 (*types)[i] = (enum lsa_SidType)centry_uint32(centry);
2111 dom = centry_string(centry, mem_ctx);
2112 if (*domain_name == NULL) {
2113 *domain_name = dom;
2114 } else {
2115 talloc_free(dom);
2118 (*names)[i] = centry_string(centry, *names);
2120 } else if (NT_STATUS_EQUAL(centry->status, NT_STATUS_NONE_MAPPED)) {
2121 have_unmapped = true;
2123 } else {
2124 /* something's definitely wrong */
2125 result = centry->status;
2126 goto error;
2129 centry_free(centry);
2132 if (!have_mapped) {
2133 return NT_STATUS_NONE_MAPPED;
2135 if (!have_unmapped) {
2136 return NT_STATUS_OK;
2138 return STATUS_SOME_UNMAPPED;
2142 None of the queried rids has been found so save all negative entries
2144 if (NT_STATUS_EQUAL(result, NT_STATUS_NONE_MAPPED)) {
2145 for (i = 0; i < num_rids; i++) {
2146 struct dom_sid sid;
2147 const char *name = "";
2148 const enum lsa_SidType type = SID_NAME_UNKNOWN;
2149 NTSTATUS status = NT_STATUS_NONE_MAPPED;
2151 if (!sid_compose(&sid, domain_sid, rids[i])) {
2152 return NT_STATUS_INTERNAL_ERROR;
2155 wcache_save_sid_to_name(domain, status, &sid, *domain_name,
2156 name, type);
2159 return result;
2163 Some or all of the queried rids have been found.
2165 if (!NT_STATUS_IS_OK(result) &&
2166 !NT_STATUS_EQUAL(result, STATUS_SOME_UNMAPPED)) {
2167 return result;
2170 refresh_sequence_number(domain, false);
2172 for (i=0; i<num_rids; i++) {
2173 struct dom_sid sid;
2174 NTSTATUS status;
2176 if (!sid_compose(&sid, domain_sid, rids[i])) {
2177 result = NT_STATUS_INTERNAL_ERROR;
2178 goto error;
2181 status = (*types)[i] == SID_NAME_UNKNOWN ?
2182 NT_STATUS_NONE_MAPPED : NT_STATUS_OK;
2184 wcache_save_sid_to_name(domain, status, &sid, *domain_name,
2185 (*names)[i], (*types)[i]);
2188 return result;
2190 error:
2191 TALLOC_FREE(*names);
2192 TALLOC_FREE(*types);
2193 return result;
2196 NTSTATUS wcache_query_user(struct winbindd_domain *domain,
2197 TALLOC_CTX *mem_ctx,
2198 const struct dom_sid *user_sid,
2199 struct wbint_userinfo *info)
2201 struct winbind_cache *cache = get_cache(domain);
2202 struct cache_entry *centry = NULL;
2203 NTSTATUS status;
2204 char *sid_string;
2206 if (cache->tdb == NULL) {
2207 return NT_STATUS_NOT_FOUND;
2210 sid_string = sid_string_tos(user_sid);
2211 if (sid_string == NULL) {
2212 return NT_STATUS_NO_MEMORY;
2215 centry = wcache_fetch(cache, domain, "U/%s", sid_string);
2216 TALLOC_FREE(sid_string);
2217 if (centry == NULL) {
2218 return NT_STATUS_NOT_FOUND;
2222 * If we have an access denied cache entry and a cached info3
2223 * in the samlogon cache then do a query. This will force the
2224 * rpc back end to return the info3 data.
2227 if (NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED) &&
2228 netsamlogon_cache_have(user_sid)) {
2229 DEBUG(10, ("query_user: cached access denied and have cached "
2230 "info3\n"));
2231 domain->last_status = NT_STATUS_OK;
2232 centry_free(centry);
2233 return NT_STATUS_NOT_FOUND;
2236 /* if status is not ok then this is a negative hit
2237 and the rest of the data doesn't matter */
2238 status = centry->status;
2239 if (NT_STATUS_IS_OK(status)) {
2240 info->acct_name = centry_string(centry, mem_ctx);
2241 info->full_name = centry_string(centry, mem_ctx);
2242 info->homedir = centry_string(centry, mem_ctx);
2243 info->shell = centry_string(centry, mem_ctx);
2244 info->primary_gid = centry_uint32(centry);
2245 centry_sid(centry, &info->user_sid);
2246 centry_sid(centry, &info->group_sid);
2249 DEBUG(10,("query_user: [Cached] - cached info for domain %s status: "
2250 "%s\n", domain->name, nt_errstr(status) ));
2252 centry_free(centry);
2253 return status;
2256 /* Lookup user information from a rid */
2257 static NTSTATUS query_user(struct winbindd_domain *domain,
2258 TALLOC_CTX *mem_ctx,
2259 const struct dom_sid *user_sid,
2260 struct wbint_userinfo *info)
2262 NTSTATUS status;
2263 bool old_status;
2265 old_status = domain->online;
2266 status = wcache_query_user(domain, mem_ctx, user_sid, info);
2267 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2268 return status;
2271 ZERO_STRUCTP(info);
2273 /* Return status value returned by seq number check */
2275 if (!NT_STATUS_IS_OK(domain->last_status))
2276 return domain->last_status;
2278 DEBUG(10,("query_user: [Cached] - doing backend query for info for domain %s\n",
2279 domain->name ));
2281 status = domain->backend->query_user(domain, mem_ctx, user_sid, info);
2283 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2284 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2285 if (!domain->internal && old_status) {
2286 set_domain_offline(domain);
2288 if (!domain->internal &&
2289 !domain->online &&
2290 old_status) {
2291 NTSTATUS cache_status;
2292 cache_status = wcache_query_user(domain, mem_ctx, user_sid, info);
2293 return cache_status;
2296 /* and save it */
2297 refresh_sequence_number(domain, false);
2298 if (!NT_STATUS_IS_OK(status)) {
2299 return status;
2301 wcache_save_user(domain, status, info);
2303 return status;
2306 NTSTATUS wcache_lookup_usergroups(struct winbindd_domain *domain,
2307 TALLOC_CTX *mem_ctx,
2308 const struct dom_sid *user_sid,
2309 uint32_t *pnum_sids,
2310 struct dom_sid **psids)
2312 struct winbind_cache *cache = get_cache(domain);
2313 struct cache_entry *centry = NULL;
2314 NTSTATUS status;
2315 uint32_t i, num_sids;
2316 struct dom_sid *sids;
2317 fstring sid_string;
2319 if (cache->tdb == NULL) {
2320 return NT_STATUS_NOT_FOUND;
2323 centry = wcache_fetch(cache, domain, "UG/%s",
2324 sid_to_fstring(sid_string, user_sid));
2325 if (centry == NULL) {
2326 return NT_STATUS_NOT_FOUND;
2329 /* If we have an access denied cache entry and a cached info3 in the
2330 samlogon cache then do a query. This will force the rpc back end
2331 to return the info3 data. */
2333 if (NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)
2334 && netsamlogon_cache_have(user_sid)) {
2335 DEBUG(10, ("lookup_usergroups: cached access denied and have "
2336 "cached info3\n"));
2337 domain->last_status = NT_STATUS_OK;
2338 centry_free(centry);
2339 return NT_STATUS_NOT_FOUND;
2342 num_sids = centry_uint32(centry);
2343 sids = talloc_array(mem_ctx, struct dom_sid, num_sids);
2344 if (sids == NULL) {
2345 centry_free(centry);
2346 return NT_STATUS_NO_MEMORY;
2349 for (i=0; i<num_sids; i++) {
2350 centry_sid(centry, &sids[i]);
2353 status = centry->status;
2355 DEBUG(10,("lookup_usergroups: [Cached] - cached info for domain %s "
2356 "status: %s\n", domain->name, nt_errstr(status)));
2358 centry_free(centry);
2360 *pnum_sids = num_sids;
2361 *psids = sids;
2362 return status;
2365 /* Lookup groups a user is a member of. */
2366 static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
2367 TALLOC_CTX *mem_ctx,
2368 const struct dom_sid *user_sid,
2369 uint32 *num_groups, struct dom_sid **user_gids)
2371 struct cache_entry *centry = NULL;
2372 NTSTATUS status;
2373 unsigned int i;
2374 fstring sid_string;
2375 bool old_status;
2377 old_status = domain->online;
2378 status = wcache_lookup_usergroups(domain, mem_ctx, user_sid,
2379 num_groups, user_gids);
2380 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2381 return status;
2384 (*num_groups) = 0;
2385 (*user_gids) = NULL;
2387 /* Return status value returned by seq number check */
2389 if (!NT_STATUS_IS_OK(domain->last_status))
2390 return domain->last_status;
2392 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info for domain %s\n",
2393 domain->name ));
2395 status = domain->backend->lookup_usergroups(domain, mem_ctx, user_sid, num_groups, user_gids);
2397 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2398 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2399 if (!domain->internal && old_status) {
2400 set_domain_offline(domain);
2402 if (!domain->internal &&
2403 !domain->online &&
2404 old_status) {
2405 NTSTATUS cache_status;
2406 cache_status = wcache_lookup_usergroups(domain, mem_ctx, user_sid,
2407 num_groups, user_gids);
2408 return cache_status;
2411 if ( NT_STATUS_EQUAL(status, NT_STATUS_SYNCHRONIZATION_REQUIRED) )
2412 goto skip_save;
2414 /* and save it */
2415 refresh_sequence_number(domain, false);
2416 if (!NT_STATUS_IS_OK(status)) {
2417 return status;
2419 centry = centry_start(domain, status);
2420 if (!centry)
2421 goto skip_save;
2423 centry_put_uint32(centry, *num_groups);
2424 for (i=0; i<(*num_groups); i++) {
2425 centry_put_sid(centry, &(*user_gids)[i]);
2428 centry_end(centry, "UG/%s", sid_to_fstring(sid_string, user_sid));
2429 centry_free(centry);
2431 skip_save:
2432 return status;
2435 static char *wcache_make_sidlist(TALLOC_CTX *mem_ctx, uint32_t num_sids,
2436 const struct dom_sid *sids)
2438 uint32_t i;
2439 char *sidlist;
2441 sidlist = talloc_strdup(mem_ctx, "");
2442 if (sidlist == NULL) {
2443 return NULL;
2445 for (i=0; i<num_sids; i++) {
2446 fstring tmp;
2447 sidlist = talloc_asprintf_append_buffer(
2448 sidlist, "/%s", sid_to_fstring(tmp, &sids[i]));
2449 if (sidlist == NULL) {
2450 return NULL;
2453 return sidlist;
2456 NTSTATUS wcache_lookup_useraliases(struct winbindd_domain *domain,
2457 TALLOC_CTX *mem_ctx, uint32_t num_sids,
2458 const struct dom_sid *sids,
2459 uint32_t *pnum_aliases, uint32_t **paliases)
2461 struct winbind_cache *cache = get_cache(domain);
2462 struct cache_entry *centry = NULL;
2463 uint32_t num_aliases;
2464 uint32_t *aliases;
2465 NTSTATUS status;
2466 char *sidlist;
2467 int i;
2469 if (cache->tdb == NULL) {
2470 return NT_STATUS_NOT_FOUND;
2473 if (num_sids == 0) {
2474 *pnum_aliases = 0;
2475 *paliases = NULL;
2476 return NT_STATUS_OK;
2479 /* We need to cache indexed by the whole list of SIDs, the aliases
2480 * resulting might come from any of the SIDs. */
2482 sidlist = wcache_make_sidlist(talloc_tos(), num_sids, sids);
2483 if (sidlist == NULL) {
2484 return NT_STATUS_NO_MEMORY;
2487 centry = wcache_fetch(cache, domain, "UA%s", sidlist);
2488 TALLOC_FREE(sidlist);
2489 if (centry == NULL) {
2490 return NT_STATUS_NOT_FOUND;
2493 num_aliases = centry_uint32(centry);
2494 aliases = talloc_array(mem_ctx, uint32_t, num_aliases);
2495 if (aliases == NULL) {
2496 centry_free(centry);
2497 return NT_STATUS_NO_MEMORY;
2500 for (i=0; i<num_aliases; i++) {
2501 aliases[i] = centry_uint32(centry);
2504 status = centry->status;
2506 DEBUG(10,("lookup_useraliases: [Cached] - cached info for domain: %s "
2507 "status %s\n", domain->name, nt_errstr(status)));
2509 centry_free(centry);
2511 *pnum_aliases = num_aliases;
2512 *paliases = aliases;
2514 return status;
2517 static NTSTATUS lookup_useraliases(struct winbindd_domain *domain,
2518 TALLOC_CTX *mem_ctx,
2519 uint32 num_sids, const struct dom_sid *sids,
2520 uint32 *num_aliases, uint32 **alias_rids)
2522 struct cache_entry *centry = NULL;
2523 NTSTATUS status;
2524 char *sidlist;
2525 int i;
2526 bool old_status;
2528 old_status = domain->online;
2529 status = wcache_lookup_useraliases(domain, mem_ctx, num_sids, sids,
2530 num_aliases, alias_rids);
2531 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2532 return status;
2535 (*num_aliases) = 0;
2536 (*alias_rids) = NULL;
2538 if (!NT_STATUS_IS_OK(domain->last_status))
2539 return domain->last_status;
2541 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info "
2542 "for domain %s\n", domain->name ));
2544 sidlist = wcache_make_sidlist(talloc_tos(), num_sids, sids);
2545 if (sidlist == NULL) {
2546 return NT_STATUS_NO_MEMORY;
2549 status = domain->backend->lookup_useraliases(domain, mem_ctx,
2550 num_sids, sids,
2551 num_aliases, alias_rids);
2553 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2554 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2555 if (!domain->internal && old_status) {
2556 set_domain_offline(domain);
2558 if (!domain->internal &&
2559 !domain->online &&
2560 old_status) {
2561 NTSTATUS cache_status;
2562 cache_status = wcache_lookup_useraliases(domain, mem_ctx, num_sids,
2563 sids, num_aliases, alias_rids);
2564 return cache_status;
2567 /* and save it */
2568 refresh_sequence_number(domain, false);
2569 if (!NT_STATUS_IS_OK(status)) {
2570 return status;
2572 centry = centry_start(domain, status);
2573 if (!centry)
2574 goto skip_save;
2575 centry_put_uint32(centry, *num_aliases);
2576 for (i=0; i<(*num_aliases); i++)
2577 centry_put_uint32(centry, (*alias_rids)[i]);
2578 centry_end(centry, "UA%s", sidlist);
2579 centry_free(centry);
2581 skip_save:
2582 return status;
2585 NTSTATUS wcache_lookup_groupmem(struct winbindd_domain *domain,
2586 TALLOC_CTX *mem_ctx,
2587 const struct dom_sid *group_sid,
2588 uint32_t *num_names,
2589 struct dom_sid **sid_mem, char ***names,
2590 uint32_t **name_types)
2592 struct winbind_cache *cache = get_cache(domain);
2593 struct cache_entry *centry = NULL;
2594 NTSTATUS status;
2595 unsigned int i;
2596 char *sid_string;
2598 if (cache->tdb == NULL) {
2599 return NT_STATUS_NOT_FOUND;
2602 sid_string = sid_string_tos(group_sid);
2603 if (sid_string == NULL) {
2604 return NT_STATUS_NO_MEMORY;
2607 centry = wcache_fetch(cache, domain, "GM/%s", sid_string);
2608 TALLOC_FREE(sid_string);
2609 if (centry == NULL) {
2610 return NT_STATUS_NOT_FOUND;
2613 *sid_mem = NULL;
2614 *names = NULL;
2615 *name_types = NULL;
2617 *num_names = centry_uint32(centry);
2618 if (*num_names == 0) {
2619 centry_free(centry);
2620 return NT_STATUS_OK;
2623 *sid_mem = talloc_array(mem_ctx, struct dom_sid, *num_names);
2624 *names = talloc_array(mem_ctx, char *, *num_names);
2625 *name_types = talloc_array(mem_ctx, uint32, *num_names);
2627 if ((*sid_mem == NULL) || (*names == NULL) || (*name_types == NULL)) {
2628 TALLOC_FREE(*sid_mem);
2629 TALLOC_FREE(*names);
2630 TALLOC_FREE(*name_types);
2631 centry_free(centry);
2632 return NT_STATUS_NO_MEMORY;
2635 for (i=0; i<(*num_names); i++) {
2636 centry_sid(centry, &(*sid_mem)[i]);
2637 (*names)[i] = centry_string(centry, mem_ctx);
2638 (*name_types)[i] = centry_uint32(centry);
2641 status = centry->status;
2643 DEBUG(10,("lookup_groupmem: [Cached] - cached info for domain %s "
2644 "status: %s\n", domain->name, nt_errstr(status)));
2646 centry_free(centry);
2647 return status;
2650 static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
2651 TALLOC_CTX *mem_ctx,
2652 const struct dom_sid *group_sid,
2653 enum lsa_SidType type,
2654 uint32 *num_names,
2655 struct dom_sid **sid_mem, char ***names,
2656 uint32 **name_types)
2658 struct cache_entry *centry = NULL;
2659 NTSTATUS status;
2660 unsigned int i;
2661 fstring sid_string;
2662 bool old_status;
2664 old_status = domain->online;
2665 status = wcache_lookup_groupmem(domain, mem_ctx, group_sid, num_names,
2666 sid_mem, names, name_types);
2667 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2668 return status;
2671 (*num_names) = 0;
2672 (*sid_mem) = NULL;
2673 (*names) = NULL;
2674 (*name_types) = NULL;
2676 /* Return status value returned by seq number check */
2678 if (!NT_STATUS_IS_OK(domain->last_status))
2679 return domain->last_status;
2681 DEBUG(10,("lookup_groupmem: [Cached] - doing backend query for info for domain %s\n",
2682 domain->name ));
2684 status = domain->backend->lookup_groupmem(domain, mem_ctx, group_sid,
2685 type, num_names,
2686 sid_mem, names, name_types);
2688 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2689 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2690 if (!domain->internal && old_status) {
2691 set_domain_offline(domain);
2693 if (!domain->internal &&
2694 !domain->online &&
2695 old_status) {
2696 NTSTATUS cache_status;
2697 cache_status = wcache_lookup_groupmem(domain, mem_ctx, group_sid,
2698 num_names, sid_mem, names,
2699 name_types);
2700 return cache_status;
2703 /* and save it */
2704 refresh_sequence_number(domain, false);
2705 if (!NT_STATUS_IS_OK(status)) {
2706 return status;
2708 centry = centry_start(domain, status);
2709 if (!centry)
2710 goto skip_save;
2711 centry_put_uint32(centry, *num_names);
2712 for (i=0; i<(*num_names); i++) {
2713 centry_put_sid(centry, &(*sid_mem)[i]);
2714 centry_put_string(centry, (*names)[i]);
2715 centry_put_uint32(centry, (*name_types)[i]);
2717 centry_end(centry, "GM/%s", sid_to_fstring(sid_string, group_sid));
2718 centry_free(centry);
2720 skip_save:
2721 return status;
2724 /* find the sequence number for a domain */
2725 static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
2727 refresh_sequence_number(domain, false);
2729 *seq = domain->sequence_number;
2731 return NT_STATUS_OK;
2734 /* enumerate trusted domains
2735 * (we need to have the list of trustdoms in the cache when we go offline) -
2736 * Guenther */
2737 static NTSTATUS trusted_domains(struct winbindd_domain *domain,
2738 TALLOC_CTX *mem_ctx,
2739 struct netr_DomainTrustList *trusts)
2741 NTSTATUS status;
2742 struct winbind_cache *cache;
2743 struct winbindd_tdc_domain *dom_list = NULL;
2744 size_t num_domains = 0;
2745 bool retval = false;
2746 int i;
2747 bool old_status;
2749 old_status = domain->online;
2750 trusts->count = 0;
2751 trusts->array = NULL;
2753 cache = get_cache(domain);
2754 if (!cache || !cache->tdb) {
2755 goto do_query;
2758 if (domain->online) {
2759 goto do_query;
2762 retval = wcache_tdc_fetch_list(&dom_list, &num_domains);
2763 if (!retval || !num_domains || !dom_list) {
2764 TALLOC_FREE(dom_list);
2765 goto do_query;
2768 do_fetch_cache:
2769 trusts->array = TALLOC_ZERO_ARRAY(mem_ctx, struct netr_DomainTrust, num_domains);
2770 if (!trusts->array) {
2771 TALLOC_FREE(dom_list);
2772 return NT_STATUS_NO_MEMORY;
2775 for (i = 0; i < num_domains; i++) {
2776 struct netr_DomainTrust *trust;
2777 struct dom_sid *sid;
2778 struct winbindd_domain *dom;
2780 dom = find_domain_from_name_noinit(dom_list[i].domain_name);
2781 if (dom && dom->internal) {
2782 continue;
2785 trust = &trusts->array[trusts->count];
2786 trust->netbios_name = talloc_strdup(trusts->array, dom_list[i].domain_name);
2787 trust->dns_name = talloc_strdup(trusts->array, dom_list[i].dns_name);
2788 sid = talloc(trusts->array, struct dom_sid);
2789 if (!trust->netbios_name || !trust->dns_name ||
2790 !sid) {
2791 TALLOC_FREE(dom_list);
2792 TALLOC_FREE(trusts->array);
2793 return NT_STATUS_NO_MEMORY;
2796 trust->trust_flags = dom_list[i].trust_flags;
2797 trust->trust_attributes = dom_list[i].trust_attribs;
2798 trust->trust_type = dom_list[i].trust_type;
2799 sid_copy(sid, &dom_list[i].sid);
2800 trust->sid = sid;
2801 trusts->count++;
2804 TALLOC_FREE(dom_list);
2805 return NT_STATUS_OK;
2807 do_query:
2808 /* Return status value returned by seq number check */
2810 if (!NT_STATUS_IS_OK(domain->last_status))
2811 return domain->last_status;
2813 DEBUG(10,("trusted_domains: [Cached] - doing backend query for info for domain %s\n",
2814 domain->name ));
2816 status = domain->backend->trusted_domains(domain, mem_ctx, trusts);
2818 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2819 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2820 if (!domain->internal && old_status) {
2821 set_domain_offline(domain);
2823 if (!domain->internal &&
2824 !domain->online &&
2825 old_status) {
2826 retval = wcache_tdc_fetch_list(&dom_list, &num_domains);
2827 if (retval && num_domains && dom_list) {
2828 TALLOC_FREE(trusts->array);
2829 trusts->count = 0;
2830 goto do_fetch_cache;
2834 /* no trusts gives NT_STATUS_NO_MORE_ENTRIES resetting to NT_STATUS_OK
2835 * so that the generic centry handling still applies correctly -
2836 * Guenther*/
2838 if (!NT_STATUS_IS_ERR(status)) {
2839 status = NT_STATUS_OK;
2841 return status;
2844 /* get lockout policy */
2845 static NTSTATUS lockout_policy(struct winbindd_domain *domain,
2846 TALLOC_CTX *mem_ctx,
2847 struct samr_DomInfo12 *policy)
2849 struct winbind_cache *cache = get_cache(domain);
2850 struct cache_entry *centry = NULL;
2851 NTSTATUS status;
2852 bool old_status;
2854 old_status = domain->online;
2855 if (!cache->tdb)
2856 goto do_query;
2858 centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
2860 if (!centry)
2861 goto do_query;
2863 do_fetch_cache:
2864 policy->lockout_duration = centry_nttime(centry);
2865 policy->lockout_window = centry_nttime(centry);
2866 policy->lockout_threshold = centry_uint16(centry);
2868 status = centry->status;
2870 DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2871 domain->name, nt_errstr(status) ));
2873 centry_free(centry);
2874 return status;
2876 do_query:
2877 ZERO_STRUCTP(policy);
2879 /* Return status value returned by seq number check */
2881 if (!NT_STATUS_IS_OK(domain->last_status))
2882 return domain->last_status;
2884 DEBUG(10,("lockout_policy: [Cached] - doing backend query for info for domain %s\n",
2885 domain->name ));
2887 status = domain->backend->lockout_policy(domain, mem_ctx, policy);
2889 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2890 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2891 if (!domain->internal && old_status) {
2892 set_domain_offline(domain);
2894 if (cache->tdb &&
2895 !domain->internal &&
2896 !domain->online &&
2897 old_status) {
2898 centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
2899 if (centry) {
2900 goto do_fetch_cache;
2904 /* and save it */
2905 refresh_sequence_number(domain, false);
2906 if (!NT_STATUS_IS_OK(status)) {
2907 return status;
2909 wcache_save_lockout_policy(domain, status, policy);
2911 return status;
2914 /* get password policy */
2915 static NTSTATUS password_policy(struct winbindd_domain *domain,
2916 TALLOC_CTX *mem_ctx,
2917 struct samr_DomInfo1 *policy)
2919 struct winbind_cache *cache = get_cache(domain);
2920 struct cache_entry *centry = NULL;
2921 NTSTATUS status;
2922 bool old_status;
2924 old_status = domain->online;
2925 if (!cache->tdb)
2926 goto do_query;
2928 centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
2930 if (!centry)
2931 goto do_query;
2933 do_fetch_cache:
2934 policy->min_password_length = centry_uint16(centry);
2935 policy->password_history_length = centry_uint16(centry);
2936 policy->password_properties = centry_uint32(centry);
2937 policy->max_password_age = centry_nttime(centry);
2938 policy->min_password_age = centry_nttime(centry);
2940 status = centry->status;
2942 DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2943 domain->name, nt_errstr(status) ));
2945 centry_free(centry);
2946 return status;
2948 do_query:
2949 ZERO_STRUCTP(policy);
2951 /* Return status value returned by seq number check */
2953 if (!NT_STATUS_IS_OK(domain->last_status))
2954 return domain->last_status;
2956 DEBUG(10,("password_policy: [Cached] - doing backend query for info for domain %s\n",
2957 domain->name ));
2959 status = domain->backend->password_policy(domain, mem_ctx, policy);
2961 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2962 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2963 if (!domain->internal && old_status) {
2964 set_domain_offline(domain);
2966 if (cache->tdb &&
2967 !domain->internal &&
2968 !domain->online &&
2969 old_status) {
2970 centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
2971 if (centry) {
2972 goto do_fetch_cache;
2976 /* and save it */
2977 refresh_sequence_number(domain, false);
2978 if (!NT_STATUS_IS_OK(status)) {
2979 return status;
2981 wcache_save_password_policy(domain, status, policy);
2983 return status;
2987 /* Invalidate cached user and group lists coherently */
2989 static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
2990 void *state)
2992 if (strncmp((const char *)kbuf.dptr, "UL/", 3) == 0 ||
2993 strncmp((const char *)kbuf.dptr, "GL/", 3) == 0)
2994 tdb_delete(the_tdb, kbuf);
2996 return 0;
2999 /* Invalidate the getpwnam and getgroups entries for a winbindd domain */
3001 void wcache_invalidate_samlogon(struct winbindd_domain *domain,
3002 struct netr_SamInfo3 *info3)
3004 struct dom_sid sid;
3005 fstring key_str, sid_string;
3006 struct winbind_cache *cache;
3008 /* dont clear cached U/SID and UG/SID entries when we want to logon
3009 * offline - gd */
3011 if (lp_winbind_offline_logon()) {
3012 return;
3015 if (!domain)
3016 return;
3018 cache = get_cache(domain);
3020 if (!cache->tdb) {
3021 return;
3024 sid_compose(&sid, info3->base.domain_sid, info3->base.rid);
3026 /* Clear U/SID cache entry */
3027 fstr_sprintf(key_str, "U/%s", sid_to_fstring(sid_string, &sid));
3028 DEBUG(10, ("wcache_invalidate_samlogon: clearing %s\n", key_str));
3029 tdb_delete(cache->tdb, string_tdb_data(key_str));
3031 /* Clear UG/SID cache entry */
3032 fstr_sprintf(key_str, "UG/%s", sid_to_fstring(sid_string, &sid));
3033 DEBUG(10, ("wcache_invalidate_samlogon: clearing %s\n", key_str));
3034 tdb_delete(cache->tdb, string_tdb_data(key_str));
3036 /* Samba/winbindd never needs this. */
3037 netsamlogon_clear_cached_user(info3);
3040 bool wcache_invalidate_cache(void)
3042 struct winbindd_domain *domain;
3044 for (domain = domain_list(); domain; domain = domain->next) {
3045 struct winbind_cache *cache = get_cache(domain);
3047 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
3048 "entries for %s\n", domain->name));
3049 if (cache) {
3050 if (cache->tdb) {
3051 tdb_traverse(cache->tdb, traverse_fn, NULL);
3052 } else {
3053 return false;
3057 return true;
3060 bool wcache_invalidate_cache_noinit(void)
3062 struct winbindd_domain *domain;
3064 for (domain = domain_list(); domain; domain = domain->next) {
3065 struct winbind_cache *cache;
3067 /* Skip uninitialized domains. */
3068 if (!domain->initialized && !domain->internal) {
3069 continue;
3072 cache = get_cache(domain);
3074 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
3075 "entries for %s\n", domain->name));
3076 if (cache) {
3077 if (cache->tdb) {
3078 tdb_traverse(cache->tdb, traverse_fn, NULL);
3080 * Flushing cache has nothing to with domains.
3081 * return here if we successfully flushed once.
3082 * To avoid unnecessary traversing the cache.
3084 return true;
3085 } else {
3086 return false;
3090 return true;
3093 bool init_wcache(void)
3095 if (wcache == NULL) {
3096 wcache = SMB_XMALLOC_P(struct winbind_cache);
3097 ZERO_STRUCTP(wcache);
3100 if (wcache->tdb != NULL)
3101 return true;
3103 /* when working offline we must not clear the cache on restart */
3104 wcache->tdb = tdb_open_log(cache_path("winbindd_cache.tdb"),
3105 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
3106 TDB_INCOMPATIBLE_HASH |
3107 (lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST)),
3108 O_RDWR|O_CREAT, 0600);
3110 if (wcache->tdb == NULL) {
3111 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
3112 return false;
3115 return true;
3118 /************************************************************************
3119 This is called by the parent to initialize the cache file.
3120 We don't need sophisticated locking here as we know we're the
3121 only opener.
3122 ************************************************************************/
3124 bool initialize_winbindd_cache(void)
3126 bool cache_bad = true;
3127 uint32 vers;
3129 if (!init_wcache()) {
3130 DEBUG(0,("initialize_winbindd_cache: init_wcache failed.\n"));
3131 return false;
3134 /* Check version number. */
3135 if (tdb_fetch_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, &vers) &&
3136 vers == WINBINDD_CACHE_VERSION) {
3137 cache_bad = false;
3140 if (cache_bad) {
3141 DEBUG(0,("initialize_winbindd_cache: clearing cache "
3142 "and re-creating with version number %d\n",
3143 WINBINDD_CACHE_VERSION ));
3145 tdb_close(wcache->tdb);
3146 wcache->tdb = NULL;
3148 if (unlink(cache_path("winbindd_cache.tdb")) == -1) {
3149 DEBUG(0,("initialize_winbindd_cache: unlink %s failed %s ",
3150 cache_path("winbindd_cache.tdb"),
3151 strerror(errno) ));
3152 return false;
3154 if (!init_wcache()) {
3155 DEBUG(0,("initialize_winbindd_cache: re-initialization "
3156 "init_wcache failed.\n"));
3157 return false;
3160 /* Write the version. */
3161 if (!tdb_store_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, WINBINDD_CACHE_VERSION)) {
3162 DEBUG(0,("initialize_winbindd_cache: version number store failed %s\n",
3163 tdb_errorstr(wcache->tdb) ));
3164 return false;
3168 tdb_close(wcache->tdb);
3169 wcache->tdb = NULL;
3170 return true;
3173 void close_winbindd_cache(void)
3175 if (!wcache) {
3176 return;
3178 if (wcache->tdb) {
3179 tdb_close(wcache->tdb);
3180 wcache->tdb = NULL;
3184 bool lookup_cached_sid(TALLOC_CTX *mem_ctx, const struct dom_sid *sid,
3185 char **domain_name, char **name,
3186 enum lsa_SidType *type)
3188 struct winbindd_domain *domain;
3189 NTSTATUS status;
3191 domain = find_lookup_domain_from_sid(sid);
3192 if (domain == NULL) {
3193 return false;
3195 status = wcache_sid_to_name(domain, sid, mem_ctx, domain_name, name,
3196 type);
3197 return NT_STATUS_IS_OK(status);
3200 bool lookup_cached_name(const char *domain_name,
3201 const char *name,
3202 struct dom_sid *sid,
3203 enum lsa_SidType *type)
3205 struct winbindd_domain *domain;
3206 NTSTATUS status;
3207 bool original_online_state;
3209 domain = find_lookup_domain_from_name(domain_name);
3210 if (domain == NULL) {
3211 return false;
3214 /* If we are doing a cached logon, temporarily set the domain
3215 offline so the cache won't expire the entry */
3217 original_online_state = domain->online;
3218 domain->online = false;
3219 status = wcache_name_to_sid(domain, domain_name, name, sid, type);
3220 domain->online = original_online_state;
3222 return NT_STATUS_IS_OK(status);
3225 void cache_name2sid(struct winbindd_domain *domain,
3226 const char *domain_name, const char *name,
3227 enum lsa_SidType type, const struct dom_sid *sid)
3229 refresh_sequence_number(domain, false);
3230 wcache_save_name_to_sid(domain, NT_STATUS_OK, domain_name, name,
3231 sid, type);
3235 * The original idea that this cache only contains centries has
3236 * been blurred - now other stuff gets put in here. Ensure we
3237 * ignore these things on cleanup.
3240 static int traverse_fn_cleanup(TDB_CONTEXT *the_tdb, TDB_DATA kbuf,
3241 TDB_DATA dbuf, void *state)
3243 struct cache_entry *centry;
3245 if (is_non_centry_key(kbuf)) {
3246 return 0;
3249 centry = wcache_fetch_raw((char *)kbuf.dptr);
3250 if (!centry) {
3251 return 0;
3254 if (!NT_STATUS_IS_OK(centry->status)) {
3255 DEBUG(10,("deleting centry %s\n", (const char *)kbuf.dptr));
3256 tdb_delete(the_tdb, kbuf);
3259 centry_free(centry);
3260 return 0;
3263 /* flush the cache */
3264 void wcache_flush_cache(void)
3266 if (!wcache)
3267 return;
3268 if (wcache->tdb) {
3269 tdb_close(wcache->tdb);
3270 wcache->tdb = NULL;
3272 if (!winbindd_use_cache()) {
3273 return;
3276 /* when working offline we must not clear the cache on restart */
3277 wcache->tdb = tdb_open_log(cache_path("winbindd_cache.tdb"),
3278 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
3279 TDB_INCOMPATIBLE_HASH |
3280 (lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST)),
3281 O_RDWR|O_CREAT, 0600);
3283 if (!wcache->tdb) {
3284 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
3285 return;
3288 tdb_traverse(wcache->tdb, traverse_fn_cleanup, NULL);
3290 DEBUG(10,("wcache_flush_cache success\n"));
3293 /* Count cached creds */
3295 static int traverse_fn_cached_creds(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
3296 void *state)
3298 int *cred_count = (int*)state;
3300 if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
3301 (*cred_count)++;
3303 return 0;
3306 NTSTATUS wcache_count_cached_creds(struct winbindd_domain *domain, int *count)
3308 struct winbind_cache *cache = get_cache(domain);
3310 *count = 0;
3312 if (!cache->tdb) {
3313 return NT_STATUS_INTERNAL_DB_ERROR;
3316 tdb_traverse(cache->tdb, traverse_fn_cached_creds, (void *)count);
3318 return NT_STATUS_OK;
3321 struct cred_list {
3322 struct cred_list *prev, *next;
3323 TDB_DATA key;
3324 fstring name;
3325 time_t created;
3327 static struct cred_list *wcache_cred_list;
3329 static int traverse_fn_get_credlist(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
3330 void *state)
3332 struct cred_list *cred;
3334 if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
3336 cred = SMB_MALLOC_P(struct cred_list);
3337 if (cred == NULL) {
3338 DEBUG(0,("traverse_fn_remove_first_creds: failed to malloc new entry for list\n"));
3339 return -1;
3342 ZERO_STRUCTP(cred);
3344 /* save a copy of the key */
3346 fstrcpy(cred->name, (const char *)kbuf.dptr);
3347 DLIST_ADD(wcache_cred_list, cred);
3350 return 0;
3353 NTSTATUS wcache_remove_oldest_cached_creds(struct winbindd_domain *domain, const struct dom_sid *sid)
3355 struct winbind_cache *cache = get_cache(domain);
3356 NTSTATUS status;
3357 int ret;
3358 struct cred_list *cred, *oldest = NULL;
3360 if (!cache->tdb) {
3361 return NT_STATUS_INTERNAL_DB_ERROR;
3364 /* we possibly already have an entry */
3365 if (sid && NT_STATUS_IS_OK(wcache_cached_creds_exist(domain, sid))) {
3367 fstring key_str, tmp;
3369 DEBUG(11,("we already have an entry, deleting that\n"));
3371 fstr_sprintf(key_str, "CRED/%s", sid_to_fstring(tmp, sid));
3373 tdb_delete(cache->tdb, string_tdb_data(key_str));
3375 return NT_STATUS_OK;
3378 ret = tdb_traverse(cache->tdb, traverse_fn_get_credlist, NULL);
3379 if (ret == 0) {
3380 return NT_STATUS_OK;
3381 } else if ((ret == -1) || (wcache_cred_list == NULL)) {
3382 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
3385 ZERO_STRUCTP(oldest);
3387 for (cred = wcache_cred_list; cred; cred = cred->next) {
3389 TDB_DATA data;
3390 time_t t;
3392 data = tdb_fetch(cache->tdb, string_tdb_data(cred->name));
3393 if (!data.dptr) {
3394 DEBUG(10,("wcache_remove_oldest_cached_creds: entry for [%s] not found\n",
3395 cred->name));
3396 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
3397 goto done;
3400 t = IVAL(data.dptr, 0);
3401 SAFE_FREE(data.dptr);
3403 if (!oldest) {
3404 oldest = SMB_MALLOC_P(struct cred_list);
3405 if (oldest == NULL) {
3406 status = NT_STATUS_NO_MEMORY;
3407 goto done;
3410 fstrcpy(oldest->name, cred->name);
3411 oldest->created = t;
3412 continue;
3415 if (t < oldest->created) {
3416 fstrcpy(oldest->name, cred->name);
3417 oldest->created = t;
3421 if (tdb_delete(cache->tdb, string_tdb_data(oldest->name)) == 0) {
3422 status = NT_STATUS_OK;
3423 } else {
3424 status = NT_STATUS_UNSUCCESSFUL;
3426 done:
3427 SAFE_FREE(wcache_cred_list);
3428 SAFE_FREE(oldest);
3430 return status;
3433 /* Change the global online/offline state. */
3434 bool set_global_winbindd_state_offline(void)
3436 TDB_DATA data;
3438 DEBUG(10,("set_global_winbindd_state_offline: offline requested.\n"));
3440 /* Only go offline if someone has created
3441 the key "WINBINDD_OFFLINE" in the cache tdb. */
3443 if (wcache == NULL || wcache->tdb == NULL) {
3444 DEBUG(10,("set_global_winbindd_state_offline: wcache not open yet.\n"));
3445 return false;
3448 if (!lp_winbind_offline_logon()) {
3449 DEBUG(10,("set_global_winbindd_state_offline: rejecting.\n"));
3450 return false;
3453 if (global_winbindd_offline_state) {
3454 /* Already offline. */
3455 return true;
3458 data = tdb_fetch_bystring( wcache->tdb, "WINBINDD_OFFLINE" );
3460 if (!data.dptr || data.dsize != 4) {
3461 DEBUG(10,("set_global_winbindd_state_offline: offline state not set.\n"));
3462 SAFE_FREE(data.dptr);
3463 return false;
3464 } else {
3465 DEBUG(10,("set_global_winbindd_state_offline: offline state set.\n"));
3466 global_winbindd_offline_state = true;
3467 SAFE_FREE(data.dptr);
3468 return true;
3472 void set_global_winbindd_state_online(void)
3474 DEBUG(10,("set_global_winbindd_state_online: online requested.\n"));
3476 if (!lp_winbind_offline_logon()) {
3477 DEBUG(10,("set_global_winbindd_state_online: rejecting.\n"));
3478 return;
3481 if (!global_winbindd_offline_state) {
3482 /* Already online. */
3483 return;
3485 global_winbindd_offline_state = false;
3487 if (!wcache->tdb) {
3488 return;
3491 /* Ensure there is no key "WINBINDD_OFFLINE" in the cache tdb. */
3492 tdb_delete_bystring(wcache->tdb, "WINBINDD_OFFLINE");
3495 bool get_global_winbindd_state_offline(void)
3497 return global_winbindd_offline_state;
3500 /***********************************************************************
3501 Validate functions for all possible cache tdb keys.
3502 ***********************************************************************/
3504 static struct cache_entry *create_centry_validate(const char *kstr, TDB_DATA data,
3505 struct tdb_validation_status *state)
3507 struct cache_entry *centry;
3509 centry = SMB_XMALLOC_P(struct cache_entry);
3510 centry->data = (unsigned char *)memdup(data.dptr, data.dsize);
3511 if (!centry->data) {
3512 SAFE_FREE(centry);
3513 return NULL;
3515 centry->len = data.dsize;
3516 centry->ofs = 0;
3518 if (centry->len < 16) {
3519 /* huh? corrupt cache? */
3520 DEBUG(0,("create_centry_validate: Corrupt cache for key %s "
3521 "(len < 16) ?\n", kstr));
3522 centry_free(centry);
3523 state->bad_entry = true;
3524 state->success = false;
3525 return NULL;
3528 centry->status = NT_STATUS(centry_uint32(centry));
3529 centry->sequence_number = centry_uint32(centry);
3530 centry->timeout = centry_uint64_t(centry);
3531 return centry;
3534 static int validate_seqnum(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3535 struct tdb_validation_status *state)
3537 if (dbuf.dsize != 8) {
3538 DEBUG(0,("validate_seqnum: Corrupt cache for key %s (len %u != 8) ?\n",
3539 keystr, (unsigned int)dbuf.dsize ));
3540 state->bad_entry = true;
3541 return 1;
3543 return 0;
3546 static int validate_ns(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3547 struct tdb_validation_status *state)
3549 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3550 if (!centry) {
3551 return 1;
3554 (void)centry_uint32(centry);
3555 if (NT_STATUS_IS_OK(centry->status)) {
3556 struct dom_sid sid;
3557 (void)centry_sid(centry, &sid);
3560 centry_free(centry);
3562 if (!(state->success)) {
3563 return 1;
3565 DEBUG(10,("validate_ns: %s ok\n", keystr));
3566 return 0;
3569 static int validate_sn(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3570 struct tdb_validation_status *state)
3572 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3573 if (!centry) {
3574 return 1;
3577 if (NT_STATUS_IS_OK(centry->status)) {
3578 (void)centry_uint32(centry);
3579 (void)centry_string(centry, mem_ctx);
3580 (void)centry_string(centry, mem_ctx);
3583 centry_free(centry);
3585 if (!(state->success)) {
3586 return 1;
3588 DEBUG(10,("validate_sn: %s ok\n", keystr));
3589 return 0;
3592 static int validate_u(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3593 struct tdb_validation_status *state)
3595 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3596 struct dom_sid sid;
3598 if (!centry) {
3599 return 1;
3602 (void)centry_string(centry, mem_ctx);
3603 (void)centry_string(centry, mem_ctx);
3604 (void)centry_string(centry, mem_ctx);
3605 (void)centry_string(centry, mem_ctx);
3606 (void)centry_uint32(centry);
3607 (void)centry_sid(centry, &sid);
3608 (void)centry_sid(centry, &sid);
3610 centry_free(centry);
3612 if (!(state->success)) {
3613 return 1;
3615 DEBUG(10,("validate_u: %s ok\n", keystr));
3616 return 0;
3619 static int validate_loc_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3620 struct tdb_validation_status *state)
3622 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3624 if (!centry) {
3625 return 1;
3628 (void)centry_nttime(centry);
3629 (void)centry_nttime(centry);
3630 (void)centry_uint16(centry);
3632 centry_free(centry);
3634 if (!(state->success)) {
3635 return 1;
3637 DEBUG(10,("validate_loc_pol: %s ok\n", keystr));
3638 return 0;
3641 static int validate_pwd_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3642 struct tdb_validation_status *state)
3644 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3646 if (!centry) {
3647 return 1;
3650 (void)centry_uint16(centry);
3651 (void)centry_uint16(centry);
3652 (void)centry_uint32(centry);
3653 (void)centry_nttime(centry);
3654 (void)centry_nttime(centry);
3656 centry_free(centry);
3658 if (!(state->success)) {
3659 return 1;
3661 DEBUG(10,("validate_pwd_pol: %s ok\n", keystr));
3662 return 0;
3665 static int validate_cred(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3666 struct tdb_validation_status *state)
3668 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3670 if (!centry) {
3671 return 1;
3674 (void)centry_time(centry);
3675 (void)centry_hash16(centry, mem_ctx);
3677 /* We only have 17 bytes more data in the salted cred case. */
3678 if (centry->len - centry->ofs == 17) {
3679 (void)centry_hash16(centry, mem_ctx);
3682 centry_free(centry);
3684 if (!(state->success)) {
3685 return 1;
3687 DEBUG(10,("validate_cred: %s ok\n", keystr));
3688 return 0;
3691 static int validate_ul(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3692 struct tdb_validation_status *state)
3694 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3695 int32 num_entries, i;
3697 if (!centry) {
3698 return 1;
3701 num_entries = (int32)centry_uint32(centry);
3703 for (i=0; i< num_entries; i++) {
3704 struct dom_sid sid;
3705 (void)centry_string(centry, mem_ctx);
3706 (void)centry_string(centry, mem_ctx);
3707 (void)centry_string(centry, mem_ctx);
3708 (void)centry_string(centry, mem_ctx);
3709 (void)centry_sid(centry, &sid);
3710 (void)centry_sid(centry, &sid);
3713 centry_free(centry);
3715 if (!(state->success)) {
3716 return 1;
3718 DEBUG(10,("validate_ul: %s ok\n", keystr));
3719 return 0;
3722 static int validate_gl(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3723 struct tdb_validation_status *state)
3725 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3726 int32 num_entries, i;
3728 if (!centry) {
3729 return 1;
3732 num_entries = centry_uint32(centry);
3734 for (i=0; i< num_entries; i++) {
3735 (void)centry_string(centry, mem_ctx);
3736 (void)centry_string(centry, mem_ctx);
3737 (void)centry_uint32(centry);
3740 centry_free(centry);
3742 if (!(state->success)) {
3743 return 1;
3745 DEBUG(10,("validate_gl: %s ok\n", keystr));
3746 return 0;
3749 static int validate_ug(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3750 struct tdb_validation_status *state)
3752 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3753 int32 num_groups, i;
3755 if (!centry) {
3756 return 1;
3759 num_groups = centry_uint32(centry);
3761 for (i=0; i< num_groups; i++) {
3762 struct dom_sid sid;
3763 centry_sid(centry, &sid);
3766 centry_free(centry);
3768 if (!(state->success)) {
3769 return 1;
3771 DEBUG(10,("validate_ug: %s ok\n", keystr));
3772 return 0;
3775 static int validate_ua(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3776 struct tdb_validation_status *state)
3778 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3779 int32 num_aliases, i;
3781 if (!centry) {
3782 return 1;
3785 num_aliases = centry_uint32(centry);
3787 for (i=0; i < num_aliases; i++) {
3788 (void)centry_uint32(centry);
3791 centry_free(centry);
3793 if (!(state->success)) {
3794 return 1;
3796 DEBUG(10,("validate_ua: %s ok\n", keystr));
3797 return 0;
3800 static int validate_gm(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3801 struct tdb_validation_status *state)
3803 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3804 int32 num_names, i;
3806 if (!centry) {
3807 return 1;
3810 num_names = centry_uint32(centry);
3812 for (i=0; i< num_names; i++) {
3813 struct dom_sid sid;
3814 centry_sid(centry, &sid);
3815 (void)centry_string(centry, mem_ctx);
3816 (void)centry_uint32(centry);
3819 centry_free(centry);
3821 if (!(state->success)) {
3822 return 1;
3824 DEBUG(10,("validate_gm: %s ok\n", keystr));
3825 return 0;
3828 static int validate_dr(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3829 struct tdb_validation_status *state)
3831 /* Can't say anything about this other than must be nonzero. */
3832 if (dbuf.dsize == 0) {
3833 DEBUG(0,("validate_dr: Corrupt cache for key %s (len == 0) ?\n",
3834 keystr));
3835 state->bad_entry = true;
3836 state->success = false;
3837 return 1;
3840 DEBUG(10,("validate_dr: %s ok\n", keystr));
3841 return 0;
3844 static int validate_de(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3845 struct tdb_validation_status *state)
3847 /* Can't say anything about this other than must be nonzero. */
3848 if (dbuf.dsize == 0) {
3849 DEBUG(0,("validate_de: Corrupt cache for key %s (len == 0) ?\n",
3850 keystr));
3851 state->bad_entry = true;
3852 state->success = false;
3853 return 1;
3856 DEBUG(10,("validate_de: %s ok\n", keystr));
3857 return 0;
3860 static int validate_pwinfo(TALLOC_CTX *mem_ctx, const char *keystr,
3861 TDB_DATA dbuf, struct tdb_validation_status *state)
3863 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3865 if (!centry) {
3866 return 1;
3869 (void)centry_string(centry, mem_ctx);
3870 (void)centry_string(centry, mem_ctx);
3871 (void)centry_string(centry, mem_ctx);
3872 (void)centry_uint32(centry);
3874 centry_free(centry);
3876 if (!(state->success)) {
3877 return 1;
3879 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3880 return 0;
3883 static int validate_nss_an(TALLOC_CTX *mem_ctx, const char *keystr,
3884 TDB_DATA dbuf,
3885 struct tdb_validation_status *state)
3887 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3889 if (!centry) {
3890 return 1;
3893 (void)centry_string( centry, mem_ctx );
3895 centry_free(centry);
3897 if (!(state->success)) {
3898 return 1;
3900 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3901 return 0;
3904 static int validate_nss_na(TALLOC_CTX *mem_ctx, const char *keystr,
3905 TDB_DATA dbuf,
3906 struct tdb_validation_status *state)
3908 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3910 if (!centry) {
3911 return 1;
3914 (void)centry_string( centry, mem_ctx );
3916 centry_free(centry);
3918 if (!(state->success)) {
3919 return 1;
3921 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3922 return 0;
3925 static int validate_trustdomcache(TALLOC_CTX *mem_ctx, const char *keystr,
3926 TDB_DATA dbuf,
3927 struct tdb_validation_status *state)
3929 if (dbuf.dsize == 0) {
3930 DEBUG(0, ("validate_trustdomcache: Corrupt cache for "
3931 "key %s (len ==0) ?\n", keystr));
3932 state->bad_entry = true;
3933 state->success = false;
3934 return 1;
3937 DEBUG(10, ("validate_trustdomcache: %s ok\n", keystr));
3938 DEBUGADD(10, (" Don't trust me, I am a DUMMY!\n"));
3939 return 0;
3942 static int validate_offline(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3943 struct tdb_validation_status *state)
3945 if (dbuf.dsize != 4) {
3946 DEBUG(0,("validate_offline: Corrupt cache for key %s (len %u != 4) ?\n",
3947 keystr, (unsigned int)dbuf.dsize ));
3948 state->bad_entry = true;
3949 state->success = false;
3950 return 1;
3952 DEBUG(10,("validate_offline: %s ok\n", keystr));
3953 return 0;
3956 static int validate_ndr(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3957 struct tdb_validation_status *state)
3960 * Ignore validation for now. The proper way to do this is with a
3961 * checksum. Just pure parsing does not really catch much.
3963 return 0;
3966 static int validate_cache_version(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3967 struct tdb_validation_status *state)
3969 if (dbuf.dsize != 4) {
3970 DEBUG(0, ("validate_cache_version: Corrupt cache for "
3971 "key %s (len %u != 4) ?\n",
3972 keystr, (unsigned int)dbuf.dsize));
3973 state->bad_entry = true;
3974 state->success = false;
3975 return 1;
3978 DEBUG(10, ("validate_cache_version: %s ok\n", keystr));
3979 return 0;
3982 /***********************************************************************
3983 A list of all possible cache tdb keys with associated validation
3984 functions.
3985 ***********************************************************************/
3987 struct key_val_struct {
3988 const char *keyname;
3989 int (*validate_data_fn)(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, struct tdb_validation_status* state);
3990 } key_val[] = {
3991 {"SEQNUM/", validate_seqnum},
3992 {"NS/", validate_ns},
3993 {"SN/", validate_sn},
3994 {"U/", validate_u},
3995 {"LOC_POL/", validate_loc_pol},
3996 {"PWD_POL/", validate_pwd_pol},
3997 {"CRED/", validate_cred},
3998 {"UL/", validate_ul},
3999 {"GL/", validate_gl},
4000 {"UG/", validate_ug},
4001 {"UA", validate_ua},
4002 {"GM/", validate_gm},
4003 {"DR/", validate_dr},
4004 {"DE/", validate_de},
4005 {"NSS/PWINFO/", validate_pwinfo},
4006 {"TRUSTDOMCACHE/", validate_trustdomcache},
4007 {"NSS/NA/", validate_nss_na},
4008 {"NSS/AN/", validate_nss_an},
4009 {"WINBINDD_OFFLINE", validate_offline},
4010 {"NDR/", validate_ndr},
4011 {WINBINDD_CACHE_VERSION_KEYSTR, validate_cache_version},
4012 {NULL, NULL}
4015 /***********************************************************************
4016 Function to look at every entry in the tdb and validate it as far as
4017 possible.
4018 ***********************************************************************/
4020 static int cache_traverse_validate_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, void *state)
4022 int i;
4023 unsigned int max_key_len = 1024;
4024 struct tdb_validation_status *v_state = (struct tdb_validation_status *)state;
4026 /* Paranoia check. */
4027 if (strncmp("UA/", (const char *)kbuf.dptr, 3) == 0) {
4028 max_key_len = 1024 * 1024;
4030 if (kbuf.dsize > max_key_len) {
4031 DEBUG(0, ("cache_traverse_validate_fn: key length too large: "
4032 "(%u) > (%u)\n\n",
4033 (unsigned int)kbuf.dsize, (unsigned int)max_key_len));
4034 return 1;
4037 for (i = 0; key_val[i].keyname; i++) {
4038 size_t namelen = strlen(key_val[i].keyname);
4039 if (kbuf.dsize >= namelen && (
4040 strncmp(key_val[i].keyname, (const char *)kbuf.dptr, namelen)) == 0) {
4041 TALLOC_CTX *mem_ctx;
4042 char *keystr;
4043 int ret;
4045 keystr = SMB_MALLOC_ARRAY(char, kbuf.dsize+1);
4046 if (!keystr) {
4047 return 1;
4049 memcpy(keystr, kbuf.dptr, kbuf.dsize);
4050 keystr[kbuf.dsize] = '\0';
4052 mem_ctx = talloc_init("validate_ctx");
4053 if (!mem_ctx) {
4054 SAFE_FREE(keystr);
4055 return 1;
4058 ret = key_val[i].validate_data_fn(mem_ctx, keystr, dbuf,
4059 v_state);
4061 SAFE_FREE(keystr);
4062 talloc_destroy(mem_ctx);
4063 return ret;
4067 DEBUG(0,("cache_traverse_validate_fn: unknown cache entry\nkey :\n"));
4068 dump_data(0, (uint8 *)kbuf.dptr, kbuf.dsize);
4069 DEBUG(0,("data :\n"));
4070 dump_data(0, (uint8 *)dbuf.dptr, dbuf.dsize);
4071 v_state->unknown_key = true;
4072 v_state->success = false;
4073 return 1; /* terminate. */
4076 static void validate_panic(const char *const why)
4078 DEBUG(0,("validating cache: would panic %s\n", why ));
4079 DEBUGADD(0, ("exiting instead (cache validation mode)\n"));
4080 exit(47);
4083 /***********************************************************************
4084 Try and validate every entry in the winbindd cache. If we fail here,
4085 delete the cache tdb and return non-zero.
4086 ***********************************************************************/
4088 int winbindd_validate_cache(void)
4090 int ret = -1;
4091 const char *tdb_path = cache_path("winbindd_cache.tdb");
4092 TDB_CONTEXT *tdb = NULL;
4094 DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
4095 smb_panic_fn = validate_panic;
4098 tdb = tdb_open_log(tdb_path,
4099 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
4100 TDB_INCOMPATIBLE_HASH |
4101 ( lp_winbind_offline_logon()
4102 ? TDB_DEFAULT
4103 : TDB_DEFAULT | TDB_CLEAR_IF_FIRST ),
4104 O_RDWR|O_CREAT,
4105 0600);
4106 if (!tdb) {
4107 DEBUG(0, ("winbindd_validate_cache: "
4108 "error opening/initializing tdb\n"));
4109 goto done;
4111 tdb_close(tdb);
4113 ret = tdb_validate_and_backup(tdb_path, cache_traverse_validate_fn);
4115 if (ret != 0) {
4116 DEBUG(10, ("winbindd_validate_cache: validation not successful.\n"));
4117 DEBUGADD(10, ("removing tdb %s.\n", tdb_path));
4118 unlink(tdb_path);
4121 done:
4122 DEBUG(10, ("winbindd_validate_cache: restoring panic function\n"));
4123 smb_panic_fn = smb_panic;
4124 return ret;
4127 /***********************************************************************
4128 Try and validate every entry in the winbindd cache.
4129 ***********************************************************************/
4131 int winbindd_validate_cache_nobackup(void)
4133 int ret = -1;
4134 const char *tdb_path = cache_path("winbindd_cache.tdb");
4136 DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
4137 smb_panic_fn = validate_panic;
4140 if (wcache == NULL || wcache->tdb == NULL) {
4141 ret = tdb_validate_open(tdb_path, cache_traverse_validate_fn);
4142 } else {
4143 ret = tdb_validate(wcache->tdb, cache_traverse_validate_fn);
4146 if (ret != 0) {
4147 DEBUG(10, ("winbindd_validate_cache_nobackup: validation not "
4148 "successful.\n"));
4151 DEBUG(10, ("winbindd_validate_cache_nobackup: restoring panic "
4152 "function\n"));
4153 smb_panic_fn = smb_panic;
4154 return ret;
4157 bool winbindd_cache_validate_and_initialize(void)
4159 close_winbindd_cache();
4161 if (lp_winbind_offline_logon()) {
4162 if (winbindd_validate_cache() < 0) {
4163 DEBUG(0, ("winbindd cache tdb corrupt and no backup "
4164 "could be restored.\n"));
4168 return initialize_winbindd_cache();
4171 /*********************************************************************
4172 ********************************************************************/
4174 static bool add_wbdomain_to_tdc_array( struct winbindd_domain *new_dom,
4175 struct winbindd_tdc_domain **domains,
4176 size_t *num_domains )
4178 struct winbindd_tdc_domain *list = NULL;
4179 size_t idx;
4180 int i;
4181 bool set_only = false;
4183 /* don't allow duplicates */
4185 idx = *num_domains;
4186 list = *domains;
4188 for ( i=0; i< (*num_domains); i++ ) {
4189 if ( strequal( new_dom->name, list[i].domain_name ) ) {
4190 DEBUG(10,("add_wbdomain_to_tdc_array: Found existing record for %s\n",
4191 new_dom->name));
4192 idx = i;
4193 set_only = true;
4195 break;
4199 if ( !set_only ) {
4200 if ( !*domains ) {
4201 list = TALLOC_ARRAY( NULL, struct winbindd_tdc_domain, 1 );
4202 idx = 0;
4203 } else {
4204 list = TALLOC_REALLOC_ARRAY( *domains, *domains,
4205 struct winbindd_tdc_domain,
4206 (*num_domains)+1);
4207 idx = *num_domains;
4210 ZERO_STRUCT( list[idx] );
4213 if ( !list )
4214 return false;
4216 list[idx].domain_name = talloc_strdup( list, new_dom->name );
4217 list[idx].dns_name = talloc_strdup( list, new_dom->alt_name );
4219 if ( !is_null_sid( &new_dom->sid ) ) {
4220 sid_copy( &list[idx].sid, &new_dom->sid );
4221 } else {
4222 sid_copy(&list[idx].sid, &global_sid_NULL);
4225 if ( new_dom->domain_flags != 0x0 )
4226 list[idx].trust_flags = new_dom->domain_flags;
4228 if ( new_dom->domain_type != 0x0 )
4229 list[idx].trust_type = new_dom->domain_type;
4231 if ( new_dom->domain_trust_attribs != 0x0 )
4232 list[idx].trust_attribs = new_dom->domain_trust_attribs;
4234 if ( !set_only ) {
4235 *domains = list;
4236 *num_domains = idx + 1;
4239 return true;
4242 /*********************************************************************
4243 ********************************************************************/
4245 static TDB_DATA make_tdc_key( const char *domain_name )
4247 char *keystr = NULL;
4248 TDB_DATA key = { NULL, 0 };
4250 if ( !domain_name ) {
4251 DEBUG(5,("make_tdc_key: Keyname workgroup is NULL!\n"));
4252 return key;
4255 if (asprintf( &keystr, "TRUSTDOMCACHE/%s", domain_name ) == -1) {
4256 return key;
4258 key = string_term_tdb_data(keystr);
4260 return key;
4263 /*********************************************************************
4264 ********************************************************************/
4266 static int pack_tdc_domains( struct winbindd_tdc_domain *domains,
4267 size_t num_domains,
4268 unsigned char **buf )
4270 unsigned char *buffer = NULL;
4271 int len = 0;
4272 int buflen = 0;
4273 int i = 0;
4275 DEBUG(10,("pack_tdc_domains: Packing %d trusted domains\n",
4276 (int)num_domains));
4278 buflen = 0;
4280 again:
4281 len = 0;
4283 /* Store the number of array items first */
4284 len += tdb_pack( buffer+len, buflen-len, "d",
4285 num_domains );
4287 /* now pack each domain trust record */
4288 for ( i=0; i<num_domains; i++ ) {
4290 fstring tmp;
4292 if ( buflen > 0 ) {
4293 DEBUG(10,("pack_tdc_domains: Packing domain %s (%s)\n",
4294 domains[i].domain_name,
4295 domains[i].dns_name ? domains[i].dns_name : "UNKNOWN" ));
4298 len += tdb_pack( buffer+len, buflen-len, "fffddd",
4299 domains[i].domain_name,
4300 domains[i].dns_name,
4301 sid_to_fstring(tmp, &domains[i].sid),
4302 domains[i].trust_flags,
4303 domains[i].trust_attribs,
4304 domains[i].trust_type );
4307 if ( buflen < len ) {
4308 SAFE_FREE(buffer);
4309 if ( (buffer = SMB_MALLOC_ARRAY(unsigned char, len)) == NULL ) {
4310 DEBUG(0,("pack_tdc_domains: failed to alloc buffer!\n"));
4311 buflen = -1;
4312 goto done;
4314 buflen = len;
4315 goto again;
4318 *buf = buffer;
4320 done:
4321 return buflen;
4324 /*********************************************************************
4325 ********************************************************************/
4327 static size_t unpack_tdc_domains( unsigned char *buf, int buflen,
4328 struct winbindd_tdc_domain **domains )
4330 fstring domain_name, dns_name, sid_string;
4331 uint32 type, attribs, flags;
4332 int num_domains;
4333 int len = 0;
4334 int i;
4335 struct winbindd_tdc_domain *list = NULL;
4337 /* get the number of domains */
4338 len += tdb_unpack( buf+len, buflen-len, "d", &num_domains);
4339 if ( len == -1 ) {
4340 DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
4341 return 0;
4344 list = TALLOC_ARRAY( NULL, struct winbindd_tdc_domain, num_domains );
4345 if ( !list ) {
4346 DEBUG(0,("unpack_tdc_domains: Failed to talloc() domain list!\n"));
4347 return 0;
4350 for ( i=0; i<num_domains; i++ ) {
4351 len += tdb_unpack( buf+len, buflen-len, "fffddd",
4352 domain_name,
4353 dns_name,
4354 sid_string,
4355 &flags,
4356 &attribs,
4357 &type );
4359 if ( len == -1 ) {
4360 DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
4361 TALLOC_FREE( list );
4362 return 0;
4365 DEBUG(11,("unpack_tdc_domains: Unpacking domain %s (%s) "
4366 "SID %s, flags = 0x%x, attribs = 0x%x, type = 0x%x\n",
4367 domain_name, dns_name, sid_string,
4368 flags, attribs, type));
4370 list[i].domain_name = talloc_strdup( list, domain_name );
4371 list[i].dns_name = talloc_strdup( list, dns_name );
4372 if ( !string_to_sid( &(list[i].sid), sid_string ) ) {
4373 DEBUG(10,("unpack_tdc_domains: no SID for domain %s\n",
4374 domain_name));
4376 list[i].trust_flags = flags;
4377 list[i].trust_attribs = attribs;
4378 list[i].trust_type = type;
4381 *domains = list;
4383 return num_domains;
4386 /*********************************************************************
4387 ********************************************************************/
4389 static bool wcache_tdc_store_list( struct winbindd_tdc_domain *domains, size_t num_domains )
4391 TDB_DATA key = make_tdc_key( lp_workgroup() );
4392 TDB_DATA data = { NULL, 0 };
4393 int ret;
4395 if ( !key.dptr )
4396 return false;
4398 /* See if we were asked to delete the cache entry */
4400 if ( !domains ) {
4401 ret = tdb_delete( wcache->tdb, key );
4402 goto done;
4405 data.dsize = pack_tdc_domains( domains, num_domains, &data.dptr );
4407 if ( !data.dptr ) {
4408 ret = -1;
4409 goto done;
4412 ret = tdb_store( wcache->tdb, key, data, 0 );
4414 done:
4415 SAFE_FREE( data.dptr );
4416 SAFE_FREE( key.dptr );
4418 return ( ret != -1 );
4421 /*********************************************************************
4422 ********************************************************************/
4424 bool wcache_tdc_fetch_list( struct winbindd_tdc_domain **domains, size_t *num_domains )
4426 TDB_DATA key = make_tdc_key( lp_workgroup() );
4427 TDB_DATA data = { NULL, 0 };
4429 *domains = NULL;
4430 *num_domains = 0;
4432 if ( !key.dptr )
4433 return false;
4435 data = tdb_fetch( wcache->tdb, key );
4437 SAFE_FREE( key.dptr );
4439 if ( !data.dptr )
4440 return false;
4442 *num_domains = unpack_tdc_domains( data.dptr, data.dsize, domains );
4444 SAFE_FREE( data.dptr );
4446 if ( !*domains )
4447 return false;
4449 return true;
4452 /*********************************************************************
4453 ********************************************************************/
4455 bool wcache_tdc_add_domain( struct winbindd_domain *domain )
4457 struct winbindd_tdc_domain *dom_list = NULL;
4458 size_t num_domains = 0;
4459 bool ret = false;
4461 DEBUG(10,("wcache_tdc_add_domain: Adding domain %s (%s), SID %s, "
4462 "flags = 0x%x, attributes = 0x%x, type = 0x%x\n",
4463 domain->name, domain->alt_name,
4464 sid_string_dbg(&domain->sid),
4465 domain->domain_flags,
4466 domain->domain_trust_attribs,
4467 domain->domain_type));
4469 if ( !init_wcache() ) {
4470 return false;
4473 /* fetch the list */
4475 wcache_tdc_fetch_list( &dom_list, &num_domains );
4477 /* add the new domain */
4479 if ( !add_wbdomain_to_tdc_array( domain, &dom_list, &num_domains ) ) {
4480 goto done;
4483 /* pack the domain */
4485 if ( !wcache_tdc_store_list( dom_list, num_domains ) ) {
4486 goto done;
4489 /* Success */
4491 ret = true;
4492 done:
4493 TALLOC_FREE( dom_list );
4495 return ret;
4498 /*********************************************************************
4499 ********************************************************************/
4501 struct winbindd_tdc_domain * wcache_tdc_fetch_domain( TALLOC_CTX *ctx, const char *name )
4503 struct winbindd_tdc_domain *dom_list = NULL;
4504 size_t num_domains = 0;
4505 int i;
4506 struct winbindd_tdc_domain *d = NULL;
4508 DEBUG(10,("wcache_tdc_fetch_domain: Searching for domain %s\n", name));
4510 if ( !init_wcache() ) {
4511 return false;
4514 /* fetch the list */
4516 wcache_tdc_fetch_list( &dom_list, &num_domains );
4518 for ( i=0; i<num_domains; i++ ) {
4519 if ( strequal(name, dom_list[i].domain_name) ||
4520 strequal(name, dom_list[i].dns_name) )
4522 DEBUG(10,("wcache_tdc_fetch_domain: Found domain %s\n",
4523 name));
4525 d = TALLOC_P( ctx, struct winbindd_tdc_domain );
4526 if ( !d )
4527 break;
4529 d->domain_name = talloc_strdup( d, dom_list[i].domain_name );
4530 d->dns_name = talloc_strdup( d, dom_list[i].dns_name );
4531 sid_copy( &d->sid, &dom_list[i].sid );
4532 d->trust_flags = dom_list[i].trust_flags;
4533 d->trust_type = dom_list[i].trust_type;
4534 d->trust_attribs = dom_list[i].trust_attribs;
4536 break;
4540 TALLOC_FREE( dom_list );
4542 return d;
4545 /*********************************************************************
4546 ********************************************************************/
4548 struct winbindd_tdc_domain*
4549 wcache_tdc_fetch_domainbysid(TALLOC_CTX *ctx,
4550 const struct dom_sid *sid)
4552 struct winbindd_tdc_domain *dom_list = NULL;
4553 size_t num_domains = 0;
4554 int i;
4555 struct winbindd_tdc_domain *d = NULL;
4557 DEBUG(10,("wcache_tdc_fetch_domainbysid: Searching for domain %s\n",
4558 sid_string_dbg(sid)));
4560 if (!init_wcache()) {
4561 return false;
4564 /* fetch the list */
4566 wcache_tdc_fetch_list(&dom_list, &num_domains);
4568 for (i = 0; i<num_domains; i++) {
4569 if (sid_equal(sid, &(dom_list[i].sid))) {
4570 DEBUG(10, ("wcache_tdc_fetch_domainbysid: "
4571 "Found domain %s for SID %s\n",
4572 dom_list[i].domain_name,
4573 sid_string_dbg(sid)));
4575 d = TALLOC_P(ctx, struct winbindd_tdc_domain);
4576 if (!d)
4577 break;
4579 d->domain_name = talloc_strdup(d,
4580 dom_list[i].domain_name);
4582 d->dns_name = talloc_strdup(d, dom_list[i].dns_name);
4583 sid_copy(&d->sid, &dom_list[i].sid);
4584 d->trust_flags = dom_list[i].trust_flags;
4585 d->trust_type = dom_list[i].trust_type;
4586 d->trust_attribs = dom_list[i].trust_attribs;
4588 break;
4592 TALLOC_FREE(dom_list);
4594 return d;
4598 /*********************************************************************
4599 ********************************************************************/
4601 void wcache_tdc_clear( void )
4603 if ( !init_wcache() )
4604 return;
4606 wcache_tdc_store_list( NULL, 0 );
4608 return;
4612 /*********************************************************************
4613 ********************************************************************/
4615 static void wcache_save_user_pwinfo(struct winbindd_domain *domain,
4616 NTSTATUS status,
4617 const struct dom_sid *user_sid,
4618 const char *homedir,
4619 const char *shell,
4620 const char *gecos,
4621 uint32 gid)
4623 struct cache_entry *centry;
4624 fstring tmp;
4626 if ( (centry = centry_start(domain, status)) == NULL )
4627 return;
4629 centry_put_string( centry, homedir );
4630 centry_put_string( centry, shell );
4631 centry_put_string( centry, gecos );
4632 centry_put_uint32( centry, gid );
4634 centry_end(centry, "NSS/PWINFO/%s", sid_to_fstring(tmp, user_sid) );
4636 DEBUG(10,("wcache_save_user_pwinfo: %s\n", sid_string_dbg(user_sid) ));
4638 centry_free(centry);
4641 #ifdef HAVE_ADS
4643 NTSTATUS nss_get_info_cached( struct winbindd_domain *domain,
4644 const struct dom_sid *user_sid,
4645 TALLOC_CTX *ctx,
4646 ADS_STRUCT *ads, LDAPMessage *msg,
4647 const char **homedir, const char **shell,
4648 const char **gecos, gid_t *p_gid)
4650 struct winbind_cache *cache = get_cache(domain);
4651 struct cache_entry *centry = NULL;
4652 NTSTATUS nt_status;
4653 fstring tmp;
4655 if (!cache->tdb)
4656 goto do_query;
4658 centry = wcache_fetch(cache, domain, "NSS/PWINFO/%s",
4659 sid_to_fstring(tmp, user_sid));
4661 if (!centry)
4662 goto do_query;
4664 *homedir = centry_string( centry, ctx );
4665 *shell = centry_string( centry, ctx );
4666 *gecos = centry_string( centry, ctx );
4667 *p_gid = centry_uint32( centry );
4669 centry_free(centry);
4671 DEBUG(10,("nss_get_info_cached: [Cached] - user_sid %s\n",
4672 sid_string_dbg(user_sid)));
4674 return NT_STATUS_OK;
4676 do_query:
4678 nt_status = nss_get_info( domain->name, user_sid, ctx, ads, msg,
4679 homedir, shell, gecos, p_gid );
4681 DEBUG(10, ("nss_get_info returned %s\n", nt_errstr(nt_status)));
4683 if ( NT_STATUS_IS_OK(nt_status) ) {
4684 DEBUG(10, ("result:\n\thomedir = '%s'\n", *homedir));
4685 DEBUGADD(10, ("\tshell = '%s'\n", *shell));
4686 DEBUGADD(10, ("\tgecos = '%s'\n", *gecos));
4687 DEBUGADD(10, ("\tgid = '%u'\n", (unsigned int)*p_gid));
4689 wcache_save_user_pwinfo( domain, nt_status, user_sid,
4690 *homedir, *shell, *gecos, *p_gid );
4693 if ( NT_STATUS_EQUAL( nt_status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND ) ) {
4694 DEBUG(5,("nss_get_info_cached: Setting domain %s offline\n",
4695 domain->name ));
4696 set_domain_offline( domain );
4699 return nt_status;
4702 #endif
4704 /* the cache backend methods are exposed via this structure */
4705 struct winbindd_methods cache_methods = {
4706 true,
4707 query_user_list,
4708 enum_dom_groups,
4709 enum_local_groups,
4710 name_to_sid,
4711 sid_to_name,
4712 rids_to_names,
4713 query_user,
4714 lookup_usergroups,
4715 lookup_useraliases,
4716 lookup_groupmem,
4717 sequence_number,
4718 lockout_policy,
4719 password_policy,
4720 trusted_domains
4723 static bool wcache_ndr_key(TALLOC_CTX *mem_ctx, char *domain_name,
4724 uint32_t opnum, const DATA_BLOB *req,
4725 TDB_DATA *pkey)
4727 char *key;
4728 size_t keylen;
4730 key = talloc_asprintf(mem_ctx, "NDR/%s/%d/", domain_name, (int)opnum);
4731 if (key == NULL) {
4732 return false;
4734 keylen = talloc_get_size(key) - 1;
4736 key = talloc_realloc(mem_ctx, key, char, keylen + req->length);
4737 if (key == NULL) {
4738 return false;
4740 memcpy(key + keylen, req->data, req->length);
4742 pkey->dptr = (uint8_t *)key;
4743 pkey->dsize = talloc_get_size(key);
4744 return true;
4747 static bool wcache_opnum_cacheable(uint32_t opnum)
4749 switch (opnum) {
4750 case NDR_WBINT_PING:
4751 case NDR_WBINT_QUERYSEQUENCENUMBER:
4752 case NDR_WBINT_ALLOCATEUID:
4753 case NDR_WBINT_ALLOCATEGID:
4754 case NDR_WBINT_CHECKMACHINEACCOUNT:
4755 case NDR_WBINT_CHANGEMACHINEACCOUNT:
4756 case NDR_WBINT_PINGDC:
4757 return false;
4759 return true;
4762 bool wcache_fetch_ndr(TALLOC_CTX *mem_ctx, struct winbindd_domain *domain,
4763 uint32_t opnum, const DATA_BLOB *req, DATA_BLOB *resp)
4765 TDB_DATA key, data;
4766 bool ret = false;
4768 if (!wcache_opnum_cacheable(opnum) ||
4769 is_my_own_sam_domain(domain) ||
4770 is_builtin_domain(domain)) {
4771 return false;
4774 if (wcache->tdb == NULL) {
4775 return false;
4778 if (!wcache_ndr_key(talloc_tos(), domain->name, opnum, req, &key)) {
4779 return false;
4781 data = tdb_fetch(wcache->tdb, key);
4782 TALLOC_FREE(key.dptr);
4784 if (data.dptr == NULL) {
4785 return false;
4787 if (data.dsize < 12) {
4788 goto fail;
4791 if (!is_domain_offline(domain)) {
4792 uint32_t entry_seqnum, dom_seqnum, last_check;
4793 uint64_t entry_timeout;
4795 if (!wcache_fetch_seqnum(domain->name, &dom_seqnum,
4796 &last_check)) {
4797 goto fail;
4799 entry_seqnum = IVAL(data.dptr, 0);
4800 if (entry_seqnum != dom_seqnum) {
4801 DEBUG(10, ("Entry has wrong sequence number: %d\n",
4802 (int)entry_seqnum));
4803 goto fail;
4805 entry_timeout = BVAL(data.dptr, 4);
4806 if (entry_timeout > time(NULL)) {
4807 DEBUG(10, ("Entry has timed out\n"));
4808 goto fail;
4812 resp->data = (uint8_t *)talloc_memdup(mem_ctx, data.dptr + 12,
4813 data.dsize - 12);
4814 if (resp->data == NULL) {
4815 DEBUG(10, ("talloc failed\n"));
4816 goto fail;
4818 resp->length = data.dsize - 12;
4820 ret = true;
4821 fail:
4822 SAFE_FREE(data.dptr);
4823 return ret;
4826 void wcache_store_ndr(struct winbindd_domain *domain, uint32_t opnum,
4827 const DATA_BLOB *req, const DATA_BLOB *resp)
4829 TDB_DATA key, data;
4830 uint32_t dom_seqnum, last_check;
4831 uint64_t timeout;
4833 if (!wcache_opnum_cacheable(opnum) ||
4834 is_my_own_sam_domain(domain) ||
4835 is_builtin_domain(domain)) {
4836 return;
4839 if (wcache->tdb == NULL) {
4840 return;
4843 if (!wcache_fetch_seqnum(domain->name, &dom_seqnum, &last_check)) {
4844 DEBUG(10, ("could not fetch seqnum for domain %s\n",
4845 domain->name));
4846 return;
4849 if (!wcache_ndr_key(talloc_tos(), domain->name, opnum, req, &key)) {
4850 return;
4853 timeout = time(NULL) + lp_winbind_cache_time();
4855 data.dsize = resp->length + 12;
4856 data.dptr = talloc_array(key.dptr, uint8_t, data.dsize);
4857 if (data.dptr == NULL) {
4858 goto done;
4861 SIVAL(data.dptr, 0, dom_seqnum);
4862 SBVAL(data.dptr, 4, timeout);
4863 memcpy(data.dptr + 12, resp->data, resp->length);
4865 tdb_store(wcache->tdb, key, data, 0);
4867 done:
4868 TALLOC_FREE(key.dptr);
4869 return;