BUG 9766: Cache name_to_sid/sid_to_name correctly.
[Samba.git] / source3 / winbindd / winbindd_cache.c
blob033ea770db5daecf5752d9d0c71984059d75a507
1 /*
2 Unix SMB/CIFS implementation.
4 Winbind cache backend functions
6 Copyright (C) Andrew Tridgell 2001
7 Copyright (C) Gerald Carter 2003-2007
8 Copyright (C) Volker Lendecke 2005
9 Copyright (C) Guenther Deschner 2005
10 Copyright (C) Michael Adam 2007
12 This program is free software; you can redistribute it and/or modify
13 it under the terms of the GNU General Public License as published by
14 the Free Software Foundation; either version 3 of the License, or
15 (at your option) any later version.
17 This program is distributed in the hope that it will be useful,
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 GNU General Public License for more details.
22 You should have received a copy of the GNU General Public License
23 along with this program. If not, see <http://www.gnu.org/licenses/>.
26 #include "includes.h"
27 #include "system/filesys.h"
28 #include "winbindd.h"
29 #include "tdb_validate.h"
30 #include "../libcli/auth/libcli_auth.h"
31 #include "../librpc/gen_ndr/ndr_wbint.h"
32 #include "ads.h"
33 #include "nss_info.h"
34 #include "../libcli/security/security.h"
35 #include "passdb/machine_sid.h"
36 #include "util_tdb.h"
38 #undef DBGC_CLASS
39 #define DBGC_CLASS DBGC_WINBIND
41 #define WINBINDD_CACHE_VER1 1 /* initial db version */
42 #define WINBINDD_CACHE_VER2 2 /* second version with timeouts for NDR entries */
44 #define WINBINDD_CACHE_VERSION WINBINDD_CACHE_VER2
45 #define WINBINDD_CACHE_VERSION_KEYSTR "WINBINDD_CACHE_VERSION"
47 extern struct winbindd_methods reconnect_methods;
48 #ifdef HAVE_ADS
49 extern struct winbindd_methods ads_methods;
50 #endif
51 extern struct winbindd_methods builtin_passdb_methods;
52 extern struct winbindd_methods sam_passdb_methods;
55 * JRA. KEEP THIS LIST UP TO DATE IF YOU ADD CACHE ENTRIES.
56 * Here are the list of entry types that are *not* stored
57 * as form struct cache_entry in the cache.
60 static const char *non_centry_keys[] = {
61 "SEQNUM/",
62 "DR/",
63 "DE/",
64 "WINBINDD_OFFLINE",
65 WINBINDD_CACHE_VERSION_KEYSTR,
66 NULL
69 /************************************************************************
70 Is this key a non-centry type ?
71 ************************************************************************/
73 static bool is_non_centry_key(TDB_DATA kbuf)
75 int i;
77 if (kbuf.dptr == NULL || kbuf.dsize == 0) {
78 return false;
80 for (i = 0; non_centry_keys[i] != NULL; i++) {
81 size_t namelen = strlen(non_centry_keys[i]);
82 if (kbuf.dsize < namelen) {
83 continue;
85 if (strncmp(non_centry_keys[i], (const char *)kbuf.dptr, namelen) == 0) {
86 return true;
89 return false;
92 /* Global online/offline state - False when online. winbindd starts up online
93 and sets this to true if the first query fails and there's an entry in
94 the cache tdb telling us to stay offline. */
96 static bool global_winbindd_offline_state;
98 struct winbind_cache {
99 TDB_CONTEXT *tdb;
102 struct cache_entry {
103 NTSTATUS status;
104 uint32 sequence_number;
105 uint64_t timeout;
106 uint8 *data;
107 uint32 len, ofs;
110 void (*smb_panic_fn)(const char *const why) = smb_panic;
112 #define WINBINDD_MAX_CACHE_SIZE (50*1024*1024)
114 static struct winbind_cache *wcache;
116 /* get the winbind_cache structure */
117 static struct winbind_cache *get_cache(struct winbindd_domain *domain)
119 struct winbind_cache *ret = wcache;
121 /* We have to know what type of domain we are dealing with first. */
123 if (domain->internal) {
124 domain->backend = &builtin_passdb_methods;
125 domain->initialized = True;
128 if (strequal(domain->name, get_global_sam_name()) &&
129 sid_check_is_domain(&domain->sid)) {
130 domain->backend = &sam_passdb_methods;
131 domain->initialized = True;
134 if ( !domain->initialized ) {
135 init_dc_connection( domain );
139 OK. listen up becasue I'm only going to say this once.
140 We have the following scenarios to consider
141 (a) trusted AD domains on a Samba DC,
142 (b) trusted AD domains and we are joined to a non-kerberos domain
143 (c) trusted AD domains and we are joined to a kerberos (AD) domain
145 For (a) we can always contact the trusted domain using krb5
146 since we have the domain trust account password
148 For (b) we can only use RPC since we have no way of
149 getting a krb5 ticket in our own domain
151 For (c) we can always use krb5 since we have a kerberos trust
153 --jerry
156 if (!domain->backend) {
157 #ifdef HAVE_ADS
158 struct winbindd_domain *our_domain = domain;
160 /* find our domain first so we can figure out if we
161 are joined to a kerberized domain */
163 if ( !domain->primary )
164 our_domain = find_our_domain();
166 if ((our_domain->active_directory || IS_DC)
167 && domain->active_directory
168 && !lp_winbind_rpc_only()) {
169 DEBUG(5,("get_cache: Setting ADS methods for domain %s\n", domain->name));
170 domain->backend = &ads_methods;
171 } else {
172 #endif /* HAVE_ADS */
173 DEBUG(5,("get_cache: Setting MS-RPC methods for domain %s\n", domain->name));
174 domain->backend = &reconnect_methods;
175 #ifdef HAVE_ADS
177 #endif /* HAVE_ADS */
180 if (ret)
181 return ret;
183 ret = SMB_XMALLOC_P(struct winbind_cache);
184 ZERO_STRUCTP(ret);
186 wcache = ret;
187 wcache_flush_cache();
189 return ret;
193 free a centry structure
195 static void centry_free(struct cache_entry *centry)
197 if (!centry)
198 return;
199 SAFE_FREE(centry->data);
200 free(centry);
203 static bool centry_check_bytes(struct cache_entry *centry, size_t nbytes)
205 if (centry->len - centry->ofs < nbytes) {
206 DEBUG(0,("centry corruption? needed %u bytes, have %d\n",
207 (unsigned int)nbytes,
208 centry->len - centry->ofs));
209 return false;
211 return true;
215 pull a uint64_t from a cache entry
217 static uint64_t centry_uint64_t(struct cache_entry *centry)
219 uint64_t ret;
221 if (!centry_check_bytes(centry, 8)) {
222 smb_panic_fn("centry_uint64_t");
224 ret = BVAL(centry->data, centry->ofs);
225 centry->ofs += 8;
226 return ret;
230 pull a uint32 from a cache entry
232 static uint32 centry_uint32(struct cache_entry *centry)
234 uint32 ret;
236 if (!centry_check_bytes(centry, 4)) {
237 smb_panic_fn("centry_uint32");
239 ret = IVAL(centry->data, centry->ofs);
240 centry->ofs += 4;
241 return ret;
245 pull a uint16 from a cache entry
247 static uint16 centry_uint16(struct cache_entry *centry)
249 uint16 ret;
250 if (!centry_check_bytes(centry, 2)) {
251 smb_panic_fn("centry_uint16");
253 ret = SVAL(centry->data, centry->ofs);
254 centry->ofs += 2;
255 return ret;
259 pull a uint8 from a cache entry
261 static uint8 centry_uint8(struct cache_entry *centry)
263 uint8 ret;
264 if (!centry_check_bytes(centry, 1)) {
265 smb_panic_fn("centry_uint8");
267 ret = CVAL(centry->data, centry->ofs);
268 centry->ofs += 1;
269 return ret;
273 pull a NTTIME from a cache entry
275 static NTTIME centry_nttime(struct cache_entry *centry)
277 NTTIME ret;
278 if (!centry_check_bytes(centry, 8)) {
279 smb_panic_fn("centry_nttime");
281 ret = IVAL(centry->data, centry->ofs);
282 centry->ofs += 4;
283 ret += (uint64)IVAL(centry->data, centry->ofs) << 32;
284 centry->ofs += 4;
285 return ret;
289 pull a time_t from a cache entry. time_t stored portably as a 64-bit time.
291 static time_t centry_time(struct cache_entry *centry)
293 return (time_t)centry_nttime(centry);
296 /* pull a string from a cache entry, using the supplied
297 talloc context
299 static char *centry_string(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
301 uint32 len;
302 char *ret;
304 len = centry_uint8(centry);
306 if (len == 0xFF) {
307 /* a deliberate NULL string */
308 return NULL;
311 if (!centry_check_bytes(centry, (size_t)len)) {
312 smb_panic_fn("centry_string");
315 ret = TALLOC_ARRAY(mem_ctx, char, len+1);
316 if (!ret) {
317 smb_panic_fn("centry_string out of memory\n");
319 memcpy(ret,centry->data + centry->ofs, len);
320 ret[len] = 0;
321 centry->ofs += len;
322 return ret;
325 /* pull a hash16 from a cache entry, using the supplied
326 talloc context
328 static char *centry_hash16(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
330 uint32 len;
331 char *ret;
333 len = centry_uint8(centry);
335 if (len != 16) {
336 DEBUG(0,("centry corruption? hash len (%u) != 16\n",
337 len ));
338 return NULL;
341 if (!centry_check_bytes(centry, 16)) {
342 return NULL;
345 ret = TALLOC_ARRAY(mem_ctx, char, 16);
346 if (!ret) {
347 smb_panic_fn("centry_hash out of memory\n");
349 memcpy(ret,centry->data + centry->ofs, 16);
350 centry->ofs += 16;
351 return ret;
354 /* pull a sid from a cache entry, using the supplied
355 talloc context
357 static bool centry_sid(struct cache_entry *centry, struct dom_sid *sid)
359 char *sid_string;
360 bool ret;
362 sid_string = centry_string(centry, talloc_tos());
363 if (sid_string == NULL) {
364 return false;
366 ret = string_to_sid(sid, sid_string);
367 TALLOC_FREE(sid_string);
368 return ret;
373 pull a NTSTATUS from a cache entry
375 static NTSTATUS centry_ntstatus(struct cache_entry *centry)
377 NTSTATUS status;
379 status = NT_STATUS(centry_uint32(centry));
380 return status;
384 /* the server is considered down if it can't give us a sequence number */
385 static bool wcache_server_down(struct winbindd_domain *domain)
387 bool ret;
389 if (!wcache->tdb)
390 return false;
392 ret = (domain->sequence_number == DOM_SEQUENCE_NONE);
394 if (ret)
395 DEBUG(10,("wcache_server_down: server for Domain %s down\n",
396 domain->name ));
397 return ret;
400 static bool wcache_fetch_seqnum(const char *domain_name, uint32_t *seqnum,
401 uint32_t *last_seq_check)
403 char *key;
404 TDB_DATA data;
406 if (wcache->tdb == NULL) {
407 DEBUG(10,("wcache_fetch_seqnum: tdb == NULL\n"));
408 return false;
411 key = talloc_asprintf(talloc_tos(), "SEQNUM/%s", domain_name);
412 if (key == NULL) {
413 DEBUG(10, ("talloc failed\n"));
414 return false;
417 data = tdb_fetch_bystring(wcache->tdb, key);
418 TALLOC_FREE(key);
420 if (data.dptr == NULL) {
421 DEBUG(10, ("wcache_fetch_seqnum: %s not found\n",
422 domain_name));
423 return false;
425 if (data.dsize != 8) {
426 DEBUG(10, ("wcache_fetch_seqnum: invalid data size %d\n",
427 (int)data.dsize));
428 SAFE_FREE(data.dptr);
429 return false;
432 *seqnum = IVAL(data.dptr, 0);
433 *last_seq_check = IVAL(data.dptr, 4);
434 SAFE_FREE(data.dptr);
436 return true;
439 static NTSTATUS fetch_cache_seqnum( struct winbindd_domain *domain, time_t now )
441 uint32 last_check, time_diff;
443 if (!wcache_fetch_seqnum(domain->name, &domain->sequence_number,
444 &last_check)) {
445 return NT_STATUS_UNSUCCESSFUL;
447 domain->last_seq_check = last_check;
449 /* have we expired? */
451 time_diff = now - domain->last_seq_check;
452 if ( time_diff > lp_winbind_cache_time() ) {
453 DEBUG(10,("fetch_cache_seqnum: timeout [%s][%u @ %u]\n",
454 domain->name, domain->sequence_number,
455 (uint32)domain->last_seq_check));
456 return NT_STATUS_UNSUCCESSFUL;
459 DEBUG(10,("fetch_cache_seqnum: success [%s][%u @ %u]\n",
460 domain->name, domain->sequence_number,
461 (uint32)domain->last_seq_check));
463 return NT_STATUS_OK;
466 bool wcache_store_seqnum(const char *domain_name, uint32_t seqnum,
467 time_t last_seq_check)
469 char *key_str;
470 uint8_t buf[8];
471 int ret;
473 if (wcache->tdb == NULL) {
474 DEBUG(10, ("wcache_store_seqnum: wcache->tdb == NULL\n"));
475 return false;
478 key_str = talloc_asprintf(talloc_tos(), "SEQNUM/%s", domain_name);
479 if (key_str == NULL) {
480 DEBUG(10, ("talloc_asprintf failed\n"));
481 return false;
484 SIVAL(buf, 0, seqnum);
485 SIVAL(buf, 4, last_seq_check);
487 ret = tdb_store_bystring(wcache->tdb, key_str,
488 make_tdb_data(buf, sizeof(buf)), TDB_REPLACE);
489 TALLOC_FREE(key_str);
490 if (ret == -1) {
491 DEBUG(10, ("tdb_store_bystring failed: %s\n",
492 tdb_errorstr(wcache->tdb)));
493 TALLOC_FREE(key_str);
494 return false;
497 DEBUG(10, ("wcache_store_seqnum: success [%s][%u @ %u]\n",
498 domain_name, seqnum, (unsigned)last_seq_check));
500 return true;
503 static bool store_cache_seqnum( struct winbindd_domain *domain )
505 return wcache_store_seqnum(domain->name, domain->sequence_number,
506 domain->last_seq_check);
510 refresh the domain sequence number. If force is true
511 then always refresh it, no matter how recently we fetched it
514 static void refresh_sequence_number(struct winbindd_domain *domain, bool force)
516 NTSTATUS status;
517 unsigned time_diff;
518 time_t t = time(NULL);
519 unsigned cache_time = lp_winbind_cache_time();
521 if (is_domain_offline(domain)) {
522 return;
525 get_cache( domain );
527 #if 0 /* JERRY -- disable as the default cache time is now 5 minutes */
528 /* trying to reconnect is expensive, don't do it too often */
529 if (domain->sequence_number == DOM_SEQUENCE_NONE) {
530 cache_time *= 8;
532 #endif
534 time_diff = t - domain->last_seq_check;
536 /* see if we have to refetch the domain sequence number */
537 if (!force && (time_diff < cache_time) &&
538 (domain->sequence_number != DOM_SEQUENCE_NONE) &&
539 NT_STATUS_IS_OK(domain->last_status)) {
540 DEBUG(10, ("refresh_sequence_number: %s time ok\n", domain->name));
541 goto done;
544 /* try to get the sequence number from the tdb cache first */
545 /* this will update the timestamp as well */
547 status = fetch_cache_seqnum( domain, t );
548 if (NT_STATUS_IS_OK(status) &&
549 (domain->sequence_number != DOM_SEQUENCE_NONE) &&
550 NT_STATUS_IS_OK(domain->last_status)) {
551 goto done;
554 /* important! make sure that we know if this is a native
555 mode domain or not. And that we can contact it. */
557 if ( winbindd_can_contact_domain( domain ) ) {
558 status = domain->backend->sequence_number(domain,
559 &domain->sequence_number);
560 } else {
561 /* just use the current time */
562 status = NT_STATUS_OK;
563 domain->sequence_number = time(NULL);
567 /* the above call could have set our domain->backend to NULL when
568 * coming from offline to online mode, make sure to reinitialize the
569 * backend - Guenther */
570 get_cache( domain );
572 if (!NT_STATUS_IS_OK(status)) {
573 DEBUG(10,("refresh_sequence_number: failed with %s\n", nt_errstr(status)));
574 domain->sequence_number = DOM_SEQUENCE_NONE;
577 domain->last_status = status;
578 domain->last_seq_check = time(NULL);
580 /* save the new sequence number in the cache */
581 store_cache_seqnum( domain );
583 done:
584 DEBUG(10, ("refresh_sequence_number: %s seq number is now %d\n",
585 domain->name, domain->sequence_number));
587 return;
591 decide if a cache entry has expired
593 static bool centry_expired(struct winbindd_domain *domain, const char *keystr, struct cache_entry *centry)
595 /* If we've been told to be offline - stay in that state... */
596 if (lp_winbind_offline_logon() && global_winbindd_offline_state) {
597 DEBUG(10,("centry_expired: Key %s for domain %s valid as winbindd is globally offline.\n",
598 keystr, domain->name ));
599 return false;
602 /* when the domain is offline return the cached entry.
603 * This deals with transient offline states... */
605 if (!domain->online) {
606 DEBUG(10,("centry_expired: Key %s for domain %s valid as domain is offline.\n",
607 keystr, domain->name ));
608 return false;
611 /* if the server is OK and our cache entry came from when it was down then
612 the entry is invalid */
613 if ((domain->sequence_number != DOM_SEQUENCE_NONE) &&
614 (centry->sequence_number == DOM_SEQUENCE_NONE)) {
615 DEBUG(10,("centry_expired: Key %s for domain %s invalid sequence.\n",
616 keystr, domain->name ));
617 return true;
620 /* if the server is down or the cache entry is not older than the
621 current sequence number or it did not timeout then it is OK */
622 if (wcache_server_down(domain)
623 || ((centry->sequence_number == domain->sequence_number)
624 && (centry->timeout > time(NULL)))) {
625 DEBUG(10,("centry_expired: Key %s for domain %s is good.\n",
626 keystr, domain->name ));
627 return false;
630 DEBUG(10,("centry_expired: Key %s for domain %s expired\n",
631 keystr, domain->name ));
633 /* it's expired */
634 return true;
637 static struct cache_entry *wcache_fetch_raw(char *kstr)
639 TDB_DATA data;
640 struct cache_entry *centry;
641 TDB_DATA key;
643 key = string_tdb_data(kstr);
644 data = tdb_fetch(wcache->tdb, key);
645 if (!data.dptr) {
646 /* a cache miss */
647 return NULL;
650 centry = SMB_XMALLOC_P(struct cache_entry);
651 centry->data = (unsigned char *)data.dptr;
652 centry->len = data.dsize;
653 centry->ofs = 0;
655 if (centry->len < 16) {
656 /* huh? corrupt cache? */
657 DEBUG(10,("wcache_fetch_raw: Corrupt cache for key %s "
658 "(len < 16)?\n", kstr));
659 centry_free(centry);
660 return NULL;
663 centry->status = centry_ntstatus(centry);
664 centry->sequence_number = centry_uint32(centry);
665 centry->timeout = centry_uint64_t(centry);
667 return centry;
670 static bool is_my_own_sam_domain(struct winbindd_domain *domain)
672 if (strequal(domain->name, get_global_sam_name()) &&
673 sid_check_is_domain(&domain->sid)) {
674 return true;
677 return false;
680 static bool is_builtin_domain(struct winbindd_domain *domain)
682 if (strequal(domain->name, "BUILTIN") &&
683 sid_check_is_builtin(&domain->sid)) {
684 return true;
687 return false;
691 fetch an entry from the cache, with a varargs key. auto-fetch the sequence
692 number and return status
694 static struct cache_entry *wcache_fetch(struct winbind_cache *cache,
695 struct winbindd_domain *domain,
696 const char *format, ...) PRINTF_ATTRIBUTE(3,4);
697 static struct cache_entry *wcache_fetch(struct winbind_cache *cache,
698 struct winbindd_domain *domain,
699 const char *format, ...)
701 va_list ap;
702 char *kstr;
703 struct cache_entry *centry;
705 if (!winbindd_use_cache() ||
706 is_my_own_sam_domain(domain) ||
707 is_builtin_domain(domain)) {
708 return NULL;
711 refresh_sequence_number(domain, false);
713 va_start(ap, format);
714 smb_xvasprintf(&kstr, format, ap);
715 va_end(ap);
717 centry = wcache_fetch_raw(kstr);
718 if (centry == NULL) {
719 free(kstr);
720 return NULL;
723 if (centry_expired(domain, kstr, centry)) {
725 DEBUG(10,("wcache_fetch: entry %s expired for domain %s\n",
726 kstr, domain->name ));
728 centry_free(centry);
729 free(kstr);
730 return NULL;
733 DEBUG(10,("wcache_fetch: returning entry %s for domain %s\n",
734 kstr, domain->name ));
736 free(kstr);
737 return centry;
740 static void wcache_delete(const char *format, ...) PRINTF_ATTRIBUTE(1,2);
741 static void wcache_delete(const char *format, ...)
743 va_list ap;
744 char *kstr;
745 TDB_DATA key;
747 va_start(ap, format);
748 smb_xvasprintf(&kstr, format, ap);
749 va_end(ap);
751 key = string_tdb_data(kstr);
753 tdb_delete(wcache->tdb, key);
754 free(kstr);
758 make sure we have at least len bytes available in a centry
760 static void centry_expand(struct cache_entry *centry, uint32 len)
762 if (centry->len - centry->ofs >= len)
763 return;
764 centry->len *= 2;
765 centry->data = SMB_REALLOC_ARRAY(centry->data, unsigned char,
766 centry->len);
767 if (!centry->data) {
768 DEBUG(0,("out of memory: needed %d bytes in centry_expand\n", centry->len));
769 smb_panic_fn("out of memory in centry_expand");
774 push a uint64_t into a centry
776 static void centry_put_uint64_t(struct cache_entry *centry, uint64_t v)
778 centry_expand(centry, 8);
779 SBVAL(centry->data, centry->ofs, v);
780 centry->ofs += 8;
784 push a uint32 into a centry
786 static void centry_put_uint32(struct cache_entry *centry, uint32 v)
788 centry_expand(centry, 4);
789 SIVAL(centry->data, centry->ofs, v);
790 centry->ofs += 4;
794 push a uint16 into a centry
796 static void centry_put_uint16(struct cache_entry *centry, uint16 v)
798 centry_expand(centry, 2);
799 SSVAL(centry->data, centry->ofs, v);
800 centry->ofs += 2;
804 push a uint8 into a centry
806 static void centry_put_uint8(struct cache_entry *centry, uint8 v)
808 centry_expand(centry, 1);
809 SCVAL(centry->data, centry->ofs, v);
810 centry->ofs += 1;
814 push a string into a centry
816 static void centry_put_string(struct cache_entry *centry, const char *s)
818 int len;
820 if (!s) {
821 /* null strings are marked as len 0xFFFF */
822 centry_put_uint8(centry, 0xFF);
823 return;
826 len = strlen(s);
827 /* can't handle more than 254 char strings. Truncating is probably best */
828 if (len > 254) {
829 DEBUG(10,("centry_put_string: truncating len (%d) to: 254\n", len));
830 len = 254;
832 centry_put_uint8(centry, len);
833 centry_expand(centry, len);
834 memcpy(centry->data + centry->ofs, s, len);
835 centry->ofs += len;
839 push a 16 byte hash into a centry - treat as 16 byte string.
841 static void centry_put_hash16(struct cache_entry *centry, const uint8 val[16])
843 centry_put_uint8(centry, 16);
844 centry_expand(centry, 16);
845 memcpy(centry->data + centry->ofs, val, 16);
846 centry->ofs += 16;
849 static void centry_put_sid(struct cache_entry *centry, const struct dom_sid *sid)
851 fstring sid_string;
852 centry_put_string(centry, sid_to_fstring(sid_string, sid));
857 put NTSTATUS into a centry
859 static void centry_put_ntstatus(struct cache_entry *centry, NTSTATUS status)
861 uint32 status_value = NT_STATUS_V(status);
862 centry_put_uint32(centry, status_value);
867 push a NTTIME into a centry
869 static void centry_put_nttime(struct cache_entry *centry, NTTIME nt)
871 centry_expand(centry, 8);
872 SIVAL(centry->data, centry->ofs, nt & 0xFFFFFFFF);
873 centry->ofs += 4;
874 SIVAL(centry->data, centry->ofs, nt >> 32);
875 centry->ofs += 4;
879 push a time_t into a centry - use a 64 bit size.
880 NTTIME here is being used as a convenient 64-bit size.
882 static void centry_put_time(struct cache_entry *centry, time_t t)
884 NTTIME nt = (NTTIME)t;
885 centry_put_nttime(centry, nt);
889 start a centry for output. When finished, call centry_end()
891 struct cache_entry *centry_start(struct winbindd_domain *domain, NTSTATUS status)
893 struct cache_entry *centry;
895 if (!wcache->tdb)
896 return NULL;
898 centry = SMB_XMALLOC_P(struct cache_entry);
900 centry->len = 8192; /* reasonable default */
901 centry->data = SMB_XMALLOC_ARRAY(uint8, centry->len);
902 centry->ofs = 0;
903 centry->sequence_number = domain->sequence_number;
904 centry->timeout = lp_winbind_cache_time() + time(NULL);
905 centry_put_ntstatus(centry, status);
906 centry_put_uint32(centry, centry->sequence_number);
907 centry_put_uint64_t(centry, centry->timeout);
908 return centry;
912 finish a centry and write it to the tdb
914 static void centry_end(struct cache_entry *centry, const char *format, ...) PRINTF_ATTRIBUTE(2,3);
915 static void centry_end(struct cache_entry *centry, const char *format, ...)
917 va_list ap;
918 char *kstr;
919 TDB_DATA key, data;
921 if (!winbindd_use_cache()) {
922 return;
925 va_start(ap, format);
926 smb_xvasprintf(&kstr, format, ap);
927 va_end(ap);
929 key = string_tdb_data(kstr);
930 data.dptr = centry->data;
931 data.dsize = centry->ofs;
933 tdb_store(wcache->tdb, key, data, TDB_REPLACE);
934 free(kstr);
937 static void wcache_save_name_to_sid(struct winbindd_domain *domain,
938 NTSTATUS status, const char *domain_name,
939 const char *name, const struct dom_sid *sid,
940 enum lsa_SidType type)
942 struct cache_entry *centry;
943 fstring uname;
945 centry = centry_start(domain, status);
946 if (!centry)
947 return;
949 if (domain_name[0] == '\0') {
950 struct winbindd_domain *mydomain =
951 find_domain_from_sid_noinit(sid);
952 if (mydomain != NULL) {
953 domain_name = mydomain->name;
957 centry_put_uint32(centry, type);
958 centry_put_sid(centry, sid);
959 fstrcpy(uname, name);
960 strupper_m(uname);
961 centry_end(centry, "NS/%s/%s", domain_name, uname);
962 DEBUG(10,("wcache_save_name_to_sid: %s\\%s -> %s (%s)\n", domain_name,
963 uname, sid_string_dbg(sid), nt_errstr(status)));
964 centry_free(centry);
967 static void wcache_save_sid_to_name(struct winbindd_domain *domain, NTSTATUS status,
968 const struct dom_sid *sid, const char *domain_name, const char *name, enum lsa_SidType type)
970 struct cache_entry *centry;
971 fstring sid_string;
973 centry = centry_start(domain, status);
974 if (!centry)
975 return;
977 if (domain_name[0] == '\0') {
978 struct winbindd_domain *mydomain =
979 find_domain_from_sid_noinit(sid);
980 if (mydomain != NULL) {
981 domain_name = mydomain->name;
985 if (NT_STATUS_IS_OK(status)) {
986 centry_put_uint32(centry, type);
987 centry_put_string(centry, domain_name);
988 centry_put_string(centry, name);
991 centry_end(centry, "SN/%s", sid_to_fstring(sid_string, sid));
992 DEBUG(10,("wcache_save_sid_to_name: %s -> %s (%s)\n", sid_string,
993 name, nt_errstr(status)));
994 centry_free(centry);
998 static void wcache_save_user(struct winbindd_domain *domain, NTSTATUS status,
999 struct wbint_userinfo *info)
1001 struct cache_entry *centry;
1002 fstring sid_string;
1004 if (is_null_sid(&info->user_sid)) {
1005 return;
1008 centry = centry_start(domain, status);
1009 if (!centry)
1010 return;
1011 centry_put_string(centry, info->acct_name);
1012 centry_put_string(centry, info->full_name);
1013 centry_put_string(centry, info->homedir);
1014 centry_put_string(centry, info->shell);
1015 centry_put_uint32(centry, info->primary_gid);
1016 centry_put_sid(centry, &info->user_sid);
1017 centry_put_sid(centry, &info->group_sid);
1018 centry_end(centry, "U/%s", sid_to_fstring(sid_string,
1019 &info->user_sid));
1020 DEBUG(10,("wcache_save_user: %s (acct_name %s)\n", sid_string, info->acct_name));
1021 centry_free(centry);
1024 static void wcache_save_lockout_policy(struct winbindd_domain *domain,
1025 NTSTATUS status,
1026 struct samr_DomInfo12 *lockout_policy)
1028 struct cache_entry *centry;
1030 centry = centry_start(domain, status);
1031 if (!centry)
1032 return;
1034 centry_put_nttime(centry, lockout_policy->lockout_duration);
1035 centry_put_nttime(centry, lockout_policy->lockout_window);
1036 centry_put_uint16(centry, lockout_policy->lockout_threshold);
1038 centry_end(centry, "LOC_POL/%s", domain->name);
1040 DEBUG(10,("wcache_save_lockout_policy: %s\n", domain->name));
1042 centry_free(centry);
1047 static void wcache_save_password_policy(struct winbindd_domain *domain,
1048 NTSTATUS status,
1049 struct samr_DomInfo1 *policy)
1051 struct cache_entry *centry;
1053 centry = centry_start(domain, status);
1054 if (!centry)
1055 return;
1057 centry_put_uint16(centry, policy->min_password_length);
1058 centry_put_uint16(centry, policy->password_history_length);
1059 centry_put_uint32(centry, policy->password_properties);
1060 centry_put_nttime(centry, policy->max_password_age);
1061 centry_put_nttime(centry, policy->min_password_age);
1063 centry_end(centry, "PWD_POL/%s", domain->name);
1065 DEBUG(10,("wcache_save_password_policy: %s\n", domain->name));
1067 centry_free(centry);
1070 /***************************************************************************
1071 ***************************************************************************/
1073 static void wcache_save_username_alias(struct winbindd_domain *domain,
1074 NTSTATUS status,
1075 const char *name, const char *alias)
1077 struct cache_entry *centry;
1078 fstring uname;
1080 if ( (centry = centry_start(domain, status)) == NULL )
1081 return;
1083 centry_put_string( centry, alias );
1085 fstrcpy(uname, name);
1086 strupper_m(uname);
1087 centry_end(centry, "NSS/NA/%s", uname);
1089 DEBUG(10,("wcache_save_username_alias: %s -> %s\n", name, alias ));
1091 centry_free(centry);
1094 static void wcache_save_alias_username(struct winbindd_domain *domain,
1095 NTSTATUS status,
1096 const char *alias, const char *name)
1098 struct cache_entry *centry;
1099 fstring uname;
1101 if ( (centry = centry_start(domain, status)) == NULL )
1102 return;
1104 centry_put_string( centry, name );
1106 fstrcpy(uname, alias);
1107 strupper_m(uname);
1108 centry_end(centry, "NSS/AN/%s", uname);
1110 DEBUG(10,("wcache_save_alias_username: %s -> %s\n", alias, name ));
1112 centry_free(centry);
1115 /***************************************************************************
1116 ***************************************************************************/
1118 NTSTATUS resolve_username_to_alias( TALLOC_CTX *mem_ctx,
1119 struct winbindd_domain *domain,
1120 const char *name, char **alias )
1122 struct winbind_cache *cache = get_cache(domain);
1123 struct cache_entry *centry = NULL;
1124 NTSTATUS status;
1125 char *upper_name;
1127 if ( domain->internal )
1128 return NT_STATUS_NOT_SUPPORTED;
1130 if (!cache->tdb)
1131 goto do_query;
1133 if ( (upper_name = SMB_STRDUP(name)) == NULL )
1134 return NT_STATUS_NO_MEMORY;
1135 strupper_m(upper_name);
1137 centry = wcache_fetch(cache, domain, "NSS/NA/%s", upper_name);
1139 SAFE_FREE( upper_name );
1141 if (!centry)
1142 goto do_query;
1144 status = centry->status;
1146 if (!NT_STATUS_IS_OK(status)) {
1147 centry_free(centry);
1148 return status;
1151 *alias = centry_string( centry, mem_ctx );
1153 centry_free(centry);
1155 DEBUG(10,("resolve_username_to_alias: [Cached] - mapped %s to %s\n",
1156 name, *alias ? *alias : "(none)"));
1158 return (*alias) ? NT_STATUS_OK : NT_STATUS_OBJECT_NAME_NOT_FOUND;
1160 do_query:
1162 /* If its not in cache and we are offline, then fail */
1164 if ( get_global_winbindd_state_offline() || !domain->online ) {
1165 DEBUG(8,("resolve_username_to_alias: rejecting query "
1166 "in offline mode\n"));
1167 return NT_STATUS_NOT_FOUND;
1170 status = nss_map_to_alias( mem_ctx, domain->name, name, alias );
1172 if ( NT_STATUS_IS_OK( status ) ) {
1173 wcache_save_username_alias(domain, status, name, *alias);
1176 if ( NT_STATUS_EQUAL( status, NT_STATUS_NONE_MAPPED ) ) {
1177 wcache_save_username_alias(domain, status, name, "(NULL)");
1180 DEBUG(5,("resolve_username_to_alias: backend query returned %s\n",
1181 nt_errstr(status)));
1183 if ( NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ) {
1184 set_domain_offline( domain );
1187 return status;
1190 /***************************************************************************
1191 ***************************************************************************/
1193 NTSTATUS resolve_alias_to_username( TALLOC_CTX *mem_ctx,
1194 struct winbindd_domain *domain,
1195 const char *alias, char **name )
1197 struct winbind_cache *cache = get_cache(domain);
1198 struct cache_entry *centry = NULL;
1199 NTSTATUS status;
1200 char *upper_name;
1202 if ( domain->internal )
1203 return NT_STATUS_NOT_SUPPORTED;
1205 if (!cache->tdb)
1206 goto do_query;
1208 if ( (upper_name = SMB_STRDUP(alias)) == NULL )
1209 return NT_STATUS_NO_MEMORY;
1210 strupper_m(upper_name);
1212 centry = wcache_fetch(cache, domain, "NSS/AN/%s", upper_name);
1214 SAFE_FREE( upper_name );
1216 if (!centry)
1217 goto do_query;
1219 status = centry->status;
1221 if (!NT_STATUS_IS_OK(status)) {
1222 centry_free(centry);
1223 return status;
1226 *name = centry_string( centry, mem_ctx );
1228 centry_free(centry);
1230 DEBUG(10,("resolve_alias_to_username: [Cached] - mapped %s to %s\n",
1231 alias, *name ? *name : "(none)"));
1233 return (*name) ? NT_STATUS_OK : NT_STATUS_OBJECT_NAME_NOT_FOUND;
1235 do_query:
1237 /* If its not in cache and we are offline, then fail */
1239 if ( get_global_winbindd_state_offline() || !domain->online ) {
1240 DEBUG(8,("resolve_alias_to_username: rejecting query "
1241 "in offline mode\n"));
1242 return NT_STATUS_NOT_FOUND;
1245 /* an alias cannot contain a domain prefix or '@' */
1247 if (strchr(alias, '\\') || strchr(alias, '@')) {
1248 DEBUG(10,("resolve_alias_to_username: skipping fully "
1249 "qualified name %s\n", alias));
1250 return NT_STATUS_OBJECT_NAME_INVALID;
1253 status = nss_map_from_alias( mem_ctx, domain->name, alias, name );
1255 if ( NT_STATUS_IS_OK( status ) ) {
1256 wcache_save_alias_username( domain, status, alias, *name );
1259 if (NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) {
1260 wcache_save_alias_username(domain, status, alias, "(NULL)");
1263 DEBUG(5,("resolve_alias_to_username: backend query returned %s\n",
1264 nt_errstr(status)));
1266 if ( NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ) {
1267 set_domain_offline( domain );
1270 return status;
1273 NTSTATUS wcache_cached_creds_exist(struct winbindd_domain *domain, const struct dom_sid *sid)
1275 struct winbind_cache *cache = get_cache(domain);
1276 TDB_DATA data;
1277 fstring key_str, tmp;
1278 uint32 rid;
1280 if (!cache->tdb) {
1281 return NT_STATUS_INTERNAL_DB_ERROR;
1284 if (is_null_sid(sid)) {
1285 return NT_STATUS_INVALID_SID;
1288 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1289 return NT_STATUS_INVALID_SID;
1292 fstr_sprintf(key_str, "CRED/%s", sid_to_fstring(tmp, sid));
1294 data = tdb_fetch(cache->tdb, string_tdb_data(key_str));
1295 if (!data.dptr) {
1296 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1299 SAFE_FREE(data.dptr);
1300 return NT_STATUS_OK;
1303 /* Lookup creds for a SID - copes with old (unsalted) creds as well
1304 as new salted ones. */
1306 NTSTATUS wcache_get_creds(struct winbindd_domain *domain,
1307 TALLOC_CTX *mem_ctx,
1308 const struct dom_sid *sid,
1309 const uint8 **cached_nt_pass,
1310 const uint8 **cached_salt)
1312 struct winbind_cache *cache = get_cache(domain);
1313 struct cache_entry *centry = NULL;
1314 NTSTATUS status;
1315 time_t t;
1316 uint32 rid;
1317 fstring tmp;
1319 if (!cache->tdb) {
1320 return NT_STATUS_INTERNAL_DB_ERROR;
1323 if (is_null_sid(sid)) {
1324 return NT_STATUS_INVALID_SID;
1327 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1328 return NT_STATUS_INVALID_SID;
1331 /* Try and get a salted cred first. If we can't
1332 fall back to an unsalted cred. */
1334 centry = wcache_fetch(cache, domain, "CRED/%s",
1335 sid_to_fstring(tmp, sid));
1336 if (!centry) {
1337 DEBUG(10,("wcache_get_creds: entry for [CRED/%s] not found\n",
1338 sid_string_dbg(sid)));
1339 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1342 t = centry_time(centry);
1344 /* In the salted case this isn't actually the nt_hash itself,
1345 but the MD5 of the salt + nt_hash. Let the caller
1346 sort this out. It can tell as we only return the cached_salt
1347 if we are returning a salted cred. */
1349 *cached_nt_pass = (const uint8 *)centry_hash16(centry, mem_ctx);
1350 if (*cached_nt_pass == NULL) {
1351 fstring sidstr;
1353 sid_to_fstring(sidstr, sid);
1355 /* Bad (old) cred cache. Delete and pretend we
1356 don't have it. */
1357 DEBUG(0,("wcache_get_creds: bad entry for [CRED/%s] - deleting\n",
1358 sidstr));
1359 wcache_delete("CRED/%s", sidstr);
1360 centry_free(centry);
1361 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1364 /* We only have 17 bytes more data in the salted cred case. */
1365 if (centry->len - centry->ofs == 17) {
1366 *cached_salt = (const uint8 *)centry_hash16(centry, mem_ctx);
1367 } else {
1368 *cached_salt = NULL;
1371 dump_data_pw("cached_nt_pass", *cached_nt_pass, NT_HASH_LEN);
1372 if (*cached_salt) {
1373 dump_data_pw("cached_salt", *cached_salt, NT_HASH_LEN);
1376 status = centry->status;
1378 DEBUG(10,("wcache_get_creds: [Cached] - cached creds for user %s status: %s\n",
1379 sid_string_dbg(sid), nt_errstr(status) ));
1381 centry_free(centry);
1382 return status;
1385 /* Store creds for a SID - only writes out new salted ones. */
1387 NTSTATUS wcache_save_creds(struct winbindd_domain *domain,
1388 const struct dom_sid *sid,
1389 const uint8 nt_pass[NT_HASH_LEN])
1391 struct cache_entry *centry;
1392 fstring sid_string;
1393 uint32 rid;
1394 uint8 cred_salt[NT_HASH_LEN];
1395 uint8 salted_hash[NT_HASH_LEN];
1397 if (is_null_sid(sid)) {
1398 return NT_STATUS_INVALID_SID;
1401 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1402 return NT_STATUS_INVALID_SID;
1405 centry = centry_start(domain, NT_STATUS_OK);
1406 if (!centry) {
1407 return NT_STATUS_INTERNAL_DB_ERROR;
1410 dump_data_pw("nt_pass", nt_pass, NT_HASH_LEN);
1412 centry_put_time(centry, time(NULL));
1414 /* Create a salt and then salt the hash. */
1415 generate_random_buffer(cred_salt, NT_HASH_LEN);
1416 E_md5hash(cred_salt, nt_pass, salted_hash);
1418 centry_put_hash16(centry, salted_hash);
1419 centry_put_hash16(centry, cred_salt);
1420 centry_end(centry, "CRED/%s", sid_to_fstring(sid_string, sid));
1422 DEBUG(10,("wcache_save_creds: %s\n", sid_string));
1424 centry_free(centry);
1426 return NT_STATUS_OK;
1430 /* Query display info. This is the basic user list fn */
1431 static NTSTATUS query_user_list(struct winbindd_domain *domain,
1432 TALLOC_CTX *mem_ctx,
1433 uint32 *num_entries,
1434 struct wbint_userinfo **info)
1436 struct winbind_cache *cache = get_cache(domain);
1437 struct cache_entry *centry = NULL;
1438 NTSTATUS status;
1439 unsigned int i, retry;
1440 bool old_status = domain->online;
1442 if (!cache->tdb)
1443 goto do_query;
1445 centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
1446 if (!centry)
1447 goto do_query;
1449 do_fetch_cache:
1450 *num_entries = centry_uint32(centry);
1452 if (*num_entries == 0)
1453 goto do_cached;
1455 (*info) = TALLOC_ARRAY(mem_ctx, struct wbint_userinfo, *num_entries);
1456 if (! (*info)) {
1457 smb_panic_fn("query_user_list out of memory");
1459 for (i=0; i<(*num_entries); i++) {
1460 (*info)[i].acct_name = centry_string(centry, mem_ctx);
1461 (*info)[i].full_name = centry_string(centry, mem_ctx);
1462 (*info)[i].homedir = centry_string(centry, mem_ctx);
1463 (*info)[i].shell = centry_string(centry, mem_ctx);
1464 centry_sid(centry, &(*info)[i].user_sid);
1465 centry_sid(centry, &(*info)[i].group_sid);
1468 do_cached:
1469 status = centry->status;
1471 DEBUG(10,("query_user_list: [Cached] - cached list for domain %s status: %s\n",
1472 domain->name, nt_errstr(status) ));
1474 centry_free(centry);
1475 return status;
1477 do_query:
1478 *num_entries = 0;
1479 *info = NULL;
1481 /* Return status value returned by seq number check */
1483 if (!NT_STATUS_IS_OK(domain->last_status))
1484 return domain->last_status;
1486 /* Put the query_user_list() in a retry loop. There appears to be
1487 * some bug either with Windows 2000 or Samba's handling of large
1488 * rpc replies. This manifests itself as sudden disconnection
1489 * at a random point in the enumeration of a large (60k) user list.
1490 * The retry loop simply tries the operation again. )-: It's not
1491 * pretty but an acceptable workaround until we work out what the
1492 * real problem is. */
1494 retry = 0;
1495 do {
1497 DEBUG(10,("query_user_list: [Cached] - doing backend query for list for domain %s\n",
1498 domain->name ));
1500 status = domain->backend->query_user_list(domain, mem_ctx, num_entries, info);
1501 if (!NT_STATUS_IS_OK(status)) {
1502 DEBUG(3, ("query_user_list: returned 0x%08x, "
1503 "retrying\n", NT_STATUS_V(status)));
1505 if (NT_STATUS_EQUAL(status, NT_STATUS_UNSUCCESSFUL)) {
1506 DEBUG(3, ("query_user_list: flushing "
1507 "connection cache\n"));
1508 invalidate_cm_connection(&domain->conn);
1510 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1511 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1512 if (!domain->internal && old_status) {
1513 set_domain_offline(domain);
1515 /* store partial response. */
1516 if (*num_entries > 0) {
1518 * humm, what about the status used for cache?
1519 * Should it be NT_STATUS_OK?
1521 break;
1524 * domain is offline now, and there is no user entries,
1525 * try to fetch from cache again.
1527 if (cache->tdb && !domain->online && !domain->internal && old_status) {
1528 centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
1529 /* partial response... */
1530 if (!centry) {
1531 goto skip_save;
1532 } else {
1533 goto do_fetch_cache;
1535 } else {
1536 goto skip_save;
1540 } while (NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_UNSUCCESSFUL) &&
1541 (retry++ < 5));
1543 /* and save it */
1544 refresh_sequence_number(domain, false);
1545 if (!NT_STATUS_IS_OK(status)) {
1546 return status;
1548 centry = centry_start(domain, status);
1549 if (!centry)
1550 goto skip_save;
1551 centry_put_uint32(centry, *num_entries);
1552 for (i=0; i<(*num_entries); i++) {
1553 centry_put_string(centry, (*info)[i].acct_name);
1554 centry_put_string(centry, (*info)[i].full_name);
1555 centry_put_string(centry, (*info)[i].homedir);
1556 centry_put_string(centry, (*info)[i].shell);
1557 centry_put_sid(centry, &(*info)[i].user_sid);
1558 centry_put_sid(centry, &(*info)[i].group_sid);
1559 if (domain->backend && domain->backend->consistent) {
1560 /* when the backend is consistent we can pre-prime some mappings */
1561 wcache_save_name_to_sid(domain, NT_STATUS_OK,
1562 domain->name,
1563 (*info)[i].acct_name,
1564 &(*info)[i].user_sid,
1565 SID_NAME_USER);
1566 wcache_save_sid_to_name(domain, NT_STATUS_OK,
1567 &(*info)[i].user_sid,
1568 domain->name,
1569 (*info)[i].acct_name,
1570 SID_NAME_USER);
1571 wcache_save_user(domain, NT_STATUS_OK, &(*info)[i]);
1574 centry_end(centry, "UL/%s", domain->name);
1575 centry_free(centry);
1577 skip_save:
1578 return status;
1581 /* list all domain groups */
1582 static NTSTATUS enum_dom_groups(struct winbindd_domain *domain,
1583 TALLOC_CTX *mem_ctx,
1584 uint32 *num_entries,
1585 struct wb_acct_info **info)
1587 struct winbind_cache *cache = get_cache(domain);
1588 struct cache_entry *centry = NULL;
1589 NTSTATUS status;
1590 unsigned int i;
1591 bool old_status;
1593 old_status = domain->online;
1594 if (!cache->tdb)
1595 goto do_query;
1597 centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
1598 if (!centry)
1599 goto do_query;
1601 do_fetch_cache:
1602 *num_entries = centry_uint32(centry);
1604 if (*num_entries == 0)
1605 goto do_cached;
1607 (*info) = TALLOC_ARRAY(mem_ctx, struct wb_acct_info, *num_entries);
1608 if (! (*info)) {
1609 smb_panic_fn("enum_dom_groups out of memory");
1611 for (i=0; i<(*num_entries); i++) {
1612 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1613 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1614 (*info)[i].rid = centry_uint32(centry);
1617 do_cached:
1618 status = centry->status;
1620 DEBUG(10,("enum_dom_groups: [Cached] - cached list for domain %s status: %s\n",
1621 domain->name, nt_errstr(status) ));
1623 centry_free(centry);
1624 return status;
1626 do_query:
1627 *num_entries = 0;
1628 *info = NULL;
1630 /* Return status value returned by seq number check */
1632 if (!NT_STATUS_IS_OK(domain->last_status))
1633 return domain->last_status;
1635 DEBUG(10,("enum_dom_groups: [Cached] - doing backend query for list for domain %s\n",
1636 domain->name ));
1638 status = domain->backend->enum_dom_groups(domain, mem_ctx, num_entries, info);
1640 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1641 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1642 if (!domain->internal && old_status) {
1643 set_domain_offline(domain);
1645 if (cache->tdb &&
1646 !domain->online &&
1647 !domain->internal &&
1648 old_status) {
1649 centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
1650 if (centry) {
1651 goto do_fetch_cache;
1655 /* and save it */
1656 refresh_sequence_number(domain, false);
1657 if (!NT_STATUS_IS_OK(status)) {
1658 return status;
1660 centry = centry_start(domain, status);
1661 if (!centry)
1662 goto skip_save;
1663 centry_put_uint32(centry, *num_entries);
1664 for (i=0; i<(*num_entries); i++) {
1665 centry_put_string(centry, (*info)[i].acct_name);
1666 centry_put_string(centry, (*info)[i].acct_desc);
1667 centry_put_uint32(centry, (*info)[i].rid);
1669 centry_end(centry, "GL/%s/domain", domain->name);
1670 centry_free(centry);
1672 skip_save:
1673 return status;
1676 /* list all domain groups */
1677 static NTSTATUS enum_local_groups(struct winbindd_domain *domain,
1678 TALLOC_CTX *mem_ctx,
1679 uint32 *num_entries,
1680 struct wb_acct_info **info)
1682 struct winbind_cache *cache = get_cache(domain);
1683 struct cache_entry *centry = NULL;
1684 NTSTATUS status;
1685 unsigned int i;
1686 bool old_status;
1688 old_status = domain->online;
1689 if (!cache->tdb)
1690 goto do_query;
1692 centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
1693 if (!centry)
1694 goto do_query;
1696 do_fetch_cache:
1697 *num_entries = centry_uint32(centry);
1699 if (*num_entries == 0)
1700 goto do_cached;
1702 (*info) = TALLOC_ARRAY(mem_ctx, struct wb_acct_info, *num_entries);
1703 if (! (*info)) {
1704 smb_panic_fn("enum_dom_groups out of memory");
1706 for (i=0; i<(*num_entries); i++) {
1707 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1708 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1709 (*info)[i].rid = centry_uint32(centry);
1712 do_cached:
1714 /* If we are returning cached data and the domain controller
1715 is down then we don't know whether the data is up to date
1716 or not. Return NT_STATUS_MORE_PROCESSING_REQUIRED to
1717 indicate this. */
1719 if (wcache_server_down(domain)) {
1720 DEBUG(10, ("enum_local_groups: returning cached user list and server was down\n"));
1721 status = NT_STATUS_MORE_PROCESSING_REQUIRED;
1722 } else
1723 status = centry->status;
1725 DEBUG(10,("enum_local_groups: [Cached] - cached list for domain %s status: %s\n",
1726 domain->name, nt_errstr(status) ));
1728 centry_free(centry);
1729 return status;
1731 do_query:
1732 *num_entries = 0;
1733 *info = NULL;
1735 /* Return status value returned by seq number check */
1737 if (!NT_STATUS_IS_OK(domain->last_status))
1738 return domain->last_status;
1740 DEBUG(10,("enum_local_groups: [Cached] - doing backend query for list for domain %s\n",
1741 domain->name ));
1743 status = domain->backend->enum_local_groups(domain, mem_ctx, num_entries, info);
1745 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1746 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1747 if (!domain->internal && old_status) {
1748 set_domain_offline(domain);
1750 if (cache->tdb &&
1751 !domain->internal &&
1752 !domain->online &&
1753 old_status) {
1754 centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
1755 if (centry) {
1756 goto do_fetch_cache;
1760 /* and save it */
1761 refresh_sequence_number(domain, false);
1762 if (!NT_STATUS_IS_OK(status)) {
1763 return status;
1765 centry = centry_start(domain, status);
1766 if (!centry)
1767 goto skip_save;
1768 centry_put_uint32(centry, *num_entries);
1769 for (i=0; i<(*num_entries); i++) {
1770 centry_put_string(centry, (*info)[i].acct_name);
1771 centry_put_string(centry, (*info)[i].acct_desc);
1772 centry_put_uint32(centry, (*info)[i].rid);
1774 centry_end(centry, "GL/%s/local", domain->name);
1775 centry_free(centry);
1777 skip_save:
1778 return status;
1781 NTSTATUS wcache_name_to_sid(struct winbindd_domain *domain,
1782 const char *domain_name,
1783 const char *name,
1784 struct dom_sid *sid,
1785 enum lsa_SidType *type)
1787 struct winbind_cache *cache = get_cache(domain);
1788 struct cache_entry *centry;
1789 NTSTATUS status;
1790 char *uname;
1792 if (cache->tdb == NULL) {
1793 return NT_STATUS_NOT_FOUND;
1796 uname = talloc_strdup_upper(talloc_tos(), name);
1797 if (uname == NULL) {
1798 return NT_STATUS_NO_MEMORY;
1801 if (domain_name[0] == '\0') {
1802 domain_name = domain->name;
1805 centry = wcache_fetch(cache, domain, "NS/%s/%s", domain_name, uname);
1806 TALLOC_FREE(uname);
1807 if (centry == NULL) {
1808 return NT_STATUS_NOT_FOUND;
1811 status = centry->status;
1812 if (NT_STATUS_IS_OK(status)) {
1813 *type = (enum lsa_SidType)centry_uint32(centry);
1814 centry_sid(centry, sid);
1817 DEBUG(10,("name_to_sid: [Cached] - cached name for domain %s status: "
1818 "%s\n", domain->name, nt_errstr(status) ));
1820 centry_free(centry);
1821 return status;
1824 /* convert a single name to a sid in a domain */
1825 static NTSTATUS name_to_sid(struct winbindd_domain *domain,
1826 TALLOC_CTX *mem_ctx,
1827 const char *domain_name,
1828 const char *name,
1829 uint32_t flags,
1830 struct dom_sid *sid,
1831 enum lsa_SidType *type)
1833 NTSTATUS status;
1834 bool old_status;
1836 old_status = domain->online;
1838 status = wcache_name_to_sid(domain, domain_name, name, sid, type);
1839 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
1840 return status;
1843 ZERO_STRUCTP(sid);
1845 /* If the seq number check indicated that there is a problem
1846 * with this DC, then return that status... except for
1847 * access_denied. This is special because the dc may be in
1848 * "restrict anonymous = 1" mode, in which case it will deny
1849 * most unauthenticated operations, but *will* allow the LSA
1850 * name-to-sid that we try as a fallback. */
1852 if (!(NT_STATUS_IS_OK(domain->last_status)
1853 || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1854 return domain->last_status;
1856 DEBUG(10,("name_to_sid: [Cached] - doing backend query for name for domain %s\n",
1857 domain->name ));
1859 status = domain->backend->name_to_sid(domain, mem_ctx, domain_name,
1860 name, flags, sid, type);
1862 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1863 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1864 if (!domain->internal && old_status) {
1865 set_domain_offline(domain);
1867 if (!domain->internal &&
1868 !domain->online &&
1869 old_status) {
1870 NTSTATUS cache_status;
1871 cache_status = wcache_name_to_sid(domain, domain_name, name, sid, type);
1872 return cache_status;
1875 /* and save it */
1876 refresh_sequence_number(domain, false);
1878 if (domain->online &&
1879 (NT_STATUS_IS_OK(status) || NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED))) {
1880 wcache_save_name_to_sid(domain, status, domain_name, name, sid, *type);
1882 /* Only save the reverse mapping if this was not a UPN */
1883 if (!strchr(name, '@')) {
1884 strupper_m(CONST_DISCARD(char *,domain_name));
1885 strlower_m(CONST_DISCARD(char *,name));
1886 wcache_save_sid_to_name(domain, status, sid, domain_name, name, *type);
1890 return status;
1893 NTSTATUS wcache_sid_to_name(struct winbindd_domain *domain,
1894 const struct dom_sid *sid,
1895 TALLOC_CTX *mem_ctx,
1896 char **domain_name,
1897 char **name,
1898 enum lsa_SidType *type)
1900 struct winbind_cache *cache = get_cache(domain);
1901 struct cache_entry *centry;
1902 char *sid_string;
1903 NTSTATUS status;
1905 if (cache->tdb == NULL) {
1906 return NT_STATUS_NOT_FOUND;
1909 sid_string = sid_string_tos(sid);
1910 if (sid_string == NULL) {
1911 return NT_STATUS_NO_MEMORY;
1914 centry = wcache_fetch(cache, domain, "SN/%s", sid_string);
1915 TALLOC_FREE(sid_string);
1916 if (centry == NULL) {
1917 return NT_STATUS_NOT_FOUND;
1920 if (NT_STATUS_IS_OK(centry->status)) {
1921 *type = (enum lsa_SidType)centry_uint32(centry);
1922 *domain_name = centry_string(centry, mem_ctx);
1923 *name = centry_string(centry, mem_ctx);
1926 status = centry->status;
1927 centry_free(centry);
1929 DEBUG(10,("sid_to_name: [Cached] - cached name for domain %s status: "
1930 "%s\n", domain->name, nt_errstr(status) ));
1932 return status;
1935 /* convert a sid to a user or group name. The sid is guaranteed to be in the domain
1936 given */
1937 static NTSTATUS sid_to_name(struct winbindd_domain *domain,
1938 TALLOC_CTX *mem_ctx,
1939 const struct dom_sid *sid,
1940 char **domain_name,
1941 char **name,
1942 enum lsa_SidType *type)
1944 NTSTATUS status;
1945 bool old_status;
1947 old_status = domain->online;
1948 status = wcache_sid_to_name(domain, sid, mem_ctx, domain_name, name,
1949 type);
1950 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
1951 return status;
1954 *name = NULL;
1955 *domain_name = NULL;
1957 /* If the seq number check indicated that there is a problem
1958 * with this DC, then return that status... except for
1959 * access_denied. This is special because the dc may be in
1960 * "restrict anonymous = 1" mode, in which case it will deny
1961 * most unauthenticated operations, but *will* allow the LSA
1962 * sid-to-name that we try as a fallback. */
1964 if (!(NT_STATUS_IS_OK(domain->last_status)
1965 || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1966 return domain->last_status;
1968 DEBUG(10,("sid_to_name: [Cached] - doing backend query for name for domain %s\n",
1969 domain->name ));
1971 status = domain->backend->sid_to_name(domain, mem_ctx, sid, domain_name, name, type);
1973 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1974 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1975 if (!domain->internal && old_status) {
1976 set_domain_offline(domain);
1978 if (!domain->internal &&
1979 !domain->online &&
1980 old_status) {
1981 NTSTATUS cache_status;
1982 cache_status = wcache_sid_to_name(domain, sid, mem_ctx,
1983 domain_name, name, type);
1984 return cache_status;
1987 /* and save it */
1988 refresh_sequence_number(domain, false);
1989 if (!NT_STATUS_IS_OK(status)) {
1990 return status;
1992 wcache_save_sid_to_name(domain, status, sid, *domain_name, *name, *type);
1994 /* We can't save the name to sid mapping here, as with sid history a
1995 * later name2sid would give the wrong sid. */
1997 return status;
2000 static NTSTATUS rids_to_names(struct winbindd_domain *domain,
2001 TALLOC_CTX *mem_ctx,
2002 const struct dom_sid *domain_sid,
2003 uint32 *rids,
2004 size_t num_rids,
2005 char **domain_name,
2006 char ***names,
2007 enum lsa_SidType **types)
2009 struct winbind_cache *cache = get_cache(domain);
2010 size_t i;
2011 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
2012 bool have_mapped;
2013 bool have_unmapped;
2014 bool old_status;
2016 old_status = domain->online;
2017 *domain_name = NULL;
2018 *names = NULL;
2019 *types = NULL;
2021 if (!cache->tdb) {
2022 goto do_query;
2025 if (num_rids == 0) {
2026 return NT_STATUS_OK;
2029 *names = TALLOC_ARRAY(mem_ctx, char *, num_rids);
2030 *types = TALLOC_ARRAY(mem_ctx, enum lsa_SidType, num_rids);
2032 if ((*names == NULL) || (*types == NULL)) {
2033 result = NT_STATUS_NO_MEMORY;
2034 goto error;
2037 have_mapped = have_unmapped = false;
2039 for (i=0; i<num_rids; i++) {
2040 struct dom_sid sid;
2041 struct cache_entry *centry;
2042 fstring tmp;
2044 if (!sid_compose(&sid, domain_sid, rids[i])) {
2045 result = NT_STATUS_INTERNAL_ERROR;
2046 goto error;
2049 centry = wcache_fetch(cache, domain, "SN/%s",
2050 sid_to_fstring(tmp, &sid));
2051 if (!centry) {
2052 goto do_query;
2055 (*types)[i] = SID_NAME_UNKNOWN;
2056 (*names)[i] = talloc_strdup(*names, "");
2058 if (NT_STATUS_IS_OK(centry->status)) {
2059 char *dom;
2060 have_mapped = true;
2061 (*types)[i] = (enum lsa_SidType)centry_uint32(centry);
2063 dom = centry_string(centry, mem_ctx);
2064 if (*domain_name == NULL) {
2065 *domain_name = dom;
2066 } else {
2067 talloc_free(dom);
2070 (*names)[i] = centry_string(centry, *names);
2072 } else if (NT_STATUS_EQUAL(centry->status, NT_STATUS_NONE_MAPPED)
2073 || NT_STATUS_EQUAL(centry->status, STATUS_SOME_UNMAPPED)) {
2074 have_unmapped = true;
2076 } else {
2077 /* something's definitely wrong */
2078 result = centry->status;
2079 goto error;
2082 centry_free(centry);
2085 if (!have_mapped) {
2086 return NT_STATUS_NONE_MAPPED;
2088 if (!have_unmapped) {
2089 return NT_STATUS_OK;
2091 return STATUS_SOME_UNMAPPED;
2093 do_query:
2095 TALLOC_FREE(*names);
2096 TALLOC_FREE(*types);
2098 result = domain->backend->rids_to_names(domain, mem_ctx, domain_sid,
2099 rids, num_rids, domain_name,
2100 names, types);
2102 if (NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
2103 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2104 if (!domain->internal && old_status) {
2105 set_domain_offline(domain);
2107 if (cache->tdb &&
2108 !domain->internal &&
2109 !domain->online &&
2110 old_status) {
2111 have_mapped = have_unmapped = false;
2113 for (i=0; i<num_rids; i++) {
2114 struct dom_sid sid;
2115 struct cache_entry *centry;
2116 fstring tmp;
2118 if (!sid_compose(&sid, domain_sid, rids[i])) {
2119 result = NT_STATUS_INTERNAL_ERROR;
2120 goto error;
2123 centry = wcache_fetch(cache, domain, "SN/%s",
2124 sid_to_fstring(tmp, &sid));
2125 if (!centry) {
2126 (*types)[i] = SID_NAME_UNKNOWN;
2127 (*names)[i] = talloc_strdup(*names, "");
2128 continue;
2131 (*types)[i] = SID_NAME_UNKNOWN;
2132 (*names)[i] = talloc_strdup(*names, "");
2134 if (NT_STATUS_IS_OK(centry->status)) {
2135 char *dom;
2136 have_mapped = true;
2137 (*types)[i] = (enum lsa_SidType)centry_uint32(centry);
2139 dom = centry_string(centry, mem_ctx);
2140 if (*domain_name == NULL) {
2141 *domain_name = dom;
2142 } else {
2143 talloc_free(dom);
2146 (*names)[i] = centry_string(centry, *names);
2148 } else if (NT_STATUS_EQUAL(centry->status, NT_STATUS_NONE_MAPPED)) {
2149 have_unmapped = true;
2151 } else {
2152 /* something's definitely wrong */
2153 result = centry->status;
2154 centry_free(centry);
2155 goto error;
2158 centry_free(centry);
2161 if (!have_mapped) {
2162 return NT_STATUS_NONE_MAPPED;
2164 if (!have_unmapped) {
2165 return NT_STATUS_OK;
2167 return STATUS_SOME_UNMAPPED;
2171 None of the queried rids has been found so save all negative entries
2173 if (NT_STATUS_EQUAL(result, NT_STATUS_NONE_MAPPED)) {
2174 for (i = 0; i < num_rids; i++) {
2175 struct dom_sid sid;
2176 const char *name = "";
2177 const enum lsa_SidType type = SID_NAME_UNKNOWN;
2178 NTSTATUS status = NT_STATUS_NONE_MAPPED;
2180 if (!sid_compose(&sid, domain_sid, rids[i])) {
2181 return NT_STATUS_INTERNAL_ERROR;
2184 wcache_save_sid_to_name(domain, status, &sid, *domain_name,
2185 name, type);
2188 return result;
2192 Some or all of the queried rids have been found.
2194 if (!NT_STATUS_IS_OK(result) &&
2195 !NT_STATUS_EQUAL(result, STATUS_SOME_UNMAPPED)) {
2196 return result;
2199 refresh_sequence_number(domain, false);
2201 for (i=0; i<num_rids; i++) {
2202 struct dom_sid sid;
2203 NTSTATUS status;
2205 if (!sid_compose(&sid, domain_sid, rids[i])) {
2206 result = NT_STATUS_INTERNAL_ERROR;
2207 goto error;
2210 status = (*types)[i] == SID_NAME_UNKNOWN ?
2211 NT_STATUS_NONE_MAPPED : NT_STATUS_OK;
2213 wcache_save_sid_to_name(domain, status, &sid, *domain_name,
2214 (*names)[i], (*types)[i]);
2217 return result;
2219 error:
2220 TALLOC_FREE(*names);
2221 TALLOC_FREE(*types);
2222 return result;
2225 NTSTATUS wcache_query_user(struct winbindd_domain *domain,
2226 TALLOC_CTX *mem_ctx,
2227 const struct dom_sid *user_sid,
2228 struct wbint_userinfo *info)
2230 struct winbind_cache *cache = get_cache(domain);
2231 struct cache_entry *centry = NULL;
2232 NTSTATUS status;
2233 char *sid_string;
2235 if (cache->tdb == NULL) {
2236 return NT_STATUS_NOT_FOUND;
2239 sid_string = sid_string_tos(user_sid);
2240 if (sid_string == NULL) {
2241 return NT_STATUS_NO_MEMORY;
2244 centry = wcache_fetch(cache, domain, "U/%s", sid_string);
2245 TALLOC_FREE(sid_string);
2246 if (centry == NULL) {
2247 return NT_STATUS_NOT_FOUND;
2251 * If we have an access denied cache entry and a cached info3
2252 * in the samlogon cache then do a query. This will force the
2253 * rpc back end to return the info3 data.
2256 if (NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED) &&
2257 netsamlogon_cache_have(user_sid)) {
2258 DEBUG(10, ("query_user: cached access denied and have cached "
2259 "info3\n"));
2260 domain->last_status = NT_STATUS_OK;
2261 centry_free(centry);
2262 return NT_STATUS_NOT_FOUND;
2265 /* if status is not ok then this is a negative hit
2266 and the rest of the data doesn't matter */
2267 status = centry->status;
2268 if (NT_STATUS_IS_OK(status)) {
2269 info->acct_name = centry_string(centry, mem_ctx);
2270 info->full_name = centry_string(centry, mem_ctx);
2271 info->homedir = centry_string(centry, mem_ctx);
2272 info->shell = centry_string(centry, mem_ctx);
2273 info->primary_gid = centry_uint32(centry);
2274 centry_sid(centry, &info->user_sid);
2275 centry_sid(centry, &info->group_sid);
2278 DEBUG(10,("query_user: [Cached] - cached info for domain %s status: "
2279 "%s\n", domain->name, nt_errstr(status) ));
2281 centry_free(centry);
2282 return status;
2285 /* Lookup user information from a rid */
2286 static NTSTATUS query_user(struct winbindd_domain *domain,
2287 TALLOC_CTX *mem_ctx,
2288 const struct dom_sid *user_sid,
2289 struct wbint_userinfo *info)
2291 NTSTATUS status;
2292 bool old_status;
2294 old_status = domain->online;
2295 status = wcache_query_user(domain, mem_ctx, user_sid, info);
2296 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2297 return status;
2300 ZERO_STRUCTP(info);
2302 /* Return status value returned by seq number check */
2304 if (!NT_STATUS_IS_OK(domain->last_status))
2305 return domain->last_status;
2307 DEBUG(10,("query_user: [Cached] - doing backend query for info for domain %s\n",
2308 domain->name ));
2310 status = domain->backend->query_user(domain, mem_ctx, user_sid, info);
2312 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2313 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2314 if (!domain->internal && old_status) {
2315 set_domain_offline(domain);
2317 if (!domain->internal &&
2318 !domain->online &&
2319 old_status) {
2320 NTSTATUS cache_status;
2321 cache_status = wcache_query_user(domain, mem_ctx, user_sid, info);
2322 return cache_status;
2325 /* and save it */
2326 refresh_sequence_number(domain, false);
2327 if (!NT_STATUS_IS_OK(status)) {
2328 return status;
2330 wcache_save_user(domain, status, info);
2332 return status;
2335 NTSTATUS wcache_lookup_usergroups(struct winbindd_domain *domain,
2336 TALLOC_CTX *mem_ctx,
2337 const struct dom_sid *user_sid,
2338 uint32_t *pnum_sids,
2339 struct dom_sid **psids)
2341 struct winbind_cache *cache = get_cache(domain);
2342 struct cache_entry *centry = NULL;
2343 NTSTATUS status;
2344 uint32_t i, num_sids;
2345 struct dom_sid *sids;
2346 fstring sid_string;
2348 if (cache->tdb == NULL) {
2349 return NT_STATUS_NOT_FOUND;
2352 centry = wcache_fetch(cache, domain, "UG/%s",
2353 sid_to_fstring(sid_string, user_sid));
2354 if (centry == NULL) {
2355 return NT_STATUS_NOT_FOUND;
2358 /* If we have an access denied cache entry and a cached info3 in the
2359 samlogon cache then do a query. This will force the rpc back end
2360 to return the info3 data. */
2362 if (NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)
2363 && netsamlogon_cache_have(user_sid)) {
2364 DEBUG(10, ("lookup_usergroups: cached access denied and have "
2365 "cached info3\n"));
2366 domain->last_status = NT_STATUS_OK;
2367 centry_free(centry);
2368 return NT_STATUS_NOT_FOUND;
2371 num_sids = centry_uint32(centry);
2372 sids = talloc_array(mem_ctx, struct dom_sid, num_sids);
2373 if (sids == NULL) {
2374 centry_free(centry);
2375 return NT_STATUS_NO_MEMORY;
2378 for (i=0; i<num_sids; i++) {
2379 centry_sid(centry, &sids[i]);
2382 status = centry->status;
2384 DEBUG(10,("lookup_usergroups: [Cached] - cached info for domain %s "
2385 "status: %s\n", domain->name, nt_errstr(status)));
2387 centry_free(centry);
2389 *pnum_sids = num_sids;
2390 *psids = sids;
2391 return status;
2394 /* Lookup groups a user is a member of. */
2395 static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
2396 TALLOC_CTX *mem_ctx,
2397 const struct dom_sid *user_sid,
2398 uint32 *num_groups, struct dom_sid **user_gids)
2400 struct cache_entry *centry = NULL;
2401 NTSTATUS status;
2402 unsigned int i;
2403 fstring sid_string;
2404 bool old_status;
2406 old_status = domain->online;
2407 status = wcache_lookup_usergroups(domain, mem_ctx, user_sid,
2408 num_groups, user_gids);
2409 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2410 return status;
2413 (*num_groups) = 0;
2414 (*user_gids) = NULL;
2416 /* Return status value returned by seq number check */
2418 if (!NT_STATUS_IS_OK(domain->last_status))
2419 return domain->last_status;
2421 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info for domain %s\n",
2422 domain->name ));
2424 status = domain->backend->lookup_usergroups(domain, mem_ctx, user_sid, num_groups, user_gids);
2426 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2427 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2428 if (!domain->internal && old_status) {
2429 set_domain_offline(domain);
2431 if (!domain->internal &&
2432 !domain->online &&
2433 old_status) {
2434 NTSTATUS cache_status;
2435 cache_status = wcache_lookup_usergroups(domain, mem_ctx, user_sid,
2436 num_groups, user_gids);
2437 return cache_status;
2440 if ( NT_STATUS_EQUAL(status, NT_STATUS_SYNCHRONIZATION_REQUIRED) )
2441 goto skip_save;
2443 /* and save it */
2444 refresh_sequence_number(domain, false);
2445 if (!NT_STATUS_IS_OK(status)) {
2446 return status;
2448 centry = centry_start(domain, status);
2449 if (!centry)
2450 goto skip_save;
2452 centry_put_uint32(centry, *num_groups);
2453 for (i=0; i<(*num_groups); i++) {
2454 centry_put_sid(centry, &(*user_gids)[i]);
2457 centry_end(centry, "UG/%s", sid_to_fstring(sid_string, user_sid));
2458 centry_free(centry);
2460 skip_save:
2461 return status;
2464 static char *wcache_make_sidlist(TALLOC_CTX *mem_ctx, uint32_t num_sids,
2465 const struct dom_sid *sids)
2467 uint32_t i;
2468 char *sidlist;
2470 sidlist = talloc_strdup(mem_ctx, "");
2471 if (sidlist == NULL) {
2472 return NULL;
2474 for (i=0; i<num_sids; i++) {
2475 fstring tmp;
2476 sidlist = talloc_asprintf_append_buffer(
2477 sidlist, "/%s", sid_to_fstring(tmp, &sids[i]));
2478 if (sidlist == NULL) {
2479 return NULL;
2482 return sidlist;
2485 NTSTATUS wcache_lookup_useraliases(struct winbindd_domain *domain,
2486 TALLOC_CTX *mem_ctx, uint32_t num_sids,
2487 const struct dom_sid *sids,
2488 uint32_t *pnum_aliases, uint32_t **paliases)
2490 struct winbind_cache *cache = get_cache(domain);
2491 struct cache_entry *centry = NULL;
2492 uint32_t num_aliases;
2493 uint32_t *aliases;
2494 NTSTATUS status;
2495 char *sidlist;
2496 int i;
2498 if (cache->tdb == NULL) {
2499 return NT_STATUS_NOT_FOUND;
2502 if (num_sids == 0) {
2503 *pnum_aliases = 0;
2504 *paliases = NULL;
2505 return NT_STATUS_OK;
2508 /* We need to cache indexed by the whole list of SIDs, the aliases
2509 * resulting might come from any of the SIDs. */
2511 sidlist = wcache_make_sidlist(talloc_tos(), num_sids, sids);
2512 if (sidlist == NULL) {
2513 return NT_STATUS_NO_MEMORY;
2516 centry = wcache_fetch(cache, domain, "UA%s", sidlist);
2517 TALLOC_FREE(sidlist);
2518 if (centry == NULL) {
2519 return NT_STATUS_NOT_FOUND;
2522 num_aliases = centry_uint32(centry);
2523 aliases = talloc_array(mem_ctx, uint32_t, num_aliases);
2524 if (aliases == NULL) {
2525 centry_free(centry);
2526 return NT_STATUS_NO_MEMORY;
2529 for (i=0; i<num_aliases; i++) {
2530 aliases[i] = centry_uint32(centry);
2533 status = centry->status;
2535 DEBUG(10,("lookup_useraliases: [Cached] - cached info for domain: %s "
2536 "status %s\n", domain->name, nt_errstr(status)));
2538 centry_free(centry);
2540 *pnum_aliases = num_aliases;
2541 *paliases = aliases;
2543 return status;
2546 static NTSTATUS lookup_useraliases(struct winbindd_domain *domain,
2547 TALLOC_CTX *mem_ctx,
2548 uint32 num_sids, const struct dom_sid *sids,
2549 uint32 *num_aliases, uint32 **alias_rids)
2551 struct cache_entry *centry = NULL;
2552 NTSTATUS status;
2553 char *sidlist;
2554 int i;
2555 bool old_status;
2557 old_status = domain->online;
2558 status = wcache_lookup_useraliases(domain, mem_ctx, num_sids, sids,
2559 num_aliases, alias_rids);
2560 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2561 return status;
2564 (*num_aliases) = 0;
2565 (*alias_rids) = NULL;
2567 if (!NT_STATUS_IS_OK(domain->last_status))
2568 return domain->last_status;
2570 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info "
2571 "for domain %s\n", domain->name ));
2573 sidlist = wcache_make_sidlist(talloc_tos(), num_sids, sids);
2574 if (sidlist == NULL) {
2575 return NT_STATUS_NO_MEMORY;
2578 status = domain->backend->lookup_useraliases(domain, mem_ctx,
2579 num_sids, sids,
2580 num_aliases, alias_rids);
2582 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2583 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2584 if (!domain->internal && old_status) {
2585 set_domain_offline(domain);
2587 if (!domain->internal &&
2588 !domain->online &&
2589 old_status) {
2590 NTSTATUS cache_status;
2591 cache_status = wcache_lookup_useraliases(domain, mem_ctx, num_sids,
2592 sids, num_aliases, alias_rids);
2593 return cache_status;
2596 /* and save it */
2597 refresh_sequence_number(domain, false);
2598 if (!NT_STATUS_IS_OK(status)) {
2599 return status;
2601 centry = centry_start(domain, status);
2602 if (!centry)
2603 goto skip_save;
2604 centry_put_uint32(centry, *num_aliases);
2605 for (i=0; i<(*num_aliases); i++)
2606 centry_put_uint32(centry, (*alias_rids)[i]);
2607 centry_end(centry, "UA%s", sidlist);
2608 centry_free(centry);
2610 skip_save:
2611 return status;
2614 NTSTATUS wcache_lookup_groupmem(struct winbindd_domain *domain,
2615 TALLOC_CTX *mem_ctx,
2616 const struct dom_sid *group_sid,
2617 uint32_t *num_names,
2618 struct dom_sid **sid_mem, char ***names,
2619 uint32_t **name_types)
2621 struct winbind_cache *cache = get_cache(domain);
2622 struct cache_entry *centry = NULL;
2623 NTSTATUS status;
2624 unsigned int i;
2625 char *sid_string;
2627 if (cache->tdb == NULL) {
2628 return NT_STATUS_NOT_FOUND;
2631 sid_string = sid_string_tos(group_sid);
2632 if (sid_string == NULL) {
2633 return NT_STATUS_NO_MEMORY;
2636 centry = wcache_fetch(cache, domain, "GM/%s", sid_string);
2637 TALLOC_FREE(sid_string);
2638 if (centry == NULL) {
2639 return NT_STATUS_NOT_FOUND;
2642 *sid_mem = NULL;
2643 *names = NULL;
2644 *name_types = NULL;
2646 *num_names = centry_uint32(centry);
2647 if (*num_names == 0) {
2648 centry_free(centry);
2649 return NT_STATUS_OK;
2652 *sid_mem = talloc_array(mem_ctx, struct dom_sid, *num_names);
2653 *names = talloc_array(mem_ctx, char *, *num_names);
2654 *name_types = talloc_array(mem_ctx, uint32, *num_names);
2656 if ((*sid_mem == NULL) || (*names == NULL) || (*name_types == NULL)) {
2657 TALLOC_FREE(*sid_mem);
2658 TALLOC_FREE(*names);
2659 TALLOC_FREE(*name_types);
2660 centry_free(centry);
2661 return NT_STATUS_NO_MEMORY;
2664 for (i=0; i<(*num_names); i++) {
2665 centry_sid(centry, &(*sid_mem)[i]);
2666 (*names)[i] = centry_string(centry, mem_ctx);
2667 (*name_types)[i] = centry_uint32(centry);
2670 status = centry->status;
2672 DEBUG(10,("lookup_groupmem: [Cached] - cached info for domain %s "
2673 "status: %s\n", domain->name, nt_errstr(status)));
2675 centry_free(centry);
2676 return status;
2679 static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
2680 TALLOC_CTX *mem_ctx,
2681 const struct dom_sid *group_sid,
2682 enum lsa_SidType type,
2683 uint32 *num_names,
2684 struct dom_sid **sid_mem, char ***names,
2685 uint32 **name_types)
2687 struct cache_entry *centry = NULL;
2688 NTSTATUS status;
2689 unsigned int i;
2690 fstring sid_string;
2691 bool old_status;
2693 old_status = domain->online;
2694 status = wcache_lookup_groupmem(domain, mem_ctx, group_sid, num_names,
2695 sid_mem, names, name_types);
2696 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2697 return status;
2700 (*num_names) = 0;
2701 (*sid_mem) = NULL;
2702 (*names) = NULL;
2703 (*name_types) = NULL;
2705 /* Return status value returned by seq number check */
2707 if (!NT_STATUS_IS_OK(domain->last_status))
2708 return domain->last_status;
2710 DEBUG(10,("lookup_groupmem: [Cached] - doing backend query for info for domain %s\n",
2711 domain->name ));
2713 status = domain->backend->lookup_groupmem(domain, mem_ctx, group_sid,
2714 type, num_names,
2715 sid_mem, names, name_types);
2717 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2718 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2719 if (!domain->internal && old_status) {
2720 set_domain_offline(domain);
2722 if (!domain->internal &&
2723 !domain->online &&
2724 old_status) {
2725 NTSTATUS cache_status;
2726 cache_status = wcache_lookup_groupmem(domain, mem_ctx, group_sid,
2727 num_names, sid_mem, names,
2728 name_types);
2729 return cache_status;
2732 /* and save it */
2733 refresh_sequence_number(domain, false);
2734 if (!NT_STATUS_IS_OK(status)) {
2735 return status;
2737 centry = centry_start(domain, status);
2738 if (!centry)
2739 goto skip_save;
2740 centry_put_uint32(centry, *num_names);
2741 for (i=0; i<(*num_names); i++) {
2742 centry_put_sid(centry, &(*sid_mem)[i]);
2743 centry_put_string(centry, (*names)[i]);
2744 centry_put_uint32(centry, (*name_types)[i]);
2746 centry_end(centry, "GM/%s", sid_to_fstring(sid_string, group_sid));
2747 centry_free(centry);
2749 skip_save:
2750 return status;
2753 /* find the sequence number for a domain */
2754 static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
2756 refresh_sequence_number(domain, false);
2758 *seq = domain->sequence_number;
2760 return NT_STATUS_OK;
2763 /* enumerate trusted domains
2764 * (we need to have the list of trustdoms in the cache when we go offline) -
2765 * Guenther */
2766 static NTSTATUS trusted_domains(struct winbindd_domain *domain,
2767 TALLOC_CTX *mem_ctx,
2768 struct netr_DomainTrustList *trusts)
2770 NTSTATUS status;
2771 struct winbind_cache *cache;
2772 struct winbindd_tdc_domain *dom_list = NULL;
2773 size_t num_domains = 0;
2774 bool retval = false;
2775 int i;
2776 bool old_status;
2778 old_status = domain->online;
2779 trusts->count = 0;
2780 trusts->array = NULL;
2782 cache = get_cache(domain);
2783 if (!cache || !cache->tdb) {
2784 goto do_query;
2787 if (domain->online) {
2788 goto do_query;
2791 retval = wcache_tdc_fetch_list(&dom_list, &num_domains);
2792 if (!retval || !num_domains || !dom_list) {
2793 TALLOC_FREE(dom_list);
2794 goto do_query;
2797 do_fetch_cache:
2798 trusts->array = TALLOC_ZERO_ARRAY(mem_ctx, struct netr_DomainTrust, num_domains);
2799 if (!trusts->array) {
2800 TALLOC_FREE(dom_list);
2801 return NT_STATUS_NO_MEMORY;
2804 for (i = 0; i < num_domains; i++) {
2805 struct netr_DomainTrust *trust;
2806 struct dom_sid *sid;
2807 struct winbindd_domain *dom;
2809 dom = find_domain_from_name_noinit(dom_list[i].domain_name);
2810 if (dom && dom->internal) {
2811 continue;
2814 trust = &trusts->array[trusts->count];
2815 trust->netbios_name = talloc_strdup(trusts->array, dom_list[i].domain_name);
2816 trust->dns_name = talloc_strdup(trusts->array, dom_list[i].dns_name);
2817 sid = talloc(trusts->array, struct dom_sid);
2818 if (!trust->netbios_name || !trust->dns_name ||
2819 !sid) {
2820 TALLOC_FREE(dom_list);
2821 TALLOC_FREE(trusts->array);
2822 return NT_STATUS_NO_MEMORY;
2825 trust->trust_flags = dom_list[i].trust_flags;
2826 trust->trust_attributes = dom_list[i].trust_attribs;
2827 trust->trust_type = dom_list[i].trust_type;
2828 sid_copy(sid, &dom_list[i].sid);
2829 trust->sid = sid;
2830 trusts->count++;
2833 TALLOC_FREE(dom_list);
2834 return NT_STATUS_OK;
2836 do_query:
2837 /* Return status value returned by seq number check */
2839 if (!NT_STATUS_IS_OK(domain->last_status))
2840 return domain->last_status;
2842 DEBUG(10,("trusted_domains: [Cached] - doing backend query for info for domain %s\n",
2843 domain->name ));
2845 status = domain->backend->trusted_domains(domain, mem_ctx, trusts);
2847 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2848 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2849 if (!domain->internal && old_status) {
2850 set_domain_offline(domain);
2852 if (!domain->internal &&
2853 !domain->online &&
2854 old_status) {
2855 retval = wcache_tdc_fetch_list(&dom_list, &num_domains);
2856 if (retval && num_domains && dom_list) {
2857 TALLOC_FREE(trusts->array);
2858 trusts->count = 0;
2859 goto do_fetch_cache;
2863 /* no trusts gives NT_STATUS_NO_MORE_ENTRIES resetting to NT_STATUS_OK
2864 * so that the generic centry handling still applies correctly -
2865 * Guenther*/
2867 if (!NT_STATUS_IS_ERR(status)) {
2868 status = NT_STATUS_OK;
2870 return status;
2873 /* get lockout policy */
2874 static NTSTATUS lockout_policy(struct winbindd_domain *domain,
2875 TALLOC_CTX *mem_ctx,
2876 struct samr_DomInfo12 *policy)
2878 struct winbind_cache *cache = get_cache(domain);
2879 struct cache_entry *centry = NULL;
2880 NTSTATUS status;
2881 bool old_status;
2883 old_status = domain->online;
2884 if (!cache->tdb)
2885 goto do_query;
2887 centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
2889 if (!centry)
2890 goto do_query;
2892 do_fetch_cache:
2893 policy->lockout_duration = centry_nttime(centry);
2894 policy->lockout_window = centry_nttime(centry);
2895 policy->lockout_threshold = centry_uint16(centry);
2897 status = centry->status;
2899 DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2900 domain->name, nt_errstr(status) ));
2902 centry_free(centry);
2903 return status;
2905 do_query:
2906 ZERO_STRUCTP(policy);
2908 /* Return status value returned by seq number check */
2910 if (!NT_STATUS_IS_OK(domain->last_status))
2911 return domain->last_status;
2913 DEBUG(10,("lockout_policy: [Cached] - doing backend query for info for domain %s\n",
2914 domain->name ));
2916 status = domain->backend->lockout_policy(domain, mem_ctx, policy);
2918 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2919 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2920 if (!domain->internal && old_status) {
2921 set_domain_offline(domain);
2923 if (cache->tdb &&
2924 !domain->internal &&
2925 !domain->online &&
2926 old_status) {
2927 centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
2928 if (centry) {
2929 goto do_fetch_cache;
2933 /* and save it */
2934 refresh_sequence_number(domain, false);
2935 if (!NT_STATUS_IS_OK(status)) {
2936 return status;
2938 wcache_save_lockout_policy(domain, status, policy);
2940 return status;
2943 /* get password policy */
2944 static NTSTATUS password_policy(struct winbindd_domain *domain,
2945 TALLOC_CTX *mem_ctx,
2946 struct samr_DomInfo1 *policy)
2948 struct winbind_cache *cache = get_cache(domain);
2949 struct cache_entry *centry = NULL;
2950 NTSTATUS status;
2951 bool old_status;
2953 old_status = domain->online;
2954 if (!cache->tdb)
2955 goto do_query;
2957 centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
2959 if (!centry)
2960 goto do_query;
2962 do_fetch_cache:
2963 policy->min_password_length = centry_uint16(centry);
2964 policy->password_history_length = centry_uint16(centry);
2965 policy->password_properties = centry_uint32(centry);
2966 policy->max_password_age = centry_nttime(centry);
2967 policy->min_password_age = centry_nttime(centry);
2969 status = centry->status;
2971 DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2972 domain->name, nt_errstr(status) ));
2974 centry_free(centry);
2975 return status;
2977 do_query:
2978 ZERO_STRUCTP(policy);
2980 /* Return status value returned by seq number check */
2982 if (!NT_STATUS_IS_OK(domain->last_status))
2983 return domain->last_status;
2985 DEBUG(10,("password_policy: [Cached] - doing backend query for info for domain %s\n",
2986 domain->name ));
2988 status = domain->backend->password_policy(domain, mem_ctx, policy);
2990 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2991 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2992 if (!domain->internal && old_status) {
2993 set_domain_offline(domain);
2995 if (cache->tdb &&
2996 !domain->internal &&
2997 !domain->online &&
2998 old_status) {
2999 centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
3000 if (centry) {
3001 goto do_fetch_cache;
3005 /* and save it */
3006 refresh_sequence_number(domain, false);
3007 if (!NT_STATUS_IS_OK(status)) {
3008 return status;
3010 wcache_save_password_policy(domain, status, policy);
3012 return status;
3016 /* Invalidate cached user and group lists coherently */
3018 static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
3019 void *state)
3021 if (strncmp((const char *)kbuf.dptr, "UL/", 3) == 0 ||
3022 strncmp((const char *)kbuf.dptr, "GL/", 3) == 0)
3023 tdb_delete(the_tdb, kbuf);
3025 return 0;
3028 /* Invalidate the getpwnam and getgroups entries for a winbindd domain */
3030 void wcache_invalidate_samlogon(struct winbindd_domain *domain,
3031 const struct dom_sid *sid)
3033 fstring key_str, sid_string;
3034 struct winbind_cache *cache;
3036 /* dont clear cached U/SID and UG/SID entries when we want to logon
3037 * offline - gd */
3039 if (lp_winbind_offline_logon()) {
3040 return;
3043 if (!domain)
3044 return;
3046 cache = get_cache(domain);
3048 if (!cache->tdb) {
3049 return;
3052 /* Clear U/SID cache entry */
3053 fstr_sprintf(key_str, "U/%s", sid_to_fstring(sid_string, sid));
3054 DEBUG(10, ("wcache_invalidate_samlogon: clearing %s\n", key_str));
3055 tdb_delete(cache->tdb, string_tdb_data(key_str));
3057 /* Clear UG/SID cache entry */
3058 fstr_sprintf(key_str, "UG/%s", sid_to_fstring(sid_string, sid));
3059 DEBUG(10, ("wcache_invalidate_samlogon: clearing %s\n", key_str));
3060 tdb_delete(cache->tdb, string_tdb_data(key_str));
3062 /* Samba/winbindd never needs this. */
3063 netsamlogon_clear_cached_user(sid);
3066 bool wcache_invalidate_cache(void)
3068 struct winbindd_domain *domain;
3070 for (domain = domain_list(); domain; domain = domain->next) {
3071 struct winbind_cache *cache = get_cache(domain);
3073 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
3074 "entries for %s\n", domain->name));
3075 if (cache) {
3076 if (cache->tdb) {
3077 tdb_traverse(cache->tdb, traverse_fn, NULL);
3078 } else {
3079 return false;
3083 return true;
3086 bool wcache_invalidate_cache_noinit(void)
3088 struct winbindd_domain *domain;
3090 for (domain = domain_list(); domain; domain = domain->next) {
3091 struct winbind_cache *cache;
3093 /* Skip uninitialized domains. */
3094 if (!domain->initialized && !domain->internal) {
3095 continue;
3098 cache = get_cache(domain);
3100 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
3101 "entries for %s\n", domain->name));
3102 if (cache) {
3103 if (cache->tdb) {
3104 tdb_traverse(cache->tdb, traverse_fn, NULL);
3106 * Flushing cache has nothing to with domains.
3107 * return here if we successfully flushed once.
3108 * To avoid unnecessary traversing the cache.
3110 return true;
3111 } else {
3112 return false;
3116 return true;
3119 bool init_wcache(void)
3121 if (wcache == NULL) {
3122 wcache = SMB_XMALLOC_P(struct winbind_cache);
3123 ZERO_STRUCTP(wcache);
3126 if (wcache->tdb != NULL)
3127 return true;
3129 /* when working offline we must not clear the cache on restart */
3130 wcache->tdb = tdb_open_log(cache_path("winbindd_cache.tdb"),
3131 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
3132 TDB_INCOMPATIBLE_HASH |
3133 (lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST)),
3134 O_RDWR|O_CREAT, 0600);
3136 if (wcache->tdb == NULL) {
3137 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
3138 return false;
3141 return true;
3144 /************************************************************************
3145 This is called by the parent to initialize the cache file.
3146 We don't need sophisticated locking here as we know we're the
3147 only opener.
3148 ************************************************************************/
3150 bool initialize_winbindd_cache(void)
3152 bool cache_bad = true;
3153 uint32 vers;
3155 if (!init_wcache()) {
3156 DEBUG(0,("initialize_winbindd_cache: init_wcache failed.\n"));
3157 return false;
3160 /* Check version number. */
3161 if (tdb_fetch_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, &vers) &&
3162 vers == WINBINDD_CACHE_VERSION) {
3163 cache_bad = false;
3166 if (cache_bad) {
3167 DEBUG(0,("initialize_winbindd_cache: clearing cache "
3168 "and re-creating with version number %d\n",
3169 WINBINDD_CACHE_VERSION ));
3171 tdb_close(wcache->tdb);
3172 wcache->tdb = NULL;
3174 if (unlink(cache_path("winbindd_cache.tdb")) == -1) {
3175 DEBUG(0,("initialize_winbindd_cache: unlink %s failed %s ",
3176 cache_path("winbindd_cache.tdb"),
3177 strerror(errno) ));
3178 return false;
3180 if (!init_wcache()) {
3181 DEBUG(0,("initialize_winbindd_cache: re-initialization "
3182 "init_wcache failed.\n"));
3183 return false;
3186 /* Write the version. */
3187 if (!tdb_store_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, WINBINDD_CACHE_VERSION)) {
3188 DEBUG(0,("initialize_winbindd_cache: version number store failed %s\n",
3189 tdb_errorstr(wcache->tdb) ));
3190 return false;
3194 tdb_close(wcache->tdb);
3195 wcache->tdb = NULL;
3196 return true;
3199 void close_winbindd_cache(void)
3201 if (!wcache) {
3202 return;
3204 if (wcache->tdb) {
3205 tdb_close(wcache->tdb);
3206 wcache->tdb = NULL;
3210 bool lookup_cached_sid(TALLOC_CTX *mem_ctx, const struct dom_sid *sid,
3211 char **domain_name, char **name,
3212 enum lsa_SidType *type)
3214 struct winbindd_domain *domain;
3215 NTSTATUS status;
3217 domain = find_lookup_domain_from_sid(sid);
3218 if (domain == NULL) {
3219 return false;
3221 status = wcache_sid_to_name(domain, sid, mem_ctx, domain_name, name,
3222 type);
3223 return NT_STATUS_IS_OK(status);
3226 bool lookup_cached_name(const char *domain_name,
3227 const char *name,
3228 struct dom_sid *sid,
3229 enum lsa_SidType *type)
3231 struct winbindd_domain *domain;
3232 NTSTATUS status;
3233 bool original_online_state;
3235 domain = find_lookup_domain_from_name(domain_name);
3236 if (domain == NULL) {
3237 return false;
3240 /* If we are doing a cached logon, temporarily set the domain
3241 offline so the cache won't expire the entry */
3243 original_online_state = domain->online;
3244 domain->online = false;
3245 status = wcache_name_to_sid(domain, domain_name, name, sid, type);
3246 domain->online = original_online_state;
3248 return NT_STATUS_IS_OK(status);
3251 void cache_name2sid(struct winbindd_domain *domain,
3252 const char *domain_name, const char *name,
3253 enum lsa_SidType type, const struct dom_sid *sid)
3255 refresh_sequence_number(domain, false);
3256 wcache_save_name_to_sid(domain, NT_STATUS_OK, domain_name, name,
3257 sid, type);
3261 * The original idea that this cache only contains centries has
3262 * been blurred - now other stuff gets put in here. Ensure we
3263 * ignore these things on cleanup.
3266 static int traverse_fn_cleanup(TDB_CONTEXT *the_tdb, TDB_DATA kbuf,
3267 TDB_DATA dbuf, void *state)
3269 struct cache_entry *centry;
3271 if (is_non_centry_key(kbuf)) {
3272 return 0;
3275 centry = wcache_fetch_raw((char *)kbuf.dptr);
3276 if (!centry) {
3277 return 0;
3280 if (!NT_STATUS_IS_OK(centry->status)) {
3281 DEBUG(10,("deleting centry %s\n", (const char *)kbuf.dptr));
3282 tdb_delete(the_tdb, kbuf);
3285 centry_free(centry);
3286 return 0;
3289 /* flush the cache */
3290 void wcache_flush_cache(void)
3292 if (!wcache)
3293 return;
3294 if (wcache->tdb) {
3295 tdb_close(wcache->tdb);
3296 wcache->tdb = NULL;
3298 if (!winbindd_use_cache()) {
3299 return;
3302 /* when working offline we must not clear the cache on restart */
3303 wcache->tdb = tdb_open_log(cache_path("winbindd_cache.tdb"),
3304 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
3305 TDB_INCOMPATIBLE_HASH |
3306 (lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST)),
3307 O_RDWR|O_CREAT, 0600);
3309 if (!wcache->tdb) {
3310 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
3311 return;
3314 tdb_traverse(wcache->tdb, traverse_fn_cleanup, NULL);
3316 DEBUG(10,("wcache_flush_cache success\n"));
3319 /* Count cached creds */
3321 static int traverse_fn_cached_creds(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
3322 void *state)
3324 int *cred_count = (int*)state;
3326 if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
3327 (*cred_count)++;
3329 return 0;
3332 NTSTATUS wcache_count_cached_creds(struct winbindd_domain *domain, int *count)
3334 struct winbind_cache *cache = get_cache(domain);
3336 *count = 0;
3338 if (!cache->tdb) {
3339 return NT_STATUS_INTERNAL_DB_ERROR;
3342 tdb_traverse(cache->tdb, traverse_fn_cached_creds, (void *)count);
3344 return NT_STATUS_OK;
3347 struct cred_list {
3348 struct cred_list *prev, *next;
3349 TDB_DATA key;
3350 fstring name;
3351 time_t created;
3353 static struct cred_list *wcache_cred_list;
3355 static int traverse_fn_get_credlist(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
3356 void *state)
3358 struct cred_list *cred;
3360 if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
3362 cred = SMB_MALLOC_P(struct cred_list);
3363 if (cred == NULL) {
3364 DEBUG(0,("traverse_fn_remove_first_creds: failed to malloc new entry for list\n"));
3365 return -1;
3368 ZERO_STRUCTP(cred);
3370 /* save a copy of the key */
3372 fstrcpy(cred->name, (const char *)kbuf.dptr);
3373 DLIST_ADD(wcache_cred_list, cred);
3376 return 0;
3379 NTSTATUS wcache_remove_oldest_cached_creds(struct winbindd_domain *domain, const struct dom_sid *sid)
3381 struct winbind_cache *cache = get_cache(domain);
3382 NTSTATUS status;
3383 int ret;
3384 struct cred_list *cred, *oldest = NULL;
3386 if (!cache->tdb) {
3387 return NT_STATUS_INTERNAL_DB_ERROR;
3390 /* we possibly already have an entry */
3391 if (sid && NT_STATUS_IS_OK(wcache_cached_creds_exist(domain, sid))) {
3393 fstring key_str, tmp;
3395 DEBUG(11,("we already have an entry, deleting that\n"));
3397 fstr_sprintf(key_str, "CRED/%s", sid_to_fstring(tmp, sid));
3399 tdb_delete(cache->tdb, string_tdb_data(key_str));
3401 return NT_STATUS_OK;
3404 ret = tdb_traverse(cache->tdb, traverse_fn_get_credlist, NULL);
3405 if (ret == 0) {
3406 return NT_STATUS_OK;
3407 } else if ((ret == -1) || (wcache_cred_list == NULL)) {
3408 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
3411 ZERO_STRUCTP(oldest);
3413 for (cred = wcache_cred_list; cred; cred = cred->next) {
3415 TDB_DATA data;
3416 time_t t;
3418 data = tdb_fetch(cache->tdb, string_tdb_data(cred->name));
3419 if (!data.dptr) {
3420 DEBUG(10,("wcache_remove_oldest_cached_creds: entry for [%s] not found\n",
3421 cred->name));
3422 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
3423 goto done;
3426 t = IVAL(data.dptr, 0);
3427 SAFE_FREE(data.dptr);
3429 if (!oldest) {
3430 oldest = SMB_MALLOC_P(struct cred_list);
3431 if (oldest == NULL) {
3432 status = NT_STATUS_NO_MEMORY;
3433 goto done;
3436 fstrcpy(oldest->name, cred->name);
3437 oldest->created = t;
3438 continue;
3441 if (t < oldest->created) {
3442 fstrcpy(oldest->name, cred->name);
3443 oldest->created = t;
3447 if (tdb_delete(cache->tdb, string_tdb_data(oldest->name)) == 0) {
3448 status = NT_STATUS_OK;
3449 } else {
3450 status = NT_STATUS_UNSUCCESSFUL;
3452 done:
3453 SAFE_FREE(wcache_cred_list);
3454 SAFE_FREE(oldest);
3456 return status;
3459 /* Change the global online/offline state. */
3460 bool set_global_winbindd_state_offline(void)
3462 TDB_DATA data;
3464 DEBUG(10,("set_global_winbindd_state_offline: offline requested.\n"));
3466 /* Only go offline if someone has created
3467 the key "WINBINDD_OFFLINE" in the cache tdb. */
3469 if (wcache == NULL || wcache->tdb == NULL) {
3470 DEBUG(10,("set_global_winbindd_state_offline: wcache not open yet.\n"));
3471 return false;
3474 if (!lp_winbind_offline_logon()) {
3475 DEBUG(10,("set_global_winbindd_state_offline: rejecting.\n"));
3476 return false;
3479 if (global_winbindd_offline_state) {
3480 /* Already offline. */
3481 return true;
3484 data = tdb_fetch_bystring( wcache->tdb, "WINBINDD_OFFLINE" );
3486 if (!data.dptr || data.dsize != 4) {
3487 DEBUG(10,("set_global_winbindd_state_offline: offline state not set.\n"));
3488 SAFE_FREE(data.dptr);
3489 return false;
3490 } else {
3491 DEBUG(10,("set_global_winbindd_state_offline: offline state set.\n"));
3492 global_winbindd_offline_state = true;
3493 SAFE_FREE(data.dptr);
3494 return true;
3498 void set_global_winbindd_state_online(void)
3500 DEBUG(10,("set_global_winbindd_state_online: online requested.\n"));
3502 if (!lp_winbind_offline_logon()) {
3503 DEBUG(10,("set_global_winbindd_state_online: rejecting.\n"));
3504 return;
3507 if (!global_winbindd_offline_state) {
3508 /* Already online. */
3509 return;
3511 global_winbindd_offline_state = false;
3513 if (!wcache->tdb) {
3514 return;
3517 /* Ensure there is no key "WINBINDD_OFFLINE" in the cache tdb. */
3518 tdb_delete_bystring(wcache->tdb, "WINBINDD_OFFLINE");
3521 bool get_global_winbindd_state_offline(void)
3523 return global_winbindd_offline_state;
3526 /***********************************************************************
3527 Validate functions for all possible cache tdb keys.
3528 ***********************************************************************/
3530 static struct cache_entry *create_centry_validate(const char *kstr, TDB_DATA data,
3531 struct tdb_validation_status *state)
3533 struct cache_entry *centry;
3535 centry = SMB_XMALLOC_P(struct cache_entry);
3536 centry->data = (unsigned char *)memdup(data.dptr, data.dsize);
3537 if (!centry->data) {
3538 SAFE_FREE(centry);
3539 return NULL;
3541 centry->len = data.dsize;
3542 centry->ofs = 0;
3544 if (centry->len < 16) {
3545 /* huh? corrupt cache? */
3546 DEBUG(0,("create_centry_validate: Corrupt cache for key %s "
3547 "(len < 16) ?\n", kstr));
3548 centry_free(centry);
3549 state->bad_entry = true;
3550 state->success = false;
3551 return NULL;
3554 centry->status = NT_STATUS(centry_uint32(centry));
3555 centry->sequence_number = centry_uint32(centry);
3556 centry->timeout = centry_uint64_t(centry);
3557 return centry;
3560 static int validate_seqnum(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3561 struct tdb_validation_status *state)
3563 if (dbuf.dsize != 8) {
3564 DEBUG(0,("validate_seqnum: Corrupt cache for key %s (len %u != 8) ?\n",
3565 keystr, (unsigned int)dbuf.dsize ));
3566 state->bad_entry = true;
3567 return 1;
3569 return 0;
3572 static int validate_ns(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3573 struct tdb_validation_status *state)
3575 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3576 if (!centry) {
3577 return 1;
3580 (void)centry_uint32(centry);
3581 if (NT_STATUS_IS_OK(centry->status)) {
3582 struct dom_sid sid;
3583 (void)centry_sid(centry, &sid);
3586 centry_free(centry);
3588 if (!(state->success)) {
3589 return 1;
3591 DEBUG(10,("validate_ns: %s ok\n", keystr));
3592 return 0;
3595 static int validate_sn(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3596 struct tdb_validation_status *state)
3598 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3599 if (!centry) {
3600 return 1;
3603 if (NT_STATUS_IS_OK(centry->status)) {
3604 (void)centry_uint32(centry);
3605 (void)centry_string(centry, mem_ctx);
3606 (void)centry_string(centry, mem_ctx);
3609 centry_free(centry);
3611 if (!(state->success)) {
3612 return 1;
3614 DEBUG(10,("validate_sn: %s ok\n", keystr));
3615 return 0;
3618 static int validate_u(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3619 struct tdb_validation_status *state)
3621 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3622 struct dom_sid sid;
3624 if (!centry) {
3625 return 1;
3628 (void)centry_string(centry, mem_ctx);
3629 (void)centry_string(centry, mem_ctx);
3630 (void)centry_string(centry, mem_ctx);
3631 (void)centry_string(centry, mem_ctx);
3632 (void)centry_uint32(centry);
3633 (void)centry_sid(centry, &sid);
3634 (void)centry_sid(centry, &sid);
3636 centry_free(centry);
3638 if (!(state->success)) {
3639 return 1;
3641 DEBUG(10,("validate_u: %s ok\n", keystr));
3642 return 0;
3645 static int validate_loc_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3646 struct tdb_validation_status *state)
3648 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3650 if (!centry) {
3651 return 1;
3654 (void)centry_nttime(centry);
3655 (void)centry_nttime(centry);
3656 (void)centry_uint16(centry);
3658 centry_free(centry);
3660 if (!(state->success)) {
3661 return 1;
3663 DEBUG(10,("validate_loc_pol: %s ok\n", keystr));
3664 return 0;
3667 static int validate_pwd_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3668 struct tdb_validation_status *state)
3670 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3672 if (!centry) {
3673 return 1;
3676 (void)centry_uint16(centry);
3677 (void)centry_uint16(centry);
3678 (void)centry_uint32(centry);
3679 (void)centry_nttime(centry);
3680 (void)centry_nttime(centry);
3682 centry_free(centry);
3684 if (!(state->success)) {
3685 return 1;
3687 DEBUG(10,("validate_pwd_pol: %s ok\n", keystr));
3688 return 0;
3691 static int validate_cred(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);
3696 if (!centry) {
3697 return 1;
3700 (void)centry_time(centry);
3701 (void)centry_hash16(centry, mem_ctx);
3703 /* We only have 17 bytes more data in the salted cred case. */
3704 if (centry->len - centry->ofs == 17) {
3705 (void)centry_hash16(centry, mem_ctx);
3708 centry_free(centry);
3710 if (!(state->success)) {
3711 return 1;
3713 DEBUG(10,("validate_cred: %s ok\n", keystr));
3714 return 0;
3717 static int validate_ul(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3718 struct tdb_validation_status *state)
3720 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3721 int32 num_entries, i;
3723 if (!centry) {
3724 return 1;
3727 num_entries = (int32)centry_uint32(centry);
3729 for (i=0; i< num_entries; i++) {
3730 struct dom_sid sid;
3731 (void)centry_string(centry, mem_ctx);
3732 (void)centry_string(centry, mem_ctx);
3733 (void)centry_string(centry, mem_ctx);
3734 (void)centry_string(centry, mem_ctx);
3735 (void)centry_sid(centry, &sid);
3736 (void)centry_sid(centry, &sid);
3739 centry_free(centry);
3741 if (!(state->success)) {
3742 return 1;
3744 DEBUG(10,("validate_ul: %s ok\n", keystr));
3745 return 0;
3748 static int validate_gl(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3749 struct tdb_validation_status *state)
3751 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3752 int32 num_entries, i;
3754 if (!centry) {
3755 return 1;
3758 num_entries = centry_uint32(centry);
3760 for (i=0; i< num_entries; i++) {
3761 (void)centry_string(centry, mem_ctx);
3762 (void)centry_string(centry, mem_ctx);
3763 (void)centry_uint32(centry);
3766 centry_free(centry);
3768 if (!(state->success)) {
3769 return 1;
3771 DEBUG(10,("validate_gl: %s ok\n", keystr));
3772 return 0;
3775 static int validate_ug(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_groups, i;
3781 if (!centry) {
3782 return 1;
3785 num_groups = centry_uint32(centry);
3787 for (i=0; i< num_groups; i++) {
3788 struct dom_sid sid;
3789 centry_sid(centry, &sid);
3792 centry_free(centry);
3794 if (!(state->success)) {
3795 return 1;
3797 DEBUG(10,("validate_ug: %s ok\n", keystr));
3798 return 0;
3801 static int validate_ua(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3802 struct tdb_validation_status *state)
3804 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3805 int32 num_aliases, i;
3807 if (!centry) {
3808 return 1;
3811 num_aliases = centry_uint32(centry);
3813 for (i=0; i < num_aliases; i++) {
3814 (void)centry_uint32(centry);
3817 centry_free(centry);
3819 if (!(state->success)) {
3820 return 1;
3822 DEBUG(10,("validate_ua: %s ok\n", keystr));
3823 return 0;
3826 static int validate_gm(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3827 struct tdb_validation_status *state)
3829 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3830 int32 num_names, i;
3832 if (!centry) {
3833 return 1;
3836 num_names = centry_uint32(centry);
3838 for (i=0; i< num_names; i++) {
3839 struct dom_sid sid;
3840 centry_sid(centry, &sid);
3841 (void)centry_string(centry, mem_ctx);
3842 (void)centry_uint32(centry);
3845 centry_free(centry);
3847 if (!(state->success)) {
3848 return 1;
3850 DEBUG(10,("validate_gm: %s ok\n", keystr));
3851 return 0;
3854 static int validate_dr(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3855 struct tdb_validation_status *state)
3857 /* Can't say anything about this other than must be nonzero. */
3858 if (dbuf.dsize == 0) {
3859 DEBUG(0,("validate_dr: Corrupt cache for key %s (len == 0) ?\n",
3860 keystr));
3861 state->bad_entry = true;
3862 state->success = false;
3863 return 1;
3866 DEBUG(10,("validate_dr: %s ok\n", keystr));
3867 return 0;
3870 static int validate_de(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3871 struct tdb_validation_status *state)
3873 /* Can't say anything about this other than must be nonzero. */
3874 if (dbuf.dsize == 0) {
3875 DEBUG(0,("validate_de: Corrupt cache for key %s (len == 0) ?\n",
3876 keystr));
3877 state->bad_entry = true;
3878 state->success = false;
3879 return 1;
3882 DEBUG(10,("validate_de: %s ok\n", keystr));
3883 return 0;
3886 static int validate_pwinfo(TALLOC_CTX *mem_ctx, const char *keystr,
3887 TDB_DATA dbuf, struct tdb_validation_status *state)
3889 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3891 if (!centry) {
3892 return 1;
3895 (void)centry_string(centry, mem_ctx);
3896 (void)centry_string(centry, mem_ctx);
3897 (void)centry_string(centry, mem_ctx);
3898 (void)centry_uint32(centry);
3900 centry_free(centry);
3902 if (!(state->success)) {
3903 return 1;
3905 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3906 return 0;
3909 static int validate_nss_an(TALLOC_CTX *mem_ctx, const char *keystr,
3910 TDB_DATA dbuf,
3911 struct tdb_validation_status *state)
3913 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3915 if (!centry) {
3916 return 1;
3919 (void)centry_string( centry, mem_ctx );
3921 centry_free(centry);
3923 if (!(state->success)) {
3924 return 1;
3926 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3927 return 0;
3930 static int validate_nss_na(TALLOC_CTX *mem_ctx, const char *keystr,
3931 TDB_DATA dbuf,
3932 struct tdb_validation_status *state)
3934 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3936 if (!centry) {
3937 return 1;
3940 (void)centry_string( centry, mem_ctx );
3942 centry_free(centry);
3944 if (!(state->success)) {
3945 return 1;
3947 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3948 return 0;
3951 static int validate_trustdomcache(TALLOC_CTX *mem_ctx, const char *keystr,
3952 TDB_DATA dbuf,
3953 struct tdb_validation_status *state)
3955 if (dbuf.dsize == 0) {
3956 DEBUG(0, ("validate_trustdomcache: Corrupt cache for "
3957 "key %s (len ==0) ?\n", keystr));
3958 state->bad_entry = true;
3959 state->success = false;
3960 return 1;
3963 DEBUG(10, ("validate_trustdomcache: %s ok\n", keystr));
3964 DEBUGADD(10, (" Don't trust me, I am a DUMMY!\n"));
3965 return 0;
3968 static int validate_offline(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3969 struct tdb_validation_status *state)
3971 if (dbuf.dsize != 4) {
3972 DEBUG(0,("validate_offline: Corrupt cache for key %s (len %u != 4) ?\n",
3973 keystr, (unsigned int)dbuf.dsize ));
3974 state->bad_entry = true;
3975 state->success = false;
3976 return 1;
3978 DEBUG(10,("validate_offline: %s ok\n", keystr));
3979 return 0;
3982 static int validate_ndr(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3983 struct tdb_validation_status *state)
3986 * Ignore validation for now. The proper way to do this is with a
3987 * checksum. Just pure parsing does not really catch much.
3989 return 0;
3992 static int validate_cache_version(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3993 struct tdb_validation_status *state)
3995 if (dbuf.dsize != 4) {
3996 DEBUG(0, ("validate_cache_version: Corrupt cache for "
3997 "key %s (len %u != 4) ?\n",
3998 keystr, (unsigned int)dbuf.dsize));
3999 state->bad_entry = true;
4000 state->success = false;
4001 return 1;
4004 DEBUG(10, ("validate_cache_version: %s ok\n", keystr));
4005 return 0;
4008 /***********************************************************************
4009 A list of all possible cache tdb keys with associated validation
4010 functions.
4011 ***********************************************************************/
4013 struct key_val_struct {
4014 const char *keyname;
4015 int (*validate_data_fn)(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, struct tdb_validation_status* state);
4016 } key_val[] = {
4017 {"SEQNUM/", validate_seqnum},
4018 {"NS/", validate_ns},
4019 {"SN/", validate_sn},
4020 {"U/", validate_u},
4021 {"LOC_POL/", validate_loc_pol},
4022 {"PWD_POL/", validate_pwd_pol},
4023 {"CRED/", validate_cred},
4024 {"UL/", validate_ul},
4025 {"GL/", validate_gl},
4026 {"UG/", validate_ug},
4027 {"UA", validate_ua},
4028 {"GM/", validate_gm},
4029 {"DR/", validate_dr},
4030 {"DE/", validate_de},
4031 {"NSS/PWINFO/", validate_pwinfo},
4032 {"TRUSTDOMCACHE/", validate_trustdomcache},
4033 {"NSS/NA/", validate_nss_na},
4034 {"NSS/AN/", validate_nss_an},
4035 {"WINBINDD_OFFLINE", validate_offline},
4036 {"NDR/", validate_ndr},
4037 {WINBINDD_CACHE_VERSION_KEYSTR, validate_cache_version},
4038 {NULL, NULL}
4041 /***********************************************************************
4042 Function to look at every entry in the tdb and validate it as far as
4043 possible.
4044 ***********************************************************************/
4046 static int cache_traverse_validate_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, void *state)
4048 int i;
4049 unsigned int max_key_len = 1024;
4050 struct tdb_validation_status *v_state = (struct tdb_validation_status *)state;
4052 /* Paranoia check. */
4053 if (strncmp("UA/", (const char *)kbuf.dptr, 3) == 0) {
4054 max_key_len = 1024 * 1024;
4056 if (kbuf.dsize > max_key_len) {
4057 DEBUG(0, ("cache_traverse_validate_fn: key length too large: "
4058 "(%u) > (%u)\n\n",
4059 (unsigned int)kbuf.dsize, (unsigned int)max_key_len));
4060 return 1;
4063 for (i = 0; key_val[i].keyname; i++) {
4064 size_t namelen = strlen(key_val[i].keyname);
4065 if (kbuf.dsize >= namelen && (
4066 strncmp(key_val[i].keyname, (const char *)kbuf.dptr, namelen)) == 0) {
4067 TALLOC_CTX *mem_ctx;
4068 char *keystr;
4069 int ret;
4071 keystr = SMB_MALLOC_ARRAY(char, kbuf.dsize+1);
4072 if (!keystr) {
4073 return 1;
4075 memcpy(keystr, kbuf.dptr, kbuf.dsize);
4076 keystr[kbuf.dsize] = '\0';
4078 mem_ctx = talloc_init("validate_ctx");
4079 if (!mem_ctx) {
4080 SAFE_FREE(keystr);
4081 return 1;
4084 ret = key_val[i].validate_data_fn(mem_ctx, keystr, dbuf,
4085 v_state);
4087 SAFE_FREE(keystr);
4088 talloc_destroy(mem_ctx);
4089 return ret;
4093 DEBUG(0,("cache_traverse_validate_fn: unknown cache entry\nkey :\n"));
4094 dump_data(0, (uint8 *)kbuf.dptr, kbuf.dsize);
4095 DEBUG(0,("data :\n"));
4096 dump_data(0, (uint8 *)dbuf.dptr, dbuf.dsize);
4097 v_state->unknown_key = true;
4098 v_state->success = false;
4099 return 1; /* terminate. */
4102 static void validate_panic(const char *const why)
4104 DEBUG(0,("validating cache: would panic %s\n", why ));
4105 DEBUGADD(0, ("exiting instead (cache validation mode)\n"));
4106 exit(47);
4109 static int wbcache_update_centry_fn(TDB_CONTEXT *tdb,
4110 TDB_DATA key,
4111 TDB_DATA data,
4112 void *state)
4114 uint64_t ctimeout;
4115 TDB_DATA blob;
4117 if (is_non_centry_key(key)) {
4118 return 0;
4121 if (data.dptr == NULL || data.dsize == 0) {
4122 if (tdb_delete(tdb, key) < 0) {
4123 DEBUG(0, ("tdb_delete for [%s] failed!\n",
4124 key.dptr));
4125 return 1;
4129 /* add timeout to blob (uint64_t) */
4130 blob.dsize = data.dsize + 8;
4132 blob.dptr = SMB_XMALLOC_ARRAY(uint8_t, blob.dsize);
4133 if (blob.dptr == NULL) {
4134 return 1;
4136 memset(blob.dptr, 0, blob.dsize);
4138 /* copy status and seqnum */
4139 memcpy(blob.dptr, data.dptr, 8);
4141 /* add timeout */
4142 ctimeout = lp_winbind_cache_time() + time(NULL);
4143 SBVAL(blob.dptr, 8, ctimeout);
4145 /* copy the rest */
4146 memcpy(blob.dptr + 16, data.dptr + 8, data.dsize - 8);
4148 if (tdb_store(tdb, key, blob, TDB_REPLACE) < 0) {
4149 DEBUG(0, ("tdb_store to update [%s] failed!\n",
4150 key.dptr));
4151 SAFE_FREE(blob.dptr);
4152 return 1;
4155 SAFE_FREE(blob.dptr);
4156 return 0;
4159 static bool wbcache_upgrade_v1_to_v2(TDB_CONTEXT *tdb)
4161 int rc;
4163 DEBUG(1, ("Upgrade to version 2 of the winbindd_cache.tdb\n"));
4165 rc = tdb_traverse(tdb, wbcache_update_centry_fn, NULL);
4166 if (rc < 0) {
4167 return false;
4170 return true;
4173 /***********************************************************************
4174 Try and validate every entry in the winbindd cache. If we fail here,
4175 delete the cache tdb and return non-zero.
4176 ***********************************************************************/
4178 int winbindd_validate_cache(void)
4180 int ret = -1;
4181 const char *tdb_path = cache_path("winbindd_cache.tdb");
4182 TDB_CONTEXT *tdb = NULL;
4183 uint32_t vers_id;
4184 bool ok;
4186 DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
4187 smb_panic_fn = validate_panic;
4189 tdb = tdb_open_log(tdb_path,
4190 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
4191 TDB_INCOMPATIBLE_HASH |
4192 ( lp_winbind_offline_logon()
4193 ? TDB_DEFAULT
4194 : TDB_DEFAULT | TDB_CLEAR_IF_FIRST ),
4195 O_RDWR|O_CREAT,
4196 0600);
4197 if (!tdb) {
4198 DEBUG(0, ("winbindd_validate_cache: "
4199 "error opening/initializing tdb\n"));
4200 goto done;
4203 /* Version check and upgrade code. */
4204 if (!tdb_fetch_uint32(tdb, WINBINDD_CACHE_VERSION_KEYSTR, &vers_id)) {
4205 DEBUG(10, ("Fresh database\n"));
4206 tdb_store_uint32(tdb, WINBINDD_CACHE_VERSION_KEYSTR, WINBINDD_CACHE_VERSION);
4207 vers_id = WINBINDD_CACHE_VERSION;
4210 if (vers_id != WINBINDD_CACHE_VERSION) {
4211 if (vers_id == WINBINDD_CACHE_VER1) {
4212 ok = wbcache_upgrade_v1_to_v2(tdb);
4213 if (!ok) {
4214 DEBUG(10, ("winbindd_validate_cache: upgrade to version 2 failed.\n"));
4215 unlink(tdb_path);
4216 goto done;
4219 tdb_store_uint32(tdb,
4220 WINBINDD_CACHE_VERSION_KEYSTR,
4221 WINBINDD_CACHE_VERSION);
4222 vers_id = WINBINDD_CACHE_VER2;
4226 tdb_close(tdb);
4228 ret = tdb_validate_and_backup(tdb_path, cache_traverse_validate_fn);
4230 if (ret != 0) {
4231 DEBUG(10, ("winbindd_validate_cache: validation not successful.\n"));
4232 DEBUGADD(10, ("removing tdb %s.\n", tdb_path));
4233 unlink(tdb_path);
4236 done:
4237 DEBUG(10, ("winbindd_validate_cache: restoring panic function\n"));
4238 smb_panic_fn = smb_panic;
4239 return ret;
4242 /***********************************************************************
4243 Try and validate every entry in the winbindd cache.
4244 ***********************************************************************/
4246 int winbindd_validate_cache_nobackup(void)
4248 int ret = -1;
4249 const char *tdb_path = cache_path("winbindd_cache.tdb");
4251 DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
4252 smb_panic_fn = validate_panic;
4255 if (wcache == NULL || wcache->tdb == NULL) {
4256 ret = tdb_validate_open(tdb_path, cache_traverse_validate_fn);
4257 } else {
4258 ret = tdb_validate(wcache->tdb, cache_traverse_validate_fn);
4261 if (ret != 0) {
4262 DEBUG(10, ("winbindd_validate_cache_nobackup: validation not "
4263 "successful.\n"));
4266 DEBUG(10, ("winbindd_validate_cache_nobackup: restoring panic "
4267 "function\n"));
4268 smb_panic_fn = smb_panic;
4269 return ret;
4272 bool winbindd_cache_validate_and_initialize(void)
4274 close_winbindd_cache();
4276 if (lp_winbind_offline_logon()) {
4277 if (winbindd_validate_cache() < 0) {
4278 DEBUG(0, ("winbindd cache tdb corrupt and no backup "
4279 "could be restored.\n"));
4283 return initialize_winbindd_cache();
4286 /*********************************************************************
4287 ********************************************************************/
4289 static bool add_wbdomain_to_tdc_array( struct winbindd_domain *new_dom,
4290 struct winbindd_tdc_domain **domains,
4291 size_t *num_domains )
4293 struct winbindd_tdc_domain *list = NULL;
4294 size_t idx;
4295 int i;
4296 bool set_only = false;
4298 /* don't allow duplicates */
4300 idx = *num_domains;
4301 list = *domains;
4303 for ( i=0; i< (*num_domains); i++ ) {
4304 if ( strequal( new_dom->name, list[i].domain_name ) ) {
4305 DEBUG(10,("add_wbdomain_to_tdc_array: Found existing record for %s\n",
4306 new_dom->name));
4307 idx = i;
4308 set_only = true;
4310 break;
4314 if ( !set_only ) {
4315 if ( !*domains ) {
4316 list = TALLOC_ARRAY( NULL, struct winbindd_tdc_domain, 1 );
4317 idx = 0;
4318 } else {
4319 list = TALLOC_REALLOC_ARRAY( *domains, *domains,
4320 struct winbindd_tdc_domain,
4321 (*num_domains)+1);
4322 idx = *num_domains;
4325 ZERO_STRUCT( list[idx] );
4328 if ( !list )
4329 return false;
4331 list[idx].domain_name = talloc_strdup( list, new_dom->name );
4332 list[idx].dns_name = talloc_strdup( list, new_dom->alt_name );
4334 if ( !is_null_sid( &new_dom->sid ) ) {
4335 sid_copy( &list[idx].sid, &new_dom->sid );
4336 } else {
4337 sid_copy(&list[idx].sid, &global_sid_NULL);
4340 if ( new_dom->domain_flags != 0x0 )
4341 list[idx].trust_flags = new_dom->domain_flags;
4343 if ( new_dom->domain_type != 0x0 )
4344 list[idx].trust_type = new_dom->domain_type;
4346 if ( new_dom->domain_trust_attribs != 0x0 )
4347 list[idx].trust_attribs = new_dom->domain_trust_attribs;
4349 if ( !set_only ) {
4350 *domains = list;
4351 *num_domains = idx + 1;
4354 return true;
4357 /*********************************************************************
4358 ********************************************************************/
4360 static TDB_DATA make_tdc_key( const char *domain_name )
4362 char *keystr = NULL;
4363 TDB_DATA key = { NULL, 0 };
4365 if ( !domain_name ) {
4366 DEBUG(5,("make_tdc_key: Keyname workgroup is NULL!\n"));
4367 return key;
4370 if (asprintf( &keystr, "TRUSTDOMCACHE/%s", domain_name ) == -1) {
4371 return key;
4373 key = string_term_tdb_data(keystr);
4375 return key;
4378 /*********************************************************************
4379 ********************************************************************/
4381 static int pack_tdc_domains( struct winbindd_tdc_domain *domains,
4382 size_t num_domains,
4383 unsigned char **buf )
4385 unsigned char *buffer = NULL;
4386 int len = 0;
4387 int buflen = 0;
4388 int i = 0;
4390 DEBUG(10,("pack_tdc_domains: Packing %d trusted domains\n",
4391 (int)num_domains));
4393 buflen = 0;
4395 again:
4396 len = 0;
4398 /* Store the number of array items first */
4399 len += tdb_pack( buffer+len, buflen-len, "d",
4400 num_domains );
4402 /* now pack each domain trust record */
4403 for ( i=0; i<num_domains; i++ ) {
4405 fstring tmp;
4407 if ( buflen > 0 ) {
4408 DEBUG(10,("pack_tdc_domains: Packing domain %s (%s)\n",
4409 domains[i].domain_name,
4410 domains[i].dns_name ? domains[i].dns_name : "UNKNOWN" ));
4413 len += tdb_pack( buffer+len, buflen-len, "fffddd",
4414 domains[i].domain_name,
4415 domains[i].dns_name,
4416 sid_to_fstring(tmp, &domains[i].sid),
4417 domains[i].trust_flags,
4418 domains[i].trust_attribs,
4419 domains[i].trust_type );
4422 if ( buflen < len ) {
4423 SAFE_FREE(buffer);
4424 if ( (buffer = SMB_MALLOC_ARRAY(unsigned char, len)) == NULL ) {
4425 DEBUG(0,("pack_tdc_domains: failed to alloc buffer!\n"));
4426 buflen = -1;
4427 goto done;
4429 buflen = len;
4430 goto again;
4433 *buf = buffer;
4435 done:
4436 return buflen;
4439 /*********************************************************************
4440 ********************************************************************/
4442 static size_t unpack_tdc_domains( unsigned char *buf, int buflen,
4443 struct winbindd_tdc_domain **domains )
4445 fstring domain_name, dns_name, sid_string;
4446 uint32 type, attribs, flags;
4447 int num_domains;
4448 int len = 0;
4449 int i;
4450 struct winbindd_tdc_domain *list = NULL;
4452 /* get the number of domains */
4453 len += tdb_unpack( buf+len, buflen-len, "d", &num_domains);
4454 if ( len == -1 ) {
4455 DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
4456 return 0;
4459 list = TALLOC_ARRAY( NULL, struct winbindd_tdc_domain, num_domains );
4460 if ( !list ) {
4461 DEBUG(0,("unpack_tdc_domains: Failed to talloc() domain list!\n"));
4462 return 0;
4465 for ( i=0; i<num_domains; i++ ) {
4466 len += tdb_unpack( buf+len, buflen-len, "fffddd",
4467 domain_name,
4468 dns_name,
4469 sid_string,
4470 &flags,
4471 &attribs,
4472 &type );
4474 if ( len == -1 ) {
4475 DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
4476 TALLOC_FREE( list );
4477 return 0;
4480 DEBUG(11,("unpack_tdc_domains: Unpacking domain %s (%s) "
4481 "SID %s, flags = 0x%x, attribs = 0x%x, type = 0x%x\n",
4482 domain_name, dns_name, sid_string,
4483 flags, attribs, type));
4485 list[i].domain_name = talloc_strdup( list, domain_name );
4486 list[i].dns_name = talloc_strdup( list, dns_name );
4487 if ( !string_to_sid( &(list[i].sid), sid_string ) ) {
4488 DEBUG(10,("unpack_tdc_domains: no SID for domain %s\n",
4489 domain_name));
4491 list[i].trust_flags = flags;
4492 list[i].trust_attribs = attribs;
4493 list[i].trust_type = type;
4496 *domains = list;
4498 return num_domains;
4501 /*********************************************************************
4502 ********************************************************************/
4504 static bool wcache_tdc_store_list( struct winbindd_tdc_domain *domains, size_t num_domains )
4506 TDB_DATA key = make_tdc_key( lp_workgroup() );
4507 TDB_DATA data = { NULL, 0 };
4508 int ret;
4510 if ( !key.dptr )
4511 return false;
4513 /* See if we were asked to delete the cache entry */
4515 if ( !domains ) {
4516 ret = tdb_delete( wcache->tdb, key );
4517 goto done;
4520 data.dsize = pack_tdc_domains( domains, num_domains, &data.dptr );
4522 if ( !data.dptr ) {
4523 ret = -1;
4524 goto done;
4527 ret = tdb_store( wcache->tdb, key, data, 0 );
4529 done:
4530 SAFE_FREE( data.dptr );
4531 SAFE_FREE( key.dptr );
4533 return ( ret != -1 );
4536 /*********************************************************************
4537 ********************************************************************/
4539 bool wcache_tdc_fetch_list( struct winbindd_tdc_domain **domains, size_t *num_domains )
4541 TDB_DATA key = make_tdc_key( lp_workgroup() );
4542 TDB_DATA data = { NULL, 0 };
4544 *domains = NULL;
4545 *num_domains = 0;
4547 if ( !key.dptr )
4548 return false;
4550 data = tdb_fetch( wcache->tdb, key );
4552 SAFE_FREE( key.dptr );
4554 if ( !data.dptr )
4555 return false;
4557 *num_domains = unpack_tdc_domains( data.dptr, data.dsize, domains );
4559 SAFE_FREE( data.dptr );
4561 if ( !*domains )
4562 return false;
4564 return true;
4567 /*********************************************************************
4568 ********************************************************************/
4570 bool wcache_tdc_add_domain( struct winbindd_domain *domain )
4572 struct winbindd_tdc_domain *dom_list = NULL;
4573 size_t num_domains = 0;
4574 bool ret = false;
4576 DEBUG(10,("wcache_tdc_add_domain: Adding domain %s (%s), SID %s, "
4577 "flags = 0x%x, attributes = 0x%x, type = 0x%x\n",
4578 domain->name, domain->alt_name,
4579 sid_string_dbg(&domain->sid),
4580 domain->domain_flags,
4581 domain->domain_trust_attribs,
4582 domain->domain_type));
4584 if ( !init_wcache() ) {
4585 return false;
4588 /* fetch the list */
4590 wcache_tdc_fetch_list( &dom_list, &num_domains );
4592 /* add the new domain */
4594 if ( !add_wbdomain_to_tdc_array( domain, &dom_list, &num_domains ) ) {
4595 goto done;
4598 /* pack the domain */
4600 if ( !wcache_tdc_store_list( dom_list, num_domains ) ) {
4601 goto done;
4604 /* Success */
4606 ret = true;
4607 done:
4608 TALLOC_FREE( dom_list );
4610 return ret;
4613 /*********************************************************************
4614 ********************************************************************/
4616 struct winbindd_tdc_domain * wcache_tdc_fetch_domain( TALLOC_CTX *ctx, const char *name )
4618 struct winbindd_tdc_domain *dom_list = NULL;
4619 size_t num_domains = 0;
4620 int i;
4621 struct winbindd_tdc_domain *d = NULL;
4623 DEBUG(10,("wcache_tdc_fetch_domain: Searching for domain %s\n", name));
4625 if ( !init_wcache() ) {
4626 return false;
4629 /* fetch the list */
4631 wcache_tdc_fetch_list( &dom_list, &num_domains );
4633 for ( i=0; i<num_domains; i++ ) {
4634 if ( strequal(name, dom_list[i].domain_name) ||
4635 strequal(name, dom_list[i].dns_name) )
4637 DEBUG(10,("wcache_tdc_fetch_domain: Found domain %s\n",
4638 name));
4640 d = TALLOC_P( ctx, struct winbindd_tdc_domain );
4641 if ( !d )
4642 break;
4644 d->domain_name = talloc_strdup( d, dom_list[i].domain_name );
4645 d->dns_name = talloc_strdup( d, dom_list[i].dns_name );
4646 sid_copy( &d->sid, &dom_list[i].sid );
4647 d->trust_flags = dom_list[i].trust_flags;
4648 d->trust_type = dom_list[i].trust_type;
4649 d->trust_attribs = dom_list[i].trust_attribs;
4651 break;
4655 TALLOC_FREE( dom_list );
4657 return d;
4660 /*********************************************************************
4661 ********************************************************************/
4663 struct winbindd_tdc_domain*
4664 wcache_tdc_fetch_domainbysid(TALLOC_CTX *ctx,
4665 const struct dom_sid *sid)
4667 struct winbindd_tdc_domain *dom_list = NULL;
4668 size_t num_domains = 0;
4669 int i;
4670 struct winbindd_tdc_domain *d = NULL;
4672 DEBUG(10,("wcache_tdc_fetch_domainbysid: Searching for domain %s\n",
4673 sid_string_dbg(sid)));
4675 if (!init_wcache()) {
4676 return false;
4679 /* fetch the list */
4681 wcache_tdc_fetch_list(&dom_list, &num_domains);
4683 for (i = 0; i<num_domains; i++) {
4684 if (sid_equal(sid, &(dom_list[i].sid))) {
4685 DEBUG(10, ("wcache_tdc_fetch_domainbysid: "
4686 "Found domain %s for SID %s\n",
4687 dom_list[i].domain_name,
4688 sid_string_dbg(sid)));
4690 d = TALLOC_P(ctx, struct winbindd_tdc_domain);
4691 if (!d)
4692 break;
4694 d->domain_name = talloc_strdup(d,
4695 dom_list[i].domain_name);
4697 d->dns_name = talloc_strdup(d, dom_list[i].dns_name);
4698 sid_copy(&d->sid, &dom_list[i].sid);
4699 d->trust_flags = dom_list[i].trust_flags;
4700 d->trust_type = dom_list[i].trust_type;
4701 d->trust_attribs = dom_list[i].trust_attribs;
4703 break;
4707 TALLOC_FREE(dom_list);
4709 return d;
4713 /*********************************************************************
4714 ********************************************************************/
4716 void wcache_tdc_clear( void )
4718 if ( !init_wcache() )
4719 return;
4721 wcache_tdc_store_list( NULL, 0 );
4723 return;
4727 /*********************************************************************
4728 ********************************************************************/
4730 static void wcache_save_user_pwinfo(struct winbindd_domain *domain,
4731 NTSTATUS status,
4732 const struct dom_sid *user_sid,
4733 const char *homedir,
4734 const char *shell,
4735 const char *gecos,
4736 uint32 gid)
4738 struct cache_entry *centry;
4739 fstring tmp;
4741 if ( (centry = centry_start(domain, status)) == NULL )
4742 return;
4744 centry_put_string( centry, homedir );
4745 centry_put_string( centry, shell );
4746 centry_put_string( centry, gecos );
4747 centry_put_uint32( centry, gid );
4749 centry_end(centry, "NSS/PWINFO/%s", sid_to_fstring(tmp, user_sid) );
4751 DEBUG(10,("wcache_save_user_pwinfo: %s\n", sid_string_dbg(user_sid) ));
4753 centry_free(centry);
4756 #ifdef HAVE_ADS
4758 NTSTATUS nss_get_info_cached( struct winbindd_domain *domain,
4759 const struct dom_sid *user_sid,
4760 TALLOC_CTX *ctx,
4761 const char **homedir, const char **shell,
4762 const char **gecos, gid_t *p_gid)
4764 struct winbind_cache *cache = get_cache(domain);
4765 struct cache_entry *centry = NULL;
4766 NTSTATUS nt_status;
4767 fstring tmp;
4769 if (!cache->tdb)
4770 goto do_query;
4772 centry = wcache_fetch(cache, domain, "NSS/PWINFO/%s",
4773 sid_to_fstring(tmp, user_sid));
4775 if (!centry)
4776 goto do_query;
4778 *homedir = centry_string( centry, ctx );
4779 *shell = centry_string( centry, ctx );
4780 *gecos = centry_string( centry, ctx );
4781 *p_gid = centry_uint32( centry );
4783 centry_free(centry);
4785 DEBUG(10,("nss_get_info_cached: [Cached] - user_sid %s\n",
4786 sid_string_dbg(user_sid)));
4788 return NT_STATUS_OK;
4790 do_query:
4792 nt_status = nss_get_info( domain->name, user_sid, ctx,
4793 homedir, shell, gecos, p_gid );
4795 DEBUG(10, ("nss_get_info returned %s\n", nt_errstr(nt_status)));
4797 if ( NT_STATUS_IS_OK(nt_status) ) {
4798 DEBUG(10, ("result:\n\thomedir = '%s'\n", *homedir));
4799 DEBUGADD(10, ("\tshell = '%s'\n", *shell));
4800 DEBUGADD(10, ("\tgecos = '%s'\n", *gecos));
4801 DEBUGADD(10, ("\tgid = '%u'\n", (unsigned int)*p_gid));
4803 wcache_save_user_pwinfo( domain, nt_status, user_sid,
4804 *homedir, *shell, *gecos, *p_gid );
4807 if ( NT_STATUS_EQUAL( nt_status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND ) ) {
4808 DEBUG(5,("nss_get_info_cached: Setting domain %s offline\n",
4809 domain->name ));
4810 set_domain_offline( domain );
4813 return nt_status;
4816 #endif
4818 /* the cache backend methods are exposed via this structure */
4819 struct winbindd_methods cache_methods = {
4820 true,
4821 query_user_list,
4822 enum_dom_groups,
4823 enum_local_groups,
4824 name_to_sid,
4825 sid_to_name,
4826 rids_to_names,
4827 query_user,
4828 lookup_usergroups,
4829 lookup_useraliases,
4830 lookup_groupmem,
4831 sequence_number,
4832 lockout_policy,
4833 password_policy,
4834 trusted_domains
4837 static bool wcache_ndr_key(TALLOC_CTX *mem_ctx, char *domain_name,
4838 uint32_t opnum, const DATA_BLOB *req,
4839 TDB_DATA *pkey)
4841 char *key;
4842 size_t keylen;
4844 key = talloc_asprintf(mem_ctx, "NDR/%s/%d/", domain_name, (int)opnum);
4845 if (key == NULL) {
4846 return false;
4848 keylen = talloc_get_size(key) - 1;
4850 key = talloc_realloc(mem_ctx, key, char, keylen + req->length);
4851 if (key == NULL) {
4852 return false;
4854 memcpy(key + keylen, req->data, req->length);
4856 pkey->dptr = (uint8_t *)key;
4857 pkey->dsize = talloc_get_size(key);
4858 return true;
4861 static bool wcache_opnum_cacheable(uint32_t opnum)
4863 switch (opnum) {
4864 case NDR_WBINT_PING:
4865 case NDR_WBINT_QUERYSEQUENCENUMBER:
4866 case NDR_WBINT_ALLOCATEUID:
4867 case NDR_WBINT_ALLOCATEGID:
4868 case NDR_WBINT_CHECKMACHINEACCOUNT:
4869 case NDR_WBINT_CHANGEMACHINEACCOUNT:
4870 case NDR_WBINT_PINGDC:
4871 return false;
4873 return true;
4876 bool wcache_fetch_ndr(TALLOC_CTX *mem_ctx, struct winbindd_domain *domain,
4877 uint32_t opnum, const DATA_BLOB *req, DATA_BLOB *resp)
4879 TDB_DATA key, data;
4880 bool ret = false;
4882 if (!wcache_opnum_cacheable(opnum) ||
4883 is_my_own_sam_domain(domain) ||
4884 is_builtin_domain(domain)) {
4885 return false;
4888 if (wcache->tdb == NULL) {
4889 return false;
4892 if (!wcache_ndr_key(talloc_tos(), domain->name, opnum, req, &key)) {
4893 return false;
4895 data = tdb_fetch(wcache->tdb, key);
4896 TALLOC_FREE(key.dptr);
4898 if (data.dptr == NULL) {
4899 return false;
4901 if (data.dsize < 12) {
4902 goto fail;
4905 if (!is_domain_offline(domain)) {
4906 uint32_t entry_seqnum, dom_seqnum, last_check;
4907 uint64_t entry_timeout;
4909 if (!wcache_fetch_seqnum(domain->name, &dom_seqnum,
4910 &last_check)) {
4911 goto fail;
4913 entry_seqnum = IVAL(data.dptr, 0);
4914 if (entry_seqnum != dom_seqnum) {
4915 DEBUG(10, ("Entry has wrong sequence number: %d\n",
4916 (int)entry_seqnum));
4917 goto fail;
4919 entry_timeout = BVAL(data.dptr, 4);
4920 if (time(NULL) > entry_timeout) {
4921 DEBUG(10, ("Entry has timed out\n"));
4922 goto fail;
4926 resp->data = (uint8_t *)talloc_memdup(mem_ctx, data.dptr + 12,
4927 data.dsize - 12);
4928 if (resp->data == NULL) {
4929 DEBUG(10, ("talloc failed\n"));
4930 goto fail;
4932 resp->length = data.dsize - 12;
4934 ret = true;
4935 fail:
4936 SAFE_FREE(data.dptr);
4937 return ret;
4940 void wcache_store_ndr(struct winbindd_domain *domain, uint32_t opnum,
4941 const DATA_BLOB *req, const DATA_BLOB *resp)
4943 TDB_DATA key, data;
4944 uint32_t dom_seqnum, last_check;
4945 uint64_t timeout;
4947 if (!wcache_opnum_cacheable(opnum) ||
4948 is_my_own_sam_domain(domain) ||
4949 is_builtin_domain(domain)) {
4950 return;
4953 if (wcache->tdb == NULL) {
4954 return;
4957 if (!wcache_fetch_seqnum(domain->name, &dom_seqnum, &last_check)) {
4958 DEBUG(10, ("could not fetch seqnum for domain %s\n",
4959 domain->name));
4960 return;
4963 if (!wcache_ndr_key(talloc_tos(), domain->name, opnum, req, &key)) {
4964 return;
4967 timeout = time(NULL) + lp_winbind_cache_time();
4969 data.dsize = resp->length + 12;
4970 data.dptr = talloc_array(key.dptr, uint8_t, data.dsize);
4971 if (data.dptr == NULL) {
4972 goto done;
4975 SIVAL(data.dptr, 0, dom_seqnum);
4976 SBVAL(data.dptr, 4, timeout);
4977 memcpy(data.dptr + 12, resp->data, resp->length);
4979 tdb_store(wcache->tdb, key, data, 0);
4981 done:
4982 TALLOC_FREE(key.dptr);
4983 return;