s3: In winbind, close parent/child sockets
[Samba.git] / source3 / winbindd / winbindd_cache.c
blob68a86257da2b8f199d4416af2af0bb631048bb5e
1 /*
2 Unix SMB/CIFS implementation.
4 Winbind cache backend functions
6 Copyright (C) Andrew Tridgell 2001
7 Copyright (C) Gerald Carter 2003-2007
8 Copyright (C) Volker Lendecke 2005
9 Copyright (C) Guenther Deschner 2005
10 Copyright (C) Michael Adam 2007
12 This program is free software; you can redistribute it and/or modify
13 it under the terms of the GNU General Public License as published by
14 the Free Software Foundation; either version 3 of the License, or
15 (at your option) any later version.
17 This program is distributed in the hope that it will be useful,
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 GNU General Public License for more details.
22 You should have received a copy of the GNU General Public License
23 along with this program. If not, see <http://www.gnu.org/licenses/>.
26 #include "includes.h"
27 #include "system/filesys.h"
28 #include "winbindd.h"
29 #include "tdb_validate.h"
30 #include "../libcli/auth/libcli_auth.h"
31 #include "../librpc/gen_ndr/ndr_wbint.h"
32 #include "ads.h"
33 #include "nss_info.h"
34 #include "../libcli/security/security.h"
35 #include "passdb/machine_sid.h"
37 #undef DBGC_CLASS
38 #define DBGC_CLASS DBGC_WINBIND
40 #define WINBINDD_CACHE_VERSION 2
41 #define WINBINDD_CACHE_VERSION_KEYSTR "WINBINDD_CACHE_VERSION"
43 extern struct winbindd_methods reconnect_methods;
44 #ifdef HAVE_ADS
45 extern struct winbindd_methods ads_methods;
46 #endif
47 extern struct winbindd_methods builtin_passdb_methods;
48 extern struct winbindd_methods sam_passdb_methods;
51 * JRA. KEEP THIS LIST UP TO DATE IF YOU ADD CACHE ENTRIES.
52 * Here are the list of entry types that are *not* stored
53 * as form struct cache_entry in the cache.
56 static const char *non_centry_keys[] = {
57 "SEQNUM/",
58 "DR/",
59 "DE/",
60 "WINBINDD_OFFLINE",
61 WINBINDD_CACHE_VERSION_KEYSTR,
62 NULL
65 /************************************************************************
66 Is this key a non-centry type ?
67 ************************************************************************/
69 static bool is_non_centry_key(TDB_DATA kbuf)
71 int i;
73 if (kbuf.dptr == NULL || kbuf.dsize == 0) {
74 return false;
76 for (i = 0; non_centry_keys[i] != NULL; i++) {
77 size_t namelen = strlen(non_centry_keys[i]);
78 if (kbuf.dsize < namelen) {
79 continue;
81 if (strncmp(non_centry_keys[i], (const char *)kbuf.dptr, namelen) == 0) {
82 return true;
85 return false;
88 /* Global online/offline state - False when online. winbindd starts up online
89 and sets this to true if the first query fails and there's an entry in
90 the cache tdb telling us to stay offline. */
92 static bool global_winbindd_offline_state;
94 struct winbind_cache {
95 TDB_CONTEXT *tdb;
98 struct cache_entry {
99 NTSTATUS status;
100 uint32 sequence_number;
101 uint64_t timeout;
102 uint8 *data;
103 uint32 len, ofs;
106 void (*smb_panic_fn)(const char *const why) = smb_panic;
108 #define WINBINDD_MAX_CACHE_SIZE (50*1024*1024)
110 static struct winbind_cache *wcache;
112 /* get the winbind_cache structure */
113 static struct winbind_cache *get_cache(struct winbindd_domain *domain)
115 struct winbind_cache *ret = wcache;
117 /* We have to know what type of domain we are dealing with first. */
119 if (domain->internal) {
120 domain->backend = &builtin_passdb_methods;
121 domain->initialized = True;
124 if (strequal(domain->name, get_global_sam_name()) &&
125 sid_check_is_domain(&domain->sid)) {
126 domain->backend = &sam_passdb_methods;
127 domain->initialized = True;
130 if ( !domain->initialized ) {
131 init_dc_connection( domain );
135 OK. listen up becasue I'm only going to say this once.
136 We have the following scenarios to consider
137 (a) trusted AD domains on a Samba DC,
138 (b) trusted AD domains and we are joined to a non-kerberos domain
139 (c) trusted AD domains and we are joined to a kerberos (AD) domain
141 For (a) we can always contact the trusted domain using krb5
142 since we have the domain trust account password
144 For (b) we can only use RPC since we have no way of
145 getting a krb5 ticket in our own domain
147 For (c) we can always use krb5 since we have a kerberos trust
149 --jerry
152 if (!domain->backend) {
153 #ifdef HAVE_ADS
154 struct winbindd_domain *our_domain = domain;
156 /* find our domain first so we can figure out if we
157 are joined to a kerberized domain */
159 if ( !domain->primary )
160 our_domain = find_our_domain();
162 if ((our_domain->active_directory || IS_DC)
163 && domain->active_directory
164 && !lp_winbind_rpc_only()) {
165 DEBUG(5,("get_cache: Setting ADS methods for domain %s\n", domain->name));
166 domain->backend = &ads_methods;
167 } else {
168 #endif /* HAVE_ADS */
169 DEBUG(5,("get_cache: Setting MS-RPC methods for domain %s\n", domain->name));
170 domain->backend = &reconnect_methods;
171 #ifdef HAVE_ADS
173 #endif /* HAVE_ADS */
176 if (ret)
177 return ret;
179 ret = SMB_XMALLOC_P(struct winbind_cache);
180 ZERO_STRUCTP(ret);
182 wcache = ret;
183 wcache_flush_cache();
185 return ret;
189 free a centry structure
191 static void centry_free(struct cache_entry *centry)
193 if (!centry)
194 return;
195 SAFE_FREE(centry->data);
196 free(centry);
199 static bool centry_check_bytes(struct cache_entry *centry, size_t nbytes)
201 if (centry->len - centry->ofs < nbytes) {
202 DEBUG(0,("centry corruption? needed %u bytes, have %d\n",
203 (unsigned int)nbytes,
204 centry->len - centry->ofs));
205 return false;
207 return true;
211 pull a uint64_t from a cache entry
213 static uint64_t centry_uint64_t(struct cache_entry *centry)
215 uint64_t ret;
217 if (!centry_check_bytes(centry, 8)) {
218 smb_panic_fn("centry_uint64_t");
220 ret = BVAL(centry->data, centry->ofs);
221 centry->ofs += 8;
222 return ret;
226 pull a uint32 from a cache entry
228 static uint32 centry_uint32(struct cache_entry *centry)
230 uint32 ret;
232 if (!centry_check_bytes(centry, 4)) {
233 smb_panic_fn("centry_uint32");
235 ret = IVAL(centry->data, centry->ofs);
236 centry->ofs += 4;
237 return ret;
241 pull a uint16 from a cache entry
243 static uint16 centry_uint16(struct cache_entry *centry)
245 uint16 ret;
246 if (!centry_check_bytes(centry, 2)) {
247 smb_panic_fn("centry_uint16");
249 ret = SVAL(centry->data, centry->ofs);
250 centry->ofs += 2;
251 return ret;
255 pull a uint8 from a cache entry
257 static uint8 centry_uint8(struct cache_entry *centry)
259 uint8 ret;
260 if (!centry_check_bytes(centry, 1)) {
261 smb_panic_fn("centry_uint8");
263 ret = CVAL(centry->data, centry->ofs);
264 centry->ofs += 1;
265 return ret;
269 pull a NTTIME from a cache entry
271 static NTTIME centry_nttime(struct cache_entry *centry)
273 NTTIME ret;
274 if (!centry_check_bytes(centry, 8)) {
275 smb_panic_fn("centry_nttime");
277 ret = IVAL(centry->data, centry->ofs);
278 centry->ofs += 4;
279 ret += (uint64)IVAL(centry->data, centry->ofs) << 32;
280 centry->ofs += 4;
281 return ret;
285 pull a time_t from a cache entry. time_t stored portably as a 64-bit time.
287 static time_t centry_time(struct cache_entry *centry)
289 return (time_t)centry_nttime(centry);
292 /* pull a string from a cache entry, using the supplied
293 talloc context
295 static char *centry_string(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
297 uint32 len;
298 char *ret;
300 len = centry_uint8(centry);
302 if (len == 0xFF) {
303 /* a deliberate NULL string */
304 return NULL;
307 if (!centry_check_bytes(centry, (size_t)len)) {
308 smb_panic_fn("centry_string");
311 ret = TALLOC_ARRAY(mem_ctx, char, len+1);
312 if (!ret) {
313 smb_panic_fn("centry_string out of memory\n");
315 memcpy(ret,centry->data + centry->ofs, len);
316 ret[len] = 0;
317 centry->ofs += len;
318 return ret;
321 /* pull a hash16 from a cache entry, using the supplied
322 talloc context
324 static char *centry_hash16(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
326 uint32 len;
327 char *ret;
329 len = centry_uint8(centry);
331 if (len != 16) {
332 DEBUG(0,("centry corruption? hash len (%u) != 16\n",
333 len ));
334 return NULL;
337 if (!centry_check_bytes(centry, 16)) {
338 return NULL;
341 ret = TALLOC_ARRAY(mem_ctx, char, 16);
342 if (!ret) {
343 smb_panic_fn("centry_hash out of memory\n");
345 memcpy(ret,centry->data + centry->ofs, 16);
346 centry->ofs += 16;
347 return ret;
350 /* pull a sid from a cache entry, using the supplied
351 talloc context
353 static bool centry_sid(struct cache_entry *centry, struct dom_sid *sid)
355 char *sid_string;
356 bool ret;
358 sid_string = centry_string(centry, talloc_tos());
359 if (sid_string == NULL) {
360 return false;
362 ret = string_to_sid(sid, sid_string);
363 TALLOC_FREE(sid_string);
364 return ret;
369 pull a NTSTATUS from a cache entry
371 static NTSTATUS centry_ntstatus(struct cache_entry *centry)
373 NTSTATUS status;
375 status = NT_STATUS(centry_uint32(centry));
376 return status;
380 /* the server is considered down if it can't give us a sequence number */
381 static bool wcache_server_down(struct winbindd_domain *domain)
383 bool ret;
385 if (!wcache->tdb)
386 return false;
388 ret = (domain->sequence_number == DOM_SEQUENCE_NONE);
390 if (ret)
391 DEBUG(10,("wcache_server_down: server for Domain %s down\n",
392 domain->name ));
393 return ret;
396 static bool wcache_fetch_seqnum(const char *domain_name, uint32_t *seqnum,
397 uint32_t *last_seq_check)
399 char *key;
400 TDB_DATA data;
402 if (wcache->tdb == NULL) {
403 DEBUG(10,("wcache_fetch_seqnum: tdb == NULL\n"));
404 return false;
407 key = talloc_asprintf(talloc_tos(), "SEQNUM/%s", domain_name);
408 if (key == NULL) {
409 DEBUG(10, ("talloc failed\n"));
410 return false;
413 data = tdb_fetch_bystring(wcache->tdb, key);
414 TALLOC_FREE(key);
416 if (data.dptr == NULL) {
417 DEBUG(10, ("wcache_fetch_seqnum: %s not found\n",
418 domain_name));
419 return false;
421 if (data.dsize != 8) {
422 DEBUG(10, ("wcache_fetch_seqnum: invalid data size %d\n",
423 (int)data.dsize));
424 SAFE_FREE(data.dptr);
425 return false;
428 *seqnum = IVAL(data.dptr, 0);
429 *last_seq_check = IVAL(data.dptr, 4);
430 SAFE_FREE(data.dptr);
432 return true;
435 static NTSTATUS fetch_cache_seqnum( struct winbindd_domain *domain, time_t now )
437 uint32 last_check, time_diff;
439 if (!wcache_fetch_seqnum(domain->name, &domain->sequence_number,
440 &last_check)) {
441 return NT_STATUS_UNSUCCESSFUL;
443 domain->last_seq_check = last_check;
445 /* have we expired? */
447 time_diff = now - domain->last_seq_check;
448 if ( time_diff > lp_winbind_cache_time() ) {
449 DEBUG(10,("fetch_cache_seqnum: timeout [%s][%u @ %u]\n",
450 domain->name, domain->sequence_number,
451 (uint32)domain->last_seq_check));
452 return NT_STATUS_UNSUCCESSFUL;
455 DEBUG(10,("fetch_cache_seqnum: success [%s][%u @ %u]\n",
456 domain->name, domain->sequence_number,
457 (uint32)domain->last_seq_check));
459 return NT_STATUS_OK;
462 bool wcache_store_seqnum(const char *domain_name, uint32_t seqnum,
463 time_t last_seq_check)
465 char *key_str;
466 uint8_t buf[8];
467 int ret;
469 if (wcache->tdb == NULL) {
470 DEBUG(10, ("wcache_store_seqnum: wcache->tdb == NULL\n"));
471 return false;
474 key_str = talloc_asprintf(talloc_tos(), "SEQNUM/%s", domain_name);
475 if (key_str == NULL) {
476 DEBUG(10, ("talloc_asprintf failed\n"));
477 return false;
480 SIVAL(buf, 0, seqnum);
481 SIVAL(buf, 4, last_seq_check);
483 ret = tdb_store_bystring(wcache->tdb, key_str,
484 make_tdb_data(buf, sizeof(buf)), TDB_REPLACE);
485 TALLOC_FREE(key_str);
486 if (ret == -1) {
487 DEBUG(10, ("tdb_store_bystring failed: %s\n",
488 tdb_errorstr(wcache->tdb)));
489 TALLOC_FREE(key_str);
490 return false;
493 DEBUG(10, ("wcache_store_seqnum: success [%s][%u @ %u]\n",
494 domain_name, seqnum, (unsigned)last_seq_check));
496 return true;
499 static bool store_cache_seqnum( struct winbindd_domain *domain )
501 return wcache_store_seqnum(domain->name, domain->sequence_number,
502 domain->last_seq_check);
506 refresh the domain sequence number. If force is true
507 then always refresh it, no matter how recently we fetched it
510 static void refresh_sequence_number(struct winbindd_domain *domain, bool force)
512 NTSTATUS status;
513 unsigned time_diff;
514 time_t t = time(NULL);
515 unsigned cache_time = lp_winbind_cache_time();
517 if (is_domain_offline(domain)) {
518 return;
521 get_cache( domain );
523 #if 0 /* JERRY -- disable as the default cache time is now 5 minutes */
524 /* trying to reconnect is expensive, don't do it too often */
525 if (domain->sequence_number == DOM_SEQUENCE_NONE) {
526 cache_time *= 8;
528 #endif
530 time_diff = t - domain->last_seq_check;
532 /* see if we have to refetch the domain sequence number */
533 if (!force && (time_diff < cache_time) &&
534 (domain->sequence_number != DOM_SEQUENCE_NONE) &&
535 NT_STATUS_IS_OK(domain->last_status)) {
536 DEBUG(10, ("refresh_sequence_number: %s time ok\n", domain->name));
537 goto done;
540 /* try to get the sequence number from the tdb cache first */
541 /* this will update the timestamp as well */
543 status = fetch_cache_seqnum( domain, t );
544 if (NT_STATUS_IS_OK(status) &&
545 (domain->sequence_number != DOM_SEQUENCE_NONE) &&
546 NT_STATUS_IS_OK(domain->last_status)) {
547 goto done;
550 /* important! make sure that we know if this is a native
551 mode domain or not. And that we can contact it. */
553 if ( winbindd_can_contact_domain( domain ) ) {
554 status = domain->backend->sequence_number(domain,
555 &domain->sequence_number);
556 } else {
557 /* just use the current time */
558 status = NT_STATUS_OK;
559 domain->sequence_number = time(NULL);
563 /* the above call could have set our domain->backend to NULL when
564 * coming from offline to online mode, make sure to reinitialize the
565 * backend - Guenther */
566 get_cache( domain );
568 if (!NT_STATUS_IS_OK(status)) {
569 DEBUG(10,("refresh_sequence_number: failed with %s\n", nt_errstr(status)));
570 domain->sequence_number = DOM_SEQUENCE_NONE;
573 domain->last_status = status;
574 domain->last_seq_check = time(NULL);
576 /* save the new sequence number in the cache */
577 store_cache_seqnum( domain );
579 done:
580 DEBUG(10, ("refresh_sequence_number: %s seq number is now %d\n",
581 domain->name, domain->sequence_number));
583 return;
587 decide if a cache entry has expired
589 static bool centry_expired(struct winbindd_domain *domain, const char *keystr, struct cache_entry *centry)
591 /* If we've been told to be offline - stay in that state... */
592 if (lp_winbind_offline_logon() && global_winbindd_offline_state) {
593 DEBUG(10,("centry_expired: Key %s for domain %s valid as winbindd is globally offline.\n",
594 keystr, domain->name ));
595 return false;
598 /* when the domain is offline return the cached entry.
599 * This deals with transient offline states... */
601 if (!domain->online) {
602 DEBUG(10,("centry_expired: Key %s for domain %s valid as domain is offline.\n",
603 keystr, domain->name ));
604 return false;
607 /* if the server is OK and our cache entry came from when it was down then
608 the entry is invalid */
609 if ((domain->sequence_number != DOM_SEQUENCE_NONE) &&
610 (centry->sequence_number == DOM_SEQUENCE_NONE)) {
611 DEBUG(10,("centry_expired: Key %s for domain %s invalid sequence.\n",
612 keystr, domain->name ));
613 return true;
616 /* if the server is down or the cache entry is not older than the
617 current sequence number or it did not timeout then it is OK */
618 if (wcache_server_down(domain)
619 || ((centry->sequence_number == domain->sequence_number)
620 && (centry->timeout > time(NULL)))) {
621 DEBUG(10,("centry_expired: Key %s for domain %s is good.\n",
622 keystr, domain->name ));
623 return false;
626 DEBUG(10,("centry_expired: Key %s for domain %s expired\n",
627 keystr, domain->name ));
629 /* it's expired */
630 return true;
633 static struct cache_entry *wcache_fetch_raw(char *kstr)
635 TDB_DATA data;
636 struct cache_entry *centry;
637 TDB_DATA key;
639 key = string_tdb_data(kstr);
640 data = tdb_fetch(wcache->tdb, key);
641 if (!data.dptr) {
642 /* a cache miss */
643 return NULL;
646 centry = SMB_XMALLOC_P(struct cache_entry);
647 centry->data = (unsigned char *)data.dptr;
648 centry->len = data.dsize;
649 centry->ofs = 0;
651 if (centry->len < 16) {
652 /* huh? corrupt cache? */
653 DEBUG(10,("wcache_fetch_raw: Corrupt cache for key %s "
654 "(len < 16)?\n", kstr));
655 centry_free(centry);
656 return NULL;
659 centry->status = centry_ntstatus(centry);
660 centry->sequence_number = centry_uint32(centry);
661 centry->timeout = centry_uint64_t(centry);
663 return centry;
666 static bool is_my_own_sam_domain(struct winbindd_domain *domain)
668 if (strequal(domain->name, get_global_sam_name()) &&
669 sid_check_is_domain(&domain->sid)) {
670 return true;
673 return false;
676 static bool is_builtin_domain(struct winbindd_domain *domain)
678 if (strequal(domain->name, "BUILTIN") &&
679 sid_check_is_builtin(&domain->sid)) {
680 return true;
683 return false;
687 fetch an entry from the cache, with a varargs key. auto-fetch the sequence
688 number and return status
690 static struct cache_entry *wcache_fetch(struct winbind_cache *cache,
691 struct winbindd_domain *domain,
692 const char *format, ...) PRINTF_ATTRIBUTE(3,4);
693 static struct cache_entry *wcache_fetch(struct winbind_cache *cache,
694 struct winbindd_domain *domain,
695 const char *format, ...)
697 va_list ap;
698 char *kstr;
699 struct cache_entry *centry;
701 if (!winbindd_use_cache() ||
702 is_my_own_sam_domain(domain) ||
703 is_builtin_domain(domain)) {
704 return NULL;
707 refresh_sequence_number(domain, false);
709 va_start(ap, format);
710 smb_xvasprintf(&kstr, format, ap);
711 va_end(ap);
713 centry = wcache_fetch_raw(kstr);
714 if (centry == NULL) {
715 free(kstr);
716 return NULL;
719 if (centry_expired(domain, kstr, centry)) {
721 DEBUG(10,("wcache_fetch: entry %s expired for domain %s\n",
722 kstr, domain->name ));
724 centry_free(centry);
725 free(kstr);
726 return NULL;
729 DEBUG(10,("wcache_fetch: returning entry %s for domain %s\n",
730 kstr, domain->name ));
732 free(kstr);
733 return centry;
736 static void wcache_delete(const char *format, ...) PRINTF_ATTRIBUTE(1,2);
737 static void wcache_delete(const char *format, ...)
739 va_list ap;
740 char *kstr;
741 TDB_DATA key;
743 va_start(ap, format);
744 smb_xvasprintf(&kstr, format, ap);
745 va_end(ap);
747 key = string_tdb_data(kstr);
749 tdb_delete(wcache->tdb, key);
750 free(kstr);
754 make sure we have at least len bytes available in a centry
756 static void centry_expand(struct cache_entry *centry, uint32 len)
758 if (centry->len - centry->ofs >= len)
759 return;
760 centry->len *= 2;
761 centry->data = SMB_REALLOC_ARRAY(centry->data, unsigned char,
762 centry->len);
763 if (!centry->data) {
764 DEBUG(0,("out of memory: needed %d bytes in centry_expand\n", centry->len));
765 smb_panic_fn("out of memory in centry_expand");
770 push a uint64_t into a centry
772 static void centry_put_uint64_t(struct cache_entry *centry, uint64_t v)
774 centry_expand(centry, 8);
775 SBVAL(centry->data, centry->ofs, v);
776 centry->ofs += 8;
780 push a uint32 into a centry
782 static void centry_put_uint32(struct cache_entry *centry, uint32 v)
784 centry_expand(centry, 4);
785 SIVAL(centry->data, centry->ofs, v);
786 centry->ofs += 4;
790 push a uint16 into a centry
792 static void centry_put_uint16(struct cache_entry *centry, uint16 v)
794 centry_expand(centry, 2);
795 SSVAL(centry->data, centry->ofs, v);
796 centry->ofs += 2;
800 push a uint8 into a centry
802 static void centry_put_uint8(struct cache_entry *centry, uint8 v)
804 centry_expand(centry, 1);
805 SCVAL(centry->data, centry->ofs, v);
806 centry->ofs += 1;
810 push a string into a centry
812 static void centry_put_string(struct cache_entry *centry, const char *s)
814 int len;
816 if (!s) {
817 /* null strings are marked as len 0xFFFF */
818 centry_put_uint8(centry, 0xFF);
819 return;
822 len = strlen(s);
823 /* can't handle more than 254 char strings. Truncating is probably best */
824 if (len > 254) {
825 DEBUG(10,("centry_put_string: truncating len (%d) to: 254\n", len));
826 len = 254;
828 centry_put_uint8(centry, len);
829 centry_expand(centry, len);
830 memcpy(centry->data + centry->ofs, s, len);
831 centry->ofs += len;
835 push a 16 byte hash into a centry - treat as 16 byte string.
837 static void centry_put_hash16(struct cache_entry *centry, const uint8 val[16])
839 centry_put_uint8(centry, 16);
840 centry_expand(centry, 16);
841 memcpy(centry->data + centry->ofs, val, 16);
842 centry->ofs += 16;
845 static void centry_put_sid(struct cache_entry *centry, const struct dom_sid *sid)
847 fstring sid_string;
848 centry_put_string(centry, sid_to_fstring(sid_string, sid));
853 put NTSTATUS into a centry
855 static void centry_put_ntstatus(struct cache_entry *centry, NTSTATUS status)
857 uint32 status_value = NT_STATUS_V(status);
858 centry_put_uint32(centry, status_value);
863 push a NTTIME into a centry
865 static void centry_put_nttime(struct cache_entry *centry, NTTIME nt)
867 centry_expand(centry, 8);
868 SIVAL(centry->data, centry->ofs, nt & 0xFFFFFFFF);
869 centry->ofs += 4;
870 SIVAL(centry->data, centry->ofs, nt >> 32);
871 centry->ofs += 4;
875 push a time_t into a centry - use a 64 bit size.
876 NTTIME here is being used as a convenient 64-bit size.
878 static void centry_put_time(struct cache_entry *centry, time_t t)
880 NTTIME nt = (NTTIME)t;
881 centry_put_nttime(centry, nt);
885 start a centry for output. When finished, call centry_end()
887 struct cache_entry *centry_start(struct winbindd_domain *domain, NTSTATUS status)
889 struct cache_entry *centry;
891 if (!wcache->tdb)
892 return NULL;
894 centry = SMB_XMALLOC_P(struct cache_entry);
896 centry->len = 8192; /* reasonable default */
897 centry->data = SMB_XMALLOC_ARRAY(uint8, centry->len);
898 centry->ofs = 0;
899 centry->sequence_number = domain->sequence_number;
900 centry->timeout = lp_winbind_cache_time() + time(NULL);
901 centry_put_ntstatus(centry, status);
902 centry_put_uint32(centry, centry->sequence_number);
903 centry_put_uint64_t(centry, centry->timeout);
904 return centry;
908 finish a centry and write it to the tdb
910 static void centry_end(struct cache_entry *centry, const char *format, ...) PRINTF_ATTRIBUTE(2,3);
911 static void centry_end(struct cache_entry *centry, const char *format, ...)
913 va_list ap;
914 char *kstr;
915 TDB_DATA key, data;
917 if (!winbindd_use_cache()) {
918 return;
921 va_start(ap, format);
922 smb_xvasprintf(&kstr, format, ap);
923 va_end(ap);
925 key = string_tdb_data(kstr);
926 data.dptr = centry->data;
927 data.dsize = centry->ofs;
929 tdb_store(wcache->tdb, key, data, TDB_REPLACE);
930 free(kstr);
933 static void wcache_save_name_to_sid(struct winbindd_domain *domain,
934 NTSTATUS status, const char *domain_name,
935 const char *name, const struct dom_sid *sid,
936 enum lsa_SidType type)
938 struct cache_entry *centry;
939 fstring uname;
941 centry = centry_start(domain, status);
942 if (!centry)
943 return;
944 centry_put_uint32(centry, type);
945 centry_put_sid(centry, sid);
946 fstrcpy(uname, name);
947 strupper_m(uname);
948 centry_end(centry, "NS/%s/%s", domain_name, uname);
949 DEBUG(10,("wcache_save_name_to_sid: %s\\%s -> %s (%s)\n", domain_name,
950 uname, sid_string_dbg(sid), nt_errstr(status)));
951 centry_free(centry);
954 static void wcache_save_sid_to_name(struct winbindd_domain *domain, NTSTATUS status,
955 const struct dom_sid *sid, const char *domain_name, const char *name, enum lsa_SidType type)
957 struct cache_entry *centry;
958 fstring sid_string;
960 centry = centry_start(domain, status);
961 if (!centry)
962 return;
964 if (NT_STATUS_IS_OK(status)) {
965 centry_put_uint32(centry, type);
966 centry_put_string(centry, domain_name);
967 centry_put_string(centry, name);
970 centry_end(centry, "SN/%s", sid_to_fstring(sid_string, sid));
971 DEBUG(10,("wcache_save_sid_to_name: %s -> %s (%s)\n", sid_string,
972 name, nt_errstr(status)));
973 centry_free(centry);
977 static void wcache_save_user(struct winbindd_domain *domain, NTSTATUS status,
978 struct wbint_userinfo *info)
980 struct cache_entry *centry;
981 fstring sid_string;
983 if (is_null_sid(&info->user_sid)) {
984 return;
987 centry = centry_start(domain, status);
988 if (!centry)
989 return;
990 centry_put_string(centry, info->acct_name);
991 centry_put_string(centry, info->full_name);
992 centry_put_string(centry, info->homedir);
993 centry_put_string(centry, info->shell);
994 centry_put_uint32(centry, info->primary_gid);
995 centry_put_sid(centry, &info->user_sid);
996 centry_put_sid(centry, &info->group_sid);
997 centry_end(centry, "U/%s", sid_to_fstring(sid_string,
998 &info->user_sid));
999 DEBUG(10,("wcache_save_user: %s (acct_name %s)\n", sid_string, info->acct_name));
1000 centry_free(centry);
1003 static void wcache_save_lockout_policy(struct winbindd_domain *domain,
1004 NTSTATUS status,
1005 struct samr_DomInfo12 *lockout_policy)
1007 struct cache_entry *centry;
1009 centry = centry_start(domain, status);
1010 if (!centry)
1011 return;
1013 centry_put_nttime(centry, lockout_policy->lockout_duration);
1014 centry_put_nttime(centry, lockout_policy->lockout_window);
1015 centry_put_uint16(centry, lockout_policy->lockout_threshold);
1017 centry_end(centry, "LOC_POL/%s", domain->name);
1019 DEBUG(10,("wcache_save_lockout_policy: %s\n", domain->name));
1021 centry_free(centry);
1026 static void wcache_save_password_policy(struct winbindd_domain *domain,
1027 NTSTATUS status,
1028 struct samr_DomInfo1 *policy)
1030 struct cache_entry *centry;
1032 centry = centry_start(domain, status);
1033 if (!centry)
1034 return;
1036 centry_put_uint16(centry, policy->min_password_length);
1037 centry_put_uint16(centry, policy->password_history_length);
1038 centry_put_uint32(centry, policy->password_properties);
1039 centry_put_nttime(centry, policy->max_password_age);
1040 centry_put_nttime(centry, policy->min_password_age);
1042 centry_end(centry, "PWD_POL/%s", domain->name);
1044 DEBUG(10,("wcache_save_password_policy: %s\n", domain->name));
1046 centry_free(centry);
1049 /***************************************************************************
1050 ***************************************************************************/
1052 static void wcache_save_username_alias(struct winbindd_domain *domain,
1053 NTSTATUS status,
1054 const char *name, const char *alias)
1056 struct cache_entry *centry;
1057 fstring uname;
1059 if ( (centry = centry_start(domain, status)) == NULL )
1060 return;
1062 centry_put_string( centry, alias );
1064 fstrcpy(uname, name);
1065 strupper_m(uname);
1066 centry_end(centry, "NSS/NA/%s", uname);
1068 DEBUG(10,("wcache_save_username_alias: %s -> %s\n", name, alias ));
1070 centry_free(centry);
1073 static void wcache_save_alias_username(struct winbindd_domain *domain,
1074 NTSTATUS status,
1075 const char *alias, const char *name)
1077 struct cache_entry *centry;
1078 fstring uname;
1080 if ( (centry = centry_start(domain, status)) == NULL )
1081 return;
1083 centry_put_string( centry, name );
1085 fstrcpy(uname, alias);
1086 strupper_m(uname);
1087 centry_end(centry, "NSS/AN/%s", uname);
1089 DEBUG(10,("wcache_save_alias_username: %s -> %s\n", alias, name ));
1091 centry_free(centry);
1094 /***************************************************************************
1095 ***************************************************************************/
1097 NTSTATUS resolve_username_to_alias( TALLOC_CTX *mem_ctx,
1098 struct winbindd_domain *domain,
1099 const char *name, char **alias )
1101 struct winbind_cache *cache = get_cache(domain);
1102 struct cache_entry *centry = NULL;
1103 NTSTATUS status;
1104 char *upper_name;
1106 if ( domain->internal )
1107 return NT_STATUS_NOT_SUPPORTED;
1109 if (!cache->tdb)
1110 goto do_query;
1112 if ( (upper_name = SMB_STRDUP(name)) == NULL )
1113 return NT_STATUS_NO_MEMORY;
1114 strupper_m(upper_name);
1116 centry = wcache_fetch(cache, domain, "NSS/NA/%s", upper_name);
1118 SAFE_FREE( upper_name );
1120 if (!centry)
1121 goto do_query;
1123 status = centry->status;
1125 if (!NT_STATUS_IS_OK(status)) {
1126 centry_free(centry);
1127 return status;
1130 *alias = centry_string( centry, mem_ctx );
1132 centry_free(centry);
1134 DEBUG(10,("resolve_username_to_alias: [Cached] - mapped %s to %s\n",
1135 name, *alias ? *alias : "(none)"));
1137 return (*alias) ? NT_STATUS_OK : NT_STATUS_OBJECT_NAME_NOT_FOUND;
1139 do_query:
1141 /* If its not in cache and we are offline, then fail */
1143 if ( get_global_winbindd_state_offline() || !domain->online ) {
1144 DEBUG(8,("resolve_username_to_alias: rejecting query "
1145 "in offline mode\n"));
1146 return NT_STATUS_NOT_FOUND;
1149 status = nss_map_to_alias( mem_ctx, domain->name, name, alias );
1151 if ( NT_STATUS_IS_OK( status ) ) {
1152 wcache_save_username_alias(domain, status, name, *alias);
1155 if ( NT_STATUS_EQUAL( status, NT_STATUS_NONE_MAPPED ) ) {
1156 wcache_save_username_alias(domain, status, name, "(NULL)");
1159 DEBUG(5,("resolve_username_to_alias: backend query returned %s\n",
1160 nt_errstr(status)));
1162 if ( NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ) {
1163 set_domain_offline( domain );
1166 return status;
1169 /***************************************************************************
1170 ***************************************************************************/
1172 NTSTATUS resolve_alias_to_username( TALLOC_CTX *mem_ctx,
1173 struct winbindd_domain *domain,
1174 const char *alias, char **name )
1176 struct winbind_cache *cache = get_cache(domain);
1177 struct cache_entry *centry = NULL;
1178 NTSTATUS status;
1179 char *upper_name;
1181 if ( domain->internal )
1182 return NT_STATUS_NOT_SUPPORTED;
1184 if (!cache->tdb)
1185 goto do_query;
1187 if ( (upper_name = SMB_STRDUP(alias)) == NULL )
1188 return NT_STATUS_NO_MEMORY;
1189 strupper_m(upper_name);
1191 centry = wcache_fetch(cache, domain, "NSS/AN/%s", upper_name);
1193 SAFE_FREE( upper_name );
1195 if (!centry)
1196 goto do_query;
1198 status = centry->status;
1200 if (!NT_STATUS_IS_OK(status)) {
1201 centry_free(centry);
1202 return status;
1205 *name = centry_string( centry, mem_ctx );
1207 centry_free(centry);
1209 DEBUG(10,("resolve_alias_to_username: [Cached] - mapped %s to %s\n",
1210 alias, *name ? *name : "(none)"));
1212 return (*name) ? NT_STATUS_OK : NT_STATUS_OBJECT_NAME_NOT_FOUND;
1214 do_query:
1216 /* If its not in cache and we are offline, then fail */
1218 if ( get_global_winbindd_state_offline() || !domain->online ) {
1219 DEBUG(8,("resolve_alias_to_username: rejecting query "
1220 "in offline mode\n"));
1221 return NT_STATUS_NOT_FOUND;
1224 /* an alias cannot contain a domain prefix or '@' */
1226 if (strchr(alias, '\\') || strchr(alias, '@')) {
1227 DEBUG(10,("resolve_alias_to_username: skipping fully "
1228 "qualified name %s\n", alias));
1229 return NT_STATUS_OBJECT_NAME_INVALID;
1232 status = nss_map_from_alias( mem_ctx, domain->name, alias, name );
1234 if ( NT_STATUS_IS_OK( status ) ) {
1235 wcache_save_alias_username( domain, status, alias, *name );
1238 if (NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) {
1239 wcache_save_alias_username(domain, status, alias, "(NULL)");
1242 DEBUG(5,("resolve_alias_to_username: backend query returned %s\n",
1243 nt_errstr(status)));
1245 if ( NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ) {
1246 set_domain_offline( domain );
1249 return status;
1252 NTSTATUS wcache_cached_creds_exist(struct winbindd_domain *domain, const struct dom_sid *sid)
1254 struct winbind_cache *cache = get_cache(domain);
1255 TDB_DATA data;
1256 fstring key_str, tmp;
1257 uint32 rid;
1259 if (!cache->tdb) {
1260 return NT_STATUS_INTERNAL_DB_ERROR;
1263 if (is_null_sid(sid)) {
1264 return NT_STATUS_INVALID_SID;
1267 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1268 return NT_STATUS_INVALID_SID;
1271 fstr_sprintf(key_str, "CRED/%s", sid_to_fstring(tmp, sid));
1273 data = tdb_fetch(cache->tdb, string_tdb_data(key_str));
1274 if (!data.dptr) {
1275 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1278 SAFE_FREE(data.dptr);
1279 return NT_STATUS_OK;
1282 /* Lookup creds for a SID - copes with old (unsalted) creds as well
1283 as new salted ones. */
1285 NTSTATUS wcache_get_creds(struct winbindd_domain *domain,
1286 TALLOC_CTX *mem_ctx,
1287 const struct dom_sid *sid,
1288 const uint8 **cached_nt_pass,
1289 const uint8 **cached_salt)
1291 struct winbind_cache *cache = get_cache(domain);
1292 struct cache_entry *centry = NULL;
1293 NTSTATUS status;
1294 time_t t;
1295 uint32 rid;
1296 fstring tmp;
1298 if (!cache->tdb) {
1299 return NT_STATUS_INTERNAL_DB_ERROR;
1302 if (is_null_sid(sid)) {
1303 return NT_STATUS_INVALID_SID;
1306 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1307 return NT_STATUS_INVALID_SID;
1310 /* Try and get a salted cred first. If we can't
1311 fall back to an unsalted cred. */
1313 centry = wcache_fetch(cache, domain, "CRED/%s",
1314 sid_to_fstring(tmp, sid));
1315 if (!centry) {
1316 DEBUG(10,("wcache_get_creds: entry for [CRED/%s] not found\n",
1317 sid_string_dbg(sid)));
1318 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1321 t = centry_time(centry);
1323 /* In the salted case this isn't actually the nt_hash itself,
1324 but the MD5 of the salt + nt_hash. Let the caller
1325 sort this out. It can tell as we only return the cached_salt
1326 if we are returning a salted cred. */
1328 *cached_nt_pass = (const uint8 *)centry_hash16(centry, mem_ctx);
1329 if (*cached_nt_pass == NULL) {
1330 fstring sidstr;
1332 sid_to_fstring(sidstr, sid);
1334 /* Bad (old) cred cache. Delete and pretend we
1335 don't have it. */
1336 DEBUG(0,("wcache_get_creds: bad entry for [CRED/%s] - deleting\n",
1337 sidstr));
1338 wcache_delete("CRED/%s", sidstr);
1339 centry_free(centry);
1340 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1343 /* We only have 17 bytes more data in the salted cred case. */
1344 if (centry->len - centry->ofs == 17) {
1345 *cached_salt = (const uint8 *)centry_hash16(centry, mem_ctx);
1346 } else {
1347 *cached_salt = NULL;
1350 dump_data_pw("cached_nt_pass", *cached_nt_pass, NT_HASH_LEN);
1351 if (*cached_salt) {
1352 dump_data_pw("cached_salt", *cached_salt, NT_HASH_LEN);
1355 status = centry->status;
1357 DEBUG(10,("wcache_get_creds: [Cached] - cached creds for user %s status: %s\n",
1358 sid_string_dbg(sid), nt_errstr(status) ));
1360 centry_free(centry);
1361 return status;
1364 /* Store creds for a SID - only writes out new salted ones. */
1366 NTSTATUS wcache_save_creds(struct winbindd_domain *domain,
1367 const struct dom_sid *sid,
1368 const uint8 nt_pass[NT_HASH_LEN])
1370 struct cache_entry *centry;
1371 fstring sid_string;
1372 uint32 rid;
1373 uint8 cred_salt[NT_HASH_LEN];
1374 uint8 salted_hash[NT_HASH_LEN];
1376 if (is_null_sid(sid)) {
1377 return NT_STATUS_INVALID_SID;
1380 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1381 return NT_STATUS_INVALID_SID;
1384 centry = centry_start(domain, NT_STATUS_OK);
1385 if (!centry) {
1386 return NT_STATUS_INTERNAL_DB_ERROR;
1389 dump_data_pw("nt_pass", nt_pass, NT_HASH_LEN);
1391 centry_put_time(centry, time(NULL));
1393 /* Create a salt and then salt the hash. */
1394 generate_random_buffer(cred_salt, NT_HASH_LEN);
1395 E_md5hash(cred_salt, nt_pass, salted_hash);
1397 centry_put_hash16(centry, salted_hash);
1398 centry_put_hash16(centry, cred_salt);
1399 centry_end(centry, "CRED/%s", sid_to_fstring(sid_string, sid));
1401 DEBUG(10,("wcache_save_creds: %s\n", sid_string));
1403 centry_free(centry);
1405 return NT_STATUS_OK;
1409 /* Query display info. This is the basic user list fn */
1410 static NTSTATUS query_user_list(struct winbindd_domain *domain,
1411 TALLOC_CTX *mem_ctx,
1412 uint32 *num_entries,
1413 struct wbint_userinfo **info)
1415 struct winbind_cache *cache = get_cache(domain);
1416 struct cache_entry *centry = NULL;
1417 NTSTATUS status;
1418 unsigned int i, retry;
1419 bool old_status = domain->online;
1421 if (!cache->tdb)
1422 goto do_query;
1424 centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
1425 if (!centry)
1426 goto do_query;
1428 do_fetch_cache:
1429 *num_entries = centry_uint32(centry);
1431 if (*num_entries == 0)
1432 goto do_cached;
1434 (*info) = TALLOC_ARRAY(mem_ctx, struct wbint_userinfo, *num_entries);
1435 if (! (*info)) {
1436 smb_panic_fn("query_user_list out of memory");
1438 for (i=0; i<(*num_entries); i++) {
1439 (*info)[i].acct_name = centry_string(centry, mem_ctx);
1440 (*info)[i].full_name = centry_string(centry, mem_ctx);
1441 (*info)[i].homedir = centry_string(centry, mem_ctx);
1442 (*info)[i].shell = centry_string(centry, mem_ctx);
1443 centry_sid(centry, &(*info)[i].user_sid);
1444 centry_sid(centry, &(*info)[i].group_sid);
1447 do_cached:
1448 status = centry->status;
1450 DEBUG(10,("query_user_list: [Cached] - cached list for domain %s status: %s\n",
1451 domain->name, nt_errstr(status) ));
1453 centry_free(centry);
1454 return status;
1456 do_query:
1457 *num_entries = 0;
1458 *info = NULL;
1460 /* Return status value returned by seq number check */
1462 if (!NT_STATUS_IS_OK(domain->last_status))
1463 return domain->last_status;
1465 /* Put the query_user_list() in a retry loop. There appears to be
1466 * some bug either with Windows 2000 or Samba's handling of large
1467 * rpc replies. This manifests itself as sudden disconnection
1468 * at a random point in the enumeration of a large (60k) user list.
1469 * The retry loop simply tries the operation again. )-: It's not
1470 * pretty but an acceptable workaround until we work out what the
1471 * real problem is. */
1473 retry = 0;
1474 do {
1476 DEBUG(10,("query_user_list: [Cached] - doing backend query for list for domain %s\n",
1477 domain->name ));
1479 status = domain->backend->query_user_list(domain, mem_ctx, num_entries, info);
1480 if (!NT_STATUS_IS_OK(status)) {
1481 DEBUG(3, ("query_user_list: returned 0x%08x, "
1482 "retrying\n", NT_STATUS_V(status)));
1484 if (NT_STATUS_EQUAL(status, NT_STATUS_UNSUCCESSFUL)) {
1485 DEBUG(3, ("query_user_list: flushing "
1486 "connection cache\n"));
1487 invalidate_cm_connection(&domain->conn);
1489 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1490 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1491 if (!domain->internal && old_status) {
1492 set_domain_offline(domain);
1494 /* store partial response. */
1495 if (*num_entries > 0) {
1497 * humm, what about the status used for cache?
1498 * Should it be NT_STATUS_OK?
1500 break;
1503 * domain is offline now, and there is no user entries,
1504 * try to fetch from cache again.
1506 if (cache->tdb && !domain->online && !domain->internal && old_status) {
1507 centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
1508 /* partial response... */
1509 if (!centry) {
1510 goto skip_save;
1511 } else {
1512 goto do_fetch_cache;
1514 } else {
1515 goto skip_save;
1519 } while (NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_UNSUCCESSFUL) &&
1520 (retry++ < 5));
1522 /* and save it */
1523 refresh_sequence_number(domain, false);
1524 if (!NT_STATUS_IS_OK(status)) {
1525 return status;
1527 centry = centry_start(domain, status);
1528 if (!centry)
1529 goto skip_save;
1530 centry_put_uint32(centry, *num_entries);
1531 for (i=0; i<(*num_entries); i++) {
1532 centry_put_string(centry, (*info)[i].acct_name);
1533 centry_put_string(centry, (*info)[i].full_name);
1534 centry_put_string(centry, (*info)[i].homedir);
1535 centry_put_string(centry, (*info)[i].shell);
1536 centry_put_sid(centry, &(*info)[i].user_sid);
1537 centry_put_sid(centry, &(*info)[i].group_sid);
1538 if (domain->backend && domain->backend->consistent) {
1539 /* when the backend is consistent we can pre-prime some mappings */
1540 wcache_save_name_to_sid(domain, NT_STATUS_OK,
1541 domain->name,
1542 (*info)[i].acct_name,
1543 &(*info)[i].user_sid,
1544 SID_NAME_USER);
1545 wcache_save_sid_to_name(domain, NT_STATUS_OK,
1546 &(*info)[i].user_sid,
1547 domain->name,
1548 (*info)[i].acct_name,
1549 SID_NAME_USER);
1550 wcache_save_user(domain, NT_STATUS_OK, &(*info)[i]);
1553 centry_end(centry, "UL/%s", domain->name);
1554 centry_free(centry);
1556 skip_save:
1557 return status;
1560 /* list all domain groups */
1561 static NTSTATUS enum_dom_groups(struct winbindd_domain *domain,
1562 TALLOC_CTX *mem_ctx,
1563 uint32 *num_entries,
1564 struct wb_acct_info **info)
1566 struct winbind_cache *cache = get_cache(domain);
1567 struct cache_entry *centry = NULL;
1568 NTSTATUS status;
1569 unsigned int i;
1570 bool old_status;
1572 old_status = domain->online;
1573 if (!cache->tdb)
1574 goto do_query;
1576 centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
1577 if (!centry)
1578 goto do_query;
1580 do_fetch_cache:
1581 *num_entries = centry_uint32(centry);
1583 if (*num_entries == 0)
1584 goto do_cached;
1586 (*info) = TALLOC_ARRAY(mem_ctx, struct wb_acct_info, *num_entries);
1587 if (! (*info)) {
1588 smb_panic_fn("enum_dom_groups out of memory");
1590 for (i=0; i<(*num_entries); i++) {
1591 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1592 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1593 (*info)[i].rid = centry_uint32(centry);
1596 do_cached:
1597 status = centry->status;
1599 DEBUG(10,("enum_dom_groups: [Cached] - cached list for domain %s status: %s\n",
1600 domain->name, nt_errstr(status) ));
1602 centry_free(centry);
1603 return status;
1605 do_query:
1606 *num_entries = 0;
1607 *info = NULL;
1609 /* Return status value returned by seq number check */
1611 if (!NT_STATUS_IS_OK(domain->last_status))
1612 return domain->last_status;
1614 DEBUG(10,("enum_dom_groups: [Cached] - doing backend query for list for domain %s\n",
1615 domain->name ));
1617 status = domain->backend->enum_dom_groups(domain, mem_ctx, num_entries, info);
1619 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1620 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1621 if (!domain->internal && old_status) {
1622 set_domain_offline(domain);
1624 if (cache->tdb &&
1625 !domain->online &&
1626 !domain->internal &&
1627 old_status) {
1628 centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
1629 if (centry) {
1630 goto do_fetch_cache;
1634 /* and save it */
1635 refresh_sequence_number(domain, false);
1636 if (!NT_STATUS_IS_OK(status)) {
1637 return status;
1639 centry = centry_start(domain, status);
1640 if (!centry)
1641 goto skip_save;
1642 centry_put_uint32(centry, *num_entries);
1643 for (i=0; i<(*num_entries); i++) {
1644 centry_put_string(centry, (*info)[i].acct_name);
1645 centry_put_string(centry, (*info)[i].acct_desc);
1646 centry_put_uint32(centry, (*info)[i].rid);
1648 centry_end(centry, "GL/%s/domain", domain->name);
1649 centry_free(centry);
1651 skip_save:
1652 return status;
1655 /* list all domain groups */
1656 static NTSTATUS enum_local_groups(struct winbindd_domain *domain,
1657 TALLOC_CTX *mem_ctx,
1658 uint32 *num_entries,
1659 struct wb_acct_info **info)
1661 struct winbind_cache *cache = get_cache(domain);
1662 struct cache_entry *centry = NULL;
1663 NTSTATUS status;
1664 unsigned int i;
1665 bool old_status;
1667 old_status = domain->online;
1668 if (!cache->tdb)
1669 goto do_query;
1671 centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
1672 if (!centry)
1673 goto do_query;
1675 do_fetch_cache:
1676 *num_entries = centry_uint32(centry);
1678 if (*num_entries == 0)
1679 goto do_cached;
1681 (*info) = TALLOC_ARRAY(mem_ctx, struct wb_acct_info, *num_entries);
1682 if (! (*info)) {
1683 smb_panic_fn("enum_dom_groups out of memory");
1685 for (i=0; i<(*num_entries); i++) {
1686 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1687 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1688 (*info)[i].rid = centry_uint32(centry);
1691 do_cached:
1693 /* If we are returning cached data and the domain controller
1694 is down then we don't know whether the data is up to date
1695 or not. Return NT_STATUS_MORE_PROCESSING_REQUIRED to
1696 indicate this. */
1698 if (wcache_server_down(domain)) {
1699 DEBUG(10, ("enum_local_groups: returning cached user list and server was down\n"));
1700 status = NT_STATUS_MORE_PROCESSING_REQUIRED;
1701 } else
1702 status = centry->status;
1704 DEBUG(10,("enum_local_groups: [Cached] - cached list for domain %s status: %s\n",
1705 domain->name, nt_errstr(status) ));
1707 centry_free(centry);
1708 return status;
1710 do_query:
1711 *num_entries = 0;
1712 *info = NULL;
1714 /* Return status value returned by seq number check */
1716 if (!NT_STATUS_IS_OK(domain->last_status))
1717 return domain->last_status;
1719 DEBUG(10,("enum_local_groups: [Cached] - doing backend query for list for domain %s\n",
1720 domain->name ));
1722 status = domain->backend->enum_local_groups(domain, mem_ctx, num_entries, info);
1724 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1725 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1726 if (!domain->internal && old_status) {
1727 set_domain_offline(domain);
1729 if (cache->tdb &&
1730 !domain->internal &&
1731 !domain->online &&
1732 old_status) {
1733 centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
1734 if (centry) {
1735 goto do_fetch_cache;
1739 /* and save it */
1740 refresh_sequence_number(domain, false);
1741 if (!NT_STATUS_IS_OK(status)) {
1742 return status;
1744 centry = centry_start(domain, status);
1745 if (!centry)
1746 goto skip_save;
1747 centry_put_uint32(centry, *num_entries);
1748 for (i=0; i<(*num_entries); i++) {
1749 centry_put_string(centry, (*info)[i].acct_name);
1750 centry_put_string(centry, (*info)[i].acct_desc);
1751 centry_put_uint32(centry, (*info)[i].rid);
1753 centry_end(centry, "GL/%s/local", domain->name);
1754 centry_free(centry);
1756 skip_save:
1757 return status;
1760 NTSTATUS wcache_name_to_sid(struct winbindd_domain *domain,
1761 const char *domain_name,
1762 const char *name,
1763 struct dom_sid *sid,
1764 enum lsa_SidType *type)
1766 struct winbind_cache *cache = get_cache(domain);
1767 struct cache_entry *centry;
1768 NTSTATUS status;
1769 char *uname;
1771 if (cache->tdb == NULL) {
1772 return NT_STATUS_NOT_FOUND;
1775 uname = talloc_strdup_upper(talloc_tos(), name);
1776 if (uname == NULL) {
1777 return NT_STATUS_NO_MEMORY;
1780 centry = wcache_fetch(cache, domain, "NS/%s/%s", domain_name, uname);
1781 TALLOC_FREE(uname);
1782 if (centry == NULL) {
1783 return NT_STATUS_NOT_FOUND;
1786 status = centry->status;
1787 if (NT_STATUS_IS_OK(status)) {
1788 *type = (enum lsa_SidType)centry_uint32(centry);
1789 centry_sid(centry, sid);
1792 DEBUG(10,("name_to_sid: [Cached] - cached name for domain %s status: "
1793 "%s\n", domain->name, nt_errstr(status) ));
1795 centry_free(centry);
1796 return status;
1799 /* convert a single name to a sid in a domain */
1800 static NTSTATUS name_to_sid(struct winbindd_domain *domain,
1801 TALLOC_CTX *mem_ctx,
1802 const char *domain_name,
1803 const char *name,
1804 uint32_t flags,
1805 struct dom_sid *sid,
1806 enum lsa_SidType *type)
1808 NTSTATUS status;
1809 bool old_status;
1811 old_status = domain->online;
1813 status = wcache_name_to_sid(domain, domain_name, name, sid, type);
1814 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
1815 return status;
1818 ZERO_STRUCTP(sid);
1820 /* If the seq number check indicated that there is a problem
1821 * with this DC, then return that status... except for
1822 * access_denied. This is special because the dc may be in
1823 * "restrict anonymous = 1" mode, in which case it will deny
1824 * most unauthenticated operations, but *will* allow the LSA
1825 * name-to-sid that we try as a fallback. */
1827 if (!(NT_STATUS_IS_OK(domain->last_status)
1828 || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1829 return domain->last_status;
1831 DEBUG(10,("name_to_sid: [Cached] - doing backend query for name for domain %s\n",
1832 domain->name ));
1834 status = domain->backend->name_to_sid(domain, mem_ctx, domain_name,
1835 name, flags, sid, type);
1837 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1838 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1839 if (!domain->internal && old_status) {
1840 set_domain_offline(domain);
1842 if (!domain->internal &&
1843 !domain->online &&
1844 old_status) {
1845 NTSTATUS cache_status;
1846 cache_status = wcache_name_to_sid(domain, domain_name, name, sid, type);
1847 return cache_status;
1850 /* and save it */
1851 refresh_sequence_number(domain, false);
1853 if (domain->online &&
1854 (NT_STATUS_IS_OK(status) || NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED))) {
1855 wcache_save_name_to_sid(domain, status, domain_name, name, sid, *type);
1857 /* Only save the reverse mapping if this was not a UPN */
1858 if (!strchr(name, '@')) {
1859 strupper_m(CONST_DISCARD(char *,domain_name));
1860 strlower_m(CONST_DISCARD(char *,name));
1861 wcache_save_sid_to_name(domain, status, sid, domain_name, name, *type);
1865 return status;
1868 NTSTATUS wcache_sid_to_name(struct winbindd_domain *domain,
1869 const struct dom_sid *sid,
1870 TALLOC_CTX *mem_ctx,
1871 char **domain_name,
1872 char **name,
1873 enum lsa_SidType *type)
1875 struct winbind_cache *cache = get_cache(domain);
1876 struct cache_entry *centry;
1877 char *sid_string;
1878 NTSTATUS status;
1880 if (cache->tdb == NULL) {
1881 return NT_STATUS_NOT_FOUND;
1884 sid_string = sid_string_tos(sid);
1885 if (sid_string == NULL) {
1886 return NT_STATUS_NO_MEMORY;
1889 centry = wcache_fetch(cache, domain, "SN/%s", sid_string);
1890 TALLOC_FREE(sid_string);
1891 if (centry == NULL) {
1892 return NT_STATUS_NOT_FOUND;
1895 if (NT_STATUS_IS_OK(centry->status)) {
1896 *type = (enum lsa_SidType)centry_uint32(centry);
1897 *domain_name = centry_string(centry, mem_ctx);
1898 *name = centry_string(centry, mem_ctx);
1901 status = centry->status;
1902 centry_free(centry);
1904 DEBUG(10,("sid_to_name: [Cached] - cached name for domain %s status: "
1905 "%s\n", domain->name, nt_errstr(status) ));
1907 return status;
1910 /* convert a sid to a user or group name. The sid is guaranteed to be in the domain
1911 given */
1912 static NTSTATUS sid_to_name(struct winbindd_domain *domain,
1913 TALLOC_CTX *mem_ctx,
1914 const struct dom_sid *sid,
1915 char **domain_name,
1916 char **name,
1917 enum lsa_SidType *type)
1919 NTSTATUS status;
1920 bool old_status;
1922 old_status = domain->online;
1923 status = wcache_sid_to_name(domain, sid, mem_ctx, domain_name, name,
1924 type);
1925 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
1926 return status;
1929 *name = NULL;
1930 *domain_name = NULL;
1932 /* If the seq number check indicated that there is a problem
1933 * with this DC, then return that status... except for
1934 * access_denied. This is special because the dc may be in
1935 * "restrict anonymous = 1" mode, in which case it will deny
1936 * most unauthenticated operations, but *will* allow the LSA
1937 * sid-to-name that we try as a fallback. */
1939 if (!(NT_STATUS_IS_OK(domain->last_status)
1940 || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1941 return domain->last_status;
1943 DEBUG(10,("sid_to_name: [Cached] - doing backend query for name for domain %s\n",
1944 domain->name ));
1946 status = domain->backend->sid_to_name(domain, mem_ctx, sid, domain_name, name, type);
1948 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1949 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1950 if (!domain->internal && old_status) {
1951 set_domain_offline(domain);
1953 if (!domain->internal &&
1954 !domain->online &&
1955 old_status) {
1956 NTSTATUS cache_status;
1957 cache_status = wcache_sid_to_name(domain, sid, mem_ctx,
1958 domain_name, name, type);
1959 return cache_status;
1962 /* and save it */
1963 refresh_sequence_number(domain, false);
1964 if (!NT_STATUS_IS_OK(status)) {
1965 return status;
1967 wcache_save_sid_to_name(domain, status, sid, *domain_name, *name, *type);
1969 /* We can't save the name to sid mapping here, as with sid history a
1970 * later name2sid would give the wrong sid. */
1972 return status;
1975 static NTSTATUS rids_to_names(struct winbindd_domain *domain,
1976 TALLOC_CTX *mem_ctx,
1977 const struct dom_sid *domain_sid,
1978 uint32 *rids,
1979 size_t num_rids,
1980 char **domain_name,
1981 char ***names,
1982 enum lsa_SidType **types)
1984 struct winbind_cache *cache = get_cache(domain);
1985 size_t i;
1986 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
1987 bool have_mapped;
1988 bool have_unmapped;
1989 bool old_status;
1991 old_status = domain->online;
1992 *domain_name = NULL;
1993 *names = NULL;
1994 *types = NULL;
1996 if (!cache->tdb) {
1997 goto do_query;
2000 if (num_rids == 0) {
2001 return NT_STATUS_OK;
2004 *names = TALLOC_ARRAY(mem_ctx, char *, num_rids);
2005 *types = TALLOC_ARRAY(mem_ctx, enum lsa_SidType, num_rids);
2007 if ((*names == NULL) || (*types == NULL)) {
2008 result = NT_STATUS_NO_MEMORY;
2009 goto error;
2012 have_mapped = have_unmapped = false;
2014 for (i=0; i<num_rids; i++) {
2015 struct dom_sid sid;
2016 struct cache_entry *centry;
2017 fstring tmp;
2019 if (!sid_compose(&sid, domain_sid, rids[i])) {
2020 result = NT_STATUS_INTERNAL_ERROR;
2021 goto error;
2024 centry = wcache_fetch(cache, domain, "SN/%s",
2025 sid_to_fstring(tmp, &sid));
2026 if (!centry) {
2027 goto do_query;
2030 (*types)[i] = SID_NAME_UNKNOWN;
2031 (*names)[i] = talloc_strdup(*names, "");
2033 if (NT_STATUS_IS_OK(centry->status)) {
2034 char *dom;
2035 have_mapped = true;
2036 (*types)[i] = (enum lsa_SidType)centry_uint32(centry);
2038 dom = centry_string(centry, mem_ctx);
2039 if (*domain_name == NULL) {
2040 *domain_name = dom;
2041 } else {
2042 talloc_free(dom);
2045 (*names)[i] = centry_string(centry, *names);
2047 } else if (NT_STATUS_EQUAL(centry->status, NT_STATUS_NONE_MAPPED)
2048 || NT_STATUS_EQUAL(centry->status, STATUS_SOME_UNMAPPED)) {
2049 have_unmapped = true;
2051 } else {
2052 /* something's definitely wrong */
2053 result = centry->status;
2054 goto error;
2057 centry_free(centry);
2060 if (!have_mapped) {
2061 return NT_STATUS_NONE_MAPPED;
2063 if (!have_unmapped) {
2064 return NT_STATUS_OK;
2066 return STATUS_SOME_UNMAPPED;
2068 do_query:
2070 TALLOC_FREE(*names);
2071 TALLOC_FREE(*types);
2073 result = domain->backend->rids_to_names(domain, mem_ctx, domain_sid,
2074 rids, num_rids, domain_name,
2075 names, types);
2077 if (NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
2078 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2079 if (!domain->internal && old_status) {
2080 set_domain_offline(domain);
2082 if (cache->tdb &&
2083 !domain->internal &&
2084 !domain->online &&
2085 old_status) {
2086 have_mapped = have_unmapped = false;
2088 for (i=0; i<num_rids; i++) {
2089 struct dom_sid sid;
2090 struct cache_entry *centry;
2091 fstring tmp;
2093 if (!sid_compose(&sid, domain_sid, rids[i])) {
2094 result = NT_STATUS_INTERNAL_ERROR;
2095 goto error;
2098 centry = wcache_fetch(cache, domain, "SN/%s",
2099 sid_to_fstring(tmp, &sid));
2100 if (!centry) {
2101 (*types)[i] = SID_NAME_UNKNOWN;
2102 (*names)[i] = talloc_strdup(*names, "");
2103 continue;
2106 (*types)[i] = SID_NAME_UNKNOWN;
2107 (*names)[i] = talloc_strdup(*names, "");
2109 if (NT_STATUS_IS_OK(centry->status)) {
2110 char *dom;
2111 have_mapped = true;
2112 (*types)[i] = (enum lsa_SidType)centry_uint32(centry);
2114 dom = centry_string(centry, mem_ctx);
2115 if (*domain_name == NULL) {
2116 *domain_name = dom;
2117 } else {
2118 talloc_free(dom);
2121 (*names)[i] = centry_string(centry, *names);
2123 } else if (NT_STATUS_EQUAL(centry->status, NT_STATUS_NONE_MAPPED)) {
2124 have_unmapped = true;
2126 } else {
2127 /* something's definitely wrong */
2128 result = centry->status;
2129 goto error;
2132 centry_free(centry);
2135 if (!have_mapped) {
2136 return NT_STATUS_NONE_MAPPED;
2138 if (!have_unmapped) {
2139 return NT_STATUS_OK;
2141 return STATUS_SOME_UNMAPPED;
2145 None of the queried rids has been found so save all negative entries
2147 if (NT_STATUS_EQUAL(result, NT_STATUS_NONE_MAPPED)) {
2148 for (i = 0; i < num_rids; i++) {
2149 struct dom_sid sid;
2150 const char *name = "";
2151 const enum lsa_SidType type = SID_NAME_UNKNOWN;
2152 NTSTATUS status = NT_STATUS_NONE_MAPPED;
2154 if (!sid_compose(&sid, domain_sid, rids[i])) {
2155 return NT_STATUS_INTERNAL_ERROR;
2158 wcache_save_sid_to_name(domain, status, &sid, *domain_name,
2159 name, type);
2162 return result;
2166 Some or all of the queried rids have been found.
2168 if (!NT_STATUS_IS_OK(result) &&
2169 !NT_STATUS_EQUAL(result, STATUS_SOME_UNMAPPED)) {
2170 return result;
2173 refresh_sequence_number(domain, false);
2175 for (i=0; i<num_rids; i++) {
2176 struct dom_sid sid;
2177 NTSTATUS status;
2179 if (!sid_compose(&sid, domain_sid, rids[i])) {
2180 result = NT_STATUS_INTERNAL_ERROR;
2181 goto error;
2184 status = (*types)[i] == SID_NAME_UNKNOWN ?
2185 NT_STATUS_NONE_MAPPED : NT_STATUS_OK;
2187 wcache_save_sid_to_name(domain, status, &sid, *domain_name,
2188 (*names)[i], (*types)[i]);
2191 return result;
2193 error:
2194 TALLOC_FREE(*names);
2195 TALLOC_FREE(*types);
2196 return result;
2199 NTSTATUS wcache_query_user(struct winbindd_domain *domain,
2200 TALLOC_CTX *mem_ctx,
2201 const struct dom_sid *user_sid,
2202 struct wbint_userinfo *info)
2204 struct winbind_cache *cache = get_cache(domain);
2205 struct cache_entry *centry = NULL;
2206 NTSTATUS status;
2207 char *sid_string;
2209 if (cache->tdb == NULL) {
2210 return NT_STATUS_NOT_FOUND;
2213 sid_string = sid_string_tos(user_sid);
2214 if (sid_string == NULL) {
2215 return NT_STATUS_NO_MEMORY;
2218 centry = wcache_fetch(cache, domain, "U/%s", sid_string);
2219 TALLOC_FREE(sid_string);
2220 if (centry == NULL) {
2221 return NT_STATUS_NOT_FOUND;
2225 * If we have an access denied cache entry and a cached info3
2226 * in the samlogon cache then do a query. This will force the
2227 * rpc back end to return the info3 data.
2230 if (NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED) &&
2231 netsamlogon_cache_have(user_sid)) {
2232 DEBUG(10, ("query_user: cached access denied and have cached "
2233 "info3\n"));
2234 domain->last_status = NT_STATUS_OK;
2235 centry_free(centry);
2236 return NT_STATUS_NOT_FOUND;
2239 /* if status is not ok then this is a negative hit
2240 and the rest of the data doesn't matter */
2241 status = centry->status;
2242 if (NT_STATUS_IS_OK(status)) {
2243 info->acct_name = centry_string(centry, mem_ctx);
2244 info->full_name = centry_string(centry, mem_ctx);
2245 info->homedir = centry_string(centry, mem_ctx);
2246 info->shell = centry_string(centry, mem_ctx);
2247 info->primary_gid = centry_uint32(centry);
2248 centry_sid(centry, &info->user_sid);
2249 centry_sid(centry, &info->group_sid);
2252 DEBUG(10,("query_user: [Cached] - cached info for domain %s status: "
2253 "%s\n", domain->name, nt_errstr(status) ));
2255 centry_free(centry);
2256 return status;
2259 /* Lookup user information from a rid */
2260 static NTSTATUS query_user(struct winbindd_domain *domain,
2261 TALLOC_CTX *mem_ctx,
2262 const struct dom_sid *user_sid,
2263 struct wbint_userinfo *info)
2265 NTSTATUS status;
2266 bool old_status;
2268 old_status = domain->online;
2269 status = wcache_query_user(domain, mem_ctx, user_sid, info);
2270 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2271 return status;
2274 ZERO_STRUCTP(info);
2276 /* Return status value returned by seq number check */
2278 if (!NT_STATUS_IS_OK(domain->last_status))
2279 return domain->last_status;
2281 DEBUG(10,("query_user: [Cached] - doing backend query for info for domain %s\n",
2282 domain->name ));
2284 status = domain->backend->query_user(domain, mem_ctx, user_sid, info);
2286 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2287 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2288 if (!domain->internal && old_status) {
2289 set_domain_offline(domain);
2291 if (!domain->internal &&
2292 !domain->online &&
2293 old_status) {
2294 NTSTATUS cache_status;
2295 cache_status = wcache_query_user(domain, mem_ctx, user_sid, info);
2296 return cache_status;
2299 /* and save it */
2300 refresh_sequence_number(domain, false);
2301 if (!NT_STATUS_IS_OK(status)) {
2302 return status;
2304 wcache_save_user(domain, status, info);
2306 return status;
2309 NTSTATUS wcache_lookup_usergroups(struct winbindd_domain *domain,
2310 TALLOC_CTX *mem_ctx,
2311 const struct dom_sid *user_sid,
2312 uint32_t *pnum_sids,
2313 struct dom_sid **psids)
2315 struct winbind_cache *cache = get_cache(domain);
2316 struct cache_entry *centry = NULL;
2317 NTSTATUS status;
2318 uint32_t i, num_sids;
2319 struct dom_sid *sids;
2320 fstring sid_string;
2322 if (cache->tdb == NULL) {
2323 return NT_STATUS_NOT_FOUND;
2326 centry = wcache_fetch(cache, domain, "UG/%s",
2327 sid_to_fstring(sid_string, user_sid));
2328 if (centry == NULL) {
2329 return NT_STATUS_NOT_FOUND;
2332 /* If we have an access denied cache entry and a cached info3 in the
2333 samlogon cache then do a query. This will force the rpc back end
2334 to return the info3 data. */
2336 if (NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)
2337 && netsamlogon_cache_have(user_sid)) {
2338 DEBUG(10, ("lookup_usergroups: cached access denied and have "
2339 "cached info3\n"));
2340 domain->last_status = NT_STATUS_OK;
2341 centry_free(centry);
2342 return NT_STATUS_NOT_FOUND;
2345 num_sids = centry_uint32(centry);
2346 sids = talloc_array(mem_ctx, struct dom_sid, num_sids);
2347 if (sids == NULL) {
2348 centry_free(centry);
2349 return NT_STATUS_NO_MEMORY;
2352 for (i=0; i<num_sids; i++) {
2353 centry_sid(centry, &sids[i]);
2356 status = centry->status;
2358 DEBUG(10,("lookup_usergroups: [Cached] - cached info for domain %s "
2359 "status: %s\n", domain->name, nt_errstr(status)));
2361 centry_free(centry);
2363 *pnum_sids = num_sids;
2364 *psids = sids;
2365 return status;
2368 /* Lookup groups a user is a member of. */
2369 static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
2370 TALLOC_CTX *mem_ctx,
2371 const struct dom_sid *user_sid,
2372 uint32 *num_groups, struct dom_sid **user_gids)
2374 struct cache_entry *centry = NULL;
2375 NTSTATUS status;
2376 unsigned int i;
2377 fstring sid_string;
2378 bool old_status;
2380 old_status = domain->online;
2381 status = wcache_lookup_usergroups(domain, mem_ctx, user_sid,
2382 num_groups, user_gids);
2383 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2384 return status;
2387 (*num_groups) = 0;
2388 (*user_gids) = NULL;
2390 /* Return status value returned by seq number check */
2392 if (!NT_STATUS_IS_OK(domain->last_status))
2393 return domain->last_status;
2395 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info for domain %s\n",
2396 domain->name ));
2398 status = domain->backend->lookup_usergroups(domain, mem_ctx, user_sid, num_groups, user_gids);
2400 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2401 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2402 if (!domain->internal && old_status) {
2403 set_domain_offline(domain);
2405 if (!domain->internal &&
2406 !domain->online &&
2407 old_status) {
2408 NTSTATUS cache_status;
2409 cache_status = wcache_lookup_usergroups(domain, mem_ctx, user_sid,
2410 num_groups, user_gids);
2411 return cache_status;
2414 if ( NT_STATUS_EQUAL(status, NT_STATUS_SYNCHRONIZATION_REQUIRED) )
2415 goto skip_save;
2417 /* and save it */
2418 refresh_sequence_number(domain, false);
2419 if (!NT_STATUS_IS_OK(status)) {
2420 return status;
2422 centry = centry_start(domain, status);
2423 if (!centry)
2424 goto skip_save;
2426 centry_put_uint32(centry, *num_groups);
2427 for (i=0; i<(*num_groups); i++) {
2428 centry_put_sid(centry, &(*user_gids)[i]);
2431 centry_end(centry, "UG/%s", sid_to_fstring(sid_string, user_sid));
2432 centry_free(centry);
2434 skip_save:
2435 return status;
2438 static char *wcache_make_sidlist(TALLOC_CTX *mem_ctx, uint32_t num_sids,
2439 const struct dom_sid *sids)
2441 uint32_t i;
2442 char *sidlist;
2444 sidlist = talloc_strdup(mem_ctx, "");
2445 if (sidlist == NULL) {
2446 return NULL;
2448 for (i=0; i<num_sids; i++) {
2449 fstring tmp;
2450 sidlist = talloc_asprintf_append_buffer(
2451 sidlist, "/%s", sid_to_fstring(tmp, &sids[i]));
2452 if (sidlist == NULL) {
2453 return NULL;
2456 return sidlist;
2459 NTSTATUS wcache_lookup_useraliases(struct winbindd_domain *domain,
2460 TALLOC_CTX *mem_ctx, uint32_t num_sids,
2461 const struct dom_sid *sids,
2462 uint32_t *pnum_aliases, uint32_t **paliases)
2464 struct winbind_cache *cache = get_cache(domain);
2465 struct cache_entry *centry = NULL;
2466 uint32_t num_aliases;
2467 uint32_t *aliases;
2468 NTSTATUS status;
2469 char *sidlist;
2470 int i;
2472 if (cache->tdb == NULL) {
2473 return NT_STATUS_NOT_FOUND;
2476 if (num_sids == 0) {
2477 *pnum_aliases = 0;
2478 *paliases = NULL;
2479 return NT_STATUS_OK;
2482 /* We need to cache indexed by the whole list of SIDs, the aliases
2483 * resulting might come from any of the SIDs. */
2485 sidlist = wcache_make_sidlist(talloc_tos(), num_sids, sids);
2486 if (sidlist == NULL) {
2487 return NT_STATUS_NO_MEMORY;
2490 centry = wcache_fetch(cache, domain, "UA%s", sidlist);
2491 TALLOC_FREE(sidlist);
2492 if (centry == NULL) {
2493 return NT_STATUS_NOT_FOUND;
2496 num_aliases = centry_uint32(centry);
2497 aliases = talloc_array(mem_ctx, uint32_t, num_aliases);
2498 if (aliases == NULL) {
2499 centry_free(centry);
2500 return NT_STATUS_NO_MEMORY;
2503 for (i=0; i<num_aliases; i++) {
2504 aliases[i] = centry_uint32(centry);
2507 status = centry->status;
2509 DEBUG(10,("lookup_useraliases: [Cached] - cached info for domain: %s "
2510 "status %s\n", domain->name, nt_errstr(status)));
2512 centry_free(centry);
2514 *pnum_aliases = num_aliases;
2515 *paliases = aliases;
2517 return status;
2520 static NTSTATUS lookup_useraliases(struct winbindd_domain *domain,
2521 TALLOC_CTX *mem_ctx,
2522 uint32 num_sids, const struct dom_sid *sids,
2523 uint32 *num_aliases, uint32 **alias_rids)
2525 struct cache_entry *centry = NULL;
2526 NTSTATUS status;
2527 char *sidlist;
2528 int i;
2529 bool old_status;
2531 old_status = domain->online;
2532 status = wcache_lookup_useraliases(domain, mem_ctx, num_sids, sids,
2533 num_aliases, alias_rids);
2534 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2535 return status;
2538 (*num_aliases) = 0;
2539 (*alias_rids) = NULL;
2541 if (!NT_STATUS_IS_OK(domain->last_status))
2542 return domain->last_status;
2544 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info "
2545 "for domain %s\n", domain->name ));
2547 sidlist = wcache_make_sidlist(talloc_tos(), num_sids, sids);
2548 if (sidlist == NULL) {
2549 return NT_STATUS_NO_MEMORY;
2552 status = domain->backend->lookup_useraliases(domain, mem_ctx,
2553 num_sids, sids,
2554 num_aliases, alias_rids);
2556 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2557 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2558 if (!domain->internal && old_status) {
2559 set_domain_offline(domain);
2561 if (!domain->internal &&
2562 !domain->online &&
2563 old_status) {
2564 NTSTATUS cache_status;
2565 cache_status = wcache_lookup_useraliases(domain, mem_ctx, num_sids,
2566 sids, num_aliases, alias_rids);
2567 return cache_status;
2570 /* and save it */
2571 refresh_sequence_number(domain, false);
2572 if (!NT_STATUS_IS_OK(status)) {
2573 return status;
2575 centry = centry_start(domain, status);
2576 if (!centry)
2577 goto skip_save;
2578 centry_put_uint32(centry, *num_aliases);
2579 for (i=0; i<(*num_aliases); i++)
2580 centry_put_uint32(centry, (*alias_rids)[i]);
2581 centry_end(centry, "UA%s", sidlist);
2582 centry_free(centry);
2584 skip_save:
2585 return status;
2588 NTSTATUS wcache_lookup_groupmem(struct winbindd_domain *domain,
2589 TALLOC_CTX *mem_ctx,
2590 const struct dom_sid *group_sid,
2591 uint32_t *num_names,
2592 struct dom_sid **sid_mem, char ***names,
2593 uint32_t **name_types)
2595 struct winbind_cache *cache = get_cache(domain);
2596 struct cache_entry *centry = NULL;
2597 NTSTATUS status;
2598 unsigned int i;
2599 char *sid_string;
2601 if (cache->tdb == NULL) {
2602 return NT_STATUS_NOT_FOUND;
2605 sid_string = sid_string_tos(group_sid);
2606 if (sid_string == NULL) {
2607 return NT_STATUS_NO_MEMORY;
2610 centry = wcache_fetch(cache, domain, "GM/%s", sid_string);
2611 TALLOC_FREE(sid_string);
2612 if (centry == NULL) {
2613 return NT_STATUS_NOT_FOUND;
2616 *sid_mem = NULL;
2617 *names = NULL;
2618 *name_types = NULL;
2620 *num_names = centry_uint32(centry);
2621 if (*num_names == 0) {
2622 centry_free(centry);
2623 return NT_STATUS_OK;
2626 *sid_mem = talloc_array(mem_ctx, struct dom_sid, *num_names);
2627 *names = talloc_array(mem_ctx, char *, *num_names);
2628 *name_types = talloc_array(mem_ctx, uint32, *num_names);
2630 if ((*sid_mem == NULL) || (*names == NULL) || (*name_types == NULL)) {
2631 TALLOC_FREE(*sid_mem);
2632 TALLOC_FREE(*names);
2633 TALLOC_FREE(*name_types);
2634 centry_free(centry);
2635 return NT_STATUS_NO_MEMORY;
2638 for (i=0; i<(*num_names); i++) {
2639 centry_sid(centry, &(*sid_mem)[i]);
2640 (*names)[i] = centry_string(centry, mem_ctx);
2641 (*name_types)[i] = centry_uint32(centry);
2644 status = centry->status;
2646 DEBUG(10,("lookup_groupmem: [Cached] - cached info for domain %s "
2647 "status: %s\n", domain->name, nt_errstr(status)));
2649 centry_free(centry);
2650 return status;
2653 static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
2654 TALLOC_CTX *mem_ctx,
2655 const struct dom_sid *group_sid,
2656 enum lsa_SidType type,
2657 uint32 *num_names,
2658 struct dom_sid **sid_mem, char ***names,
2659 uint32 **name_types)
2661 struct cache_entry *centry = NULL;
2662 NTSTATUS status;
2663 unsigned int i;
2664 fstring sid_string;
2665 bool old_status;
2667 old_status = domain->online;
2668 status = wcache_lookup_groupmem(domain, mem_ctx, group_sid, num_names,
2669 sid_mem, names, name_types);
2670 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2671 return status;
2674 (*num_names) = 0;
2675 (*sid_mem) = NULL;
2676 (*names) = NULL;
2677 (*name_types) = NULL;
2679 /* Return status value returned by seq number check */
2681 if (!NT_STATUS_IS_OK(domain->last_status))
2682 return domain->last_status;
2684 DEBUG(10,("lookup_groupmem: [Cached] - doing backend query for info for domain %s\n",
2685 domain->name ));
2687 status = domain->backend->lookup_groupmem(domain, mem_ctx, group_sid,
2688 type, num_names,
2689 sid_mem, names, name_types);
2691 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2692 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2693 if (!domain->internal && old_status) {
2694 set_domain_offline(domain);
2696 if (!domain->internal &&
2697 !domain->online &&
2698 old_status) {
2699 NTSTATUS cache_status;
2700 cache_status = wcache_lookup_groupmem(domain, mem_ctx, group_sid,
2701 num_names, sid_mem, names,
2702 name_types);
2703 return cache_status;
2706 /* and save it */
2707 refresh_sequence_number(domain, false);
2708 if (!NT_STATUS_IS_OK(status)) {
2709 return status;
2711 centry = centry_start(domain, status);
2712 if (!centry)
2713 goto skip_save;
2714 centry_put_uint32(centry, *num_names);
2715 for (i=0; i<(*num_names); i++) {
2716 centry_put_sid(centry, &(*sid_mem)[i]);
2717 centry_put_string(centry, (*names)[i]);
2718 centry_put_uint32(centry, (*name_types)[i]);
2720 centry_end(centry, "GM/%s", sid_to_fstring(sid_string, group_sid));
2721 centry_free(centry);
2723 skip_save:
2724 return status;
2727 /* find the sequence number for a domain */
2728 static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
2730 refresh_sequence_number(domain, false);
2732 *seq = domain->sequence_number;
2734 return NT_STATUS_OK;
2737 /* enumerate trusted domains
2738 * (we need to have the list of trustdoms in the cache when we go offline) -
2739 * Guenther */
2740 static NTSTATUS trusted_domains(struct winbindd_domain *domain,
2741 TALLOC_CTX *mem_ctx,
2742 struct netr_DomainTrustList *trusts)
2744 NTSTATUS status;
2745 struct winbind_cache *cache;
2746 struct winbindd_tdc_domain *dom_list = NULL;
2747 size_t num_domains = 0;
2748 bool retval = false;
2749 int i;
2750 bool old_status;
2752 old_status = domain->online;
2753 trusts->count = 0;
2754 trusts->array = NULL;
2756 cache = get_cache(domain);
2757 if (!cache || !cache->tdb) {
2758 goto do_query;
2761 if (domain->online) {
2762 goto do_query;
2765 retval = wcache_tdc_fetch_list(&dom_list, &num_domains);
2766 if (!retval || !num_domains || !dom_list) {
2767 TALLOC_FREE(dom_list);
2768 goto do_query;
2771 do_fetch_cache:
2772 trusts->array = TALLOC_ZERO_ARRAY(mem_ctx, struct netr_DomainTrust, num_domains);
2773 if (!trusts->array) {
2774 TALLOC_FREE(dom_list);
2775 return NT_STATUS_NO_MEMORY;
2778 for (i = 0; i < num_domains; i++) {
2779 struct netr_DomainTrust *trust;
2780 struct dom_sid *sid;
2781 struct winbindd_domain *dom;
2783 dom = find_domain_from_name_noinit(dom_list[i].domain_name);
2784 if (dom && dom->internal) {
2785 continue;
2788 trust = &trusts->array[trusts->count];
2789 trust->netbios_name = talloc_strdup(trusts->array, dom_list[i].domain_name);
2790 trust->dns_name = talloc_strdup(trusts->array, dom_list[i].dns_name);
2791 sid = talloc(trusts->array, struct dom_sid);
2792 if (!trust->netbios_name || !trust->dns_name ||
2793 !sid) {
2794 TALLOC_FREE(dom_list);
2795 TALLOC_FREE(trusts->array);
2796 return NT_STATUS_NO_MEMORY;
2799 trust->trust_flags = dom_list[i].trust_flags;
2800 trust->trust_attributes = dom_list[i].trust_attribs;
2801 trust->trust_type = dom_list[i].trust_type;
2802 sid_copy(sid, &dom_list[i].sid);
2803 trust->sid = sid;
2804 trusts->count++;
2807 TALLOC_FREE(dom_list);
2808 return NT_STATUS_OK;
2810 do_query:
2811 /* Return status value returned by seq number check */
2813 if (!NT_STATUS_IS_OK(domain->last_status))
2814 return domain->last_status;
2816 DEBUG(10,("trusted_domains: [Cached] - doing backend query for info for domain %s\n",
2817 domain->name ));
2819 status = domain->backend->trusted_domains(domain, mem_ctx, trusts);
2821 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2822 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2823 if (!domain->internal && old_status) {
2824 set_domain_offline(domain);
2826 if (!domain->internal &&
2827 !domain->online &&
2828 old_status) {
2829 retval = wcache_tdc_fetch_list(&dom_list, &num_domains);
2830 if (retval && num_domains && dom_list) {
2831 TALLOC_FREE(trusts->array);
2832 trusts->count = 0;
2833 goto do_fetch_cache;
2837 /* no trusts gives NT_STATUS_NO_MORE_ENTRIES resetting to NT_STATUS_OK
2838 * so that the generic centry handling still applies correctly -
2839 * Guenther*/
2841 if (!NT_STATUS_IS_ERR(status)) {
2842 status = NT_STATUS_OK;
2844 return status;
2847 /* get lockout policy */
2848 static NTSTATUS lockout_policy(struct winbindd_domain *domain,
2849 TALLOC_CTX *mem_ctx,
2850 struct samr_DomInfo12 *policy)
2852 struct winbind_cache *cache = get_cache(domain);
2853 struct cache_entry *centry = NULL;
2854 NTSTATUS status;
2855 bool old_status;
2857 old_status = domain->online;
2858 if (!cache->tdb)
2859 goto do_query;
2861 centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
2863 if (!centry)
2864 goto do_query;
2866 do_fetch_cache:
2867 policy->lockout_duration = centry_nttime(centry);
2868 policy->lockout_window = centry_nttime(centry);
2869 policy->lockout_threshold = centry_uint16(centry);
2871 status = centry->status;
2873 DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2874 domain->name, nt_errstr(status) ));
2876 centry_free(centry);
2877 return status;
2879 do_query:
2880 ZERO_STRUCTP(policy);
2882 /* Return status value returned by seq number check */
2884 if (!NT_STATUS_IS_OK(domain->last_status))
2885 return domain->last_status;
2887 DEBUG(10,("lockout_policy: [Cached] - doing backend query for info for domain %s\n",
2888 domain->name ));
2890 status = domain->backend->lockout_policy(domain, mem_ctx, policy);
2892 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2893 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2894 if (!domain->internal && old_status) {
2895 set_domain_offline(domain);
2897 if (cache->tdb &&
2898 !domain->internal &&
2899 !domain->online &&
2900 old_status) {
2901 centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
2902 if (centry) {
2903 goto do_fetch_cache;
2907 /* and save it */
2908 refresh_sequence_number(domain, false);
2909 if (!NT_STATUS_IS_OK(status)) {
2910 return status;
2912 wcache_save_lockout_policy(domain, status, policy);
2914 return status;
2917 /* get password policy */
2918 static NTSTATUS password_policy(struct winbindd_domain *domain,
2919 TALLOC_CTX *mem_ctx,
2920 struct samr_DomInfo1 *policy)
2922 struct winbind_cache *cache = get_cache(domain);
2923 struct cache_entry *centry = NULL;
2924 NTSTATUS status;
2925 bool old_status;
2927 old_status = domain->online;
2928 if (!cache->tdb)
2929 goto do_query;
2931 centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
2933 if (!centry)
2934 goto do_query;
2936 do_fetch_cache:
2937 policy->min_password_length = centry_uint16(centry);
2938 policy->password_history_length = centry_uint16(centry);
2939 policy->password_properties = centry_uint32(centry);
2940 policy->max_password_age = centry_nttime(centry);
2941 policy->min_password_age = centry_nttime(centry);
2943 status = centry->status;
2945 DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2946 domain->name, nt_errstr(status) ));
2948 centry_free(centry);
2949 return status;
2951 do_query:
2952 ZERO_STRUCTP(policy);
2954 /* Return status value returned by seq number check */
2956 if (!NT_STATUS_IS_OK(domain->last_status))
2957 return domain->last_status;
2959 DEBUG(10,("password_policy: [Cached] - doing backend query for info for domain %s\n",
2960 domain->name ));
2962 status = domain->backend->password_policy(domain, mem_ctx, policy);
2964 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2965 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2966 if (!domain->internal && old_status) {
2967 set_domain_offline(domain);
2969 if (cache->tdb &&
2970 !domain->internal &&
2971 !domain->online &&
2972 old_status) {
2973 centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
2974 if (centry) {
2975 goto do_fetch_cache;
2979 /* and save it */
2980 refresh_sequence_number(domain, false);
2981 if (!NT_STATUS_IS_OK(status)) {
2982 return status;
2984 wcache_save_password_policy(domain, status, policy);
2986 return status;
2990 /* Invalidate cached user and group lists coherently */
2992 static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
2993 void *state)
2995 if (strncmp((const char *)kbuf.dptr, "UL/", 3) == 0 ||
2996 strncmp((const char *)kbuf.dptr, "GL/", 3) == 0)
2997 tdb_delete(the_tdb, kbuf);
2999 return 0;
3002 /* Invalidate the getpwnam and getgroups entries for a winbindd domain */
3004 void wcache_invalidate_samlogon(struct winbindd_domain *domain,
3005 const struct dom_sid *sid)
3007 fstring key_str, sid_string;
3008 struct winbind_cache *cache;
3010 /* dont clear cached U/SID and UG/SID entries when we want to logon
3011 * offline - gd */
3013 if (lp_winbind_offline_logon()) {
3014 return;
3017 if (!domain)
3018 return;
3020 cache = get_cache(domain);
3022 if (!cache->tdb) {
3023 return;
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(sid);
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 const char **homedir, const char **shell,
4647 const char **gecos, gid_t *p_gid)
4649 struct winbind_cache *cache = get_cache(domain);
4650 struct cache_entry *centry = NULL;
4651 NTSTATUS nt_status;
4652 fstring tmp;
4654 if (!cache->tdb)
4655 goto do_query;
4657 centry = wcache_fetch(cache, domain, "NSS/PWINFO/%s",
4658 sid_to_fstring(tmp, user_sid));
4660 if (!centry)
4661 goto do_query;
4663 *homedir = centry_string( centry, ctx );
4664 *shell = centry_string( centry, ctx );
4665 *gecos = centry_string( centry, ctx );
4666 *p_gid = centry_uint32( centry );
4668 centry_free(centry);
4670 DEBUG(10,("nss_get_info_cached: [Cached] - user_sid %s\n",
4671 sid_string_dbg(user_sid)));
4673 return NT_STATUS_OK;
4675 do_query:
4677 nt_status = nss_get_info( domain->name, user_sid, ctx,
4678 homedir, shell, gecos, p_gid );
4680 DEBUG(10, ("nss_get_info returned %s\n", nt_errstr(nt_status)));
4682 if ( NT_STATUS_IS_OK(nt_status) ) {
4683 DEBUG(10, ("result:\n\thomedir = '%s'\n", *homedir));
4684 DEBUGADD(10, ("\tshell = '%s'\n", *shell));
4685 DEBUGADD(10, ("\tgecos = '%s'\n", *gecos));
4686 DEBUGADD(10, ("\tgid = '%u'\n", (unsigned int)*p_gid));
4688 wcache_save_user_pwinfo( domain, nt_status, user_sid,
4689 *homedir, *shell, *gecos, *p_gid );
4692 if ( NT_STATUS_EQUAL( nt_status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND ) ) {
4693 DEBUG(5,("nss_get_info_cached: Setting domain %s offline\n",
4694 domain->name ));
4695 set_domain_offline( domain );
4698 return nt_status;
4701 #endif
4703 /* the cache backend methods are exposed via this structure */
4704 struct winbindd_methods cache_methods = {
4705 true,
4706 query_user_list,
4707 enum_dom_groups,
4708 enum_local_groups,
4709 name_to_sid,
4710 sid_to_name,
4711 rids_to_names,
4712 query_user,
4713 lookup_usergroups,
4714 lookup_useraliases,
4715 lookup_groupmem,
4716 sequence_number,
4717 lockout_policy,
4718 password_policy,
4719 trusted_domains
4722 static bool wcache_ndr_key(TALLOC_CTX *mem_ctx, char *domain_name,
4723 uint32_t opnum, const DATA_BLOB *req,
4724 TDB_DATA *pkey)
4726 char *key;
4727 size_t keylen;
4729 key = talloc_asprintf(mem_ctx, "NDR/%s/%d/", domain_name, (int)opnum);
4730 if (key == NULL) {
4731 return false;
4733 keylen = talloc_get_size(key) - 1;
4735 key = talloc_realloc(mem_ctx, key, char, keylen + req->length);
4736 if (key == NULL) {
4737 return false;
4739 memcpy(key + keylen, req->data, req->length);
4741 pkey->dptr = (uint8_t *)key;
4742 pkey->dsize = talloc_get_size(key);
4743 return true;
4746 static bool wcache_opnum_cacheable(uint32_t opnum)
4748 switch (opnum) {
4749 case NDR_WBINT_PING:
4750 case NDR_WBINT_QUERYSEQUENCENUMBER:
4751 case NDR_WBINT_ALLOCATEUID:
4752 case NDR_WBINT_ALLOCATEGID:
4753 case NDR_WBINT_CHECKMACHINEACCOUNT:
4754 case NDR_WBINT_CHANGEMACHINEACCOUNT:
4755 case NDR_WBINT_PINGDC:
4756 return false;
4758 return true;
4761 bool wcache_fetch_ndr(TALLOC_CTX *mem_ctx, struct winbindd_domain *domain,
4762 uint32_t opnum, const DATA_BLOB *req, DATA_BLOB *resp)
4764 TDB_DATA key, data;
4765 bool ret = false;
4767 if (!wcache_opnum_cacheable(opnum) ||
4768 is_my_own_sam_domain(domain) ||
4769 is_builtin_domain(domain)) {
4770 return false;
4773 if (wcache->tdb == NULL) {
4774 return false;
4777 if (!wcache_ndr_key(talloc_tos(), domain->name, opnum, req, &key)) {
4778 return false;
4780 data = tdb_fetch(wcache->tdb, key);
4781 TALLOC_FREE(key.dptr);
4783 if (data.dptr == NULL) {
4784 return false;
4786 if (data.dsize < 12) {
4787 goto fail;
4790 if (!is_domain_offline(domain)) {
4791 uint32_t entry_seqnum, dom_seqnum, last_check;
4792 uint64_t entry_timeout;
4794 if (!wcache_fetch_seqnum(domain->name, &dom_seqnum,
4795 &last_check)) {
4796 goto fail;
4798 entry_seqnum = IVAL(data.dptr, 0);
4799 if (entry_seqnum != dom_seqnum) {
4800 DEBUG(10, ("Entry has wrong sequence number: %d\n",
4801 (int)entry_seqnum));
4802 goto fail;
4804 entry_timeout = BVAL(data.dptr, 4);
4805 if (entry_timeout > time(NULL)) {
4806 DEBUG(10, ("Entry has timed out\n"));
4807 goto fail;
4811 resp->data = (uint8_t *)talloc_memdup(mem_ctx, data.dptr + 12,
4812 data.dsize - 12);
4813 if (resp->data == NULL) {
4814 DEBUG(10, ("talloc failed\n"));
4815 goto fail;
4817 resp->length = data.dsize - 12;
4819 ret = true;
4820 fail:
4821 SAFE_FREE(data.dptr);
4822 return ret;
4825 void wcache_store_ndr(struct winbindd_domain *domain, uint32_t opnum,
4826 const DATA_BLOB *req, const DATA_BLOB *resp)
4828 TDB_DATA key, data;
4829 uint32_t dom_seqnum, last_check;
4830 uint64_t timeout;
4832 if (!wcache_opnum_cacheable(opnum) ||
4833 is_my_own_sam_domain(domain) ||
4834 is_builtin_domain(domain)) {
4835 return;
4838 if (wcache->tdb == NULL) {
4839 return;
4842 if (!wcache_fetch_seqnum(domain->name, &dom_seqnum, &last_check)) {
4843 DEBUG(10, ("could not fetch seqnum for domain %s\n",
4844 domain->name));
4845 return;
4848 if (!wcache_ndr_key(talloc_tos(), domain->name, opnum, req, &key)) {
4849 return;
4852 timeout = time(NULL) + lp_winbind_cache_time();
4854 data.dsize = resp->length + 12;
4855 data.dptr = talloc_array(key.dptr, uint8_t, data.dsize);
4856 if (data.dptr == NULL) {
4857 goto done;
4860 SIVAL(data.dptr, 0, dom_seqnum);
4861 SBVAL(data.dptr, 4, timeout);
4862 memcpy(data.dptr + 12, resp->data, resp->length);
4864 tdb_store(wcache->tdb, key, data, 0);
4866 done:
4867 TALLOC_FREE(key.dptr);
4868 return;