s3-rpc_server: Give log messages on failure
[Samba.git] / source3 / winbindd / winbindd_cache.c
blob9c4b5bd4e82bd884f414ba335ddf1303230c0bcb
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 "WINBINDD_OFFLINE",
63 WINBINDD_CACHE_VERSION_KEYSTR,
64 NULL
67 /************************************************************************
68 Is this key a non-centry type ?
69 ************************************************************************/
71 static bool is_non_centry_key(TDB_DATA kbuf)
73 int i;
75 if (kbuf.dptr == NULL || kbuf.dsize == 0) {
76 return false;
78 for (i = 0; non_centry_keys[i] != NULL; i++) {
79 size_t namelen = strlen(non_centry_keys[i]);
80 if (kbuf.dsize < namelen) {
81 continue;
83 if (strncmp(non_centry_keys[i], (const char *)kbuf.dptr, namelen) == 0) {
84 return true;
87 return false;
90 /* Global online/offline state - False when online. winbindd starts up online
91 and sets this to true if the first query fails and there's an entry in
92 the cache tdb telling us to stay offline. */
94 static bool global_winbindd_offline_state;
96 struct winbind_cache {
97 TDB_CONTEXT *tdb;
100 struct cache_entry {
101 NTSTATUS status;
102 uint32 sequence_number;
103 uint64_t timeout;
104 uint8 *data;
105 uint32 len, ofs;
108 void (*smb_panic_fn)(const char *const why) = smb_panic;
110 #define WINBINDD_MAX_CACHE_SIZE (50*1024*1024)
112 static struct winbind_cache *wcache;
114 /* get the winbind_cache structure */
115 static struct winbind_cache *get_cache(struct winbindd_domain *domain)
117 struct winbind_cache *ret = wcache;
119 /* We have to know what type of domain we are dealing with first. */
121 if (domain->internal) {
122 domain->backend = &builtin_passdb_methods;
123 domain->initialized = True;
126 if (strequal(domain->name, get_global_sam_name()) &&
127 sid_check_is_our_sam(&domain->sid)) {
128 domain->backend = &sam_passdb_methods;
129 domain->initialized = True;
132 if ( !domain->initialized ) {
133 init_dc_connection( domain );
137 OK. listen up becasue I'm only going to say this once.
138 We have the following scenarios to consider
139 (a) trusted AD domains on a Samba DC,
140 (b) trusted AD domains and we are joined to a non-kerberos domain
141 (c) trusted AD domains and we are joined to a kerberos (AD) domain
143 For (a) we can always contact the trusted domain using krb5
144 since we have the domain trust account password
146 For (b) we can only use RPC since we have no way of
147 getting a krb5 ticket in our own domain
149 For (c) we can always use krb5 since we have a kerberos trust
151 --jerry
154 if (!domain->backend) {
155 #ifdef HAVE_ADS
156 struct winbindd_domain *our_domain = domain;
158 /* find our domain first so we can figure out if we
159 are joined to a kerberized domain */
161 if ( !domain->primary )
162 our_domain = find_our_domain();
164 if ((our_domain->active_directory || IS_DC)
165 && domain->active_directory
166 && !lp_winbind_rpc_only()) {
167 DEBUG(5,("get_cache: Setting ADS methods for domain %s\n", domain->name));
168 domain->backend = &ads_methods;
169 } else {
170 #endif /* HAVE_ADS */
171 DEBUG(5,("get_cache: Setting MS-RPC methods for domain %s\n", domain->name));
172 domain->backend = &reconnect_methods;
173 #ifdef HAVE_ADS
175 #endif /* HAVE_ADS */
178 if (ret)
179 return ret;
181 ret = SMB_XMALLOC_P(struct winbind_cache);
182 ZERO_STRUCTP(ret);
184 wcache = ret;
185 wcache_flush_cache();
187 return ret;
191 free a centry structure
193 static void centry_free(struct cache_entry *centry)
195 if (!centry)
196 return;
197 SAFE_FREE(centry->data);
198 free(centry);
201 static bool centry_check_bytes(struct cache_entry *centry, size_t nbytes)
203 if (centry->len - centry->ofs < nbytes) {
204 DEBUG(0,("centry corruption? needed %u bytes, have %d\n",
205 (unsigned int)nbytes,
206 centry->len - centry->ofs));
207 return false;
209 return true;
213 pull a uint64_t from a cache entry
215 static uint64_t centry_uint64_t(struct cache_entry *centry)
217 uint64_t ret;
219 if (!centry_check_bytes(centry, 8)) {
220 smb_panic_fn("centry_uint64_t");
222 ret = BVAL(centry->data, centry->ofs);
223 centry->ofs += 8;
224 return ret;
228 pull a uint32 from a cache entry
230 static uint32 centry_uint32(struct cache_entry *centry)
232 uint32 ret;
234 if (!centry_check_bytes(centry, 4)) {
235 smb_panic_fn("centry_uint32");
237 ret = IVAL(centry->data, centry->ofs);
238 centry->ofs += 4;
239 return ret;
243 pull a uint16 from a cache entry
245 static uint16 centry_uint16(struct cache_entry *centry)
247 uint16 ret;
248 if (!centry_check_bytes(centry, 2)) {
249 smb_panic_fn("centry_uint16");
251 ret = SVAL(centry->data, centry->ofs);
252 centry->ofs += 2;
253 return ret;
257 pull a uint8 from a cache entry
259 static uint8 centry_uint8(struct cache_entry *centry)
261 uint8 ret;
262 if (!centry_check_bytes(centry, 1)) {
263 smb_panic_fn("centry_uint8");
265 ret = CVAL(centry->data, centry->ofs);
266 centry->ofs += 1;
267 return ret;
271 pull a NTTIME from a cache entry
273 static NTTIME centry_nttime(struct cache_entry *centry)
275 NTTIME ret;
276 if (!centry_check_bytes(centry, 8)) {
277 smb_panic_fn("centry_nttime");
279 ret = IVAL(centry->data, centry->ofs);
280 centry->ofs += 4;
281 ret += (uint64)IVAL(centry->data, centry->ofs) << 32;
282 centry->ofs += 4;
283 return ret;
287 pull a time_t from a cache entry. time_t stored portably as a 64-bit time.
289 static time_t centry_time(struct cache_entry *centry)
291 return (time_t)centry_nttime(centry);
294 /* pull a string from a cache entry, using the supplied
295 talloc context
297 static char *centry_string(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
299 uint32 len;
300 char *ret;
302 len = centry_uint8(centry);
304 if (len == 0xFF) {
305 /* a deliberate NULL string */
306 return NULL;
309 if (!centry_check_bytes(centry, (size_t)len)) {
310 smb_panic_fn("centry_string");
313 ret = talloc_array(mem_ctx, char, len+1);
314 if (!ret) {
315 smb_panic_fn("centry_string out of memory\n");
317 memcpy(ret,centry->data + centry->ofs, len);
318 ret[len] = 0;
319 centry->ofs += len;
320 return ret;
323 /* pull a hash16 from a cache entry, using the supplied
324 talloc context
326 static char *centry_hash16(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
328 uint32 len;
329 char *ret;
331 len = centry_uint8(centry);
333 if (len != 16) {
334 DEBUG(0,("centry corruption? hash len (%u) != 16\n",
335 len ));
336 return NULL;
339 if (!centry_check_bytes(centry, 16)) {
340 return NULL;
343 ret = talloc_array(mem_ctx, char, 16);
344 if (!ret) {
345 smb_panic_fn("centry_hash out of memory\n");
347 memcpy(ret,centry->data + centry->ofs, 16);
348 centry->ofs += 16;
349 return ret;
352 /* pull a sid from a cache entry, using the supplied
353 talloc context
355 static bool centry_sid(struct cache_entry *centry, struct dom_sid *sid)
357 char *sid_string;
358 bool ret;
360 sid_string = centry_string(centry, talloc_tos());
361 if (sid_string == NULL) {
362 return false;
364 ret = string_to_sid(sid, sid_string);
365 TALLOC_FREE(sid_string);
366 return ret;
371 pull a NTSTATUS from a cache entry
373 static NTSTATUS centry_ntstatus(struct cache_entry *centry)
375 NTSTATUS status;
377 status = NT_STATUS(centry_uint32(centry));
378 return status;
382 /* the server is considered down if it can't give us a sequence number */
383 static bool wcache_server_down(struct winbindd_domain *domain)
385 bool ret;
387 if (!wcache->tdb)
388 return false;
390 ret = (domain->sequence_number == DOM_SEQUENCE_NONE);
392 if (ret)
393 DEBUG(10,("wcache_server_down: server for Domain %s down\n",
394 domain->name ));
395 return ret;
398 static bool wcache_fetch_seqnum(const char *domain_name, uint32_t *seqnum,
399 uint32_t *last_seq_check)
401 char *key;
402 TDB_DATA data;
404 if (wcache->tdb == NULL) {
405 DEBUG(10,("wcache_fetch_seqnum: tdb == NULL\n"));
406 return false;
409 key = talloc_asprintf(talloc_tos(), "SEQNUM/%s", domain_name);
410 if (key == NULL) {
411 DEBUG(10, ("talloc failed\n"));
412 return false;
415 data = tdb_fetch_bystring(wcache->tdb, key);
416 TALLOC_FREE(key);
418 if (data.dptr == NULL) {
419 DEBUG(10, ("wcache_fetch_seqnum: %s not found\n",
420 domain_name));
421 return false;
423 if (data.dsize != 8) {
424 DEBUG(10, ("wcache_fetch_seqnum: invalid data size %d\n",
425 (int)data.dsize));
426 SAFE_FREE(data.dptr);
427 return false;
430 *seqnum = IVAL(data.dptr, 0);
431 *last_seq_check = IVAL(data.dptr, 4);
432 SAFE_FREE(data.dptr);
434 return true;
437 static NTSTATUS fetch_cache_seqnum( struct winbindd_domain *domain, time_t now )
439 uint32 last_check, time_diff;
441 if (!wcache_fetch_seqnum(domain->name, &domain->sequence_number,
442 &last_check)) {
443 return NT_STATUS_UNSUCCESSFUL;
445 domain->last_seq_check = last_check;
447 /* have we expired? */
449 time_diff = now - domain->last_seq_check;
450 if ( time_diff > lp_winbind_cache_time() ) {
451 DEBUG(10,("fetch_cache_seqnum: timeout [%s][%u @ %u]\n",
452 domain->name, domain->sequence_number,
453 (uint32)domain->last_seq_check));
454 return NT_STATUS_UNSUCCESSFUL;
457 DEBUG(10,("fetch_cache_seqnum: success [%s][%u @ %u]\n",
458 domain->name, domain->sequence_number,
459 (uint32)domain->last_seq_check));
461 return NT_STATUS_OK;
464 bool wcache_store_seqnum(const char *domain_name, uint32_t seqnum,
465 time_t last_seq_check)
467 char *key_str;
468 uint8_t buf[8];
469 int ret;
471 if (wcache->tdb == NULL) {
472 DEBUG(10, ("wcache_store_seqnum: wcache->tdb == NULL\n"));
473 return false;
476 key_str = talloc_asprintf(talloc_tos(), "SEQNUM/%s", domain_name);
477 if (key_str == NULL) {
478 DEBUG(10, ("talloc_asprintf failed\n"));
479 return false;
482 SIVAL(buf, 0, seqnum);
483 SIVAL(buf, 4, last_seq_check);
485 ret = tdb_store_bystring(wcache->tdb, key_str,
486 make_tdb_data(buf, sizeof(buf)), TDB_REPLACE);
487 TALLOC_FREE(key_str);
488 if (ret != 0) {
489 DEBUG(10, ("tdb_store_bystring failed: %s\n",
490 tdb_errorstr_compat(wcache->tdb)));
491 TALLOC_FREE(key_str);
492 return false;
495 DEBUG(10, ("wcache_store_seqnum: success [%s][%u @ %u]\n",
496 domain_name, seqnum, (unsigned)last_seq_check));
498 return true;
501 static bool store_cache_seqnum( struct winbindd_domain *domain )
503 return wcache_store_seqnum(domain->name, domain->sequence_number,
504 domain->last_seq_check);
508 refresh the domain sequence number. If force is true
509 then always refresh it, no matter how recently we fetched it
512 static void refresh_sequence_number(struct winbindd_domain *domain, bool force)
514 NTSTATUS status;
515 unsigned time_diff;
516 time_t t = time(NULL);
517 unsigned cache_time = lp_winbind_cache_time();
519 if (is_domain_offline(domain)) {
520 return;
523 get_cache( domain );
525 #if 0 /* JERRY -- disable as the default cache time is now 5 minutes */
526 /* trying to reconnect is expensive, don't do it too often */
527 if (domain->sequence_number == DOM_SEQUENCE_NONE) {
528 cache_time *= 8;
530 #endif
532 time_diff = t - domain->last_seq_check;
534 /* see if we have to refetch the domain sequence number */
535 if (!force && (time_diff < cache_time) &&
536 (domain->sequence_number != DOM_SEQUENCE_NONE) &&
537 NT_STATUS_IS_OK(domain->last_status)) {
538 DEBUG(10, ("refresh_sequence_number: %s time ok\n", domain->name));
539 goto done;
542 /* try to get the sequence number from the tdb cache first */
543 /* this will update the timestamp as well */
545 status = fetch_cache_seqnum( domain, t );
546 if (NT_STATUS_IS_OK(status) &&
547 (domain->sequence_number != DOM_SEQUENCE_NONE) &&
548 NT_STATUS_IS_OK(domain->last_status)) {
549 goto done;
552 /* important! make sure that we know if this is a native
553 mode domain or not. And that we can contact it. */
555 if ( winbindd_can_contact_domain( domain ) ) {
556 status = domain->backend->sequence_number(domain,
557 &domain->sequence_number);
558 } else {
559 /* just use the current time */
560 status = NT_STATUS_OK;
561 domain->sequence_number = time(NULL);
565 /* the above call could have set our domain->backend to NULL when
566 * coming from offline to online mode, make sure to reinitialize the
567 * backend - Guenther */
568 get_cache( domain );
570 if (!NT_STATUS_IS_OK(status)) {
571 DEBUG(10,("refresh_sequence_number: failed with %s\n", nt_errstr(status)));
572 domain->sequence_number = DOM_SEQUENCE_NONE;
575 domain->last_status = status;
576 domain->last_seq_check = time(NULL);
578 /* save the new sequence number in the cache */
579 store_cache_seqnum( domain );
581 done:
582 DEBUG(10, ("refresh_sequence_number: %s seq number is now %d\n",
583 domain->name, domain->sequence_number));
585 return;
589 decide if a cache entry has expired
591 static bool centry_expired(struct winbindd_domain *domain, const char *keystr, struct cache_entry *centry)
593 /* If we've been told to be offline - stay in that state... */
594 if (lp_winbind_offline_logon() && global_winbindd_offline_state) {
595 DEBUG(10,("centry_expired: Key %s for domain %s valid as winbindd is globally offline.\n",
596 keystr, domain->name ));
597 return false;
600 /* when the domain is offline return the cached entry.
601 * This deals with transient offline states... */
603 if (!domain->online) {
604 DEBUG(10,("centry_expired: Key %s for domain %s valid as domain is offline.\n",
605 keystr, domain->name ));
606 return false;
609 /* if the server is OK and our cache entry came from when it was down then
610 the entry is invalid */
611 if ((domain->sequence_number != DOM_SEQUENCE_NONE) &&
612 (centry->sequence_number == DOM_SEQUENCE_NONE)) {
613 DEBUG(10,("centry_expired: Key %s for domain %s invalid sequence.\n",
614 keystr, domain->name ));
615 return true;
618 /* if the server is down or the cache entry is not older than the
619 current sequence number or it did not timeout then it is OK */
620 if (wcache_server_down(domain)
621 || ((centry->sequence_number == domain->sequence_number)
622 && (centry->timeout > time(NULL)))) {
623 DEBUG(10,("centry_expired: Key %s for domain %s is good.\n",
624 keystr, domain->name ));
625 return false;
628 DEBUG(10,("centry_expired: Key %s for domain %s expired\n",
629 keystr, domain->name ));
631 /* it's expired */
632 return true;
635 static struct cache_entry *wcache_fetch_raw(char *kstr)
637 TDB_DATA data;
638 struct cache_entry *centry;
639 TDB_DATA key;
641 key = string_tdb_data(kstr);
642 data = tdb_fetch_compat(wcache->tdb, key);
643 if (!data.dptr) {
644 /* a cache miss */
645 return NULL;
648 centry = SMB_XMALLOC_P(struct cache_entry);
649 centry->data = (unsigned char *)data.dptr;
650 centry->len = data.dsize;
651 centry->ofs = 0;
653 if (centry->len < 16) {
654 /* huh? corrupt cache? */
655 DEBUG(10,("wcache_fetch_raw: Corrupt cache for key %s "
656 "(len < 16)?\n", kstr));
657 centry_free(centry);
658 return NULL;
661 centry->status = centry_ntstatus(centry);
662 centry->sequence_number = centry_uint32(centry);
663 centry->timeout = centry_uint64_t(centry);
665 return centry;
668 static bool is_my_own_sam_domain(struct winbindd_domain *domain)
670 if (strequal(domain->name, get_global_sam_name()) &&
671 sid_check_is_our_sam(&domain->sid)) {
672 return true;
675 return false;
678 static bool is_builtin_domain(struct winbindd_domain *domain)
680 if (strequal(domain->name, "BUILTIN") &&
681 sid_check_is_builtin(&domain->sid)) {
682 return true;
685 return false;
689 fetch an entry from the cache, with a varargs key. auto-fetch the sequence
690 number and return status
692 static struct cache_entry *wcache_fetch(struct winbind_cache *cache,
693 struct winbindd_domain *domain,
694 const char *format, ...) PRINTF_ATTRIBUTE(3,4);
695 static struct cache_entry *wcache_fetch(struct winbind_cache *cache,
696 struct winbindd_domain *domain,
697 const char *format, ...)
699 va_list ap;
700 char *kstr;
701 struct cache_entry *centry;
703 if (!winbindd_use_cache() ||
704 is_my_own_sam_domain(domain) ||
705 is_builtin_domain(domain)) {
706 return NULL;
709 refresh_sequence_number(domain, false);
711 va_start(ap, format);
712 smb_xvasprintf(&kstr, format, ap);
713 va_end(ap);
715 centry = wcache_fetch_raw(kstr);
716 if (centry == NULL) {
717 free(kstr);
718 return NULL;
721 if (centry_expired(domain, kstr, centry)) {
723 DEBUG(10,("wcache_fetch: entry %s expired for domain %s\n",
724 kstr, domain->name ));
726 centry_free(centry);
727 free(kstr);
728 return NULL;
731 DEBUG(10,("wcache_fetch: returning entry %s for domain %s\n",
732 kstr, domain->name ));
734 free(kstr);
735 return centry;
738 static void wcache_delete(const char *format, ...) PRINTF_ATTRIBUTE(1,2);
739 static void wcache_delete(const char *format, ...)
741 va_list ap;
742 char *kstr;
743 TDB_DATA key;
745 va_start(ap, format);
746 smb_xvasprintf(&kstr, format, ap);
747 va_end(ap);
749 key = string_tdb_data(kstr);
751 tdb_delete(wcache->tdb, key);
752 free(kstr);
756 make sure we have at least len bytes available in a centry
758 static void centry_expand(struct cache_entry *centry, uint32 len)
760 if (centry->len - centry->ofs >= len)
761 return;
762 centry->len *= 2;
763 centry->data = SMB_REALLOC_ARRAY(centry->data, unsigned char,
764 centry->len);
765 if (!centry->data) {
766 DEBUG(0,("out of memory: needed %d bytes in centry_expand\n", centry->len));
767 smb_panic_fn("out of memory in centry_expand");
772 push a uint64_t into a centry
774 static void centry_put_uint64_t(struct cache_entry *centry, uint64_t v)
776 centry_expand(centry, 8);
777 SBVAL(centry->data, centry->ofs, v);
778 centry->ofs += 8;
782 push a uint32 into a centry
784 static void centry_put_uint32(struct cache_entry *centry, uint32 v)
786 centry_expand(centry, 4);
787 SIVAL(centry->data, centry->ofs, v);
788 centry->ofs += 4;
792 push a uint16 into a centry
794 static void centry_put_uint16(struct cache_entry *centry, uint16 v)
796 centry_expand(centry, 2);
797 SSVAL(centry->data, centry->ofs, v);
798 centry->ofs += 2;
802 push a uint8 into a centry
804 static void centry_put_uint8(struct cache_entry *centry, uint8 v)
806 centry_expand(centry, 1);
807 SCVAL(centry->data, centry->ofs, v);
808 centry->ofs += 1;
812 push a string into a centry
814 static void centry_put_string(struct cache_entry *centry, const char *s)
816 int len;
818 if (!s) {
819 /* null strings are marked as len 0xFFFF */
820 centry_put_uint8(centry, 0xFF);
821 return;
824 len = strlen(s);
825 /* can't handle more than 254 char strings. Truncating is probably best */
826 if (len > 254) {
827 DEBUG(10,("centry_put_string: truncating len (%d) to: 254\n", len));
828 len = 254;
830 centry_put_uint8(centry, len);
831 centry_expand(centry, len);
832 memcpy(centry->data + centry->ofs, s, len);
833 centry->ofs += len;
837 push a 16 byte hash into a centry - treat as 16 byte string.
839 static void centry_put_hash16(struct cache_entry *centry, const uint8 val[16])
841 centry_put_uint8(centry, 16);
842 centry_expand(centry, 16);
843 memcpy(centry->data + centry->ofs, val, 16);
844 centry->ofs += 16;
847 static void centry_put_sid(struct cache_entry *centry, const struct dom_sid *sid)
849 fstring sid_string;
850 centry_put_string(centry, sid_to_fstring(sid_string, sid));
855 put NTSTATUS into a centry
857 static void centry_put_ntstatus(struct cache_entry *centry, NTSTATUS status)
859 uint32 status_value = NT_STATUS_V(status);
860 centry_put_uint32(centry, status_value);
865 push a NTTIME into a centry
867 static void centry_put_nttime(struct cache_entry *centry, NTTIME nt)
869 centry_expand(centry, 8);
870 SIVAL(centry->data, centry->ofs, nt & 0xFFFFFFFF);
871 centry->ofs += 4;
872 SIVAL(centry->data, centry->ofs, nt >> 32);
873 centry->ofs += 4;
877 push a time_t into a centry - use a 64 bit size.
878 NTTIME here is being used as a convenient 64-bit size.
880 static void centry_put_time(struct cache_entry *centry, time_t t)
882 NTTIME nt = (NTTIME)t;
883 centry_put_nttime(centry, nt);
887 start a centry for output. When finished, call centry_end()
889 static struct cache_entry *centry_start(struct winbindd_domain *domain,
890 NTSTATUS status)
892 struct cache_entry *centry;
894 if (!wcache->tdb)
895 return NULL;
897 centry = SMB_XMALLOC_P(struct cache_entry);
899 centry->len = 8192; /* reasonable default */
900 centry->data = SMB_XMALLOC_ARRAY(uint8, centry->len);
901 centry->ofs = 0;
902 centry->sequence_number = domain->sequence_number;
903 centry->timeout = lp_winbind_cache_time() + time(NULL);
904 centry_put_ntstatus(centry, status);
905 centry_put_uint32(centry, centry->sequence_number);
906 centry_put_uint64_t(centry, centry->timeout);
907 return centry;
911 finish a centry and write it to the tdb
913 static void centry_end(struct cache_entry *centry, const char *format, ...) PRINTF_ATTRIBUTE(2,3);
914 static void centry_end(struct cache_entry *centry, const char *format, ...)
916 va_list ap;
917 char *kstr;
918 TDB_DATA key, data;
920 if (!winbindd_use_cache()) {
921 return;
924 va_start(ap, format);
925 smb_xvasprintf(&kstr, format, ap);
926 va_end(ap);
928 key = string_tdb_data(kstr);
929 data.dptr = centry->data;
930 data.dsize = centry->ofs;
932 tdb_store(wcache->tdb, key, data, TDB_REPLACE);
933 free(kstr);
936 static void wcache_save_name_to_sid(struct winbindd_domain *domain,
937 NTSTATUS status, const char *domain_name,
938 const char *name, const struct dom_sid *sid,
939 enum lsa_SidType type)
941 struct cache_entry *centry;
942 fstring uname;
944 centry = centry_start(domain, status);
945 if (!centry)
946 return;
948 if ((domain_name == NULL) || (domain_name[0] == '\0')) {
949 struct winbindd_domain *mydomain =
950 find_domain_from_sid_noinit(sid);
951 if (mydomain != NULL) {
952 domain_name = mydomain->name;
956 centry_put_uint32(centry, type);
957 centry_put_sid(centry, sid);
958 fstrcpy(uname, name);
959 (void)strupper_m(uname);
960 centry_end(centry, "NS/%s/%s", domain_name, uname);
961 DEBUG(10,("wcache_save_name_to_sid: %s\\%s -> %s (%s)\n", domain_name,
962 uname, sid_string_dbg(sid), nt_errstr(status)));
963 centry_free(centry);
966 static void wcache_save_sid_to_name(struct winbindd_domain *domain, NTSTATUS status,
967 const struct dom_sid *sid, const char *domain_name, const char *name, enum lsa_SidType type)
969 struct cache_entry *centry;
970 fstring sid_string;
972 centry = centry_start(domain, status);
973 if (!centry)
974 return;
976 if ((domain_name == NULL) || (domain_name[0] == '\0')) {
977 struct winbindd_domain *mydomain =
978 find_domain_from_sid_noinit(sid);
979 if (mydomain != NULL) {
980 domain_name = mydomain->name;
984 if (NT_STATUS_IS_OK(status)) {
985 centry_put_uint32(centry, type);
986 centry_put_string(centry, domain_name);
987 centry_put_string(centry, name);
990 centry_end(centry, "SN/%s", sid_to_fstring(sid_string, sid));
991 DEBUG(10,("wcache_save_sid_to_name: %s -> %s\\%s (%s)\n", sid_string,
992 domain_name, name, nt_errstr(status)));
993 centry_free(centry);
997 static void wcache_save_user(struct winbindd_domain *domain, NTSTATUS status,
998 struct wbint_userinfo *info)
1000 struct cache_entry *centry;
1001 fstring sid_string;
1003 if (is_null_sid(&info->user_sid)) {
1004 return;
1007 centry = centry_start(domain, status);
1008 if (!centry)
1009 return;
1010 centry_put_string(centry, info->acct_name);
1011 centry_put_string(centry, info->full_name);
1012 centry_put_string(centry, info->homedir);
1013 centry_put_string(centry, info->shell);
1014 centry_put_uint32(centry, info->primary_gid);
1015 centry_put_sid(centry, &info->user_sid);
1016 centry_put_sid(centry, &info->group_sid);
1017 centry_end(centry, "U/%s", sid_to_fstring(sid_string,
1018 &info->user_sid));
1019 DEBUG(10,("wcache_save_user: %s (acct_name %s)\n", sid_string, info->acct_name));
1020 centry_free(centry);
1023 static void wcache_save_lockout_policy(struct winbindd_domain *domain,
1024 NTSTATUS status,
1025 struct samr_DomInfo12 *lockout_policy)
1027 struct cache_entry *centry;
1029 centry = centry_start(domain, status);
1030 if (!centry)
1031 return;
1033 centry_put_nttime(centry, lockout_policy->lockout_duration);
1034 centry_put_nttime(centry, lockout_policy->lockout_window);
1035 centry_put_uint16(centry, lockout_policy->lockout_threshold);
1037 centry_end(centry, "LOC_POL/%s", domain->name);
1039 DEBUG(10,("wcache_save_lockout_policy: %s\n", domain->name));
1041 centry_free(centry);
1046 static void wcache_save_password_policy(struct winbindd_domain *domain,
1047 NTSTATUS status,
1048 struct samr_DomInfo1 *policy)
1050 struct cache_entry *centry;
1052 centry = centry_start(domain, status);
1053 if (!centry)
1054 return;
1056 centry_put_uint16(centry, policy->min_password_length);
1057 centry_put_uint16(centry, policy->password_history_length);
1058 centry_put_uint32(centry, policy->password_properties);
1059 centry_put_nttime(centry, policy->max_password_age);
1060 centry_put_nttime(centry, policy->min_password_age);
1062 centry_end(centry, "PWD_POL/%s", domain->name);
1064 DEBUG(10,("wcache_save_password_policy: %s\n", domain->name));
1066 centry_free(centry);
1069 /***************************************************************************
1070 ***************************************************************************/
1072 static void wcache_save_username_alias(struct winbindd_domain *domain,
1073 NTSTATUS status,
1074 const char *name, const char *alias)
1076 struct cache_entry *centry;
1077 fstring uname;
1079 if ( (centry = centry_start(domain, status)) == NULL )
1080 return;
1082 centry_put_string( centry, alias );
1084 fstrcpy(uname, name);
1085 (void)strupper_m(uname);
1086 centry_end(centry, "NSS/NA/%s", uname);
1088 DEBUG(10,("wcache_save_username_alias: %s -> %s\n", name, alias ));
1090 centry_free(centry);
1093 static void wcache_save_alias_username(struct winbindd_domain *domain,
1094 NTSTATUS status,
1095 const char *alias, const char *name)
1097 struct cache_entry *centry;
1098 fstring uname;
1100 if ( (centry = centry_start(domain, status)) == NULL )
1101 return;
1103 centry_put_string( centry, name );
1105 fstrcpy(uname, alias);
1106 (void)strupper_m(uname);
1107 centry_end(centry, "NSS/AN/%s", uname);
1109 DEBUG(10,("wcache_save_alias_username: %s -> %s\n", alias, name ));
1111 centry_free(centry);
1114 /***************************************************************************
1115 ***************************************************************************/
1117 NTSTATUS resolve_username_to_alias( TALLOC_CTX *mem_ctx,
1118 struct winbindd_domain *domain,
1119 const char *name, char **alias )
1121 struct winbind_cache *cache = get_cache(domain);
1122 struct cache_entry *centry = NULL;
1123 NTSTATUS status;
1124 char *upper_name;
1126 if ( domain->internal )
1127 return NT_STATUS_NOT_SUPPORTED;
1129 if (!cache->tdb)
1130 goto do_query;
1132 upper_name = talloc_strdup(mem_ctx, name);
1133 if (upper_name == NULL) {
1134 return NT_STATUS_NO_MEMORY;
1136 if (!strupper_m(upper_name)) {
1137 talloc_free(upper_name);
1138 return NT_STATUS_INVALID_PARAMETER;
1141 centry = wcache_fetch(cache, domain, "NSS/NA/%s", upper_name);
1143 talloc_free(upper_name);
1145 if (!centry)
1146 goto do_query;
1148 status = centry->status;
1150 if (!NT_STATUS_IS_OK(status)) {
1151 centry_free(centry);
1152 return status;
1155 *alias = centry_string( centry, mem_ctx );
1157 centry_free(centry);
1159 DEBUG(10,("resolve_username_to_alias: [Cached] - mapped %s to %s\n",
1160 name, *alias ? *alias : "(none)"));
1162 return (*alias) ? NT_STATUS_OK : NT_STATUS_OBJECT_NAME_NOT_FOUND;
1164 do_query:
1166 /* If its not in cache and we are offline, then fail */
1168 if ( get_global_winbindd_state_offline() || !domain->online ) {
1169 DEBUG(8,("resolve_username_to_alias: rejecting query "
1170 "in offline mode\n"));
1171 return NT_STATUS_NOT_FOUND;
1174 status = nss_map_to_alias( mem_ctx, domain->name, name, alias );
1176 if ( NT_STATUS_IS_OK( status ) ) {
1177 wcache_save_username_alias(domain, status, name, *alias);
1180 if ( NT_STATUS_EQUAL( status, NT_STATUS_NONE_MAPPED ) ) {
1181 wcache_save_username_alias(domain, status, name, "(NULL)");
1184 DEBUG(5,("resolve_username_to_alias: backend query returned %s\n",
1185 nt_errstr(status)));
1187 if ( NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ) {
1188 set_domain_offline( domain );
1191 return status;
1194 /***************************************************************************
1195 ***************************************************************************/
1197 NTSTATUS resolve_alias_to_username( TALLOC_CTX *mem_ctx,
1198 struct winbindd_domain *domain,
1199 const char *alias, char **name )
1201 struct winbind_cache *cache = get_cache(domain);
1202 struct cache_entry *centry = NULL;
1203 NTSTATUS status;
1204 char *upper_name;
1206 if ( domain->internal )
1207 return NT_STATUS_NOT_SUPPORTED;
1209 if (!cache->tdb)
1210 goto do_query;
1212 upper_name = talloc_strdup(mem_ctx, alias);
1213 if (upper_name == NULL) {
1214 return NT_STATUS_NO_MEMORY;
1216 if (!strupper_m(upper_name)) {
1217 talloc_free(upper_name);
1218 return NT_STATUS_INVALID_PARAMETER;
1221 centry = wcache_fetch(cache, domain, "NSS/AN/%s", upper_name);
1223 talloc_free(upper_name);
1225 if (!centry)
1226 goto do_query;
1228 status = centry->status;
1230 if (!NT_STATUS_IS_OK(status)) {
1231 centry_free(centry);
1232 return status;
1235 *name = centry_string( centry, mem_ctx );
1237 centry_free(centry);
1239 DEBUG(10,("resolve_alias_to_username: [Cached] - mapped %s to %s\n",
1240 alias, *name ? *name : "(none)"));
1242 return (*name) ? NT_STATUS_OK : NT_STATUS_OBJECT_NAME_NOT_FOUND;
1244 do_query:
1246 /* If its not in cache and we are offline, then fail */
1248 if ( get_global_winbindd_state_offline() || !domain->online ) {
1249 DEBUG(8,("resolve_alias_to_username: rejecting query "
1250 "in offline mode\n"));
1251 return NT_STATUS_NOT_FOUND;
1254 /* an alias cannot contain a domain prefix or '@' */
1256 if (strchr(alias, '\\') || strchr(alias, '@')) {
1257 DEBUG(10,("resolve_alias_to_username: skipping fully "
1258 "qualified name %s\n", alias));
1259 return NT_STATUS_OBJECT_NAME_INVALID;
1262 status = nss_map_from_alias( mem_ctx, domain->name, alias, name );
1264 if ( NT_STATUS_IS_OK( status ) ) {
1265 wcache_save_alias_username( domain, status, alias, *name );
1268 if (NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) {
1269 wcache_save_alias_username(domain, status, alias, "(NULL)");
1272 DEBUG(5,("resolve_alias_to_username: backend query returned %s\n",
1273 nt_errstr(status)));
1275 if ( NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ) {
1276 set_domain_offline( domain );
1279 return status;
1282 NTSTATUS wcache_cached_creds_exist(struct winbindd_domain *domain, const struct dom_sid *sid)
1284 struct winbind_cache *cache = get_cache(domain);
1285 TDB_DATA data;
1286 fstring key_str, tmp;
1287 uint32 rid;
1289 if (!cache->tdb) {
1290 return NT_STATUS_INTERNAL_DB_ERROR;
1293 if (is_null_sid(sid)) {
1294 return NT_STATUS_INVALID_SID;
1297 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1298 return NT_STATUS_INVALID_SID;
1301 fstr_sprintf(key_str, "CRED/%s", sid_to_fstring(tmp, sid));
1303 data = tdb_fetch_compat(cache->tdb, string_tdb_data(key_str));
1304 if (!data.dptr) {
1305 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1308 SAFE_FREE(data.dptr);
1309 return NT_STATUS_OK;
1312 /* Lookup creds for a SID - copes with old (unsalted) creds as well
1313 as new salted ones. */
1315 NTSTATUS wcache_get_creds(struct winbindd_domain *domain,
1316 TALLOC_CTX *mem_ctx,
1317 const struct dom_sid *sid,
1318 const uint8 **cached_nt_pass,
1319 const uint8 **cached_salt)
1321 struct winbind_cache *cache = get_cache(domain);
1322 struct cache_entry *centry = NULL;
1323 NTSTATUS status;
1324 uint32 rid;
1325 fstring tmp;
1327 if (!cache->tdb) {
1328 return NT_STATUS_INTERNAL_DB_ERROR;
1331 if (is_null_sid(sid)) {
1332 return NT_STATUS_INVALID_SID;
1335 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1336 return NT_STATUS_INVALID_SID;
1339 /* Try and get a salted cred first. If we can't
1340 fall back to an unsalted cred. */
1342 centry = wcache_fetch(cache, domain, "CRED/%s",
1343 sid_to_fstring(tmp, sid));
1344 if (!centry) {
1345 DEBUG(10,("wcache_get_creds: entry for [CRED/%s] not found\n",
1346 sid_string_dbg(sid)));
1347 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1351 * We don't use the time element at this moment,
1352 * but we have to consume it, so that we don't
1353 * neet to change the disk format of the cache.
1355 (void)centry_time(centry);
1357 /* In the salted case this isn't actually the nt_hash itself,
1358 but the MD5 of the salt + nt_hash. Let the caller
1359 sort this out. It can tell as we only return the cached_salt
1360 if we are returning a salted cred. */
1362 *cached_nt_pass = (const uint8 *)centry_hash16(centry, mem_ctx);
1363 if (*cached_nt_pass == NULL) {
1364 fstring sidstr;
1366 sid_to_fstring(sidstr, sid);
1368 /* Bad (old) cred cache. Delete and pretend we
1369 don't have it. */
1370 DEBUG(0,("wcache_get_creds: bad entry for [CRED/%s] - deleting\n",
1371 sidstr));
1372 wcache_delete("CRED/%s", sidstr);
1373 centry_free(centry);
1374 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1377 /* We only have 17 bytes more data in the salted cred case. */
1378 if (centry->len - centry->ofs == 17) {
1379 *cached_salt = (const uint8 *)centry_hash16(centry, mem_ctx);
1380 } else {
1381 *cached_salt = NULL;
1384 dump_data_pw("cached_nt_pass", *cached_nt_pass, NT_HASH_LEN);
1385 if (*cached_salt) {
1386 dump_data_pw("cached_salt", *cached_salt, NT_HASH_LEN);
1389 status = centry->status;
1391 DEBUG(10,("wcache_get_creds: [Cached] - cached creds for user %s status: %s\n",
1392 sid_string_dbg(sid), nt_errstr(status) ));
1394 centry_free(centry);
1395 return status;
1398 /* Store creds for a SID - only writes out new salted ones. */
1400 NTSTATUS wcache_save_creds(struct winbindd_domain *domain,
1401 const struct dom_sid *sid,
1402 const uint8 nt_pass[NT_HASH_LEN])
1404 struct cache_entry *centry;
1405 fstring sid_string;
1406 uint32 rid;
1407 uint8 cred_salt[NT_HASH_LEN];
1408 uint8 salted_hash[NT_HASH_LEN];
1410 if (is_null_sid(sid)) {
1411 return NT_STATUS_INVALID_SID;
1414 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1415 return NT_STATUS_INVALID_SID;
1418 centry = centry_start(domain, NT_STATUS_OK);
1419 if (!centry) {
1420 return NT_STATUS_INTERNAL_DB_ERROR;
1423 dump_data_pw("nt_pass", nt_pass, NT_HASH_LEN);
1425 centry_put_time(centry, time(NULL));
1427 /* Create a salt and then salt the hash. */
1428 generate_random_buffer(cred_salt, NT_HASH_LEN);
1429 E_md5hash(cred_salt, nt_pass, salted_hash);
1431 centry_put_hash16(centry, salted_hash);
1432 centry_put_hash16(centry, cred_salt);
1433 centry_end(centry, "CRED/%s", sid_to_fstring(sid_string, sid));
1435 DEBUG(10,("wcache_save_creds: %s\n", sid_string));
1437 centry_free(centry);
1439 return NT_STATUS_OK;
1443 /* Query display info. This is the basic user list fn */
1444 static NTSTATUS query_user_list(struct winbindd_domain *domain,
1445 TALLOC_CTX *mem_ctx,
1446 uint32 *num_entries,
1447 struct wbint_userinfo **info)
1449 struct winbind_cache *cache = get_cache(domain);
1450 struct cache_entry *centry = NULL;
1451 NTSTATUS status;
1452 unsigned int i, retry;
1453 bool old_status = domain->online;
1455 if (!cache->tdb)
1456 goto do_query;
1458 centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
1459 if (!centry)
1460 goto do_query;
1462 do_fetch_cache:
1463 *num_entries = centry_uint32(centry);
1465 if (*num_entries == 0)
1466 goto do_cached;
1468 (*info) = talloc_array(mem_ctx, struct wbint_userinfo, *num_entries);
1469 if (! (*info)) {
1470 smb_panic_fn("query_user_list out of memory");
1472 for (i=0; i<(*num_entries); i++) {
1473 (*info)[i].acct_name = centry_string(centry, mem_ctx);
1474 (*info)[i].full_name = centry_string(centry, mem_ctx);
1475 (*info)[i].homedir = centry_string(centry, mem_ctx);
1476 (*info)[i].shell = centry_string(centry, mem_ctx);
1477 centry_sid(centry, &(*info)[i].user_sid);
1478 centry_sid(centry, &(*info)[i].group_sid);
1481 do_cached:
1482 status = centry->status;
1484 DEBUG(10,("query_user_list: [Cached] - cached list for domain %s status: %s\n",
1485 domain->name, nt_errstr(status) ));
1487 centry_free(centry);
1488 return status;
1490 do_query:
1491 *num_entries = 0;
1492 *info = NULL;
1494 /* Return status value returned by seq number check */
1496 if (!NT_STATUS_IS_OK(domain->last_status))
1497 return domain->last_status;
1499 /* Put the query_user_list() in a retry loop. There appears to be
1500 * some bug either with Windows 2000 or Samba's handling of large
1501 * rpc replies. This manifests itself as sudden disconnection
1502 * at a random point in the enumeration of a large (60k) user list.
1503 * The retry loop simply tries the operation again. )-: It's not
1504 * pretty but an acceptable workaround until we work out what the
1505 * real problem is. */
1507 retry = 0;
1508 do {
1510 DEBUG(10,("query_user_list: [Cached] - doing backend query for list for domain %s\n",
1511 domain->name ));
1513 status = domain->backend->query_user_list(domain, mem_ctx, num_entries, info);
1514 if (!NT_STATUS_IS_OK(status)) {
1515 DEBUG(3, ("query_user_list: returned 0x%08x, "
1516 "retrying\n", NT_STATUS_V(status)));
1518 if (NT_STATUS_EQUAL(status, NT_STATUS_UNSUCCESSFUL)) {
1519 DEBUG(3, ("query_user_list: flushing "
1520 "connection cache\n"));
1521 invalidate_cm_connection(&domain->conn);
1523 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1524 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1525 if (!domain->internal && old_status) {
1526 set_domain_offline(domain);
1528 /* store partial response. */
1529 if (*num_entries > 0) {
1531 * humm, what about the status used for cache?
1532 * Should it be NT_STATUS_OK?
1534 break;
1537 * domain is offline now, and there is no user entries,
1538 * try to fetch from cache again.
1540 if (cache->tdb && !domain->online && !domain->internal && old_status) {
1541 centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
1542 /* partial response... */
1543 if (!centry) {
1544 goto skip_save;
1545 } else {
1546 goto do_fetch_cache;
1548 } else {
1549 goto skip_save;
1553 } while (NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_UNSUCCESSFUL) &&
1554 (retry++ < 5));
1556 /* and save it */
1557 refresh_sequence_number(domain, false);
1558 if (!NT_STATUS_IS_OK(status)) {
1559 return status;
1561 centry = centry_start(domain, status);
1562 if (!centry)
1563 goto skip_save;
1564 centry_put_uint32(centry, *num_entries);
1565 for (i=0; i<(*num_entries); i++) {
1566 centry_put_string(centry, (*info)[i].acct_name);
1567 centry_put_string(centry, (*info)[i].full_name);
1568 centry_put_string(centry, (*info)[i].homedir);
1569 centry_put_string(centry, (*info)[i].shell);
1570 centry_put_sid(centry, &(*info)[i].user_sid);
1571 centry_put_sid(centry, &(*info)[i].group_sid);
1572 if (domain->backend && domain->backend->consistent) {
1573 /* when the backend is consistent we can pre-prime some mappings */
1574 wcache_save_name_to_sid(domain, NT_STATUS_OK,
1575 domain->name,
1576 (*info)[i].acct_name,
1577 &(*info)[i].user_sid,
1578 SID_NAME_USER);
1579 wcache_save_sid_to_name(domain, NT_STATUS_OK,
1580 &(*info)[i].user_sid,
1581 domain->name,
1582 (*info)[i].acct_name,
1583 SID_NAME_USER);
1584 wcache_save_user(domain, NT_STATUS_OK, &(*info)[i]);
1587 centry_end(centry, "UL/%s", domain->name);
1588 centry_free(centry);
1590 skip_save:
1591 return status;
1594 /* list all domain groups */
1595 static NTSTATUS enum_dom_groups(struct winbindd_domain *domain,
1596 TALLOC_CTX *mem_ctx,
1597 uint32 *num_entries,
1598 struct wb_acct_info **info)
1600 struct winbind_cache *cache = get_cache(domain);
1601 struct cache_entry *centry = NULL;
1602 NTSTATUS status;
1603 unsigned int i;
1604 bool old_status;
1606 old_status = domain->online;
1607 if (!cache->tdb)
1608 goto do_query;
1610 centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
1611 if (!centry)
1612 goto do_query;
1614 do_fetch_cache:
1615 *num_entries = centry_uint32(centry);
1617 if (*num_entries == 0)
1618 goto do_cached;
1620 (*info) = talloc_array(mem_ctx, struct wb_acct_info, *num_entries);
1621 if (! (*info)) {
1622 smb_panic_fn("enum_dom_groups out of memory");
1624 for (i=0; i<(*num_entries); i++) {
1625 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1626 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1627 (*info)[i].rid = centry_uint32(centry);
1630 do_cached:
1631 status = centry->status;
1633 DEBUG(10,("enum_dom_groups: [Cached] - cached list for domain %s status: %s\n",
1634 domain->name, nt_errstr(status) ));
1636 centry_free(centry);
1637 return status;
1639 do_query:
1640 *num_entries = 0;
1641 *info = NULL;
1643 /* Return status value returned by seq number check */
1645 if (!NT_STATUS_IS_OK(domain->last_status))
1646 return domain->last_status;
1648 DEBUG(10,("enum_dom_groups: [Cached] - doing backend query for list for domain %s\n",
1649 domain->name ));
1651 status = domain->backend->enum_dom_groups(domain, mem_ctx, num_entries, info);
1653 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1654 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1655 if (!domain->internal && old_status) {
1656 set_domain_offline(domain);
1658 if (cache->tdb &&
1659 !domain->online &&
1660 !domain->internal &&
1661 old_status) {
1662 centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
1663 if (centry) {
1664 goto do_fetch_cache;
1668 /* and save it */
1669 refresh_sequence_number(domain, false);
1670 if (!NT_STATUS_IS_OK(status)) {
1671 return status;
1673 centry = centry_start(domain, status);
1674 if (!centry)
1675 goto skip_save;
1676 centry_put_uint32(centry, *num_entries);
1677 for (i=0; i<(*num_entries); i++) {
1678 centry_put_string(centry, (*info)[i].acct_name);
1679 centry_put_string(centry, (*info)[i].acct_desc);
1680 centry_put_uint32(centry, (*info)[i].rid);
1682 centry_end(centry, "GL/%s/domain", domain->name);
1683 centry_free(centry);
1685 skip_save:
1686 return status;
1689 /* list all domain groups */
1690 static NTSTATUS enum_local_groups(struct winbindd_domain *domain,
1691 TALLOC_CTX *mem_ctx,
1692 uint32 *num_entries,
1693 struct wb_acct_info **info)
1695 struct winbind_cache *cache = get_cache(domain);
1696 struct cache_entry *centry = NULL;
1697 NTSTATUS status;
1698 unsigned int i;
1699 bool old_status;
1701 old_status = domain->online;
1702 if (!cache->tdb)
1703 goto do_query;
1705 centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
1706 if (!centry)
1707 goto do_query;
1709 do_fetch_cache:
1710 *num_entries = centry_uint32(centry);
1712 if (*num_entries == 0)
1713 goto do_cached;
1715 (*info) = talloc_array(mem_ctx, struct wb_acct_info, *num_entries);
1716 if (! (*info)) {
1717 smb_panic_fn("enum_dom_groups out of memory");
1719 for (i=0; i<(*num_entries); i++) {
1720 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1721 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1722 (*info)[i].rid = centry_uint32(centry);
1725 do_cached:
1727 /* If we are returning cached data and the domain controller
1728 is down then we don't know whether the data is up to date
1729 or not. Return NT_STATUS_MORE_PROCESSING_REQUIRED to
1730 indicate this. */
1732 if (wcache_server_down(domain)) {
1733 DEBUG(10, ("enum_local_groups: returning cached user list and server was down\n"));
1734 status = NT_STATUS_MORE_PROCESSING_REQUIRED;
1735 } else
1736 status = centry->status;
1738 DEBUG(10,("enum_local_groups: [Cached] - cached list for domain %s status: %s\n",
1739 domain->name, nt_errstr(status) ));
1741 centry_free(centry);
1742 return status;
1744 do_query:
1745 *num_entries = 0;
1746 *info = NULL;
1748 /* Return status value returned by seq number check */
1750 if (!NT_STATUS_IS_OK(domain->last_status))
1751 return domain->last_status;
1753 DEBUG(10,("enum_local_groups: [Cached] - doing backend query for list for domain %s\n",
1754 domain->name ));
1756 status = domain->backend->enum_local_groups(domain, mem_ctx, num_entries, info);
1758 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1759 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1760 if (!domain->internal && old_status) {
1761 set_domain_offline(domain);
1763 if (cache->tdb &&
1764 !domain->internal &&
1765 !domain->online &&
1766 old_status) {
1767 centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
1768 if (centry) {
1769 goto do_fetch_cache;
1773 /* and save it */
1774 refresh_sequence_number(domain, false);
1775 if (!NT_STATUS_IS_OK(status)) {
1776 return status;
1778 centry = centry_start(domain, status);
1779 if (!centry)
1780 goto skip_save;
1781 centry_put_uint32(centry, *num_entries);
1782 for (i=0; i<(*num_entries); i++) {
1783 centry_put_string(centry, (*info)[i].acct_name);
1784 centry_put_string(centry, (*info)[i].acct_desc);
1785 centry_put_uint32(centry, (*info)[i].rid);
1787 centry_end(centry, "GL/%s/local", domain->name);
1788 centry_free(centry);
1790 skip_save:
1791 return status;
1794 NTSTATUS wcache_name_to_sid(struct winbindd_domain *domain,
1795 const char *domain_name,
1796 const char *name,
1797 struct dom_sid *sid,
1798 enum lsa_SidType *type)
1800 struct winbind_cache *cache = get_cache(domain);
1801 struct cache_entry *centry;
1802 NTSTATUS status;
1803 char *uname;
1805 if (cache->tdb == NULL) {
1806 return NT_STATUS_NOT_FOUND;
1809 uname = talloc_strdup_upper(talloc_tos(), name);
1810 if (uname == NULL) {
1811 return NT_STATUS_NO_MEMORY;
1814 if ((domain_name == NULL) || (domain_name[0] == '\0')) {
1815 domain_name = domain->name;
1818 centry = wcache_fetch(cache, domain, "NS/%s/%s", domain_name, uname);
1819 TALLOC_FREE(uname);
1820 if (centry == NULL) {
1821 return NT_STATUS_NOT_FOUND;
1824 status = centry->status;
1825 if (NT_STATUS_IS_OK(status)) {
1826 *type = (enum lsa_SidType)centry_uint32(centry);
1827 centry_sid(centry, sid);
1830 DEBUG(10,("name_to_sid: [Cached] - cached name for domain %s status: "
1831 "%s\n", domain->name, nt_errstr(status) ));
1833 centry_free(centry);
1834 return status;
1837 /* convert a single name to a sid in a domain */
1838 static NTSTATUS name_to_sid(struct winbindd_domain *domain,
1839 TALLOC_CTX *mem_ctx,
1840 const char *domain_name,
1841 const char *name,
1842 uint32_t flags,
1843 struct dom_sid *sid,
1844 enum lsa_SidType *type)
1846 NTSTATUS status;
1847 bool old_status;
1849 old_status = domain->online;
1851 status = wcache_name_to_sid(domain, domain_name, name, sid, type);
1852 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
1853 return status;
1856 ZERO_STRUCTP(sid);
1858 /* If the seq number check indicated that there is a problem
1859 * with this DC, then return that status... except for
1860 * access_denied. This is special because the dc may be in
1861 * "restrict anonymous = 1" mode, in which case it will deny
1862 * most unauthenticated operations, but *will* allow the LSA
1863 * name-to-sid that we try as a fallback. */
1865 if (!(NT_STATUS_IS_OK(domain->last_status)
1866 || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1867 return domain->last_status;
1869 DEBUG(10,("name_to_sid: [Cached] - doing backend query for name for domain %s\n",
1870 domain->name ));
1872 status = domain->backend->name_to_sid(domain, mem_ctx, domain_name,
1873 name, flags, sid, type);
1875 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1876 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1877 if (!domain->internal && old_status) {
1878 set_domain_offline(domain);
1880 if (!domain->internal &&
1881 !domain->online &&
1882 old_status) {
1883 NTSTATUS cache_status;
1884 cache_status = wcache_name_to_sid(domain, domain_name, name, sid, type);
1885 return cache_status;
1888 /* and save it */
1889 refresh_sequence_number(domain, false);
1891 if (domain->online &&
1892 (NT_STATUS_IS_OK(status) || NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED))) {
1893 wcache_save_name_to_sid(domain, status, domain_name, name, sid, *type);
1895 /* Only save the reverse mapping if this was not a UPN */
1896 if (!strchr(name, '@')) {
1897 if (!strupper_m(discard_const_p(char, domain_name))) {
1898 return NT_STATUS_INVALID_PARAMETER;
1900 (void)strlower_m(discard_const_p(char, name));
1901 wcache_save_sid_to_name(domain, status, sid, domain_name, name, *type);
1905 return status;
1908 NTSTATUS wcache_sid_to_name(struct winbindd_domain *domain,
1909 const struct dom_sid *sid,
1910 TALLOC_CTX *mem_ctx,
1911 char **domain_name,
1912 char **name,
1913 enum lsa_SidType *type)
1915 struct winbind_cache *cache = get_cache(domain);
1916 struct cache_entry *centry;
1917 char *sid_string;
1918 NTSTATUS status;
1920 if (cache->tdb == NULL) {
1921 return NT_STATUS_NOT_FOUND;
1924 sid_string = sid_string_tos(sid);
1925 if (sid_string == NULL) {
1926 return NT_STATUS_NO_MEMORY;
1929 centry = wcache_fetch(cache, domain, "SN/%s", sid_string);
1930 TALLOC_FREE(sid_string);
1931 if (centry == NULL) {
1932 return NT_STATUS_NOT_FOUND;
1935 if (NT_STATUS_IS_OK(centry->status)) {
1936 *type = (enum lsa_SidType)centry_uint32(centry);
1937 *domain_name = centry_string(centry, mem_ctx);
1938 *name = centry_string(centry, mem_ctx);
1941 status = centry->status;
1942 centry_free(centry);
1944 DEBUG(10,("sid_to_name: [Cached] - cached name for domain %s status: "
1945 "%s\n", domain->name, nt_errstr(status) ));
1947 return status;
1950 /* convert a sid to a user or group name. The sid is guaranteed to be in the domain
1951 given */
1952 static NTSTATUS sid_to_name(struct winbindd_domain *domain,
1953 TALLOC_CTX *mem_ctx,
1954 const struct dom_sid *sid,
1955 char **domain_name,
1956 char **name,
1957 enum lsa_SidType *type)
1959 NTSTATUS status;
1960 bool old_status;
1962 old_status = domain->online;
1963 status = wcache_sid_to_name(domain, sid, mem_ctx, domain_name, name,
1964 type);
1965 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
1966 return status;
1969 *name = NULL;
1970 *domain_name = NULL;
1972 /* If the seq number check indicated that there is a problem
1973 * with this DC, then return that status... except for
1974 * access_denied. This is special because the dc may be in
1975 * "restrict anonymous = 1" mode, in which case it will deny
1976 * most unauthenticated operations, but *will* allow the LSA
1977 * sid-to-name that we try as a fallback. */
1979 if (!(NT_STATUS_IS_OK(domain->last_status)
1980 || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1981 return domain->last_status;
1983 DEBUG(10,("sid_to_name: [Cached] - doing backend query for name for domain %s\n",
1984 domain->name ));
1986 status = domain->backend->sid_to_name(domain, mem_ctx, sid, domain_name, name, type);
1988 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1989 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1990 if (!domain->internal && old_status) {
1991 set_domain_offline(domain);
1993 if (!domain->internal &&
1994 !domain->online &&
1995 old_status) {
1996 NTSTATUS cache_status;
1997 cache_status = wcache_sid_to_name(domain, sid, mem_ctx,
1998 domain_name, name, type);
1999 return cache_status;
2002 /* and save it */
2003 refresh_sequence_number(domain, false);
2004 if (!NT_STATUS_IS_OK(status)) {
2005 return status;
2007 wcache_save_sid_to_name(domain, status, sid, *domain_name, *name, *type);
2009 /* We can't save the name to sid mapping here, as with sid history a
2010 * later name2sid would give the wrong sid. */
2012 return status;
2015 static NTSTATUS rids_to_names(struct winbindd_domain *domain,
2016 TALLOC_CTX *mem_ctx,
2017 const struct dom_sid *domain_sid,
2018 uint32 *rids,
2019 size_t num_rids,
2020 char **domain_name,
2021 char ***names,
2022 enum lsa_SidType **types)
2024 struct winbind_cache *cache = get_cache(domain);
2025 size_t i;
2026 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
2027 bool have_mapped;
2028 bool have_unmapped;
2029 bool old_status;
2031 old_status = domain->online;
2032 *domain_name = NULL;
2033 *names = NULL;
2034 *types = NULL;
2036 if (!cache->tdb) {
2037 goto do_query;
2040 if (num_rids == 0) {
2041 return NT_STATUS_OK;
2044 *names = talloc_array(mem_ctx, char *, num_rids);
2045 *types = talloc_array(mem_ctx, enum lsa_SidType, num_rids);
2047 if ((*names == NULL) || (*types == NULL)) {
2048 result = NT_STATUS_NO_MEMORY;
2049 goto error;
2052 have_mapped = have_unmapped = false;
2054 for (i=0; i<num_rids; i++) {
2055 struct dom_sid sid;
2056 struct cache_entry *centry;
2057 fstring tmp;
2059 if (!sid_compose(&sid, domain_sid, rids[i])) {
2060 result = NT_STATUS_INTERNAL_ERROR;
2061 goto error;
2064 centry = wcache_fetch(cache, domain, "SN/%s",
2065 sid_to_fstring(tmp, &sid));
2066 if (!centry) {
2067 goto do_query;
2070 (*types)[i] = SID_NAME_UNKNOWN;
2071 (*names)[i] = talloc_strdup(*names, "");
2073 if (NT_STATUS_IS_OK(centry->status)) {
2074 char *dom;
2075 have_mapped = true;
2076 (*types)[i] = (enum lsa_SidType)centry_uint32(centry);
2078 dom = centry_string(centry, mem_ctx);
2079 if (*domain_name == NULL) {
2080 *domain_name = dom;
2081 } else {
2082 talloc_free(dom);
2085 (*names)[i] = centry_string(centry, *names);
2087 } else if (NT_STATUS_EQUAL(centry->status, NT_STATUS_NONE_MAPPED)
2088 || NT_STATUS_EQUAL(centry->status, STATUS_SOME_UNMAPPED)) {
2089 have_unmapped = true;
2091 } else {
2092 /* something's definitely wrong */
2093 result = centry->status;
2094 centry_free(centry);
2095 goto error;
2098 centry_free(centry);
2101 if (!have_mapped) {
2102 return NT_STATUS_NONE_MAPPED;
2104 if (!have_unmapped) {
2105 return NT_STATUS_OK;
2107 return STATUS_SOME_UNMAPPED;
2109 do_query:
2111 TALLOC_FREE(*names);
2112 TALLOC_FREE(*types);
2114 result = domain->backend->rids_to_names(domain, mem_ctx, domain_sid,
2115 rids, num_rids, domain_name,
2116 names, types);
2118 if (NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
2119 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2120 if (!domain->internal && old_status) {
2121 set_domain_offline(domain);
2123 if (cache->tdb &&
2124 !domain->internal &&
2125 !domain->online &&
2126 old_status) {
2127 have_mapped = have_unmapped = false;
2129 *names = talloc_array(mem_ctx, char *, num_rids);
2130 if (*names != NULL) {
2131 result = NT_STATUS_NO_MEMORY;
2132 goto error;
2135 *types = talloc_array(mem_ctx, enum lsa_SidType,
2136 num_rids);
2137 if (*types != NULL) {
2138 result = NT_STATUS_NO_MEMORY;
2139 goto error;
2142 for (i=0; i<num_rids; i++) {
2143 struct dom_sid sid;
2144 struct cache_entry *centry;
2145 fstring tmp;
2147 if (!sid_compose(&sid, domain_sid, rids[i])) {
2148 result = NT_STATUS_INTERNAL_ERROR;
2149 goto error;
2152 centry = wcache_fetch(cache, domain, "SN/%s",
2153 sid_to_fstring(tmp, &sid));
2154 if (!centry) {
2155 (*types)[i] = SID_NAME_UNKNOWN;
2156 (*names)[i] = talloc_strdup(*names, "");
2157 continue;
2160 (*types)[i] = SID_NAME_UNKNOWN;
2161 (*names)[i] = talloc_strdup(*names, "");
2163 if (NT_STATUS_IS_OK(centry->status)) {
2164 char *dom;
2165 have_mapped = true;
2166 (*types)[i] = (enum lsa_SidType)centry_uint32(centry);
2168 dom = centry_string(centry, mem_ctx);
2169 if (*domain_name == NULL) {
2170 *domain_name = dom;
2171 } else {
2172 talloc_free(dom);
2175 (*names)[i] = centry_string(centry, *names);
2177 } else if (NT_STATUS_EQUAL(centry->status, NT_STATUS_NONE_MAPPED)) {
2178 have_unmapped = true;
2180 } else {
2181 /* something's definitely wrong */
2182 result = centry->status;
2183 centry_free(centry);
2184 goto error;
2187 centry_free(centry);
2190 if (!have_mapped) {
2191 return NT_STATUS_NONE_MAPPED;
2193 if (!have_unmapped) {
2194 return NT_STATUS_OK;
2196 return STATUS_SOME_UNMAPPED;
2200 None of the queried rids has been found so save all negative entries
2202 if (NT_STATUS_EQUAL(result, NT_STATUS_NONE_MAPPED)) {
2203 for (i = 0; i < num_rids; i++) {
2204 struct dom_sid sid;
2205 const char *name = "";
2206 const enum lsa_SidType type = SID_NAME_UNKNOWN;
2207 NTSTATUS status = NT_STATUS_NONE_MAPPED;
2209 if (!sid_compose(&sid, domain_sid, rids[i])) {
2210 return NT_STATUS_INTERNAL_ERROR;
2213 wcache_save_sid_to_name(domain, status, &sid, *domain_name,
2214 name, type);
2217 return result;
2221 Some or all of the queried rids have been found.
2223 if (!NT_STATUS_IS_OK(result) &&
2224 !NT_STATUS_EQUAL(result, STATUS_SOME_UNMAPPED)) {
2225 return result;
2228 refresh_sequence_number(domain, false);
2230 for (i=0; i<num_rids; i++) {
2231 struct dom_sid sid;
2232 NTSTATUS status;
2234 if (!sid_compose(&sid, domain_sid, rids[i])) {
2235 result = NT_STATUS_INTERNAL_ERROR;
2236 goto error;
2239 status = (*types)[i] == SID_NAME_UNKNOWN ?
2240 NT_STATUS_NONE_MAPPED : NT_STATUS_OK;
2242 wcache_save_sid_to_name(domain, status, &sid, *domain_name,
2243 (*names)[i], (*types)[i]);
2246 return result;
2248 error:
2249 TALLOC_FREE(*names);
2250 TALLOC_FREE(*types);
2251 return result;
2254 NTSTATUS wcache_query_user(struct winbindd_domain *domain,
2255 TALLOC_CTX *mem_ctx,
2256 const struct dom_sid *user_sid,
2257 struct wbint_userinfo *info)
2259 struct winbind_cache *cache = get_cache(domain);
2260 struct cache_entry *centry = NULL;
2261 NTSTATUS status;
2262 char *sid_string;
2264 if (cache->tdb == NULL) {
2265 return NT_STATUS_NOT_FOUND;
2268 sid_string = sid_string_tos(user_sid);
2269 if (sid_string == NULL) {
2270 return NT_STATUS_NO_MEMORY;
2273 centry = wcache_fetch(cache, domain, "U/%s", sid_string);
2274 TALLOC_FREE(sid_string);
2275 if (centry == NULL) {
2276 return NT_STATUS_NOT_FOUND;
2280 * If we have an access denied cache entry and a cached info3
2281 * in the samlogon cache then do a query. This will force the
2282 * rpc back end to return the info3 data.
2285 if (NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED) &&
2286 netsamlogon_cache_have(user_sid)) {
2287 DEBUG(10, ("query_user: cached access denied and have cached "
2288 "info3\n"));
2289 domain->last_status = NT_STATUS_OK;
2290 centry_free(centry);
2291 return NT_STATUS_NOT_FOUND;
2294 /* if status is not ok then this is a negative hit
2295 and the rest of the data doesn't matter */
2296 status = centry->status;
2297 if (NT_STATUS_IS_OK(status)) {
2298 info->acct_name = centry_string(centry, mem_ctx);
2299 info->full_name = centry_string(centry, mem_ctx);
2300 info->homedir = centry_string(centry, mem_ctx);
2301 info->shell = centry_string(centry, mem_ctx);
2302 info->primary_gid = centry_uint32(centry);
2303 centry_sid(centry, &info->user_sid);
2304 centry_sid(centry, &info->group_sid);
2307 DEBUG(10,("query_user: [Cached] - cached info for domain %s status: "
2308 "%s\n", domain->name, nt_errstr(status) ));
2310 centry_free(centry);
2311 return status;
2314 /* Lookup user information from a rid */
2315 static NTSTATUS query_user(struct winbindd_domain *domain,
2316 TALLOC_CTX *mem_ctx,
2317 const struct dom_sid *user_sid,
2318 struct wbint_userinfo *info)
2320 NTSTATUS status;
2321 bool old_status;
2323 old_status = domain->online;
2324 status = wcache_query_user(domain, mem_ctx, user_sid, info);
2325 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2326 return status;
2329 ZERO_STRUCTP(info);
2331 /* Return status value returned by seq number check */
2333 if (!NT_STATUS_IS_OK(domain->last_status))
2334 return domain->last_status;
2336 DEBUG(10,("query_user: [Cached] - doing backend query for info for domain %s\n",
2337 domain->name ));
2339 status = domain->backend->query_user(domain, mem_ctx, user_sid, info);
2341 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2342 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2343 if (!domain->internal && old_status) {
2344 set_domain_offline(domain);
2346 if (!domain->internal &&
2347 !domain->online &&
2348 old_status) {
2349 NTSTATUS cache_status;
2350 cache_status = wcache_query_user(domain, mem_ctx, user_sid, info);
2351 return cache_status;
2354 /* and save it */
2355 refresh_sequence_number(domain, false);
2356 if (!NT_STATUS_IS_OK(status)) {
2357 return status;
2359 wcache_save_user(domain, status, info);
2361 return status;
2364 NTSTATUS wcache_lookup_usergroups(struct winbindd_domain *domain,
2365 TALLOC_CTX *mem_ctx,
2366 const struct dom_sid *user_sid,
2367 uint32_t *pnum_sids,
2368 struct dom_sid **psids)
2370 struct winbind_cache *cache = get_cache(domain);
2371 struct cache_entry *centry = NULL;
2372 NTSTATUS status;
2373 uint32_t i, num_sids;
2374 struct dom_sid *sids;
2375 fstring sid_string;
2377 if (cache->tdb == NULL) {
2378 return NT_STATUS_NOT_FOUND;
2381 centry = wcache_fetch(cache, domain, "UG/%s",
2382 sid_to_fstring(sid_string, user_sid));
2383 if (centry == NULL) {
2384 return NT_STATUS_NOT_FOUND;
2387 /* If we have an access denied cache entry and a cached info3 in the
2388 samlogon cache then do a query. This will force the rpc back end
2389 to return the info3 data. */
2391 if (NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)
2392 && netsamlogon_cache_have(user_sid)) {
2393 DEBUG(10, ("lookup_usergroups: cached access denied and have "
2394 "cached info3\n"));
2395 domain->last_status = NT_STATUS_OK;
2396 centry_free(centry);
2397 return NT_STATUS_NOT_FOUND;
2400 num_sids = centry_uint32(centry);
2401 sids = talloc_array(mem_ctx, struct dom_sid, num_sids);
2402 if (sids == NULL) {
2403 centry_free(centry);
2404 return NT_STATUS_NO_MEMORY;
2407 for (i=0; i<num_sids; i++) {
2408 centry_sid(centry, &sids[i]);
2411 status = centry->status;
2413 DEBUG(10,("lookup_usergroups: [Cached] - cached info for domain %s "
2414 "status: %s\n", domain->name, nt_errstr(status)));
2416 centry_free(centry);
2418 *pnum_sids = num_sids;
2419 *psids = sids;
2420 return status;
2423 /* Lookup groups a user is a member of. */
2424 static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
2425 TALLOC_CTX *mem_ctx,
2426 const struct dom_sid *user_sid,
2427 uint32 *num_groups, struct dom_sid **user_gids)
2429 struct cache_entry *centry = NULL;
2430 NTSTATUS status;
2431 unsigned int i;
2432 fstring sid_string;
2433 bool old_status;
2435 old_status = domain->online;
2436 status = wcache_lookup_usergroups(domain, mem_ctx, user_sid,
2437 num_groups, user_gids);
2438 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2439 return status;
2442 (*num_groups) = 0;
2443 (*user_gids) = NULL;
2445 /* Return status value returned by seq number check */
2447 if (!NT_STATUS_IS_OK(domain->last_status))
2448 return domain->last_status;
2450 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info for domain %s\n",
2451 domain->name ));
2453 status = domain->backend->lookup_usergroups(domain, mem_ctx, user_sid, num_groups, user_gids);
2455 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2456 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2457 if (!domain->internal && old_status) {
2458 set_domain_offline(domain);
2460 if (!domain->internal &&
2461 !domain->online &&
2462 old_status) {
2463 NTSTATUS cache_status;
2464 cache_status = wcache_lookup_usergroups(domain, mem_ctx, user_sid,
2465 num_groups, user_gids);
2466 return cache_status;
2469 if ( NT_STATUS_EQUAL(status, NT_STATUS_SYNCHRONIZATION_REQUIRED) )
2470 goto skip_save;
2472 /* and save it */
2473 refresh_sequence_number(domain, false);
2474 if (!NT_STATUS_IS_OK(status)) {
2475 return status;
2477 centry = centry_start(domain, status);
2478 if (!centry)
2479 goto skip_save;
2481 centry_put_uint32(centry, *num_groups);
2482 for (i=0; i<(*num_groups); i++) {
2483 centry_put_sid(centry, &(*user_gids)[i]);
2486 centry_end(centry, "UG/%s", sid_to_fstring(sid_string, user_sid));
2487 centry_free(centry);
2489 skip_save:
2490 return status;
2493 static char *wcache_make_sidlist(TALLOC_CTX *mem_ctx, uint32_t num_sids,
2494 const struct dom_sid *sids)
2496 uint32_t i;
2497 char *sidlist;
2499 sidlist = talloc_strdup(mem_ctx, "");
2500 if (sidlist == NULL) {
2501 return NULL;
2503 for (i=0; i<num_sids; i++) {
2504 fstring tmp;
2505 sidlist = talloc_asprintf_append_buffer(
2506 sidlist, "/%s", sid_to_fstring(tmp, &sids[i]));
2507 if (sidlist == NULL) {
2508 return NULL;
2511 return sidlist;
2514 NTSTATUS wcache_lookup_useraliases(struct winbindd_domain *domain,
2515 TALLOC_CTX *mem_ctx, uint32_t num_sids,
2516 const struct dom_sid *sids,
2517 uint32_t *pnum_aliases, uint32_t **paliases)
2519 struct winbind_cache *cache = get_cache(domain);
2520 struct cache_entry *centry = NULL;
2521 uint32_t num_aliases;
2522 uint32_t *aliases;
2523 NTSTATUS status;
2524 char *sidlist;
2525 int i;
2527 if (cache->tdb == NULL) {
2528 return NT_STATUS_NOT_FOUND;
2531 if (num_sids == 0) {
2532 *pnum_aliases = 0;
2533 *paliases = NULL;
2534 return NT_STATUS_OK;
2537 /* We need to cache indexed by the whole list of SIDs, the aliases
2538 * resulting might come from any of the SIDs. */
2540 sidlist = wcache_make_sidlist(talloc_tos(), num_sids, sids);
2541 if (sidlist == NULL) {
2542 return NT_STATUS_NO_MEMORY;
2545 centry = wcache_fetch(cache, domain, "UA%s", sidlist);
2546 TALLOC_FREE(sidlist);
2547 if (centry == NULL) {
2548 return NT_STATUS_NOT_FOUND;
2551 num_aliases = centry_uint32(centry);
2552 aliases = talloc_array(mem_ctx, uint32_t, num_aliases);
2553 if (aliases == NULL) {
2554 centry_free(centry);
2555 return NT_STATUS_NO_MEMORY;
2558 for (i=0; i<num_aliases; i++) {
2559 aliases[i] = centry_uint32(centry);
2562 status = centry->status;
2564 DEBUG(10,("lookup_useraliases: [Cached] - cached info for domain: %s "
2565 "status %s\n", domain->name, nt_errstr(status)));
2567 centry_free(centry);
2569 *pnum_aliases = num_aliases;
2570 *paliases = aliases;
2572 return status;
2575 static NTSTATUS lookup_useraliases(struct winbindd_domain *domain,
2576 TALLOC_CTX *mem_ctx,
2577 uint32 num_sids, const struct dom_sid *sids,
2578 uint32 *num_aliases, uint32 **alias_rids)
2580 struct cache_entry *centry = NULL;
2581 NTSTATUS status;
2582 char *sidlist;
2583 int i;
2584 bool old_status;
2586 old_status = domain->online;
2587 status = wcache_lookup_useraliases(domain, mem_ctx, num_sids, sids,
2588 num_aliases, alias_rids);
2589 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2590 return status;
2593 (*num_aliases) = 0;
2594 (*alias_rids) = NULL;
2596 if (!NT_STATUS_IS_OK(domain->last_status))
2597 return domain->last_status;
2599 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info "
2600 "for domain %s\n", domain->name ));
2602 sidlist = wcache_make_sidlist(talloc_tos(), num_sids, sids);
2603 if (sidlist == NULL) {
2604 return NT_STATUS_NO_MEMORY;
2607 status = domain->backend->lookup_useraliases(domain, mem_ctx,
2608 num_sids, sids,
2609 num_aliases, alias_rids);
2611 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2612 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2613 if (!domain->internal && old_status) {
2614 set_domain_offline(domain);
2616 if (!domain->internal &&
2617 !domain->online &&
2618 old_status) {
2619 NTSTATUS cache_status;
2620 cache_status = wcache_lookup_useraliases(domain, mem_ctx, num_sids,
2621 sids, num_aliases, alias_rids);
2622 return cache_status;
2625 /* and save it */
2626 refresh_sequence_number(domain, false);
2627 if (!NT_STATUS_IS_OK(status)) {
2628 return status;
2630 centry = centry_start(domain, status);
2631 if (!centry)
2632 goto skip_save;
2633 centry_put_uint32(centry, *num_aliases);
2634 for (i=0; i<(*num_aliases); i++)
2635 centry_put_uint32(centry, (*alias_rids)[i]);
2636 centry_end(centry, "UA%s", sidlist);
2637 centry_free(centry);
2639 skip_save:
2640 return status;
2643 NTSTATUS wcache_lookup_groupmem(struct winbindd_domain *domain,
2644 TALLOC_CTX *mem_ctx,
2645 const struct dom_sid *group_sid,
2646 uint32_t *num_names,
2647 struct dom_sid **sid_mem, char ***names,
2648 uint32_t **name_types)
2650 struct winbind_cache *cache = get_cache(domain);
2651 struct cache_entry *centry = NULL;
2652 NTSTATUS status;
2653 unsigned int i;
2654 char *sid_string;
2656 if (cache->tdb == NULL) {
2657 return NT_STATUS_NOT_FOUND;
2660 sid_string = sid_string_tos(group_sid);
2661 if (sid_string == NULL) {
2662 return NT_STATUS_NO_MEMORY;
2665 centry = wcache_fetch(cache, domain, "GM/%s", sid_string);
2666 TALLOC_FREE(sid_string);
2667 if (centry == NULL) {
2668 return NT_STATUS_NOT_FOUND;
2671 *sid_mem = NULL;
2672 *names = NULL;
2673 *name_types = NULL;
2675 *num_names = centry_uint32(centry);
2676 if (*num_names == 0) {
2677 centry_free(centry);
2678 return NT_STATUS_OK;
2681 *sid_mem = talloc_array(mem_ctx, struct dom_sid, *num_names);
2682 *names = talloc_array(mem_ctx, char *, *num_names);
2683 *name_types = talloc_array(mem_ctx, uint32, *num_names);
2685 if ((*sid_mem == NULL) || (*names == NULL) || (*name_types == NULL)) {
2686 TALLOC_FREE(*sid_mem);
2687 TALLOC_FREE(*names);
2688 TALLOC_FREE(*name_types);
2689 centry_free(centry);
2690 return NT_STATUS_NO_MEMORY;
2693 for (i=0; i<(*num_names); i++) {
2694 centry_sid(centry, &(*sid_mem)[i]);
2695 (*names)[i] = centry_string(centry, mem_ctx);
2696 (*name_types)[i] = centry_uint32(centry);
2699 status = centry->status;
2701 DEBUG(10,("lookup_groupmem: [Cached] - cached info for domain %s "
2702 "status: %s\n", domain->name, nt_errstr(status)));
2704 centry_free(centry);
2705 return status;
2708 static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
2709 TALLOC_CTX *mem_ctx,
2710 const struct dom_sid *group_sid,
2711 enum lsa_SidType type,
2712 uint32 *num_names,
2713 struct dom_sid **sid_mem, char ***names,
2714 uint32 **name_types)
2716 struct cache_entry *centry = NULL;
2717 NTSTATUS status;
2718 unsigned int i;
2719 fstring sid_string;
2720 bool old_status;
2722 old_status = domain->online;
2723 status = wcache_lookup_groupmem(domain, mem_ctx, group_sid, num_names,
2724 sid_mem, names, name_types);
2725 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2726 return status;
2729 (*num_names) = 0;
2730 (*sid_mem) = NULL;
2731 (*names) = NULL;
2732 (*name_types) = NULL;
2734 /* Return status value returned by seq number check */
2736 if (!NT_STATUS_IS_OK(domain->last_status))
2737 return domain->last_status;
2739 DEBUG(10,("lookup_groupmem: [Cached] - doing backend query for info for domain %s\n",
2740 domain->name ));
2742 status = domain->backend->lookup_groupmem(domain, mem_ctx, group_sid,
2743 type, num_names,
2744 sid_mem, names, name_types);
2746 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2747 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2748 if (!domain->internal && old_status) {
2749 set_domain_offline(domain);
2751 if (!domain->internal &&
2752 !domain->online &&
2753 old_status) {
2754 NTSTATUS cache_status;
2755 cache_status = wcache_lookup_groupmem(domain, mem_ctx, group_sid,
2756 num_names, sid_mem, names,
2757 name_types);
2758 return cache_status;
2761 /* and save it */
2762 refresh_sequence_number(domain, false);
2763 if (!NT_STATUS_IS_OK(status)) {
2764 return status;
2766 centry = centry_start(domain, status);
2767 if (!centry)
2768 goto skip_save;
2769 centry_put_uint32(centry, *num_names);
2770 for (i=0; i<(*num_names); i++) {
2771 centry_put_sid(centry, &(*sid_mem)[i]);
2772 centry_put_string(centry, (*names)[i]);
2773 centry_put_uint32(centry, (*name_types)[i]);
2775 centry_end(centry, "GM/%s", sid_to_fstring(sid_string, group_sid));
2776 centry_free(centry);
2778 skip_save:
2779 return status;
2782 /* find the sequence number for a domain */
2783 static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
2785 refresh_sequence_number(domain, false);
2787 *seq = domain->sequence_number;
2789 return NT_STATUS_OK;
2792 /* enumerate trusted domains
2793 * (we need to have the list of trustdoms in the cache when we go offline) -
2794 * Guenther */
2795 static NTSTATUS trusted_domains(struct winbindd_domain *domain,
2796 TALLOC_CTX *mem_ctx,
2797 struct netr_DomainTrustList *trusts)
2799 NTSTATUS status;
2800 struct winbind_cache *cache;
2801 struct winbindd_tdc_domain *dom_list = NULL;
2802 size_t num_domains = 0;
2803 bool retval = false;
2804 int i;
2805 bool old_status;
2807 old_status = domain->online;
2808 trusts->count = 0;
2809 trusts->array = NULL;
2811 cache = get_cache(domain);
2812 if (!cache || !cache->tdb) {
2813 goto do_query;
2816 if (domain->online) {
2817 goto do_query;
2820 retval = wcache_tdc_fetch_list(&dom_list, &num_domains);
2821 if (!retval || !num_domains || !dom_list) {
2822 TALLOC_FREE(dom_list);
2823 goto do_query;
2826 do_fetch_cache:
2827 trusts->array = talloc_zero_array(mem_ctx, struct netr_DomainTrust, num_domains);
2828 if (!trusts->array) {
2829 TALLOC_FREE(dom_list);
2830 return NT_STATUS_NO_MEMORY;
2833 for (i = 0; i < num_domains; i++) {
2834 struct netr_DomainTrust *trust;
2835 struct dom_sid *sid;
2836 struct winbindd_domain *dom;
2838 dom = find_domain_from_name_noinit(dom_list[i].domain_name);
2839 if (dom && dom->internal) {
2840 continue;
2843 trust = &trusts->array[trusts->count];
2844 trust->netbios_name = talloc_strdup(trusts->array, dom_list[i].domain_name);
2845 trust->dns_name = talloc_strdup(trusts->array, dom_list[i].dns_name);
2846 sid = talloc(trusts->array, struct dom_sid);
2847 if (!trust->netbios_name || !trust->dns_name ||
2848 !sid) {
2849 TALLOC_FREE(dom_list);
2850 TALLOC_FREE(trusts->array);
2851 return NT_STATUS_NO_MEMORY;
2854 trust->trust_flags = dom_list[i].trust_flags;
2855 trust->trust_attributes = dom_list[i].trust_attribs;
2856 trust->trust_type = dom_list[i].trust_type;
2857 sid_copy(sid, &dom_list[i].sid);
2858 trust->sid = sid;
2859 trusts->count++;
2862 TALLOC_FREE(dom_list);
2863 return NT_STATUS_OK;
2865 do_query:
2866 /* Return status value returned by seq number check */
2868 if (!NT_STATUS_IS_OK(domain->last_status))
2869 return domain->last_status;
2871 DEBUG(10,("trusted_domains: [Cached] - doing backend query for info for domain %s\n",
2872 domain->name ));
2874 status = domain->backend->trusted_domains(domain, mem_ctx, trusts);
2876 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2877 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2878 if (!domain->internal && old_status) {
2879 set_domain_offline(domain);
2881 if (!domain->internal &&
2882 !domain->online &&
2883 old_status) {
2884 retval = wcache_tdc_fetch_list(&dom_list, &num_domains);
2885 if (retval && num_domains && dom_list) {
2886 TALLOC_FREE(trusts->array);
2887 trusts->count = 0;
2888 goto do_fetch_cache;
2892 /* no trusts gives NT_STATUS_NO_MORE_ENTRIES resetting to NT_STATUS_OK
2893 * so that the generic centry handling still applies correctly -
2894 * Guenther*/
2896 if (!NT_STATUS_IS_ERR(status)) {
2897 status = NT_STATUS_OK;
2899 return status;
2902 /* get lockout policy */
2903 static NTSTATUS lockout_policy(struct winbindd_domain *domain,
2904 TALLOC_CTX *mem_ctx,
2905 struct samr_DomInfo12 *policy)
2907 struct winbind_cache *cache = get_cache(domain);
2908 struct cache_entry *centry = NULL;
2909 NTSTATUS status;
2910 bool old_status;
2912 old_status = domain->online;
2913 if (!cache->tdb)
2914 goto do_query;
2916 centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
2918 if (!centry)
2919 goto do_query;
2921 do_fetch_cache:
2922 policy->lockout_duration = centry_nttime(centry);
2923 policy->lockout_window = centry_nttime(centry);
2924 policy->lockout_threshold = centry_uint16(centry);
2926 status = centry->status;
2928 DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2929 domain->name, nt_errstr(status) ));
2931 centry_free(centry);
2932 return status;
2934 do_query:
2935 ZERO_STRUCTP(policy);
2937 /* Return status value returned by seq number check */
2939 if (!NT_STATUS_IS_OK(domain->last_status))
2940 return domain->last_status;
2942 DEBUG(10,("lockout_policy: [Cached] - doing backend query for info for domain %s\n",
2943 domain->name ));
2945 status = domain->backend->lockout_policy(domain, mem_ctx, policy);
2947 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2948 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2949 if (!domain->internal && old_status) {
2950 set_domain_offline(domain);
2952 if (cache->tdb &&
2953 !domain->internal &&
2954 !domain->online &&
2955 old_status) {
2956 centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
2957 if (centry) {
2958 goto do_fetch_cache;
2962 /* and save it */
2963 refresh_sequence_number(domain, false);
2964 if (!NT_STATUS_IS_OK(status)) {
2965 return status;
2967 wcache_save_lockout_policy(domain, status, policy);
2969 return status;
2972 /* get password policy */
2973 static NTSTATUS password_policy(struct winbindd_domain *domain,
2974 TALLOC_CTX *mem_ctx,
2975 struct samr_DomInfo1 *policy)
2977 struct winbind_cache *cache = get_cache(domain);
2978 struct cache_entry *centry = NULL;
2979 NTSTATUS status;
2980 bool old_status;
2982 old_status = domain->online;
2983 if (!cache->tdb)
2984 goto do_query;
2986 centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
2988 if (!centry)
2989 goto do_query;
2991 do_fetch_cache:
2992 policy->min_password_length = centry_uint16(centry);
2993 policy->password_history_length = centry_uint16(centry);
2994 policy->password_properties = centry_uint32(centry);
2995 policy->max_password_age = centry_nttime(centry);
2996 policy->min_password_age = centry_nttime(centry);
2998 status = centry->status;
3000 DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
3001 domain->name, nt_errstr(status) ));
3003 centry_free(centry);
3004 return status;
3006 do_query:
3007 ZERO_STRUCTP(policy);
3009 /* Return status value returned by seq number check */
3011 if (!NT_STATUS_IS_OK(domain->last_status))
3012 return domain->last_status;
3014 DEBUG(10,("password_policy: [Cached] - doing backend query for info for domain %s\n",
3015 domain->name ));
3017 status = domain->backend->password_policy(domain, mem_ctx, policy);
3019 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
3020 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
3021 if (!domain->internal && old_status) {
3022 set_domain_offline(domain);
3024 if (cache->tdb &&
3025 !domain->internal &&
3026 !domain->online &&
3027 old_status) {
3028 centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
3029 if (centry) {
3030 goto do_fetch_cache;
3034 /* and save it */
3035 refresh_sequence_number(domain, false);
3036 if (!NT_STATUS_IS_OK(status)) {
3037 return status;
3039 wcache_save_password_policy(domain, status, policy);
3041 return status;
3045 /* Invalidate cached user and group lists coherently */
3047 static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
3048 void *state)
3050 if (strncmp((const char *)kbuf.dptr, "UL/", 3) == 0 ||
3051 strncmp((const char *)kbuf.dptr, "GL/", 3) == 0)
3052 tdb_delete(the_tdb, kbuf);
3054 return 0;
3057 /* Invalidate the getpwnam and getgroups entries for a winbindd domain */
3059 void wcache_invalidate_samlogon(struct winbindd_domain *domain,
3060 const struct dom_sid *sid)
3062 fstring key_str, sid_string;
3063 struct winbind_cache *cache;
3065 /* dont clear cached U/SID and UG/SID entries when we want to logon
3066 * offline - gd */
3068 if (lp_winbind_offline_logon()) {
3069 return;
3072 if (!domain)
3073 return;
3075 cache = get_cache(domain);
3077 if (!cache->tdb) {
3078 return;
3081 /* Clear U/SID cache entry */
3082 fstr_sprintf(key_str, "U/%s", sid_to_fstring(sid_string, sid));
3083 DEBUG(10, ("wcache_invalidate_samlogon: clearing %s\n", key_str));
3084 tdb_delete(cache->tdb, string_tdb_data(key_str));
3086 /* Clear UG/SID cache entry */
3087 fstr_sprintf(key_str, "UG/%s", sid_to_fstring(sid_string, sid));
3088 DEBUG(10, ("wcache_invalidate_samlogon: clearing %s\n", key_str));
3089 tdb_delete(cache->tdb, string_tdb_data(key_str));
3091 /* Samba/winbindd never needs this. */
3092 netsamlogon_clear_cached_user(sid);
3095 bool wcache_invalidate_cache(void)
3097 struct winbindd_domain *domain;
3099 for (domain = domain_list(); domain; domain = domain->next) {
3100 struct winbind_cache *cache = get_cache(domain);
3102 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
3103 "entries for %s\n", domain->name));
3104 if (cache) {
3105 if (cache->tdb) {
3106 tdb_traverse(cache->tdb, traverse_fn, NULL);
3107 } else {
3108 return false;
3112 return true;
3115 bool wcache_invalidate_cache_noinit(void)
3117 struct winbindd_domain *domain;
3119 for (domain = domain_list(); domain; domain = domain->next) {
3120 struct winbind_cache *cache;
3122 /* Skip uninitialized domains. */
3123 if (!domain->initialized && !domain->internal) {
3124 continue;
3127 cache = get_cache(domain);
3129 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
3130 "entries for %s\n", domain->name));
3131 if (cache) {
3132 if (cache->tdb) {
3133 tdb_traverse(cache->tdb, traverse_fn, NULL);
3135 * Flushing cache has nothing to with domains.
3136 * return here if we successfully flushed once.
3137 * To avoid unnecessary traversing the cache.
3139 return true;
3140 } else {
3141 return false;
3145 return true;
3148 bool init_wcache(void)
3150 if (wcache == NULL) {
3151 wcache = SMB_XMALLOC_P(struct winbind_cache);
3152 ZERO_STRUCTP(wcache);
3155 if (wcache->tdb != NULL)
3156 return true;
3158 /* when working offline we must not clear the cache on restart */
3159 wcache->tdb = tdb_open_log(state_path("winbindd_cache.tdb"),
3160 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
3161 TDB_INCOMPATIBLE_HASH |
3162 (lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST)),
3163 O_RDWR|O_CREAT, 0600);
3165 if (wcache->tdb == NULL) {
3166 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
3167 return false;
3170 return true;
3173 /************************************************************************
3174 This is called by the parent to initialize the cache file.
3175 We don't need sophisticated locking here as we know we're the
3176 only opener.
3177 ************************************************************************/
3179 bool initialize_winbindd_cache(void)
3181 bool cache_bad = true;
3182 uint32 vers;
3184 if (!init_wcache()) {
3185 DEBUG(0,("initialize_winbindd_cache: init_wcache failed.\n"));
3186 return false;
3189 /* Check version number. */
3190 if (tdb_fetch_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, &vers) &&
3191 vers == WINBINDD_CACHE_VERSION) {
3192 cache_bad = false;
3195 if (cache_bad) {
3196 DEBUG(0,("initialize_winbindd_cache: clearing cache "
3197 "and re-creating with version number %d\n",
3198 WINBINDD_CACHE_VERSION ));
3200 tdb_close(wcache->tdb);
3201 wcache->tdb = NULL;
3203 if (unlink(state_path("winbindd_cache.tdb")) == -1) {
3204 DEBUG(0,("initialize_winbindd_cache: unlink %s failed %s ",
3205 state_path("winbindd_cache.tdb"),
3206 strerror(errno) ));
3207 return false;
3209 if (!init_wcache()) {
3210 DEBUG(0,("initialize_winbindd_cache: re-initialization "
3211 "init_wcache failed.\n"));
3212 return false;
3215 /* Write the version. */
3216 if (!tdb_store_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, WINBINDD_CACHE_VERSION)) {
3217 DEBUG(0,("initialize_winbindd_cache: version number store failed %s\n",
3218 tdb_errorstr_compat(wcache->tdb) ));
3219 return false;
3223 tdb_close(wcache->tdb);
3224 wcache->tdb = NULL;
3225 return true;
3228 void close_winbindd_cache(void)
3230 if (!wcache) {
3231 return;
3233 if (wcache->tdb) {
3234 tdb_close(wcache->tdb);
3235 wcache->tdb = NULL;
3239 bool lookup_cached_sid(TALLOC_CTX *mem_ctx, const struct dom_sid *sid,
3240 char **domain_name, char **name,
3241 enum lsa_SidType *type)
3243 struct winbindd_domain *domain;
3244 NTSTATUS status;
3246 domain = find_lookup_domain_from_sid(sid);
3247 if (domain == NULL) {
3248 return false;
3250 status = wcache_sid_to_name(domain, sid, mem_ctx, domain_name, name,
3251 type);
3252 return NT_STATUS_IS_OK(status);
3255 bool lookup_cached_name(const char *domain_name,
3256 const char *name,
3257 struct dom_sid *sid,
3258 enum lsa_SidType *type)
3260 struct winbindd_domain *domain;
3261 NTSTATUS status;
3262 bool original_online_state;
3264 domain = find_lookup_domain_from_name(domain_name);
3265 if (domain == NULL) {
3266 return false;
3269 /* If we are doing a cached logon, temporarily set the domain
3270 offline so the cache won't expire the entry */
3272 original_online_state = domain->online;
3273 domain->online = false;
3274 status = wcache_name_to_sid(domain, domain_name, name, sid, type);
3275 domain->online = original_online_state;
3277 return NT_STATUS_IS_OK(status);
3280 void cache_name2sid(struct winbindd_domain *domain,
3281 const char *domain_name, const char *name,
3282 enum lsa_SidType type, const struct dom_sid *sid)
3284 refresh_sequence_number(domain, false);
3285 wcache_save_name_to_sid(domain, NT_STATUS_OK, domain_name, name,
3286 sid, type);
3290 * The original idea that this cache only contains centries has
3291 * been blurred - now other stuff gets put in here. Ensure we
3292 * ignore these things on cleanup.
3295 static int traverse_fn_cleanup(TDB_CONTEXT *the_tdb, TDB_DATA kbuf,
3296 TDB_DATA dbuf, void *state)
3298 struct cache_entry *centry;
3300 if (is_non_centry_key(kbuf)) {
3301 return 0;
3304 centry = wcache_fetch_raw((char *)kbuf.dptr);
3305 if (!centry) {
3306 return 0;
3309 if (!NT_STATUS_IS_OK(centry->status)) {
3310 DEBUG(10,("deleting centry %s\n", (const char *)kbuf.dptr));
3311 tdb_delete(the_tdb, kbuf);
3314 centry_free(centry);
3315 return 0;
3318 /* flush the cache */
3319 void wcache_flush_cache(void)
3321 if (!wcache)
3322 return;
3323 if (wcache->tdb) {
3324 tdb_close(wcache->tdb);
3325 wcache->tdb = NULL;
3327 if (!winbindd_use_cache()) {
3328 return;
3331 /* when working offline we must not clear the cache on restart */
3332 wcache->tdb = tdb_open_log(state_path("winbindd_cache.tdb"),
3333 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
3334 TDB_INCOMPATIBLE_HASH |
3335 (lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST)),
3336 O_RDWR|O_CREAT, 0600);
3338 if (!wcache->tdb) {
3339 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
3340 return;
3343 tdb_traverse(wcache->tdb, traverse_fn_cleanup, NULL);
3345 DEBUG(10,("wcache_flush_cache success\n"));
3348 /* Count cached creds */
3350 static int traverse_fn_cached_creds(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
3351 void *state)
3353 int *cred_count = (int*)state;
3355 if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
3356 (*cred_count)++;
3358 return 0;
3361 NTSTATUS wcache_count_cached_creds(struct winbindd_domain *domain, int *count)
3363 struct winbind_cache *cache = get_cache(domain);
3365 *count = 0;
3367 if (!cache->tdb) {
3368 return NT_STATUS_INTERNAL_DB_ERROR;
3371 tdb_traverse(cache->tdb, traverse_fn_cached_creds, (void *)count);
3373 return NT_STATUS_OK;
3376 struct cred_list {
3377 struct cred_list *prev, *next;
3378 TDB_DATA key;
3379 fstring name;
3380 time_t created;
3382 static struct cred_list *wcache_cred_list;
3384 static int traverse_fn_get_credlist(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
3385 void *state)
3387 struct cred_list *cred;
3389 if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
3391 cred = SMB_MALLOC_P(struct cred_list);
3392 if (cred == NULL) {
3393 DEBUG(0,("traverse_fn_remove_first_creds: failed to malloc new entry for list\n"));
3394 return -1;
3397 ZERO_STRUCTP(cred);
3399 /* save a copy of the key */
3401 fstrcpy(cred->name, (const char *)kbuf.dptr);
3402 DLIST_ADD(wcache_cred_list, cred);
3405 return 0;
3408 NTSTATUS wcache_remove_oldest_cached_creds(struct winbindd_domain *domain, const struct dom_sid *sid)
3410 struct winbind_cache *cache = get_cache(domain);
3411 NTSTATUS status;
3412 int ret;
3413 struct cred_list *cred, *oldest = NULL;
3415 if (!cache->tdb) {
3416 return NT_STATUS_INTERNAL_DB_ERROR;
3419 /* we possibly already have an entry */
3420 if (sid && NT_STATUS_IS_OK(wcache_cached_creds_exist(domain, sid))) {
3422 fstring key_str, tmp;
3424 DEBUG(11,("we already have an entry, deleting that\n"));
3426 fstr_sprintf(key_str, "CRED/%s", sid_to_fstring(tmp, sid));
3428 tdb_delete(cache->tdb, string_tdb_data(key_str));
3430 return NT_STATUS_OK;
3433 ret = tdb_traverse(cache->tdb, traverse_fn_get_credlist, NULL);
3434 if (ret == 0) {
3435 return NT_STATUS_OK;
3436 } else if ((ret < 0) || (wcache_cred_list == NULL)) {
3437 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
3440 ZERO_STRUCTP(oldest);
3442 for (cred = wcache_cred_list; cred; cred = cred->next) {
3444 TDB_DATA data;
3445 time_t t;
3447 data = tdb_fetch_compat(cache->tdb, string_tdb_data(cred->name));
3448 if (!data.dptr) {
3449 DEBUG(10,("wcache_remove_oldest_cached_creds: entry for [%s] not found\n",
3450 cred->name));
3451 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
3452 goto done;
3455 t = IVAL(data.dptr, 0);
3456 SAFE_FREE(data.dptr);
3458 if (!oldest) {
3459 oldest = SMB_MALLOC_P(struct cred_list);
3460 if (oldest == NULL) {
3461 status = NT_STATUS_NO_MEMORY;
3462 goto done;
3465 fstrcpy(oldest->name, cred->name);
3466 oldest->created = t;
3467 continue;
3470 if (t < oldest->created) {
3471 fstrcpy(oldest->name, cred->name);
3472 oldest->created = t;
3476 if (tdb_delete(cache->tdb, string_tdb_data(oldest->name)) == 0) {
3477 status = NT_STATUS_OK;
3478 } else {
3479 status = NT_STATUS_UNSUCCESSFUL;
3481 done:
3482 SAFE_FREE(wcache_cred_list);
3483 SAFE_FREE(oldest);
3485 return status;
3488 /* Change the global online/offline state. */
3489 bool set_global_winbindd_state_offline(void)
3491 TDB_DATA data;
3493 DEBUG(10,("set_global_winbindd_state_offline: offline requested.\n"));
3495 /* Only go offline if someone has created
3496 the key "WINBINDD_OFFLINE" in the cache tdb. */
3498 if (wcache == NULL || wcache->tdb == NULL) {
3499 DEBUG(10,("set_global_winbindd_state_offline: wcache not open yet.\n"));
3500 return false;
3503 if (!lp_winbind_offline_logon()) {
3504 DEBUG(10,("set_global_winbindd_state_offline: rejecting.\n"));
3505 return false;
3508 if (global_winbindd_offline_state) {
3509 /* Already offline. */
3510 return true;
3513 data = tdb_fetch_bystring( wcache->tdb, "WINBINDD_OFFLINE" );
3515 if (!data.dptr || data.dsize != 4) {
3516 DEBUG(10,("set_global_winbindd_state_offline: offline state not set.\n"));
3517 SAFE_FREE(data.dptr);
3518 return false;
3519 } else {
3520 DEBUG(10,("set_global_winbindd_state_offline: offline state set.\n"));
3521 global_winbindd_offline_state = true;
3522 SAFE_FREE(data.dptr);
3523 return true;
3527 void set_global_winbindd_state_online(void)
3529 DEBUG(10,("set_global_winbindd_state_online: online requested.\n"));
3531 if (!lp_winbind_offline_logon()) {
3532 DEBUG(10,("set_global_winbindd_state_online: rejecting.\n"));
3533 return;
3536 if (!global_winbindd_offline_state) {
3537 /* Already online. */
3538 return;
3540 global_winbindd_offline_state = false;
3542 if (!wcache->tdb) {
3543 return;
3546 /* Ensure there is no key "WINBINDD_OFFLINE" in the cache tdb. */
3547 tdb_delete_bystring(wcache->tdb, "WINBINDD_OFFLINE");
3550 bool get_global_winbindd_state_offline(void)
3552 return global_winbindd_offline_state;
3555 /***********************************************************************
3556 Validate functions for all possible cache tdb keys.
3557 ***********************************************************************/
3559 static struct cache_entry *create_centry_validate(const char *kstr, TDB_DATA data,
3560 struct tdb_validation_status *state)
3562 struct cache_entry *centry;
3564 centry = SMB_XMALLOC_P(struct cache_entry);
3565 centry->data = (unsigned char *)smb_memdup(data.dptr, data.dsize);
3566 if (!centry->data) {
3567 SAFE_FREE(centry);
3568 return NULL;
3570 centry->len = data.dsize;
3571 centry->ofs = 0;
3573 if (centry->len < 16) {
3574 /* huh? corrupt cache? */
3575 DEBUG(0,("create_centry_validate: Corrupt cache for key %s "
3576 "(len < 16) ?\n", kstr));
3577 centry_free(centry);
3578 state->bad_entry = true;
3579 state->success = false;
3580 return NULL;
3583 centry->status = NT_STATUS(centry_uint32(centry));
3584 centry->sequence_number = centry_uint32(centry);
3585 centry->timeout = centry_uint64_t(centry);
3586 return centry;
3589 static int validate_seqnum(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3590 struct tdb_validation_status *state)
3592 if (dbuf.dsize != 8) {
3593 DEBUG(0,("validate_seqnum: Corrupt cache for key %s (len %u != 8) ?\n",
3594 keystr, (unsigned int)dbuf.dsize ));
3595 state->bad_entry = true;
3596 return 1;
3598 return 0;
3601 static int validate_ns(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3602 struct tdb_validation_status *state)
3604 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3605 if (!centry) {
3606 return 1;
3609 (void)centry_uint32(centry);
3610 if (NT_STATUS_IS_OK(centry->status)) {
3611 struct dom_sid sid;
3612 (void)centry_sid(centry, &sid);
3615 centry_free(centry);
3617 if (!(state->success)) {
3618 return 1;
3620 DEBUG(10,("validate_ns: %s ok\n", keystr));
3621 return 0;
3624 static int validate_sn(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3625 struct tdb_validation_status *state)
3627 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3628 if (!centry) {
3629 return 1;
3632 if (NT_STATUS_IS_OK(centry->status)) {
3633 (void)centry_uint32(centry);
3634 (void)centry_string(centry, mem_ctx);
3635 (void)centry_string(centry, mem_ctx);
3638 centry_free(centry);
3640 if (!(state->success)) {
3641 return 1;
3643 DEBUG(10,("validate_sn: %s ok\n", keystr));
3644 return 0;
3647 static int validate_u(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3648 struct tdb_validation_status *state)
3650 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3651 struct dom_sid sid;
3653 if (!centry) {
3654 return 1;
3657 (void)centry_string(centry, mem_ctx);
3658 (void)centry_string(centry, mem_ctx);
3659 (void)centry_string(centry, mem_ctx);
3660 (void)centry_string(centry, mem_ctx);
3661 (void)centry_uint32(centry);
3662 (void)centry_sid(centry, &sid);
3663 (void)centry_sid(centry, &sid);
3665 centry_free(centry);
3667 if (!(state->success)) {
3668 return 1;
3670 DEBUG(10,("validate_u: %s ok\n", keystr));
3671 return 0;
3674 static int validate_loc_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3675 struct tdb_validation_status *state)
3677 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3679 if (!centry) {
3680 return 1;
3683 (void)centry_nttime(centry);
3684 (void)centry_nttime(centry);
3685 (void)centry_uint16(centry);
3687 centry_free(centry);
3689 if (!(state->success)) {
3690 return 1;
3692 DEBUG(10,("validate_loc_pol: %s ok\n", keystr));
3693 return 0;
3696 static int validate_pwd_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3697 struct tdb_validation_status *state)
3699 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3701 if (!centry) {
3702 return 1;
3705 (void)centry_uint16(centry);
3706 (void)centry_uint16(centry);
3707 (void)centry_uint32(centry);
3708 (void)centry_nttime(centry);
3709 (void)centry_nttime(centry);
3711 centry_free(centry);
3713 if (!(state->success)) {
3714 return 1;
3716 DEBUG(10,("validate_pwd_pol: %s ok\n", keystr));
3717 return 0;
3720 static int validate_cred(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3721 struct tdb_validation_status *state)
3723 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3725 if (!centry) {
3726 return 1;
3729 (void)centry_time(centry);
3730 (void)centry_hash16(centry, mem_ctx);
3732 /* We only have 17 bytes more data in the salted cred case. */
3733 if (centry->len - centry->ofs == 17) {
3734 (void)centry_hash16(centry, mem_ctx);
3737 centry_free(centry);
3739 if (!(state->success)) {
3740 return 1;
3742 DEBUG(10,("validate_cred: %s ok\n", keystr));
3743 return 0;
3746 static int validate_ul(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3747 struct tdb_validation_status *state)
3749 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3750 int32 num_entries, i;
3752 if (!centry) {
3753 return 1;
3756 num_entries = (int32)centry_uint32(centry);
3758 for (i=0; i< num_entries; i++) {
3759 struct dom_sid sid;
3760 (void)centry_string(centry, mem_ctx);
3761 (void)centry_string(centry, mem_ctx);
3762 (void)centry_string(centry, mem_ctx);
3763 (void)centry_string(centry, mem_ctx);
3764 (void)centry_sid(centry, &sid);
3765 (void)centry_sid(centry, &sid);
3768 centry_free(centry);
3770 if (!(state->success)) {
3771 return 1;
3773 DEBUG(10,("validate_ul: %s ok\n", keystr));
3774 return 0;
3777 static int validate_gl(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3778 struct tdb_validation_status *state)
3780 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3781 int32 num_entries, i;
3783 if (!centry) {
3784 return 1;
3787 num_entries = centry_uint32(centry);
3789 for (i=0; i< num_entries; i++) {
3790 (void)centry_string(centry, mem_ctx);
3791 (void)centry_string(centry, mem_ctx);
3792 (void)centry_uint32(centry);
3795 centry_free(centry);
3797 if (!(state->success)) {
3798 return 1;
3800 DEBUG(10,("validate_gl: %s ok\n", keystr));
3801 return 0;
3804 static int validate_ug(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3805 struct tdb_validation_status *state)
3807 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3808 int32 num_groups, i;
3810 if (!centry) {
3811 return 1;
3814 num_groups = centry_uint32(centry);
3816 for (i=0; i< num_groups; i++) {
3817 struct dom_sid sid;
3818 centry_sid(centry, &sid);
3821 centry_free(centry);
3823 if (!(state->success)) {
3824 return 1;
3826 DEBUG(10,("validate_ug: %s ok\n", keystr));
3827 return 0;
3830 static int validate_ua(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3831 struct tdb_validation_status *state)
3833 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3834 int32 num_aliases, i;
3836 if (!centry) {
3837 return 1;
3840 num_aliases = centry_uint32(centry);
3842 for (i=0; i < num_aliases; i++) {
3843 (void)centry_uint32(centry);
3846 centry_free(centry);
3848 if (!(state->success)) {
3849 return 1;
3851 DEBUG(10,("validate_ua: %s ok\n", keystr));
3852 return 0;
3855 static int validate_gm(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3856 struct tdb_validation_status *state)
3858 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3859 int32 num_names, i;
3861 if (!centry) {
3862 return 1;
3865 num_names = centry_uint32(centry);
3867 for (i=0; i< num_names; i++) {
3868 struct dom_sid sid;
3869 centry_sid(centry, &sid);
3870 (void)centry_string(centry, mem_ctx);
3871 (void)centry_uint32(centry);
3874 centry_free(centry);
3876 if (!(state->success)) {
3877 return 1;
3879 DEBUG(10,("validate_gm: %s ok\n", keystr));
3880 return 0;
3883 static int validate_dr(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3884 struct tdb_validation_status *state)
3886 /* Can't say anything about this other than must be nonzero. */
3887 if (dbuf.dsize == 0) {
3888 DEBUG(0,("validate_dr: Corrupt cache for key %s (len == 0) ?\n",
3889 keystr));
3890 state->bad_entry = true;
3891 state->success = false;
3892 return 1;
3895 DEBUG(10,("validate_dr: %s ok\n", keystr));
3896 return 0;
3899 static int validate_de(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3900 struct tdb_validation_status *state)
3902 /* Can't say anything about this other than must be nonzero. */
3903 if (dbuf.dsize == 0) {
3904 DEBUG(0,("validate_de: Corrupt cache for key %s (len == 0) ?\n",
3905 keystr));
3906 state->bad_entry = true;
3907 state->success = false;
3908 return 1;
3911 DEBUG(10,("validate_de: %s ok\n", keystr));
3912 return 0;
3915 static int validate_pwinfo(TALLOC_CTX *mem_ctx, const char *keystr,
3916 TDB_DATA dbuf, struct tdb_validation_status *state)
3918 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3920 if (!centry) {
3921 return 1;
3924 (void)centry_string(centry, mem_ctx);
3925 (void)centry_string(centry, mem_ctx);
3926 (void)centry_string(centry, mem_ctx);
3927 (void)centry_uint32(centry);
3929 centry_free(centry);
3931 if (!(state->success)) {
3932 return 1;
3934 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3935 return 0;
3938 static int validate_nss_an(TALLOC_CTX *mem_ctx, const char *keystr,
3939 TDB_DATA dbuf,
3940 struct tdb_validation_status *state)
3942 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3944 if (!centry) {
3945 return 1;
3948 (void)centry_string( centry, mem_ctx );
3950 centry_free(centry);
3952 if (!(state->success)) {
3953 return 1;
3955 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3956 return 0;
3959 static int validate_nss_na(TALLOC_CTX *mem_ctx, const char *keystr,
3960 TDB_DATA dbuf,
3961 struct tdb_validation_status *state)
3963 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3965 if (!centry) {
3966 return 1;
3969 (void)centry_string( centry, mem_ctx );
3971 centry_free(centry);
3973 if (!(state->success)) {
3974 return 1;
3976 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3977 return 0;
3980 static int validate_trustdomcache(TALLOC_CTX *mem_ctx, const char *keystr,
3981 TDB_DATA dbuf,
3982 struct tdb_validation_status *state)
3984 if (dbuf.dsize == 0) {
3985 DEBUG(0, ("validate_trustdomcache: Corrupt cache for "
3986 "key %s (len ==0) ?\n", keystr));
3987 state->bad_entry = true;
3988 state->success = false;
3989 return 1;
3992 DEBUG(10, ("validate_trustdomcache: %s ok\n", keystr));
3993 DEBUGADD(10, (" Don't trust me, I am a DUMMY!\n"));
3994 return 0;
3997 static int validate_offline(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3998 struct tdb_validation_status *state)
4000 if (dbuf.dsize != 4) {
4001 DEBUG(0,("validate_offline: Corrupt cache for key %s (len %u != 4) ?\n",
4002 keystr, (unsigned int)dbuf.dsize ));
4003 state->bad_entry = true;
4004 state->success = false;
4005 return 1;
4007 DEBUG(10,("validate_offline: %s ok\n", keystr));
4008 return 0;
4011 static int validate_ndr(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
4012 struct tdb_validation_status *state)
4015 * Ignore validation for now. The proper way to do this is with a
4016 * checksum. Just pure parsing does not really catch much.
4018 return 0;
4021 static int validate_cache_version(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
4022 struct tdb_validation_status *state)
4024 if (dbuf.dsize != 4) {
4025 DEBUG(0, ("validate_cache_version: Corrupt cache for "
4026 "key %s (len %u != 4) ?\n",
4027 keystr, (unsigned int)dbuf.dsize));
4028 state->bad_entry = true;
4029 state->success = false;
4030 return 1;
4033 DEBUG(10, ("validate_cache_version: %s ok\n", keystr));
4034 return 0;
4037 /***********************************************************************
4038 A list of all possible cache tdb keys with associated validation
4039 functions.
4040 ***********************************************************************/
4042 struct key_val_struct {
4043 const char *keyname;
4044 int (*validate_data_fn)(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, struct tdb_validation_status* state);
4045 } key_val[] = {
4046 {"SEQNUM/", validate_seqnum},
4047 {"NS/", validate_ns},
4048 {"SN/", validate_sn},
4049 {"U/", validate_u},
4050 {"LOC_POL/", validate_loc_pol},
4051 {"PWD_POL/", validate_pwd_pol},
4052 {"CRED/", validate_cred},
4053 {"UL/", validate_ul},
4054 {"GL/", validate_gl},
4055 {"UG/", validate_ug},
4056 {"UA", validate_ua},
4057 {"GM/", validate_gm},
4058 {"DR/", validate_dr},
4059 {"DE/", validate_de},
4060 {"NSS/PWINFO/", validate_pwinfo},
4061 {"TRUSTDOMCACHE/", validate_trustdomcache},
4062 {"NSS/NA/", validate_nss_na},
4063 {"NSS/AN/", validate_nss_an},
4064 {"WINBINDD_OFFLINE", validate_offline},
4065 {"NDR/", validate_ndr},
4066 {WINBINDD_CACHE_VERSION_KEYSTR, validate_cache_version},
4067 {NULL, NULL}
4070 /***********************************************************************
4071 Function to look at every entry in the tdb and validate it as far as
4072 possible.
4073 ***********************************************************************/
4075 static int cache_traverse_validate_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, void *state)
4077 int i;
4078 unsigned int max_key_len = 1024;
4079 struct tdb_validation_status *v_state = (struct tdb_validation_status *)state;
4081 /* Paranoia check. */
4082 if (strncmp("UA/", (const char *)kbuf.dptr, 3) == 0 ||
4083 strncmp("NDR/", (const char *)kbuf.dptr, 4) == 0) {
4084 max_key_len = 1024 * 1024;
4086 if (kbuf.dsize > max_key_len) {
4087 DEBUG(0, ("cache_traverse_validate_fn: key length too large: "
4088 "(%u) > (%u)\n\n",
4089 (unsigned int)kbuf.dsize, (unsigned int)max_key_len));
4090 return 1;
4093 for (i = 0; key_val[i].keyname; i++) {
4094 size_t namelen = strlen(key_val[i].keyname);
4095 if (kbuf.dsize >= namelen && (
4096 strncmp(key_val[i].keyname, (const char *)kbuf.dptr, namelen)) == 0) {
4097 TALLOC_CTX *mem_ctx;
4098 char *keystr;
4099 int ret;
4101 keystr = SMB_MALLOC_ARRAY(char, kbuf.dsize+1);
4102 if (!keystr) {
4103 return 1;
4105 memcpy(keystr, kbuf.dptr, kbuf.dsize);
4106 keystr[kbuf.dsize] = '\0';
4108 mem_ctx = talloc_init("validate_ctx");
4109 if (!mem_ctx) {
4110 SAFE_FREE(keystr);
4111 return 1;
4114 ret = key_val[i].validate_data_fn(mem_ctx, keystr, dbuf,
4115 v_state);
4117 SAFE_FREE(keystr);
4118 talloc_destroy(mem_ctx);
4119 return ret;
4123 DEBUG(0,("cache_traverse_validate_fn: unknown cache entry\nkey :\n"));
4124 dump_data(0, (uint8 *)kbuf.dptr, kbuf.dsize);
4125 DEBUG(0,("data :\n"));
4126 dump_data(0, (uint8 *)dbuf.dptr, dbuf.dsize);
4127 v_state->unknown_key = true;
4128 v_state->success = false;
4129 return 1; /* terminate. */
4132 static void validate_panic(const char *const why)
4134 DEBUG(0,("validating cache: would panic %s\n", why ));
4135 DEBUGADD(0, ("exiting instead (cache validation mode)\n"));
4136 exit(47);
4139 static int wbcache_update_centry_fn(TDB_CONTEXT *tdb,
4140 TDB_DATA key,
4141 TDB_DATA data,
4142 void *state)
4144 uint64_t ctimeout;
4145 TDB_DATA blob;
4147 if (is_non_centry_key(key)) {
4148 return 0;
4151 if (data.dptr == NULL || data.dsize == 0) {
4152 if (tdb_delete(tdb, key) < 0) {
4153 DEBUG(0, ("tdb_delete for [%s] failed!\n",
4154 key.dptr));
4155 return 1;
4159 /* add timeout to blob (uint64_t) */
4160 blob.dsize = data.dsize + 8;
4162 blob.dptr = SMB_XMALLOC_ARRAY(uint8_t, blob.dsize);
4163 if (blob.dptr == NULL) {
4164 return 1;
4166 memset(blob.dptr, 0, blob.dsize);
4168 /* copy status and seqnum */
4169 memcpy(blob.dptr, data.dptr, 8);
4171 /* add timeout */
4172 ctimeout = lp_winbind_cache_time() + time(NULL);
4173 SBVAL(blob.dptr, 8, ctimeout);
4175 /* copy the rest */
4176 memcpy(blob.dptr + 16, data.dptr + 8, data.dsize - 8);
4178 if (tdb_store(tdb, key, blob, TDB_REPLACE) < 0) {
4179 DEBUG(0, ("tdb_store to update [%s] failed!\n",
4180 key.dptr));
4181 SAFE_FREE(blob.dptr);
4182 return 1;
4185 SAFE_FREE(blob.dptr);
4186 return 0;
4189 static bool wbcache_upgrade_v1_to_v2(TDB_CONTEXT *tdb)
4191 int rc;
4193 DEBUG(1, ("Upgrade to version 2 of the winbindd_cache.tdb\n"));
4195 rc = tdb_traverse(tdb, wbcache_update_centry_fn, NULL);
4196 if (rc < 0) {
4197 return false;
4200 return true;
4203 /***********************************************************************
4204 Try and validate every entry in the winbindd cache. If we fail here,
4205 delete the cache tdb and return non-zero.
4206 ***********************************************************************/
4208 int winbindd_validate_cache(void)
4210 int ret = -1;
4211 const char *tdb_path = state_path("winbindd_cache.tdb");
4212 TDB_CONTEXT *tdb = NULL;
4213 uint32_t vers_id;
4214 bool ok;
4216 DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
4217 smb_panic_fn = validate_panic;
4219 tdb = tdb_open_log(tdb_path,
4220 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
4221 TDB_INCOMPATIBLE_HASH |
4222 ( lp_winbind_offline_logon()
4223 ? TDB_DEFAULT
4224 : TDB_DEFAULT | TDB_CLEAR_IF_FIRST ),
4225 O_RDWR|O_CREAT,
4226 0600);
4227 if (!tdb) {
4228 DEBUG(0, ("winbindd_validate_cache: "
4229 "error opening/initializing tdb\n"));
4230 goto done;
4233 /* Version check and upgrade code. */
4234 if (!tdb_fetch_uint32(tdb, WINBINDD_CACHE_VERSION_KEYSTR, &vers_id)) {
4235 DEBUG(10, ("Fresh database\n"));
4236 tdb_store_uint32(tdb, WINBINDD_CACHE_VERSION_KEYSTR, WINBINDD_CACHE_VERSION);
4237 vers_id = WINBINDD_CACHE_VERSION;
4240 if (vers_id != WINBINDD_CACHE_VERSION) {
4241 if (vers_id == WINBINDD_CACHE_VER1) {
4242 ok = wbcache_upgrade_v1_to_v2(tdb);
4243 if (!ok) {
4244 DEBUG(10, ("winbindd_validate_cache: upgrade to version 2 failed.\n"));
4245 unlink(tdb_path);
4246 goto done;
4249 tdb_store_uint32(tdb,
4250 WINBINDD_CACHE_VERSION_KEYSTR,
4251 WINBINDD_CACHE_VERSION);
4252 vers_id = WINBINDD_CACHE_VER2;
4256 tdb_close(tdb);
4258 ret = tdb_validate_and_backup(tdb_path, cache_traverse_validate_fn);
4260 if (ret != 0) {
4261 DEBUG(10, ("winbindd_validate_cache: validation not successful.\n"));
4262 DEBUGADD(10, ("removing tdb %s.\n", tdb_path));
4263 unlink(tdb_path);
4266 done:
4267 DEBUG(10, ("winbindd_validate_cache: restoring panic function\n"));
4268 smb_panic_fn = smb_panic;
4269 return ret;
4272 /***********************************************************************
4273 Try and validate every entry in the winbindd cache.
4274 ***********************************************************************/
4276 int winbindd_validate_cache_nobackup(void)
4278 int ret = -1;
4279 const char *tdb_path = state_path("winbindd_cache.tdb");
4281 DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
4282 smb_panic_fn = validate_panic;
4285 if (wcache == NULL || wcache->tdb == NULL) {
4286 ret = tdb_validate_open(tdb_path, cache_traverse_validate_fn);
4287 } else {
4288 ret = tdb_validate(wcache->tdb, cache_traverse_validate_fn);
4291 if (ret != 0) {
4292 DEBUG(10, ("winbindd_validate_cache_nobackup: validation not "
4293 "successful.\n"));
4296 DEBUG(10, ("winbindd_validate_cache_nobackup: restoring panic "
4297 "function\n"));
4298 smb_panic_fn = smb_panic;
4299 return ret;
4302 bool winbindd_cache_validate_and_initialize(void)
4304 close_winbindd_cache();
4306 if (lp_winbind_offline_logon()) {
4307 if (winbindd_validate_cache() < 0) {
4308 DEBUG(0, ("winbindd cache tdb corrupt and no backup "
4309 "could be restored.\n"));
4313 return initialize_winbindd_cache();
4316 /*********************************************************************
4317 ********************************************************************/
4319 static bool add_wbdomain_to_tdc_array( struct winbindd_domain *new_dom,
4320 struct winbindd_tdc_domain **domains,
4321 size_t *num_domains )
4323 struct winbindd_tdc_domain *list = NULL;
4324 size_t idx;
4325 int i;
4326 bool set_only = false;
4328 /* don't allow duplicates */
4330 idx = *num_domains;
4331 list = *domains;
4333 for ( i=0; i< (*num_domains); i++ ) {
4334 if ( strequal( new_dom->name, list[i].domain_name ) ) {
4335 DEBUG(10,("add_wbdomain_to_tdc_array: Found existing record for %s\n",
4336 new_dom->name));
4337 idx = i;
4338 set_only = true;
4340 break;
4344 if ( !set_only ) {
4345 if ( !*domains ) {
4346 list = talloc_array( NULL, struct winbindd_tdc_domain, 1 );
4347 idx = 0;
4348 } else {
4349 list = talloc_realloc( *domains, *domains,
4350 struct winbindd_tdc_domain,
4351 (*num_domains)+1);
4352 idx = *num_domains;
4355 ZERO_STRUCT( list[idx] );
4358 if ( !list )
4359 return false;
4361 list[idx].domain_name = talloc_strdup(list, new_dom->name);
4362 if (list[idx].domain_name == NULL) {
4363 return false;
4365 if (new_dom->alt_name != NULL) {
4366 list[idx].dns_name = talloc_strdup(list, new_dom->alt_name);
4367 if (list[idx].dns_name == NULL) {
4368 return false;
4372 if ( !is_null_sid( &new_dom->sid ) ) {
4373 sid_copy( &list[idx].sid, &new_dom->sid );
4374 } else {
4375 sid_copy(&list[idx].sid, &global_sid_NULL);
4378 if ( new_dom->domain_flags != 0x0 )
4379 list[idx].trust_flags = new_dom->domain_flags;
4381 if ( new_dom->domain_type != 0x0 )
4382 list[idx].trust_type = new_dom->domain_type;
4384 if ( new_dom->domain_trust_attribs != 0x0 )
4385 list[idx].trust_attribs = new_dom->domain_trust_attribs;
4387 if ( !set_only ) {
4388 *domains = list;
4389 *num_domains = idx + 1;
4392 return true;
4395 /*********************************************************************
4396 ********************************************************************/
4398 static TDB_DATA make_tdc_key( const char *domain_name )
4400 char *keystr = NULL;
4401 TDB_DATA key = { NULL, 0 };
4403 if ( !domain_name ) {
4404 DEBUG(5,("make_tdc_key: Keyname workgroup is NULL!\n"));
4405 return key;
4408 if (asprintf( &keystr, "TRUSTDOMCACHE/%s", domain_name ) == -1) {
4409 return key;
4411 key = string_term_tdb_data(keystr);
4413 return key;
4416 /*********************************************************************
4417 ********************************************************************/
4419 static int pack_tdc_domains( struct winbindd_tdc_domain *domains,
4420 size_t num_domains,
4421 unsigned char **buf )
4423 unsigned char *buffer = NULL;
4424 int len = 0;
4425 int buflen = 0;
4426 int i = 0;
4428 DEBUG(10,("pack_tdc_domains: Packing %d trusted domains\n",
4429 (int)num_domains));
4431 buflen = 0;
4433 again:
4434 len = 0;
4436 /* Store the number of array items first */
4437 len += tdb_pack( buffer+len, buflen-len, "d",
4438 num_domains );
4440 /* now pack each domain trust record */
4441 for ( i=0; i<num_domains; i++ ) {
4443 fstring tmp;
4445 if ( buflen > 0 ) {
4446 DEBUG(10,("pack_tdc_domains: Packing domain %s (%s)\n",
4447 domains[i].domain_name,
4448 domains[i].dns_name ? domains[i].dns_name : "UNKNOWN" ));
4451 len += tdb_pack( buffer+len, buflen-len, "fffddd",
4452 domains[i].domain_name,
4453 domains[i].dns_name ? domains[i].dns_name : "",
4454 sid_to_fstring(tmp, &domains[i].sid),
4455 domains[i].trust_flags,
4456 domains[i].trust_attribs,
4457 domains[i].trust_type );
4460 if ( buflen < len ) {
4461 SAFE_FREE(buffer);
4462 if ( (buffer = SMB_MALLOC_ARRAY(unsigned char, len)) == NULL ) {
4463 DEBUG(0,("pack_tdc_domains: failed to alloc buffer!\n"));
4464 buflen = -1;
4465 goto done;
4467 buflen = len;
4468 goto again;
4471 *buf = buffer;
4473 done:
4474 return buflen;
4477 /*********************************************************************
4478 ********************************************************************/
4480 static size_t unpack_tdc_domains( unsigned char *buf, int buflen,
4481 struct winbindd_tdc_domain **domains )
4483 fstring domain_name, dns_name, sid_string;
4484 uint32 type, attribs, flags;
4485 int num_domains;
4486 int len = 0;
4487 int i;
4488 struct winbindd_tdc_domain *list = NULL;
4490 /* get the number of domains */
4491 len += tdb_unpack( buf+len, buflen-len, "d", &num_domains);
4492 if ( len == -1 ) {
4493 DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
4494 return 0;
4497 list = talloc_array( NULL, struct winbindd_tdc_domain, num_domains );
4498 if ( !list ) {
4499 DEBUG(0,("unpack_tdc_domains: Failed to talloc() domain list!\n"));
4500 return 0;
4503 for ( i=0; i<num_domains; i++ ) {
4504 int this_len;
4506 this_len = tdb_unpack( buf+len, buflen-len, "fffddd",
4507 domain_name,
4508 dns_name,
4509 sid_string,
4510 &flags,
4511 &attribs,
4512 &type );
4514 if ( this_len == -1 ) {
4515 DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
4516 TALLOC_FREE( list );
4517 return 0;
4519 len += this_len;
4521 DEBUG(11,("unpack_tdc_domains: Unpacking domain %s (%s) "
4522 "SID %s, flags = 0x%x, attribs = 0x%x, type = 0x%x\n",
4523 domain_name, dns_name, sid_string,
4524 flags, attribs, type));
4526 list[i].domain_name = talloc_strdup( list, domain_name );
4527 list[i].dns_name = NULL;
4528 if (dns_name[0] != '\0') {
4529 list[i].dns_name = talloc_strdup(list, dns_name);
4531 if ( !string_to_sid( &(list[i].sid), sid_string ) ) {
4532 DEBUG(10,("unpack_tdc_domains: no SID for domain %s\n",
4533 domain_name));
4535 list[i].trust_flags = flags;
4536 list[i].trust_attribs = attribs;
4537 list[i].trust_type = type;
4540 *domains = list;
4542 return num_domains;
4545 /*********************************************************************
4546 ********************************************************************/
4548 static bool wcache_tdc_store_list( struct winbindd_tdc_domain *domains, size_t num_domains )
4550 TDB_DATA key = make_tdc_key( lp_workgroup() );
4551 TDB_DATA data = { NULL, 0 };
4552 int ret;
4554 if ( !key.dptr )
4555 return false;
4557 /* See if we were asked to delete the cache entry */
4559 if ( !domains ) {
4560 ret = tdb_delete( wcache->tdb, key );
4561 goto done;
4564 data.dsize = pack_tdc_domains( domains, num_domains, &data.dptr );
4566 if ( !data.dptr ) {
4567 ret = -1;
4568 goto done;
4571 ret = tdb_store( wcache->tdb, key, data, 0 );
4573 done:
4574 SAFE_FREE( data.dptr );
4575 SAFE_FREE( key.dptr );
4577 return ( ret == 0 );
4580 /*********************************************************************
4581 ********************************************************************/
4583 bool wcache_tdc_fetch_list( struct winbindd_tdc_domain **domains, size_t *num_domains )
4585 TDB_DATA key = make_tdc_key( lp_workgroup() );
4586 TDB_DATA data = { NULL, 0 };
4588 *domains = NULL;
4589 *num_domains = 0;
4591 if ( !key.dptr )
4592 return false;
4594 data = tdb_fetch_compat( wcache->tdb, key );
4596 SAFE_FREE( key.dptr );
4598 if ( !data.dptr )
4599 return false;
4601 *num_domains = unpack_tdc_domains( data.dptr, data.dsize, domains );
4603 SAFE_FREE( data.dptr );
4605 if ( !*domains )
4606 return false;
4608 return true;
4611 /*********************************************************************
4612 ********************************************************************/
4614 bool wcache_tdc_add_domain( struct winbindd_domain *domain )
4616 struct winbindd_tdc_domain *dom_list = NULL;
4617 size_t num_domains = 0;
4618 bool ret = false;
4620 DEBUG(10,("wcache_tdc_add_domain: Adding domain %s (%s), SID %s, "
4621 "flags = 0x%x, attributes = 0x%x, type = 0x%x\n",
4622 domain->name, domain->alt_name,
4623 sid_string_dbg(&domain->sid),
4624 domain->domain_flags,
4625 domain->domain_trust_attribs,
4626 domain->domain_type));
4628 if ( !init_wcache() ) {
4629 return false;
4632 /* fetch the list */
4634 wcache_tdc_fetch_list( &dom_list, &num_domains );
4636 /* add the new domain */
4638 if ( !add_wbdomain_to_tdc_array( domain, &dom_list, &num_domains ) ) {
4639 goto done;
4642 /* pack the domain */
4644 if ( !wcache_tdc_store_list( dom_list, num_domains ) ) {
4645 goto done;
4648 /* Success */
4650 ret = true;
4651 done:
4652 TALLOC_FREE( dom_list );
4654 return ret;
4657 static struct winbindd_tdc_domain *wcache_tdc_dup_domain(
4658 TALLOC_CTX *mem_ctx, const struct winbindd_tdc_domain *src)
4660 struct winbindd_tdc_domain *dst;
4662 dst = talloc(mem_ctx, struct winbindd_tdc_domain);
4663 if (dst == NULL) {
4664 goto fail;
4666 dst->domain_name = talloc_strdup(dst, src->domain_name);
4667 if (dst->domain_name == NULL) {
4668 goto fail;
4671 dst->dns_name = NULL;
4672 if (src->dns_name != NULL) {
4673 dst->dns_name = talloc_strdup(dst, src->dns_name);
4674 if (dst->dns_name == NULL) {
4675 goto fail;
4679 sid_copy(&dst->sid, &src->sid);
4680 dst->trust_flags = src->trust_flags;
4681 dst->trust_type = src->trust_type;
4682 dst->trust_attribs = src->trust_attribs;
4683 return dst;
4684 fail:
4685 TALLOC_FREE(dst);
4686 return NULL;
4689 /*********************************************************************
4690 ********************************************************************/
4692 struct winbindd_tdc_domain * wcache_tdc_fetch_domain( TALLOC_CTX *ctx, const char *name )
4694 struct winbindd_tdc_domain *dom_list = NULL;
4695 size_t num_domains = 0;
4696 int i;
4697 struct winbindd_tdc_domain *d = NULL;
4699 DEBUG(10,("wcache_tdc_fetch_domain: Searching for domain %s\n", name));
4701 if ( !init_wcache() ) {
4702 return NULL;
4705 /* fetch the list */
4707 wcache_tdc_fetch_list( &dom_list, &num_domains );
4709 for ( i=0; i<num_domains; i++ ) {
4710 if ( strequal(name, dom_list[i].domain_name) ||
4711 strequal(name, dom_list[i].dns_name) )
4713 DEBUG(10,("wcache_tdc_fetch_domain: Found domain %s\n",
4714 name));
4716 d = wcache_tdc_dup_domain(ctx, &dom_list[i]);
4717 break;
4721 TALLOC_FREE( dom_list );
4723 return d;
4726 /*********************************************************************
4727 ********************************************************************/
4729 struct winbindd_tdc_domain*
4730 wcache_tdc_fetch_domainbysid(TALLOC_CTX *ctx,
4731 const struct dom_sid *sid)
4733 struct winbindd_tdc_domain *dom_list = NULL;
4734 size_t num_domains = 0;
4735 int i;
4736 struct winbindd_tdc_domain *d = NULL;
4738 DEBUG(10,("wcache_tdc_fetch_domainbysid: Searching for domain %s\n",
4739 sid_string_dbg(sid)));
4741 if (!init_wcache()) {
4742 return NULL;
4745 /* fetch the list */
4747 wcache_tdc_fetch_list(&dom_list, &num_domains);
4749 for (i = 0; i<num_domains; i++) {
4750 if (dom_sid_equal(sid, &(dom_list[i].sid))) {
4751 DEBUG(10, ("wcache_tdc_fetch_domainbysid: "
4752 "Found domain %s for SID %s\n",
4753 dom_list[i].domain_name,
4754 sid_string_dbg(sid)));
4756 d = wcache_tdc_dup_domain(ctx, &dom_list[i]);
4757 break;
4761 TALLOC_FREE(dom_list);
4763 return d;
4767 /*********************************************************************
4768 ********************************************************************/
4770 void wcache_tdc_clear( void )
4772 if ( !init_wcache() )
4773 return;
4775 wcache_tdc_store_list( NULL, 0 );
4777 return;
4781 /*********************************************************************
4782 ********************************************************************/
4784 static void wcache_save_user_pwinfo(struct winbindd_domain *domain,
4785 NTSTATUS status,
4786 const struct dom_sid *user_sid,
4787 const char *homedir,
4788 const char *shell,
4789 const char *gecos,
4790 uint32 gid)
4792 struct cache_entry *centry;
4793 fstring tmp;
4795 if ( (centry = centry_start(domain, status)) == NULL )
4796 return;
4798 centry_put_string( centry, homedir );
4799 centry_put_string( centry, shell );
4800 centry_put_string( centry, gecos );
4801 centry_put_uint32( centry, gid );
4803 centry_end(centry, "NSS/PWINFO/%s", sid_to_fstring(tmp, user_sid) );
4805 DEBUG(10,("wcache_save_user_pwinfo: %s\n", sid_string_dbg(user_sid) ));
4807 centry_free(centry);
4810 #ifdef HAVE_ADS
4812 NTSTATUS nss_get_info_cached( struct winbindd_domain *domain,
4813 const struct dom_sid *user_sid,
4814 TALLOC_CTX *ctx,
4815 const char **homedir, const char **shell,
4816 const char **gecos, gid_t *p_gid)
4818 struct winbind_cache *cache = get_cache(domain);
4819 struct cache_entry *centry = NULL;
4820 NTSTATUS nt_status;
4821 fstring tmp;
4823 if (!cache->tdb)
4824 goto do_query;
4826 centry = wcache_fetch(cache, domain, "NSS/PWINFO/%s",
4827 sid_to_fstring(tmp, user_sid));
4829 if (!centry)
4830 goto do_query;
4832 *homedir = centry_string( centry, ctx );
4833 *shell = centry_string( centry, ctx );
4834 *gecos = centry_string( centry, ctx );
4835 *p_gid = centry_uint32( centry );
4837 centry_free(centry);
4839 DEBUG(10,("nss_get_info_cached: [Cached] - user_sid %s\n",
4840 sid_string_dbg(user_sid)));
4842 return NT_STATUS_OK;
4844 do_query:
4846 nt_status = nss_get_info( domain->name, user_sid, ctx,
4847 homedir, shell, gecos, p_gid );
4849 DEBUG(10, ("nss_get_info returned %s\n", nt_errstr(nt_status)));
4851 if ( NT_STATUS_IS_OK(nt_status) ) {
4852 DEBUG(10, ("result:\n\thomedir = '%s'\n", *homedir));
4853 DEBUGADD(10, ("\tshell = '%s'\n", *shell));
4854 DEBUGADD(10, ("\tgecos = '%s'\n", *gecos));
4855 DEBUGADD(10, ("\tgid = '%u'\n", (unsigned int)*p_gid));
4857 wcache_save_user_pwinfo( domain, nt_status, user_sid,
4858 *homedir, *shell, *gecos, *p_gid );
4861 if ( NT_STATUS_EQUAL( nt_status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND ) ) {
4862 DEBUG(5,("nss_get_info_cached: Setting domain %s offline\n",
4863 domain->name ));
4864 set_domain_offline( domain );
4867 return nt_status;
4870 #endif
4872 /* the cache backend methods are exposed via this structure */
4873 struct winbindd_methods cache_methods = {
4874 true,
4875 query_user_list,
4876 enum_dom_groups,
4877 enum_local_groups,
4878 name_to_sid,
4879 sid_to_name,
4880 rids_to_names,
4881 query_user,
4882 lookup_usergroups,
4883 lookup_useraliases,
4884 lookup_groupmem,
4885 sequence_number,
4886 lockout_policy,
4887 password_policy,
4888 trusted_domains
4891 static bool wcache_ndr_key(TALLOC_CTX *mem_ctx, const char *domain_name,
4892 uint32_t opnum, const DATA_BLOB *req,
4893 TDB_DATA *pkey)
4895 char *key;
4896 size_t keylen;
4898 key = talloc_asprintf(mem_ctx, "NDR/%s/%d/", domain_name, (int)opnum);
4899 if (key == NULL) {
4900 return false;
4902 keylen = talloc_get_size(key) - 1;
4904 key = talloc_realloc(mem_ctx, key, char, keylen + req->length);
4905 if (key == NULL) {
4906 return false;
4908 memcpy(key + keylen, req->data, req->length);
4910 pkey->dptr = (uint8_t *)key;
4911 pkey->dsize = talloc_get_size(key);
4912 return true;
4915 static bool wcache_opnum_cacheable(uint32_t opnum)
4917 switch (opnum) {
4918 case NDR_WBINT_PING:
4919 case NDR_WBINT_QUERYSEQUENCENUMBER:
4920 case NDR_WBINT_ALLOCATEUID:
4921 case NDR_WBINT_ALLOCATEGID:
4922 case NDR_WBINT_CHECKMACHINEACCOUNT:
4923 case NDR_WBINT_CHANGEMACHINEACCOUNT:
4924 case NDR_WBINT_PINGDC:
4925 return false;
4927 return true;
4930 bool wcache_fetch_ndr(TALLOC_CTX *mem_ctx, struct winbindd_domain *domain,
4931 uint32_t opnum, const DATA_BLOB *req, DATA_BLOB *resp)
4933 TDB_DATA key, data;
4934 bool ret = false;
4936 if (!wcache_opnum_cacheable(opnum) ||
4937 is_my_own_sam_domain(domain) ||
4938 is_builtin_domain(domain)) {
4939 return false;
4942 if (wcache->tdb == NULL) {
4943 return false;
4946 if (!wcache_ndr_key(talloc_tos(), domain->name, opnum, req, &key)) {
4947 return false;
4949 data = tdb_fetch_compat(wcache->tdb, key);
4950 TALLOC_FREE(key.dptr);
4952 if (data.dptr == NULL) {
4953 return false;
4955 if (data.dsize < 12) {
4956 goto fail;
4959 if (!is_domain_offline(domain)) {
4960 uint32_t entry_seqnum, dom_seqnum, last_check;
4961 uint64_t entry_timeout;
4963 if (!wcache_fetch_seqnum(domain->name, &dom_seqnum,
4964 &last_check)) {
4965 goto fail;
4967 entry_seqnum = IVAL(data.dptr, 0);
4968 if (entry_seqnum != dom_seqnum) {
4969 DEBUG(10, ("Entry has wrong sequence number: %d\n",
4970 (int)entry_seqnum));
4971 goto fail;
4973 entry_timeout = BVAL(data.dptr, 4);
4974 if (time(NULL) > entry_timeout) {
4975 DEBUG(10, ("Entry has timed out\n"));
4976 goto fail;
4980 resp->data = (uint8_t *)talloc_memdup(mem_ctx, data.dptr + 12,
4981 data.dsize - 12);
4982 if (resp->data == NULL) {
4983 DEBUG(10, ("talloc failed\n"));
4984 goto fail;
4986 resp->length = data.dsize - 12;
4988 ret = true;
4989 fail:
4990 SAFE_FREE(data.dptr);
4991 return ret;
4994 void wcache_store_ndr(struct winbindd_domain *domain, uint32_t opnum,
4995 const DATA_BLOB *req, const DATA_BLOB *resp)
4997 TDB_DATA key, data;
4998 uint32_t dom_seqnum, last_check;
4999 uint64_t timeout;
5001 if (!wcache_opnum_cacheable(opnum) ||
5002 is_my_own_sam_domain(domain) ||
5003 is_builtin_domain(domain)) {
5004 return;
5007 if (wcache->tdb == NULL) {
5008 return;
5011 if (!wcache_fetch_seqnum(domain->name, &dom_seqnum, &last_check)) {
5012 DEBUG(10, ("could not fetch seqnum for domain %s\n",
5013 domain->name));
5014 return;
5017 if (!wcache_ndr_key(talloc_tos(), domain->name, opnum, req, &key)) {
5018 return;
5021 timeout = time(NULL) + lp_winbind_cache_time();
5023 data.dsize = resp->length + 12;
5024 data.dptr = talloc_array(key.dptr, uint8_t, data.dsize);
5025 if (data.dptr == NULL) {
5026 goto done;
5029 SIVAL(data.dptr, 0, dom_seqnum);
5030 SBVAL(data.dptr, 4, timeout);
5031 memcpy(data.dptr + 12, resp->data, resp->length);
5033 tdb_store(wcache->tdb, key, data, 0);
5035 done:
5036 TALLOC_FREE(key.dptr);
5037 return;